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: strategy:
matrix: matrix:
bench: bench:
- connect #- connect
- end_to_end #- end_to_end
- pipeline #- pipeline
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2

View File

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

View File

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

View File

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

View File

@@ -4,54 +4,59 @@
extern crate test; extern crate test;
use std::io::{Read, Write}; use std::io::{Read, Write};
use std::net::{TcpListener, TcpStream}; use std::net::{SocketAddr, TcpListener, TcpStream};
use std::sync::mpsc; use std::sync::mpsc;
use std::time::Duration; use std::time::Duration;
use futures_util::{stream, StreamExt}; use futures_util::{stream, StreamExt};
use http_body_util::StreamBody; use http_body_util::{BodyExt, StreamBody};
use tokio::sync::oneshot; use tokio::sync::oneshot;
use hyper::service::{make_service_fn, service_fn}; use hyper::server::conn::Http;
use hyper::{Response, Server}; use hyper::service::service_fn;
use hyper::Response;
macro_rules! bench_server { macro_rules! bench_server {
($b:ident, $header:expr, $body:expr) => {{ ($b:ident, $header:expr, $body:expr) => {{
let _ = pretty_env_logger::try_init(); let _ = pretty_env_logger::try_init();
let (_until_tx, until_rx) = oneshot::channel::<()>(); let (_until_tx, until_rx) = oneshot::channel::<()>();
let addr = { let addr = {
let (addr_tx, addr_rx) = mpsc::channel(); let (addr_tx, addr_rx) = mpsc::channel();
std::thread::spawn(move || { std::thread::spawn(move || {
let addr = "127.0.0.1:0".parse().unwrap(); let addr: SocketAddr = "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 rt = tokio::runtime::Builder::new_current_thread() let rt = tokio::runtime::Builder::new_current_thread()
.enable_all() .enable_all()
.build() .build()
.expect("rt 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 { Http::new()
until_rx.await.ok(); .serve_connection(
}); stream,
rt.block_on(async move { service_fn(|_| async {
if let Err(e) = graceful.await { Ok::<_, hyper::Error>(
panic!("server error: {}", e); 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() addr_rx.recv().unwrap()
@@ -100,9 +105,11 @@ fn throughput_fixedsize_large_payload(b: &mut test::Bencher) {
#[bench] #[bench]
fn throughput_fixedsize_many_chunks(b: &mut test::Bencher) { 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 _; 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) { fn throughput_chunked_many_chunks(b: &mut test::Bencher) {
bench_server!(b, ("transfer-encoding", "chunked"), || { bench_server!(b, ("transfer-encoding", "chunked"), || {
static S: &[&[u8]] = &[&[b'x'; 1_000] as &[u8]; 1_000] as _; 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)] #![warn(rust_2018_idioms)]
use std::env; use std::env;
use hyper::{body::HttpBody as _, Client}; use hyper::{body::HttpBody as _, Body, Request};
use tokio::io::{self, AsyncWriteExt as _}; use tokio::io::{self, AsyncWriteExt as _};
use tokio::net::TcpStream;
// A simple type alias so as to DRY. // A simple type alias so as to DRY.
type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>; 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<()> { 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!("Response: {}", res.status());
println!("Headers: {:#?}\n", res.headers()); println!("Headers: {:#?}\n", res.headers());

View File

@@ -1,9 +1,10 @@
#![deny(warnings)] #![deny(warnings)]
#![warn(rust_2018_idioms)] #![warn(rust_2018_idioms)]
use hyper::body::Buf; use hyper::Body;
use hyper::Client; use hyper::{body::Buf, Request};
use serde::Deserialize; use serde::Deserialize;
use tokio::net::TcpStream;
// A simple type alias so as to DRY. // A simple type alias so as to DRY.
type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>; 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>> { 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... // 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 // asynchronously aggregate the chunks of the body
let body = hyper::body::aggregate(res).await?; let body = hyper::body::aggregate(res).await?;

View File

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

View File

@@ -1,51 +1,63 @@
#![deny(warnings)] #![deny(warnings)]
use hyper::service::{make_service_fn, service_fn}; use hyper::{server::conn::Http, service::service_fn};
use hyper::{Client, Error, Server};
use std::net::SocketAddr; use std::net::SocketAddr;
use tokio::net::{TcpListener, TcpStream};
#[tokio::main] #[tokio::main]
async fn main() { async fn main() -> Result<(), Box<dyn std::error::Error>> {
pretty_env_logger::init(); 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 out_addr: SocketAddr = ([127, 0, 0, 1], 3000).into();
let client_main = Client::new();
let out_addr_clone = out_addr.clone(); let out_addr_clone = out_addr.clone();
// The closure inside `make_service_fn` is run for each connection, let listener = TcpListener::bind(in_addr).await?;
// 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);
println!("Listening on http://{}", in_addr); println!("Listening on http://{}", in_addr);
println!("Proxying on http://{}", out_addr); println!("Proxying on http://{}", out_addr);
if let Err(e) = server.await { loop {
eprintln!("server error: {}", e); 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)] #![deny(warnings)]
use std::convert::Infallible; use std::convert::Infallible;
use std::net::SocketAddr;
use hyper::service::{make_service_fn, service_fn}; use hyper::server::conn::Http;
use hyper::{Body, Request, Response, Server}; use hyper::service::service_fn;
use hyper::{Body, Request, Response};
use tokio::net::TcpListener;
async fn hello(_: Request<Body>) -> Result<Response<Body>, Infallible> { async fn hello(_: Request<Body>) -> Result<Response<Body>, Infallible> {
Ok(Response::new(Body::from("Hello World!"))) 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>> { pub async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
pretty_env_logger::init(); pretty_env_logger::init();
// For every connection, we must make a `Service` to handle all let addr: SocketAddr = ([127, 0, 0, 1], 3000).into();
// 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 listener = TcpListener::bind(addr).await?;
println!("Listening on http://{}", addr); println!("Listening on http://{}", addr);
loop {
let (stream, _) = listener.accept().await?;
server.await?; tokio::task::spawn(async move {
if let Err(err) = Http::new()
Ok(()) .serve_connection(stream, service_fn(hello))
.await
{
println!("Error serving connection: {:?}", err);
}
});
}
} }

View File

@@ -1,15 +1,14 @@
#![deny(warnings)] #![deny(warnings)]
use std::convert::Infallible;
use std::net::SocketAddr; 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::upgrade::Upgraded;
use hyper::{Body, Client, Method, Request, Response, Server}; use hyper::{Body, Method, Request, Response};
use tokio::net::TcpStream; use tokio::net::{TcpListener, TcpStream};
type HttpClient = Client<hyper::client::HttpConnector>;
// To try this example: // To try this example:
// 1. cargo run --example http_proxy // 1. cargo run --example http_proxy
@@ -19,32 +18,29 @@ type HttpClient = Client<hyper::client::HttpConnector>;
// 3. send requests // 3. send requests
// $ curl -i https://www.some_domain.com/ // $ curl -i https://www.some_domain.com/
#[tokio::main] #[tokio::main]
async fn main() { async fn main() -> Result<(), Box<dyn std::error::Error>> {
let addr = SocketAddr::from(([127, 0, 0, 1], 8100)); let addr = SocketAddr::from(([127, 0, 0, 1], 8100));
let client = Client::builder() let listener = TcpListener::bind(addr).await?;
.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);
println!("Listening on http://{}", addr); println!("Listening on http://{}", addr);
if let Err(e) = server.await { loop {
eprintln!("server error: {}", e); 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); println!("req: {:?}", req);
if Method::CONNECT == req.method() { if Method::CONNECT == req.method() {
@@ -82,7 +78,24 @@ async fn proxy(client: HttpClient, req: Request<Body>) -> Result<Response<Body>,
Ok(resp) Ok(resp)
} }
} else { } 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)] #![deny(warnings)]
#![warn(rust_2018_idioms)] #![warn(rust_2018_idioms)]
use std::net::SocketAddr;
use futures_util::future::join; use futures_util::future::join;
use hyper::service::{make_service_fn, service_fn}; use hyper::server::conn::Http;
use hyper::{Body, Request, Response, Server}; use hyper::service::service_fn;
use hyper::{Body, Request, Response};
use tokio::net::TcpListener;
static INDEX1: &[u8] = b"The 1st service!"; static INDEX1: &[u8] = b"The 1st service!";
static INDEX2: &[u8] = b"The 2nd 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>> { async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
pretty_env_logger::init(); pretty_env_logger::init();
let addr1 = ([127, 0, 0, 1], 1337).into(); let addr1: SocketAddr = ([127, 0, 0, 1], 1337).into();
let addr2 = ([127, 0, 0, 1], 1338).into(); let addr2: SocketAddr = ([127, 0, 0, 1], 1338).into();
let srv1 = Server::bind(&addr1).serve(make_service_fn(|_| async { let srv1 = async move {
Ok::<_, hyper::Error>(service_fn(index1)) let listener = TcpListener::bind(addr1).await.unwrap();
})); loop {
let (stream, _) = listener.accept().await.unwrap();
let srv2 = Server::bind(&addr2).serve(make_service_fn(|_| async { tokio::task::spawn(async move {
Ok::<_, hyper::Error>(service_fn(index2)) 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); 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 // #![deny(warnings)] // FIXME: https://github.com/rust-lang/rust/issues/62411
#![warn(rust_2018_idioms)] #![warn(rust_2018_idioms)]
use hyper::service::{make_service_fn, service_fn}; use hyper::server::conn::Http;
use hyper::{Body, Method, Request, Response, Server, StatusCode}; use hyper::service::service_fn;
use hyper::{Body, Method, Request, Response, StatusCode};
use tokio::net::TcpListener;
use std::collections::HashMap; use std::collections::HashMap;
use std::net::SocketAddr;
use url::form_urlencoded; 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>"; 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>> { async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
pretty_env_logger::init(); pretty_env_logger::init();
let addr = ([127, 0, 0, 1], 1337).into(); let addr: SocketAddr = ([127, 0, 0, 1], 1337).into();
let server = Server::bind(&addr).serve(make_service_fn(|_| async {
Ok::<_, hyper::Error>(service_fn(param_example))
}));
let listener = TcpListener::bind(addr).await?;
println!("Listening on http://{}", addr); println!("Listening on http://{}", addr);
loop {
let (stream, _) = listener.accept().await?;
server.await?; tokio::task::spawn(async move {
if let Err(err) = Http::new()
Ok(()) .serve_connection(stream, service_fn(param_example))
.await
{
println!("Error serving connection: {:?}", err);
}
});
}
} }

View File

@@ -1,26 +1,36 @@
#![deny(warnings)] #![deny(warnings)]
use hyper::service::{make_service_fn, service_fn}; use std::net::SocketAddr;
use hyper::{Body, Method, Request, Response, Result, Server, StatusCode};
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 INDEX: &str = "examples/send_file_index.html";
static NOTFOUND: &[u8] = b"Not Found"; static NOTFOUND: &[u8] = b"Not Found";
#[tokio::main] #[tokio::main]
async fn main() { async fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
pretty_env_logger::init(); pretty_env_logger::init();
let addr = "127.0.0.1:1337".parse().unwrap(); let addr: SocketAddr = "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 listener = TcpListener::bind(addr).await?;
println!("Listening on http://{}", addr); println!("Listening on http://{}", addr);
if let Err(e) = server.await { loop {
eprintln!("server error: {}", e); 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::service::Service;
use hyper::{Body, Request, Response, Server}; use hyper::{Body, Request, Response};
use tokio::net::TcpListener;
use std::future::Future; use std::future::Future;
use std::net::SocketAddr;
use std::pin::Pin; use std::pin::Pin;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
@@ -9,13 +12,23 @@ type Counter = i32;
#[tokio::main] #[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> { 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); println!("Listening on http://{}", addr);
server.await?; loop {
Ok(()) 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 { struct Svc {
@@ -54,23 +67,3 @@ impl Service<Request<Body>> for Svc {
Box::pin(async { res }) 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)] #![deny(warnings)]
use hyper::server::conn::Http;
use std::cell::Cell; use std::cell::Cell;
use std::net::SocketAddr;
use std::rc::Rc; use std::rc::Rc;
use tokio::sync::oneshot; use tokio::net::TcpListener;
use hyper::body::{Bytes, HttpBody}; use hyper::body::{Bytes, HttpBody};
use hyper::header::{HeaderMap, HeaderValue}; use hyper::header::{HeaderMap, HeaderValue};
use hyper::service::{make_service_fn, service_fn}; use hyper::service::service_fn;
use hyper::{Error, Response, Server}; use hyper::{Error, Response};
use std::marker::PhantomData; use std::marker::PhantomData;
use std::pin::Pin; use std::pin::Pin;
use std::task::{Context, Poll}; 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(); pretty_env_logger::init();
// Configure a runtime that runs everything on the current thread // 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... // Combine it with a `LocalSet, which means it can spawn !Send futures...
let local = tokio::task::LocalSet::new(); let local = tokio::task::LocalSet::new();
local.block_on(&rt, run()); local.block_on(&rt, run())
} }
async fn run() { async fn run() -> Result<(), Box<dyn std::error::Error>> {
let addr = ([127, 0, 0, 1], 3000).into(); let addr: SocketAddr = ([127, 0, 0, 1], 3000).into();
// Using a !Send request counter is fine on 1 thread... // Using a !Send request counter is fine on 1 thread...
let counter = Rc::new(Cell::new(0)); 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... // For each connection, clone the counter to use in our service...
let cnt = counter.clone(); let cnt = counter.clone();
async move { let service = service_fn(move |_| {
Ok::<_, Error>(service_fn(move |_| { let prev = cnt.get();
let prev = cnt.get(); cnt.set(prev + 1);
cnt.set(prev + 1); let value = cnt.get();
let value = cnt.get(); async move { Ok::<_, Error>(Response::new(Body::from(format!("Request #{}", value)))) }
async move { Ok::<_, Error>(Response::new(Body::from(format!("Request #{}", value)))) } });
}))
}
});
let server = Server::bind(&addr).executor(LocalExec).serve(make_service); tokio::task::spawn_local(async move {
if let Err(err) = Http::new()
// Just shows that with_graceful_shutdown compiles with !Send, .with_executor(LocalExec)
// !Sync HttpBody. .serve_connection(stream, service)
let (_tx, rx) = oneshot::channel::<()>(); .await
let server = server.with_graceful_shutdown(async move { {
rx.await.ok(); println!("Error serving connection: {:?}", err);
}); }
});
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);
} }
} }

View File

@@ -1,52 +1,46 @@
#![deny(warnings)] #![deny(warnings)]
use std::net::SocketAddr;
use std::sync::{ use std::sync::{
atomic::{AtomicUsize, Ordering}, atomic::{AtomicUsize, Ordering},
Arc, Arc,
}; };
use hyper::service::{make_service_fn, service_fn}; use hyper::{server::conn::Http, service::service_fn};
use hyper::{Body, Error, Response, Server}; use hyper::{Body, Error, Response};
use tokio::net::TcpListener;
#[tokio::main] #[tokio::main]
async fn main() { async fn main() -> Result<(), Box<dyn std::error::Error>> {
pretty_env_logger::init(); 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 // 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. // with each request, and we send its value back in the response.
let counter = Arc::new(AtomicUsize::new(0)); let counter = Arc::new(AtomicUsize::new(0));
// The closure inside `make_service_fn` is run for each connection, let listener = TcpListener::bind(addr).await?;
// creating a 'service' to handle requests for that specific connection. println!("Listening on http://{}", addr);
let make_service = make_service_fn(move |_| { loop {
// While the state was moved into the make_service closure, let (stream, _) = listener.accept().await?;
// we need to clone it here because this closure is called
// once for every connection.
//
// Each connection could send multiple requests, so // Each connection could send multiple requests, so
// the `Service` needs a clone to handle later requests. // the `Service` needs a clone to handle later requests.
let counter = counter.clone(); let counter = counter.clone();
async move { // This is the `Service` that will handle the connection.
// This is the `Service` that will handle the connection. // `service_fn` is a helper to convert a function that
// `service_fn` is a helper to convert a function that // returns a Response into a `Service`.
// returns a Response into a `Service`. let service = service_fn(move |_req| {
Ok::<_, Error>(service_fn(move |_req| { // Get the current count, and also increment by 1, in a single
// Get the current count, and also increment by 1, in a single // atomic operation.
// atomic operation. let count = counter.fetch_add(1, Ordering::AcqRel);
let count = counter.fetch_add(1, Ordering::AcqRel); async move { Ok::<_, Error>(Response::new(Body::from(format!("Request #{}", count)))) }
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)] #![deny(warnings)]
use std::net::SocketAddr;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
use futures_util::future; use futures_util::future;
use hyper::server::conn::Http;
use hyper::service::Service; use hyper::service::Service;
use hyper::{Body, Request, Response, Server}; use hyper::{Body, Request, Response};
use tokio::net::TcpListener;
const ROOT: &str = "/"; 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] #[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> { async fn main() -> Result<(), Box<dyn std::error::Error>> {
pretty_env_logger::init(); pretty_env_logger::init();
let addr = "127.0.0.1:1337".parse().unwrap(); let addr: SocketAddr = "127.0.0.1:1337".parse().unwrap();
let server = Server::bind(&addr).serve(MakeSvc);
let listener = TcpListener::bind(addr).await?;
println!("Listening on http://{}", addr); 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. // Note: `hyper::upgrade` docs link to this upgrade.
use std::str; use std::str;
use hyper::server::conn::Http;
use tokio::io::{AsyncReadExt, AsyncWriteExt}; 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::header::{HeaderValue, UPGRADE};
use hyper::service::{make_service_fn, service_fn}; use hyper::service::service_fn;
use hyper::upgrade::Upgraded; use hyper::upgrade::Upgraded;
use hyper::{Body, Client, Request, Response, Server, StatusCode}; use hyper::{Body, Request, Response, StatusCode};
use std::net::SocketAddr; use std::net::SocketAddr;
// A simple type alias so as to DRY. // A simple type alias so as to DRY.
@@ -92,7 +94,17 @@ async fn client_upgrade_request(addr: SocketAddr) -> Result<()> {
.body(Body::empty()) .body(Body::empty())
.unwrap(); .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 { if res.status() != StatusCode::SWITCHING_PROTOCOLS {
panic!("Our server didn't upgrade: {}", res.status()); 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 // 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 // it, so the exact port isn't important. Instead, let the OS give us an
// unused port. // unused port.
let addr = ([127, 0, 0, 1], 0).into(); let addr: SocketAddr = ([127, 0, 0, 1], 0).into();
let make_service = let listener = TcpListener::bind(addr).await.expect("failed to bind");
make_service_fn(|_| async { Ok::<_, hyper::Error>(service_fn(server_upgrade)) });
let server = Server::bind(&addr).serve(make_service);
// We need the assigned address for the client to send it messages. // 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, // For this example, a oneshot is used to signal that after 1 request,
// the server should be shutdown. // the server should be shutdown.
let (tx, rx) = oneshot::channel::<()>(); let (tx, mut rx) = watch::channel(false);
let server = server.with_graceful_shutdown(async move {
rx.await.ok();
});
// Spawn server on the default executor, // Spawn server on the default executor,
// which is usually a thread-pool from tokio default runtime. // which is usually a thread-pool from tokio default runtime.
tokio::task::spawn(async move { tokio::task::spawn(async move {
if let Err(e) = server.await { loop {
eprintln!("server error: {}", e); 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 // Complete the oneshot so that the server stops
// listening and the process can close down. // listening and the process can close down.
let _ = tx.send(()); let _ = tx.send(true);
} }

View File

@@ -1,9 +1,12 @@
#![deny(warnings)] #![deny(warnings)]
use std::net::SocketAddr;
use bytes::Buf; use bytes::Buf;
use hyper::client::HttpConnector; use hyper::server::conn::Http;
use hyper::service::{make_service_fn, service_fn}; use hyper::service::service_fn;
use hyper::{header, Body, Client, Method, Request, Response, Server, StatusCode}; use hyper::{header, Body, Method, Request, Response, StatusCode};
use tokio::net::{TcpListener, TcpStream};
type GenericError = Box<dyn std::error::Error + Send + Sync>; type GenericError = Box<dyn std::error::Error + Send + Sync>;
type Result<T> = std::result::Result<T, GenericError>; 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 POST_DATA: &str = r#"{"original": "data"}"#;
static URL: &str = "http://127.0.0.1:1337/json_api"; 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() let req = Request::builder()
.method(Method::POST) .method(Method::POST)
.uri(URL) .uri(URL)
@@ -22,7 +25,19 @@ async fn client_request_response(client: &Client<HttpConnector>) -> Result<Respo
.body(POST_DATA.into()) .body(POST_DATA.into())
.unwrap(); .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(); let res_body = web_res.into_body();
@@ -60,13 +75,10 @@ async fn api_get_response() -> Result<Response<Body>> {
Ok(res) Ok(res)
} }
async fn response_examples( async fn response_examples(req: Request<Body>) -> Result<Response<Body>> {
req: Request<Body>,
client: Client<HttpConnector>,
) -> Result<Response<Body>> {
match (req.method(), req.uri().path()) { match (req.method(), req.uri().path()) {
(&Method::GET, "/") | (&Method::GET, "/index.html") => Ok(Response::new(INDEX.into())), (&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::POST, "/json_api") => api_post_response(req).await,
(&Method::GET, "/json_api") => api_get_response().await, (&Method::GET, "/json_api") => api_get_response().await,
_ => { _ => {
@@ -83,27 +95,19 @@ async fn response_examples(
async fn main() -> Result<()> { async fn main() -> Result<()> {
pretty_env_logger::init(); pretty_env_logger::init();
let addr = "127.0.0.1:1337".parse().unwrap(); let addr: SocketAddr = "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 listener = TcpListener::bind(&addr).await?;
println!("Listening on http://{}", addr); 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 super::DecodedLength;
use crate::common::Future; use crate::common::Future;
#[cfg(all(feature = "client", any(feature = "http1", feature = "http2")))]
use crate::common::Never;
use crate::common::{task, watch, Pin, Poll}; use crate::common::{task, watch, Pin, Poll};
#[cfg(all(feature = "http2", any(feature = "client", feature = "server")))] #[cfg(all(feature = "http2", any(feature = "client", feature = "server")))]
use crate::proto::h2::ping; use crate::proto::h2::ping;
@@ -29,9 +27,6 @@ type TrailersSender = oneshot::Sender<HeaderMap>;
#[must_use = "streams do nothing unless polled"] #[must_use = "streams do nothing unless polled"]
pub struct Body { pub struct Body {
kind: Kind, 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 { enum Kind {
@@ -52,34 +47,6 @@ enum Kind {
Ffi(crate::ffi::UserBody), 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()`]. /// A sender half created through [`Body::channel()`].
/// ///
/// Useful when wanting to stream chunks from another thread. /// Useful when wanting to stream chunks from another thread.
@@ -153,7 +120,7 @@ impl Body {
} }
fn new(kind: Kind) -> Body { fn new(kind: Kind) -> Body {
Body { kind, extra: None } Body { kind }
} }
#[cfg(all(feature = "http2", any(feature = "client", feature = "server")))] #[cfg(all(feature = "http2", any(feature = "client", feature = "server")))]
@@ -176,62 +143,6 @@ impl Body {
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")] #[cfg(feature = "ffi")]
pub(crate) fn as_ffi_mut(&mut self) -> &mut crate::ffi::UserBody { pub(crate) fn as_ffi_mut(&mut self) -> &mut crate::ffi::UserBody {
match self.kind { match self.kind {
@@ -313,7 +224,7 @@ impl HttpBody for Body {
mut self: Pin<&mut Self>, mut self: Pin<&mut Self>,
cx: &mut task::Context<'_>, cx: &mut task::Context<'_>,
) -> Poll<Option<Result<Self::Data, Self::Error>>> { ) -> Poll<Option<Result<Self::Data, Self::Error>>> {
self.poll_eof(cx) self.poll_inner(cx)
} }
fn poll_trailers( fn poll_trailers(
@@ -608,6 +519,7 @@ mod tests {
); );
} }
#[cfg(not(miri))]
#[tokio::test] #[tokio::test]
async fn channel_abort() { async fn channel_abort() {
let (tx, mut rx) = Body::channel(); let (tx, mut rx) = Body::channel();
@@ -618,6 +530,7 @@ mod tests {
assert!(err.is_body_write_aborted(), "{:?}", err); assert!(err.is_body_write_aborted(), "{:?}", err);
} }
#[cfg(not(miri))]
#[tokio::test] #[tokio::test]
async fn channel_abort_when_buffer_is_full() { async fn channel_abort_when_buffer_is_full() {
let (mut tx, mut rx) = Body::channel(); let (mut tx, mut rx) = Body::channel();
@@ -644,6 +557,7 @@ mod tests {
assert_eq!(chunk2, "chunk 2"); assert_eq!(chunk2, "chunk 2");
} }
#[cfg(not(miri))]
#[tokio::test] #[tokio::test]
async fn channel_empty() { async fn channel_empty() {
let (_, mut rx) = Body::channel(); let (_, mut rx) = Body::channel();

View File

@@ -17,17 +17,11 @@ use super::HttpBody;
/// # Example /// # Example
/// ///
/// ``` /// ```
/// # #[cfg(all(feature = "client", feature = "tcp", any(feature = "http1", feature = "http2")))]
/// # async fn doc() -> hyper::Result<()> { /// # async fn doc() -> hyper::Result<()> {
/// use hyper::{body::HttpBody}; /// # use hyper::{Body, Response};
/// /// # use hyper::body::HttpBody;
/// # let request = hyper::Request::builder() /// #
/// # .method(hyper::Method::POST) /// let response = Response::new(Body::from("response body"));
/// # .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?;
/// ///
/// const MAX_ALLOWED_RESPONSE_SIZE: u64 = 1024; /// 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. /// The sender side of an established connection.
pub struct SendRequest<B> { 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. /// 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. /// Polls to determine whether this sender can be used yet for a request.
/// ///
/// If the associated connection is closed, this returns an Error. /// If the associated connection is closed, this returns an Error.
pub fn poll_ready(&mut self, cx: &mut task::Context<'_>) -> Poll<crate::Result<()>> { pub fn poll_ready(&mut self, _cx: &mut task::Context<'_>) -> Poll<crate::Result<()>> {
self.dispatch.poll_ready(cx) 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 { pub(super) fn is_ready(&self) -> bool {
self.dispatch.is_ready() self.dispatch.is_ready()
} }
*/
pub(super) fn is_closed(&self) -> bool { pub(super) fn is_closed(&self) -> bool {
self.dispatch.is_closed() self.dispatch.is_closed()
} }
*/
} }
impl<B> SendRequest<B> impl<B> SendRequest<B>
@@ -423,7 +427,7 @@ impl Builder {
proto::h2::client::handshake(io, rx, &opts.h2_builder, opts.exec) proto::h2::client::handshake(io, rx, &opts.h2_builder, opts.exec)
.await?; .await?;
Ok(( Ok((
SendRequest { dispatch: tx }, SendRequest { dispatch: tx.unbound() },
Connection { inner: (PhantomData, h2) }, Connection { inner: (PhantomData, h2) },
)) ))
} }

View File

@@ -63,7 +63,7 @@ use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use bytes::Bytes; use bytes::Bytes;
use futures_util::future::{self, Either, FutureExt as _}; use futures_util::future;
use httparse::ParserConfig; use httparse::ParserConfig;
use pin_project_lite::pin_project; use pin_project_lite::pin_project;
use tokio::io::{AsyncRead, AsyncWrite}; use tokio::io::{AsyncRead, AsyncWrite};
@@ -214,16 +214,6 @@ pub struct Parts<T> {
_inner: (), _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 SendRequest
impl<B> SendRequest<B> { 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<()>> { pub fn poll_ready(&mut self, cx: &mut task::Context<'_>) -> Poll<crate::Result<()>> {
self.dispatch.poll_ready(cx) 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> impl<B> SendRequest<B>
@@ -316,32 +282,6 @@ where
ResponseFuture { inner } 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> 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 Connection
impl<T, B> Connection<T, B> 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::Pin;
use crate::common::{task, Poll}; use crate::common::{task, Poll};
#[cfg(test)]
pub(crate) type RetryPromise<T, U> = oneshot::Receiver<Result<U, (crate::Error, Option<T>)>>; 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>>; 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()) .map_err(|_| crate::Error::new_closed())
} }
#[cfg(test)]
pub(crate) fn is_ready(&self) -> bool { pub(crate) fn is_ready(&self) -> bool {
self.giver.is_wanting() self.giver.is_wanting()
} }
/*
pub(crate) fn is_closed(&self) -> bool { pub(crate) fn is_closed(&self) -> bool {
self.giver.is_canceled() self.giver.is_canceled()
} }
*/
fn can_send(&mut self) -> bool { fn can_send(&mut self) -> bool {
if self.giver.give() || !self.buffered_once { 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> { pub(crate) fn try_send(&mut self, val: T) -> Result<RetryPromise<T, U>, T> {
if !self.can_send() { if !self.can_send() {
return Err(val); return Err(val);
@@ -112,14 +117,17 @@ impl<T, U> Sender<T, U> {
#[cfg(feature = "http2")] #[cfg(feature = "http2")]
impl<T, U> UnboundedSender<T, U> { impl<T, U> UnboundedSender<T, U> {
/*
pub(crate) fn is_ready(&self) -> bool { pub(crate) fn is_ready(&self) -> bool {
!self.giver.is_canceled() !self.giver.is_canceled()
} }
*/
pub(crate) fn is_closed(&self) -> bool { pub(crate) fn is_closed(&self) -> bool {
self.giver.is_canceled() self.giver.is_canceled()
} }
#[cfg(test)]
pub(crate) fn try_send(&mut self, val: T) -> Result<RetryPromise<T, U>, T> { pub(crate) fn try_send(&mut self, val: T) -> Result<RetryPromise<T, U>, T> {
let (tx, rx) = oneshot::channel(); let (tx, rx) = oneshot::channel();
self.inner self.inner
@@ -127,6 +135,14 @@ impl<T, U> UnboundedSender<T, U> {
.map(move |_| rx) .map(move |_| rx)
.map_err(|mut e| (e.0).0.take().expect("envelope not dropped").0) .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")] #[cfg(feature = "http2")]
@@ -198,6 +214,7 @@ impl<T, U> Drop for Envelope<T, U> {
} }
pub(crate) enum Callback<T, U> { pub(crate) enum Callback<T, U> {
#[allow(unused)]
Retry(oneshot::Sender<Result<U, (crate::Error, Option<T>)>>), Retry(oneshot::Sender<Result<U, (crate::Error, Option<T>)>>),
NoRetry(oneshot::Sender<Result<U, crate::Error>>), NoRetry(oneshot::Sender<Result<U, crate::Error>>),
} }
@@ -301,6 +318,7 @@ mod tests {
} }
} }
#[cfg(not(miri))]
#[tokio::test] #[tokio::test]
async fn drop_receiver_sends_cancel_errors() { async fn drop_receiver_sends_cancel_errors() {
let _ = pretty_env_logger::try_init(); let _ = pretty_env_logger::try_init();
@@ -323,6 +341,7 @@ mod tests {
} }
} }
#[cfg(not(miri))]
#[tokio::test] #[tokio::test]
async fn sender_checks_for_want_on_send() { async fn sender_checks_for_want_on_send() {
let (mut tx, mut rx) = channel::<Custom, ()>(); let (mut tx, mut rx) = channel::<Custom, ()>();
@@ -363,7 +382,6 @@ mod tests {
use crate::{Body, Request, Response}; use crate::{Body, Request, Response};
let rt = tokio::runtime::Builder::new_current_thread() let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build() .build()
.unwrap(); .unwrap();
let (mut tx, mut rx) = channel::<Request<Body>, Response<Body>>(); let (mut tx, mut rx) = channel::<Request<Body>, Response<Body>>();
@@ -386,7 +404,6 @@ mod tests {
#[bench] #[bench]
fn giver_queue_not_ready(b: &mut test::Bencher) { fn giver_queue_not_ready(b: &mut test::Bencher) {
let rt = tokio::runtime::Builder::new_current_thread() let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build() .build()
.unwrap(); .unwrap();
let (_tx, mut rx) = channel::<i32, ()>(); let (_tx, mut rx) = channel::<i32, ()>();

View File

@@ -1,68 +1,18 @@
//! HTTP Client //! HTTP Client
//! //!
//! There are two levels of APIs provided for construct HTTP clients: //! hyper provides HTTP over a single connection. See the [`conn`](conn) module.
//!
//! - 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).
//! //!
//! ## Example //! ## Example
//! //!
//! For a small example program simply fetching a URL, take a look at the //! 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). //! [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"))] #[cfg(all(test, feature = "runtime"))]
mod tests; mod tests;
cfg_feature! { cfg_feature! {
#![any(feature = "http1", feature = "http2")] #![any(feature = "http1", feature = "http2")]
pub use self::client::{Builder, Client, ResponseFuture};
mod client;
pub mod conn; pub mod conn;
pub(super) mod dispatch; 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` // FIXME: re-implement tests with `async/await`
#[test] #[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::pin::Pin;
use std::sync::Arc; use std::sync::Arc;
#[cfg(all(feature = "server", any(feature = "http1", feature = "http2")))]
use crate::body::Body;
#[cfg(feature = "server")] #[cfg(feature = "server")]
use crate::body::HttpBody; use crate::body::HttpBody;
#[cfg(all(feature = "http2", feature = "server"))] #[cfg(all(feature = "http2", feature = "server"))]
use crate::proto::h2::server::H2Stream; use crate::proto::h2::server::H2Stream;
use crate::rt::Executor; 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")] #[cfg(feature = "server")]
pub trait ConnStreamExec<F, B: HttpBody>: Clone { pub trait ConnStreamExec<F, B: HttpBody>: Clone {
fn execute_h2stream(&mut self, fut: H2Stream<F, B>); 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>>; pub(crate) type BoxSendFuture = Pin<Box<dyn Future<Output = ()> + Send>>;
// Either the user provides an executor for background tasks, or we use // Either the user provides an executor for background tasks, or we use
@@ -44,13 +33,13 @@ impl Exec {
{ {
match *self { match *self {
Exec::Default => { Exec::Default => {
#[cfg(feature = "tcp")] #[cfg(feature = "runtime")]
{ {
tokio::task::spawn(fut); tokio::task::spawn(fut);
} }
#[cfg(not(feature = "tcp"))]
#[cfg(not(feature = "runtime"))]
{ {
// If no runtime, we need an executor!
panic!("executor must be set") 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 ===== // ==== impl Executor =====
#[cfg(feature = "server")] #[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 // 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? // 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; pub(crate) mod buf;
#[cfg(all(feature = "server", any(feature = "http1", feature = "http2")))] #[cfg(all(feature = "server", any(feature = "http1", feature = "http2")))]
pub(crate) mod date; 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"))] #[cfg(any(feature = "http1", feature = "http2", feature = "server"))]
pub(crate) mod exec; pub(crate) mod exec;
pub(crate) mod io; pub(crate) mod io;
#[cfg(all(feature = "client", any(feature = "http1", feature = "http2")))]
mod lazy;
mod never; mod never;
#[cfg(all(feature = "client", any(feature = "http1", feature = "http2")))]
pub(crate) mod sync_wrapper;
pub(crate) mod task; pub(crate) mod task;
pub(crate) mod watch; 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"))] #[cfg(any(feature = "http1", feature = "http2", feature = "runtime"))]
pub(crate) use self::never::Never; pub(crate) use self::never::Never;
pub(crate) use self::task::Poll; 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. /// An `io::Error` that occurred while trying to read or write to a network stream.
#[cfg(any(feature = "http1", feature = "http2"))] #[cfg(any(feature = "http1", feature = "http2"))]
Io, Io,
/// Error occurred while connecting.
#[allow(unused)]
Connect,
/// Error creating a TcpListener. /// Error creating a TcpListener.
#[cfg(all(feature = "tcp", feature = "server"))] #[cfg(all(feature = "tcp", feature = "server"))]
Listen, Listen,
/// Error accepting on an Incoming stream.
#[cfg(any(feature = "http1", feature = "http2"))]
#[cfg(feature = "server")]
Accept,
/// User took too long to send headers /// User took too long to send headers
#[cfg(all(feature = "http1", feature = "server", feature = "runtime"))] #[cfg(all(feature = "http1", feature = "server", feature = "runtime"))]
HeaderTimeout, HeaderTimeout,
@@ -96,10 +89,6 @@ pub(super) enum User {
Body, Body,
/// The user aborted writing of the outgoing body. /// The user aborted writing of the outgoing body.
BodyWriteAborted, BodyWriteAborted,
/// Error calling user's MakeService.
#[cfg(any(feature = "http1", feature = "http2"))]
#[cfg(feature = "server")]
MakeService,
/// Error from future of user's Service. /// Error from future of user's Service.
#[cfg(any(feature = "http1", feature = "http2"))] #[cfg(any(feature = "http1", feature = "http2"))]
Service, Service,
@@ -109,22 +98,10 @@ pub(super) enum User {
#[cfg(any(feature = "http1", feature = "http2"))] #[cfg(any(feature = "http1", feature = "http2"))]
#[cfg(feature = "server")] #[cfg(feature = "server")]
UnexpectedHeader, 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. /// User tried to respond with a 1xx (not 101) response code.
#[cfg(feature = "http1")] #[cfg(feature = "http1")]
#[cfg(feature = "server")] #[cfg(feature = "server")]
UnsupportedStatusCode, 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. /// User tried polling for an upgrade that doesn't exist.
NoUpgrade, NoUpgrade,
@@ -181,11 +158,6 @@ impl Error {
matches!(self.inner.kind, Kind::ChannelClosed) 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. /// Returns true if the connection closed before a message could complete.
pub fn is_incomplete_message(&self) -> bool { pub fn is_incomplete_message(&self) -> bool {
matches!(self.inner.kind, Kind::IncompleteMessage) matches!(self.inner.kind, Kind::IncompleteMessage)
@@ -278,18 +250,6 @@ impl Error {
Error::new(Kind::Listen).with(cause) 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 { pub(super) fn new_closed() -> Error {
Error::new(Kind::ChannelClosed) Error::new(Kind::ChannelClosed)
} }
@@ -323,30 +283,12 @@ impl Error {
Error::new(Kind::HeaderTimeout) 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 = "http1")]
#[cfg(feature = "server")] #[cfg(feature = "server")]
pub(super) fn new_user_unsupported_status_code() -> Error { pub(super) fn new_user_unsupported_status_code() -> Error {
Error::new_user(User::UnsupportedStatusCode) 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 { pub(super) fn new_user_no_upgrade() -> Error {
Error::new_user(User::NoUpgrade) Error::new_user(User::NoUpgrade)
} }
@@ -356,12 +298,6 @@ impl Error {
Error::new_user(User::ManualUpgrade) 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"))] #[cfg(any(feature = "http1", feature = "http2"))]
pub(super) fn new_user_service<E: Into<Cause>>(cause: E) -> Error { pub(super) fn new_user_service<E: Into<Cause>>(cause: E) -> Error {
Error::new_user(User::Service).with(cause) Error::new_user(User::Service).with(cause)
@@ -431,13 +367,9 @@ impl Error {
#[cfg(feature = "http1")] #[cfg(feature = "http1")]
Kind::UnexpectedMessage => "received unexpected message from connection", Kind::UnexpectedMessage => "received unexpected message from connection",
Kind::ChannelClosed => "channel closed", Kind::ChannelClosed => "channel closed",
Kind::Connect => "error trying to connect",
Kind::Canceled => "operation was canceled", Kind::Canceled => "operation was canceled",
#[cfg(all(feature = "server", feature = "tcp"))] #[cfg(all(feature = "server", feature = "tcp"))]
Kind::Listen => "error creating server listener", 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"))] #[cfg(all(feature = "http1", feature = "server", feature = "runtime"))]
Kind::HeaderTimeout => "read header from client timeout", Kind::HeaderTimeout => "read header from client timeout",
#[cfg(any(feature = "http1", feature = "http2"))] #[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::Body) => "error from user's HttpBody stream",
Kind::User(User::BodyWriteAborted) => "user body write aborted", Kind::User(User::BodyWriteAborted) => "user body write aborted",
#[cfg(any(feature = "http1", feature = "http2"))] #[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", Kind::User(User::Service) => "error from user's Service",
#[cfg(any(feature = "http1", feature = "http2"))] #[cfg(any(feature = "http1", feature = "http2"))]
#[cfg(feature = "server")] #[cfg(feature = "server")]
Kind::User(User::UnexpectedHeader) => "user sent unexpected header", 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 = "http1")]
#[cfg(feature = "server")] #[cfg(feature = "server")]
Kind::User(User::UnsupportedStatusCode) => { Kind::User(User::UnsupportedStatusCode) => {
"response has 1xx status code, not supported by server" "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", Kind::User(User::NoUpgrade) => "no upgrade available",
#[cfg(feature = "http1")] #[cfg(feature = "http1")]
Kind::User(User::ManualUpgrade) => "upgrade expected but low level API in use", Kind::User(User::ManualUpgrade) => "upgrade expected but low level API in use",

View File

@@ -51,7 +51,6 @@
//! - `server`: Enables the HTTP `server`. //! - `server`: Enables the HTTP `server`.
//! - `runtime`: Enables convenient integration with `tokio`, providing //! - `runtime`: Enables convenient integration with `tokio`, providing
//! connectors and acceptors for TCP, and a default executor. //! 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 //! [feature flags]: https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section
@@ -94,15 +93,10 @@ cfg_feature! {
#![feature = "client"] #![feature = "client"]
pub mod client; pub mod client;
#[cfg(any(feature = "http1", feature = "http2"))]
#[doc(no_inline)]
pub use crate::client::Client;
} }
cfg_feature! { cfg_feature! {
#![feature = "server"] #![feature = "server"]
pub mod 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 .h1_parser_config
.obsolete_multiline_headers_in_responses_are_allowed() .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` // SAFETY: array is valid up to `headers_len`
let header = unsafe { &*header.as_ptr() }; let header = unsafe { &mut *header.as_mut_ptr() };
for b in &mut slice[header.value.0..header.value.1] { Client::obs_fold_line(&mut slice, header);
if *b == b'\r' || *b == b'\n' {
*b = b' ';
}
}
} }
} }
@@ -1344,6 +1341,65 @@ impl Client {
set_content_length(headers, len) 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 { 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] #[test]
fn test_client_request_encode_title_case() { fn test_client_request_encode_title_case() {
use crate::proto::BodyLength; 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 //! are not handled at this level. This module provides the building blocks to
//! customize those things externally. //! customize those things externally.
//! //!
//! If you don't have need to manage connections yourself, consider using the
//! higher-level [Server](super) API.
//!
//! ## Example //! ## Example
//! A simple example that uses the `Http` struct to talk HTTP over a Tokio TCP stream //! A simple example that uses the `Http` struct to talk HTTP over a Tokio TCP stream
//! ```no_run //! ```no_run
@@ -69,7 +66,6 @@ cfg_feature! {
use tokio::io::{AsyncRead, AsyncWrite}; use tokio::io::{AsyncRead, AsyncWrite};
use tracing::trace; use tracing::trace;
pub use super::server::Connecting;
use crate::body::{Body, HttpBody}; use crate::body::{Body, HttpBody};
use crate::common::{task, Future, Pin, Poll, Unpin}; use crate::common::{task, Future, Pin, Poll, Unpin};
#[cfg(not(all(feature = "http1", feature = "http2")))] #[cfg(not(all(feature = "http1", feature = "http2")))]
@@ -84,9 +80,6 @@ cfg_feature! {
/// A lower-level configuration of the HTTP protocol. /// A lower-level configuration of the HTTP protocol.
/// ///
/// This structure is used to configure options for an HTTP server connection. /// 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)] #[derive(Clone, Debug)]
#[cfg(any(feature = "http1", feature = "http2"))] #[cfg(any(feature = "http1", feature = "http2"))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "http1", feature = "http2"))))] #[cfg_attr(docsrs, doc(cfg(any(feature = "http1", feature = "http2"))))]

View File

@@ -1,172 +1,10 @@
//! HTTP Server //! HTTP Server
//! //!
//! A `Server` is created to listen on a port, parse HTTP requests, and hand //! A "server" is usually created by listening on a port for new connections,
//! them off to a `Service`. //! parse HTTP requests, and hand them off to a `Service`.
//! //!
//! There are two levels of APIs provide for constructing HTTP servers: //! 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
//! - The higher-level [`Server`](Server) type. //! it with the types in the [`conn`](conn) module.
//! - 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;
pub mod conn; 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 //! - `HttpService`: This is blanketly implemented for all types that
//! implement `Service<http::Request<B1>, Response = http::Response<B2>>`. //! 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 //! # HttpService
//! //!
@@ -24,32 +20,13 @@
//! The helper [`service_fn`](service_fn) should be sufficient for most cases, but //! 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 //! if you need to implement `Service` for a type manually, you can follow the example
//! in `service_struct_impl.rs`. //! 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; pub use tower_service::Service;
mod http; mod http;
mod make;
#[cfg(all(any(feature = "http1", feature = "http2"), feature = "client"))]
mod oneshot;
mod util; 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"))] #[cfg(all(any(feature = "http1", feature = "http2"), feature = "server"))]
pub(super) use self::make::MakeServiceRef; pub(super) use self::http::HttpService;
#[cfg(all(any(feature = "http1", feature = "http2"), feature = "client"))]
pub(super) use self::oneshot::{oneshot, Oneshot};
pub use self::make::make_service_fn;
pub use self::util::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 h2::{RecvStream, SendStream};
use http::header::{HeaderName, HeaderValue}; use http::header::{HeaderName, HeaderValue};
use http_body_util::{combinators::BoxBody, BodyExt, StreamBody}; use http_body_util::{combinators::BoxBody, BodyExt, StreamBody};
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt, ReadBuf}; use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
use tokio::net::{TcpListener, TcpStream as TkTcpStream}; use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::{TcpListener as TkTcpListener, TcpListener, TcpStream as TkTcpStream};
use hyper::body::HttpBody as _; use hyper::body::HttpBody;
use hyper::client::Client;
use hyper::server::conn::Http; use hyper::server::conn::Http;
use hyper::server::Server; use hyper::service::service_fn;
use hyper::service::{make_service_fn, service_fn}; use hyper::{Body, Method, Request, Response, StatusCode, Uri, Version};
use hyper::{Body, Request, Response, StatusCode, Version};
mod support; mod support;
@@ -320,15 +319,11 @@ mod response_body_lengths {
#[tokio::test] #[tokio::test]
async fn http2_auto_response_with_known_length() { async fn http2_auto_response_with_known_length() {
use http_body::Body;
let server = serve(); let server = serve();
let addr_str = format!("http://{}", server.addr()); let addr_str = format!("http://{}", server.addr());
server.reply().body("Hello, World!"); server.reply().body("Hello, World!");
let client = Client::builder() let client = TestClient::new().http2_only();
.http2_only(true)
.build_http::<hyper::Body>();
let uri = addr_str let uri = addr_str
.parse::<hyper::Uri>() .parse::<hyper::Uri>()
.expect("server addr should parse"); .expect("server addr should parse");
@@ -340,8 +335,6 @@ mod response_body_lengths {
#[tokio::test] #[tokio::test]
async fn http2_auto_response_with_conflicting_lengths() { async fn http2_auto_response_with_conflicting_lengths() {
use http_body::Body;
let server = serve(); let server = serve();
let addr_str = format!("http://{}", server.addr()); let addr_str = format!("http://{}", server.addr());
server server
@@ -349,9 +342,7 @@ mod response_body_lengths {
.header("content-length", "10") .header("content-length", "10")
.body("Hello, World!"); .body("Hello, World!");
let client = Client::builder() let client = TestClient::new().http2_only();
.http2_only(true)
.build_http::<hyper::Body>();
let uri = addr_str let uri = addr_str
.parse::<hyper::Uri>() .parse::<hyper::Uri>()
.expect("server addr should parse"); .expect("server addr should parse");
@@ -363,15 +354,11 @@ mod response_body_lengths {
#[tokio::test] #[tokio::test]
async fn http2_implicit_empty_size_hint() { async fn http2_implicit_empty_size_hint() {
use http_body::Body;
let server = serve(); let server = serve();
let addr_str = format!("http://{}", server.addr()); let addr_str = format!("http://{}", server.addr());
server.reply(); server.reply();
let client = Client::builder() let client = TestClient::new().http2_only();
.http2_only(true)
.build_http::<hyper::Body>();
let uri = addr_str let uri = addr_str
.parse::<hyper::Uri>() .parse::<hyper::Uri>()
.expect("server addr should parse"); .expect("server addr should parse");
@@ -1480,8 +1467,6 @@ async fn header_read_timeout_slow_writes_multiple_requests() {
#[tokio::test] #[tokio::test]
async fn upgrades() { async fn upgrades() {
use tokio::io::{AsyncReadExt, AsyncWriteExt};
let _ = pretty_env_logger::try_init(); let _ = pretty_env_logger::try_init();
let listener = tcp_bind(&"127.0.0.1:0".parse().unwrap()).unwrap(); let listener = tcp_bind(&"127.0.0.1:0".parse().unwrap()).unwrap();
let addr = listener.local_addr().unwrap(); let addr = listener.local_addr().unwrap();
@@ -1539,8 +1524,6 @@ async fn upgrades() {
#[tokio::test] #[tokio::test]
async fn http_connect() { async fn http_connect() {
use tokio::io::{AsyncReadExt, AsyncWriteExt};
let _ = pretty_env_logger::try_init(); let _ = pretty_env_logger::try_init();
let listener = tcp_bind(&"127.0.0.1:0".parse().unwrap()).unwrap(); let listener = tcp_bind(&"127.0.0.1:0".parse().unwrap()).unwrap();
let addr = listener.local_addr().unwrap(); let addr = listener.local_addr().unwrap();
@@ -1675,15 +1658,19 @@ async fn upgrades_ignored() {
future::ok::<_, hyper::Error>(Response::new(hyper::Body::empty())) future::ok::<_, hyper::Error>(Response::new(hyper::Body::empty()))
}); });
let (socket, _) = listener.accept().await.unwrap(); loop {
Http::new() let (socket, _) = listener.accept().await.unwrap();
.serve_connection(socket, svc) tokio::task::spawn(async move {
.with_upgrades() Http::new()
.await .serve_connection(socket, svc)
.expect("server task"); .with_upgrades()
.await
.expect("server task");
});
}
}); });
let client = hyper::Client::new(); let client = TestClient::new();
let url = format!("http://{}/", addr); let url = format!("http://{}/", addr);
let make_req = || { let make_req = || {
@@ -1705,8 +1692,6 @@ async fn upgrades_ignored() {
#[tokio::test] #[tokio::test]
async fn http_connect_new() { async fn http_connect_new() {
use tokio::io::{AsyncReadExt, AsyncWriteExt};
let _ = pretty_env_logger::try_init(); let _ = pretty_env_logger::try_init();
let listener = tcp_bind(&"127.0.0.1:0".parse().unwrap()).unwrap(); let listener = tcp_bind(&"127.0.0.1:0".parse().unwrap()).unwrap();
let addr = listener.local_addr().unwrap(); let addr = listener.local_addr().unwrap();
@@ -1771,8 +1756,6 @@ async fn http_connect_new() {
#[tokio::test] #[tokio::test]
async fn h2_connect() { async fn h2_connect() {
use tokio::io::{AsyncReadExt, AsyncWriteExt};
let _ = pretty_env_logger::try_init(); let _ = pretty_env_logger::try_init();
let listener = tcp_bind(&"127.0.0.1:0".parse().unwrap()).unwrap(); let listener = tcp_bind(&"127.0.0.1:0".parse().unwrap()).unwrap();
let addr = listener.local_addr().unwrap(); let addr = listener.local_addr().unwrap();
@@ -1843,7 +1826,6 @@ async fn h2_connect() {
async fn h2_connect_multiplex() { async fn h2_connect_multiplex() {
use futures_util::stream::FuturesUnordered; use futures_util::stream::FuturesUnordered;
use futures_util::StreamExt; use futures_util::StreamExt;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
let _ = pretty_env_logger::try_init(); let _ = pretty_env_logger::try_init();
let listener = tcp_bind(&"127.0.0.1:0".parse().unwrap()).unwrap(); let listener = tcp_bind(&"127.0.0.1:0".parse().unwrap()).unwrap();
@@ -1954,8 +1936,6 @@ async fn h2_connect_multiplex() {
#[tokio::test] #[tokio::test]
async fn h2_connect_large_body() { async fn h2_connect_large_body() {
use tokio::io::{AsyncReadExt, AsyncWriteExt};
let _ = pretty_env_logger::try_init(); let _ = pretty_env_logger::try_init();
let listener = tcp_bind(&"127.0.0.1:0".parse().unwrap()).unwrap(); let listener = tcp_bind(&"127.0.0.1:0".parse().unwrap()).unwrap();
let addr = listener.local_addr().unwrap(); let addr = listener.local_addr().unwrap();
@@ -2031,8 +2011,6 @@ async fn h2_connect_large_body() {
#[tokio::test] #[tokio::test]
async fn h2_connect_empty_frames() { async fn h2_connect_empty_frames() {
use tokio::io::{AsyncReadExt, AsyncWriteExt};
let _ = pretty_env_logger::try_init(); let _ = pretty_env_logger::try_init();
let listener = tcp_bind(&"127.0.0.1:0".parse().unwrap()).unwrap(); let listener = tcp_bind(&"127.0.0.1:0".parse().unwrap()).unwrap();
let addr = listener.local_addr().unwrap(); let addr = listener.local_addr().unwrap();
@@ -2225,8 +2203,8 @@ fn http1_response_with_http2_version() {
server.reply().version(hyper::Version::HTTP_2); server.reply().version(hyper::Version::HTTP_2);
let client = TestClient::new();
rt.block_on({ rt.block_on({
let client = Client::new();
let uri = addr_str.parse().expect("server addr should parse"); let uri = addr_str.parse().expect("server addr should parse");
client.get(uri) client.get(uri)
}) })
@@ -2240,10 +2218,8 @@ fn try_h2() {
let rt = support::runtime(); let rt = support::runtime();
let client = TestClient::new().http2_only();
rt.block_on({ rt.block_on({
let client = Client::builder()
.http2_only(true)
.build_http::<hyper::Body>();
let uri = addr_str.parse().expect("server addr should parse"); let uri = addr_str.parse().expect("server addr should parse");
client.get(uri).map_ok(|_| ()).map_err(|_e| ()) client.get(uri).map_ok(|_| ()).map_err(|_e| ())
@@ -2260,10 +2236,8 @@ fn http1_only() {
let rt = support::runtime(); let rt = support::runtime();
let client = TestClient::new().http2_only();
rt.block_on({ rt.block_on({
let client = Client::builder()
.http2_only(true)
.build_http::<hyper::Body>();
let uri = addr_str.parse().expect("server addr should parse"); let uri = addr_str.parse().expect("server addr should parse");
client.get(uri) 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"); let uri = addr_str.parse().expect("server addr should parse");
dbg!("start"); dbg!("start");
let err = dbg!(Client::builder() let err = dbg!(TestClient::new()
.http2_only(true) .http2_only()
.build_http::<hyper::Body>()
.get(uri) .get(uri)
.await .await
.expect_err("client.get")); .expect_err("client.get"));
@@ -2314,9 +2287,8 @@ fn http2_body_user_error_sends_reset_reason() {
let err: hyper::Error = rt let err: hyper::Error = rt
.block_on(async move { .block_on(async move {
let client = Client::builder() let client = TestClient::new().http2_only();
.http2_only(true)
.build_http::<hyper::Body>();
let uri = addr_str.parse().expect("server addr should parse"); let uri = addr_str.parse().expect("server addr should parse");
let mut res = client.get(uri).await?; 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 _ = pretty_env_logger::try_init();
let server = hyper::Server::bind(&([127, 0, 0, 1], 0).into()) let listener = TkTcpListener::bind(SocketAddr::from(([127, 0, 0, 1], 0)))
.http2_only(true) .await
.serve(make_service_fn(|_| async move { .unwrap();
Ok::<_, BoxError>(Http2ReadyErrorSvc)
}));
let addr_str = format!("http://{}", server.local_addr()); let addr_str = format!("http://{}", listener.local_addr().unwrap());
tokio::task::spawn(async move { 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 uri = addr_str.parse().expect("server addr should parse");
let err = dbg!(Client::builder() let err = dbg!(TestClient::new()
.http2_only(true) .http2_only()
.build_http::<hyper::Body>()
.get(uri) .get(uri)
.await .await
.expect_err("client.get should fail")); .expect_err("client.get should fail"));
@@ -2948,9 +2931,9 @@ impl ServeOptions {
let (addr_tx, addr_rx) = mpsc::channel(); let (addr_tx, addr_rx) = mpsc::channel();
let (msg_tx, msg_rx) = mpsc::channel(); let (msg_tx, msg_rx) = mpsc::channel();
let (reply_tx, reply_rx) = spmc::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!( let thread_name = format!(
"test-server-{}", "test-server-{}",
@@ -2961,36 +2944,46 @@ impl ServeOptions {
let thread = thread::Builder::new() let thread = thread::Builder::new()
.name(thread_name) .name(thread_name)
.spawn(move || { .spawn(move || {
support::runtime() support::runtime().block_on(async move {
.block_on(async move { let listener = TkTcpListener::bind(addr).await.unwrap();
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,
})
});
let builder = Server::bind(&addr); addr_tx
.send(listener.local_addr().unwrap())
.expect("server addr tx");
#[cfg(feature = "http1")] loop {
let builder = builder let msg_tx = msg_tx.clone();
.http1_only(_options.http1_only) let reply_rx = reply_rx.clone();
.http1_keepalive(_options.keep_alive)
.http1_pipeline_flush(_options.pipeline);
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 #[cfg(feature = "http1")]
.with_graceful_shutdown(async { let http = http
let _ = shutdown_rx.await; .http1_only(_options.http1_only)
}) .http1_keep_alive(_options.keep_alive)
.await .pipeline_flush(_options.pipeline);
})
.expect("serve()"); 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"); .expect("thread spawn");
@@ -3119,3 +3112,49 @@ impl Drop for Dropped {
self.0.store(true, Ordering::SeqCst); 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, Arc, Mutex,
}; };
use hyper::client::HttpConnector; use hyper::client::conn::Builder;
use hyper::service::{make_service_fn, service_fn}; use hyper::server::conn::Http;
use hyper::{Body, Client, Request, Response, Server, Version}; use tokio::net::{TcpListener, TcpStream};
use hyper::service::service_fn;
use hyper::{Body, Request, Response, Version};
pub use futures_util::{ pub use futures_util::{
future, FutureExt as _, StreamExt as _, TryFutureExt as _, TryStreamExt as _, future, FutureExt as _, StreamExt as _, TryFutureExt as _, TryStreamExt as _,
@@ -326,16 +329,20 @@ async fn async_test(cfg: __TestConfig) {
Version::HTTP_11 Version::HTTP_11
}; };
let connector = HttpConnector::new(); let http2_only = cfg.server_version == 2;
let client = Client::builder()
.http2_only(cfg.client_version == 2)
.build::<_, Body>(connector);
let serve_handles = Arc::new(Mutex::new(cfg.server_msgs)); 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 expected_connections = cfg.connections;
let mut cnt = 0; tokio::task::spawn(async move {
let new_service = make_service_fn(move |_| { let mut cnt = 0;
cnt += 1; cnt += 1;
assert!( assert!(
cnt <= expected_connections, cnt <= expected_connections,
@@ -344,98 +351,108 @@ async fn async_test(cfg: __TestConfig) {
cnt cnt
); );
// Move a clone into the service_fn loop {
let serve_handles = serve_handles.clone(); let (stream, _) = listener.accept().await.expect("server error");
future::ok::<_, hyper::Error>(service_fn(move |req: Request<Body>| {
let (sreq, sres) = serve_handles.lock().unwrap().remove(0);
assert_eq!(req.uri().path(), sreq.uri, "client path"); // Move a clone into the service_fn
assert_eq!(req.method(), &sreq.method, "client method"); let serve_handles = serve_handles.clone();
assert_eq!(req.version(), version, "client version"); let service = service_fn(move |req: Request<Body>| {
for func in &sreq.headers { let (sreq, sres) = serve_handles.lock().unwrap().remove(0);
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() assert_eq!(req.uri().path(), sreq.uri, "client path");
.status(sres.status) assert_eq!(req.method(), &sreq.method, "client method");
.body(Body::from(sres.body)) assert_eq!(req.version(), version, "client version");
.expect("Response::build"); for func in &sreq.headers {
*res.headers_mut() = sres.headers; func(&req.headers());
res }
}) 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 { if cfg.proxy {
let (proxy_addr, proxy) = naive_proxy(ProxyConfig { let (proxy_addr, proxy) = naive_proxy(ProxyConfig {
connections: cfg.connections, connections: cfg.connections,
dst: addr, dst: addr,
version: cfg.server_version, version: cfg.server_version,
}); })
.await;
tokio::task::spawn(proxy); tokio::task::spawn(proxy);
addr = proxy_addr; addr = proxy_addr;
} }
let make_request = Arc::new( let make_request = Arc::new(move |creq: __CReq, cres: __CRes| {
move |client: &Client<HttpConnector>, creq: __CReq, cres: __CRes| { let uri = format!("http://{}{}", addr, creq.uri);
let uri = format!("http://{}{}", addr, creq.uri); let mut req = Request::builder()
let mut req = Request::builder() .method(creq.method)
.method(creq.method) .uri(uri)
.uri(uri) //.headers(creq.headers)
//.headers(creq.headers) .body(creq.body.into())
.body(creq.body.into()) .expect("Request::build");
.expect("Request::build"); *req.headers_mut() = creq.headers;
*req.headers_mut() = creq.headers; let cstatus = cres.status;
let cstatus = cres.status; let cheaders = cres.headers;
let cheaders = cres.headers; let cbody = cres.body;
let cbody = cres.body;
client async move {
.request(req) let stream = TcpStream::connect(addr).await.unwrap();
.and_then(move |res| {
assert_eq!(res.status(), cstatus, "server status"); let (mut sender, conn) = hyper::client::conn::Builder::new()
assert_eq!(res.version(), version, "server version"); .http2_only(http2_only)
for func in &cheaders { .handshake::<TcpStream, Body>(stream)
func(&res.headers()); .await
} .unwrap();
hyper::body::to_bytes(res)
}) tokio::task::spawn(async move {
.map_ok(move |body| { if let Err(err) = conn.await {
assert_eq!(body.as_ref(), cbody.as_slice(), "server body"); panic!("{:?}", err);
}) }
.map(|res| res.expect("client error")) });
},
); 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 client_futures: Pin<Box<dyn Future<Output = ()> + Send>> = if cfg.parallel {
let mut client_futures = vec![]; let mut client_futures = vec![];
for (creq, cres) in cfg.client_msgs { 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(|_| ())) Box::pin(future::join_all(client_futures).map(|_| ()))
} else { } else {
let mut client_futures: Pin<Box<dyn Future<Output = Client<HttpConnector>> + Send>> = let mut client_futures: Pin<Box<dyn Future<Output = ()> + Send>> =
Box::pin(future::ready(client)); Box::pin(future::ready(()));
for (creq, cres) in cfg.client_msgs { for (creq, cres) in cfg.client_msgs {
let mk_request = make_request.clone(); let mk_request = make_request.clone();
client_futures = Box::pin(client_futures.then(move |client| { client_futures = Box::pin(client_futures.then(move |_| mk_request(creq, cres)));
let fut = mk_request(&client, creq, cres);
fut.map(move |()| client)
}));
} }
Box::pin(client_futures.map(|_| ())) Box::pin(client_futures.map(|_| ()))
}; };
@@ -449,27 +466,75 @@ struct ProxyConfig {
version: usize, version: usize,
} }
fn naive_proxy(cfg: ProxyConfig) -> (SocketAddr, impl Future<Output = ()>) { async fn naive_proxy(cfg: ProxyConfig) -> (SocketAddr, impl Future<Output = ()>) {
let client = Client::builder()
.http2_only(cfg.version == 2)
.build_http::<Body>();
let dst_addr = cfg.dst; let dst_addr = cfg.dst;
let max_connections = cfg.connections; let max_connections = cfg.connections;
let counter = AtomicUsize::new(0); 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 listener = TcpListener::bind(SocketAddr::from(([127, 0, 0, 1], 0)))
let prev = counter.fetch_add(1, Ordering::Relaxed); .await
assert!(max_connections > prev, "proxy max connections"); .unwrap();
let client = client.clone();
future::ok::<_, hyper::Error>(service_fn(move |mut req| { let proxy_addr = listener.local_addr().unwrap();
let uri = format!("http://{}{}", dst_addr, req.uri().path())
.parse() let fut = async move {
.expect("proxy new uri parse"); tokio::task::spawn(async move {
*req.uri_mut() = uri; let prev = counter.fetch_add(1, Ordering::Relaxed);
client.request(req) assert!(max_connections > prev, "proxy max connections");
}))
})); loop {
let proxy_addr = srv.local_addr(); let (stream, _) = listener.accept().await.unwrap();
(proxy_addr, srv.map(|res| res.expect("proxy error")))
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)
} }