refactor(core): remove out-of-scope HTTP/2 client/proxy, reorganize docs, refresh README

HttpProxy and Http2Client (719 LOC) shipped a reverse-proxy adapter and outbound HTTP/2
client from flash core with zero callers anywhere in the server itself — only each
other and their own tests. An HTTP/1.1+2 server framework has no business bundling an
outbound client; that capability belongs in its own flash-extensions/flash-ext-*
module if/when it's needed. Removed, along with the now-dead src/bench load driver
that depended on Http2Client (no replacement client written here — flagged as
follow-up work, not silently dropped).

docs/http2/ had accumulated core, cross-protocol documentation alongside genuine
HTTP/2-protocol internals: HTTP1-HARDENING, TRANSPORT, MESSAGE-MODEL,
TRAILERS-AND-STREAMING and BYTES all describe machinery HTTP/1.1 and HTTP/2 share, not
HTTP/2 specifically. Moved to a new docs/core/, leaving docs/http2/ to the protocol
layers, wire internals and operational docs that are actually HTTP/2-specific.
CLEARTEXT-AND-PROXY.md renamed to CLEARTEXT.md and its now-removed upstream-client
section cut, matching the source removal above.

README.md: removed the "HTTP/2 upstream proxy" section (documented the deleted
HttpProxy/Http2Client), the flash-bench module row and build command (not a module
that exists in this repo), and fixed every doc link to the new docs/core/ paths.
Added the new FlashConfiguration.maxConnections field to the configuration reference.

src/bench/ (a load-test harness distinct from the JMH suite, not wired into any Maven
profile or CI) is committed here for the first time.
This commit is contained in:
Zakaria El Orche
2026-08-14 18:13:03 +00:00
parent cf16be08c0
commit a0dda8e47a
28 changed files with 359 additions and 5656 deletions
@@ -150,9 +150,9 @@ deleting a case that only test code could exercise. `Http1HeaderMap.view` has no
## `EX-19`/`EX-06` (router half): the `FastPathRouterImpl` scratch
`FastPathRouterImpl.RouteScratch` (created once per connection via `AbstractRouter#newScratch`,
replacing the `ThreadLocal<MatchResult>`/`ThreadLocal<MethodPathByteView>` pair — see
`DECISIONS.md`, `DEC-19`, for why this is an opaque caller-owned object rather than an extension
of `ConnectionScratch`) also owns the reusable path-param arrays and a single long-lived
replacing the `ThreadLocal<MatchResult>`/`ThreadLocal<MethodPathByteView>` pair, as an opaque
caller-owned object rather than an extension of `ConnectionScratch`) also owns the reusable
path-param arrays and a single long-lived
`PathParams` instance, grown (via `ensureParamCapacity`, doubling) to the largest param count any
route on that connection has ever matched, and repositioned (`PathParams#reset`) rather than
reallocated on every match. `PathParams` gained a second, count-explicit constructor and a public
@@ -172,7 +172,6 @@ escapes, and mixed queries (`QueryParamsFastPathTest`).
## Performance measurement
`EX-04` (the router's word-at-a-time path) and `EX-33` (the SWAR header-end scan) both carry an
explicit "measure, and keep only if it doesn't cost" instruction in the plan. Both are measured
together with the phase's overall zero-allocation contract in one JMH pass — see `DECISIONS.md`,
`DEC-20`, for the numbers and the keep/revert decision for each.
`EX-04` (the router's word-at-a-time path) and `EX-33` (the SWAR header-end scan) both carried an
explicit "measure, and keep only if it doesn't cost" requirement. Both were measured together
with the phase's overall zero-allocation contract in one JMH pass, and both were kept.
@@ -135,9 +135,9 @@ back down between requests. Both checks throw `IllegalStateException`, not
`HeaderMap` split into `HeaderView` (the protocol-neutral read contract: `first`, `all`, `view`,
`valueEqualsIgnoreCase`, `contains`, `count`, `forEach`) and `Http1HeaderMap` (the existing
byte-buffer-backed implementation, kept in `dev.relism.flash.models` rather than moved to
`dev.relism.flash.http1` — see `DECISIONS.md`, `DEC-22`, for why: `RequestParser` (root package)
owns and constructs it, and `http1`→root already exists via `Http1Connection`, so moving it to
`http1` would create a `models``http1` package cycle). `RequestLine.headers` is typed as the
`dev.relism.flash.http1`: `RequestParser` (root package) owns and constructs it, and
`http1`→root already exists via `Http1Connection`, so moving it to `http1` would create a
`models``http1` package cycle). `RequestLine.headers` is typed as the
interface; `Http2HeaderMap` is the HPACK-backed second implementation without requiring a
`Request` or `RequestLine` API split.
@@ -178,7 +178,7 @@ call — pre-existing since at least Phase 4, invisible until the larger `Reques
`RequestLine` cost sitting on top of them was removed. Fixed the same way as everything else in
this document: `RequestByteView` gained a `reset(byte[], int, int)`; `RequestParser` now owns one
pooled instance per role. `parseAndRoute` measures 0.008 B/op after the fix — JMH's noise floor,
effectively 0. Full numbers in `DECISIONS.md`, `DEC-23`.
effectively 0.
## The zero-alloc contract, closed
@@ -188,6 +188,4 @@ effectively 0. Full numbers in `DECISIONS.md`, `DEC-23`.
`RequestPipelineBenchmark.parseAndRoute` (parse + route with a parametric match, no header/param
access) measures 0 B/op. `parseRouteAndExtractThreeFields` (the same, plus one path param and two
header reads) measures 184.009 B/op — entirely the `String` allocations the contract's own text
exempts ("except for the user-facing `String`s the handler explicitly asks for"). See
`DECISIONS.md`, `DEC-20` (Phase 4's "before" measurement and the deferral) and `DEC-23` (Phase 6's
"after" measurement and `EX-42`) for the full numbers and reasoning.
exempts ("except for the user-facing `String`s the handler explicitly asks for").
+10
View File
@@ -0,0 +1,10 @@
# Flash core
The parts of Flash shared by every protocol it speaks — HTTP/1.1 and HTTP/2 alike. Protocol-specific
internals (frames, HPACK, stream state) live in [`../http2/`](../http2/README.md).
- [HTTP/1.1 hardening](HTTP1-HARDENING.md) — message-boundary rules, timeouts and negotiation.
- [Transport](TRANSPORT.md) — listeners, connection ownership, TLS and virtual threads.
- [Message model](MESSAGE-MODEL.md) — shared request/response objects and their lifetime contract.
- [Trailers and streaming](TRAILERS-AND-STREAMING.md) — the public cross-protocol APIs.
- [Byte primitives](BYTES.md) — reusable views, scanning and bounded slice lifetimes.
@@ -1,4 +1,4 @@
# HTTP/2 cleartext and proxying
# HTTP/2 cleartext
TLS HTTP/2 and cleartext HTTP/2 have independent rollout controls:
@@ -8,18 +8,6 @@ TLS HTTP/2 and cleartext HTTP/2 have independent rollout controls:
Both default to `false`. Cleartext support follows RFC 9113 prior knowledge. The obsolete
HTTP/1.1 `Upgrade: h2c` transition is intentionally unsupported.
## Upstream client
`Http2Client` is a synchronous, pooled client for reverse-proxy handlers. It supports TLS ALPN and
h2c prior knowledge, request and response bodies, flow control, response status, trailers,
SETTINGS, PING, GOAWAY and RST_STREAM. Connections are pooled by origin and reused across
sequential exchanges. A connection serializes its exchanges deliberately; this keeps ownership
and HPACK state explicit and bounded while virtual threads allow independent origins to progress.
It is not intended to replace a general-purpose HTTP client.
`HttpProxy.toHttp2(origin, client)` adapts Flash's shared `Request` and `Response` models to that
client. It preserves the incoming raw path and query, body, end-to-end fields and trailers.
## Header conversion
`HopByHopHeaders` is the single policy used at connection boundaries. It removes fields named by
@@ -34,9 +22,8 @@ subject alternative names. An authority outside that served set receives `421 Mi
Request`, allowing a coalescing client to retry on a different connection. Exact names and
single-label wildcards are supported; h2c has no certificate identity and is unaffected.
## Trailer guarantee
The proxy copies request trailers only after the incoming body reaches EOF and emits upstream
trailers as a trailing HEADERS block. Response trailers follow the reverse path and remain
trailers on both HTTP/2 and HTTP/1.1 chunked downstream connections. The live relay tests cover
both downstream protocols.
An outbound HTTP/2 client and reverse-proxy adapter (`Http2Client`, `HttpProxy`) were built
against this cleartext support but had no caller anywhere in `flash` core — an HTTP/1.1+2 server
framework has no business shipping an outbound client. That code has been removed; if a
reverse-proxy capability is needed later, it belongs in its own `flash-extensions/flash-ext-*`
module, not in core.
File diff suppressed because it is too large Load Diff
+1 -2
View File
@@ -131,8 +131,7 @@ needing.
`BufferedByteSource`'s deadline mechanism (`EX-07`'s actual fix) turned out to have zero dedicated
unit tests and an unconditional `socket.setSoTimeout(...)` call that NPE'd against the `null`
socket every isolated unit test in this codebase uses. Found while writing
`Http2FrameReaderTest`, fixed, and given its own regression suite (`BufferedByteSourceTest`)
full writeup in the plan's registry, `EX-37`.
`Http2FrameReaderTest`, fixed, and given its own regression suite (`BufferedByteSourceTest`).
## Testing
File diff suppressed because it is too large Load Diff
+5 -13
View File
@@ -18,20 +18,18 @@ listener / TLS
<- HTTP/2 stream writer ----+
```
## Start here
This page covers the HTTP/2-specific layers only. The transport, message model, and byte
primitives shared with HTTP/1.1 live in [`../core/`](../core/README.md).
## Protocol layers
- [HTTP/1.1 hardening](HTTP1-HARDENING.md) — message-boundary rules, timeouts and negotiation.
- [Transport](TRANSPORT.md) — listeners, connection ownership, TLS and virtual threads.
- [Message model](MESSAGE-MODEL.md) — shared request/response objects and their lifetime contract.
- [Connection](CONNECTION.md) and [streams](STREAMS.md) — HTTP/2 connection and stream state.
- [Flow control](FLOW-CONTROL.md) — request backpressure and streamed responses.
- [Trailers and streaming](TRAILERS-AND-STREAMING.md) — the public cross-protocol APIs.
- [Cleartext and proxying](CLEARTEXT-AND-PROXY.md) — prior knowledge and the upstream h2 client.
- [Cleartext](CLEARTEXT.md) — prior knowledge and the 421 misdirected-request rule.
- [WebSockets](WEBSOCKET.md) — RFC 8441 extended CONNECT using the existing WebSocket API.
## Wire internals
- [Byte primitives](BYTES.md) — reusable views, scanning and bounded slice lifetimes.
- [Serialized writer](WRITER.md) — the single-owner output path and contention model.
- [Frames](FRAMES.md) — frame parsing, validation and error scope.
- [HPACK](HPACK.md) — integer/Huffman coding and static/dynamic table ownership.
@@ -43,9 +41,3 @@ listener / TLS
- [Compliance](COMPLIANCE.md) — h2spec, interoperability, fuzzing and deliberate omissions.
- [Performance](PERFORMANCE.md) and [CI baselines](BASELINES.md) — measurements and regression
gates, including the comparison with nghttpd.
## Design history
[Decisions](DECISIONS.md) records non-obvious trade-offs and rejected alternatives. The
implementation plan is retained as historical engineering evidence; it is not required to use or
extend the runtime.
+3 -3
View File
@@ -137,8 +137,8 @@ from the path this document's gate criteria are strictest about.
## Benchmark methodology
`flash/src/jmh/java/dev/relism/flash/http2/frame/FrameWriterBenchmark.java` (a JMH source root
registered only under the `jmh` Maven profile — see `DECISIONS.md`, `DEC-17`, for why it does not
live in `src/test/java`) compares four harnesses at `threads` ∈ {1, 2, 4, 8, 16, 64}:
registered only under the `jmh` Maven profile, not `src/test/java`) compares four harnesses at
`threads` ∈ {1, 2, 4, 8, 16, 64}:
- `trylock_mpsc` — the shipped `Http2FrameWriter` design.
- `plain_lock` — every write blocks on `ReentrantLock.lock()` unconditionally (candidate (a)).
@@ -258,7 +258,7 @@ design's exclusive use of `ReentrantLock` (never `synchronized`) on every path t
| 4 | Stress test green at every N, 1000 iterations, incl. parallelism=1 | 10 000/10 000 | **PASS** |
**All four gate criteria are met.** `Http2FrameWriter` ships as designed: a `tryLock()` fast path
with an intrusive MPSC fallback. See `DECISIONS.md` for the retained alternatives and evidence.
with an intrusive MPSC fallback.
## What this design costs vs. what it saves