Allow hooks to modify response

This commit is contained in:
Marius 2021-11-28 16:42:19 +01:00
parent bff657c6b3
commit 273a47db7f
1 changed files with 45 additions and 27 deletions

View File

@ -46,7 +46,7 @@ func NewError(errCode string, message string, statusCode int) Error {
Message: message, Message: message,
HTTPResponse: HTTPResponse{ HTTPResponse: HTTPResponse{
StatusCode: statusCode, StatusCode: statusCode,
Body: []byte(errCode + ": " + message + "\n"), Body: errCode + ": " + message + "\n",
Headers: HTTPHeaders{ Headers: HTTPHeaders{
"Content-Type": "text/plain; charset=utf-8", "Content-Type": "text/plain; charset=utf-8",
}, },
@ -96,12 +96,9 @@ type HTTPRequest struct {
type HTTPHeaders map[string]string type HTTPHeaders map[string]string
type HTTPResponse struct { type HTTPResponse struct {
// HTTPStatus, HTTPHeaders and HTTPBody control these details of the corresponding
// HTTP response.
// TODO: Currently only works for error responses
StatusCode int StatusCode int
Headers HTTPHeaders Headers HTTPHeaders
Body []byte Body string
} }
func (resp HTTPResponse) writeTo(w http.ResponseWriter) { func (resp HTTPResponse) writeTo(w http.ResponseWriter) {
@ -117,7 +114,21 @@ func (resp HTTPResponse) writeTo(w http.ResponseWriter) {
w.WriteHeader(resp.StatusCode) w.WriteHeader(resp.StatusCode)
if len(resp.Body) > 0 { if len(resp.Body) > 0 {
w.Write(resp.Body) w.Write([]byte(resp.Body))
}
}
func (resp *HTTPResponse) MergeWith(resp2 HTTPResponse) {
if resp2.StatusCode != 0 {
resp.StatusCode = resp2.StatusCode
}
for key, value := range resp2.Headers {
resp.Headers[key] = value
}
if len(resp2.Body) > 0 {
resp.Body = resp2.Body
} }
} }
@ -378,11 +389,18 @@ func (handler *UnroutedHandler) PostFile(w http.ResponseWriter, r *http.Request)
PartialUploads: partialUploadIDs, PartialUploads: partialUploadIDs,
} }
resp := HTTPResponse{
StatusCode: http.StatusCreated,
Headers: HTTPHeaders{},
}
if handler.config.PreUploadCreateCallback != nil { if handler.config.PreUploadCreateCallback != nil {
if _, err := handler.config.PreUploadCreateCallback(newHookEvent(info, r)); err != nil { resp2, err := handler.config.PreUploadCreateCallback(newHookEvent(info, r))
if err != nil {
handler.sendError(w, r, err) handler.sendError(w, r, err)
return return
} }
resp.MergeWith(resp2)
} }
upload, err := handler.composer.Core.NewUpload(ctx, info) upload, err := handler.composer.Core.NewUpload(ctx, info)
@ -402,12 +420,7 @@ func (handler *UnroutedHandler) PostFile(w http.ResponseWriter, r *http.Request)
// Add the Location header directly after creating the new resource to even // Add the Location header directly after creating the new resource to even
// include it in cases of failure when an error is returned // include it in cases of failure when an error is returned
url := handler.absFileURL(r, id) url := handler.absFileURL(r, id)
resp := HTTPResponse{ resp.Headers["Location"] = url
StatusCode: http.StatusCreated,
Headers: HTTPHeaders{
"Location": url,
},
}
handler.Metrics.incUploadsCreated() handler.Metrics.incUploadsCreated()
handler.log("UploadCreated", "id", id, "size", i64toa(size), "url", url) handler.log("UploadCreated", "id", id, "size", i64toa(size), "url", url)
@ -440,7 +453,8 @@ func (handler *UnroutedHandler) PostFile(w http.ResponseWriter, r *http.Request)
defer lock.Unlock() defer lock.Unlock()
} }
if err := handler.writeChunk(ctx, upload, info, resp, r); err != nil { resp, err = handler.writeChunk(ctx, upload, info, resp, r)
if err != nil {
handler.sendError(w, r, err) handler.sendError(w, r, err)
return return
} }
@ -448,7 +462,8 @@ 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 { resp, err = handler.finishUploadIfComplete(ctx, upload, info, resp, r)
if err != nil {
handler.sendError(w, r, err) handler.sendError(w, r, err)
return return
} }
@ -620,7 +635,8 @@ func (handler *UnroutedHandler) PatchFile(w http.ResponseWriter, r *http.Request
info.SizeIsDeferred = false info.SizeIsDeferred = false
} }
if err := handler.writeChunk(ctx, upload, info, resp, r); err != nil { resp, err = handler.writeChunk(ctx, upload, info, resp, r)
if err != nil {
handler.sendError(w, r, err) handler.sendError(w, r, err)
return return
} }
@ -631,7 +647,7 @@ func (handler *UnroutedHandler) PatchFile(w http.ResponseWriter, r *http.Request
// writeChunk reads the body from the requests r and appends it to the upload // writeChunk reads the body from the requests r and appends it to the upload
// with the corresponding id. Afterwards, it will set the necessary response // with the corresponding id. Afterwards, it will set the necessary response
// headers but will not send the response. // headers but will not send the response.
func (handler *UnroutedHandler) writeChunk(ctx context.Context, upload Upload, info FileInfo, resp HTTPResponse, r *http.Request) error { func (handler *UnroutedHandler) writeChunk(ctx context.Context, upload Upload, info FileInfo, resp HTTPResponse, r *http.Request) (HTTPResponse, error) {
// Get Content-Length if possible // Get Content-Length if possible
length := r.ContentLength length := r.ContentLength
offset := info.Offset offset := info.Offset
@ -639,7 +655,7 @@ func (handler *UnroutedHandler) writeChunk(ctx context.Context, upload Upload, i
// Test if this upload fits into the file's size // Test if this upload fits into the file's size
if !info.SizeIsDeferred && offset+length > info.Size { if !info.SizeIsDeferred && offset+length > info.Size {
return ErrSizeExceeded return resp, ErrSizeExceeded
} }
maxSize := info.Size - offset maxSize := info.Size - offset
@ -719,7 +735,7 @@ func (handler *UnroutedHandler) writeChunk(ctx context.Context, upload Upload, i
handler.log("ChunkWriteComplete", "id", id, "bytesWritten", i64toa(bytesWritten)) handler.log("ChunkWriteComplete", "id", id, "bytesWritten", i64toa(bytesWritten))
if err != nil { if err != nil {
return err return resp, err
} }
// Send new offset to client // Send new offset to client
@ -728,18 +744,18 @@ 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, resp, r)
} }
// 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, resp HTTPResponse, r *http.Request) (HTTPResponse, 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
if err := upload.FinishUpload(ctx); err != nil { if err := upload.FinishUpload(ctx); err != nil {
return err return resp, err
} }
// ... send the info out to the channel // ... send the info out to the channel
@ -750,13 +766,15 @@ 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 { resp2, err := handler.config.PreFinishResponseCallback(newHookEvent(info, r))
return err if err != nil {
return resp, err
} }
resp.MergeWith(resp2)
} }
} }
return nil return resp, nil
} }
// GetFile handles requests to download a file using a GET request. This is not // GetFile handles requests to download a file using a GET request. This is not
@ -800,7 +818,7 @@ func (handler *UnroutedHandler) GetFile(w http.ResponseWriter, r *http.Request)
"Content-Type": contentType, "Content-Type": contentType,
"Content-Disposition": contentDisposition, "Content-Disposition": contentDisposition,
}, },
Body: nil, // Body is intentionally left nil, and we copy it manually in later. Body: "", // Body is intentionally left empty, and we copy it manually in later.
} }
// If no data has been uploaded yet, respond with an empty "204 No Content" status. // If no data has been uploaded yet, respond with an empty "204 No Content" status.
@ -1006,7 +1024,7 @@ func (handler *UnroutedHandler) sendError(w http.ResponseWriter, r *http.Request
// If we are sending the response for a HEAD request, ensure that we are not including // If we are sending the response for a HEAD request, ensure that we are not including
// any response body. // any response body.
if r.Method == "HEAD" { if r.Method == "HEAD" {
detailedErr.HTTPResponse.Body = nil detailedErr.HTTPResponse.Body = ""
} }
handler.sendResp(w, r, detailedErr.HTTPResponse) handler.sendResp(w, r, detailedErr.HTTPResponse)