Package io.github.mahmoudimus.http2
Class FrameReader
java.lang.Object
io.github.mahmoudimus.http2.FrameReader
Reads HTTP/2 frames from a blocking stream, one call per frame, validating everything that can
be checked from a frame on its own (RFC 9113 §4, §6). Meant to be driven by one (virtual)
thread per connection; it buffers nothing itself, so give it a
BufferedInputStream
or similar. Not thread-safe.
FrameReader reader = new FrameReader(new BufferedInputStream(socket.getInputStream()));
reader.readClientPreface(); // server side
for (Frame frame; (frame = reader.readFrame()) != null; ) {
switch (frame) {
case Frame.Headers h -> handle(h.streamId(), hpack.decode(h.streamId(), h.fieldBlock()));
case Frame.Data d -> ...
default -> ...
}
}
What it enforces:
- The length of every frame against
SETTINGS_MAX_FRAME_SIZE(connection error FRAME_SIZE_ERROR), before reading or allocating its payload. Memory per frame is bounded by that size, and per field block bysetMaxHeaderBlockSize(int). - Fixed lengths (PRIORITY, RST_STREAM, SETTINGS, PING, GOAWAY, WINDOW_UPDATE), padding that does not fit in the payload, frames on the wrong kind of stream (stream 0 or not), a zero WINDOW_UPDATE increment, and invalid SETTINGS values.
- Field blocks: HEADERS and PUSH_PROMISE without END_HEADERS are joined with the CONTINUATION
frames that follow into one frame; any other frame in between, a CONTINUATION for another
stream or one without a preceding HEADERS is a connection error PROTOCOL_ERROR. A block
longer than
setMaxHeaderBlockSize(int)or split into more thansetMaxContinuationFrames(int)CONTINUATION frames is a connection error ENHANCE_YOUR_CALM (the CONTINUATION flood). - Frames of unknown types are skipped (§5.5), without allocating their payload, unless the
caller asks for them, or for ORIGIN frames (
setDeliverOriginFrames(boolean)).
Stream states, stream-id ordering and parity, SETTINGS acknowledgement and flow control are the
caller's business. After an Http2Exception that is a connection error the reader is
unusable; after a stream error the offending frame has been consumed and reading can go on.
-
Field Summary
FieldsModifier and TypeFieldDescriptionstatic final StringThe client connection preface (RFC 9113 §3.4).static final intThe default limit on CONTINUATION frames per field block.static final intThe default limit on a field block's encoded size, 64 KiB. -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionintReturns the current frame payload limit.intReturns the encoded field block limit.voidReads and checks the client connection preface, failing at the first wrong byte so that, say, an HTTP/1.1 request is rejected without waiting for 24 bytes.Reads the next frame.voidsetDeliverOriginFrames(boolean deliverOriginFrames) Return ORIGIN frames (RFC 8336) asFrame.Unknown, forOriginFrameto read, even when other frames of unknown types are skipped.voidsetDeliverUnknownFrames(boolean deliverUnknownFrames) Return frames of unknown types asFrame.Unknowninstead of skipping them.voidsetMaxContinuationFrames(int maxContinuationFrames) The most CONTINUATION frames accepted in one field block.voidsetMaxFrameSize(int maxFrameSize) The largest frame payload accepted: the SETTINGS_MAX_FRAME_SIZE this endpoint advertised (16384 until the peer acknowledges a larger one).voidsetMaxHeaderBlockSize(int maxHeaderBlockSize) The largest encoded field block (HEADERS or PUSH_PROMISE plus CONTINUATION payloads) accepted.
-
Field Details
-
CLIENT_PREFACE
The client connection preface (RFC 9113 §3.4).- See Also:
-
DEFAULT_MAX_HEADER_BLOCK_SIZE
public static final int DEFAULT_MAX_HEADER_BLOCK_SIZEThe default limit on a field block's encoded size, 64 KiB.- See Also:
-
DEFAULT_MAX_CONTINUATION_FRAMES
public static final int DEFAULT_MAX_CONTINUATION_FRAMESThe default limit on CONTINUATION frames per field block.- See Also:
-
-
Constructor Details
-
FrameReader
Creates a reader for the supplied input stream.- Parameters:
in- the non-null stream to read
-
-
Method Details
-
setMaxFrameSize
public void setMaxFrameSize(int maxFrameSize) The largest frame payload accepted: the SETTINGS_MAX_FRAME_SIZE this endpoint advertised (16384 until the peer acknowledges a larger one).- Parameters:
maxFrameSize- the maximum payload size from 2^14 to 2^24-1 octets
-
maxFrameSize
public int maxFrameSize()Returns the current frame payload limit.- Returns:
- the maximum payload size in octets
-
setMaxHeaderBlockSize
public void setMaxHeaderBlockSize(int maxHeaderBlockSize) The largest encoded field block (HEADERS or PUSH_PROMISE plus CONTINUATION payloads) accepted.- Parameters:
maxHeaderBlockSize- the nonnegative encoded field block limit in octets
-
maxHeaderBlockSize
public int maxHeaderBlockSize()Returns the encoded field block limit.- Returns:
- the encoded field block limit in octets
-
setMaxContinuationFrames
public void setMaxContinuationFrames(int maxContinuationFrames) The most CONTINUATION frames accepted in one field block.- Parameters:
maxContinuationFrames- the nonnegative maximum number of continuation frames
-
setDeliverUnknownFrames
public void setDeliverUnknownFrames(boolean deliverUnknownFrames) Return frames of unknown types asFrame.Unknowninstead of skipping them.- Parameters:
deliverUnknownFrames- true to return extension frames, false to discard them
-
setDeliverOriginFrames
public void setDeliverOriginFrames(boolean deliverOriginFrames) Return ORIGIN frames (RFC 8336) asFrame.Unknown, forOriginFrameto read, even when other frames of unknown types are skipped. A client that coalesces connections needs them; nothing else does.- Parameters:
deliverOriginFrames- true to return ORIGIN frames, false to treat them as any other extension frame
-
readClientPreface
Reads and checks the client connection preface, failing at the first wrong byte so that, say, an HTTP/1.1 request is rejected without waiting for 24 bytes.- Throws:
Http2Exception- a connection error PROTOCOL_ERROR if the bytes differEOFException- if the stream ends firstIOException- if reading the underlying stream fails
-
readFrame
Reads the next frame.- Returns:
- the frame, or null if the stream ended cleanly between frames
- Throws:
Http2Exception- if the frame breaks the protocol or a limitEOFException- if the stream ends inside a frame or a field blockIOException- if reading the underlying stream fails
-