From bff977b73ca8d737f5492c86c09fd64735c45461 Mon Sep 17 00:00:00 2001 From: Sean McArthur Date: Wed, 8 Dec 2021 15:03:00 -0800 Subject: [PATCH 1/4] feat(http2): add `http2_max_send_buf_size` option to client and server This value is like a high-water mark. It applies per stream. Once a stream has buffered that amount of bytes to send, it won't poll more data from the `HttpBody` until the stream has been able to flush under it. --- Cargo.toml | 2 +- src/client/client.rs | 14 ++++++++++++++ src/client/conn.rs | 15 +++++++++++++++ src/proto/h2/client.rs | 4 ++++ src/proto/h2/server.rs | 6 +++++- src/server/conn.rs | 15 +++++++++++++++ src/server/server.rs | 14 ++++++++++++++ 7 files changed, 68 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 06a6863624..a71bf2af7c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,7 @@ http = "0.2" http-body = "0.4" httpdate = "1.0" httparse = "1.5.1" -h2 = { version = "0.3.3", optional = true } +h2 = { version = "0.3.9", optional = true } itoa = "0.4.1" tracing = { version = "0.1", default-features = false, features = ["std"] } pin-project-lite = "0.2.4" diff --git a/src/client/client.rs b/src/client/client.rs index 6d9d678702..3c1a843090 100644 --- a/src/client/client.rs +++ b/src/client/client.rs @@ -1223,6 +1223,20 @@ impl Builder { self } + /// Set the maximum write buffer size for each HTTP/2 stream. + /// + /// Default is currently 1MB, but may change. + /// + /// # Panics + /// + /// The value must be no larger than `u32::MAX`. + #[cfg(feature = "http2")] + #[cfg_attr(docsrs, doc(cfg(feature = "http2")))] + pub fn http2_max_send_buf_size(&mut self, max: usize) -> &mut Self { + self.conn_builder.http2_max_send_buf_size(max); + self + } + /// Set whether to retry requests that get disrupted before ever starting /// to write. /// diff --git a/src/client/conn.rs b/src/client/conn.rs index 5f087a55e1..1273edabf5 100644 --- a/src/client/conn.rs +++ b/src/client/conn.rs @@ -837,6 +837,21 @@ impl Builder { self } + /// Set the maximum write buffer size for each HTTP/2 stream. + /// + /// Default is currently 1MB, but may change. + /// + /// # Panics + /// + /// The value must be no larger than `u32::MAX`. + #[cfg(feature = "http2")] + #[cfg_attr(docsrs, doc(cfg(feature = "http2")))] + pub fn http2_max_send_buf_size(&mut self, max: usize) -> &mut Self { + assert!(max <= std::u32::MAX as usize); + self.h2_builder.max_send_buffer_size = max; + self + } + /// Constructs a connection with the configured options and IO. /// See [`client::conn`](crate::client::conn) for more. /// diff --git a/src/proto/h2/client.rs b/src/proto/h2/client.rs index ae20c8515b..809d8b8505 100644 --- a/src/proto/h2/client.rs +++ b/src/proto/h2/client.rs @@ -36,6 +36,7 @@ type ConnEof = oneshot::Receiver; const DEFAULT_CONN_WINDOW: u32 = 1024 * 1024 * 5; // 5mb const DEFAULT_STREAM_WINDOW: u32 = 1024 * 1024 * 2; // 2mb const DEFAULT_MAX_FRAME_SIZE: u32 = 1024 * 16; // 16kb +const DEFAULT_MAX_SEND_BUF_SIZE: usize = 1024 * 1024; // 1mb #[derive(Clone, Debug)] pub(crate) struct Config { @@ -50,6 +51,7 @@ pub(crate) struct Config { #[cfg(feature = "runtime")] pub(crate) keep_alive_while_idle: bool, pub(crate) max_concurrent_reset_streams: Option, + pub(crate) max_send_buffer_size: usize, } impl Default for Config { @@ -66,6 +68,7 @@ impl Default for Config { #[cfg(feature = "runtime")] keep_alive_while_idle: false, max_concurrent_reset_streams: None, + max_send_buffer_size: DEFAULT_MAX_SEND_BUF_SIZE, } } } @@ -76,6 +79,7 @@ fn new_builder(config: &Config) -> Builder { .initial_window_size(config.initial_stream_window_size) .initial_connection_window_size(config.initial_conn_window_size) .max_frame_size(config.max_frame_size) + .max_send_buffer_size(config.max_send_buffer_size) .enable_push(false); if let Some(max) = config.max_concurrent_reset_streams { builder.max_concurrent_reset_streams(max); diff --git a/src/proto/h2/server.rs b/src/proto/h2/server.rs index c9c1380d54..94452fb4bb 100644 --- a/src/proto/h2/server.rs +++ b/src/proto/h2/server.rs @@ -33,6 +33,7 @@ use crate::{Body, Response}; const DEFAULT_CONN_WINDOW: u32 = 1024 * 1024; // 1mb const DEFAULT_STREAM_WINDOW: u32 = 1024 * 1024; // 1mb const DEFAULT_MAX_FRAME_SIZE: u32 = 1024 * 16; // 16kb +const DEFAULT_MAX_SEND_BUF_SIZE: usize = 1024 * 400; // 400kb #[derive(Clone, Debug)] pub(crate) struct Config { @@ -45,6 +46,7 @@ pub(crate) struct Config { pub(crate) keep_alive_interval: Option, #[cfg(feature = "runtime")] pub(crate) keep_alive_timeout: Duration, + pub(crate) max_send_buffer_size: usize, } impl Default for Config { @@ -59,6 +61,7 @@ impl Default for Config { keep_alive_interval: None, #[cfg(feature = "runtime")] keep_alive_timeout: Duration::from_secs(20), + max_send_buffer_size: DEFAULT_MAX_SEND_BUF_SIZE, } } } @@ -109,7 +112,8 @@ where builder .initial_window_size(config.initial_stream_window_size) .initial_connection_window_size(config.initial_conn_window_size) - .max_frame_size(config.max_frame_size); + .max_frame_size(config.max_frame_size) + .max_send_buffer_size(config.max_send_buffer_size); if let Some(max) = config.max_concurrent_streams { builder.max_concurrent_streams(max); } diff --git a/src/server/conn.rs b/src/server/conn.rs index c49c8ae571..eb9d847788 100644 --- a/src/server/conn.rs +++ b/src/server/conn.rs @@ -543,6 +543,21 @@ impl Http { self } + /// Set the maximum write buffer size for each HTTP/2 stream. + /// + /// Default is currently ~400KB, but may change. + /// + /// # Panics + /// + /// The value must be no larger than `u32::MAX`. + #[cfg(feature = "http2")] + #[cfg_attr(docsrs, doc(cfg(feature = "http2")))] + pub fn http2_max_send_buf_size(&mut self, max: usize) -> &mut Self { + assert!(max <= std::u32::MAX as usize); + self.h2_builder.max_send_buffer_size = max; + self + } + /// Set the maximum buffer size for the connection. /// /// Default is ~400kb. diff --git a/src/server/server.rs b/src/server/server.rs index 87027bbefc..9cfb8157bc 100644 --- a/src/server/server.rs +++ b/src/server/server.rs @@ -439,6 +439,20 @@ impl Builder { self } + /// Set the maximum write buffer size for each HTTP/2 stream. + /// + /// Default is currently ~400KB, but may change. + /// + /// # Panics + /// + /// The value must be no larger than `u32::MAX`. + #[cfg(feature = "http2")] + #[cfg_attr(docsrs, doc(cfg(feature = "http2")))] + pub fn http2_max_send_buf_size(mut self, max: usize) -> Self { + self.protocol.http2_max_send_buf_size(max); + self + } + /// Sets the `Executor` to deal with connection tasks. /// /// Default is `tokio::spawn`. From 41dee4a79bd5068c0c19edb34746b87a6a87c09b Mon Sep 17 00:00:00 2001 From: Sean McArthur Date: Thu, 9 Dec 2021 10:14:08 -0800 Subject: [PATCH 2/4] v0.14.16 --- CHANGELOG.md | 16 ++++++++++++++++ Cargo.toml | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 49068fa60f..090ecf744f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,19 @@ +### v0.14.16 (2021-12-09) + + +#### Bug Fixes + +* **http1:** return 414 when URI contains more than 65534 characters (#2706) ([5f938fff](https://github.com/hyperium/hyper/commit/5f938fffa64df23a2e4af81ed4e6d8bd760e2d05), closes [#2701](https://github.com/hyperium/hyper/issues/2701)) +* **http2:** received `Body::size_hint()` now return 0 if implicitly empty (#2715) ([84b78b6c](https://github.com/hyperium/hyper/commit/84b78b6c877ff9aaa28d1e348a5deb63a9282503)) +* **server:** use case-insensitive comparison for Expect: 100-continue (#2709) ([7435cc33](https://github.com/hyperium/hyper/commit/7435cc3399895643062f4e399fae6d5b20b049a1), closes [#2708](https://github.com/hyperium/hyper/issues/2708)) + + +#### Features + +* **http2:** add `http2_max_send_buf_size` option to client and server ([bff977b7](https://github.com/hyperium/hyper/commit/bff977b73ca8d737f5492c86c09fd64735c45461)) +* **server:** add HTTP/1 header read timeout option (#2675) ([842c6553](https://github.com/hyperium/hyper/commit/842c6553a5414a3a4a0fbf973079200612a9c3d2), closes [#2457](https://github.com/hyperium/hyper/issues/2457)) + + ### v0.14.15 (2021-11-16) #### Bug Fixes diff --git a/Cargo.toml b/Cargo.toml index a71bf2af7c..8941140742 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hyper" -version = "0.14.15" +version = "0.14.16" description = "A fast and correct HTTP library." readme = "README.md" homepage = "https://hyper.rs" From 9ee73231cc9c81155be6f33e94e4a1a756d46a3f Mon Sep 17 00:00:00 2001 From: Sean McArthur Date: Thu, 9 Dec 2021 17:02:25 -0800 Subject: [PATCH 3/4] docs(dev): rename dev docs directory to 'docs' --- CONTRIBUTING.md | 2 +- {dev => docs}/COMMITS.md | 0 {dev => docs}/MSRV.md | 0 {dev => docs}/README.md | 0 4 files changed, 1 insertion(+), 1 deletion(-) rename {dev => docs}/COMMITS.md (100%) rename {dev => docs}/MSRV.md (100%) rename {dev => docs}/README.md (100%) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ba4b4213f1..5cbe51bd44 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -7,4 +7,4 @@ You want to contribute? You're awesome! Don't know where to start? Check the [li ## Pull Requests -- [Commit Guidelines](./dev/COMMITS.md) +- [Commit Guidelines](./docs/COMMITS.md) diff --git a/dev/COMMITS.md b/docs/COMMITS.md similarity index 100% rename from dev/COMMITS.md rename to docs/COMMITS.md diff --git a/dev/MSRV.md b/docs/MSRV.md similarity index 100% rename from dev/MSRV.md rename to docs/MSRV.md diff --git a/dev/README.md b/docs/README.md similarity index 100% rename from dev/README.md rename to docs/README.md From e8cccce0c1a66f54c7e32a8c71ec4cd53d8a4cb4 Mon Sep 17 00:00:00 2001 From: Sean McArthur Date: Thu, 9 Dec 2021 18:15:04 -0800 Subject: [PATCH 4/4] docs(contrib): add ISSUES doc --- docs/ISSUES.md | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 docs/ISSUES.md diff --git a/docs/ISSUES.md b/docs/ISSUES.md new file mode 100644 index 0000000000..403143bdd9 --- /dev/null +++ b/docs/ISSUES.md @@ -0,0 +1,49 @@ +# Issues + +The [issue tracker][issues] for hyper is where we track all features, bugs, and discuss proposals. + +## Labels + +Issues are organized with a set of labels. Most labels follow a system of being prefixed by a "type". + +### Area + +The area labels describe what part of hyper is relevant. + +- **A-body**: streaming request and response bodies. +- **A-client**: the HTTP client. +- **A-dependencies**: library dependencies. +- **A-docs**: documentation. +- **A-error**: error reporting and types. +- **A-ffi**: the C API. +- **A-http1**: the HTTP/1 specifics. +- **A-http2**: the HTTP/2 specifics. +- **A-server**: the HTTP server. +- **A-tests**: the unit and integration tests. + +### Blocked + +These labels indicate an issue is "blocked" for some reason. + +- **B-breaking-change**: a breaking change that is waiting for the next semver-compatible release. +- **B-rfc**: request for comments. More discussion is needed to explore the design. +- **B-upstream**: waiting on something in a dependency or the compiler. + +### Effort + +The effort labels are a best-guess at roughly how much effort and knowledge of hyper is needed to accomplish the task. + +- **E-easy**: a great starting point for a new contributor. +- **E-medium**: some knowledge of how hyper internals work would be useful. +- **E-hard**: likely requires a deeper understanding of how hyper internals work. + +### Severity + +The severity marks how _severe_ the issue is. Note this isn't "importance" or "priority". + +- **S-bug**: something is wrong, this is bad! +- **S-feature**: this is a new feature request, adding something new. +- **S-performance**: make existing working code go faster. +- **S-refactor**: improve internal code to help readability and maintenance. + +[issues]: https://github.com/hyperium/hyper/issues