From 9c2d2275a853e0646a02f8de6eb66dcb225a21a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Geisendo=CC=88rfer?= Date: Mon, 18 Mar 2013 12:24:44 +0100 Subject: [PATCH] Initial X-Missing work --- README.md | 6 ++--- src/cmd/tusd/data.go | 61 ++++++++++++++++++++++++++++++++++++++++++++ src/cmd/tusd/http.go | 26 ++++++++++++++++--- 3 files changed, 87 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 8039a23..75f4671 100644 --- a/README.md +++ b/README.md @@ -129,15 +129,15 @@ Host: tus.example.com HTTP/1.1 200 Ok Content-Length: 100 Content-Type: image/jpg -X-Resume: bytes=20-50,60-99 +X-Missing: bytes=20-50,60-99 ``` -The `X-Resume` header holds a [byte +The `X-Missing` header holds a [byte range](http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35.1) that informs the client which parts of the file have not been received yet. It is up to the client to choose appropiate `PUT` requests to complete the upload. -The absence of a `X-Resume` header means that the the entire file has been +The absence of a `X-Missing` header means that the the entire file has been received by the server. ### GET \ diff --git a/src/cmd/tusd/data.go b/src/cmd/tusd/data.go index 9114b65..756761b 100644 --- a/src/cmd/tusd/data.go +++ b/src/cmd/tusd/data.go @@ -6,8 +6,11 @@ import ( "errors" "fmt" "io" + "io/ioutil" "os" "path" + "strconv" + "strings" ) var dataDir string @@ -82,3 +85,61 @@ func putFileChunk(fileId string, start int64, end int64, r io.Reader) error { return nil } + +func getReceivedChunks(fileId string) (chunkSet, error) { + l := logPath(fileId) + // @TODO stream the file / limit log file size? + data, err := ioutil.ReadFile(l) + if err != nil { + return nil, err + } + lines := strings.Split(string(data), "\n") + + + chunks := make(chunkSet, 0, len(lines)-1) + for i, line := range lines { + // last line is always empty, skip it + if lastLine := i+1 == len(lines); lastLine { + break + } + + parts := strings.Split(line, ",") + if len(parts) != 2 { + return nil, errors.New("getReceivedChunks: corrupt log line: "+line) + } + + start, err := strconv.ParseInt(parts[0], 10, 64) + if err != nil { + return nil, errors.New("getReceivedChunks: invalid start: "+parts[0]) + } + + end, err := strconv.ParseInt(parts[1], 10, 64) + if err != nil { + return nil, errors.New("getReceivedChunks: invalid end: "+parts[1]) + } + + chunks.Add(chunk{Start: start, End: end}) + } + + return chunks, nil +} + +func getMissingChunks(fileId string) (chunkSet, error) { + d := dataPath(fileId) + stat, err := os.Stat(d) + if err != nil { + return nil, err + } + + receivedChunks, err := getReceivedChunks(fileId) + if err != nil { + return nil, err + } + + // @TODO actually calcuate missing chunks instead of received + _ = stat + + //chunks := chunkSet{{Start: 0, End: stat.Size()-1}} + //chunks := make(chunkSet, 0) + return receivedChunks, nil +} diff --git a/src/cmd/tusd/http.go b/src/cmd/tusd/http.go index 6629440..c51b94d 100644 --- a/src/cmd/tusd/http.go +++ b/src/cmd/tusd/http.go @@ -30,7 +30,7 @@ func route(w http.ResponseWriter, r *http.Request) { // WIP switch r.Method { case "HEAD": - w.Header().Set("X-Resume", "bytes=0-99") + headFile(w, r, id) case "GET": reply(w, http.StatusNotImplemented, "File download") case "PUT": @@ -76,7 +76,7 @@ func postFiles(w http.ResponseWriter, r *http.Request) { return } - // @TODO: Return X-Resume header + // @TODO: Return X-Missing header w.Header().Set("Location", "/files/"+id) w.WriteHeader(http.StatusCreated) @@ -98,5 +98,25 @@ func putFile(w http.ResponseWriter, r *http.Request, fileId string) { return } - // @TODO: Return X-Resume header + // @TODO: Return X-Missing header +} + +func headFile(w http.ResponseWriter, r *http.Request, fileId string) { + chunks, err := getMissingChunks(fileId) + if err != nil { + reply(w, http.StatusInternalServerError, err.Error()) + return + } + + missing := "" + for i, chunk := range chunks { + missing += fmt.Sprintf("%d-%d", chunk.Start, chunk.End) + if i + 1 < len(chunks) { + missing += "," + } + } + + if missing != "" { + w.Header().Set("X-Missing", "bytes="+missing) + } }