From 9f29ced4ec91c084e5a557982a9549ad21420a66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Geisendo=CC=88rfer?= Date: Wed, 8 May 2013 16:03:59 +0200 Subject: [PATCH] Remove old tusd code --- src/cmd/tusd/content_range.go | 74 --------- src/cmd/tusd/content_range_test.go | 54 ------ src/cmd/tusd/http.go | 257 ----------------------------- 3 files changed, 385 deletions(-) delete mode 100644 src/cmd/tusd/content_range.go delete mode 100644 src/cmd/tusd/content_range_test.go delete mode 100644 src/cmd/tusd/http.go diff --git a/src/cmd/tusd/content_range.go b/src/cmd/tusd/content_range.go deleted file mode 100644 index 49697e8..0000000 --- a/src/cmd/tusd/content_range.go +++ /dev/null @@ -1,74 +0,0 @@ -package main - -import ( - "errors" - "strconv" - "strings" -) - -var errInvalidRange = errors.New("invalid Content-Range") - -type contentRange struct { - Start int64 - End int64 - Size int64 -} - -// parseContentRange parse a Content-Range string like "5-10/100". -// see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.16 . -// "*" values causes End/Size to be set to -1, see test case for more details. -func parseContentRange(s string) (*contentRange, error) { - const prefix = "bytes " - offset := strings.Index(s, prefix) - if offset != 0 { - return nil, errInvalidRange - } - s = s[len(prefix):] - - parts := strings.Split(s, "/") - if len(parts) != 2 { - return nil, errInvalidRange - } - - r := new(contentRange) - - if parts[0] == "*" { - r.Start = 0 - r.End = -1 - } else { - offsets := strings.Split(parts[0], "-") - if len(offsets) != 2 { - return nil, errInvalidRange - } - - if offset, err := strconv.ParseInt(offsets[0], 10, 64); err == nil { - r.Start = offset - } else { - return nil, errInvalidRange - } - - if offset, err := strconv.ParseInt(offsets[1], 10, 64); err == nil { - r.End = offset - } else { - return nil, errInvalidRange - } - - // A byte-content-range-spec with a byte-range-resp-spec whose last- - // byte-pos value is less than its first-byte-pos value, or whose - // instance-length value is less than or equal to its last-byte-pos value, - // is invalid. The recipient of an invalid byte-content-range- spec MUST - // ignore it and any content transferred along with it. - if r.End <= r.Start { - return nil, errInvalidRange - } - } - - if parts[1] == "*" { - r.Size = -1 - return r, nil - } else if size, err := strconv.ParseInt(parts[1], 10, 64); err == nil { - r.Size = size - return r, nil - } - return nil, errInvalidRange -} diff --git a/src/cmd/tusd/content_range_test.go b/src/cmd/tusd/content_range_test.go deleted file mode 100644 index 680b7cd..0000000 --- a/src/cmd/tusd/content_range_test.go +++ /dev/null @@ -1,54 +0,0 @@ -package main - -import ( - "regexp" - "testing" -) - -var ContentRangeTests = []struct { - s string - want contentRange - err string -}{ - {s: "bytes 0-5/100", want: contentRange{Start: 0, End: 5, Size: 100}}, - {s: "bytes 5-20/30", want: contentRange{Start: 5, End: 20, Size: 30}}, - {s: "bytes */100", want: contentRange{Start: 0, End: -1, Size: 100}}, - {s: "bytes 5-20/*", want: contentRange{Start: 5, End: 20, Size: -1}}, - {s: "bytes */*", want: contentRange{Start: 0, End: -1, Size: -1}}, - {s: "bytes 0-2147483647/2147483648", want: contentRange{Start: 0, End: 2147483647, Size: 2147483648}}, - {s: "bytes 5-20", err: "invalid"}, - {s: "bytes 5-5/100", err: "invalid"}, - {s: "bytes 5-4/100", err: "invalid"}, - {s: "bytes ", err: "invalid"}, - {s: "", err: "invalid"}, -} - -func TestParseContentRange(t *testing.T) { - for _, test := range ContentRangeTests { - t.Logf("testing: %s", test.s) - - r, err := parseContentRange(test.s) - if test.err != "" { - if err == nil { - t.Errorf("got no error, but expected: %s", test.err) - continue - } - - errMatch := regexp.MustCompile(test.err) - if !errMatch.MatchString(err.Error()) { - t.Errorf("unexpected error: %s, wanted: %s", err, test.err) - continue - } - - continue - } else if err != nil { - t.Errorf("unexpected error: %s, wanted: %+v", err, test.want) - continue - } - - if *r != test.want { - t.Errorf("got: %+v, wanted: %+v", r, test.want) - continue - } - } -} diff --git a/src/cmd/tusd/http.go b/src/cmd/tusd/http.go deleted file mode 100644 index 7550a6b..0000000 --- a/src/cmd/tusd/http.go +++ /dev/null @@ -1,257 +0,0 @@ -package main - -import ( - "errors" - "fmt" - "io" - "log" - "net/http" - "os" - "path" - "regexp" - "strconv" - "time" -) - -// fileRoute matches /files/. 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 filesRoute = regexp.MustCompile("^/files/?$") -var dataStore *DataStore - -func init() { - wd, err := os.Getwd() - if err != nil { - panic(err) - } - - 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 { - panic(err) - } - dataStore = NewDataStore(dataDir, dataStoreSize) -} - -func serveHttp() error { - http.HandleFunc("/", route) - - addr := ":1080" - if port := os.Getenv("TUSD_PORT"); port != "" { - addr = ":" + port - } - log.Printf("serving clients at %s", addr) - - return http.ListenAndServe(addr, nil) -} - -func route(w http.ResponseWriter, r *http.Request) { - start := time.Now() - log.Printf("request: %s %s", r.Method, r.URL.RequestURI()) - - 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-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-Expose-Headers", "Location, Range, Content-Disposition") - - if r.Method == "OPTIONS" { - reply(w, http.StatusOK, "") - return - } - - if r.Method == "POST" && filesRoute.Match([]byte(r.URL.Path)) { - postFiles(w, r) - } else if match := fileRoute.FindStringSubmatch(r.URL.Path); match != nil { - id := match[1] - switch r.Method { - case "HEAD": - headFile(w, r, id) - case "GET": - getFile(w, r, id) - case "PUT": - putFile(w, r, id) - default: - reply(w, http.StatusMethodNotAllowed, "Invalid http method") - } - } else { - reply(w, http.StatusNotFound, "No matching route") - } - - duration := time.Since(start) - log.Printf("finished: %s %s (took %s)", r.Method, r.URL.RequestURI(), duration) -} - -func reply(w http.ResponseWriter, code int, message string) { - w.WriteHeader(code) - fmt.Fprintf(w, "%d - %s: %s\n", code, http.StatusText(code), message) -} - -func postFiles(w http.ResponseWriter, r *http.Request) { - contentRange, err := parseContentRange(r.Header.Get("Content-Range")) - if err != nil { - reply(w, http.StatusBadRequest, err.Error()) - return - } - - if contentRange.Size == -1 { - reply(w, http.StatusBadRequest, "Content-Range must indicate total file size.") - return - } - - contentType := r.Header.Get("Content-Type") - if contentType == "" { - contentType = "application/octet-stream" - } - - contentDisposition := r.Header.Get("Content-Disposition") - - id := uid() - if err := dataStore.CreateFile(id, contentRange.Size, contentType, contentDisposition); err != nil { - reply(w, http.StatusInternalServerError, err.Error()) - return - } - - if contentRange.End != -1 { - err := dataStore.WriteFileChunk(id, contentRange.Start, contentRange.End, r.Body) - if os.IsNotExist(err) { - reply(w, http.StatusNotFound, err.Error()) - return - } else if err != nil { - reply(w, http.StatusInternalServerError, err.Error()) - return - } - - } - - w.Header().Set("Location", "http://"+r.Host+"/files/"+id) - setFileHeaders(w, id) - w.WriteHeader(http.StatusCreated) -} - -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 - // 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) { - meta, err := dataStore.GetFileMeta(fileId) - if os.IsNotExist(err) { - reply(w, http.StatusNotFound, err.Error()) - return - } else if err != nil { - reply(w, http.StatusInternalServerError, err.Error()) - return - } - - data, err := dataStore.ReadFile(fileId) - if os.IsNotExist(err) { - reply(w, http.StatusNotFound, err.Error()) - return - } else if err != nil { - reply(w, http.StatusInternalServerError, err.Error()) - return - } - - defer data.Close() - - setFileHeaders(w, fileId) - w.Header().Set("Content-Length", strconv.FormatInt(meta.Size, 10)) - - if _, err := io.CopyN(w, data, meta.Size); err != nil { - log.Printf("getFile: CopyN of fileId %s failed with: %s. Is the upload complete yet?", fileId, err.Error()) - return - } -} - -func putFile(w http.ResponseWriter, r *http.Request, fileId string) { - var start int64 = 0 - var end int64 = 0 - - contentRange, err := parseContentRange(r.Header.Get("Content-Range")) - if err != nil { - contentLength := r.Header.Get("Content-Length") - end, err = strconv.ParseInt(contentLength, 10, 64) - if err != nil { - reply(w, http.StatusBadRequest, "Invalid content length provided") - } - - // we are zero-indexed - end = end - 1 - - // @TODO: Make sure contentLength matches the content length of the initial - // POST request - } else { - - // @TODO: Make sure contentRange.Size matches file size - - start = contentRange.Start - end = contentRange.End - } - - // @TODO: Check that file exists - - err = dataStore.WriteFileChunk(fileId, start, end, r.Body) - if os.IsNotExist(err) { - reply(w, http.StatusNotFound, err.Error()) - return - } else if err != nil { - 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) { - reply(w, http.StatusNotFound, err.Error()) - return - } else if err != nil { - reply(w, http.StatusInternalServerError, err.Error()) - return - } - - rangeHeader := "" - for i, chunk := range meta.Chunks { - rangeHeader += fmt.Sprintf("%d-%d", chunk.Start, chunk.End) - if i+1 < len(meta.Chunks) { - rangeHeader += "," - } - } - - if rangeHeader != "" { - w.Header().Set("Range", "bytes="+rangeHeader) - } - - w.Header().Set("Content-Type", meta.ContentType) - w.Header().Set("Content-Disposition", meta.ContentDisposition) -}