From 82bdfbca560e27a68b40d650588a8c877af08985 Mon Sep 17 00:00:00 2001 From: Gleb Pomykalov Date: Thu, 20 Jul 2023 19:15:41 +0200 Subject: [PATCH] use http headers try_ methods --- Cargo.toml | 3 ++ examples/upgrades.rs | 3 +- src/client/client.rs | 11 ++++- src/error.rs | 46 ++++++++++++++++++ src/ext.rs | 12 +++-- src/ffi/error.rs | 2 + src/ffi/http_types.rs | 28 +++++++++-- src/headers.rs | 18 +++++-- src/lib.rs | 1 + src/proto/h1/conn.rs | 20 +++++--- src/proto/h1/role.rs | 103 +++++++++++++++++++++-------------------- src/proto/h2/client.rs | 2 +- src/proto/h2/server.rs | 6 +-- tests/server.rs | 2 +- tests/support/mod.rs | 2 +- 15 files changed, 181 insertions(+), 78 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f7696c3e19..32657b2d32 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -256,3 +256,6 @@ required-features = ["full"] name = "server" path = "tests/server.rs" required-features = ["full"] + +[patch.crates-io] +http = { git = "https://github.com/glebpom/http", branch = "failable-headers-allocations" } diff --git a/examples/upgrades.rs b/examples/upgrades.rs index 38cfded386..6d8ef8d166 100644 --- a/examples/upgrades.rs +++ b/examples/upgrades.rs @@ -66,7 +66,8 @@ async fn server_upgrade(mut req: Request) -> Result> { // made-up 'foobar' protocol. *res.status_mut() = StatusCode::SWITCHING_PROTOCOLS; res.headers_mut() - .insert(UPGRADE, HeaderValue::from_static("foobar")); + .try_insert(UPGRADE, HeaderValue::from_static("foobar")) + .expect("FIXME"); Ok(res) } diff --git a/src/client/client.rs b/src/client/client.rs index bf4db79fde..2292e17378 100644 --- a/src/client/client.rs +++ b/src/client/client.rs @@ -257,7 +257,8 @@ where if self.config.set_host { let uri = req.uri().clone(); - req.headers_mut().entry(HOST).or_insert_with(|| { + let entry = headers_op!(req.headers_mut().try_entry(HOST), error); + headers_op!(entry.or_try_insert_with(|| { let hostname = uri.host().expect("authority implies host"); if let Some(port) = get_non_default_port(&uri) { let s = format!("{}:{}", hostname, port); @@ -266,7 +267,7 @@ where HeaderValue::from_str(hostname) } .expect("uri host is valid header value") - }); + }), capacity); } // CONNECT always sends authority-form, so check it first... @@ -752,6 +753,12 @@ enum ClientError { }, } +impl From for ClientError { + fn from(e: crate::error::Parse) -> Self { + ClientError::Normal(e.into()) + } +} + impl ClientError { fn map_with_reused(conn_reused: bool) -> impl Fn((crate::Error, Option>)) -> Self { move |(err, orig_req)| { diff --git a/src/error.rs b/src/error.rs index 5beedeb8b2..adea7fed98 100644 --- a/src/error.rs +++ b/src/error.rs @@ -65,6 +65,9 @@ pub(super) enum Kind { /// A general error from h2. #[cfg(feature = "http2")] Http2, + + /// Error with HTTP request or response + Http(http::Error), } #[derive(Debug)] @@ -518,6 +521,7 @@ impl Error { Kind::User(User::DispatchGone) => "dispatch task is gone", #[cfg(feature = "ffi")] Kind::User(User::AbortedByCallback) => "operation aborted by an application callback", + Kind::Http(ref _e) => "FIXME: HTTP request or response building error" } } } @@ -559,6 +563,20 @@ impl From for Error { } } +#[doc(hidden)] +impl From for Error { + fn from(err: http::CapacityOverflow) -> Error { + Error::new(Kind::Http(err.into())) + } +} + +#[doc(hidden)] +impl From for Error { + fn from(err: http::Error) -> Error { + Error::new(Kind::Http(err)) + } +} + #[cfg(feature = "http1")] impl Parse { pub(crate) fn content_length_invalid() -> Self { @@ -628,6 +646,34 @@ impl fmt::Display for TimedOut { impl StdError for TimedOut {} +macro_rules! headers_op { + ($op:expr, capacity) => { + match $op { + Err(http::CapacityOverflow { .. }) => { + return Err(crate::error::Parse::TooLarge.into()); + }, + Ok(r) => r, + } + }; + ($op:expr, error) => { + match $op { + Err(e) if e.is::() => { + return Err(crate::error::Parse::TooLarge.into()); + }, + Err(e) if e.is::() => { + // FIXME! + return Err(crate::error::Parse::Internal.into()); + }, + Err(_e) => { + // FIXME! + return Err(crate::error::Parse::Internal.into()); + }, + Ok(r) => r, + } + }; +} + + #[cfg(test)] mod tests { use super::*; diff --git a/src/ext.rs b/src/ext.rs index 224206dd66..8c6ea4d59e 100644 --- a/src/ext.rs +++ b/src/ext.rs @@ -5,7 +5,7 @@ use bytes::Bytes; use http::header::HeaderName; #[cfg(feature = "http1")] use http::header::{IntoHeaderName, ValueIter}; -use http::HeaderMap; +use http::{CapacityOverflow, HeaderMap}; #[cfg(feature = "ffi")] use std::collections::HashMap; #[cfg(feature = "http2")] @@ -119,15 +119,17 @@ impl HeaderCaseMap { } #[cfg(any(test, feature = "ffi"))] - pub(crate) fn insert(&mut self, name: HeaderName, orig: Bytes) { - self.0.insert(name, orig); + pub(crate) fn try_insert(&mut self, name: HeaderName, orig: Bytes) -> Result<(), CapacityOverflow> { + self.0.try_insert(name, orig)?; + Ok(()) } - pub(crate) fn append(&mut self, name: N, orig: Bytes) + pub(crate) fn try_append(&mut self, name: N, orig: Bytes) -> Result<(), CapacityOverflow> where N: IntoHeaderName, { - self.0.append(name, orig); + self.0.try_append(name, orig)?; + Ok(()) } } diff --git a/src/ffi/error.rs b/src/ffi/error.rs index 015e595aee..b3017b7114 100644 --- a/src/ffi/error.rs +++ b/src/ffi/error.rs @@ -24,6 +24,8 @@ pub enum hyper_code { HYPERE_FEATURE_NOT_ENABLED, /// The peer sent an HTTP message that could not be parsed. HYPERE_INVALID_PEER_MESSAGE, + /// HTTP headers reached the maximum capacity + HYPERE_HEADERS_CAPACITY, } // ===== impl hyper_error ===== diff --git a/src/ffi/http_types.rs b/src/ffi/http_types.rs index ea10f139cb..d0a62cdf09 100644 --- a/src/ffi/http_types.rs +++ b/src/ffi/http_types.rs @@ -468,8 +468,18 @@ ffi_fn! { let headers = non_null!(&mut *headers ?= hyper_code::HYPERE_INVALID_ARG); match unsafe { raw_name_value(name, name_len, value, value_len) } { Ok((name, value, orig_name)) => { - headers.headers.insert(&name, value); - headers.orig_casing.insert(name.clone(), orig_name.clone()); + match headers.headers.try_insert(&name, value) { + Err(http::CapacityOverflow { .. }) => { + return hyper_code::HYPERE_HEADERS_CAPACITY; + } + Ok(_) => {}, + } + match headers.orig_casing.try_insert(name.clone(), orig_name.clone()) { + Err(http::CapacityOverflow { .. }) => { + return hyper_code::HYPERE_HEADERS_CAPACITY; + } + Ok(()) => {}, + } headers.orig_order.insert(name); hyper_code::HYPERE_OK } @@ -488,8 +498,18 @@ ffi_fn! { match unsafe { raw_name_value(name, name_len, value, value_len) } { Ok((name, value, orig_name)) => { - headers.headers.append(&name, value); - headers.orig_casing.append(&name, orig_name.clone()); + match headers.headers.try_append(&name, value) { + Err(http::CapacityOverflow { .. }) => { + return hyper_code::HYPERE_HEADERS_CAPACITY; + } + Ok(_) => {}, + } + match headers.orig_casing.try_append(&name, orig_name.clone()) { + Err(http::CapacityOverflow { .. }) => { + return hyper_code::HYPERE_HEADERS_CAPACITY; + } + Ok(()) => {}, + } headers.orig_order.append(name); hyper_code::HYPERE_OK } diff --git a/src/headers.rs b/src/headers.rs index 8407be185f..2839f0e037 100644 --- a/src/headers.rs +++ b/src/headers.rs @@ -2,7 +2,7 @@ use bytes::BytesMut; use http::header::CONTENT_LENGTH; use http::header::{HeaderValue, ValueIter}; -use http::HeaderMap; +use http::{CapacityOverflow, HeaderMap}; #[cfg(all(feature = "http2", feature = "client"))] use http::Method; @@ -100,10 +100,18 @@ pub(super) fn method_has_defined_payload_semantics(method: &Method) -> bool { } #[cfg(feature = "http2")] -pub(super) fn set_content_length_if_missing(headers: &mut HeaderMap, len: u64) { - headers - .entry(CONTENT_LENGTH) - .or_insert_with(|| HeaderValue::from(len)); +pub(super) fn set_content_length_if_missing(headers: &mut HeaderMap, len: u64) -> Result<(), CapacityOverflow> { + match headers + .try_entry(CONTENT_LENGTH){ + Err(e) if e.is::() => return Err(CapacityOverflow::new()), + Err(_) => { + unreachable!() + }, + Ok(e) => { + e.or_try_insert_with(|| HeaderValue::from(len))?; + } + }; + Ok(()) } #[cfg(feature = "http1")] diff --git a/src/lib.rs b/src/lib.rs index e5e4cfc56e..9c7c0df4cf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -77,6 +77,7 @@ mod cfg; #[macro_use] mod common; pub mod body; +#[macro_use] mod error; pub mod ext; #[cfg(test)] diff --git a/src/proto/h1/conn.rs b/src/proto/h1/conn.rs index 5ebff2803e..fe5824a827 100644 --- a/src/proto/h1/conn.rs +++ b/src/proto/h1/conn.rs @@ -551,7 +551,11 @@ where self.state.busy(); } - self.enforce_version(&mut head); + if let Err(err) = self.enforce_version(&mut head) { + self.state.error = Some(err.into()); + self.state.writing = Writing::Closed; + return None; + } let buf = self.io.headers_buf(); match super::role::encode_headers::( @@ -587,7 +591,7 @@ where } // Fix keep-alive when Connection: keep-alive header is not present - fn fix_keep_alive(&mut self, head: &mut MessageHead) { + fn fix_keep_alive(&mut self, head: &mut MessageHead) -> Result<(), http::Error> { let outgoing_is_keep_alive = head .headers .get(CONNECTION) @@ -604,20 +608,22 @@ where Version::HTTP_11 => { if self.state.wants_keep_alive() { head.headers - .insert(CONNECTION, HeaderValue::from_static("keep-alive")); + .try_insert(CONNECTION, HeaderValue::from_static("keep-alive"))?; } } _ => (), } } + + Ok(()) } // If we know the remote speaks an older version, we try to fix up any messages // to work with our older peer. - fn enforce_version(&mut self, head: &mut MessageHead) { + fn enforce_version(&mut self, head: &mut MessageHead) -> Result<(), http::Error> { if let Version::HTTP_10 = self.state.version { // Fixes response or connection when keep-alive header is not present - self.fix_keep_alive(head); + self.fix_keep_alive(head)?; // If the remote only knows HTTP/1.0, we should force ourselves // to do only speak HTTP/1.0 as well. head.version = Version::HTTP_10; @@ -625,6 +631,8 @@ where // If the remote speaks HTTP/1.1, then it *should* be fine with // both HTTP/1.0 and HTTP/1.1 from us. So again, we just let // the user's headers be. + + Ok(()) } pub(crate) fn write_body(&mut self, chunk: B) { @@ -1061,7 +1069,7 @@ mod tests { let io = tokio_test::io::Builder::new().build(); let mut conn = Conn::<_, bytes::Bytes, crate::proto::h1::ServerTransaction>::new(io); *conn.io.read_buf_mut() = ::bytes::BytesMut::from(&s[..]); - conn.state.cached_headers = Some(HeaderMap::with_capacity(2)); + conn.state.cached_headers = Some(HeaderMap::try_with_capacity(2).unwrap()); let rt = tokio::runtime::Builder::new_current_thread() .enable_all() diff --git a/src/proto/h1/role.rs b/src/proto/h1/role.rs index 6252207baf..0a2936938e 100644 --- a/src/proto/h1/role.rs +++ b/src/proto/h1/role.rs @@ -224,7 +224,7 @@ impl Http1Transaction for Server { let mut headers = ctx.cached_headers.take().unwrap_or_else(HeaderMap::new); - headers.reserve(headers_len); + headers_op!(headers.try_reserve(headers_len), capacity); for header in &headers_indices[..headers_len] { // SAFETY: array is valid up to `headers_len` @@ -295,7 +295,7 @@ impl Http1Transaction for Server { } if let Some(ref mut header_case_map) = header_case_map { - header_case_map.append(&name, slice.slice(header.name.0..header.name.1)); + headers_op!(header_case_map.try_append(&name, slice.slice(header.name.0..header.name.1)), capacity); } #[cfg(feature = "ffi")] @@ -303,7 +303,7 @@ impl Http1Transaction for Server { header_order.append(&name); } - headers.append(name, value); + headers_op!(headers.try_append(name, value), capacity); } if is_te && !is_te_chunked { @@ -1020,7 +1020,7 @@ impl Http1Transaction for Client { None }; - headers.reserve(headers_len); + headers_op!(headers.try_reserve(headers_len), capacity); for header in &headers_indices[..headers_len] { // SAFETY: array is valid up to `headers_len` let header = unsafe { &*header.as_ptr() }; @@ -1039,7 +1039,7 @@ impl Http1Transaction for Client { } if let Some(ref mut header_case_map) = header_case_map { - header_case_map.append(&name, slice.slice(header.name.0..header.name.1)); + headers_op!(header_case_map.try_append(&name, slice.slice(header.name.0..header.name.1)), capacity); } #[cfg(feature = "ffi")] @@ -1047,7 +1047,12 @@ impl Http1Transaction for Client { header_order.append(&name); } - headers.append(name, value); + match headers.try_append(name.clone(), value) { + Err(http::CapacityOverflow { .. }) => { + return Err(Parse::TooLarge); + }, + Ok(_) => {} + } } let mut extensions = http::Extensions::default(); @@ -1152,7 +1157,7 @@ impl Http1Transaction for Client { extend(dst, b"\r\n"); msg.head.headers.clear(); //TODO: remove when switching to drain() - Ok(body) + body } fn on_error(_err: &crate::Error) -> Option> { @@ -1233,12 +1238,12 @@ impl Client { Ok(Some((DecodedLength::CLOSE_DELIMITED, false))) } } - fn set_length(head: &mut RequestHead, body: Option) -> Encoder { + fn set_length(head: &mut RequestHead, body: Option) -> crate::Result { let body = if let Some(body) = body { body } else { head.headers.remove(header::TRANSFER_ENCODING); - return Encoder::length(0); + return Ok(Encoder::length(0)); }; // HTTP/1.0 doesn't know about chunked @@ -1262,19 +1267,19 @@ impl Client { } return if let Some(len) = existing_con_len { - Encoder::length(len) + Ok(Encoder::length(len)) } else if let BodyLength::Known(len) = body { - set_content_length(headers, len) + Ok(headers_op!(set_content_length(headers, len), error)) } else { // HTTP/1.0 client requests without a content-length // cannot have any body at all. - Encoder::length(0) + Ok(Encoder::length(0)) }; } // If the user set a transfer-encoding, respect that. Let's just // make sure `chunked` is the final encoding. - let encoder = match headers.entry(header::TRANSFER_ENCODING) { + let encoder = match headers_op!(headers.try_entry(header::TRANSFER_ENCODING), error) { Entry::Occupied(te) => { should_remove_con_len = true; if headers::is_chunked(te.iter()) { @@ -1314,7 +1319,7 @@ impl Client { match head.subject.0 { Method::GET | Method::HEAD | Method::CONNECT => Some(Encoder::length(0)), _ => { - te.insert(HeaderValue::from_static("chunked")); + headers_op!(te.try_insert(HeaderValue::from_static("chunked")), capacity); Some(Encoder::chunked()) } } @@ -1330,7 +1335,7 @@ impl Client { if should_remove_con_len && existing_con_len.is_some() { headers.remove(header::CONTENT_LENGTH); } - return encoder; + return Ok(encoder); } // User didn't set transfer-encoding, AND we know body length, @@ -1342,11 +1347,11 @@ impl Client { unreachable!("BodyLength::Unknown would set chunked"); }; - set_content_length(headers, len) + Ok(headers_op!(set_content_length(headers, len), error)) } } -fn set_content_length(headers: &mut HeaderMap, len: u64) -> Encoder { +fn set_content_length(headers: &mut HeaderMap, len: u64) -> Result { // At this point, there should not be a valid Content-Length // header. However, since we'll be indexing in anyways, we can // warn the user if there was an existing illegal header. @@ -1355,7 +1360,7 @@ fn set_content_length(headers: &mut HeaderMap, len: u64) -> Encoder { // so perhaps only do that while the user is developing/testing. if cfg!(debug_assertions) { - match headers.entry(header::CONTENT_LENGTH) { + match headers.try_entry(header::CONTENT_LENGTH)? { Entry::Occupied(mut cl) => { // Internal sanity check, we should have already determined // that the header was illegal before calling this function. @@ -1366,16 +1371,16 @@ fn set_content_length(headers: &mut HeaderMap, len: u64) -> Encoder { error!("user provided content-length header was invalid"); cl.insert(HeaderValue::from(len)); - Encoder::length(len) + Ok(Encoder::length(len)) } Entry::Vacant(cl) => { - cl.insert(HeaderValue::from(len)); - Encoder::length(len) + cl.try_insert(HeaderValue::from(len))?; + Ok(Encoder::length(len)) } } } else { - headers.insert(header::CONTENT_LENGTH, HeaderValue::from(len)); - Encoder::length(len) + headers.try_insert(header::CONTENT_LENGTH, HeaderValue::from(len))?; + Ok(Encoder::length(len)) } } @@ -2391,10 +2396,10 @@ mod tests { let mut head = MessageHead::default(); head.headers - .insert("content-length", HeaderValue::from_static("10")); + .try_insert("content-length", HeaderValue::from_static("10")).unwrap(); head.headers - .insert("content-type", HeaderValue::from_static("application/json")); - head.headers.insert("*-*", HeaderValue::from_static("o_o")); + .try_insert("content-type", HeaderValue::from_static("application/json")).unwrap(); + head.headers.try_insert("*-*", HeaderValue::from_static("o_o")).unwrap(); let mut vec = Vec::new(); Client::encode( @@ -2419,12 +2424,12 @@ mod tests { let mut head = MessageHead::default(); head.headers - .insert("content-length", HeaderValue::from_static("10")); + .try_insert("content-length", HeaderValue::from_static("10")).unwrap(); head.headers - .insert("content-type", HeaderValue::from_static("application/json")); + .try_insert("content-type", HeaderValue::from_static("application/json")).unwrap(); let mut orig_headers = HeaderCaseMap::default(); - orig_headers.insert(CONTENT_LENGTH, "CONTENT-LENGTH".into()); + orig_headers.try_insert(CONTENT_LENGTH, "CONTENT-LENGTH".into()).unwrap(); head.extensions.insert(orig_headers); let mut vec = Vec::new(); @@ -2453,12 +2458,12 @@ mod tests { let mut head = MessageHead::default(); head.headers - .insert("content-length", HeaderValue::from_static("10")); + .try_insert("content-length", HeaderValue::from_static("10")).unwrap(); head.headers - .insert("content-type", HeaderValue::from_static("application/json")); + .try_insert("content-type", HeaderValue::from_static("application/json")).unwrap(); let mut orig_headers = HeaderCaseMap::default(); - orig_headers.insert(CONTENT_LENGTH, "CONTENT-LENGTH".into()); + orig_headers.try_insert(CONTENT_LENGTH, "CONTENT-LENGTH".into()).unwrap(); head.extensions.insert(orig_headers); let mut vec = Vec::new(); @@ -2508,11 +2513,11 @@ mod tests { let mut head = MessageHead::default(); head.headers - .insert("content-length", HeaderValue::from_static("10")); + .try_insert("content-length", HeaderValue::from_static("10")).unwrap(); head.headers - .insert("content-type", HeaderValue::from_static("application/json")); + .try_insert("content-type", HeaderValue::from_static("application/json")).unwrap(); head.headers - .insert("weird--header", HeaderValue::from_static("")); + .try_insert("weird--header", HeaderValue::from_static("")).unwrap(); let mut vec = Vec::new(); Server::encode( @@ -2540,12 +2545,12 @@ mod tests { let mut head = MessageHead::default(); head.headers - .insert("content-length", HeaderValue::from_static("10")); + .try_insert("content-length", HeaderValue::from_static("10")).unwrap(); head.headers - .insert("content-type", HeaderValue::from_static("application/json")); + .try_insert("content-type", HeaderValue::from_static("application/json")).unwrap(); let mut orig_headers = HeaderCaseMap::default(); - orig_headers.insert(CONTENT_LENGTH, "CONTENT-LENGTH".into()); + orig_headers.try_insert(CONTENT_LENGTH, "CONTENT-LENGTH".into()).unwrap(); head.extensions.insert(orig_headers); let mut vec = Vec::new(); @@ -2574,12 +2579,12 @@ mod tests { let mut head = MessageHead::default(); head.headers - .insert("content-length", HeaderValue::from_static("10")); + .try_insert("content-length", HeaderValue::from_static("10")).unwrap(); head.headers - .insert("content-type", HeaderValue::from_static("application/json")); + .try_insert("content-type", HeaderValue::from_static("application/json")).unwrap(); let mut orig_headers = HeaderCaseMap::default(); - orig_headers.insert(CONTENT_LENGTH, "CONTENT-LENGTH".into()); + orig_headers.try_insert(CONTENT_LENGTH, "CONTENT-LENGTH".into()).unwrap(); head.extensions.insert(orig_headers); let mut vec = Vec::new(); @@ -2636,9 +2641,9 @@ mod tests { fn test_write_headers_orig_case_empty_value() { let mut headers = HeaderMap::new(); let name = http::header::HeaderName::from_static("x-empty"); - headers.insert(&name, "".parse().expect("parse empty")); + headers.try_insert(&name, "".parse().expect("parse empty")).unwrap(); let mut orig_cases = HeaderCaseMap::default(); - orig_cases.insert(name, Bytes::from_static(b"X-EmptY")); + orig_cases.try_insert(name, Bytes::from_static(b"X-EmptY")).unwrap(); let mut dst = Vec::new(); super::write_headers_original_case(&headers, &orig_cases, &mut dst, false); @@ -2653,12 +2658,12 @@ mod tests { fn test_write_headers_orig_case_multiple_entries() { let mut headers = HeaderMap::new(); let name = http::header::HeaderName::from_static("x-empty"); - headers.insert(&name, "a".parse().unwrap()); - headers.append(&name, "b".parse().unwrap()); + headers.try_insert(&name, "a".parse().unwrap()).unwrap(); + headers.try_append(&name, "b".parse().unwrap()).unwrap(); let mut orig_cases = HeaderCaseMap::default(); - orig_cases.insert(name.clone(), Bytes::from_static(b"X-Empty")); - orig_cases.append(name, Bytes::from_static(b"X-EMPTY")); + orig_cases.try_insert(name.clone(), Bytes::from_static(b"X-Empty")).unwrap(); + orig_cases.try_append(name, Bytes::from_static(b"X-EMPTY")).unwrap(); let mut dst = Vec::new(); super::write_headers_original_case(&headers, &orig_cases, &mut dst, false); @@ -2793,8 +2798,8 @@ mod tests { let mut head = MessageHead::default(); let mut headers = HeaderMap::new(); - headers.insert("content-length", HeaderValue::from_static("10")); - headers.insert("content-type", HeaderValue::from_static("application/json")); + headers.try_insert("content-length", HeaderValue::from_static("10")).unwrap(); + headers.try_insert("content-type", HeaderValue::from_static("application/json")).unwrap(); b.iter(|| { let mut vec = Vec::new(); diff --git a/src/proto/h2/client.rs b/src/proto/h2/client.rs index bac8eceb3a..56310eb215 100644 --- a/src/proto/h2/client.rs +++ b/src/proto/h2/client.rs @@ -371,7 +371,7 @@ where super::strip_connection_headers(req.headers_mut(), true); if let Some(len) = body.size_hint().exact() { if len != 0 || headers::method_has_defined_payload_semantics(req.method()) { - headers::set_content_length_if_missing(req.headers_mut(), len); + headers::set_content_length_if_missing(req.headers_mut(), len)?; } } diff --git a/src/proto/h2/server.rs b/src/proto/h2/server.rs index 4127387e71..adfdb56b8d 100644 --- a/src/proto/h2/server.rs +++ b/src/proto/h2/server.rs @@ -482,8 +482,8 @@ where // set Date header if it isn't already set... res.headers_mut() - .entry(::http::header::DATE) - .or_insert_with(date::update_and_header_value); + .try_entry(::http::header::DATE)? + .or_try_insert_with(date::update_and_header_value)?; if let Some(connect_parts) = connect_parts.take() { if res.status().is_success() { @@ -511,7 +511,7 @@ where if !body.is_end_stream() { // automatically set Content-Length from body... if let Some(len) = body.size_hint().exact() { - headers::set_content_length_if_missing(res.headers_mut(), len); + headers::set_content_length_if_missing(res.headers_mut(), len)?; } let body_tx = reply!(me, res, false); diff --git a/tests/server.rs b/tests/server.rs index 786bb4b42f..c23d375de0 100644 --- a/tests/server.rs +++ b/tests/server.rs @@ -3003,7 +3003,7 @@ impl TestService { *res.version_mut() = v; } Reply::Header(name, value) => { - res.headers_mut().insert(name, value); + res.headers_mut().try_insert(name, value)?; } Reply::Body(body) => { *res.body_mut() = body; diff --git a/tests/support/mod.rs b/tests/support/mod.rs index 6b3c8f4472..5ae314de29 100644 --- a/tests/support/mod.rs +++ b/tests/support/mod.rs @@ -202,7 +202,7 @@ macro_rules! __internal_req_res_prop { macro_rules! __internal_headers_map { ($headers:ident, { $($name:expr => $val:expr,)* }) => { $( - $headers.insert($name, $val.to_string().parse().expect("header value")); + $headers.try_insert($name, $val.to_string().parse().expect("header value")).unwrap(); )* } }