Skip to content

Properly response to HEAD with Content-Length on compressed content#1040

Merged
jviotti merged 2 commits into
mainfrom
gzip-header
Jun 9, 2026
Merged

Properly response to HEAD with Content-Length on compressed content#1040
jviotti merged 2 commits into
mainfrom
gzip-header

Conversation

@jviotti

@jviotti jviotti commented Jun 9, 2026

Copy link
Copy Markdown
Member

Signed-off-by: Juan Cruz Viotti jv@jviotti.com

Signed-off-by: Juan Cruz Viotti <jv@jviotti.com>
@augmentcode

augmentcode Bot commented Jun 9, 2026

Copy link
Copy Markdown
🤖 Augment PR Summary

Summary: Improves compliance for HEAD responses when clients request gzip, ensuring the reported Content-Length matches what a corresponding GET would send.

Changes:

  • Adds compressed_bytes to the metapack header/info and bumps the metapack format version to v2.
  • Computes and persists the gzip-compressed size at index time (even for identity-stored artifacts).
  • Plumbs an optional precomputed compressed size into send_response/HTTPResponse::send so HEAD can be answered without recompressing.
  • Updates artifact serving to pass the precomputed size and adds unit/e2e coverage for GET vs HEAD Content-Length under gzip.

🤖 Was this summary useful? React with 👍 or 👎

@augmentcode augmentcode Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Review completed. 1 suggestion posted.

Fix All in Augment

Comment augment review to trigger a new review at any time.

// precomputed compressed size, so we can answer HEAD without
// running the compressor just to discard its output.
if (method == "head" && precomputed_compressed_size.has_value()) {
this->response_->endWithoutBody(precomputed_compressed_size);

@augmentcode augmentcode Bot Jun 9, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

endWithoutBody is called with precomputed_compressed_size (an std::optional<std::size_t>), which likely needs the contained numeric value; otherwise this risks a compile error and/or an incorrect Content-Length for HEAD responses.

Severity: high

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

3 issues found across 7 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/metapack/include/sourcemeta/one/metapack.h">

<violation number="1" location="src/metapack/include/sourcemeta/one/metapack.h:22">
P2: Changing `METAPACK_VERSION` to 2 without a backward-compatible v1 read path makes existing metapack artifacts unreadable at runtime.</violation>
</file>

<file name="src/metapack/metapack.cc">

<violation number="1" location="src/metapack/metapack.cc:82">
P2: Identity metapack writes now hard-depend on gzip success, introducing a new failure path for non-gzip storage.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic


static constexpr std::uint32_t METAPACK_MAGIC{0x4154454D};
static constexpr std::uint16_t METAPACK_VERSION{1};
static constexpr std::uint16_t METAPACK_VERSION{2};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: Changing METAPACK_VERSION to 2 without a backward-compatible v1 read path makes existing metapack artifacts unreadable at runtime.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/metapack/include/sourcemeta/one/metapack.h, line 22:

<comment>Changing `METAPACK_VERSION` to 2 without a backward-compatible v1 read path makes existing metapack artifacts unreadable at runtime.</comment>

<file context>
@@ -19,7 +19,7 @@
 
 static constexpr std::uint32_t METAPACK_MAGIC{0x4154454D};
-static constexpr std::uint16_t METAPACK_VERSION{1};
+static constexpr std::uint16_t METAPACK_VERSION{2};
 static constexpr std::uint64_t METAPACK_MAX_DECOMPRESSION_RATIO{1024};
 
</file context>

Comment thread src/router/artifact.cc
Comment thread src/metapack/metapack.cc
// so a future codec swap is mechanical). For compressed-storage
// artifacts the bytes are also what we write to disk; for Identity
// storage only the size is kept.
const auto compressed{sourcemeta::core::gzip(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: Identity metapack writes now hard-depend on gzip success, introducing a new failure path for non-gzip storage.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/metapack/metapack.cc, line 82:

<comment>Identity metapack writes now hard-depend on gzip success, introducing a new failure path for non-gzip storage.</comment>

<file context>
@@ -72,14 +74,18 @@ static auto write_metapack(const std::filesystem::path &destination,
+  // so a future codec swap is mechanical). For compressed-storage
+  // artifacts the bytes are also what we write to disk; for Identity
+  // storage only the size is kept.
+  const auto compressed{sourcemeta::core::gzip(
+      reinterpret_cast<const std::uint8_t *>(content.data()), content.size())};
   sourcemeta::core::write_file(destination, [&](std::ostream &output) {
</file context>

@github-actions github-actions 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.

Benchmark Index (community)

Details
Benchmark suite Current: e44ae13 Previous: de8d2e4 Ratio
Add one schema (0 existing) 343 ms 391 ms 0.88
Add one schema (100 existing) 22 ms 30 ms 0.73
Add one schema (1000 existing) 74 ms 90 ms 0.82
Add one schema (10000 existing) 570 ms 734 ms 0.78
Update one schema (1 existing) 16 ms 22 ms 0.73
Update one schema (101 existing) 23 ms 29 ms 0.79
Update one schema (1001 existing) 68 ms 91 ms 0.75
Update one schema (10001 existing) 604 ms 747 ms 0.81
Cached rebuild (1 existing) 4 ms 7 ms 0.57
Cached rebuild (101 existing) 5 ms 9 ms 0.56
Cached rebuild (1001 existing) 26 ms 31 ms 0.84
Cached rebuild (10001 existing) 161 ms 274 ms 0.59
Index 100 schemas 560 ms 556 ms 1.01
Index 1000 schemas 1299 ms 1574 ms 0.83
Index 10000 schemas 11627 ms 13551 ms 0.86
Index 10000 schemas (custom meta-schema) 136684 ms 133514 ms 1.02

This comment was automatically generated by workflow using github-action-benchmark.

@github-actions github-actions 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.

Benchmark Index (enterprise)

Details
Benchmark suite Current: e44ae13 Previous: de8d2e4 Ratio
Add one schema (0 existing) 377 ms 291 ms 1.30
Add one schema (100 existing) 33 ms 26 ms 1.27
Add one schema (1000 existing) 94 ms 82 ms 1.15
Add one schema (10000 existing) 878 ms 795 ms 1.10
Update one schema (1 existing) 25 ms 19 ms 1.32
Update one schema (101 existing) 31 ms 25 ms 1.24
Update one schema (1001 existing) 95 ms 90 ms 1.06
Update one schema (10001 existing) 789 ms 837 ms 0.94
Cached rebuild (1 existing) 8 ms 6 ms 1.33
Cached rebuild (101 existing) 10 ms 8 ms 1.25
Cached rebuild (1001 existing) 33 ms 27 ms 1.22
Cached rebuild (10001 existing) 279 ms 220 ms 1.27
Index 100 schemas 576 ms 571 ms 1.01
Index 1000 schemas 1562 ms 1306 ms 1.20
Index 10000 schemas 13657 ms 12746 ms 1.07
Index 10000 schemas (custom meta-schema) 131797 ms 104594 ms 1.26

This comment was automatically generated by workflow using github-action-benchmark.

Signed-off-by: Juan Cruz Viotti <jv@jviotti.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

1 issue found across 3 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/router/artifact.cc">

<violation number="1" location="src/router/artifact.cc:347">
P2: Debug-only `assert()` guards a narrowing `static_cast`—the check disappears in release builds, so a >4 GB `compressed_bytes` on a 32-bit build would silently truncate and produce an incorrect `Content-Length` in HEAD responses.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread src/router/artifact.cc
// and the assert guards a hypothetical 32-bit build from truncating
// a >4 GB artifact (which the indexer would never produce on
// realistic schema inputs).
assert(info->compressed_bytes <= std::numeric_limits<std::size_t>::max());

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: Debug-only assert() guards a narrowing static_cast—the check disappears in release builds, so a >4 GB compressed_bytes on a 32-bit build would silently truncate and produce an incorrect Content-Length in HEAD responses.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/router/artifact.cc, line 347:

<comment>Debug-only `assert()` guards a narrowing `static_cast`—the check disappears in release builds, so a >4 GB `compressed_bytes` on a 32-bit build would silently truncate and produce an incorrect `Content-Length` in HEAD responses.</comment>

<file context>
@@ -336,6 +338,13 @@ auto RouterAction::artifact_serve(
+    // and the assert guards a hypothetical 32-bit build from truncating
+    // a >4 GB artifact (which the indexer would never produce on
+    // realistic schema inputs).
+    assert(info->compressed_bytes <= std::numeric_limits<std::size_t>::max());
     sourcemeta::one::send_response(
         status, request, response, contents,
</file context>

@jviotti jviotti merged commit 04906ca into main Jun 9, 2026
5 checks passed
@jviotti jviotti deleted the gzip-header branch June 9, 2026 14:16
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