Class FrameReader

java.lang.Object
io.github.mahmoudimus.http2.FrameReader

public final class FrameReader extends Object
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 by setMaxHeaderBlockSize(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 than setMaxContinuationFrames(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 Details

    • CLIENT_PREFACE

      public static final String 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_SIZE
      The default limit on a field block's encoded size, 64 KiB.
      See Also:
    • DEFAULT_MAX_CONTINUATION_FRAMES

      public static final int DEFAULT_MAX_CONTINUATION_FRAMES
      The default limit on CONTINUATION frames per field block.
      See Also:
  • Constructor Details

    • FrameReader

      public FrameReader(InputStream in)
      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 as Frame.Unknown instead 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) as Frame.Unknown, for OriginFrame to 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

      public void readClientPreface() throws IOException
      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 differ
      EOFException - if the stream ends first
      IOException - if reading the underlying stream fails
    • readFrame

      public Frame readFrame() throws IOException
      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 limit
      EOFException - if the stream ends inside a frame or a field block
      IOException - if reading the underlying stream fails