Forward status code and body from pre-create HTTP hook (#236)
* Forward status code from Pre-create http hook Fix #170 * Allow the returned body to difer from the logged error * Update HTTP Hooks documentation
This commit is contained in:
parent
df77e34fed
commit
0baa9eaf48
|
@ -30,8 +30,26 @@ type hookDataStore struct {
|
||||||
tusd.DataStore
|
tusd.DataStore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type hookError struct {
|
||||||
|
error
|
||||||
|
statusCode int
|
||||||
|
body []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (herr hookError) StatusCode() int {
|
||||||
|
return herr.statusCode
|
||||||
|
}
|
||||||
|
|
||||||
|
func (herr hookError) Body() []byte {
|
||||||
|
return herr.body
|
||||||
|
}
|
||||||
|
|
||||||
func (store hookDataStore) NewUpload(info tusd.FileInfo) (id string, err error) {
|
func (store hookDataStore) NewUpload(info tusd.FileInfo) (id string, err error) {
|
||||||
if output, err := invokeHookSync(HookPreCreate, info, true); err != nil {
|
if output, err := invokeHookSync(HookPreCreate, info, true); err != nil {
|
||||||
|
if hookErr, ok := err.(hookError); ok {
|
||||||
|
hookErr.error = fmt.Errorf("pre-create hook failed: %s", err)
|
||||||
|
return "", hookErr
|
||||||
|
}
|
||||||
return "", fmt.Errorf("pre-create hook failed: %s\n%s", err, string(output))
|
return "", fmt.Errorf("pre-create hook failed: %s\n%s", err, string(output))
|
||||||
}
|
}
|
||||||
return store.DataStore.NewUpload(info)
|
return store.DataStore.NewUpload(info)
|
||||||
|
@ -144,7 +162,7 @@ func invokeHttpHook(name string, typ HookType, info tusd.FileInfo, captureOutput
|
||||||
}
|
}
|
||||||
|
|
||||||
if resp.StatusCode >= http.StatusBadRequest {
|
if resp.StatusCode >= http.StatusBadRequest {
|
||||||
return body, fmt.Errorf("endpoint returned: %s\n%s", resp.Status, body)
|
return body, hookError{fmt.Errorf("endpoint returned: %s", resp.Status), resp.StatusCode, body}
|
||||||
}
|
}
|
||||||
|
|
||||||
if captureOutput {
|
if captureOutput {
|
||||||
|
|
|
@ -7,11 +7,21 @@ When a specific action happens during an upload (pre-create, post-receive, post-
|
||||||
1. Execute an arbitrary file, mirroring the Git hook system, called File Hooks.
|
1. Execute an arbitrary file, mirroring the Git hook system, called File Hooks.
|
||||||
2. Fire off an HTTP POST request to a custom endpoint, called HTTP Hooks.
|
2. Fire off an HTTP POST request to a custom endpoint, called HTTP Hooks.
|
||||||
|
|
||||||
## Blocking and Non-Blocking Hooks
|
## Non-Blocking Hooks
|
||||||
|
|
||||||
If not otherwise noted, all hooks are invoked in a *non-blocking* way, meaning that tusd will not wait until the hook process has finished and exited. Therefore, the hook process is not able to influence how tusd may continue handling the current request, regardless of which exit code it may set. Furthermore, the hook process' stdout and stderr will be piped to tusd's stdout and stderr correspondingly, allowing one to use these channels for additional logging.
|
If not otherwise noted, all hooks are invoked in a *non-blocking* way, meaning that tusd will not wait until the hook process has finished and exited. Therefore, the hook process is not able to influence how tusd may continue handling the current request, regardless of which exit code it may set. Furthermore, the hook process' stdout and stderr will be piped to tusd's stdout and stderr correspondingly, allowing one to use these channels for additional logging.
|
||||||
|
|
||||||
On the other hand, there are a few *blocking* hooks, such as caused by the `pre-create` event. Because their exit code will dictate whether tusd will accept the current incoming request, tusd will wait until the hook process has exited. Therefore, in order to keep the response times low, one should avoid to make time-consuming operations inside the processes for blocking hooks. An exit code of `0` indicates that tusd should continue handling the request as normal. On the other hand, a non-zero exit code tells tusd to reject the request with a `500 Internal Server Error` response containing the process' output from stderr. For the sake of logging, the process' output from stdout will always be piped to tusd's stdout.
|
## Blocking Hooks
|
||||||
|
|
||||||
|
On the other hand, there are a few *blocking* hooks, such as caused by the `pre-create` event. Because their exit code will dictate whether tusd will accept the current incoming request, tusd will wait until the hook process has exited. Therefore, in order to keep the response times low, one should avoid to make time-consuming operations inside the processes for blocking hooks.
|
||||||
|
|
||||||
|
### Blocking File Hooks
|
||||||
|
|
||||||
|
An exit code of `0` indicates that tusd should continue handling the request as normal. On the other hand, a non-zero exit code tells tusd to reject the request with a `500 Internal Server Error` response containing the process' output from stderr. For the sake of logging, the process' output from stdout will always be piped to tusd's stdout.
|
||||||
|
|
||||||
|
### Blocking HTTP Hooks
|
||||||
|
|
||||||
|
A successful HTTP response code (i.e. smaller than `400`) indicates that tusd should continue handling the request as normal. On the other hand, an HTTP response code greater than `400` will be forwarded to the client performing the upload, along with the body of the hook response. Only the response code will be logged by tusd.
|
||||||
|
|
||||||
## List of Available Hooks
|
## List of Available Hooks
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@ var (
|
||||||
type HTTPError interface {
|
type HTTPError interface {
|
||||||
error
|
error
|
||||||
StatusCode() int
|
StatusCode() int
|
||||||
|
Body() []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
type httpError struct {
|
type httpError struct {
|
||||||
|
@ -42,6 +43,10 @@ func (err httpError) StatusCode() int {
|
||||||
return err.statusCode
|
return err.statusCode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (err httpError) Body() []byte {
|
||||||
|
return []byte(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
// NewHTTPError adds the given status code to the provided error and returns
|
// NewHTTPError adds the given status code to the provided error and returns
|
||||||
// the new error instance. The status code may be used in corresponding HTTP
|
// the new error instance. The status code may be used in corresponding HTTP
|
||||||
// responses. See the net/http package for standardized status codes.
|
// responses. See the net/http package for standardized status codes.
|
||||||
|
@ -766,15 +771,15 @@ func (handler *UnroutedHandler) sendError(w http.ResponseWriter, r *http.Request
|
||||||
statusErr = NewHTTPError(err, http.StatusInternalServerError)
|
statusErr = NewHTTPError(err, http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
|
|
||||||
reason := err.Error() + "\n"
|
reason := append(statusErr.Body(), '\n')
|
||||||
if r.Method == "HEAD" {
|
if r.Method == "HEAD" {
|
||||||
reason = ""
|
reason = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
||||||
w.Header().Set("Content-Length", strconv.Itoa(len(reason)))
|
w.Header().Set("Content-Length", strconv.Itoa(len(reason)))
|
||||||
w.WriteHeader(statusErr.StatusCode())
|
w.WriteHeader(statusErr.StatusCode())
|
||||||
w.Write([]byte(reason))
|
w.Write(reason)
|
||||||
|
|
||||||
handler.log("ResponseOutgoing", "status", strconv.Itoa(statusErr.StatusCode()), "method", r.Method, "path", r.URL.Path, "error", err.Error())
|
handler.log("ResponseOutgoing", "status", strconv.Itoa(statusErr.StatusCode()), "method", r.Method, "path", r.URL.Path, "error", err.Error())
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue