add support for Termination extension
This commit is contained in:
parent
09056611d6
commit
9f94d0591b
|
@ -48,4 +48,7 @@ type DataStore interface {
|
|||
// tusd.ErrNotImplemented. The length of the resource is determined by
|
||||
// retrieving the offset using GetInfo.
|
||||
GetReader(id string) (io.Reader, error)
|
||||
// Terminate an upload so any further requests to the resource, both reading
|
||||
// and writing, must return os.ErrNotExist or similar.
|
||||
Terminate(id string) error
|
||||
}
|
||||
|
|
|
@ -72,6 +72,16 @@ func (store FileStore) GetReader(id string) (io.Reader, error) {
|
|||
return os.Open(store.binPath(id))
|
||||
}
|
||||
|
||||
func (store FileStore) Terminate(id string) error {
|
||||
if err := os.Remove(store.infoPath(id)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.Remove(store.binPath(id)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Return the path to the .bin storing the binary data
|
||||
func (store FileStore) binPath(id string) string {
|
||||
return store.Path + "/" + id + ".bin"
|
||||
|
|
|
@ -2,6 +2,7 @@ package filestore
|
|||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
|
@ -75,4 +76,14 @@ func TestFilestore(t *testing.T) {
|
|||
if string(content) != "hello world" {
|
||||
t.Errorf("expected content to be 'hello world'")
|
||||
}
|
||||
|
||||
// Terminate upload
|
||||
if err := store.Terminate(id); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Test if upload is deleted
|
||||
if _, err := store.GetInfo(id); !os.IsNotExist(err) {
|
||||
t.Fatal("expected os.ErrIsNotExist")
|
||||
}
|
||||
}
|
||||
|
|
33
handler.go
33
handler.go
|
@ -104,6 +104,7 @@ func NewHandler(config Config) (*Handler, error) {
|
|||
mux.Post("", http.HandlerFunc(handler.postFile))
|
||||
mux.Head(":id", http.HandlerFunc(handler.headFile))
|
||||
mux.Get(":id", http.HandlerFunc(handler.getFile))
|
||||
mux.Del(":id", http.HandlerFunc(handler.delFile))
|
||||
mux.Add("PATCH", ":id", http.HandlerFunc(handler.patchFile))
|
||||
|
||||
return handler, nil
|
||||
|
@ -141,7 +142,7 @@ func (handler *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
header.Set("TUS-Version", "1.0.0")
|
||||
header.Set("TUS-Extension", "file-creation,metadata,concatenation")
|
||||
header.Set("TUS-Extension", "file-creation,metadata,concatenation,termination")
|
||||
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
return
|
||||
|
@ -370,6 +371,36 @@ func (handler *Handler) getFile(w http.ResponseWriter, r *http.Request) {
|
|||
io.Copy(w, src)
|
||||
}
|
||||
|
||||
// Terminate an upload permanently.
|
||||
func (handler *Handler) delFile(w http.ResponseWriter, r *http.Request) {
|
||||
id := r.URL.Query().Get(":id")
|
||||
|
||||
// Ensure file is not locked
|
||||
if _, ok := handler.locks[id]; ok {
|
||||
handler.sendError(w, ErrFileLocked)
|
||||
return
|
||||
}
|
||||
|
||||
// Lock file for further writes (heads are allowed)
|
||||
handler.locks[id] = true
|
||||
|
||||
// File will be unlocked regardless of an error or success
|
||||
defer func() {
|
||||
delete(handler.locks, id)
|
||||
}()
|
||||
|
||||
err := handler.dataStore.Terminate(id)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
err = ErrNotFound
|
||||
}
|
||||
handler.sendError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// Send the error in the response body. The status code will be looked up in
|
||||
// ErrStatusCodes. If none is found 500 Internal Error will be used.
|
||||
func (handler *Handler) sendError(w http.ResponseWriter, err error) {
|
||||
|
|
|
@ -24,6 +24,10 @@ func (store zeroStore) GetReader(id string) (io.Reader, error) {
|
|||
return nil, ErrNotImplemented
|
||||
}
|
||||
|
||||
func (store zeroStore) Terminate(id string) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
type httpTest struct {
|
||||
Name string
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ func TestOptions(t *testing.T) {
|
|||
Method: "OPTIONS",
|
||||
Code: http.StatusNoContent,
|
||||
ResHeader: map[string]string{
|
||||
"TUS-Extension": "file-creation,metadata,concatenation",
|
||||
"TUS-Extension": "file-creation,metadata,concatenation,termination",
|
||||
"TUS-Version": "1.0.0",
|
||||
"TUS-Resumable": "1.0.0",
|
||||
"TUS-Max-Size": "400",
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
package tusd
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type terminateStore struct {
|
||||
t *testing.T
|
||||
zeroStore
|
||||
}
|
||||
|
||||
func (s terminateStore) Terminate(id string) error {
|
||||
if id != "foo" {
|
||||
s.t.Fatal("unexpected id")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestTerminate(t *testing.T) {
|
||||
handler, _ := NewHandler(Config{
|
||||
DataStore: terminateStore{
|
||||
t: t,
|
||||
},
|
||||
})
|
||||
|
||||
(&httpTest{
|
||||
Name: "Successful request",
|
||||
Method: "DELETE",
|
||||
URL: "foo",
|
||||
ReqHeader: map[string]string{
|
||||
"TUS-Resumable": "1.0.0",
|
||||
},
|
||||
Code: http.StatusNoContent,
|
||||
}).Run(handler, t)
|
||||
}
|
Loading…
Reference in New Issue