Turn every @todo 404 into an actual 404

This commit is contained in:
Kevin van Zonneveld 2013-03-27 22:30:48 +01:00
parent b8f27ed6bc
commit 7601e3a77e
2 changed files with 39 additions and 73 deletions

View File

@ -67,3 +67,8 @@ echo -ne "GET '${SERVICE}${location}' \t\t"
has_content=$(curl -s ${SERVICE}${location}) has_content=$(curl -s ${SERVICE}${location})
echo "<-- ${has_content}" echo "<-- ${has_content}"
# get 404 with GET
echo -ne "GET '${SERVICE}${location}a' \t\t"
has_content=$(curl -s ${SERVICE}${location})
echo "<-- ${has_content}"

View File

@ -1,7 +1,6 @@
package main package main
import ( import (
"errors"
"fmt" "fmt"
"io" "io"
"log" "log"
@ -12,11 +11,7 @@ import (
"strconv" "strconv"
) )
// fileRoute matches /files/<id>. Go seems to use \r to terminate header
// values, so to ease bash scripting, the route ignores a trailing \r in the
// route. Better ideas are welcome.
var fileRoute = regexp.MustCompile("^/files/([^/\r\n]+)\r?$") var fileRoute = regexp.MustCompile("^/files/([^/\r\n]+)\r?$")
var filesRoute = regexp.MustCompile("^/files/?$") var filesRoute = regexp.MustCompile("^/files/?$")
var dataStore *DataStore var dataStore *DataStore
@ -27,39 +22,16 @@ func init() {
} }
dataDir := path.Join(wd, "tus_data") dataDir := path.Join(wd, "tus_data")
if configDir := os.Getenv("TUSD_DATA_DIR"); configDir != "" {
dataDir = configDir
}
// dataStoreSize limits the storage used by the data store. If exceeded, the
// data store will start garbage collection old files until enough storage is
// available again.
var dataStoreSize int64
dataStoreSize = 1024 * 1024 * 1024
if configStoreSize := os.Getenv("TUSD_DATA_STORE_MAXSIZE"); configStoreSize != "" {
parsed, err := strconv.ParseInt(configStoreSize, 10, 64)
if err != nil {
panic(errors.New("Invalid data store max size configured"))
}
dataStoreSize = parsed
}
log.Print("Datastore directory: ", dataDir)
log.Print("Datastore max size: ", dataStoreSize)
if err := os.MkdirAll(dataDir, 0777); err != nil { if err := os.MkdirAll(dataDir, 0777); err != nil {
panic(err) panic(err)
} }
dataStore = NewDataStore(dataDir, dataStoreSize) dataStore = NewDataStore(dataDir)
} }
func serveHttp() error { func serveHttp() error {
http.HandleFunc("/", route) http.HandleFunc("/", route)
addr := ":1080" addr := ":1080"
if port := os.Getenv("TUSD_PORT"); port != "" {
addr = ":" + port
}
log.Printf("serving clients at %s", addr) log.Printf("serving clients at %s", addr)
return http.ListenAndServe(addr, nil) return http.ListenAndServe(addr, nil)
@ -69,13 +41,9 @@ func route(w http.ResponseWriter, r *http.Request) {
log.Printf("request: %s %s", r.Method, r.URL.RequestURI()) log.Printf("request: %s %s", r.Method, r.URL.RequestURI())
w.Header().Set("Server", "tusd") w.Header().Set("Server", "tusd")
// Allow CORS for almost everything. This needs to be revisted / limited to
// routes and methods that need it.
w.Header().Add("Access-Control-Allow-Origin", "*") w.Header().Add("Access-Control-Allow-Origin", "*")
w.Header().Add("Access-Control-Allow-Methods", "HEAD,GET,PUT,POST,DELETE")
w.Header().Add("Access-Control-Allow-Headers", "Origin, x-requested-with, content-type, accept, Content-Range, Content-Disposition") w.Header().Add("Access-Control-Allow-Headers", "Origin, x-requested-with, content-type, accept, Content-Range, Content-Disposition")
w.Header().Add("Access-Control-Expose-Headers", "Location, Range, Content-Disposition") w.Header().Add("Access-Control-Expose-Headers", "Location, Range")
if r.Method == "OPTIONS" { if r.Method == "OPTIONS" {
reply(w, http.StatusOK, "") reply(w, http.StatusOK, "")
@ -91,6 +59,8 @@ func route(w http.ResponseWriter, r *http.Request) {
headFile(w, r, id) headFile(w, r, id)
case "GET": case "GET":
getFile(w, r, id) getFile(w, r, id)
case "POST":
putFile(w, r, id)
case "PUT": case "PUT":
putFile(w, r, id) putFile(w, r, id)
default: default:
@ -109,11 +79,13 @@ func reply(w http.ResponseWriter, code int, message string) {
func postFiles(w http.ResponseWriter, r *http.Request) { func postFiles(w http.ResponseWriter, r *http.Request) {
contentRange, err := parseContentRange(r.Header.Get("Content-Range")) contentRange, err := parseContentRange(r.Header.Get("Content-Range"))
if err != nil { if err != nil {
log.Print("FOO")
reply(w, http.StatusBadRequest, err.Error()) reply(w, http.StatusBadRequest, err.Error())
return return
} }
if contentRange.Size == -1 { if contentRange.Size == -1 {
log.Print("FOO2")
reply(w, http.StatusBadRequest, "Content-Range must indicate total file size.") reply(w, http.StatusBadRequest, "Content-Range must indicate total file size.")
return return
} }
@ -123,55 +95,47 @@ func postFiles(w http.ResponseWriter, r *http.Request) {
contentType = "application/octet-stream" contentType = "application/octet-stream"
} }
contentDisposition := r.Header.Get("Content-Disposition")
id := uid() id := uid()
if err := dataStore.CreateFile(id, contentRange.Size, contentType, contentDisposition); err != nil { if err := dataStore.CreateFile(id, contentRange.Size, contentType); err != nil {
reply(w, http.StatusInternalServerError, err.Error()) reply(w, http.StatusInternalServerError, err.Error())
return return
} }
if contentRange.End != -1 { if contentRange.End != -1 {
if err := dataStore.WriteFileChunk(id, contentRange.Start, contentRange.End, r.Body); err != nil { err := dataStore.WriteFileChunk(id, contentRange.Start, contentRange.End, r.Body)
// @TODO: Could be a 404 as well if os.IsNotExist(err) {
reply(w, http.StatusNotFound, err.Error())
return
} else if err != nil {
reply(w, http.StatusInternalServerError, err.Error()) reply(w, http.StatusInternalServerError, err.Error())
return return
} }
} }
w.Header().Set("Location", "/files/"+id) w.Header().Set("Location", "/files/"+id)
setFileHeaders(w, id) setFileRangeHeader(w, id)
w.WriteHeader(http.StatusCreated) w.WriteHeader(http.StatusCreated)
} }
func headFile(w http.ResponseWriter, r *http.Request, fileId string) { func headFile(w http.ResponseWriter, r *http.Request, fileId string) {
// Work around a bug in Go that would cause HEAD responses to hang. Should be setFileRangeHeader(w, fileId)
// fixed in future release, see:
// http://code.google.com/p/go/issues/detail?id=4126
w.Header().Set("Content-Length", "0")
setFileHeaders(w, fileId)
} }
func getFile(w http.ResponseWriter, r *http.Request, fileId string) { func getFile(w http.ResponseWriter, r *http.Request, fileId string) {
meta, err := dataStore.GetFileMeta(fileId) data, size, err := dataStore.ReadFile(fileId)
if err != nil { if os.IsNotExist(err) {
// @TODO: Could be a 404 as well reply(w, http.StatusNotFound, err.Error())
reply(w, http.StatusInternalServerError, err.Error())
return return
} } else if err != nil {
data, err := dataStore.ReadFile(fileId)
if err != nil {
// @TODO: Could be a 404 as well
reply(w, http.StatusInternalServerError, err.Error()) reply(w, http.StatusInternalServerError, err.Error())
return return
} }
defer data.Close() defer data.Close()
setFileHeaders(w, fileId) setFileRangeHeader(w, fileId)
w.Header().Set("Content-Length", strconv.FormatInt(meta.Size, 10)) w.Header().Set("Content-Length", strconv.FormatInt(size, 10))
if _, err := io.CopyN(w, data, meta.Size); err != nil { if _, err := io.CopyN(w, data, size); err != nil {
log.Printf("getFile: CopyN failed with: %s", err.Error()) log.Printf("getFile: CopyN failed with: %s", err.Error())
return return
} }
@ -204,17 +168,7 @@ func putFile(w http.ResponseWriter, r *http.Request, fileId string) {
// @TODO: Check that file exists // @TODO: Check that file exists
if err := dataStore.WriteFileChunk(fileId, start, end, r.Body); err != nil { err = dataStore.WriteFileChunk(fileId, start, end, r.Body)
// @TODO: Could be a 404 as well
reply(w, http.StatusInternalServerError, err.Error())
return
}
setFileHeaders(w, fileId)
}
func setFileHeaders(w http.ResponseWriter, fileId string) {
meta, err := dataStore.GetFileMeta(fileId)
if os.IsNotExist(err) { if os.IsNotExist(err) {
reply(w, http.StatusNotFound, err.Error()) reply(w, http.StatusNotFound, err.Error())
return return
@ -223,10 +177,20 @@ func setFileHeaders(w http.ResponseWriter, fileId string) {
return return
} }
setFileRangeHeader(w, fileId)
}
func setFileRangeHeader(w http.ResponseWriter, fileId string) {
chunks, err := dataStore.GetFileChunks(fileId)
if err != nil {
reply(w, http.StatusInternalServerError, err.Error())
return
}
rangeHeader := "" rangeHeader := ""
for i, chunk := range meta.Chunks { for i, chunk := range chunks {
rangeHeader += fmt.Sprintf("%d-%d", chunk.Start, chunk.End) rangeHeader += fmt.Sprintf("%d-%d", chunk.Start, chunk.End)
if i+1 < len(meta.Chunks) { if i+1 < len(chunks) {
rangeHeader += "," rangeHeader += ","
} }
} }
@ -234,7 +198,4 @@ func setFileHeaders(w http.ResponseWriter, fileId string) {
if rangeHeader != "" { if rangeHeader != "" {
w.Header().Set("Range", "bytes="+rangeHeader) w.Header().Set("Range", "bytes="+rangeHeader)
} }
w.Header().Set("Content-Type", meta.ContentType)
w.Header().Set("Content-Disposition", meta.ContentDisposition)
} }