Merge branch 'master' of github.com:tus/tusd into v2

This commit is contained in:
Marius 2022-04-10 18:24:30 +02:00
commit 9508fd3910
10 changed files with 76 additions and 24 deletions

View File

@ -25,7 +25,7 @@ RUN set -xe \
-o /go/bin/tusd ./cmd/tusd/main.go
# start a new stage that copies in the binary built in the previous stage
FROM alpine:3.15.1
FROM alpine:3.15.3
WORKDIR /srv/tusd-data
RUN apk add --no-cache ca-certificates jq \

View File

@ -21,6 +21,8 @@ var Flags struct {
UploadDir string
Basepath string
ShowGreeting bool
DisableDownload bool
DisableTermination bool
Timeout int64
S3Bucket string
S3ObjectPrefix string
@ -73,6 +75,8 @@ func ParseFlags() {
flag.StringVar(&Flags.UploadDir, "upload-dir", "./data", "Directory to store uploads in")
flag.StringVar(&Flags.Basepath, "base-path", "/files/", "Basepath of the HTTP server")
flag.BoolVar(&Flags.ShowGreeting, "show-greeting", true, "Show the greeting message")
flag.BoolVar(&Flags.DisableDownload, "disable-download", false, "Disable the download endpoint")
flag.BoolVar(&Flags.DisableTermination, "disable-termination", false, "Disable the termination endpoint")
flag.Int64Var(&Flags.Timeout, "timeout", 6*1000, "Read timeout for connections in milliseconds. A zero value means that reads will not timeout")
flag.StringVar(&Flags.S3Bucket, "s3-bucket", "", "Use AWS S3 with this bucket as storage backend (requires the AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY and AWS_REGION environment variables to be set)")
flag.StringVar(&Flags.S3ObjectPrefix, "s3-object-prefix", "", "Prefix for S3 object names")

View File

@ -27,6 +27,8 @@ func Serve() {
MaxSize: Flags.MaxSize,
BasePath: Flags.Basepath,
RespectForwardedHeaders: Flags.BehindProxy,
DisableDownload: Flags.DisableDownload,
DisableTermination: Flags.DisableTermination,
StoreComposer: Composer,
NotifyCompleteUploads: true,
NotifyTerminatedUploads: true,

View File

@ -43,7 +43,7 @@ A non-zero exit code or HTTP response greater than `400` will return a HTTP 500
### post-finish
This event will be triggered after an upload is fully finished, meaning that all chunks have been transfered and saved in the storage. After this point, no further modifications, except possible deletion, can be made to the upload entity and it may be desirable to use the file for further processing or notify other applications of the completions of this upload.
This event will be triggered after an upload is fully finished, meaning that all chunks have been transferred and saved in the storage. After this point, no further modifications, except possible deletion, can be made to the upload entity and it may be desirable to use the file for further processing or notify other applications of the completions of this upload.
### post-terminate
@ -51,7 +51,7 @@ This event will be triggered after an upload has been terminated, meaning that t
### post-receive
This event will be triggered for every running upload to indicate its current progress. It will be emitted whenever the server has received more data from the client but at most every second. The offset property will be set to the number of bytes which have been transfered to the server, at the time in total. Please be aware that this number may be higher than the number of bytes which have been stored by the data store!
This event will be triggered for every running upload to indicate its current progress. It will be emitted whenever the server has received more data from the client but at most every second. The offset property will be set to the number of bytes which have been transferred to the server, at the time in total. Please be aware that this number may be higher than the number of bytes which have been stored by the data store!
## Whitelisting Hook Events
@ -95,7 +95,7 @@ The process of the hook files are provided with information about the event and
// If the upload is a final one, this value will be an array of upload IDs
// which are concatenated to produce the upload.
"PartialUploads": null,
// The upload's meta data which can be supplied by the clients as it wishes.
// The upload's metadata which can be supplied by the clients as it wishes.
// All keys and values in this object will be strings.
// Be aware that it may contain maliciously crafted values and you must not
// trust it without escaping it first!
@ -103,7 +103,7 @@ The process of the hook files are provided with information about the event and
"filename": "transloadit.png"
},
// Details about where the data store saved the uploaded file. The different
// availabl keys vary depending on the used data store.
// available keys vary depending on the used data store.
"Storage": {
// For example, the filestore supplies the absolute file path:
"Type": "filestore",
@ -170,7 +170,7 @@ Tusd will issue a `POST` request to the specified URL endpoint, specifying the h
// If the upload is a final one, this value will be an array of upload IDs
// which are concatenated to produce the upload.
"PartialUploads": null,
// The upload's meta data which can be supplied by the clients as it wishes.
// The upload's metadata which can be supplied by the clients as it wishes.
// All keys and values in this object will be strings.
// Be aware that it may contain maliciously crafted values and you must not
// trust it without escaping it first!
@ -178,7 +178,7 @@ Tusd will issue a `POST` request to the specified URL endpoint, specifying the h
"filename": "transloadit.png"
},
// Details about where the data store saved the uploaded file. The different
// availabl keys vary depending on the used data store.
// available keys vary depending on the used data store.
"Storage": {
// For example, the filestore supplies the absolute file path:
"Type": "filestore",
@ -247,7 +247,7 @@ Tusd will issue a `gRPC` request to the specified endpoint, specifying the hook
// If the upload is a final one, this value will be an array of upload IDs
// which are concatenated to produce the upload.
"PartialUploads": null,
// The upload's meta data which can be supplied by the clients as it wishes.
// The upload's metadata which can be supplied by the clients as it wishes.
// All keys and values in this object will be strings.
// Be aware that it may contain maliciously crafted values and you must not
// trust it without escaping it first!
@ -255,7 +255,7 @@ Tusd will issue a `gRPC` request to the specified endpoint, specifying the hook
"filename": "transloadit.png"
},
// Details about where the data store saved the uploaded file. The different
// availabl keys vary depending on the used data store.
// available keys vary depending on the used data store.
"Storage": {
// For example, the filestore supplies the absolute file path:
"Type": "filestore",

4
go.mod
View File

@ -8,7 +8,7 @@ go 1.16
require (
cloud.google.com/go/storage v1.21.0
github.com/Azure/azure-storage-blob-go v0.14.0
github.com/aws/aws-sdk-go v1.43.21
github.com/aws/aws-sdk-go v1.43.31
github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40
github.com/golang/mock v1.6.0
github.com/golang/protobuf v1.5.2
@ -20,7 +20,7 @@ require (
github.com/stretchr/testify v1.7.1
github.com/vimeo/go-util v1.4.1
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
google.golang.org/api v0.73.0
google.golang.org/api v0.74.0
google.golang.org/grpc v1.45.0
gopkg.in/Acconut/lockfile.v1 v1.1.0
gopkg.in/h2non/gock.v1 v1.1.2

25
go.sum
View File

@ -80,8 +80,8 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/aws/aws-sdk-go v1.43.21 h1:E4S2eX3d2gKJyI/ISrcIrSwXwqjIvCK85gtBMt4sAPE=
github.com/aws/aws-sdk-go v1.43.21/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
github.com/aws/aws-sdk-go v1.43.31 h1:yJZIr8nMV1hXjAvvOLUFqZRJcHV7udPQBfhJqawDzI0=
github.com/aws/aws-sdk-go v1.43.31/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
@ -216,8 +216,9 @@ github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
github.com/googleapis/gax-go/v2 v2.1.1 h1:dp3bWCh+PPO1zjRRiCSczJav13sBvG4UhNyVTa1KqdU=
github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM=
github.com/googleapis/gax-go/v2 v2.2.0 h1:s7jOdKSaksJVOxE0Y/S32otcfiP+UQ0cL8/GTKaONwE=
github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM=
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw=
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
@ -430,8 +431,9 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220325170049-de3da57026de h1:pZB1TWnKi+o4bENlbzAgLrEbY4RMYmUIRobMcSmfeYc=
golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -449,6 +451,7 @@ golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ
golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a h1:qfl7ob3DIEs3Ml9oLuPwY2N04gymzAW04WsUQHIClgM=
golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -527,8 +530,9 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5 h1:y/woIyUBFbpQGKS0u1aHF/40WUDnek3fPOyD08H5Vng=
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886 h1:eJv7u3ksNXoLbGSKuv2s/SIO4tJVxc/A+MTpzxDgz/Q=
golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -638,8 +642,9 @@ google.golang.org/api v0.66.0/go.mod h1:I1dmXYpX7HGwz/ejRxwQp2qj5bFAz93HiCU1C1oY
google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g=
google.golang.org/api v0.69.0/go.mod h1:boanBiw+h5c3s+tBPgEzLDRHfFLWV0qXxRHz3ws7C80=
google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA=
google.golang.org/api v0.73.0 h1:O9bThUh35K1rvUrQwTUQ1eqLC/IYyzUpWavYIO2EXvo=
google.golang.org/api v0.73.0/go.mod h1:lbd/q6BRFJbdpV6OUCXstVeiI5mL/d3/WifG7iNKnjI=
google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8=
google.golang.org/api v0.74.0 h1:ExR2D+5TYIrMphWgs5JCgwRhEDlPDXXrLwHHMgPHTXE=
google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@ -727,8 +732,10 @@ google.golang.org/genproto v0.0.0-20220211171837-173942840c17/go.mod h1:kGP+zUP2
google.golang.org/genproto v0.0.0-20220216160803-4663080d8bc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6 h1:FglFEfyj61zP3c6LgjmVHxYxZWXYul9oiS1EZqD5gLc=
google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb h1:0m9wktIpOxGw+SSKmydXWB3Z3GTfcPP6+q75HCQa6HI=
google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=

View File

@ -22,6 +22,12 @@ type Config struct {
// absolute URL containing a scheme, e.g. "http://tus.io"
BasePath string
isAbs 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
// NotifyCompleteUploads indicates whether sending notifications about
// completed uploads using the CompleteUploads channel should be enabled.
NotifyCompleteUploads bool

View File

@ -22,7 +22,29 @@ func TestCORS(t *testing.T) {
Code: http.StatusOK,
ResHeader: map[string]string{
"Access-Control-Allow-Headers": "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",
"Access-Control-Allow-Methods": "POST, GET, HEAD, PATCH, DELETE, OPTIONS",
"Access-Control-Allow-Methods": "POST, HEAD, PATCH, OPTIONS, GET, DELETE",
"Access-Control-Max-Age": "86400",
"Access-Control-Allow-Origin": "tus.io",
},
}).Run(handler, t)
})
SubTest(t, "Conditional allow methods", func(t *testing.T, store *MockFullDataStore, composer *StoreComposer) {
handler, _ := NewHandler(Config{
StoreComposer: composer,
DisableTermination: true,
DisableDownload: true,
})
(&httpTest{
Method: "OPTIONS",
ReqHeader: map[string]string{
"Origin": "tus.io",
},
Code: http.StatusOK,
ResHeader: map[string]string{
"Access-Control-Allow-Headers": "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",
"Access-Control-Allow-Methods": "POST, HEAD, PATCH, OPTIONS",
"Access-Control-Max-Age": "86400",
"Access-Control-Allow-Origin": "tus.io",
},

View File

@ -40,10 +40,12 @@ func NewHandler(config Config) (*Handler, error) {
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 {
if config.StoreComposer.UsesTerminater && !config.DisableTermination {
mux.Del(":id", http.HandlerFunc(handler.DelFile))
}

View File

@ -162,8 +162,17 @@ func (handler *UnroutedHandler) Middleware(h http.Handler) http.Handler {
header.Set("Access-Control-Allow-Origin", origin)
if r.Method == "OPTIONS" {
allowedMethods := "POST, HEAD, PATCH, OPTIONS"
if !handler.config.DisableDownload {
allowedMethods += ", GET"
}
if !handler.config.DisableTermination {
allowedMethods += ", DELETE"
}
// Preflight request
header.Add("Access-Control-Allow-Methods", "POST, GET, HEAD, PATCH, DELETE, OPTIONS")
header.Add("Access-Control-Allow-Methods", allowedMethods)
header.Add("Access-Control-Allow-Headers", "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")
header.Set("Access-Control-Max-Age", "86400")