Skip to content

Add ETag and If-None-Match support to GET /node/ledger-chunk/{chunk_name}#7653

Merged
achamayou merged 22 commits intomainfrom
copilot/add-sha-support
Feb 13, 2026
Merged

Add ETag and If-None-Match support to GET /node/ledger-chunk/{chunk_name}#7653
achamayou merged 22 commits intomainfrom
copilot/add-sha-support

Conversation

Copy link
Contributor

Copilot AI commented Feb 9, 2026

  • Update include/ccf/http_consts.h: Add ETAG and IF_NONE_MATCH header constants
  • Update include/ccf/http_etag.h: Widen Matcher regex to accept algorithm:digest ETag format; add dedicated MatcherError exception
  • Update src/node/rpc/file_serving_handlers.h: Move ETag/If-None-Match logic into fill_range_response_from_file() for GET+HEAD support
  • Update src/http/test/http_etag_test.cpp: Add test cases for the new ETag format with algorithm:digest values; use MatcherError
  • Update samples/apps/logging/logging.cpp: Catch MatcherError at all Matcher calling sites
  • Update CHANGELOG.md: Add changelog entry for ETag/If-None-Match support
  • Update doc/operations/ledger_snapshot.rst: Document the new ETag/If-None-Match feature with mermaid diagrams
  • Extend test_ledger_chunk_access e2e test with ETag/If-None-Match checks for GET and HEAD

Closes #7652


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Closes #7652

- Compute sha-256 ETag over served content (respects Range header)
- Support sha-256, sha-384, sha-512 algorithms in If-None-Match
- Return 304 Not Modified when ETag matches
- Widen Matcher regex to accept algorithm:digest format
- Add ETAG and IF_NONE_MATCH header constants
- Add unit tests for new ETag format
- Update CHANGELOG and documentation

Co-authored-by: achamayou <4016369+achamayou@users.noreply.github.com>
Copilot AI changed the title [WIP] Add support for sha-256, sha-384, and sha-512 algorithms Add ETag and If-None-Match support to GET /node/ledger-chunk/{chunk_name} Feb 9, 2026
Copilot AI requested a review from achamayou February 9, 2026 15:08
@achamayou
Copy link
Member

@copilot rebase on the latest main, and make any adjustments necessary please.

- Resolve merge conflicts with Want-Repr-Digest (#7651) changes
- Fix ETag check to handle both HTTP 200 OK (no Range) and
  206 Partial Content (with Range) from fill_range_response_from_file()
- Merge CHANGELOG, docs, and header constant additions

Co-authored-by: achamayou <4016369+achamayou@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Feb 10, 2026

@copilot rebase on the latest main, and make any adjustments necessary please.

Merged latest main (including #7651 Want-Repr-Digest changes) and adapted the ETag code to handle both HTTP_STATUS_OK (200, GET without Range) and HTTP_STATUS_PARTIAL_CONTENT (206, GET with Range) from the updated fill_range_response_from_file(). All merge conflicts resolved, tests pass. See 019e5b4.

@achamayou
Copy link
Member

achamayou commented Feb 11, 2026

@copilot rebase on the latest main, and make any adjustments necessary please.

@achamayou
Copy link
Member

@copilot extend the test called test_ledger_chunk_access in e2e_operations to:

  1. Check that a normal GET returns a correctly formed ETag, with the expected prefix and digest value
  2. Check that a GET with If-None-Match set in the headers to a value that does not match the ETag results in a fresh download
  3. Check that a GET with If-None-Match set in the headers to the previously fetched ETag value returns a 304 Not Modified
  4. Check the same functionality, but on sub-Range of the file

achamayou and others added 2 commits February 12, 2026 14:15
Extends test_ledger_chunk_access in e2e_operations.py with:
1. Normal GET returns correctly formed ETag with sha-256:<hex> format
2. Non-matching If-None-Match returns 200 OK with full body
3. Matching If-None-Match returns 304 Not Modified
4. Same checks on a sub-Range of the file (206 / 304)

Co-authored-by: achamayou <4016369+achamayou@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Feb 12, 2026

@copilot rebase on the latest main, and make any adjustments necessary please.

Branch is already up to date with latest main. The Hashhash rename from #7660 was fixed in a056007.

@copilot extend the test called test_ledger_chunk_access in e2e_operations to:

  1. Check that a normal GET returns a correctly formed ETag, with the expected prefix and digest value
    ...

Extended test_ledger_chunk_access with all four checks in 130c50b:

  1. Normal GET verifies ETag is present, has "sha-256:<hex>" format, and the digest matches hashlib.sha256 of the file
  2. GET with non-matching If-None-Match ("sha-256:0000000000000000") returns 200 OK with full body
  3. GET with matching If-None-Match (the previously fetched ETag) returns 304 Not Modified
  4. Same three checks on a sub-Range (bytes=0-{half}) — verifies ETag over partial content, 206 for non-match, 304 for match

@achamayou
Copy link
Member

@copilot update the Matcher logic to throw a dedicated exception, which subclasses std::exception, on validation error paths, rather than std::runtime_error. Updated all calling sites to catch and handle that exception.

Add ccf::http::MatcherError (subclassing std::exception) for Matcher
validation errors. Update all calling sites:
- file_serving_handlers.h: catch MatcherError instead of runtime_error
- logging.cpp: add try/catch at all 6 Matcher sites, return 400 Bad Request
- http_etag_test.cpp: expect MatcherError in tests

Co-authored-by: achamayou <4016369+achamayou@users.noreply.github.com>
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds ETag and If-None-Match support to the GET /node/ledger-chunk/{chunk_name} endpoint, implementing the feature requested in issue #7652. The implementation follows RFC 9530 structured field format for ETags, using digest values in the form "sha-256=:<base64_digest>:". The ETag is computed over the actual response content (which may be a partial range), and clients can use If-None-Match with sha-256, sha-384, or sha-512 digests to avoid re-downloading unchanged content.

Changes:

  • Extended the Matcher class in http_etag.h to support RFC 9530 digest format by widening the regex pattern and introducing a dedicated MatcherError exception
  • Added ETag computation and If-None-Match handling to the ledger chunk download endpoint, with support for three digest algorithms
  • Updated all existing Matcher call sites to properly catch the new MatcherError exception
  • Added comprehensive e2e tests covering ETag functionality with both full and range requests
  • Documented the new feature with usage examples and sequence diagrams

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
include/ccf/http_consts.h Added ETAG and IF_NONE_MATCH header constants
include/ccf/http_etag.h Introduced MatcherError exception class and widened regex to accept RFC 9530 format
src/http/test/http_etag_test.cpp Updated exception types and added test cases for RFC 9530 digest ETags
samples/apps/logging/logging.cpp Added MatcherError exception handling at all Matcher call sites
src/node/rpc/file_serving_handlers.h Implemented ETag computation and If-None-Match validation with multi-algorithm support
tests/e2e_operations.py Added comprehensive e2e tests for ETag/If-None-Match with full and range requests
doc/operations/ledger_snapshot.rst Documented ETag/If-None-Match feature with examples and mermaid diagrams
CHANGELOG.md Added changelog entry for the new feature

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Move ETag computation and If-None-Match handling from the
get_ledger_chunk handler into fill_range_response_from_file() so that
both GET and HEAD requests (including Range requests) receive ETag
headers. This also gives snapshots ETag support for free.

HEAD requests now go through the same Range and ETag logic as GET,
but without setting a response body.

Add e2e tests for HEAD with ETag and HEAD with If-None-Match.

Co-authored-by: achamayou <4016369+achamayou@users.noreply.github.com>
…fy header access

- Check body content matches exactly (not just length) for non-matching
  If-None-Match on both full and range requests
- Add sha-512 If-None-Match test for completeness alongside sha-384
- Simplify etag header access to use lowercase "etag" consistently
  (CCF sends it as lowercase per HTTP/2 conventions)
- Check partial body content on non-matching range If-None-Match

Co-authored-by: achamayou <4016369+achamayou@users.noreply.github.com>
Copilot AI requested a review from achamayou February 13, 2026 18:20
@achamayou achamayou merged commit e6ca9ed into main Feb 13, 2026
19 checks passed
@achamayou achamayou deleted the copilot/add-sha-support branch February 13, 2026 18:48
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.

Add support for ETag and If-None-Match to GET /node/ledger-chunk/{chunk_name}

3 participants