Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: hyperium/hyper
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: master
Choose a base ref
...
head repository: svix/hyper
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: main
Choose a head ref
Can’t automatically merge. Don’t worry, you can still create the pull request.
  • 16 commits
  • 14 files changed
  • 7 contributors

Commits on Jul 7, 2022

  1. fix(http1): fix http1_header_read_timeout to use same future (#2891)

    Co-authored-by: silence <[email protected]>
    (cherry picked from commit 5fa113e)
    silence-coding authored and seanmonstar committed Jul 7, 2022

    Verified

    This commit was signed with the committer’s verified signature.
    paescuj Pascal Jufer
    Copy the full SHA
    c5a14e7 View commit details
  2. chore(lib): bump MSRV to 1.56 (#2902)

    (cherry picked from commit a563404)
    seanmonstar committed Jul 7, 2022

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    128bc7f View commit details
  3. v0.14.20

    seanmonstar committed Jul 7, 2022

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    0ff6213 View commit details

Commits on Sep 23, 2022

  1. feat(server): add Server::tcp_keepalive_interval and `Server::tcp_k…

    …eepalive_retries` (#2991)
    
    If the platform supports setting the options, otherwise it's a noop.
    hansonchar authored Sep 23, 2022

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    287d712 View commit details

Commits on Oct 27, 2022

  1. Verified

    This commit was signed with the committer’s verified signature.
    paescuj Pascal Jufer
    Copy the full SHA
    73dd474 View commit details

Commits on Oct 28, 2022

  1. Verified

    This commit was signed with the committer’s verified signature.
    paescuj Pascal Jufer
    Copy the full SHA
    78e2c58 View commit details
  2. Verified

    This commit was signed with the committer’s verified signature.
    paescuj Pascal Jufer
    Copy the full SHA
    9fa3638 View commit details

Commits on Oct 31, 2022

  1. v0.14.21

    seanmonstar committed Oct 31, 2022

    Verified

    This commit was signed with the committer’s verified signature.
    paescuj Pascal Jufer
    Copy the full SHA
    9ad4055 View commit details
  2. Verified

    This commit was signed with the committer’s verified signature.
    paescuj Pascal Jufer
    Copy the full SHA
    e8765e0 View commit details
  3. v0.14.22

    seanmonstar committed Oct 31, 2022

    Verified

    This commit was signed with the committer’s verified signature.
    paescuj Pascal Jufer
    Copy the full SHA
    04d637e View commit details

Commits on Nov 7, 2022

  1. fix(http2): Fix race condition in client dispatcher (#3041)

    There exists a race condition in ClientTask::poll() when the request
    that is sent via h2::client::send_request() is pending open. A task will
    be spawned to wait for send capacity on the sendstream. Because this
    same stream is also stored in the pending member of
    h2::client::SendRequest the next iteration of the poll() loop can call
    poll_ready() and call wait_send() on the same stream passed into the
    spawned task.
    
    Fix this by always calling poll_ready() after send_request(). If this
    call to poll_ready() returns Pending save the necessary context in
    ClientTask and only spawn the task that will eventually resolve to the
    response after poll_ready() returns Ok.
    
    Closes #2419
    jfourie1 authored Nov 7, 2022

    Verified

    This commit was signed with the committer’s verified signature.
    paescuj Pascal Jufer
    Copy the full SHA
    2f1c0b7 View commit details
  2. Copy the full SHA
    4912c6d View commit details
  3. v0.14.23

    seanmonstar committed Nov 7, 2022
    Copy the full SHA
    d32beb3 View commit details

Commits on Dec 1, 2022

  1. Allow special headers w/ arbitrary name casing (#1)

    Allows adding headers with arbitrary casing
    so that we can have headers that are all-caps,
    all upper-case, or whatever else we want.
    
    Co-authored-by: James Lucas <[email protected]>
    svix-james and jaymell authored Dec 1, 2022
    Copy the full SHA
    ac7f12a View commit details

Commits on Dec 9, 2022

  1. Make HeaderCaseMap public (#2)

    This allows case-sensitive headers to be added
    by simply adding a HeaderCaseMap-typed extension
    to the hyper Request object.
    
    Co-authored-by: James Lucas <[email protected]>
    svix-james and jaymell authored Dec 9, 2022
    Copy the full SHA
    5ef6b75 View commit details

Commits on Dec 14, 2022

  1. Copy the full SHA
    b901ca7 View commit details
Showing with 544 additions and 120 deletions.
  1. +1 −1 .github/workflows/CI.yml
  2. +44 −0 CHANGELOG.md
  3. +3 −3 Cargo.toml
  4. +1 −1 docs/MSRV.md
  5. +33 −0 src/client/client.rs
  6. +19 −0 src/client/conn.rs
  7. +40 −14 src/client/dispatch.rs
  8. +11 −0 src/error.rs
  9. +13 −1 src/ext.rs
  10. +1 −1 src/proto/h1/role.rs
  11. +143 −81 src/proto/h2/client.rs
  12. +18 −5 src/server/server.rs
  13. +179 −13 src/server/tcp.rs
  14. +38 −0 tests/client.rs
2 changes: 1 addition & 1 deletion .github/workflows/CI.yml
Original file line number Diff line number Diff line change
@@ -101,7 +101,7 @@ jobs:
strategy:
matrix:
rust:
- 1.49 # keep in sync with MSRV.md dev doc
- 1.56 # keep in sync with MSRV.md dev doc

os:
- ubuntu-latest
44 changes: 44 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,47 @@
### v0.14.23 (2022-11-07)


#### Bug Fixes

* **http2:** Fix race condition in client dispatcher (#3041) ([2f1c0b72](https://github.com/hyperium/hyper/commit/2f1c0b720da4553fff216a38018a78ecafe23d60), closes [#2419](https://github.com/hyperium/hyper/issues/2419))


### v0.14.22 (2022-10-31)


#### Bug Fixes

* **server:** fix compile-time cfgs for TCP keepalive options (#3039) ([e8765e0f](https://github.com/hyperium/hyper/commit/e8765e0febd0267472799dcd1109af75944c2637), closes [#3038](https://github.com/hyperium/hyper/issues/3038))


### v0.14.21 (2022-10-31)


#### Bug Fixes

* **client:** send an error back to client when dispatch misbehaves () ([9fa36382](https://github.com/hyperium/hyper/commit/9fa363829ced232acb18c31ebab8ffb93f691ecc), closes [#2649](https://github.com/hyperium/hyper/issues/2649))
* **http1:** fix `http1_header_read_timeout` to use same future (#2891) ([c5a14e7c](https://github.com/hyperium/hyper/commit/c5a14e7c087424001223aaeb2dad532ba4ee6063))


#### Features

* **http1:** allow ignoring invalid header lines in requests ([73dd4746](https://github.com/hyperium/hyper/commit/73dd474652f5e71fe8a87baa6f9b2490ae746eb3))
* **server:** add `Server::tcp_keepalive_interval` and `Server::tcp_keepalive_retries` (#2991) ([287d7124](https://github.com/hyperium/hyper/commit/287d712483aec6671427438d60ed2a72f856fd9f))


### v0.14.20 (2022-07-07)


#### Bug Fixes

* **http1:** fix `http1_header_read_timeout` to use same future (#2891) ([c5a14e7c](https://github.com/hyperium/hyper/commit/c5a14e7c087424001223aaeb2dad532ba4ee6063))


#### Features

* **ext:** support non-canonical HTTP/1 reason phrases (#2792) ([b2052a43](https://github.com/hyperium/hyper/commit/b2052a433fd151d7d745ee9c5b27a2031db1dc32))


### v0.14.19 (2022-05-27)


6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "hyper"
version = "0.14.19"
version = "0.14.23"
description = "A fast and correct HTTP library."
readme = "README.md"
homepage = "https://hyper.rs"
@@ -27,7 +27,7 @@ futures-util = { version = "0.3", default-features = false }
http = "0.2"
http-body = "0.4"
httpdate = "1.0"
httparse = "1.6"
httparse = "1.8"
h2 = { version = "0.3.9", optional = true }
itoa = "1"
tracing = { version = "0.1", default-features = false, features = ["std"] }
@@ -39,7 +39,7 @@ want = "0.3"
# Optional

libc = { version = "0.2", optional = true }
socket2 = { version = "0.4", optional = true }
socket2 = { version = "0.4.7", optional = true, features = ["all"] }

[dev-dependencies]
futures-util = { version = "0.3", default-features = false, features = ["alloc"] }
2 changes: 1 addition & 1 deletion docs/MSRV.md
Original file line number Diff line number Diff line change
@@ -6,4 +6,4 @@ hyper. It is possible that an older compiler can work, but that is not
guaranteed. We try to increase the MSRV responsibly, only when a significant
new feature is needed.

The current MSRV is: **1.49**.
The current MSRV is: **1.56**.
33 changes: 33 additions & 0 deletions src/client/client.rs
Original file line number Diff line number Diff line change
@@ -1063,6 +1063,39 @@ impl Builder {
self
}

/// Sets whether invalid header lines should be silently ignored in HTTP/1 responses.
///
/// This mimicks the behaviour of major browsers. You probably don't want this.
/// You should only want this if you are implementing a proxy whose main
/// purpose is to sit in front of browsers whose users access arbitrary content
/// which may be malformed, and they expect everything that works without
/// the proxy to keep working with the proxy.
///
/// This option will prevent Hyper's client from returning an error encountered
/// when parsing a header, except if the error was caused by the character NUL
/// (ASCII code 0), as Chrome specifically always reject those.
///
/// The ignorable errors are:
/// * empty header names;
/// * characters that are not allowed in header names, except for `\0` and `\r`;
/// * when `allow_spaces_after_header_name_in_responses` is not enabled,
/// spaces and tabs between the header name and the colon;
/// * missing colon between header name and colon;
/// * characters that are not allowed in header values except for `\0` and `\r`.
///
/// If an ignorable error is encountered, the parser tries to find the next
/// line in the input to resume parsing the rest of the headers. An error
/// will be emitted nonetheless if it finds `\0` or a lone `\r` while
/// looking for the next line.
pub fn http1_ignore_invalid_headers_in_responses(
&mut self,
val: bool,
) -> &mut Builder {
self.conn_builder
.http1_ignore_invalid_headers_in_responses(val);
self
}

/// Set whether HTTP/1 connections should try to use vectored writes,
/// or always flatten into a single buffer.
///
19 changes: 19 additions & 0 deletions src/client/conn.rs
Original file line number Diff line number Diff line change
@@ -662,6 +662,24 @@ impl Builder {
self
}

/// Set whether HTTP/1 connections will silently ignored malformed header lines.
///
/// If this is enabled and and a header line does not start with a valid header
/// name, or does not include a colon at all, the line will be silently ignored
/// and no error will be reported.
///
/// Note that this setting does not affect HTTP/2.
///
/// Default is false.
pub fn http1_ignore_invalid_headers_in_responses(
&mut self,
enabled: bool,
) -> &mut Builder {
self.h1_parser_config
.ignore_invalid_headers_in_responses(enabled);
self
}

/// Set whether HTTP/1 connections should try to use vectored writes,
/// or always flatten into a single buffer.
///
@@ -970,6 +988,7 @@ impl Builder {
if opts.h1_preserve_header_case {
conn.set_preserve_header_case();
}

#[cfg(feature = "ffi")]
if opts.h1_preserve_header_order {
conn.set_preserve_header_order();
54 changes: 40 additions & 14 deletions src/client/dispatch.rs
Original file line number Diff line number Diff line change
@@ -86,7 +86,7 @@ impl<T, U> Sender<T, U> {
}
let (tx, rx) = oneshot::channel();
self.inner
.send(Envelope(Some((val, Callback::Retry(tx)))))
.send(Envelope(Some((val, Callback::Retry(Some(tx))))))
.map(move |_| rx)
.map_err(|mut e| (e.0).0.take().expect("envelope not dropped").0)
}
@@ -97,7 +97,7 @@ impl<T, U> Sender<T, U> {
}
let (tx, rx) = oneshot::channel();
self.inner
.send(Envelope(Some((val, Callback::NoRetry(tx)))))
.send(Envelope(Some((val, Callback::NoRetry(Some(tx))))))
.map(move |_| rx)
.map_err(|mut e| (e.0).0.take().expect("envelope not dropped").0)
}
@@ -124,7 +124,7 @@ impl<T, U> UnboundedSender<T, U> {
pub(crate) fn try_send(&mut self, val: T) -> Result<RetryPromise<T, U>, T> {
let (tx, rx) = oneshot::channel();
self.inner
.send(Envelope(Some((val, Callback::Retry(tx)))))
.send(Envelope(Some((val, Callback::Retry(Some(tx))))))
.map(move |_| rx)
.map_err(|mut e| (e.0).0.take().expect("envelope not dropped").0)
}
@@ -198,33 +198,59 @@ impl<T, U> Drop for Envelope<T, U> {
}

pub(crate) enum Callback<T, U> {
Retry(oneshot::Sender<Result<U, (crate::Error, Option<T>)>>),
NoRetry(oneshot::Sender<Result<U, crate::Error>>),
Retry(Option<oneshot::Sender<Result<U, (crate::Error, Option<T>)>>>),
NoRetry(Option<oneshot::Sender<Result<U, crate::Error>>>),
}

impl<T, U> Drop for Callback<T, U> {
fn drop(&mut self) {
// FIXME(nox): What errors do we want here?
let error = crate::Error::new_user_dispatch_gone().with(if std::thread::panicking() {
"user code panicked"
} else {
"runtime dropped the dispatch task"
});

match self {
Callback::Retry(tx) => {
if let Some(tx) = tx.take() {
let _ = tx.send(Err((error, None)));
}
}
Callback::NoRetry(tx) => {
if let Some(tx) = tx.take() {
let _ = tx.send(Err(error));
}
}
}
}
}

impl<T, U> Callback<T, U> {
#[cfg(feature = "http2")]
pub(crate) fn is_canceled(&self) -> bool {
match *self {
Callback::Retry(ref tx) => tx.is_closed(),
Callback::NoRetry(ref tx) => tx.is_closed(),
Callback::Retry(Some(ref tx)) => tx.is_closed(),
Callback::NoRetry(Some(ref tx)) => tx.is_closed(),
_ => unreachable!(),
}
}

pub(crate) fn poll_canceled(&mut self, cx: &mut task::Context<'_>) -> Poll<()> {
match *self {
Callback::Retry(ref mut tx) => tx.poll_closed(cx),
Callback::NoRetry(ref mut tx) => tx.poll_closed(cx),
Callback::Retry(Some(ref mut tx)) => tx.poll_closed(cx),
Callback::NoRetry(Some(ref mut tx)) => tx.poll_closed(cx),
_ => unreachable!(),
}
}

pub(crate) fn send(self, val: Result<U, (crate::Error, Option<T>)>) {
pub(crate) fn send(mut self, val: Result<U, (crate::Error, Option<T>)>) {
match self {
Callback::Retry(tx) => {
let _ = tx.send(val);
Callback::Retry(ref mut tx) => {
let _ = tx.take().unwrap().send(val);
}
Callback::NoRetry(tx) => {
let _ = tx.send(val.map_err(|e| e.0));
Callback::NoRetry(ref mut tx) => {
let _ = tx.take().unwrap().send(val.map_err(|e| e.0));
}
}
}
11 changes: 11 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -137,6 +137,10 @@ pub(super) enum User {
#[cfg(feature = "server")]
WithoutShutdownNonHttp1,

/// The dispatch task is gone.
#[cfg(feature = "client")]
DispatchGone,

/// User aborted in an FFI callback.
#[cfg(feature = "ffi")]
AbortedByCallback,
@@ -387,6 +391,11 @@ impl Error {
Error::new_user(User::AbortedByCallback)
}

#[cfg(feature = "client")]
pub(super) fn new_user_dispatch_gone() -> Error {
Error::new(Kind::User(User::DispatchGone))
}

#[cfg(feature = "http2")]
pub(super) fn new_h2(cause: ::h2::Error) -> Error {
if cause.is_io() {
@@ -483,6 +492,8 @@ impl Error {
Kind::User(User::WithoutShutdownNonHttp1) => {
"without_shutdown() called on a non-HTTP/1 connection"
}
#[cfg(feature = "client")]
Kind::User(User::DispatchGone) => "dispatch task is gone",
#[cfg(feature = "ffi")]
Kind::User(User::AbortedByCallback) => "operation aborted by an application callback",
}
14 changes: 13 additions & 1 deletion src/ext.rs
Original file line number Diff line number Diff line change
@@ -40,6 +40,7 @@ impl Protocol {
self.inner.as_str()
}

#[cfg(feature = "server")]
pub(crate) fn from_inner(inner: h2::ext::Protocol) -> Self {
Self { inner }
}
@@ -94,10 +95,15 @@ impl fmt::Debug for Protocol {
///
/// [`http1_preserve_header_case`]: /client/struct.Client.html#method.http1_preserve_header_case
#[derive(Clone, Debug)]
pub(crate) struct HeaderCaseMap(HeaderMap<Bytes>);
pub struct HeaderCaseMap(HeaderMap<Bytes>);

#[cfg(feature = "http1")]
impl HeaderCaseMap {
/// Thank you hyper for not just making this public to being with
pub fn get(&self, key: HeaderName) -> Option<&Bytes> {
self.0.get(key)
}

/// Returns a view of all spellings associated with that header name,
/// in the order they were found.
pub(crate) fn get_all<'a>(
@@ -130,6 +136,12 @@ impl HeaderCaseMap {
}
}

impl From<HeaderMap<Bytes>> for HeaderCaseMap {
fn from(hdr_map: HeaderMap<Bytes>) -> Self {
Self(hdr_map)
}
}

#[cfg(feature = "ffi")]
#[derive(Clone, Debug)]
/// Hashmap<Headername, numheaders with that name>
2 changes: 1 addition & 1 deletion src/proto/h1/role.rs
Original file line number Diff line number Diff line change
@@ -79,7 +79,7 @@ where
if !*ctx.h1_header_read_timeout_running {
if let Some(h1_header_read_timeout) = ctx.h1_header_read_timeout {
let deadline = Instant::now() + h1_header_read_timeout;

*ctx.h1_header_read_timeout_running = true;
match ctx.h1_header_read_timeout_fut {
Some(h1_header_read_timeout_fut) => {
debug!("resetting h1 header read timeout timer");
Loading