Pools Request/RequestBody/RequestLine/Response per connection (EX-20..EX-24),
following the same reset()/dev-mode-guard idiom Http1HeaderMap already used.
HeaderMap splits into HeaderView (interface) + Http1HeaderMap (impl, DEC-22).
Response gains byte-level structured headers, PreEncodedHeader, and
ResponseSerializer as the single source of truth for a response's header
sequence, consumed by Http1ResponseWriter's single-bulk-write rewrite (EX-27).
ByteTemplate gets O(1) slot lookup plus a buffer-writing overload (EX-28).
Multipart audited: three resource-exhaustion gaps found and fixed — unbounded
buffered part size, part count, and per-part header parsing (EX-38..EX-40) —
and boundary length confirmed already bounded (EX-41).
Re-measuring RequestPipelineBenchmark after the pooling work surfaced one more
per-request allocation underneath it (RequestParser building fresh
RequestByteViews every call) and, while checking the phase's own DoD text, an
unbounded Response.header(...) loop hazard neither had a limit — both fixed
(EX-42, EX-43). The h1 zero-alloc contract now holds: parseAndRoute measures
0.008 B/op (JMH noise floor), down from Phase 4's 120.008 B/op (DEC-20, DEC-23).
MESSAGE-MODEL.md records the pooling model; README gains an "Object lifetime"
section documenting the do-not-retain-past-the-handler contract. 503/503 tests
green.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Breaks HttpServer (563 lines, eleven responsibilities) into named,
single-purpose components and introduces the ConnectionProtocol seam
HTTP/2 plugs into starting Phase 8, per
flash/docs/http2/IMPLEMENTATION-PLAN.md Phase 2.
New packages:
- dev.relism.flash.transport: TransportFactory (composition root, EX-34),
ListenerBinder, BoundListener, TransportTuning, AcceptLoop,
ConnectionRunner (per-connection setup/teardown), ConnectionProtocol
(the h1/h2 seam), ConnectionContext, ConnectionScratch + ScratchPool
(EX-06), ServerLifecycle (implements ServerHandle; start/stop/graceful
shutdown, EX-32).
- dev.relism.flash.http1: Http1Connection (the keep-alive request loop,
implements ConnectionProtocol), Http1ResponseWriter, Http1KeepAlive
(the shared Connection-header token-list scanner, EX-13).
- dev.relism.flash.websocket additions: WebSocketUpgrade (detection +
handshake), WebSocketLoop (session loop), WebSocketProtocolException.
Existing-code defects fixed (EX-nn):
- EX-01: WebSocketSession's two blocking-write sites use ReentrantLock
instead of synchronized (out) -- a virtual thread blocking inside
synchronized pins its carrier platform thread on Java 21.
- EX-06: HttpServer's three ThreadLocals (SHA1, LONG_BUF,
STREAM_RELAY_BUFFER) replaced by ConnectionScratch, pooled via
ScratchPool instead of one-per-virtual-thread (i.e. one-per-connection)
growth. The router's ThreadLocals are deliberately deferred to Phase 4
per this EX item's own phasing -- see DEC-15 for the plan-wording fix.
- EX-11: WebSocketSession.readFrame's extended-length and mask-key bytes
are now read in a single bounded readFully instead of one at a time.
- EX-12: full RFC 6455 frame validation -- continuation-frame
reassembly, mandatory masking-direction enforcement, opcode
validation, control-frame constraints (not fragmented, <=125 bytes),
and WebSocketProtocolException carrying the correct close code (1002
protocol error, 1009 message too big).
- EX-13: Connection header token-list scanning shared between the
keep-alive decision and the WebSocket upgrade check.
- EX-14: HEAD responses report Content-Length but write no body.
- EX-15: Content-Type omitted when empty; Content-Length and the body
omitted entirely for 204/304/1xx responses.
- EX-16: Date header (dev.relism.flash.http.DateHeader), refreshed once
per second by a shared daemon thread; FlashConfiguration.sendDate.
- EX-32: two-stage graceful shutdown -- stop accepting, force
Connection: close on the response an in-flight handler is still
producing (re-checked after the handler runs, not just before
dispatch, so a shutdown beginning mid-handler is still honoured),
drain up to shutdownDrainTimeoutMs, then force-close.
- EX-34: ServerHandle.create delegates to TransportFactory instead of
constructing HttpServer directly.
Two plan corrections recorded: DEC-15 (Phase 2's "no ThreadLocal
anywhere" DoD line contradicted EX-06's own multi-phase assignment --
corrected to match the registry) and DEC-16 (no separate
WebSocketFrameCodec class this phase; the EX-11/EX-12 fixes stay inside
WebSocketSession, which is one cohesive state machine under R6's own
carve-out -- revisit at Phase 15 if RFC 8441 needs the decoupling for
real).
HttpServer.java deleted.
311/311 tests green (flash module), run three times for stability of
the wall-clock-based timeout/shutdown tests. Whole-repo build green.
h1 benchmark regression check remains unverified in the plan's DoD (no
JMH harness until Phase 3, same caveat as Phase 1).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Fixes the request-smuggling and resource-exhaustion debt in the existing
HTTP/1.1 parser, and adds the ALPN/h2c-preface negotiation seam so a
connection's protocol is decided once, before any request is parsed, per
flash/docs/http2/IMPLEMENTATION-PLAN.md Phase 1.
Existing-code defects fixed (EX-nn):
- EX-02: reject Content-Length + Transfer-Encoding together (RFC 9112 6.1
CL.TE/TE.CL smuggling), and conflicting duplicate Content-Length values.
- EX-03: strict, overflow-safe Content-Length parsing, replacing a parser
that silently skipped non-digit bytes ("5abc" -> 5, "-1" -> 1).
- EX-07: header-read / idle-keep-alive / body-read timeouts enforced by an
absolute deadline (dev.relism.flash.transport.BufferedByteSource), not
merely Socket#setSoTimeout, which never trips against a peer trickling
one byte per read within the window.
- EX-08: header count / name length / value length / request-line length
bounds (Http1Limits), 431 on violation.
- EX-10: ChunkedInputStream now reads through BufferedByteSource instead
of the raw unbuffered socket stream, and the header-parser's read-ahead
bytes are handed over via a zero-copy prependOnce() instead of a
SequenceInputStream/ByteArrayInputStream pair.
- EX-17: HttpStatus's status-code bound is computed from values() instead
of a hand-maintained constant that silently threw
ArrayIndexOutOfBoundsException when a code above it was added; added
421, 431, 505, 507, 511 and others HTTP/2 and this hardening need.
- EX-18: bare-CR desync and obsolete line folding rejected.
- EX-30: the TLS handshake is forced explicitly, under a timeout, before
any protocol decision -- SSLSocket#getApplicationProtocol() returned
null until the handshake had run, and nothing previously forced it.
- EX-31: TLS 1.2 cipher suites on the RFC 9113 Appendix A blocklist are
filtered out of a listener's enabled set whenever it offers h2 via ALPN.
- EX-35 (found in this phase): Transfer-Encoding values listing multiple
codings ("gzip, chunked") were silently treated as not chunked at all,
corrupting the message boundary -- only the whole value was compared.
- EX-36 (found in this phase): a header line with no ':' was silently
skipped instead of rejected.
New:
- dev.relism.flash.transport.BufferedByteSource: the single buffered,
deadline-aware, peekable view over a connection's inbound bytes.
- dev.relism.flash.transport.ProtocolNegotiator/NegotiatedProtocol: ALPN
and h2c prior-knowledge detection. In this phase an H2 result is always
closed cleanly -- there is no Http2Connection to hand off to until
Phase 8. FlashConfiguration.http2Enabled gates the h2c preface peek.
- dev.relism.flash.exceptions.MalformedRequestException: a typed,
status-carrying rejection distinct from HttpException, caught at the
parse site so a malformed request never reaches the handler chain or
the user's exception handler, and the connection is always closed.
Two small plan-document corrections recorded as DEC-12 (Phase 1's Files
list omitted BufferedByteSource.java and MalformedRequestException.java;
the request-line-length check description pointed at the wrong offset).
DEC-13/DEC-14 record the deadline and exception-hierarchy designs.
277/277 tests green (flash module), run twice for stability of the new
wall-clock-based HttpServerTimeoutTest cases. Whole-repo build green.
h1 benchmark regression check is left unverified in the plan's DoD: no
JMH harness exists yet (Phase 3 deliverable).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Streamable HTTP transport (JSON-RPC 2.0 over POST), one-class-per-tool/resource/prompt
API mirroring RequestHandler, boot-time-precompiled schema/list payloads for a zero-alloc
hot path, and optional OAuth2 protection built on flash-ext-oidc (lazy-loaded, RFC 8707
audience binding, RFC 9728 Protected Resource Metadata). Registers the module in the
root and flash-extensions POMs and adds the ext-mcp commit scope to AGENTS.md.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- TlsConfig.applicationProtocols(String...) sets the listener's negotiable
ALPN protocol list via SSLParameters, inherited by every accepted socket
like clientAuth — works on both keystore() and ofContext(), untouched
unless called. Enables TLS-ALPN-01 (RFC 8737) style on-demand cert
issuance: a custom KeyManager can read the already-resolved protocol via
engine/socket getHandshakeApplicationProtocol() inside
chooseEngineServerAlias/chooseServerAlias, since ALPN is resolved during
ClientHello/ServerHello, always before Certificate production.
- Request gains isSecure()/sslSession(), threaded through RequestParser from
the accepted SSLSocket exactly like remoteAddress() — reference-only,
zero per-request allocation. sslSession() defers to SSLSocket#getSession()
lazily, so it's a cached-field read (handshake already completed by the
time a handler can call it), never a forced handshake.
- WebSocketSession.isSecure()/sslSession() delegate to the upgrading
Request rather than tracking the socket a second time.
- Documents TLS end-to-end in README.md (listeners, TlsConfig, SNI, ALPN,
mTLS, Request/WebSocketSession accessors).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>