diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index 6e4f8ec32..ae253a0e9 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -2,6 +2,7 @@ ## Unreleased +- Remove `Option` wrapper from `RequestHeadType::Rc` extra headers; add `RequestHeadType::extra_headers_mut()` method. - When configured, gracefully close HTTP/1 connections after early responses to unread request bodies. [#3967] - Wake HTTP/1 payload receivers with an incomplete-payload error when the sender is dropped before EOF. [#3100] - Update `foldhash` dependency to `0.2`. diff --git a/actix-http/src/h1/encoder.rs b/actix-http/src/h1/encoder.rs index 1853843c3..05a8b8eb4 100644 --- a/actix-http/src/h1/encoder.rs +++ b/actix-http/src/h1/encoder.rs @@ -648,7 +648,7 @@ mod tests { ); extra_headers.insert(DATE, HeaderValue::from_static("date")); - let mut head = RequestHeadType::Rc(Rc::new(head), Some(extra_headers)); + let mut head = RequestHeadType::Rc(Rc::new(head), extra_headers); let _ = head.encode_headers( &mut bytes, diff --git a/actix-http/src/requests/head.rs b/actix-http/src/requests/head.rs index ddc9dd98f..314df5bf2 100644 --- a/actix-http/src/requests/head.rs +++ b/actix-http/src/requests/head.rs @@ -150,14 +150,32 @@ impl RequestHead { #[derive(Debug)] pub enum RequestHeadType { Owned(RequestHead), - Rc(Rc, Option), + Rc(Rc, HeaderMap), } impl RequestHeadType { pub fn extra_headers(&self) -> Option<&HeaderMap> { match self { RequestHeadType::Owned(_) => None, - RequestHeadType::Rc(_, headers) => headers.as_ref(), + RequestHeadType::Rc(_, headers) => { + if headers.is_empty() { + None + } else { + Some(headers) + } + } + } + } + + /// Returns a mutable reference to the headers that can be modified on this request. + /// + /// For `Owned` variants, this returns the main header map. + /// For `Rc` variants, this returns the extra headers map (which overrides base headers + /// during encoding). + pub fn extra_headers_mut(&mut self) -> &mut HeaderMap { + match self { + RequestHeadType::Owned(head) => &mut head.headers, + RequestHeadType::Rc(_, headers) => headers, } } } diff --git a/awc/CHANGES.md b/awc/CHANGES.md index 9888c66c3..b16dd8a47 100644 --- a/awc/CHANGES.md +++ b/awc/CHANGES.md @@ -2,6 +2,7 @@ ## Unreleased +- Add `ConnectRequest::headers_mut()` to allow middleware to modify request headers. - Add camel-case header controls to `WebsocketsRequest` via `camel_case_headers()` and `set_camel_case_headers()`. [#3953] - Update `hickory-resolver` dependency to `0.26.1`. - Update `rand` dependency to `0.10`. diff --git a/awc/src/client/h1proto.rs b/awc/src/client/h1proto.rs index 3d8d8db08..3ad5fb2cd 100644 --- a/awc/src/client/h1proto.rs +++ b/awc/src/client/h1proto.rs @@ -9,7 +9,7 @@ use actix_http::{ body::{BodySize, MessageBody}, error::PayloadError, h1, - header::{HeaderMap, TryIntoHeaderValue, EXPECT, HOST}, + header::{TryIntoHeaderValue, EXPECT, HOST}, Payload, RequestHeadType, ResponseHead, StatusCode, }; use actix_utils::future::poll_fn; @@ -76,15 +76,9 @@ where }; match wrt.get_mut().split().freeze().try_into_value() { - Ok(value) => match head { - RequestHeadType::Owned(ref mut head) => { - head.headers.insert(HOST, value); - } - RequestHeadType::Rc(_, ref mut extra_headers) => { - let headers = extra_headers.get_or_insert(HeaderMap::new()); - headers.insert(HOST, value); - } - }, + Ok(value) => { + head.extra_headers_mut().insert(HOST, value); + } Err(err) => log::error!("Can not set HOST header {err}"), } } diff --git a/awc/src/client/h2proto.rs b/awc/src/client/h2proto.rs index 2e0da2e4f..16a6d534c 100644 --- a/awc/src/client/h2proto.rs +++ b/awc/src/client/h2proto.rs @@ -77,10 +77,9 @@ where // Extracting extra headers from RequestHeadType. HeaderMap::new() does not allocate. let (head, extra_headers) = match head { RequestHeadType::Owned(head) => (RequestHeadType::Owned(head), HeaderMap::new()), - RequestHeadType::Rc(head, extra_headers) => ( - RequestHeadType::Rc(head, None), - extra_headers.unwrap_or_else(HeaderMap::new), - ), + RequestHeadType::Rc(head, extra_headers) => { + (RequestHeadType::Rc(head, HeaderMap::new()), extra_headers) + } }; // merging headers from head and extra headers. diff --git a/awc/src/connect.rs b/awc/src/connect.rs index 14ed9e958..cb7e17e00 100644 --- a/awc/src/connect.rs +++ b/awc/src/connect.rs @@ -41,6 +41,19 @@ pub enum ConnectRequest { Tunnel(RequestHead, Option), } +impl ConnectRequest { + /// Returns a mutable reference to the request headers. + /// + /// For `Client` requests using the `Rc` variant, this returns the extra headers map + /// (which takes priority over base headers during encoding). + pub fn headers_mut(&mut self) -> &mut actix_http::header::HeaderMap { + match self { + ConnectRequest::Client(head, ..) => head.extra_headers_mut(), + ConnectRequest::Tunnel(head, ..) => &mut head.headers, + } + } +} + /// Combined HTTP response & WebSocket tunnel type returned from connection service. pub enum ConnectResponse { /// Standard HTTP response. diff --git a/awc/src/frozen.rs b/awc/src/frozen.rs index 862405234..6936b1d0c 100644 --- a/awc/src/frozen.rs +++ b/awc/src/frozen.rs @@ -49,7 +49,7 @@ impl FrozenClientRequest { where B: MessageBody + 'static, { - RequestSender::Rc(Rc::clone(&self.head), None).send_body( + RequestSender::Rc(Rc::clone(&self.head), HeaderMap::new()).send_body( self.addr, self.response_decompress, self.timeout, @@ -60,7 +60,7 @@ impl FrozenClientRequest { /// Send a json body. pub fn send_json(&self, value: &T) -> SendClientRequest { - RequestSender::Rc(Rc::clone(&self.head), None).send_json( + RequestSender::Rc(Rc::clone(&self.head), HeaderMap::new()).send_json( self.addr, self.response_decompress, self.timeout, @@ -71,7 +71,7 @@ impl FrozenClientRequest { /// Send an urlencoded body. pub fn send_form(&self, value: &T) -> SendClientRequest { - RequestSender::Rc(Rc::clone(&self.head), None).send_form( + RequestSender::Rc(Rc::clone(&self.head), HeaderMap::new()).send_form( self.addr, self.response_decompress, self.timeout, @@ -86,7 +86,7 @@ impl FrozenClientRequest { S: Stream> + 'static, E: Into + 'static, { - RequestSender::Rc(Rc::clone(&self.head), None).send_stream( + RequestSender::Rc(Rc::clone(&self.head), HeaderMap::new()).send_stream( self.addr, self.response_decompress, self.timeout, @@ -97,7 +97,7 @@ impl FrozenClientRequest { /// Send an empty body. pub fn send(&self) -> SendClientRequest { - RequestSender::Rc(Rc::clone(&self.head), None).send( + RequestSender::Rc(Rc::clone(&self.head), HeaderMap::new()).send( self.addr, self.response_decompress, self.timeout, @@ -151,7 +151,7 @@ impl FrozenSendBuilder { return err.into(); } - RequestSender::Rc(self.req.head, Some(self.extra_headers)).send_body( + RequestSender::Rc(self.req.head, self.extra_headers).send_body( self.req.addr, self.req.response_decompress, self.req.timeout, @@ -166,7 +166,7 @@ impl FrozenSendBuilder { return err.into(); } - RequestSender::Rc(self.req.head, Some(self.extra_headers)).send_json( + RequestSender::Rc(self.req.head, self.extra_headers).send_json( self.req.addr, self.req.response_decompress, self.req.timeout, @@ -181,7 +181,7 @@ impl FrozenSendBuilder { return err.into(); } - RequestSender::Rc(self.req.head, Some(self.extra_headers)).send_form( + RequestSender::Rc(self.req.head, self.extra_headers).send_form( self.req.addr, self.req.response_decompress, self.req.timeout, @@ -200,7 +200,7 @@ impl FrozenSendBuilder { return err.into(); } - RequestSender::Rc(self.req.head, Some(self.extra_headers)).send_stream( + RequestSender::Rc(self.req.head, self.extra_headers).send_stream( self.req.addr, self.req.response_decompress, self.req.timeout, @@ -215,7 +215,7 @@ impl FrozenSendBuilder { return err.into(); } - RequestSender::Rc(self.req.head, Some(self.extra_headers)).send( + RequestSender::Rc(self.req.head, self.extra_headers).send( self.req.addr, self.req.response_decompress, self.req.timeout, diff --git a/awc/src/sender.rs b/awc/src/sender.rs index 0015743bd..196682bd4 100644 --- a/awc/src/sender.rs +++ b/awc/src/sender.rs @@ -176,7 +176,7 @@ impl From for SendClientRequest { #[derive(Debug)] pub(crate) enum RequestSender { Owned(RequestHead), - Rc(Rc, Option), + Rc(Rc, HeaderMap), } impl RequestSender { @@ -296,14 +296,9 @@ impl RequestSender { } } RequestSender::Rc(head, extra_headers) => { - if !head.headers.contains_key(&key) - && !extra_headers.iter().any(|h| h.contains_key(&key)) - { + if !head.headers.contains_key(&key) && !extra_headers.contains_key(&key) { match value.try_into_value() { - Ok(v) => { - let h = extra_headers.get_or_insert(HeaderMap::new()); - h.insert(key, v) - } + Ok(v) => extra_headers.insert(key, v), Err(err) => return Err(err.into()), }; }