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:
@@ -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").
|
||||
@@ -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
@@ -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
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user