diff --git a/capi/include/hyper.h b/capi/include/hyper.h index 1f938b8714..e14de53116 100644 --- a/capi/include/hyper.h +++ b/capi/include/hyper.h @@ -391,17 +391,6 @@ void hyper_clientconn_options_exec(struct hyper_clientconn_options *opts, */ enum hyper_code hyper_clientconn_options_http2(struct hyper_clientconn_options *opts, int enabled); -/* - Set the whether to include a copy of the raw headers in responses - received on this connection. - - Pass `0` to disable, `1` to enable. - - If enabled, see `hyper_response_headers_raw()` for usage. - */ -enum hyper_code hyper_clientconn_options_headers_raw(struct hyper_clientconn_options *opts, - int enabled); - /* Frees a `hyper_error`. */ @@ -557,21 +546,6 @@ const uint8_t *hyper_response_reason_phrase(const struct hyper_response *resp); */ size_t hyper_response_reason_phrase_len(const struct hyper_response *resp); -/* - Get a reference to the full raw headers of this response. - - You must have enabled `hyper_clientconn_options_headers_raw()`, or this - will return NULL. - - The returned `hyper_buf *` is just a reference, owned by the response. - You need to make a copy if you wish to use it after freeing the - response. - - The buffer is not null-terminated, see the `hyper_buf` functions for - getting the bytes and length. - */ -const struct hyper_buf *hyper_response_headers_raw(const struct hyper_response *resp); - /* Get the HTTP version used by this response. diff --git a/src/client/conn.rs b/src/client/conn.rs index 8da457da64..7fd8ae023f 100644 --- a/src/client/conn.rs +++ b/src/client/conn.rs @@ -192,8 +192,7 @@ pub struct Builder { h1_preserve_header_order: bool, h1_read_buf_exact_size: Option, h1_max_buf_size: Option, - #[cfg(feature = "ffi")] - h1_headers_raw: bool, + h1_raw_message: bool, #[cfg(feature = "http2")] h2_builder: proto::h2::client::Config, version: Proto, @@ -603,8 +602,7 @@ impl Builder { #[cfg(feature = "ffi")] h1_preserve_header_order: false, h1_max_buf_size: None, - #[cfg(feature = "ffi")] - h1_headers_raw: false, + h1_raw_message: false, #[cfg(feature = "http2")] h2_builder: Default::default(), #[cfg(feature = "http1")] @@ -811,9 +809,16 @@ impl Builder { self } - #[cfg(feature = "ffi")] - pub(crate) fn http1_headers_raw(&mut self, enabled: bool) -> &mut Self { - self.h1_headers_raw = enabled; + /// Set whether to include the raw bytes of HTTP/1 requests and responses. + /// + /// This will store a [`Http1RawMessage`](crate::ext::Http1RawMessage) + /// in extensions of HTTP/1 requests and responses. + /// + /// Note that this setting does not affect HTTP/2. + /// + /// Default is false. + pub fn http1_raw_message(&mut self, enabled: bool) -> &mut Self { + self.h1_raw_message = enabled; self } @@ -1033,8 +1038,9 @@ impl Builder { conn.set_h09_responses(); } - #[cfg(feature = "ffi")] - conn.set_raw_headers(opts.h1_headers_raw); + if opts.h1_raw_message { + conn.set_h1_raw_message(); + } if let Some(sz) = opts.h1_read_buf_exact_size { conn.set_read_buf_exact_size(sz); diff --git a/src/ext.rs b/src/ext.rs index 224206dd66..ae7cd47ea8 100644 --- a/src/ext.rs +++ b/src/ext.rs @@ -10,6 +10,7 @@ use http::HeaderMap; use std::collections::HashMap; #[cfg(feature = "http2")] use std::fmt; +use std::ops::Deref; #[cfg(any(feature = "http1", feature = "ffi"))] mod h1_reason_phrase; @@ -131,6 +132,41 @@ impl HeaderCaseMap { } } +/// Raw bytes of HTTP/1 requests and responses. +/// +/// Included in HTTP/1 requests and responses when `http1_raw_message` is set +/// to true. +#[derive(Clone, Debug, PartialEq, PartialOrd, Hash)] +pub struct Http1RawMessage { + pub(crate) buf: Bytes, +} + +impl Deref for Http1RawMessage { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + &self.buf + } +} + +impl AsRef for Http1RawMessage { + fn as_ref(&self) -> &Bytes { + &self.buf + } +} + +impl AsRef<[u8]> for Http1RawMessage { + fn as_ref(&self) -> &[u8] { + &self + } +} + +impl From for Bytes { + fn from(message: Http1RawMessage) -> Self { + message.buf + } +} + #[cfg(feature = "ffi")] #[derive(Clone, Debug)] /// Hashmap diff --git a/src/ffi/client.rs b/src/ffi/client.rs index 670f77d141..485a3bd81b 100644 --- a/src/ffi/client.rs +++ b/src/ffi/client.rs @@ -166,17 +166,3 @@ ffi_fn! { } } } - -ffi_fn! { - /// Set the whether to include a copy of the raw headers in responses - /// received on this connection. - /// - /// Pass `0` to disable, `1` to enable. - /// - /// If enabled, see `hyper_response_headers_raw()` for usage. - fn hyper_clientconn_options_headers_raw(opts: *mut hyper_clientconn_options, enabled: c_int) -> hyper_code { - let opts = non_null! { &mut *opts ?= hyper_code::HYPERE_INVALID_ARG }; - opts.builder.http1_headers_raw(enabled != 0); - hyper_code::HYPERE_OK - } -} diff --git a/src/ffi/http_types.rs b/src/ffi/http_types.rs index ea10f139cb..098f618e1f 100644 --- a/src/ffi/http_types.rs +++ b/src/ffi/http_types.rs @@ -2,7 +2,7 @@ use bytes::Bytes; use libc::{c_int, size_t}; use std::ffi::c_void; -use super::body::{hyper_body, hyper_buf}; +use super::body::hyper_body; use super::error::hyper_code; use super::task::{hyper_task_return_type, AsTaskType}; use super::{UserDataPointer, HYPER_ITER_CONTINUE}; @@ -25,8 +25,6 @@ pub struct hyper_headers { orig_order: OriginalHeaderOrder, } -pub(crate) struct RawHeaders(pub(crate) hyper_buf); - pub(crate) struct OnInformational { func: hyper_request_on_informational_callback, data: UserDataPointer, @@ -278,27 +276,6 @@ ffi_fn! { } } -ffi_fn! { - /// Get a reference to the full raw headers of this response. - /// - /// You must have enabled `hyper_clientconn_options_headers_raw()`, or this - /// will return NULL. - /// - /// The returned `hyper_buf *` is just a reference, owned by the response. - /// You need to make a copy if you wish to use it after freeing the - /// response. - /// - /// The buffer is not null-terminated, see the `hyper_buf` functions for - /// getting the bytes and length. - fn hyper_response_headers_raw(resp: *const hyper_response) -> *const hyper_buf { - let resp = non_null!(&*resp ?= std::ptr::null()); - match resp.0.extensions().get::() { - Some(raw) => &raw.0, - None => std::ptr::null(), - } - } ?= std::ptr::null() -} - ffi_fn! { /// Get the HTTP version used by this response. /// diff --git a/src/proto/h1/conn.rs b/src/proto/h1/conn.rs index 5ab72f264e..4d044c67e7 100644 --- a/src/proto/h1/conn.rs +++ b/src/proto/h1/conn.rs @@ -66,8 +66,7 @@ where h09_responses: false, #[cfg(feature = "ffi")] on_informational: None, - #[cfg(feature = "ffi")] - raw_headers: false, + h1_raw_message: false, notify_read: false, reading: Reading::Init, writing: Writing::Init, @@ -135,9 +134,8 @@ where self.state.allow_half_close = true; } - #[cfg(feature = "ffi")] - pub(crate) fn set_raw_headers(&mut self, enabled: bool) { - self.state.raw_headers = enabled; + pub(crate) fn set_h1_raw_message(&mut self) { + self.state.h1_raw_message = true; } pub(crate) fn into_inner(self) -> (I, Bytes) { @@ -210,8 +208,7 @@ where h09_responses: self.state.h09_responses, #[cfg(feature = "ffi")] on_informational: &mut self.state.on_informational, - #[cfg(feature = "ffi")] - raw_headers: self.state.raw_headers, + h1_raw_message: self.state.h1_raw_message, } )) { Ok(msg) => msg, @@ -838,8 +835,7 @@ struct State { /// received. #[cfg(feature = "ffi")] on_informational: Option, - #[cfg(feature = "ffi")] - raw_headers: bool, + h1_raw_message: bool, /// Set to true when the Dispatcher should poll read operations /// again. See the `maybe_notify` method for more. notify_read: bool, diff --git a/src/proto/h1/io.rs b/src/proto/h1/io.rs index 02d8a4a9ec..4ba8120833 100644 --- a/src/proto/h1/io.rs +++ b/src/proto/h1/io.rs @@ -200,8 +200,7 @@ where h09_responses: parse_ctx.h09_responses, #[cfg(feature = "ffi")] on_informational: parse_ctx.on_informational, - #[cfg(feature = "ffi")] - raw_headers: parse_ctx.raw_headers, + h1_raw_message: parse_ctx.h1_raw_message, }, )? { Some(msg) => { @@ -745,8 +744,7 @@ mod tests { h09_responses: false, #[cfg(feature = "ffi")] on_informational: &mut None, - #[cfg(feature = "ffi")] - raw_headers: false, + h1_raw_message: false, }; assert!(buffered .parse::(cx, parse_ctx) diff --git a/src/proto/h1/mod.rs b/src/proto/h1/mod.rs index 5a2587a843..75f446e975 100644 --- a/src/proto/h1/mod.rs +++ b/src/proto/h1/mod.rs @@ -88,8 +88,7 @@ pub(crate) struct ParseContext<'a> { h09_responses: bool, #[cfg(feature = "ffi")] on_informational: &'a mut Option, - #[cfg(feature = "ffi")] - raw_headers: bool, + h1_raw_message: bool, } /// Passed to Http1Transaction::encode diff --git a/src/proto/h1/role.rs b/src/proto/h1/role.rs index 6252207baf..c72c288731 100644 --- a/src/proto/h1/role.rs +++ b/src/proto/h1/role.rs @@ -15,9 +15,9 @@ use crate::body::DecodedLength; #[cfg(feature = "server")] use crate::common::date; use crate::error::Parse; -use crate::ext::HeaderCaseMap; #[cfg(feature = "ffi")] use crate::ext::OriginalHeaderOrder; +use crate::ext::{HeaderCaseMap, Http1RawMessage}; use crate::headers; use crate::proto::h1::{ Encode, Encoder, Http1Transaction, ParseContext, ParseResult, ParsedMessage, @@ -193,6 +193,12 @@ impl Http1Transaction for Server { let slice = buf.split_to(len).freeze(); + let mut extensions = http::Extensions::default(); + + if ctx.h1_raw_message { + extensions.insert(Http1RawMessage { buf: slice.clone() }); + } + // According to https://tools.ietf.org/html/rfc7230#section-3.3.3 // 1. (irrelevant to Request) // 2. (irrelevant to Request) @@ -311,8 +317,6 @@ impl Http1Transaction for Server { return Err(Parse::transfer_encoding_invalid()); } - let mut extensions = http::Extensions::default(); - if let Some(header_case_map) = header_case_map { extensions.insert(header_case_map); } @@ -986,10 +990,18 @@ impl Http1Transaction for Client { let mut slice = buf.split_to(len); - if ctx + let mut extensions = http::Extensions::default(); + + let slice = if ctx .h1_parser_config .obsolete_multiline_headers_in_responses_are_allowed() { + if ctx.h1_raw_message { + extensions.insert(Http1RawMessage { + buf: slice.clone().freeze(), + }); + } + for header in &headers_indices[..headers_len] { // SAFETY: array is valid up to `headers_len` let header = unsafe { &*header.as_ptr() }; @@ -999,9 +1011,17 @@ impl Http1Transaction for Client { } } } - } - let slice = slice.freeze(); + slice.freeze() + } else { + let slice = slice.freeze(); + + if ctx.h1_raw_message { + extensions.insert(Http1RawMessage { buf: slice.clone() }); + } + + slice + }; let mut headers = ctx.cached_headers.take().unwrap_or_else(HeaderMap::new); @@ -1050,8 +1070,6 @@ impl Http1Transaction for Client { headers.append(name, value); } - let mut extensions = http::Extensions::default(); - if let Some(header_case_map) = header_case_map { extensions.insert(header_case_map); } @@ -1068,11 +1086,6 @@ impl Http1Transaction for Client { extensions.insert(reason); } - #[cfg(feature = "ffi")] - if ctx.raw_headers { - extensions.insert(crate::ffi::RawHeaders(crate::ffi::hyper_buf(slice))); - } - let head = MessageHead { version, subject: status, @@ -1535,8 +1548,7 @@ mod tests { h09_responses: false, #[cfg(feature = "ffi")] on_informational: &mut None, - #[cfg(feature = "ffi")] - raw_headers: false, + h1_raw_message: false, }, ) .unwrap() @@ -1570,8 +1582,7 @@ mod tests { h09_responses: false, #[cfg(feature = "ffi")] on_informational: &mut None, - #[cfg(feature = "ffi")] - raw_headers: false, + h1_raw_message: false, }; let msg = Client::parse(&mut raw, ctx).unwrap().unwrap(); assert_eq!(raw.len(), 0); @@ -1600,8 +1611,7 @@ mod tests { h09_responses: false, #[cfg(feature = "ffi")] on_informational: &mut None, - #[cfg(feature = "ffi")] - raw_headers: false, + h1_raw_message: false, }; Server::parse(&mut raw, ctx).unwrap_err(); } @@ -1628,8 +1638,7 @@ mod tests { h09_responses: true, #[cfg(feature = "ffi")] on_informational: &mut None, - #[cfg(feature = "ffi")] - raw_headers: false, + h1_raw_message: false, }; let msg = Client::parse(&mut raw, ctx).unwrap().unwrap(); assert_eq!(raw, H09_RESPONSE); @@ -1658,8 +1667,7 @@ mod tests { h09_responses: false, #[cfg(feature = "ffi")] on_informational: &mut None, - #[cfg(feature = "ffi")] - raw_headers: false, + h1_raw_message: false, }; Client::parse(&mut raw, ctx).unwrap_err(); assert_eq!(raw, H09_RESPONSE); @@ -1692,8 +1700,7 @@ mod tests { h09_responses: false, #[cfg(feature = "ffi")] on_informational: &mut None, - #[cfg(feature = "ffi")] - raw_headers: false, + h1_raw_message: false, }; let msg = Client::parse(&mut raw, ctx).unwrap().unwrap(); assert_eq!(raw.len(), 0); @@ -1723,8 +1730,7 @@ mod tests { h09_responses: false, #[cfg(feature = "ffi")] on_informational: &mut None, - #[cfg(feature = "ffi")] - raw_headers: false, + h1_raw_message: false, }; Client::parse(&mut raw, ctx).unwrap_err(); } @@ -1749,8 +1755,7 @@ mod tests { h09_responses: false, #[cfg(feature = "ffi")] on_informational: &mut None, - #[cfg(feature = "ffi")] - raw_headers: false, + h1_raw_message: false, }; let parsed_message = Server::parse(&mut raw, ctx).unwrap().unwrap(); let orig_headers = parsed_message @@ -1796,8 +1801,7 @@ mod tests { h09_responses: false, #[cfg(feature = "ffi")] on_informational: &mut None, - #[cfg(feature = "ffi")] - raw_headers: false, + h1_raw_message: false, }, ) .expect("parse ok") @@ -1824,8 +1828,7 @@ mod tests { h09_responses: false, #[cfg(feature = "ffi")] on_informational: &mut None, - #[cfg(feature = "ffi")] - raw_headers: false, + h1_raw_message: false, }, ) .expect_err(comment) @@ -2061,8 +2064,7 @@ mod tests { h09_responses: false, #[cfg(feature = "ffi")] on_informational: &mut None, - #[cfg(feature = "ffi")] - raw_headers: false, + h1_raw_message: false, } ) .expect("parse ok") @@ -2089,8 +2091,7 @@ mod tests { h09_responses: false, #[cfg(feature = "ffi")] on_informational: &mut None, - #[cfg(feature = "ffi")] - raw_headers: false, + h1_raw_message: false, }, ) .expect("parse ok") @@ -2117,8 +2118,7 @@ mod tests { h09_responses: false, #[cfg(feature = "ffi")] on_informational: &mut None, - #[cfg(feature = "ffi")] - raw_headers: false, + h1_raw_message: false, }, ) .expect_err("parse should err") @@ -2622,8 +2622,7 @@ mod tests { h09_responses: false, #[cfg(feature = "ffi")] on_informational: &mut None, - #[cfg(feature = "ffi")] - raw_headers: false, + h1_raw_message: false, }, ) .expect("parse ok") @@ -2714,8 +2713,7 @@ mod tests { h09_responses: false, #[cfg(feature = "ffi")] on_informational: &mut None, - #[cfg(feature = "ffi")] - raw_headers: false, + h1_raw_message: false, }, ) .unwrap() @@ -2762,8 +2760,7 @@ mod tests { h09_responses: false, #[cfg(feature = "ffi")] on_informational: &mut None, - #[cfg(feature = "ffi")] - raw_headers: false, + h1_raw_message: false, }, ) .unwrap() diff --git a/src/server/conn.rs b/src/server/conn.rs index 8ce4c95193..e2b99987bd 100644 --- a/src/server/conn.rs +++ b/src/server/conn.rs @@ -113,6 +113,7 @@ pub struct Http { h1_keep_alive: bool, h1_title_case_headers: bool, h1_preserve_header_case: bool, + h1_raw_message: bool, #[cfg(all(feature = "http1", feature = "runtime"))] h1_header_read_timeout: Option, h1_writev: Option, @@ -259,6 +260,7 @@ impl Http { h1_keep_alive: true, h1_title_case_headers: false, h1_preserve_header_case: false, + h1_raw_message: false, #[cfg(all(feature = "http1", feature = "runtime"))] h1_header_read_timeout: None, h1_writev: None, @@ -349,6 +351,19 @@ impl Http { self } + /// Set whether to include the raw bytes of HTTP/1 requests and responses. + /// + /// This will store a [`ext::Http1RawMessage`] in extensions of + /// HTTP/1 requests and responses. + /// + /// Default is false. + #[cfg(feature = "http1")] + #[cfg_attr(docsrs, doc(cfg(feature = "http1")))] + pub fn http1_raw_message(&mut self, enabled: bool) -> &mut Self { + self.h1_raw_message = enabled; + self + } + /// Set a timeout for reading client request headers. If a client does not /// transmit the entire header within this time, the connection is closed. /// @@ -606,6 +621,7 @@ impl Http { h1_keep_alive: self.h1_keep_alive, h1_title_case_headers: self.h1_title_case_headers, h1_preserve_header_case: self.h1_preserve_header_case, + h1_raw_message: self.h1_raw_message, #[cfg(all(feature = "http1", feature = "runtime"))] h1_header_read_timeout: self.h1_header_read_timeout, h1_writev: self.h1_writev, @@ -670,6 +686,9 @@ impl Http { if self.h1_preserve_header_case { conn.set_preserve_header_case(); } + if self.h1_raw_message { + conn.set_h1_raw_message(); + } #[cfg(all(feature = "http1", feature = "runtime"))] if let Some(header_read_timeout) = self.h1_header_read_timeout { conn.set_http1_header_read_timeout(header_read_timeout); diff --git a/src/server/conn/http1.rs b/src/server/conn/http1.rs index ab833b938b..a219b8d2b2 100644 --- a/src/server/conn/http1.rs +++ b/src/server/conn/http1.rs @@ -42,6 +42,7 @@ pub struct Builder { h1_keep_alive: bool, h1_title_case_headers: bool, h1_preserve_header_case: bool, + h1_raw_message: bool, h1_header_read_timeout: Option, h1_writev: Option, max_buf_size: Option, @@ -208,6 +209,7 @@ impl Builder { h1_keep_alive: true, h1_title_case_headers: false, h1_preserve_header_case: false, + h1_raw_message: false, h1_header_read_timeout: None, h1_writev: None, max_buf_size: None, @@ -260,6 +262,17 @@ impl Builder { self } + /// Set whether to include the raw bytes of HTTP/1 requests and responses. + /// + /// This will store a [`ext::Http1RawMessage`] in extensions of + /// HTTP/1 requests and responses. + /// + /// Default is false. + pub fn raw_message(&mut self, enabled: bool) -> &mut Self { + self.h1_raw_message = enabled; + self + } + /// Set a timeout for reading client request headers. If a client does not /// transmit the entire header within this time, the connection is closed. /// @@ -370,6 +383,9 @@ impl Builder { if self.h1_preserve_header_case { conn.set_preserve_header_case(); } + if self.h1_raw_message { + conn.set_h1_raw_message(); + } if let Some(header_read_timeout) = self.h1_header_read_timeout { conn.set_http1_header_read_timeout(header_read_timeout); }