|
7 | 7 | package http3
|
8 | 8 |
|
9 | 9 | import (
|
| 10 | + "errors" |
10 | 11 | "io"
|
11 | 12 |
|
12 | 13 | "golang.org/x/net/http2/hpack"
|
13 | 14 | )
|
14 | 15 |
|
| 16 | +// QPACK (RFC 9204) header compression wire encoding. |
| 17 | +// https://www.rfc-editor.org/rfc/rfc9204.html |
| 18 | + |
| 19 | +// tableType is the static or dynamic table. |
| 20 | +// |
| 21 | +// The T bit in QPACK instructions indicates whether a table index refers to |
| 22 | +// the dynamic (T=0) or static (T=1) table. tableTypeForTBit and tableType.tbit |
| 23 | +// convert a T bit from the wire encoding to/from a tableType. |
| 24 | +type tableType byte |
| 25 | + |
| 26 | +const ( |
| 27 | + dynamicTable = 0x00 // T=0, dynamic table |
| 28 | + staticTable = 0xff // T=1, static table |
| 29 | +) |
| 30 | + |
| 31 | +// tableTypeForTbit returns the table type corresponding to a T bit value. |
| 32 | +// The input parameter contains a byte masked to contain only the T bit. |
| 33 | +func tableTypeForTbit(bit byte) tableType { |
| 34 | + if bit == 0 { |
| 35 | + return dynamicTable |
| 36 | + } |
| 37 | + return staticTable |
| 38 | +} |
| 39 | + |
| 40 | +// tbit produces the T bit corresponding to the table type. |
| 41 | +// The input parameter contains a byte with the T bit set to 1, |
| 42 | +// and the return is either the input or 0 depending on the table type. |
| 43 | +func (t tableType) tbit(bit byte) byte { |
| 44 | + return bit & byte(t) |
| 45 | +} |
| 46 | + |
| 47 | +// indexType indicates a literal's indexing status. |
| 48 | +// |
| 49 | +// The N bit in QPACK instructions indicates whether a literal is "never-indexed". |
| 50 | +// A never-indexed literal (N=1) must not be encoded as an indexed literal if it |
| 51 | +// forwarded on another connection. |
| 52 | +// |
| 53 | +// (See https://www.rfc-editor.org/rfc/rfc9204.html#section-7.1 for details on the |
| 54 | +// security reasons for never-indexed literals.) |
| 55 | +type indexType byte |
| 56 | + |
| 57 | +const ( |
| 58 | + mayIndex = 0x00 // N=0, not a never-indexed literal |
| 59 | + neverIndex = 0xff // N=1, never-indexed literal |
| 60 | +) |
| 61 | + |
| 62 | +// indexTypeForNBit returns the index type corresponding to a N bit value. |
| 63 | +// The input parameter contains a byte masked to contain only the N bit. |
| 64 | +func indexTypeForNBit(bit byte) indexType { |
| 65 | + if bit == 0 { |
| 66 | + return mayIndex |
| 67 | + } |
| 68 | + return neverIndex |
| 69 | +} |
| 70 | + |
| 71 | +// nbit produces the N bit corresponding to the table type. |
| 72 | +// The input parameter contains a byte with the N bit set to 1, |
| 73 | +// and the return is either the input or 0 depending on the table type. |
| 74 | +func (t indexType) nbit(bit byte) byte { |
| 75 | + return bit & byte(t) |
| 76 | +} |
| 77 | + |
| 78 | +// Indexed Field Line: |
| 79 | +// |
| 80 | +// 0 1 2 3 4 5 6 7 |
| 81 | +// +---+---+---+---+---+---+---+---+ |
| 82 | +// | 1 | T | Index (6+) | |
| 83 | +// +---+---+-----------------------+ |
| 84 | +// |
| 85 | +// https://www.rfc-editor.org/rfc/rfc9204.html#section-4.5.2 |
| 86 | + |
| 87 | +func appendIndexedFieldLine(b []byte, ttype tableType, index int) []byte { |
| 88 | + const tbit = 0b_01000000 |
| 89 | + return appendPrefixedInt(b, 0b_1000_0000|ttype.tbit(tbit), 6, int64(index)) |
| 90 | +} |
| 91 | + |
| 92 | +func (st *stream) decodeIndexedFieldLine(b byte) (itype indexType, name, value string, err error) { |
| 93 | + index, err := st.readPrefixedIntWithByte(b, 6) |
| 94 | + if err != nil { |
| 95 | + return 0, "", "", err |
| 96 | + } |
| 97 | + const tbit = 0b_0100_0000 |
| 98 | + if tableTypeForTbit(b&tbit) == staticTable { |
| 99 | + ent, err := staticTableEntry(index) |
| 100 | + if err != nil { |
| 101 | + return 0, "", "", err |
| 102 | + } |
| 103 | + return mayIndex, ent.name, ent.value, nil |
| 104 | + } else { |
| 105 | + return 0, "", "", errors.New("dynamic table is not supported yet") |
| 106 | + } |
| 107 | +} |
| 108 | + |
| 109 | +// Literal Field Line With Name Reference: |
| 110 | +// |
| 111 | +// 0 1 2 3 4 5 6 7 |
| 112 | +// +---+---+---+---+---+---+---+---+ |
| 113 | +// | 0 | 1 | N | T |Name Index (4+)| |
| 114 | +// +---+---+---+---+---------------+ |
| 115 | +// | H | Value Length (7+) | |
| 116 | +// +---+---------------------------+ |
| 117 | +// | Value String (Length bytes) | |
| 118 | +// +-------------------------------+ |
| 119 | +// |
| 120 | +// https://www.rfc-editor.org/rfc/rfc9204.html#section-4.5.4 |
| 121 | + |
| 122 | +func appendLiteralFieldLineWithNameReference(b []byte, ttype tableType, itype indexType, nameIndex int, value string) []byte { |
| 123 | + const tbit = 0b_0001_0000 |
| 124 | + const nbit = 0b_0010_0000 |
| 125 | + b = appendPrefixedInt(b, 0b_0100_0000|itype.nbit(nbit)|ttype.tbit(tbit), 4, int64(nameIndex)) |
| 126 | + b = appendPrefixedString(b, 0, 7, value) |
| 127 | + return b |
| 128 | +} |
| 129 | + |
| 130 | +func (st *stream) decodeLiteralFieldLineWithNameReference(b byte) (itype indexType, name, value string, err error) { |
| 131 | + nameIndex, err := st.readPrefixedIntWithByte(b, 4) |
| 132 | + if err != nil { |
| 133 | + return 0, "", "", err |
| 134 | + } |
| 135 | + |
| 136 | + const tbit = 0b_0001_0000 |
| 137 | + if tableTypeForTbit(b&tbit) == staticTable { |
| 138 | + ent, err := staticTableEntry(nameIndex) |
| 139 | + if err != nil { |
| 140 | + return 0, "", "", err |
| 141 | + } |
| 142 | + name = ent.name |
| 143 | + } else { |
| 144 | + return 0, "", "", errors.New("dynamic table is not supported yet") |
| 145 | + } |
| 146 | + |
| 147 | + _, value, err = st.readPrefixedString(7) |
| 148 | + if err != nil { |
| 149 | + return 0, "", "", err |
| 150 | + } |
| 151 | + |
| 152 | + const nbit = 0b_0010_0000 |
| 153 | + itype = indexTypeForNBit(b & nbit) |
| 154 | + |
| 155 | + return itype, name, value, nil |
| 156 | +} |
| 157 | + |
| 158 | +// Literal Field Line with Literal Name: |
| 159 | +// |
| 160 | +// 0 1 2 3 4 5 6 7 |
| 161 | +// +---+---+---+---+---+---+---+---+ |
| 162 | +// | 0 | 0 | 1 | N | H |NameLen(3+)| |
| 163 | +// +---+---+---+---+---+-----------+ |
| 164 | +// | Name String (Length bytes) | |
| 165 | +// +---+---------------------------+ |
| 166 | +// | H | Value Length (7+) | |
| 167 | +// +---+---------------------------+ |
| 168 | +// | Value String (Length bytes) | |
| 169 | +// +-------------------------------+ |
| 170 | +// |
| 171 | +// https://www.rfc-editor.org/rfc/rfc9204.html#section-4.5.6 |
| 172 | + |
| 173 | +func appendLiteralFieldLineWithLiteralName(b []byte, itype indexType, name, value string) []byte { |
| 174 | + const nbit = 0b_0001_0000 |
| 175 | + b = appendPrefixedString(b, 0b_0010_0000|itype.nbit(nbit), 3, name) |
| 176 | + b = appendPrefixedString(b, 0, 7, value) |
| 177 | + return b |
| 178 | +} |
| 179 | + |
| 180 | +func (st *stream) decodeLiteralFieldLineWithLiteralName(b byte) (itype indexType, name, value string, err error) { |
| 181 | + name, err = st.readPrefixedStringWithByte(b, 3) |
| 182 | + if err != nil { |
| 183 | + return 0, "", "", err |
| 184 | + } |
| 185 | + _, value, err = st.readPrefixedString(7) |
| 186 | + if err != nil { |
| 187 | + return 0, "", "", err |
| 188 | + } |
| 189 | + const nbit = 0b_0001_0000 |
| 190 | + itype = indexTypeForNBit(b & nbit) |
| 191 | + return itype, name, value, nil |
| 192 | +} |
| 193 | + |
15 | 194 | // Prefixed-integer encoding from RFC 7541, section 5.1
|
16 | 195 | //
|
17 | 196 | // Prefixed integers consist of some number of bits of data,
|
@@ -135,7 +314,9 @@ func (st *stream) readPrefixedStringWithByte(firstByte byte, prefixLen uint8) (s
|
135 | 314 | return string(data), nil
|
136 | 315 | }
|
137 | 316 |
|
138 |
| -// appendPrefixedString appends an RFC 7541 string to st. |
| 317 | +// appendPrefixedString appends an RFC 7541 string to st, |
| 318 | +// applying Huffman encoding and setting the H bit (indicating Huffman encoding) |
| 319 | +// when appropriate. |
139 | 320 | //
|
140 | 321 | // The firstByte parameter includes the non-integer bits of the first byte.
|
141 | 322 | // The other bits must be zero.
|
|
0 commit comments