Browse Source

fix: cancel volume server requests on client disconnect during S3 downloads (#8373)

* fix: cancel volume server requests on client disconnect during S3 downloads

- Use http.NewRequestWithContext in ReadUrlAsStream so in-flight volume
  server requests are properly aborted when the client disconnects and
  the request context is canceled
- Distinguish context-canceled errors (client disconnect, expected) from
  real server errors in streamFromVolumeServers; log at V(3) instead of
  ERROR to reduce noise from client-side disconnects (e.g. Nginx upstream
  timeout, browser cancel, curl --max-time)

Fixes: streamFromVolumeServers: streamFn failed...context canceled"

* fixup: separate Canceled/DeadlineExceeded log severity in streamFromVolumeServers

- context.Canceled → V(3) Infof "client disconnected" (expected, no noise)
- context.DeadlineExceeded → Warningf "server-side deadline exceeded" (unexpected, needs attention)
- all other errors → Errorf (unchanged)"
bucket-config-enhancements
Chris Lu 1 day ago
committed by GitHub
parent
commit
38e14a867b
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 11
      weed/s3api/s3api_object_handlers.go
  2. 4
      weed/util/http/http_global_client_util.go

11
weed/s3api/s3api_object_handlers.go

@ -1093,7 +1093,16 @@ func (s3a *S3ApiServer) streamFromVolumeServers(w http.ResponseWriter, r *http.R
BucketTrafficSent(cw.written, r)
}
if err != nil {
glog.Errorf("streamFromVolumeServers: streamFn failed after writing %d bytes: %v", cw.written, err)
switch {
case errors.Is(err, context.Canceled):
// Client disconnected mid-stream (e.g. Nginx upstream timeout, browser cancel) - expected
glog.V(3).Infof("streamFromVolumeServers: client disconnected after writing %d bytes: %v", cw.written, err)
case errors.Is(err, context.DeadlineExceeded):
// Server-side deadline exceeded - unexpected, warrants operator attention
glog.Warningf("streamFromVolumeServers: server-side deadline exceeded after writing %d bytes: %v", cw.written, err)
default:
glog.Errorf("streamFromVolumeServers: streamFn failed after writing %d bytes: %v", cw.written, err)
}
// Streaming error after WriteHeader was called - response already partially written
return newStreamErrorWithResponse(err)
}

4
weed/util/http/http_global_client_util.go

@ -311,11 +311,11 @@ func ReadUrlAsStream(ctx context.Context, fileUrl, jwt string, cipherKey []byte,
return readEncryptedUrl(ctx, fileUrl, jwt, cipherKey, isContentGzipped, isFullChunk, offset, size, fn)
}
req, err := http.NewRequest(http.MethodGet, fileUrl, nil)
maybeAddAuth(req, jwt)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, fileUrl, nil)
if err != nil {
return false, err
}
maybeAddAuth(req, jwt)
if isFullChunk {
req.Header.Add("Accept-Encoding", "gzip")

Loading…
Cancel
Save