From c9207d8c5ff957e876f5d672c0d99c56730ae87c Mon Sep 17 00:00:00 2001 From: Marius Date: Tue, 19 Jan 2016 22:32:15 +0100 Subject: [PATCH] Extract GetReader into own interface --- datastore.go | 26 ++++++++++++++++++-------- handler.go | 6 +++++- unrouted_handler.go | 15 +++++++++++++-- 3 files changed, 36 insertions(+), 11 deletions(-) diff --git a/datastore.go b/datastore.go index 92b27d1..214eae9 100644 --- a/datastore.go +++ b/datastore.go @@ -43,14 +43,6 @@ type DataStore interface { // requests. It may return an os.ErrNotExist which will be interpreted as a // 404 Not Found. GetInfo(id string) (FileInfo, error) - // Get an io.Reader to allow downloading the file. This feature is not - // part of the official tus specification. If this additional function - // should not be enabled any call to GetReader should return - // tusd.ErrNotImplemented. The length of the resource is determined by - // retrieving the offset using GetInfo. - // If the returned reader also implements the io.Closer interface, the - // Close() method will be invoked once everything has been read. - GetReader(id string) (io.Reader, error) } // TerminaterDataStore is the interface which must be implemented by DataStores @@ -97,3 +89,21 @@ type LockerDataStore interface { // UnlockUpload releases an existing lock for the given upload. UnlockUpload(id string) error } + +// GetReaderDataStore is the interface which must be implemented if handler should +// expose and support the GET route. It will allow clients to download the +// content of an upload regardless whether it's finished or not. +// Please, be aware that this feature is not part of the official tus +// specification. Instead it's a custom mechanism by tusd. +type GetReaderDataStore interface { + DataStore + + // GetReader returns a reader which allows iterating of the content of an + // upload specified by its ID. It should attempt to provide a reader even if + // the upload has not been finished yet but it's not required. + // If the returned reader also implements the io.Closer interface, the + // Close() method will be invoked once everything has been read. + // If the given upload could not be found, the error tusd.ErrNotFound should + // be returned. + GetReader(id string) (io.Reader, error) +} diff --git a/handler.go b/handler.go index 0aae179..146e442 100644 --- a/handler.go +++ b/handler.go @@ -38,7 +38,6 @@ 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.Add("PATCH", ":id", http.HandlerFunc(handler.PatchFile)) // Only attach the DELETE handler if the Terminate() method is provided @@ -46,6 +45,11 @@ func NewHandler(config Config) (*Handler, error) { mux.Del(":id", http.HandlerFunc(handler.DelFile)) } + // GET handler requires the GetReader() method + if _, ok := config.DataStore.(GetReaderDataStore); ok { + mux.Get(":id", http.HandlerFunc(handler.GetFile)) + } + return routedHandler, nil } diff --git a/unrouted_handler.go b/unrouted_handler.go index 399fe7a..83ea194 100644 --- a/unrouted_handler.go +++ b/unrouted_handler.go @@ -402,6 +402,12 @@ func (handler *UnroutedHandler) PatchFile(w http.ResponseWriter, r *http.Request // GetFile handles requests to download a file using a GET request. This is not // part of the specification. func (handler *UnroutedHandler) GetFile(w http.ResponseWriter, r *http.Request) { + dataStore, ok := handler.dataStore.(GetReaderDataStore) + if !ok { + handler.sendError(w, r, ErrNotImplemented) + return + } + id, err := extractIDFromPath(r.URL.Path) if err != nil { handler.sendError(w, r, err) @@ -430,7 +436,7 @@ func (handler *UnroutedHandler) GetFile(w http.ResponseWriter, r *http.Request) } // Get reader - src, err := handler.dataStore.GetReader(id) + src, err := dataStore.GetReader(id) if err != nil { handler.sendError(w, r, err) return @@ -545,10 +551,15 @@ func (handler *UnroutedHandler) sizeOfUploads(ids []string) (size int64, err err // Fill an empty upload with the content of the uploads by their ids. The data // will be written in the order as they appear in the slice func (handler *UnroutedHandler) fillFinalUpload(id string, uploads []string) error { + dataStore, ok := handler.dataStore.(GetReaderDataStore) + if !ok { + return ErrNotImplemented + } + readers := make([]io.Reader, len(uploads)) for index, uploadID := range uploads { - reader, err := handler.dataStore.GetReader(uploadID) + reader, err := dataStore.GetReader(uploadID) if err != nil { return err }