Class QpackEncoder

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

public final class QpackEncoder extends Object
Encodes field sections with QPACK (RFC 9204). One encoder per connection, for the field sections this endpoint sends. Not thread-safe: callers serialize access, and must send each section's encoder-stream instructions (see encoderStreamBytes()) no later than the section.

Two modes:

  • Static only (QpackEncoder(), or until the peer allows a dynamic table): fields are indexed in the static table or sent as literals. Sections never depend on the encoder stream (Required Insert Count 0), so they can never block. Always safe.
  • Dynamic (QpackEncoder(long) with a non-zero limit, once applyPeerSettings(io.github.mahmoudimus.http3.Http3Settings) gives a non-zero SETTINGS_QPACK_MAX_TABLE_CAPACITY): the encoder sets the table capacity to the smaller of its limit and the peer's, inserts fields into the dynamic table with encoder-stream instructions (Set Dynamic Table Capacity, Insert With Name Reference, Insert With Literal Name, Duplicate) and references them. A section may reference entries the peer has not acknowledged yet, and so block, only while fewer than the peer's SETTINGS_QPACK_BLOCKED_STREAMS streams are blocked (or the stream already is); otherwise it uses only acknowledged entries and literals.

 QpackEncoder qpack = new QpackEncoder(4096);
 qpack.applyPeerSettings(peerSettings);
 byte[] section = qpack.encode(streamId, fields);
 encoderStream.write(qpack.encoderStreamBytes());   // first, or together with the section
 requestStream.writeHeaders(section);
 // Bytes from the peer's QPACK decoder stream:
 qpack.onDecoderStream(bytes, 0, n);
 

The peer's decoder-stream instructions (Section Acknowledgment, Stream Cancellation, Insert Count Increment) release references and advance the Known Received Count. An entry is evicted only once its insertion has been acknowledged and no unacknowledged section references it (§2.1.1); when that does not leave room, the field is sent as a literal instead.

A field marked HeaderField.sensitive(), or (unless turned off) one named in SENSITIVE_NAMES, is sent as a literal with the 'N' bit set and never enters the dynamic table. Strings are Huffman-coded unless that would make them longer or setHuffman(boolean) turns it off. Names are sent as given: lower-case them first.

  • Field Summary

    Fields
    Modifier and Type
    Field
    Description
    static final Set<String>
    Names whose fields are sent never-indexed by default: they carry credentials.
  • Constructor Summary

    Constructors
    Constructor
    Description
    A static-table-only encoder: it never uses the dynamic table, whatever the peer allows.
    QpackEncoder(long maxTableCapacity)
    An encoder that will use a dynamic table of up to maxTableCapacity octets (memory it is willing to spend), and no more than the peer's SETTINGS_QPACK_MAX_TABLE_CAPACITY.
  • Method Summary

    Modifier and Type
    Method
    Description
    void
    Applies the QPACK settings and MAX_FIELD_SECTION_SIZE from the peer's SETTINGS.
    int
    The number of streams with a section that the peer cannot decode until it receives more inserts.
    long
    duplicate(long absoluteIndex)
    Re-inserts an existing entry with a Duplicate instruction (§4.3.4), so that sections can reference a fresh copy while the old one becomes evictable.
    long
    The current capacity of the dynamic table.
    dynamicTableEntry(long absoluteIndex)
    The dynamic table entry with this absolute index, 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).
    byte[]
    encode(long streamId, List<HeaderField> fields)
    Encodes one field section for a HEADERS or PUSH_PROMISE frame on the given stream, queuing any encoder-stream instructions it needs.
    byte[]
    Takes the encoder-stream instructions produced so far; send them on the QPACK encoder stream before, or together with, the sections that depend on them.
    long
    insert(String name, String value)
    Inserts a field into the dynamic table without referencing it, for later sections (a speculative insert), choosing a name reference where one exists.
    long
    The number of entries inserted so far (the Insert Count).
    long
    How many inserts the peer's decoder has acknowledged (§2.1.4).
    long
    The largest capacity the dynamic table may have: the smaller of our limit and the peer's.
    void
    onDecoderStream(byte[] data)
    Processes bytes from the peer's QPACK decoder stream; see onDecoderStream(byte[], int, int).
    void
    onDecoderStream(byte[] data, int offset, int length)
    Processes bytes from the peer's QPACK decoder stream (§4.4), in any pieces: an instruction split between calls is completed by the next.
    int
    referenceCount(long absoluteIndex)
    The number of unacknowledged field sections that reference this entry.
    void
    setCapacity(long capacity)
    Sets the dynamic table capacity, emitting a Set Dynamic Table Capacity instruction (§4.3.1).
    setHuffman(boolean huffman)
    Whether to Huffman-code strings that do not get longer for it (default true).
    setNeverIndexSensitiveNames(boolean neverIndexSensitiveNames)
    Whether to send fields named in SENSITIVE_NAMES never-indexed (default true).
    void
    setPeerSettings(long maxTableCapacity, long maxBlockedStreams)
    Applies the peer's SETTINGS_QPACK_MAX_TABLE_CAPACITY and SETTINGS_QPACK_BLOCKED_STREAMS.

    Methods inherited from class java.lang.Object

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

    • SENSITIVE_NAMES

      public static final Set<String> SENSITIVE_NAMES
      Names whose fields are sent never-indexed by default: they carry credentials.
  • Constructor Details

    • QpackEncoder

      public QpackEncoder()
      A static-table-only encoder: it never uses the dynamic table, whatever the peer allows.
    • QpackEncoder

      public QpackEncoder(long maxTableCapacity)
      An encoder that will use a dynamic table of up to maxTableCapacity octets (memory it is willing to spend), and no more than the peer's SETTINGS_QPACK_MAX_TABLE_CAPACITY. Until applyPeerSettings(io.github.mahmoudimus.http3.Http3Settings) it works in static-only mode, which is valid with any peer.
      Parameters:
      maxTableCapacity - the largest dynamic table this encoder will use, in octets
  • Method Details

    • setHuffman

      public QpackEncoder setHuffman(boolean huffman)
      Whether to Huffman-code strings that do not get longer for it (default true).
      Parameters:
      huffman - whether to Huffman-code
      Returns:
      this encoder
    • setNeverIndexSensitiveNames

      public QpackEncoder setNeverIndexSensitiveNames(boolean neverIndexSensitiveNames)
      Whether to send fields named in SENSITIVE_NAMES never-indexed (default true).
      Parameters:
      neverIndexSensitiveNames - whether to
      Returns:
      this encoder
    • applyPeerSettings

      public void applyPeerSettings(Http3Settings peer)
      Applies the QPACK settings and MAX_FIELD_SECTION_SIZE from the peer's SETTINGS.
      Parameters:
      peer - the peer's settings
    • setPeerSettings

      public void setPeerSettings(long maxTableCapacity, long maxBlockedStreams)
      Applies the peer's SETTINGS_QPACK_MAX_TABLE_CAPACITY and SETTINGS_QPACK_BLOCKED_STREAMS. HTTP/3 sends SETTINGS once, so call this once.
      Parameters:
      maxTableCapacity - the peer's SETTINGS_QPACK_MAX_TABLE_CAPACITY
      maxBlockedStreams - the peer's SETTINGS_QPACK_BLOCKED_STREAMS
    • maxDynamicTableCapacity

      public long maxDynamicTableCapacity()
      The largest capacity the dynamic table may have: the smaller of our limit and the peer's.
      Returns:
      the capacity in octets
    • insertCount

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

      public long knownReceivedCount()
      How many inserts the peer's decoder has acknowledged (§2.1.4).
      Returns:
      the Known Received 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.
      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, or null if absent.
      Parameters:
      absoluteIndex - the absolute index
      Returns:
      the entry, or null
    • referenceCount

      public int referenceCount(long absoluteIndex)
      The number of unacknowledged field sections that reference this entry.
      Parameters:
      absoluteIndex - the absolute index
      Returns:
      the number of sections
    • blockedStreams

      public int blockedStreams()
      The number of streams with a section that the peer cannot decode until it receives more inserts.
      Returns:
      the number of streams
    • encoderStreamBytes

      public byte[] encoderStreamBytes()
      Takes the encoder-stream instructions produced so far; send them on the QPACK encoder stream before, or together with, the sections that depend on them.
      Returns:
      the bytes, possibly none
    • setCapacity

      public void setCapacity(long capacity)
      Sets the dynamic table capacity, emitting a Set Dynamic Table Capacity instruction (§4.3.1). Without this call, the first section or insert sets it to maxDynamicTableCapacity().
      Parameters:
      capacity - the new capacity in octets
      Throws:
      IllegalArgumentException - above maxDynamicTableCapacity()
      IllegalStateException - if shrinking would evict an entry that is not evictable
    • insert

      public long insert(String name, String value)
      Inserts a field into the dynamic table without referencing it, for later sections (a speculative insert), choosing a name reference where one exists.
      Parameters:
      name - the field name
      value - the field value
      Returns:
      the new entry's absolute index, or -1 if it did not fit
    • duplicate

      public long duplicate(long absoluteIndex)
      Re-inserts an existing entry with a Duplicate instruction (§4.3.4), so that sections can reference a fresh copy while the old one becomes evictable.
      Parameters:
      absoluteIndex - the entry to duplicate
      Returns:
      the new entry's absolute index, or -1 if there was no room
      Throws:
      IllegalArgumentException - if the entry is not in the table
    • encode

      public byte[] encode(long streamId, List<HeaderField> fields)
      Encodes one field section for a HEADERS or PUSH_PROMISE frame on the given stream, queuing any encoder-stream instructions it needs.
      Parameters:
      streamId - the stream the section goes on
      fields - the fields
      Returns:
      the encoded field section
      Throws:
      IllegalArgumentException - if a name or value has a char above U+00FF (not an octet), or the section is larger than the peer's SETTINGS_MAX_FIELD_SECTION_SIZE; nothing has changed then
    • onDecoderStream

      public void onDecoderStream(byte[] data) throws Http3Exception
      Processes bytes from the peer's QPACK decoder stream; see onDecoderStream(byte[], int, int).
      Parameters:
      data - the bytes
      Throws:
      Http3Exception - a connection error QPACK_DECODER_STREAM_ERROR for an invalid instruction
    • onDecoderStream

      public void onDecoderStream(byte[] data, int offset, int length) throws Http3Exception
      Processes bytes from the peer's QPACK decoder stream (§4.4), 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
      Throws:
      Http3Exception - a connection error QPACK_DECODER_STREAM_ERROR for an acknowledgment of a section that was not sent, an Insert Count Increment of 0 or past the inserts sent, or an integer overflow