Skip to content

Commit 89b23fc

Browse files
committed
Merge remote-tracking branch 'upstream/master' into server-ffi
2 parents ca2aaf6 + c68d424 commit 89b23fc

File tree

18 files changed

+267
-32
lines changed

18 files changed

+267
-32
lines changed

.github/workflows/CI.yml

+1
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ jobs:
118118
- name: Pin some dependencies
119119
run: |
120120
cargo update -p tokio --precise 1.38.1
121+
cargo update -p tokio-util --precise 0.7.11
121122
122123
- name: Check
123124
run: cargo check --features full

CHANGELOG.md

+20-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,21 @@
1+
## v1.5.0 (2024-10-15)
2+
3+
4+
#### Bug Fixes
5+
6+
* **http1:**
7+
* improve performance of parsing sequentially partial messages (#3764) ([3900a23](https://github.com/hyperium/hyper/commit/3900a2381b96a7e7f608a5e031b3e90ddcdfcd74))
8+
* send 'connection: close' when connection is ending (#3725) ([c86a6bcb](https://github.com/hyperium/hyper/commit/c86a6bcb4acb0f92e731ea2e4c1e4a839248a600), closes [#3720](https://github.com/hyperium/hyper/issues/3720))
9+
* make `date_header` effective (#3718) ([7de02373](https://github.com/hyperium/hyper/commit/7de02373f5e4ce392587a4d9d7710c6faf9c6165))
10+
* **http2:** strip content-length header in response to CONNECT requests (#3748) ([67a4a498](https://github.com/hyperium/hyper/commit/67a4a498d8bbdce4e604bc578da4693fb048f83d))
11+
12+
13+
#### Features
14+
15+
* **client:** Add HTTP/2 builder options `header_table_size()` and `max_concurrent_streams()` ([4c84e8c1](https://github.com/hyperium/hyper/commit/4c84e8c1c26a1464221de96b9f39816ce7251a5f))
16+
* **rt:** add `ReadBufCursor` methods `remaining()` and `put_slice()` (#3700) ([5a13041e](https://github.com/hyperium/hyper/commit/5a13041ed7033c9dab6e2adafd08b6af20cd33fb))
17+
18+
119
### v1.4.1 (2024-07-09)
220

321

@@ -212,7 +230,7 @@ Be sure to check out the [upgrading guide](https://hyper.rs/guides/1/upgrading).
212230

213231
#### Breaking Changes
214232

215-
* Any IO transport type provided must not implement `hyper::rt::{Read, Write}` instead of
233+
* Any IO transport type provided must now implement `hyper::rt::{Read, Write}` instead of
216234
`tokio::io` traits. You can grab a helper type from `hyper-util` to wrap Tokio types, or implement the traits yourself,
217235
if it's a custom type.
218236
([f9f65b7a](https://github.com/hyperium/hyper/commit/f9f65b7aa67fa3ec0267fe015945973726285bc2))
@@ -1600,7 +1618,7 @@ Be sure to check out the [upgrading guide](https://hyper.rs/guides/1/upgrading).
16001618

16011619
* **client:**
16021620
* check for dead connections in Pool ([44af2738](https://github.com/hyperium/hyper/commit/44af273853f82b81591b813d13627e143a14a6b7), closes [#1429](https://github.com/hyperium/hyper/issues/1429))
1603-
* error on unsupport 101 responses, ignore other 1xx codes ([22774222](https://github.com/hyperium/hyper/commit/227742221fa7830a14c18becbbc6137d97b57729))
1621+
* error on unsupported 101 responses, ignore other 1xx codes ([22774222](https://github.com/hyperium/hyper/commit/227742221fa7830a14c18becbbc6137d97b57729))
16041622
* **server:**
16051623
* send 400 responses on parse errors before closing connection ([7cb72d20](https://github.com/hyperium/hyper/commit/7cb72d2019bffbc667b9ad2d8cbc19c1a513fcf7))
16061624
* error if Response code is 1xx ([44c34ce9](https://github.com/hyperium/hyper/commit/44c34ce9adc888916bd67656cc54c35f7908f536))

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "hyper"
3-
version = "1.4.1"
3+
version = "1.5.0"
44
description = "A fast and correct HTTP library."
55
readme = "README.md"
66
homepage = "https://hyper.rs"

benches/pipeline.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ fn hello_world_16(b: &mut test::Bencher) {
7676
tcp.write_all(b"GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n")
7777
.unwrap();
7878
let mut buf = Vec::new();
79-
tcp.read_to_end(&mut buf).unwrap()
79+
tcp.read_to_end(&mut buf).unwrap() - "connection: close\r\n".len()
8080
} * PIPELINED_REQUESTS;
8181

8282
let mut tcp = TcpStream::connect(addr).unwrap();

benches/server.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ macro_rules! bench_server {
7272
tcp.write_all(b"GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n")
7373
.unwrap();
7474
let mut buf = Vec::new();
75-
tcp.read_to_end(&mut buf).unwrap()
75+
tcp.read_to_end(&mut buf).unwrap() - "connection: close\r\n".len()
7676
};
7777

7878
let mut tcp = TcpStream::connect(addr).unwrap();

examples/hello-http2.rs

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
#![deny(warnings)]
2+
3+
use std::convert::Infallible;
4+
use std::net::SocketAddr;
5+
6+
use http_body_util::Full;
7+
use hyper::body::Bytes;
8+
use hyper::server::conn::http2;
9+
use hyper::service::service_fn;
10+
use hyper::{Request, Response};
11+
use tokio::net::TcpListener;
12+
13+
// This would normally come from the `hyper-util` crate, but we can't depend
14+
// on that here because it would be a cyclical dependency.
15+
#[path = "../benches/support/mod.rs"]
16+
mod support;
17+
use support::TokioIo;
18+
19+
// An async function that consumes a request, does nothing with it and returns a
20+
// response.
21+
async fn hello(_: Request<hyper::body::Incoming>) -> Result<Response<Full<Bytes>>, Infallible> {
22+
Ok(Response::new(Full::new(Bytes::from("Hello, World!"))))
23+
}
24+
25+
#[derive(Clone)]
26+
// An Executor that uses the tokio runtime.
27+
pub struct TokioExecutor;
28+
29+
// Implement the `hyper::rt::Executor` trait for `TokioExecutor` so that it can be used to spawn
30+
// tasks in the hyper runtime.
31+
// An Executor allows us to manage execution of tasks which can help us improve the efficiency and
32+
// scalability of the server.
33+
impl<F> hyper::rt::Executor<F> for TokioExecutor
34+
where
35+
F: std::future::Future + Send + 'static,
36+
F::Output: Send + 'static,
37+
{
38+
fn execute(&self, fut: F) {
39+
tokio::task::spawn(fut);
40+
}
41+
}
42+
43+
#[tokio::main]
44+
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
45+
pretty_env_logger::init();
46+
47+
// This address is localhost
48+
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
49+
50+
// Bind to the port and listen for incoming TCP connections
51+
let listener = TcpListener::bind(addr).await?;
52+
53+
loop {
54+
// When an incoming TCP connection is received grab a TCP stream for
55+
// client-server communication.
56+
//
57+
// Note, this is a .await point, this loop will loop forever but is not a busy loop. The
58+
// .await point allows the Tokio runtime to pull the task off of the thread until the task
59+
// has work to do. In this case, a connection arrives on the port we are listening on and
60+
// the task is woken up, at which point the task is then put back on a thread, and is
61+
// driven forward by the runtime, eventually yielding a TCP stream.
62+
let (stream, _) = listener.accept().await?;
63+
// Use an adapter to access something implementing `tokio::io` traits as if they implement
64+
// `hyper::rt` IO traits.
65+
let io = TokioIo::new(stream);
66+
67+
// Spin up a new task in Tokio so we can continue to listen for new TCP connection on the
68+
// current task without waiting for the processing of the HTTP/2 connection we just received
69+
// to finish
70+
tokio::task::spawn(async move {
71+
// Handle the connection from the client using HTTP/2 with an executor and pass any
72+
// HTTP requests received on that connection to the `hello` function
73+
if let Err(err) = http2::Builder::new(TokioExecutor)
74+
.serve_connection(io, service_fn(hello))
75+
.await
76+
{
77+
eprintln!("Error serving connection: {}", err);
78+
}
79+
});
80+
}
81+
}

src/client/conn/http1.rs

+2
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ pub struct Parts<T> {
4848
///
4949
/// In most cases, this should just be spawned into an executor, so that it
5050
/// can process incoming and outgoing messages, notice hangups, and the like.
51+
///
52+
/// Instances of this type are typically created via the [`handshake`] function
5153
#[must_use = "futures do nothing unless polled"]
5254
pub struct Connection<T, B>
5355
where

src/client/conn/http2.rs

+44-6
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ impl<B> Clone for SendRequest<B> {
3737
///
3838
/// In most cases, this should just be spawned into an executor, so that it
3939
/// can process incoming and outgoing messages, notice hangups, and the like.
40+
///
41+
/// Instances of this type are typically created via the [`handshake`] function
4042
#[must_use = "futures do nothing unless polled"]
4143
pub struct Connection<T, B, E>
4244
where
@@ -337,13 +339,9 @@ where
337339

338340
/// Sets the maximum frame size to use for HTTP2.
339341
///
340-
/// Passing `None` will do nothing.
341-
///
342-
/// If not set, hyper will use a default.
342+
/// Default is currently 16KB, but can change.
343343
pub fn max_frame_size(&mut self, sz: impl Into<Option<u32>>) -> &mut Self {
344-
if let Some(sz) = sz.into() {
345-
self.h2_builder.max_frame_size = sz;
346-
}
344+
self.h2_builder.max_frame_size = sz.into();
347345
self
348346
}
349347

@@ -355,6 +353,46 @@ where
355353
self
356354
}
357355

356+
/// Sets the header table size.
357+
///
358+
/// This setting informs the peer of the maximum size of the header compression
359+
/// table used to encode header blocks, in octets. The encoder may select any value
360+
/// equal to or less than the header table size specified by the sender.
361+
///
362+
/// The default value of crate `h2` is 4,096.
363+
pub fn header_table_size(&mut self, size: impl Into<Option<u32>>) -> &mut Self {
364+
self.h2_builder.header_table_size = size.into();
365+
self
366+
}
367+
368+
/// Sets the maximum number of concurrent streams.
369+
///
370+
/// The maximum concurrent streams setting only controls the maximum number
371+
/// of streams that can be initiated by the remote peer. In other words,
372+
/// when this setting is set to 100, this does not limit the number of
373+
/// concurrent streams that can be created by the caller.
374+
///
375+
/// It is recommended that this value be no smaller than 100, so as to not
376+
/// unnecessarily limit parallelism. However, any value is legal, including
377+
/// 0. If `max` is set to 0, then the remote will not be permitted to
378+
/// initiate streams.
379+
///
380+
/// Note that streams in the reserved state, i.e., push promises that have
381+
/// been reserved but the stream has not started, do not count against this
382+
/// setting.
383+
///
384+
/// Also note that if the remote *does* exceed the value set here, it is not
385+
/// a protocol level error. Instead, the `h2` library will immediately reset
386+
/// the stream.
387+
///
388+
/// See [Section 5.1.2] in the HTTP/2 spec for more details.
389+
///
390+
/// [Section 5.1.2]: https://http2.github.io/http2-spec/#rfc.section.5.1.2
391+
pub fn max_concurrent_streams(&mut self, max: impl Into<Option<u32>>) -> &mut Self {
392+
self.h2_builder.max_concurrent_streams = max.into();
393+
self
394+
}
395+
358396
/// Sets an interval for HTTP2 Ping frames should be sent to keep a
359397
/// connection alive.
360398
///

src/client/dispatch.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ pub(crate) type Promise<T> = oneshot::Receiver<Result<T, crate::Error>>;
1818

1919
/// An error when calling `try_send_request`.
2020
///
21-
/// There is a possibility of an error occuring on a connection in-between the
21+
/// There is a possibility of an error occurring on a connection in-between the
2222
/// time that a request is queued and when it is actually written to the IO
2323
/// transport. If that happens, it is safe to return the request back to the
2424
/// caller, as it was never fully sent.

src/ext/mod.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -191,9 +191,9 @@ impl OriginalHeaderOrder {
191191
self.entry_order.push((name, idx));
192192
}
193193

194-
// No doc test is possible here because (a) `RUSTDOCFLAGS='--cfg hyper_unstable_ffi'`
195-
// is needed to enable this feature and (b) because this is a private interface and doctests
196-
// can only see public symbols.
194+
// No doc test is run here because `RUSTFLAGS='--cfg hyper_unstable_ffi'`
195+
// is needed to compile. Once ffi is stabilized `no_run` should be removed
196+
// here.
197197
/// This returns an iterator that provides header names and indexes
198198
/// in the original order received.
199199
///

src/proto/h1/conn.rs

+17-8
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use super::{Decoder, Encode, EncodedBuf, Encoder, Http1Transaction, ParseContext
2121
use crate::body::DecodedLength;
2222
#[cfg(feature = "server")]
2323
use crate::common::time::Time;
24-
use crate::headers::connection_keep_alive;
24+
use crate::headers;
2525
use crate::proto::{BodyLength, MessageHead};
2626
#[cfg(feature = "server")]
2727
use crate::rt::Sleep;
@@ -657,7 +657,7 @@ where
657657
let outgoing_is_keep_alive = head
658658
.headers
659659
.get(CONNECTION)
660-
.map_or(false, connection_keep_alive);
660+
.map_or(false, headers::connection_keep_alive);
661661

662662
if !outgoing_is_keep_alive {
663663
match head.version {
@@ -680,12 +680,21 @@ where
680680
// If we know the remote speaks an older version, we try to fix up any messages
681681
// to work with our older peer.
682682
fn enforce_version(&mut self, head: &mut MessageHead<T::Outgoing>) {
683-
if let Version::HTTP_10 = self.state.version {
684-
// Fixes response or connection when keep-alive header is not present
685-
self.fix_keep_alive(head);
686-
// If the remote only knows HTTP/1.0, we should force ourselves
687-
// to do only speak HTTP/1.0 as well.
688-
head.version = Version::HTTP_10;
683+
match self.state.version {
684+
Version::HTTP_10 => {
685+
// Fixes response or connection when keep-alive header is not present
686+
self.fix_keep_alive(head);
687+
// If the remote only knows HTTP/1.0, we should force ourselves
688+
// to do only speak HTTP/1.0 as well.
689+
head.version = Version::HTTP_10;
690+
}
691+
Version::HTTP_11 => {
692+
if let KA::Disabled = self.state.keep_alive.status() {
693+
head.headers
694+
.insert(CONNECTION, HeaderValue::from_static("close"));
695+
}
696+
}
697+
_ => (),
689698
}
690699
// If the remote speaks HTTP/1.1, then it *should* be fine with
691700
// both HTTP/1.0 and HTTP/1.1 from us. So again, we just let

src/proto/h1/io.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ const MAX_BUF_LIST_BUFFERS: usize = 16;
3232
pub(crate) struct Buffered<T, B> {
3333
flush_pipeline: bool,
3434
io: T,
35+
partial_len: Option<usize>,
3536
read_blocked: bool,
3637
read_buf: BytesMut,
3738
read_buf_strategy: ReadStrategy,
@@ -65,6 +66,7 @@ where
6566
Buffered {
6667
flush_pipeline: false,
6768
io,
69+
partial_len: None,
6870
read_blocked: false,
6971
read_buf: BytesMut::with_capacity(0),
7072
read_buf_strategy: ReadStrategy::default(),
@@ -176,6 +178,7 @@ where
176178
loop {
177179
match super::role::parse_headers::<S>(
178180
&mut self.read_buf,
181+
self.partial_len,
179182
ParseContext {
180183
cached_headers: parse_ctx.cached_headers,
181184
req_method: parse_ctx.req_method,
@@ -191,14 +194,19 @@ where
191194
)? {
192195
Some(msg) => {
193196
debug!("parsed {} headers", msg.head.headers.len());
197+
self.partial_len = None;
194198
return Poll::Ready(Ok(msg));
195199
}
196200
None => {
197201
let max = self.read_buf_strategy.max();
198-
if self.read_buf.len() >= max {
202+
let curr_len = self.read_buf.len();
203+
if curr_len >= max {
199204
debug!("max_buf_size ({}) reached, closing", max);
200205
return Poll::Ready(Err(crate::Error::new_too_large()));
201206
}
207+
if curr_len > 0 {
208+
self.partial_len = Some(curr_len);
209+
}
202210
}
203211
}
204212
if ready!(self.poll_read_from_io(cx)).map_err(crate::Error::new_io)? == 0 {

0 commit comments

Comments
 (0)