Initial X-Missing work

This commit is contained in:
Felix Geisendörfer 2013-03-18 12:24:44 +01:00
parent 4f811a8ffe
commit 9c2d2275a8
3 changed files with 87 additions and 6 deletions

View File

@ -129,15 +129,15 @@ Host: tus.example.com
HTTP/1.1 200 Ok HTTP/1.1 200 Ok
Content-Length: 100 Content-Length: 100
Content-Type: image/jpg 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 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 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. 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. received by the server.
### GET \<fileUrl\> ### GET \<fileUrl\>

View File

@ -6,8 +6,11 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"os" "os"
"path" "path"
"strconv"
"strings"
) )
var dataDir string var dataDir string
@ -82,3 +85,61 @@ func putFileChunk(fileId string, start int64, end int64, r io.Reader) error {
return nil 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
}

View File

@ -30,7 +30,7 @@ func route(w http.ResponseWriter, r *http.Request) {
// WIP // WIP
switch r.Method { switch r.Method {
case "HEAD": case "HEAD":
w.Header().Set("X-Resume", "bytes=0-99") headFile(w, r, id)
case "GET": case "GET":
reply(w, http.StatusNotImplemented, "File download") reply(w, http.StatusNotImplemented, "File download")
case "PUT": case "PUT":
@ -76,7 +76,7 @@ func postFiles(w http.ResponseWriter, r *http.Request) {
return return
} }
// @TODO: Return X-Resume header // @TODO: Return X-Missing header
w.Header().Set("Location", "/files/"+id) w.Header().Set("Location", "/files/"+id)
w.WriteHeader(http.StatusCreated) w.WriteHeader(http.StatusCreated)
@ -98,5 +98,25 @@ func putFile(w http.ResponseWriter, r *http.Request, fileId string) {
return 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)
}
} }