Compare commits

...

10 Commits

Author SHA1 Message Date
Sean McArthur
5e20688398 feat(client): remove client::connect module (#2949)
Some checks failed
CI / CI is green (push) Has been cancelled
CI / Check Style (push) Has been cancelled
CI / Test beta on macOS-latest (push) Has been cancelled
CI / Test stable on macOS-latest (push) Has been cancelled
CI / Test beta on ubuntu-latest (push) Has been cancelled
CI / Test stable on ubuntu-latest (push) Has been cancelled
CI / Test beta on windows-latest (push) Has been cancelled
CI / Test stable on windows-latest (push) Has been cancelled
CI / Test nightly on macOS-latest (push) Has been cancelled
CI / Test nightly on ubuntu-latest (push) Has been cancelled
CI / Test nightly on windows-latest (push) Has been cancelled
CI / Check MSRV (1.56) (push) Has been cancelled
CI / Test with Miri (push) Has been cancelled
CI / features (push) Has been cancelled
CI / Test C API (FFI) (push) Has been cancelled
CI / Verify hyper.h is up to date (push) Has been cancelled
CI / Build docs (push) Has been cancelled
Benchmark / Benchmark (push) Has been cancelled
The connect pieces will be available in `hyper-util`.

BREAKING CHANGE: Use `connect` from `hyper-util`.
2022-08-17 10:50:40 -07:00
Sean McArthur
bb3af17ce1 feat(client): remove higher-level hyper::Client (#2941)
This removes the following types and methods from hyper:

- `Client`
- `Error::is_connect()`

BREAKING CHANGE: A pooling client is in the hyper-util crate.
2022-08-15 09:15:59 -07:00
Sean McArthur
889fa2d872 feat(client): remove hyper::client::server (#2940)
BREAKING CHANGE: Tower `Service` utilities will exist in `hyper-util`.
2022-08-12 13:54:45 -07:00
Sean McArthur
cd32454403 docs(contrib): add GOVERNANCE and MAINTAINERS files (#2938) 2022-08-12 10:37:32 -07:00
Oddbjørn Grødem
c558647762 test(benches): re-enable pipeline and server bench (#2934)
re-enable the recently disabled pipeline and server bench using
`hyper::server::conn` instead of the removed higher-level `Server` api
2022-08-02 06:46:26 -07:00
Sean McArthur
3c7bef3b6f feat(server): remove the high-level Server API (#2932)
This removes `hyper::Server`, and it's related parts:

- `hyper::server::Builder`
- `hyper::server::accept`
- `hyper::service::make_service_fn`

New utilities for managing servers will exist in `hyper-util`.
2022-08-01 14:28:23 -07:00
Sean McArthur
491b076bca chore(ci): disable CI benchmarks publishing (#2933) 2022-07-29 16:55:49 -07:00
Sean McArthur
ca99e23e27 refactor(client): clean up unused lint warnings in dns module (#2931) 2022-07-29 16:22:29 -07:00
Sean McArthur
0c8ee93d7f feat(client,server): remove tcp feature and code (#2929)
This removes the `tcp` feature from hyper's `Cargo.toml`, and the code it enabled:

- `HttpConnector`
- `GaiResolver`
- `AddrStream`

And parts of `Client` and `Server` that used those types. Alternatives will be available in the `hyper-util` crate.

Closes #2856 
Co-authored-by: MrGunflame <mrgunflame@protonmail.com>
2022-07-29 10:07:09 -07:00
Sean McArthur
d4b5bd4ee6 fix(http1): trim obs-folded headers when unfolding (#2926) 2022-07-27 07:00:53 -07:00
58 changed files with 1644 additions and 8977 deletions

View File

@@ -11,9 +11,9 @@ jobs:
strategy:
matrix:
bench:
- connect
- end_to_end
- pipeline
#- connect
#- end_to_end
#- pipeline
steps:
- uses: actions/checkout@v2

View File

@@ -94,12 +94,6 @@ server = []
# Tokio support
runtime = [
"tcp",
"tokio/rt",
"tokio/time",
]
tcp = [
"socket2",
"tokio/net",
"tokio/rt",
"tokio/time",
@@ -187,11 +181,6 @@ name = "state"
path = "examples/state.rs"
required-features = ["full"]
[[example]]
name = "tower_client"
path = "examples/tower_client.rs"
required-features = ["full"]
[[example]]
name = "tower_server"
path = "examples/tower_server.rs"

View File

@@ -3,35 +3,38 @@
extern crate test;
use http::Uri;
use hyper::client::connect::HttpConnector;
use hyper::service::Service;
use std::net::SocketAddr;
use tokio::net::TcpListener;
// TODO: Reimplement http_connector bench using hyper::client::conn
// (instead of removed HttpConnector).
#[bench]
fn http_connector(b: &mut test::Bencher) {
let _ = pretty_env_logger::try_init();
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.expect("rt build");
let listener = rt
.block_on(TcpListener::bind(&SocketAddr::from(([127, 0, 0, 1], 0))))
.expect("bind");
let addr = listener.local_addr().expect("local_addr");
let dst: Uri = format!("http://{}/", addr).parse().expect("uri parse");
let mut connector = HttpConnector::new();
// use http::Uri;
// use hyper::client::connect::HttpConnector;
// use hyper::service::Service;
// use std::net::SocketAddr;
// use tokio::net::TcpListener;
rt.spawn(async move {
loop {
let _ = listener.accept().await;
}
});
// #[bench]
// fn http_connector(b: &mut test::Bencher) {
// let _ = pretty_env_logger::try_init();
// let rt = tokio::runtime::Builder::new_current_thread()
// .enable_all()
// .build()
// .expect("rt build");
// let listener = rt
// .block_on(TcpListener::bind(&SocketAddr::from(([127, 0, 0, 1], 0))))
// .expect("bind");
// let addr = listener.local_addr().expect("local_addr");
// let dst: Uri = format!("http://{}/", addr).parse().expect("uri parse");
// let mut connector = HttpConnector::new();
b.iter(|| {
rt.block_on(async {
connector.call(dst.clone()).await.expect("connect");
});
});
}
// rt.spawn(async move {
// loop {
// let _ = listener.accept().await;
// }
// });
// b.iter(|| {
// rt.block_on(async {
// connector.call(dst.clone()).await.expect("connect");
// });
// });
// }

View File

@@ -3,380 +3,383 @@
extern crate test;
use std::net::SocketAddr;
// TODO: Reimplement Opts::bench using hyper::server::conn and hyper::client::conn
// (instead of Server and HttpClient).
use futures_util::future::join_all;
// use std::net::SocketAddr;
use hyper::client::HttpConnector;
use hyper::{body::HttpBody as _, Body, Method, Request, Response, Server};
// use futures_util::future::join_all;
// HTTP1
// use hyper::client::HttpConnector;
// use hyper::{body::HttpBody as _, Body, Method, Request, Response, Server};
#[bench]
fn http1_consecutive_x1_empty(b: &mut test::Bencher) {
opts().bench(b)
}
// // HTTP1
#[bench]
fn http1_consecutive_x1_req_10b(b: &mut test::Bencher) {
opts()
.method(Method::POST)
.request_body(&[b's'; 10])
.bench(b)
}
// #[bench]
// fn http1_consecutive_x1_empty(b: &mut test::Bencher) {
// opts().bench(b)
// }
#[bench]
fn http1_consecutive_x1_both_100kb(b: &mut test::Bencher) {
let body = &[b'x'; 1024 * 100];
opts()
.method(Method::POST)
.request_body(body)
.response_body(body)
.bench(b)
}
// #[bench]
// fn http1_consecutive_x1_req_10b(b: &mut test::Bencher) {
// opts()
// .method(Method::POST)
// .request_body(&[b's'; 10])
// .bench(b)
// }
#[bench]
fn http1_consecutive_x1_both_10mb(b: &mut test::Bencher) {
let body = &[b'x'; 1024 * 1024 * 10];
opts()
.method(Method::POST)
.request_body(body)
.response_body(body)
.bench(b)
}
// #[bench]
// fn http1_consecutive_x1_both_100kb(b: &mut test::Bencher) {
// let body = &[b'x'; 1024 * 100];
// opts()
// .method(Method::POST)
// .request_body(body)
// .response_body(body)
// .bench(b)
// }
#[bench]
fn http1_parallel_x10_empty(b: &mut test::Bencher) {
opts().parallel(10).bench(b)
}
// #[bench]
// fn http1_consecutive_x1_both_10mb(b: &mut test::Bencher) {
// let body = &[b'x'; 1024 * 1024 * 10];
// opts()
// .method(Method::POST)
// .request_body(body)
// .response_body(body)
// .bench(b)
// }
#[bench]
fn http1_parallel_x10_req_10mb(b: &mut test::Bencher) {
let body = &[b'x'; 1024 * 1024 * 10];
opts()
.parallel(10)
.method(Method::POST)
.request_body(body)
.bench(b)
}
// #[bench]
// fn http1_parallel_x10_empty(b: &mut test::Bencher) {
// opts().parallel(10).bench(b)
// }
#[bench]
fn http1_parallel_x10_req_10kb_100_chunks(b: &mut test::Bencher) {
let body = &[b'x'; 1024 * 10];
opts()
.parallel(10)
.method(Method::POST)
.request_chunks(body, 100)
.bench(b)
}
// #[bench]
// fn http1_parallel_x10_req_10mb(b: &mut test::Bencher) {
// let body = &[b'x'; 1024 * 1024 * 10];
// opts()
// .parallel(10)
// .method(Method::POST)
// .request_body(body)
// .bench(b)
// }
#[bench]
fn http1_parallel_x10_res_1mb(b: &mut test::Bencher) {
let body = &[b'x'; 1024 * 1024 * 1];
opts().parallel(10).response_body(body).bench(b)
}
// #[bench]
// fn http1_parallel_x10_req_10kb_100_chunks(b: &mut test::Bencher) {
// let body = &[b'x'; 1024 * 10];
// opts()
// .parallel(10)
// .method(Method::POST)
// .request_chunks(body, 100)
// .bench(b)
// }
#[bench]
fn http1_parallel_x10_res_10mb(b: &mut test::Bencher) {
let body = &[b'x'; 1024 * 1024 * 10];
opts().parallel(10).response_body(body).bench(b)
}
// #[bench]
// fn http1_parallel_x10_res_1mb(b: &mut test::Bencher) {
// let body = &[b'x'; 1024 * 1024 * 1];
// opts().parallel(10).response_body(body).bench(b)
// }
// HTTP2
// #[bench]
// fn http1_parallel_x10_res_10mb(b: &mut test::Bencher) {
// let body = &[b'x'; 1024 * 1024 * 10];
// opts().parallel(10).response_body(body).bench(b)
// }
const HTTP2_MAX_WINDOW: u32 = std::u32::MAX >> 1;
// // HTTP2
#[bench]
fn http2_consecutive_x1_empty(b: &mut test::Bencher) {
opts().http2().bench(b)
}
// const HTTP2_MAX_WINDOW: u32 = std::u32::MAX >> 1;
#[bench]
fn http2_consecutive_x1_req_10b(b: &mut test::Bencher) {
opts()
.http2()
.method(Method::POST)
.request_body(&[b's'; 10])
.bench(b)
}
// #[bench]
// fn http2_consecutive_x1_empty(b: &mut test::Bencher) {
// opts().http2().bench(b)
// }
#[bench]
fn http2_consecutive_x1_req_100kb(b: &mut test::Bencher) {
let body = &[b'x'; 1024 * 100];
opts()
.http2()
.method(Method::POST)
.request_body(body)
.bench(b)
}
// #[bench]
// fn http2_consecutive_x1_req_10b(b: &mut test::Bencher) {
// opts()
// .http2()
// .method(Method::POST)
// .request_body(&[b's'; 10])
// .bench(b)
// }
#[bench]
fn http2_parallel_x10_empty(b: &mut test::Bencher) {
opts().http2().parallel(10).bench(b)
}
// #[bench]
// fn http2_consecutive_x1_req_100kb(b: &mut test::Bencher) {
// let body = &[b'x'; 1024 * 100];
// opts()
// .http2()
// .method(Method::POST)
// .request_body(body)
// .bench(b)
// }
#[bench]
fn http2_parallel_x10_req_10mb(b: &mut test::Bencher) {
let body = &[b'x'; 1024 * 1024 * 10];
opts()
.http2()
.parallel(10)
.method(Method::POST)
.request_body(body)
.http2_stream_window(HTTP2_MAX_WINDOW)
.http2_conn_window(HTTP2_MAX_WINDOW)
.bench(b)
}
// #[bench]
// fn http2_parallel_x10_empty(b: &mut test::Bencher) {
// opts().http2().parallel(10).bench(b)
// }
#[bench]
fn http2_parallel_x10_req_10kb_100_chunks(b: &mut test::Bencher) {
let body = &[b'x'; 1024 * 10];
opts()
.http2()
.parallel(10)
.method(Method::POST)
.request_chunks(body, 100)
.bench(b)
}
// #[bench]
// fn http2_parallel_x10_req_10mb(b: &mut test::Bencher) {
// let body = &[b'x'; 1024 * 1024 * 10];
// opts()
// .http2()
// .parallel(10)
// .method(Method::POST)
// .request_body(body)
// .http2_stream_window(HTTP2_MAX_WINDOW)
// .http2_conn_window(HTTP2_MAX_WINDOW)
// .bench(b)
// }
#[bench]
fn http2_parallel_x10_req_10kb_100_chunks_adaptive_window(b: &mut test::Bencher) {
let body = &[b'x'; 1024 * 10];
opts()
.http2()
.parallel(10)
.method(Method::POST)
.request_chunks(body, 100)
.http2_adaptive_window()
.bench(b)
}
// #[bench]
// fn http2_parallel_x10_req_10kb_100_chunks(b: &mut test::Bencher) {
// let body = &[b'x'; 1024 * 10];
// opts()
// .http2()
// .parallel(10)
// .method(Method::POST)
// .request_chunks(body, 100)
// .bench(b)
// }
#[bench]
fn http2_parallel_x10_req_10kb_100_chunks_max_window(b: &mut test::Bencher) {
let body = &[b'x'; 1024 * 10];
opts()
.http2()
.parallel(10)
.method(Method::POST)
.request_chunks(body, 100)
.http2_stream_window(HTTP2_MAX_WINDOW)
.http2_conn_window(HTTP2_MAX_WINDOW)
.bench(b)
}
// #[bench]
// fn http2_parallel_x10_req_10kb_100_chunks_adaptive_window(b: &mut test::Bencher) {
// let body = &[b'x'; 1024 * 10];
// opts()
// .http2()
// .parallel(10)
// .method(Method::POST)
// .request_chunks(body, 100)
// .http2_adaptive_window()
// .bench(b)
// }
#[bench]
fn http2_parallel_x10_res_1mb(b: &mut test::Bencher) {
let body = &[b'x'; 1024 * 1024 * 1];
opts()
.http2()
.parallel(10)
.response_body(body)
.http2_stream_window(HTTP2_MAX_WINDOW)
.http2_conn_window(HTTP2_MAX_WINDOW)
.bench(b)
}
// #[bench]
// fn http2_parallel_x10_req_10kb_100_chunks_max_window(b: &mut test::Bencher) {
// let body = &[b'x'; 1024 * 10];
// opts()
// .http2()
// .parallel(10)
// .method(Method::POST)
// .request_chunks(body, 100)
// .http2_stream_window(HTTP2_MAX_WINDOW)
// .http2_conn_window(HTTP2_MAX_WINDOW)
// .bench(b)
// }
#[bench]
fn http2_parallel_x10_res_10mb(b: &mut test::Bencher) {
let body = &[b'x'; 1024 * 1024 * 10];
opts()
.http2()
.parallel(10)
.response_body(body)
.http2_stream_window(HTTP2_MAX_WINDOW)
.http2_conn_window(HTTP2_MAX_WINDOW)
.bench(b)
}
// #[bench]
// fn http2_parallel_x10_res_1mb(b: &mut test::Bencher) {
// let body = &[b'x'; 1024 * 1024 * 1];
// opts()
// .http2()
// .parallel(10)
// .response_body(body)
// .http2_stream_window(HTTP2_MAX_WINDOW)
// .http2_conn_window(HTTP2_MAX_WINDOW)
// .bench(b)
// }
// ==== Benchmark Options =====
// #[bench]
// fn http2_parallel_x10_res_10mb(b: &mut test::Bencher) {
// let body = &[b'x'; 1024 * 1024 * 10];
// opts()
// .http2()
// .parallel(10)
// .response_body(body)
// .http2_stream_window(HTTP2_MAX_WINDOW)
// .http2_conn_window(HTTP2_MAX_WINDOW)
// .bench(b)
// }
struct Opts {
http2: bool,
http2_stream_window: Option<u32>,
http2_conn_window: Option<u32>,
http2_adaptive_window: bool,
parallel_cnt: u32,
request_method: Method,
request_body: Option<&'static [u8]>,
request_chunks: usize,
response_body: &'static [u8],
}
// // ==== Benchmark Options =====
fn opts() -> Opts {
Opts {
http2: false,
http2_stream_window: None,
http2_conn_window: None,
http2_adaptive_window: false,
parallel_cnt: 1,
request_method: Method::GET,
request_body: None,
request_chunks: 0,
response_body: b"",
}
}
// struct Opts {
// http2: bool,
// http2_stream_window: Option<u32>,
// http2_conn_window: Option<u32>,
// http2_adaptive_window: bool,
// parallel_cnt: u32,
// request_method: Method,
// request_body: Option<&'static [u8]>,
// request_chunks: usize,
// response_body: &'static [u8],
// }
impl Opts {
fn http2(mut self) -> Self {
self.http2 = true;
self
}
// fn opts() -> Opts {
// Opts {
// http2: false,
// http2_stream_window: None,
// http2_conn_window: None,
// http2_adaptive_window: false,
// parallel_cnt: 1,
// request_method: Method::GET,
// request_body: None,
// request_chunks: 0,
// response_body: b"",
// }
// }
fn http2_stream_window(mut self, sz: impl Into<Option<u32>>) -> Self {
assert!(!self.http2_adaptive_window);
self.http2_stream_window = sz.into();
self
}
// impl Opts {
// fn http2(mut self) -> Self {
// self.http2 = true;
// self
// }
fn http2_conn_window(mut self, sz: impl Into<Option<u32>>) -> Self {
assert!(!self.http2_adaptive_window);
self.http2_conn_window = sz.into();
self
}
// fn http2_stream_window(mut self, sz: impl Into<Option<u32>>) -> Self {
// assert!(!self.http2_adaptive_window);
// self.http2_stream_window = sz.into();
// self
// }
fn http2_adaptive_window(mut self) -> Self {
assert!(self.http2_stream_window.is_none());
assert!(self.http2_conn_window.is_none());
self.http2_adaptive_window = true;
self
}
// fn http2_conn_window(mut self, sz: impl Into<Option<u32>>) -> Self {
// assert!(!self.http2_adaptive_window);
// self.http2_conn_window = sz.into();
// self
// }
fn method(mut self, m: Method) -> Self {
self.request_method = m;
self
}
// fn http2_adaptive_window(mut self) -> Self {
// assert!(self.http2_stream_window.is_none());
// assert!(self.http2_conn_window.is_none());
// self.http2_adaptive_window = true;
// self
// }
fn request_body(mut self, body: &'static [u8]) -> Self {
self.request_body = Some(body);
self
}
// fn method(mut self, m: Method) -> Self {
// self.request_method = m;
// self
// }
fn request_chunks(mut self, chunk: &'static [u8], cnt: usize) -> Self {
assert!(cnt > 0);
self.request_body = Some(chunk);
self.request_chunks = cnt;
self
}
// fn request_body(mut self, body: &'static [u8]) -> Self {
// self.request_body = Some(body);
// self
// }
fn response_body(mut self, body: &'static [u8]) -> Self {
self.response_body = body;
self
}
// fn request_chunks(mut self, chunk: &'static [u8], cnt: usize) -> Self {
// assert!(cnt > 0);
// self.request_body = Some(chunk);
// self.request_chunks = cnt;
// self
// }
fn parallel(mut self, cnt: u32) -> Self {
assert!(cnt > 0, "parallel count must be larger than 0");
self.parallel_cnt = cnt;
self
}
// fn response_body(mut self, body: &'static [u8]) -> Self {
// self.response_body = body;
// self
// }
fn bench(self, b: &mut test::Bencher) {
use std::sync::Arc;
let _ = pretty_env_logger::try_init();
// Create a runtime of current thread.
let rt = Arc::new(
tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.expect("rt build"),
);
let exec = rt.clone();
// fn parallel(mut self, cnt: u32) -> Self {
// assert!(cnt > 0, "parallel count must be larger than 0");
// self.parallel_cnt = cnt;
// self
// }
let req_len = self.request_body.map(|b| b.len()).unwrap_or(0) as u64;
let req_len = if self.request_chunks > 0 {
req_len * self.request_chunks as u64
} else {
req_len
};
let bytes_per_iter = (req_len + self.response_body.len() as u64) * self.parallel_cnt as u64;
b.bytes = bytes_per_iter;
// fn bench(self, b: &mut test::Bencher) {
// use std::sync::Arc;
// let _ = pretty_env_logger::try_init();
// // Create a runtime of current thread.
// let rt = Arc::new(
// tokio::runtime::Builder::new_current_thread()
// .enable_all()
// .build()
// .expect("rt build"),
// );
// let exec = rt.clone();
let addr = spawn_server(&rt, &self);
// let req_len = self.request_body.map(|b| b.len()).unwrap_or(0) as u64;
// let req_len = if self.request_chunks > 0 {
// req_len * self.request_chunks as u64
// } else {
// req_len
// };
// let bytes_per_iter = (req_len + self.response_body.len() as u64) * self.parallel_cnt as u64;
// b.bytes = bytes_per_iter;
let connector = HttpConnector::new();
let client = hyper::Client::builder()
.http2_only(self.http2)
.http2_initial_stream_window_size(self.http2_stream_window)
.http2_initial_connection_window_size(self.http2_conn_window)
.http2_adaptive_window(self.http2_adaptive_window)
.build::<_, Body>(connector);
// let addr = spawn_server(&rt, &self);
let url: hyper::Uri = format!("http://{}/hello", addr).parse().unwrap();
// let connector = HttpConnector::new();
// let client = hyper::Client::builder()
// .http2_only(self.http2)
// .http2_initial_stream_window_size(self.http2_stream_window)
// .http2_initial_connection_window_size(self.http2_conn_window)
// .http2_adaptive_window(self.http2_adaptive_window)
// .build::<_, Body>(connector);
let make_request = || {
let chunk_cnt = self.request_chunks;
let body = if chunk_cnt > 0 {
let (mut tx, body) = Body::channel();
let chunk = self
.request_body
.expect("request_chunks means request_body");
exec.spawn(async move {
for _ in 0..chunk_cnt {
tx.send_data(chunk.into()).await.expect("send_data");
}
});
body
} else {
self.request_body
.map(Body::from)
.unwrap_or_else(Body::empty)
};
let mut req = Request::new(body);
*req.method_mut() = self.request_method.clone();
*req.uri_mut() = url.clone();
req
};
// let url: hyper::Uri = format!("http://{}/hello", addr).parse().unwrap();
let send_request = |req: Request<Body>| {
let fut = client.request(req);
async {
let res = fut.await.expect("client wait");
let mut body = res.into_body();
while let Some(_chunk) = body.data().await {}
}
};
// let make_request = || {
// let chunk_cnt = self.request_chunks;
// let body = if chunk_cnt > 0 {
// let (mut tx, body) = Body::channel();
// let chunk = self
// .request_body
// .expect("request_chunks means request_body");
// exec.spawn(async move {
// for _ in 0..chunk_cnt {
// tx.send_data(chunk.into()).await.expect("send_data");
// }
// });
// body
// } else {
// self.request_body
// .map(Body::from)
// .unwrap_or_else(Body::empty)
// };
// let mut req = Request::new(body);
// *req.method_mut() = self.request_method.clone();
// *req.uri_mut() = url.clone();
// req
// };
if self.parallel_cnt == 1 {
b.iter(|| {
let req = make_request();
rt.block_on(send_request(req));
});
} else {
b.iter(|| {
let futs = (0..self.parallel_cnt).map(|_| {
let req = make_request();
send_request(req)
});
// Await all spawned futures becoming completed.
rt.block_on(join_all(futs));
});
}
}
}
// let send_request = |req: Request<Body>| {
// let fut = client.request(req);
// async {
// let res = fut.await.expect("client wait");
// let mut body = res.into_body();
// while let Some(_chunk) = body.data().await {}
// }
// };
fn spawn_server(rt: &tokio::runtime::Runtime, opts: &Opts) -> SocketAddr {
use hyper::service::{make_service_fn, service_fn};
let addr = "127.0.0.1:0".parse().unwrap();
// if self.parallel_cnt == 1 {
// b.iter(|| {
// let req = make_request();
// rt.block_on(send_request(req));
// });
// } else {
// b.iter(|| {
// let futs = (0..self.parallel_cnt).map(|_| {
// let req = make_request();
// send_request(req)
// });
// // Await all spawned futures becoming completed.
// rt.block_on(join_all(futs));
// });
// }
// }
// }
let body = opts.response_body;
let srv = rt.block_on(async move {
Server::bind(&addr)
.http2_only(opts.http2)
.http2_initial_stream_window_size(opts.http2_stream_window)
.http2_initial_connection_window_size(opts.http2_conn_window)
.http2_adaptive_window(opts.http2_adaptive_window)
.serve(make_service_fn(move |_| async move {
Ok::<_, hyper::Error>(service_fn(move |req: Request<Body>| async move {
let mut req_body = req.into_body();
while let Some(_chunk) = req_body.data().await {}
Ok::<_, hyper::Error>(Response::new(Body::from(body)))
}))
}))
});
let addr = srv.local_addr();
rt.spawn(async {
if let Err(err) = srv.await {
panic!("server error: {}", err);
}
});
addr
}
// fn spawn_server(rt: &tokio::runtime::Runtime, opts: &Opts) -> SocketAddr {
// use hyper::service::{make_service_fn, service_fn};
// let addr = "127.0.0.1:0".parse().unwrap();
// let body = opts.response_body;
// let srv = rt.block_on(async move {
// Server::bind(&addr)
// .http2_only(opts.http2)
// .http2_initial_stream_window_size(opts.http2_stream_window)
// .http2_initial_connection_window_size(opts.http2_conn_window)
// .http2_adaptive_window(opts.http2_adaptive_window)
// .serve(make_service_fn(move |_| async move {
// Ok::<_, hyper::Error>(service_fn(move |req: Request<Body>| async move {
// let mut req_body = req.into_body();
// while let Some(_chunk) = req_body.data().await {}
// Ok::<_, hyper::Error>(Response::new(Body::from(body)))
// }))
// }))
// });
// let addr = srv.local_addr();
// rt.spawn(async {
// if let Err(err) = srv.await {
// panic!("server error: {}", err);
// }
// });
// addr
// }

View File

@@ -4,14 +4,16 @@
extern crate test;
use std::io::{Read, Write};
use std::net::TcpStream;
use std::net::{SocketAddr, TcpStream};
use std::sync::mpsc;
use std::time::Duration;
use tokio::net::TcpListener;
use tokio::sync::oneshot;
use hyper::service::{make_service_fn, service_fn};
use hyper::{Body, Response, Server};
use hyper::server::conn::Http;
use hyper::service::service_fn;
use hyper::{Body, Response};
const PIPELINED_REQUESTS: usize = 16;
@@ -23,35 +25,34 @@ fn hello_world_16(b: &mut test::Bencher) {
let addr = {
let (addr_tx, addr_rx) = mpsc::channel();
std::thread::spawn(move || {
let addr = "127.0.0.1:0".parse().unwrap();
let make_svc = make_service_fn(|_| async {
Ok::<_, hyper::Error>(service_fn(|_| async {
Ok::<_, hyper::Error>(Response::new(Body::from("Hello, World!")))
}))
});
let addr: SocketAddr = "127.0.0.1:0".parse().unwrap();
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.expect("rt build");
let srv = rt.block_on(async move {
Server::bind(&addr)
.http1_pipeline_flush(true)
.serve(make_svc)
});
addr_tx.send(srv.local_addr()).unwrap();
let listener = rt.block_on(TcpListener::bind(addr)).unwrap();
let addr = listener.local_addr().unwrap();
let graceful = srv.with_graceful_shutdown(async {
until_rx.await.ok();
});
rt.spawn(async move {
loop {
let (stream, _addr) = listener.accept().await.expect("accept");
rt.block_on(async {
if let Err(e) = graceful.await {
panic!("server error: {}", e);
Http::new()
.pipeline_flush(true)
.serve_connection(
stream,
service_fn(|_| async {
Ok::<_, hyper::Error>(Response::new(Body::from("Hello, World!")))
}),
)
.await
.unwrap();
}
});
addr_tx.send(addr).unwrap();
rt.block_on(until_rx).ok();
});
addr_rx.recv().unwrap()

View File

@@ -4,54 +4,59 @@
extern crate test;
use std::io::{Read, Write};
use std::net::{TcpListener, TcpStream};
use std::net::{SocketAddr, TcpListener, TcpStream};
use std::sync::mpsc;
use std::time::Duration;
use futures_util::{stream, StreamExt};
use http_body_util::StreamBody;
use http_body_util::{BodyExt, StreamBody};
use tokio::sync::oneshot;
use hyper::service::{make_service_fn, service_fn};
use hyper::{Response, Server};
use hyper::server::conn::Http;
use hyper::service::service_fn;
use hyper::Response;
macro_rules! bench_server {
($b:ident, $header:expr, $body:expr) => {{
let _ = pretty_env_logger::try_init();
let (_until_tx, until_rx) = oneshot::channel::<()>();
let addr = {
let (addr_tx, addr_rx) = mpsc::channel();
std::thread::spawn(move || {
let addr = "127.0.0.1:0".parse().unwrap();
let make_svc = make_service_fn(|_| async {
Ok::<_, hyper::Error>(service_fn(|_| async {
Ok::<_, hyper::Error>(
Response::builder()
.header($header.0, $header.1)
.header("content-type", "text/plain")
.body($body())
.unwrap(),
)
}))
});
let addr: SocketAddr = "127.0.0.1:0".parse().unwrap();
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.expect("rt build");
let srv = rt.block_on(async move { Server::bind(&addr).serve(make_svc) });
let listener = rt.block_on(tokio::net::TcpListener::bind(addr)).unwrap();
let addr = listener.local_addr().unwrap();
addr_tx.send(srv.local_addr()).unwrap();
rt.spawn(async move {
loop {
let (stream, _) = listener.accept().await.expect("accept");
let graceful = srv.with_graceful_shutdown(async {
until_rx.await.ok();
});
rt.block_on(async move {
if let Err(e) = graceful.await {
panic!("server error: {}", e);
Http::new()
.serve_connection(
stream,
service_fn(|_| async {
Ok::<_, hyper::Error>(
Response::builder()
.header($header.0, $header.1)
.header("content-type", "text/plain")
.body($body())
.unwrap(),
)
}),
)
.await
.unwrap();
}
});
addr_tx.send(addr).unwrap();
rt.block_on(until_rx).ok();
});
addr_rx.recv().unwrap()
@@ -100,9 +105,11 @@ fn throughput_fixedsize_large_payload(b: &mut test::Bencher) {
#[bench]
fn throughput_fixedsize_many_chunks(b: &mut test::Bencher) {
bench_server!(b, ("content-length", "1000000"), || {
bench_server!(b, ("content-length", "1000000"), move || {
static S: &[&[u8]] = &[&[b'x'; 1_000] as &[u8]; 1_000] as _;
StreamBody::new(stream::iter(S.iter()).map(|&s| Ok::<_, String>(s)))
BodyExt::boxed(StreamBody::new(
stream::iter(S.iter()).map(|&s| Ok::<_, String>(s)),
))
})
}
@@ -124,7 +131,9 @@ fn throughput_chunked_large_payload(b: &mut test::Bencher) {
fn throughput_chunked_many_chunks(b: &mut test::Bencher) {
bench_server!(b, ("transfer-encoding", "chunked"), || {
static S: &[&[u8]] = &[&[b'x'; 1_000] as &[u8]; 1_000] as _;
StreamBody::new(stream::iter(S.iter()).map(|&s| Ok::<_, String>(s)))
BodyExt::boxed(StreamBody::new(
stream::iter(S.iter()).map(|&s| Ok::<_, String>(s)),
))
})
}

111
docs/GOVERNANCE.md Normal file
View File

@@ -0,0 +1,111 @@
# Governance
## Making decisions
There's two main pieces to the way decisions are made in hyper:
1. A decision-making framework
2. The people who apply it
The people are described [lower down in this document](#roles).
### Decision-making framework
We start with the users. The project wouldn't exist without them, and it exists
in order to enable users to do amazing things with HTTP. We listen to our
users. Some actively contribute their thoughts, but many others we must seek
out to learn about their usage, joys, and headaches. Those insights allow our
experts to determine the best solutions for the users.
We then define a set of [TENETS](./TENETS.md), which are guiding principles
that can be used to measure aspects of individual decisions. It should be
possible to identify one or more tenets that apply to why a decision is made.
And the set helps us balance which priorities are more important for our users.
We combine the usecases with the tenets to come up with a [VISION](./VISION.md)
that provides a longer-term plan of what hyper _should_ look like.
Finally, we define a [ROADMAP](./ROADMAP.md) that describes what the
short-term, tactical changes to bring hyper closer in line with the vision.
## Roles
These are the roles people can fill when participating in the project. A list
of the people in each role is available in [MAINTAINERS](./MAINTAINERS.md).
### Contributor
A contributor is anyone who contributes their time to provide value for the
project. This could be in the form of code, documentation, filing issues,
discussing designs, or helping others on the issue tracker or in chat.
All contributors MUST follow the [Code of Conduct][coc].
👋 **New here?** [This could be you!][contrib]
### Triager
Triagers assess issues on the issue tracker. They help make sure the work is
well organized, and are critical for making new issue reporters feeling
welcome.
Responsibilities:
- Adhere to the [Code of Conduct][coc]
- Follow the [triager's guide][triage-guide]
Privileges:
- Can edit, label, and close issues
- Member of the organization
- Can be assigned issues and pull requests
How to become:
- Make a few [contributions][contrib] to the project, to show you can follow
the [Code of Conduct][coc].
- Self-nominate by making a pull request adding yourself to the
[list](./MAINTAINERS.md#triagers).
### Collaborator
Collaborators are contributors who have been helping out in a consistent basis.
Responsibilities:
- Be exemplars of the [Code of Conduct][coc]
- Internalize the [VISION](./VISION.md)
- Reviewing pull requests from other contributors
- Provide feedback on proposed features and design documents
- [Welcome new contributors][triage-guide]
- Answer questions in issues and/or chat
- Mentor contributors to become members
Privileges:
- Can review and merge pull requests
- Can trigger re-runs of CI, and approve CI for first-time contributors
- Can assign issues and pull requests to other organization members
How to become:
- Work at fulfilling the above responsibilities.
- Any collaborator may nominate a contributor who has been around for some time
and is already filling the responsibilities.
- Another collaborator must second the nomination.
- If there are no objections, a maintainer will welcome the new collaborator.
Don't be afraid to ask a collaborator for what you could work on to reach this
goal!
### Maintainer
Maintainers are the project admins. Besides being a collaborator, they take care
house-keeping duties, help lead the direction, and have the final authority when
required.
[coc]: ./CODE_OF_CONDUCT.md
[contrib]: ../CONTRIBUTING.md
[triage-guide]: ./ISSUES.md#triaging

30
docs/MAINTAINERS.md Normal file
View File

@@ -0,0 +1,30 @@
# The People
To see what these roles do, and how to become one, look at [GOVERNANCE](./GOVERNANCED.md).
## Triagers
## Collaborators
- Sean McArthur (@seanmonstar)
- Steven Fackler (@sfackler)
- Oliver Gould (@olix0r)
- Eliza Weisman (@hawkw)
- Lucio Franco (@LucioFranco)
- Anthony Ramine (@nox)
- David Pedersen (@davidpdrsn)
- Adam Foltzer (@acfoltzer)
<details>
<summary>Emeriti</summary>
### Collaborator emeriti
- Jonathan Reem (@reem)
- Carl Lerche (@carllerche)
</details>
## Maintainers
- Sean McArthur (@seanmonstar)

View File

@@ -2,8 +2,9 @@
#![warn(rust_2018_idioms)]
use std::env;
use hyper::{body::HttpBody as _, Client};
use hyper::{body::HttpBody as _, Body, Request};
use tokio::io::{self, AsyncWriteExt as _};
use tokio::net::TcpStream;
// A simple type alias so as to DRY.
type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
@@ -33,9 +34,20 @@ async fn main() -> Result<()> {
}
async fn fetch_url(url: hyper::Uri) -> Result<()> {
let client = Client::new();
let host = url.host().expect("uri has no host");
let port = url.port_u16().unwrap_or(80);
let addr = format!("{}:{}", host, port);
let stream = TcpStream::connect(addr).await?;
let mut res = client.get(url).await?;
let (mut sender, conn) = hyper::client::conn::handshake(stream).await?;
tokio::task::spawn(async move {
if let Err(err) = conn.await {
println!("Connection failed: {:?}", err);
}
});
let req = Request::builder().uri(url).body(Body::empty()).unwrap();
let mut res = sender.send_request(req).await?;
println!("Response: {}", res.status());
println!("Headers: {:#?}\n", res.headers());

View File

@@ -1,9 +1,10 @@
#![deny(warnings)]
#![warn(rust_2018_idioms)]
use hyper::body::Buf;
use hyper::Client;
use hyper::Body;
use hyper::{body::Buf, Request};
use serde::Deserialize;
use tokio::net::TcpStream;
// A simple type alias so as to DRY.
type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
@@ -22,10 +23,22 @@ async fn main() -> Result<()> {
}
async fn fetch_json(url: hyper::Uri) -> Result<Vec<User>> {
let client = Client::new();
let host = url.host().expect("uri has no host");
let port = url.port_u16().unwrap_or(80);
let addr = format!("{}:{}", host, port);
let stream = TcpStream::connect(addr).await?;
let (mut sender, conn) = hyper::client::conn::handshake(stream).await?;
tokio::task::spawn(async move {
if let Err(err) = conn.await {
println!("Connection failed: {:?}", err);
}
});
// Fetch the url...
let res = client.get(url).await?;
let req = Request::builder().uri(url).body(Body::empty()).unwrap();
let res = sender.send_request(req).await?;
// asynchronously aggregate the chunks of the body
let body = hyper::body::aggregate(res).await?;

View File

@@ -1,7 +1,11 @@
#![deny(warnings)]
use hyper::service::{make_service_fn, service_fn};
use hyper::{Body, Method, Request, Response, Server, StatusCode};
use std::net::SocketAddr;
use hyper::server::conn::Http;
use hyper::service::service_fn;
use hyper::{Body, Method, Request, Response, StatusCode};
use tokio::net::TcpListener;
/// This is our service handler. It receives a Request, routes on its
/// path, and returns a Future of a Response.
@@ -51,15 +55,17 @@ async fn echo(req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let addr = ([127, 0, 0, 1], 3000).into();
let service = make_service_fn(|_| async { Ok::<_, hyper::Error>(service_fn(echo)) });
let server = Server::bind(&addr).serve(service);
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
let listener = TcpListener::bind(addr).await?;
println!("Listening on http://{}", addr);
loop {
let (stream, _) = listener.accept().await?;
server.await?;
Ok(())
tokio::task::spawn(async move {
if let Err(err) = Http::new().serve_connection(stream, service_fn(echo)).await {
println!("Error serving connection: {:?}", err);
}
});
}
}

View File

@@ -1,51 +1,63 @@
#![deny(warnings)]
use hyper::service::{make_service_fn, service_fn};
use hyper::{Client, Error, Server};
use hyper::{server::conn::Http, service::service_fn};
use std::net::SocketAddr;
use tokio::net::{TcpListener, TcpStream};
#[tokio::main]
async fn main() {
async fn main() -> Result<(), Box<dyn std::error::Error>> {
pretty_env_logger::init();
let in_addr = ([127, 0, 0, 1], 3001).into();
let in_addr: SocketAddr = ([127, 0, 0, 1], 3001).into();
let out_addr: SocketAddr = ([127, 0, 0, 1], 3000).into();
let client_main = Client::new();
let out_addr_clone = out_addr.clone();
// The closure inside `make_service_fn` is run for each connection,
// creating a 'service' to handle requests for that specific connection.
let make_service = make_service_fn(move |_| {
let client = client_main.clone();
async move {
// This is the `Service` that will handle the connection.
// `service_fn` is a helper to convert a function that
// returns a Response into a `Service`.
Ok::<_, Error>(service_fn(move |mut req| {
let uri_string = format!(
"http://{}{}",
out_addr_clone,
req.uri()
.path_and_query()
.map(|x| x.as_str())
.unwrap_or("/")
);
let uri = uri_string.parse().unwrap();
*req.uri_mut() = uri;
client.request(req)
}))
}
});
let server = Server::bind(&in_addr).serve(make_service);
let listener = TcpListener::bind(in_addr).await?;
println!("Listening on http://{}", in_addr);
println!("Proxying on http://{}", out_addr);
if let Err(e) = server.await {
eprintln!("server error: {}", e);
loop {
let (stream, _) = listener.accept().await?;
// This is the `Service` that will handle the connection.
// `service_fn` is a helper to convert a function that
// returns a Response into a `Service`.
let service = service_fn(move |mut req| {
let uri_string = format!(
"http://{}{}",
out_addr_clone,
req.uri()
.path_and_query()
.map(|x| x.as_str())
.unwrap_or("/")
);
let uri = uri_string.parse().unwrap();
*req.uri_mut() = uri;
let host = req.uri().host().expect("uri has no host");
let port = req.uri().port_u16().unwrap_or(80);
let addr = format!("{}:{}", host, port);
async move {
let client_stream = TcpStream::connect(addr).await.unwrap();
let (mut sender, conn) = hyper::client::conn::handshake(client_stream).await?;
tokio::task::spawn(async move {
if let Err(err) = conn.await {
println!("Connection failed: {:?}", err);
}
});
sender.send_request(req).await
}
});
tokio::task::spawn(async move {
if let Err(err) = Http::new().serve_connection(stream, service).await {
println!("Failed to servce connection: {:?}", err);
}
});
}
}

View File

@@ -1,9 +1,12 @@
#![deny(warnings)]
use std::convert::Infallible;
use std::net::SocketAddr;
use hyper::service::{make_service_fn, service_fn};
use hyper::{Body, Request, Response, Server};
use hyper::server::conn::Http;
use hyper::service::service_fn;
use hyper::{Body, Request, Response};
use tokio::net::TcpListener;
async fn hello(_: Request<Body>) -> Result<Response<Body>, Infallible> {
Ok(Response::new(Body::from("Hello World!")))
@@ -13,22 +16,20 @@ async fn hello(_: Request<Body>) -> Result<Response<Body>, Infallible> {
pub async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
pretty_env_logger::init();
// For every connection, we must make a `Service` to handle all
// incoming HTTP requests on said connection.
let make_svc = make_service_fn(|_conn| {
// This is the `Service` that will handle the connection.
// `service_fn` is a helper to convert a function that
// returns a Response into a `Service`.
async { Ok::<_, Infallible>(service_fn(hello)) }
});
let addr = ([127, 0, 0, 1], 3000).into();
let server = Server::bind(&addr).serve(make_svc);
let addr: SocketAddr = ([127, 0, 0, 1], 3000).into();
let listener = TcpListener::bind(addr).await?;
println!("Listening on http://{}", addr);
loop {
let (stream, _) = listener.accept().await?;
server.await?;
Ok(())
tokio::task::spawn(async move {
if let Err(err) = Http::new()
.serve_connection(stream, service_fn(hello))
.await
{
println!("Error serving connection: {:?}", err);
}
});
}
}

View File

@@ -1,15 +1,14 @@
#![deny(warnings)]
use std::convert::Infallible;
use std::net::SocketAddr;
use hyper::service::{make_service_fn, service_fn};
use hyper::client::conn::Builder;
use hyper::server::conn::Http;
use hyper::service::service_fn;
use hyper::upgrade::Upgraded;
use hyper::{Body, Client, Method, Request, Response, Server};
use hyper::{Body, Method, Request, Response};
use tokio::net::TcpStream;
type HttpClient = Client<hyper::client::HttpConnector>;
use tokio::net::{TcpListener, TcpStream};
// To try this example:
// 1. cargo run --example http_proxy
@@ -19,32 +18,29 @@ type HttpClient = Client<hyper::client::HttpConnector>;
// 3. send requests
// $ curl -i https://www.some_domain.com/
#[tokio::main]
async fn main() {
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let addr = SocketAddr::from(([127, 0, 0, 1], 8100));
let client = Client::builder()
.http1_title_case_headers(true)
.http1_preserve_header_case(true)
.build_http();
let make_service = make_service_fn(move |_| {
let client = client.clone();
async move { Ok::<_, Infallible>(service_fn(move |req| proxy(client.clone(), req))) }
});
let server = Server::bind(&addr)
.http1_preserve_header_case(true)
.http1_title_case_headers(true)
.serve(make_service);
let listener = TcpListener::bind(addr).await?;
println!("Listening on http://{}", addr);
if let Err(e) = server.await {
eprintln!("server error: {}", e);
loop {
let (stream, _) = listener.accept().await?;
tokio::task::spawn(async move {
if let Err(err) = Http::new()
.http1_preserve_header_case(true)
.http1_title_case_headers(true)
.serve_connection(stream, service_fn(proxy))
.await
{
println!("Failed to serve connection: {:?}", err);
}
});
}
}
async fn proxy(client: HttpClient, req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
async fn proxy(req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
println!("req: {:?}", req);
if Method::CONNECT == req.method() {
@@ -82,7 +78,24 @@ async fn proxy(client: HttpClient, req: Request<Body>) -> Result<Response<Body>,
Ok(resp)
}
} else {
client.request(req).await
let host = req.uri().host().expect("uri has no host");
let port = req.uri().port_u16().unwrap_or(80);
let addr = format!("{}:{}", host, port);
let stream = TcpStream::connect(addr).await.unwrap();
let (mut sender, conn) = Builder::new()
.http1_preserve_header_case(true)
.http1_title_case_headers(true)
.handshake(stream)
.await?;
tokio::task::spawn(async move {
if let Err(err) = conn.await {
println!("Connection failed: {:?}", err);
}
});
sender.send_request(req).await
}
}

View File

@@ -1,9 +1,13 @@
#![deny(warnings)]
#![warn(rust_2018_idioms)]
use std::net::SocketAddr;
use futures_util::future::join;
use hyper::service::{make_service_fn, service_fn};
use hyper::{Body, Request, Response, Server};
use hyper::server::conn::Http;
use hyper::service::service_fn;
use hyper::{Body, Request, Response};
use tokio::net::TcpListener;
static INDEX1: &[u8] = b"The 1st service!";
static INDEX2: &[u8] = b"The 2nd service!";
@@ -20,16 +24,40 @@ async fn index2(_: Request<Body>) -> Result<Response<Body>, hyper::Error> {
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
pretty_env_logger::init();
let addr1 = ([127, 0, 0, 1], 1337).into();
let addr2 = ([127, 0, 0, 1], 1338).into();
let addr1: SocketAddr = ([127, 0, 0, 1], 1337).into();
let addr2: SocketAddr = ([127, 0, 0, 1], 1338).into();
let srv1 = Server::bind(&addr1).serve(make_service_fn(|_| async {
Ok::<_, hyper::Error>(service_fn(index1))
}));
let srv1 = async move {
let listener = TcpListener::bind(addr1).await.unwrap();
loop {
let (stream, _) = listener.accept().await.unwrap();
let srv2 = Server::bind(&addr2).serve(make_service_fn(|_| async {
Ok::<_, hyper::Error>(service_fn(index2))
}));
tokio::task::spawn(async move {
if let Err(err) = Http::new()
.serve_connection(stream, service_fn(index1))
.await
{
println!("Error serving connection: {:?}", err);
}
});
}
};
let srv2 = async move {
let listener = TcpListener::bind(addr2).await.unwrap();
loop {
let (stream, _) = listener.accept().await.unwrap();
tokio::task::spawn(async move {
if let Err(err) = Http::new()
.serve_connection(stream, service_fn(index2))
.await
{
println!("Error serving connection: {:?}", err);
}
});
}
};
println!("Listening on http://{} and http://{}", addr1, addr2);

View File

@@ -1,10 +1,13 @@
// #![deny(warnings)] // FIXME: https://github.com/rust-lang/rust/issues/62411
#![warn(rust_2018_idioms)]
use hyper::service::{make_service_fn, service_fn};
use hyper::{Body, Method, Request, Response, Server, StatusCode};
use hyper::server::conn::Http;
use hyper::service::service_fn;
use hyper::{Body, Method, Request, Response, StatusCode};
use tokio::net::TcpListener;
use std::collections::HashMap;
use std::net::SocketAddr;
use url::form_urlencoded;
static INDEX: &[u8] = b"<html><body><form action=\"post\" method=\"post\">Name: <input type=\"text\" name=\"name\"><br>Number: <input type=\"text\" name=\"number\"><br><input type=\"submit\"></body></html>";
@@ -102,15 +105,20 @@ async fn param_example(req: Request<Body>) -> Result<Response<Body>, hyper::Erro
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
pretty_env_logger::init();
let addr = ([127, 0, 0, 1], 1337).into();
let server = Server::bind(&addr).serve(make_service_fn(|_| async {
Ok::<_, hyper::Error>(service_fn(param_example))
}));
let addr: SocketAddr = ([127, 0, 0, 1], 1337).into();
let listener = TcpListener::bind(addr).await?;
println!("Listening on http://{}", addr);
loop {
let (stream, _) = listener.accept().await?;
server.await?;
Ok(())
tokio::task::spawn(async move {
if let Err(err) = Http::new()
.serve_connection(stream, service_fn(param_example))
.await
{
println!("Error serving connection: {:?}", err);
}
});
}
}

View File

@@ -1,26 +1,36 @@
#![deny(warnings)]
use hyper::service::{make_service_fn, service_fn};
use hyper::{Body, Method, Request, Response, Result, Server, StatusCode};
use std::net::SocketAddr;
use hyper::server::conn::Http;
use tokio::net::TcpListener;
use hyper::service::service_fn;
use hyper::{Body, Method, Request, Response, Result, StatusCode};
static INDEX: &str = "examples/send_file_index.html";
static NOTFOUND: &[u8] = b"Not Found";
#[tokio::main]
async fn main() {
async fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
pretty_env_logger::init();
let addr = "127.0.0.1:1337".parse().unwrap();
let make_service =
make_service_fn(|_| async { Ok::<_, hyper::Error>(service_fn(response_examples)) });
let server = Server::bind(&addr).serve(make_service);
let addr: SocketAddr = "127.0.0.1:1337".parse().unwrap();
let listener = TcpListener::bind(addr).await?;
println!("Listening on http://{}", addr);
if let Err(e) = server.await {
eprintln!("server error: {}", e);
loop {
let (stream, _) = listener.accept().await?;
tokio::task::spawn(async move {
if let Err(err) = Http::new()
.serve_connection(stream, service_fn(response_examples))
.await
{
println!("Failed to serve connection: {:?}", err);
}
});
}
}

View File

@@ -1,7 +1,10 @@
use hyper::server::conn::Http;
use hyper::service::Service;
use hyper::{Body, Request, Response, Server};
use hyper::{Body, Request, Response};
use tokio::net::TcpListener;
use std::future::Future;
use std::net::SocketAddr;
use std::pin::Pin;
use std::task::{Context, Poll};
@@ -9,13 +12,23 @@ type Counter = i32;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let addr = ([127, 0, 0, 1], 3000).into();
let addr: SocketAddr = ([127, 0, 0, 1], 3000).into();
let server = Server::bind(&addr).serve(MakeSvc { counter: 81818 });
let listener = TcpListener::bind(addr).await?;
println!("Listening on http://{}", addr);
server.await?;
Ok(())
loop {
let (stream, _) = listener.accept().await?;
tokio::task::spawn(async move {
if let Err(err) = Http::new()
.serve_connection(stream, Svc { counter: 81818 })
.await
{
println!("Failed to serve connection: {:?}", err);
}
});
}
}
struct Svc {
@@ -54,23 +67,3 @@ impl Service<Request<Body>> for Svc {
Box::pin(async { res })
}
}
struct MakeSvc {
counter: Counter,
}
impl<T> Service<T> for MakeSvc {
type Response = Svc;
type Error = hyper::Error;
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
fn poll_ready(&mut self, _: &mut Context) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, _: T) -> Self::Future {
let counter = self.counter.clone();
let fut = async move { Ok(Svc { counter }) };
Box::pin(fut)
}
}

View File

@@ -1,13 +1,15 @@
#![deny(warnings)]
use hyper::server::conn::Http;
use std::cell::Cell;
use std::net::SocketAddr;
use std::rc::Rc;
use tokio::sync::oneshot;
use tokio::net::TcpListener;
use hyper::body::{Bytes, HttpBody};
use hyper::header::{HeaderMap, HeaderValue};
use hyper::service::{make_service_fn, service_fn};
use hyper::{Error, Response, Server};
use hyper::service::service_fn;
use hyper::{Error, Response};
use std::marker::PhantomData;
use std::pin::Pin;
use std::task::{Context, Poll};
@@ -46,7 +48,7 @@ impl HttpBody for Body {
}
}
fn main() {
fn main() -> Result<(), Box<dyn std::error::Error>> {
pretty_env_logger::init();
// Configure a runtime that runs everything on the current thread
@@ -57,43 +59,39 @@ fn main() {
// Combine it with a `LocalSet, which means it can spawn !Send futures...
let local = tokio::task::LocalSet::new();
local.block_on(&rt, run());
local.block_on(&rt, run())
}
async fn run() {
let addr = ([127, 0, 0, 1], 3000).into();
async fn run() -> Result<(), Box<dyn std::error::Error>> {
let addr: SocketAddr = ([127, 0, 0, 1], 3000).into();
// Using a !Send request counter is fine on 1 thread...
let counter = Rc::new(Cell::new(0));
let make_service = make_service_fn(move |_| {
let listener = TcpListener::bind(addr).await?;
println!("Listening on http://{}", addr);
loop {
let (stream, _) = listener.accept().await?;
// For each connection, clone the counter to use in our service...
let cnt = counter.clone();
async move {
Ok::<_, Error>(service_fn(move |_| {
let prev = cnt.get();
cnt.set(prev + 1);
let value = cnt.get();
async move { Ok::<_, Error>(Response::new(Body::from(format!("Request #{}", value)))) }
}))
}
});
let service = service_fn(move |_| {
let prev = cnt.get();
cnt.set(prev + 1);
let value = cnt.get();
async move { Ok::<_, Error>(Response::new(Body::from(format!("Request #{}", value)))) }
});
let server = Server::bind(&addr).executor(LocalExec).serve(make_service);
// Just shows that with_graceful_shutdown compiles with !Send,
// !Sync HttpBody.
let (_tx, rx) = oneshot::channel::<()>();
let server = server.with_graceful_shutdown(async move {
rx.await.ok();
});
println!("Listening on http://{}", addr);
// The server would block on current thread to await !Send futures.
if let Err(e) = server.await {
eprintln!("server error: {}", e);
tokio::task::spawn_local(async move {
if let Err(err) = Http::new()
.with_executor(LocalExec)
.serve_connection(stream, service)
.await
{
println!("Error serving connection: {:?}", err);
}
});
}
}

View File

@@ -1,52 +1,46 @@
#![deny(warnings)]
use std::net::SocketAddr;
use std::sync::{
atomic::{AtomicUsize, Ordering},
Arc,
};
use hyper::service::{make_service_fn, service_fn};
use hyper::{Body, Error, Response, Server};
use hyper::{server::conn::Http, service::service_fn};
use hyper::{Body, Error, Response};
use tokio::net::TcpListener;
#[tokio::main]
async fn main() {
async fn main() -> Result<(), Box<dyn std::error::Error>> {
pretty_env_logger::init();
let addr = ([127, 0, 0, 1], 3000).into();
let addr: SocketAddr = ([127, 0, 0, 1], 3000).into();
// For the most basic of state, we just share a counter, that increments
// with each request, and we send its value back in the response.
let counter = Arc::new(AtomicUsize::new(0));
// The closure inside `make_service_fn` is run for each connection,
// creating a 'service' to handle requests for that specific connection.
let make_service = make_service_fn(move |_| {
// While the state was moved into the make_service closure,
// we need to clone it here because this closure is called
// once for every connection.
//
let listener = TcpListener::bind(addr).await?;
println!("Listening on http://{}", addr);
loop {
let (stream, _) = listener.accept().await?;
// Each connection could send multiple requests, so
// the `Service` needs a clone to handle later requests.
let counter = counter.clone();
async move {
// This is the `Service` that will handle the connection.
// `service_fn` is a helper to convert a function that
// returns a Response into a `Service`.
Ok::<_, Error>(service_fn(move |_req| {
// Get the current count, and also increment by 1, in a single
// atomic operation.
let count = counter.fetch_add(1, Ordering::AcqRel);
async move { Ok::<_, Error>(Response::new(Body::from(format!("Request #{}", count)))) }
}))
// This is the `Service` that will handle the connection.
// `service_fn` is a helper to convert a function that
// returns a Response into a `Service`.
let service = service_fn(move |_req| {
// Get the current count, and also increment by 1, in a single
// atomic operation.
let count = counter.fetch_add(1, Ordering::AcqRel);
async move { Ok::<_, Error>(Response::new(Body::from(format!("Request #{}", count)))) }
});
if let Err(err) = Http::new().serve_connection(stream, service).await {
println!("Error serving connection: {:?}", err);
}
});
let server = Server::bind(&addr).serve(make_service);
println!("Listening on http://{}", addr);
if let Err(e) = server.await {
eprintln!("server error: {}", e);
}
}

View File

@@ -1,27 +0,0 @@
#![deny(warnings)]
use hyper::client::conn::Builder;
use hyper::client::connect::HttpConnector;
use hyper::client::service::Connect;
use hyper::service::Service;
use hyper::{Body, Request};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
pretty_env_logger::init();
let mut mk_svc = Connect::new(HttpConnector::new(), Builder::new());
let uri = "http://127.0.0.1:8080".parse::<http::Uri>()?;
let mut svc = mk_svc.call(uri.clone()).await?;
let body = Body::empty();
let req = Request::get(uri).body(body)?;
let res = svc.call(req).await?;
println!("RESPONSE={:?}", res);
Ok(())
}

View File

@@ -1,10 +1,13 @@
#![deny(warnings)]
use std::net::SocketAddr;
use std::task::{Context, Poll};
use futures_util::future;
use hyper::server::conn::Http;
use hyper::service::Service;
use hyper::{Body, Request, Response, Server};
use hyper::{Body, Request, Response};
use tokio::net::TcpListener;
const ROOT: &str = "/";
@@ -36,33 +39,22 @@ impl Service<Request<Body>> for Svc {
}
}
pub struct MakeSvc;
impl<T> Service<T> for MakeSvc {
type Response = Svc;
type Error = std::io::Error;
type Future = future::Ready<Result<Self::Response, Self::Error>>;
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Ok(()).into()
}
fn call(&mut self, _: T) -> Self::Future {
future::ok(Svc)
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
pretty_env_logger::init();
let addr = "127.0.0.1:1337".parse().unwrap();
let server = Server::bind(&addr).serve(MakeSvc);
let addr: SocketAddr = "127.0.0.1:1337".parse().unwrap();
let listener = TcpListener::bind(addr).await?;
println!("Listening on http://{}", addr);
server.await?;
loop {
let (stream, _) = listener.accept().await?;
Ok(())
tokio::task::spawn(async move {
if let Err(err) = Http::new().serve_connection(stream, Svc).await {
println!("Failed to serve connection: {:?}", err);
}
});
}
}

View File

@@ -3,13 +3,15 @@
// Note: `hyper::upgrade` docs link to this upgrade.
use std::str;
use hyper::server::conn::Http;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::sync::oneshot;
use tokio::net::{TcpListener, TcpStream};
use tokio::sync::watch;
use hyper::header::{HeaderValue, UPGRADE};
use hyper::service::{make_service_fn, service_fn};
use hyper::service::service_fn;
use hyper::upgrade::Upgraded;
use hyper::{Body, Client, Request, Response, Server, StatusCode};
use hyper::{Body, Request, Response, StatusCode};
use std::net::SocketAddr;
// A simple type alias so as to DRY.
@@ -92,7 +94,17 @@ async fn client_upgrade_request(addr: SocketAddr) -> Result<()> {
.body(Body::empty())
.unwrap();
let res = Client::new().request(req).await?;
let stream = TcpStream::connect(addr).await?;
let (mut sender, conn) = hyper::client::conn::handshake(stream).await?;
tokio::task::spawn(async move {
if let Err(err) = conn.await {
println!("Connection failed: {:?}", err);
}
});
let res = sender.send_request(req).await?;
if res.status() != StatusCode::SWITCHING_PROTOCOLS {
panic!("Our server didn't upgrade: {}", res.status());
}
@@ -114,28 +126,52 @@ async fn main() {
// For this example, we just make a server and our own client to talk to
// it, so the exact port isn't important. Instead, let the OS give us an
// unused port.
let addr = ([127, 0, 0, 1], 0).into();
let addr: SocketAddr = ([127, 0, 0, 1], 0).into();
let make_service =
make_service_fn(|_| async { Ok::<_, hyper::Error>(service_fn(server_upgrade)) });
let server = Server::bind(&addr).serve(make_service);
let listener = TcpListener::bind(addr).await.expect("failed to bind");
// We need the assigned address for the client to send it messages.
let addr = server.local_addr();
let addr = listener.local_addr().unwrap();
// For this example, a oneshot is used to signal that after 1 request,
// the server should be shutdown.
let (tx, rx) = oneshot::channel::<()>();
let server = server.with_graceful_shutdown(async move {
rx.await.ok();
});
let (tx, mut rx) = watch::channel(false);
// Spawn server on the default executor,
// which is usually a thread-pool from tokio default runtime.
tokio::task::spawn(async move {
if let Err(e) = server.await {
eprintln!("server error: {}", e);
loop {
tokio::select! {
res = listener.accept() => {
let (stream, _) = res.expect("Failed to accept");
let mut rx = rx.clone();
tokio::task::spawn(async move {
let conn = Http::new().serve_connection(stream, service_fn(server_upgrade));
// Don't forget to enable upgrades on the connection.
let mut conn = conn.with_upgrades();
let mut conn = Pin::new(&mut conn);
tokio::select! {
res = &mut conn => {
if let Err(err) = res {
println!("Error serving connection: {:?}", err);
return;
}
}
// Continue polling the connection after enabling graceful shutdown.
_ = rx.changed() => {
conn.graceful_shutdown();
}
}
});
}
_ = rx.changed() => {
break;
}
}
}
});
@@ -147,5 +183,5 @@ async fn main() {
// Complete the oneshot so that the server stops
// listening and the process can close down.
let _ = tx.send(());
let _ = tx.send(true);
}

View File

@@ -1,9 +1,12 @@
#![deny(warnings)]
use std::net::SocketAddr;
use bytes::Buf;
use hyper::client::HttpConnector;
use hyper::service::{make_service_fn, service_fn};
use hyper::{header, Body, Client, Method, Request, Response, Server, StatusCode};
use hyper::server::conn::Http;
use hyper::service::service_fn;
use hyper::{header, Body, Method, Request, Response, StatusCode};
use tokio::net::{TcpListener, TcpStream};
type GenericError = Box<dyn std::error::Error + Send + Sync>;
type Result<T> = std::result::Result<T, GenericError>;
@@ -14,7 +17,7 @@ static NOTFOUND: &[u8] = b"Not Found";
static POST_DATA: &str = r#"{"original": "data"}"#;
static URL: &str = "http://127.0.0.1:1337/json_api";
async fn client_request_response(client: &Client<HttpConnector>) -> Result<Response<Body>> {
async fn client_request_response() -> Result<Response<Body>> {
let req = Request::builder()
.method(Method::POST)
.uri(URL)
@@ -22,7 +25,19 @@ async fn client_request_response(client: &Client<HttpConnector>) -> Result<Respo
.body(POST_DATA.into())
.unwrap();
let web_res = client.request(req).await?;
let host = req.uri().host().expect("uri has no host");
let port = req.uri().port_u16().expect("uri has no port");
let stream = TcpStream::connect(format!("{}:{}", host, port)).await?;
let (mut sender, conn) = hyper::client::conn::handshake(stream).await?;
tokio::task::spawn(async move {
if let Err(err) = conn.await {
println!("Connection error: {:?}", err);
}
});
let web_res = sender.send_request(req).await?;
let res_body = web_res.into_body();
@@ -60,13 +75,10 @@ async fn api_get_response() -> Result<Response<Body>> {
Ok(res)
}
async fn response_examples(
req: Request<Body>,
client: Client<HttpConnector>,
) -> Result<Response<Body>> {
async fn response_examples(req: Request<Body>) -> Result<Response<Body>> {
match (req.method(), req.uri().path()) {
(&Method::GET, "/") | (&Method::GET, "/index.html") => Ok(Response::new(INDEX.into())),
(&Method::GET, "/test.html") => client_request_response(&client).await,
(&Method::GET, "/test.html") => client_request_response().await,
(&Method::POST, "/json_api") => api_post_response(req).await,
(&Method::GET, "/json_api") => api_get_response().await,
_ => {
@@ -83,27 +95,19 @@ async fn response_examples(
async fn main() -> Result<()> {
pretty_env_logger::init();
let addr = "127.0.0.1:1337".parse().unwrap();
// Share a `Client` with all `Service`s
let client = Client::new();
let new_service = make_service_fn(move |_| {
// Move a clone of `client` into the `service_fn`.
let client = client.clone();
async {
Ok::<_, GenericError>(service_fn(move |req| {
// Clone again to ensure that client outlives this closure.
response_examples(req, client.to_owned())
}))
}
});
let server = Server::bind(&addr).serve(new_service);
let addr: SocketAddr = "127.0.0.1:1337".parse().unwrap();
let listener = TcpListener::bind(&addr).await?;
println!("Listening on http://{}", addr);
loop {
let (stream, _) = listener.accept().await?;
server.await?;
tokio::task::spawn(async move {
let service = service_fn(move |req| response_examples(req));
Ok(())
if let Err(err) = Http::new().serve_connection(stream, service).await {
println!("Failed to serve connection: {:?}", err);
}
});
}
}

View File

@@ -10,8 +10,6 @@ use http_body::{Body as HttpBody, SizeHint};
use super::DecodedLength;
use crate::common::Future;
#[cfg(all(feature = "client", any(feature = "http1", feature = "http2")))]
use crate::common::Never;
use crate::common::{task, watch, Pin, Poll};
#[cfg(all(feature = "http2", any(feature = "client", feature = "server")))]
use crate::proto::h2::ping;
@@ -29,9 +27,6 @@ type TrailersSender = oneshot::Sender<HeaderMap>;
#[must_use = "streams do nothing unless polled"]
pub struct Body {
kind: Kind,
/// Keep the extra bits in an `Option<Box<Extra>>`, so that
/// Body stays small in the common case (no extras needed).
extra: Option<Box<Extra>>,
}
enum Kind {
@@ -52,34 +47,6 @@ enum Kind {
Ffi(crate::ffi::UserBody),
}
struct Extra {
/// Allow the client to pass a future to delay the `Body` from returning
/// EOF. This allows the `Client` to try to put the idle connection
/// back into the pool before the body is "finished".
///
/// The reason for this is so that creating a new request after finishing
/// streaming the body of a response could sometimes result in creating
/// a brand new connection, since the pool didn't know about the idle
/// connection yet.
delayed_eof: Option<DelayEof>,
}
#[cfg(all(feature = "client", any(feature = "http1", feature = "http2")))]
type DelayEofUntil = oneshot::Receiver<Never>;
enum DelayEof {
/// Initial state, stream hasn't seen EOF yet.
#[cfg(any(feature = "http1", feature = "http2"))]
#[cfg(feature = "client")]
NotEof(DelayEofUntil),
/// Transitions to this state once we've seen `poll` try to
/// return EOF (`None`). This future is then polled, and
/// when it completes, the Body finally returns EOF (`None`).
#[cfg(any(feature = "http1", feature = "http2"))]
#[cfg(feature = "client")]
Eof(DelayEofUntil),
}
/// A sender half created through [`Body::channel()`].
///
/// Useful when wanting to stream chunks from another thread.
@@ -153,7 +120,7 @@ impl Body {
}
fn new(kind: Kind) -> Body {
Body { kind, extra: None }
Body { kind }
}
#[cfg(all(feature = "http2", any(feature = "client", feature = "server")))]
@@ -176,62 +143,6 @@ impl Body {
body
}
#[cfg(any(feature = "http1", feature = "http2"))]
#[cfg(feature = "client")]
pub(crate) fn delayed_eof(&mut self, fut: DelayEofUntil) {
self.extra_mut().delayed_eof = Some(DelayEof::NotEof(fut));
}
fn take_delayed_eof(&mut self) -> Option<DelayEof> {
self.extra
.as_mut()
.and_then(|extra| extra.delayed_eof.take())
}
#[cfg(any(feature = "http1", feature = "http2"))]
fn extra_mut(&mut self) -> &mut Extra {
self.extra
.get_or_insert_with(|| Box::new(Extra { delayed_eof: None }))
}
fn poll_eof(&mut self, cx: &mut task::Context<'_>) -> Poll<Option<crate::Result<Bytes>>> {
match self.take_delayed_eof() {
#[cfg(any(feature = "http1", feature = "http2"))]
#[cfg(feature = "client")]
Some(DelayEof::NotEof(mut delay)) => match self.poll_inner(cx) {
ok @ Poll::Ready(Some(Ok(..))) | ok @ Poll::Pending => {
self.extra_mut().delayed_eof = Some(DelayEof::NotEof(delay));
ok
}
Poll::Ready(None) => match Pin::new(&mut delay).poll(cx) {
Poll::Ready(Ok(never)) => match never {},
Poll::Pending => {
self.extra_mut().delayed_eof = Some(DelayEof::Eof(delay));
Poll::Pending
}
Poll::Ready(Err(_done)) => Poll::Ready(None),
},
Poll::Ready(Some(Err(e))) => Poll::Ready(Some(Err(e))),
},
#[cfg(any(feature = "http1", feature = "http2"))]
#[cfg(feature = "client")]
Some(DelayEof::Eof(mut delay)) => match Pin::new(&mut delay).poll(cx) {
Poll::Ready(Ok(never)) => match never {},
Poll::Pending => {
self.extra_mut().delayed_eof = Some(DelayEof::Eof(delay));
Poll::Pending
}
Poll::Ready(Err(_done)) => Poll::Ready(None),
},
#[cfg(any(
not(any(feature = "http1", feature = "http2")),
not(feature = "client")
))]
Some(delay_eof) => match delay_eof {},
None => self.poll_inner(cx),
}
}
#[cfg(feature = "ffi")]
pub(crate) fn as_ffi_mut(&mut self) -> &mut crate::ffi::UserBody {
match self.kind {
@@ -313,7 +224,7 @@ impl HttpBody for Body {
mut self: Pin<&mut Self>,
cx: &mut task::Context<'_>,
) -> Poll<Option<Result<Self::Data, Self::Error>>> {
self.poll_eof(cx)
self.poll_inner(cx)
}
fn poll_trailers(
@@ -608,6 +519,7 @@ mod tests {
);
}
#[cfg(not(miri))]
#[tokio::test]
async fn channel_abort() {
let (tx, mut rx) = Body::channel();
@@ -618,6 +530,7 @@ mod tests {
assert!(err.is_body_write_aborted(), "{:?}", err);
}
#[cfg(not(miri))]
#[tokio::test]
async fn channel_abort_when_buffer_is_full() {
let (mut tx, mut rx) = Body::channel();
@@ -644,6 +557,7 @@ mod tests {
assert_eq!(chunk2, "chunk 2");
}
#[cfg(not(miri))]
#[tokio::test]
async fn channel_empty() {
let (_, mut rx) = Body::channel();

View File

@@ -17,17 +17,11 @@ use super::HttpBody;
/// # Example
///
/// ```
/// # #[cfg(all(feature = "client", feature = "tcp", any(feature = "http1", feature = "http2")))]
/// # async fn doc() -> hyper::Result<()> {
/// use hyper::{body::HttpBody};
///
/// # let request = hyper::Request::builder()
/// # .method(hyper::Method::POST)
/// # .uri("http://httpbin.org/post")
/// # .header("content-type", "application/json")
/// # .body(hyper::Body::from(r#"{"library":"hyper"}"#)).unwrap();
/// # let client = hyper::Client::new();
/// let response = client.request(request).await?;
/// # use hyper::{Body, Response};
/// # use hyper::body::HttpBody;
/// #
/// let response = Response::new(Body::from("response body"));
///
/// const MAX_ALLOWED_RESPONSE_SIZE: u64 = 1024;
///

File diff suppressed because it is too large Load Diff

View File

@@ -22,7 +22,7 @@ use super::super::dispatch;
/// The sender side of an established connection.
pub struct SendRequest<B> {
dispatch: dispatch::Sender<Request<B>, Response<Body>>,
dispatch: dispatch::UnboundedSender<Request<B>, Response<Body>>,
}
/// A future that processes all HTTP state for the IO object.
@@ -66,8 +66,12 @@ impl<B> SendRequest<B> {
/// Polls to determine whether this sender can be used yet for a request.
///
/// If the associated connection is closed, this returns an Error.
pub fn poll_ready(&mut self, cx: &mut task::Context<'_>) -> Poll<crate::Result<()>> {
self.dispatch.poll_ready(cx)
pub fn poll_ready(&mut self, _cx: &mut task::Context<'_>) -> Poll<crate::Result<()>> {
if self.is_closed() {
Poll::Ready(Err(crate::Error::new_closed()))
} else {
Poll::Ready(Ok(()))
}
}
/*
@@ -83,11 +87,11 @@ impl<B> SendRequest<B> {
pub(super) fn is_ready(&self) -> bool {
self.dispatch.is_ready()
}
*/
pub(super) fn is_closed(&self) -> bool {
self.dispatch.is_closed()
}
*/
}
impl<B> SendRequest<B>
@@ -423,7 +427,7 @@ impl Builder {
proto::h2::client::handshake(io, rx, &opts.h2_builder, opts.exec)
.await?;
Ok((
SendRequest { dispatch: tx },
SendRequest { dispatch: tx.unbound() },
Connection { inner: (PhantomData, h2) },
))
}

View File

@@ -63,7 +63,7 @@ use std::sync::Arc;
use std::time::Duration;
use bytes::Bytes;
use futures_util::future::{self, Either, FutureExt as _};
use futures_util::future;
use httparse::ParserConfig;
use pin_project_lite::pin_project;
use tokio::io::{AsyncRead, AsyncWrite};
@@ -214,16 +214,6 @@ pub struct Parts<T> {
_inner: (),
}
// ========== internal client api
// A `SendRequest` that can be cloned to send HTTP2 requests.
// private for now, probably not a great idea of a type...
#[must_use = "futures do nothing unless polled"]
#[cfg(feature = "http2")]
pub(super) struct Http2SendRequest<B> {
dispatch: dispatch::UnboundedSender<Request<B>, Response<Body>>,
}
// ===== impl SendRequest
impl<B> SendRequest<B> {
@@ -233,30 +223,6 @@ impl<B> SendRequest<B> {
pub fn poll_ready(&mut self, cx: &mut task::Context<'_>) -> Poll<crate::Result<()>> {
self.dispatch.poll_ready(cx)
}
pub(super) async fn when_ready(self) -> crate::Result<Self> {
let mut me = Some(self);
future::poll_fn(move |cx| {
ready!(me.as_mut().unwrap().poll_ready(cx))?;
Poll::Ready(Ok(me.take().unwrap()))
})
.await
}
pub(super) fn is_ready(&self) -> bool {
self.dispatch.is_ready()
}
pub(super) fn is_closed(&self) -> bool {
self.dispatch.is_closed()
}
#[cfg(feature = "http2")]
pub(super) fn into_http2(self) -> Http2SendRequest<B> {
Http2SendRequest {
dispatch: self.dispatch.unbound(),
}
}
}
impl<B> SendRequest<B>
@@ -316,32 +282,6 @@ where
ResponseFuture { inner }
}
pub(super) fn send_request_retryable(
&mut self,
req: Request<B>,
) -> impl Future<Output = Result<Response<Body>, (crate::Error, Option<Request<B>>)>> + Unpin
where
B: Send,
{
match self.dispatch.try_send(req) {
Ok(rx) => {
Either::Left(rx.then(move |res| {
match res {
Ok(Ok(res)) => future::ok(res),
Ok(Err(err)) => future::err(err),
// this is definite bug if it happens, but it shouldn't happen!
Err(_) => panic!("dispatch dropped without returning error"),
}
}))
}
Err(req) => {
debug!("connection was not ready");
let err = crate::Error::new_canceled().with("connection was not ready");
Either::Right(future::err((err, Some(req))))
}
}
}
}
impl<B> Service<Request<B>> for SendRequest<B>
@@ -367,67 +307,6 @@ impl<B> fmt::Debug for SendRequest<B> {
}
}
// ===== impl Http2SendRequest
#[cfg(feature = "http2")]
impl<B> Http2SendRequest<B> {
pub(super) fn is_ready(&self) -> bool {
self.dispatch.is_ready()
}
pub(super) fn is_closed(&self) -> bool {
self.dispatch.is_closed()
}
}
#[cfg(feature = "http2")]
impl<B> Http2SendRequest<B>
where
B: HttpBody + 'static,
{
pub(super) fn send_request_retryable(
&mut self,
req: Request<B>,
) -> impl Future<Output = Result<Response<Body>, (crate::Error, Option<Request<B>>)>>
where
B: Send,
{
match self.dispatch.try_send(req) {
Ok(rx) => {
Either::Left(rx.then(move |res| {
match res {
Ok(Ok(res)) => future::ok(res),
Ok(Err(err)) => future::err(err),
// this is definite bug if it happens, but it shouldn't happen!
Err(_) => panic!("dispatch dropped without returning error"),
}
}))
}
Err(req) => {
debug!("connection was not ready");
let err = crate::Error::new_canceled().with("connection was not ready");
Either::Right(future::err((err, Some(req))))
}
}
}
}
#[cfg(feature = "http2")]
impl<B> fmt::Debug for Http2SendRequest<B> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Http2SendRequest").finish()
}
}
#[cfg(feature = "http2")]
impl<B> Clone for Http2SendRequest<B> {
fn clone(&self) -> Self {
Http2SendRequest {
dispatch: self.dispatch.clone(),
}
}
}
// ===== impl Connection
impl<T, B> Connection<T, B>

View File

@@ -1,425 +0,0 @@
//! DNS Resolution used by the `HttpConnector`.
//!
//! This module contains:
//!
//! - A [`GaiResolver`](GaiResolver) that is the default resolver for the
//! `HttpConnector`.
//! - The `Name` type used as an argument to custom resolvers.
//!
//! # Resolvers are `Service`s
//!
//! A resolver is just a
//! `Service<Name, Response = impl Iterator<Item = SocketAddr>>`.
//!
//! A simple resolver that ignores the name and always returns a specific
//! address:
//!
//! ```rust,ignore
//! use std::{convert::Infallible, iter, net::SocketAddr};
//!
//! let resolver = tower::service_fn(|_name| async {
//! Ok::<_, Infallible>(iter::once(SocketAddr::from(([127, 0, 0, 1], 8080))))
//! });
//! ```
use std::error::Error;
use std::future::Future;
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs};
use std::pin::Pin;
use std::str::FromStr;
use std::task::{self, Poll};
use std::{fmt, io, vec};
use tokio::task::JoinHandle;
use tower_service::Service;
use tracing::debug;
pub(super) use self::sealed::Resolve;
/// A domain name to resolve into IP addresses.
#[derive(Clone, Hash, Eq, PartialEq)]
pub struct Name {
host: Box<str>,
}
/// A resolver using blocking `getaddrinfo` calls in a threadpool.
#[derive(Clone)]
pub struct GaiResolver {
_priv: (),
}
/// An iterator of IP addresses returned from `getaddrinfo`.
pub struct GaiAddrs {
inner: SocketAddrs,
}
/// A future to resolve a name returned by `GaiResolver`.
pub struct GaiFuture {
inner: JoinHandle<Result<SocketAddrs, io::Error>>,
}
impl Name {
pub(super) fn new(host: Box<str>) -> Name {
Name { host }
}
/// View the hostname as a string slice.
pub fn as_str(&self) -> &str {
&self.host
}
}
impl fmt::Debug for Name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.host, f)
}
}
impl fmt::Display for Name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.host, f)
}
}
impl FromStr for Name {
type Err = InvalidNameError;
fn from_str(host: &str) -> Result<Self, Self::Err> {
// Possibly add validation later
Ok(Name::new(host.into()))
}
}
/// Error indicating a given string was not a valid domain name.
#[derive(Debug)]
pub struct InvalidNameError(());
impl fmt::Display for InvalidNameError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Not a valid domain name")
}
}
impl Error for InvalidNameError {}
impl GaiResolver {
/// Construct a new `GaiResolver`.
pub fn new() -> Self {
GaiResolver { _priv: () }
}
}
impl Service<Name> for GaiResolver {
type Response = GaiAddrs;
type Error = io::Error;
type Future = GaiFuture;
fn poll_ready(&mut self, _cx: &mut task::Context<'_>) -> Poll<Result<(), io::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, name: Name) -> Self::Future {
let blocking = tokio::task::spawn_blocking(move || {
debug!("resolving host={:?}", name.host);
(&*name.host, 0)
.to_socket_addrs()
.map(|i| SocketAddrs { iter: i })
});
GaiFuture { inner: blocking }
}
}
impl fmt::Debug for GaiResolver {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.pad("GaiResolver")
}
}
impl Future for GaiFuture {
type Output = Result<GaiAddrs, io::Error>;
fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
Pin::new(&mut self.inner).poll(cx).map(|res| match res {
Ok(Ok(addrs)) => Ok(GaiAddrs { inner: addrs }),
Ok(Err(err)) => Err(err),
Err(join_err) => {
if join_err.is_cancelled() {
Err(io::Error::new(io::ErrorKind::Interrupted, join_err))
} else {
panic!("gai background task failed: {:?}", join_err)
}
}
})
}
}
impl fmt::Debug for GaiFuture {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.pad("GaiFuture")
}
}
impl Drop for GaiFuture {
fn drop(&mut self) {
self.inner.abort();
}
}
impl Iterator for GaiAddrs {
type Item = SocketAddr;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
}
impl fmt::Debug for GaiAddrs {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.pad("GaiAddrs")
}
}
pub(super) struct SocketAddrs {
iter: vec::IntoIter<SocketAddr>,
}
impl SocketAddrs {
pub(super) fn new(addrs: Vec<SocketAddr>) -> Self {
SocketAddrs {
iter: addrs.into_iter(),
}
}
pub(super) fn try_parse(host: &str, port: u16) -> Option<SocketAddrs> {
if let Ok(addr) = host.parse::<Ipv4Addr>() {
let addr = SocketAddrV4::new(addr, port);
return Some(SocketAddrs {
iter: vec![SocketAddr::V4(addr)].into_iter(),
});
}
if let Ok(addr) = host.parse::<Ipv6Addr>() {
let addr = SocketAddrV6::new(addr, port, 0, 0);
return Some(SocketAddrs {
iter: vec![SocketAddr::V6(addr)].into_iter(),
});
}
None
}
#[inline]
fn filter(self, predicate: impl FnMut(&SocketAddr) -> bool) -> SocketAddrs {
SocketAddrs::new(self.iter.filter(predicate).collect())
}
pub(super) fn split_by_preference(
self,
local_addr_ipv4: Option<Ipv4Addr>,
local_addr_ipv6: Option<Ipv6Addr>,
) -> (SocketAddrs, SocketAddrs) {
match (local_addr_ipv4, local_addr_ipv6) {
(Some(_), None) => (self.filter(SocketAddr::is_ipv4), SocketAddrs::new(vec![])),
(None, Some(_)) => (self.filter(SocketAddr::is_ipv6), SocketAddrs::new(vec![])),
_ => {
let preferring_v6 = self
.iter
.as_slice()
.first()
.map(SocketAddr::is_ipv6)
.unwrap_or(false);
let (preferred, fallback) = self
.iter
.partition::<Vec<_>, _>(|addr| addr.is_ipv6() == preferring_v6);
(SocketAddrs::new(preferred), SocketAddrs::new(fallback))
}
}
}
pub(super) fn is_empty(&self) -> bool {
self.iter.as_slice().is_empty()
}
pub(super) fn len(&self) -> usize {
self.iter.as_slice().len()
}
}
impl Iterator for SocketAddrs {
type Item = SocketAddr;
#[inline]
fn next(&mut self) -> Option<SocketAddr> {
self.iter.next()
}
}
/*
/// A resolver using `getaddrinfo` calls via the `tokio_executor::threadpool::blocking` API.
///
/// Unlike the `GaiResolver` this will not spawn dedicated threads, but only works when running on the
/// multi-threaded Tokio runtime.
#[cfg(feature = "runtime")]
#[derive(Clone, Debug)]
pub struct TokioThreadpoolGaiResolver(());
/// The future returned by `TokioThreadpoolGaiResolver`.
#[cfg(feature = "runtime")]
#[derive(Debug)]
pub struct TokioThreadpoolGaiFuture {
name: Name,
}
#[cfg(feature = "runtime")]
impl TokioThreadpoolGaiResolver {
/// Creates a new DNS resolver that will use tokio threadpool's blocking
/// feature.
///
/// **Requires** its futures to be run on the threadpool runtime.
pub fn new() -> Self {
TokioThreadpoolGaiResolver(())
}
}
#[cfg(feature = "runtime")]
impl Service<Name> for TokioThreadpoolGaiResolver {
type Response = GaiAddrs;
type Error = io::Error;
type Future = TokioThreadpoolGaiFuture;
fn poll_ready(&mut self, _cx: &mut task::Context<'_>) -> Poll<Result<(), io::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, name: Name) -> Self::Future {
TokioThreadpoolGaiFuture { name }
}
}
#[cfg(feature = "runtime")]
impl Future for TokioThreadpoolGaiFuture {
type Output = Result<GaiAddrs, io::Error>;
fn poll(self: Pin<&mut Self>, _cx: &mut task::Context<'_>) -> Poll<Self::Output> {
match ready!(tokio_executor::threadpool::blocking(|| (
self.name.as_str(),
0
)
.to_socket_addrs()))
{
Ok(Ok(iter)) => Poll::Ready(Ok(GaiAddrs {
inner: IpAddrs { iter },
})),
Ok(Err(e)) => Poll::Ready(Err(e)),
// a BlockingError, meaning not on a tokio_executor::threadpool :(
Err(e) => Poll::Ready(Err(io::Error::new(io::ErrorKind::Other, e))),
}
}
}
*/
mod sealed {
use super::{SocketAddr, Name};
use crate::common::{task, Future, Poll};
use tower_service::Service;
// "Trait alias" for `Service<Name, Response = Addrs>`
pub trait Resolve {
type Addrs: Iterator<Item = SocketAddr>;
type Error: Into<Box<dyn std::error::Error + Send + Sync>>;
type Future: Future<Output = Result<Self::Addrs, Self::Error>>;
fn poll_ready(&mut self, cx: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>>;
fn resolve(&mut self, name: Name) -> Self::Future;
}
impl<S> Resolve for S
where
S: Service<Name>,
S::Response: Iterator<Item = SocketAddr>,
S::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
{
type Addrs = S::Response;
type Error = S::Error;
type Future = S::Future;
fn poll_ready(&mut self, cx: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>> {
Service::poll_ready(self, cx)
}
fn resolve(&mut self, name: Name) -> Self::Future {
Service::call(self, name)
}
}
}
pub(super) async fn resolve<R>(resolver: &mut R, name: Name) -> Result<R::Addrs, R::Error>
where
R: Resolve,
{
futures_util::future::poll_fn(|cx| resolver.poll_ready(cx)).await?;
resolver.resolve(name).await
}
#[cfg(test)]
mod tests {
use super::*;
use std::net::{Ipv4Addr, Ipv6Addr};
#[test]
fn test_ip_addrs_split_by_preference() {
let ip_v4 = Ipv4Addr::new(127, 0, 0, 1);
let ip_v6 = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1);
let v4_addr = (ip_v4, 80).into();
let v6_addr = (ip_v6, 80).into();
let (mut preferred, mut fallback) = SocketAddrs {
iter: vec![v4_addr, v6_addr].into_iter(),
}
.split_by_preference(None, None);
assert!(preferred.next().unwrap().is_ipv4());
assert!(fallback.next().unwrap().is_ipv6());
let (mut preferred, mut fallback) = SocketAddrs {
iter: vec![v6_addr, v4_addr].into_iter(),
}
.split_by_preference(None, None);
assert!(preferred.next().unwrap().is_ipv6());
assert!(fallback.next().unwrap().is_ipv4());
let (mut preferred, mut fallback) = SocketAddrs {
iter: vec![v4_addr, v6_addr].into_iter(),
}
.split_by_preference(Some(ip_v4), Some(ip_v6));
assert!(preferred.next().unwrap().is_ipv4());
assert!(fallback.next().unwrap().is_ipv6());
let (mut preferred, mut fallback) = SocketAddrs {
iter: vec![v6_addr, v4_addr].into_iter(),
}
.split_by_preference(Some(ip_v4), Some(ip_v6));
assert!(preferred.next().unwrap().is_ipv6());
assert!(fallback.next().unwrap().is_ipv4());
let (mut preferred, fallback) = SocketAddrs {
iter: vec![v4_addr, v6_addr].into_iter(),
}
.split_by_preference(Some(ip_v4), None);
assert!(preferred.next().unwrap().is_ipv4());
assert!(fallback.is_empty());
let (mut preferred, fallback) = SocketAddrs {
iter: vec![v4_addr, v6_addr].into_iter(),
}
.split_by_preference(None, Some(ip_v6));
assert!(preferred.next().unwrap().is_ipv6());
assert!(fallback.is_empty());
}
#[test]
fn test_name_from_str() {
const DOMAIN: &str = "test.example.com";
let name = Name::from_str(DOMAIN).expect("Should be a valid domain");
assert_eq!(name.as_str(), DOMAIN);
assert_eq!(name.to_string(), DOMAIN);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,412 +0,0 @@
//! Connectors used by the `Client`.
//!
//! This module contains:
//!
//! - A default [`HttpConnector`][] that does DNS resolution and establishes
//! connections over TCP.
//! - Types to build custom connectors.
//!
//! # Connectors
//!
//! A "connector" is a [`Service`][] that takes a [`Uri`][] destination, and
//! its `Response` is some type implementing [`AsyncRead`][], [`AsyncWrite`][],
//! and [`Connection`][].
//!
//! ## Custom Connectors
//!
//! A simple connector that ignores the `Uri` destination and always returns
//! a TCP connection to the same address could be written like this:
//!
//! ```rust,ignore
//! let connector = tower::service_fn(|_dst| async {
//! tokio::net::TcpStream::connect("127.0.0.1:1337")
//! })
//! ```
//!
//! Or, fully written out:
//!
//! ```
//! # #[cfg(feature = "runtime")]
//! # mod rt {
//! use std::{future::Future, net::SocketAddr, pin::Pin, task::{self, Poll}};
//! use hyper::{service::Service, Uri};
//! use tokio::net::TcpStream;
//!
//! #[derive(Clone)]
//! struct LocalConnector;
//!
//! impl Service<Uri> for LocalConnector {
//! type Response = TcpStream;
//! type Error = std::io::Error;
//! // We can't "name" an `async` generated future.
//! type Future = Pin<Box<
//! dyn Future<Output = Result<Self::Response, Self::Error>> + Send
//! >>;
//!
//! fn poll_ready(&mut self, _: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>> {
//! // This connector is always ready, but others might not be.
//! Poll::Ready(Ok(()))
//! }
//!
//! fn call(&mut self, _: Uri) -> Self::Future {
//! Box::pin(TcpStream::connect(SocketAddr::from(([127, 0, 0, 1], 1337))))
//! }
//! }
//! # }
//! ```
//!
//! It's worth noting that for `TcpStream`s, the [`HttpConnector`][] is a
//! better starting place to extend from.
//!
//! Using either of the above connector examples, it can be used with the
//! `Client` like this:
//!
//! ```
//! # #[cfg(feature = "runtime")]
//! # fn rt () {
//! # let connector = hyper::client::HttpConnector::new();
//! // let connector = ...
//!
//! let client = hyper::Client::builder()
//! .build::<_, hyper::Body>(connector);
//! # }
//! ```
//!
//!
//! [`HttpConnector`]: HttpConnector
//! [`Service`]: crate::service::Service
//! [`Uri`]: ::http::Uri
//! [`AsyncRead`]: tokio::io::AsyncRead
//! [`AsyncWrite`]: tokio::io::AsyncWrite
//! [`Connection`]: Connection
use std::fmt;
use ::http::Extensions;
cfg_feature! {
#![feature = "tcp"]
pub use self::http::{HttpConnector, HttpInfo};
pub mod dns;
mod http;
}
cfg_feature! {
#![any(feature = "http1", feature = "http2")]
pub use self::sealed::Connect;
}
/// Describes a type returned by a connector.
pub trait Connection {
/// Return metadata describing the connection.
fn connected(&self) -> Connected;
}
/// Extra information about the connected transport.
///
/// This can be used to inform recipients about things like if ALPN
/// was used, or if connected to an HTTP proxy.
#[derive(Debug)]
pub struct Connected {
pub(super) alpn: Alpn,
pub(super) is_proxied: bool,
pub(super) extra: Option<Extra>,
}
pub(super) struct Extra(Box<dyn ExtraInner>);
#[derive(Clone, Copy, Debug, PartialEq)]
pub(super) enum Alpn {
H2,
None,
}
impl Connected {
/// Create new `Connected` type with empty metadata.
pub fn new() -> Connected {
Connected {
alpn: Alpn::None,
is_proxied: false,
extra: None,
}
}
/// Set whether the connected transport is to an HTTP proxy.
///
/// This setting will affect if HTTP/1 requests written on the transport
/// will have the request-target in absolute-form or origin-form:
///
/// - When `proxy(false)`:
///
/// ```http
/// GET /guide HTTP/1.1
/// ```
///
/// - When `proxy(true)`:
///
/// ```http
/// GET http://hyper.rs/guide HTTP/1.1
/// ```
///
/// Default is `false`.
pub fn proxy(mut self, is_proxied: bool) -> Connected {
self.is_proxied = is_proxied;
self
}
/// Determines if the connected transport is to an HTTP proxy.
pub fn is_proxied(&self) -> bool {
self.is_proxied
}
/// Set extra connection information to be set in the extensions of every `Response`.
pub fn extra<T: Clone + Send + Sync + 'static>(mut self, extra: T) -> Connected {
if let Some(prev) = self.extra {
self.extra = Some(Extra(Box::new(ExtraChain(prev.0, extra))));
} else {
self.extra = Some(Extra(Box::new(ExtraEnvelope(extra))));
}
self
}
/// Copies the extra connection information into an `Extensions` map.
pub fn get_extras(&self, extensions: &mut Extensions) {
if let Some(extra) = &self.extra {
extra.set(extensions);
}
}
/// Set that the connected transport negotiated HTTP/2 as its next protocol.
pub fn negotiated_h2(mut self) -> Connected {
self.alpn = Alpn::H2;
self
}
/// Determines if the connected transport negotiated HTTP/2 as its next protocol.
pub fn is_negotiated_h2(&self) -> bool {
self.alpn == Alpn::H2
}
// Don't public expose that `Connected` is `Clone`, unsure if we want to
// keep that contract...
#[cfg(feature = "http2")]
pub(super) fn clone(&self) -> Connected {
Connected {
alpn: self.alpn.clone(),
is_proxied: self.is_proxied,
extra: self.extra.clone(),
}
}
}
// ===== impl Extra =====
impl Extra {
pub(super) fn set(&self, res: &mut Extensions) {
self.0.set(res);
}
}
impl Clone for Extra {
fn clone(&self) -> Extra {
Extra(self.0.clone_box())
}
}
impl fmt::Debug for Extra {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Extra").finish()
}
}
trait ExtraInner: Send + Sync {
fn clone_box(&self) -> Box<dyn ExtraInner>;
fn set(&self, res: &mut Extensions);
}
// This indirection allows the `Connected` to have a type-erased "extra" value,
// while that type still knows its inner extra type. This allows the correct
// TypeId to be used when inserting into `res.extensions_mut()`.
#[derive(Clone)]
struct ExtraEnvelope<T>(T);
impl<T> ExtraInner for ExtraEnvelope<T>
where
T: Clone + Send + Sync + 'static,
{
fn clone_box(&self) -> Box<dyn ExtraInner> {
Box::new(self.clone())
}
fn set(&self, res: &mut Extensions) {
res.insert(self.0.clone());
}
}
struct ExtraChain<T>(Box<dyn ExtraInner>, T);
impl<T: Clone> Clone for ExtraChain<T> {
fn clone(&self) -> Self {
ExtraChain(self.0.clone_box(), self.1.clone())
}
}
impl<T> ExtraInner for ExtraChain<T>
where
T: Clone + Send + Sync + 'static,
{
fn clone_box(&self) -> Box<dyn ExtraInner> {
Box::new(self.clone())
}
fn set(&self, res: &mut Extensions) {
self.0.set(res);
res.insert(self.1.clone());
}
}
#[cfg(any(feature = "http1", feature = "http2"))]
pub(super) mod sealed {
use std::error::Error as StdError;
use ::http::Uri;
use tokio::io::{AsyncRead, AsyncWrite};
use super::Connection;
use crate::common::{Future, Unpin};
/// Connect to a destination, returning an IO transport.
///
/// A connector receives a [`Uri`](::http::Uri) and returns a `Future` of the
/// ready connection.
///
/// # Trait Alias
///
/// This is really just an *alias* for the `tower::Service` trait, with
/// additional bounds set for convenience *inside* hyper. You don't actually
/// implement this trait, but `tower::Service<Uri>` instead.
// The `Sized` bound is to prevent creating `dyn Connect`, since they cannot
// fit the `Connect` bounds because of the blanket impl for `Service`.
pub trait Connect: Sealed + Sized {
#[doc(hidden)]
type _Svc: ConnectSvc;
#[doc(hidden)]
fn connect(self, internal_only: Internal, dst: Uri) -> <Self::_Svc as ConnectSvc>::Future;
}
pub trait ConnectSvc {
type Connection: AsyncRead + AsyncWrite + Connection + Unpin + Send + 'static;
type Error: Into<Box<dyn StdError + Send + Sync>>;
type Future: Future<Output = Result<Self::Connection, Self::Error>> + Unpin + Send + 'static;
fn connect(self, internal_only: Internal, dst: Uri) -> Self::Future;
}
impl<S, T> Connect for S
where
S: tower_service::Service<Uri, Response = T> + Send + 'static,
S::Error: Into<Box<dyn StdError + Send + Sync>>,
S::Future: Unpin + Send,
T: AsyncRead + AsyncWrite + Connection + Unpin + Send + 'static,
{
type _Svc = S;
fn connect(self, _: Internal, dst: Uri) -> crate::service::Oneshot<S, Uri> {
crate::service::oneshot(self, dst)
}
}
impl<S, T> ConnectSvc for S
where
S: tower_service::Service<Uri, Response = T> + Send + 'static,
S::Error: Into<Box<dyn StdError + Send + Sync>>,
S::Future: Unpin + Send,
T: AsyncRead + AsyncWrite + Connection + Unpin + Send + 'static,
{
type Connection = T;
type Error = S::Error;
type Future = crate::service::Oneshot<S, Uri>;
fn connect(self, _: Internal, dst: Uri) -> Self::Future {
crate::service::oneshot(self, dst)
}
}
impl<S, T> Sealed for S
where
S: tower_service::Service<Uri, Response = T> + Send,
S::Error: Into<Box<dyn StdError + Send + Sync>>,
S::Future: Unpin + Send,
T: AsyncRead + AsyncWrite + Connection + Unpin + Send + 'static,
{
}
pub trait Sealed {}
#[allow(missing_debug_implementations)]
pub struct Internal;
}
#[cfg(test)]
mod tests {
use super::Connected;
#[derive(Clone, Debug, PartialEq)]
struct Ex1(usize);
#[derive(Clone, Debug, PartialEq)]
struct Ex2(&'static str);
#[derive(Clone, Debug, PartialEq)]
struct Ex3(&'static str);
#[test]
fn test_connected_extra() {
let c1 = Connected::new().extra(Ex1(41));
let mut ex = ::http::Extensions::new();
assert_eq!(ex.get::<Ex1>(), None);
c1.extra.as_ref().expect("c1 extra").set(&mut ex);
assert_eq!(ex.get::<Ex1>(), Some(&Ex1(41)));
}
#[test]
fn test_connected_extra_chain() {
// If a user composes connectors and at each stage, there's "extra"
// info to attach, it shouldn't override the previous extras.
let c1 = Connected::new()
.extra(Ex1(45))
.extra(Ex2("zoom"))
.extra(Ex3("pew pew"));
let mut ex1 = ::http::Extensions::new();
assert_eq!(ex1.get::<Ex1>(), None);
assert_eq!(ex1.get::<Ex2>(), None);
assert_eq!(ex1.get::<Ex3>(), None);
c1.extra.as_ref().expect("c1 extra").set(&mut ex1);
assert_eq!(ex1.get::<Ex1>(), Some(&Ex1(45)));
assert_eq!(ex1.get::<Ex2>(), Some(&Ex2("zoom")));
assert_eq!(ex1.get::<Ex3>(), Some(&Ex3("pew pew")));
// Just like extensions, inserting the same type overrides previous type.
let c2 = Connected::new()
.extra(Ex1(33))
.extra(Ex2("hiccup"))
.extra(Ex1(99));
let mut ex2 = ::http::Extensions::new();
c2.extra.as_ref().expect("c2 extra").set(&mut ex2);
assert_eq!(ex2.get::<Ex1>(), Some(&Ex1(99)));
assert_eq!(ex2.get::<Ex2>(), Some(&Ex2("hiccup")));
}
}

View File

@@ -7,6 +7,7 @@ use tokio::sync::{mpsc, oneshot};
use crate::common::Pin;
use crate::common::{task, Poll};
#[cfg(test)]
pub(crate) type RetryPromise<T, U> = oneshot::Receiver<Result<U, (crate::Error, Option<T>)>>;
pub(crate) type Promise<T> = oneshot::Receiver<Result<T, crate::Error>>;
@@ -58,13 +59,16 @@ impl<T, U> Sender<T, U> {
.map_err(|_| crate::Error::new_closed())
}
#[cfg(test)]
pub(crate) fn is_ready(&self) -> bool {
self.giver.is_wanting()
}
/*
pub(crate) fn is_closed(&self) -> bool {
self.giver.is_canceled()
}
*/
fn can_send(&mut self) -> bool {
if self.giver.give() || !self.buffered_once {
@@ -79,6 +83,7 @@ impl<T, U> Sender<T, U> {
}
}
#[cfg(test)]
pub(crate) fn try_send(&mut self, val: T) -> Result<RetryPromise<T, U>, T> {
if !self.can_send() {
return Err(val);
@@ -112,14 +117,17 @@ impl<T, U> Sender<T, U> {
#[cfg(feature = "http2")]
impl<T, U> UnboundedSender<T, U> {
/*
pub(crate) fn is_ready(&self) -> bool {
!self.giver.is_canceled()
}
*/
pub(crate) fn is_closed(&self) -> bool {
self.giver.is_canceled()
}
#[cfg(test)]
pub(crate) fn try_send(&mut self, val: T) -> Result<RetryPromise<T, U>, T> {
let (tx, rx) = oneshot::channel();
self.inner
@@ -127,6 +135,14 @@ impl<T, U> UnboundedSender<T, U> {
.map(move |_| rx)
.map_err(|mut e| (e.0).0.take().expect("envelope not dropped").0)
}
pub(crate) fn send(&mut self, val: T) -> Result<Promise<U>, T> {
let (tx, rx) = oneshot::channel();
self.inner
.send(Envelope(Some((val, Callback::NoRetry(tx)))))
.map(move |_| rx)
.map_err(|mut e| (e.0).0.take().expect("envelope not dropped").0)
}
}
#[cfg(feature = "http2")]
@@ -198,6 +214,7 @@ impl<T, U> Drop for Envelope<T, U> {
}
pub(crate) enum Callback<T, U> {
#[allow(unused)]
Retry(oneshot::Sender<Result<U, (crate::Error, Option<T>)>>),
NoRetry(oneshot::Sender<Result<U, crate::Error>>),
}
@@ -301,6 +318,7 @@ mod tests {
}
}
#[cfg(not(miri))]
#[tokio::test]
async fn drop_receiver_sends_cancel_errors() {
let _ = pretty_env_logger::try_init();
@@ -323,6 +341,7 @@ mod tests {
}
}
#[cfg(not(miri))]
#[tokio::test]
async fn sender_checks_for_want_on_send() {
let (mut tx, mut rx) = channel::<Custom, ()>();
@@ -363,7 +382,6 @@ mod tests {
use crate::{Body, Request, Response};
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap();
let (mut tx, mut rx) = channel::<Request<Body>, Response<Body>>();
@@ -386,7 +404,6 @@ mod tests {
#[bench]
fn giver_queue_not_ready(b: &mut test::Bencher) {
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap();
let (_tx, mut rx) = channel::<i32, ()>();

View File

@@ -1,68 +1,18 @@
//! HTTP Client
//!
//! There are two levels of APIs provided for construct HTTP clients:
//!
//! - The higher-level [`Client`](Client) type.
//! - The lower-level [`conn`](conn) module.
//!
//! # Client
//!
//! The [`Client`](Client) is the main way to send HTTP requests to a server.
//! The default `Client` provides these things on top of the lower-level API:
//!
//! - A default **connector**, able to resolve hostnames and connect to
//! destinations over plain-text TCP.
//! - A **pool** of existing connections, allowing better performance when
//! making multiple requests to the same hostname.
//! - Automatic setting of the `Host` header, based on the request `Uri`.
//! - Automatic request **retries** when a pooled connection is closed by the
//! server before any bytes have been written.
//!
//! Many of these features can configured, by making use of
//! [`Client::builder`](Client::builder).
//! hyper provides HTTP over a single connection. See the [`conn`](conn) module.
//!
//! ## Example
//!
//! For a small example program simply fetching a URL, take a look at the
//! [full client example](https://github.com/hyperium/hyper/blob/master/examples/client.rs).
//!
//! ```
//! # #[cfg(all(feature = "tcp", feature = "client", any(feature = "http1", feature = "http2")))]
//! # async fn fetch_httpbin() -> hyper::Result<()> {
//! use hyper::{body::HttpBody as _, Client, Uri};
//!
//! let client = Client::new();
//!
//! // Make a GET /ip to 'http://httpbin.org'
//! let res = client.get(Uri::from_static("http://httpbin.org/ip")).await?;
//!
//! // And then, if the request gets a response...
//! println!("status: {}", res.status());
//!
//! // Concatenate the body stream into a single buffer...
//! let buf = hyper::body::to_bytes(res).await?;
//!
//! println!("body: {:?}", buf);
//! # Ok(())
//! # }
//! # fn main () {}
//! ```
#[cfg(feature = "tcp")]
pub use self::connect::HttpConnector;
pub mod connect;
#[cfg(all(test, feature = "runtime"))]
mod tests;
cfg_feature! {
#![any(feature = "http1", feature = "http2")]
pub use self::client::{Builder, Client, ResponseFuture};
mod client;
pub mod conn;
pub(super) mod dispatch;
mod pool;
pub mod service;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,89 +0,0 @@
//! Utilities used to interact with the Tower ecosystem.
//!
//! This module provides `Connect` which hook-ins into the Tower ecosystem.
use std::error::Error as StdError;
use std::future::Future;
use std::marker::PhantomData;
use tracing::debug;
use super::conn::{Builder, SendRequest};
use crate::{
body::HttpBody,
common::{task, Pin, Poll},
service::{MakeConnection, Service},
};
/// Creates a connection via `SendRequest`.
///
/// This accepts a `hyper::client::conn::Builder` and provides
/// a `MakeService` implementation to create connections from some
/// target `T`.
#[derive(Debug)]
pub struct Connect<C, B, T> {
inner: C,
builder: Builder,
_pd: PhantomData<fn(T, B)>,
}
impl<C, B, T> Connect<C, B, T> {
/// Create a new `Connect` with some inner connector `C` and a connection
/// builder.
pub fn new(inner: C, builder: Builder) -> Self {
Self {
inner,
builder,
_pd: PhantomData,
}
}
}
impl<C, B, T> Service<T> for Connect<C, B, T>
where
C: MakeConnection<T>,
C::Connection: Unpin + Send + 'static,
C::Future: Send + 'static,
C::Error: Into<Box<dyn StdError + Send + Sync>> + Send,
B: HttpBody + Unpin + Send + 'static,
B::Data: Send + Unpin,
B::Error: Into<Box<dyn StdError + Send + Sync>>,
{
type Response = SendRequest<B>;
type Error = crate::Error;
type Future =
Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send + 'static>>;
fn poll_ready(&mut self, cx: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>> {
self.inner
.poll_ready(cx)
.map_err(|e| crate::Error::new(crate::error::Kind::Connect).with(e.into()))
}
fn call(&mut self, req: T) -> Self::Future {
let builder = self.builder.clone();
let io = self.inner.make_connection(req);
let fut = async move {
match io.await {
Ok(io) => match builder.handshake(io).await {
Ok((sr, conn)) => {
builder.exec.execute(async move {
if let Err(e) = conn.await {
debug!("connection error: {:?}", e);
}
});
Ok(sr)
}
Err(e) => Err(e),
},
Err(e) => {
let err = crate::Error::new(crate::error::Kind::Connect).with(e.into());
Err(err)
}
}
};
Box::pin(fut)
}
}

View File

@@ -1,28 +1,3 @@
use std::io;
use futures_util::future;
use tokio::net::TcpStream;
use super::Client;
#[tokio::test]
async fn client_connect_uri_argument() {
let connector = tower::service_fn(|dst: http::Uri| {
assert_eq!(dst.scheme(), Some(&http::uri::Scheme::HTTP));
assert_eq!(dst.host(), Some("example.local"));
assert_eq!(dst.port(), None);
assert_eq!(dst.path(), "/", "path should be removed");
future::err::<TcpStream, _>(io::Error::new(io::ErrorKind::Other, "expect me"))
});
let client = Client::builder().build::<_, crate::Body>(connector);
let _ = client
.get("http://example.local/and/a/path".parse().unwrap())
.await
.expect_err("response should fail");
}
/*
// FIXME: re-implement tests with `async/await`
#[test]

View File

@@ -1,217 +0,0 @@
use std::mem;
use pin_project_lite::pin_project;
use tokio::sync::watch;
use super::{task, Future, Pin, Poll};
pub(crate) fn channel() -> (Signal, Watch) {
let (tx, rx) = watch::channel(());
(Signal { tx }, Watch { rx })
}
pub(crate) struct Signal {
tx: watch::Sender<()>,
}
pub(crate) struct Draining(Pin<Box<dyn Future<Output = ()> + Send + Sync>>);
#[derive(Clone)]
pub(crate) struct Watch {
rx: watch::Receiver<()>,
}
pin_project! {
#[allow(missing_debug_implementations)]
pub struct Watching<F, FN> {
#[pin]
future: F,
state: State<FN>,
watch: Pin<Box<dyn Future<Output = ()> + Send + Sync>>,
_rx: watch::Receiver<()>,
}
}
enum State<F> {
Watch(F),
Draining,
}
impl Signal {
pub(crate) fn drain(self) -> Draining {
let _ = self.tx.send(());
Draining(Box::pin(async move { self.tx.closed().await }))
}
}
impl Future for Draining {
type Output = ();
fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
Pin::new(&mut self.as_mut().0).poll(cx)
}
}
impl Watch {
pub(crate) fn watch<F, FN>(self, future: F, on_drain: FN) -> Watching<F, FN>
where
F: Future,
FN: FnOnce(Pin<&mut F>),
{
let Self { mut rx } = self;
let _rx = rx.clone();
Watching {
future,
state: State::Watch(on_drain),
watch: Box::pin(async move {
let _ = rx.changed().await;
}),
// Keep the receiver alive until the future completes, so that
// dropping it can signal that draining has completed.
_rx,
}
}
}
impl<F, FN> Future for Watching<F, FN>
where
F: Future,
FN: FnOnce(Pin<&mut F>),
{
type Output = F::Output;
fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
let mut me = self.project();
loop {
match mem::replace(me.state, State::Draining) {
State::Watch(on_drain) => {
match Pin::new(&mut me.watch).poll(cx) {
Poll::Ready(()) => {
// Drain has been triggered!
on_drain(me.future.as_mut());
}
Poll::Pending => {
*me.state = State::Watch(on_drain);
return me.future.poll(cx);
}
}
}
State::Draining => return me.future.poll(cx),
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
struct TestMe {
draining: bool,
finished: bool,
poll_cnt: usize,
}
impl Future for TestMe {
type Output = ();
fn poll(mut self: Pin<&mut Self>, _: &mut task::Context<'_>) -> Poll<Self::Output> {
self.poll_cnt += 1;
if self.finished {
Poll::Ready(())
} else {
Poll::Pending
}
}
}
#[test]
fn watch() {
let mut mock = tokio_test::task::spawn(());
mock.enter(|cx, _| {
let (tx, rx) = channel();
let fut = TestMe {
draining: false,
finished: false,
poll_cnt: 0,
};
let mut watch = rx.watch(fut, |mut fut| {
fut.draining = true;
});
assert_eq!(watch.future.poll_cnt, 0);
// First poll should poll the inner future
assert!(Pin::new(&mut watch).poll(cx).is_pending());
assert_eq!(watch.future.poll_cnt, 1);
// Second poll should poll the inner future again
assert!(Pin::new(&mut watch).poll(cx).is_pending());
assert_eq!(watch.future.poll_cnt, 2);
let mut draining = tx.drain();
// Drain signaled, but needs another poll to be noticed.
assert!(!watch.future.draining);
assert_eq!(watch.future.poll_cnt, 2);
// Now, poll after drain has been signaled.
assert!(Pin::new(&mut watch).poll(cx).is_pending());
assert_eq!(watch.future.poll_cnt, 3);
assert!(watch.future.draining);
// Draining is not ready until watcher completes
assert!(Pin::new(&mut draining).poll(cx).is_pending());
// Finishing up the watch future
watch.future.finished = true;
assert!(Pin::new(&mut watch).poll(cx).is_ready());
assert_eq!(watch.future.poll_cnt, 4);
drop(watch);
assert!(Pin::new(&mut draining).poll(cx).is_ready());
})
}
#[test]
fn watch_clones() {
let mut mock = tokio_test::task::spawn(());
mock.enter(|cx, _| {
let (tx, rx) = channel();
let fut1 = TestMe {
draining: false,
finished: false,
poll_cnt: 0,
};
let fut2 = TestMe {
draining: false,
finished: false,
poll_cnt: 0,
};
let watch1 = rx.clone().watch(fut1, |mut fut| {
fut.draining = true;
});
let watch2 = rx.watch(fut2, |mut fut| {
fut.draining = true;
});
let mut draining = tx.drain();
// Still 2 outstanding watchers
assert!(Pin::new(&mut draining).poll(cx).is_pending());
// drop 1 for whatever reason
drop(watch1);
// Still not ready, 1 other watcher still pending
assert!(Pin::new(&mut draining).poll(cx).is_pending());
drop(watch2);
// Now all watchers are gone, draining is complete
assert!(Pin::new(&mut draining).poll(cx).is_ready());
});
}
}

View File

@@ -3,28 +3,17 @@ use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
#[cfg(all(feature = "server", any(feature = "http1", feature = "http2")))]
use crate::body::Body;
#[cfg(feature = "server")]
use crate::body::HttpBody;
#[cfg(all(feature = "http2", feature = "server"))]
use crate::proto::h2::server::H2Stream;
use crate::rt::Executor;
#[cfg(all(feature = "server", any(feature = "http1", feature = "http2")))]
use crate::server::server::{new_svc::NewSvcTask, Watcher};
#[cfg(all(feature = "server", any(feature = "http1", feature = "http2")))]
use crate::service::HttpService;
#[cfg(feature = "server")]
pub trait ConnStreamExec<F, B: HttpBody>: Clone {
fn execute_h2stream(&mut self, fut: H2Stream<F, B>);
}
#[cfg(all(feature = "server", any(feature = "http1", feature = "http2")))]
pub trait NewSvcExec<I, N, S: HttpService<Body>, E, W: Watcher<I, S, E>>: Clone {
fn execute_new_svc(&mut self, fut: NewSvcTask<I, N, S, E, W>);
}
pub(crate) type BoxSendFuture = Pin<Box<dyn Future<Output = ()> + Send>>;
// Either the user provides an executor for background tasks, or we use
@@ -44,13 +33,13 @@ impl Exec {
{
match *self {
Exec::Default => {
#[cfg(feature = "tcp")]
#[cfg(feature = "runtime")]
{
tokio::task::spawn(fut);
}
#[cfg(not(feature = "tcp"))]
#[cfg(not(feature = "runtime"))]
{
// If no runtime, we need an executor!
panic!("executor must be set")
}
}
@@ -78,18 +67,6 @@ where
}
}
#[cfg(all(feature = "server", any(feature = "http1", feature = "http2")))]
impl<I, N, S, E, W> NewSvcExec<I, N, S, E, W> for Exec
where
NewSvcTask<I, N, S, E, W>: Future<Output = ()> + Send + 'static,
S: HttpService<Body>,
W: Watcher<I, S, E>,
{
fn execute_new_svc(&mut self, fut: NewSvcTask<I, N, S, E, W>) {
self.execute(fut)
}
}
// ==== impl Executor =====
#[cfg(feature = "server")]
@@ -104,19 +81,6 @@ where
}
}
#[cfg(all(feature = "server", any(feature = "http1", feature = "http2")))]
impl<I, N, S, E, W> NewSvcExec<I, N, S, E, W> for E
where
E: Executor<NewSvcTask<I, N, S, E, W>> + Clone,
NewSvcTask<I, N, S, E, W>: Future<Output = ()>,
S: HttpService<Body>,
W: Watcher<I, S, E>,
{
fn execute_new_svc(&mut self, fut: NewSvcTask<I, N, S, E, W>) {
self.execute(fut)
}
}
// If http2 is not enable, we just have a stub here, so that the trait bounds
// that *would* have been needed are still checked. Why?
//

View File

@@ -1,76 +0,0 @@
use pin_project_lite::pin_project;
use super::{task, Future, Pin, Poll};
pub(crate) trait Started: Future {
fn started(&self) -> bool;
}
pub(crate) fn lazy<F, R>(func: F) -> Lazy<F, R>
where
F: FnOnce() -> R,
R: Future + Unpin,
{
Lazy {
inner: Inner::Init { func },
}
}
// FIXME: allow() required due to `impl Trait` leaking types to this lint
pin_project! {
#[allow(missing_debug_implementations)]
pub(crate) struct Lazy<F, R> {
#[pin]
inner: Inner<F, R>,
}
}
pin_project! {
#[project = InnerProj]
#[project_replace = InnerProjReplace]
enum Inner<F, R> {
Init { func: F },
Fut { #[pin] fut: R },
Empty,
}
}
impl<F, R> Started for Lazy<F, R>
where
F: FnOnce() -> R,
R: Future,
{
fn started(&self) -> bool {
match self.inner {
Inner::Init { .. } => false,
Inner::Fut { .. } | Inner::Empty => true,
}
}
}
impl<F, R> Future for Lazy<F, R>
where
F: FnOnce() -> R,
R: Future,
{
type Output = R::Output;
fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
let mut this = self.project();
if let InnerProj::Fut { fut } = this.inner.as_mut().project() {
return fut.poll(cx);
}
match this.inner.as_mut().project_replace(Inner::Empty) {
InnerProjReplace::Init { func } => {
this.inner.set(Inner::Fut { fut: func() });
if let InnerProj::Fut { fut } = this.inner.project() {
return fut.poll(cx);
}
unreachable!()
}
_ => unreachable!("lazy state wrong"),
}
}
}

View File

@@ -10,21 +10,13 @@ macro_rules! ready {
pub(crate) mod buf;
#[cfg(all(feature = "server", any(feature = "http1", feature = "http2")))]
pub(crate) mod date;
#[cfg(all(feature = "server", any(feature = "http1", feature = "http2")))]
pub(crate) mod drain;
#[cfg(any(feature = "http1", feature = "http2", feature = "server"))]
pub(crate) mod exec;
pub(crate) mod io;
#[cfg(all(feature = "client", any(feature = "http1", feature = "http2")))]
mod lazy;
mod never;
#[cfg(all(feature = "client", any(feature = "http1", feature = "http2")))]
pub(crate) mod sync_wrapper;
pub(crate) mod task;
pub(crate) mod watch;
#[cfg(all(feature = "client", any(feature = "http1", feature = "http2")))]
pub(crate) use self::lazy::{lazy, Started as Lazy};
#[cfg(any(feature = "http1", feature = "http2", feature = "runtime"))]
pub(crate) use self::never::Never;
pub(crate) use self::task::Poll;

View File

@@ -1,110 +0,0 @@
/*
* This is a copy of the sync_wrapper crate.
*/
/// A mutual exclusion primitive that relies on static type information only
///
/// In some cases synchronization can be proven statically: whenever you hold an exclusive `&mut`
/// reference, the Rust type system ensures that no other part of the program can hold another
/// reference to the data. Therefore it is safe to access it even if the current thread obtained
/// this reference via a channel. Whenever this is the case, the overhead of allocating and locking
/// a [`Mutex`] can be avoided by using this static version.
///
/// One example where this is often applicable is [`Future`], which requires an exclusive reference
/// for its [`poll`] method: While a given `Future` implementation may not be safe to access by
/// multiple threads concurrently, the executor can only run the `Future` on one thread at any
/// given time, making it [`Sync`] in practice as long as the implementation is `Send`. You can
/// therefore use the sync wrapper to prove that your data structure is `Sync` even though it
/// contains such a `Future`.
///
/// # Example
///
/// ```ignore
/// use hyper::common::sync_wrapper::SyncWrapper;
/// use std::future::Future;
///
/// struct MyThing {
/// future: SyncWrapper<Box<dyn Future<Output = String> + Send>>,
/// }
///
/// impl MyThing {
/// // all accesses to `self.future` now require an exclusive reference or ownership
/// }
///
/// fn assert_sync<T: Sync>() {}
///
/// assert_sync::<MyThing>();
/// ```
///
/// [`Mutex`]: https://doc.rust-lang.org/std/sync/struct.Mutex.html
/// [`Future`]: https://doc.rust-lang.org/std/future/trait.Future.html
/// [`poll`]: https://doc.rust-lang.org/std/future/trait.Future.html#method.poll
/// [`Sync`]: https://doc.rust-lang.org/std/marker/trait.Sync.html
#[repr(transparent)]
pub(crate) struct SyncWrapper<T>(T);
impl<T> SyncWrapper<T> {
/// Creates a new SyncWrapper containing the given value.
///
/// # Examples
///
/// ```ignore
/// use hyper::common::sync_wrapper::SyncWrapper;
///
/// let wrapped = SyncWrapper::new(42);
/// ```
pub(crate) fn new(value: T) -> Self {
Self(value)
}
/// Acquires a reference to the protected value.
///
/// This is safe because it requires an exclusive reference to the wrapper. Therefore this method
/// neither panics nor does it return an error. This is in contrast to [`Mutex::get_mut`] which
/// returns an error if another thread panicked while holding the lock. It is not recommended
/// to send an exclusive reference to a potentially damaged value to another thread for further
/// processing.
///
/// [`Mutex::get_mut`]: https://doc.rust-lang.org/std/sync/struct.Mutex.html#method.get_mut
///
/// # Examples
///
/// ```ignore
/// use hyper::common::sync_wrapper::SyncWrapper;
///
/// let mut wrapped = SyncWrapper::new(42);
/// let value = wrapped.get_mut();
/// *value = 0;
/// assert_eq!(*wrapped.get_mut(), 0);
/// ```
pub(crate) fn get_mut(&mut self) -> &mut T {
&mut self.0
}
/// Consumes this wrapper, returning the underlying data.
///
/// This is safe because it requires ownership of the wrapper, aherefore this method will neither
/// panic nor does it return an error. This is in contrast to [`Mutex::into_inner`] which
/// returns an error if another thread panicked while holding the lock. It is not recommended
/// to send an exclusive reference to a potentially damaged value to another thread for further
/// processing.
///
/// [`Mutex::into_inner`]: https://doc.rust-lang.org/std/sync/struct.Mutex.html#method.into_inner
///
/// # Examples
///
/// ```ignore
/// use hyper::common::sync_wrapper::SyncWrapper;
///
/// let mut wrapped = SyncWrapper::new(42);
/// assert_eq!(wrapped.into_inner(), 42);
/// ```
#[allow(dead_code)]
pub(crate) fn into_inner(self) -> T {
self.0
}
}
// this is safe because the only operations permitted on this data structure require exclusive
// access or ownership
unsafe impl<T: Send> Sync for SyncWrapper<T> {}

View File

@@ -34,16 +34,9 @@ pub(super) enum Kind {
/// An `io::Error` that occurred while trying to read or write to a network stream.
#[cfg(any(feature = "http1", feature = "http2"))]
Io,
/// Error occurred while connecting.
#[allow(unused)]
Connect,
/// Error creating a TcpListener.
#[cfg(all(feature = "tcp", feature = "server"))]
Listen,
/// Error accepting on an Incoming stream.
#[cfg(any(feature = "http1", feature = "http2"))]
#[cfg(feature = "server")]
Accept,
/// User took too long to send headers
#[cfg(all(feature = "http1", feature = "server", feature = "runtime"))]
HeaderTimeout,
@@ -96,10 +89,6 @@ pub(super) enum User {
Body,
/// The user aborted writing of the outgoing body.
BodyWriteAborted,
/// Error calling user's MakeService.
#[cfg(any(feature = "http1", feature = "http2"))]
#[cfg(feature = "server")]
MakeService,
/// Error from future of user's Service.
#[cfg(any(feature = "http1", feature = "http2"))]
Service,
@@ -109,22 +98,10 @@ pub(super) enum User {
#[cfg(any(feature = "http1", feature = "http2"))]
#[cfg(feature = "server")]
UnexpectedHeader,
/// User tried to create a Request with bad version.
#[cfg(any(feature = "http1", feature = "http2"))]
#[cfg(feature = "client")]
UnsupportedVersion,
/// User tried to create a CONNECT Request with the Client.
#[cfg(any(feature = "http1", feature = "http2"))]
#[cfg(feature = "client")]
UnsupportedRequestMethod,
/// User tried to respond with a 1xx (not 101) response code.
#[cfg(feature = "http1")]
#[cfg(feature = "server")]
UnsupportedStatusCode,
/// User tried to send a Request with Client with non-absolute URI.
#[cfg(any(feature = "http1", feature = "http2"))]
#[cfg(feature = "client")]
AbsoluteUriRequired,
/// User tried polling for an upgrade that doesn't exist.
NoUpgrade,
@@ -181,11 +158,6 @@ impl Error {
matches!(self.inner.kind, Kind::ChannelClosed)
}
/// Returns true if this was an error from `Connect`.
pub fn is_connect(&self) -> bool {
matches!(self.inner.kind, Kind::Connect)
}
/// Returns true if the connection closed before a message could complete.
pub fn is_incomplete_message(&self) -> bool {
matches!(self.inner.kind, Kind::IncompleteMessage)
@@ -278,18 +250,6 @@ impl Error {
Error::new(Kind::Listen).with(cause)
}
#[cfg(any(feature = "http1", feature = "http2"))]
#[cfg(feature = "server")]
pub(super) fn new_accept<E: Into<Cause>>(cause: E) -> Error {
Error::new(Kind::Accept).with(cause)
}
#[cfg(any(feature = "http1", feature = "http2"))]
#[cfg(feature = "client")]
pub(super) fn new_connect<E: Into<Cause>>(cause: E) -> Error {
Error::new(Kind::Connect).with(cause)
}
pub(super) fn new_closed() -> Error {
Error::new(Kind::ChannelClosed)
}
@@ -323,30 +283,12 @@ impl Error {
Error::new(Kind::HeaderTimeout)
}
#[cfg(any(feature = "http1", feature = "http2"))]
#[cfg(feature = "client")]
pub(super) fn new_user_unsupported_version() -> Error {
Error::new_user(User::UnsupportedVersion)
}
#[cfg(any(feature = "http1", feature = "http2"))]
#[cfg(feature = "client")]
pub(super) fn new_user_unsupported_request_method() -> Error {
Error::new_user(User::UnsupportedRequestMethod)
}
#[cfg(feature = "http1")]
#[cfg(feature = "server")]
pub(super) fn new_user_unsupported_status_code() -> Error {
Error::new_user(User::UnsupportedStatusCode)
}
#[cfg(any(feature = "http1", feature = "http2"))]
#[cfg(feature = "client")]
pub(super) fn new_user_absolute_uri_required() -> Error {
Error::new_user(User::AbsoluteUriRequired)
}
pub(super) fn new_user_no_upgrade() -> Error {
Error::new_user(User::NoUpgrade)
}
@@ -356,12 +298,6 @@ impl Error {
Error::new_user(User::ManualUpgrade)
}
#[cfg(any(feature = "http1", feature = "http2"))]
#[cfg(feature = "server")]
pub(super) fn new_user_make_service<E: Into<Cause>>(cause: E) -> Error {
Error::new_user(User::MakeService).with(cause)
}
#[cfg(any(feature = "http1", feature = "http2"))]
pub(super) fn new_user_service<E: Into<Cause>>(cause: E) -> Error {
Error::new_user(User::Service).with(cause)
@@ -431,13 +367,9 @@ impl Error {
#[cfg(feature = "http1")]
Kind::UnexpectedMessage => "received unexpected message from connection",
Kind::ChannelClosed => "channel closed",
Kind::Connect => "error trying to connect",
Kind::Canceled => "operation was canceled",
#[cfg(all(feature = "server", feature = "tcp"))]
Kind::Listen => "error creating server listener",
#[cfg(any(feature = "http1", feature = "http2"))]
#[cfg(feature = "server")]
Kind::Accept => "error accepting connection",
#[cfg(all(feature = "http1", feature = "server", feature = "runtime"))]
Kind::HeaderTimeout => "read header from client timeout",
#[cfg(any(feature = "http1", feature = "http2"))]
@@ -455,27 +387,15 @@ impl Error {
Kind::User(User::Body) => "error from user's HttpBody stream",
Kind::User(User::BodyWriteAborted) => "user body write aborted",
#[cfg(any(feature = "http1", feature = "http2"))]
#[cfg(feature = "server")]
Kind::User(User::MakeService) => "error from user's MakeService",
#[cfg(any(feature = "http1", feature = "http2"))]
Kind::User(User::Service) => "error from user's Service",
#[cfg(any(feature = "http1", feature = "http2"))]
#[cfg(feature = "server")]
Kind::User(User::UnexpectedHeader) => "user sent unexpected header",
#[cfg(any(feature = "http1", feature = "http2"))]
#[cfg(feature = "client")]
Kind::User(User::UnsupportedVersion) => "request has unsupported HTTP version",
#[cfg(any(feature = "http1", feature = "http2"))]
#[cfg(feature = "client")]
Kind::User(User::UnsupportedRequestMethod) => "request has unsupported HTTP method",
#[cfg(feature = "http1")]
#[cfg(feature = "server")]
Kind::User(User::UnsupportedStatusCode) => {
"response has 1xx status code, not supported by server"
}
#[cfg(any(feature = "http1", feature = "http2"))]
#[cfg(feature = "client")]
Kind::User(User::AbsoluteUriRequired) => "client requires absolute-form URIs",
Kind::User(User::NoUpgrade) => "no upgrade available",
#[cfg(feature = "http1")]
Kind::User(User::ManualUpgrade) => "upgrade expected but low level API in use",

View File

@@ -51,7 +51,6 @@
//! - `server`: Enables the HTTP `server`.
//! - `runtime`: Enables convenient integration with `tokio`, providing
//! connectors and acceptors for TCP, and a default executor.
//! - `tcp`: Enables convenient implementations over TCP (using tokio).
//!
//! [feature flags]: https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section
@@ -94,15 +93,10 @@ cfg_feature! {
#![feature = "client"]
pub mod client;
#[cfg(any(feature = "http1", feature = "http2"))]
#[doc(no_inline)]
pub use crate::client::Client;
}
cfg_feature! {
#![feature = "server"]
pub mod server;
#[doc(no_inline)]
pub use crate::server::Server;
}

View File

@@ -990,14 +990,11 @@ impl Http1Transaction for Client {
.h1_parser_config
.obsolete_multiline_headers_in_responses_are_allowed()
{
for header in &headers_indices[..headers_len] {
for header in &mut headers_indices[..headers_len] {
// SAFETY: array is valid up to `headers_len`
let header = unsafe { &*header.as_ptr() };
for b in &mut slice[header.value.0..header.value.1] {
if *b == b'\r' || *b == b'\n' {
*b = b' ';
}
}
let header = unsafe { &mut *header.as_mut_ptr() };
Client::obs_fold_line(&mut slice, header);
}
}
@@ -1344,6 +1341,65 @@ impl Client {
set_content_length(headers, len)
}
fn obs_fold_line(all: &mut [u8], idx: &mut HeaderIndices) {
// If the value has obs-folded text, then in-place shift the bytes out
// of here.
//
// https://httpwg.org/specs/rfc9112.html#line.folding
//
// > A user agent that receives an obs-fold MUST replace each received
// > obs-fold with one or more SP octets prior to interpreting the
// > field value.
//
// This means strings like "\r\n\t foo" must replace the "\r\n\t " with
// a single space.
let buf = &mut all[idx.value.0..idx.value.1];
// look for a newline, otherwise bail out
let first_nl = match buf.iter().position(|b| *b == b'\n') {
Some(i) => i,
None => return,
};
// not on standard slices because whatever, sigh
fn trim_start(mut s: &[u8]) -> &[u8] {
while let [first, rest @ ..] = s {
if first.is_ascii_whitespace() {
s = rest;
} else {
break;
}
}
s
}
fn trim_end(mut s: &[u8]) -> &[u8] {
while let [rest @ .., last] = s {
if last.is_ascii_whitespace() {
s = rest;
} else {
break;
}
}
s
}
fn trim(s: &[u8]) -> &[u8] {
trim_start(trim_end(s))
}
// TODO(perf): we could do the moves in-place, but this is so uncommon
// that it shouldn't matter.
let mut unfolded = trim_end(&buf[..first_nl]).to_vec();
for line in buf[first_nl + 1..].split(|b| *b == b'\n') {
unfolded.push(b' ');
unfolded.extend_from_slice(trim(line));
}
buf[..unfolded.len()].copy_from_slice(&unfolded);
idx.value.1 = idx.value.0 + unfolded.len();
}
}
fn set_content_length(headers: &mut HeaderMap, len: u64) -> Encoder {
@@ -2384,6 +2440,30 @@ mod tests {
);
}
#[cfg(feature = "client")]
#[test]
fn test_client_obs_fold_line() {
fn unfold(src: &str) -> String {
let mut buf = src.as_bytes().to_vec();
let mut idx = HeaderIndices {
name: (0, 0),
value: (0, buf.len()),
};
Client::obs_fold_line(&mut buf, &mut idx);
String::from_utf8(buf[idx.value.0 .. idx.value.1].to_vec()).unwrap()
}
assert_eq!(
unfold("a normal line"),
"a normal line",
);
assert_eq!(
unfold("obs\r\n fold\r\n\t line"),
"obs fold line",
);
}
#[test]
fn test_client_request_encode_title_case() {
use crate::proto::BodyLength;

View File

@@ -1,71 +0,0 @@
//! The `Accept` trait and supporting types.
//!
//! This module contains:
//!
//! - The [`Accept`](Accept) trait used to asynchronously accept incoming
//! connections.
//! - Utilities like `poll_fn` to ease creating a custom `Accept`.
use crate::common::{
task::{self, Poll},
Pin,
};
/// Asynchronously accept incoming connections.
pub trait Accept {
/// The connection type that can be accepted.
type Conn;
/// The error type that can occur when accepting a connection.
type Error;
/// Poll to accept the next connection.
fn poll_accept(
self: Pin<&mut Self>,
cx: &mut task::Context<'_>,
) -> Poll<Option<Result<Self::Conn, Self::Error>>>;
}
/// Create an `Accept` with a polling function.
///
/// # Example
///
/// ```
/// use std::task::Poll;
/// use hyper::server::{accept, Server};
///
/// # let mock_conn = ();
/// // If we created some mocked connection...
/// let mut conn = Some(mock_conn);
///
/// // And accept just the mocked conn once...
/// let once = accept::poll_fn(move |cx| {
/// Poll::Ready(conn.take().map(Ok::<_, ()>))
/// });
///
/// let builder = Server::builder(once);
/// ```
pub fn poll_fn<F, IO, E>(func: F) -> impl Accept<Conn = IO, Error = E>
where
F: FnMut(&mut task::Context<'_>) -> Poll<Option<Result<IO, E>>>,
{
struct PollFn<F>(F);
// The closure `F` is never pinned
impl<F> Unpin for PollFn<F> {}
impl<F, IO, E> Accept for PollFn<F>
where
F: FnMut(&mut task::Context<'_>) -> Poll<Option<Result<IO, E>>>,
{
type Conn = IO;
type Error = E;
fn poll_accept(
self: Pin<&mut Self>,
cx: &mut task::Context<'_>,
) -> Poll<Option<Result<Self::Conn, Self::Error>>> {
(self.get_mut().0)(cx)
}
}
PollFn(func)
}

View File

@@ -5,9 +5,6 @@
//! are not handled at this level. This module provides the building blocks to
//! customize those things externally.
//!
//! If you don't have need to manage connections yourself, consider using the
//! higher-level [Server](super) API.
//!
//! ## Example
//! A simple example that uses the `Http` struct to talk HTTP over a Tokio TCP stream
//! ```no_run
@@ -69,7 +66,6 @@ cfg_feature! {
use tokio::io::{AsyncRead, AsyncWrite};
use tracing::trace;
pub use super::server::Connecting;
use crate::body::{Body, HttpBody};
use crate::common::{task, Future, Pin, Poll, Unpin};
#[cfg(not(all(feature = "http1", feature = "http2")))]
@@ -84,9 +80,6 @@ cfg_feature! {
/// A lower-level configuration of the HTTP protocol.
///
/// This structure is used to configure options for an HTTP server connection.
///
/// If you don't have need to manage connections yourself, consider using the
/// higher-level [Server](super) API.
#[derive(Clone, Debug)]
#[cfg(any(feature = "http1", feature = "http2"))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "http1", feature = "http2"))))]
@@ -324,7 +317,7 @@ impl<E> Http<E> {
self
}
/// Set a timeout for reading client request headers. If a client does not
/// Set a timeout for reading client request headers. If a client does not
/// transmit the entire header within this time, the connection is closed.
///
/// Default is None.

View File

@@ -1,172 +1,10 @@
//! HTTP Server
//!
//! A `Server` is created to listen on a port, parse HTTP requests, and hand
//! them off to a `Service`.
//! A "server" is usually created by listening on a port for new connections,
//! parse HTTP requests, and hand them off to a `Service`.
//!
//! There are two levels of APIs provide for constructing HTTP servers:
//!
//! - The higher-level [`Server`](Server) type.
//! - The lower-level [`conn`](conn) module.
//!
//! # Server
//!
//! The [`Server`](Server) is main way to start listening for HTTP requests.
//! It wraps a listener with a [`MakeService`](crate::service), and then should
//! be executed to start serving requests.
//!
//! [`Server`](Server) accepts connections in both HTTP1 and HTTP2 by default.
//!
//! ## Examples
//!
//! ```no_run
//! use std::convert::Infallible;
//! use std::net::SocketAddr;
//! use hyper::{Body, Request, Response, Server};
//! use hyper::service::{make_service_fn, service_fn};
//!
//! async fn handle(_req: Request<Body>) -> Result<Response<Body>, Infallible> {
//! Ok(Response::new(Body::from("Hello World")))
//! }
//!
//! # #[cfg(feature = "runtime")]
//! #[tokio::main]
//! async fn main() {
//! // Construct our SocketAddr to listen on...
//! let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
//!
//! // And a MakeService to handle each connection...
//! let make_service = make_service_fn(|_conn| async {
//! Ok::<_, Infallible>(service_fn(handle))
//! });
//!
//! // Then bind and serve...
//! let server = Server::bind(&addr).serve(make_service);
//!
//! // And run forever...
//! if let Err(e) = server.await {
//! eprintln!("server error: {}", e);
//! }
//! }
//! # #[cfg(not(feature = "runtime"))]
//! # fn main() {}
//! ```
//!
//! If you don't need the connection and your service implements `Clone` you can use
//! [`tower::make::Shared`] instead of `make_service_fn` which is a bit simpler:
//!
//! ```no_run
//! # use std::convert::Infallible;
//! # use std::net::SocketAddr;
//! # use hyper::{Body, Request, Response, Server};
//! # use hyper::service::{make_service_fn, service_fn};
//! # use tower::make::Shared;
//! # async fn handle(_req: Request<Body>) -> Result<Response<Body>, Infallible> {
//! # Ok(Response::new(Body::from("Hello World")))
//! # }
//! # #[cfg(feature = "runtime")]
//! #[tokio::main]
//! async fn main() {
//! // Construct our SocketAddr to listen on...
//! let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
//!
//! // Shared is a MakeService that produces services by cloning an inner service...
//! let make_service = Shared::new(service_fn(handle));
//!
//! // Then bind and serve...
//! let server = Server::bind(&addr).serve(make_service);
//!
//! // And run forever...
//! if let Err(e) = server.await {
//! eprintln!("server error: {}", e);
//! }
//! }
//! # #[cfg(not(feature = "runtime"))]
//! # fn main() {}
//! ```
//!
//! Passing data to your request handler can be done like so:
//!
//! ```no_run
//! use std::convert::Infallible;
//! use std::net::SocketAddr;
//! use hyper::{Body, Request, Response, Server};
//! use hyper::service::{make_service_fn, service_fn};
//! # #[cfg(feature = "runtime")]
//! use tokio::net::TcpStream;
//!
//! #[derive(Clone)]
//! struct AppContext {
//! // Whatever data your application needs can go here
//! }
//!
//! async fn handle(
//! context: AppContext,
//! addr: SocketAddr,
//! req: Request<Body>
//! ) -> Result<Response<Body>, Infallible> {
//! Ok(Response::new(Body::from("Hello World")))
//! }
//!
//! # #[cfg(feature = "runtime")]
//! #[tokio::main]
//! async fn main() {
//! let context = AppContext {
//! // ...
//! };
//!
//! // A `MakeService` that produces a `Service` to handle each connection.
//! let make_service = make_service_fn(move |conn: &TcpStream| {
//! // We have to clone the context to share it with each invocation of
//! // `make_service`. If your data doesn't implement `Clone` consider using
//! // an `std::sync::Arc`.
//! let context = context.clone();
//!
//! // You can grab the address of the incoming connection like so.
//! let addr = conn.peer_addr().unwrap();
//!
//! // Create a `Service` for responding to the request.
//! let service = service_fn(move |req| {
//! handle(context.clone(), addr, req)
//! });
//!
//! // Return the service to hyper.
//! async move { Ok::<_, Infallible>(service) }
//! });
//!
//! // Run the server like above...
//! let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
//!
//! let server = Server::bind(&addr).serve(make_service);
//!
//! if let Err(e) = server.await {
//! eprintln!("server error: {}", e);
//! }
//! }
//! # #[cfg(not(feature = "runtime"))]
//! # fn main() {}
//! ```
//!
//! [`tower::make::Shared`]: https://docs.rs/tower/latest/tower/make/struct.Shared.html
pub mod accept;
//! How exactly you choose to listen for connections is not something hyper
//! concerns itself with. After you have a connection, you can handle HTTP over
//! it with the types in the [`conn`](conn) module.
pub mod conn;
#[cfg(feature = "tcp")]
mod tcp;
pub use self::server::Server;
cfg_feature! {
#![any(feature = "http1", feature = "http2")]
pub(crate) mod server;
pub use self::server::Builder;
mod shutdown;
}
cfg_feature! {
#![not(any(feature = "http1", feature = "http2"))]
mod server_stub;
use server_stub as server;
}

View File

@@ -1,776 +0,0 @@
use std::error::Error as StdError;
use std::fmt;
#[cfg(feature = "tcp")]
use std::net::{SocketAddr, TcpListener as StdTcpListener};
#[cfg(any(feature = "tcp", feature = "http1"))]
use std::time::Duration;
use pin_project_lite::pin_project;
use tokio::io::{AsyncRead, AsyncWrite};
use tracing::trace;
use super::accept::Accept;
#[cfg(all(feature = "tcp"))]
use super::tcp::AddrIncoming;
use crate::body::{Body, HttpBody};
use crate::common::exec::Exec;
use crate::common::exec::{ConnStreamExec, NewSvcExec};
use crate::common::{task, Future, Pin, Poll, Unpin};
// Renamed `Http` as `Http_` for now so that people upgrading don't see an
// error that `hyper::server::Http` is private...
use super::conn::{Connection, Http as Http_, UpgradeableConnection};
use super::shutdown::{Graceful, GracefulWatcher};
use crate::service::{HttpService, MakeServiceRef};
use self::new_svc::NewSvcTask;
pin_project! {
/// A listening HTTP server that accepts connections in both HTTP1 and HTTP2 by default.
///
/// `Server` is a `Future` mapping a bound listener with a set of service
/// handlers. It is built using the [`Builder`](Builder), and the future
/// completes when the server has been shutdown. It should be run by an
/// `Executor`.
pub struct Server<I, S, E = Exec> {
#[pin]
incoming: I,
make_service: S,
protocol: Http_<E>,
}
}
/// A builder for a [`Server`](Server).
#[derive(Debug)]
#[cfg_attr(docsrs, doc(cfg(any(feature = "http1", feature = "http2"))))]
pub struct Builder<I, E = Exec> {
incoming: I,
protocol: Http_<E>,
}
// ===== impl Server =====
#[cfg_attr(docsrs, doc(cfg(any(feature = "http1", feature = "http2"))))]
impl<I> Server<I, ()> {
/// Starts a [`Builder`](Builder) with the provided incoming stream.
pub fn builder(incoming: I) -> Builder<I> {
Builder {
incoming,
protocol: Http_::new(),
}
}
}
#[cfg(feature = "tcp")]
#[cfg_attr(
docsrs,
doc(cfg(all(feature = "tcp", any(feature = "http1", feature = "http2"))))
)]
impl Server<AddrIncoming, ()> {
/// Binds to the provided address, and returns a [`Builder`](Builder).
///
/// # Panics
///
/// This method will panic if binding to the address fails. For a method
/// to bind to an address and return a `Result`, see `Server::try_bind`.
pub fn bind(addr: &SocketAddr) -> Builder<AddrIncoming> {
let incoming = AddrIncoming::new(addr).unwrap_or_else(|e| {
panic!("error binding to {}: {}", addr, e);
});
Server::builder(incoming)
}
/// Tries to bind to the provided address, and returns a [`Builder`](Builder).
pub fn try_bind(addr: &SocketAddr) -> crate::Result<Builder<AddrIncoming>> {
AddrIncoming::new(addr).map(Server::builder)
}
/// Create a new instance from a `std::net::TcpListener` instance.
pub fn from_tcp(listener: StdTcpListener) -> Result<Builder<AddrIncoming>, crate::Error> {
AddrIncoming::from_std(listener).map(Server::builder)
}
}
#[cfg(feature = "tcp")]
#[cfg_attr(
docsrs,
doc(cfg(all(feature = "tcp", any(feature = "http1", feature = "http2"))))
)]
impl<S, E> Server<AddrIncoming, S, E> {
/// Returns the local address that this server is bound to.
pub fn local_addr(&self) -> SocketAddr {
self.incoming.local_addr()
}
}
#[cfg_attr(docsrs, doc(cfg(any(feature = "http1", feature = "http2"))))]
impl<I, IO, IE, S, E, B> Server<I, S, E>
where
I: Accept<Conn = IO, Error = IE>,
IE: Into<Box<dyn StdError + Send + Sync>>,
IO: AsyncRead + AsyncWrite + Unpin + Send + 'static,
S: MakeServiceRef<IO, Body, ResBody = B>,
S::Error: Into<Box<dyn StdError + Send + Sync>>,
B: HttpBody + 'static,
B::Error: Into<Box<dyn StdError + Send + Sync>>,
E: ConnStreamExec<<S::Service as HttpService<Body>>::Future, B>,
{
/// Prepares a server to handle graceful shutdown when the provided future
/// completes.
///
/// # Example
///
/// ```
/// # fn main() {}
/// # #[cfg(feature = "tcp")]
/// # async fn run() {
/// # use hyper::{Body, Response, Server, Error};
/// # use hyper::service::{make_service_fn, service_fn};
/// # let make_service = make_service_fn(|_| async {
/// # Ok::<_, Error>(service_fn(|_req| async {
/// # Ok::<_, Error>(Response::new(Body::from("Hello World")))
/// # }))
/// # });
/// // Make a server from the previous examples...
/// let server = Server::bind(&([127, 0, 0, 1], 3000).into())
/// .serve(make_service);
///
/// // Prepare some signal for when the server should start shutting down...
/// let (tx, rx) = tokio::sync::oneshot::channel::<()>();
/// let graceful = server
/// .with_graceful_shutdown(async {
/// rx.await.ok();
/// });
///
/// // Await the `server` receiving the signal...
/// if let Err(e) = graceful.await {
/// eprintln!("server error: {}", e);
/// }
///
/// // And later, trigger the signal by calling `tx.send(())`.
/// let _ = tx.send(());
/// # }
/// ```
pub fn with_graceful_shutdown<F>(self, signal: F) -> Graceful<I, S, F, E>
where
F: Future<Output = ()>,
E: NewSvcExec<IO, S::Future, S::Service, E, GracefulWatcher>,
{
Graceful::new(self, signal)
}
fn poll_next_(
self: Pin<&mut Self>,
cx: &mut task::Context<'_>,
) -> Poll<Option<crate::Result<Connecting<IO, S::Future, E>>>> {
let me = self.project();
match ready!(me.make_service.poll_ready_ref(cx)) {
Ok(()) => (),
Err(e) => {
trace!("make_service closed");
return Poll::Ready(Some(Err(crate::Error::new_user_make_service(e))));
}
}
if let Some(item) = ready!(me.incoming.poll_accept(cx)) {
let io = item.map_err(crate::Error::new_accept)?;
let new_fut = me.make_service.make_service_ref(&io);
Poll::Ready(Some(Ok(Connecting {
future: new_fut,
io: Some(io),
protocol: me.protocol.clone(),
})))
} else {
Poll::Ready(None)
}
}
pub(super) fn poll_watch<W>(
mut self: Pin<&mut Self>,
cx: &mut task::Context<'_>,
watcher: &W,
) -> Poll<crate::Result<()>>
where
E: NewSvcExec<IO, S::Future, S::Service, E, W>,
W: Watcher<IO, S::Service, E>,
{
loop {
if let Some(connecting) = ready!(self.as_mut().poll_next_(cx)?) {
let fut = NewSvcTask::new(connecting, watcher.clone());
self.as_mut().project().protocol.exec.execute_new_svc(fut);
} else {
return Poll::Ready(Ok(()));
}
}
}
}
#[cfg_attr(docsrs, doc(cfg(any(feature = "http1", feature = "http2"))))]
impl<I, IO, IE, S, B, E> Future for Server<I, S, E>
where
I: Accept<Conn = IO, Error = IE>,
IE: Into<Box<dyn StdError + Send + Sync>>,
IO: AsyncRead + AsyncWrite + Unpin + Send + 'static,
S: MakeServiceRef<IO, Body, ResBody = B>,
S::Error: Into<Box<dyn StdError + Send + Sync>>,
B: HttpBody + 'static,
B::Error: Into<Box<dyn StdError + Send + Sync>>,
E: ConnStreamExec<<S::Service as HttpService<Body>>::Future, B>,
E: NewSvcExec<IO, S::Future, S::Service, E, NoopWatcher>,
{
type Output = crate::Result<()>;
fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
self.poll_watch(cx, &NoopWatcher)
}
}
impl<I: fmt::Debug, S: fmt::Debug> fmt::Debug for Server<I, S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut st = f.debug_struct("Server");
st.field("listener", &self.incoming);
st.finish()
}
}
// ===== impl Builder =====
#[cfg_attr(docsrs, doc(cfg(any(feature = "http1", feature = "http2"))))]
impl<I, E> Builder<I, E> {
/// Start a new builder, wrapping an incoming stream and low-level options.
///
/// For a more convenient constructor, see [`Server::bind`](Server::bind).
pub fn new(incoming: I, protocol: Http_<E>) -> Self {
Builder { incoming, protocol }
}
/// Sets whether to use keep-alive for HTTP/1 connections.
///
/// Default is `true`.
#[cfg(feature = "http1")]
#[cfg_attr(docsrs, doc(cfg(feature = "http1")))]
pub fn http1_keepalive(mut self, val: bool) -> Self {
self.protocol.http1_keep_alive(val);
self
}
/// Set whether HTTP/1 connections should support half-closures.
///
/// Clients can chose to shutdown their write-side while waiting
/// for the server to respond. Setting this to `true` will
/// prevent closing the connection immediately if `read`
/// detects an EOF in the middle of a request.
///
/// Default is `false`.
#[cfg(feature = "http1")]
#[cfg_attr(docsrs, doc(cfg(feature = "http1")))]
pub fn http1_half_close(mut self, val: bool) -> Self {
self.protocol.http1_half_close(val);
self
}
/// Set the maximum buffer size.
///
/// Default is ~ 400kb.
#[cfg(feature = "http1")]
#[cfg_attr(docsrs, doc(cfg(feature = "http1")))]
pub fn http1_max_buf_size(mut self, val: usize) -> Self {
self.protocol.max_buf_size(val);
self
}
// Sets whether to bunch up HTTP/1 writes until the read buffer is empty.
//
// This isn't really desirable in most cases, only really being useful in
// silly pipeline benchmarks.
#[doc(hidden)]
#[cfg(feature = "http1")]
pub fn http1_pipeline_flush(mut self, val: bool) -> Self {
self.protocol.pipeline_flush(val);
self
}
/// Set whether HTTP/1 connections should try to use vectored writes,
/// or always flatten into a single buffer.
///
/// Note that setting this to false may mean more copies of body data,
/// but may also improve performance when an IO transport doesn't
/// support vectored writes well, such as most TLS implementations.
///
/// Setting this to true will force hyper to use queued strategy
/// which may eliminate unnecessary cloning on some TLS backends
///
/// Default is `auto`. In this mode hyper will try to guess which
/// mode to use
#[cfg(feature = "http1")]
pub fn http1_writev(mut self, enabled: bool) -> Self {
self.protocol.http1_writev(enabled);
self
}
/// Set whether HTTP/1 connections will write header names as title case at
/// the socket level.
///
/// Note that this setting does not affect HTTP/2.
///
/// Default is false.
#[cfg(feature = "http1")]
#[cfg_attr(docsrs, doc(cfg(feature = "http1")))]
pub fn http1_title_case_headers(mut self, val: bool) -> Self {
self.protocol.http1_title_case_headers(val);
self
}
/// Set whether to support preserving original header cases.
///
/// Currently, this will record the original cases received, and store them
/// in a private extension on the `Request`. It will also look for and use
/// such an extension in any provided `Response`.
///
/// Since the relevant extension is still private, there is no way to
/// interact with the original cases. The only effect this can have now is
/// to forward the cases in a proxy-like fashion.
///
/// Note that this setting does not affect HTTP/2.
///
/// Default is false.
#[cfg(feature = "http1")]
#[cfg_attr(docsrs, doc(cfg(feature = "http1")))]
pub fn http1_preserve_header_case(mut self, val: bool) -> Self {
self.protocol.http1_preserve_header_case(val);
self
}
/// Set a timeout for reading client request headers. If a client does not
/// transmit the entire header within this time, the connection is closed.
///
/// Default is None.
#[cfg(all(feature = "http1", feature = "runtime"))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "http1", feature = "runtime"))))]
pub fn http1_header_read_timeout(mut self, read_timeout: Duration) -> Self {
self.protocol.http1_header_read_timeout(read_timeout);
self
}
/// Sets whether HTTP/1 is required.
///
/// Default is `false`.
#[cfg(feature = "http1")]
#[cfg_attr(docsrs, doc(cfg(feature = "http1")))]
pub fn http1_only(mut self, val: bool) -> Self {
self.protocol.http1_only(val);
self
}
/// Sets whether HTTP/2 is required.
///
/// Default is `false`.
#[cfg(feature = "http2")]
#[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
pub fn http2_only(mut self, val: bool) -> Self {
self.protocol.http2_only(val);
self
}
/// Sets the [`SETTINGS_INITIAL_WINDOW_SIZE`][spec] option for HTTP2
/// stream-level flow control.
///
/// Passing `None` will do nothing.
///
/// If not set, hyper will use a default.
///
/// [spec]: https://http2.github.io/http2-spec/#SETTINGS_INITIAL_WINDOW_SIZE
#[cfg(feature = "http2")]
#[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
pub fn http2_initial_stream_window_size(mut self, sz: impl Into<Option<u32>>) -> Self {
self.protocol.http2_initial_stream_window_size(sz.into());
self
}
/// Sets the max connection-level flow control for HTTP2
///
/// Passing `None` will do nothing.
///
/// If not set, hyper will use a default.
#[cfg(feature = "http2")]
#[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
pub fn http2_initial_connection_window_size(mut self, sz: impl Into<Option<u32>>) -> Self {
self.protocol
.http2_initial_connection_window_size(sz.into());
self
}
/// Sets whether to use an adaptive flow control.
///
/// Enabling this will override the limits set in
/// `http2_initial_stream_window_size` and
/// `http2_initial_connection_window_size`.
#[cfg(feature = "http2")]
#[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
pub fn http2_adaptive_window(mut self, enabled: bool) -> Self {
self.protocol.http2_adaptive_window(enabled);
self
}
/// Sets the maximum frame size to use for HTTP2.
///
/// Passing `None` will do nothing.
///
/// If not set, hyper will use a default.
#[cfg(feature = "http2")]
#[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
pub fn http2_max_frame_size(mut self, sz: impl Into<Option<u32>>) -> Self {
self.protocol.http2_max_frame_size(sz);
self
}
/// Sets the [`SETTINGS_MAX_CONCURRENT_STREAMS`][spec] option for HTTP2
/// connections.
///
/// Default is no limit (`std::u32::MAX`). Passing `None` will do nothing.
///
/// [spec]: https://http2.github.io/http2-spec/#SETTINGS_MAX_CONCURRENT_STREAMS
#[cfg(feature = "http2")]
#[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
pub fn http2_max_concurrent_streams(mut self, max: impl Into<Option<u32>>) -> Self {
self.protocol.http2_max_concurrent_streams(max.into());
self
}
/// Sets an interval for HTTP2 Ping frames should be sent to keep a
/// connection alive.
///
/// Pass `None` to disable HTTP2 keep-alive.
///
/// Default is currently disabled.
///
/// # Cargo Feature
///
/// Requires the `runtime` cargo feature to be enabled.
#[cfg(all(feature = "runtime", feature = "http2"))]
#[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
pub fn http2_keep_alive_interval(mut self, interval: impl Into<Option<Duration>>) -> Self {
self.protocol.http2_keep_alive_interval(interval);
self
}
/// Sets a timeout for receiving an acknowledgement of the keep-alive ping.
///
/// If the ping is not acknowledged within the timeout, the connection will
/// be closed. Does nothing if `http2_keep_alive_interval` is disabled.
///
/// Default is 20 seconds.
///
/// # Cargo Feature
///
/// Requires the `runtime` cargo feature to be enabled.
#[cfg(all(feature = "runtime", feature = "http2"))]
#[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
pub fn http2_keep_alive_timeout(mut self, timeout: Duration) -> Self {
self.protocol.http2_keep_alive_timeout(timeout);
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
}
/// Enables the [extended CONNECT protocol].
///
/// [extended CONNECT protocol]: https://datatracker.ietf.org/doc/html/rfc8441#section-4
#[cfg(feature = "http2")]
pub fn http2_enable_connect_protocol(mut self) -> Self {
self.protocol.http2_enable_connect_protocol();
self
}
/// Sets the `Executor` to deal with connection tasks.
///
/// Default is `tokio::spawn`.
pub fn executor<E2>(self, executor: E2) -> Builder<I, E2> {
Builder {
incoming: self.incoming,
protocol: self.protocol.with_executor(executor),
}
}
/// Consume this `Builder`, creating a [`Server`](Server).
///
/// # Example
///
/// ```
/// # #[cfg(feature = "tcp")]
/// # async fn run() {
/// use hyper::{Body, Error, Response, Server};
/// use hyper::service::{make_service_fn, service_fn};
///
/// // Construct our SocketAddr to listen on...
/// let addr = ([127, 0, 0, 1], 3000).into();
///
/// // And a MakeService to handle each connection...
/// let make_svc = make_service_fn(|_| async {
/// Ok::<_, Error>(service_fn(|_req| async {
/// Ok::<_, Error>(Response::new(Body::from("Hello World")))
/// }))
/// });
///
/// // Then bind and serve...
/// let server = Server::bind(&addr)
/// .serve(make_svc);
///
/// // Run forever-ish...
/// if let Err(err) = server.await {
/// eprintln!("server error: {}", err);
/// }
/// # }
/// ```
pub fn serve<S, B>(self, make_service: S) -> Server<I, S, E>
where
I: Accept,
I::Error: Into<Box<dyn StdError + Send + Sync>>,
I::Conn: AsyncRead + AsyncWrite + Unpin + Send + 'static,
S: MakeServiceRef<I::Conn, Body, ResBody = B>,
S::Error: Into<Box<dyn StdError + Send + Sync>>,
B: HttpBody + 'static,
B::Error: Into<Box<dyn StdError + Send + Sync>>,
E: NewSvcExec<I::Conn, S::Future, S::Service, E, NoopWatcher>,
E: ConnStreamExec<<S::Service as HttpService<Body>>::Future, B>,
{
Server {
incoming: self.incoming,
make_service,
protocol: self.protocol.clone(),
}
}
}
#[cfg(feature = "tcp")]
#[cfg_attr(
docsrs,
doc(cfg(all(feature = "tcp", any(feature = "http1", feature = "http2"))))
)]
impl<E> Builder<AddrIncoming, E> {
/// Set whether TCP keepalive messages are enabled on accepted connections.
///
/// If `None` is specified, keepalive is disabled, otherwise the duration
/// specified will be the time to remain idle before sending TCP keepalive
/// probes.
pub fn tcp_keepalive(mut self, keepalive: Option<Duration>) -> Self {
self.incoming.set_keepalive(keepalive);
self
}
/// Set the value of `TCP_NODELAY` option for accepted connections.
pub fn tcp_nodelay(mut self, enabled: bool) -> Self {
self.incoming.set_nodelay(enabled);
self
}
/// Set whether to sleep on accept errors.
///
/// A possible scenario is that the process has hit the max open files
/// allowed, and so trying to accept a new connection will fail with
/// EMFILE. In some cases, it's preferable to just wait for some time, if
/// the application will likely close some files (or connections), and try
/// to accept the connection again. If this option is true, the error will
/// be logged at the error level, since it is still a big deal, and then
/// the listener will sleep for 1 second.
///
/// In other cases, hitting the max open files should be treat similarly
/// to being out-of-memory, and simply error (and shutdown). Setting this
/// option to false will allow that.
///
/// For more details see [`AddrIncoming::set_sleep_on_errors`]
pub fn tcp_sleep_on_accept_errors(mut self, val: bool) -> Self {
self.incoming.set_sleep_on_errors(val);
self
}
}
// Used by `Server` to optionally watch a `Connection` future.
//
// The regular `hyper::Server` just uses a `NoopWatcher`, which does
// not need to watch anything, and so returns the `Connection` untouched.
//
// The `Server::with_graceful_shutdown` needs to keep track of all active
// connections, and signal that they start to shutdown when prompted, so
// it has a `GracefulWatcher` implementation to do that.
pub trait Watcher<I, S: HttpService<Body>, E>: Clone {
type Future: Future<Output = crate::Result<()>>;
fn watch(&self, conn: UpgradeableConnection<I, S, E>) -> Self::Future;
}
#[allow(missing_debug_implementations)]
#[derive(Copy, Clone)]
pub struct NoopWatcher;
impl<I, S, E> Watcher<I, S, E> for NoopWatcher
where
I: AsyncRead + AsyncWrite + Unpin + Send + 'static,
S: HttpService<Body>,
E: ConnStreamExec<S::Future, S::ResBody>,
S::ResBody: 'static,
<S::ResBody as HttpBody>::Error: Into<Box<dyn StdError + Send + Sync>>,
{
type Future = UpgradeableConnection<I, S, E>;
fn watch(&self, conn: UpgradeableConnection<I, S, E>) -> Self::Future {
conn
}
}
// used by exec.rs
pub(crate) mod new_svc {
use std::error::Error as StdError;
use tokio::io::{AsyncRead, AsyncWrite};
use tracing::debug;
use super::{Connecting, Watcher};
use crate::body::{Body, HttpBody};
use crate::common::exec::ConnStreamExec;
use crate::common::{task, Future, Pin, Poll, Unpin};
use crate::service::HttpService;
use pin_project_lite::pin_project;
// This is a `Future<Item=(), Error=()>` spawned to an `Executor` inside
// the `Server`. By being a nameable type, we can be generic over the
// user's `Service::Future`, and thus an `Executor` can execute it.
//
// Doing this allows for the server to conditionally require `Send` futures,
// depending on the `Executor` configured.
//
// Users cannot import this type, nor the associated `NewSvcExec`. Instead,
// a blanket implementation for `Executor<impl Future>` is sufficient.
pin_project! {
#[allow(missing_debug_implementations)]
pub struct NewSvcTask<I, N, S: HttpService<Body>, E, W: Watcher<I, S, E>> {
#[pin]
state: State<I, N, S, E, W>,
}
}
pin_project! {
#[project = StateProj]
pub(super) enum State<I, N, S: HttpService<Body>, E, W: Watcher<I, S, E>> {
Connecting {
#[pin]
connecting: Connecting<I, N, E>,
watcher: W,
},
Connected {
#[pin]
future: W::Future,
},
}
}
impl<I, N, S: HttpService<Body>, E, W: Watcher<I, S, E>> NewSvcTask<I, N, S, E, W> {
pub(super) fn new(connecting: Connecting<I, N, E>, watcher: W) -> Self {
NewSvcTask {
state: State::Connecting {
connecting,
watcher,
},
}
}
}
impl<I, N, S, NE, B, E, W> Future for NewSvcTask<I, N, S, E, W>
where
I: AsyncRead + AsyncWrite + Unpin + Send + 'static,
N: Future<Output = Result<S, NE>>,
NE: Into<Box<dyn StdError + Send + Sync>>,
S: HttpService<Body, ResBody = B>,
B: HttpBody + 'static,
B::Error: Into<Box<dyn StdError + Send + Sync>>,
E: ConnStreamExec<S::Future, B>,
W: Watcher<I, S, E>,
{
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
// If it weren't for needing to name this type so the `Send` bounds
// could be projected to the `Serve` executor, this could just be
// an `async fn`, and much safer. Woe is me.
let mut me = self.project();
loop {
let next = {
match me.state.as_mut().project() {
StateProj::Connecting {
connecting,
watcher,
} => {
let res = ready!(connecting.poll(cx));
let conn = match res {
Ok(conn) => conn,
Err(err) => {
let err = crate::Error::new_user_make_service(err);
debug!("connecting error: {}", err);
return Poll::Ready(());
}
};
let future = watcher.watch(conn.with_upgrades());
State::Connected { future }
}
StateProj::Connected { future } => {
return future.poll(cx).map(|res| {
if let Err(err) = res {
debug!("connection error: {}", err);
}
});
}
}
};
me.state.set(next);
}
}
}
}
pin_project! {
/// A future building a new `Service` to a `Connection`.
///
/// Wraps the future returned from `MakeService` into one that returns
/// a `Connection`.
#[must_use = "futures do nothing unless polled"]
#[derive(Debug)]
#[cfg_attr(docsrs, doc(cfg(any(feature = "http1", feature = "http2"))))]
pub struct Connecting<I, F, E = Exec> {
#[pin]
future: F,
io: Option<I>,
protocol: Http_<E>,
}
}
impl<I, F, S, FE, E, B> Future for Connecting<I, F, E>
where
I: AsyncRead + AsyncWrite + Unpin,
F: Future<Output = Result<S, FE>>,
S: HttpService<Body, ResBody = B>,
B: HttpBody + 'static,
B::Error: Into<Box<dyn StdError + Send + Sync>>,
E: ConnStreamExec<S::Future, B>,
{
type Output = Result<Connection<I, S, E>, FE>;
fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
let mut me = self.project();
let service = ready!(me.future.poll(cx))?;
let io = Option::take(&mut me.io).expect("polled after complete");
Poll::Ready(Ok(me.protocol.serve_connection(io, service)))
}
}

View File

@@ -1,16 +0,0 @@
use std::fmt;
use crate::common::exec::Exec;
/// A listening HTTP server that accepts connections in both HTTP1 and HTTP2 by default.
///
/// Needs at least one of the `http1` and `http2` features to be activated to actually be useful.
pub struct Server<I, S, E = Exec> {
_marker: std::marker::PhantomData<(I, S, E)>,
}
impl<I: fmt::Debug, S: fmt::Debug> fmt::Debug for Server<I, S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Server").finish()
}
}

View File

@@ -1,128 +0,0 @@
use std::error::Error as StdError;
use pin_project_lite::pin_project;
use tokio::io::{AsyncRead, AsyncWrite};
use tracing::debug;
use super::accept::Accept;
use super::conn::UpgradeableConnection;
use super::server::{Server, Watcher};
use crate::body::{Body, HttpBody};
use crate::common::drain::{self, Draining, Signal, Watch, Watching};
use crate::common::exec::{ConnStreamExec, NewSvcExec};
use crate::common::{task, Future, Pin, Poll, Unpin};
use crate::service::{HttpService, MakeServiceRef};
pin_project! {
#[allow(missing_debug_implementations)]
pub struct Graceful<I, S, F, E> {
#[pin]
state: State<I, S, F, E>,
}
}
pin_project! {
#[project = StateProj]
pub(super) enum State<I, S, F, E> {
Running {
drain: Option<(Signal, Watch)>,
#[pin]
server: Server<I, S, E>,
#[pin]
signal: F,
},
Draining { draining: Draining },
}
}
impl<I, S, F, E> Graceful<I, S, F, E> {
pub(super) fn new(server: Server<I, S, E>, signal: F) -> Self {
let drain = Some(drain::channel());
Graceful {
state: State::Running {
drain,
server,
signal,
},
}
}
}
impl<I, IO, IE, S, B, F, E> Future for Graceful<I, S, F, E>
where
I: Accept<Conn = IO, Error = IE>,
IE: Into<Box<dyn StdError + Send + Sync>>,
IO: AsyncRead + AsyncWrite + Unpin + Send + 'static,
S: MakeServiceRef<IO, Body, ResBody = B>,
S::Error: Into<Box<dyn StdError + Send + Sync>>,
B: HttpBody + 'static,
B::Error: Into<Box<dyn StdError + Send + Sync>>,
F: Future<Output = ()>,
E: ConnStreamExec<<S::Service as HttpService<Body>>::Future, B>,
E: NewSvcExec<IO, S::Future, S::Service, E, GracefulWatcher>,
{
type Output = crate::Result<()>;
fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
let mut me = self.project();
loop {
let next = {
match me.state.as_mut().project() {
StateProj::Running {
drain,
server,
signal,
} => match signal.poll(cx) {
Poll::Ready(()) => {
debug!("signal received, starting graceful shutdown");
let sig = drain.take().expect("drain channel").0;
State::Draining {
draining: sig.drain(),
}
}
Poll::Pending => {
let watch = drain.as_ref().expect("drain channel").1.clone();
return server.poll_watch(cx, &GracefulWatcher(watch));
}
},
StateProj::Draining { ref mut draining } => {
return Pin::new(draining).poll(cx).map(Ok);
}
}
};
me.state.set(next);
}
}
}
#[allow(missing_debug_implementations)]
#[derive(Clone)]
pub struct GracefulWatcher(Watch);
impl<I, S, E> Watcher<I, S, E> for GracefulWatcher
where
I: AsyncRead + AsyncWrite + Unpin + Send + 'static,
S: HttpService<Body>,
E: ConnStreamExec<S::Future, S::ResBody>,
S::ResBody: 'static,
<S::ResBody as HttpBody>::Error: Into<Box<dyn StdError + Send + Sync>>,
{
type Future =
Watching<UpgradeableConnection<I, S, E>, fn(Pin<&mut UpgradeableConnection<I, S, E>>)>;
fn watch(&self, conn: UpgradeableConnection<I, S, E>) -> Self::Future {
self.0.clone().watch(conn, on_drain)
}
}
fn on_drain<I, S, E>(conn: Pin<&mut UpgradeableConnection<I, S, E>>)
where
S: HttpService<Body>,
S::Error: Into<Box<dyn StdError + Send + Sync>>,
I: AsyncRead + AsyncWrite + Unpin,
S::ResBody: HttpBody + 'static,
<S::ResBody as HttpBody>::Error: Into<Box<dyn StdError + Send + Sync>>,
E: ConnStreamExec<S::Future, S::ResBody>,
{
conn.graceful_shutdown()
}

View File

@@ -1,192 +0,0 @@
use std::fmt;
use std::io;
use std::net::{SocketAddr, TcpListener as StdTcpListener};
use std::time::Duration;
use tokio::net::{TcpListener, TcpStream};
use tokio::time::Sleep;
use tracing::{debug, error, trace};
use crate::common::{task, Future, Pin, Poll};
use super::accept::Accept;
/// A stream of connections from binding to an address.
#[must_use = "streams do nothing unless polled"]
pub struct AddrIncoming {
addr: SocketAddr,
listener: TcpListener,
sleep_on_errors: bool,
tcp_keepalive_timeout: Option<Duration>,
tcp_nodelay: bool,
timeout: Option<Pin<Box<Sleep>>>,
}
impl AddrIncoming {
pub(super) fn new(addr: &SocketAddr) -> crate::Result<Self> {
let std_listener = StdTcpListener::bind(addr).map_err(crate::Error::new_listen)?;
AddrIncoming::from_std(std_listener)
}
pub(super) fn from_std(std_listener: StdTcpListener) -> crate::Result<Self> {
// TcpListener::from_std doesn't set O_NONBLOCK
std_listener
.set_nonblocking(true)
.map_err(crate::Error::new_listen)?;
let listener = TcpListener::from_std(std_listener).map_err(crate::Error::new_listen)?;
AddrIncoming::from_listener(listener)
}
/// Creates a new `AddrIncoming` binding to provided socket address.
pub fn bind(addr: &SocketAddr) -> crate::Result<Self> {
AddrIncoming::new(addr)
}
/// Creates a new `AddrIncoming` from an existing `tokio::net::TcpListener`.
pub fn from_listener(listener: TcpListener) -> crate::Result<Self> {
let addr = listener.local_addr().map_err(crate::Error::new_listen)?;
Ok(AddrIncoming {
listener,
addr,
sleep_on_errors: true,
tcp_keepalive_timeout: None,
tcp_nodelay: false,
timeout: None,
})
}
/// Get the local address bound to this listener.
pub fn local_addr(&self) -> SocketAddr {
self.addr
}
/// Set whether TCP keepalive messages are enabled on accepted connections.
///
/// If `None` is specified, keepalive is disabled, otherwise the duration
/// specified will be the time to remain idle before sending TCP keepalive
/// probes.
pub fn set_keepalive(&mut self, keepalive: Option<Duration>) -> &mut Self {
self.tcp_keepalive_timeout = keepalive;
self
}
/// Set the value of `TCP_NODELAY` option for accepted connections.
pub fn set_nodelay(&mut self, enabled: bool) -> &mut Self {
self.tcp_nodelay = enabled;
self
}
/// Set whether to sleep on accept errors.
///
/// A possible scenario is that the process has hit the max open files
/// allowed, and so trying to accept a new connection will fail with
/// `EMFILE`. In some cases, it's preferable to just wait for some time, if
/// the application will likely close some files (or connections), and try
/// to accept the connection again. If this option is `true`, the error
/// will be logged at the `error` level, since it is still a big deal,
/// and then the listener will sleep for 1 second.
///
/// In other cases, hitting the max open files should be treat similarly
/// to being out-of-memory, and simply error (and shutdown). Setting
/// this option to `false` will allow that.
///
/// Default is `true`.
pub fn set_sleep_on_errors(&mut self, val: bool) {
self.sleep_on_errors = val;
}
fn poll_next_(&mut self, cx: &mut task::Context<'_>) -> Poll<io::Result<TcpStream>> {
// Check if a previous timeout is active that was set by IO errors.
if let Some(ref mut to) = self.timeout {
ready!(Pin::new(to).poll(cx));
}
self.timeout = None;
loop {
match ready!(self.listener.poll_accept(cx)) {
Ok((socket, _)) => {
if let Some(dur) = self.tcp_keepalive_timeout {
let socket = socket2::SockRef::from(&socket);
let conf = socket2::TcpKeepalive::new().with_time(dur);
if let Err(e) = socket.set_tcp_keepalive(&conf) {
trace!("error trying to set TCP keepalive: {}", e);
}
}
if let Err(e) = socket.set_nodelay(self.tcp_nodelay) {
trace!("error trying to set TCP nodelay: {}", e);
}
return Poll::Ready(Ok(socket));
}
Err(e) => {
// Connection errors can be ignored directly, continue by
// accepting the next request.
if is_connection_error(&e) {
debug!("accepted connection already errored: {}", e);
continue;
}
if self.sleep_on_errors {
error!("accept error: {}", e);
// Sleep 1s.
let mut timeout = Box::pin(tokio::time::sleep(Duration::from_secs(1)));
match timeout.as_mut().poll(cx) {
Poll::Ready(()) => {
// Wow, it's been a second already? Ok then...
continue;
}
Poll::Pending => {
self.timeout = Some(timeout);
return Poll::Pending;
}
}
} else {
return Poll::Ready(Err(e));
}
}
}
}
}
}
impl Accept for AddrIncoming {
type Conn = TcpStream;
type Error = io::Error;
fn poll_accept(
mut self: Pin<&mut Self>,
cx: &mut task::Context<'_>,
) -> Poll<Option<Result<Self::Conn, Self::Error>>> {
let result = ready!(self.poll_next_(cx));
Poll::Ready(Some(result))
}
}
/// This function defines errors that are per-connection. Which basically
/// means that if we get this error from `accept()` system call it means
/// next connection might be ready to be accepted.
///
/// All other errors will incur a timeout before next `accept()` is performed.
/// The timeout is useful to handle resource exhaustion errors like ENFILE
/// and EMFILE. Otherwise, could enter into tight loop.
fn is_connection_error(e: &io::Error) -> bool {
matches!(
e.kind(),
io::ErrorKind::ConnectionRefused
| io::ErrorKind::ConnectionAborted
| io::ErrorKind::ConnectionReset
)
}
impl fmt::Debug for AddrIncoming {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("AddrIncoming")
.field("addr", &self.addr)
.field("sleep_on_errors", &self.sleep_on_errors)
.field("tcp_keepalive_timeout", &self.tcp_keepalive_timeout)
.field("tcp_nodelay", &self.tcp_nodelay)
.finish()
}
}

View File

@@ -1,187 +0,0 @@
use std::error::Error as StdError;
use std::fmt;
use tokio::io::{AsyncRead, AsyncWrite};
use super::{HttpService, Service};
use crate::body::HttpBody;
use crate::common::{task, Future, Poll};
// The same "trait alias" as tower::MakeConnection, but inlined to reduce
// dependencies.
pub trait MakeConnection<Target>: self::sealed::Sealed<(Target,)> {
type Connection: AsyncRead + AsyncWrite;
type Error;
type Future: Future<Output = Result<Self::Connection, Self::Error>>;
fn poll_ready(&mut self, cx: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>>;
fn make_connection(&mut self, target: Target) -> Self::Future;
}
impl<S, Target> self::sealed::Sealed<(Target,)> for S where S: Service<Target> {}
impl<S, Target> MakeConnection<Target> for S
where
S: Service<Target>,
S::Response: AsyncRead + AsyncWrite,
{
type Connection = S::Response;
type Error = S::Error;
type Future = S::Future;
fn poll_ready(&mut self, cx: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>> {
Service::poll_ready(self, cx)
}
fn make_connection(&mut self, target: Target) -> Self::Future {
Service::call(self, target)
}
}
// Just a sort-of "trait alias" of `MakeService`, not to be implemented
// by anyone, only used as bounds.
pub trait MakeServiceRef<Target, ReqBody>: self::sealed::Sealed<(Target, ReqBody)> {
type ResBody: HttpBody;
type Error: Into<Box<dyn StdError + Send + Sync>>;
type Service: HttpService<ReqBody, ResBody = Self::ResBody, Error = Self::Error>;
type MakeError: Into<Box<dyn StdError + Send + Sync>>;
type Future: Future<Output = Result<Self::Service, Self::MakeError>>;
// Acting like a #[non_exhaustive] for associated types of this trait.
//
// Basically, no one outside of hyper should be able to set this type
// or declare bounds on it, so it should prevent people from creating
// trait objects or otherwise writing code that requires using *all*
// of the associated types.
//
// Why? So we can add new associated types to this alias in the future,
// if necessary.
type __DontNameMe: self::sealed::CantImpl;
fn poll_ready_ref(&mut self, cx: &mut task::Context<'_>) -> Poll<Result<(), Self::MakeError>>;
fn make_service_ref(&mut self, target: &Target) -> Self::Future;
}
impl<T, Target, E, ME, S, F, IB, OB> MakeServiceRef<Target, IB> for T
where
T: for<'a> Service<&'a Target, Error = ME, Response = S, Future = F>,
E: Into<Box<dyn StdError + Send + Sync>>,
ME: Into<Box<dyn StdError + Send + Sync>>,
S: HttpService<IB, ResBody = OB, Error = E>,
F: Future<Output = Result<S, ME>>,
IB: HttpBody,
OB: HttpBody,
{
type Error = E;
type Service = S;
type ResBody = OB;
type MakeError = ME;
type Future = F;
type __DontNameMe = self::sealed::CantName;
fn poll_ready_ref(&mut self, cx: &mut task::Context<'_>) -> Poll<Result<(), Self::MakeError>> {
self.poll_ready(cx)
}
fn make_service_ref(&mut self, target: &Target) -> Self::Future {
self.call(target)
}
}
impl<T, Target, S, B1, B2> self::sealed::Sealed<(Target, B1)> for T
where
T: for<'a> Service<&'a Target, Response = S>,
S: HttpService<B1, ResBody = B2>,
B1: HttpBody,
B2: HttpBody,
{
}
/// Create a `MakeService` from a function.
///
/// # Example
///
/// ```
/// # #[cfg(feature = "runtime")]
/// # async fn run() {
/// use std::convert::Infallible;
/// use hyper::{Body, Request, Response, Server};
/// use tokio::net::TcpStream;
/// use hyper::service::{make_service_fn, service_fn};
///
/// let addr = ([127, 0, 0, 1], 3000).into();
///
/// let make_svc = make_service_fn(|socket: &TcpStream| {
/// let remote_addr = socket.peer_addr().unwrap();
/// async move {
/// Ok::<_, Infallible>(service_fn(move |_: Request<Body>| async move {
/// Ok::<_, Infallible>(
/// Response::new(Body::from(format!("Hello, {}!", remote_addr)))
/// )
/// }))
/// }
/// });
///
/// // Then bind and serve...
/// let server = Server::bind(&addr)
/// .serve(make_svc);
///
/// // Finally, spawn `server` onto an Executor...
/// if let Err(e) = server.await {
/// eprintln!("server error: {}", e);
/// }
/// # }
/// # fn main() {}
/// ```
pub fn make_service_fn<F, Target, Ret>(f: F) -> MakeServiceFn<F>
where
F: FnMut(&Target) -> Ret,
Ret: Future,
{
MakeServiceFn { f }
}
/// `MakeService` returned from [`make_service_fn`]
#[derive(Clone, Copy)]
pub struct MakeServiceFn<F> {
f: F,
}
impl<'t, F, Ret, Target, Svc, MkErr> Service<&'t Target> for MakeServiceFn<F>
where
F: FnMut(&Target) -> Ret,
Ret: Future<Output = Result<Svc, MkErr>>,
MkErr: Into<Box<dyn StdError + Send + Sync>>,
{
type Error = MkErr;
type Response = Svc;
type Future = Ret;
fn poll_ready(&mut self, _cx: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, target: &'t Target) -> Self::Future {
(self.f)(target)
}
}
impl<F> fmt::Debug for MakeServiceFn<F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("MakeServiceFn").finish()
}
}
mod sealed {
pub trait Sealed<X> {}
#[allow(unreachable_pub)] // This is intentional.
pub trait CantImpl {}
#[allow(missing_debug_implementations)]
pub enum CantName {}
impl CantImpl for CantName {}
}

View File

@@ -10,10 +10,6 @@
//!
//! - `HttpService`: This is blanketly implemented for all types that
//! implement `Service<http::Request<B1>, Response = http::Response<B2>>`.
//! - `MakeService`: When a `Service` returns a new `Service` as its "response",
//! we consider it a `MakeService`. Again, blanketly implemented in those cases.
//! - `MakeConnection`: A `Service` that returns a "connection", a type that
//! implements `AsyncRead` and `AsyncWrite`.
//!
//! # HttpService
//!
@@ -24,32 +20,13 @@
//! The helper [`service_fn`](service_fn) should be sufficient for most cases, but
//! if you need to implement `Service` for a type manually, you can follow the example
//! in `service_struct_impl.rs`.
//!
//! # MakeService
//!
//! Since a `Service` is bound to a single connection, a [`Server`](crate::Server)
//! needs a way to make them as it accepts connections. This is what a
//! `MakeService` does.
//!
//! Resources that need to be shared by all `Service`s can be put into a
//! `MakeService`, and then passed to individual `Service`s when `call`
//! is called.
pub use tower_service::Service;
mod http;
mod make;
#[cfg(all(any(feature = "http1", feature = "http2"), feature = "client"))]
mod oneshot;
mod util;
pub(super) use self::http::HttpService;
#[cfg(all(any(feature = "http1", feature = "http2"), feature = "client"))]
pub(super) use self::make::MakeConnection;
#[cfg(all(any(feature = "http1", feature = "http2"), feature = "server"))]
pub(super) use self::make::MakeServiceRef;
#[cfg(all(any(feature = "http1", feature = "http2"), feature = "client"))]
pub(super) use self::oneshot::{oneshot, Oneshot};
pub(super) use self::http::HttpService;
pub use self::make::make_service_fn;
pub use self::util::service_fn;

View File

@@ -1,73 +0,0 @@
// TODO: Eventually to be replaced with tower_util::Oneshot.
use pin_project_lite::pin_project;
use tower_service::Service;
use crate::common::{task, Future, Pin, Poll};
pub(crate) fn oneshot<S, Req>(svc: S, req: Req) -> Oneshot<S, Req>
where
S: Service<Req>,
{
Oneshot {
state: State::NotReady { svc, req },
}
}
pin_project! {
// A `Future` consuming a `Service` and request, waiting until the `Service`
// is ready, and then calling `Service::call` with the request, and
// waiting for that `Future`.
#[allow(missing_debug_implementations)]
pub struct Oneshot<S: Service<Req>, Req> {
#[pin]
state: State<S, Req>,
}
}
pin_project! {
#[project = StateProj]
#[project_replace = StateProjOwn]
enum State<S: Service<Req>, Req> {
NotReady {
svc: S,
req: Req,
},
Called {
#[pin]
fut: S::Future,
},
Tmp,
}
}
impl<S, Req> Future for Oneshot<S, Req>
where
S: Service<Req>,
{
type Output = Result<S::Response, S::Error>;
fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
let mut me = self.project();
loop {
match me.state.as_mut().project() {
StateProj::NotReady { ref mut svc, .. } => {
ready!(svc.poll_ready(cx))?;
// fallthrough out of the match's borrow
}
StateProj::Called { fut } => {
return fut.poll(cx);
}
StateProj::Tmp => unreachable!(),
}
match me.state.as_mut().project_replace(State::Tmp) {
StateProjOwn::NotReady { mut svc, req } => {
me.state.set(State::Called { fut: svc.call(req) });
}
_ => unreachable!(),
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -21,15 +21,14 @@ use h2::client::SendRequest;
use h2::{RecvStream, SendStream};
use http::header::{HeaderName, HeaderValue};
use http_body_util::{combinators::BoxBody, BodyExt, StreamBody};
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt, ReadBuf};
use tokio::net::{TcpListener, TcpStream as TkTcpStream};
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::{TcpListener as TkTcpListener, TcpListener, TcpStream as TkTcpStream};
use hyper::body::HttpBody as _;
use hyper::client::Client;
use hyper::body::HttpBody;
use hyper::server::conn::Http;
use hyper::server::Server;
use hyper::service::{make_service_fn, service_fn};
use hyper::{Body, Request, Response, StatusCode, Version};
use hyper::service::service_fn;
use hyper::{Body, Method, Request, Response, StatusCode, Uri, Version};
mod support;
@@ -320,15 +319,11 @@ mod response_body_lengths {
#[tokio::test]
async fn http2_auto_response_with_known_length() {
use http_body::Body;
let server = serve();
let addr_str = format!("http://{}", server.addr());
server.reply().body("Hello, World!");
let client = Client::builder()
.http2_only(true)
.build_http::<hyper::Body>();
let client = TestClient::new().http2_only();
let uri = addr_str
.parse::<hyper::Uri>()
.expect("server addr should parse");
@@ -340,8 +335,6 @@ mod response_body_lengths {
#[tokio::test]
async fn http2_auto_response_with_conflicting_lengths() {
use http_body::Body;
let server = serve();
let addr_str = format!("http://{}", server.addr());
server
@@ -349,9 +342,7 @@ mod response_body_lengths {
.header("content-length", "10")
.body("Hello, World!");
let client = Client::builder()
.http2_only(true)
.build_http::<hyper::Body>();
let client = TestClient::new().http2_only();
let uri = addr_str
.parse::<hyper::Uri>()
.expect("server addr should parse");
@@ -363,15 +354,11 @@ mod response_body_lengths {
#[tokio::test]
async fn http2_implicit_empty_size_hint() {
use http_body::Body;
let server = serve();
let addr_str = format!("http://{}", server.addr());
server.reply();
let client = Client::builder()
.http2_only(true)
.build_http::<hyper::Body>();
let client = TestClient::new().http2_only();
let uri = addr_str
.parse::<hyper::Uri>()
.expect("server addr should parse");
@@ -1480,8 +1467,6 @@ async fn header_read_timeout_slow_writes_multiple_requests() {
#[tokio::test]
async fn upgrades() {
use tokio::io::{AsyncReadExt, AsyncWriteExt};
let _ = pretty_env_logger::try_init();
let listener = tcp_bind(&"127.0.0.1:0".parse().unwrap()).unwrap();
let addr = listener.local_addr().unwrap();
@@ -1539,8 +1524,6 @@ async fn upgrades() {
#[tokio::test]
async fn http_connect() {
use tokio::io::{AsyncReadExt, AsyncWriteExt};
let _ = pretty_env_logger::try_init();
let listener = tcp_bind(&"127.0.0.1:0".parse().unwrap()).unwrap();
let addr = listener.local_addr().unwrap();
@@ -1675,15 +1658,19 @@ async fn upgrades_ignored() {
future::ok::<_, hyper::Error>(Response::new(hyper::Body::empty()))
});
let (socket, _) = listener.accept().await.unwrap();
Http::new()
.serve_connection(socket, svc)
.with_upgrades()
.await
.expect("server task");
loop {
let (socket, _) = listener.accept().await.unwrap();
tokio::task::spawn(async move {
Http::new()
.serve_connection(socket, svc)
.with_upgrades()
.await
.expect("server task");
});
}
});
let client = hyper::Client::new();
let client = TestClient::new();
let url = format!("http://{}/", addr);
let make_req = || {
@@ -1705,8 +1692,6 @@ async fn upgrades_ignored() {
#[tokio::test]
async fn http_connect_new() {
use tokio::io::{AsyncReadExt, AsyncWriteExt};
let _ = pretty_env_logger::try_init();
let listener = tcp_bind(&"127.0.0.1:0".parse().unwrap()).unwrap();
let addr = listener.local_addr().unwrap();
@@ -1771,8 +1756,6 @@ async fn http_connect_new() {
#[tokio::test]
async fn h2_connect() {
use tokio::io::{AsyncReadExt, AsyncWriteExt};
let _ = pretty_env_logger::try_init();
let listener = tcp_bind(&"127.0.0.1:0".parse().unwrap()).unwrap();
let addr = listener.local_addr().unwrap();
@@ -1843,7 +1826,6 @@ async fn h2_connect() {
async fn h2_connect_multiplex() {
use futures_util::stream::FuturesUnordered;
use futures_util::StreamExt;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
let _ = pretty_env_logger::try_init();
let listener = tcp_bind(&"127.0.0.1:0".parse().unwrap()).unwrap();
@@ -1954,8 +1936,6 @@ async fn h2_connect_multiplex() {
#[tokio::test]
async fn h2_connect_large_body() {
use tokio::io::{AsyncReadExt, AsyncWriteExt};
let _ = pretty_env_logger::try_init();
let listener = tcp_bind(&"127.0.0.1:0".parse().unwrap()).unwrap();
let addr = listener.local_addr().unwrap();
@@ -2031,8 +2011,6 @@ async fn h2_connect_large_body() {
#[tokio::test]
async fn h2_connect_empty_frames() {
use tokio::io::{AsyncReadExt, AsyncWriteExt};
let _ = pretty_env_logger::try_init();
let listener = tcp_bind(&"127.0.0.1:0".parse().unwrap()).unwrap();
let addr = listener.local_addr().unwrap();
@@ -2225,8 +2203,8 @@ fn http1_response_with_http2_version() {
server.reply().version(hyper::Version::HTTP_2);
let client = TestClient::new();
rt.block_on({
let client = Client::new();
let uri = addr_str.parse().expect("server addr should parse");
client.get(uri)
})
@@ -2240,10 +2218,8 @@ fn try_h2() {
let rt = support::runtime();
let client = TestClient::new().http2_only();
rt.block_on({
let client = Client::builder()
.http2_only(true)
.build_http::<hyper::Body>();
let uri = addr_str.parse().expect("server addr should parse");
client.get(uri).map_ok(|_| ()).map_err(|_e| ())
@@ -2260,10 +2236,8 @@ fn http1_only() {
let rt = support::runtime();
let client = TestClient::new().http2_only();
rt.block_on({
let client = Client::builder()
.http2_only(true)
.build_http::<hyper::Body>();
let uri = addr_str.parse().expect("server addr should parse");
client.get(uri)
})
@@ -2283,9 +2257,8 @@ async fn http2_service_error_sends_reset_reason() {
let uri = addr_str.parse().expect("server addr should parse");
dbg!("start");
let err = dbg!(Client::builder()
.http2_only(true)
.build_http::<hyper::Body>()
let err = dbg!(TestClient::new()
.http2_only()
.get(uri)
.await
.expect_err("client.get"));
@@ -2314,9 +2287,8 @@ fn http2_body_user_error_sends_reset_reason() {
let err: hyper::Error = rt
.block_on(async move {
let client = Client::builder()
.http2_only(true)
.build_http::<hyper::Body>();
let client = TestClient::new().http2_only();
let uri = addr_str.parse().expect("server addr should parse");
let mut res = client.get(uri).await?;
@@ -2363,22 +2335,33 @@ async fn http2_service_poll_ready_error_sends_goaway() {
let _ = pretty_env_logger::try_init();
let server = hyper::Server::bind(&([127, 0, 0, 1], 0).into())
.http2_only(true)
.serve(make_service_fn(|_| async move {
Ok::<_, BoxError>(Http2ReadyErrorSvc)
}));
let listener = TkTcpListener::bind(SocketAddr::from(([127, 0, 0, 1], 0)))
.await
.unwrap();
let addr_str = format!("http://{}", server.local_addr());
let addr_str = format!("http://{}", listener.local_addr().unwrap());
tokio::task::spawn(async move {
server.await.expect("server");
loop {
tokio::select! {
res = listener.accept() => {
let (stream, _) = res.unwrap();
tokio::task::spawn(async move {
let mut http = Http::new();
http.http2_only(true);
let service = Http2ReadyErrorSvc;
http.serve_connection(stream, service).await.unwrap();
});
}
}
}
});
let uri = addr_str.parse().expect("server addr should parse");
let err = dbg!(Client::builder()
.http2_only(true)
.build_http::<hyper::Body>()
let err = dbg!(TestClient::new()
.http2_only()
.get(uri)
.await
.expect_err("client.get should fail"));
@@ -2948,9 +2931,9 @@ impl ServeOptions {
let (addr_tx, addr_rx) = mpsc::channel();
let (msg_tx, msg_rx) = mpsc::channel();
let (reply_tx, reply_rx) = spmc::channel();
let (shutdown_tx, shutdown_rx) = oneshot::channel();
let (shutdown_tx, mut shutdown_rx) = oneshot::channel();
let addr = ([127, 0, 0, 1], 0).into();
let addr: SocketAddr = ([127, 0, 0, 1], 0).into();
let thread_name = format!(
"test-server-{}",
@@ -2961,36 +2944,46 @@ impl ServeOptions {
let thread = thread::Builder::new()
.name(thread_name)
.spawn(move || {
support::runtime()
.block_on(async move {
let service = make_service_fn(|_| {
let msg_tx = msg_tx.clone();
let reply_rx = reply_rx.clone();
future::ok::<_, BoxError>(TestService {
tx: msg_tx,
reply: reply_rx,
})
});
support::runtime().block_on(async move {
let listener = TkTcpListener::bind(addr).await.unwrap();
let builder = Server::bind(&addr);
addr_tx
.send(listener.local_addr().unwrap())
.expect("server addr tx");
#[cfg(feature = "http1")]
let builder = builder
.http1_only(_options.http1_only)
.http1_keepalive(_options.keep_alive)
.http1_pipeline_flush(_options.pipeline);
loop {
let msg_tx = msg_tx.clone();
let reply_rx = reply_rx.clone();
let server = builder.serve(service);
tokio::select! {
res = listener.accept() => {
let (stream, _) = res.unwrap();
addr_tx.send(server.local_addr()).expect("server addr tx");
tokio::task::spawn(async move {
let mut http = Http::new();
server
.with_graceful_shutdown(async {
let _ = shutdown_rx.await;
})
.await
})
.expect("serve()");
#[cfg(feature = "http1")]
let http = http
.http1_only(_options.http1_only)
.http1_keep_alive(_options.keep_alive)
.pipeline_flush(_options.pipeline);
let msg_tx = msg_tx.clone();
let reply_rx = reply_rx.clone();
let service = TestService {
tx: msg_tx,
reply: reply_rx,
};
http.serve_connection(stream, service).await.unwrap();
});
}
_ = &mut shutdown_rx => {
break;
}
}
}
})
})
.expect("thread spawn");
@@ -3119,3 +3112,49 @@ impl Drop for Dropped {
self.0.store(true, Ordering::SeqCst);
}
}
struct TestClient {
http2_only: bool,
}
impl TestClient {
fn new() -> Self {
Self { http2_only: false }
}
fn http2_only(mut self) -> Self {
self.http2_only = true;
self
}
async fn get(&self, uri: Uri) -> Result<Response<Body>, hyper::Error> {
self.request(
Request::builder()
.uri(uri)
.method(Method::GET)
.body(Body::empty())
.unwrap(),
)
.await
}
async fn request(&self, req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
let host = req.uri().host().expect("uri has no host");
let port = req.uri().port_u16().expect("uri has no port");
let mut builder = hyper::client::conn::Builder::new();
builder.http2_only(self.http2_only);
let stream = TkTcpStream::connect(format!("{}:{}", host, port))
.await
.unwrap();
let (mut sender, conn) = builder.handshake(stream).await.unwrap();
tokio::task::spawn(async move {
conn.await.unwrap();
});
sender.send_request(req).await
}
}

View File

@@ -6,9 +6,12 @@ use std::sync::{
Arc, Mutex,
};
use hyper::client::HttpConnector;
use hyper::service::{make_service_fn, service_fn};
use hyper::{Body, Client, Request, Response, Server, Version};
use hyper::client::conn::Builder;
use hyper::server::conn::Http;
use tokio::net::{TcpListener, TcpStream};
use hyper::service::service_fn;
use hyper::{Body, Request, Response, Version};
pub use futures_util::{
future, FutureExt as _, StreamExt as _, TryFutureExt as _, TryStreamExt as _,
@@ -326,16 +329,20 @@ async fn async_test(cfg: __TestConfig) {
Version::HTTP_11
};
let connector = HttpConnector::new();
let client = Client::builder()
.http2_only(cfg.client_version == 2)
.build::<_, Body>(connector);
let http2_only = cfg.server_version == 2;
let serve_handles = Arc::new(Mutex::new(cfg.server_msgs));
let listener = TcpListener::bind(&SocketAddr::from(([127, 0, 0, 1], 0)))
.await
.unwrap();
let mut addr = listener.local_addr().unwrap();
let expected_connections = cfg.connections;
let mut cnt = 0;
let new_service = make_service_fn(move |_| {
tokio::task::spawn(async move {
let mut cnt = 0;
cnt += 1;
assert!(
cnt <= expected_connections,
@@ -344,98 +351,108 @@ async fn async_test(cfg: __TestConfig) {
cnt
);
// Move a clone into the service_fn
let serve_handles = serve_handles.clone();
future::ok::<_, hyper::Error>(service_fn(move |req: Request<Body>| {
let (sreq, sres) = serve_handles.lock().unwrap().remove(0);
loop {
let (stream, _) = listener.accept().await.expect("server error");
assert_eq!(req.uri().path(), sreq.uri, "client path");
assert_eq!(req.method(), &sreq.method, "client method");
assert_eq!(req.version(), version, "client version");
for func in &sreq.headers {
func(&req.headers());
}
let sbody = sreq.body;
hyper::body::to_bytes(req).map_ok(move |body| {
assert_eq!(body.as_ref(), sbody.as_slice(), "client body");
// Move a clone into the service_fn
let serve_handles = serve_handles.clone();
let service = service_fn(move |req: Request<Body>| {
let (sreq, sres) = serve_handles.lock().unwrap().remove(0);
let mut res = Response::builder()
.status(sres.status)
.body(Body::from(sres.body))
.expect("Response::build");
*res.headers_mut() = sres.headers;
res
})
}))
assert_eq!(req.uri().path(), sreq.uri, "client path");
assert_eq!(req.method(), &sreq.method, "client method");
assert_eq!(req.version(), version, "client version");
for func in &sreq.headers {
func(&req.headers());
}
let sbody = sreq.body;
hyper::body::to_bytes(req).map_ok(move |body| {
assert_eq!(body.as_ref(), sbody.as_slice(), "client body");
let mut res = Response::builder()
.status(sres.status)
.body(Body::from(sres.body))
.expect("Response::build");
*res.headers_mut() = sres.headers;
res
})
});
tokio::task::spawn(async move {
Http::new()
.http2_only(http2_only)
.serve_connection(stream, service)
.await
.expect("server error");
});
}
});
let server = hyper::Server::bind(&SocketAddr::from(([127, 0, 0, 1], 0)))
.http2_only(cfg.server_version == 2)
.serve(new_service);
let mut addr = server.local_addr();
tokio::task::spawn(server.map(|result| {
result.expect("server error");
}));
if cfg.proxy {
let (proxy_addr, proxy) = naive_proxy(ProxyConfig {
connections: cfg.connections,
dst: addr,
version: cfg.server_version,
});
})
.await;
tokio::task::spawn(proxy);
addr = proxy_addr;
}
let make_request = Arc::new(
move |client: &Client<HttpConnector>, creq: __CReq, cres: __CRes| {
let uri = format!("http://{}{}", addr, creq.uri);
let mut req = Request::builder()
.method(creq.method)
.uri(uri)
//.headers(creq.headers)
.body(creq.body.into())
.expect("Request::build");
*req.headers_mut() = creq.headers;
let cstatus = cres.status;
let cheaders = cres.headers;
let cbody = cres.body;
let make_request = Arc::new(move |creq: __CReq, cres: __CRes| {
let uri = format!("http://{}{}", addr, creq.uri);
let mut req = Request::builder()
.method(creq.method)
.uri(uri)
//.headers(creq.headers)
.body(creq.body.into())
.expect("Request::build");
*req.headers_mut() = creq.headers;
let cstatus = cres.status;
let cheaders = cres.headers;
let cbody = cres.body;
client
.request(req)
.and_then(move |res| {
assert_eq!(res.status(), cstatus, "server status");
assert_eq!(res.version(), version, "server version");
for func in &cheaders {
func(&res.headers());
}
hyper::body::to_bytes(res)
})
.map_ok(move |body| {
assert_eq!(body.as_ref(), cbody.as_slice(), "server body");
})
.map(|res| res.expect("client error"))
},
);
async move {
let stream = TcpStream::connect(addr).await.unwrap();
let (mut sender, conn) = hyper::client::conn::Builder::new()
.http2_only(http2_only)
.handshake::<TcpStream, Body>(stream)
.await
.unwrap();
tokio::task::spawn(async move {
if let Err(err) = conn.await {
panic!("{:?}", err);
}
});
let res = sender.send_request(req).await.unwrap();
assert_eq!(res.status(), cstatus, "server status");
assert_eq!(res.version(), version, "server version");
for func in &cheaders {
func(&res.headers());
}
let body = hyper::body::to_bytes(res).await.unwrap();
assert_eq!(body.as_ref(), cbody.as_slice(), "server body");
}
});
let client_futures: Pin<Box<dyn Future<Output = ()> + Send>> = if cfg.parallel {
let mut client_futures = vec![];
for (creq, cres) in cfg.client_msgs {
client_futures.push(make_request(&client, creq, cres));
client_futures.push(make_request(creq, cres));
}
drop(client);
Box::pin(future::join_all(client_futures).map(|_| ()))
} else {
let mut client_futures: Pin<Box<dyn Future<Output = Client<HttpConnector>> + Send>> =
Box::pin(future::ready(client));
let mut client_futures: Pin<Box<dyn Future<Output = ()> + Send>> =
Box::pin(future::ready(()));
for (creq, cres) in cfg.client_msgs {
let mk_request = make_request.clone();
client_futures = Box::pin(client_futures.then(move |client| {
let fut = mk_request(&client, creq, cres);
fut.map(move |()| client)
}));
client_futures = Box::pin(client_futures.then(move |_| mk_request(creq, cres)));
}
Box::pin(client_futures.map(|_| ()))
};
@@ -449,27 +466,75 @@ struct ProxyConfig {
version: usize,
}
fn naive_proxy(cfg: ProxyConfig) -> (SocketAddr, impl Future<Output = ()>) {
let client = Client::builder()
.http2_only(cfg.version == 2)
.build_http::<Body>();
async fn naive_proxy(cfg: ProxyConfig) -> (SocketAddr, impl Future<Output = ()>) {
let dst_addr = cfg.dst;
let max_connections = cfg.connections;
let counter = AtomicUsize::new(0);
let http2_only = cfg.version == 2;
let srv = Server::bind(&([127, 0, 0, 1], 0).into()).serve(make_service_fn(move |_| {
let prev = counter.fetch_add(1, Ordering::Relaxed);
assert!(max_connections > prev, "proxy max connections");
let client = client.clone();
future::ok::<_, hyper::Error>(service_fn(move |mut req| {
let uri = format!("http://{}{}", dst_addr, req.uri().path())
.parse()
.expect("proxy new uri parse");
*req.uri_mut() = uri;
client.request(req)
}))
}));
let proxy_addr = srv.local_addr();
(proxy_addr, srv.map(|res| res.expect("proxy error")))
let listener = TcpListener::bind(SocketAddr::from(([127, 0, 0, 1], 0)))
.await
.unwrap();
let proxy_addr = listener.local_addr().unwrap();
let fut = async move {
tokio::task::spawn(async move {
let prev = counter.fetch_add(1, Ordering::Relaxed);
assert!(max_connections > prev, "proxy max connections");
loop {
let (stream, _) = listener.accept().await.unwrap();
let service = service_fn(move |mut req| {
async move {
let uri = format!("http://{}{}", dst_addr, req.uri().path())
.parse()
.expect("proxy new uri parse");
*req.uri_mut() = uri;
// Make the client request
let uri = req.uri().host().expect("uri has no host");
let port = req.uri().port_u16().expect("uri has no port");
let stream = TcpStream::connect(format!("{}:{}", uri, port))
.await
.unwrap();
let mut builder = Builder::new();
builder.http2_only(http2_only);
let (mut sender, conn) = builder.handshake(stream).await.unwrap();
tokio::task::spawn(async move {
if let Err(err) = conn.await {
panic!("{:?}", err);
}
});
let resp = sender.send_request(req).await?;
let (mut parts, body) = resp.into_parts();
// Remove the Connection header for HTTP/1.1 proxy connections.
if !http2_only {
parts.headers.remove("Connection");
}
let mut builder = Response::builder().status(parts.status);
*builder.headers_mut().unwrap() = parts.headers;
Result::<Response<Body>, hyper::Error>::Ok(builder.body(body).unwrap())
}
});
Http::new()
.http2_only(http2_only)
.serve_connection(stream, service)
.await
.unwrap();
}
});
};
(proxy_addr, fut)
}