Revert "Turn every @todo 404 into an actual 404"
This reverts commit 7601e3a77e
.
This commit is contained in:
parent
7601e3a77e
commit
817129c2a5
|
@ -67,8 +67,3 @@ 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}"
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
|
@ -11,7 +12,11 @@ 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
|
||||||
|
|
||||||
|
@ -22,16 +27,39 @@ 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)
|
dataStore = NewDataStore(dataDir, dataStoreSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
|
@ -41,9 +69,13 @@ 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")
|
w.Header().Add("Access-Control-Expose-Headers", "Location, Range, Content-Disposition")
|
||||||
|
|
||||||
if r.Method == "OPTIONS" {
|
if r.Method == "OPTIONS" {
|
||||||
reply(w, http.StatusOK, "")
|
reply(w, http.StatusOK, "")
|
||||||
|
@ -59,8 +91,6 @@ 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:
|
||||||
|
@ -79,13 +109,11 @@ 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
|
||||||
}
|
}
|
||||||
|
@ -95,47 +123,55 @@ 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); err != nil {
|
if err := dataStore.CreateFile(id, contentRange.Size, contentType, contentDisposition); err != nil {
|
||||||
reply(w, http.StatusInternalServerError, err.Error())
|
reply(w, http.StatusInternalServerError, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if contentRange.End != -1 {
|
if contentRange.End != -1 {
|
||||||
err := dataStore.WriteFileChunk(id, contentRange.Start, contentRange.End, r.Body)
|
if err := dataStore.WriteFileChunk(id, contentRange.Start, contentRange.End, r.Body); err != nil {
|
||||||
if os.IsNotExist(err) {
|
// @TODO: Could be a 404 as well
|
||||||
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)
|
||||||
setFileRangeHeader(w, id)
|
setFileHeaders(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) {
|
||||||
setFileRangeHeader(w, fileId)
|
// Work around a bug in Go that would cause HEAD responses to hang. Should be
|
||||||
|
// 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) {
|
||||||
data, size, err := dataStore.ReadFile(fileId)
|
meta, err := dataStore.GetFileMeta(fileId)
|
||||||
if os.IsNotExist(err) {
|
if err != nil {
|
||||||
reply(w, http.StatusNotFound, err.Error())
|
// @TODO: Could be a 404 as well
|
||||||
|
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()
|
||||||
|
|
||||||
setFileRangeHeader(w, fileId)
|
setFileHeaders(w, fileId)
|
||||||
w.Header().Set("Content-Length", strconv.FormatInt(size, 10))
|
w.Header().Set("Content-Length", strconv.FormatInt(meta.Size, 10))
|
||||||
|
|
||||||
if _, err := io.CopyN(w, data, size); err != nil {
|
if _, err := io.CopyN(w, data, meta.Size); err != nil {
|
||||||
log.Printf("getFile: CopyN failed with: %s", err.Error())
|
log.Printf("getFile: CopyN failed with: %s", err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -168,7 +204,17 @@ func putFile(w http.ResponseWriter, r *http.Request, fileId string) {
|
||||||
|
|
||||||
// @TODO: Check that file exists
|
// @TODO: Check that file exists
|
||||||
|
|
||||||
err = dataStore.WriteFileChunk(fileId, start, end, r.Body)
|
if err := dataStore.WriteFileChunk(fileId, start, end, r.Body); err != nil {
|
||||||
|
// @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
|
||||||
|
@ -177,20 +223,10 @@ func putFile(w http.ResponseWriter, r *http.Request, 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 chunks {
|
for i, chunk := range meta.Chunks {
|
||||||
rangeHeader += fmt.Sprintf("%d-%d", chunk.Start, chunk.End)
|
rangeHeader += fmt.Sprintf("%d-%d", chunk.Start, chunk.End)
|
||||||
if i+1 < len(chunks) {
|
if i+1 < len(meta.Chunks) {
|
||||||
rangeHeader += ","
|
rangeHeader += ","
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -198,4 +234,7 @@ func setFileRangeHeader(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)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue