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:
oliverpool 2019-02-12 22:45:08 +01:00 committed by Marius
parent df77e34fed
commit 0baa9eaf48
3 changed files with 39 additions and 6 deletions

View File

@ -30,8 +30,26 @@ type hookDataStore struct {
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) {
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 store.DataStore.NewUpload(info)
@ -144,7 +162,7 @@ func invokeHttpHook(name string, typ HookType, info tusd.FileInfo, captureOutput
}
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 {

View File

@ -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.
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.
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

View File

@ -31,6 +31,7 @@ var (
type HTTPError interface {
error
StatusCode() int
Body() []byte
}
type httpError struct {
@ -42,6 +43,10 @@ func (err httpError) StatusCode() int {
return err.statusCode
}
func (err httpError) Body() []byte {
return []byte(err.Error())
}
// 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
// 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)
}
reason := err.Error() + "\n"
reason := append(statusErr.Body(), '\n')
if r.Method == "HEAD" {
reason = ""
reason = nil
}
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
w.Header().Set("Content-Length", strconv.Itoa(len(reason)))
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())