Skip to content

Commit

Permalink
sync rama-http-core code with hyperium/hyper v1.6.0
Browse files Browse the repository at this point in the history
- ext: add ext::on_informational() callback extension
  (<hyperium/hyper#3818>)
  (<hyperium/hyper@8ce1fcf>,
    closes <hyperium/hyper#2565>)
- server: add http1::Builder::ignore_invalid_headers(bool) option
  (<hyperium/hyper#3824>)
  (<hyperium/hyper@3817a79>)

- server:
    - start http1 header read timeout when conn is idle
      (<hyperium/hyper#3828>)
      (<hyperium/hyper@10b09ff>,
        closes <hyperium/hyper#3780>,
          <hyperium/hyper#3781>)
    - change max_local_error_reset_streams function to &mut self (#3820) (e981a91e)

- http2::Builder::max_local_error_reset_streams() now takes
  &mut self and returns &mut Self. In practice, this shouldn't break almost anyone.
  It was the wrong receiver and return types.
  (<hyperium/hyper@e981a91>)

Co-authored-by: Sean McArthur <[email protected]>
Co-authored-by: Finn Bear <[email protected]>
Co-authored-by: tottoto <[email protected]>
  • Loading branch information
4 people committed Feb 20, 2025
1 parent c271fe4 commit 764b8a6
Show file tree
Hide file tree
Showing 13 changed files with 234 additions and 9 deletions.
2 changes: 1 addition & 1 deletion FORK.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ as a distant relative.
### hyperium

- <https://github.com/hyperium/h2/tree/v0.4.8>
- <https://github.com/hyperium/hyper/tree/v1.5.2>
- <https://github.com/hyperium/hyper/tree/v1.6.0>
- <https://github.com/hyperium/hyper-util/tree/v0.1.10>

### tower-rs
Expand Down
4 changes: 2 additions & 2 deletions LICENSE-MIT
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2023 - Glen Henri J. De Cauwsemaecker
Copyright (c) 2022-2025 Glen Henri J. De Cauwsemaecker

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand All @@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
SOFTWARE.
3 changes: 3 additions & 0 deletions rama-http-core/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,9 @@ impl Error {

/// Returns true if the error was caused by a timeout.
pub fn is_timeout(&self) -> bool {
if matches!(self.inner.kind, Kind::HeaderTimeout) {
return true;
}
self.find_source::<TimedOut>().is_some()
}

Expand Down
86 changes: 86 additions & 0 deletions rama-http-core/src/ext/informational.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
use std::sync::Arc;

#[derive(Clone)]
pub(crate) struct OnInformational(Arc<dyn OnInformationalCallback + Send + Sync>);

/// Add a callback for 1xx informational responses.
///
/// # Example
///
/// ```
/// # let some_body = ();
/// let mut req = rama_http_types::Request::new(some_body);
///
/// rama_http_core::ext::on_informational(&mut req, |res| {
/// println!("informational: {:?}", res.status());
/// });
///
/// // send request on a client connection...
/// ```
pub fn on_informational<B, F>(req: &mut rama_http_types::Request<B>, callback: F)
where
F: Fn(Response<'_>) + Send + Sync + 'static,
{
on_informational_raw(req, OnInformationalClosure(callback));
}

pub(crate) fn on_informational_raw<B, C>(req: &mut rama_http_types::Request<B>, callback: C)
where
C: OnInformationalCallback + Send + Sync + 'static,
{
req.extensions_mut()
.insert(OnInformational(Arc::new(callback)));
}

// Sealed, not actually nameable bounds
pub(crate) trait OnInformationalCallback {
fn on_informational(&self, res: rama_http_types::Response<()>);
}

impl OnInformational {
pub(crate) fn call(&self, res: rama_http_types::Response<()>) {
self.0.on_informational(res);
}
}

struct OnInformationalClosure<F>(F);

impl<F> OnInformationalCallback for OnInformationalClosure<F>
where
F: Fn(Response<'_>) + Send + Sync + 'static,
{
fn on_informational(&self, res: rama_http_types::Response<()>) {
let res = Response(&res);
(self.0)(res);
}
}

// A facade over rama_http_types::Response.
//
// It purposefully hides being able to move the response out of the closure,
// while also not being able to expect it to be a reference `&Response`.
// (Otherwise, a closure can be written as `|res: &_|`, and then be broken if
// we make the closure take ownership.)
//
// With the type not being nameable, we could change from being a facade to
// being either a real reference, or moving the rama_http_types::Response into the closure,
// in a backwards-compatible change in the future.
#[derive(Debug)]
pub struct Response<'a>(&'a rama_http_types::Response<()>);

impl Response<'_> {
#[inline]
pub fn status(&self) -> rama_http_types::StatusCode {
self.0.status()
}

#[inline]
pub fn version(&self) -> rama_http_types::Version {
self.0.version()
}

#[inline]
pub fn headers(&self) -> &rama_http_types::HeaderMap {
self.0.headers()
}
}
5 changes: 5 additions & 0 deletions rama-http-core/src/ext/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ use std::fmt;
mod h1_reason_phrase;
pub use h1_reason_phrase::ReasonPhrase;

mod informational;
pub use informational::on_informational;
pub(crate) use informational::OnInformational;
// pub(crate) use informational::{on_informational_raw, OnInformationalCallback}; // ffi feature in hyperium/hyper

/// Represents the `:protocol` pseudo-header used by
/// the [Extended CONNECT Protocol].
///
Expand Down
22 changes: 21 additions & 1 deletion rama-http-core/src/proto/h1/conn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ where
date_header: true,
title_case_headers: false,
h09_responses: false,
on_informational: None,
notify_read: false,
reading: Reading::Init,
writing: Writing::Init,
Expand Down Expand Up @@ -203,6 +204,7 @@ where
h1_parser_config: self.state.h1_parser_config.clone(),
h1_max_headers: self.state.h1_max_headers,
h09_responses: self.state.h09_responses,
on_informational: &mut self.state.on_informational,
},
) {
Poll::Ready(Ok(msg)) => msg,
Expand Down Expand Up @@ -236,6 +238,9 @@ where
// Prevent accepting HTTP/0.9 responses after the initial one, if any.
self.state.h09_responses = false;

// Drop any OnInformational callbacks, we're done there!
self.state.on_informational = None;

self.state.busy();
self.state.keep_alive &= msg.keep_alive;
self.state.version = msg.head.version;
Expand Down Expand Up @@ -579,7 +584,11 @@ where
},
buf,
) {
Ok(encoder) => Some(encoder),
Ok(encoder) => {
self.state.on_informational =
head.extensions.remove::<crate::ext::OnInformational>();
Some(encoder)
}
Err(err) => {
self.state.error = Some(err);
self.state.writing = Writing::Closed;
Expand Down Expand Up @@ -861,6 +870,10 @@ struct State {
date_header: bool,
title_case_headers: bool,
h09_responses: bool,
/// If set, called with each 1xx informational response received for
/// the current request. MUST be unset after a non-1xx response is
/// received.
on_informational: Option<crate::ext::OnInformational>,
/// Set to true when the Dispatcher should poll read operations
/// again. See the `maybe_notify` method for more.
notify_read: bool,
Expand Down Expand Up @@ -1039,6 +1052,13 @@ impl State {
if !T::should_read_first() {
self.notify_read = true;
}

if self.h1_header_read_timeout.is_some() {
// Next read will start and poll the header read timeout,
// so we can close the connection if another header isn't
// received in a timely manner.
self.notify_read = true;
}
}

fn is_idle(&self) -> bool {
Expand Down
2 changes: 2 additions & 0 deletions rama-http-core/src/proto/h1/io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ where
h1_parser_config: parse_ctx.h1_parser_config.clone(),
h1_max_headers: parse_ctx.h1_max_headers,
h09_responses: parse_ctx.h09_responses,
on_informational: parse_ctx.on_informational,
},
)? {
Some(msg) => {
Expand Down Expand Up @@ -690,6 +691,7 @@ mod tests {
h1_parser_config: Default::default(),
h1_max_headers: None,
h09_responses: false,
on_informational: &mut None,
};
assert!(buffered
.parse::<ClientTransaction>(cx, parse_ctx)
Expand Down
1 change: 1 addition & 0 deletions rama-http-core/src/proto/h1/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ pub(crate) struct ParseContext<'a> {
h1_parser_config: ParserConfig,
h1_max_headers: Option<usize>,
h09_responses: bool,
on_informational: &'a mut Option<crate::ext::OnInformational>,
}

struct EncodeHead<'a, S> {
Expand Down
22 changes: 22 additions & 0 deletions rama-http-core/src/proto/h1/role.rs
Original file line number Diff line number Diff line change
Expand Up @@ -956,6 +956,12 @@ impl Http1Transaction for Client {
}));
}

if head.subject.is_informational() {
if let Some(callback) = ctx.on_informational {
callback.call(head.into_response(()));
}
}

// Parsing a 1xx response could have consumed the buffer, check if
// it is empty now...
if buf.is_empty() {
Expand Down Expand Up @@ -1430,6 +1436,7 @@ mod tests {
h1_parser_config: Default::default(),
h1_max_headers: None,
h09_responses: false,
on_informational: &mut None,
},
)
.unwrap()
Expand All @@ -1451,6 +1458,7 @@ mod tests {
h1_parser_config: Default::default(),
h1_max_headers: None,
h09_responses: false,
on_informational: &mut None,
};
let msg = Client::parse(&mut raw, ctx).unwrap().unwrap();
assert_eq!(raw.len(), 0);
Expand All @@ -1468,6 +1476,7 @@ mod tests {
h1_parser_config: Default::default(),
h1_max_headers: None,
h09_responses: false,
on_informational: &mut None,
};
Server::parse(&mut raw, ctx).unwrap_err();
}
Expand All @@ -1482,6 +1491,7 @@ mod tests {
h1_parser_config: Default::default(),
h1_max_headers: None,
h09_responses: true,
on_informational: &mut None,
};
let msg = Client::parse(&mut raw, ctx).unwrap().unwrap();
assert_eq!(raw, H09_RESPONSE);
Expand All @@ -1498,6 +1508,7 @@ mod tests {
h1_parser_config: Default::default(),
h1_max_headers: None,
h09_responses: false,
on_informational: &mut None,
};
Client::parse(&mut raw, ctx).unwrap_err();
assert_eq!(raw, H09_RESPONSE);
Expand All @@ -1518,6 +1529,7 @@ mod tests {
h1_parser_config,
h1_max_headers: None,
h09_responses: false,
on_informational: &mut None,
};
let msg = Client::parse(&mut raw, ctx).unwrap().unwrap();
assert_eq!(raw.len(), 0);
Expand All @@ -1535,6 +1547,7 @@ mod tests {
h1_parser_config: Default::default(),
h1_max_headers: None,
h09_responses: false,
on_informational: &mut None,
};
Client::parse(&mut raw, ctx).unwrap_err();
}
Expand All @@ -1548,6 +1561,7 @@ mod tests {
h1_parser_config: Default::default(),
h1_max_headers: None,
h09_responses: false,
on_informational: &mut None,
};
let parsed_message = Server::parse(&mut raw, ctx).unwrap().unwrap();
let mut orig_headers = parsed_message
Expand All @@ -1572,6 +1586,7 @@ mod tests {
h1_parser_config: Default::default(),
h1_max_headers: None,
h09_responses: false,
on_informational: &mut None,
},
)
.expect("parse ok")
Expand All @@ -1587,6 +1602,7 @@ mod tests {
h1_parser_config: Default::default(),
h1_max_headers: None,
h09_responses: false,
on_informational: &mut None,
},
)
.expect_err(comment)
Expand Down Expand Up @@ -1811,6 +1827,7 @@ mod tests {
h1_parser_config: Default::default(),
h1_max_headers: None,
h09_responses: false,
on_informational: &mut None,
}
)
.expect("parse ok")
Expand All @@ -1826,6 +1843,7 @@ mod tests {
h1_parser_config: Default::default(),
h1_max_headers: None,
h09_responses: false,
on_informational: &mut None,
},
)
.expect("parse ok")
Expand All @@ -1841,6 +1859,7 @@ mod tests {
h1_parser_config: Default::default(),
h1_max_headers: None,
h09_responses: false,
on_informational: &mut None,
},
)
.expect_err("parse should err")
Expand Down Expand Up @@ -2433,6 +2452,7 @@ mod tests {
h1_parser_config: Default::default(),
h1_max_headers: None,
h09_responses: false,
on_informational: &mut None,
},
)
.expect("parse ok")
Expand Down Expand Up @@ -2470,6 +2490,7 @@ mod tests {
h1_parser_config: Default::default(),
h1_max_headers: max_headers,
h09_responses: false,
on_informational: &mut None,
},
);
if should_success {
Expand All @@ -2488,6 +2509,7 @@ mod tests {
h1_parser_config: Default::default(),
h1_max_headers: max_headers,
h09_responses: false,
on_informational: &mut None,
},
);
if should_success {
Expand Down
2 changes: 1 addition & 1 deletion rama-http-core/src/server/conn/http2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ impl Builder {
/// This is not advised, as it can potentially expose servers to DOS vulnerabilities.
///
/// See <https://rustsec.org/advisories/RUSTSEC-2024-0003.html> for more information.
pub fn max_local_error_reset_streams(mut self, max: impl Into<Option<usize>>) -> Self {
pub fn max_local_error_reset_streams(&mut self, max: impl Into<Option<usize>>) -> &mut Self {
self.h2_builder.max_local_error_reset_streams = max.into();
self
}
Expand Down
Loading

0 comments on commit 764b8a6

Please sign in to comment.