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

This commit is contained in:
Marius 2019-09-09 13:37:53 +02:00
commit 12114b1ae8
7 changed files with 62 additions and 10 deletions

View File

@ -54,7 +54,7 @@ snippet demonstrates how to start a tusd process which accepts tus uploads at
`http://localhost:1080/files/` (notice the trailing slash) and stores them locally in the `./data` directory: `http://localhost:1080/files/` (notice the trailing slash) and stores them locally in the `./data` directory:
``` ```
$ tusd -dir ./data $ tusd -dir=./data
[tusd] Using './data' as directory storage. [tusd] Using './data' as directory storage.
[tusd] Using 0.00MB as maximum size. [tusd] Using 0.00MB as maximum size.
[tusd] Using 0.0.0.0:1080 as address to listen. [tusd] Using 0.0.0.0:1080 as address to listen.
@ -71,7 +71,7 @@ option):
$ export AWS_ACCESS_KEY_ID=xxxxx $ export AWS_ACCESS_KEY_ID=xxxxx
$ export AWS_SECRET_ACCESS_KEY=xxxxx $ export AWS_SECRET_ACCESS_KEY=xxxxx
$ export AWS_REGION=eu-west-1 $ export AWS_REGION=eu-west-1
$ tusd -s3-bucket my-test-bucket.com $ tusd -s3-bucket=my-test-bucket.com
[tusd] Using 's3://my-test-bucket.com' as S3 bucket for storage. [tusd] Using 's3://my-test-bucket.com' as S3 bucket for storage.
[tusd] Using 0.00MB as maximum size. [tusd] Using 0.00MB as maximum size.
[tusd] Using 0.0.0.0:1080 as address to listen. [tusd] Using 0.0.0.0:1080 as address to listen.
@ -86,7 +86,7 @@ enable this feature, supply the path to your account file containing the necessa
``` ```
$ export GCS_SERVICE_ACCOUNT_FILE=./account.json $ export GCS_SERVICE_ACCOUNT_FILE=./account.json
$ tusd -gcs-bucket my-test-bucket.com $ tusd -gcs-bucket=my-test-bucket.com
[tusd] Using 'gcs://my-test-bucket.com' as GCS bucket for storage. [tusd] Using 'gcs://my-test-bucket.com' as GCS bucket for storage.
[tusd] Using 0.00MB as maximum size. [tusd] Using 0.00MB as maximum size.
[tusd] Using 0.0.0.0:1080 as address to listen. [tusd] Using 0.0.0.0:1080 as address to listen.
@ -112,6 +112,8 @@ Usage of tusd:
Use Google Cloud Storage with this bucket as storage backend (requires the GCS_SERVICE_ACCOUNT_FILE environment variable to be set) Use Google Cloud Storage with this bucket as storage backend (requires the GCS_SERVICE_ACCOUNT_FILE environment variable to be set)
-hooks-dir string -hooks-dir string
Directory to search for available hooks scripts Directory to search for available hooks scripts
-hooks-enabled-events string
Comma separated list of enabled hook events (e.g. post-create,post-finish). Leave empty to enable all events
-hooks-http string -hooks-http string
An HTTP endpoint to which hook events will be sent to An HTTP endpoint to which hook events will be sent to
-hooks-http-backoff int -hooks-http-backoff int

View File

@ -4,6 +4,8 @@ import (
"flag" "flag"
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/tus/tusd/cmd/tusd/cli/hooks"
) )
var Flags struct { var Flags struct {
@ -19,12 +21,14 @@ var Flags struct {
S3Endpoint string S3Endpoint string
GCSBucket string GCSBucket string
GCSObjectPrefix string GCSObjectPrefix string
EnabledHooksString string
FileHooksDir string FileHooksDir string
HttpHooksEndpoint string HttpHooksEndpoint string
HttpHooksRetry int HttpHooksRetry int
HttpHooksBackoff int HttpHooksBackoff int
HooksStopUploadCode int HooksStopUploadCode int
PluginHookPath string PluginHookPath string
EnabledHooks []hooks.HookType
ShowVersion bool ShowVersion bool
ExposeMetrics bool ExposeMetrics bool
MetricsPath string MetricsPath string
@ -48,6 +52,7 @@ func ParseFlags() {
flag.StringVar(&Flags.S3Endpoint, "s3-endpoint", "", "Endpoint to use S3 compatible implementations like minio (requires s3-bucket to be pass)") flag.StringVar(&Flags.S3Endpoint, "s3-endpoint", "", "Endpoint to use S3 compatible implementations like minio (requires s3-bucket to be pass)")
flag.StringVar(&Flags.GCSBucket, "gcs-bucket", "", "Use Google Cloud Storage with this bucket as storage backend (requires the GCS_SERVICE_ACCOUNT_FILE environment variable to be set)") flag.StringVar(&Flags.GCSBucket, "gcs-bucket", "", "Use Google Cloud Storage with this bucket as storage backend (requires the GCS_SERVICE_ACCOUNT_FILE environment variable to be set)")
flag.StringVar(&Flags.GCSObjectPrefix, "gcs-object-prefix", "", "Prefix for GCS object names (can't contain underscore character)") flag.StringVar(&Flags.GCSObjectPrefix, "gcs-object-prefix", "", "Prefix for GCS object names (can't contain underscore character)")
flag.StringVar(&Flags.EnabledHooksString, "hooks-enabled-events", "", "Comma separated list of enabled hook events (e.g. post-create,post-finish). Leave empty to enable all events")
flag.StringVar(&Flags.FileHooksDir, "hooks-dir", "", "Directory to search for available hooks scripts") flag.StringVar(&Flags.FileHooksDir, "hooks-dir", "", "Directory to search for available hooks scripts")
flag.StringVar(&Flags.HttpHooksEndpoint, "hooks-http", "", "An HTTP endpoint to which hook events will be sent to") flag.StringVar(&Flags.HttpHooksEndpoint, "hooks-http", "", "An HTTP endpoint to which hook events will be sent to")
flag.IntVar(&Flags.HttpHooksRetry, "hooks-http-retry", 3, "Number of times to retry on a 500 or network timeout") flag.IntVar(&Flags.HttpHooksRetry, "hooks-http-retry", 3, "Number of times to retry on a 500 or network timeout")
@ -59,9 +64,10 @@ func ParseFlags() {
flag.StringVar(&Flags.MetricsPath, "metrics-path", "/metrics", "Path under which the metrics endpoint will be accessible") flag.StringVar(&Flags.MetricsPath, "metrics-path", "/metrics", "Path under which the metrics endpoint will be accessible")
flag.BoolVar(&Flags.BehindProxy, "behind-proxy", false, "Respect X-Forwarded-* and similar headers which may be set by proxies") flag.BoolVar(&Flags.BehindProxy, "behind-proxy", false, "Respect X-Forwarded-* and similar headers which may be set by proxies")
flag.BoolVar(&Flags.VerboseOutput, "verbose", true, "Enable verbose logging output") flag.BoolVar(&Flags.VerboseOutput, "verbose", true, "Enable verbose logging output")
flag.Parse() flag.Parse()
SetEnabledHooks()
if Flags.FileHooksDir != "" { if Flags.FileHooksDir != "" {
Flags.FileHooksDir, _ = filepath.Abs(Flags.FileHooksDir) Flags.FileHooksDir, _ = filepath.Abs(Flags.FileHooksDir)
Flags.FileHooksInstalled = true Flags.FileHooksInstalled = true
@ -87,3 +93,30 @@ func ParseFlags() {
"Please remove underscore from the value", Flags.GCSObjectPrefix) "Please remove underscore from the value", Flags.GCSObjectPrefix)
} }
} }
func SetEnabledHooks() {
if Flags.EnabledHooksString != "" {
slc := strings.Split(Flags.EnabledHooksString, ",")
for i, h := range slc {
slc[i] = strings.TrimSpace(h)
if !hookTypeInSlice(hooks.HookType(h), hooks.AvailableHooks) {
stderr.Fatalf("Unknown hook event type in -hooks-enabled-events flag: %s", h)
}
Flags.EnabledHooks = append(Flags.EnabledHooks, hooks.HookType(h))
}
}
if len(Flags.EnabledHooks) == 0 {
Flags.EnabledHooks = hooks.AvailableHooks
}
var enabledHooksString []string
for _, h := range Flags.EnabledHooks {
enabledHooksString = append(enabledHooksString, string(h))
}
stdout.Printf("Enabled hook events: %s", strings.Join(enabledHooksString, ", "))
}

View File

@ -10,6 +10,15 @@ import (
var hookHandler hooks.HookHandler = nil var hookHandler hooks.HookHandler = nil
func hookTypeInSlice(a hooks.HookType, list []hooks.HookType) bool {
for _, b := range list {
if b == a {
return true
}
}
return false
}
type hookDataStore struct { type hookDataStore struct {
handler.DataStore handler.DataStore
} }
@ -62,7 +71,6 @@ func SetupPreHooks(composer *handler.StoreComposer) error {
composer.UseCore(hookDataStore{ composer.UseCore(hookDataStore{
DataStore: composer.Core, DataStore: composer.Core,
}) })
return nil return nil
} }
@ -91,6 +99,10 @@ func invokeHookAsync(typ hooks.HookType, info handler.FileInfo) {
} }
func invokeHookSync(typ hooks.HookType, info handler.FileInfo, captureOutput bool) ([]byte, error) { func invokeHookSync(typ hooks.HookType, info handler.FileInfo, captureOutput bool) ([]byte, error) {
if !hookTypeInSlice(typ, Flags.EnabledHooks) {
return nil, nil
}
switch typ { switch typ {
case hooks.HookPostFinish: case hooks.HookPostFinish:
logEv(stdout, "UploadFinished", "id", info.ID, "size", strconv.FormatInt(info.Size, 10)) logEv(stdout, "UploadFinished", "id", info.ID, "size", strconv.FormatInt(info.Size, 10))

View File

@ -19,6 +19,8 @@ const (
HookPreCreate HookType = "pre-create" HookPreCreate HookType = "pre-create"
) )
var AvailableHooks []HookType = []HookType{HookPreCreate, HookPostCreate, HookPostReceive, HookPostTerminate, HookPostFinish}
type hookDataStore struct { type hookDataStore struct {
handler.DataStore handler.DataStore
} }

View File

@ -45,6 +45,9 @@ This event will be triggered after an upload has been terminated, meaning that t
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 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!
## Whitelisting Hook Events
The `--hooks-enabled-events` option for the tusd binary works as a whitelist for hook events and takes a comma separated list of hook events (for instance: `pre-create,post-create`). This can be useful to limit the number of hook executions and save resources if you are only interested in some events. If the `--hooks-enabled-events` option is omitted, all hook events are enabled.
## File Hooks ## File Hooks
### The Hook Directory ### The Hook Directory

View File

@ -140,8 +140,8 @@ func TestGet(t *testing.T) {
upload.EXPECT().GetInfo().Return(FileInfo{ upload.EXPECT().GetInfo().Return(FileInfo{
Offset: 0, Offset: 0,
MetaData: map[string]string{ MetaData: map[string]string{
"filetype": "text/html", "filetype": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"filename": "invoice.html", "filename": "invoice.docx",
}, },
}, nil), }, nil),
) )
@ -155,8 +155,8 @@ func TestGet(t *testing.T) {
URL: "yes", URL: "yes",
ResHeader: map[string]string{ ResHeader: map[string]string{
"Content-Length": "0", "Content-Length": "0",
"Content-Type": "text/html", "Content-Type": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"Content-Disposition": `attachment;filename="invoice.html"`, "Content-Disposition": `attachment;filename="invoice.docx"`,
}, },
Code: http.StatusNoContent, Code: http.StatusNoContent,
ResBody: "", ResBody: "",

View File

@ -23,7 +23,7 @@ var (
reExtractFileID = regexp.MustCompile(`([^/]+)\/?$`) reExtractFileID = regexp.MustCompile(`([^/]+)\/?$`)
reForwardedHost = regexp.MustCompile(`host=([^,]+)`) reForwardedHost = regexp.MustCompile(`host=([^,]+)`)
reForwardedProto = regexp.MustCompile(`proto=(https?)`) reForwardedProto = regexp.MustCompile(`proto=(https?)`)
reMimeType = regexp.MustCompile(`^[a-z]+\/[a-z\-\+]+$`) reMimeType = regexp.MustCompile(`^[a-z]+\/[a-z\-\+\.]+$`)
) )
// HTTPError represents an error with an additional status code attached // HTTPError represents an error with an additional status code attached