| // Copyright 2025 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| //go:build go1.24 |
| |
| package http3 |
| |
| import ( |
| "errors" |
| "io" |
| |
| "golang.org/x/net/http2/hpack" |
| ) |
| |
| // QPACK (RFC 9204) header compression wire encoding. |
| // https://www.rfc-editor.org/rfc/rfc9204.html |
| |
| // tableType is the static or dynamic table. |
| // |
| // The T bit in QPACK instructions indicates whether a table index refers to |
| // the dynamic (T=0) or static (T=1) table. tableTypeForTBit and tableType.tbit |
| // convert a T bit from the wire encoding to/from a tableType. |
| type tableType byte |
| |
| const ( |
| dynamicTable = 0x00 // T=0, dynamic table |
| staticTable = 0xff // T=1, static table |
| ) |
| |
| // tableTypeForTbit returns the table type corresponding to a T bit value. |
| // The input parameter contains a byte masked to contain only the T bit. |
| func tableTypeForTbit(bit byte) tableType { |
| if bit == 0 { |
| return dynamicTable |
| } |
| return staticTable |
| } |
| |
| // tbit produces the T bit corresponding to the table type. |
| // The input parameter contains a byte with the T bit set to 1, |
| // and the return is either the input or 0 depending on the table type. |
| func (t tableType) tbit(bit byte) byte { |
| return bit & byte(t) |
| } |
| |
| // indexType indicates a literal's indexing status. |
| // |
| // The N bit in QPACK instructions indicates whether a literal is "never-indexed". |
| // A never-indexed literal (N=1) must not be encoded as an indexed literal if it |
| // forwarded on another connection. |
| // |
| // (See https://www.rfc-editor.org/rfc/rfc9204.html#section-7.1 for details on the |
| // security reasons for never-indexed literals.) |
| type indexType byte |
| |
| const ( |
| mayIndex = 0x00 // N=0, not a never-indexed literal |
| neverIndex = 0xff // N=1, never-indexed literal |
| ) |
| |
| // indexTypeForNBit returns the index type corresponding to a N bit value. |
| // The input parameter contains a byte masked to contain only the N bit. |
| func indexTypeForNBit(bit byte) indexType { |
| if bit == 0 { |
| return mayIndex |
| } |
| return neverIndex |
| } |
| |
| // nbit produces the N bit corresponding to the table type. |
| // The input parameter contains a byte with the N bit set to 1, |
| // and the return is either the input or 0 depending on the table type. |
| func (t indexType) nbit(bit byte) byte { |
| return bit & byte(t) |
| } |
| |
| // Indexed Field Line: |
| // |
| // 0 1 2 3 4 5 6 7 |
| // +---+---+---+---+---+---+---+---+ |
| // | 1 | T | Index (6+) | |
| // +---+---+-----------------------+ |
| // |
| // https://www.rfc-editor.org/rfc/rfc9204.html#section-4.5.2 |
| |
| func appendIndexedFieldLine(b []byte, ttype tableType, index int) []byte { |
| const tbit = 0b_01000000 |
| return appendPrefixedInt(b, 0b_1000_0000|ttype.tbit(tbit), 6, int64(index)) |
| } |
| |
| func (st *stream) decodeIndexedFieldLine(b byte) (itype indexType, name, value string, err error) { |
| index, err := st.readPrefixedIntWithByte(b, 6) |
| if err != nil { |
| return 0, "", "", err |
| } |
| const tbit = 0b_0100_0000 |
| if tableTypeForTbit(b&tbit) == staticTable { |
| ent, err := staticTableEntry(index) |
| if err != nil { |
| return 0, "", "", err |
| } |
| return mayIndex, ent.name, ent.value, nil |
| } else { |
| return 0, "", "", errors.New("dynamic table is not supported yet") |
| } |
| } |
| |
| // Literal Field Line With Name Reference: |
| // |
| // 0 1 2 3 4 5 6 7 |
| // +---+---+---+---+---+---+---+---+ |
| // | 0 | 1 | N | T |Name Index (4+)| |
| // +---+---+---+---+---------------+ |
| // | H | Value Length (7+) | |
| // +---+---------------------------+ |
| // | Value String (Length bytes) | |
| // +-------------------------------+ |
| // |
| // https://www.rfc-editor.org/rfc/rfc9204.html#section-4.5.4 |
| |
| func appendLiteralFieldLineWithNameReference(b []byte, ttype tableType, itype indexType, nameIndex int, value string) []byte { |
| const tbit = 0b_0001_0000 |
| const nbit = 0b_0010_0000 |
| b = appendPrefixedInt(b, 0b_0100_0000|itype.nbit(nbit)|ttype.tbit(tbit), 4, int64(nameIndex)) |
| b = appendPrefixedString(b, 0, 7, value) |
| return b |
| } |
| |
| func (st *stream) decodeLiteralFieldLineWithNameReference(b byte) (itype indexType, name, value string, err error) { |
| nameIndex, err := st.readPrefixedIntWithByte(b, 4) |
| if err != nil { |
| return 0, "", "", err |
| } |
| |
| const tbit = 0b_0001_0000 |
| if tableTypeForTbit(b&tbit) == staticTable { |
| ent, err := staticTableEntry(nameIndex) |
| if err != nil { |
| return 0, "", "", err |
| } |
| name = ent.name |
| } else { |
| return 0, "", "", errors.New("dynamic table is not supported yet") |
| } |
| |
| _, value, err = st.readPrefixedString(7) |
| if err != nil { |
| return 0, "", "", err |
| } |
| |
| const nbit = 0b_0010_0000 |
| itype = indexTypeForNBit(b & nbit) |
| |
| return itype, name, value, nil |
| } |
| |
| // Literal Field Line with Literal Name: |
| // |
| // 0 1 2 3 4 5 6 7 |
| // +---+---+---+---+---+---+---+---+ |
| // | 0 | 0 | 1 | N | H |NameLen(3+)| |
| // +---+---+---+---+---+-----------+ |
| // | Name String (Length bytes) | |
| // +---+---------------------------+ |
| // | H | Value Length (7+) | |
| // +---+---------------------------+ |
| // | Value String (Length bytes) | |
| // +-------------------------------+ |
| // |
| // https://www.rfc-editor.org/rfc/rfc9204.html#section-4.5.6 |
| |
| func appendLiteralFieldLineWithLiteralName(b []byte, itype indexType, name, value string) []byte { |
| const nbit = 0b_0001_0000 |
| b = appendPrefixedString(b, 0b_0010_0000|itype.nbit(nbit), 3, name) |
| b = appendPrefixedString(b, 0, 7, value) |
| return b |
| } |
| |
| func (st *stream) decodeLiteralFieldLineWithLiteralName(b byte) (itype indexType, name, value string, err error) { |
| name, err = st.readPrefixedStringWithByte(b, 3) |
| if err != nil { |
| return 0, "", "", err |
| } |
| _, value, err = st.readPrefixedString(7) |
| if err != nil { |
| return 0, "", "", err |
| } |
| const nbit = 0b_0001_0000 |
| itype = indexTypeForNBit(b & nbit) |
| return itype, name, value, nil |
| } |
| |
| // Prefixed-integer encoding from RFC 7541, section 5.1 |
| // |
| // Prefixed integers consist of some number of bits of data, |
| // N bits of encoded integer, and 0 or more additional bytes of |
| // encoded integer. |
| // |
| // The RFCs represent this as, for example: |
| // |
| // 0 1 2 3 4 5 6 7 |
| // +---+---+---+---+---+---+---+---+ |
| // | 0 | 0 | 1 | Capacity (5+) | |
| // +---+---+---+-------------------+ |
| // |
| // "Capacity" is an integer with a 5-bit prefix. |
| // |
| // In the following functions, a "prefixLen" parameter is the number |
| // of integer bits in the first byte (5 in the above example), and |
| // a "firstByte" parameter is a byte containing the first byte of |
| // the encoded value (0x001x_xxxx in the above example). |
| // |
| // https://www.rfc-editor.org/rfc/rfc9204.html#section-4.1.1 |
| // https://www.rfc-editor.org/rfc/rfc7541#section-5.1 |
| |
| // readPrefixedInt reads an RFC 7541 prefixed integer from st. |
| func (st *stream) readPrefixedInt(prefixLen uint8) (firstByte byte, v int64, err error) { |
| firstByte, err = st.ReadByte() |
| if err != nil { |
| return 0, 0, errQPACKDecompressionFailed |
| } |
| v, err = st.readPrefixedIntWithByte(firstByte, prefixLen) |
| return firstByte, v, err |
| } |
| |
| // readPrefixedInt reads an RFC 7541 prefixed integer from st. |
| // The first byte has already been read from the stream. |
| func (st *stream) readPrefixedIntWithByte(firstByte byte, prefixLen uint8) (v int64, err error) { |
| prefixMask := (byte(1) << prefixLen) - 1 |
| v = int64(firstByte & prefixMask) |
| if v != int64(prefixMask) { |
| return v, nil |
| } |
| m := 0 |
| for { |
| b, err := st.ReadByte() |
| if err != nil { |
| return 0, errQPACKDecompressionFailed |
| } |
| v += int64(b&127) << m |
| m += 7 |
| if b&128 == 0 { |
| break |
| } |
| } |
| return v, err |
| } |
| |
| // appendPrefixedInt appends an RFC 7541 prefixed integer to b. |
| // |
| // The firstByte parameter includes the non-integer bits of the first byte. |
| // The other bits must be zero. |
| func appendPrefixedInt(b []byte, firstByte byte, prefixLen uint8, i int64) []byte { |
| u := uint64(i) |
| prefixMask := (uint64(1) << prefixLen) - 1 |
| if u < prefixMask { |
| return append(b, firstByte|byte(u)) |
| } |
| b = append(b, firstByte|byte(prefixMask)) |
| u -= prefixMask |
| for u >= 128 { |
| b = append(b, 0x80|byte(u&0x7f)) |
| u >>= 7 |
| } |
| return append(b, byte(u)) |
| } |
| |
| // String literal encoding from RFC 7541, section 5.2 |
| // |
| // String literals consist of a single bit flag indicating |
| // whether the string is Huffman-encoded, a prefixed integer (see above), |
| // and the string. |
| // |
| // https://www.rfc-editor.org/rfc/rfc9204.html#section-4.1.2 |
| // https://www.rfc-editor.org/rfc/rfc7541#section-5.2 |
| |
| // readPrefixedString reads an RFC 7541 string from st. |
| func (st *stream) readPrefixedString(prefixLen uint8) (firstByte byte, s string, err error) { |
| firstByte, err = st.ReadByte() |
| if err != nil { |
| return 0, "", errQPACKDecompressionFailed |
| } |
| s, err = st.readPrefixedStringWithByte(firstByte, prefixLen) |
| return firstByte, s, err |
| } |
| |
| // readPrefixedString reads an RFC 7541 string from st. |
| // The first byte has already been read from the stream. |
| func (st *stream) readPrefixedStringWithByte(firstByte byte, prefixLen uint8) (s string, err error) { |
| size, err := st.readPrefixedIntWithByte(firstByte, prefixLen) |
| if err != nil { |
| return "", errQPACKDecompressionFailed |
| } |
| |
| hbit := byte(1) << prefixLen |
| isHuffman := firstByte&hbit != 0 |
| |
| // TODO: Avoid allocating here. |
| data := make([]byte, size) |
| if _, err := io.ReadFull(st, data); err != nil { |
| return "", errQPACKDecompressionFailed |
| } |
| if isHuffman { |
| // TODO: Move Huffman functions into a new package that hpack (HTTP/2) |
| // and this package can both import. Most of the hpack package isn't |
| // relevant to HTTP/3. |
| s, err := hpack.HuffmanDecodeToString(data) |
| if err != nil { |
| return "", errQPACKDecompressionFailed |
| } |
| return s, nil |
| } |
| return string(data), nil |
| } |
| |
| // appendPrefixedString appends an RFC 7541 string to st, |
| // applying Huffman encoding and setting the H bit (indicating Huffman encoding) |
| // when appropriate. |
| // |
| // The firstByte parameter includes the non-integer bits of the first byte. |
| // The other bits must be zero. |
| func appendPrefixedString(b []byte, firstByte byte, prefixLen uint8, s string) []byte { |
| huffmanLen := hpack.HuffmanEncodeLength(s) |
| if huffmanLen < uint64(len(s)) { |
| hbit := byte(1) << prefixLen |
| b = appendPrefixedInt(b, firstByte|hbit, prefixLen, int64(huffmanLen)) |
| b = hpack.AppendHuffmanString(b, s) |
| } else { |
| b = appendPrefixedInt(b, firstByte, prefixLen, int64(len(s))) |
| b = append(b, s...) |
| } |
| return b |
| } |