mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-04-21 10:18:21 -05:00
feat(tusd): bump tusd pkg to v2.4.0
Signed-off-by: jkoberg <jkoberg@owncloud.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
Enhancement: Bump tusd pkg to v2
|
||||
|
||||
Bumps the tusd pkg to v2.4.0
|
||||
|
||||
https://github.com/owncloud/ocis/pull/9714
|
||||
@@ -88,7 +88,7 @@ require (
|
||||
github.com/test-go/testify v1.1.4
|
||||
github.com/thejerf/suture/v4 v4.0.5
|
||||
github.com/tidwall/gjson v1.17.1
|
||||
github.com/tus/tusd v1.13.0
|
||||
github.com/tus/tusd/v2 v2.4.0
|
||||
github.com/unrolled/secure v1.14.0
|
||||
github.com/urfave/cli/v2 v2.27.2
|
||||
github.com/xhit/go-simple-mail/v2 v2.16.0
|
||||
@@ -156,7 +156,6 @@ require (
|
||||
github.com/blevesearch/zapx/v15 v15.3.13 // indirect
|
||||
github.com/blevesearch/zapx/v16 v16.1.5 // indirect
|
||||
github.com/bluele/gcache v0.0.2 // indirect
|
||||
github.com/bmizerany/pat v0.0.0-20210406213842-e4b6760bdd6f // indirect
|
||||
github.com/bombsimon/logrusr/v3 v3.1.0 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/ceph/go-ceph v0.18.0 // indirect
|
||||
@@ -226,6 +225,7 @@ require (
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/gomodule/redigo v1.8.9 // indirect
|
||||
github.com/google/flatbuffers v2.0.8+incompatible // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 // indirect
|
||||
github.com/google/renameio/v2 v2.0.0 // indirect
|
||||
@@ -323,7 +323,6 @@ require (
|
||||
github.com/tidwall/pretty v1.2.1 // indirect
|
||||
github.com/toorop/go-dkim v0.0.0-20201103131630-e1cd1a0a5208 // indirect
|
||||
github.com/trustelem/zxcvbn v1.0.1 // indirect
|
||||
github.com/tus/tusd/v2 v2.4.0 // indirect
|
||||
github.com/wk8/go-ordered-map v1.0.0 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
"github.com/go-chi/chi/v5/middleware"
|
||||
"github.com/go-chi/render"
|
||||
tusd "github.com/tus/tusd/pkg/handler"
|
||||
tusd "github.com/tus/tusd/v2/pkg/handler"
|
||||
"google.golang.org/grpc/metadata"
|
||||
|
||||
revactx "github.com/cs3org/reva/v2/pkg/ctx"
|
||||
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
*.prof
|
||||
*.out
|
||||
example/example
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
Copyright (C) 2012 by Keith Rarick, Blake Mizerany
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
-82
@@ -1,82 +0,0 @@
|
||||
# pat (formerly pat.go) - A Sinatra style pattern muxer for Go's net/http library
|
||||
|
||||
[](https://godoc.org/github.com/bmizerany/pat)
|
||||
|
||||
## INSTALL
|
||||
|
||||
$ go get github.com/bmizerany/pat
|
||||
|
||||
## USE
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"github.com/bmizerany/pat"
|
||||
"log"
|
||||
)
|
||||
|
||||
// hello world, the web server
|
||||
func HelloServer(w http.ResponseWriter, req *http.Request) {
|
||||
io.WriteString(w, "hello, "+req.URL.Query().Get(":name")+"!\n")
|
||||
}
|
||||
|
||||
func main() {
|
||||
m := pat.New()
|
||||
m.Get("/hello/:name", http.HandlerFunc(HelloServer))
|
||||
|
||||
// Register this pat with the default serve mux so that other packages
|
||||
// may also be exported. (i.e. /debug/pprof/*)
|
||||
http.Handle("/", m)
|
||||
err := http.ListenAndServe(":12345", nil)
|
||||
if err != nil {
|
||||
log.Fatal("ListenAndServe: ", err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
It's that simple.
|
||||
|
||||
For more information, see:
|
||||
http://godoc.org/github.com/bmizerany/pat
|
||||
|
||||
## CONTRIBUTORS
|
||||
|
||||
* Alexis Svinartchouk (@zvin)
|
||||
* Blake Mizerany (@bmizerany)
|
||||
* Brian Ketelsen (@bketelsen)
|
||||
* Bryan Matsuo (@bmatsuo)
|
||||
* Caleb Spare (@cespare)
|
||||
* Evan Shaw (@edsrzf)
|
||||
* Gary Burd (@garyburd)
|
||||
* George Rogers (@georgerogers42)
|
||||
* Keith Rarick (@kr)
|
||||
* Matt Williams (@mattyw)
|
||||
* Mike Stipicevic (@wickedchicken)
|
||||
* Nick Saika (@nesv)
|
||||
* Timothy Cyrus (@tcyrus)
|
||||
* binqin (@binku87)
|
||||
|
||||
## LICENSE
|
||||
|
||||
Copyright (C) 2012 by Keith Rarick, Blake Mizerany
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
-314
@@ -1,314 +0,0 @@
|
||||
// Package pat implements a simple URL pattern muxer
|
||||
package pat
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// PatternServeMux is an HTTP request multiplexer. It matches the URL of each
|
||||
// incoming request against a list of registered patterns with their associated
|
||||
// methods and calls the handler for the pattern that most closely matches the
|
||||
// URL.
|
||||
//
|
||||
// Pattern matching attempts each pattern in the order in which they were
|
||||
// registered.
|
||||
//
|
||||
// Patterns may contain literals or captures. Capture names start with a colon
|
||||
// and consist of letters A-Z, a-z, _, and 0-9. The rest of the pattern
|
||||
// matches literally. The portion of the URL matching each name ends with an
|
||||
// occurrence of the character in the pattern immediately following the name,
|
||||
// or a /, whichever comes first. It is possible for a name to match the empty
|
||||
// string.
|
||||
//
|
||||
// Example pattern with one capture:
|
||||
// /hello/:name
|
||||
// Will match:
|
||||
// /hello/blake
|
||||
// /hello/keith
|
||||
// Will not match:
|
||||
// /hello/blake/
|
||||
// /hello/blake/foo
|
||||
// /foo
|
||||
// /foo/bar
|
||||
//
|
||||
// Example 2:
|
||||
// /hello/:name/
|
||||
// Will match:
|
||||
// /hello/blake/
|
||||
// /hello/keith/foo
|
||||
// /hello/blake
|
||||
// /hello/keith
|
||||
// Will not match:
|
||||
// /foo
|
||||
// /foo/bar
|
||||
//
|
||||
// A pattern ending with a slash will add an implicit redirect for its non-slash
|
||||
// version. For example: Get("/foo/", handler) also registers
|
||||
// Get("/foo", handler) as a redirect. You may override it by registering
|
||||
// Get("/foo", anotherhandler) before the slash version.
|
||||
//
|
||||
// Retrieve the capture from the r.URL.Query().Get(":name") in a handler (note
|
||||
// the colon). If a capture name appears more than once, the additional values
|
||||
// are appended to the previous values (see
|
||||
// http://golang.org/pkg/net/url/#Values)
|
||||
//
|
||||
// A trivial example server is:
|
||||
//
|
||||
// package main
|
||||
//
|
||||
// import (
|
||||
// "io"
|
||||
// "net/http"
|
||||
// "github.com/bmizerany/pat"
|
||||
// "log"
|
||||
// )
|
||||
//
|
||||
// // hello world, the web server
|
||||
// func HelloServer(w http.ResponseWriter, req *http.Request) {
|
||||
// io.WriteString(w, "hello, "+req.URL.Query().Get(":name")+"!\n")
|
||||
// }
|
||||
//
|
||||
// func main() {
|
||||
// m := pat.New()
|
||||
// m.Get("/hello/:name", http.HandlerFunc(HelloServer))
|
||||
//
|
||||
// // Register this pat with the default serve mux so that other packages
|
||||
// // may also be exported. (i.e. /debug/pprof/*)
|
||||
// http.Handle("/", m)
|
||||
// err := http.ListenAndServe(":12345", nil)
|
||||
// if err != nil {
|
||||
// log.Fatal("ListenAndServe: ", err)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// When "Method Not Allowed":
|
||||
//
|
||||
// Pat knows what methods are allowed given a pattern and a URI. For
|
||||
// convenience, PatternServeMux will add the Allow header for requests that
|
||||
// match a pattern for a method other than the method requested and set the
|
||||
// Status to "405 Method Not Allowed".
|
||||
//
|
||||
// If the NotFound handler is set, then it is used whenever the pattern doesn't
|
||||
// match the request path for the current method (and the Allow header is not
|
||||
// altered).
|
||||
type PatternServeMux struct {
|
||||
// NotFound, if set, is used whenever the request doesn't match any
|
||||
// pattern for its method. NotFound should be set before serving any
|
||||
// requests.
|
||||
NotFound http.Handler
|
||||
handlers map[string][]*patHandler
|
||||
}
|
||||
|
||||
// New returns a new PatternServeMux.
|
||||
func New() *PatternServeMux {
|
||||
return &PatternServeMux{handlers: make(map[string][]*patHandler)}
|
||||
}
|
||||
|
||||
// ServeHTTP matches r.URL.Path against its routing table using the rules
|
||||
// described above.
|
||||
func (p *PatternServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
for _, ph := range p.handlers[r.Method] {
|
||||
if params, ok := ph.try(r.URL.EscapedPath()); ok {
|
||||
if len(params) > 0 && !ph.redirect {
|
||||
r.URL.RawQuery = url.Values(params).Encode() + "&" + r.URL.RawQuery
|
||||
}
|
||||
ph.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if p.NotFound != nil {
|
||||
p.NotFound.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
allowed := make([]string, 0, len(p.handlers))
|
||||
for meth, handlers := range p.handlers {
|
||||
if meth == r.Method {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, ph := range handlers {
|
||||
if _, ok := ph.try(r.URL.EscapedPath()); ok {
|
||||
allowed = append(allowed, meth)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(allowed) == 0 {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Add("Allow", strings.Join(allowed, ", "))
|
||||
http.Error(w, "Method Not Allowed", 405)
|
||||
}
|
||||
|
||||
// Head will register a pattern with a handler for HEAD requests.
|
||||
func (p *PatternServeMux) Head(pat string, h http.Handler) {
|
||||
p.Add("HEAD", pat, h)
|
||||
}
|
||||
|
||||
// Get will register a pattern with a handler for GET requests.
|
||||
// It also registers pat for HEAD requests. If this needs to be overridden, use
|
||||
// Head before Get with pat.
|
||||
func (p *PatternServeMux) Get(pat string, h http.Handler) {
|
||||
p.Add("HEAD", pat, h)
|
||||
p.Add("GET", pat, h)
|
||||
}
|
||||
|
||||
// Post will register a pattern with a handler for POST requests.
|
||||
func (p *PatternServeMux) Post(pat string, h http.Handler) {
|
||||
p.Add("POST", pat, h)
|
||||
}
|
||||
|
||||
// Put will register a pattern with a handler for PUT requests.
|
||||
func (p *PatternServeMux) Put(pat string, h http.Handler) {
|
||||
p.Add("PUT", pat, h)
|
||||
}
|
||||
|
||||
// Del will register a pattern with a handler for DELETE requests.
|
||||
func (p *PatternServeMux) Del(pat string, h http.Handler) {
|
||||
p.Add("DELETE", pat, h)
|
||||
}
|
||||
|
||||
// Options will register a pattern with a handler for OPTIONS requests.
|
||||
func (p *PatternServeMux) Options(pat string, h http.Handler) {
|
||||
p.Add("OPTIONS", pat, h)
|
||||
}
|
||||
|
||||
// Patch will register a pattern with a handler for PATCH requests.
|
||||
func (p *PatternServeMux) Patch(pat string, h http.Handler) {
|
||||
p.Add("PATCH", pat, h)
|
||||
}
|
||||
|
||||
// Add will register a pattern with a handler for meth requests.
|
||||
func (p *PatternServeMux) Add(meth, pat string, h http.Handler) {
|
||||
p.add(meth, pat, h, false)
|
||||
}
|
||||
|
||||
func (p *PatternServeMux) add(meth, pat string, h http.Handler, redirect bool) {
|
||||
handlers := p.handlers[meth]
|
||||
for _, p1 := range handlers {
|
||||
if p1.pat == pat {
|
||||
return // found existing pattern; do nothing
|
||||
}
|
||||
}
|
||||
handler := &patHandler{
|
||||
pat: pat,
|
||||
Handler: h,
|
||||
redirect: redirect,
|
||||
}
|
||||
p.handlers[meth] = append(handlers, handler)
|
||||
|
||||
n := len(pat)
|
||||
if n > 0 && pat[n-1] == '/' {
|
||||
p.add(meth, pat[:n-1], http.HandlerFunc(addSlashRedirect), true)
|
||||
}
|
||||
}
|
||||
|
||||
func addSlashRedirect(w http.ResponseWriter, r *http.Request) {
|
||||
u := *r.URL
|
||||
u.Path += "/"
|
||||
http.Redirect(w, r, u.String(), http.StatusMovedPermanently)
|
||||
}
|
||||
|
||||
// Tail returns the trailing string in path after the final slash for a pat ending with a slash.
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// Tail("/hello/:title/", "/hello/mr/mizerany") == "mizerany"
|
||||
// Tail("/:a/", "/x/y/z") == "y/z"
|
||||
//
|
||||
func Tail(pat, path string) string {
|
||||
var i, j int
|
||||
for i < len(path) {
|
||||
switch {
|
||||
case j >= len(pat):
|
||||
if pat[len(pat)-1] == '/' {
|
||||
return path[i:]
|
||||
}
|
||||
return ""
|
||||
case pat[j] == ':':
|
||||
var nextc byte
|
||||
_, nextc, j = match(pat, isAlnum, j+1)
|
||||
_, _, i = match(path, matchPart(nextc), i)
|
||||
case path[i] == pat[j]:
|
||||
i++
|
||||
j++
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type patHandler struct {
|
||||
pat string
|
||||
http.Handler
|
||||
redirect bool
|
||||
}
|
||||
|
||||
func (ph *patHandler) try(path string) (url.Values, bool) {
|
||||
p := make(url.Values)
|
||||
var i, j int
|
||||
for i < len(path) {
|
||||
switch {
|
||||
case j >= len(ph.pat):
|
||||
if ph.pat != "/" && len(ph.pat) > 0 && ph.pat[len(ph.pat)-1] == '/' {
|
||||
return p, true
|
||||
}
|
||||
return nil, false
|
||||
case ph.pat[j] == ':':
|
||||
var name, val string
|
||||
var nextc byte
|
||||
name, nextc, j = match(ph.pat, isAlnum, j+1)
|
||||
val, _, i = match(path, matchPart(nextc), i)
|
||||
escval, err := url.QueryUnescape(val)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
p.Add(":"+name, escval)
|
||||
case path[i] == ph.pat[j]:
|
||||
i++
|
||||
j++
|
||||
default:
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
if j != len(ph.pat) {
|
||||
return nil, false
|
||||
}
|
||||
return p, true
|
||||
}
|
||||
|
||||
func matchPart(b byte) func(byte) bool {
|
||||
return func(c byte) bool {
|
||||
return c != b && c != '/'
|
||||
}
|
||||
}
|
||||
|
||||
func match(s string, f func(byte) bool, i int) (matched string, next byte, j int) {
|
||||
j = i
|
||||
for j < len(s) && f(s[j]) {
|
||||
j++
|
||||
}
|
||||
if j < len(s) {
|
||||
next = s[j]
|
||||
}
|
||||
return s[i:j], next, j
|
||||
}
|
||||
|
||||
func isAlpha(ch byte) bool {
|
||||
return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_'
|
||||
}
|
||||
|
||||
func isDigit(ch byte) bool {
|
||||
return '0' <= ch && ch <= '9'
|
||||
}
|
||||
|
||||
func isAlnum(ch byte) bool {
|
||||
return isAlpha(ch) || isDigit(ch)
|
||||
}
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
Copyright (c) 2013-2017 Transloadit Ltd and Contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
-53
@@ -1,53 +0,0 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"io"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// bodyReader is an io.Reader, which is intended to wrap the request
|
||||
// body reader. If an error occurr during reading the request body, it
|
||||
// will not return this error to the reading entity, but instead store
|
||||
// the error and close the io.Reader, so that the error can be checked
|
||||
// afterwards. This is helpful, so that the stores do not have to handle
|
||||
// the error but this can instead be done in the handler.
|
||||
// In addition, the bodyReader keeps track of how many bytes were read.
|
||||
type bodyReader struct {
|
||||
reader io.Reader
|
||||
err error
|
||||
bytesCounter int64
|
||||
}
|
||||
|
||||
func newBodyReader(r io.Reader) *bodyReader {
|
||||
return &bodyReader{
|
||||
reader: r,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *bodyReader) Read(b []byte) (int, error) {
|
||||
if r.err != nil {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
n, err := r.reader.Read(b)
|
||||
atomic.AddInt64(&r.bytesCounter, int64(n))
|
||||
r.err = err
|
||||
|
||||
if err == io.EOF {
|
||||
return n, io.EOF
|
||||
} else {
|
||||
return n, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (r bodyReader) hasError() error {
|
||||
if r.err == io.EOF {
|
||||
return nil
|
||||
}
|
||||
|
||||
return r.err
|
||||
}
|
||||
|
||||
func (r *bodyReader) bytesRead() int64 {
|
||||
return atomic.LoadInt64(&r.bytesCounter)
|
||||
}
|
||||
-87
@@ -1,87 +0,0 @@
|
||||
package handler
|
||||
|
||||
// StoreComposer represents a composable data store. It consists of the core
|
||||
// data store and optional extensions. Please consult the package's overview
|
||||
// for a more detailed introduction in how to use this structure.
|
||||
type StoreComposer struct {
|
||||
Core DataStore
|
||||
|
||||
UsesTerminater bool
|
||||
Terminater TerminaterDataStore
|
||||
UsesLocker bool
|
||||
Locker Locker
|
||||
UsesConcater bool
|
||||
Concater ConcaterDataStore
|
||||
UsesLengthDeferrer bool
|
||||
LengthDeferrer LengthDeferrerDataStore
|
||||
}
|
||||
|
||||
// NewStoreComposer creates a new and empty store composer.
|
||||
func NewStoreComposer() *StoreComposer {
|
||||
return &StoreComposer{}
|
||||
}
|
||||
|
||||
// Capabilities returns a string representing the provided extensions in a
|
||||
// human-readable format meant for debugging.
|
||||
func (store *StoreComposer) Capabilities() string {
|
||||
str := "Core: "
|
||||
|
||||
if store.Core != nil {
|
||||
str += "✓"
|
||||
} else {
|
||||
str += "✗"
|
||||
}
|
||||
|
||||
str += ` Terminater: `
|
||||
if store.UsesTerminater {
|
||||
str += "✓"
|
||||
} else {
|
||||
str += "✗"
|
||||
}
|
||||
str += ` Locker: `
|
||||
if store.UsesLocker {
|
||||
str += "✓"
|
||||
} else {
|
||||
str += "✗"
|
||||
}
|
||||
str += ` Concater: `
|
||||
if store.UsesConcater {
|
||||
str += "✓"
|
||||
} else {
|
||||
str += "✗"
|
||||
}
|
||||
str += ` LengthDeferrer: `
|
||||
if store.UsesLengthDeferrer {
|
||||
str += "✓"
|
||||
} else {
|
||||
str += "✗"
|
||||
}
|
||||
|
||||
return str
|
||||
}
|
||||
|
||||
// UseCore will set the used core data store. If the argument is nil, the
|
||||
// property will be unset.
|
||||
func (store *StoreComposer) UseCore(core DataStore) {
|
||||
store.Core = core
|
||||
}
|
||||
|
||||
func (store *StoreComposer) UseTerminater(ext TerminaterDataStore) {
|
||||
store.UsesTerminater = ext != nil
|
||||
store.Terminater = ext
|
||||
}
|
||||
|
||||
func (store *StoreComposer) UseLocker(ext Locker) {
|
||||
store.UsesLocker = ext != nil
|
||||
store.Locker = ext
|
||||
}
|
||||
|
||||
func (store *StoreComposer) UseConcater(ext ConcaterDataStore) {
|
||||
store.UsesConcater = ext != nil
|
||||
store.Concater = ext
|
||||
}
|
||||
|
||||
func (store *StoreComposer) UseLengthDeferrer(ext LengthDeferrerDataStore) {
|
||||
store.UsesLengthDeferrer = ext != nil
|
||||
store.LengthDeferrer = ext
|
||||
}
|
||||
-74
@@ -1,74 +0,0 @@
|
||||
package handler
|
||||
|
||||
#define USE_FUNC(TYPE) \
|
||||
func (store *StoreComposer) Use ## TYPE(ext TYPE ## DataStore) { \
|
||||
store.Uses ## TYPE = ext != nil; \
|
||||
store.TYPE = ext; \
|
||||
}
|
||||
|
||||
#define USE_FIELD(TYPE) Uses ## TYPE bool; \
|
||||
TYPE TYPE ## DataStore
|
||||
|
||||
#define USE_FROM(TYPE) if mod, ok := store.(TYPE ## DataStore); ok { \
|
||||
composer.Use ## TYPE (mod) \
|
||||
}
|
||||
|
||||
#define USE_CAP(TYPE) str += ` TYPE: `; \
|
||||
if store.Uses ## TYPE { \
|
||||
str += "✓" \
|
||||
} else { \
|
||||
str += "✗" \
|
||||
}
|
||||
|
||||
// StoreComposer represents a composable data store. It consists of the core
|
||||
// data store and optional extensions. Please consult the package's overview
|
||||
// for a more detailed introduction in how to use this structure.
|
||||
type StoreComposer struct {
|
||||
Core DataStore
|
||||
|
||||
USE_FIELD(Terminater)
|
||||
USE_FIELD(Finisher)
|
||||
USE_FIELD(Locker)
|
||||
USE_FIELD(GetReader)
|
||||
USE_FIELD(Concater)
|
||||
USE_FIELD(LengthDeferrer)
|
||||
}
|
||||
|
||||
// NewStoreComposer creates a new and empty store composer.
|
||||
func NewStoreComposer() *StoreComposer {
|
||||
return &StoreComposer{}
|
||||
}
|
||||
|
||||
// Capabilities returns a string representing the provided extensions in a
|
||||
// human-readable format meant for debugging.
|
||||
func (store *StoreComposer) Capabilities() string {
|
||||
str := "Core: "
|
||||
|
||||
if store.Core != nil {
|
||||
str += "✓"
|
||||
} else {
|
||||
str += "✗"
|
||||
}
|
||||
|
||||
USE_CAP(Terminater)
|
||||
USE_CAP(Finisher)
|
||||
USE_CAP(Locker)
|
||||
USE_CAP(GetReader)
|
||||
USE_CAP(Concater)
|
||||
USE_CAP(LengthDeferrer)
|
||||
|
||||
return str
|
||||
}
|
||||
|
||||
// UseCore will set the used core data store. If the argument is nil, the
|
||||
// property will be unset.
|
||||
func (store *StoreComposer) UseCore(core DataStore) {
|
||||
store.Core = core
|
||||
}
|
||||
|
||||
USE_FUNC(Terminater)
|
||||
USE_FUNC(Finisher)
|
||||
USE_FUNC(Locker)
|
||||
USE_FUNC(GetReader)
|
||||
USE_FUNC(Concater)
|
||||
USE_FUNC(LengthDeferrer)
|
||||
-158
@@ -1,158 +0,0 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
"net/url"
|
||||
"os"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
// Config provides a way to configure the Handler depending on your needs.
|
||||
type Config struct {
|
||||
// StoreComposer points to the store composer from which the core data store
|
||||
// and optional dependencies should be taken. May only be nil if DataStore is
|
||||
// set.
|
||||
// TODO: Remove pointer?
|
||||
StoreComposer *StoreComposer
|
||||
// MaxSize defines how many bytes may be stored in one single upload. If its
|
||||
// value is is 0 or smaller no limit will be enforced.
|
||||
MaxSize int64
|
||||
// BasePath defines the URL path used for handling uploads, e.g. "/files/".
|
||||
// If no trailing slash is presented it will be added. You may specify an
|
||||
// absolute URL containing a scheme, e.g. "http://tus.io"
|
||||
BasePath string
|
||||
isAbs bool
|
||||
// EnableExperimentalProtocol controls whether the new resumable upload protocol draft
|
||||
// from the IETF's HTTP working group is accepted next to the current tus v1 protocol.
|
||||
// See https://datatracker.ietf.org/doc/draft-ietf-httpbis-resumable-upload/
|
||||
EnableExperimentalProtocol bool
|
||||
// DisableDownload indicates whether the server will refuse downloads of the
|
||||
// uploaded file, by not mounting the GET handler.
|
||||
DisableDownload bool
|
||||
// DisableTermination indicates whether the server will refuse termination
|
||||
// requests of the uploaded file, by not mounting the DELETE handler.
|
||||
DisableTermination bool
|
||||
// Disable cors headers. If set to true, tusd will not send any CORS related header.
|
||||
// This is useful if you have a proxy sitting in front of tusd that handles CORS.
|
||||
//
|
||||
// Deprecated: All CORS-related settings are available in via the Cors field. Use
|
||||
// Cors.Disable instead of DisableCors.
|
||||
DisableCors bool
|
||||
// Cors can be used to customize the handling of Cross-Origin Resource Sharing (CORS).
|
||||
// See the CorsConfig struct for more details.
|
||||
// Defaults to DefaultCorsConfig.
|
||||
Cors *CorsConfig
|
||||
// NotifyCompleteUploads indicates whether sending notifications about
|
||||
// completed uploads using the CompleteUploads channel should be enabled.
|
||||
NotifyCompleteUploads bool
|
||||
// NotifyTerminatedUploads indicates whether sending notifications about
|
||||
// terminated uploads using the TerminatedUploads channel should be enabled.
|
||||
NotifyTerminatedUploads bool
|
||||
// NotifyUploadProgress indicates whether sending notifications about
|
||||
// the upload progress using the UploadProgress channel should be enabled.
|
||||
NotifyUploadProgress bool
|
||||
// NotifyCreatedUploads indicates whether sending notifications about
|
||||
// the upload having been created using the CreatedUploads channel should be enabled.
|
||||
NotifyCreatedUploads bool
|
||||
// Logger is the logger to use internally, mostly for printing requests.
|
||||
Logger *log.Logger
|
||||
// Respect the X-Forwarded-Host, X-Forwarded-Proto and Forwarded headers
|
||||
// potentially set by proxies when generating an absolute URL in the
|
||||
// response to POST requests.
|
||||
RespectForwardedHeaders bool
|
||||
// PreUploadCreateCallback will be invoked before a new upload is created, if the
|
||||
// property is supplied. If the callback returns nil, the upload will be created.
|
||||
// Otherwise the HTTP request will be aborted. This can be used to implement
|
||||
// validation of upload metadata etc.
|
||||
PreUploadCreateCallback func(hook HookEvent) error
|
||||
// PreFinishResponseCallback will be invoked after an upload is completed but before
|
||||
// a response is returned to the client. Error responses from the callback will be passed
|
||||
// back to the client. This can be used to implement post-processing validation.
|
||||
PreFinishResponseCallback func(hook HookEvent) error
|
||||
}
|
||||
|
||||
// CorsConfig provides a way to customize the the handling of Cross-Origin Resource Sharing (CORS).
|
||||
// More details about CORS are available at https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS.
|
||||
type CorsConfig struct {
|
||||
// Disable instructs the handler to ignore all CORS-related headers and never set a
|
||||
// CORS-related header in a response. This is useful if CORS is already handled by a proxy.
|
||||
Disable bool
|
||||
// AllowOrigin is a regular expression used to check if a request is allowed to participate in the
|
||||
// CORS protocol. If the request's Origin header matches the regular expression, CORS is allowed.
|
||||
// If not, a 403 Forbidden response is sent, rejecting the CORS request.
|
||||
AllowOrigin *regexp.Regexp
|
||||
// AllowCredentials defines whether the `Access-Control-Allow-Credentials: true` header should be
|
||||
// included in CORS responses. This allows clients to share credentials using the Cookie and
|
||||
// Authorization header
|
||||
AllowCredentials bool
|
||||
// AllowMethods defines the value for the `Access-Control-Allow-Methods` header in the response to
|
||||
// preflight requests. You can add custom methods here, but make sure that all tus-specific methods
|
||||
// from DefaultConfig.AllowMethods are included as well.
|
||||
AllowMethods string
|
||||
// AllowHeaders defines the value for the `Access-Control-Allow-Headers` header in the response to
|
||||
// preflight requests. You can add custom headers here, but make sure that all tus-specific header
|
||||
// from DefaultConfig.AllowHeaders are included as well.
|
||||
AllowHeaders string
|
||||
// MaxAge defines the value for the `Access-Control-Max-Age` header in the response to preflight
|
||||
// requests.
|
||||
MaxAge string
|
||||
// ExposeHeaders defines the value for the `Access-Control-Expose-Headers` header in the response to
|
||||
// actual requests. You can add custom headers here, but make sure that all tus-specific header
|
||||
// from DefaultConfig.ExposeHeaders are included as well.
|
||||
ExposeHeaders string
|
||||
}
|
||||
|
||||
// DefaultCorsConfig is the configuration that will be used in none is provided.
|
||||
var DefaultCorsConfig = CorsConfig{
|
||||
Disable: false,
|
||||
AllowOrigin: regexp.MustCompile(".*"),
|
||||
AllowCredentials: false,
|
||||
AllowMethods: "POST, HEAD, PATCH, OPTIONS, GET, DELETE",
|
||||
AllowHeaders: "Authorization, Origin, X-Requested-With, X-Request-ID, X-HTTP-Method-Override, Content-Type, Upload-Length, Upload-Offset, Tus-Resumable, Upload-Metadata, Upload-Defer-Length, Upload-Concat, Upload-Incomplete, Upload-Draft-Interop-Version",
|
||||
MaxAge: "86400",
|
||||
ExposeHeaders: "Upload-Offset, Location, Upload-Length, Tus-Version, Tus-Resumable, Tus-Max-Size, Tus-Extension, Upload-Metadata, Upload-Defer-Length, Upload-Concat, Upload-Incomplete, Upload-Draft-Interop-Version",
|
||||
}
|
||||
|
||||
func (config *Config) validate() error {
|
||||
if config.Logger == nil {
|
||||
config.Logger = log.New(os.Stdout, "[tusd] ", log.Ldate|log.Lmicroseconds)
|
||||
}
|
||||
|
||||
base := config.BasePath
|
||||
uri, err := url.Parse(base)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Ensure base path ends with slash to remove logic from absFileURL
|
||||
if base != "" && string(base[len(base)-1]) != "/" {
|
||||
base += "/"
|
||||
}
|
||||
|
||||
// Ensure base path begins with slash if not absolute (starts with scheme)
|
||||
if !uri.IsAbs() && len(base) > 0 && string(base[0]) != "/" {
|
||||
base = "/" + base
|
||||
}
|
||||
config.BasePath = base
|
||||
config.isAbs = uri.IsAbs()
|
||||
|
||||
if config.StoreComposer == nil {
|
||||
return errors.New("tusd: StoreComposer must no be nil")
|
||||
}
|
||||
|
||||
if config.StoreComposer.Core == nil {
|
||||
return errors.New("tusd: StoreComposer in Config needs to contain a non-nil core")
|
||||
}
|
||||
|
||||
if config.Cors == nil {
|
||||
config.Cors = &DefaultCorsConfig
|
||||
}
|
||||
|
||||
// Support previous settings for disabling CORS.
|
||||
if config.DisableCors {
|
||||
config.Cors.Disable = true
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
-156
@@ -1,156 +0,0 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
)
|
||||
|
||||
type MetaData map[string]string
|
||||
|
||||
type FileInfo struct {
|
||||
ID string
|
||||
// Total file size in bytes specified in the NewUpload call
|
||||
Size int64
|
||||
// Indicates whether the total file size is deferred until later
|
||||
SizeIsDeferred bool
|
||||
// Offset in bytes (zero-based)
|
||||
Offset int64
|
||||
MetaData MetaData
|
||||
// Indicates that this is a partial upload which will later be used to form
|
||||
// a final upload by concatenation. Partial uploads should not be processed
|
||||
// when they are finished since they are only incomplete chunks of files.
|
||||
IsPartial bool
|
||||
// Indicates that this is a final upload
|
||||
IsFinal bool
|
||||
// If the upload is a final one (see IsFinal) this will be a non-empty
|
||||
// ordered slice containing the ids of the uploads of which the final upload
|
||||
// will consist after concatenation.
|
||||
PartialUploads []string
|
||||
// Storage contains information about where the data storage saves the upload,
|
||||
// for example a file path. The available values vary depending on what data
|
||||
// store is used. This map may also be nil.
|
||||
Storage map[string]string
|
||||
|
||||
// stopUpload is the cancel function for the upload's context.Context. When
|
||||
// invoked it will interrupt the writes to DataStore#WriteChunk.
|
||||
stopUpload context.CancelFunc
|
||||
}
|
||||
|
||||
// StopUpload interrupts an running upload from the server-side. This means that
|
||||
// the current request body is closed, so that the data store does not get any
|
||||
// more data. Furthermore, a response is sent to notify the client of the
|
||||
// interrupting and the upload is terminated (if supported by the data store),
|
||||
// so the upload cannot be resumed anymore.
|
||||
func (f FileInfo) StopUpload() {
|
||||
if f.stopUpload != nil {
|
||||
f.stopUpload()
|
||||
}
|
||||
}
|
||||
|
||||
type Upload interface {
|
||||
// Write the chunk read from src into the file specified by the id at the
|
||||
// given offset. The handler will take care of validating the offset and
|
||||
// limiting the size of the src to not overflow the file's size. It may
|
||||
// return an os.ErrNotExist which will be interpreted as a 404 Not Found.
|
||||
// It will also lock resources while they are written to ensure only one
|
||||
// write happens per time.
|
||||
// The function call must return the number of bytes written.
|
||||
WriteChunk(ctx context.Context, offset int64, src io.Reader) (int64, error)
|
||||
// Read the fileinformation used to validate the offset and respond to HEAD
|
||||
// requests. It may return an os.ErrNotExist which will be interpreted as a
|
||||
// 404 Not Found.
|
||||
GetInfo(ctx context.Context) (FileInfo, error)
|
||||
// GetReader returns a reader which allows iterating of the content of an
|
||||
// upload specified by its ID. It should attempt to provide a reader even if
|
||||
// the upload has not been finished yet but it's not required.
|
||||
// If the returned reader also implements the io.Closer interface, the
|
||||
// Close() method will be invoked once everything has been read.
|
||||
// If the given upload could not be found, the error tusd.ErrNotFound should
|
||||
// be returned.
|
||||
GetReader(ctx context.Context) (io.Reader, error)
|
||||
// FinisherDataStore is the interface which can be implemented by DataStores
|
||||
// which need to do additional operations once an entire upload has been
|
||||
// completed. These tasks may include but are not limited to freeing unused
|
||||
// resources or notifying other services. For example, S3Store uses this
|
||||
// interface for removing a temporary object.
|
||||
// FinishUpload executes additional operations for the finished upload which
|
||||
// is specified by its ID.
|
||||
FinishUpload(ctx context.Context) error
|
||||
}
|
||||
|
||||
type DataStore interface {
|
||||
// Create a new upload using the size as the file's length. The method must
|
||||
// return an unique id which is used to identify the upload. If no backend
|
||||
// (e.g. Riak) specifes the id you may want to use the uid package to
|
||||
// generate one. The properties Size and MetaData will be filled.
|
||||
NewUpload(ctx context.Context, info FileInfo) (upload Upload, err error)
|
||||
|
||||
GetUpload(ctx context.Context, id string) (upload Upload, err error)
|
||||
}
|
||||
|
||||
type TerminatableUpload interface {
|
||||
// Terminate an upload so any further requests to the resource, both reading
|
||||
// and writing, must return os.ErrNotExist or similar.
|
||||
Terminate(ctx context.Context) error
|
||||
}
|
||||
|
||||
// TerminaterDataStore is the interface which must be implemented by DataStores
|
||||
// if they want to receive DELETE requests using the Handler. If this interface
|
||||
// is not implemented, no request handler for this method is attached.
|
||||
type TerminaterDataStore interface {
|
||||
AsTerminatableUpload(upload Upload) TerminatableUpload
|
||||
}
|
||||
|
||||
// ConcaterDataStore is the interface required to be implemented if the
|
||||
// Concatenation extension should be enabled. Only in this case, the handler
|
||||
// will parse and respect the Upload-Concat header.
|
||||
type ConcaterDataStore interface {
|
||||
AsConcatableUpload(upload Upload) ConcatableUpload
|
||||
}
|
||||
|
||||
type ConcatableUpload interface {
|
||||
// ConcatUploads concatenates the content from the provided partial uploads
|
||||
// and writes the result in the destination upload.
|
||||
// The caller (usually the handler) must and will ensure that this
|
||||
// destination upload has been created before with enough space to hold all
|
||||
// partial uploads. The order, in which the partial uploads are supplied,
|
||||
// must be respected during concatenation.
|
||||
ConcatUploads(ctx context.Context, partialUploads []Upload) error
|
||||
}
|
||||
|
||||
// LengthDeferrerDataStore is the interface that must be implemented if the
|
||||
// creation-defer-length extension should be enabled. The extension enables a
|
||||
// client to upload files when their total size is not yet known. Instead, the
|
||||
// client must send the total size as soon as it becomes known.
|
||||
type LengthDeferrerDataStore interface {
|
||||
AsLengthDeclarableUpload(upload Upload) LengthDeclarableUpload
|
||||
}
|
||||
|
||||
type LengthDeclarableUpload interface {
|
||||
DeclareLength(ctx context.Context, length int64) error
|
||||
}
|
||||
|
||||
// Locker is the interface required for custom lock persisting mechanisms.
|
||||
// Common ways to store this information is in memory, on disk or using an
|
||||
// external service, such as Redis.
|
||||
// When multiple processes are attempting to access an upload, whether it be
|
||||
// by reading or writing, a synchronization mechanism is required to prevent
|
||||
// data corruption, especially to ensure correct offset values and the proper
|
||||
// order of chunks inside a single upload.
|
||||
type Locker interface {
|
||||
// NewLock creates a new unlocked lock object for the given upload ID.
|
||||
NewLock(id string) (Lock, error)
|
||||
}
|
||||
|
||||
// Lock is the interface for a lock as returned from a Locker.
|
||||
type Lock interface {
|
||||
// Lock attempts to obtain an exclusive lock for the upload specified
|
||||
// by its id.
|
||||
// If this operation fails because the resource is already locked, the
|
||||
// tusd.ErrFileLocked must be returned. If no error is returned, the attempt
|
||||
// is consider to be successful and the upload to be locked until UnlockUpload
|
||||
// is invoked for the same upload.
|
||||
Lock() error
|
||||
// Unlock releases an existing lock for the given upload.
|
||||
Unlock() error
|
||||
}
|
||||
-69
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
Package handler provides ways to accept tus 1.0 calls using HTTP.
|
||||
|
||||
tus is a protocol based on HTTP for resumable file uploads. Resumable means that
|
||||
an upload can be interrupted at any moment and can be resumed without
|
||||
re-uploading the previous data again. An interruption may happen willingly, if
|
||||
the user wants to pause, or by accident in case of an network issue or server
|
||||
outage (http://tus.io).
|
||||
|
||||
The basics of tusd
|
||||
|
||||
tusd was designed in way which allows an flexible and customizable usage. We
|
||||
wanted to avoid binding this package to a specific storage system – particularly
|
||||
a proprietary third-party software. Therefore tusd is an abstract layer whose
|
||||
only job is to accept incoming HTTP requests, validate them according to the
|
||||
specification and finally passes them to the data store.
|
||||
|
||||
The data store is another important component in tusd's architecture whose
|
||||
purpose is to do the actual file handling. It has to write the incoming upload
|
||||
to a persistent storage system and retrieve information about an upload's
|
||||
current state. Therefore it is the only part of the system which communicates
|
||||
directly with the underlying storage system, whether it be the local disk, a
|
||||
remote FTP server or cloud providers such as AWS S3.
|
||||
|
||||
Using a store composer
|
||||
|
||||
The only hard requirements for a data store can be found in the DataStore
|
||||
interface. It contains methods for creating uploads (NewUpload), writing to
|
||||
them (WriteChunk) and retrieving their status (GetInfo). However, there
|
||||
are many more features which are not mandatory but may still be used.
|
||||
These are contained in their own interfaces which all share the *DataStore
|
||||
suffix. For example, GetReaderDataStore which enables downloading uploads or
|
||||
TerminaterDataStore which allows uploads to be terminated.
|
||||
|
||||
The store composer offers a way to combine the basic data store - the core -
|
||||
implementation and these additional extensions:
|
||||
|
||||
composer := tusd.NewStoreComposer()
|
||||
composer.UseCore(dataStore) // Implements DataStore
|
||||
composer.UseTerminater(terminater) // Implements TerminaterDataStore
|
||||
composer.UseLocker(locker) // Implements LockerDataStore
|
||||
|
||||
The corresponding methods for adding an extension to the composer are prefixed
|
||||
with Use* followed by the name of the corresponding interface. However, most
|
||||
data store provide multiple extensions and adding all of them manually can be
|
||||
tedious and error-prone. Therefore, all data store distributed with tusd provide
|
||||
an UseIn() method which does this job automatically. For example, this is the
|
||||
S3 store in action (see S3Store.UseIn):
|
||||
|
||||
store := s3store.New(…)
|
||||
locker := memorylocker.New()
|
||||
composer := tusd.NewStoreComposer()
|
||||
store.UseIn(composer)
|
||||
locker.UseIn(composer)
|
||||
|
||||
Finally, once you are done with composing your data store, you can pass it
|
||||
inside the Config struct in order to create create a new tusd HTTP handler:
|
||||
|
||||
config := tusd.Config{
|
||||
StoreComposer: composer,
|
||||
BasePath: "/files/",
|
||||
}
|
||||
handler, err := tusd.NewHandler(config)
|
||||
|
||||
This handler can then be mounted to a specific path, e.g. /files:
|
||||
|
||||
http.Handle("/files/", http.StripPrefix("/files/", handler))
|
||||
*/
|
||||
package handler
|
||||
-53
@@ -1,53 +0,0 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/bmizerany/pat"
|
||||
)
|
||||
|
||||
// Handler is a ready to use handler with routing (using pat)
|
||||
type Handler struct {
|
||||
*UnroutedHandler
|
||||
http.Handler
|
||||
}
|
||||
|
||||
// NewHandler creates a routed tus protocol handler. This is the simplest
|
||||
// way to use tusd but may not be as configurable as you require. If you are
|
||||
// integrating this into an existing app you may like to use tusd.NewUnroutedHandler
|
||||
// instead. Using tusd.NewUnroutedHandler allows the tus handlers to be combined into
|
||||
// your existing router (aka mux) directly. It also allows the GET and DELETE
|
||||
// endpoints to be customized. These are not part of the protocol so can be
|
||||
// changed depending on your needs.
|
||||
func NewHandler(config Config) (*Handler, error) {
|
||||
if err := config.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
handler, err := NewUnroutedHandler(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
routedHandler := &Handler{
|
||||
UnroutedHandler: handler,
|
||||
}
|
||||
|
||||
mux := pat.New()
|
||||
|
||||
routedHandler.Handler = handler.Middleware(mux)
|
||||
|
||||
mux.Post("", http.HandlerFunc(handler.PostFile))
|
||||
mux.Head(":id", http.HandlerFunc(handler.HeadFile))
|
||||
mux.Add("PATCH", ":id", http.HandlerFunc(handler.PatchFile))
|
||||
if !config.DisableDownload {
|
||||
mux.Get(":id", http.HandlerFunc(handler.GetFile))
|
||||
}
|
||||
|
||||
// Only attach the DELETE handler if the Terminate() method is provided
|
||||
if config.StoreComposer.UsesTerminater && !config.DisableTermination {
|
||||
mux.Del(":id", http.HandlerFunc(handler.DelFile))
|
||||
}
|
||||
|
||||
return routedHandler, nil
|
||||
}
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"log"
|
||||
)
|
||||
|
||||
func (h *UnroutedHandler) log(eventName string, details ...string) {
|
||||
LogEvent(h.logger, eventName, details...)
|
||||
}
|
||||
|
||||
func LogEvent(logger *log.Logger, eventName string, details ...string) {
|
||||
result := make([]byte, 0, 100)
|
||||
|
||||
result = append(result, `event="`...)
|
||||
result = append(result, eventName...)
|
||||
result = append(result, `" `...)
|
||||
|
||||
for i := 0; i < len(details); i += 2 {
|
||||
result = append(result, details[i]...)
|
||||
result = append(result, `="`...)
|
||||
result = append(result, details[i+1]...)
|
||||
result = append(result, `" `...)
|
||||
}
|
||||
|
||||
result = append(result, "\n"...)
|
||||
logger.Output(2, string(result))
|
||||
}
|
||||
-137
@@ -1,137 +0,0 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// Metrics provides numbers about the usage of the tusd handler. Since these may
|
||||
// be accessed from multiple goroutines, it is necessary to read and modify them
|
||||
// atomically using the functions exposed in the sync/atomic package, such as
|
||||
// atomic.LoadUint64. In addition the maps must not be modified to prevent data
|
||||
// races.
|
||||
type Metrics struct {
|
||||
// RequestTotal counts the number of incoming requests per method
|
||||
RequestsTotal map[string]*uint64
|
||||
// ErrorsTotal counts the number of returned errors by their message
|
||||
ErrorsTotal *ErrorsTotalMap
|
||||
BytesReceived *uint64
|
||||
UploadsFinished *uint64
|
||||
UploadsCreated *uint64
|
||||
UploadsTerminated *uint64
|
||||
}
|
||||
|
||||
// incRequestsTotal increases the counter for this request method atomically by
|
||||
// one. The method must be one of GET, HEAD, POST, PATCH, DELETE.
|
||||
func (m Metrics) incRequestsTotal(method string) {
|
||||
if ptr, ok := m.RequestsTotal[method]; ok {
|
||||
atomic.AddUint64(ptr, 1)
|
||||
}
|
||||
}
|
||||
|
||||
// incErrorsTotal increases the counter for this error atomically by one.
|
||||
func (m Metrics) incErrorsTotal(err HTTPError) {
|
||||
ptr := m.ErrorsTotal.retrievePointerFor(err)
|
||||
atomic.AddUint64(ptr, 1)
|
||||
}
|
||||
|
||||
// incBytesReceived increases the number of received bytes atomically be the
|
||||
// specified number.
|
||||
func (m Metrics) incBytesReceived(delta uint64) {
|
||||
atomic.AddUint64(m.BytesReceived, delta)
|
||||
}
|
||||
|
||||
// incUploadsFinished increases the counter for finished uploads atomically by one.
|
||||
func (m Metrics) incUploadsFinished() {
|
||||
atomic.AddUint64(m.UploadsFinished, 1)
|
||||
}
|
||||
|
||||
// incUploadsCreated increases the counter for completed uploads atomically by one.
|
||||
func (m Metrics) incUploadsCreated() {
|
||||
atomic.AddUint64(m.UploadsCreated, 1)
|
||||
}
|
||||
|
||||
// incUploadsTerminated increases the counter for completed uploads atomically by one.
|
||||
func (m Metrics) incUploadsTerminated() {
|
||||
atomic.AddUint64(m.UploadsTerminated, 1)
|
||||
}
|
||||
|
||||
func newMetrics() Metrics {
|
||||
return Metrics{
|
||||
RequestsTotal: map[string]*uint64{
|
||||
"GET": new(uint64),
|
||||
"HEAD": new(uint64),
|
||||
"POST": new(uint64),
|
||||
"PATCH": new(uint64),
|
||||
"DELETE": new(uint64),
|
||||
"OPTIONS": new(uint64),
|
||||
},
|
||||
ErrorsTotal: newErrorsTotalMap(),
|
||||
BytesReceived: new(uint64),
|
||||
UploadsFinished: new(uint64),
|
||||
UploadsCreated: new(uint64),
|
||||
UploadsTerminated: new(uint64),
|
||||
}
|
||||
}
|
||||
|
||||
// ErrorsTotalMap stores the counters for the different HTTP errors.
|
||||
type ErrorsTotalMap struct {
|
||||
lock sync.RWMutex
|
||||
counter map[simpleHTTPError]*uint64
|
||||
}
|
||||
|
||||
type simpleHTTPError struct {
|
||||
Message string
|
||||
StatusCode int
|
||||
}
|
||||
|
||||
func simplifyHTTPError(err HTTPError) simpleHTTPError {
|
||||
return simpleHTTPError{
|
||||
Message: err.Error(),
|
||||
StatusCode: err.StatusCode(),
|
||||
}
|
||||
}
|
||||
|
||||
func newErrorsTotalMap() *ErrorsTotalMap {
|
||||
m := make(map[simpleHTTPError]*uint64, 20)
|
||||
return &ErrorsTotalMap{
|
||||
counter: m,
|
||||
}
|
||||
}
|
||||
|
||||
// retrievePointerFor returns (after creating it if necessary) the pointer to
|
||||
// the counter for the error.
|
||||
func (e *ErrorsTotalMap) retrievePointerFor(err HTTPError) *uint64 {
|
||||
serr := simplifyHTTPError(err)
|
||||
e.lock.RLock()
|
||||
ptr, ok := e.counter[serr]
|
||||
e.lock.RUnlock()
|
||||
if ok {
|
||||
return ptr
|
||||
}
|
||||
|
||||
// For pointer creation, a write-lock is required
|
||||
e.lock.Lock()
|
||||
// We ensure that the pointer wasn't created in the meantime
|
||||
if ptr, ok = e.counter[serr]; !ok {
|
||||
ptr = new(uint64)
|
||||
e.counter[serr] = ptr
|
||||
}
|
||||
e.lock.Unlock()
|
||||
|
||||
return ptr
|
||||
}
|
||||
|
||||
// Load retrieves the map of the counter pointers atomically
|
||||
func (e *ErrorsTotalMap) Load() map[HTTPError]*uint64 {
|
||||
m := make(map[HTTPError]*uint64, len(e.counter))
|
||||
e.lock.RLock()
|
||||
for err, ptr := range e.counter {
|
||||
httpErr := NewHTTPError(errors.New(err.Message), err.StatusCode)
|
||||
m[httpErr] = ptr
|
||||
}
|
||||
e.lock.RUnlock()
|
||||
|
||||
return m
|
||||
}
|
||||
-1468
File diff suppressed because it is too large
Load Diff
Vendored
+2
-6
@@ -261,9 +261,6 @@ github.com/blevesearch/zapx/v16
|
||||
# github.com/bluele/gcache v0.0.2
|
||||
## explicit; go 1.15
|
||||
github.com/bluele/gcache
|
||||
# github.com/bmizerany/pat v0.0.0-20210406213842-e4b6760bdd6f
|
||||
## explicit
|
||||
github.com/bmizerany/pat
|
||||
# github.com/bombsimon/logrusr/v3 v3.1.0
|
||||
## explicit; go 1.17
|
||||
github.com/bombsimon/logrusr/v3
|
||||
@@ -1100,6 +1097,8 @@ github.com/golang/snappy
|
||||
# github.com/gomodule/redigo v1.8.9
|
||||
## explicit; go 1.16
|
||||
github.com/gomodule/redigo/redis
|
||||
# github.com/google/flatbuffers v2.0.8+incompatible
|
||||
## explicit
|
||||
# github.com/google/go-cmp v0.6.0
|
||||
## explicit; go 1.13
|
||||
github.com/google/go-cmp/cmp
|
||||
@@ -1882,9 +1881,6 @@ github.com/trustelem/zxcvbn/internal/mathutils
|
||||
github.com/trustelem/zxcvbn/match
|
||||
github.com/trustelem/zxcvbn/matching
|
||||
github.com/trustelem/zxcvbn/scoring
|
||||
# github.com/tus/tusd v1.13.0
|
||||
## explicit; go 1.16
|
||||
github.com/tus/tusd/pkg/handler
|
||||
# github.com/tus/tusd/v2 v2.4.0
|
||||
## explicit; go 1.20
|
||||
github.com/tus/tusd/v2/pkg/handler
|
||||
|
||||
Reference in New Issue
Block a user