Repository navigation
Intern known request header names to static AsciiString - #2206
Merged
Merged
Conversation
hyperxpro
pushed a commit
that referenced
this pull request
Nov 6, 2026
## Summary `NettyRequestFactory` set three header values in forms that cost work on every HTTP/1.1 request: - A body-less POST, PUT or PATCH set `Content-Length` from an `Integer` constant. Netty's header value converter calls `toString()`, so every such request allocated a new `"0"` string. - A body's length was boxed into a `Long` before the same conversion. - The configured `User-Agent` is a `String`, so `HttpHeadersEncoder.writeAscii` encoded it char by char through `setCharSequence` instead of the `AsciiString` bulk copy. `HeaderEncodeBenchmark` measured the same effect for header names (#2206). Changes: - `Content-Length: 0` uses `HttpHeaderValues.ZERO`. - A body length that fits in an `int` goes through `setInt`. `HttpHeaders` has no `setLong`, so larger lengths keep the old path. - The `User-Agent` is converted to an `AsciiString` once, in the constructor. ## Behaviour notes - The bytes on the wire are unchanged for all three headers. `AsciiString` maps chars the same way the `String` encode path does: Latin-1 chars become their own byte, and anything above U+00FF becomes `?`. A test pins this. - The validating request headers still reject an illegal `User-Agent` (CR, LF and the like) with the same `IllegalArgumentException`, on every request, as before. - The `User-Agent` is now read from the config once per factory, the way the cookie encoder already is. Only a custom `AsyncHttpClientConfig` whose `getUserAgent()` changes between calls would notice. `DefaultAsyncHttpClientConfig` is immutable. - No public API change: Revapi passes. ## Benchmark New `RequestHeaderValueBenchmark`: builds and encodes a request with the factory's own headers, with each value in its old and new form. JMH 1.37, JDK 17.0.2, Windows, `-prof gc`, 3 forks, 5 x 1 s warmup, 10 x 1 s measurement. | Case | Before | After | | --- | ---: | ---: | | Body-less POST, `Content-Length: 0` | 817.5 B/op | 769.5 B/op | | POST with a body, `Content-Length: 1536` | 841.5 B/op | 817.5 B/op | | Writing the `User-Agent` value (`writeAscii` branch only) | 21.3 ± 6.2 ns/op | 11.1 ± 2.6 ns/op | The host was 75-92% busy, so whole-request times (~800 ns/op) stayed within noise. Allocation is the reliable figure. The `User-Agent` gain was measured on the encoder's two branches in isolation. Neither branch allocates. ## Tests `NettyRequestFactoryHeaderValuesTest` encodes the factory's requests with `HttpRequestEncoder` and checks: - the default `User-Agent` is sent as configured; - a non-ASCII `User-Agent` produces the same bytes as the `String` value did; - a `User-Agent` with CRLF is still rejected on every request; - a caller-set `User-Agent` wins over the configured one; - `Content-Length: 0` is sent for body-less POST/PUT/PATCH, and no `Content-Length` for GET; - body lengths, including one above `Integer.MAX_VALUE`. ## Verification - `mvnw.cmd -B -ntp clean verify -Dgpg.skip=true` on JDK 17.0.2 (Windows): 1,919 tests, 0 failures, 0 errors, 35 skipped. Error Prone, NullAway and Revapi passed. Local verify ran on JDK 17 because there is no JDK 11 on this machine; CI covers JDK 11. `-Dgpg.skip` is set only because gpg is not installed here. - `RedirectConnectionUsageTest` timed out once under load. It fails the same way on unchanged `main` and passed in the next runs. Claude Code on behalf of Pavel Ptashyts 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation:
When building the Netty request, well-known header names were passed as plain Strings, so Netty re-hashed and re-encoded them on every request instead of reusing cached AsciiString constants.
Modification:
Intern recognised header names to static AsciiString constants, applied only on a byte-identical lowercase match so the bytes put on the wire are unchanged.
Result:
Faster header encoding on the request path (it hits Netty's bulk-copy branch), with on-wire output unchanged.