Skip to content

fix(core): fix Content-Length mismatch in resumable upload initiation#25888

Open
buzz-watcha wants to merge 1 commit intogoogleapis:mainfrom
buzz-watcha:fix/resumable-upload-content-length
Open

fix(core): fix Content-Length mismatch in resumable upload initiation#25888
buzz-watcha wants to merge 1 commit intogoogleapis:mainfrom
buzz-watcha:fix/resumable-upload-content-length

Conversation

@buzz-watcha
Copy link

Summary

initiate_resumable_upload sets the Content-Length header to upload_io.size (the total file size), but the actual request body is only small JSON metadata.

This worked with the previous HTTPClient backend because HTTPClient auto-recalculates Content-Length from the actual body size. However, after the Faraday migration (#23524), HTTP adapters like Excon respect user-set headers as-is, causing a deadlock:

  1. Client sends POST with Content-Length: <file size> but body is only small JSON metadata
  2. Server waits for the remaining data to match Content-Length
  3. Client waits for the server's response
  4. Both sides wait indefinitely until read_timeout is reached

This issue does not reproduce with faraday-net_http (the default adapter) because Net::HTTP also auto-recalculates Content-Length, but it affects any adapter that respects user-set headers (e.g. faraday-excon).

Fix

  • Remove the incorrect Content-Length header from the initiate request (let the HTTP adapter compute it from the body)
  • Use X-Upload-Content-Length to communicate the total file size, per the resumable upload protocol

Test plan

  • Existing storage_upload_spec.rb tests pass (32 examples, 0 failures)
  • Added test to verify X-Upload-Content-Length header is sent in initiate request
  • Verified fix resolves the timeout issue in production with google-cloud-storage 1.58.0 + google-apis-core 1.0.2 + faraday-excon 2.1.0

The `initiate_resumable_upload` method was setting the `Content-Length`
header to `upload_io.size` (the total file size), but the request body
only contains small JSON metadata. This worked with the previous
HTTPClient backend because HTTPClient auto-recalculates `Content-Length`
from the actual body. However, after the Faraday migration (googleapis#23524),
HTTP adapters like Excon respect user-set headers as-is, causing a
deadlock: the server waits for more data matching the large
`Content-Length`, while the client waits for a response.

Fix: Remove the incorrect `Content-Length` header (let the HTTP adapter
compute it from the body) and use `X-Upload-Content-Length` to
communicate the total file size per the resumable upload protocol.

ref: https://cloud.google.com/storage/docs/performing-resumable-uploads
@buzz-watcha buzz-watcha requested a review from a team as a code owner March 9, 2026 09:37
@google-cla
Copy link

google-cla bot commented Mar 9, 2026

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant