1use crate::{ForbiddenHeaderFilter, WasiHttpHooks};
2use http::header::Entry;
3use http::{HeaderMap, HeaderName, HeaderValue};
4use std::fmt;
5use std::ops::Deref;
6use std::sync::Arc;
7use wasmtime::Result;
8
9#[derive(Debug, Clone)]
25pub struct FieldMap {
26 map: Arc<HeaderMap>,
27 limit: Limit,
28 size: usize,
29}
30
31#[derive(Debug, Clone)]
32enum Limit {
33 Mutable(usize),
34 Immutable,
35}
36
37impl Default for FieldMap {
38 fn default() -> Self {
39 Self {
40 map: Arc::new(HeaderMap::new()),
41 size: 0,
42 limit: Limit::Immutable,
43 }
44 }
45}
46
47impl FieldMap {
48 pub fn new_immutable(hooks: &mut dyn WasiHttpHooks, mut map: HeaderMap) -> Self {
54 remove_forbidden(&mut map, &hooks.forbidden_header_filter());
57
58 let size = Self::content_size(&map);
59 Self {
60 map: Arc::new(map),
61 size,
62 limit: Limit::Immutable,
63 }
64 }
65
66 pub fn new_mutable(limit: usize) -> Self {
71 Self {
72 map: Arc::new(HeaderMap::new()),
73 size: 0,
74 limit: Limit::Mutable(limit),
75 }
76 }
77
78 fn content_size(map: &HeaderMap) -> usize {
81 let mut sum = 0;
82 for key in map.keys() {
83 sum += header_name_size(key);
84 }
85 for value in map.values() {
86 sum += header_value_size(value);
87 }
88 sum
89 }
90
91 pub fn set(
99 &mut self,
100 hooks: &mut dyn WasiHttpHooks,
101 key: String,
102 values: Vec<Vec<u8>>,
103 ) -> Result<(), FieldMapError> {
104 let key = key.parse()?;
105 if (hooks.forbidden_header_filter())(&key) {
106 return Err(FieldMapError::Forbidden);
107 }
108 let (map, limit, size) = self.mutable()?;
109
110 if key == http::header::CONTENT_LENGTH && values.len() > 1 {
112 return Err(FieldMapError::InvalidHeaderValue);
113 }
114 let values = values
115 .into_iter()
116 .map(|v| parse_header_value(&key, v))
117 .collect::<Result<Vec<_>, _>>()?;
118
119 let key_size = header_name_size(&key);
120 let prev_values_size: usize = map.get_all(&key).iter().map(header_value_size).sum();
121 let mut new_size = *size - prev_values_size;
122 new_size += values.iter().map(header_value_size).sum::<usize>();
123
124 let mut values = values.into_iter();
125 let Some(first) = values.next() else {
126 if map.remove(&key).is_some() {
127 *size = new_size - key_size;
128 }
129 return Ok(());
130 };
131 if !map.contains_key(&key) {
132 new_size += key_size;
133 }
134 if new_size > limit {
135 return Err(FieldMapError::TotalSizeTooBig);
136 }
137
138 map.remove(&key);
140 let Entry::Vacant(entry) = map
142 .try_entry(key)
143 .map_err(|_| FieldMapError::TooManyFields)?
144 else {
145 unreachable!();
147 };
148 let mut entry = entry.try_insert_entry(first)?;
149 for value in values {
150 entry.append(value);
151 }
152 *size = new_size;
153 Ok(())
154 }
155
156 pub fn remove_all(
160 &mut self,
161 hooks: &mut dyn WasiHttpHooks,
162 key: String,
163 ) -> Result<Vec<HeaderValue>, FieldMapError> {
164 let key = key.parse()?;
165 if (hooks.forbidden_header_filter())(&key) {
166 return Err(FieldMapError::Forbidden);
167 }
168 let (map, _limit, size) = self.mutable()?;
169 let values = map.get_all(&key).iter().cloned().collect::<Vec<_>>();
171 if values.is_empty() {
172 return Ok(Vec::new());
173 }
174 let mut removed = header_name_size(&key);
175 for v in values.iter() {
176 removed += header_value_size(v);
177 }
178 let _ = map.remove(&key);
179 *size -= removed;
180 Ok(values)
181 }
182
183 fn mutable(&mut self) -> Result<(&mut HeaderMap, usize, &mut usize), FieldMapError> {
184 match self.limit {
185 Limit::Immutable => Err(FieldMapError::Immutable),
186 Limit::Mutable(limit) => Ok((Arc::make_mut(&mut self.map), limit, &mut self.size)),
187 }
188 }
189
190 pub fn append(
195 &mut self,
196 hooks: &mut dyn WasiHttpHooks,
197 key: String,
198 value: Vec<u8>,
199 ) -> Result<bool, FieldMapError> {
200 let key = key.parse()?;
201 if (hooks.forbidden_header_filter())(&key) {
202 return Err(FieldMapError::Forbidden);
203 }
204 let value = parse_header_value(&key, value)?;
205 if key == http::header::CONTENT_LENGTH && self.contains_key(&key) {
209 return Err(FieldMapError::InvalidHeaderValue);
210 }
211 self.append_raw(key, value)
212 }
213
214 pub fn append_raw(
220 &mut self,
221 key: HeaderName,
222 value: HeaderValue,
223 ) -> Result<bool, FieldMapError> {
224 let (map, limit, size) = self.mutable()?;
225 let key_size = header_name_size(&key);
226 let val_size = header_value_size(&value);
227 let new_size = if !map.contains_key(&key) {
228 *size + key_size + val_size
229 } else {
230 *size + val_size
231 };
232 if new_size > limit {
233 return Err(FieldMapError::TotalSizeTooBig);
234 }
235 let already_present = map.try_append(key, value)?;
236 *size = new_size;
237 Ok(already_present)
238 }
239
240 pub fn set_mutable(&mut self, limit: usize) {
243 self.limit = Limit::Mutable(limit);
244 }
245
246 pub fn set_immutable(&mut self) {
248 self.limit = Limit::Immutable;
249 }
250}
251
252fn header_name_size(name: &HeaderName) -> usize {
257 name.as_str().len() + size_of::<HeaderName>()
258}
259
260fn header_value_size(value: &HeaderValue) -> usize {
266 value.len() + size_of::<HeaderValue>()
267}
268
269impl Deref for FieldMap {
272 type Target = HeaderMap;
273
274 fn deref(&self) -> &HeaderMap {
275 &self.map
276 }
277}
278
279impl From<FieldMap> for HeaderMap {
280 fn from(map: FieldMap) -> Self {
281 Arc::unwrap_or_clone(map.map)
282 }
283}
284
285#[derive(Debug, PartialEq, Eq, Clone, Copy)]
287pub enum FieldMapError {
288 Immutable,
290 TooManyFields,
295 TotalSizeTooBig,
297 InvalidHeaderName,
299 InvalidHeaderValue,
301 Forbidden,
303}
304
305impl fmt::Display for FieldMapError {
306 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
307 let s = match self {
308 FieldMapError::Immutable => "cannot mutate an immutable field map",
309 FieldMapError::TooManyFields => "too many fields in the field map",
310 FieldMapError::TotalSizeTooBig => "total size of fields exceeds limit",
311 FieldMapError::InvalidHeaderName => "invalid header name",
312 FieldMapError::InvalidHeaderValue => "invalid header value",
313 FieldMapError::Forbidden => "forbidden header name",
314 };
315 f.write_str(s)
316 }
317}
318
319impl std::error::Error for FieldMapError {}
320
321impl From<http::header::MaxSizeReached> for FieldMapError {
322 fn from(_: http::header::MaxSizeReached) -> Self {
323 Self::TooManyFields
324 }
325}
326
327impl From<http::header::InvalidHeaderName> for FieldMapError {
328 fn from(_: http::header::InvalidHeaderName) -> Self {
329 Self::InvalidHeaderName
330 }
331}
332
333impl From<http::header::InvalidHeaderValue> for FieldMapError {
334 fn from(_: http::header::InvalidHeaderValue) -> Self {
335 Self::InvalidHeaderValue
336 }
337}
338
339fn parse_header_value(
340 name: &http::HeaderName,
341 value: Vec<u8>,
342) -> Result<http::HeaderValue, FieldMapError> {
343 if name == http::header::CONTENT_LENGTH {
344 let s = str::from_utf8(value.as_ref()).or(Err(FieldMapError::InvalidHeaderValue))?;
345 if s.is_empty() || !s.bytes().all(|b| b.is_ascii_digit()) {
349 return Err(FieldMapError::InvalidHeaderValue);
350 }
351 let v: u64 = s.parse().or(Err(FieldMapError::InvalidHeaderValue))?;
352 Ok(v.into())
353 } else {
354 let value = value.try_into()?;
355 Ok(value)
356 }
357}
358
359#[cfg(test)]
360mod tests {
361 use super::{FieldMap, FieldMapError, parse_header_value};
362 use crate::default_hooks;
363 use http::header::{CONTENT_LENGTH, CONTENT_TYPE};
364 use http::{HeaderName, HeaderValue};
365
366 #[test]
367 fn test_immutable() {
368 let mut map = FieldMap::default();
369 assert_eq!(
370 map.set(default_hooks(), "foo".to_owned(), vec![b"bar".to_vec()]),
371 Err(FieldMapError::Immutable)
372 );
373 assert_eq!(
374 map.append(default_hooks(), "foo".to_owned(), b"bar".to_vec()),
375 Err(FieldMapError::Immutable)
376 );
377 assert_eq!(
378 map.remove_all(default_hooks(), "foo".to_owned()),
379 Err(FieldMapError::Immutable)
380 );
381 }
382
383 #[test]
384 fn test_limits() {
385 let mut map = FieldMap::new_mutable(100);
386 loop {
387 match map.append(default_hooks(), "foo".to_owned(), b"bar".to_vec()) {
388 Ok(_) => {}
389 Err(FieldMapError::TotalSizeTooBig) => break,
390 Err(e) => panic!("unexpected error: {e}"),
391 }
392 }
393
394 map = FieldMap::new_mutable(100);
395 for i in 0.. {
396 match map.set(
397 default_hooks(),
398 "foo".to_owned(),
399 (0..i).map(|j| format!("bar{j}").into_bytes()).collect(),
400 ) {
401 Ok(_) => {}
402 Err(FieldMapError::TotalSizeTooBig) => break,
403 Err(e) => panic!("unexpected error: {e}"),
404 }
405 }
406
407 map = FieldMap::new_mutable(100);
408 for i in 0.. {
409 match map.set(default_hooks(), format!("foo{i}"), vec![b"bar".to_vec()]) {
410 Ok(_) => {}
411 Err(FieldMapError::TotalSizeTooBig) => break,
412 Err(e) => panic!("unexpected error: {e}"),
413 }
414 }
415 }
416
417 #[test]
418 fn test_size() -> Result<(), FieldMapError> {
419 let mut map = FieldMap::new_mutable(2000);
420 let name = "foo".to_owned();
421 let hooks = default_hooks();
422
423 map.append(hooks, name.clone(), b"bar".to_vec())?;
424 assert!(map.size > 0);
425 map.remove_all(hooks, name.clone())?;
426 assert_eq!(map.size, 0);
427
428 map.set(hooks, name.clone(), vec![b"bar".to_vec()])?;
429 assert!(map.size > 0);
430 map.remove_all(hooks, name.clone())?;
431 assert_eq!(map.size, 0);
432
433 map.set(hooks, name.clone(), vec![])?;
434 assert_eq!(map.size, 0);
435 map.set(hooks, name.clone(), vec![b"bar".to_vec()])?;
436 assert!(map.size > 0);
437 map.set(hooks, name.clone(), vec![])?;
438 assert_eq!(map.size, 0);
439
440 map.set(hooks, name.clone(), vec![b"bar".to_vec()])?;
441 assert!(map.size > 0);
442 map.set(hooks, name.clone(), vec![b"bar".to_vec(), b"baz".to_vec()])?;
443 assert!(map.size > 0);
444 map.remove_all(hooks, name.clone())?;
445 assert_eq!(map.size, 0);
446
447 Ok(())
448 }
449
450 #[test]
451 fn set_empty_over_limit() -> Result<(), FieldMapError> {
452 let mut map = FieldMap::new_mutable(usize::MAX);
453 let hooks = default_hooks();
454 map.set(hooks, "foo".to_owned(), vec![b"bar".to_vec()])?;
455 map.set_mutable(0);
456
457 map.set(hooks, "new".to_owned(), vec![])?;
458 map.set(hooks, "foo".to_owned(), vec![])?;
459 assert_eq!(map.size, 0);
460
461 Ok(())
462 }
463
464 #[test]
465 fn append_raw_too_many_fields_keeps_size() {
466 let mut map = FieldMap::new_mutable(usize::MAX);
467 let mut i = 0;
468 let err = loop {
469 let name = HeaderName::try_from(format!("foo{i}")).unwrap();
470 match map.append_raw(name, HeaderValue::from_static("bar")) {
471 Ok(_) => i += 1,
472 Err(e) => break e,
473 }
474 };
475 assert_eq!(err, FieldMapError::TooManyFields);
476 assert_eq!(map.size, FieldMap::content_size(&map));
477 }
478
479 #[test]
480 fn content_length_rejects_non_digits() {
481 assert!(parse_header_value(&CONTENT_LENGTH, b"0".to_vec()).is_ok());
482 assert!(parse_header_value(&CONTENT_LENGTH, b"1234".to_vec()).is_ok());
483
484 assert!(parse_header_value(&CONTENT_LENGTH, b"+5".to_vec()).is_err());
486 assert!(parse_header_value(&CONTENT_LENGTH, b"-5".to_vec()).is_err());
487 assert!(parse_header_value(&CONTENT_LENGTH, b" 5".to_vec()).is_err());
488 assert!(parse_header_value(&CONTENT_LENGTH, b"".to_vec()).is_err());
489
490 assert!(parse_header_value(&CONTENT_TYPE, b"text/plain".to_vec()).is_ok());
492 }
493
494 #[test]
495 fn content_length_rejects_multiple_values() {
496 let hooks = default_hooks();
497 let cl = || CONTENT_LENGTH.as_str().to_owned();
498
499 let mut map = FieldMap::new_mutable(2000);
500 map.append(hooks, cl(), b"5".to_vec()).unwrap();
501 assert_eq!(
502 map.append(hooks, cl(), b"6".to_vec()),
503 Err(FieldMapError::InvalidHeaderValue)
504 );
505 assert_eq!(
506 map.append(hooks, cl(), b"5".to_vec()),
507 Err(FieldMapError::InvalidHeaderValue)
508 );
509 assert_eq!(
510 map.append(hooks, "Content-Length".to_owned(), b"6".to_vec()),
511 Err(FieldMapError::InvalidHeaderValue)
512 );
513 assert_eq!(map.get_all(CONTENT_LENGTH).iter().count(), 1);
514 assert_eq!(map.size, FieldMap::content_size(&map.map));
515
516 assert_eq!(
517 map.set(hooks, cl(), vec![b"5".to_vec(), b"6".to_vec()]),
518 Err(FieldMapError::InvalidHeaderValue)
519 );
520 assert_eq!(
521 map.set(hooks, cl(), vec![b"5".to_vec(), b"5".to_vec()]),
522 Err(FieldMapError::InvalidHeaderValue)
523 );
524 assert_eq!(map[CONTENT_LENGTH], "5");
525
526 map.set(hooks, cl(), vec![b"6".to_vec()]).unwrap();
528 assert_eq!(map[CONTENT_LENGTH], "6");
529 assert_eq!(map.get_all(CONTENT_LENGTH).iter().count(), 1);
530 assert_eq!(map.size, FieldMap::content_size(&map.map));
531 }
532
533 #[test]
534 fn full_map_is_not_invalid_syntax() {
535 let mut map = FieldMap::new_mutable(usize::MAX);
538 let mut i = 0;
539 let err = loop {
540 assert_eq!(map.size, FieldMap::content_size(&map.map));
541 match map.set(default_hooks(), format!("k{i}"), vec![b"v".to_vec()]) {
542 Ok(()) => i += 1,
543 Err(e) => break e,
544 }
545 };
546 assert_eq!(err, FieldMapError::TooManyFields);
547 assert_eq!(map.size, FieldMap::content_size(&map.map));
548
549 assert_eq!(
550 map.set(
551 default_hooks(),
552 "new".to_owned(),
553 vec![b"a".to_vec(), b"b".to_vec()],
554 ),
555 Err(FieldMapError::TooManyFields)
556 );
557 assert!(!map.contains_key("new"));
558 assert_eq!(map.size, FieldMap::content_size(&map.map));
559
560 assert_eq!(
561 map.set(
562 default_hooks(),
563 "k0".to_owned(),
564 vec![b"foo".to_vec(), b"bar".to_vec()]
565 ),
566 Ok(())
567 );
568 assert_eq!(map.get("k0"), Some(&HeaderValue::from_static("foo")));
569 assert_eq!(
570 map.get_all("k0").iter().collect::<Vec<_>>(),
571 [
572 HeaderValue::from_static("foo"),
573 HeaderValue::from_static("bar")
574 ]
575 );
576 assert_eq!(map.size, FieldMap::content_size(&map.map));
577
578 assert_eq!(map.set(default_hooks(), "k0".to_owned(), vec![]), Ok(()));
579 assert!(!map.contains_key("k0"));
580 assert_eq!(map.size, FieldMap::content_size(&map.map));
581
582 assert_eq!(
583 map.set(default_hooks(), "k0".to_owned(), vec![b"v".to_vec()]),
584 Ok(())
585 );
586 assert_eq!(map.get("k0"), Some(&HeaderValue::from_static("v")));
587 assert_eq!(map.size, FieldMap::content_size(&map.map));
588
589 assert_eq!(
591 map.remove_all(default_hooks(), "k0".to_owned())
592 .map(|v| v.len()),
593 Ok(1)
594 );
595 assert_eq!(map.size, FieldMap::content_size(&map.map));
596 }
597}
598
599pub(crate) fn remove_forbidden(map: &mut HeaderMap, is_forbidden: &ForbiddenHeaderFilter) {
601 let forbidden_keys = Vec::from_iter(map.keys().filter(|name| is_forbidden(name)).cloned());
602 for name in forbidden_keys {
603 map.remove(&name);
604 }
605}