├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE └── workflows │ ├── CI.yml │ ├── bench.yml │ └── external-types.toml ├── .gitignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Cargo.toml ├── LICENSE ├── README.md ├── SECURITY.md ├── benches ├── body.rs ├── connect.rs ├── end_to_end.rs ├── pipeline.rs ├── server.rs └── support │ ├── mod.rs │ └── tokiort.rs ├── capi ├── README.md ├── cbindgen.toml ├── examples │ ├── Makefile │ ├── client.c │ └── upload.c ├── gen_header.sh └── include │ └── hyper.h ├── docs ├── CODE_OF_CONDUCT.md ├── CODE_STYLE.md ├── COMMITS.md ├── GOVERNANCE.md ├── ISSUES.md ├── MAINTAINERS.md ├── MSRV.md ├── PULL_REQUESTS.md ├── README.md ├── ROADMAP-1.0.md ├── ROADMAP.md ├── TENETS.md ├── VISION.md └── vision-arch.svg ├── examples ├── README.md ├── client.rs ├── client_json.rs ├── echo.rs ├── gateway.rs ├── graceful_shutdown.rs ├── hello-http2.rs ├── hello.rs ├── http_proxy.rs ├── multi_server.rs ├── params.rs ├── send_file.rs ├── send_file_index.html ├── service_struct_impl.rs ├── single_threaded.rs ├── state.rs ├── upgrades.rs └── web_api.rs ├── src ├── body │ ├── incoming.rs │ ├── length.rs │ └── mod.rs ├── cfg.rs ├── client │ ├── conn │ │ ├── http1.rs │ │ ├── http2.rs │ │ └── mod.rs │ ├── dispatch.rs │ ├── mod.rs │ └── tests.rs ├── common │ ├── buf.rs │ ├── date.rs │ ├── either.rs │ ├── future.rs │ ├── io │ │ ├── compat.rs │ │ ├── mod.rs │ │ └── rewind.rs │ ├── mod.rs │ ├── task.rs │ ├── time.rs │ └── watch.rs ├── error.rs ├── ext │ ├── h1_reason_phrase.rs │ ├── informational.rs │ └── mod.rs ├── ffi │ ├── body.rs │ ├── client.rs │ ├── error.rs │ ├── http_types.rs │ ├── io.rs │ ├── macros.rs │ ├── mod.rs │ └── task.rs ├── headers.rs ├── lib.rs ├── mock.rs ├── proto │ ├── h1 │ │ ├── conn.rs │ │ ├── decode.rs │ │ ├── dispatch.rs │ │ ├── encode.rs │ │ ├── io.rs │ │ ├── mod.rs │ │ └── role.rs │ ├── h2 │ │ ├── client.rs │ │ ├── mod.rs │ │ ├── ping.rs │ │ └── server.rs │ └── mod.rs ├── rt │ ├── bounds.rs │ ├── io.rs │ ├── mod.rs │ └── timer.rs ├── server │ ├── conn │ │ ├── http1.rs │ │ ├── http2.rs │ │ └── mod.rs │ └── mod.rs ├── service │ ├── http.rs │ ├── mod.rs │ ├── service.rs │ └── util.rs ├── trace.rs └── upgrade.rs └── tests ├── client.rs ├── integration.rs ├── server.rs └── support ├── mod.rs ├── tokiort.rs └── trailers.rs /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: seanmonstar 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "Bug report \U0001F41B" 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: C-bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Version** 11 | List the version(s) of `hyper`, and any relevant hyper dependency (such as `h2` if this is related to HTTP/2). 12 | 13 | **Platform** 14 | The output of `uname -a` (UNIX), or version and 32 or 64-bit (Windows) 15 | 16 | **Description** 17 | Enter your issue details here. 18 | One way to structure the description: 19 | 20 | [short summary of the bug] 21 | 22 | I tried this code: 23 | 24 | [code sample that causes the bug] 25 | 26 | I expected to see this happen: [explanation] 27 | 28 | Instead, this happened: [explanation] 29 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "Feature request \U0001F4A1" 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: C-feature 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyperium/hyper/c88df7886c74a1ade69c0b4c68eaf570c8111622/.github/PULL_REQUEST_TEMPLATE -------------------------------------------------------------------------------- /.github/workflows/bench.yml: -------------------------------------------------------------------------------- 1 | name: Benchmark 2 | on: 3 | push: 4 | branches: 5 | - master 6 | 7 | jobs: 8 | benchmark: 9 | name: Benchmark 10 | runs-on: ubuntu-latest 11 | strategy: 12 | matrix: 13 | bench: 14 | - end_to_end 15 | - pipeline 16 | steps: 17 | - uses: actions/checkout@v4 18 | 19 | - name: Install Rust 20 | uses: dtolnay/rust-toolchain@nightly 21 | 22 | # Run benchmark and stores the output to a file 23 | - name: Run benchmark 24 | run: cargo bench --features full --bench ${{ matrix.bench }} | tee output.txt 25 | 26 | # Download previous benchmark result from cache (if exists) 27 | - name: Download previous benchmark data 28 | uses: actions/cache@v3 29 | with: 30 | path: ./cache 31 | key: ${{ runner.os }}-benchmark 32 | 33 | # Run `github-action-benchmark` action 34 | - name: Store benchmark result 35 | uses: seanmonstar/github-action-benchmark@v1-patch-1 36 | with: 37 | name: ${{ matrix.bench }} 38 | # What benchmark tool the output.txt came from 39 | tool: 'cargo' 40 | # Where the output from the benchmark tool is stored 41 | output-file-path: output.txt 42 | # # Where the previous data file is stored 43 | # external-data-json-path: ./cache/benchmark-data.json 44 | # Workflow will fail when an alert happens 45 | fail-on-alert: true 46 | # GitHub API token to make a commit comment 47 | github-token: ${{ secrets.GITHUB_TOKEN }} 48 | # Enable alert commit comment 49 | comment-on-alert: true 50 | #alert-comment-cc-users: '@seanmonstar' 51 | auto-push: true 52 | 53 | # Upload the updated cache file for the next job by actions/cache 54 | -------------------------------------------------------------------------------- /.github/workflows/external-types.toml: -------------------------------------------------------------------------------- 1 | allowed_external_types = [ 2 | "bytes::buf::buf_impl::Buf", 3 | "bytes::bytes::Bytes", 4 | "http::header", 5 | "http::header::map::HeaderMap", 6 | "http::method::Method", 7 | "http::request::Request", 8 | "http::response::Response", 9 | "http::status::StatusCode", 10 | "http::uri::Uri", 11 | "http::version::Version", 12 | "http_body::Body", 13 | "http_body::frame::Frame", 14 | "http_body::size_hint::SizeHint", 15 | ] 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Hyper 2 | 3 | You want to contribute? You're awesome! 4 | 5 | Contributions come in all shapes and sizes. Let's take a tour of some of the different wants you could contribute. 6 | 7 | ## [Code of Conduct](./docs/CODE_OF_CONDUCT.md) 8 | 9 | Firstly, all interactions with the project need to abide by the code of conduct. This is to make sure everyone is treated kindly. 10 | 11 | ## [Issues](./docs/ISSUES.md) 12 | 13 | - **Filing an issue** is a contribution. We appreciate you letting us know about bugs you've found, and any information that you can provide that we can use to make hyper better. Without your filing it, we may not be aware of the bug. 14 | - [Triaging issues](./docs/ISSUES.md#triaging) is a huge help. By your helping make issues better, the reporters can get answers sooner, and others can fix them with less effort. You can also volunteer as a frequent [triager](./docs/MAINTAINERS.md#triagers). 15 | - Discuss [feature requests][feat] (especially those marked [request-for-comment][b-rfc]). 16 | 17 | [feat]: https://github.com/hyperium/hyper/issues?q=is%3Aissue+is%3Aopen+label%3AC-feature 18 | [b-rfc]: https://github.com/hyperium/hyper/issues?q=is%3Aissue+is%3Aopen+label%3AB-rfc 19 | 20 | 21 | ## [Pull Requests](./docs/PULL_REQUESTS.md) 22 | 23 | By the way, consider checking the [list of easy issues](https://github.com/hyperium/hyper/issues?q=is%3Aopen+is%3Aissue+label%3AE-easy) if you want to submit something. 24 | 25 | - [Submitting a Pull Request](./docs/PULL_REQUESTS.md#submitting-a-pull-request) 26 | - [Commit Guidelines](./docs/COMMITS.md) 27 | 28 | ## Documentation 29 | 30 | Improving hyper's documentation is a huge help for everyone who is trying to _use_ hyper. 31 | 32 | - The API documentation (rendered at https://docs.rs/hyper) is stored as rustdoc comments directly in the source. 33 | - The main website has [tutorial-style guides](https://hyper.rs/guides). As of v1, they are currently in a [revamp](https://github.com/hyperium/hyper/issues/3411), and would greatly benefit from being filled out. 34 | 35 | ## Help 36 | 37 | Helping others use hyper in their specific workflows is a very valuable way to contribute. 38 | 39 | - Answer questions asked directly in hyper's [Discussions](https://github.com/hyperium/hyper/discussions). 40 | - Join our [Discord](https://discord.gg/kkwpueZ) and help those who show up with questions. 41 | - **Blog about hyper.** You writing a blog post about how you use hyper to do something specific, or how you contributed a new feature, or debugged something in it, is a great idea. Not all examples can fit in the hyper repo. Search engines will help people find their use cases on your blog. And you can describe processes in more depth or from a different perspective. 42 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hyper" 3 | version = "1.6.0" 4 | description = "A protective and efficient HTTP library for all." 5 | readme = "README.md" 6 | homepage = "https://hyper.rs" 7 | documentation = "https://docs.rs/hyper" 8 | repository = "https://github.com/hyperium/hyper" 9 | license = "MIT" 10 | authors = ["Sean McArthur "] 11 | keywords = ["http", "hyper", "hyperium"] 12 | categories = ["network-programming", "web-programming::http-client", "web-programming::http-server"] 13 | edition = "2021" 14 | rust-version = "1.63" # keep in sync with MSRV.md dev doc 15 | 16 | include = [ 17 | "Cargo.toml", 18 | "LICENSE", 19 | "src/**/*", 20 | ] 21 | 22 | [dependencies] 23 | bytes = "1.2" 24 | http = "1" 25 | http-body = "1" 26 | tokio = { version = "1", features = ["sync"] } 27 | 28 | # Optional 29 | 30 | atomic-waker = { version = "1.1.2", optional = true } 31 | futures-channel = { version = "0.3", optional = true } 32 | futures-core = { version = "0.3.31", optional = true } 33 | futures-util = { version = "0.3", default-features = false, features = ["alloc"], optional = true } 34 | h2 = { version = "0.4.2", optional = true } 35 | http-body-util = { version = "0.1", optional = true } 36 | httparse = { version = "1.9", optional = true } 37 | httpdate = { version = "1.0", optional = true } 38 | itoa = { version = "1", optional = true } 39 | pin-project-lite = { version = "0.2.4", optional = true } 40 | pin-utils = { version = "0.1", optional = true } # TODO: replace with std::pin::pin! once MSRV >= 1.68 41 | smallvec = { version = "1.12", features = ["const_generics", "const_new"], optional = true } 42 | tracing = { version = "0.1", default-features = false, features = ["std"], optional = true } 43 | want = { version = "0.3", optional = true } 44 | 45 | [dev-dependencies] 46 | form_urlencoded = "1" 47 | futures-channel = { version = "0.3", features = ["sink"] } 48 | futures-util = { version = "0.3", default-features = false, features = ["alloc", "sink"] } 49 | http-body-util = "0.1" 50 | pretty_env_logger = "0.5" 51 | pin-project-lite = "0.2.4" 52 | spmc = "0.3" 53 | serde = { version = "1.0", features = ["derive"] } 54 | serde_json = "1.0" 55 | tokio = { version = "1", features = [ 56 | "fs", 57 | "macros", 58 | "net", 59 | "io-std", 60 | "io-util", 61 | "rt", 62 | "rt-multi-thread", # so examples can use #[tokio::main] 63 | "sync", 64 | "time", 65 | "test-util", 66 | ] } 67 | tokio-test = "0.4" 68 | tokio-util = "0.7.10" 69 | 70 | [features] 71 | # Nothing by default 72 | default = [] 73 | 74 | # Easily turn it all on 75 | full = [ 76 | "client", 77 | "http1", 78 | "http2", 79 | "server", 80 | ] 81 | 82 | # HTTP versions 83 | http1 = ["dep:atomic-waker", "dep:futures-channel", "dep:futures-core", "dep:httparse", "dep:itoa", "dep:pin-utils"] 84 | http2 = ["dep:futures-channel", "dep:futures-core", "dep:h2"] 85 | 86 | # Client/Server 87 | client = ["dep:want", "dep:pin-project-lite", "dep:smallvec"] 88 | server = ["dep:httpdate", "dep:pin-project-lite", "dep:smallvec"] 89 | 90 | # C-API support (currently unstable (no semver)) 91 | ffi = ["dep:http-body-util", "futures-util"] 92 | capi = [] 93 | 94 | # Utilize tracing (currently unstable) 95 | tracing = ["dep:tracing"] 96 | 97 | # internal features used in CI 98 | nightly = [] 99 | 100 | [lints.rust.unexpected_cfgs] 101 | level = "warn" 102 | check-cfg = [ 103 | 'cfg(hyper_unstable_tracing)', 104 | 'cfg(hyper_unstable_ffi)' 105 | ] 106 | 107 | [package.metadata.docs.rs] 108 | features = ["ffi", "full", "tracing"] 109 | rustdoc-args = ["--cfg", "hyper_unstable_ffi", "--cfg", "hyper_unstable_tracing"] 110 | 111 | [package.metadata.playground] 112 | features = ["full"] 113 | 114 | [package.metadata.capi.header] 115 | generation = false 116 | subdirectory = false 117 | 118 | [package.metadata.capi.install.include] 119 | asset = [{ from="capi/include/hyper.h" }] 120 | 121 | [profile.release] 122 | codegen-units = 1 123 | incremental = false 124 | 125 | [profile.bench] 126 | codegen-units = 1 127 | incremental = false 128 | 129 | [[example]] 130 | name = "client" 131 | path = "examples/client.rs" 132 | required-features = ["full"] 133 | 134 | [[example]] 135 | name = "client_json" 136 | path = "examples/client_json.rs" 137 | required-features = ["full"] 138 | 139 | [[example]] 140 | name = "echo" 141 | path = "examples/echo.rs" 142 | required-features = ["full"] 143 | 144 | [[example]] 145 | name = "gateway" 146 | path = "examples/gateway.rs" 147 | required-features = ["full"] 148 | 149 | [[example]] 150 | name = "graceful_shutdown" 151 | path = "examples/graceful_shutdown.rs" 152 | required-features = ["full"] 153 | 154 | [[example]] 155 | name = "hello" 156 | path = "examples/hello.rs" 157 | required-features = ["full"] 158 | 159 | [[example]] 160 | name = "http_proxy" 161 | path = "examples/http_proxy.rs" 162 | required-features = ["full"] 163 | 164 | [[example]] 165 | name = "multi_server" 166 | path = "examples/multi_server.rs" 167 | required-features = ["full"] 168 | 169 | [[example]] 170 | name = "params" 171 | path = "examples/params.rs" 172 | required-features = ["full"] 173 | 174 | [[example]] 175 | name = "send_file" 176 | path = "examples/send_file.rs" 177 | required-features = ["full"] 178 | 179 | [[example]] 180 | name = "service_struct_impl" 181 | path = "examples/service_struct_impl.rs" 182 | required-features = ["full"] 183 | 184 | [[example]] 185 | name = "single_threaded" 186 | path = "examples/single_threaded.rs" 187 | required-features = ["full"] 188 | 189 | [[example]] 190 | name = "state" 191 | path = "examples/state.rs" 192 | required-features = ["full"] 193 | 194 | [[example]] 195 | name = "upgrades" 196 | path = "examples/upgrades.rs" 197 | required-features = ["full"] 198 | 199 | 200 | [[example]] 201 | name = "web_api" 202 | path = "examples/web_api.rs" 203 | required-features = ["full"] 204 | 205 | 206 | [[bench]] 207 | name = "body" 208 | path = "benches/body.rs" 209 | required-features = ["full"] 210 | 211 | [[bench]] 212 | name = "connect" 213 | path = "benches/connect.rs" 214 | required-features = ["full"] 215 | 216 | [[bench]] 217 | name = "end_to_end" 218 | path = "benches/end_to_end.rs" 219 | required-features = ["full"] 220 | 221 | [[bench]] 222 | name = "pipeline" 223 | path = "benches/pipeline.rs" 224 | required-features = ["full"] 225 | 226 | [[bench]] 227 | name = "server" 228 | path = "benches/server.rs" 229 | required-features = ["full"] 230 | 231 | 232 | [[test]] 233 | name = "client" 234 | path = "tests/client.rs" 235 | required-features = ["full"] 236 | 237 | [[test]] 238 | name = "integration" 239 | path = "tests/integration.rs" 240 | required-features = ["full"] 241 | 242 | [[test]] 243 | name = "server" 244 | path = "tests/server.rs" 245 | required-features = ["full"] 246 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-2025 Sean McArthur 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [hyper](https://hyper.rs) 2 | 3 | [![crates.io](https://img.shields.io/crates/v/hyper.svg)](https://crates.io/crates/hyper) 4 | [![Released API docs](https://docs.rs/hyper/badge.svg)](https://docs.rs/hyper) 5 | [![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE) 6 | [![CI](https://github.com/hyperium/hyper/workflows/CI/badge.svg)](https://github.com/hyperium/hyper/actions?query=workflow%3ACI) 7 | [![Discord chat][discord-badge]][discord-url] 8 | 9 | A protective and efficient HTTP library for all. 10 | 11 | - HTTP/1 and HTTP/2 12 | - Asynchronous design 13 | - Leading in performance 14 | - Tested and **correct** 15 | - Extensive production use 16 | - Client and Server APIs 17 | 18 | **Get started** by looking over the [guides](https://hyper.rs/guides/1/). 19 | 20 | ## "Low-level" 21 | 22 | hyper is a relatively low-level library, meant to be a building block for 23 | libraries and applications. 24 | 25 | If you are looking for a convenient HTTP client, then you may wish to consider 26 | [reqwest](https://github.com/seanmonstar/reqwest). 27 | 28 | If you are not sure what HTTP server to choose, then you may want to consider 29 | [axum](https://github.com/tokio-rs/axum) or 30 | [warp](https://github.com/seanmonstar/warp), the latter taking a more functional 31 | approach. Both are built on top of this library. 32 | 33 | ## Contributing 34 | 35 | To get involved, take a look at [CONTRIBUTING](CONTRIBUTING.md). 36 | 37 | If you prefer chatting, there is an active community in the [Discord server][discord-url]. 38 | 39 | ## License 40 | 41 | hyper is provided under the MIT license. See [LICENSE](LICENSE). 42 | 43 | [discord-badge]: https://img.shields.io/discord/500028886025895936.svg?logo=discord 44 | [discord-url]: https://discord.gg/kkwpueZ 45 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | hyper (and related projects in hyperium) take security seriously, and greatly appreciate responsibile disclosure. 4 | 5 | ## Report a security issue 6 | 7 | To report a security issue in hyper, or another crate in the hyperium organization, please [report a new draft GitHub Security Advisory](https://github.com/hyperium/hyper/security/advisories/new). 8 | 9 | We will discuss it privately with you. hyper maintainers will determine the impact and release details. Participation in security issue coordination is at the discretion of hyper maintainers. 10 | 11 | ## Transparency 12 | 13 | We are committed to transparency in the security issue disclosure process. Advisories will be disclosed publicly once a patch is released, and if appropriate, added to the RustSec advisory database. 14 | -------------------------------------------------------------------------------- /benches/body.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | #![deny(warnings)] 3 | 4 | extern crate test; 5 | 6 | use bytes::Buf; 7 | use futures_util::stream; 8 | use futures_util::StreamExt; 9 | use http_body::Frame; 10 | use http_body_util::{BodyExt, StreamBody}; 11 | 12 | macro_rules! bench_stream { 13 | ($bencher:ident, bytes: $bytes:expr, count: $count:expr, $total_ident:ident, $body_pat:pat, $block:expr) => {{ 14 | let rt = tokio::runtime::Builder::new_current_thread() 15 | .build() 16 | .expect("rt build"); 17 | 18 | let $total_ident: usize = $bytes * $count; 19 | $bencher.bytes = $total_ident as u64; 20 | let __s: &'static [&'static [u8]] = &[&[b'x'; $bytes] as &[u8]; $count] as _; 21 | 22 | $bencher.iter(|| { 23 | rt.block_on(async { 24 | let $body_pat = StreamBody::new( 25 | stream::iter(__s.iter()) 26 | .map(|&s| Ok::<_, std::convert::Infallible>(Frame::data(s))), 27 | ); 28 | 29 | $block; 30 | }); 31 | }); 32 | }}; 33 | } 34 | 35 | macro_rules! benches { 36 | ($($name:ident, $bytes:expr, $count:expr;)+) => ( 37 | mod aggregate { 38 | use super::*; 39 | 40 | $( 41 | #[bench] 42 | fn $name(b: &mut test::Bencher) { 43 | bench_stream!(b, bytes: $bytes, count: $count, total, body, { 44 | let buf = BodyExt::collect(body).await.unwrap().aggregate(); 45 | assert_eq!(buf.remaining(), total); 46 | }); 47 | } 48 | )+ 49 | } 50 | 51 | mod manual_into_vec { 52 | use super::*; 53 | 54 | $( 55 | #[bench] 56 | fn $name(b: &mut test::Bencher) { 57 | bench_stream!(b, bytes: $bytes, count: $count, total, mut body, { 58 | let mut vec = Vec::new(); 59 | while let Some(chunk) = body.next().await { 60 | vec.extend_from_slice(&chunk.unwrap().into_data().unwrap()); 61 | } 62 | assert_eq!(vec.len(), total); 63 | }); 64 | } 65 | )+ 66 | } 67 | 68 | mod to_bytes { 69 | use super::*; 70 | 71 | $( 72 | #[bench] 73 | fn $name(b: &mut test::Bencher) { 74 | bench_stream!(b, bytes: $bytes, count: $count, total, body, { 75 | let bytes = BodyExt::collect(body).await.unwrap().to_bytes(); 76 | assert_eq!(bytes.len(), total); 77 | }); 78 | } 79 | )+ 80 | } 81 | ) 82 | } 83 | 84 | // ===== Actual Benchmarks ===== 85 | 86 | benches! { 87 | bytes_1_000_count_2, 1_000, 2; 88 | bytes_1_000_count_10, 1_000, 10; 89 | bytes_10_000_count_1, 10_000, 1; 90 | bytes_10_000_count_10, 10_000, 10; 91 | } 92 | -------------------------------------------------------------------------------- /benches/connect.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | #![deny(warnings)] 3 | 4 | extern crate test; 5 | 6 | // TODO: Reimplement http_connector bench using hyper::client::conn 7 | // (instead of removed HttpConnector). 8 | 9 | // use http::Uri; 10 | // use hyper::client::connect::HttpConnector; 11 | // use hyper::service::Service; 12 | // use std::net::SocketAddr; 13 | // use tokio::net::TcpListener; 14 | 15 | // #[bench] 16 | // fn http_connector(b: &mut test::Bencher) { 17 | // let _ = pretty_env_logger::try_init(); 18 | // let rt = tokio::runtime::Builder::new_current_thread() 19 | // .enable_all() 20 | // .build() 21 | // .expect("rt build"); 22 | // let listener = rt 23 | // .block_on(TcpListener::bind(&SocketAddr::from(([127, 0, 0, 1], 0)))) 24 | // .expect("bind"); 25 | // let addr = listener.local_addr().expect("local_addr"); 26 | // let dst: Uri = format!("http://{}/", addr).parse().expect("uri parse"); 27 | // let mut connector = HttpConnector::new(); 28 | 29 | // rt.spawn(async move { 30 | // loop { 31 | // let _ = listener.accept().await; 32 | // } 33 | // }); 34 | 35 | // b.iter(|| { 36 | // rt.block_on(async { 37 | // connector.call(dst.clone()).await.expect("connect"); 38 | // }); 39 | // }); 40 | // } 41 | -------------------------------------------------------------------------------- /benches/pipeline.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | #![deny(warnings)] 3 | 4 | extern crate test; 5 | 6 | mod support; 7 | 8 | use std::convert::Infallible; 9 | use std::io::{Read, Write}; 10 | use std::net::{SocketAddr, TcpStream}; 11 | use std::sync::mpsc; 12 | use std::time::Duration; 13 | 14 | use bytes::Bytes; 15 | use http_body_util::Full; 16 | use tokio::net::TcpListener; 17 | use tokio::sync::oneshot; 18 | 19 | use hyper::server::conn::http1; 20 | use hyper::service::service_fn; 21 | use hyper::Response; 22 | 23 | const PIPELINED_REQUESTS: usize = 16; 24 | 25 | #[bench] 26 | fn hello_world_16(b: &mut test::Bencher) { 27 | let _ = pretty_env_logger::try_init(); 28 | let (_until_tx, until_rx) = oneshot::channel::<()>(); 29 | 30 | let addr = { 31 | let (addr_tx, addr_rx) = mpsc::channel(); 32 | std::thread::spawn(move || { 33 | let addr: SocketAddr = "127.0.0.1:0".parse().unwrap(); 34 | let rt = tokio::runtime::Builder::new_current_thread() 35 | .enable_all() 36 | .build() 37 | .expect("rt build"); 38 | 39 | let listener = rt.block_on(TcpListener::bind(addr)).unwrap(); 40 | let addr = listener.local_addr().unwrap(); 41 | 42 | rt.spawn(async move { 43 | loop { 44 | let (stream, _addr) = listener.accept().await.expect("accept"); 45 | let io = support::TokioIo::new(stream); 46 | 47 | http1::Builder::new() 48 | .pipeline_flush(true) 49 | .serve_connection( 50 | io, 51 | service_fn(|_| async { 52 | Ok::<_, Infallible>(Response::new(Full::new(Bytes::from( 53 | "Hello, World!", 54 | )))) 55 | }), 56 | ) 57 | .await 58 | .unwrap(); 59 | } 60 | }); 61 | 62 | addr_tx.send(addr).unwrap(); 63 | rt.block_on(until_rx).ok(); 64 | }); 65 | 66 | addr_rx.recv().unwrap() 67 | }; 68 | 69 | let mut pipelined_reqs = Vec::new(); 70 | for _ in 0..PIPELINED_REQUESTS { 71 | pipelined_reqs.extend_from_slice(b"GET / HTTP/1.1\r\nHost: localhost\r\n\r\n"); 72 | } 73 | 74 | let total_bytes = { 75 | let mut tcp = TcpStream::connect(addr).unwrap(); 76 | tcp.write_all(b"GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n") 77 | .unwrap(); 78 | let mut buf = Vec::new(); 79 | tcp.read_to_end(&mut buf).unwrap() - "connection: close\r\n".len() 80 | } * PIPELINED_REQUESTS; 81 | 82 | let mut tcp = TcpStream::connect(addr).unwrap(); 83 | tcp.set_read_timeout(Some(Duration::from_secs(3))).unwrap(); 84 | let mut buf = [0u8; 8192]; 85 | 86 | b.bytes = (pipelined_reqs.len() + total_bytes) as u64; 87 | b.iter(|| { 88 | tcp.write_all(&pipelined_reqs).unwrap(); 89 | let mut sum = 0; 90 | while sum < total_bytes { 91 | sum += tcp.read(&mut buf).unwrap(); 92 | } 93 | assert_eq!(sum, total_bytes); 94 | }); 95 | } 96 | -------------------------------------------------------------------------------- /benches/support/mod.rs: -------------------------------------------------------------------------------- 1 | mod tokiort; 2 | #[allow(unused)] 3 | pub use tokiort::{TokioExecutor, TokioIo, TokioTimer}; 4 | -------------------------------------------------------------------------------- /benches/support/tokiort.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | //! Various runtimes for hyper 3 | use std::{ 4 | future::Future, 5 | pin::Pin, 6 | task::{Context, Poll}, 7 | time::{Duration, Instant}, 8 | }; 9 | 10 | use hyper::rt::{Sleep, Timer}; 11 | use pin_project_lite::pin_project; 12 | 13 | #[derive(Clone)] 14 | /// An Executor that uses the tokio runtime. 15 | pub struct TokioExecutor; 16 | 17 | impl hyper::rt::Executor for TokioExecutor 18 | where 19 | F: std::future::Future + Send + 'static, 20 | F::Output: Send + 'static, 21 | { 22 | fn execute(&self, fut: F) { 23 | tokio::task::spawn(fut); 24 | } 25 | } 26 | 27 | /// A Timer that uses the tokio runtime. 28 | 29 | #[derive(Clone, Debug)] 30 | pub struct TokioTimer; 31 | 32 | impl Timer for TokioTimer { 33 | fn sleep(&self, duration: Duration) -> Pin> { 34 | Box::pin(TokioSleep { 35 | inner: tokio::time::sleep(duration), 36 | }) 37 | } 38 | 39 | fn sleep_until(&self, deadline: Instant) -> Pin> { 40 | Box::pin(TokioSleep { 41 | inner: tokio::time::sleep_until(deadline.into()), 42 | }) 43 | } 44 | 45 | fn reset(&self, sleep: &mut Pin>, new_deadline: Instant) { 46 | if let Some(sleep) = sleep.as_mut().downcast_mut_pin::() { 47 | sleep.reset(new_deadline) 48 | } 49 | } 50 | } 51 | 52 | impl TokioTimer { 53 | /// Create a new TokioTimer 54 | pub fn new() -> Self { 55 | Self {} 56 | } 57 | } 58 | 59 | // Use TokioSleep to get tokio::time::Sleep to implement Unpin. 60 | // see https://docs.rs/tokio/latest/tokio/time/struct.Sleep.html 61 | pin_project! { 62 | pub(crate) struct TokioSleep { 63 | #[pin] 64 | pub(crate) inner: tokio::time::Sleep, 65 | } 66 | } 67 | 68 | impl Future for TokioSleep { 69 | type Output = (); 70 | 71 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 72 | self.project().inner.poll(cx) 73 | } 74 | } 75 | 76 | impl Sleep for TokioSleep {} 77 | 78 | impl TokioSleep { 79 | pub fn reset(self: Pin<&mut Self>, deadline: Instant) { 80 | self.project().inner.as_mut().reset(deadline.into()); 81 | } 82 | } 83 | 84 | pin_project! { 85 | #[derive(Debug)] 86 | pub struct TokioIo { 87 | #[pin] 88 | inner: T, 89 | } 90 | } 91 | 92 | impl TokioIo { 93 | pub fn new(inner: T) -> Self { 94 | Self { inner } 95 | } 96 | 97 | pub fn inner(self) -> T { 98 | self.inner 99 | } 100 | } 101 | 102 | impl hyper::rt::Read for TokioIo 103 | where 104 | T: tokio::io::AsyncRead, 105 | { 106 | fn poll_read( 107 | self: Pin<&mut Self>, 108 | cx: &mut Context<'_>, 109 | mut buf: hyper::rt::ReadBufCursor<'_>, 110 | ) -> Poll> { 111 | let n = unsafe { 112 | let mut tbuf = tokio::io::ReadBuf::uninit(buf.as_mut()); 113 | match tokio::io::AsyncRead::poll_read(self.project().inner, cx, &mut tbuf) { 114 | Poll::Ready(Ok(())) => tbuf.filled().len(), 115 | other => return other, 116 | } 117 | }; 118 | 119 | unsafe { 120 | buf.advance(n); 121 | } 122 | Poll::Ready(Ok(())) 123 | } 124 | } 125 | 126 | impl hyper::rt::Write for TokioIo 127 | where 128 | T: tokio::io::AsyncWrite, 129 | { 130 | fn poll_write( 131 | self: Pin<&mut Self>, 132 | cx: &mut Context<'_>, 133 | buf: &[u8], 134 | ) -> Poll> { 135 | tokio::io::AsyncWrite::poll_write(self.project().inner, cx, buf) 136 | } 137 | 138 | fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 139 | tokio::io::AsyncWrite::poll_flush(self.project().inner, cx) 140 | } 141 | 142 | fn poll_shutdown( 143 | self: Pin<&mut Self>, 144 | cx: &mut Context<'_>, 145 | ) -> Poll> { 146 | tokio::io::AsyncWrite::poll_shutdown(self.project().inner, cx) 147 | } 148 | 149 | fn is_write_vectored(&self) -> bool { 150 | tokio::io::AsyncWrite::is_write_vectored(&self.inner) 151 | } 152 | 153 | fn poll_write_vectored( 154 | self: Pin<&mut Self>, 155 | cx: &mut Context<'_>, 156 | bufs: &[std::io::IoSlice<'_>], 157 | ) -> Poll> { 158 | tokio::io::AsyncWrite::poll_write_vectored(self.project().inner, cx, bufs) 159 | } 160 | } 161 | 162 | impl tokio::io::AsyncRead for TokioIo 163 | where 164 | T: hyper::rt::Read, 165 | { 166 | fn poll_read( 167 | self: Pin<&mut Self>, 168 | cx: &mut Context<'_>, 169 | tbuf: &mut tokio::io::ReadBuf<'_>, 170 | ) -> Poll> { 171 | //let init = tbuf.initialized().len(); 172 | let filled = tbuf.filled().len(); 173 | let sub_filled = unsafe { 174 | let mut buf = hyper::rt::ReadBuf::uninit(tbuf.unfilled_mut()); 175 | 176 | match hyper::rt::Read::poll_read(self.project().inner, cx, buf.unfilled()) { 177 | Poll::Ready(Ok(())) => buf.filled().len(), 178 | other => return other, 179 | } 180 | }; 181 | 182 | let n_filled = filled + sub_filled; 183 | // At least sub_filled bytes had to have been initialized. 184 | let n_init = sub_filled; 185 | unsafe { 186 | tbuf.assume_init(n_init); 187 | tbuf.set_filled(n_filled); 188 | } 189 | 190 | Poll::Ready(Ok(())) 191 | } 192 | } 193 | 194 | impl tokio::io::AsyncWrite for TokioIo 195 | where 196 | T: hyper::rt::Write, 197 | { 198 | fn poll_write( 199 | self: Pin<&mut Self>, 200 | cx: &mut Context<'_>, 201 | buf: &[u8], 202 | ) -> Poll> { 203 | hyper::rt::Write::poll_write(self.project().inner, cx, buf) 204 | } 205 | 206 | fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 207 | hyper::rt::Write::poll_flush(self.project().inner, cx) 208 | } 209 | 210 | fn poll_shutdown( 211 | self: Pin<&mut Self>, 212 | cx: &mut Context<'_>, 213 | ) -> Poll> { 214 | hyper::rt::Write::poll_shutdown(self.project().inner, cx) 215 | } 216 | 217 | fn is_write_vectored(&self) -> bool { 218 | hyper::rt::Write::is_write_vectored(&self.inner) 219 | } 220 | 221 | fn poll_write_vectored( 222 | self: Pin<&mut Self>, 223 | cx: &mut Context<'_>, 224 | bufs: &[std::io::IoSlice<'_>], 225 | ) -> Poll> { 226 | hyper::rt::Write::poll_write_vectored(self.project().inner, cx, bufs) 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /capi/README.md: -------------------------------------------------------------------------------- 1 | # C API for hyper 2 | 3 | This provides auxiliary pieces for a C API to use the hyper library. 4 | 5 | ## Unstable 6 | 7 | The C API of hyper is currently **unstable**, which means it's not part of the semver contract as the rest of the Rust API is. 8 | 9 | Because of that, it's only accessible if `--cfg hyper_unstable_ffi` is passed to `rustc` when compiling. The easiest way to do that is setting the `RUSTFLAGS` environment variable. 10 | 11 | ## Building 12 | 13 | The C API is part of the Rust library, but isn't compiled by default. Using `cargo`, staring with `1.64.0`, it can be compiled with the following command: 14 | 15 | ``` 16 | RUSTFLAGS="--cfg hyper_unstable_ffi" cargo rustc --features client,http1,http2,ffi --crate-type cdylib 17 | ``` 18 | 19 | ### (Optional) With `cargo-c` 20 | 21 | If using `cargo-c`, you can build and install a shared library with the following command: 22 | 23 | ``` 24 | RUSTFLAGS="--cfg hyper_unstable_ffi" cargo cbuild --features client,http1,http2,ffi --release 25 | ``` 26 | -------------------------------------------------------------------------------- /capi/cbindgen.toml: -------------------------------------------------------------------------------- 1 | # See https://github.com/mozilla/cbindgen/blob/master/docs.md#cbindgentoml for 2 | # a list of possible configuration values. 3 | language = "C" 4 | header = """/* 5 | * Copyright 2023 Sean McArthur. MIT License. 6 | * Generated by gen_header.sh. Do not edit directly. 7 | * 8 | * Full docs at: https://docs.rs/hyper/latest/hyper/ffi/index.html 9 | */""" 10 | include_guard = "_HYPER_H" 11 | no_includes = true 12 | sys_includes = ["stdint.h", "stddef.h", "stdbool.h"] 13 | cpp_compat = true 14 | documentation_style = "c" 15 | documentation_length = "short" 16 | -------------------------------------------------------------------------------- /capi/examples/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Build the example client 3 | # 4 | 5 | TARGET = client 6 | TARGET2 = upload 7 | 8 | OBJS = client.o 9 | OBJS2 = upload.o 10 | 11 | RPATH=$(PWD)/../../target/debug 12 | CFLAGS = -I../include 13 | LDFLAGS = -L$(RPATH) -Wl,-rpath,$(RPATH) 14 | LIBS = -lhyper 15 | 16 | all: $(TARGET) $(TARGET2) 17 | 18 | $(TARGET): $(OBJS) 19 | $(CC) -o $(TARGET) $(OBJS) $(LDFLAGS) $(LIBS) 20 | 21 | $(TARGET2): $(OBJS2) 22 | $(CC) -o $(TARGET2) $(OBJS2) $(LDFLAGS) $(LIBS) 23 | 24 | clean: 25 | rm -f $(OBJS) $(TARGET) $(OBJS2) $(TARGET2) 26 | -------------------------------------------------------------------------------- /capi/gen_header.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # This script regenerates hyper.h. 4 | # nightly build of Rust. 5 | # 6 | # Requirements: 7 | # 8 | # cargo install cbindgen 9 | # cargo install cargo-expand 10 | 11 | set -e 12 | 13 | CAPI_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 14 | header_file="$CAPI_DIR/include/hyper.h" 15 | header_file_backup="$CAPI_DIR/include/hyper.h.backup" 16 | verify_flag=$1 17 | function cleanup { 18 | rm -rf "$WORK_DIR" || true 19 | if [[ "--verify" == "$verify_flag" ]]; then 20 | rm "$header_file_backup" || true 21 | fi 22 | } 23 | 24 | trap cleanup EXIT 25 | 26 | WORK_DIR=$(mktemp -d) 27 | 28 | # check if tmp dir was created 29 | if [[ ! "$WORK_DIR" || ! -d "$WORK_DIR" ]]; then 30 | echo "Could not create temp dir" 31 | exit 1 32 | fi 33 | 34 | # backup hyper.h 35 | if [[ "--verify" == "$verify_flag" ]]; then 36 | cp "$header_file" "$header_file_backup" 37 | fi 38 | 39 | # Expand just the ffi module 40 | if ! RUSTFLAGS='--cfg hyper_unstable_ffi' cargo expand --features client,http1,http2,ffi ::ffi 2> $WORK_DIR/expand_stderr.err > $WORK_DIR/expanded.rs; then 41 | cat $WORK_DIR/expand_stderr.err 42 | fi 43 | 44 | # Bindgen! 45 | if ! cbindgen \ 46 | --config "$CAPI_DIR/cbindgen.toml" \ 47 | --lockfile "$CAPI_DIR/../Cargo.lock" \ 48 | --output "$header_file" \ 49 | "${@}"\ 50 | $WORK_DIR/expanded.rs 2> $WORK_DIR/cbindgen_stderr.err; then 51 | bindgen_exit_code=$? 52 | if [[ "--verify" == "$verify_flag" ]]; then 53 | echo "Changes from previous header (old < > new)" 54 | diff -u "$header_file_backup" "$header_file" 55 | else 56 | echo "cbindgen failed:" 57 | cat $WORK_DIR/cbindgen_stderr.err 58 | fi 59 | exit $bindgen_exit_code 60 | fi 61 | 62 | exit 0 63 | -------------------------------------------------------------------------------- /docs/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | ## Be Kind 4 | 5 | - Don't be mean. 6 | - Insulting anyone is prohibited. 7 | - Harassment of any kind is prohibited. 8 | - If another person feels uncomfortable with your remarks, stop it. 9 | - If a moderator deems your comment or conduct as inappropriate, stop it. 10 | - Disagreeing is fine, but keep it to technical arguments. Never attack the person. 11 | - Give the benefit of the doubt. Assume good intentions. 12 | - Show empathy. There are 3 interpretations to any message: what we thought, what we said, and what they understand. 13 | - This does mean we exclude people who are not kind. We are happy to make that sacrifice. 14 | 15 | ## Or Else 16 | 17 | - Violations of the Code of Conduct will result in 1 warning. 18 | - If the violation is major, a moderator may just ban immediately. 19 | - If a warning has already been given, a moderator will ban the offender. 20 | - There is no process for appealing a ban. 21 | - Any violations can be reported to sean@seanmonstar.com. 22 | -------------------------------------------------------------------------------- /docs/CODE_STYLE.md: -------------------------------------------------------------------------------- 1 | # Code Style 2 | 3 | hyper uses the default configuration of `rustfmt`. 4 | 5 | ## cargo fmt 6 | 7 | `cargo fmt --all` does not work in hyper. Please use the following commands: 8 | 9 | ```txt 10 | # Mac or Linux 11 | rustfmt --check --edition 2021 $(git ls-files '*.rs') 12 | 13 | # Powershell 14 | Get-ChildItem . -Filter "*.rs" -Recurse | foreach { rustfmt --check --edition 2021 $_.FullName } 15 | ``` 16 | 17 | > **NOTE**: If you are using `rust-analyzer`, you can add the following two lines in your `settings.json` to make sure the features get taken into account when checking the project: 18 | > 19 | > ```json 20 | > "rust-analyzer.cargo.features": ["full"], 21 | > "rust-analyzer.check.features": ["full"], 22 | > ``` 23 | -------------------------------------------------------------------------------- /docs/COMMITS.md: -------------------------------------------------------------------------------- 1 | # Git Commit Guidelines 2 | 3 | We have very precise rules over how our git commit messages can be formatted. This leads to **more 4 | readable messages** that are easy to follow when looking through the **project history**. But also, 5 | we use the git commit messages to **generate the change log**. 6 | 7 | ## Commit Message Format 8 | Each commit message consists of a **header**, a **body** and a **footer**. The header has a special 9 | format that includes a **type**, a **scope** and a **subject**: 10 | 11 | ``` 12 | (): 13 | 14 | 15 | 16 |