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
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 \<fileUrl\>

View File

@ -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
}

View File

@ -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)
}
}