Class QpackDecoder

java.lang.Object
io.github.mahmoudimus.http3.QpackDecoder

public final class QpackDecoder extends Object
Decodes QPACK field sections (RFC 9204) and keeps the dynamic table in step with the peer's encoder. One decoder per connection, for the field sections the peer sends. Not thread-safe: field sections from many streams and the encoder stream all go through it, so callers serialize access (with a ReentrantLock, say, or one thread).

 QpackDecoder qpack = new QpackDecoder(4096, 16);   // what our SETTINGS advertise
 // HEADERS on a request stream:
 List<HeaderField> fields = qpack.decode(streamId, headers.fieldSection());
 if (fields == null) { ... blocked: wait for the encoder stream ... }
 // Bytes from the peer's QPACK encoder stream:
 for (long ready : qpack.onEncoderStream(bytes)) resumeStream(ready, qpack.resume(ready));
 // Then send what the peer's encoder needs to hear on our QPACK decoder stream:
 decoderStream.write(qpack.decoderStreamBytes());
 

The decoder handles every field line representation (§4.5): indexed (static, dynamic relative and post-base), literal with a name reference (static, dynamic and post-base) and literal with a literal name. A literal's 'N' bit comes back as HeaderField.sensitive().

A field section whose Required Insert Count is above the number of entries received so far is blocked: decode(long, byte[]) keeps it and returns null, and onEncoderStream(byte[]) reports its stream once enough entries have arrived, after which resume(long) decodes it. At most maxBlockedStreams streams may be blocked (our SETTINGS_QPACK_BLOCKED_STREAMS); one more is a connection error QPACK_DECOMPRESSION_FAILED (§2.2.1).

The decoder queues the decoder-stream instructions the encoder needs (§4.4): a Section Acknowledgment for each decoded section that used the dynamic table, a Stream Cancellation for each cancelStream(long), and an Insert Count Increment for entries not otherwise acknowledged, all returned by decoderStreamBytes().

Limits: setMaxFieldSectionSize(long) (our SETTINGS_MAX_FIELD_SECTION_SIZE, default 64 KiB) bounds a decoded section, counting name + value + 32 per field; a larger one raises FieldSectionSizeException, a stream error, after the section has been processed and acknowledged, keeping only the fields within the limit. The table capacity bounds the encoder stream's instructions: an entry that cannot fit is an error as soon as its length is read. Any other malformed field section is a connection error QPACK_DECOMPRESSION_FAILED, and any malformed encoder instruction a connection error QPACK_ENCODER_STREAM_ERROR; the decoder must not be used after a connection error.

  • Field Summary

    Fields
    Modifier and Type
    Field
    Description
    static final long
    The default limit on a decoded field section, 64 KiB.
  • Constructor Summary

    Constructors
    Constructor
    Description
    A static-table-only decoder: SETTINGS_QPACK_MAX_TABLE_CAPACITY and BLOCKED_STREAMS of 0.
    QpackDecoder(long maxTableCapacity, int maxBlockedStreams)
    A decoder for the given settings, which this endpoint must advertise in its SETTINGS.
  • Method Summary

    Modifier and Type
    Method
    Description
    int
    The number of streams with a blocked field section.
    void
    cancelStream(long streamId)
    Reports that a stream was reset, or that its reading was abandoned, before all its field sections were decoded.
    decode(long streamId, byte[] section)
    Decodes one encoded field section (the payload of a HEADERS or PUSH_PROMISE frame).
    byte[]
    Takes the decoder-stream instructions queued so far, ending with an Insert Count Increment for any entries received but not yet acknowledged; send them on the QPACK decoder stream.
    long
    The current capacity of the dynamic table, as last set by the peer's encoder.
    dynamicTableEntry(long absoluteIndex)
    The dynamic table entry with this absolute index (0 is the first ever inserted), or null if absent.
    int
    The number of entries in the dynamic table.
    long
    The current size of the dynamic table in octets (entries plus 32 each).
    long
    The number of entries inserted so far (the Insert Count).
    boolean
    isBlocked(long streamId)
    Whether a field section of this stream is waiting for encoder-stream updates.
    int
    How many streams may wait for the encoder stream: our SETTINGS_QPACK_BLOCKED_STREAMS.
    long
    The largest decoded field section accepted.
    long
    The largest dynamic table the encoder may use: our SETTINGS_QPACK_MAX_TABLE_CAPACITY.
    onEncoderStream(byte[] data)
    Processes bytes from the peer's QPACK encoder stream; see onEncoderStream(byte[], int, int).
    onEncoderStream(byte[] data, int offset, int length)
    Processes bytes from the peer's QPACK encoder stream (§4.3), in any pieces: an instruction split between calls is completed by the next.
    resume(long streamId)
    Decodes the blocked field section of a stream that onEncoderStream(byte[]) reported as ready.
    void
    setMaxFieldSectionSize(long maxFieldSectionSize)
    SETTINGS_MAX_FIELD_SECTION_SIZE: the largest decoded field section accepted.

    Methods inherited from class java.lang.Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Field Details

    • DEFAULT_MAX_FIELD_SECTION_SIZE

      public static final long DEFAULT_MAX_FIELD_SECTION_SIZE
      The default limit on a decoded field section, 64 KiB.
      See Also:
  • Constructor Details

    • QpackDecoder

      public QpackDecoder()
      A static-table-only decoder: SETTINGS_QPACK_MAX_TABLE_CAPACITY and BLOCKED_STREAMS of 0.
    • QpackDecoder

      public QpackDecoder(long maxTableCapacity, int maxBlockedStreams)
      A decoder for the given settings, which this endpoint must advertise in its SETTINGS.
      Parameters:
      maxTableCapacity - SETTINGS_QPACK_MAX_TABLE_CAPACITY: the largest dynamic table the peer's encoder may use
      maxBlockedStreams - SETTINGS_QPACK_BLOCKED_STREAMS: how many streams may wait for encoder-stream updates at once
  • Method Details

    • setMaxFieldSectionSize

      public void setMaxFieldSectionSize(long maxFieldSectionSize)
      SETTINGS_MAX_FIELD_SECTION_SIZE: the largest decoded field section accepted.
      Parameters:
      maxFieldSectionSize - the limit in octets
    • maxFieldSectionSize

      public long maxFieldSectionSize()
      The largest decoded field section accepted.
      Returns:
      the limit in octets
    • maxTableCapacity

      public long maxTableCapacity()
      The largest dynamic table the encoder may use: our SETTINGS_QPACK_MAX_TABLE_CAPACITY.
      Returns:
      the capacity in octets
    • maxBlockedStreams

      public int maxBlockedStreams()
      How many streams may wait for the encoder stream: our SETTINGS_QPACK_BLOCKED_STREAMS.
      Returns:
      the number of streams
    • insertCount

      public long insertCount()
      The number of entries inserted so far (the Insert Count).
      Returns:
      the Insert Count
    • dynamicTableSize

      public long dynamicTableSize()
      The current size of the dynamic table in octets (entries plus 32 each).
      Returns:
      the size in octets
    • dynamicTableCapacity

      public long dynamicTableCapacity()
      The current capacity of the dynamic table, as last set by the peer's encoder.
      Returns:
      the capacity in octets
    • dynamicTableLength

      public int dynamicTableLength()
      The number of entries in the dynamic table.
      Returns:
      the number of entries
    • dynamicTableEntry

      public HeaderField dynamicTableEntry(long absoluteIndex)
      The dynamic table entry with this absolute index (0 is the first ever inserted), or null if absent.
      Parameters:
      absoluteIndex - the absolute index
      Returns:
      the entry, or null
    • isBlocked

      public boolean isBlocked(long streamId)
      Whether a field section of this stream is waiting for encoder-stream updates.
      Parameters:
      streamId - the stream
      Returns:
      whether it is blocked
    • blockedStreams

      public int blockedStreams()
      The number of streams with a blocked field section.
      Returns:
      the number of blocked streams
    • decode

      public List<HeaderField> decode(long streamId, byte[] section) throws Http3Exception
      Decodes one encoded field section (the payload of a HEADERS or PUSH_PROMISE frame).
      Parameters:
      streamId - the stream the section came on
      section - the encoded field section
      Returns:
      the fields in order, with never-indexed literals marked HeaderField.sensitive(); or null if the section is blocked, in which case the decoder keeps a copy of it
      Throws:
      FieldSectionSizeException - if the section is larger than the limit (a stream error)
      Http3Exception - a connection error QPACK_DECOMPRESSION_FAILED if the section is malformed or one stream too many would be blocked
      IllegalStateException - if a section of this stream is already blocked
    • resume

      public List<HeaderField> resume(long streamId) throws Http3Exception
      Decodes the blocked field section of a stream that onEncoderStream(byte[]) reported as ready.
      Parameters:
      streamId - the stream
      Returns:
      the decoded fields
      Throws:
      IllegalStateException - if the stream has no blocked section, or it is still blocked
      Http3Exception - as decode(long, byte[])
    • cancelStream

      public void cancelStream(long streamId)
      Reports that a stream was reset, or that its reading was abandoned, before all its field sections were decoded. Drops any blocked section and queues a Stream Cancellation (§4.4.2), unless the table capacity is 0 and the encoder cannot have referenced the table.
      Parameters:
      streamId - the stream
    • decoderStreamBytes

      public byte[] decoderStreamBytes()
      Takes the decoder-stream instructions queued so far, ending with an Insert Count Increment for any entries received but not yet acknowledged; send them on the QPACK decoder stream.
      Returns:
      the bytes, possibly none
    • onEncoderStream

      public List<Long> onEncoderStream(byte[] data) throws Http3Exception
      Processes bytes from the peer's QPACK encoder stream; see onEncoderStream(byte[], int, int).
      Parameters:
      data - the bytes
      Returns:
      the streams that can now be resumed
      Throws:
      Http3Exception - a connection error QPACK_ENCODER_STREAM_ERROR for an invalid instruction
    • onEncoderStream

      public List<Long> onEncoderStream(byte[] data, int offset, int length) throws Http3Exception
      Processes bytes from the peer's QPACK encoder stream (§4.3), in any pieces: an instruction split between calls is completed by the next.
      Parameters:
      data - the bytes
      offset - where they start in data
      length - how many
      Returns:
      the streams whose blocked field sections can now be decoded with resume(long), each reported once, in the order they blocked
      Throws:
      Http3Exception - a connection error QPACK_ENCODER_STREAM_ERROR for an invalid instruction