Basic context implementation in webhooks.
This commit is contained in:
+12
-12
@@ -1,24 +1,24 @@
|
|||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>Pusher Spec</title>
|
<title>Pusher Spec</title>
|
||||||
<link href="https://cdn.rawgit.com/mochajs/mocha/2.2.5/mocha.css" rel="stylesheet" />
|
<link href="https://cdn.rawgit.com/mochajs/mocha/2.2.5/mocha.css" rel="stylesheet"/>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="mocha"></div>
|
<div id="mocha"></div>
|
||||||
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/mocha/2.3.4/mocha.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/mocha/2.3.4/mocha.min.js"></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/chai/3.4.1/chai.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/chai/3.4.1/chai.min.js"></script>
|
||||||
|
|
||||||
<script src="//js.pusher.com/3.2/pusher.min.js"></script>
|
<script src="//js.pusher.com/3.2/pusher.min.js"></script>
|
||||||
|
|
||||||
<script>mocha.setup('bdd')</script>
|
<script>mocha.setup('bdd')</script>
|
||||||
<script src="test.pusher.js"></script>
|
<script src="test.pusher.js"></script>
|
||||||
<script>
|
<script>
|
||||||
mocha.checkLeaks();
|
mocha.checkLeaks();
|
||||||
mocha.globals(['jQuery', 'Pusher']);
|
mocha.globals(['jQuery', 'Pusher']);
|
||||||
mocha.run();
|
mocha.run();
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ func pusherPresenceAuth(res http.ResponseWriter, req *http.Request) {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Fprintf(res, string(response))
|
fmt.Fprint(res, string(response))
|
||||||
}
|
}
|
||||||
|
|
||||||
func pusherPrivateAuth(res http.ResponseWriter, req *http.Request) {
|
func pusherPrivateAuth(res http.ResponseWriter, req *http.Request) {
|
||||||
@@ -48,13 +48,13 @@ func pusherPrivateAuth(res http.ResponseWriter, req *http.Request) {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Fprintf(res, string(response))
|
fmt.Fprint(res, string(response))
|
||||||
}
|
}
|
||||||
|
|
||||||
func triggerMessage(res http.ResponseWriter, req *http.Request) {
|
func triggerMessage(res http.ResponseWriter, _ *http.Request) {
|
||||||
client.Trigger("private-messages", "messages", "The message from server")
|
client.Trigger("private-messages", "messages", "The message from server")
|
||||||
|
|
||||||
fmt.Fprintf(res, "OK")
|
fmt.Fprint(res, "OK")
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
{
|
{
|
||||||
"Host": ":8080",
|
"Host": ":8080",
|
||||||
"Encrypted": false,
|
"Encrypted": false,
|
||||||
"SSLHost": ":8090",
|
"SSLHost": ":8090",
|
||||||
"SSLKeyFile": "key.pem",
|
"SSLKeyFile": "key.pem",
|
||||||
"SSLCertFile": "cert.pem",
|
"SSLCertFile": "cert.pem",
|
||||||
"Apps": [
|
"Apps": [
|
||||||
{
|
{
|
||||||
"ApplicationDisabled": false,
|
"ApplicationDisabled": false,
|
||||||
"OnlySSL": false,
|
"OnlySSL": false,
|
||||||
"Secret": "7ad3753142a6693b25b9",
|
"Secret": "7ad3753142a6693b25b9",
|
||||||
"Key": "278d525bdf162c739803",
|
"Key": "278d525bdf162c739803",
|
||||||
"Name": "App for Functional Test",
|
"Name": "App for Functional Test",
|
||||||
"AppID": "1",
|
"AppID": "1",
|
||||||
"UserEvents": true,
|
"UserEvents": true,
|
||||||
"WebHooks": false,
|
"WebHooks": false,
|
||||||
"URLWebHook": "http://127.0.0.1:4567/php/hook.php"
|
"URLWebHook": "http://127.0.0.1:4567/php/hook.php"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
+18
-18
@@ -1,20 +1,20 @@
|
|||||||
{
|
{
|
||||||
"Host": ":8080",
|
"Host": ":8080",
|
||||||
"SSL": false,
|
"SSL": false,
|
||||||
"SSLHost": ":4433",
|
"SSLHost": ":4433",
|
||||||
"SSLKeyFile": "A key.pem file",
|
"SSLKeyFile": "A key.pem file",
|
||||||
"SSLCertFile": "A cert.pem file",
|
"SSLCertFile": "A cert.pem file",
|
||||||
"Apps": [
|
"Apps": [
|
||||||
{
|
{
|
||||||
"ApplicationDisabled": false,
|
"ApplicationDisabled": false,
|
||||||
"Secret": "A really secret random string",
|
"Secret": "A really secret random string",
|
||||||
"Key": "A random Key string",
|
"Key": "A random Key string",
|
||||||
"OnlySSL": false,
|
"OnlySSL": false,
|
||||||
"Name": "The app name",
|
"Name": "The app name",
|
||||||
"AppID": "The app ID",
|
"AppID": "The app ID",
|
||||||
"UserEvents": true,
|
"UserEvents": true,
|
||||||
"WebHooks": true,
|
"WebHooks": true,
|
||||||
"URLWebHook": "Some URL to send webhooks"
|
"URLWebHook": "Some URL to send webhooks"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
+38
-7
@@ -10,10 +10,14 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
"github.com/dimiro1/ipe/utils"
|
"github.com/dimiro1/ipe/utils"
|
||||||
log "github.com/golang/glog"
|
log "github.com/golang/glog"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const maxTimeout = 3 * time.Second
|
||||||
|
|
||||||
// A WebHook is sent as a HTTP POST request to the url which you specify.
|
// A WebHook is sent as a HTTP POST request to the url which you specify.
|
||||||
// The POST request payload (body) contains a JSON document, and follows the following format:
|
// The POST request payload (body) contains a JSON document, and follows the following format:
|
||||||
// {
|
// {
|
||||||
@@ -71,14 +75,19 @@ func newClientHook(channel *channel, s *subscription, event string, data interfa
|
|||||||
// { "name": "channel_occupied", "channel": "test_channel" }
|
// { "name": "channel_occupied", "channel": "test_channel" }
|
||||||
func (a *app) TriggerChannelOccupiedHook(c *channel) {
|
func (a *app) TriggerChannelOccupiedHook(c *channel) {
|
||||||
event := newChannelOcuppiedHook(c)
|
event := newChannelOcuppiedHook(c)
|
||||||
triggerHook(event.Name, a, c, event)
|
ctx, cancel := context.WithTimeout(context.Background(), maxTimeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
triggerHook(ctx, event.Name, a, c, event)
|
||||||
}
|
}
|
||||||
|
|
||||||
// channel_vacated
|
// channel_vacated
|
||||||
// { "name": "channel_vacated", "channel": "test_channel" }
|
// { "name": "channel_vacated", "channel": "test_channel" }
|
||||||
func (a *app) TriggerChannelVacatedHook(c *channel) {
|
func (a *app) TriggerChannelVacatedHook(c *channel) {
|
||||||
event := newChannelVacatedHook(c)
|
event := newChannelVacatedHook(c)
|
||||||
triggerHook(event.Name, a, c, event)
|
ctx, cancel := context.WithTimeout(context.Background(), maxTimeout)
|
||||||
|
defer cancel()
|
||||||
|
triggerHook(ctx, event.Name, a, c, event)
|
||||||
}
|
}
|
||||||
|
|
||||||
// {
|
// {
|
||||||
@@ -96,7 +105,9 @@ func (a *app) TriggerClientEventHook(c *channel, s *subscription, clientEvent st
|
|||||||
event.UserID = s.ID
|
event.UserID = s.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
triggerHook(event.Name, a, c, event)
|
ctx, cancel := context.WithTimeout(context.Background(), maxTimeout)
|
||||||
|
defer cancel()
|
||||||
|
triggerHook(ctx, event.Name, a, c, event)
|
||||||
}
|
}
|
||||||
|
|
||||||
// {
|
// {
|
||||||
@@ -106,7 +117,9 @@ func (a *app) TriggerClientEventHook(c *channel, s *subscription, clientEvent st
|
|||||||
// }
|
// }
|
||||||
func (a *app) TriggerMemberAddedHook(c *channel, s *subscription) {
|
func (a *app) TriggerMemberAddedHook(c *channel, s *subscription) {
|
||||||
event := newMemberAddedHook(c, s)
|
event := newMemberAddedHook(c, s)
|
||||||
triggerHook(event.Name, a, c, event)
|
ctx, cancel := context.WithTimeout(context.Background(), maxTimeout)
|
||||||
|
defer cancel()
|
||||||
|
triggerHook(ctx, event.Name, a, c, event)
|
||||||
}
|
}
|
||||||
|
|
||||||
// {
|
// {
|
||||||
@@ -116,15 +129,20 @@ func (a *app) TriggerMemberAddedHook(c *channel, s *subscription) {
|
|||||||
// }
|
// }
|
||||||
func (a *app) TriggerMemberRemovedHook(c *channel, s *subscription) {
|
func (a *app) TriggerMemberRemovedHook(c *channel, s *subscription) {
|
||||||
event := newMemberRemovedHook(c, s)
|
event := newMemberRemovedHook(c, s)
|
||||||
triggerHook(event.Name, a, c, event)
|
ctx, cancel := context.WithTimeout(context.Background(), maxTimeout)
|
||||||
|
defer cancel()
|
||||||
|
triggerHook(ctx, event.Name, a, c, event)
|
||||||
}
|
}
|
||||||
|
|
||||||
func triggerHook(name string, a *app, c *channel, event hookEvent) {
|
func triggerHook(ctx context.Context, name string, a *app, _ *channel, event hookEvent) error {
|
||||||
if !a.WebHooks {
|
if !a.WebHooks {
|
||||||
log.Infof("Webhooks are not enabled for app: %s", a.Name)
|
log.Infof("Webhooks are not enabled for app: %s", a.Name)
|
||||||
return
|
return fmt.Errorf("Webhooks are not enabled for app: %s", a.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var done chan (bool)
|
||||||
|
defer close(done)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
log.Infof("Triggering %s event", name)
|
log.Infof("Triggering %s event", name)
|
||||||
|
|
||||||
@@ -145,11 +163,14 @@ func triggerHook(name string, a *app, c *channel, event hookEvent) {
|
|||||||
var req *http.Request
|
var req *http.Request
|
||||||
|
|
||||||
req, err = http.NewRequest("POST", a.URLWebHook, bytes.NewReader(js))
|
req, err = http.NewRequest("POST", a.URLWebHook, bytes.NewReader(js))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Error creating request: %+v", err)
|
log.Errorf("Error creating request: %+v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
req.WithContext(ctx)
|
||||||
|
|
||||||
req.Header.Set("User-Agent", "Ipe UA; (+https://github.com/dimiro1/ipe)")
|
req.Header.Set("User-Agent", "Ipe UA; (+https://github.com/dimiro1/ipe)")
|
||||||
req.Header.Set("Content-Type", "application/json")
|
req.Header.Set("Content-Type", "application/json")
|
||||||
req.Header.Set("X-Pusher-Key", a.Key)
|
req.Header.Set("X-Pusher-Key", a.Key)
|
||||||
@@ -168,5 +189,15 @@ func triggerHook(name string, a *app, c *channel, event hookEvent) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Error posting %s event: %+v", name, err)
|
log.Errorf("Error posting %s event: %+v", name, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Successfully terminated
|
||||||
|
done <- true
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
case <-done:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-23
@@ -26,13 +26,12 @@ import (
|
|||||||
var upgrader = websocket.Upgrader{
|
var upgrader = websocket.Upgrader{
|
||||||
ReadBufferSize: 1024,
|
ReadBufferSize: 1024,
|
||||||
WriteBufferSize: 1024,
|
WriteBufferSize: 1024,
|
||||||
CheckOrigin: func(r *http.Request) bool { return true },
|
CheckOrigin: func(_ *http.Request) bool {
|
||||||
|
return true
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleMessages(
|
func handleMessages(conn *websocket.Conn, sessionID string, app *app) {
|
||||||
conn *websocket.Conn, w http.ResponseWriter,
|
|
||||||
r *http.Request, sessionID string, app *app) {
|
|
||||||
|
|
||||||
var event struct {
|
var event struct {
|
||||||
Event string `json:"event"`
|
Event string `json:"event"`
|
||||||
}
|
}
|
||||||
@@ -64,7 +63,7 @@ func handleMessages(
|
|||||||
onClientEvent(conn, sessionID, app, message)
|
onClientEvent(conn, sessionID, app, message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // For
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleError(conn *websocket.Conn, sessionID string, app *app, err error) {
|
func handleError(conn *websocket.Conn, sessionID string, app *app, err error) {
|
||||||
@@ -78,10 +77,7 @@ func handleError(conn *websocket.Conn, sessionID string, app *app, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func onOpen(
|
func onOpen(conn *websocket.Conn, r *http.Request, sessionID string, app *app) error {
|
||||||
conn *websocket.Conn, w http.ResponseWriter,
|
|
||||||
r *http.Request, sessionID string, app *app) error {
|
|
||||||
|
|
||||||
params := r.URL.Query()
|
params := r.URL.Query()
|
||||||
p := params.Get("protocol")
|
p := params.Get("protocol")
|
||||||
|
|
||||||
@@ -126,9 +122,7 @@ func onPing(conn *websocket.Conn) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func onClientEvent(
|
func onClientEvent(conn *websocket.Conn, sessionID string, app *app, message []byte) {
|
||||||
conn *websocket.Conn, sessionID string, app *app, message []byte) {
|
|
||||||
|
|
||||||
if !app.UserEvents {
|
if !app.UserEvents {
|
||||||
emitWSError(newGenericError("To send client events, you must enable this feature in the Settings."), conn)
|
emitWSError(newGenericError("To send client events, you must enable this feature in the Settings."), conn)
|
||||||
}
|
}
|
||||||
@@ -159,9 +153,7 @@ func onClientEvent(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func onUnsubscribe(
|
func onUnsubscribe(conn *websocket.Conn, sessionID string, app *app, message []byte) {
|
||||||
conn *websocket.Conn, sessionID string, app *app, message []byte) {
|
|
||||||
|
|
||||||
unsubscribeEvent := unsubscribeEvent{}
|
unsubscribeEvent := unsubscribeEvent{}
|
||||||
|
|
||||||
if err := json.Unmarshal(message, &unsubscribeEvent); err != nil {
|
if err := json.Unmarshal(message, &unsubscribeEvent); err != nil {
|
||||||
@@ -186,9 +178,7 @@ func onUnsubscribe(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func onSubscribe(
|
func onSubscribe(conn *websocket.Conn, sessionID string, app *app, message []byte) {
|
||||||
conn *websocket.Conn, sessionID string, app *app, message []byte) {
|
|
||||||
|
|
||||||
subscribeEvent := subscribeEvent{}
|
subscribeEvent := subscribeEvent{}
|
||||||
|
|
||||||
if err := json.Unmarshal(message, &subscribeEvent); err != nil {
|
if err := json.Unmarshal(message, &subscribeEvent); err != nil {
|
||||||
@@ -206,7 +196,7 @@ func onSubscribe(
|
|||||||
channelName := strings.TrimSpace(subscribeEvent.Data.Channel)
|
channelName := strings.TrimSpace(subscribeEvent.Data.Channel)
|
||||||
|
|
||||||
if !utils.IsChannelNameValid(channelName) {
|
if !utils.IsChannelNameValid(channelName) {
|
||||||
emitWSError(newGenericError(fmt.Sprintf("This channel name is not valid")), conn)
|
emitWSError(newGenericError("This channel name is not valid"), conn)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -259,7 +249,9 @@ func newWebsocketHandler(DB db) goji.Handler {
|
|||||||
return &websocketHandler{DB}
|
return &websocketHandler{DB}
|
||||||
}
|
}
|
||||||
|
|
||||||
type websocketHandler struct{ DB db }
|
type websocketHandler struct {
|
||||||
|
DB db
|
||||||
|
}
|
||||||
|
|
||||||
// Websocket GET /app/{key}
|
// Websocket GET /app/{key}
|
||||||
func (h *websocketHandler) ServeHTTPC(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
func (h *websocketHandler) ServeHTTPC(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
||||||
@@ -287,10 +279,10 @@ func (h *websocketHandler) ServeHTTPC(ctx context.Context, w http.ResponseWriter
|
|||||||
|
|
||||||
sessionID := utils.GenerateSessionID()
|
sessionID := utils.GenerateSessionID()
|
||||||
|
|
||||||
if err := onOpen(conn, w, r, sessionID, app); err != nil {
|
if err := onOpen(conn, r, sessionID, app); err != nil {
|
||||||
emitWSError(err, conn)
|
emitWSError(err, conn)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
handleMessages(conn, w, r, sessionID, app)
|
handleMessages(conn, sessionID, app)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user