1use core::fmt;
4
5use super::context::PendingSizes;
6use super::huffman::{HpackHuffmanEncodeError, HpackHuffmanEncoder};
7use super::integer::{canonical_encoded_len, write_canonical_for_valid_prefix};
8use super::representation::HpackLiteralMode;
9use super::representation_builder::{
10 HpackDynamicTableSizeUpdateBuilder, HpackIndexedFieldBuilder, HpackRepresentationBuildError,
11};
12use super::table::{
13 HPACK_STATIC_TABLE_LEN, HpackDynamicTable, HpackDynamicTableError, HpackHeaderFieldRef,
14 HpackStaticTable,
15};
16
17#[derive(Clone, Copy, Debug, Eq, PartialEq)]
19pub enum HpackEncodeLiteralName<'a> {
20 Indexed {
22 index: u64,
24 decoded: &'a [u8],
26 },
27 Literal(&'a [u8]),
29}
30
31#[derive(Clone, Copy, Debug, Eq, PartialEq)]
33pub struct HpackLiteralHuffman(u8);
34
35impl HpackLiteralHuffman {
36 pub const NONE: Self = Self(0);
38 pub const NAME: Self = Self(1);
40 pub const VALUE: Self = Self(2);
42 pub const BOTH: Self = Self(3);
44
45 pub const fn new(name: bool, value: bool) -> Self {
47 Self((name as u8) | ((value as u8) << 1))
48 }
49
50 pub const fn encodes_name(self) -> bool {
52 self.0 & Self::NAME.0 != 0
53 }
54
55 pub const fn encodes_value(self) -> bool {
57 self.0 & Self::VALUE.0 != 0
58 }
59}
60
61#[derive(Clone, Copy, Debug, Eq, PartialEq)]
63pub enum HpackEncodeError {
64 AllowedMaximumExceedsCapacity {
66 allowed: usize,
68 capacity: usize,
70 },
71 DynamicTableSizeUpdateRequired,
73 DynamicTableSizeUpdateMismatch {
75 expected: usize,
77 requested: usize,
79 },
80 DynamicTableSizeUpdateAfterField,
82 DynamicTableSizeUpdateExceedsAllowed {
84 requested: usize,
86 allowed: usize,
88 },
89 IndexNotRepresentable {
91 index: u64,
93 },
94 UnavailableIndex {
96 index: usize,
98 },
99 IndexedLiteralNameMismatch {
101 index: u64,
103 },
104 HuffmanNameForIndexedName,
106 HuffmanName(HpackHuffmanEncodeError),
108 HuffmanValue(HpackHuffmanEncodeError),
110 StringLengthNotRepresentable {
112 length: usize,
114 },
115 RepresentationLengthOverflow,
117 DynamicTableEntrySize(HpackDynamicTableError),
119 MaximumSizeNotRepresentable {
121 maximum_size: usize,
123 },
124 Representation(HpackRepresentationBuildError),
126}
127
128impl fmt::Display for HpackEncodeError {
129 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
130 match self {
131 Self::AllowedMaximumExceedsCapacity { allowed, capacity } => write!(
132 f,
133 "HPACK allowed maximum {allowed} exceeds table capacity {capacity}"
134 ),
135 Self::DynamicTableSizeUpdateRequired => {
136 f.write_str("HPACK dynamic-table size update is required before a header field")
137 }
138 Self::DynamicTableSizeUpdateMismatch {
139 expected,
140 requested,
141 } => write!(
142 f,
143 "HPACK dynamic-table update {requested} does not match required update {expected}"
144 ),
145 Self::DynamicTableSizeUpdateAfterField => {
146 f.write_str("HPACK dynamic-table update follows a header field")
147 }
148 Self::DynamicTableSizeUpdateExceedsAllowed { requested, allowed } => write!(
149 f,
150 "HPACK dynamic-table update {requested} exceeds allowed maximum {allowed}"
151 ),
152 Self::IndexNotRepresentable { index } => {
153 write!(f, "HPACK index {index} cannot be represented on the wire")
154 }
155 Self::UnavailableIndex { index } => {
156 write!(f, "HPACK index {index} is unavailable")
157 }
158 Self::IndexedLiteralNameMismatch { index } => {
159 write!(
160 f,
161 "HPACK indexed literal name at index {index} does not match"
162 )
163 }
164 Self::HuffmanNameForIndexedName => {
165 f.write_str("HPACK Huffman name encoding requires an emitted literal name")
166 }
167 Self::HuffmanName(error) => {
168 write!(f, "could not Huffman encode HPACK literal name: {error}")
169 }
170 Self::HuffmanValue(error) => {
171 write!(f, "could not Huffman encode HPACK literal value: {error}")
172 }
173 Self::StringLengthNotRepresentable { length } => write!(
174 f,
175 "HPACK string length {length} cannot be represented on the wire"
176 ),
177 Self::RepresentationLengthOverflow => {
178 f.write_str("HPACK literal representation length overflows usize")
179 }
180 Self::DynamicTableEntrySize(error) => write!(
181 f,
182 "could not calculate HPACK dynamic-table entry size: {error:?}"
183 ),
184 Self::MaximumSizeNotRepresentable { maximum_size } => write!(
185 f,
186 "HPACK maximum size {maximum_size} cannot be represented on the wire"
187 ),
188 Self::Representation(error) => {
189 write!(f, "could not build HPACK representation: {error}")
190 }
191 }
192 }
193}
194
195pub struct HpackBlockEncoder<'table, 'storage, 'entries> {
197 table: &'table mut HpackDynamicTable<'storage, 'entries>,
198 pending: &'table mut PendingSizes,
199 allowed_maximum_size: usize,
200 has_emitted_field: bool,
201}
202
203impl<'table, 'storage, 'entries> HpackBlockEncoder<'table, 'storage, 'entries> {
204 pub(super) fn from_context(
205 table: &'table mut HpackDynamicTable<'storage, 'entries>,
206 pending: &'table mut PendingSizes,
207 allowed_maximum_size: usize,
208 ) -> Self {
209 Self {
210 table,
211 pending,
212 allowed_maximum_size,
213 has_emitted_field: false,
214 }
215 }
216
217 pub const fn requires_size_update(&self) -> bool {
219 self.pending.next().is_some()
220 }
221
222 pub const fn has_emitted_field(&self) -> bool {
224 self.has_emitted_field
225 }
226
227 pub fn encode_indexed<'destination>(
229 &mut self,
230 destination: &'destination mut [u8],
231 index: u64,
232 ) -> Result<&'destination [u8], HpackEncodeError> {
233 if self.pending.next().is_some() {
234 return Err(HpackEncodeError::DynamicTableSizeUpdateRequired);
235 }
236 self.resolve(index)?;
237 let output = HpackIndexedFieldBuilder::new(destination, index)
238 .build()
239 .map_err(HpackEncodeError::Representation)?;
240 self.has_emitted_field = true;
241 Ok(output)
242 }
243
244 pub fn encode_literal<'destination>(
248 &mut self,
249 destination: &'destination mut [u8],
250 mode: HpackLiteralMode,
251 name: HpackEncodeLiteralName<'_>,
252 value: &[u8],
253 ) -> Result<&'destination [u8], HpackEncodeError> {
254 self.encode_literal_with_huffman(destination, mode, name, value, HpackLiteralHuffman::NONE)
255 }
256
257 pub fn encode_literal_with_huffman<'destination>(
261 &mut self,
262 destination: &'destination mut [u8],
263 mode: HpackLiteralMode,
264 name: HpackEncodeLiteralName<'_>,
265 value: &[u8],
266 strategy: HpackLiteralHuffman,
267 ) -> Result<&'destination [u8], HpackEncodeError> {
268 if self.pending.next().is_some() {
269 return Err(HpackEncodeError::DynamicTableSizeUpdateRequired);
270 }
271
272 let (decoded_name, name_index, literal_name) = match name {
273 HpackEncodeLiteralName::Indexed { index, decoded } => {
274 if strategy.encodes_name() {
275 return Err(HpackEncodeError::HuffmanNameForIndexedName);
276 }
277 let resolved = self.resolve(index)?;
278 if resolved.name() != decoded {
279 return Err(HpackEncodeError::IndexedLiteralNameMismatch { index });
280 }
281 (decoded, index, None)
282 }
283 HpackEncodeLiteralName::Literal(decoded) => (decoded, 0, Some(decoded)),
284 };
285 let entry_size = if mode == HpackLiteralMode::IncrementalIndexing {
286 Some(
287 HpackDynamicTable::entry_size(decoded_name, value)
288 .map_err(HpackEncodeError::DynamicTableEntrySize)?,
289 )
290 } else {
291 None
292 };
293 let name_encoded_len = match literal_name {
294 Some(decoded) if strategy.encodes_name() => {
295 HpackHuffmanEncoder::required_encoded_len(decoded)
296 .map_err(HpackEncodeError::HuffmanName)?
297 }
298 Some(decoded) => decoded.len(),
299 None => 0,
300 };
301 let value_encoded_len = if strategy.encodes_value() {
302 HpackHuffmanEncoder::required_encoded_len(value)
303 .map_err(HpackEncodeError::HuffmanValue)?
304 } else {
305 value.len()
306 };
307 let name_string_len = literal_name
308 .map(|_| string_len(name_encoded_len))
309 .transpose()?;
310 let value_string_len = string_len(value_encoded_len)?;
311 let required = canonical_encoded_len(name_index, prefix_mask(mode))
312 .checked_add(name_string_len.unwrap_or(0))
313 .and_then(|length| length.checked_add(value_string_len))
314 .ok_or(HpackEncodeError::RepresentationLengthOverflow)?;
315 if destination.len() < required {
316 return Err(HpackEncodeError::Representation(
317 HpackRepresentationBuildError::BufferTooShort {
318 required,
319 available: destination.len(),
320 },
321 ));
322 }
323
324 let representation = &mut destination[..required];
325 let name_index_len = canonical_encoded_len(name_index, prefix_mask(mode));
326 let (name_index_destination, remainder) = representation.split_at_mut(name_index_len);
327 write_canonical_for_valid_prefix(
328 name_index_destination,
329 prefix_bits(mode),
330 prefix_mask(mode),
331 high_bits(mode),
332 name_index,
333 );
334 let remainder = if let Some(decoded) = literal_name {
335 let encoded_len = name_encoded_len;
336 let string_len = name_string_len.unwrap_or(0);
337 let (string_destination, remainder) = remainder.split_at_mut(string_len);
338 write_string(
339 string_destination,
340 decoded,
341 encoded_len,
342 strategy.encodes_name(),
343 );
344 remainder
345 } else {
346 remainder
347 };
348 write_string(
349 remainder,
350 value,
351 value_encoded_len,
352 strategy.encodes_value(),
353 );
354
355 if let Some(entry_size) = entry_size {
356 self.table
357 .insert_prevalidated(decoded_name, value, entry_size);
358 }
359 self.has_emitted_field = true;
360 Ok(&*representation)
361 }
362
363 pub fn encode_dynamic_table_size_update<'destination>(
365 &mut self,
366 destination: &'destination mut [u8],
367 maximum_size: usize,
368 ) -> Result<&'destination [u8], HpackEncodeError> {
369 if self.has_emitted_field {
370 return Err(HpackEncodeError::DynamicTableSizeUpdateAfterField);
371 }
372 if let Some(expected) = self.pending.next()
373 && maximum_size != expected
374 {
375 return Err(HpackEncodeError::DynamicTableSizeUpdateMismatch {
376 expected,
377 requested: maximum_size,
378 });
379 }
380 let permitted_maximum_size = self.allowed_maximum_size.min(self.table.capacity());
381 if maximum_size > permitted_maximum_size {
382 return Err(HpackEncodeError::DynamicTableSizeUpdateExceedsAllowed {
383 requested: maximum_size,
384 allowed: permitted_maximum_size,
385 });
386 }
387 let maximum_size_u64 = u64::try_from(maximum_size)
388 .map_err(|_| HpackEncodeError::MaximumSizeNotRepresentable { maximum_size })?;
389 let required = canonical_encoded_len(maximum_size_u64, 0x1f);
390 if destination.len() < required {
391 return Err(HpackEncodeError::Representation(
392 HpackRepresentationBuildError::BufferTooShort {
393 required,
394 available: destination.len(),
395 },
396 ));
397 }
398 let output = HpackDynamicTableSizeUpdateBuilder::new(destination, maximum_size_u64)
399 .build()
400 .map_err(HpackEncodeError::Representation)?;
401 self.table.set_maximum_size_prevalidated(maximum_size);
402 if self.pending.next().is_some() {
403 self.pending.advance();
404 }
405 Ok(output)
406 }
407
408 pub fn finish(self) -> Result<(), HpackEncodeError> {
410 if self.pending.next().is_some() {
411 Err(HpackEncodeError::DynamicTableSizeUpdateRequired)
412 } else {
413 Ok(())
414 }
415 }
416
417 fn resolve(&self, index: u64) -> Result<HpackHeaderFieldRef<'_>, HpackEncodeError> {
418 let index = usize::try_from(index)
419 .map_err(|_| HpackEncodeError::IndexNotRepresentable { index })?;
420 if index == 0 {
421 return Err(HpackEncodeError::UnavailableIndex { index });
422 }
423 if index <= HPACK_STATIC_TABLE_LEN {
424 return HpackStaticTable::get(index)
425 .ok_or(HpackEncodeError::UnavailableIndex { index });
426 }
427 self.table
428 .get(index - HPACK_STATIC_TABLE_LEN)
429 .ok_or(HpackEncodeError::UnavailableIndex { index })
430 }
431}
432
433fn prefix_bits(mode: HpackLiteralMode) -> u8 {
434 match mode {
435 HpackLiteralMode::IncrementalIndexing => 6,
436 HpackLiteralMode::WithoutIndexing | HpackLiteralMode::NeverIndexed => 4,
437 }
438}
439
440fn prefix_mask(mode: HpackLiteralMode) -> u8 {
441 (1 << prefix_bits(mode)) - 1
442}
443
444fn high_bits(mode: HpackLiteralMode) -> u8 {
445 match mode {
446 HpackLiteralMode::IncrementalIndexing => 0x40,
447 HpackLiteralMode::WithoutIndexing => 0,
448 HpackLiteralMode::NeverIndexed => 0x10,
449 }
450}
451
452fn string_len(encoded_len: usize) -> Result<usize, HpackEncodeError> {
453 let encoded_len_u64 =
454 u64::try_from(encoded_len).map_err(|_| HpackEncodeError::StringLengthNotRepresentable {
455 length: encoded_len,
456 })?;
457 canonical_encoded_len(encoded_len_u64, 0x7f)
458 .checked_add(encoded_len)
459 .ok_or(HpackEncodeError::RepresentationLengthOverflow)
460}
461
462fn write_string(destination: &mut [u8], decoded: &[u8], encoded_len: usize, huffman: bool) {
463 let encoded_len_u64 = encoded_len as u64;
464 let length_len = canonical_encoded_len(encoded_len_u64, 0x7f);
465 let (length_destination, payload_destination) = destination.split_at_mut(length_len);
466 write_canonical_for_valid_prefix(
467 length_destination,
468 7,
469 0x7f,
470 if huffman { 0x80 } else { 0 },
471 encoded_len_u64,
472 );
473 if huffman {
474 HpackHuffmanEncoder::encode_prevalidated(decoded, payload_destination);
475 } else {
476 payload_destination.copy_from_slice(decoded);
477 }
478}