Responses to the pre-finish hook will be passed to the client as custom header "X-pre-finish-response"
This commit is contained in:
Julian Berkner 2020-05-27 10:14:11 +02:00
parent 52181920c2
commit c611f55608
3 changed files with 22 additions and 14 deletions

View File

@ -20,27 +20,31 @@ func hookTypeInSlice(a hooks.HookType, list []hooks.HookType) bool {
return false return false
} }
func hookCallback(typ hooks.HookType, info handler.HookEvent) error { func hookCallback(typ hooks.HookType, info handler.HookEvent, captureOutput bool) (error, []byte) {
if output, err := invokeHookSync(typ, info, true); err != nil { output, err := invokeHookSync(typ, info, true)
if err != nil {
if hookErr, ok := err.(hooks.HookError); ok { if hookErr, ok := err.(hooks.HookError); ok {
return hooks.NewHookError( return hooks.NewHookError(
fmt.Errorf("%s hook failed: %s", typ, err), fmt.Errorf("%s hook failed: %s", typ, err),
hookErr.StatusCode(), hookErr.StatusCode(),
hookErr.Body(), 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
} }
if captureOutput {
return nil return nil, output
}
return nil, nil
} }
func preCreateCallback(info handler.HookEvent) error { 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 { func preFinishCallback(info handler.HookEvent) (error, []byte) {
return hookCallback(hooks.HookPreFinish, info) return hookCallback(hooks.HookPreFinish, info, true)
} }
func SetupHookMetrics() { func SetupHookMetrics() {

View File

@ -48,7 +48,7 @@ type Config struct {
// PreFinishResponseCallback will be invoked after an upload is completed but before // 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 // 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. // 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 { func (config *Config) validate() error {

View File

@ -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). // 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 // This statement is in an else-if block to avoid causing duplicate calls
// to finishUploadIfComplete if an upload is empty and contains a chunk. // 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) handler.sendError(w, r, err)
return return
} }
@ -668,13 +668,13 @@ func (handler *UnroutedHandler) writeChunk(ctx context.Context, upload Upload, i
handler.Metrics.incBytesReceived(uint64(bytesWritten)) handler.Metrics.incBytesReceived(uint64(bytesWritten))
info.Offset = newOffset 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 // 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 // matches upload size) and if so, it will call the data store's FinishUpload
// function and send the necessary message on the CompleteUpload channel. // 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 the upload is completed, ...
if !info.SizeIsDeferred && info.Offset == info.Size { if !info.SizeIsDeferred && info.Offset == info.Size {
// ... allow custom mechanism to finish and cleanup the upload // ... allow custom mechanism to finish and cleanup the upload
@ -690,9 +690,13 @@ func (handler *UnroutedHandler) finishUploadIfComplete(ctx context.Context, uplo
handler.Metrics.incUploadsFinished() handler.Metrics.incUploadsFinished()
if handler.config.PreFinishResponseCallback != nil { 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 return err
} }
if output != nil {
w.Header().Set("X-pre-finish-response", string(output))
}
} }
} }