From 3a3fff1399d50a7480ba233813d30e767ad14000 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Thu, 26 Mar 2026 14:06:21 -0700 Subject: [PATCH] Fix TUS chunked upload and resume failures (#8783) (#8786) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix TUS chunked upload and resume failures caused by request context cancellation (#8783) The filer's TCP connections use a 10-second inactivity timeout (net_timeout.go). After the TUS PATCH request body is fully consumed, internal operations (assigning file IDs via gRPC to the master, uploading data to volume servers, completing uploads) do not generate any activity on the client connection, so the inactivity timer fires and Go's HTTP server cancels the request context. This caused HTTP 500 errors on PATCH requests where body reading + internal processing exceeded the timeout. Fix by using context.WithoutCancel in TUS create and patch handlers, matching the existing pattern used by assignNewFileInfo. This ensures internal operations complete regardless of client connection state. Fixes seaweedfs/seaweedfs#8783 * Add comment to tusCreateHandler explaining context.WithoutCancel rationale * Run TUS integration tests on all PRs, not just TUS file changes The previous path filter meant these tests only ran when TUS-specific files changed. This allowed regressions from changes to shared infrastructure (net_timeout.go, upload paths, gRPC) to go undetected — which is exactly how the context cancellation bug in #8783 was missed. Matches the pattern used by s3-go-tests.yml. --- .github/workflows/tus-tests.yml | 11 ----------- weed/server/filer_server_tus_handlers.go | 11 +++++++++-- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/.github/workflows/tus-tests.yml b/.github/workflows/tus-tests.yml index 62b393311..2acfa6e1d 100644 --- a/.github/workflows/tus-tests.yml +++ b/.github/workflows/tus-tests.yml @@ -2,17 +2,6 @@ name: "TUS Protocol Tests" on: pull_request: - paths: - - 'weed/server/filer_server_tus*.go' - - 'weed/server/filer_server.go' - - 'test/tus/**' - - '.github/workflows/tus-tests.yml' - push: - branches: [ master, main ] - paths: - - 'weed/server/filer_server_tus*.go' - - 'weed/server/filer_server.go' - - 'test/tus/**' concurrency: group: ${{ github.head_ref || github.ref }}/tus-tests diff --git a/weed/server/filer_server_tus_handlers.go b/weed/server/filer_server_tus_handlers.go index 8e31e32d4..765040182 100644 --- a/weed/server/filer_server_tus_handlers.go +++ b/weed/server/filer_server_tus_handlers.go @@ -80,7 +80,10 @@ func (fs *FilerServer) tusOptionsHandler(w http.ResponseWriter, r *http.Request) // tusCreateHandler handles POST requests to create new uploads func (fs *FilerServer) tusCreateHandler(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() + // Use a context that ignores cancellation from the request context. + // Internal operations (creating TUS session, writing data, completing uploads) + // may exceed the filer's client connection inactivity timeout. + ctx := context.WithoutCancel(r.Context()) // Parse Upload-Length header (required) uploadLengthStr := r.Header.Get("Upload-Length") @@ -195,7 +198,11 @@ func (fs *FilerServer) tusHeadHandler(w http.ResponseWriter, r *http.Request, up // tusPatchHandler handles PATCH requests to upload data func (fs *FilerServer) tusPatchHandler(w http.ResponseWriter, r *http.Request, uploadID string) { - ctx := r.Context() + // Use a context that ignores cancellation from the request context. + // The filer's connection has an inactivity timeout: after the request body is fully read, + // internal operations (assigning file IDs, uploading to volume servers, completing uploads) + // may exceed the timeout, causing the request context to be canceled. + ctx := context.WithoutCancel(r.Context()) // Validate Content-Type contentType := r.Header.Get("Content-Type")