diff --git a/weed/s3api/s3api_object_handlers.go b/weed/s3api/s3api_object_handlers.go index 6a209485f..2a401ac29 100644 --- a/weed/s3api/s3api_object_handlers.go +++ b/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) } diff --git a/weed/util/http/http_global_client_util.go b/weed/util/http/http_global_client_util.go index a374c8a2b..612d538ec 100644 --- a/weed/util/http/http_global_client_util.go +++ b/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")