Skip to content

Commit 9234d9c

Browse files
authored
Merge branch 'master' into header-casing
2 parents d19a17e + 166c6ca commit 9234d9c

File tree

14 files changed

+459
-301
lines changed

14 files changed

+459
-301
lines changed

CHANGELOG.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,45 @@
1+
### v1.8.1 (2025-11-13)
2+
3+
4+
#### Bug Fixes
5+
6+
* **http1:** fix consuming extra CPU from previous change (#3977) ([4492f31e](https://github.com/hyperium/hyper/commit/4492f31e9429c34166da5a069c00b65be20e4a02))
7+
8+
9+
## v1.8.0 (2025-11-11)
10+
11+
12+
#### Bug Fixes
13+
14+
* **http1:** fix rare missed write wakeup on connections (#3952) ([2377b893](https://github.com/hyperium/hyper/commit/2377b893f6e64ca9878e4f25d1472b96baa7e3ea))
15+
* **http2:** fix internals of HTTP/2 CONNECT upgrades (#3967) ([58e0e7dc](https://github.com/hyperium/hyper/commit/58e0e7dc70612117ccdc40da395922f791cb273a), closes [#3966](https://github.com/hyperium/hyper/issues/3966))
16+
17+
18+
#### Features
19+
20+
* **rt:** add `Timer::now()` method to allow overriding the instant returned (#3965) ([5509ebe6](https://github.com/hyperium/hyper/commit/5509ebe6156e32d4f8986fafa25c2918a30005be))
21+
22+
23+
#### Breaking Changes
24+
25+
* The HTTP/2 client connection no longer allows an executor
26+
that can not spawn itself.
27+
28+
This was an oversight originally. The client connection will now include spawning
29+
a future that keeps a copy of the executor to spawn other futures. Thus, if it is
30+
`!Send`, it needs to spawn `!Send` futures. The likelihood of executors that match
31+
the previously allowed behavior should be very remote.
32+
33+
There is also technically a semver break in here, which is that the
34+
`Http2ClientConnExec` trait no longer dyn-compatible, because it now expects to
35+
be `Clone`. This should not break usage of the `conn` builder, because it already
36+
separately had `E: Clone` bounds. If someone were using `dyn Http2ClientConnExec`,
37+
that will break. However, there is no purpose for doing so, and it is not usable
38+
otherwise, since the trait only exists to propagate bounds into hyper. Thus, the
39+
breakage should not affect anyone.
40+
([58e0e7dc](https://github.com/hyperium/hyper/commit/58e0e7dc70612117ccdc40da395922f791cb273a))
41+
42+
143
## v1.7.0 (2025-08-18)
244

345

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "hyper"
3-
version = "1.7.0"
3+
version = "1.8.1"
44
description = "A protective and efficient HTTP library for all."
55
readme = "README.md"
66
homepage = "https://hyper.rs"

benches/support/tokiort.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ impl Timer for TokioTimer {
4242
})
4343
}
4444

45+
fn now(&self) -> Instant {
46+
tokio::time::Instant::now().into()
47+
}
48+
4549
fn reset(&self, sleep: &mut Pin<Box<dyn Sleep>>, new_deadline: Instant) {
4650
if let Some(sleep) = sleep.as_mut().downcast_mut_pin::<TokioSleep>() {
4751
sleep.reset(new_deadline)

src/client/conn/http2.rs

Lines changed: 0 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -640,44 +640,6 @@ mod tests {
640640
}
641641
}
642642

643-
#[tokio::test]
644-
#[ignore] // only compilation is checked
645-
async fn not_send_not_sync_executor_of_send_futures() {
646-
#[derive(Clone)]
647-
struct TokioExecutor {
648-
// !Send, !Sync
649-
_x: std::marker::PhantomData<std::rc::Rc<()>>,
650-
}
651-
652-
impl<F> crate::rt::Executor<F> for TokioExecutor
653-
where
654-
F: std::future::Future + 'static + Send,
655-
F::Output: Send + 'static,
656-
{
657-
fn execute(&self, fut: F) {
658-
tokio::task::spawn(fut);
659-
}
660-
}
661-
662-
#[allow(unused)]
663-
async fn run(io: impl crate::rt::Read + crate::rt::Write + Send + Unpin + 'static) {
664-
let (_sender, conn) =
665-
crate::client::conn::http2::handshake::<_, _, http_body_util::Empty<bytes::Bytes>>(
666-
TokioExecutor {
667-
_x: Default::default(),
668-
},
669-
io,
670-
)
671-
.await
672-
.unwrap();
673-
674-
tokio::task::spawn_local(async move {
675-
// can't use spawn here because when executor is !Send
676-
conn.await.unwrap();
677-
});
678-
}
679-
}
680-
681643
#[tokio::test]
682644
#[ignore] // only compilation is checked
683645
async fn send_not_sync_executor_of_send_futures() {

src/client/dispatch.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -325,22 +325,23 @@ impl<T> TrySendError<T> {
325325

326326
#[cfg(feature = "http2")]
327327
pin_project! {
328-
pub struct SendWhen<B>
328+
pub struct SendWhen<B, E>
329329
where
330330
B: Body,
331331
B: 'static,
332332
{
333333
#[pin]
334-
pub(crate) when: ResponseFutMap<B>,
334+
pub(crate) when: ResponseFutMap<B, E>,
335335
#[pin]
336336
pub(crate) call_back: Option<Callback<Request<B>, Response<Incoming>>>,
337337
}
338338
}
339339

340340
#[cfg(feature = "http2")]
341-
impl<B> Future for SendWhen<B>
341+
impl<B, E> Future for SendWhen<B, E>
342342
where
343343
B: Body + 'static,
344+
E: crate::rt::bounds::Http2UpgradedExec<B::Data>,
344345
{
345346
type Output = ();
346347

src/common/time.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,13 @@ impl Time {
5050
}
5151
}
5252

53+
pub(crate) fn now(&self) -> Instant {
54+
match *self {
55+
Time::Empty => Instant::now(),
56+
Time::Timer(ref t) => t.now(),
57+
}
58+
}
59+
5360
pub(crate) fn reset(&self, sleep: &mut Pin<Box<dyn Sleep>>, new_deadline: Instant) {
5461
match *self {
5562
Time::Empty => {

src/proto/h1/conn.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use std::marker::{PhantomData, Unpin};
66
use std::pin::Pin;
77
use std::task::{Context, Poll};
88
#[cfg(feature = "server")]
9-
use std::time::{Duration, Instant};
9+
use std::time::Duration;
1010

1111
use crate::rt::{Read, Write};
1212
use bytes::{Buf, Bytes};
@@ -218,7 +218,7 @@ where
218218
#[cfg(feature = "server")]
219219
if !self.state.h1_header_read_timeout_running {
220220
if let Some(h1_header_read_timeout) = self.state.h1_header_read_timeout {
221-
let deadline = Instant::now() + h1_header_read_timeout;
221+
let deadline = self.state.timer.now() + h1_header_read_timeout;
222222
self.state.h1_header_read_timeout_running = true;
223223
match self.state.h1_header_read_timeout_fut {
224224
Some(ref mut h1_header_read_timeout_fut) => {

src/proto/h2/client.rs

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,16 @@ use http::{Method, StatusCode};
1818
use pin_project_lite::pin_project;
1919

2020
use super::ping::{Ponger, Recorder};
21-
use super::{ping, H2Upgraded, PipeToSendStream, SendBuf};
21+
use super::{ping, PipeToSendStream, SendBuf};
2222
use crate::body::{Body, Incoming as IncomingBody};
2323
use crate::client::dispatch::{Callback, SendWhen, TrySendError};
2424
use crate::common::either::Either;
2525
use crate::common::io::Compat;
2626
use crate::common::time::Time;
2727
use crate::ext::Protocol;
2828
use crate::headers;
29-
use crate::proto::h2::UpgradedSendStream;
3029
use crate::proto::Dispatched;
31-
use crate::rt::bounds::Http2ClientConnExec;
30+
use crate::rt::bounds::{Http2ClientConnExec, Http2UpgradedExec};
3231
use crate::upgrade::Upgraded;
3332
use crate::{Request, Response};
3433
use h2::client::ResponseFuture;
@@ -151,7 +150,7 @@ where
151150
T: Read + Write + Unpin,
152151
B: Body + 'static,
153152
B::Data: Send + 'static,
154-
E: Http2ClientConnExec<B, T> + Unpin,
153+
E: Http2ClientConnExec<B, T> + Clone + Unpin,
155154
B::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
156155
{
157156
let (h2_tx, mut conn) = new_builder(config)
@@ -357,7 +356,7 @@ where
357356

358357
pin_project! {
359358
#[project = H2ClientFutureProject]
360-
pub enum H2ClientFuture<B, T>
359+
pub enum H2ClientFuture<B, T, E>
361360
where
362361
B: http_body::Body,
363362
B: 'static,
@@ -372,7 +371,7 @@ pin_project! {
372371
},
373372
Send {
374373
#[pin]
375-
send_when: SendWhen<B>,
374+
send_when: SendWhen<B, E>,
376375
},
377376
Task {
378377
#[pin]
@@ -381,11 +380,12 @@ pin_project! {
381380
}
382381
}
383382

384-
impl<B, T> Future for H2ClientFuture<B, T>
383+
impl<B, T, E> Future for H2ClientFuture<B, T, E>
385384
where
386385
B: http_body::Body + 'static,
387386
B::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
388387
T: Read + Write + Unpin,
388+
E: Http2UpgradedExec<B::Data>,
389389
{
390390
type Output = ();
391391

@@ -484,7 +484,7 @@ impl<B, E, T> ClientTask<B, E, T>
484484
where
485485
B: Body + 'static + Unpin,
486486
B::Data: Send,
487-
E: Http2ClientConnExec<B, T> + Unpin,
487+
E: Http2ClientConnExec<B, T> + Clone + Unpin,
488488
B::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
489489
T: Read + Write + Unpin,
490490
{
@@ -529,6 +529,7 @@ where
529529
fut: f.fut,
530530
ping: Some(ping),
531531
send_stream: Some(send_stream),
532+
exec: self.executor.clone(),
532533
},
533534
call_back: Some(f.cb),
534535
},
@@ -537,28 +538,29 @@ where
537538
}
538539

539540
pin_project! {
540-
pub(crate) struct ResponseFutMap<B>
541+
pub(crate) struct ResponseFutMap<B, E>
541542
where
542543
B: Body,
543544
B: 'static,
544545
{
545546
#[pin]
546547
fut: ResponseFuture,
547-
#[pin]
548548
ping: Option<Recorder>,
549549
#[pin]
550550
send_stream: Option<Option<SendStream<SendBuf<<B as Body>::Data>>>>,
551+
exec: E,
551552
}
552553
}
553554

554-
impl<B> Future for ResponseFutMap<B>
555+
impl<B, E> Future for ResponseFutMap<B, E>
555556
where
556557
B: Body + 'static,
558+
E: Http2UpgradedExec<B::Data>,
557559
{
558560
type Output = Result<Response<crate::body::Incoming>, (crate::Error, Option<Request<B>>)>;
559561

560-
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
561-
let mut this = self.project();
562+
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
563+
let mut this = self.as_mut().project();
562564

563565
let result = ready!(this.fut.poll(cx));
564566

@@ -585,13 +587,10 @@ where
585587
let mut res = Response::from_parts(parts, IncomingBody::empty());
586588

587589
let (pending, on_upgrade) = crate::upgrade::pending();
588-
let io = H2Upgraded {
589-
ping,
590-
send_stream: unsafe { UpgradedSendStream::new(send_stream) },
591-
recv_stream,
592-
buf: Bytes::new(),
593-
};
594-
let upgraded = Upgraded::new(io, Bytes::new());
590+
591+
let (h2_up, up_task) = super::upgrade::pair(send_stream, recv_stream, ping);
592+
self.exec.execute_upgrade(up_task);
593+
let upgraded = Upgraded::new(h2_up, Bytes::new());
595594

596595
pending.fulfill(upgraded);
597596
res.extensions_mut().insert(on_upgrade);
@@ -620,7 +619,7 @@ where
620619
B: Body + 'static + Unpin,
621620
B::Data: Send,
622621
B::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
623-
E: Http2ClientConnExec<B, T> + Unpin,
622+
E: Http2ClientConnExec<B, T> + Clone + Unpin,
624623
T: Read + Write + Unpin,
625624
{
626625
type Output = crate::Result<Dispatched>;

0 commit comments

Comments
 (0)