Skip to content

fix(storage): validate CRC32C on HTTP downloads that were not decompressed - #14805

Open
Dhriti07 wants to merge 6 commits into
googleapis:mainfrom
Dhriti07:fix/storage-http-crc32c-validation
Open

Dhriti07 wants to merge 6 commits into
googleapis:mainfrom
Dhriti07:fix/storage-http-crc32c-validation

Conversation

@Dhriti07

@Dhriti07 Dhriti07 commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

GCS returns x-goog-stored-content-encoding and x-goog-stored-content-length on every object download, including objects that are not compressed:

HttpStorageRpcHasherHelper.isTranscoded() treated the presence of either header as a sign of decompressive transcoding. So shouldValidate() always returned false, and CRC32C validation was skipped for all HTTP reads (readAllBytes, downloadTo, reader).

In this pr we fix it by checking only the following:

  • the object is stored gzip-compressed (x-goog-stored-content-encoding: gzip) and was served decompressed (no Content-Encoding: gzip), or
  • the HTTP client decompressed the response (existing isDecompressedByClient).

Reference: Transcoding of gzip-compressed files. GCS only decompresses objects stored gzip-compressed with Content-Encoding: gzip.

Once validation is enabled, a reader() download that is resumed after a broken stream would fail incorrectly:

Related fix: case-insensitive Content-Encoding in isDecompressedByClient

GCS echoes an object's Content-Encoding metadata as-is, so an object uploaded with Content-Encoding: GZIP is served with Content-Encoding: GZIP. The HTTP client decompresses when the value matches gzip/x-gzip case-insensitively, but isDecompressedByClient used a case-sensitive contains("gzip"). With validation now running, such an object read with shouldReturnRawInputStream(false) reported a false ChecksumMismatchException (verified against GCS). Both checks now share isGzip(), which trims and lowercases the value.

…essed

GCS returns x-goog-stored-content-encoding and x-goog-stored-content-length on
every object download, but HttpStorageRpcHasherHelper treated the presence of
either header as transcoding, so CRC32C validation was skipped for all HTTP
reads (readAllBytes, downloadTo, reader).

Only skip validation when the object is stored gzip-compressed and was served
decompressed, or when the HTTP client decompressed it.

Fixes googleapis#14773
@Dhriti07
Dhriti07 requested review from a team as code owners October 28, 2026 09:38

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors the transcoding detection logic in HttpStorageRpcHasherHelper to prevent skipping checksum validation on normal downloads. Previously, the presence of x-goog-stored-content-encoding or x-goog-stored-content-length headers (which GCS returns on all downloads) incorrectly flagged responses as transcoded. The updated logic specifically checks if the object was stored as gzip but served decompressed. Corresponding unit tests have been added and updated to ensure proper checksum validation behavior. I have no further feedback to provide as the changes are well-implemented and covered by tests.

Dhriti Chopra and others added 5 commits October 28, 2026 09:49
…) download

Now that CRC32C validation runs on HTTP downloads, a reader() download that is
resumed after a broken stream kept the hashing stream from the first (full
object) response. The resumed response is partial and is not validated, so at
EOF the hash of only the first part was compared with the full object checksum,
reporting a false ChecksumMismatchException.

Clear the hashing stream when the current response is not validated.
…g client decompression

GCS echoes an object's Content-Encoding metadata as-is (e.g. "GZIP"), and the
HTTP client decompresses when the value matches gzip case-insensitively.
isDecompressedByClient used a case-sensitive check, so such responses were
validated against the stored object's checksum after being decompressed,
reporting a false ChecksumMismatchException.
@Dhriti07
Dhriti07 requested a review from ShreyasSinha October 29, 2026 07:07

This branch has not been deployed

No deployments
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