netget/server/http3/
wire.rs1use anyhow::{ensure, Context, Result};
3use http::{HeaderMap, HeaderName, HeaderValue};
4use serde_json::{Map, Value};
5pub const MAX_BODY: usize = 8 * 1024 * 1024;
6pub const MAX_HEADERS: usize = 32 * 1024;
7pub const MAX_INBOUND_BYTES: usize = MAX_BODY + 2 * MAX_HEADERS;
8fn header_size(headers: &HeaderMap) -> usize {
9 headers.iter().fold(0usize, |size, (key, value)| {
10 size.saturating_add(key.as_str().len() + value.as_bytes().len() + 32)
11 })
12}
13pub fn check_headers(headers: &HeaderMap) -> Result<()> {
14 check_headers_for(headers, false)
15}
16pub fn check_request_headers(headers: &HeaderMap) -> Result<()> {
17 check_headers_for(headers, true)
18}
19fn check_headers_for(headers: &HeaderMap, request: bool) -> Result<()> {
20 ensure!(
21 header_size(headers) <= MAX_HEADERS,
22 "HTTP3 field section exceeds 32 KiB"
23 );
24 for (k, value) in headers {
25 ensure!(
26 !matches!(
27 k.as_str(),
28 "connection" | "keep-alive" | "proxy-connection" | "transfer-encoding" | "upgrade"
29 ),
30 "HTTP3 forbids connection-specific headers"
31 );
32 if k == "te" {
33 ensure!(
34 request && value.to_str()?.trim().eq_ignore_ascii_case("trailers"),
35 "HTTP3 TE is allowed only in request headers with value trailers"
36 );
37 }
38 }
39 Ok(())
40}
41pub fn check_field_section(headers: &HeaderMap, pseudo: &[(&str, &str)]) -> Result<()> {
44 check_headers_for(headers, pseudo.iter().any(|(key, _)| *key == ":method"))?;
45 let size = pseudo
46 .iter()
47 .fold(header_size(headers), |size, (key, value)| {
48 size.saturating_add(key.len() + value.len() + 32)
49 });
50 ensure!(size <= MAX_HEADERS, "HTTP3 field section exceeds 32 KiB");
51 Ok(())
52}
53pub fn parse_headers(value: &Value) -> Result<HeaderMap> {
54 parse_headers_for(value, false)
55}
56pub fn parse_request_headers(value: &Value) -> Result<HeaderMap> {
57 parse_headers_for(value, true)
58}
59fn parse_headers_for(value: &Value, request: bool) -> Result<HeaderMap> {
60 let mut headers = HeaderMap::new();
61 if value.is_null() {
62 return Ok(headers);
63 }
64 let object = value
65 .as_object()
66 .context("HTTP3 headers must be an object")?;
67 let mut size = 0usize;
68 for (key, value) in object {
69 let name = HeaderName::from_bytes(key.as_bytes())?;
70 let values: Vec<&str> = if let Some(s) = value.as_str() {
71 vec![s]
72 } else {
73 value
74 .as_array()
75 .context("Header must be a string or string array")?
76 .iter()
77 .map(|v| v.as_str().context("Header array must contain strings"))
78 .collect::<Result<_>>()?
79 };
80 for v in values {
81 size = size.saturating_add(key.len() + v.len() + 32);
82 ensure!(size <= MAX_HEADERS, "HTTP3 field section exceeds 32 KiB");
83 headers.append(name.clone(), HeaderValue::from_str(v)?);
84 }
85 }
86 check_headers_for(&headers, request)?;
87 Ok(headers)
88}
89pub fn header_json(headers: &HeaderMap) -> Result<Map<String, Value>> {
90 header_json_for(headers, false)
91}
92pub fn request_header_json(headers: &HeaderMap) -> Result<Map<String, Value>> {
93 header_json_for(headers, true)
94}
95fn header_json_for(headers: &HeaderMap, request: bool) -> Result<Map<String, Value>> {
96 check_headers_for(headers, request)?;
97 let mut object = Map::new();
98 for key in headers.keys() {
99 let values: Vec<_> = headers
100 .get_all(key)
101 .iter()
102 .map(|v| v.to_str().map(str::to_owned))
103 .collect::<std::result::Result<_, _>>()?;
104 object.insert(
105 key.to_string(),
106 if values.len() == 1 {
107 Value::String(values[0].clone())
108 } else {
109 serde_json::json!(values)
110 },
111 );
112 }
113 Ok(object)
114}
115pub fn validate_length(headers: &HeaderMap, actual: usize) -> Result<()> {
116 for value in headers.get_all("content-length") {
117 ensure!(
118 value.to_str()?.parse::<usize>()? == actual,
119 "HTTP3 content-length does not match body"
120 );
121 }
122 Ok(())
123}
124
125pub struct EndpointOwner(pub crate::utils::quic::EndpointGuard);
128impl std::ops::Deref for EndpointOwner {
129 type Target = quinn::Endpoint;
130 fn deref(&self) -> &Self::Target {
131 &self.0 .0
132 }
133}
134impl Drop for EndpointOwner {
135 fn drop(&mut self) {
136 self.0 .0.close(0x100u32.into(), b"HTTP3 endpoint stopped");
137 }
138}
139pub struct ConnectionOwner(pub crate::utils::quic::ConnectionGuard);
140impl std::ops::Deref for ConnectionOwner {
141 type Target = quinn::Connection;
142 fn deref(&self) -> &Self::Target {
143 &self.0 .0
144 }
145}
146impl Drop for ConnectionOwner {
147 fn drop(&mut self) {
148 self.0
149 .0
150 .close(0x100u32.into(), b"HTTP3 connection stopped");
151 }
152}