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>
Establishes the package layout, limits/error model and decision-log
convention that every later HTTP/2 phase depends on, per
flash/docs/http2/IMPLEMENTATION-PLAN.md Phase 0.
- dev.relism.flash.h2: package-info (architecture overview), Http2ErrorCode
(the 14 RFC 9113 §7 codes with precomputed 4-byte wire encodings),
Http2Exception (connection error -> GOAWAY) and Http2StreamException
(stream error -> RST_STREAM), neither extending IOException, both with
stack-trace capture disabled on the hot rejection path.
- Http2Limits: every bound Phase 0 requires (concurrent streams, frame
size, header list size, CONTINUATION/reset/settings/ping rate bounds,
flow-control windows, HPACK table size/string length, assembly and idle
timeouts), each documented with the attack or RFC clause it addresses.
- dev.relism.flash.http.Http1Limits: the h1 bounds needed by EX-03 (strict
Content-Length) and EX-08 (header count/size limits).
- flash/docs/http2/DECISIONS.md seeded with DEC-01..DEC-11 (the ten
decisions implied by the plan itself, plus DEC-11 recording that commits
keep scope `core` rather than adding `h2` to AGENTS.md).
- flash/docs/http2/IMPLEMENTATION-PLAN.md: added the Progress Ledger
(tracks phase status across sessions) and checked off Phase 0's DoD.
19 new tests, full flash module suite green (226/226).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Data/RepositoryFactory/HibernateData give applications one cached,
stateless entry point for repositories per entity type instead of
per-request instantiation, with write()/afterCommit() replacing manual
transaction+reload choreography.
McpConfig.rolesClaimPath is removed - MCP now derives the claim path
from OidcMiddleware.rolesClaimPath() so applications never duplicate
the roles-claim config between OIDC and MCP. McpPackageScanner is
rebuilt on the shared PackageScanner.discover(packageName) primitive,
doing only McpTool/McpResource/McpPrompt classification itself.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds the QUERY method (RFC 10008) — safe and idempotent like GET,
but carries a request body like POST, useful for complex filters
that don't fit in a URL query string.
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>
Flash can now serve HTTPS and WSS, on one or many listeners per app:
- FlashConfiguration gains an optional `tls` field for the single default
listener, and a `listeners` list for apps that bind multiple ports (each
independently plain or TLS).
- New dev.relism.flash.tls package: TlsConfig.keystore(path, password) builds
an SSLContext from a PKCS12/JKS keystore, with SNI-based certificate
selection for free when the keystore holds more than one alias (matched by
SAN/CN, pure JDK APIs). TlsConfig.ofContext(sslContext) is a full escape
hatch — Flash never calls setSSLParameters on that path, so caller-set
protocols/cipher suites/ALPN survive untouched. TlsConfig.clientAuth(...)
adds optional/required mTLS on either path.
- HttpServer moves from a single ServerSocket to a list of bound listeners;
the per-request hot path (RequestParser, routing, response writing) is
untouched — TLS only changes which bytes come out of accept(), so WSS needs
no separate code path from WS.
- process()'s catch is widened to log non-IOException failures (e.g. a
misbehaving custom KeyManager/TrustManager on the ofContext path) instead
of swallowing them silently; the failure was already isolated to the one
connection via the existing try-with-resources/executor-submission
boundary — this only fixes the missing log line.
- Fixes a pre-existing gap where FlashConfiguration#host was accepted but
never used to bind (listeners always bound to the wildcard address).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- WebSocketSession supports client-mode outgoing frame masking (RFC 6455) via
in-place XOR, reusing the unmask routine already used for inbound frames.
- HeaderMap gains an allocation-free forEach(HeaderConsumer) for callers that
must handle an open-ended set of header names (e.g. proxying).
- HttpServer relays streaming/chunked response bodies through a shared
per-connection ThreadLocal buffer instead of relying on InputStream#transferTo
(which allocates internally) or a fresh byte[8192] per chunked write.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Move JteStaticServing to dev.relism.flash package
- Fix imports in HttpStatus and test files
- Add fluent config methods to JteExtension (templateRoot, serveStatics, staticPrefix, etc.)
- Implement routes() for static asset serving with HEAD support
- Include Main.java entry point