Browse Source

Make TUS base path configurable via CLI

- Add -tus.path CLI flag to filer command
- TUS is disabled by default (empty path)
- Example: -tus.path=/.tus to enable at /.tus endpoint
- Update test Makefile to use -tus.path flag
- Update README with TUS enabling instructions
feature/tus-protocol
chrislu 3 days ago
parent
commit
31214b2874
  1. 3
      test/tus/Makefile
  2. 14
      test/tus/README.md
  3. 3
      weed/command/filer.go
  4. 9
      weed/server/filer_server.go
  5. 22
      weed/server/filer_server_tus_handlers.go

3
test/tus/Makefile

@ -95,12 +95,13 @@ start-seaweedfs: check-binary
> /tmp/seaweedfs-tus-volume.log 2>&1 & > /tmp/seaweedfs-tus-volume.log 2>&1 &
@sleep 3 @sleep 3
# Start filer server
# Start filer server with TUS enabled
@echo "Starting filer server..." @echo "Starting filer server..."
@nohup $(SEAWEEDFS_ROOT)/weed/weed filer \ @nohup $(SEAWEEDFS_ROOT)/weed/weed filer \
-port=$(FILER_PORT) \ -port=$(FILER_PORT) \
-master=127.0.0.1:$(MASTER_PORT) \ -master=127.0.0.1:$(MASTER_PORT) \
-ip=127.0.0.1 \ -ip=127.0.0.1 \
-tus.path=/.tus \
> /tmp/seaweedfs-tus-filer.log 2>&1 & > /tmp/seaweedfs-tus-filer.log 2>&1 &
@sleep 5 @sleep 5

14
test/tus/README.md

@ -40,6 +40,20 @@ TUS is an open protocol for resumable file uploads over HTTP. It allows clients
- `Upload-Offset` - Current byte offset - `Upload-Offset` - Current byte offset
- `Location` - Upload URL (on POST) - `Location` - Upload URL (on POST)
## Enabling TUS
TUS protocol support must be explicitly enabled when starting the filer server using the `-tus.path` flag:
```bash
# Start filer with TUS enabled at /.tus path
weed filer -master=localhost:9333 -tus.path=/.tus
# Or use a custom path
weed filer -master=localhost:9333 -tus.path=/uploads/tus
```
If `-tus.path` is not specified, TUS endpoints are disabled.
## Test Structure ## Test Structure
### Integration Tests ### Integration Tests

3
weed/command/filer.go

@ -74,6 +74,7 @@ type FilerOptions struct {
diskType *string diskType *string
allowedOrigins *string allowedOrigins *string
exposeDirectoryData *bool exposeDirectoryData *bool
tusPath *string
certProvider certprovider.Provider certProvider certprovider.Provider
} }
@ -109,6 +110,7 @@ func init() {
f.diskType = cmdFiler.Flag.String("disk", "", "[hdd|ssd|<tag>] hard drive or solid state drive or any tag") f.diskType = cmdFiler.Flag.String("disk", "", "[hdd|ssd|<tag>] hard drive or solid state drive or any tag")
f.allowedOrigins = cmdFiler.Flag.String("allowedOrigins", "*", "comma separated list of allowed origins") f.allowedOrigins = cmdFiler.Flag.String("allowedOrigins", "*", "comma separated list of allowed origins")
f.exposeDirectoryData = cmdFiler.Flag.Bool("exposeDirectoryData", true, "whether to return directory metadata and content in Filer UI") f.exposeDirectoryData = cmdFiler.Flag.Bool("exposeDirectoryData", true, "whether to return directory metadata and content in Filer UI")
f.tusPath = cmdFiler.Flag.String("tus.path", "", "TUS resumable upload endpoint path, e.g., /.tus (disabled if empty)")
// start s3 on filer // start s3 on filer
filerStartS3 = cmdFiler.Flag.Bool("s3", false, "whether to start S3 gateway") filerStartS3 = cmdFiler.Flag.Bool("s3", false, "whether to start S3 gateway")
@ -334,6 +336,7 @@ func (fo *FilerOptions) startFiler() {
DownloadMaxBytesPs: int64(*fo.downloadMaxMBps) * 1024 * 1024, DownloadMaxBytesPs: int64(*fo.downloadMaxMBps) * 1024 * 1024,
DiskType: *fo.diskType, DiskType: *fo.diskType,
AllowedOrigins: strings.Split(*fo.allowedOrigins, ","), AllowedOrigins: strings.Split(*fo.allowedOrigins, ","),
TusPath: *fo.tusPath,
}) })
if nfs_err != nil { if nfs_err != nil {
glog.Fatalf("Filer startup error: %v", nfs_err) glog.Fatalf("Filer startup error: %v", nfs_err)

9
weed/server/filer_server.go

@ -79,6 +79,7 @@ type FilerOption struct {
DiskType string DiskType string
AllowedOrigins []string AllowedOrigins []string
ExposeDirectoryData bool ExposeDirectoryData bool
TusPath string
} }
type FilerServer struct { type FilerServer struct {
@ -196,7 +197,13 @@ func NewFilerServer(defaultMux, readonlyMux *http.ServeMux, option *FilerOption)
if !option.DisableHttp { if !option.DisableHttp {
defaultMux.HandleFunc("/healthz", requestIDMiddleware(fs.filerHealthzHandler)) defaultMux.HandleFunc("/healthz", requestIDMiddleware(fs.filerHealthzHandler))
// TUS resumable upload protocol handler // TUS resumable upload protocol handler
defaultMux.HandleFunc("/.tus/", fs.filerGuard.WhiteList(requestIDMiddleware(fs.tusHandler)))
if option.TusPath != "" {
tusPath := option.TusPath
if !strings.HasSuffix(tusPath, "/") {
tusPath += "/"
}
defaultMux.HandleFunc(tusPath, fs.filerGuard.WhiteList(requestIDMiddleware(fs.tusHandler)))
}
defaultMux.HandleFunc("/", fs.filerGuard.WhiteList(requestIDMiddleware(fs.filerHandler))) defaultMux.HandleFunc("/", fs.filerGuard.WhiteList(requestIDMiddleware(fs.filerHandler)))
} }
if defaultMux != readonlyMux { if defaultMux != readonlyMux {

22
weed/server/filer_server_tus_handlers.go

@ -35,11 +35,15 @@ func (fs *FilerServer) tusHandler(w http.ResponseWriter, r *http.Request) {
// Route based on method and path // Route based on method and path
path := r.URL.Path path := r.URL.Path
tusPrefix := "/.tus"
tusPrefix := fs.option.TusPath
if tusPrefix == "" {
tusPrefix = "/.tus"
}
// Check if this is an upload location (contains upload ID after /.tus/.uploads/)
if strings.HasPrefix(path, tusPrefix+"/.uploads/") {
uploadID := strings.TrimPrefix(path, tusPrefix+"/.uploads/")
// Check if this is an upload location (contains upload ID after {tusPrefix}/.uploads/)
uploadsPrefix := tusPrefix + "/.uploads/"
if strings.HasPrefix(path, uploadsPrefix) {
uploadID := strings.TrimPrefix(path, uploadsPrefix)
uploadID = strings.Split(uploadID, "/")[0] // Get just the ID, not any trailing path uploadID = strings.Split(uploadID, "/")[0] // Get just the ID, not any trailing path
switch r.Method { switch r.Method {
@ -97,8 +101,14 @@ func (fs *FilerServer) tusCreateHandler(w http.ResponseWriter, r *http.Request)
// Parse Upload-Metadata header (optional) // Parse Upload-Metadata header (optional)
metadata := parseTusMetadata(r.Header.Get("Upload-Metadata")) metadata := parseTusMetadata(r.Header.Get("Upload-Metadata"))
// Get TUS path prefix
tusPrefix := fs.option.TusPath
if tusPrefix == "" {
tusPrefix = "/.tus"
}
// Determine target path from request URL // Determine target path from request URL
targetPath := strings.TrimPrefix(r.URL.Path, "/.tus")
targetPath := strings.TrimPrefix(r.URL.Path, tusPrefix)
if targetPath == "" || targetPath == "/" { if targetPath == "" || targetPath == "/" {
http.Error(w, "Target path required", http.StatusBadRequest) http.Error(w, "Target path required", http.StatusBadRequest)
return return
@ -116,7 +126,7 @@ func (fs *FilerServer) tusCreateHandler(w http.ResponseWriter, r *http.Request)
} }
// Build upload location URL // Build upload location URL
uploadLocation := fmt.Sprintf("/.tus/.uploads/%s", uploadID)
uploadLocation := fmt.Sprintf("%s/.uploads/%s", tusPrefix, uploadID)
// Handle creation-with-upload extension // Handle creation-with-upload extension
if r.ContentLength > 0 && r.Header.Get("Content-Type") == "application/offset+octet-stream" { if r.ContentLength > 0 && r.Header.Get("Content-Type") == "application/offset+octet-stream" {

Loading…
Cancel
Save