Emit post-finish event for empty uploads

See https://github.com/tus/tus-js-client/issues/106
This commit is contained in:
Marius 2018-03-29 14:40:43 +02:00
parent e4017ff47e
commit c470fe3a9d
3 changed files with 59 additions and 7 deletions

View File

@ -223,6 +223,7 @@ func TestPatch(t *testing.T) {
gomock.InOrder( gomock.InOrder(
store.EXPECT().GetInfo("yes").Return(FileInfo{ store.EXPECT().GetInfo("yes").Return(FileInfo{
ID: "yes",
Offset: 5, Offset: 5,
Size: 20, Size: 20,
}, nil), }, nil),

View File

@ -52,6 +52,42 @@ func TestPost(t *testing.T) {
a.Equal(int64(300), info.Size) a.Equal(int64(300), info.Size)
}) })
SubTest(t, "CreateEmptyUpload", func(t *testing.T, store *MockFullDataStore) {
store.EXPECT().NewUpload(FileInfo{
Size: 0,
MetaData: map[string]string{},
}).Return("foo", nil)
store.EXPECT().FinishUpload("foo").Return(nil)
handler, _ := NewHandler(Config{
DataStore: store,
BasePath: "https://buy.art/files/",
NotifyCompleteUploads: true,
})
handler.CompleteUploads = make(chan FileInfo, 1)
(&httpTest{
Method: "POST",
ReqHeader: map[string]string{
"Tus-Resumable": "1.0.0",
"Upload-Length": "0",
},
Code: http.StatusCreated,
ResHeader: map[string]string{
"Location": "https://buy.art/files/foo",
},
}).Run(handler, t)
info := <-handler.CompleteUploads
a := assert.New(t)
a.Equal("foo", info.ID)
a.Equal(int64(0), info.Size)
a.Equal(int64(0), info.Offset)
})
SubTest(t, "CreateExceedingMaxSizeFail", func(t *testing.T, store *MockFullDataStore) { SubTest(t, "CreateExceedingMaxSizeFail", func(t *testing.T, store *MockFullDataStore) {
handler, _ := NewHandler(Config{ handler, _ := NewHandler(Config{
MaxSize: 400, MaxSize: 400,

View File

@ -284,6 +284,8 @@ func (handler *UnroutedHandler) PostFile(w http.ResponseWriter, r *http.Request)
return return
} }
info.ID = id
// 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)
@ -293,7 +295,6 @@ func (handler *UnroutedHandler) PostFile(w http.ResponseWriter, r *http.Request)
handler.log("UploadCreated", "id", id, "size", i64toa(size), "url", url) handler.log("UploadCreated", "id", id, "size", i64toa(size), "url", url)
if handler.config.NotifyCreatedUploads { if handler.config.NotifyCreatedUploads {
info.ID = id
handler.CreatedUploads <- info handler.CreatedUploads <- info
} }
@ -305,7 +306,6 @@ func (handler *UnroutedHandler) PostFile(w http.ResponseWriter, r *http.Request)
info.Offset = size info.Offset = size
if handler.config.NotifyCompleteUploads { if handler.config.NotifyCompleteUploads {
info.ID = id
handler.CompleteUploads <- info handler.CompleteUploads <- info
} }
} }
@ -325,6 +325,11 @@ func (handler *UnroutedHandler) PostFile(w http.ResponseWriter, r *http.Request)
handler.sendError(w, r, err) handler.sendError(w, r, err)
return return
} }
} else if size == 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
// to finishUploadIfComplete if an upload is empty and contains a chunk.
handler.finishUploadIfComplete(info)
} }
handler.sendResp(w, r, http.StatusCreated) handler.sendResp(w, r, http.StatusCreated)
@ -381,7 +386,8 @@ func (handler *UnroutedHandler) HeadFile(w http.ResponseWriter, r *http.Request)
handler.sendResp(w, r, http.StatusOK) handler.sendResp(w, r, http.StatusOK)
} }
// PatchFile adds a chunk to an upload. Only allowed enough space is left. // PatchFile adds a chunk to an upload. This operation is only allowed
// if enough space in the upload is left.
func (handler *UnroutedHandler) PatchFile(w http.ResponseWriter, r *http.Request) { func (handler *UnroutedHandler) PatchFile(w http.ResponseWriter, r *http.Request) {
// Check for presence of application/offset+octet-stream // Check for presence of application/offset+octet-stream
@ -445,7 +451,9 @@ func (handler *UnroutedHandler) PatchFile(w http.ResponseWriter, r *http.Request
handler.sendResp(w, r, http.StatusNoContent) handler.sendResp(w, r, http.StatusNoContent)
} }
// PatchFile adds a chunk to an upload. Only allowed enough space is left. // 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
// headers but will not send the response.
func (handler *UnroutedHandler) writeChunk(id string, info FileInfo, w http.ResponseWriter, r *http.Request) error { func (handler *UnroutedHandler) writeChunk(id string, info FileInfo, w http.ResponseWriter, r *http.Request) error {
// Get Content-Length if possible // Get Content-Length if possible
length := r.ContentLength length := r.ContentLength
@ -489,19 +497,26 @@ func (handler *UnroutedHandler) writeChunk(id string, info FileInfo, w http.Resp
newOffset := offset + bytesWritten newOffset := offset + bytesWritten
w.Header().Set("Upload-Offset", strconv.FormatInt(newOffset, 10)) w.Header().Set("Upload-Offset", strconv.FormatInt(newOffset, 10))
go handler.Metrics.incBytesReceived(uint64(bytesWritten)) go handler.Metrics.incBytesReceived(uint64(bytesWritten))
info.Offset = newOffset
return handler.finishUploadIfComplete(info)
}
// 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(info FileInfo) error {
// If the upload is completed, ... // If the upload is completed, ...
if newOffset == info.Size { if info.Offset == info.Size {
// ... allow custom mechanism to finish and cleanup the upload // ... allow custom mechanism to finish and cleanup the upload
if handler.composer.UsesFinisher { if handler.composer.UsesFinisher {
if err := handler.composer.Finisher.FinishUpload(id); err != nil { if err := handler.composer.Finisher.FinishUpload(info.ID); err != nil {
return err return err
} }
} }
// ... send the info out to the channel // ... send the info out to the channel
if handler.config.NotifyCompleteUploads { if handler.config.NotifyCompleteUploads {
info.Offset = newOffset
handler.CompleteUploads <- info handler.CompleteUploads <- info
} }