feat(api): use GET and POST requests for hooks

This commit is contained in:
Nicolas Carlier 2019-01-07 17:16:01 +00:00
parent d11da6fa54
commit e7fac829aa
2 changed files with 54 additions and 23 deletions

View File

@ -90,8 +90,14 @@ You can override the default using the `APP_SCRIPTS_DIR` environment variable.
### Webhook URL
The directory structure define the webhook URL.
The Webhook can only be call with HTTP POST verb.
If the script exists, the HTTP response will be a `text/event-stream` content type (Server-sent events).
If the script exists, the output the will be streamed to the HTTP response.
The streaming technology depends on the HTTP method used.
With `POST` the response will be chunked.
With `GET` the response will use [Server-sent events][sse].
[sse]: https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events
*Example:*
@ -104,8 +110,27 @@ echo "foo foo foo"
echo "bar bar bar"
```
Output using `POST` (`Chunked transfer encoding`):
```bash
$ curl -XPOST http://localhost:8080/foo/bar
$ curl -v -XPOST http://localhost:8080/foo/bar
< HTTP/1.1 200 OK
< Content-Type: text/plain; charset=utf-8
< Transfer-Encoding: chunked
< X-Hook-Id: 7
foo foo foo
bar bar bar
done
```
Output using `GET` (`Server-sent events`):
```bash
$ curl -v -XGET http://localhost:8080/foo/bar
< HTTP/1.1 200 OK
< Content-Type: text/event-stream
< Transfer-Encoding: chunked
< X-Hook-Id: 8
data: foo foo foo
data: bar bar bar
@ -123,7 +148,7 @@ You have several way to provide parameters to your webhook script:
*ex: `CONTENT-TYPE` will become `content_type`.*
- Body content (text/plain or application/json) is transmit to the script as parameter.
- When using `POST`, body content (text/plain or application/json) is transmit to the script as parameter.
*Example:*
@ -141,13 +166,10 @@ The result:
```bash
$ curl --data @test.json http://localhost:8080/echo?foo=bar
data: Query parameter: foo=bar
data: Header parameter: user-agent=curl/7.52.1
data: Script parameter: {"foo": "bar"}
data: done
Query parameter: foo=bar
Header parameter: user-agent=curl/7.52.1
Script parameter: {"foo": "bar"}
done
```
### Webhook timeout configuration
@ -162,7 +184,7 @@ You can override this global behavior per request by setting the HTTP header:
*Example:*
```bash
$ curl -XPOST -H "X-Hook-Timeout: 5" http://localhost:8080/echo?foo=bar
$ curl -H "X-Hook-Timeout: 5" http://localhost:8080/echo?foo=bar
```
### Webhook logs
@ -176,7 +198,7 @@ The hook ID is returned as an HTTP header with the Webhook response: `X-Hook-ID`
```bash
$ # Call webhook
$ curl -v -XPOST http://localhost:8080/echo?foo=bar
$ curl -v http://localhost:8080/echo?foo=bar
...
< HTTP/1.1 200 OK
< Content-Type: text/event-stream

View File

@ -6,6 +6,7 @@ import (
"io/ioutil"
"net/http"
"path"
"path/filepath"
"strconv"
"strings"
@ -36,14 +37,13 @@ func index(conf *config.Config) http.Handler {
}
func webhookHandler(w http.ResponseWriter, r *http.Request) {
if r.Method == "POST" {
triggerWebhook(w, r)
} else if r.Method == "GET" {
getWebhookLog(w, r)
} else {
http.Error(w, "405 Method Not Allowed", http.StatusMethodNotAllowed)
return
if r.Method == "GET" {
if _, err := strconv.Atoi(filepath.Base(r.URL.Path)); err == nil {
getWebhookLog(w, r)
return
}
}
triggerWebhook(w, r)
}
func triggerWebhook(w http.ResponseWriter, r *http.Request) {
@ -82,10 +82,15 @@ func triggerWebhook(w http.ResponseWriter, r *http.Request) {
// Put work in queue
worker.WorkQueue <- *work
w.Header().Set("Content-Type", "text/event-stream")
if r.Method == "GET" {
// Send SSE response
w.Header().Set("Content-Type", "text/event-stream")
} else {
// Send chunked response
w.Header().Set("X-Content-Type-Options", "nosniff")
}
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Connection", "keep-alive")
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("X-Hook-ID", strconv.FormatUint(work.ID, 10))
for {
@ -95,7 +100,11 @@ func triggerWebhook(w http.ResponseWriter, r *http.Request) {
break
}
fmt.Fprintf(w, "data: %s\n\n", msg)
if r.Method == "GET" {
fmt.Fprintf(w, "data: %s\n\n", msg) // Send SSE response
} else {
fmt.Fprintf(w, "%s\n", msg) // Send chunked response
}
// Flush the data immediatly instead of buffering it for later.
flusher.Flush()