Allow hooks to modify response
This commit is contained in:
parent
bff657c6b3
commit
273a47db7f
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue