From 07ac9a43f5d50af39709f1536ad49eedd52aeff3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Geisendo=CC=88rfer?= Date: Mon, 18 Mar 2013 11:24:18 +0100 Subject: [PATCH] Initial PUT implementation --- src/cmd/tusd/data.go | 26 ++++++++++++++++++++++++++ src/cmd/tusd/http.go | 28 +++++++++++++++++++++++++--- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/src/cmd/tusd/data.go b/src/cmd/tusd/data.go index aa8a743..72f12cd 100644 --- a/src/cmd/tusd/data.go +++ b/src/cmd/tusd/data.go @@ -3,6 +3,8 @@ package main // This is very simple for now and will be enhanced as needed. import ( + "errors" + "io" "os" "path" ) @@ -43,3 +45,27 @@ func initFile(fileId string, size int64, contentType string) error { return nil } + +func putFileChunk(fileId string, start int64, end int64, r io.Reader) error { + d := dataPath(fileId) + file, err := os.OpenFile(d, os.O_WRONLY, 0666) + if err != nil { + return err + } + defer file.Close() + + if n, err := file.Seek(start, os.SEEK_SET); err != nil { + return err + } else if n != start { + return errors.New("putFileChunk: seek failure") + } + + size := end - start + 1 + if n, err := io.CopyN(file, r, size); err != nil { + return err + } else if n != size { + return errors.New("putFileChunk: partial copy") + } + + return nil +} diff --git a/src/cmd/tusd/http.go b/src/cmd/tusd/http.go index 41ba995..6629440 100644 --- a/src/cmd/tusd/http.go +++ b/src/cmd/tusd/http.go @@ -24,8 +24,9 @@ func route(w http.ResponseWriter, r *http.Request) { w.Header().Set("Server", "tusd") if r.Method == "POST" && r.URL.Path == "/files" { - createFile(w, r) + postFiles(w, r) } else if match := fileRoute.FindStringSubmatch(r.URL.Path); match != nil { + id := match[1] // WIP switch r.Method { case "HEAD": @@ -33,7 +34,7 @@ func route(w http.ResponseWriter, r *http.Request) { case "GET": reply(w, http.StatusNotImplemented, "File download") case "PUT": - reply(w, http.StatusOK, "chunk created") + putFile(w, r, id) default: reply(w, http.StatusMethodNotAllowed, "Invalid http method") } @@ -47,7 +48,7 @@ func reply(w http.ResponseWriter, code int, message string) { fmt.Fprintf(w, "%d - %s: %s\n", code, http.StatusText(code), message) } -func createFile(w http.ResponseWriter, r *http.Request) { +func postFiles(w http.ResponseWriter, r *http.Request) { contentRange, err := parseContentRange(r.Header.Get("Content-Range")) if err != nil { reply(w, http.StatusBadRequest, err.Error()) @@ -75,6 +76,27 @@ func createFile(w http.ResponseWriter, r *http.Request) { return } + // @TODO: Return X-Resume header + w.Header().Set("Location", "/files/"+id) w.WriteHeader(http.StatusCreated) } + +func putFile(w http.ResponseWriter, r *http.Request, fileId string) { + contentRange, err := parseContentRange(r.Header.Get("Content-Range")) + if err != nil { + reply(w, http.StatusBadRequest, err.Error()) + return + } + + // @TODO: Check that file exists + // @TODO: Make sure contentRange.Size matches file size + + if err := putFileChunk(fileId, contentRange.Start, contentRange.End, r.Body); err != nil { + // @TODO: Could be a 404 as well + reply(w, http.StatusInternalServerError, err.Error()) + return + } + + // @TODO: Return X-Resume header +}