diff --git a/cmd/tusd/cli/hooks.go b/cmd/tusd/cli/hooks.go index 1bb9210..46b0919 100644 --- a/cmd/tusd/cli/hooks.go +++ b/cmd/tusd/cli/hooks.go @@ -20,27 +20,31 @@ func hookTypeInSlice(a hooks.HookType, list []hooks.HookType) bool { return false } -func hookCallback(typ hooks.HookType, info handler.HookEvent) error { - if output, err := invokeHookSync(typ, info, true); err != nil { +func hookCallback(typ hooks.HookType, info handler.HookEvent, captureOutput bool) (error, []byte) { + output, err := invokeHookSync(typ, info, true) + if err != nil { if hookErr, ok := err.(hooks.HookError); ok { return hooks.NewHookError( fmt.Errorf("%s hook failed: %s", typ, err), hookErr.StatusCode(), hookErr.Body(), - ) + ), nil } - return fmt.Errorf("%s hook failed: %s\n%s", typ, err, string(output)) + return fmt.Errorf("%s hook failed: %s\n%s", typ, err, string(output)), nil } - - return nil + if captureOutput { + return nil, output + } + return nil, nil } func preCreateCallback(info handler.HookEvent) error { - return hookCallback(hooks.HookPreCreate, info) + err, _ := hookCallback(hooks.HookPreCreate, info, false) + return err } -func preFinishCallback(info handler.HookEvent) error { - return hookCallback(hooks.HookPreFinish, info) +func preFinishCallback(info handler.HookEvent) (error, []byte) { + return hookCallback(hooks.HookPreFinish, info, true) } func SetupHookMetrics() { diff --git a/pkg/handler/config.go b/pkg/handler/config.go index ae9676b..5fb5431 100644 --- a/pkg/handler/config.go +++ b/pkg/handler/config.go @@ -48,7 +48,7 @@ type Config struct { // PreFinishResponseCallback will be invoked after an upload is completed but before // a response is returned to the client. Error responses from the callback will be passed // back to the client. This can be used to implement post-processing validation. - PreFinishResponseCallback func(hook HookEvent) error + PreFinishResponseCallback func(hook HookEvent) (error, []byte) } func (config *Config) validate() error { diff --git a/pkg/handler/unrouted_handler.go b/pkg/handler/unrouted_handler.go index b4b2de6..de66c40 100644 --- a/pkg/handler/unrouted_handler.go +++ b/pkg/handler/unrouted_handler.go @@ -406,7 +406,7 @@ func (handler *UnroutedHandler) PostFile(w http.ResponseWriter, r *http.Request) // Directly finish the upload if the upload is empty (i.e. has a size of 0). // This statement is in an else-if block to avoid causing duplicate calls // to finishUploadIfComplete if an upload is empty and contains a chunk. - if err := handler.finishUploadIfComplete(ctx, upload, info, r); err != nil { + if err := handler.finishUploadIfComplete(ctx, upload, info, r, w); err != nil { handler.sendError(w, r, err) return } @@ -668,13 +668,13 @@ func (handler *UnroutedHandler) writeChunk(ctx context.Context, upload Upload, i handler.Metrics.incBytesReceived(uint64(bytesWritten)) info.Offset = newOffset - return handler.finishUploadIfComplete(ctx, upload, info, r) + return handler.finishUploadIfComplete(ctx, upload, info, r, w) } // finishUploadIfComplete checks whether an upload is completed (i.e. upload offset // matches upload size) and if so, it will call the data store's FinishUpload // function and send the necessary message on the CompleteUpload channel. -func (handler *UnroutedHandler) finishUploadIfComplete(ctx context.Context, upload Upload, info FileInfo, r *http.Request) error { +func (handler *UnroutedHandler) finishUploadIfComplete(ctx context.Context, upload Upload, info FileInfo, r *http.Request, w http.ResponseWriter) error { // If the upload is completed, ... if !info.SizeIsDeferred && info.Offset == info.Size { // ... allow custom mechanism to finish and cleanup the upload @@ -690,9 +690,13 @@ func (handler *UnroutedHandler) finishUploadIfComplete(ctx context.Context, uplo handler.Metrics.incUploadsFinished() if handler.config.PreFinishResponseCallback != nil { - if err := handler.config.PreFinishResponseCallback(newHookEvent(info, r)); err != nil { + err, output := handler.config.PreFinishResponseCallback(newHookEvent(info, r)) + if err != nil { return err } + if output != nil { + w.Header().Set("X-pre-finish-response", string(output)) + } } }