Files
Flash5/flash/src/main/java/dev/relism/flash/h2/Http2StreamException.java
T
Zakaria El OrcheandClaude Sonnet 5 db6e4a4d0c feat(core): HTTP/2 Phase 0 — groundwork (limits, error model, decision log)
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>
2026-08-13 10:59:49 +00:00

47 lines
2.1 KiB
Java

package dev.relism.flash.h2;
/**
* A <b>stream-level</b> HTTP/2 error, scoped to one stream id. Results in an {@code RST_STREAM}
* frame for {@link #streamId()} with {@link #errorCode()}; the connection and every other
* stream on it are unaffected. Compare {@link Http2Exception}, whose scope is the whole
* connection.
*
* <p>Deliberately does <b>not</b> extend {@link java.io.IOException}, for the same reason as
* {@link Http2Exception}: the connection loop must be able to distinguish "we decided to reject
* this stream" from "the socket failed" by catching unrelated exception types.
*
* <h3>Why this allocates, unlike {@code Http2Exception}'s singletons</h3>
* Every instance carries a distinct {@link #streamId()}, so it cannot be a shared singleton the
* way {@code Http2Exception}'s message-less constants are. This is still acceptable under R2:
* {@code RST_STREAM} generation is an error path, not the steady-state request path, and R2
* exempts error paths. The scenario where this matters most — a peer opening and resetting
* thousands of streams per second (the Rapid Reset pattern, CVE-2023-44487) — is bounded by
* rate limits (Phase 13), not by making the rejection itself allocation-free; a hostile peer
* that can force RST_STREAM generation fast enough for GC pressure to matter has already
* tripped {@code Http2Limits.MAX_RESET_STREAMS_PER_INTERVAL} and the connection is being torn
* down anyway.
*
* <p>Stack trace capture is disabled for the same cost reason as {@link Http2Exception}.
*/
public final class Http2StreamException extends RuntimeException {
private final Http2ErrorCode errorCode;
private final int streamId;
public Http2StreamException(int streamId, Http2ErrorCode errorCode, String message) {
super(message, null, false, false);
this.streamId = streamId;
this.errorCode = errorCode;
}
/** The id of the stream this error terminates. */
public int streamId() {
return streamId;
}
/** The RFC 9113 §7 error code to send in the {@code RST_STREAM} frame. */
public Http2ErrorCode errorCode() {
return errorCode;
}
}