style(lib): run rustfmt and enforce in CI
This commit is contained in:
24
.github/workflows/CI.yml
vendored
24
.github/workflows/CI.yml
vendored
@@ -9,9 +9,31 @@ env:
|
|||||||
RUST_BACKTRACE: 1
|
RUST_BACKTRACE: 1
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
style:
|
||||||
|
name: Check Style
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v1
|
||||||
|
|
||||||
|
- name: Install Rust
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
profile: minimal
|
||||||
|
toolchain: stable
|
||||||
|
override: true
|
||||||
|
components: rustfmt
|
||||||
|
|
||||||
|
- name: cargo fmt --check
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: fmt
|
||||||
|
args: --all -- --check
|
||||||
|
|
||||||
|
|
||||||
test:
|
test:
|
||||||
name: Test ${{ matrix.rust }} on ${{ matrix.os }}
|
name: Test ${{ matrix.rust }} on ${{ matrix.os }}
|
||||||
|
needs: [style]
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
rust:
|
rust:
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
disable_all_formatting = true
|
|
||||||
@@ -3,11 +3,11 @@
|
|||||||
|
|
||||||
extern crate test;
|
extern crate test;
|
||||||
|
|
||||||
|
use http::Uri;
|
||||||
|
use hyper::client::connect::HttpConnector;
|
||||||
|
use hyper::service::Service;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use tokio::net::TcpListener;
|
use tokio::net::TcpListener;
|
||||||
use hyper::client::connect::{HttpConnector};
|
|
||||||
use hyper::service::Service;
|
|
||||||
use http::Uri;
|
|
||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
fn http_connector(b: &mut test::Bencher) {
|
fn http_connector(b: &mut test::Bencher) {
|
||||||
@@ -17,7 +17,9 @@ fn http_connector(b: &mut test::Bencher) {
|
|||||||
.basic_scheduler()
|
.basic_scheduler()
|
||||||
.build()
|
.build()
|
||||||
.expect("rt build");
|
.expect("rt build");
|
||||||
let mut listener = rt.block_on(TcpListener::bind(&SocketAddr::from(([127, 0, 0, 1], 0)))).expect("bind");
|
let mut listener = rt
|
||||||
|
.block_on(TcpListener::bind(&SocketAddr::from(([127, 0, 0, 1], 0))))
|
||||||
|
.expect("bind");
|
||||||
let addr = listener.local_addr().expect("local_addr");
|
let addr = listener.local_addr().expect("local_addr");
|
||||||
let dst: Uri = format!("http://{}/", addr).parse().expect("uri parse");
|
let dst: Uri = format!("http://{}/", addr).parse().expect("uri parse");
|
||||||
let mut connector = HttpConnector::new();
|
let mut connector = HttpConnector::new();
|
||||||
@@ -28,7 +30,6 @@ fn http_connector(b: &mut test::Bencher) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
rt.block_on(async {
|
rt.block_on(async {
|
||||||
connector.call(dst.clone()).await.expect("connect");
|
connector.call(dst.clone()).await.expect("connect");
|
||||||
|
|||||||
@@ -7,15 +7,14 @@ use std::net::SocketAddr;
|
|||||||
|
|
||||||
use futures_util::future::join_all;
|
use futures_util::future::join_all;
|
||||||
|
|
||||||
use hyper::{body::HttpBody as _, Body, Method, Request, Response, Server};
|
|
||||||
use hyper::client::HttpConnector;
|
use hyper::client::HttpConnector;
|
||||||
|
use hyper::{body::HttpBody as _, Body, Method, Request, Response, Server};
|
||||||
|
|
||||||
// HTTP1
|
// HTTP1
|
||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
fn http1_get(b: &mut test::Bencher) {
|
fn http1_get(b: &mut test::Bencher) {
|
||||||
opts()
|
opts().bench(b)
|
||||||
.bench(b)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
@@ -48,9 +47,7 @@ fn http1_body_both_10mb(b: &mut test::Bencher) {
|
|||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
fn http1_parallel_x10_empty(b: &mut test::Bencher) {
|
fn http1_parallel_x10_empty(b: &mut test::Bencher) {
|
||||||
opts()
|
opts().parallel(10).bench(b)
|
||||||
.parallel(10)
|
|
||||||
.bench(b)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
@@ -76,19 +73,13 @@ fn http1_parallel_x10_req_10kb_100_chunks(b: &mut test::Bencher) {
|
|||||||
#[bench]
|
#[bench]
|
||||||
fn http1_parallel_x10_res_1mb(b: &mut test::Bencher) {
|
fn http1_parallel_x10_res_1mb(b: &mut test::Bencher) {
|
||||||
let body = &[b'x'; 1024 * 1024 * 1];
|
let body = &[b'x'; 1024 * 1024 * 1];
|
||||||
opts()
|
opts().parallel(10).response_body(body).bench(b)
|
||||||
.parallel(10)
|
|
||||||
.response_body(body)
|
|
||||||
.bench(b)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
fn http1_parallel_x10_res_10mb(b: &mut test::Bencher) {
|
fn http1_parallel_x10_res_10mb(b: &mut test::Bencher) {
|
||||||
let body = &[b'x'; 1024 * 1024 * 10];
|
let body = &[b'x'; 1024 * 1024 * 10];
|
||||||
opts()
|
opts().parallel(10).response_body(body).bench(b)
|
||||||
.parallel(10)
|
|
||||||
.response_body(body)
|
|
||||||
.bench(b)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// HTTP2
|
// HTTP2
|
||||||
@@ -97,9 +88,7 @@ const HTTP2_MAX_WINDOW: u32 = std::u32::MAX >> 1;
|
|||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
fn http2_get(b: &mut test::Bencher) {
|
fn http2_get(b: &mut test::Bencher) {
|
||||||
opts()
|
opts().http2().bench(b)
|
||||||
.http2()
|
|
||||||
.bench(b)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
@@ -123,10 +112,7 @@ fn http2_req_100kb(b: &mut test::Bencher) {
|
|||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
fn http2_parallel_x10_empty(b: &mut test::Bencher) {
|
fn http2_parallel_x10_empty(b: &mut test::Bencher) {
|
||||||
opts()
|
opts().http2().parallel(10).bench(b)
|
||||||
.http2()
|
|
||||||
.parallel(10)
|
|
||||||
.bench(b)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
@@ -293,9 +279,10 @@ impl Opts {
|
|||||||
let make_request = || {
|
let make_request = || {
|
||||||
let chunk_cnt = self.request_chunks;
|
let chunk_cnt = self.request_chunks;
|
||||||
let body = if chunk_cnt > 0 {
|
let body = if chunk_cnt > 0 {
|
||||||
|
|
||||||
let (mut tx, body) = Body::channel();
|
let (mut tx, body) = Body::channel();
|
||||||
let chunk = self.request_body.expect("request_chunks means request_body");
|
let chunk = self
|
||||||
|
.request_body
|
||||||
|
.expect("request_chunks means request_body");
|
||||||
exec.spawn(async move {
|
exec.spawn(async move {
|
||||||
for _ in 0..chunk_cnt {
|
for _ in 0..chunk_cnt {
|
||||||
tx.send_data(chunk.into()).await.expect("send_data");
|
tx.send_data(chunk.into()).await.expect("send_data");
|
||||||
@@ -303,8 +290,7 @@ impl Opts {
|
|||||||
});
|
});
|
||||||
body
|
body
|
||||||
} else {
|
} else {
|
||||||
self
|
self.request_body
|
||||||
.request_body
|
|
||||||
.map(Body::from)
|
.map(Body::from)
|
||||||
.unwrap_or_else(|| Body::empty())
|
.unwrap_or_else(|| Body::empty())
|
||||||
};
|
};
|
||||||
@@ -328,14 +314,12 @@ impl Opts {
|
|||||||
let req = make_request();
|
let req = make_request();
|
||||||
rt.block_on(send_request(req));
|
rt.block_on(send_request(req));
|
||||||
});
|
});
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
let futs = (0..self.parallel_cnt)
|
let futs = (0..self.parallel_cnt).map(|_| {
|
||||||
.map(|_| {
|
let req = make_request();
|
||||||
let req = make_request();
|
send_request(req)
|
||||||
send_request(req)
|
});
|
||||||
});
|
|
||||||
// Await all spawned futures becoming completed.
|
// Await all spawned futures becoming completed.
|
||||||
rt.block_on(join_all(futs));
|
rt.block_on(join_all(futs));
|
||||||
});
|
});
|
||||||
@@ -353,12 +337,16 @@ fn spawn_server(rt: &mut tokio::runtime::Runtime, opts: &Opts) -> SocketAddr {
|
|||||||
.http2_only(opts.http2)
|
.http2_only(opts.http2)
|
||||||
.http2_initial_stream_window_size(opts.http2_stream_window)
|
.http2_initial_stream_window_size(opts.http2_stream_window)
|
||||||
.http2_initial_connection_window_size(opts.http2_conn_window)
|
.http2_initial_connection_window_size(opts.http2_conn_window)
|
||||||
.serve(make_service_fn( move |_| async move {
|
.serve(make_service_fn(move |_| {
|
||||||
Ok::<_, hyper::Error>(service_fn(move |req: Request<Body>| async move {
|
async move {
|
||||||
let mut req_body = req.into_body();
|
Ok::<_, hyper::Error>(service_fn(move |req: Request<Body>| {
|
||||||
while let Some(_chunk) = req_body.data().await {}
|
async move {
|
||||||
Ok::<_, hyper::Error>(Response::new(Body::from(body)))
|
let mut req_body = req.into_body();
|
||||||
}))
|
while let Some(_chunk) = req_body.data().await {}
|
||||||
|
Ok::<_, hyper::Error>(Response::new(Body::from(body)))
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
}))
|
}))
|
||||||
});
|
});
|
||||||
let addr = srv.local_addr();
|
let addr = srv.local_addr();
|
||||||
@@ -367,5 +355,5 @@ fn spawn_server(rt: &mut tokio::runtime::Runtime, opts: &Opts) -> SocketAddr {
|
|||||||
panic!("server error: {}", err);
|
panic!("server error: {}", err);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return addr
|
return addr;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,14 +4,14 @@
|
|||||||
extern crate test;
|
extern crate test;
|
||||||
|
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
use std::net::{TcpStream};
|
use std::net::TcpStream;
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use tokio::sync::oneshot;
|
use tokio::sync::oneshot;
|
||||||
|
|
||||||
use hyper::{Body, Response, Server};
|
|
||||||
use hyper::service::{make_service_fn, service_fn};
|
use hyper::service::{make_service_fn, service_fn};
|
||||||
|
use hyper::{Body, Response, Server};
|
||||||
|
|
||||||
const PIPELINED_REQUESTS: usize = 16;
|
const PIPELINED_REQUESTS: usize = 16;
|
||||||
|
|
||||||
@@ -25,10 +25,12 @@ fn hello_world(b: &mut test::Bencher) {
|
|||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
let addr = "127.0.0.1:0".parse().unwrap();
|
let addr = "127.0.0.1:0".parse().unwrap();
|
||||||
|
|
||||||
let make_svc = make_service_fn(|_| async {
|
let make_svc = make_service_fn(|_| {
|
||||||
Ok::<_, hyper::Error>(service_fn(|_| async {
|
async {
|
||||||
Ok::<_, hyper::Error>(Response::new(Body::from("Hello, World!")))
|
Ok::<_, hyper::Error>(service_fn(|_| {
|
||||||
}))
|
async { Ok::<_, hyper::Error>(Response::new(Body::from("Hello, World!"))) }
|
||||||
|
}))
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut rt = tokio::runtime::Builder::new()
|
let mut rt = tokio::runtime::Builder::new()
|
||||||
@@ -44,10 +46,9 @@ fn hello_world(b: &mut test::Bencher) {
|
|||||||
|
|
||||||
addr_tx.send(srv.local_addr()).unwrap();
|
addr_tx.send(srv.local_addr()).unwrap();
|
||||||
|
|
||||||
let graceful = srv
|
let graceful = srv.with_graceful_shutdown(async {
|
||||||
.with_graceful_shutdown(async {
|
until_rx.await.ok();
|
||||||
until_rx.await.ok();
|
});
|
||||||
});
|
|
||||||
|
|
||||||
rt.block_on(async {
|
rt.block_on(async {
|
||||||
if let Err(e) = graceful.await {
|
if let Err(e) = graceful.await {
|
||||||
@@ -66,7 +67,8 @@ fn hello_world(b: &mut test::Bencher) {
|
|||||||
|
|
||||||
let total_bytes = {
|
let total_bytes = {
|
||||||
let mut tcp = TcpStream::connect(addr).unwrap();
|
let mut tcp = TcpStream::connect(addr).unwrap();
|
||||||
tcp.write_all(b"GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n").unwrap();
|
tcp.write_all(b"GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n")
|
||||||
|
.unwrap();
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
tcp.read_to_end(&mut buf).unwrap()
|
tcp.read_to_end(&mut buf).unwrap()
|
||||||
} * PIPELINED_REQUESTS;
|
} * PIPELINED_REQUESTS;
|
||||||
@@ -85,4 +87,3 @@ fn hello_world(b: &mut test::Bencher) {
|
|||||||
assert_eq!(sum, total_bytes);
|
assert_eq!(sum, total_bytes);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,26 +11,31 @@ use std::time::Duration;
|
|||||||
use futures_util::{stream, StreamExt};
|
use futures_util::{stream, StreamExt};
|
||||||
use tokio::sync::oneshot;
|
use tokio::sync::oneshot;
|
||||||
|
|
||||||
use hyper::{Body, Response, Server};
|
|
||||||
use hyper::service::{make_service_fn, service_fn};
|
use hyper::service::{make_service_fn, service_fn};
|
||||||
|
use hyper::{Body, Response, Server};
|
||||||
|
|
||||||
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 = "127.0.0.1:0".parse().unwrap();
|
||||||
let make_svc = make_service_fn(|_| async {
|
let make_svc = make_service_fn(|_| {
|
||||||
Ok::<_, hyper::Error>(service_fn(|_| async {
|
async {
|
||||||
Ok::<_, hyper::Error>(Response::builder()
|
Ok::<_, hyper::Error>(service_fn(|_| {
|
||||||
.header($header.0, $header.1)
|
async {
|
||||||
.header("content-type", "text/plain")
|
Ok::<_, hyper::Error>(
|
||||||
.body($body())
|
Response::builder()
|
||||||
.unwrap()
|
.header($header.0, $header.1)
|
||||||
)
|
.header("content-type", "text/plain")
|
||||||
}))
|
.body($body())
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut rt = tokio::runtime::Builder::new()
|
let mut rt = tokio::runtime::Builder::new()
|
||||||
@@ -39,17 +44,13 @@ macro_rules! bench_server {
|
|||||||
.build()
|
.build()
|
||||||
.expect("rt build");
|
.expect("rt build");
|
||||||
|
|
||||||
let srv = rt.block_on(async move {
|
let srv = rt.block_on(async move { Server::bind(&addr).serve(make_svc) });
|
||||||
Server::bind(&addr)
|
|
||||||
.serve(make_svc)
|
|
||||||
});
|
|
||||||
|
|
||||||
addr_tx.send(srv.local_addr()).unwrap();
|
addr_tx.send(srv.local_addr()).unwrap();
|
||||||
|
|
||||||
let graceful = srv
|
let graceful = srv.with_graceful_shutdown(async {
|
||||||
.with_graceful_shutdown(async {
|
until_rx.await.ok();
|
||||||
until_rx.await.ok();
|
});
|
||||||
});
|
|
||||||
rt.block_on(async move {
|
rt.block_on(async move {
|
||||||
if let Err(e) = graceful.await {
|
if let Err(e) = graceful.await {
|
||||||
panic!("server error: {}", e);
|
panic!("server error: {}", e);
|
||||||
@@ -62,7 +63,8 @@ macro_rules! bench_server {
|
|||||||
|
|
||||||
let total_bytes = {
|
let total_bytes = {
|
||||||
let mut tcp = TcpStream::connect(addr).unwrap();
|
let mut tcp = TcpStream::connect(addr).unwrap();
|
||||||
tcp.write_all(b"GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n").unwrap();
|
tcp.write_all(b"GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n")
|
||||||
|
.unwrap();
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
tcp.read_to_end(&mut buf).unwrap()
|
tcp.read_to_end(&mut buf).unwrap()
|
||||||
};
|
};
|
||||||
@@ -73,14 +75,15 @@ macro_rules! bench_server {
|
|||||||
|
|
||||||
$b.bytes = 35 + total_bytes as u64;
|
$b.bytes = 35 + total_bytes as u64;
|
||||||
$b.iter(|| {
|
$b.iter(|| {
|
||||||
tcp.write_all(b"GET / HTTP/1.1\r\nHost: localhost\r\n\r\n").unwrap();
|
tcp.write_all(b"GET / HTTP/1.1\r\nHost: localhost\r\n\r\n")
|
||||||
|
.unwrap();
|
||||||
let mut sum = 0;
|
let mut sum = 0;
|
||||||
while sum < total_bytes {
|
while sum < total_bytes {
|
||||||
sum += tcp.read(&mut buf).unwrap();
|
sum += tcp.read(&mut buf).unwrap();
|
||||||
}
|
}
|
||||||
assert_eq!(sum, total_bytes);
|
assert_eq!(sum, total_bytes);
|
||||||
});
|
});
|
||||||
})
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn body(b: &'static [u8]) -> hyper::Body {
|
fn body(b: &'static [u8]) -> hyper::Body {
|
||||||
@@ -94,7 +97,9 @@ fn throughput_fixedsize_small_payload(b: &mut test::Bencher) {
|
|||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
fn throughput_fixedsize_large_payload(b: &mut test::Bencher) {
|
fn throughput_fixedsize_large_payload(b: &mut test::Bencher) {
|
||||||
bench_server!(b, ("content-length", "1000000"), || body(&[b'x'; 1_000_000]))
|
bench_server!(b, ("content-length", "1000000"), || body(
|
||||||
|
&[b'x'; 1_000_000]
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
@@ -107,12 +112,16 @@ fn throughput_fixedsize_many_chunks(b: &mut test::Bencher) {
|
|||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
fn throughput_chunked_small_payload(b: &mut test::Bencher) {
|
fn throughput_chunked_small_payload(b: &mut test::Bencher) {
|
||||||
bench_server!(b, ("transfer-encoding", "chunked"), || body(b"Hello, World!"))
|
bench_server!(b, ("transfer-encoding", "chunked"), || body(
|
||||||
|
b"Hello, World!"
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
fn throughput_chunked_large_payload(b: &mut test::Bencher) {
|
fn throughput_chunked_large_payload(b: &mut test::Bencher) {
|
||||||
bench_server!(b, ("transfer-encoding", "chunked"), || body(&[b'x'; 1_000_000]))
|
bench_server!(b, ("transfer-encoding", "chunked"), || body(
|
||||||
|
&[b'x'; 1_000_000]
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
@@ -134,14 +143,17 @@ fn raw_tcp_throughput_small_payload(b: &mut test::Bencher) {
|
|||||||
let mut buf = [0u8; 8192];
|
let mut buf = [0u8; 8192];
|
||||||
while rx.try_recv().is_err() {
|
while rx.try_recv().is_err() {
|
||||||
sock.read(&mut buf).unwrap();
|
sock.read(&mut buf).unwrap();
|
||||||
sock.write_all(b"\
|
sock.write_all(
|
||||||
|
b"\
|
||||||
HTTP/1.1 200 OK\r\n\
|
HTTP/1.1 200 OK\r\n\
|
||||||
Content-Length: 13\r\n\
|
Content-Length: 13\r\n\
|
||||||
Content-Type: text/plain; charset=utf-8\r\n\
|
Content-Type: text/plain; charset=utf-8\r\n\
|
||||||
Date: Fri, 12 May 2017 18:21:45 GMT\r\n\
|
Date: Fri, 12 May 2017 18:21:45 GMT\r\n\
|
||||||
\r\n\
|
\r\n\
|
||||||
Hello, World!\
|
Hello, World!\
|
||||||
").unwrap();
|
",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -150,7 +162,8 @@ fn raw_tcp_throughput_small_payload(b: &mut test::Bencher) {
|
|||||||
|
|
||||||
b.bytes = 130 + 35;
|
b.bytes = 130 + 35;
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
tcp.write_all(b"GET / HTTP/1.1\r\nHost: localhost\r\n\r\n").unwrap();
|
tcp.write_all(b"GET / HTTP/1.1\r\nHost: localhost\r\n\r\n")
|
||||||
|
.unwrap();
|
||||||
let n = tcp.read(&mut buf).unwrap();
|
let n = tcp.read(&mut buf).unwrap();
|
||||||
assert_eq!(n, 130);
|
assert_eq!(n, 130);
|
||||||
});
|
});
|
||||||
@@ -191,7 +204,8 @@ fn raw_tcp_throughput_large_payload(b: &mut test::Bencher) {
|
|||||||
b.bytes = expect_read as u64 + 35;
|
b.bytes = expect_read as u64 + 35;
|
||||||
|
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
tcp.write_all(b"GET / HTTP/1.1\r\nHost: localhost\r\n\r\n").unwrap();
|
tcp.write_all(b"GET / HTTP/1.1\r\nHost: localhost\r\n\r\n")
|
||||||
|
.unwrap();
|
||||||
let mut sum = 0;
|
let mut sum = 0;
|
||||||
while sum < expect_read {
|
while sum < expect_read {
|
||||||
sum += tcp.read(&mut buf).unwrap();
|
sum += tcp.read(&mut buf).unwrap();
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
#![warn(rust_2018_idioms)]
|
#![warn(rust_2018_idioms)]
|
||||||
use std::env;
|
use std::env;
|
||||||
|
|
||||||
use hyper::{Client, body::HttpBody as _};
|
use hyper::{body::HttpBody as _, Client};
|
||||||
use tokio::io::{self, AsyncWriteExt as _};
|
use tokio::io::{self, AsyncWriteExt as _};
|
||||||
|
|
||||||
// A simple type alias so as to DRY.
|
// A simple type alias so as to DRY.
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate serde_derive;
|
extern crate serde_derive;
|
||||||
|
|
||||||
use hyper::Client;
|
|
||||||
use futures_util::StreamExt;
|
use futures_util::StreamExt;
|
||||||
|
use hyper::Client;
|
||||||
|
|
||||||
// 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>>;
|
||||||
|
|||||||
@@ -1,22 +1,20 @@
|
|||||||
//#![deny(warnings)]
|
//#![deny(warnings)]
|
||||||
|
|
||||||
use hyper::{Body, Method, Request, Response, Server, StatusCode};
|
|
||||||
use hyper::service::{make_service_fn, service_fn};
|
|
||||||
use futures_util::{StreamExt, TryStreamExt};
|
use futures_util::{StreamExt, TryStreamExt};
|
||||||
|
use hyper::service::{make_service_fn, service_fn};
|
||||||
|
use hyper::{Body, Method, Request, Response, Server, StatusCode};
|
||||||
|
|
||||||
/// 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.
|
||||||
async fn echo(mut req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
|
async fn echo(mut req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
|
||||||
match (req.method(), req.uri().path()) {
|
match (req.method(), req.uri().path()) {
|
||||||
// Serve some instructions at /
|
// Serve some instructions at /
|
||||||
(&Method::GET, "/") => {
|
(&Method::GET, "/") => Ok(Response::new(Body::from(
|
||||||
Ok(Response::new(Body::from("Try POSTing data to /echo such as: `curl localhost:3000/echo -XPOST -d 'hello world'`")))
|
"Try POSTing data to /echo such as: `curl localhost:3000/echo -XPOST -d 'hello world'`",
|
||||||
}
|
))),
|
||||||
|
|
||||||
// Simply echo the body back to the client.
|
// Simply echo the body back to the client.
|
||||||
(&Method::POST, "/echo") => {
|
(&Method::POST, "/echo") => Ok(Response::new(req.into_body())),
|
||||||
Ok(Response::new(req.into_body()))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert to uppercase before sending back to client using a stream.
|
// Convert to uppercase before sending back to client using a stream.
|
||||||
(&Method::POST, "/echo/uppercase") => {
|
(&Method::POST, "/echo/uppercase") => {
|
||||||
@@ -41,11 +39,7 @@ async fn echo(mut req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
|
|||||||
whole_body.extend_from_slice(&chunk?);
|
whole_body.extend_from_slice(&chunk?);
|
||||||
}
|
}
|
||||||
|
|
||||||
let reversed_body = whole_body
|
let reversed_body = whole_body.iter().rev().cloned().collect::<Vec<u8>>();
|
||||||
.iter()
|
|
||||||
.rev()
|
|
||||||
.cloned()
|
|
||||||
.collect::<Vec<u8>>();
|
|
||||||
Ok(Response::new(Body::from(reversed_body)))
|
Ok(Response::new(Body::from(reversed_body)))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,19 +52,13 @@ async fn echo(mut 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 = ([127, 0, 0, 1], 3000).into();
|
||||||
|
|
||||||
let service = make_service_fn(|_| {
|
let service = make_service_fn(|_| async { Ok::<_, hyper::Error>(service_fn(echo)) });
|
||||||
async {
|
|
||||||
Ok::<_, hyper::Error>(service_fn(echo))
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let server = Server::bind(&addr)
|
let server = Server::bind(&addr).serve(service);
|
||||||
.serve(service);
|
|
||||||
|
|
||||||
println!("Listening on http://{}", addr);
|
println!("Listening on http://{}", addr);
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
use std::convert::Infallible;
|
use std::convert::Infallible;
|
||||||
|
|
||||||
use hyper::{Body, Request, Response, Server};
|
|
||||||
use hyper::service::{make_service_fn, service_fn};
|
use hyper::service::{make_service_fn, service_fn};
|
||||||
|
use hyper::{Body, Request, Response, Server};
|
||||||
|
|
||||||
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!")))
|
||||||
@@ -19,9 +19,7 @@ pub async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|||||||
// 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`.
|
||||||
async {
|
async { Ok::<_, Infallible>(service_fn(hello)) }
|
||||||
Ok::<_, Infallible>(service_fn(hello))
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let addr = ([127, 0, 0, 1], 3000).into();
|
let addr = ([127, 0, 0, 1], 3000).into();
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
#![deny(warnings)]
|
#![deny(warnings)]
|
||||||
#![warn(rust_2018_idioms)]
|
#![warn(rust_2018_idioms)]
|
||||||
|
|
||||||
use hyper::{Body, Request, Response, Server};
|
|
||||||
use hyper::service::{service_fn, make_service_fn};
|
|
||||||
use futures_util::future::join;
|
use futures_util::future::join;
|
||||||
|
use hyper::service::{make_service_fn, service_fn};
|
||||||
|
use hyper::{Body, Request, Response, Server};
|
||||||
|
|
||||||
static INDEX1: &'static [u8] = b"The 1st service!";
|
static INDEX1: &'static [u8] = b"The 1st service!";
|
||||||
static INDEX2: &'static [u8] = b"The 2nd service!";
|
static INDEX2: &'static [u8] = b"The 2nd service!";
|
||||||
@@ -23,19 +23,13 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|||||||
let addr1 = ([127, 0, 0, 1], 1337).into();
|
let addr1 = ([127, 0, 0, 1], 1337).into();
|
||||||
let addr2 = ([127, 0, 0, 1], 1338).into();
|
let addr2 = ([127, 0, 0, 1], 1338).into();
|
||||||
|
|
||||||
let srv1 = Server::bind(&addr1)
|
let srv1 = Server::bind(&addr1).serve(make_service_fn(|_| {
|
||||||
.serve(make_service_fn(|_| {
|
async { Ok::<_, hyper::Error>(service_fn(index1)) }
|
||||||
async {
|
}));
|
||||||
Ok::<_, hyper::Error>(service_fn(index1))
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
let srv2 = Server::bind(&addr2)
|
let srv2 = Server::bind(&addr2).serve(make_service_fn(|_| {
|
||||||
.serve(make_service_fn(|_| {
|
async { Ok::<_, hyper::Error>(service_fn(index2)) }
|
||||||
async {
|
}));
|
||||||
Ok::<_, hyper::Error>(service_fn(index2))
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
println!("Listening on http://{} and http://{}", addr1, addr2);
|
println!("Listening on http://{} and http://{}", addr1, addr2);
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
// #![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::{Body, Method, Request, Response, Server, StatusCode};
|
use hyper::{Body, Method, Request, Response, Server, StatusCode};
|
||||||
use hyper::service::{service_fn, make_service_fn};
|
|
||||||
|
|
||||||
|
use futures_util::StreamExt;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use url::form_urlencoded;
|
use url::form_urlencoded;
|
||||||
use futures_util::StreamExt;
|
|
||||||
|
|
||||||
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>";
|
||||||
static MISSING: &[u8] = b"Missing field";
|
static MISSING: &[u8] = b"Missing field";
|
||||||
@@ -15,9 +15,7 @@ static NOTNUMERIC: &[u8] = b"Number field is not numeric";
|
|||||||
// Using service_fn, we can turn this function into a `Service`.
|
// Using service_fn, we can turn this function into a `Service`.
|
||||||
async fn param_example(mut req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
|
async fn param_example(mut req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
|
||||||
match (req.method(), req.uri().path()) {
|
match (req.method(), req.uri().path()) {
|
||||||
(&Method::GET, "/") | (&Method::GET, "/post") => {
|
(&Method::GET, "/") | (&Method::GET, "/post") => Ok(Response::new(INDEX.into())),
|
||||||
Ok(Response::new(INDEX.into()))
|
|
||||||
},
|
|
||||||
(&Method::POST, "/post") => {
|
(&Method::POST, "/post") => {
|
||||||
// Concatenate the body...
|
// Concatenate the body...
|
||||||
let mut b = Vec::new();
|
let mut b = Vec::new();
|
||||||
@@ -35,7 +33,9 @@ async fn param_example(mut req: Request<Body>) -> Result<Response<Body>, hyper::
|
|||||||
// form, and the values should be rolled up into a
|
// form, and the values should be rolled up into a
|
||||||
// HashMap<String, Vec<String>>. However in this
|
// HashMap<String, Vec<String>>. However in this
|
||||||
// example the simpler approach is sufficient.
|
// example the simpler approach is sufficient.
|
||||||
let params = form_urlencoded::parse(b.as_ref()).into_owned().collect::<HashMap<String, String>>();
|
let params = form_urlencoded::parse(b.as_ref())
|
||||||
|
.into_owned()
|
||||||
|
.collect::<HashMap<String, String>>();
|
||||||
|
|
||||||
// Validate the request parameters, returning
|
// Validate the request parameters, returning
|
||||||
// early if an invalid input is detected.
|
// early if an invalid input is detected.
|
||||||
@@ -71,13 +71,11 @@ async fn param_example(mut req: Request<Body>) -> Result<Response<Body>, hyper::
|
|||||||
// needed here, too.
|
// needed here, too.
|
||||||
let body = format!("Hello {}, your number is {}", name, number);
|
let body = format!("Hello {}, your number is {}", name, number);
|
||||||
Ok(Response::new(body.into()))
|
Ok(Response::new(body.into()))
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
Ok(Response::builder()
|
|
||||||
.status(StatusCode::NOT_FOUND)
|
|
||||||
.body(Body::empty())
|
|
||||||
.unwrap())
|
|
||||||
}
|
}
|
||||||
|
_ => Ok(Response::builder()
|
||||||
|
.status(StatusCode::NOT_FOUND)
|
||||||
|
.body(Body::empty())
|
||||||
|
.unwrap()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,12 +85,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|||||||
|
|
||||||
let addr = ([127, 0, 0, 1], 1337).into();
|
let addr = ([127, 0, 0, 1], 1337).into();
|
||||||
|
|
||||||
let server = Server::bind(&addr)
|
let server = Server::bind(&addr).serve(make_service_fn(|_| {
|
||||||
.serve(make_service_fn(|_| {
|
async { Ok::<_, hyper::Error>(service_fn(param_example)) }
|
||||||
async {
|
}));
|
||||||
Ok::<_, hyper::Error>(service_fn(param_example))
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
println!("Listening on http://{}", addr);
|
println!("Listening on http://{}", addr);
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#![deny(warnings)]
|
#![deny(warnings)]
|
||||||
|
|
||||||
use hyper::{Client, Error, Server};
|
|
||||||
use hyper::service::{make_service_fn, service_fn};
|
use hyper::service::{make_service_fn, service_fn};
|
||||||
|
use hyper::{Client, Error, Server};
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
@@ -25,9 +25,11 @@ async fn main() {
|
|||||||
// `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`.
|
||||||
Ok::<_, Error>(service_fn(move |mut req| {
|
Ok::<_, Error>(service_fn(move |mut req| {
|
||||||
let uri_string = format!("http://{}/{}",
|
let uri_string = format!(
|
||||||
out_addr_clone,
|
"http://{}/{}",
|
||||||
req.uri().path_and_query().map(|x| x.as_str()).unwrap_or(""));
|
out_addr_clone,
|
||||||
|
req.uri().path_and_query().map(|x| x.as_str()).unwrap_or("")
|
||||||
|
);
|
||||||
let uri = uri_string.parse().unwrap();
|
let uri = uri_string.parse().unwrap();
|
||||||
*req.uri_mut() = uri;
|
*req.uri_mut() = uri;
|
||||||
client.request(req)
|
client.request(req)
|
||||||
@@ -35,8 +37,7 @@ async fn main() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let server = Server::bind(&in_addr)
|
let server = Server::bind(&in_addr).serve(make_service);
|
||||||
.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);
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
#![deny(warnings)]
|
#![deny(warnings)]
|
||||||
|
|
||||||
use tokio::io::AsyncReadExt;
|
|
||||||
use tokio::fs::File;
|
use tokio::fs::File;
|
||||||
|
use tokio::io::AsyncReadExt;
|
||||||
|
|
||||||
use hyper::{Body, Method, Result, Request, Response, Server, StatusCode};
|
|
||||||
use hyper::service::{make_service_fn, service_fn};
|
use hyper::service::{make_service_fn, service_fn};
|
||||||
|
use hyper::{Body, Method, Request, Response, Result, Server, StatusCode};
|
||||||
|
|
||||||
static INDEX: &str = "examples/send_file_index.html";
|
static INDEX: &str = "examples/send_file_index.html";
|
||||||
static INTERNAL_SERVER_ERROR: &[u8] = b"Internal Server Error";
|
static INTERNAL_SERVER_ERROR: &[u8] = b"Internal Server Error";
|
||||||
@@ -16,12 +16,10 @@ async fn main() {
|
|||||||
|
|
||||||
let addr = "127.0.0.1:1337".parse().unwrap();
|
let addr = "127.0.0.1:1337".parse().unwrap();
|
||||||
|
|
||||||
let make_service = make_service_fn(|_| async {
|
let make_service =
|
||||||
Ok::<_, hyper::Error>(service_fn(response_examples))
|
make_service_fn(|_| async { Ok::<_, hyper::Error>(service_fn(response_examples)) });
|
||||||
});
|
|
||||||
|
|
||||||
let server = Server::bind(&addr)
|
let server = Server::bind(&addr).serve(make_service);
|
||||||
.serve(make_service);
|
|
||||||
|
|
||||||
println!("Listening on http://{}", addr);
|
println!("Listening on http://{}", addr);
|
||||||
|
|
||||||
@@ -32,16 +30,14 @@ async fn main() {
|
|||||||
|
|
||||||
async fn response_examples(req: Request<Body>) -> Result<Response<Body>> {
|
async fn response_examples(req: Request<Body>) -> Result<Response<Body>> {
|
||||||
match (req.method(), req.uri().path()) {
|
match (req.method(), req.uri().path()) {
|
||||||
(&Method::GET, "/") |
|
(&Method::GET, "/") | (&Method::GET, "/index.html") | (&Method::GET, "/big_file.html") => {
|
||||||
(&Method::GET, "/index.html") |
|
|
||||||
(&Method::GET, "/big_file.html") => {
|
|
||||||
simple_file_send(INDEX).await
|
simple_file_send(INDEX).await
|
||||||
}
|
}
|
||||||
(&Method::GET, "/no_file.html") => {
|
(&Method::GET, "/no_file.html") => {
|
||||||
// Test what happens when file cannot be be found
|
// Test what happens when file cannot be be found
|
||||||
simple_file_send("this_file_should_not_exist.html").await
|
simple_file_send("this_file_should_not_exist.html").await
|
||||||
}
|
}
|
||||||
_ => Ok(not_found())
|
_ => Ok(not_found()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use hyper::{Body, Error, Response, Server};
|
|
||||||
use hyper::service::{make_service_fn, service_fn};
|
use hyper::service::{make_service_fn, service_fn};
|
||||||
|
use hyper::{Body, Error, Response, Server};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
pretty_env_logger::init();
|
pretty_env_logger::init();
|
||||||
@@ -22,7 +22,6 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn run() {
|
async fn run() {
|
||||||
|
|
||||||
let addr = ([127, 0, 0, 1], 3000).into();
|
let addr = ([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...
|
||||||
@@ -37,18 +36,12 @@ async fn run() {
|
|||||||
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 {
|
async move { Ok::<_, Error>(Response::new(Body::from(format!("Request #{}", value)))) }
|
||||||
Ok::<_, Error>(Response::new(Body::from(
|
|
||||||
format!("Request #{}", value)
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let server = Server::bind(&addr)
|
let server = Server::bind(&addr).executor(LocalExec).serve(make_service);
|
||||||
.executor(LocalExec)
|
|
||||||
.serve(make_service);
|
|
||||||
|
|
||||||
println!("Listening on http://{}", addr);
|
println!("Listening on http://{}", addr);
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
#![deny(warnings)]
|
#![deny(warnings)]
|
||||||
|
|
||||||
use std::sync::{Arc, atomic::{AtomicUsize, Ordering}};
|
use std::sync::{
|
||||||
|
atomic::{AtomicUsize, Ordering},
|
||||||
|
Arc,
|
||||||
|
};
|
||||||
|
|
||||||
use hyper::{Body, Error, Response, Server};
|
|
||||||
use hyper::service::{make_service_fn, service_fn};
|
use hyper::service::{make_service_fn, service_fn};
|
||||||
|
use hyper::{Body, Error, Response, Server};
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
@@ -34,17 +37,12 @@ async fn main() {
|
|||||||
// 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 {
|
async move { Ok::<_, Error>(Response::new(Body::from(format!("Request #{}", count)))) }
|
||||||
Ok::<_, Error>(
|
|
||||||
Response::new(Body::from(format!("Request #{}", count)))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let server = Server::bind(&addr)
|
let server = Server::bind(&addr).serve(make_service);
|
||||||
.serve(make_service);
|
|
||||||
|
|
||||||
println!("Listening on http://{}", addr);
|
println!("Listening on http://{}", addr);
|
||||||
|
|
||||||
@@ -52,4 +50,3 @@ async fn main() {
|
|||||||
eprintln!("server error: {}", e);
|
eprintln!("server error: {}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#![deny(warnings)]
|
#![deny(warnings)]
|
||||||
|
|
||||||
use hyper::client::service::Connect;
|
|
||||||
use hyper::client::conn::Builder;
|
use hyper::client::conn::Builder;
|
||||||
use hyper::client::connect::HttpConnector;
|
use hyper::client::connect::HttpConnector;
|
||||||
|
use hyper::client::service::Connect;
|
||||||
use hyper::service::Service;
|
use hyper::service::Service;
|
||||||
use hyper::{Body, Request};
|
use hyper::{Body, Request};
|
||||||
|
|
||||||
@@ -14,7 +14,6 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
|
|
||||||
let uri = "http://127.0.0.1:8080".parse::<http::Uri>()?;
|
let uri = "http://127.0.0.1:8080".parse::<http::Uri>()?;
|
||||||
|
|
||||||
|
|
||||||
let mut svc = mk_svc.call(uri.clone()).await?;
|
let mut svc = mk_svc.call(uri.clone()).await?;
|
||||||
|
|
||||||
let body = Body::empty();
|
let body = Body::empty();
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
use std::task::{Context, Poll};
|
use std::task::{Context, Poll};
|
||||||
|
|
||||||
use futures_util::future;
|
use futures_util::future;
|
||||||
use hyper::{Body, Request, Response, Server};
|
|
||||||
use hyper::service::Service;
|
use hyper::service::Service;
|
||||||
|
use hyper::{Body, Request, Response, Server};
|
||||||
|
|
||||||
const ROOT: &'static str = "/";
|
const ROOT: &'static str = "/";
|
||||||
|
|
||||||
@@ -58,9 +58,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
|
|
||||||
let addr = "127.0.0.1:1337".parse().unwrap();
|
let addr = "127.0.0.1:1337".parse().unwrap();
|
||||||
|
|
||||||
|
let server = Server::bind(&addr).serve(MakeSvc);
|
||||||
let server = Server::bind(&addr)
|
|
||||||
.serve(MakeSvc);
|
|
||||||
|
|
||||||
println!("Listening on http://{}", addr);
|
println!("Listening on http://{}", addr);
|
||||||
|
|
||||||
|
|||||||
@@ -3,13 +3,13 @@
|
|||||||
// Note: `hyper::upgrade` docs link to this upgrade.
|
// Note: `hyper::upgrade` docs link to this upgrade.
|
||||||
use std::str;
|
use std::str;
|
||||||
|
|
||||||
use tokio::sync::oneshot;
|
|
||||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||||
|
use tokio::sync::oneshot;
|
||||||
|
|
||||||
use hyper::{Body, Client, Request, Response, Server, StatusCode};
|
use hyper::header::{HeaderValue, UPGRADE};
|
||||||
use hyper::header::{UPGRADE, HeaderValue};
|
|
||||||
use hyper::service::{make_service_fn, service_fn};
|
use hyper::service::{make_service_fn, service_fn};
|
||||||
use hyper::upgrade::Upgraded;
|
use hyper::upgrade::Upgraded;
|
||||||
|
use hyper::{Body, Client, Request, Response, Server, 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.
|
||||||
@@ -58,14 +58,15 @@ async fn server_upgrade(req: Request<Body>) -> Result<Response<Body>> {
|
|||||||
eprintln!("server foobar io error: {}", e)
|
eprintln!("server foobar io error: {}", e)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
Err(e) => eprintln!("upgrade error: {}", e)
|
Err(e) => eprintln!("upgrade error: {}", e),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Now return a 101 Response saying we agree to the upgrade to some
|
// Now return a 101 Response saying we agree to the upgrade to some
|
||||||
// made-up 'foobar' protocol.
|
// made-up 'foobar' protocol.
|
||||||
*res.status_mut() = StatusCode::SWITCHING_PROTOCOLS;
|
*res.status_mut() = StatusCode::SWITCHING_PROTOCOLS;
|
||||||
res.headers_mut().insert(UPGRADE, HeaderValue::from_static("foobar"));
|
res.headers_mut()
|
||||||
|
.insert(UPGRADE, HeaderValue::from_static("foobar"));
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,7 +103,7 @@ async fn client_upgrade_request(addr: SocketAddr) -> Result<()> {
|
|||||||
eprintln!("client foobar io error: {}", e)
|
eprintln!("client foobar io error: {}", e)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
Err(e) => eprintln!("upgrade error: {}", e)
|
Err(e) => eprintln!("upgrade error: {}", e),
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -115,12 +116,10 @@ async fn main() {
|
|||||||
// unused port.
|
// unused port.
|
||||||
let addr = ([127, 0, 0, 1], 0).into();
|
let addr = ([127, 0, 0, 1], 0).into();
|
||||||
|
|
||||||
let make_service = make_service_fn(|_| async {
|
let make_service =
|
||||||
Ok::<_, hyper::Error>(service_fn(server_upgrade))
|
make_service_fn(|_| async { Ok::<_, hyper::Error>(service_fn(server_upgrade)) });
|
||||||
});
|
|
||||||
|
|
||||||
let server = Server::bind(&addr)
|
let server = Server::bind(&addr).serve(make_service);
|
||||||
.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 = server.local_addr();
|
||||||
@@ -128,10 +127,9 @@ async fn main() {
|
|||||||
// 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, rx) = oneshot::channel::<()>();
|
||||||
let server = server
|
let server = server.with_graceful_shutdown(async move {
|
||||||
.with_graceful_shutdown(async move {
|
rx.await.ok();
|
||||||
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.
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
#![deny(warnings)]
|
#![deny(warnings)]
|
||||||
|
|
||||||
use hyper::{Body, Chunk, Client, Method, Request, Response, Server, StatusCode, header};
|
use futures_util::{StreamExt, TryStreamExt};
|
||||||
use hyper::client::HttpConnector;
|
use hyper::client::HttpConnector;
|
||||||
use hyper::service::{make_service_fn, service_fn};
|
use hyper::service::{make_service_fn, service_fn};
|
||||||
use futures_util::{StreamExt, TryStreamExt};
|
use hyper::{header, Body, Chunk, Client, Method, Request, Response, Server, StatusCode};
|
||||||
|
|
||||||
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,9 +14,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(
|
async fn client_request_response(client: &Client<HttpConnector>) -> Result<Response<Body>> {
|
||||||
client: &Client<HttpConnector>
|
|
||||||
) -> Result<Response<Body>> {
|
|
||||||
let req = Request::builder()
|
let req = Request::builder()
|
||||||
.method(Method::POST)
|
.method(Method::POST)
|
||||||
.uri(URL)
|
.uri(URL)
|
||||||
@@ -27,9 +25,11 @@ async fn client_request_response(
|
|||||||
let web_res = client.request(req).await?;
|
let web_res = client.request(req).await?;
|
||||||
// Compare the JSON we sent (before) with what we received (after):
|
// Compare the JSON we sent (before) with what we received (after):
|
||||||
let body = Body::wrap_stream(web_res.into_body().map_ok(|b| {
|
let body = Body::wrap_stream(web_res.into_body().map_ok(|b| {
|
||||||
Chunk::from(format!("<b>POST request body</b>: {}<br><b>Response</b>: {}",
|
Chunk::from(format!(
|
||||||
POST_DATA,
|
"<b>POST request body</b>: {}<br><b>Response</b>: {}",
|
||||||
std::str::from_utf8(&b).unwrap()))
|
POST_DATA,
|
||||||
|
std::str::from_utf8(&b).unwrap()
|
||||||
|
))
|
||||||
}));
|
}));
|
||||||
|
|
||||||
Ok(Response::new(body))
|
Ok(Response::new(body))
|
||||||
@@ -57,40 +57,27 @@ async fn api_post_response(mut req: Request<Body>) -> Result<Response<Body>> {
|
|||||||
async fn api_get_response() -> Result<Response<Body>> {
|
async fn api_get_response() -> Result<Response<Body>> {
|
||||||
let data = vec!["foo", "bar"];
|
let data = vec!["foo", "bar"];
|
||||||
let res = match serde_json::to_string(&data) {
|
let res = match serde_json::to_string(&data) {
|
||||||
Ok(json) => {
|
Ok(json) => Response::builder()
|
||||||
Response::builder()
|
.header(header::CONTENT_TYPE, "application/json")
|
||||||
.header(header::CONTENT_TYPE, "application/json")
|
.body(Body::from(json))
|
||||||
.body(Body::from(json))
|
.unwrap(),
|
||||||
.unwrap()
|
Err(_) => Response::builder()
|
||||||
}
|
.status(StatusCode::INTERNAL_SERVER_ERROR)
|
||||||
Err(_) => {
|
.body(INTERNAL_SERVER_ERROR.into())
|
||||||
Response::builder()
|
.unwrap(),
|
||||||
.status(StatusCode::INTERNAL_SERVER_ERROR)
|
|
||||||
.body(INTERNAL_SERVER_ERROR.into())
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn response_examples(
|
async fn response_examples(
|
||||||
req: Request<Body>,
|
req: Request<Body>,
|
||||||
client: Client<HttpConnector>
|
client: Client<HttpConnector>,
|
||||||
) -> Result<Response<Body>> {
|
) -> Result<Response<Body>> {
|
||||||
match (req.method(), req.uri().path()) {
|
match (req.method(), req.uri().path()) {
|
||||||
(&Method::GET, "/") |
|
(&Method::GET, "/") | (&Method::GET, "/index.html") => Ok(Response::new(INDEX.into())),
|
||||||
(&Method::GET, "/index.html") => {
|
(&Method::GET, "/test.html") => client_request_response(&client).await,
|
||||||
Ok(Response::new(INDEX.into()))
|
(&Method::POST, "/json_api") => api_post_response(req).await,
|
||||||
},
|
(&Method::GET, "/json_api") => api_get_response().await,
|
||||||
(&Method::GET, "/test.html") => {
|
|
||||||
client_request_response(&client).await
|
|
||||||
},
|
|
||||||
(&Method::POST, "/json_api") => {
|
|
||||||
api_post_response(req).await
|
|
||||||
},
|
|
||||||
(&Method::GET, "/json_api") => {
|
|
||||||
api_get_response().await
|
|
||||||
}
|
|
||||||
_ => {
|
_ => {
|
||||||
// Return 404 not found response.
|
// Return 404 not found response.
|
||||||
Ok(Response::builder()
|
Ok(Response::builder()
|
||||||
@@ -121,8 +108,7 @@ async fn main() -> Result<()> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let server = Server::bind(&addr)
|
let server = Server::bind(&addr).serve(new_service);
|
||||||
.serve(new_service);
|
|
||||||
|
|
||||||
println!("Listening on http://{}", addr);
|
println!("Listening on http://{}", addr);
|
||||||
|
|
||||||
|
|||||||
113
src/body/body.rs
113
src/body/body.rs
@@ -4,15 +4,15 @@ use std::error::Error as StdError;
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use futures_core::Stream; // for mpsc::Receiver
|
|
||||||
use futures_channel::{mpsc, oneshot};
|
use futures_channel::{mpsc, oneshot};
|
||||||
|
use futures_core::Stream; // for mpsc::Receiver
|
||||||
#[cfg(feature = "stream")]
|
#[cfg(feature = "stream")]
|
||||||
use futures_util::TryStreamExt;
|
use futures_util::TryStreamExt;
|
||||||
use http_body::{SizeHint, Body as HttpBody};
|
|
||||||
use http::HeaderMap;
|
use http::HeaderMap;
|
||||||
|
use http_body::{Body as HttpBody, SizeHint};
|
||||||
|
|
||||||
use crate::common::{Future, Never, Pin, Poll, task};
|
|
||||||
use super::Chunk;
|
use super::Chunk;
|
||||||
|
use crate::common::{task, Future, Never, Pin, Poll};
|
||||||
use crate::upgrade::OnUpgrade;
|
use crate::upgrade::OnUpgrade;
|
||||||
|
|
||||||
type BodySender = mpsc::Sender<Result<Chunk, crate::Error>>;
|
type BodySender = mpsc::Sender<Result<Chunk, crate::Error>>;
|
||||||
@@ -44,7 +44,9 @@ enum Kind {
|
|||||||
//
|
//
|
||||||
// See https://github.com/rust-lang/rust/issues/57017
|
// See https://github.com/rust-lang/rust/issues/57017
|
||||||
#[cfg(feature = "stream")]
|
#[cfg(feature = "stream")]
|
||||||
Wrapped(Pin<Box<dyn Stream<Item = Result<Chunk, Box<dyn StdError + Send + Sync>>> + Send + Sync>>),
|
Wrapped(
|
||||||
|
Pin<Box<dyn Stream<Item = Result<Chunk, Box<dyn StdError + Send + Sync>>> + Send + Sync>>,
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Extra {
|
struct Extra {
|
||||||
@@ -161,8 +163,7 @@ impl Body {
|
|||||||
///
|
///
|
||||||
/// See [the `upgrade` module](::upgrade) for more.
|
/// See [the `upgrade` module](::upgrade) for more.
|
||||||
pub fn on_upgrade(self) -> OnUpgrade {
|
pub fn on_upgrade(self) -> OnUpgrade {
|
||||||
self
|
self.extra
|
||||||
.extra
|
|
||||||
.map(|ex| ex.on_upgrade)
|
.map(|ex| ex.on_upgrade)
|
||||||
.unwrap_or_else(OnUpgrade::none)
|
.unwrap_or_else(OnUpgrade::none)
|
||||||
}
|
}
|
||||||
@@ -193,54 +194,44 @@ impl Body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn take_delayed_eof(&mut self) -> Option<DelayEof> {
|
fn take_delayed_eof(&mut self) -> Option<DelayEof> {
|
||||||
self
|
self.extra
|
||||||
.extra
|
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.and_then(|extra| extra.delayed_eof.take())
|
.and_then(|extra| extra.delayed_eof.take())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extra_mut(&mut self) -> &mut Extra {
|
fn extra_mut(&mut self) -> &mut Extra {
|
||||||
self
|
self.extra.get_or_insert_with(|| {
|
||||||
.extra
|
Box::new(Extra {
|
||||||
.get_or_insert_with(|| Box::new(Extra {
|
|
||||||
delayed_eof: None,
|
delayed_eof: None,
|
||||||
on_upgrade: OnUpgrade::none(),
|
on_upgrade: OnUpgrade::none(),
|
||||||
}))
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll_eof(&mut self, cx: &mut task::Context<'_>) -> Poll<Option<crate::Result<Chunk>>> {
|
fn poll_eof(&mut self, cx: &mut task::Context<'_>) -> Poll<Option<crate::Result<Chunk>>> {
|
||||||
match self.take_delayed_eof() {
|
match self.take_delayed_eof() {
|
||||||
Some(DelayEof::NotEof(mut delay)) => {
|
Some(DelayEof::NotEof(mut delay)) => match self.poll_inner(cx) {
|
||||||
match self.poll_inner(cx) {
|
ok @ Poll::Ready(Some(Ok(..))) | ok @ Poll::Pending => {
|
||||||
ok @ Poll::Ready(Some(Ok(..))) |
|
self.extra_mut().delayed_eof = Some(DelayEof::NotEof(delay));
|
||||||
ok @ Poll::Pending => {
|
ok
|
||||||
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))),
|
|
||||||
}
|
}
|
||||||
},
|
Poll::Ready(None) => match Pin::new(&mut delay).poll(cx) {
|
||||||
Some(DelayEof::Eof(mut delay)) => {
|
|
||||||
match Pin::new(&mut delay).poll(cx) {
|
|
||||||
Poll::Ready(Ok(never)) => match never {},
|
Poll::Ready(Ok(never)) => match never {},
|
||||||
Poll::Pending => {
|
Poll::Pending => {
|
||||||
self.extra_mut().delayed_eof = Some(DelayEof::Eof(delay));
|
self.extra_mut().delayed_eof = Some(DelayEof::Eof(delay));
|
||||||
Poll::Pending
|
Poll::Pending
|
||||||
},
|
}
|
||||||
Poll::Ready(Err(_done)) => {
|
Poll::Ready(Err(_done)) => Poll::Ready(None),
|
||||||
Poll::Ready(None)
|
},
|
||||||
},
|
Poll::Ready(Some(Err(e))) => Poll::Ready(Some(Err(e))),
|
||||||
|
},
|
||||||
|
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),
|
||||||
},
|
},
|
||||||
None => self.poll_inner(cx),
|
None => self.poll_inner(cx),
|
||||||
}
|
}
|
||||||
@@ -268,25 +259,23 @@ impl Body {
|
|||||||
}
|
}
|
||||||
None => Poll::Ready(None),
|
None => Poll::Ready(None),
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
Kind::H2 {
|
Kind::H2 {
|
||||||
recv: ref mut h2, ..
|
recv: ref mut h2, ..
|
||||||
} => match ready!(h2.poll_data(cx)) {
|
} => match ready!(h2.poll_data(cx)) {
|
||||||
Some(Ok(bytes)) => {
|
Some(Ok(bytes)) => {
|
||||||
let _ = h2.flow_control().release_capacity(bytes.len());
|
let _ = h2.flow_control().release_capacity(bytes.len());
|
||||||
Poll::Ready(Some(Ok(Chunk::from(bytes))))
|
Poll::Ready(Some(Ok(Chunk::from(bytes))))
|
||||||
},
|
}
|
||||||
Some(Err(e)) => Poll::Ready(Some(Err(crate::Error::new_body(e)))),
|
Some(Err(e)) => Poll::Ready(Some(Err(crate::Error::new_body(e)))),
|
||||||
None => Poll::Ready(None),
|
None => Poll::Ready(None),
|
||||||
},
|
},
|
||||||
|
|
||||||
#[cfg(feature = "stream")]
|
#[cfg(feature = "stream")]
|
||||||
Kind::Wrapped(ref mut s) => {
|
Kind::Wrapped(ref mut s) => match ready!(s.as_mut().poll_next(cx)) {
|
||||||
match ready!(s.as_mut().poll_next(cx)) {
|
Some(res) => Poll::Ready(Some(res.map_err(crate::Error::new_body))),
|
||||||
Some(res) => Poll::Ready(Some(res.map_err(crate::Error::new_body))),
|
None => Poll::Ready(None),
|
||||||
None => Poll::Ready(None),
|
},
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -311,13 +300,21 @@ impl HttpBody for Body {
|
|||||||
type Data = Chunk;
|
type Data = Chunk;
|
||||||
type Error = crate::Error;
|
type Error = crate::Error;
|
||||||
|
|
||||||
fn poll_data(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Option<Result<Self::Data, Self::Error>>> {
|
fn poll_data(
|
||||||
|
mut self: Pin<&mut Self>,
|
||||||
|
cx: &mut task::Context<'_>,
|
||||||
|
) -> Poll<Option<Result<Self::Data, Self::Error>>> {
|
||||||
self.poll_eof(cx)
|
self.poll_eof(cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll_trailers(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Result<Option<HeaderMap>, Self::Error>> {
|
fn poll_trailers(
|
||||||
|
mut self: Pin<&mut Self>,
|
||||||
|
cx: &mut task::Context<'_>,
|
||||||
|
) -> Poll<Result<Option<HeaderMap>, Self::Error>> {
|
||||||
match self.kind {
|
match self.kind {
|
||||||
Kind::H2 { recv: ref mut h2, .. } => match ready!(h2.poll_trailers(cx)) {
|
Kind::H2 {
|
||||||
|
recv: ref mut h2, ..
|
||||||
|
} => match ready!(h2.poll_trailers(cx)) {
|
||||||
Ok(t) => Poll::Ready(Ok(t)),
|
Ok(t) => Poll::Ready(Ok(t)),
|
||||||
Err(e) => Poll::Ready(Err(crate::Error::new_h2(e))),
|
Err(e) => Poll::Ready(Err(crate::Error::new_h2(e))),
|
||||||
},
|
},
|
||||||
@@ -341,10 +338,8 @@ impl HttpBody for Body {
|
|||||||
let mut hint = SizeHint::default();
|
let mut hint = SizeHint::default();
|
||||||
hint.set_exact(val.len() as u64);
|
hint.set_exact(val.len() as u64);
|
||||||
hint
|
hint
|
||||||
},
|
}
|
||||||
Kind::Once(None) => {
|
Kind::Once(None) => SizeHint::default(),
|
||||||
SizeHint::default()
|
|
||||||
},
|
|
||||||
#[cfg(feature = "stream")]
|
#[cfg(feature = "stream")]
|
||||||
Kind::Wrapped(..) => SizeHint::default(),
|
Kind::Wrapped(..) => SizeHint::default(),
|
||||||
Kind::Chan { content_length, .. } | Kind::H2 { content_length, .. } => {
|
Kind::Chan { content_length, .. } | Kind::H2 { content_length, .. } => {
|
||||||
@@ -355,7 +350,7 @@ impl HttpBody for Body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
hint
|
hint
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -393,14 +388,12 @@ impl Stream for Body {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// # Optional
|
/// # Optional
|
||||||
///
|
///
|
||||||
/// This function requires enabling the `stream` feature in your
|
/// This function requires enabling the `stream` feature in your
|
||||||
/// `Cargo.toml`.
|
/// `Cargo.toml`.
|
||||||
#[cfg(feature = "stream")]
|
#[cfg(feature = "stream")]
|
||||||
impl
|
impl From<Box<dyn Stream<Item = Result<Chunk, Box<dyn StdError + Send + Sync>>> + Send + Sync>>
|
||||||
From<Box<dyn Stream<Item = Result<Chunk, Box<dyn StdError + Send + Sync>>> + Send + Sync>>
|
|
||||||
for Body
|
for Body
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -487,13 +480,17 @@ impl Sender {
|
|||||||
Poll::Pending => (), // fallthrough
|
Poll::Pending => (), // fallthrough
|
||||||
}
|
}
|
||||||
|
|
||||||
self.tx.poll_ready(cx).map_err(|_| crate::Error::new_closed())
|
self.tx
|
||||||
|
.poll_ready(cx)
|
||||||
|
.map_err(|_| crate::Error::new_closed())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send data on this channel when it is ready.
|
/// Send data on this channel when it is ready.
|
||||||
pub async fn send_data(&mut self, chunk: Chunk) -> crate::Result<()> {
|
pub async fn send_data(&mut self, chunk: Chunk) -> crate::Result<()> {
|
||||||
futures_util::future::poll_fn(|cx| self.poll_ready(cx)).await?;
|
futures_util::future::poll_fn(|cx| self.poll_ready(cx)).await?;
|
||||||
self.tx.try_send(Ok(chunk)).map_err(|_| crate::Error::new_closed())
|
self.tx
|
||||||
|
.try_send(Ok(chunk))
|
||||||
|
.map_err(|_| crate::Error::new_closed())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Try to send data on this channel.
|
/// Try to send data on this channel.
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ pub struct IntoIter {
|
|||||||
inner: <Bytes as IntoIterator>::IntoIter,
|
inner: <Bytes as IntoIterator>::IntoIter,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl Chunk {
|
impl Chunk {
|
||||||
/// Converts this `Chunk` directly into the `Bytes` type without copies.
|
/// Converts this `Chunk` directly into the `Bytes` type without copies.
|
||||||
///
|
///
|
||||||
@@ -82,9 +81,7 @@ impl From<&'static str> for Chunk {
|
|||||||
impl From<Bytes> for Chunk {
|
impl From<Bytes> for Chunk {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(bytes: Bytes) -> Chunk {
|
fn from(bytes: Bytes) -> Chunk {
|
||||||
Chunk {
|
Chunk { bytes: bytes }
|
||||||
bytes: bytes,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,4 +173,3 @@ mod tests {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -59,4 +59,3 @@ fn _assert_send_sync() {
|
|||||||
_assert_send::<Chunk>();
|
_assert_send::<Chunk>();
|
||||||
_assert_sync::<Chunk>();
|
_assert_sync::<Chunk>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use std::error::Error as StdError;
|
|||||||
use bytes::Buf;
|
use bytes::Buf;
|
||||||
use http::HeaderMap;
|
use http::HeaderMap;
|
||||||
|
|
||||||
use crate::common::{Pin, Poll, task};
|
use crate::common::{task, Pin, Poll};
|
||||||
use http_body::{Body as HttpBody, SizeHint};
|
use http_body::{Body as HttpBody, SizeHint};
|
||||||
|
|
||||||
/// This trait represents a streaming body of a `Request` or `Response`.
|
/// This trait represents a streaming body of a `Request` or `Response`.
|
||||||
@@ -21,14 +21,20 @@ pub trait Payload: sealed::Sealed + Send + 'static {
|
|||||||
///
|
///
|
||||||
/// Similar to `Stream::poll_next`, this yields `Some(Data)` until
|
/// Similar to `Stream::poll_next`, this yields `Some(Data)` until
|
||||||
/// the body ends, when it yields `None`.
|
/// the body ends, when it yields `None`.
|
||||||
fn poll_data(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Option<Result<Self::Data, Self::Error>>>;
|
fn poll_data(
|
||||||
|
self: Pin<&mut Self>,
|
||||||
|
cx: &mut task::Context<'_>,
|
||||||
|
) -> Poll<Option<Result<Self::Data, Self::Error>>>;
|
||||||
|
|
||||||
/// Poll for an optional **single** `HeaderMap` of trailers.
|
/// Poll for an optional **single** `HeaderMap` of trailers.
|
||||||
///
|
///
|
||||||
/// This should **only** be called after `poll_data` has ended.
|
/// This should **only** be called after `poll_data` has ended.
|
||||||
///
|
///
|
||||||
/// Note: Trailers aren't currently used for HTTP/1, only for HTTP/2.
|
/// Note: Trailers aren't currently used for HTTP/1, only for HTTP/2.
|
||||||
fn poll_trailers(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Result<Option<HeaderMap>, Self::Error>> {
|
fn poll_trailers(
|
||||||
|
self: Pin<&mut Self>,
|
||||||
|
cx: &mut task::Context<'_>,
|
||||||
|
) -> Poll<Result<Option<HeaderMap>, Self::Error>> {
|
||||||
drop(cx);
|
drop(cx);
|
||||||
Poll::Ready(Ok(None))
|
Poll::Ready(Ok(None))
|
||||||
}
|
}
|
||||||
@@ -61,7 +67,6 @@ pub trait Payload: sealed::Sealed + Send + 'static {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl<T> Payload for T
|
impl<T> Payload for T
|
||||||
where
|
where
|
||||||
T: HttpBody + Send + 'static,
|
T: HttpBody + Send + 'static,
|
||||||
@@ -71,11 +76,17 @@ where
|
|||||||
type Data = T::Data;
|
type Data = T::Data;
|
||||||
type Error = T::Error;
|
type Error = T::Error;
|
||||||
|
|
||||||
fn poll_data(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Option<Result<Self::Data, Self::Error>>> {
|
fn poll_data(
|
||||||
|
self: Pin<&mut Self>,
|
||||||
|
cx: &mut task::Context<'_>,
|
||||||
|
) -> Poll<Option<Result<Self::Data, Self::Error>>> {
|
||||||
HttpBody::poll_data(self, cx)
|
HttpBody::poll_data(self, cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll_trailers(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Result<Option<HeaderMap>, Self::Error>> {
|
fn poll_trailers(
|
||||||
|
self: Pin<&mut Self>,
|
||||||
|
cx: &mut task::Context<'_>,
|
||||||
|
) -> Poll<Result<Option<HeaderMap>, Self::Error>> {
|
||||||
HttpBody::poll_trailers(self, cx)
|
HttpBody::poll_trailers(self, cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,5 +138,3 @@ impl<E: Payload> Payload for Box<E> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -17,19 +17,14 @@ use pin_project::{pin_project, project};
|
|||||||
use tokio::io::{AsyncRead, AsyncWrite};
|
use tokio::io::{AsyncRead, AsyncWrite};
|
||||||
use tower_service::Service;
|
use tower_service::Service;
|
||||||
|
|
||||||
use crate::body::Payload;
|
|
||||||
use crate::common::{BoxSendFuture, Exec, Executor, Future, Pin, Poll, task};
|
|
||||||
use crate::upgrade::Upgraded;
|
|
||||||
use crate::proto;
|
|
||||||
use super::dispatch;
|
use super::dispatch;
|
||||||
|
use crate::body::Payload;
|
||||||
|
use crate::common::{task, BoxSendFuture, Exec, Executor, Future, Pin, Poll};
|
||||||
|
use crate::proto;
|
||||||
|
use crate::upgrade::Upgraded;
|
||||||
use crate::{Body, Request, Response};
|
use crate::{Body, Request, Response};
|
||||||
|
|
||||||
type Http1Dispatcher<T, B, R> = proto::dispatch::Dispatcher<
|
type Http1Dispatcher<T, B, R> = proto::dispatch::Dispatcher<proto::dispatch::Client<B>, B, T, R>;
|
||||||
proto::dispatch::Client<B>,
|
|
||||||
B,
|
|
||||||
T,
|
|
||||||
R,
|
|
||||||
>;
|
|
||||||
|
|
||||||
#[pin_project]
|
#[pin_project]
|
||||||
enum ProtoClient<T, B>
|
enum ProtoClient<T, B>
|
||||||
@@ -46,18 +41,16 @@ where
|
|||||||
const DEFAULT_HTTP2_CONN_WINDOW: u32 = 1024 * 1024 * 5; // 5mb
|
const DEFAULT_HTTP2_CONN_WINDOW: u32 = 1024 * 1024 * 5; // 5mb
|
||||||
const DEFAULT_HTTP2_STREAM_WINDOW: u32 = 1024 * 1024 * 2; // 2mb
|
const DEFAULT_HTTP2_STREAM_WINDOW: u32 = 1024 * 1024 * 2; // 2mb
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// Returns a handshake future over some IO.
|
/// Returns a handshake future over some IO.
|
||||||
///
|
///
|
||||||
/// This is a shortcut for `Builder::new().handshake(io)`.
|
/// This is a shortcut for `Builder::new().handshake(io)`.
|
||||||
pub async fn handshake<T>(io: T) -> crate::Result<(SendRequest<crate::Body>, Connection<T, crate::Body>)>
|
pub async fn handshake<T>(
|
||||||
|
io: T,
|
||||||
|
) -> crate::Result<(SendRequest<crate::Body>, Connection<T, crate::Body>)>
|
||||||
where
|
where
|
||||||
T: AsyncRead + AsyncWrite + Unpin + Send + 'static,
|
T: AsyncRead + AsyncWrite + Unpin + Send + 'static,
|
||||||
{
|
{
|
||||||
Builder::new()
|
Builder::new().handshake(io).await
|
||||||
.handshake(io)
|
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The sender side of an established connection.
|
/// The sender side of an established connection.
|
||||||
@@ -65,7 +58,6 @@ pub struct SendRequest<B> {
|
|||||||
dispatch: dispatch::Sender<Request<B>, Response<Body>>,
|
dispatch: dispatch::Sender<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.
|
||||||
///
|
///
|
||||||
/// In most cases, this should just be spawned into an executor, so that it
|
/// In most cases, this should just be spawned into an executor, so that it
|
||||||
@@ -79,7 +71,6 @@ where
|
|||||||
inner: Option<ProtoClient<T, B>>,
|
inner: Option<ProtoClient<T, B>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// A builder to configure an HTTP connection.
|
/// A builder to configure an HTTP connection.
|
||||||
///
|
///
|
||||||
/// After setting options, the builder is used to create a handshake future.
|
/// After setting options, the builder is used to create a handshake future.
|
||||||
@@ -99,7 +90,7 @@ pub struct Builder {
|
|||||||
/// Yields a `Response` if successful.
|
/// Yields a `Response` if successful.
|
||||||
#[must_use = "futures do nothing unless polled"]
|
#[must_use = "futures do nothing unless polled"]
|
||||||
pub struct ResponseFuture {
|
pub struct ResponseFuture {
|
||||||
inner: ResponseFutureState
|
inner: ResponseFutureState,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ResponseFutureState {
|
enum ResponseFutureState {
|
||||||
@@ -139,8 +130,7 @@ pub(super) struct Http2SendRequest<B> {
|
|||||||
|
|
||||||
// ===== impl SendRequest
|
// ===== impl SendRequest
|
||||||
|
|
||||||
impl<B> SendRequest<B>
|
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.
|
||||||
@@ -148,7 +138,7 @@ impl<B> SendRequest<B>
|
|||||||
self.dispatch.poll_ready(cx)
|
self.dispatch.poll_ready(cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn when_ready(self) -> impl Future<Output=crate::Result<Self>> {
|
pub(super) fn when_ready(self) -> impl Future<Output = crate::Result<Self>> {
|
||||||
let mut me = Some(self);
|
let mut me = Some(self);
|
||||||
future::poll_fn(move |cx| {
|
future::poll_fn(move |cx| {
|
||||||
ready!(me.as_mut().unwrap().poll_ready(cx))?;
|
ready!(me.as_mut().unwrap().poll_ready(cx))?;
|
||||||
@@ -218,9 +208,7 @@ where
|
|||||||
/// ```
|
/// ```
|
||||||
pub fn send_request(&mut self, req: Request<B>) -> ResponseFuture {
|
pub fn send_request(&mut self, req: Request<B>) -> ResponseFuture {
|
||||||
let inner = match self.dispatch.send(req) {
|
let inner = match self.dispatch.send(req) {
|
||||||
Ok(rx) => {
|
Ok(rx) => ResponseFutureState::Waiting(rx),
|
||||||
ResponseFutureState::Waiting(rx)
|
|
||||||
},
|
|
||||||
Err(_req) => {
|
Err(_req) => {
|
||||||
debug!("connection was not ready");
|
debug!("connection was not ready");
|
||||||
let err = crate::Error::new_canceled().with("connection was not ready");
|
let err = crate::Error::new_canceled().with("connection was not ready");
|
||||||
@@ -228,12 +216,13 @@ where
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ResponseFuture {
|
ResponseFuture { inner }
|
||||||
inner,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn send_request_retryable(&mut self, req: Request<B>) -> impl Future<Output = Result<Response<Body>, (crate::Error, Option<Request<B>>)>> + Unpin
|
pub(crate) fn send_request_retryable(
|
||||||
|
&mut self,
|
||||||
|
req: Request<B>,
|
||||||
|
) -> impl Future<Output = Result<Response<Body>, (crate::Error, Option<Request<B>>)>> + Unpin
|
||||||
where
|
where
|
||||||
B: Send,
|
B: Send,
|
||||||
{
|
{
|
||||||
@@ -247,7 +236,7 @@ where
|
|||||||
Err(_) => panic!("dispatch dropped without returning error"),
|
Err(_) => panic!("dispatch dropped without returning error"),
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
},
|
}
|
||||||
Err(req) => {
|
Err(req) => {
|
||||||
debug!("connection was not ready");
|
debug!("connection was not ready");
|
||||||
let err = crate::Error::new_canceled().with("connection was not ready");
|
let err = crate::Error::new_canceled().with("connection was not ready");
|
||||||
@@ -259,7 +248,8 @@ where
|
|||||||
|
|
||||||
impl<B> Service<Request<B>> for SendRequest<B>
|
impl<B> Service<Request<B>> for SendRequest<B>
|
||||||
where
|
where
|
||||||
B: Payload + 'static, {
|
B: Payload + 'static,
|
||||||
|
{
|
||||||
type Response = Response<Body>;
|
type Response = Response<Body>;
|
||||||
type Error = crate::Error;
|
type Error = crate::Error;
|
||||||
type Future = ResponseFuture;
|
type Future = ResponseFuture;
|
||||||
@@ -275,8 +265,7 @@ where
|
|||||||
|
|
||||||
impl<B> fmt::Debug for SendRequest<B> {
|
impl<B> fmt::Debug for SendRequest<B> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.debug_struct("SendRequest")
|
f.debug_struct("SendRequest").finish()
|
||||||
.finish()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -296,7 +285,10 @@ impl<B> Http2SendRequest<B>
|
|||||||
where
|
where
|
||||||
B: Payload + 'static,
|
B: Payload + 'static,
|
||||||
{
|
{
|
||||||
pub(super) fn send_request_retryable(&mut self, req: Request<B>) -> impl Future<Output=Result<Response<Body>, (crate::Error, Option<Request<B>>)>>
|
pub(super) fn send_request_retryable(
|
||||||
|
&mut self,
|
||||||
|
req: Request<B>,
|
||||||
|
) -> impl Future<Output = Result<Response<Body>, (crate::Error, Option<Request<B>>)>>
|
||||||
where
|
where
|
||||||
B: Send,
|
B: Send,
|
||||||
{
|
{
|
||||||
@@ -310,7 +302,7 @@ where
|
|||||||
Err(_) => panic!("dispatch dropped without returning error"),
|
Err(_) => panic!("dispatch dropped without returning error"),
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
},
|
}
|
||||||
Err(req) => {
|
Err(req) => {
|
||||||
debug!("connection was not ready");
|
debug!("connection was not ready");
|
||||||
let err = crate::Error::new_canceled().with("connection was not ready");
|
let err = crate::Error::new_canceled().with("connection was not ready");
|
||||||
@@ -322,8 +314,7 @@ where
|
|||||||
|
|
||||||
impl<B> fmt::Debug for Http2SendRequest<B> {
|
impl<B> fmt::Debug for Http2SendRequest<B> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.debug_struct("Http2SendRequest")
|
f.debug_struct("Http2SendRequest").finish()
|
||||||
.finish()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -374,18 +365,14 @@ where
|
|||||||
/// to work with this function; or use the `without_shutdown` wrapper.
|
/// to work with this function; or use the `without_shutdown` wrapper.
|
||||||
pub fn poll_without_shutdown(&mut self, cx: &mut task::Context<'_>) -> Poll<crate::Result<()>> {
|
pub fn poll_without_shutdown(&mut self, cx: &mut task::Context<'_>) -> Poll<crate::Result<()>> {
|
||||||
match self.inner.as_mut().expect("already upgraded") {
|
match self.inner.as_mut().expect("already upgraded") {
|
||||||
&mut ProtoClient::H1(ref mut h1) => {
|
&mut ProtoClient::H1(ref mut h1) => h1.poll_without_shutdown(cx),
|
||||||
h1.poll_without_shutdown(cx)
|
&mut ProtoClient::H2(ref mut h2) => Pin::new(h2).poll(cx).map_ok(|_| ()),
|
||||||
},
|
|
||||||
&mut ProtoClient::H2(ref mut h2) => {
|
|
||||||
Pin::new(h2).poll(cx).map_ok(|_| ())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Prevent shutdown of the underlying IO object at the end of service the request,
|
/// Prevent shutdown of the underlying IO object at the end of service the request,
|
||||||
/// instead run `into_parts`. This is a convenience wrapper over `poll_without_shutdown`.
|
/// instead run `into_parts`. This is a convenience wrapper over `poll_without_shutdown`.
|
||||||
pub fn without_shutdown(self) -> impl Future<Output=crate::Result<Parts<T>>> {
|
pub fn without_shutdown(self) -> impl Future<Output = crate::Result<Parts<T>>> {
|
||||||
let mut conn = Some(self);
|
let mut conn = Some(self);
|
||||||
future::poll_fn(move |cx| -> Poll<crate::Result<Parts<T>>> {
|
future::poll_fn(move |cx| -> Poll<crate::Result<Parts<T>>> {
|
||||||
ready!(conn.as_mut().unwrap().poll_without_shutdown(cx))?;
|
ready!(conn.as_mut().unwrap().poll_without_shutdown(cx))?;
|
||||||
@@ -404,9 +391,7 @@ where
|
|||||||
|
|
||||||
fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
|
fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
|
||||||
match ready!(Pin::new(self.inner.as_mut().unwrap()).poll(cx))? {
|
match ready!(Pin::new(self.inner.as_mut().unwrap()).poll(cx))? {
|
||||||
proto::Dispatched::Shutdown => {
|
proto::Dispatched::Shutdown => Poll::Ready(Ok(())),
|
||||||
Poll::Ready(Ok(()))
|
|
||||||
},
|
|
||||||
proto::Dispatched::Upgrade(pending) => {
|
proto::Dispatched::Upgrade(pending) => {
|
||||||
let h1 = match mem::replace(&mut self.inner, None) {
|
let h1 = match mem::replace(&mut self.inner, None) {
|
||||||
Some(ProtoClient::H1(h1)) => h1,
|
Some(ProtoClient::H1(h1)) => h1,
|
||||||
@@ -427,8 +412,7 @@ where
|
|||||||
B: Payload + 'static,
|
B: Payload + 'static,
|
||||||
{
|
{
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.debug_struct("Connection")
|
f.debug_struct("Connection").finish()
|
||||||
.finish()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -519,7 +503,10 @@ impl Builder {
|
|||||||
/// Passing `None` will do nothing.
|
/// Passing `None` will do nothing.
|
||||||
///
|
///
|
||||||
/// If not set, hyper will use a default.
|
/// If not set, hyper will use a default.
|
||||||
pub fn http2_initial_connection_window_size(&mut self, sz: impl Into<Option<u32>>) -> &mut Self {
|
pub fn http2_initial_connection_window_size(
|
||||||
|
&mut self,
|
||||||
|
sz: impl Into<Option<u32>>,
|
||||||
|
) -> &mut Self {
|
||||||
if let Some(sz) = sz.into() {
|
if let Some(sz) = sz.into() {
|
||||||
self.h2_builder.initial_connection_window_size(sz);
|
self.h2_builder.initial_connection_window_size(sz);
|
||||||
}
|
}
|
||||||
@@ -527,7 +514,10 @@ impl Builder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Constructs a connection with the configured options and IO.
|
/// Constructs a connection with the configured options and IO.
|
||||||
pub fn handshake<T, B>(&self, io: T) -> impl Future<Output = crate::Result<(SendRequest<B>, Connection<T, B>)>>
|
pub fn handshake<T, B>(
|
||||||
|
&self,
|
||||||
|
io: T,
|
||||||
|
) -> impl Future<Output = crate::Result<(SendRequest<B>, Connection<T, B>)>>
|
||||||
where
|
where
|
||||||
T: AsyncRead + AsyncWrite + Unpin + Send + 'static,
|
T: AsyncRead + AsyncWrite + Unpin + Send + 'static,
|
||||||
B: Payload + 'static,
|
B: Payload + 'static,
|
||||||
@@ -563,12 +553,8 @@ impl Builder {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
SendRequest {
|
SendRequest { dispatch: tx },
|
||||||
dispatch: tx,
|
Connection { inner: Some(proto) },
|
||||||
},
|
|
||||||
Connection {
|
|
||||||
inner: Some(proto),
|
|
||||||
},
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -588,7 +574,7 @@ impl Future for ResponseFuture {
|
|||||||
// this is definite bug if it happens, but it shouldn't happen!
|
// this is definite bug if it happens, but it shouldn't happen!
|
||||||
Err(_canceled) => panic!("dispatch dropped without returning error"),
|
Err(_canceled) => panic!("dispatch dropped without returning error"),
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
ResponseFutureState::Error(ref mut err) => {
|
ResponseFutureState::Error(ref mut err) => {
|
||||||
Poll::Ready(Err(err.take().expect("polled after ready")))
|
Poll::Ready(Err(err.take().expect("polled after ready")))
|
||||||
}
|
}
|
||||||
@@ -598,8 +584,7 @@ impl Future for ResponseFuture {
|
|||||||
|
|
||||||
impl fmt::Debug for ResponseFuture {
|
impl fmt::Debug for ResponseFuture {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.debug_struct("ResponseFuture")
|
f.debug_struct("ResponseFuture").finish()
|
||||||
.finish()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -628,7 +613,6 @@ where
|
|||||||
trait AssertSend: Send {}
|
trait AssertSend: Send {}
|
||||||
trait AssertSendSync: Send + Sync {}
|
trait AssertSendSync: Send + Sync {}
|
||||||
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
impl<B: Send> AssertSendSync for SendRequest<B> {}
|
impl<B: Send> AssertSendSync for SendRequest<B> {}
|
||||||
|
|
||||||
@@ -637,7 +621,8 @@ impl<T: Send, B: Send> AssertSend for Connection<T, B>
|
|||||||
where
|
where
|
||||||
T: AsyncRead + AsyncWrite + Send + 'static,
|
T: AsyncRead + AsyncWrite + Send + 'static,
|
||||||
B: Payload + 'static,
|
B: Payload + 'static,
|
||||||
{}
|
{
|
||||||
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
impl<T: Send + Sync, B: Send + Sync> AssertSendSync for Connection<T, B>
|
impl<T: Send + Sync, B: Send + Sync> AssertSendSync for Connection<T, B>
|
||||||
@@ -645,11 +630,11 @@ where
|
|||||||
T: AsyncRead + AsyncWrite + Send + 'static,
|
T: AsyncRead + AsyncWrite + Send + 'static,
|
||||||
B: Payload + 'static,
|
B: Payload + 'static,
|
||||||
B::Data: Send + Sync + 'static,
|
B::Data: Send + Sync + 'static,
|
||||||
{}
|
{
|
||||||
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
impl AssertSendSync for Builder {}
|
impl AssertSendSync for Builder {}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
impl AssertSend for ResponseFuture {}
|
impl AssertSend for ResponseFuture {}
|
||||||
|
|
||||||
|
|||||||
@@ -22,11 +22,11 @@
|
|||||||
//! });
|
//! });
|
||||||
//! ```
|
//! ```
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
use std::future::Future;
|
||||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs};
|
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs};
|
||||||
|
use std::pin::Pin;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::task::{self, Poll};
|
use std::task::{self, Poll};
|
||||||
use std::pin::Pin;
|
|
||||||
use std::future::Future;
|
|
||||||
use std::{fmt, io, vec};
|
use std::{fmt, io, vec};
|
||||||
|
|
||||||
use tokio::task::JoinHandle;
|
use tokio::task::JoinHandle;
|
||||||
@@ -394,7 +394,8 @@ mod tests {
|
|||||||
let dst = ::http::Uri::from_static("http://[::1]:8080/");
|
let dst = ::http::Uri::from_static("http://[::1]:8080/");
|
||||||
|
|
||||||
let mut addrs =
|
let mut addrs =
|
||||||
IpAddrs::try_parse(dst.host().expect("host"), dst.port_u16().expect("port")).expect("try_parse");
|
IpAddrs::try_parse(dst.host().expect("host"), dst.port_u16().expect("port"))
|
||||||
|
.expect("try_parse");
|
||||||
|
|
||||||
let expected = "[::1]:8080".parse::<SocketAddr>().expect("expected");
|
let expected = "[::1]:8080".parse::<SocketAddr>().expect("expected");
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
use std::error::Error as StdError;
|
use std::error::Error as StdError;
|
||||||
use std::future::Future;
|
|
||||||
use std::marker::PhantomData;
|
|
||||||
use std::pin::Pin;
|
|
||||||
use std::task::{self, Poll};
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::future::Future;
|
||||||
use std::io;
|
use std::io;
|
||||||
|
use std::marker::PhantomData;
|
||||||
use std::net::{IpAddr, SocketAddr};
|
use std::net::{IpAddr, SocketAddr};
|
||||||
|
use std::pin::Pin;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use std::task::{self, Poll};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use futures_util::future::Either;
|
use futures_util::future::Either;
|
||||||
@@ -20,7 +20,6 @@ use super::dns::{self, resolve, GaiResolver, Resolve};
|
|||||||
use super::{Connected, Connection};
|
use super::{Connected, Connection};
|
||||||
//#[cfg(feature = "runtime")] use super::dns::TokioThreadpoolGaiResolver;
|
//#[cfg(feature = "runtime")] use super::dns::TokioThreadpoolGaiResolver;
|
||||||
|
|
||||||
|
|
||||||
/// A connector for the `http` scheme.
|
/// A connector for the `http` scheme.
|
||||||
///
|
///
|
||||||
/// Performs DNS resolution in a thread pool, and then connects over TCP.
|
/// Performs DNS resolution in a thread pool, and then connects over TCP.
|
||||||
@@ -256,10 +255,7 @@ impl<R> HttpConnector<R>
|
|||||||
where
|
where
|
||||||
R: Resolve,
|
R: Resolve,
|
||||||
{
|
{
|
||||||
async fn call_async(
|
async fn call_async(&mut self, dst: Uri) -> Result<TcpStream, ConnectError> {
|
||||||
&mut self,
|
|
||||||
dst: Uri,
|
|
||||||
) -> Result<TcpStream, ConnectError> {
|
|
||||||
trace!(
|
trace!(
|
||||||
"Http::connect; scheme={:?}, host={:?}, port={:?}",
|
"Http::connect; scheme={:?}, host={:?}, port={:?}",
|
||||||
dst.scheme(),
|
dst.scheme(),
|
||||||
@@ -292,7 +288,13 @@ where
|
|||||||
};
|
};
|
||||||
let port = match dst.port() {
|
let port = match dst.port() {
|
||||||
Some(port) => port.as_u16(),
|
Some(port) => port.as_u16(),
|
||||||
None => if dst.scheme() == Some(&Scheme::HTTPS) { 443 } else { 80 },
|
None => {
|
||||||
|
if dst.scheme() == Some(&Scheme::HTTPS) {
|
||||||
|
443
|
||||||
|
} else {
|
||||||
|
80
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let config = &self.config;
|
let config = &self.config;
|
||||||
@@ -348,9 +350,7 @@ impl Connection for TcpStream {
|
|||||||
fn connected(&self) -> Connected {
|
fn connected(&self) -> Connected {
|
||||||
let connected = Connected::new();
|
let connected = Connected::new();
|
||||||
if let Ok(remote_addr) = self.peer_addr() {
|
if let Ok(remote_addr) = self.peer_addr() {
|
||||||
connected.extra(HttpInfo {
|
connected.extra(HttpInfo { remote_addr })
|
||||||
remote_addr,
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
connected
|
connected
|
||||||
}
|
}
|
||||||
@@ -389,7 +389,6 @@ impl<R: Resolve> Future for HttpConnecting<R> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Not publicly exported (so missing_docs doesn't trigger).
|
// Not publicly exported (so missing_docs doesn't trigger).
|
||||||
pub struct ConnectError {
|
pub struct ConnectError {
|
||||||
msg: Box<str>,
|
msg: Box<str>,
|
||||||
@@ -531,14 +530,7 @@ impl ConnectingTcpRemote {
|
|||||||
let mut err = None;
|
let mut err = None;
|
||||||
for addr in &mut self.addrs {
|
for addr in &mut self.addrs {
|
||||||
debug!("connecting to {}", addr);
|
debug!("connecting to {}", addr);
|
||||||
match connect(
|
match connect(&addr, local_addr, reuse_address, self.connect_timeout)?.await {
|
||||||
&addr,
|
|
||||||
local_addr,
|
|
||||||
reuse_address,
|
|
||||||
self.connect_timeout,
|
|
||||||
)?
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
Ok(tcp) => {
|
Ok(tcp) => {
|
||||||
debug!("connected to {:?}", tcp.peer_addr().ok());
|
debug!("connected to {:?}", tcp.peer_addr().ok());
|
||||||
return Ok(tcp);
|
return Ok(tcp);
|
||||||
@@ -606,13 +598,9 @@ impl ConnectingTcp {
|
|||||||
..
|
..
|
||||||
} = self;
|
} = self;
|
||||||
match self.fallback {
|
match self.fallback {
|
||||||
None => {
|
None => self.preferred.connect(local_addr, reuse_address).await,
|
||||||
self.preferred
|
|
||||||
.connect(local_addr, reuse_address)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
Some(mut fallback) => {
|
Some(mut fallback) => {
|
||||||
let preferred_fut = self.preferred.connect(local_addr, reuse_address);
|
let preferred_fut = self.preferred.connect(local_addr, reuse_address);
|
||||||
futures_util::pin_mut!(preferred_fut);
|
futures_util::pin_mut!(preferred_fut);
|
||||||
|
|
||||||
let fallback_fut = fallback.remote.connect(local_addr, reuse_address);
|
let fallback_fut = fallback.remote.connect(local_addr, reuse_address);
|
||||||
@@ -652,10 +640,7 @@ mod tests {
|
|||||||
use super::super::sealed::Connect;
|
use super::super::sealed::Connect;
|
||||||
use super::HttpConnector;
|
use super::HttpConnector;
|
||||||
|
|
||||||
async fn connect<C>(
|
async fn connect<C>(connector: C, dst: Uri) -> Result<C::Transport, C::Error>
|
||||||
connector: C,
|
|
||||||
dst: Uri,
|
|
||||||
) -> Result<C::Transport, C::Error>
|
|
||||||
where
|
where
|
||||||
C: Connect,
|
C: Connect,
|
||||||
{
|
{
|
||||||
@@ -795,7 +780,6 @@ mod tests {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let (start, stream) = rt
|
let (start, stream) = rt
|
||||||
.block_on(async move {
|
.block_on(async move {
|
||||||
let addrs = hosts
|
let addrs = hosts
|
||||||
|
|||||||
@@ -7,11 +7,14 @@
|
|||||||
//! - Types to build custom connectors.
|
//! - Types to build custom connectors.
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use ::http::{Response};
|
use ::http::Response;
|
||||||
|
|
||||||
#[cfg(feature = "tcp")] pub mod dns;
|
#[cfg(feature = "tcp")]
|
||||||
#[cfg(feature = "tcp")] mod http;
|
pub mod dns;
|
||||||
#[cfg(feature = "tcp")] pub use self::http::{HttpConnector, HttpInfo};
|
#[cfg(feature = "tcp")]
|
||||||
|
mod http;
|
||||||
|
#[cfg(feature = "tcp")]
|
||||||
|
pub use self::http::{HttpConnector, HttpInfo};
|
||||||
|
|
||||||
/// Describes a type returned by a connector.
|
/// Describes a type returned by a connector.
|
||||||
pub trait Connection {
|
pub trait Connection {
|
||||||
@@ -115,8 +118,7 @@ impl Clone for Extra {
|
|||||||
|
|
||||||
impl fmt::Debug for Extra {
|
impl fmt::Debug for Extra {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.debug_struct("Extra")
|
f.debug_struct("Extra").finish()
|
||||||
.finish()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,7 +135,7 @@ struct ExtraEnvelope<T>(T);
|
|||||||
|
|
||||||
impl<T> ExtraInner for ExtraEnvelope<T>
|
impl<T> ExtraInner for ExtraEnvelope<T>
|
||||||
where
|
where
|
||||||
T: Clone + Send + Sync + 'static
|
T: Clone + Send + Sync + 'static,
|
||||||
{
|
{
|
||||||
fn clone_box(&self) -> Box<dyn ExtraInner> {
|
fn clone_box(&self) -> Box<dyn ExtraInner> {
|
||||||
Box::new(self.clone())
|
Box::new(self.clone())
|
||||||
@@ -154,7 +156,7 @@ impl<T: Clone> Clone for ExtraChain<T> {
|
|||||||
|
|
||||||
impl<T> ExtraInner for ExtraChain<T>
|
impl<T> ExtraInner for ExtraChain<T>
|
||||||
where
|
where
|
||||||
T: Clone + Send + Sync + 'static
|
T: Clone + Send + Sync + 'static,
|
||||||
{
|
{
|
||||||
fn clone_box(&self) -> Box<dyn ExtraInner> {
|
fn clone_box(&self) -> Box<dyn ExtraInner> {
|
||||||
Box::new(self.clone())
|
Box::new(self.clone())
|
||||||
@@ -172,8 +174,8 @@ pub(super) mod sealed {
|
|||||||
use ::http::Uri;
|
use ::http::Uri;
|
||||||
use tokio::io::{AsyncRead, AsyncWrite};
|
use tokio::io::{AsyncRead, AsyncWrite};
|
||||||
|
|
||||||
|
use super::Connection;
|
||||||
use crate::common::{Future, Unpin};
|
use crate::common::{Future, Unpin};
|
||||||
use super::{Connection};
|
|
||||||
|
|
||||||
/// Connect to a destination, returning an IO transport.
|
/// Connect to a destination, returning an IO transport.
|
||||||
///
|
///
|
||||||
@@ -193,14 +195,14 @@ pub(super) mod sealed {
|
|||||||
/// An error occured when trying to connect.
|
/// An error occured when trying to connect.
|
||||||
type Error: Into<Box<dyn StdError + Send + Sync>>;
|
type Error: Into<Box<dyn StdError + Send + Sync>>;
|
||||||
/// A Future that will resolve to the connected Transport.
|
/// A Future that will resolve to the connected Transport.
|
||||||
type Future: Future<Output=Result<Self::Transport, Self::Error>>;
|
type Future: Future<Output = Result<Self::Transport, Self::Error>>;
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
fn connect(self, internal_only: Internal, dst: Uri) -> Self::Future;
|
fn connect(self, internal_only: Internal, dst: Uri) -> Self::Future;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S, T> Connect for S
|
impl<S, T> Connect for S
|
||||||
where
|
where
|
||||||
S: tower_service::Service<Uri, Response=T> + Send,
|
S: tower_service::Service<Uri, Response = T> + Send,
|
||||||
S::Error: Into<Box<dyn StdError + Send + Sync>>,
|
S::Error: Into<Box<dyn StdError + Send + Sync>>,
|
||||||
S::Future: Unpin + Send,
|
S::Future: Unpin + Send,
|
||||||
T: AsyncRead + AsyncWrite + Connection + Unpin + Send + 'static,
|
T: AsyncRead + AsyncWrite + Connection + Unpin + Send + 'static,
|
||||||
@@ -215,11 +217,12 @@ pub(super) mod sealed {
|
|||||||
|
|
||||||
impl<S, T> Sealed for S
|
impl<S, T> Sealed for S
|
||||||
where
|
where
|
||||||
S: tower_service::Service<Uri, Response=T> + Send,
|
S: tower_service::Service<Uri, Response = T> + Send,
|
||||||
S::Error: Into<Box<dyn StdError + Send + Sync>>,
|
S::Error: Into<Box<dyn StdError + Send + Sync>>,
|
||||||
S::Future: Unpin + Send,
|
S::Future: Unpin + Send,
|
||||||
T: AsyncRead + AsyncWrite + Connection + Unpin + Send + 'static,
|
T: AsyncRead + AsyncWrite + Connection + Unpin + Send + 'static,
|
||||||
{}
|
{
|
||||||
|
}
|
||||||
|
|
||||||
pub trait Sealed {}
|
pub trait Sealed {}
|
||||||
#[allow(missing_debug_implementations)]
|
#[allow(missing_debug_implementations)]
|
||||||
@@ -228,7 +231,7 @@ pub(super) mod sealed {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{Connected};
|
use super::Connected;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
struct Ex1(usize);
|
struct Ex1(usize);
|
||||||
@@ -241,18 +244,13 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_connected_extra() {
|
fn test_connected_extra() {
|
||||||
let c1 = Connected::new()
|
let c1 = Connected::new().extra(Ex1(41));
|
||||||
.extra(Ex1(41));
|
|
||||||
|
|
||||||
let mut res1 = crate::Response::new(crate::Body::empty());
|
let mut res1 = crate::Response::new(crate::Body::empty());
|
||||||
|
|
||||||
assert_eq!(res1.extensions().get::<Ex1>(), None);
|
assert_eq!(res1.extensions().get::<Ex1>(), None);
|
||||||
|
|
||||||
c1
|
c1.extra.as_ref().expect("c1 extra").set(&mut res1);
|
||||||
.extra
|
|
||||||
.as_ref()
|
|
||||||
.expect("c1 extra")
|
|
||||||
.set(&mut res1);
|
|
||||||
|
|
||||||
assert_eq!(res1.extensions().get::<Ex1>(), Some(&Ex1(41)));
|
assert_eq!(res1.extensions().get::<Ex1>(), Some(&Ex1(41)));
|
||||||
}
|
}
|
||||||
@@ -273,11 +271,7 @@ mod tests {
|
|||||||
assert_eq!(res1.extensions().get::<Ex2>(), None);
|
assert_eq!(res1.extensions().get::<Ex2>(), None);
|
||||||
assert_eq!(res1.extensions().get::<Ex3>(), None);
|
assert_eq!(res1.extensions().get::<Ex3>(), None);
|
||||||
|
|
||||||
c1
|
c1.extra.as_ref().expect("c1 extra").set(&mut res1);
|
||||||
.extra
|
|
||||||
.as_ref()
|
|
||||||
.expect("c1 extra")
|
|
||||||
.set(&mut res1);
|
|
||||||
|
|
||||||
assert_eq!(res1.extensions().get::<Ex1>(), Some(&Ex1(45)));
|
assert_eq!(res1.extensions().get::<Ex1>(), Some(&Ex1(45)));
|
||||||
assert_eq!(res1.extensions().get::<Ex2>(), Some(&Ex2("zoom")));
|
assert_eq!(res1.extensions().get::<Ex2>(), Some(&Ex2("zoom")));
|
||||||
@@ -291,11 +285,7 @@ mod tests {
|
|||||||
|
|
||||||
let mut res2 = crate::Response::new(crate::Body::empty());
|
let mut res2 = crate::Response::new(crate::Body::empty());
|
||||||
|
|
||||||
c2
|
c2.extra.as_ref().expect("c2 extra").set(&mut res2);
|
||||||
.extra
|
|
||||||
.as_ref()
|
|
||||||
.expect("c2 extra")
|
|
||||||
.set(&mut res2);
|
|
||||||
|
|
||||||
assert_eq!(res2.extensions().get::<Ex1>(), Some(&Ex1(99)));
|
assert_eq!(res2.extensions().get::<Ex1>(), Some(&Ex1(99)));
|
||||||
assert_eq!(res2.extensions().get::<Ex2>(), Some(&Ex2("hiccup")));
|
assert_eq!(res2.extensions().get::<Ex2>(), Some(&Ex2("hiccup")));
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use futures_core::Stream;
|
|
||||||
use futures_channel::{mpsc, oneshot};
|
use futures_channel::{mpsc, oneshot};
|
||||||
|
use futures_core::Stream;
|
||||||
use futures_util::future;
|
use futures_util::future;
|
||||||
|
|
||||||
use crate::common::{Future, Pin, Poll, task};
|
use crate::common::{task, Future, Pin, Poll};
|
||||||
|
|
||||||
pub type RetryPromise<T, U> = oneshot::Receiver<Result<U, (crate::Error, Option<T>)>>;
|
pub type RetryPromise<T, U> = oneshot::Receiver<Result<U, (crate::Error, Option<T>)>>;
|
||||||
pub type Promise<T> = oneshot::Receiver<Result<T, crate::Error>>;
|
pub type Promise<T> = oneshot::Receiver<Result<T, crate::Error>>;
|
||||||
@@ -52,7 +52,8 @@ pub struct UnboundedSender<T, U> {
|
|||||||
|
|
||||||
impl<T, U> Sender<T, U> {
|
impl<T, U> Sender<T, U> {
|
||||||
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.giver.poll_want(cx)
|
self.giver
|
||||||
|
.poll_want(cx)
|
||||||
.map_err(|_| crate::Error::new_closed())
|
.map_err(|_| crate::Error::new_closed())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,7 +83,8 @@ impl<T, U> Sender<T, U> {
|
|||||||
return Err(val);
|
return Err(val);
|
||||||
}
|
}
|
||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
self.inner.unbounded_send(Envelope(Some((val, Callback::Retry(tx)))))
|
self.inner
|
||||||
|
.unbounded_send(Envelope(Some((val, Callback::Retry(tx)))))
|
||||||
.map(move |_| rx)
|
.map(move |_| rx)
|
||||||
.map_err(|e| e.into_inner().0.take().expect("envelope not dropped").0)
|
.map_err(|e| e.into_inner().0.take().expect("envelope not dropped").0)
|
||||||
}
|
}
|
||||||
@@ -92,7 +94,8 @@ impl<T, U> Sender<T, U> {
|
|||||||
return Err(val);
|
return Err(val);
|
||||||
}
|
}
|
||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
self.inner.unbounded_send(Envelope(Some((val, Callback::NoRetry(tx)))))
|
self.inner
|
||||||
|
.unbounded_send(Envelope(Some((val, Callback::NoRetry(tx)))))
|
||||||
.map(move |_| rx)
|
.map(move |_| rx)
|
||||||
.map_err(|e| e.into_inner().0.take().expect("envelope not dropped").0)
|
.map_err(|e| e.into_inner().0.take().expect("envelope not dropped").0)
|
||||||
}
|
}
|
||||||
@@ -116,7 +119,8 @@ impl<T, U> UnboundedSender<T, U> {
|
|||||||
|
|
||||||
pub fn try_send(&mut self, val: T) -> Result<RetryPromise<T, U>, T> {
|
pub fn try_send(&mut self, val: T) -> Result<RetryPromise<T, U>, T> {
|
||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
self.inner.unbounded_send(Envelope(Some((val, Callback::Retry(tx)))))
|
self.inner
|
||||||
|
.unbounded_send(Envelope(Some((val, Callback::Retry(tx)))))
|
||||||
.map(move |_| rx)
|
.map(move |_| rx)
|
||||||
.map_err(|e| e.into_inner().0.take().expect("envelope not dropped").0)
|
.map_err(|e| e.into_inner().0.take().expect("envelope not dropped").0)
|
||||||
}
|
}
|
||||||
@@ -137,15 +141,18 @@ pub struct Receiver<T, U> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T, U> Receiver<T, U> {
|
impl<T, U> Receiver<T, U> {
|
||||||
pub(crate) fn poll_next(&mut self, cx: &mut task::Context<'_>) -> Poll<Option<(T, Callback<T, U>)>> {
|
pub(crate) fn poll_next(
|
||||||
|
&mut self,
|
||||||
|
cx: &mut task::Context<'_>,
|
||||||
|
) -> Poll<Option<(T, Callback<T, U>)>> {
|
||||||
match Pin::new(&mut self.inner).poll_next(cx) {
|
match Pin::new(&mut self.inner).poll_next(cx) {
|
||||||
Poll::Ready(item) => Poll::Ready(item.map(|mut env| {
|
Poll::Ready(item) => {
|
||||||
env.0.take().expect("envelope not dropped")
|
Poll::Ready(item.map(|mut env| env.0.take().expect("envelope not dropped")))
|
||||||
})),
|
}
|
||||||
Poll::Pending => {
|
Poll::Pending => {
|
||||||
self.taker.want();
|
self.taker.want();
|
||||||
Poll::Pending
|
Poll::Pending
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,7 +183,10 @@ struct Envelope<T, U>(Option<(T, Callback<T, U>)>);
|
|||||||
impl<T, U> Drop for Envelope<T, U> {
|
impl<T, U> Drop for Envelope<T, U> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if let Some((val, cb)) = self.0.take() {
|
if let Some((val, cb)) = self.0.take() {
|
||||||
let _ = cb.send(Err((crate::Error::new_canceled().with("connection closed"), Some(val))));
|
let _ = cb.send(Err((
|
||||||
|
crate::Error::new_canceled().with("connection closed"),
|
||||||
|
Some(val),
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -205,7 +215,7 @@ impl<T, U> Callback<T, U> {
|
|||||||
match self {
|
match self {
|
||||||
Callback::Retry(tx) => {
|
Callback::Retry(tx) => {
|
||||||
let _ = tx.send(val);
|
let _ = tx.send(val);
|
||||||
},
|
}
|
||||||
Callback::NoRetry(tx) => {
|
Callback::NoRetry(tx) => {
|
||||||
let _ = tx.send(val.map_err(|e| e.0));
|
let _ = tx.send(val.map_err(|e| e.0));
|
||||||
}
|
}
|
||||||
@@ -214,29 +224,25 @@ impl<T, U> Callback<T, U> {
|
|||||||
|
|
||||||
pub(crate) fn send_when(
|
pub(crate) fn send_when(
|
||||||
self,
|
self,
|
||||||
mut when: impl Future<Output=Result<U, (crate::Error, Option<T>)>> + Unpin,
|
mut when: impl Future<Output = Result<U, (crate::Error, Option<T>)>> + Unpin,
|
||||||
) -> impl Future<Output=()> {
|
) -> impl Future<Output = ()> {
|
||||||
let mut cb = Some(self);
|
let mut cb = Some(self);
|
||||||
|
|
||||||
// "select" on this callback being canceled, and the future completing
|
// "select" on this callback being canceled, and the future completing
|
||||||
future::poll_fn(move |cx| {
|
future::poll_fn(move |cx| {
|
||||||
match Pin::new(&mut when).poll(cx) {
|
match Pin::new(&mut when).poll(cx) {
|
||||||
Poll::Ready(Ok(res)) => {
|
Poll::Ready(Ok(res)) => {
|
||||||
cb.take()
|
cb.take().expect("polled after complete").send(Ok(res));
|
||||||
.expect("polled after complete")
|
|
||||||
.send(Ok(res));
|
|
||||||
Poll::Ready(())
|
Poll::Ready(())
|
||||||
},
|
}
|
||||||
Poll::Pending => {
|
Poll::Pending => {
|
||||||
// check if the callback is canceled
|
// check if the callback is canceled
|
||||||
ready!(cb.as_mut().unwrap().poll_canceled(cx));
|
ready!(cb.as_mut().unwrap().poll_canceled(cx));
|
||||||
trace!("send_when canceled");
|
trace!("send_when canceled");
|
||||||
Poll::Ready(())
|
Poll::Ready(())
|
||||||
},
|
}
|
||||||
Poll::Ready(Err(err)) => {
|
Poll::Ready(Err(err)) => {
|
||||||
cb.take()
|
cb.take().expect("polled after complete").send(Err(err));
|
||||||
.expect("polled after complete")
|
|
||||||
.send(Err(err));
|
|
||||||
Poll::Ready(())
|
Poll::Ready(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -253,7 +259,7 @@ mod tests {
|
|||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::task::{Context, Poll};
|
use std::task::{Context, Poll};
|
||||||
|
|
||||||
use super::{Callback, channel, Receiver};
|
use super::{channel, Callback, Receiver};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Custom(i32);
|
struct Custom(i32);
|
||||||
@@ -271,14 +277,14 @@ mod tests {
|
|||||||
|
|
||||||
impl<F, T> Future for PollOnce<'_, F>
|
impl<F, T> Future for PollOnce<'_, F>
|
||||||
where
|
where
|
||||||
F: Future<Output = T> + Unpin
|
F: Future<Output = T> + Unpin,
|
||||||
{
|
{
|
||||||
type Output = Option<()>;
|
type Output = Option<()>;
|
||||||
|
|
||||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
match Pin::new(&mut self.0).poll(cx) {
|
match Pin::new(&mut self.0).poll(cx) {
|
||||||
Poll::Ready(_) => Poll::Ready(Some(())),
|
Poll::Ready(_) => Poll::Ready(Some(())),
|
||||||
Poll::Pending => Poll::Ready(None)
|
Poll::Pending => Poll::Ready(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -357,7 +363,7 @@ mod tests {
|
|||||||
let poll_once = PollOnce(&mut rx);
|
let poll_once = PollOnce(&mut rx);
|
||||||
let opt = poll_once.await;
|
let opt = poll_once.await;
|
||||||
if opt.is_none() {
|
if opt.is_none() {
|
||||||
break
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -64,17 +64,18 @@ use std::sync::Arc;
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use futures_channel::oneshot;
|
use futures_channel::oneshot;
|
||||||
use futures_util::future::{self, FutureExt as _, TryFutureExt as _, Either};
|
use futures_util::future::{self, Either, FutureExt as _, TryFutureExt as _};
|
||||||
use http::{Method, Request, Response, Uri, Version};
|
|
||||||
use http::header::{HeaderValue, HOST};
|
use http::header::{HeaderValue, HOST};
|
||||||
use http::uri::Scheme;
|
use http::uri::Scheme;
|
||||||
|
use http::{Method, Request, Response, Uri, Version};
|
||||||
|
|
||||||
use crate::body::{Body, Payload};
|
use self::connect::{sealed::Connect, Alpn, Connected, Connection};
|
||||||
use crate::common::{lazy as hyper_lazy, BoxSendFuture, Executor, Lazy, Future, Pin, Poll, task};
|
|
||||||
use self::connect::{Alpn, sealed::Connect, Connected, Connection};
|
|
||||||
use self::pool::{Key as PoolKey, Pool, Poolable, Pooled, Reservation};
|
use self::pool::{Key as PoolKey, Pool, Poolable, Pooled, Reservation};
|
||||||
|
use crate::body::{Body, Payload};
|
||||||
|
use crate::common::{lazy as hyper_lazy, task, BoxSendFuture, Executor, Future, Lazy, Pin, Poll};
|
||||||
|
|
||||||
#[cfg(feature = "tcp")] pub use self::connect::HttpConnector;
|
#[cfg(feature = "tcp")]
|
||||||
|
pub use self::connect::HttpConnector;
|
||||||
|
|
||||||
pub mod conn;
|
pub mod conn;
|
||||||
pub mod connect;
|
pub mod connect;
|
||||||
@@ -104,7 +105,7 @@ struct Config {
|
|||||||
/// This is returned by `Client::request` (and `Client::get`).
|
/// This is returned by `Client::request` (and `Client::get`).
|
||||||
#[must_use = "futures do nothing unless polled"]
|
#[must_use = "futures do nothing unless polled"]
|
||||||
pub struct ResponseFuture {
|
pub struct ResponseFuture {
|
||||||
inner: Pin<Box<dyn Future<Output=crate::Result<Response<Body>>> + Send>>,
|
inner: Pin<Box<dyn Future<Output = crate::Result<Response<Body>>> + Send>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== impl Client =====
|
// ===== impl Client =====
|
||||||
@@ -157,11 +158,12 @@ impl Client<(), Body> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<C, B> Client<C, B>
|
impl<C, B> Client<C, B>
|
||||||
where C: Connect + Clone + Send + Sync + 'static,
|
where
|
||||||
C::Transport: Unpin + Send + 'static,
|
C: Connect + Clone + Send + Sync + 'static,
|
||||||
C::Future: Unpin + Send + 'static,
|
C::Transport: Unpin + Send + 'static,
|
||||||
B: Payload + Unpin + Send + 'static,
|
C::Future: Unpin + Send + 'static,
|
||||||
B::Data: Send + Unpin,
|
B: Payload + Unpin + Send + 'static,
|
||||||
|
B::Data: Send + Unpin,
|
||||||
{
|
{
|
||||||
/// Send a `GET` request to the supplied `Uri`.
|
/// Send a `GET` request to the supplied `Uri`.
|
||||||
///
|
///
|
||||||
@@ -223,13 +225,19 @@ where C: Connect + Clone + Send + Sync + 'static,
|
|||||||
let is_http_connect = req.method() == &Method::CONNECT;
|
let is_http_connect = req.method() == &Method::CONNECT;
|
||||||
match req.version() {
|
match req.version() {
|
||||||
Version::HTTP_11 => (),
|
Version::HTTP_11 => (),
|
||||||
Version::HTTP_10 => if is_http_connect {
|
Version::HTTP_10 => {
|
||||||
warn!("CONNECT is not allowed for HTTP/1.0");
|
if is_http_connect {
|
||||||
return ResponseFuture::new(Box::new(future::err(crate::Error::new_user_unsupported_request_method())));
|
warn!("CONNECT is not allowed for HTTP/1.0");
|
||||||
},
|
return ResponseFuture::new(Box::new(future::err(
|
||||||
other_h2 @ Version::HTTP_2 => if self.config.ver != Ver::Http2 {
|
crate::Error::new_user_unsupported_request_method(),
|
||||||
return ResponseFuture::error_version(other_h2);
|
)));
|
||||||
},
|
}
|
||||||
|
}
|
||||||
|
other_h2 @ Version::HTTP_2 => {
|
||||||
|
if self.config.ver != Ver::Http2 {
|
||||||
|
return ResponseFuture::error_version(other_h2);
|
||||||
|
}
|
||||||
|
}
|
||||||
// completely unsupported HTTP version (like HTTP/0.9)!
|
// completely unsupported HTTP version (like HTTP/0.9)!
|
||||||
other => return ResponseFuture::error_version(other),
|
other => return ResponseFuture::error_version(other),
|
||||||
};
|
};
|
||||||
@@ -245,7 +253,11 @@ where C: Connect + Clone + Send + Sync + 'static,
|
|||||||
ResponseFuture::new(Box::new(self.retryably_send_request(req, pool_key)))
|
ResponseFuture::new(Box::new(self.retryably_send_request(req, pool_key)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn retryably_send_request(&self, req: Request<B>, pool_key: PoolKey) -> impl Future<Output=crate::Result<Response<Body>>> {
|
fn retryably_send_request(
|
||||||
|
&self,
|
||||||
|
req: Request<B>,
|
||||||
|
pool_key: PoolKey,
|
||||||
|
) -> impl Future<Output = crate::Result<Response<Body>>> {
|
||||||
let client = self.clone();
|
let client = self.clone();
|
||||||
let uri = req.uri().clone();
|
let uri = req.uri().clone();
|
||||||
|
|
||||||
@@ -265,7 +277,10 @@ where C: Connect + Clone + Send + Sync + 'static,
|
|||||||
return Poll::Ready(Err(reason));
|
return Poll::Ready(Err(reason));
|
||||||
}
|
}
|
||||||
|
|
||||||
trace!("unstarted request canceled, trying again (reason={:?})", reason);
|
trace!(
|
||||||
|
"unstarted request canceled, trying again (reason={:?})",
|
||||||
|
reason
|
||||||
|
);
|
||||||
*req.uri_mut() = uri.clone();
|
*req.uri_mut() = uri.clone();
|
||||||
send_fut = client.send_request(req, pool_key.clone());
|
send_fut = client.send_request(req, pool_key.clone());
|
||||||
}
|
}
|
||||||
@@ -273,7 +288,11 @@ where C: Connect + Clone + Send + Sync + 'static,
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send_request(&self, mut req: Request<B>, pool_key: PoolKey) -> impl Future<Output=Result<Response<Body>, ClientError<B>>> + Unpin {
|
fn send_request(
|
||||||
|
&self,
|
||||||
|
mut req: Request<B>,
|
||||||
|
pool_key: PoolKey,
|
||||||
|
) -> impl Future<Output = Result<Response<Body>, ClientError<B>>> + Unpin {
|
||||||
let conn = self.connection_for(req.uri().clone(), pool_key);
|
let conn = self.connection_for(req.uri().clone(), pool_key);
|
||||||
|
|
||||||
let set_host = self.config.set_host;
|
let set_host = self.config.set_host;
|
||||||
@@ -282,18 +301,16 @@ where C: Connect + Clone + Send + Sync + 'static,
|
|||||||
if pooled.is_http1() {
|
if pooled.is_http1() {
|
||||||
if set_host {
|
if set_host {
|
||||||
let uri = req.uri().clone();
|
let uri = req.uri().clone();
|
||||||
req
|
req.headers_mut().entry(HOST).or_insert_with(|| {
|
||||||
.headers_mut()
|
let hostname = uri.host().expect("authority implies host");
|
||||||
.entry(HOST)
|
if let Some(port) = uri.port() {
|
||||||
.or_insert_with(|| {
|
let s = format!("{}:{}", hostname, port);
|
||||||
let hostname = uri.host().expect("authority implies host");
|
HeaderValue::from_str(&s)
|
||||||
if let Some(port) = uri.port() {
|
} else {
|
||||||
let s = format!("{}:{}", hostname, port);
|
HeaderValue::from_str(hostname)
|
||||||
HeaderValue::from_str(&s)
|
}
|
||||||
} else {
|
.expect("uri host is valid header value")
|
||||||
HeaderValue::from_str(hostname)
|
});
|
||||||
}.expect("uri host is valid header value")
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CONNECT always sends authority-form, so check it first...
|
// CONNECT always sends authority-form, so check it first...
|
||||||
@@ -306,10 +323,13 @@ where C: Connect + Clone + Send + Sync + 'static,
|
|||||||
};
|
};
|
||||||
} else if req.method() == &Method::CONNECT {
|
} else if req.method() == &Method::CONNECT {
|
||||||
debug!("client does not support CONNECT requests over HTTP2");
|
debug!("client does not support CONNECT requests over HTTP2");
|
||||||
return Either::Left(future::err(ClientError::Normal(crate::Error::new_user_unsupported_request_method())));
|
return Either::Left(future::err(ClientError::Normal(
|
||||||
|
crate::Error::new_user_unsupported_request_method(),
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
let fut = pooled.send_request_retryable(req)
|
let fut = pooled
|
||||||
|
.send_request_retryable(req)
|
||||||
.map_err(ClientError::map_with_reused(pooled.is_reused()));
|
.map_err(ClientError::map_with_reused(pooled.is_reused()));
|
||||||
|
|
||||||
// If the Connector included 'extra' info, add to Response...
|
// If the Connector included 'extra' info, add to Response...
|
||||||
@@ -332,51 +352,46 @@ where C: Connect + Clone + Send + Sync + 'static,
|
|||||||
return Either::Right(Either::Left(fut));
|
return Either::Right(Either::Left(fut));
|
||||||
}
|
}
|
||||||
|
|
||||||
Either::Right(Either::Right(fut
|
Either::Right(Either::Right(fut.map_ok(move |mut res| {
|
||||||
.map_ok(move |mut res| {
|
// If pooled is HTTP/2, we can toss this reference immediately.
|
||||||
// If pooled is HTTP/2, we can toss this reference immediately.
|
//
|
||||||
//
|
// when pooled is dropped, it will try to insert back into the
|
||||||
// when pooled is dropped, it will try to insert back into the
|
// pool. To delay that, spawn a future that completes once the
|
||||||
// pool. To delay that, spawn a future that completes once the
|
// sender is ready again.
|
||||||
// sender is ready again.
|
//
|
||||||
//
|
// This *should* only be once the related `Connection` has polled
|
||||||
// This *should* only be once the related `Connection` has polled
|
// for a new request to start.
|
||||||
// for a new request to start.
|
//
|
||||||
//
|
// It won't be ready if there is a body to stream.
|
||||||
// It won't be ready if there is a body to stream.
|
if pooled.is_http2() || !pooled.is_pool_enabled() || pooled.is_ready() {
|
||||||
if pooled.is_http2() || !pooled.is_pool_enabled() || pooled.is_ready() {
|
drop(pooled);
|
||||||
drop(pooled);
|
} else if !res.body().is_end_stream() {
|
||||||
} else if !res.body().is_end_stream() {
|
let (delayed_tx, delayed_rx) = oneshot::channel();
|
||||||
let (delayed_tx, delayed_rx) = oneshot::channel();
|
res.body_mut().delayed_eof(delayed_rx);
|
||||||
res.body_mut().delayed_eof(delayed_rx);
|
let on_idle = future::poll_fn(move |cx| pooled.poll_ready(cx)).map(move |_| {
|
||||||
let on_idle = future::poll_fn(move |cx| {
|
// At this point, `pooled` is dropped, and had a chance
|
||||||
pooled.poll_ready(cx)
|
// to insert into the pool (if conn was idle)
|
||||||
})
|
drop(delayed_tx);
|
||||||
.map(move |_| {
|
});
|
||||||
// At this point, `pooled` is dropped, and had a chance
|
|
||||||
// to insert into the pool (if conn was idle)
|
|
||||||
drop(delayed_tx);
|
|
||||||
});
|
|
||||||
|
|
||||||
executor.execute(on_idle);
|
executor.execute(on_idle);
|
||||||
} else {
|
} else {
|
||||||
// There's no body to delay, but the connection isn't
|
// There's no body to delay, but the connection isn't
|
||||||
// ready yet. Only re-insert when it's ready
|
// ready yet. Only re-insert when it's ready
|
||||||
let on_idle = future::poll_fn(move |cx| {
|
let on_idle = future::poll_fn(move |cx| pooled.poll_ready(cx)).map(|_| ());
|
||||||
pooled.poll_ready(cx)
|
|
||||||
})
|
|
||||||
.map(|_| ());
|
|
||||||
|
|
||||||
executor.execute(on_idle);
|
executor.execute(on_idle);
|
||||||
}
|
}
|
||||||
res
|
res
|
||||||
})))
|
})))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn connection_for(&self, uri: Uri, pool_key: PoolKey)
|
fn connection_for(
|
||||||
-> impl Future<Output=Result<Pooled<PoolClient<B>>, ClientError<B>>>
|
&self,
|
||||||
{
|
uri: Uri,
|
||||||
|
pool_key: PoolKey,
|
||||||
|
) -> impl Future<Output = Result<Pooled<PoolClient<B>>, ClientError<B>>> {
|
||||||
// This actually races 2 different futures to try to get a ready
|
// This actually races 2 different futures to try to get a ready
|
||||||
// connection the fastest, and to reduce connection churn.
|
// connection the fastest, and to reduce connection churn.
|
||||||
//
|
//
|
||||||
@@ -395,67 +410,66 @@ where C: Connect + Clone + Send + Sync + 'static,
|
|||||||
|
|
||||||
let executor = self.conn_builder.exec.clone();
|
let executor = self.conn_builder.exec.clone();
|
||||||
// The order of the `select` is depended on below...
|
// The order of the `select` is depended on below...
|
||||||
future::select(checkout, connect)
|
future::select(checkout, connect).then(move |either| match either {
|
||||||
.then(move |either| match either {
|
// Checkout won, connect future may have been started or not.
|
||||||
// Checkout won, connect future may have been started or not.
|
//
|
||||||
|
// If it has, let it finish and insert back into the pool,
|
||||||
|
// so as to not waste the socket...
|
||||||
|
Either::Left((Ok(checked_out), connecting)) => {
|
||||||
|
// This depends on the `select` above having the correct
|
||||||
|
// order, such that if the checkout future were ready
|
||||||
|
// immediately, the connect future will never have been
|
||||||
|
// started.
|
||||||
//
|
//
|
||||||
// If it has, let it finish and insert back into the pool,
|
// If it *wasn't* ready yet, then the connect future will
|
||||||
// so as to not waste the socket...
|
// have been started...
|
||||||
Either::Left((Ok(checked_out), connecting)) => {
|
if connecting.started() {
|
||||||
// This depends on the `select` above having the correct
|
let bg = connecting
|
||||||
// order, such that if the checkout future were ready
|
.map_err(|err| {
|
||||||
// immediately, the connect future will never have been
|
trace!("background connect error: {}", err);
|
||||||
// started.
|
})
|
||||||
//
|
.map(|_pooled| {
|
||||||
// If it *wasn't* ready yet, then the connect future will
|
// dropping here should just place it in
|
||||||
// have been started...
|
// the Pool for us...
|
||||||
if connecting.started() {
|
});
|
||||||
let bg = connecting
|
// An execute error here isn't important, we're just trying
|
||||||
.map_err(|err| {
|
// to prevent a waste of a socket...
|
||||||
trace!("background connect error: {}", err);
|
let _ = executor.execute(bg);
|
||||||
})
|
}
|
||||||
.map(|_pooled| {
|
Either::Left(future::ok(checked_out))
|
||||||
// dropping here should just place it in
|
}
|
||||||
// the Pool for us...
|
// Connect won, checkout can just be dropped.
|
||||||
});
|
Either::Right((Ok(connected), _checkout)) => Either::Left(future::ok(connected)),
|
||||||
// An execute error here isn't important, we're just trying
|
// Either checkout or connect could get canceled:
|
||||||
// to prevent a waste of a socket...
|
//
|
||||||
let _ = executor.execute(bg);
|
// 1. Connect is canceled if this is HTTP/2 and there is
|
||||||
}
|
// an outstanding HTTP/2 connecting task.
|
||||||
Either::Left(future::ok(checked_out))
|
// 2. Checkout is canceled if the pool cannot deliver an
|
||||||
},
|
// idle connection reliably.
|
||||||
// Connect won, checkout can just be dropped.
|
//
|
||||||
Either::Right((Ok(connected), _checkout)) => {
|
// In both cases, we should just wait for the other future.
|
||||||
Either::Left(future::ok(connected))
|
Either::Left((Err(err), connecting)) => Either::Right(Either::Left({
|
||||||
},
|
if err.is_canceled() {
|
||||||
// Either checkout or connect could get canceled:
|
Either::Left(connecting.map_err(ClientError::Normal))
|
||||||
//
|
} else {
|
||||||
// 1. Connect is canceled if this is HTTP/2 and there is
|
Either::Right(future::err(ClientError::Normal(err)))
|
||||||
// an outstanding HTTP/2 connecting task.
|
}
|
||||||
// 2. Checkout is canceled if the pool cannot deliver an
|
})),
|
||||||
// idle connection reliably.
|
Either::Right((Err(err), checkout)) => Either::Right(Either::Right({
|
||||||
//
|
if err.is_canceled() {
|
||||||
// In both cases, we should just wait for the other future.
|
Either::Left(checkout.map_err(ClientError::Normal))
|
||||||
Either::Left((Err(err), connecting)) => Either::Right(Either::Left({
|
} else {
|
||||||
if err.is_canceled() {
|
Either::Right(future::err(ClientError::Normal(err)))
|
||||||
Either::Left(connecting.map_err(ClientError::Normal))
|
}
|
||||||
} else {
|
})),
|
||||||
Either::Right(future::err(ClientError::Normal(err)))
|
})
|
||||||
}
|
|
||||||
})),
|
|
||||||
Either::Right((Err(err), checkout)) => Either::Right(Either::Right({
|
|
||||||
if err.is_canceled() {
|
|
||||||
Either::Left(checkout.map_err(ClientError::Normal))
|
|
||||||
} else {
|
|
||||||
Either::Right(future::err(ClientError::Normal(err)))
|
|
||||||
}
|
|
||||||
})),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn connect_to(&self, uri: Uri, pool_key: PoolKey)
|
fn connect_to(
|
||||||
-> impl Lazy<Output=crate::Result<Pooled<PoolClient<B>>>> + Unpin
|
&self,
|
||||||
{
|
uri: Uri,
|
||||||
|
pool_key: PoolKey,
|
||||||
|
) -> impl Lazy<Output = crate::Result<Pooled<PoolClient<B>>>> + Unpin {
|
||||||
let executor = self.conn_builder.exec.clone();
|
let executor = self.conn_builder.exec.clone();
|
||||||
let pool = self.pool.clone();
|
let pool = self.pool.clone();
|
||||||
let mut conn_builder = self.conn_builder.clone();
|
let mut conn_builder = self.conn_builder.clone();
|
||||||
@@ -472,68 +486,82 @@ where C: Connect + Clone + Send + Sync + 'static,
|
|||||||
let connecting = match pool.connecting(&pool_key, ver) {
|
let connecting = match pool.connecting(&pool_key, ver) {
|
||||||
Some(lock) => lock,
|
Some(lock) => lock,
|
||||||
None => {
|
None => {
|
||||||
let canceled = crate::Error::new_canceled().with("HTTP/2 connection in progress");
|
let canceled =
|
||||||
|
crate::Error::new_canceled().with("HTTP/2 connection in progress");
|
||||||
return Either::Right(future::err(canceled));
|
return Either::Right(future::err(canceled));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Either::Left(connector.connect(connect::sealed::Internal, dst)
|
Either::Left(
|
||||||
.map_err(crate::Error::new_connect)
|
connector
|
||||||
.and_then(move |io| {
|
.connect(connect::sealed::Internal, dst)
|
||||||
let connected = io.connected();
|
.map_err(crate::Error::new_connect)
|
||||||
// If ALPN is h2 and we aren't http2_only already,
|
.and_then(move |io| {
|
||||||
// then we need to convert our pool checkout into
|
let connected = io.connected();
|
||||||
// a single HTTP2 one.
|
// If ALPN is h2 and we aren't http2_only already,
|
||||||
let connecting = if connected.alpn == Alpn::H2 && !is_ver_h2 {
|
// then we need to convert our pool checkout into
|
||||||
match connecting.alpn_h2(&pool) {
|
// a single HTTP2 one.
|
||||||
Some(lock) => {
|
let connecting = if connected.alpn == Alpn::H2 && !is_ver_h2 {
|
||||||
trace!("ALPN negotiated h2, updating pool");
|
match connecting.alpn_h2(&pool) {
|
||||||
lock
|
Some(lock) => {
|
||||||
},
|
trace!("ALPN negotiated h2, updating pool");
|
||||||
None => {
|
lock
|
||||||
// Another connection has already upgraded,
|
}
|
||||||
// the pool checkout should finish up for us.
|
None => {
|
||||||
let canceled = crate::Error::new_canceled().with("ALPN upgraded to HTTP/2");
|
// Another connection has already upgraded,
|
||||||
return Either::Right(future::err(canceled));
|
// the pool checkout should finish up for us.
|
||||||
|
let canceled = crate::Error::new_canceled()
|
||||||
|
.with("ALPN upgraded to HTTP/2");
|
||||||
|
return Either::Right(future::err(canceled));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
} else {
|
connecting
|
||||||
connecting
|
};
|
||||||
};
|
let is_h2 = is_ver_h2 || connected.alpn == Alpn::H2;
|
||||||
let is_h2 = is_ver_h2 || connected.alpn == Alpn::H2;
|
Either::Left(Box::pin(
|
||||||
Either::Left(Box::pin(conn_builder
|
conn_builder
|
||||||
.http2_only(is_h2)
|
.http2_only(is_h2)
|
||||||
.handshake(io)
|
.handshake(io)
|
||||||
.and_then(move |(tx, conn)| {
|
.and_then(move |(tx, conn)| {
|
||||||
trace!("handshake complete, spawning background dispatcher task");
|
trace!(
|
||||||
executor.execute(conn.map_err(|e| {
|
"handshake complete, spawning background dispatcher task"
|
||||||
debug!("client connection error: {}", e)
|
);
|
||||||
}).map(|_| ()));
|
executor.execute(
|
||||||
|
conn.map_err(|e| debug!("client connection error: {}", e))
|
||||||
|
.map(|_| ()),
|
||||||
|
);
|
||||||
|
|
||||||
// Wait for 'conn' to ready up before we
|
// Wait for 'conn' to ready up before we
|
||||||
// declare this tx as usable
|
// declare this tx as usable
|
||||||
tx.when_ready()
|
tx.when_ready()
|
||||||
})
|
})
|
||||||
.map_ok(move |tx| {
|
.map_ok(move |tx| {
|
||||||
pool.pooled(connecting, PoolClient {
|
pool.pooled(
|
||||||
conn_info: connected,
|
connecting,
|
||||||
tx: if is_h2 {
|
PoolClient {
|
||||||
PoolTx::Http2(tx.into_http2())
|
conn_info: connected,
|
||||||
} else {
|
tx: if is_h2 {
|
||||||
PoolTx::Http1(tx)
|
PoolTx::Http2(tx.into_http2())
|
||||||
},
|
} else {
|
||||||
})
|
PoolTx::Http1(tx)
|
||||||
})))
|
},
|
||||||
}))
|
},
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
))
|
||||||
|
}),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C, B> tower_service::Service<Request<B>> for Client<C, B>
|
impl<C, B> tower_service::Service<Request<B>> for Client<C, B>
|
||||||
where C: Connect + Clone + Send + Sync + 'static,
|
where
|
||||||
C::Transport: Unpin + Send + 'static,
|
C: Connect + Clone + Send + Sync + 'static,
|
||||||
C::Future: Unpin + Send + 'static,
|
C::Transport: Unpin + Send + 'static,
|
||||||
B: Payload + Unpin + Send + 'static,
|
C::Future: Unpin + Send + 'static,
|
||||||
B::Data: Send + Unpin,
|
B: Payload + Unpin + Send + 'static,
|
||||||
|
B::Data: Send + Unpin,
|
||||||
{
|
{
|
||||||
type Response = Response<Body>;
|
type Response = Response<Body>;
|
||||||
type Error = crate::Error;
|
type Error = crate::Error;
|
||||||
@@ -561,23 +589,22 @@ impl<C: Clone, B> Clone for Client<C, B> {
|
|||||||
|
|
||||||
impl<C, B> fmt::Debug for Client<C, B> {
|
impl<C, B> fmt::Debug for Client<C, B> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.debug_struct("Client")
|
f.debug_struct("Client").finish()
|
||||||
.finish()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== impl ResponseFuture =====
|
// ===== impl ResponseFuture =====
|
||||||
|
|
||||||
impl ResponseFuture {
|
impl ResponseFuture {
|
||||||
fn new(fut: Box<dyn Future<Output=crate::Result<Response<Body>>> + Send>) -> Self {
|
fn new(fut: Box<dyn Future<Output = crate::Result<Response<Body>>> + Send>) -> Self {
|
||||||
Self {
|
Self { inner: fut.into() }
|
||||||
inner: fut.into(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn error_version(ver: Version) -> Self {
|
fn error_version(ver: Version) -> Self {
|
||||||
warn!("Request has unsupported version \"{:?}\"", ver);
|
warn!("Request has unsupported version \"{:?}\"", ver);
|
||||||
ResponseFuture::new(Box::new(future::err(crate::Error::new_user_unsupported_version())))
|
ResponseFuture::new(Box::new(future::err(
|
||||||
|
crate::Error::new_user_unsupported_version(),
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -644,7 +671,10 @@ impl<B> PoolClient<B> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<B: Payload + 'static> PoolClient<B> {
|
impl<B: Payload + 'static> PoolClient<B> {
|
||||||
fn send_request_retryable(&mut self, req: Request<B>) -> impl Future<Output = Result<Response<Body>, (crate::Error, Option<Request<B>>)>>
|
fn send_request_retryable(
|
||||||
|
&mut self,
|
||||||
|
req: Request<B>,
|
||||||
|
) -> impl Future<Output = Result<Response<Body>, (crate::Error, Option<Request<B>>)>>
|
||||||
where
|
where
|
||||||
B: Send,
|
B: Send,
|
||||||
{
|
{
|
||||||
@@ -668,12 +698,10 @@ where
|
|||||||
|
|
||||||
fn reserve(self) -> Reservation<Self> {
|
fn reserve(self) -> Reservation<Self> {
|
||||||
match self.tx {
|
match self.tx {
|
||||||
PoolTx::Http1(tx) => {
|
PoolTx::Http1(tx) => Reservation::Unique(PoolClient {
|
||||||
Reservation::Unique(PoolClient {
|
conn_info: self.conn_info,
|
||||||
conn_info: self.conn_info,
|
tx: PoolTx::Http1(tx),
|
||||||
tx: PoolTx::Http1(tx),
|
}),
|
||||||
})
|
|
||||||
},
|
|
||||||
PoolTx::Http2(tx) => {
|
PoolTx::Http2(tx) => {
|
||||||
let b = PoolClient {
|
let b = PoolClient {
|
||||||
conn_info: self.conn_info.clone(),
|
conn_info: self.conn_info.clone(),
|
||||||
@@ -703,13 +731,11 @@ enum ClientError<B> {
|
|||||||
connection_reused: bool,
|
connection_reused: bool,
|
||||||
req: Request<B>,
|
req: Request<B>,
|
||||||
reason: crate::Error,
|
reason: crate::Error,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<B> ClientError<B> {
|
impl<B> ClientError<B> {
|
||||||
fn map_with_reused(conn_reused: bool)
|
fn map_with_reused(conn_reused: bool) -> impl Fn((crate::Error, Option<Request<B>>)) -> Self {
|
||||||
-> impl Fn((crate::Error, Option<Request<B>>)) -> Self
|
|
||||||
{
|
|
||||||
move |(err, orig_req)| {
|
move |(err, orig_req)| {
|
||||||
if let Some(req) = orig_req {
|
if let Some(req) = orig_req {
|
||||||
ClientError::Canceled {
|
ClientError::Canceled {
|
||||||
@@ -737,7 +763,7 @@ fn origin_form(uri: &mut Uri) {
|
|||||||
let mut parts = ::http::uri::Parts::default();
|
let mut parts = ::http::uri::Parts::default();
|
||||||
parts.path_and_query = Some(path.clone());
|
parts.path_and_query = Some(path.clone());
|
||||||
Uri::from_parts(parts).expect("path is valid uri")
|
Uri::from_parts(parts).expect("path is valid uri")
|
||||||
},
|
}
|
||||||
_none_or_just_slash => {
|
_none_or_just_slash => {
|
||||||
debug_assert!(Uri::default() == "/");
|
debug_assert!(Uri::default() == "/");
|
||||||
Uri::default()
|
Uri::default()
|
||||||
@@ -748,7 +774,10 @@ fn origin_form(uri: &mut Uri) {
|
|||||||
|
|
||||||
fn absolute_form(uri: &mut Uri) {
|
fn absolute_form(uri: &mut Uri) {
|
||||||
debug_assert!(uri.scheme().is_some(), "absolute_form needs a scheme");
|
debug_assert!(uri.scheme().is_some(), "absolute_form needs a scheme");
|
||||||
debug_assert!(uri.authority().is_some(), "absolute_form needs an authority");
|
debug_assert!(
|
||||||
|
uri.authority().is_some(),
|
||||||
|
"absolute_form needs an authority"
|
||||||
|
);
|
||||||
// If the URI is to HTTPS, and the connector claimed to be a proxy,
|
// If the URI is to HTTPS, and the connector claimed to be a proxy,
|
||||||
// then it *should* have tunneled, and so we don't want to send
|
// then it *should* have tunneled, and so we don't want to send
|
||||||
// absolute-form in that case.
|
// absolute-form in that case.
|
||||||
@@ -763,10 +792,7 @@ fn authority_form(uri: &mut Uri) {
|
|||||||
// `https://hyper.rs` would parse with `/` path, don't
|
// `https://hyper.rs` would parse with `/` path, don't
|
||||||
// annoy people about that...
|
// annoy people about that...
|
||||||
if path != "/" {
|
if path != "/" {
|
||||||
warn!(
|
warn!("HTTP/1.1 CONNECT request stripping path: {:?}", path);
|
||||||
"HTTP/1.1 CONNECT request stripping path: {:?}",
|
|
||||||
path
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -775,7 +801,7 @@ fn authority_form(uri: &mut Uri) {
|
|||||||
let mut parts = ::http::uri::Parts::default();
|
let mut parts = ::http::uri::Parts::default();
|
||||||
parts.authority = Some(auth.clone());
|
parts.authority = Some(auth.clone());
|
||||||
Uri::from_parts(parts).expect("authority is valid")
|
Uri::from_parts(parts).expect("authority is valid")
|
||||||
},
|
}
|
||||||
None => {
|
None => {
|
||||||
unreachable!("authority_form with relative uri");
|
unreachable!("authority_form with relative uri");
|
||||||
}
|
}
|
||||||
@@ -785,9 +811,7 @@ fn authority_form(uri: &mut Uri) {
|
|||||||
fn extract_domain(uri: &mut Uri, is_http_connect: bool) -> crate::Result<String> {
|
fn extract_domain(uri: &mut Uri, is_http_connect: bool) -> crate::Result<String> {
|
||||||
let uri_clone = uri.clone();
|
let uri_clone = uri.clone();
|
||||||
match (uri_clone.scheme(), uri_clone.authority()) {
|
match (uri_clone.scheme(), uri_clone.authority()) {
|
||||||
(Some(scheme), Some(auth)) => {
|
(Some(scheme), Some(auth)) => Ok(format!("{}://{}", scheme, auth)),
|
||||||
Ok(format!("{}://{}", scheme, auth))
|
|
||||||
}
|
|
||||||
(None, Some(auth)) if is_http_connect => {
|
(None, Some(auth)) if is_http_connect => {
|
||||||
let scheme = match auth.port_u16() {
|
let scheme = match auth.port_u16() {
|
||||||
Some(443) => {
|
Some(443) => {
|
||||||
@@ -797,10 +821,10 @@ fn extract_domain(uri: &mut Uri, is_http_connect: bool) -> crate::Result<String>
|
|||||||
_ => {
|
_ => {
|
||||||
set_scheme(uri, Scheme::HTTP);
|
set_scheme(uri, Scheme::HTTP);
|
||||||
"http"
|
"http"
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
Ok(format!("{}://{}", scheme, auth))
|
Ok(format!("{}://{}", scheme, auth))
|
||||||
},
|
}
|
||||||
_ => {
|
_ => {
|
||||||
debug!("Client requires absolute-form URIs, received: {:?}", uri);
|
debug!("Client requires absolute-form URIs, received: {:?}", uri);
|
||||||
Err(crate::Error::new_user_absolute_uri_required())
|
Err(crate::Error::new_user_absolute_uri_required())
|
||||||
@@ -809,7 +833,10 @@ fn extract_domain(uri: &mut Uri, is_http_connect: bool) -> crate::Result<String>
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn set_scheme(uri: &mut Uri, scheme: Scheme) {
|
fn set_scheme(uri: &mut Uri, scheme: Scheme) {
|
||||||
debug_assert!(uri.scheme().is_none(), "set_scheme expects no existing scheme");
|
debug_assert!(
|
||||||
|
uri.scheme().is_none(),
|
||||||
|
"set_scheme expects no existing scheme"
|
||||||
|
);
|
||||||
let old = mem::replace(uri, Uri::default());
|
let old = mem::replace(uri, Uri::default());
|
||||||
let mut parts: ::http::uri::Parts = old.into();
|
let mut parts: ::http::uri::Parts = old.into();
|
||||||
parts.scheme = Some(scheme);
|
parts.scheme = Some(scheme);
|
||||||
@@ -946,11 +973,7 @@ impl Builder {
|
|||||||
///
|
///
|
||||||
/// Default is false.
|
/// Default is false.
|
||||||
pub fn http2_only(&mut self, val: bool) -> &mut Self {
|
pub fn http2_only(&mut self, val: bool) -> &mut Self {
|
||||||
self.client_config.ver = if val {
|
self.client_config.ver = if val { Ver::Http2 } else { Ver::Auto };
|
||||||
Ver::Http2
|
|
||||||
} else {
|
|
||||||
Ver::Auto
|
|
||||||
};
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -963,7 +986,8 @@ impl Builder {
|
|||||||
///
|
///
|
||||||
/// [spec]: https://http2.github.io/http2-spec/#SETTINGS_INITIAL_WINDOW_SIZE
|
/// [spec]: https://http2.github.io/http2-spec/#SETTINGS_INITIAL_WINDOW_SIZE
|
||||||
pub fn http2_initial_stream_window_size(&mut self, sz: impl Into<Option<u32>>) -> &mut Self {
|
pub fn http2_initial_stream_window_size(&mut self, sz: impl Into<Option<u32>>) -> &mut Self {
|
||||||
self.conn_builder.http2_initial_stream_window_size(sz.into());
|
self.conn_builder
|
||||||
|
.http2_initial_stream_window_size(sz.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -972,8 +996,12 @@ impl Builder {
|
|||||||
/// Passing `None` will do nothing.
|
/// Passing `None` will do nothing.
|
||||||
///
|
///
|
||||||
/// If not set, hyper will use a default.
|
/// If not set, hyper will use a default.
|
||||||
pub fn http2_initial_connection_window_size(&mut self, sz: impl Into<Option<u32>>) -> &mut Self {
|
pub fn http2_initial_connection_window_size(
|
||||||
self.conn_builder.http2_initial_connection_window_size(sz.into());
|
&mut self,
|
||||||
|
sz: impl Into<Option<u32>>,
|
||||||
|
) -> &mut Self {
|
||||||
|
self.conn_builder
|
||||||
|
.http2_initial_connection_window_size(sz.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ use futures_channel::oneshot;
|
|||||||
#[cfg(feature = "runtime")]
|
#[cfg(feature = "runtime")]
|
||||||
use tokio::time::{Duration, Instant, Interval};
|
use tokio::time::{Duration, Instant, Interval};
|
||||||
|
|
||||||
use crate::common::{Exec, Future, Pin, Poll, Unpin, task};
|
|
||||||
use super::Ver;
|
use super::Ver;
|
||||||
|
use crate::common::{task, Exec, Future, Pin, Poll, Unpin};
|
||||||
|
|
||||||
// FIXME: allow() required due to `impl Trait` leaking types to this lint
|
// FIXME: allow() required due to `impl Trait` leaking types to this lint
|
||||||
#[allow(missing_debug_implementations)]
|
#[allow(missing_debug_implementations)]
|
||||||
@@ -96,7 +96,7 @@ pub(super) struct Config {
|
|||||||
impl<T> Pool<T> {
|
impl<T> Pool<T> {
|
||||||
pub fn new(config: Config, __exec: &Exec) -> Pool<T> {
|
pub fn new(config: Config, __exec: &Exec) -> Pool<T> {
|
||||||
let inner = if config.enabled {
|
let inner = if config.enabled {
|
||||||
Some(Arc::new(Mutex::new(PoolInner {
|
Some(Arc::new(Mutex::new(PoolInner {
|
||||||
connecting: HashSet::new(),
|
connecting: HashSet::new(),
|
||||||
idle: HashMap::new(),
|
idle: HashMap::new(),
|
||||||
#[cfg(feature = "runtime")]
|
#[cfg(feature = "runtime")]
|
||||||
@@ -106,14 +106,12 @@ impl<T> Pool<T> {
|
|||||||
#[cfg(feature = "runtime")]
|
#[cfg(feature = "runtime")]
|
||||||
exec: __exec.clone(),
|
exec: __exec.clone(),
|
||||||
timeout: config.keep_alive_timeout,
|
timeout: config.keep_alive_timeout,
|
||||||
})))
|
})))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
Pool {
|
Pool { inner }
|
||||||
inner,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_enabled(&self) -> bool {
|
fn is_enabled(&self) -> bool {
|
||||||
@@ -174,12 +172,7 @@ impl<T: Poolable> Pool<T> {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
fn locked(&self) -> ::std::sync::MutexGuard<'_, PoolInner<T>> {
|
fn locked(&self) -> ::std::sync::MutexGuard<'_, PoolInner<T>> {
|
||||||
self
|
self.inner.as_ref().expect("enabled").lock().expect("lock")
|
||||||
.inner
|
|
||||||
.as_ref()
|
|
||||||
.expect("enabled")
|
|
||||||
.lock()
|
|
||||||
.expect("lock")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Used in client/tests.rs...
|
/* Used in client/tests.rs...
|
||||||
@@ -216,13 +209,13 @@ impl<T: Poolable> Pool<T> {
|
|||||||
// Shared reservations don't need a reference to the pool,
|
// Shared reservations don't need a reference to the pool,
|
||||||
// since the pool always keeps a copy.
|
// since the pool always keeps a copy.
|
||||||
(to_return, WeakOpt::none())
|
(to_return, WeakOpt::none())
|
||||||
},
|
}
|
||||||
Reservation::Unique(value) => {
|
Reservation::Unique(value) => {
|
||||||
// Unique reservations must take a reference to the pool
|
// Unique reservations must take a reference to the pool
|
||||||
// since they hope to reinsert once the reservation is
|
// since they hope to reinsert once the reservation is
|
||||||
// completed
|
// completed
|
||||||
(value, WeakOpt::downgrade(enabled))
|
(value, WeakOpt::downgrade(enabled))
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// If pool is not enabled, skip all the things...
|
// If pool is not enabled, skip all the things...
|
||||||
@@ -236,7 +229,7 @@ impl<T: Poolable> Pool<T> {
|
|||||||
key: connecting.key.clone(),
|
key: connecting.key.clone(),
|
||||||
is_reused: false,
|
is_reused: false,
|
||||||
pool: pool_ref,
|
pool: pool_ref,
|
||||||
value: Some(value)
|
value: Some(value),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -299,10 +292,8 @@ impl<'a, T: Poolable + 'a> IdlePopper<'a, T> {
|
|||||||
value: to_reinsert,
|
value: to_reinsert,
|
||||||
});
|
});
|
||||||
to_checkout
|
to_checkout
|
||||||
},
|
|
||||||
Reservation::Unique(unique) => {
|
|
||||||
unique
|
|
||||||
}
|
}
|
||||||
|
Reservation::Unique(unique) => unique,
|
||||||
};
|
};
|
||||||
|
|
||||||
return Some(Idle {
|
return Some(Idle {
|
||||||
@@ -332,7 +323,7 @@ impl<T: Poolable> PoolInner<T> {
|
|||||||
Reservation::Shared(to_keep, to_send) => {
|
Reservation::Shared(to_keep, to_send) => {
|
||||||
value = Some(to_keep);
|
value = Some(to_keep);
|
||||||
to_send
|
to_send
|
||||||
},
|
}
|
||||||
Reservation::Unique(uniq) => uniq,
|
Reservation::Unique(uniq) => uniq,
|
||||||
};
|
};
|
||||||
match tx.send(reserved) {
|
match tx.send(reserved) {
|
||||||
@@ -342,7 +333,7 @@ impl<T: Poolable> PoolInner<T> {
|
|||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
value = Some(e);
|
value = Some(e);
|
||||||
}
|
}
|
||||||
@@ -361,10 +352,7 @@ impl<T: Poolable> PoolInner<T> {
|
|||||||
Some(value) => {
|
Some(value) => {
|
||||||
// borrow-check scope...
|
// borrow-check scope...
|
||||||
{
|
{
|
||||||
let idle_list = self
|
let idle_list = self.idle.entry(key.clone()).or_insert(Vec::new());
|
||||||
.idle
|
|
||||||
.entry(key.clone())
|
|
||||||
.or_insert(Vec::new());
|
|
||||||
if self.max_idle_per_host <= idle_list.len() {
|
if self.max_idle_per_host <= idle_list.len() {
|
||||||
trace!("max idle per host for {:?}, dropping connection", key);
|
trace!("max idle per host for {:?}, dropping connection", key);
|
||||||
return;
|
return;
|
||||||
@@ -390,10 +378,7 @@ impl<T: Poolable> PoolInner<T> {
|
|||||||
/// but the lock is going away, so clean up.
|
/// but the lock is going away, so clean up.
|
||||||
fn connected(&mut self, key: &Key) {
|
fn connected(&mut self, key: &Key) {
|
||||||
let existed = self.connecting.remove(key);
|
let existed = self.connecting.remove(key);
|
||||||
debug_assert!(
|
debug_assert!(existed, "Connecting dropped, key not in pool.connecting");
|
||||||
existed,
|
|
||||||
"Connecting dropped, key not in pool.connecting"
|
|
||||||
);
|
|
||||||
// cancel any waiters. if there are any, it's because
|
// cancel any waiters. if there are any, it's because
|
||||||
// this Connecting task didn't complete successfully.
|
// this Connecting task didn't complete successfully.
|
||||||
// those waiters would never receive a connection.
|
// those waiters would never receive a connection.
|
||||||
@@ -412,7 +397,7 @@ impl<T: Poolable> PoolInner<T> {
|
|||||||
self.idle_interval_ref = Some(tx);
|
self.idle_interval_ref = Some(tx);
|
||||||
(dur, rx)
|
(dur, rx)
|
||||||
} else {
|
} else {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -434,9 +419,7 @@ impl<T> PoolInner<T> {
|
|||||||
fn clean_waiters(&mut self, key: &Key) {
|
fn clean_waiters(&mut self, key: &Key) {
|
||||||
let mut remove_waiters = false;
|
let mut remove_waiters = false;
|
||||||
if let Some(waiters) = self.waiters.get_mut(key) {
|
if let Some(waiters) = self.waiters.get_mut(key) {
|
||||||
waiters.retain(|tx| {
|
waiters.retain(|tx| !tx.is_canceled());
|
||||||
!tx.is_canceled()
|
|
||||||
});
|
|
||||||
remove_waiters = waiters.is_empty();
|
remove_waiters = waiters.is_empty();
|
||||||
}
|
}
|
||||||
if remove_waiters {
|
if remove_waiters {
|
||||||
@@ -547,9 +530,7 @@ impl<T: Poolable> Drop for Pooled<T> {
|
|||||||
|
|
||||||
impl<T: Poolable> fmt::Debug for Pooled<T> {
|
impl<T: Poolable> fmt::Debug for Pooled<T> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.debug_struct("Pooled")
|
f.debug_struct("Pooled").field("key", &self.key).finish()
|
||||||
.field("key", &self.key)
|
|
||||||
.finish()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -567,7 +548,10 @@ pub(super) struct Checkout<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Poolable> Checkout<T> {
|
impl<T: Poolable> Checkout<T> {
|
||||||
fn poll_waiter(&mut self, cx: &mut task::Context<'_>) -> Poll<Option<crate::Result<Pooled<T>>>> {
|
fn poll_waiter(
|
||||||
|
&mut self,
|
||||||
|
cx: &mut task::Context<'_>,
|
||||||
|
) -> Poll<Option<crate::Result<Pooled<T>>>> {
|
||||||
static CANCELED: &str = "pool checkout failed";
|
static CANCELED: &str = "pool checkout failed";
|
||||||
if let Some(mut rx) = self.waiter.take() {
|
if let Some(mut rx) = self.waiter.take() {
|
||||||
match Pin::new(&mut rx).poll(cx) {
|
match Pin::new(&mut rx).poll(cx) {
|
||||||
@@ -577,12 +561,14 @@ impl<T: Poolable> Checkout<T> {
|
|||||||
} else {
|
} else {
|
||||||
Poll::Ready(Some(Err(crate::Error::new_canceled().with(CANCELED))))
|
Poll::Ready(Some(Err(crate::Error::new_canceled().with(CANCELED))))
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
Poll::Pending => {
|
Poll::Pending => {
|
||||||
self.waiter = Some(rx);
|
self.waiter = Some(rx);
|
||||||
Poll::Pending
|
Poll::Pending
|
||||||
},
|
}
|
||||||
Poll::Ready(Err(_canceled)) => Poll::Ready(Some(Err(crate::Error::new_canceled().with(CANCELED)))),
|
Poll::Ready(Err(_canceled)) => {
|
||||||
|
Poll::Ready(Some(Err(crate::Error::new_canceled().with(CANCELED))))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Poll::Ready(None)
|
Poll::Ready(None)
|
||||||
@@ -593,20 +579,19 @@ impl<T: Poolable> Checkout<T> {
|
|||||||
let entry = {
|
let entry = {
|
||||||
let mut inner = self.pool.inner.as_ref()?.lock().unwrap();
|
let mut inner = self.pool.inner.as_ref()?.lock().unwrap();
|
||||||
let expiration = Expiration::new(inner.timeout);
|
let expiration = Expiration::new(inner.timeout);
|
||||||
let maybe_entry = inner.idle.get_mut(&self.key)
|
let maybe_entry = inner.idle.get_mut(&self.key).and_then(|list| {
|
||||||
.and_then(|list| {
|
trace!("take? {:?}: expiration = {:?}", self.key, expiration.0);
|
||||||
trace!("take? {:?}: expiration = {:?}", self.key, expiration.0);
|
// A block to end the mutable borrow on list,
|
||||||
// A block to end the mutable borrow on list,
|
// so the map below can check is_empty()
|
||||||
// so the map below can check is_empty()
|
{
|
||||||
{
|
let popper = IdlePopper {
|
||||||
let popper = IdlePopper {
|
key: &self.key,
|
||||||
key: &self.key,
|
list,
|
||||||
list,
|
};
|
||||||
};
|
popper.pop(&expiration)
|
||||||
popper.pop(&expiration)
|
}
|
||||||
}
|
.map(|e| (e, list.is_empty()))
|
||||||
.map(|e| (e, list.is_empty()))
|
});
|
||||||
});
|
|
||||||
|
|
||||||
let (entry, empty) = if let Some((e, empty)) = maybe_entry {
|
let (entry, empty) = if let Some((e, empty)) = maybe_entry {
|
||||||
(Some(e), empty)
|
(Some(e), empty)
|
||||||
@@ -764,9 +749,7 @@ impl<T> WeakOpt<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn upgrade(&self) -> Option<Arc<T>> {
|
fn upgrade(&self) -> Option<Arc<T>> {
|
||||||
self.0
|
self.0.as_ref().and_then(Weak::upgrade)
|
||||||
.as_ref()
|
|
||||||
.and_then(Weak::upgrade)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -776,8 +759,8 @@ mod tests {
|
|||||||
use std::task::Poll;
|
use std::task::Poll;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use crate::common::{Exec, Future, Pin, task};
|
use super::{Connecting, Key, Pool, Poolable, Reservation, WeakOpt};
|
||||||
use super::{Connecting, Key, Poolable, Pool, Reservation, WeakOpt};
|
use crate::common::{task, Exec, Future, Pin};
|
||||||
|
|
||||||
/// Test unique reservations.
|
/// Test unique reservations.
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
@@ -809,7 +792,8 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn pool_max_idle_no_timer<T>(max_idle: usize) -> Pool<T> {
|
fn pool_max_idle_no_timer<T>(max_idle: usize) -> Pool<T> {
|
||||||
let pool = Pool::new(super::Config {
|
let pool = Pool::new(
|
||||||
|
super::Config {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
keep_alive_timeout: Some(Duration::from_millis(100)),
|
keep_alive_timeout: Some(Duration::from_millis(100)),
|
||||||
max_idle_per_host: max_idle,
|
max_idle_per_host: max_idle,
|
||||||
@@ -838,7 +822,8 @@ mod tests {
|
|||||||
struct PollOnce<'a, F>(&'a mut F);
|
struct PollOnce<'a, F>(&'a mut F);
|
||||||
|
|
||||||
impl<F, T, U> Future for PollOnce<'_, F>
|
impl<F, T, U> Future for PollOnce<'_, F>
|
||||||
where F: Future<Output = Result<T, U>> + Unpin
|
where
|
||||||
|
F: Future<Output = Result<T, U>> + Unpin,
|
||||||
{
|
{
|
||||||
type Output = Option<()>;
|
type Output = Option<()>;
|
||||||
|
|
||||||
@@ -846,7 +831,7 @@ mod tests {
|
|||||||
match Pin::new(&mut self.0).poll(cx) {
|
match Pin::new(&mut self.0).poll(cx) {
|
||||||
Poll::Ready(Ok(_)) => Poll::Ready(Some(())),
|
Poll::Ready(Ok(_)) => Poll::Ready(Some(())),
|
||||||
Poll::Ready(Err(_)) => Poll::Ready(Some(())),
|
Poll::Ready(Err(_)) => Poll::Ready(Some(())),
|
||||||
Poll::Pending => Poll::Ready(None)
|
Poll::Pending => Poll::Ready(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -875,7 +860,10 @@ mod tests {
|
|||||||
pool.pooled(c(key.clone()), Uniq(5));
|
pool.pooled(c(key.clone()), Uniq(5));
|
||||||
pool.pooled(c(key.clone()), Uniq(99));
|
pool.pooled(c(key.clone()), Uniq(99));
|
||||||
|
|
||||||
assert_eq!(pool.locked().idle.get(&key).map(|entries| entries.len()), Some(3));
|
assert_eq!(
|
||||||
|
pool.locked().idle.get(&key).map(|entries| entries.len()),
|
||||||
|
Some(3)
|
||||||
|
);
|
||||||
tokio::time::delay_for(pool.locked().timeout.unwrap()).await;
|
tokio::time::delay_for(pool.locked().timeout.unwrap()).await;
|
||||||
|
|
||||||
let mut checkout = pool.checkout(key.clone());
|
let mut checkout = pool.checkout(key.clone());
|
||||||
@@ -895,7 +883,10 @@ mod tests {
|
|||||||
pool.pooled(c(key.clone()), Uniq(99));
|
pool.pooled(c(key.clone()), Uniq(99));
|
||||||
|
|
||||||
// pooled and dropped 3, max_idle should only allow 2
|
// pooled and dropped 3, max_idle should only allow 2
|
||||||
assert_eq!(pool.locked().idle.get(&key).map(|entries| entries.len()), Some(2));
|
assert_eq!(
|
||||||
|
pool.locked().idle.get(&key).map(|entries| entries.len()),
|
||||||
|
Some(2)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "runtime")]
|
#[cfg(feature = "runtime")]
|
||||||
@@ -904,7 +895,8 @@ mod tests {
|
|||||||
let _ = pretty_env_logger::try_init();
|
let _ = pretty_env_logger::try_init();
|
||||||
tokio::time::pause();
|
tokio::time::pause();
|
||||||
|
|
||||||
let pool = Pool::new(super::Config {
|
let pool = Pool::new(
|
||||||
|
super::Config {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
keep_alive_timeout: Some(Duration::from_millis(10)),
|
keep_alive_timeout: Some(Duration::from_millis(10)),
|
||||||
max_idle_per_host: ::std::usize::MAX,
|
max_idle_per_host: ::std::usize::MAX,
|
||||||
@@ -918,7 +910,10 @@ mod tests {
|
|||||||
pool.pooled(c(key.clone()), Uniq(5));
|
pool.pooled(c(key.clone()), Uniq(5));
|
||||||
pool.pooled(c(key.clone()), Uniq(99));
|
pool.pooled(c(key.clone()), Uniq(99));
|
||||||
|
|
||||||
assert_eq!(pool.locked().idle.get(&key).map(|entries| entries.len()), Some(3));
|
assert_eq!(
|
||||||
|
pool.locked().idle.get(&key).map(|entries| entries.len()),
|
||||||
|
Some(3)
|
||||||
|
);
|
||||||
|
|
||||||
// Let the timer tick passed the expiration...
|
// Let the timer tick passed the expiration...
|
||||||
tokio::time::advance(Duration::from_millis(30)).await;
|
tokio::time::advance(Duration::from_millis(30)).await;
|
||||||
@@ -937,17 +932,15 @@ mod tests {
|
|||||||
let key = Arc::new("foo".to_string());
|
let key = Arc::new("foo".to_string());
|
||||||
let pooled = pool.pooled(c(key.clone()), Uniq(41));
|
let pooled = pool.pooled(c(key.clone()), Uniq(41));
|
||||||
|
|
||||||
let checkout = join(
|
let checkout = join(pool.checkout(key), async {
|
||||||
pool.checkout(key),
|
// the checkout future will park first,
|
||||||
async {
|
// and then this lazy future will be polled, which will insert
|
||||||
// the checkout future will park first,
|
// the pooled back into the pool
|
||||||
// and then this lazy future will be polled, which will insert
|
//
|
||||||
// the pooled back into the pool
|
// this test makes sure that doing so will unpark the checkout
|
||||||
//
|
drop(pooled);
|
||||||
// this test makes sure that doing so will unpark the checkout
|
})
|
||||||
drop(pooled);
|
.map(|(entry, _)| entry);
|
||||||
},
|
|
||||||
).map(|(entry, _)| entry);
|
|
||||||
|
|
||||||
assert_eq!(*checkout.await.unwrap(), Uniq(41));
|
assert_eq!(*checkout.await.unwrap(), Uniq(41));
|
||||||
}
|
}
|
||||||
@@ -1001,10 +994,13 @@ mod tests {
|
|||||||
fn pooled_drop_if_closed_doesnt_reinsert() {
|
fn pooled_drop_if_closed_doesnt_reinsert() {
|
||||||
let pool = pool_no_timer();
|
let pool = pool_no_timer();
|
||||||
let key = Arc::new("localhost:12345".to_string());
|
let key = Arc::new("localhost:12345".to_string());
|
||||||
pool.pooled(c(key.clone()), CanClose {
|
pool.pooled(
|
||||||
val: 57,
|
c(key.clone()),
|
||||||
closed: true,
|
CanClose {
|
||||||
});
|
val: 57,
|
||||||
|
closed: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
assert!(!pool.locked().idle.contains_key(&key));
|
assert!(!pool.locked().idle.contains_key(&key));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,16 @@
|
|||||||
//!
|
//!
|
||||||
//! This module provides `Connect` which hook-ins into the Tower ecosystem.
|
//! This module provides `Connect` which hook-ins into the Tower ecosystem.
|
||||||
|
|
||||||
use std::marker::PhantomData;
|
|
||||||
use std::future::Future;
|
|
||||||
use std::error::Error as StdError;
|
use std::error::Error as StdError;
|
||||||
|
use std::future::Future;
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use crate::{common::{Poll, task, Pin}, body::Payload, service::{MakeConnection, Service}};
|
use super::conn::{Builder, SendRequest};
|
||||||
use super::conn::{SendRequest, Builder};
|
use crate::{
|
||||||
|
body::Payload,
|
||||||
|
common::{task, Pin, Poll},
|
||||||
|
service::{MakeConnection, Service},
|
||||||
|
};
|
||||||
|
|
||||||
/// Creates a connection via `SendRequest`.
|
/// Creates a connection via `SendRequest`.
|
||||||
///
|
///
|
||||||
@@ -18,7 +22,7 @@ use super::conn::{SendRequest, Builder};
|
|||||||
pub struct Connect<C, B, T> {
|
pub struct Connect<C, B, T> {
|
||||||
inner: C,
|
inner: C,
|
||||||
builder: Builder,
|
builder: Builder,
|
||||||
_pd: PhantomData<fn(T, B)>
|
_pd: PhantomData<fn(T, B)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C, B, T> Connect<C, B, T> {
|
impl<C, B, T> Connect<C, B, T> {
|
||||||
@@ -28,7 +32,7 @@ impl<C, B, T> Connect<C, B, T> {
|
|||||||
Self {
|
Self {
|
||||||
inner,
|
inner,
|
||||||
builder,
|
builder,
|
||||||
_pd: PhantomData
|
_pd: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -44,10 +48,13 @@ where
|
|||||||
{
|
{
|
||||||
type Response = SendRequest<B>;
|
type Response = SendRequest<B>;
|
||||||
type Error = crate::Error;
|
type Error = crate::Error;
|
||||||
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send + 'static>>;
|
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>> {
|
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()))
|
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 {
|
fn call(&mut self, req: T) -> Self::Future {
|
||||||
@@ -56,18 +63,16 @@ where
|
|||||||
|
|
||||||
let fut = async move {
|
let fut = async move {
|
||||||
match io.await {
|
match io.await {
|
||||||
Ok(io) => {
|
Ok(io) => match builder.handshake(io).await {
|
||||||
match builder.handshake(io).await {
|
Ok((sr, conn)) => {
|
||||||
Ok((sr, conn)) => {
|
builder.exec.execute(async move {
|
||||||
builder.exec.execute(async move {
|
if let Err(e) = conn.await {
|
||||||
if let Err(e) = conn.await {
|
debug!("connection error: {:?}", e);
|
||||||
debug!("connection error: {:?}", e);
|
}
|
||||||
}
|
});
|
||||||
});
|
Ok(sr)
|
||||||
Ok(sr)
|
|
||||||
},
|
|
||||||
Err(e) => Err(e)
|
|
||||||
}
|
}
|
||||||
|
Err(e) => Err(e),
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let err = crate::Error::new(crate::error::Kind::Connect).with(e.into());
|
let err = crate::Error::new(crate::error::Kind::Connect).with(e.into());
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
use tokio::sync::{mpsc, watch};
|
|
||||||
use pin_project::pin_project;
|
use pin_project::pin_project;
|
||||||
|
use tokio::sync::{mpsc, watch};
|
||||||
|
|
||||||
use super::{Future, Never, Poll, Pin, task};
|
use super::{task, Future, Never, Pin, Poll};
|
||||||
|
|
||||||
// Sentinel value signaling that the watch is still open
|
// Sentinel value signaling that the watch is still open
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
@@ -21,10 +21,7 @@ pub fn channel() -> (Signal, Watch) {
|
|||||||
drained_rx,
|
drained_rx,
|
||||||
_tx: tx,
|
_tx: tx,
|
||||||
},
|
},
|
||||||
Watch {
|
Watch { drained_tx, rx },
|
||||||
drained_tx,
|
|
||||||
rx,
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,17 +104,14 @@ where
|
|||||||
Poll::Ready(None) => {
|
Poll::Ready(None) => {
|
||||||
// Drain has been triggered!
|
// Drain has been triggered!
|
||||||
on_drain(me.future.as_mut());
|
on_drain(me.future.as_mut());
|
||||||
},
|
}
|
||||||
Poll::Ready(Some(_/*State::Open*/)) |
|
Poll::Ready(Some(_ /*State::Open*/)) | Poll::Pending => {
|
||||||
Poll::Pending => {
|
|
||||||
*me.state = State::Watch(on_drain);
|
*me.state = State::Watch(on_drain);
|
||||||
return me.future.poll(cx);
|
return me.future.poll(cx);
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
State::Draining => {
|
State::Draining => return me.future.poll(cx),
|
||||||
return me.future.poll(cx)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -236,4 +230,3 @@ mod tests {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use std::future::Future;
|
|||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::body::{Payload, Body};
|
use crate::body::{Body, Payload};
|
||||||
use crate::proto::h2::server::H2Stream;
|
use crate::proto::h2::server::H2Stream;
|
||||||
use crate::server::conn::spawn_all::{NewSvcTask, Watcher};
|
use crate::server::conn::spawn_all::{NewSvcTask, Watcher};
|
||||||
use crate::service::HttpService;
|
use crate::service::HttpService;
|
||||||
@@ -22,7 +22,7 @@ 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>);
|
fn execute_new_svc(&mut self, fut: NewSvcTask<I, N, S, E, W>);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type BoxSendFuture = Pin<Box<dyn Future<Output=()> + Send>>;
|
pub 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
|
||||||
// `tokio::spawn`.
|
// `tokio::spawn`.
|
||||||
@@ -37,7 +37,7 @@ pub enum Exec {
|
|||||||
impl Exec {
|
impl Exec {
|
||||||
pub(crate) fn execute<F>(&self, fut: F)
|
pub(crate) fn execute<F>(&self, fut: F)
|
||||||
where
|
where
|
||||||
F: Future<Output=()> + Send + 'static,
|
F: Future<Output = ()> + Send + 'static,
|
||||||
{
|
{
|
||||||
match *self {
|
match *self {
|
||||||
Exec::Default => {
|
Exec::Default => {
|
||||||
@@ -50,22 +50,20 @@ impl Exec {
|
|||||||
// If no runtime, we need an executor!
|
// If no runtime, we need an executor!
|
||||||
panic!("executor must be set")
|
panic!("executor must be set")
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
Exec::Executor(ref e) => {
|
Exec::Executor(ref e) => {
|
||||||
e.execute(Box::pin(fut));
|
e.execute(Box::pin(fut));
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Exec {
|
impl fmt::Debug for Exec {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.debug_struct("Exec")
|
f.debug_struct("Exec").finish()
|
||||||
.finish()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl<F, B> H2Exec<F, B> for Exec
|
impl<F, B> H2Exec<F, B> for Exec
|
||||||
where
|
where
|
||||||
H2Stream<F, B>: Future<Output = ()> + Send + 'static,
|
H2Stream<F, B>: Future<Output = ()> + Send + 'static,
|
||||||
@@ -78,7 +76,7 @@ where
|
|||||||
|
|
||||||
impl<I, N, S, E, W> NewSvcExec<I, N, S, E, W> for Exec
|
impl<I, N, S, E, W> NewSvcExec<I, N, S, E, W> for Exec
|
||||||
where
|
where
|
||||||
NewSvcTask<I, N, S, E, W>: Future<Output=()> + Send + 'static,
|
NewSvcTask<I, N, S, E, W>: Future<Output = ()> + Send + 'static,
|
||||||
S: HttpService<Body>,
|
S: HttpService<Body>,
|
||||||
W: Watcher<I, S, E>,
|
W: Watcher<I, S, E>,
|
||||||
{
|
{
|
||||||
@@ -92,7 +90,7 @@ where
|
|||||||
impl<E, F, B> H2Exec<F, B> for E
|
impl<E, F, B> H2Exec<F, B> for E
|
||||||
where
|
where
|
||||||
E: Executor<H2Stream<F, B>> + Clone,
|
E: Executor<H2Stream<F, B>> + Clone,
|
||||||
H2Stream<F, B>: Future<Output=()>,
|
H2Stream<F, B>: Future<Output = ()>,
|
||||||
B: Payload,
|
B: Payload,
|
||||||
{
|
{
|
||||||
fn execute_h2stream(&mut self, fut: H2Stream<F, B>) {
|
fn execute_h2stream(&mut self, fut: H2Stream<F, B>) {
|
||||||
@@ -103,7 +101,7 @@ where
|
|||||||
impl<I, N, S, E, W> NewSvcExec<I, N, S, E, W> for E
|
impl<I, N, S, E, W> NewSvcExec<I, N, S, E, W> for E
|
||||||
where
|
where
|
||||||
E: Executor<NewSvcTask<I, N, S, E, W>> + Clone,
|
E: Executor<NewSvcTask<I, N, S, E, W>> + Clone,
|
||||||
NewSvcTask<I, N, S, E, W>: Future<Output=()>,
|
NewSvcTask<I, N, S, E, W>: Future<Output = ()>,
|
||||||
S: HttpService<Body>,
|
S: HttpService<Body>,
|
||||||
W: Watcher<I, S, E>,
|
W: Watcher<I, S, E>,
|
||||||
{
|
{
|
||||||
@@ -111,4 +109,3 @@ where
|
|||||||
self.execute(fut)
|
self.execute(fut)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
use std::{cmp, io};
|
|
||||||
use std::marker::Unpin;
|
use std::marker::Unpin;
|
||||||
|
use std::{cmp, io};
|
||||||
|
|
||||||
use bytes::{Buf, Bytes};
|
use bytes::{Buf, Bytes};
|
||||||
use tokio::io::{AsyncRead, AsyncWrite};
|
use tokio::io::{AsyncRead, AsyncWrite};
|
||||||
|
|
||||||
use crate::common::{Pin, Poll, task};
|
use crate::common::{task, Pin, Poll};
|
||||||
|
|
||||||
/// Combine a buffer with an IO, rewinding reads to use the buffer.
|
/// Combine a buffer with an IO, rewinding reads to use the buffer.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -47,7 +47,11 @@ where
|
|||||||
self.inner.prepare_uninitialized_buffer(buf)
|
self.inner.prepare_uninitialized_buffer(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll_read(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>, buf: &mut [u8]) -> Poll<io::Result<usize>> {
|
fn poll_read(
|
||||||
|
mut self: Pin<&mut Self>,
|
||||||
|
cx: &mut task::Context<'_>,
|
||||||
|
buf: &mut [u8],
|
||||||
|
) -> Poll<io::Result<usize>> {
|
||||||
if let Some(mut prefix) = self.pre.take() {
|
if let Some(mut prefix) = self.pre.take() {
|
||||||
// If there are no remaining bytes, let the bytes get dropped.
|
// If there are no remaining bytes, let the bytes get dropped.
|
||||||
if prefix.len() > 0 {
|
if prefix.len() > 0 {
|
||||||
@@ -69,7 +73,11 @@ impl<T> AsyncWrite for Rewind<T>
|
|||||||
where
|
where
|
||||||
T: AsyncWrite + Unpin,
|
T: AsyncWrite + Unpin,
|
||||||
{
|
{
|
||||||
fn poll_write(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>, buf: &[u8]) -> Poll<io::Result<usize>> {
|
fn poll_write(
|
||||||
|
mut self: Pin<&mut Self>,
|
||||||
|
cx: &mut task::Context<'_>,
|
||||||
|
buf: &[u8],
|
||||||
|
) -> Poll<io::Result<usize>> {
|
||||||
Pin::new(&mut self.inner).poll_write(cx, buf)
|
Pin::new(&mut self.inner).poll_write(cx, buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,7 +90,11 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn poll_write_buf<B: Buf>(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>, buf: &mut B) -> Poll<io::Result<usize>> {
|
fn poll_write_buf<B: Buf>(
|
||||||
|
mut self: Pin<&mut Self>,
|
||||||
|
cx: &mut task::Context<'_>,
|
||||||
|
buf: &mut B,
|
||||||
|
) -> Poll<io::Result<usize>> {
|
||||||
Pin::new(&mut self.inner).poll_write_buf(cx, buf)
|
Pin::new(&mut self.inner).poll_write_buf(cx, buf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -91,36 +103,27 @@ where
|
|||||||
mod tests {
|
mod tests {
|
||||||
// FIXME: re-implement tests with `async/await`, this import should
|
// FIXME: re-implement tests with `async/await`, this import should
|
||||||
// trigger a warning to remind us
|
// trigger a warning to remind us
|
||||||
|
use super::Rewind;
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use tokio::io::AsyncReadExt;
|
use tokio::io::AsyncReadExt;
|
||||||
use super::Rewind;
|
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn partial_rewind() {
|
async fn partial_rewind() {
|
||||||
let underlying = [104, 101, 108, 108, 111];
|
let underlying = [104, 101, 108, 108, 111];
|
||||||
|
|
||||||
let mock = tokio_test::io::Builder::new()
|
let mock = tokio_test::io::Builder::new().read(&underlying).build();
|
||||||
.read(&underlying)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let mut stream = Rewind::new(mock);
|
let mut stream = Rewind::new(mock);
|
||||||
|
|
||||||
// Read off some bytes, ensure we filled o1
|
// Read off some bytes, ensure we filled o1
|
||||||
let mut buf = [0; 2];
|
let mut buf = [0; 2];
|
||||||
stream
|
stream.read_exact(&mut buf).await.expect("read1");
|
||||||
.read_exact(&mut buf)
|
|
||||||
.await
|
|
||||||
.expect("read1");
|
|
||||||
|
|
||||||
|
|
||||||
// Rewind the stream so that it is as if we never read in the first place.
|
// Rewind the stream so that it is as if we never read in the first place.
|
||||||
stream.rewind(Bytes::copy_from_slice(&buf[..]));
|
stream.rewind(Bytes::copy_from_slice(&buf[..]));
|
||||||
|
|
||||||
let mut buf = [0; 5];
|
let mut buf = [0; 5];
|
||||||
stream
|
stream.read_exact(&mut buf).await.expect("read1");
|
||||||
.read_exact(&mut buf)
|
|
||||||
.await
|
|
||||||
.expect("read1");
|
|
||||||
|
|
||||||
// At this point we should have read everything that was in the MockStream
|
// At this point we should have read everything that was in the MockStream
|
||||||
assert_eq!(&buf, &underlying);
|
assert_eq!(&buf, &underlying);
|
||||||
@@ -130,26 +133,17 @@ mod tests {
|
|||||||
async fn full_rewind() {
|
async fn full_rewind() {
|
||||||
let underlying = [104, 101, 108, 108, 111];
|
let underlying = [104, 101, 108, 108, 111];
|
||||||
|
|
||||||
let mock = tokio_test::io::Builder::new()
|
let mock = tokio_test::io::Builder::new().read(&underlying).build();
|
||||||
.read(&underlying)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let mut stream = Rewind::new(mock);
|
let mut stream = Rewind::new(mock);
|
||||||
|
|
||||||
let mut buf = [0; 5];
|
let mut buf = [0; 5];
|
||||||
stream
|
stream.read_exact(&mut buf).await.expect("read1");
|
||||||
.read_exact(&mut buf)
|
|
||||||
.await
|
|
||||||
.expect("read1");
|
|
||||||
|
|
||||||
|
|
||||||
// Rewind the stream so that it is as if we never read in the first place.
|
// Rewind the stream so that it is as if we never read in the first place.
|
||||||
stream.rewind(Bytes::copy_from_slice(&buf[..]));
|
stream.rewind(Bytes::copy_from_slice(&buf[..]));
|
||||||
|
|
||||||
let mut buf = [0; 5];
|
let mut buf = [0; 5];
|
||||||
stream
|
stream.read_exact(&mut buf).await.expect("read1");
|
||||||
.read_exact(&mut buf)
|
|
||||||
.await
|
|
||||||
.expect("read1");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
use super::{Future, Pin, Poll, task};
|
use super::{task, Future, Pin, Poll};
|
||||||
|
|
||||||
pub(crate) trait Started: Future {
|
pub(crate) trait Started: Future {
|
||||||
fn started(&self) -> bool;
|
fn started(&self) -> bool;
|
||||||
@@ -19,7 +19,7 @@ where
|
|||||||
// FIXME: allow() required due to `impl Trait` leaking types to this lint
|
// FIXME: allow() required due to `impl Trait` leaking types to this lint
|
||||||
#[allow(missing_debug_implementations)]
|
#[allow(missing_debug_implementations)]
|
||||||
pub(crate) struct Lazy<F, R> {
|
pub(crate) struct Lazy<F, R> {
|
||||||
inner: Inner<F, R>
|
inner: Inner<F, R>,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Inner<F, R> {
|
enum Inner<F, R> {
|
||||||
@@ -36,8 +36,7 @@ where
|
|||||||
fn started(&self) -> bool {
|
fn started(&self) -> bool {
|
||||||
match self.inner {
|
match self.inner {
|
||||||
Inner::Init(_) => false,
|
Inner::Init(_) => false,
|
||||||
Inner::Fut(_) |
|
Inner::Fut(_) | Inner::Empty => true,
|
||||||
Inner::Empty => true,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -61,7 +60,7 @@ where
|
|||||||
let ret = Pin::new(&mut fut).poll(cx);
|
let ret = Pin::new(&mut fut).poll(cx);
|
||||||
self.inner = Inner::Fut(fut);
|
self.inner = Inner::Fut(fut);
|
||||||
ret
|
ret
|
||||||
},
|
}
|
||||||
_ => unreachable!("lazy state wrong"),
|
_ => unreachable!("lazy state wrong"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -69,4 +68,3 @@ where
|
|||||||
|
|
||||||
// The closure `F` is never pinned
|
// The closure `F` is never pinned
|
||||||
impl<F, R: Unpin> Unpin for Lazy<F, R> {}
|
impl<F, R: Unpin> Unpin for Lazy<F, R> {}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
macro_rules! ready {
|
macro_rules! ready {
|
||||||
($e:expr) => (
|
($e:expr) => {
|
||||||
match $e {
|
match $e {
|
||||||
::std::task::Poll::Ready(v) => v,
|
::std::task::Poll::Ready(v) => v,
|
||||||
::std::task::Poll::Pending => return ::std::task::Poll::Pending,
|
::std::task::Poll::Pending => return ::std::task::Poll::Pending,
|
||||||
}
|
}
|
||||||
)
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) mod drain;
|
pub(crate) mod drain;
|
||||||
@@ -14,16 +14,11 @@ mod lazy;
|
|||||||
mod never;
|
mod never;
|
||||||
pub(crate) mod task;
|
pub(crate) mod task;
|
||||||
|
|
||||||
pub(crate) use self::exec::{BoxSendFuture, Exec};
|
|
||||||
pub use self::exec::Executor;
|
pub use self::exec::Executor;
|
||||||
|
pub(crate) use self::exec::{BoxSendFuture, Exec};
|
||||||
pub(crate) use self::lazy::{lazy, Started as Lazy};
|
pub(crate) use self::lazy::{lazy, Started as Lazy};
|
||||||
pub use self::never::Never;
|
pub use self::never::Never;
|
||||||
pub(crate) use self::task::Poll;
|
pub(crate) use self::task::Poll;
|
||||||
|
|
||||||
// group up types normally needed for `Future`
|
// group up types normally needed for `Future`
|
||||||
pub(crate) use std::{
|
pub(crate) use std::{future::Future, marker::Unpin, pin::Pin};
|
||||||
future::Future,
|
|
||||||
marker::Unpin,
|
|
||||||
pin::Pin,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|||||||
@@ -19,4 +19,3 @@ impl Error for Never {
|
|||||||
match *self {}
|
match *self {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
pub(crate) use std::task::{Context, Poll};
|
|
||||||
use super::Never;
|
use super::Never;
|
||||||
|
pub(crate) use std::task::{Context, Poll};
|
||||||
|
|
||||||
/// A function to help "yield" a future, such that it is re-scheduled immediately.
|
/// A function to help "yield" a future, such that it is re-scheduled immediately.
|
||||||
///
|
///
|
||||||
|
|||||||
26
src/error.rs
26
src/error.rs
@@ -140,10 +140,7 @@ impl Error {
|
|||||||
|
|
||||||
pub(crate) fn new(kind: Kind) -> Error {
|
pub(crate) fn new(kind: Kind) -> Error {
|
||||||
Error {
|
Error {
|
||||||
inner: Box::new(ErrorImpl {
|
inner: Box::new(ErrorImpl { kind, cause: None }),
|
||||||
kind,
|
|
||||||
cause: None,
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,9 +159,7 @@ impl Error {
|
|||||||
let mut cause = self.source();
|
let mut cause = self.source();
|
||||||
while let Some(err) = cause {
|
while let Some(err) = cause {
|
||||||
if let Some(h2_err) = err.downcast_ref::<h2::Error>() {
|
if let Some(h2_err) = err.downcast_ref::<h2::Error>() {
|
||||||
return h2_err
|
return h2_err.reason().unwrap_or(h2::Reason::INTERNAL_ERROR);
|
||||||
.reason()
|
|
||||||
.unwrap_or(h2::Reason::INTERNAL_ERROR);
|
|
||||||
}
|
}
|
||||||
cause = err.source();
|
cause = err.source();
|
||||||
}
|
}
|
||||||
@@ -335,7 +330,9 @@ impl StdError for Error {
|
|||||||
Kind::User(User::UnexpectedHeader) => "user sent unexpected header",
|
Kind::User(User::UnexpectedHeader) => "user sent unexpected header",
|
||||||
Kind::User(User::UnsupportedVersion) => "request has unsupported HTTP version",
|
Kind::User(User::UnsupportedVersion) => "request has unsupported HTTP version",
|
||||||
Kind::User(User::UnsupportedRequestMethod) => "request has unsupported HTTP method",
|
Kind::User(User::UnsupportedRequestMethod) => "request has unsupported HTTP method",
|
||||||
Kind::User(User::UnsupportedStatusCode) => "response has 1xx status code, not supported by server",
|
Kind::User(User::UnsupportedStatusCode) => {
|
||||||
|
"response has 1xx status code, not supported by server"
|
||||||
|
}
|
||||||
Kind::User(User::AbsoluteUriRequired) => "client requires absolute-form URIs",
|
Kind::User(User::AbsoluteUriRequired) => "client requires absolute-form URIs",
|
||||||
Kind::User(User::NoUpgrade) => "no upgrade available",
|
Kind::User(User::NoUpgrade) => "no upgrade available",
|
||||||
Kind::User(User::ManualUpgrade) => "upgrade expected but low level API in use",
|
Kind::User(User::ManualUpgrade) => "upgrade expected but low level API in use",
|
||||||
@@ -343,8 +340,7 @@ impl StdError for Error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn source(&self) -> Option<&(dyn StdError + 'static)> {
|
fn source(&self) -> Option<&(dyn StdError + 'static)> {
|
||||||
self
|
self.inner
|
||||||
.inner
|
|
||||||
.cause
|
.cause
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|cause| &**cause as &(dyn StdError + 'static))
|
.map(|cause| &**cause as &(dyn StdError + 'static))
|
||||||
@@ -361,10 +357,10 @@ impl From<Parse> for Error {
|
|||||||
impl From<httparse::Error> for Parse {
|
impl From<httparse::Error> for Parse {
|
||||||
fn from(err: httparse::Error) -> Parse {
|
fn from(err: httparse::Error) -> Parse {
|
||||||
match err {
|
match err {
|
||||||
httparse::Error::HeaderName |
|
httparse::Error::HeaderName
|
||||||
httparse::Error::HeaderValue |
|
| httparse::Error::HeaderValue
|
||||||
httparse::Error::NewLine |
|
| httparse::Error::NewLine
|
||||||
httparse::Error::Token => Parse::Header,
|
| httparse::Error::Token => Parse::Header,
|
||||||
httparse::Error::Status => Parse::Status,
|
httparse::Error::Status => Parse::Status,
|
||||||
httparse::Error::TooManyHeaders => Parse::TooLarge,
|
httparse::Error::TooManyHeaders => Parse::TooLarge,
|
||||||
httparse::Error::Version => Parse::Version,
|
httparse::Error::Version => Parse::Version,
|
||||||
@@ -403,8 +399,8 @@ impl AssertSendSync for Error {}
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::mem;
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn error_size_of() {
|
fn error_size_of() {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use bytes::BytesMut;
|
use bytes::BytesMut;
|
||||||
use http::HeaderMap;
|
|
||||||
use http::header::{CONTENT_LENGTH, TRANSFER_ENCODING};
|
|
||||||
use http::header::{HeaderValue, OccupiedEntry, ValueIter};
|
use http::header::{HeaderValue, OccupiedEntry, ValueIter};
|
||||||
|
use http::header::{CONTENT_LENGTH, TRANSFER_ENCODING};
|
||||||
|
use http::HeaderMap;
|
||||||
|
|
||||||
pub fn connection_keep_alive(value: &HeaderValue) -> bool {
|
pub fn connection_keep_alive(value: &HeaderValue) -> bool {
|
||||||
connection_has(value, "keep-alive")
|
connection_has(value, "keep-alive")
|
||||||
@@ -23,10 +23,7 @@ fn connection_has(value: &HeaderValue, needle: &str) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn content_length_parse(value: &HeaderValue) -> Option<u64> {
|
pub fn content_length_parse(value: &HeaderValue) -> Option<u64> {
|
||||||
value
|
value.to_str().ok().and_then(|s| s.parse().ok())
|
||||||
.to_str()
|
|
||||||
.ok()
|
|
||||||
.and_then(|s| s.parse().ok())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn content_length_parse_all(headers: &HeaderMap) -> Option<u64> {
|
pub fn content_length_parse_all(headers: &HeaderMap) -> Option<u64> {
|
||||||
@@ -38,23 +35,20 @@ pub fn content_length_parse_all_values(values: ValueIter<'_, HeaderValue>) -> Op
|
|||||||
// be alright if they all contain the same value, and all parse
|
// be alright if they all contain the same value, and all parse
|
||||||
// correctly. If not, then it's an error.
|
// correctly. If not, then it's an error.
|
||||||
|
|
||||||
let folded = values
|
let folded = values.fold(None, |prev, line| match prev {
|
||||||
.fold(None, |prev, line| match prev {
|
Some(Ok(prev)) => Some(
|
||||||
Some(Ok(prev)) => {
|
line.to_str()
|
||||||
Some(line
|
.map_err(|_| ())
|
||||||
.to_str()
|
.and_then(|s| s.parse().map_err(|_| ()))
|
||||||
.map_err(|_| ())
|
.and_then(|n| if prev == n { Ok(n) } else { Err(()) }),
|
||||||
.and_then(|s| s.parse().map_err(|_| ()))
|
),
|
||||||
.and_then(|n| if prev == n { Ok(n) } else { Err(()) }))
|
None => Some(
|
||||||
},
|
line.to_str()
|
||||||
None => {
|
.map_err(|_| ())
|
||||||
Some(line
|
.and_then(|s| s.parse().map_err(|_| ())),
|
||||||
.to_str()
|
),
|
||||||
.map_err(|_| ())
|
Some(Err(())) => Some(Err(())),
|
||||||
.and_then(|s| s.parse().map_err(|_| ())))
|
});
|
||||||
},
|
|
||||||
Some(Err(())) => Some(Err(())),
|
|
||||||
});
|
|
||||||
|
|
||||||
if let Some(Ok(n)) = folded {
|
if let Some(Ok(n)) = folded {
|
||||||
Some(n)
|
Some(n)
|
||||||
|
|||||||
27
src/lib.rs
27
src/lib.rs
@@ -29,38 +29,31 @@
|
|||||||
//! TCP (using tokio).
|
//! TCP (using tokio).
|
||||||
//! - `stream` (*enabled by default*): Provides `futures::Stream` capabilities.
|
//! - `stream` (*enabled by default*): Provides `futures::Stream` capabilities.
|
||||||
|
|
||||||
#[doc(hidden)] pub use http;
|
#[doc(hidden)]
|
||||||
#[macro_use] extern crate log;
|
pub use http;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate log;
|
||||||
|
|
||||||
#[cfg(all(test, feature = "nightly"))]
|
#[cfg(all(test, feature = "nightly"))]
|
||||||
extern crate test;
|
extern crate test;
|
||||||
|
|
||||||
pub use http::{
|
pub use http::{header, HeaderMap, Method, Request, Response, StatusCode, Uri, Version};
|
||||||
header,
|
|
||||||
HeaderMap,
|
|
||||||
Method,
|
|
||||||
Request,
|
|
||||||
Response,
|
|
||||||
StatusCode,
|
|
||||||
Uri,
|
|
||||||
Version,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub use crate::client::Client;
|
|
||||||
pub use crate::error::{Result, Error};
|
|
||||||
pub use crate::body::{Body, Chunk};
|
pub use crate::body::{Body, Chunk};
|
||||||
|
pub use crate::client::Client;
|
||||||
|
pub use crate::error::{Error, Result};
|
||||||
pub use crate::server::Server;
|
pub use crate::server::Server;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod common;
|
mod common;
|
||||||
#[cfg(test)]
|
|
||||||
mod mock;
|
|
||||||
pub mod body;
|
pub mod body;
|
||||||
pub mod client;
|
pub mod client;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
mod headers;
|
mod headers;
|
||||||
|
#[cfg(test)]
|
||||||
|
mod mock;
|
||||||
mod proto;
|
mod proto;
|
||||||
|
pub mod rt;
|
||||||
pub mod server;
|
pub mod server;
|
||||||
pub mod service;
|
pub mod service;
|
||||||
pub mod rt;
|
|
||||||
pub mod upgrade;
|
pub mod upgrade;
|
||||||
|
|||||||
@@ -3,16 +3,16 @@ use std::io::{self};
|
|||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use bytes::{Buf, Bytes};
|
use bytes::{Buf, Bytes};
|
||||||
use http::{HeaderMap, Method, Version};
|
|
||||||
use http::header::{HeaderValue, CONNECTION};
|
use http::header::{HeaderValue, CONNECTION};
|
||||||
|
use http::{HeaderMap, Method, Version};
|
||||||
use tokio::io::{AsyncRead, AsyncWrite};
|
use tokio::io::{AsyncRead, AsyncWrite};
|
||||||
|
|
||||||
use crate::Chunk;
|
use super::io::Buffered;
|
||||||
use crate::common::{Pin, Poll, Unpin, task};
|
use super::{/*Decode,*/ Decoder, Encode, EncodedBuf, Encoder, Http1Transaction, ParseContext,};
|
||||||
use crate::proto::{BodyLength, DecodedLength, MessageHead};
|
use crate::common::{task, Pin, Poll, Unpin};
|
||||||
use crate::headers::connection_keep_alive;
|
use crate::headers::connection_keep_alive;
|
||||||
use super::io::{Buffered};
|
use crate::proto::{BodyLength, DecodedLength, MessageHead};
|
||||||
use super::{EncodedBuf, Encode, Encoder, /*Decode,*/ Decoder, Http1Transaction, ParseContext};
|
use crate::Chunk;
|
||||||
|
|
||||||
const H2_PREFACE: &'static [u8] = b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
|
const H2_PREFACE: &'static [u8] = b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
|
||||||
|
|
||||||
@@ -26,13 +26,14 @@ const H2_PREFACE: &'static [u8] = b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
|
|||||||
pub(crate) struct Conn<I, B, T> {
|
pub(crate) struct Conn<I, B, T> {
|
||||||
io: Buffered<I, EncodedBuf<B>>,
|
io: Buffered<I, EncodedBuf<B>>,
|
||||||
state: State,
|
state: State,
|
||||||
_marker: PhantomData<fn(T)>
|
_marker: PhantomData<fn(T)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I, B, T> Conn<I, B, T>
|
impl<I, B, T> Conn<I, B, T>
|
||||||
where I: AsyncRead + AsyncWrite + Unpin,
|
where
|
||||||
B: Buf,
|
I: AsyncRead + AsyncWrite + Unpin,
|
||||||
T: Http1Transaction,
|
B: Buf,
|
||||||
|
T: Http1Transaction,
|
||||||
{
|
{
|
||||||
pub fn new(io: I) -> Conn<I, B, T> {
|
pub fn new(io: I) -> Conn<I, B, T> {
|
||||||
Conn {
|
Conn {
|
||||||
@@ -107,7 +108,7 @@ where I: AsyncRead + AsyncWrite + Unpin,
|
|||||||
_ => true,
|
_ => true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -129,14 +130,20 @@ where I: AsyncRead + AsyncWrite + Unpin,
|
|||||||
read_buf.len() >= 24 && read_buf[..24] == *H2_PREFACE
|
read_buf.len() >= 24 && read_buf[..24] == *H2_PREFACE
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn poll_read_head(&mut self, cx: &mut task::Context<'_>) -> Poll<Option<crate::Result<(MessageHead<T::Incoming>, DecodedLength, bool)>>> {
|
pub fn poll_read_head(
|
||||||
|
&mut self,
|
||||||
|
cx: &mut task::Context<'_>,
|
||||||
|
) -> Poll<Option<crate::Result<(MessageHead<T::Incoming>, DecodedLength, bool)>>> {
|
||||||
debug_assert!(self.can_read_head());
|
debug_assert!(self.can_read_head());
|
||||||
trace!("Conn::read_head");
|
trace!("Conn::read_head");
|
||||||
|
|
||||||
let msg = match ready!(self.io.parse::<T>(cx, ParseContext {
|
let msg = match ready!(self.io.parse::<T>(
|
||||||
cached_headers: &mut self.state.cached_headers,
|
cx,
|
||||||
req_method: &mut self.state.method,
|
ParseContext {
|
||||||
})) {
|
cached_headers: &mut self.state.cached_headers,
|
||||||
|
req_method: &mut self.state.method,
|
||||||
|
}
|
||||||
|
)) {
|
||||||
Ok(msg) => msg,
|
Ok(msg) => msg,
|
||||||
Err(e) => return self.on_read_head_error(e),
|
Err(e) => return self.on_read_head_error(e),
|
||||||
};
|
};
|
||||||
@@ -179,11 +186,14 @@ where I: AsyncRead + AsyncWrite + Unpin,
|
|||||||
let was_mid_parse = e.is_parse() || !self.io.read_buf().is_empty();
|
let was_mid_parse = e.is_parse() || !self.io.read_buf().is_empty();
|
||||||
if was_mid_parse || must_error {
|
if was_mid_parse || must_error {
|
||||||
// We check if the buf contains the h2 Preface
|
// We check if the buf contains the h2 Preface
|
||||||
debug!("parse error ({}) with {} bytes", e, self.io.read_buf().len());
|
debug!(
|
||||||
|
"parse error ({}) with {} bytes",
|
||||||
|
e,
|
||||||
|
self.io.read_buf().len()
|
||||||
|
);
|
||||||
match self.on_parse_error(e) {
|
match self.on_parse_error(e) {
|
||||||
Ok(()) => Poll::Pending, // XXX: wat?
|
Ok(()) => Poll::Pending, // XXX: wat?
|
||||||
Err(e) => Poll::Ready(Some(Err(e))),
|
Err(e) => Poll::Ready(Some(Err(e))),
|
||||||
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
debug!("read eof");
|
debug!("read eof");
|
||||||
@@ -192,7 +202,10 @@ where I: AsyncRead + AsyncWrite + Unpin,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn poll_read_body(&mut self, cx: &mut task::Context<'_>) -> Poll<Option<io::Result<Chunk>>> {
|
pub fn poll_read_body(
|
||||||
|
&mut self,
|
||||||
|
cx: &mut task::Context<'_>,
|
||||||
|
) -> Poll<Option<io::Result<Chunk>>> {
|
||||||
debug_assert!(self.can_read_body());
|
debug_assert!(self.can_read_body());
|
||||||
|
|
||||||
let (reading, ret) = match self.state.reading {
|
let (reading, ret) = match self.state.reading {
|
||||||
@@ -201,11 +214,14 @@ where I: AsyncRead + AsyncWrite + Unpin,
|
|||||||
Poll::Ready(Ok(slice)) => {
|
Poll::Ready(Ok(slice)) => {
|
||||||
let (reading, chunk) = if decoder.is_eof() {
|
let (reading, chunk) = if decoder.is_eof() {
|
||||||
debug!("incoming body completed");
|
debug!("incoming body completed");
|
||||||
(Reading::KeepAlive, if !slice.is_empty() {
|
(
|
||||||
Some(Ok(Chunk::from(slice)))
|
Reading::KeepAlive,
|
||||||
} else {
|
if !slice.is_empty() {
|
||||||
None
|
Some(Ok(Chunk::from(slice)))
|
||||||
})
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
)
|
||||||
} else if slice.is_empty() {
|
} else if slice.is_empty() {
|
||||||
error!("incoming body unexpectedly ended");
|
error!("incoming body unexpectedly ended");
|
||||||
// This should be unreachable, since all 3 decoders
|
// This should be unreachable, since all 3 decoders
|
||||||
@@ -216,14 +232,14 @@ where I: AsyncRead + AsyncWrite + Unpin,
|
|||||||
return Poll::Ready(Some(Ok(Chunk::from(slice))));
|
return Poll::Ready(Some(Ok(Chunk::from(slice))));
|
||||||
};
|
};
|
||||||
(reading, Poll::Ready(chunk))
|
(reading, Poll::Ready(chunk))
|
||||||
},
|
}
|
||||||
Poll::Pending => return Poll::Pending,
|
Poll::Pending => return Poll::Pending,
|
||||||
Poll::Ready(Err(e)) => {
|
Poll::Ready(Err(e)) => {
|
||||||
debug!("incoming body decode error: {}", e);
|
debug!("incoming body decode error: {}", e);
|
||||||
(Reading::Closed, Poll::Ready(Some(Err(e))))
|
(Reading::Closed, Poll::Ready(Some(Err(e))))
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
_ => unreachable!("read_body invalid state: {:?}", self.state.reading),
|
_ => unreachable!("read_body invalid state: {:?}", self.state.reading),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -287,7 +303,10 @@ where I: AsyncRead + AsyncWrite + Unpin,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!("received unexpected {} bytes on an idle connection", num_read);
|
debug!(
|
||||||
|
"received unexpected {} bytes on an idle connection",
|
||||||
|
num_read
|
||||||
|
);
|
||||||
Poll::Ready(Err(crate::Error::new_unexpected_message()))
|
Poll::Ready(Err(crate::Error::new_unexpected_message()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -318,30 +337,23 @@ where I: AsyncRead + AsyncWrite + Unpin,
|
|||||||
trace!("force_io_read; io error = {:?}", e);
|
trace!("force_io_read; io error = {:?}", e);
|
||||||
self.state.close();
|
self.state.close();
|
||||||
e
|
e
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn maybe_notify(&mut self, cx: &mut task::Context<'_>) {
|
fn maybe_notify(&mut self, cx: &mut task::Context<'_>) {
|
||||||
// its possible that we returned NotReady from poll() without having
|
// its possible that we returned NotReady from poll() without having
|
||||||
// exhausted the underlying Io. We would have done this when we
|
// exhausted the underlying Io. We would have done this when we
|
||||||
// determined we couldn't keep reading until we knew how writing
|
// determined we couldn't keep reading until we knew how writing
|
||||||
// would finish.
|
// would finish.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
match self.state.reading {
|
match self.state.reading {
|
||||||
Reading::Body(..) |
|
Reading::Body(..) | Reading::KeepAlive | Reading::Closed => return,
|
||||||
Reading::KeepAlive |
|
|
||||||
Reading::Closed => return,
|
|
||||||
Reading::Init => (),
|
Reading::Init => (),
|
||||||
};
|
};
|
||||||
|
|
||||||
match self.state.writing {
|
match self.state.writing {
|
||||||
Writing::Body(..) => return,
|
Writing::Body(..) => return,
|
||||||
Writing::Init |
|
Writing::Init | Writing::KeepAlive | Writing::Closed => (),
|
||||||
Writing::KeepAlive |
|
|
||||||
Writing::Closed => (),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !self.io.is_read_blocked() {
|
if !self.io.is_read_blocked() {
|
||||||
@@ -357,11 +369,11 @@ where I: AsyncRead + AsyncWrite + Unpin,
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
Poll::Pending => {
|
Poll::Pending => {
|
||||||
trace!("maybe_notify; read_from_io blocked");
|
trace!("maybe_notify; read_from_io blocked");
|
||||||
return
|
return;
|
||||||
},
|
}
|
||||||
Poll::Ready(Err(e)) => {
|
Poll::Ready(Err(e)) => {
|
||||||
trace!("maybe_notify; read_from_io error: {}", e);
|
trace!("maybe_notify; read_from_io error: {}", e);
|
||||||
self.state.close();
|
self.state.close();
|
||||||
@@ -382,21 +394,19 @@ where I: AsyncRead + AsyncWrite + Unpin,
|
|||||||
if !T::should_read_first() {
|
if !T::should_read_first() {
|
||||||
match self.state.reading {
|
match self.state.reading {
|
||||||
Reading::Closed => return false,
|
Reading::Closed => return false,
|
||||||
_ => {},
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
match self.state.writing {
|
match self.state.writing {
|
||||||
Writing::Init => true,
|
Writing::Init => true,
|
||||||
_ => false
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn can_write_body(&self) -> bool {
|
pub fn can_write_body(&self) -> bool {
|
||||||
match self.state.writing {
|
match self.state.writing {
|
||||||
Writing::Body(..) => true,
|
Writing::Body(..) => true,
|
||||||
Writing::Init |
|
Writing::Init | Writing::KeepAlive | Writing::Closed => false,
|
||||||
Writing::KeepAlive |
|
|
||||||
Writing::Closed => false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -417,7 +427,9 @@ where I: AsyncRead + AsyncWrite + Unpin,
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_full_msg(&mut self, head: MessageHead<T::Outgoing>, body: B) {
|
pub fn write_full_msg(&mut self, head: MessageHead<T::Outgoing>, body: B) {
|
||||||
if let Some(encoder) = self.encode_head(head, Some(BodyLength::Known(body.remaining() as u64))) {
|
if let Some(encoder) =
|
||||||
|
self.encode_head(head, Some(BodyLength::Known(body.remaining() as u64)))
|
||||||
|
{
|
||||||
let is_last = encoder.is_last();
|
let is_last = encoder.is_last();
|
||||||
// Make sure we don't write a body if we weren't actually allowed
|
// Make sure we don't write a body if we weren't actually allowed
|
||||||
// to do so, like because its a HEAD request.
|
// to do so, like because its a HEAD request.
|
||||||
@@ -432,7 +444,11 @@ where I: AsyncRead + AsyncWrite + Unpin,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn encode_head(&mut self, mut head: MessageHead<T::Outgoing>, body: Option<BodyLength>) -> Option<Encoder> {
|
fn encode_head(
|
||||||
|
&mut self,
|
||||||
|
mut head: MessageHead<T::Outgoing>,
|
||||||
|
body: Option<BodyLength>,
|
||||||
|
) -> Option<Encoder> {
|
||||||
debug_assert!(self.can_write_head());
|
debug_assert!(self.can_write_head());
|
||||||
|
|
||||||
if !T::should_read_first() {
|
if !T::should_read_first() {
|
||||||
@@ -442,24 +458,27 @@ where I: AsyncRead + AsyncWrite + Unpin,
|
|||||||
self.enforce_version(&mut head);
|
self.enforce_version(&mut head);
|
||||||
|
|
||||||
let buf = self.io.headers_buf();
|
let buf = self.io.headers_buf();
|
||||||
match T::encode(Encode {
|
match T::encode(
|
||||||
head: &mut head,
|
Encode {
|
||||||
body,
|
head: &mut head,
|
||||||
keep_alive: self.state.wants_keep_alive(),
|
body,
|
||||||
req_method: &mut self.state.method,
|
keep_alive: self.state.wants_keep_alive(),
|
||||||
title_case_headers: self.state.title_case_headers,
|
req_method: &mut self.state.method,
|
||||||
}, buf) {
|
title_case_headers: self.state.title_case_headers,
|
||||||
|
},
|
||||||
|
buf,
|
||||||
|
) {
|
||||||
Ok(encoder) => {
|
Ok(encoder) => {
|
||||||
debug_assert!(self.state.cached_headers.is_none());
|
debug_assert!(self.state.cached_headers.is_none());
|
||||||
debug_assert!(head.headers.is_empty());
|
debug_assert!(head.headers.is_empty());
|
||||||
self.state.cached_headers = Some(head.headers);
|
self.state.cached_headers = Some(head.headers);
|
||||||
Some(encoder)
|
Some(encoder)
|
||||||
},
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
self.state.error = Some(err);
|
self.state.error = Some(err);
|
||||||
self.state.writing = Writing::Closed;
|
self.state.writing = Writing::Closed;
|
||||||
None
|
None
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -478,10 +497,12 @@ where I: AsyncRead + AsyncWrite + Unpin,
|
|||||||
Version::HTTP_10 => self.state.disable_keep_alive(),
|
Version::HTTP_10 => self.state.disable_keep_alive(),
|
||||||
// If response is version 1.1 and keep-alive is wanted, add
|
// If response is version 1.1 and keep-alive is wanted, add
|
||||||
// Connection: keep-alive header when not present
|
// Connection: keep-alive header when not present
|
||||||
Version::HTTP_11 => if self.state.wants_keep_alive() {
|
Version::HTTP_11 => {
|
||||||
head.headers
|
if self.state.wants_keep_alive() {
|
||||||
.insert(CONNECTION, HeaderValue::from_static("keep-alive"));
|
head.headers
|
||||||
},
|
.insert(CONNECTION, HeaderValue::from_static("keep-alive"));
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -490,7 +511,6 @@ where I: AsyncRead + AsyncWrite + Unpin,
|
|||||||
// If we know the remote speaks an older version, we try to fix up any messages
|
// If we know the remote speaks an older version, we try to fix up any messages
|
||||||
// to work with our older peer.
|
// to work with our older peer.
|
||||||
fn enforce_version(&mut self, head: &mut MessageHead<T::Outgoing>) {
|
fn enforce_version(&mut self, head: &mut MessageHead<T::Outgoing>) {
|
||||||
|
|
||||||
match self.state.version {
|
match self.state.version {
|
||||||
Version::HTTP_10 => {
|
Version::HTTP_10 => {
|
||||||
// Fixes response or connection when keep-alive header is not present
|
// Fixes response or connection when keep-alive header is not present
|
||||||
@@ -498,7 +518,7 @@ where I: AsyncRead + AsyncWrite + Unpin,
|
|||||||
// If the remote only knows HTTP/1.0, we should force ourselves
|
// If the remote only knows HTTP/1.0, we should force ourselves
|
||||||
// to do only speak HTTP/1.0 as well.
|
// to do only speak HTTP/1.0 as well.
|
||||||
head.version = Version::HTTP_10;
|
head.version = Version::HTTP_10;
|
||||||
},
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// If the remote speaks HTTP/1.1, then it *should* be fine with
|
// If the remote speaks HTTP/1.1, then it *should* be fine with
|
||||||
// both HTTP/1.0 and HTTP/1.1 from us. So again, we just let
|
// both HTTP/1.0 and HTTP/1.1 from us. So again, we just let
|
||||||
@@ -525,7 +545,7 @@ where I: AsyncRead + AsyncWrite + Unpin,
|
|||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
_ => unreachable!("write_body invalid state: {:?}", self.state.writing),
|
_ => unreachable!("write_body invalid state: {:?}", self.state.writing),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -545,7 +565,7 @@ where I: AsyncRead + AsyncWrite + Unpin,
|
|||||||
} else {
|
} else {
|
||||||
Writing::Closed
|
Writing::Closed
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
_ => unreachable!("write_body invalid state: {:?}", self.state.writing),
|
_ => unreachable!("write_body invalid state: {:?}", self.state.writing),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -568,15 +588,14 @@ where I: AsyncRead + AsyncWrite + Unpin,
|
|||||||
} else {
|
} else {
|
||||||
Writing::KeepAlive
|
Writing::KeepAlive
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
Err(_not_eof) => Writing::Closed,
|
Err(_not_eof) => Writing::Closed,
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.state.writing = state;
|
self.state.writing = state;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// When we get a parse error, depending on what side we are, we might be able
|
// When we get a parse error, depending on what side we are, we might be able
|
||||||
@@ -585,11 +604,10 @@ where I: AsyncRead + AsyncWrite + Unpin,
|
|||||||
// - Client: there is nothing we can do
|
// - Client: there is nothing we can do
|
||||||
// - Server: if Response hasn't been written yet, we can send a 4xx response
|
// - Server: if Response hasn't been written yet, we can send a 4xx response
|
||||||
fn on_parse_error(&mut self, err: crate::Error) -> crate::Result<()> {
|
fn on_parse_error(&mut self, err: crate::Error) -> crate::Result<()> {
|
||||||
|
|
||||||
match self.state.writing {
|
match self.state.writing {
|
||||||
Writing::Init => {
|
Writing::Init => {
|
||||||
if self.has_h2_prefix() {
|
if self.has_h2_prefix() {
|
||||||
return Err(crate::Error::new_version_h2())
|
return Err(crate::Error::new_version_h2());
|
||||||
}
|
}
|
||||||
if let Some(msg) = T::on_error(&err) {
|
if let Some(msg) = T::on_error(&err) {
|
||||||
// Drop the cached headers so as to not trigger a debug
|
// Drop the cached headers so as to not trigger a debug
|
||||||
@@ -619,7 +637,7 @@ where I: AsyncRead + AsyncWrite + Unpin,
|
|||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
trace!("shut down IO complete");
|
trace!("shut down IO complete");
|
||||||
Poll::Ready(Ok(()))
|
Poll::Ready(Ok(()))
|
||||||
},
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
debug!("error shutting down IO: {}", e);
|
debug!("error shutting down IO: {}", e);
|
||||||
Poll::Ready(Err(e))
|
Poll::Ready(Err(e))
|
||||||
@@ -741,9 +759,7 @@ impl fmt::Debug for Writing {
|
|||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match *self {
|
match *self {
|
||||||
Writing::Init => f.write_str("Init"),
|
Writing::Init => f.write_str("Init"),
|
||||||
Writing::Body(ref enc) => f.debug_tuple("Body")
|
Writing::Body(ref enc) => f.debug_tuple("Body").field(enc).finish(),
|
||||||
.field(enc)
|
|
||||||
.finish(),
|
|
||||||
Writing::KeepAlive => f.write_str("KeepAlive"),
|
Writing::KeepAlive => f.write_str("KeepAlive"),
|
||||||
Writing::Closed => f.write_str("Closed"),
|
Writing::Closed => f.write_str("Closed"),
|
||||||
}
|
}
|
||||||
@@ -824,15 +840,18 @@ impl State {
|
|||||||
if let KA::Busy = self.keep_alive.status() {
|
if let KA::Busy = self.keep_alive.status() {
|
||||||
self.idle::<T>();
|
self.idle::<T>();
|
||||||
} else {
|
} else {
|
||||||
trace!("try_keep_alive({}): could keep-alive, but status = {:?}", T::LOG, self.keep_alive);
|
trace!(
|
||||||
|
"try_keep_alive({}): could keep-alive, but status = {:?}",
|
||||||
|
T::LOG,
|
||||||
|
self.keep_alive
|
||||||
|
);
|
||||||
self.close();
|
self.close();
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
(&Reading::Closed, &Writing::KeepAlive) |
|
(&Reading::Closed, &Writing::KeepAlive) | (&Reading::KeepAlive, &Writing::Closed) => {
|
||||||
(&Reading::KeepAlive, &Writing::Closed) => {
|
|
||||||
self.close()
|
self.close()
|
||||||
}
|
}
|
||||||
_ => ()
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -880,14 +899,14 @@ impl State {
|
|||||||
fn is_read_closed(&self) -> bool {
|
fn is_read_closed(&self) -> bool {
|
||||||
match self.reading {
|
match self.reading {
|
||||||
Reading::Closed => true,
|
Reading::Closed => true,
|
||||||
_ => false
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_write_closed(&self) -> bool {
|
fn is_write_closed(&self) -> bool {
|
||||||
match self.writing {
|
match self.writing {
|
||||||
Writing::Closed => true,
|
Writing::Closed => true,
|
||||||
_ => false
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -929,11 +948,10 @@ mod tests {
|
|||||||
let mut headers = x.0.headers;
|
let mut headers = x.0.headers;
|
||||||
headers.clear();
|
headers.clear();
|
||||||
conn.state.cached_headers = Some(headers);
|
conn.state.cached_headers = Some(headers);
|
||||||
},
|
}
|
||||||
f => panic!("expected Ready(Some(Ok(..))): {:?}", f)
|
f => panic!("expected Ready(Some(Ok(..))): {:?}", f),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
conn.io.read_buf_mut().reserve(1);
|
conn.io.read_buf_mut().reserve(1);
|
||||||
unsafe {
|
unsafe {
|
||||||
conn.io.read_buf_mut().set_len(len);
|
conn.io.read_buf_mut().set_len(len);
|
||||||
|
|||||||
@@ -24,8 +24,7 @@ pub(crate) fn update_and_header_value() -> HeaderValue {
|
|||||||
CACHED.with(|cache| {
|
CACHED.with(|cache| {
|
||||||
let mut cache = cache.borrow_mut();
|
let mut cache = cache.borrow_mut();
|
||||||
cache.check();
|
cache.check();
|
||||||
HeaderValue::from_bytes(cache.buffer())
|
HeaderValue::from_bytes(cache.buffer()).expect("Date format should be valid HeaderValue")
|
||||||
.expect("Date format should be valid HeaderValue")
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
use std::error::Error as StdError;
|
use std::error::Error as StdError;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::usize;
|
|
||||||
use std::io;
|
use std::io;
|
||||||
|
use std::usize;
|
||||||
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
|
|
||||||
use crate::common::{Poll, task};
|
use crate::common::{task, Poll};
|
||||||
|
|
||||||
use super::io::MemRead;
|
use super::io::MemRead;
|
||||||
use super::{DecodedLength};
|
use super::DecodedLength;
|
||||||
|
|
||||||
use self::Kind::{Length, Chunked, Eof};
|
use self::Kind::{Chunked, Eof, Length};
|
||||||
|
|
||||||
/// Decoders to handle different Transfer-Encodings.
|
/// Decoders to handle different Transfer-Encodings.
|
||||||
///
|
///
|
||||||
@@ -64,15 +64,21 @@ impl Decoder {
|
|||||||
// constructors
|
// constructors
|
||||||
|
|
||||||
pub fn length(x: u64) -> Decoder {
|
pub fn length(x: u64) -> Decoder {
|
||||||
Decoder { kind: Kind::Length(x) }
|
Decoder {
|
||||||
|
kind: Kind::Length(x),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn chunked() -> Decoder {
|
pub fn chunked() -> Decoder {
|
||||||
Decoder { kind: Kind::Chunked(ChunkedState::Size, 0) }
|
Decoder {
|
||||||
|
kind: Kind::Chunked(ChunkedState::Size, 0),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn eof() -> Decoder {
|
pub fn eof() -> Decoder {
|
||||||
Decoder { kind: Kind::Eof(false) }
|
Decoder {
|
||||||
|
kind: Kind::Eof(false),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn new(len: DecodedLength) -> Self {
|
pub(super) fn new(len: DecodedLength) -> Self {
|
||||||
@@ -87,14 +93,16 @@ impl Decoder {
|
|||||||
|
|
||||||
pub fn is_eof(&self) -> bool {
|
pub fn is_eof(&self) -> bool {
|
||||||
match self.kind {
|
match self.kind {
|
||||||
Length(0) |
|
Length(0) | Chunked(ChunkedState::End, _) | Eof(true) => true,
|
||||||
Chunked(ChunkedState::End, _) |
|
|
||||||
Eof(true) => true,
|
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn decode<R: MemRead>(&mut self, cx: &mut task::Context<'_>, body: &mut R) -> Poll<Result<Bytes, io::Error>> {
|
pub fn decode<R: MemRead>(
|
||||||
|
&mut self,
|
||||||
|
cx: &mut task::Context<'_>,
|
||||||
|
body: &mut R,
|
||||||
|
) -> Poll<Result<Bytes, io::Error>> {
|
||||||
trace!("decode; state={:?}", self.kind);
|
trace!("decode; state={:?}", self.kind);
|
||||||
match self.kind {
|
match self.kind {
|
||||||
Length(ref mut remaining) => {
|
Length(ref mut remaining) => {
|
||||||
@@ -107,7 +115,10 @@ impl Decoder {
|
|||||||
if num > *remaining {
|
if num > *remaining {
|
||||||
*remaining = 0;
|
*remaining = 0;
|
||||||
} else if num == 0 {
|
} else if num == 0 {
|
||||||
return Poll::Ready(Err(io::Error::new(io::ErrorKind::UnexpectedEof, IncompleteBody)));
|
return Poll::Ready(Err(io::Error::new(
|
||||||
|
io::ErrorKind::UnexpectedEof,
|
||||||
|
IncompleteBody,
|
||||||
|
)));
|
||||||
} else {
|
} else {
|
||||||
*remaining -= num;
|
*remaining -= num;
|
||||||
}
|
}
|
||||||
@@ -146,13 +157,10 @@ impl Decoder {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
async fn decode_fut<R: MemRead>(&mut self, body: &mut R) -> Result<Bytes, io::Error> {
|
async fn decode_fut<R: MemRead>(&mut self, body: &mut R) -> Result<Bytes, io::Error> {
|
||||||
futures_util::future::poll_fn(move |cx| {
|
futures_util::future::poll_fn(move |cx| self.decode(cx, body)).await
|
||||||
self.decode(cx, body)
|
|
||||||
}).await
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl fmt::Debug for Decoder {
|
impl fmt::Debug for Decoder {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
fmt::Debug::fmt(&self.kind, f)
|
fmt::Debug::fmt(&self.kind, f)
|
||||||
@@ -172,12 +180,13 @@ macro_rules! byte (
|
|||||||
);
|
);
|
||||||
|
|
||||||
impl ChunkedState {
|
impl ChunkedState {
|
||||||
fn step<R: MemRead>(&self,
|
fn step<R: MemRead>(
|
||||||
cx: &mut task::Context<'_>,
|
&self,
|
||||||
body: &mut R,
|
cx: &mut task::Context<'_>,
|
||||||
size: &mut u64,
|
body: &mut R,
|
||||||
buf: &mut Option<Bytes>)
|
size: &mut u64,
|
||||||
-> Poll<Result<ChunkedState, io::Error>> {
|
buf: &mut Option<Bytes>,
|
||||||
|
) -> Poll<Result<ChunkedState, io::Error>> {
|
||||||
use self::ChunkedState::*;
|
use self::ChunkedState::*;
|
||||||
match *self {
|
match *self {
|
||||||
Size => ChunkedState::read_size(cx, body, size),
|
Size => ChunkedState::read_size(cx, body, size),
|
||||||
@@ -192,7 +201,11 @@ impl ChunkedState {
|
|||||||
End => Poll::Ready(Ok(ChunkedState::End)),
|
End => Poll::Ready(Ok(ChunkedState::End)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn read_size<R: MemRead>(cx: &mut task::Context<'_>, rdr: &mut R, size: &mut u64) -> Poll<Result<ChunkedState, io::Error>> {
|
fn read_size<R: MemRead>(
|
||||||
|
cx: &mut task::Context<'_>,
|
||||||
|
rdr: &mut R,
|
||||||
|
size: &mut u64,
|
||||||
|
) -> Poll<Result<ChunkedState, io::Error>> {
|
||||||
trace!("Read chunk hex size");
|
trace!("Read chunk hex size");
|
||||||
let radix = 16;
|
let radix = 16;
|
||||||
match byte!(rdr, cx) {
|
match byte!(rdr, cx) {
|
||||||
@@ -212,33 +225,45 @@ impl ChunkedState {
|
|||||||
b';' => return Poll::Ready(Ok(ChunkedState::Extension)),
|
b';' => return Poll::Ready(Ok(ChunkedState::Extension)),
|
||||||
b'\r' => return Poll::Ready(Ok(ChunkedState::SizeLf)),
|
b'\r' => return Poll::Ready(Ok(ChunkedState::SizeLf)),
|
||||||
_ => {
|
_ => {
|
||||||
return Poll::Ready(Err(io::Error::new(io::ErrorKind::InvalidInput,
|
return Poll::Ready(Err(io::Error::new(
|
||||||
"Invalid chunk size line: Invalid Size")));
|
io::ErrorKind::InvalidInput,
|
||||||
|
"Invalid chunk size line: Invalid Size",
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Poll::Ready(Ok(ChunkedState::Size))
|
Poll::Ready(Ok(ChunkedState::Size))
|
||||||
}
|
}
|
||||||
fn read_size_lws<R: MemRead>(cx: &mut task::Context<'_>, rdr: &mut R) -> Poll<Result<ChunkedState, io::Error>> {
|
fn read_size_lws<R: MemRead>(
|
||||||
|
cx: &mut task::Context<'_>,
|
||||||
|
rdr: &mut R,
|
||||||
|
) -> Poll<Result<ChunkedState, io::Error>> {
|
||||||
trace!("read_size_lws");
|
trace!("read_size_lws");
|
||||||
match byte!(rdr, cx) {
|
match byte!(rdr, cx) {
|
||||||
// LWS can follow the chunk size, but no more digits can come
|
// LWS can follow the chunk size, but no more digits can come
|
||||||
b'\t' | b' ' => Poll::Ready(Ok(ChunkedState::SizeLws)),
|
b'\t' | b' ' => Poll::Ready(Ok(ChunkedState::SizeLws)),
|
||||||
b';' => Poll::Ready(Ok(ChunkedState::Extension)),
|
b';' => Poll::Ready(Ok(ChunkedState::Extension)),
|
||||||
b'\r' => Poll::Ready(Ok(ChunkedState::SizeLf)),
|
b'\r' => Poll::Ready(Ok(ChunkedState::SizeLf)),
|
||||||
_ => {
|
_ => Poll::Ready(Err(io::Error::new(
|
||||||
Poll::Ready(Err(io::Error::new(io::ErrorKind::InvalidInput,
|
io::ErrorKind::InvalidInput,
|
||||||
"Invalid chunk size linear white space")))
|
"Invalid chunk size linear white space",
|
||||||
}
|
))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn read_extension<R: MemRead>(cx: &mut task::Context<'_>, rdr: &mut R) -> Poll<Result<ChunkedState, io::Error>> {
|
fn read_extension<R: MemRead>(
|
||||||
|
cx: &mut task::Context<'_>,
|
||||||
|
rdr: &mut R,
|
||||||
|
) -> Poll<Result<ChunkedState, io::Error>> {
|
||||||
trace!("read_extension");
|
trace!("read_extension");
|
||||||
match byte!(rdr, cx) {
|
match byte!(rdr, cx) {
|
||||||
b'\r' => Poll::Ready(Ok(ChunkedState::SizeLf)),
|
b'\r' => Poll::Ready(Ok(ChunkedState::SizeLf)),
|
||||||
_ => Poll::Ready(Ok(ChunkedState::Extension)), // no supported extensions
|
_ => Poll::Ready(Ok(ChunkedState::Extension)), // no supported extensions
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn read_size_lf<R: MemRead>(cx: &mut task::Context<'_>, rdr: &mut R, size: u64) -> Poll<Result<ChunkedState, io::Error>> {
|
fn read_size_lf<R: MemRead>(
|
||||||
|
cx: &mut task::Context<'_>,
|
||||||
|
rdr: &mut R,
|
||||||
|
size: u64,
|
||||||
|
) -> Poll<Result<ChunkedState, io::Error>> {
|
||||||
trace!("Chunk size is {:?}", size);
|
trace!("Chunk size is {:?}", size);
|
||||||
match byte!(rdr, cx) {
|
match byte!(rdr, cx) {
|
||||||
b'\n' => {
|
b'\n' => {
|
||||||
@@ -248,15 +273,20 @@ impl ChunkedState {
|
|||||||
debug!("incoming chunked header: {0:#X} ({0} bytes)", size);
|
debug!("incoming chunked header: {0:#X} ({0} bytes)", size);
|
||||||
Poll::Ready(Ok(ChunkedState::Body))
|
Poll::Ready(Ok(ChunkedState::Body))
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
_ => Poll::Ready(Err(io::Error::new(io::ErrorKind::InvalidInput, "Invalid chunk size LF"))),
|
_ => Poll::Ready(Err(io::Error::new(
|
||||||
|
io::ErrorKind::InvalidInput,
|
||||||
|
"Invalid chunk size LF",
|
||||||
|
))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_body<R: MemRead>(cx: &mut task::Context<'_>, rdr: &mut R,
|
fn read_body<R: MemRead>(
|
||||||
rem: &mut u64,
|
cx: &mut task::Context<'_>,
|
||||||
buf: &mut Option<Bytes>)
|
rdr: &mut R,
|
||||||
-> Poll<Result<ChunkedState, io::Error>> {
|
rem: &mut u64,
|
||||||
|
buf: &mut Option<Bytes>,
|
||||||
|
) -> Poll<Result<ChunkedState, io::Error>> {
|
||||||
trace!("Chunked read, remaining={:?}", rem);
|
trace!("Chunked read, remaining={:?}", rem);
|
||||||
|
|
||||||
// cap remaining bytes at the max capacity of usize
|
// cap remaining bytes at the max capacity of usize
|
||||||
@@ -271,7 +301,10 @@ impl ChunkedState {
|
|||||||
|
|
||||||
if count == 0 {
|
if count == 0 {
|
||||||
*rem = 0;
|
*rem = 0;
|
||||||
return Poll::Ready(Err(io::Error::new(io::ErrorKind::UnexpectedEof, IncompleteBody)));
|
return Poll::Ready(Err(io::Error::new(
|
||||||
|
io::ErrorKind::UnexpectedEof,
|
||||||
|
IncompleteBody,
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
*buf = Some(slice);
|
*buf = Some(slice);
|
||||||
*rem -= count as u64;
|
*rem -= count as u64;
|
||||||
@@ -282,29 +315,53 @@ impl ChunkedState {
|
|||||||
Poll::Ready(Ok(ChunkedState::BodyCr))
|
Poll::Ready(Ok(ChunkedState::BodyCr))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn read_body_cr<R: MemRead>(cx: &mut task::Context<'_>, rdr: &mut R) -> Poll<Result<ChunkedState, io::Error>> {
|
fn read_body_cr<R: MemRead>(
|
||||||
|
cx: &mut task::Context<'_>,
|
||||||
|
rdr: &mut R,
|
||||||
|
) -> Poll<Result<ChunkedState, io::Error>> {
|
||||||
match byte!(rdr, cx) {
|
match byte!(rdr, cx) {
|
||||||
b'\r' => Poll::Ready(Ok(ChunkedState::BodyLf)),
|
b'\r' => Poll::Ready(Ok(ChunkedState::BodyLf)),
|
||||||
_ => Poll::Ready(Err(io::Error::new(io::ErrorKind::InvalidInput, "Invalid chunk body CR"))),
|
_ => Poll::Ready(Err(io::Error::new(
|
||||||
|
io::ErrorKind::InvalidInput,
|
||||||
|
"Invalid chunk body CR",
|
||||||
|
))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn read_body_lf<R: MemRead>(cx: &mut task::Context<'_>, rdr: &mut R) -> Poll<Result<ChunkedState, io::Error>> {
|
fn read_body_lf<R: MemRead>(
|
||||||
|
cx: &mut task::Context<'_>,
|
||||||
|
rdr: &mut R,
|
||||||
|
) -> Poll<Result<ChunkedState, io::Error>> {
|
||||||
match byte!(rdr, cx) {
|
match byte!(rdr, cx) {
|
||||||
b'\n' => Poll::Ready(Ok(ChunkedState::Size)),
|
b'\n' => Poll::Ready(Ok(ChunkedState::Size)),
|
||||||
_ => Poll::Ready(Err(io::Error::new(io::ErrorKind::InvalidInput, "Invalid chunk body LF"))),
|
_ => Poll::Ready(Err(io::Error::new(
|
||||||
|
io::ErrorKind::InvalidInput,
|
||||||
|
"Invalid chunk body LF",
|
||||||
|
))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_end_cr<R: MemRead>(cx: &mut task::Context<'_>, rdr: &mut R) -> Poll<Result<ChunkedState, io::Error>> {
|
fn read_end_cr<R: MemRead>(
|
||||||
|
cx: &mut task::Context<'_>,
|
||||||
|
rdr: &mut R,
|
||||||
|
) -> Poll<Result<ChunkedState, io::Error>> {
|
||||||
match byte!(rdr, cx) {
|
match byte!(rdr, cx) {
|
||||||
b'\r' => Poll::Ready(Ok(ChunkedState::EndLf)),
|
b'\r' => Poll::Ready(Ok(ChunkedState::EndLf)),
|
||||||
_ => Poll::Ready(Err(io::Error::new(io::ErrorKind::InvalidInput, "Invalid chunk end CR"))),
|
_ => Poll::Ready(Err(io::Error::new(
|
||||||
|
io::ErrorKind::InvalidInput,
|
||||||
|
"Invalid chunk end CR",
|
||||||
|
))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn read_end_lf<R: MemRead>(cx: &mut task::Context<'_>, rdr: &mut R) -> Poll<Result<ChunkedState, io::Error>> {
|
fn read_end_lf<R: MemRead>(
|
||||||
|
cx: &mut task::Context<'_>,
|
||||||
|
rdr: &mut R,
|
||||||
|
) -> Poll<Result<ChunkedState, io::Error>> {
|
||||||
match byte!(rdr, cx) {
|
match byte!(rdr, cx) {
|
||||||
b'\n' => Poll::Ready(Ok(ChunkedState::End)),
|
b'\n' => Poll::Ready(Ok(ChunkedState::End)),
|
||||||
_ => Poll::Ready(Err(io::Error::new(io::ErrorKind::InvalidInput, "Invalid chunk end LF"))),
|
_ => Poll::Ready(Err(io::Error::new(
|
||||||
|
io::ErrorKind::InvalidInput,
|
||||||
|
"Invalid chunk end LF",
|
||||||
|
))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -326,10 +383,10 @@ impl StdError for IncompleteBody {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::time::Duration;
|
|
||||||
use std::pin::Pin;
|
|
||||||
use tokio::io::AsyncRead;
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use std::pin::Pin;
|
||||||
|
use std::time::Duration;
|
||||||
|
use tokio::io::AsyncRead;
|
||||||
|
|
||||||
impl<'a> MemRead for &'a [u8] {
|
impl<'a> MemRead for &'a [u8] {
|
||||||
fn read_mem(&mut self, _: &mut task::Context<'_>, len: usize) -> Poll<io::Result<Bytes>> {
|
fn read_mem(&mut self, _: &mut task::Context<'_>, len: usize) -> Poll<io::Result<Bytes>> {
|
||||||
@@ -363,19 +420,18 @@ mod tests {
|
|||||||
use crate::mock::AsyncIo;
|
use crate::mock::AsyncIo;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_read_chunk_size() {
|
async fn test_read_chunk_size() {
|
||||||
use std::io::ErrorKind::{UnexpectedEof, InvalidInput};
|
use std::io::ErrorKind::{InvalidInput, UnexpectedEof};
|
||||||
|
|
||||||
async fn read(s: &str) -> u64 {
|
async fn read(s: &str) -> u64 {
|
||||||
let mut state = ChunkedState::Size;
|
let mut state = ChunkedState::Size;
|
||||||
let rdr = &mut s.as_bytes();
|
let rdr = &mut s.as_bytes();
|
||||||
let mut size = 0;
|
let mut size = 0;
|
||||||
loop {
|
loop {
|
||||||
let result = futures_util::future::poll_fn(|cx| {
|
let result =
|
||||||
state.step(cx, rdr, &mut size, &mut None)
|
futures_util::future::poll_fn(|cx| state.step(cx, rdr, &mut size, &mut None))
|
||||||
}).await;
|
.await;
|
||||||
let desc = format!("read_size failed for {:?}", s);
|
let desc = format!("read_size failed for {:?}", s);
|
||||||
state = result.expect(desc.as_str());
|
state = result.expect(desc.as_str());
|
||||||
if state == ChunkedState::Body || state == ChunkedState::EndCr {
|
if state == ChunkedState::Body || state == ChunkedState::EndCr {
|
||||||
@@ -390,14 +446,19 @@ mod tests {
|
|||||||
let rdr = &mut s.as_bytes();
|
let rdr = &mut s.as_bytes();
|
||||||
let mut size = 0;
|
let mut size = 0;
|
||||||
loop {
|
loop {
|
||||||
let result = futures_util::future::poll_fn(|cx| {
|
let result =
|
||||||
state.step(cx, rdr, &mut size, &mut None)
|
futures_util::future::poll_fn(|cx| state.step(cx, rdr, &mut size, &mut None))
|
||||||
}).await;
|
.await;
|
||||||
state = match result {
|
state = match result {
|
||||||
Ok(s) => s,
|
Ok(s) => s,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
assert!(expected_err == e.kind(), "Reading {:?}, expected {:?}, but got {:?}",
|
assert!(
|
||||||
s, expected_err, e.kind());
|
expected_err == e.kind(),
|
||||||
|
"Reading {:?}, expected {:?}, but got {:?}",
|
||||||
|
s,
|
||||||
|
expected_err,
|
||||||
|
e.kind()
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -462,7 +523,10 @@ mod tests {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_read_chunked_single_read() {
|
async fn test_read_chunked_single_read() {
|
||||||
let mut mock_buf = &b"10\r\n1234567890abcdef\r\n0\r\n"[..];
|
let mut mock_buf = &b"10\r\n1234567890abcdef\r\n0\r\n"[..];
|
||||||
let buf = Decoder::chunked().decode_fut(&mut mock_buf).await.expect("decode");
|
let buf = Decoder::chunked()
|
||||||
|
.decode_fut(&mut mock_buf)
|
||||||
|
.await
|
||||||
|
.expect("decode");
|
||||||
assert_eq!(16, buf.len());
|
assert_eq!(16, buf.len());
|
||||||
let result = String::from_utf8(buf.as_ref().to_vec()).expect("decode String");
|
let result = String::from_utf8(buf.as_ref().to_vec()).expect("decode String");
|
||||||
assert_eq!("1234567890abcdef", &result);
|
assert_eq!("1234567890abcdef", &result);
|
||||||
@@ -490,10 +554,7 @@ mod tests {
|
|||||||
|
|
||||||
// perform an async read using a custom buffer size and causing a blocking
|
// perform an async read using a custom buffer size and causing a blocking
|
||||||
// read at the specified byte
|
// read at the specified byte
|
||||||
async fn read_async(mut decoder: Decoder,
|
async fn read_async(mut decoder: Decoder, content: &[u8], block_at: usize) -> String {
|
||||||
content: &[u8],
|
|
||||||
block_at: usize)
|
|
||||||
-> String {
|
|
||||||
let mut outs = Vec::new();
|
let mut outs = Vec::new();
|
||||||
|
|
||||||
let mut ins = if block_at == 0 {
|
let mut ins = if block_at == 0 {
|
||||||
|
|||||||
@@ -4,10 +4,13 @@ use bytes::{Buf, Bytes};
|
|||||||
use http::{Request, Response, StatusCode};
|
use http::{Request, Response, StatusCode};
|
||||||
use tokio::io::{AsyncRead, AsyncWrite};
|
use tokio::io::{AsyncRead, AsyncWrite};
|
||||||
|
|
||||||
use crate::body::{Body, Payload};
|
|
||||||
use crate::common::{Future, Never, Poll, Pin, Unpin, task};
|
|
||||||
use crate::proto::{BodyLength, DecodedLength, Conn, Dispatched, MessageHead, RequestHead, RequestLine, ResponseHead};
|
|
||||||
use super::Http1Transaction;
|
use super::Http1Transaction;
|
||||||
|
use crate::body::{Body, Payload};
|
||||||
|
use crate::common::{task, Future, Never, Pin, Poll, Unpin};
|
||||||
|
use crate::proto::{
|
||||||
|
BodyLength, Conn, DecodedLength, Dispatched, MessageHead, RequestHead, RequestLine,
|
||||||
|
ResponseHead,
|
||||||
|
};
|
||||||
use crate::service::HttpService;
|
use crate::service::HttpService;
|
||||||
|
|
||||||
pub(crate) struct Dispatcher<D, Bs: Payload, I, T> {
|
pub(crate) struct Dispatcher<D, Bs: Payload, I, T> {
|
||||||
@@ -23,7 +26,10 @@ pub(crate) trait Dispatch {
|
|||||||
type PollBody;
|
type PollBody;
|
||||||
type PollError;
|
type PollError;
|
||||||
type RecvItem;
|
type RecvItem;
|
||||||
fn poll_msg(&mut self, cx: &mut task::Context<'_>) -> Poll<Option<Result<(Self::PollItem, Self::PollBody), Self::PollError>>>;
|
fn poll_msg(
|
||||||
|
&mut self,
|
||||||
|
cx: &mut task::Context<'_>,
|
||||||
|
) -> Poll<Option<Result<(Self::PollItem, Self::PollBody), Self::PollError>>>;
|
||||||
fn recv_msg(&mut self, msg: crate::Result<(Self::RecvItem, Body)>) -> crate::Result<()>;
|
fn recv_msg(&mut self, msg: crate::Result<(Self::RecvItem, Body)>) -> crate::Result<()>;
|
||||||
fn poll_ready(&mut self, cx: &mut task::Context<'_>) -> Poll<Result<(), ()>>;
|
fn poll_ready(&mut self, cx: &mut task::Context<'_>) -> Poll<Result<(), ()>>;
|
||||||
fn should_poll(&self) -> bool;
|
fn should_poll(&self) -> bool;
|
||||||
@@ -44,7 +50,11 @@ type ClientRx<B> = crate::client::dispatch::Receiver<Request<B>, Response<Body>>
|
|||||||
|
|
||||||
impl<D, Bs, I, T> Dispatcher<D, Bs, I, T>
|
impl<D, Bs, I, T> Dispatcher<D, Bs, I, T>
|
||||||
where
|
where
|
||||||
D: Dispatch<PollItem=MessageHead<T::Outgoing>, PollBody=Bs, RecvItem=MessageHead<T::Incoming>> + Unpin,
|
D: Dispatch<
|
||||||
|
PollItem = MessageHead<T::Outgoing>,
|
||||||
|
PollBody = Bs,
|
||||||
|
RecvItem = MessageHead<T::Incoming>,
|
||||||
|
> + Unpin,
|
||||||
D::PollError: Into<Box<dyn StdError + Send + Sync>>,
|
D::PollError: Into<Box<dyn StdError + Send + Sync>>,
|
||||||
I: AsyncRead + AsyncWrite + Unpin,
|
I: AsyncRead + AsyncWrite + Unpin,
|
||||||
T: Http1Transaction + Unpin,
|
T: Http1Transaction + Unpin,
|
||||||
@@ -77,7 +87,10 @@ where
|
|||||||
///
|
///
|
||||||
/// This is useful for old-style HTTP upgrades, but ignores
|
/// This is useful for old-style HTTP upgrades, but ignores
|
||||||
/// newer-style upgrade API.
|
/// newer-style upgrade API.
|
||||||
pub(crate) fn poll_without_shutdown(&mut self, cx: &mut task::Context<'_>) -> Poll<crate::Result<()>>
|
pub(crate) fn poll_without_shutdown(
|
||||||
|
&mut self,
|
||||||
|
cx: &mut task::Context<'_>,
|
||||||
|
) -> Poll<crate::Result<()>>
|
||||||
where
|
where
|
||||||
Self: Unpin,
|
Self: Unpin,
|
||||||
{
|
{
|
||||||
@@ -88,7 +101,11 @@ where
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll_catch(&mut self, cx: &mut task::Context<'_>, should_shutdown: bool) -> Poll<crate::Result<Dispatched>> {
|
fn poll_catch(
|
||||||
|
&mut self,
|
||||||
|
cx: &mut task::Context<'_>,
|
||||||
|
should_shutdown: bool,
|
||||||
|
) -> Poll<crate::Result<Dispatched>> {
|
||||||
Poll::Ready(ready!(self.poll_inner(cx, should_shutdown)).or_else(|e| {
|
Poll::Ready(ready!(self.poll_inner(cx, should_shutdown)).or_else(|e| {
|
||||||
// An error means we're shutting down either way.
|
// An error means we're shutting down either way.
|
||||||
// We just try to give the error to the user,
|
// We just try to give the error to the user,
|
||||||
@@ -99,7 +116,11 @@ where
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll_inner(&mut self, cx: &mut task::Context<'_>, should_shutdown: bool) -> Poll<crate::Result<Dispatched>> {
|
fn poll_inner(
|
||||||
|
&mut self,
|
||||||
|
cx: &mut task::Context<'_>,
|
||||||
|
should_shutdown: bool,
|
||||||
|
) -> Poll<crate::Result<Dispatched>> {
|
||||||
T::update_date();
|
T::update_date();
|
||||||
|
|
||||||
ready!(self.poll_loop(cx))?;
|
ready!(self.poll_loop(cx))?;
|
||||||
@@ -161,7 +182,7 @@ where
|
|||||||
Poll::Pending => {
|
Poll::Pending => {
|
||||||
self.body_tx = Some(body);
|
self.body_tx = Some(body);
|
||||||
return Poll::Pending;
|
return Poll::Pending;
|
||||||
},
|
}
|
||||||
Poll::Ready(Err(_canceled)) => {
|
Poll::Ready(Err(_canceled)) => {
|
||||||
// user doesn't care about the body
|
// user doesn't care about the body
|
||||||
// so we should stop reading
|
// so we should stop reading
|
||||||
@@ -171,22 +192,20 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
match self.conn.poll_read_body(cx) {
|
match self.conn.poll_read_body(cx) {
|
||||||
Poll::Ready(Some(Ok(chunk))) => {
|
Poll::Ready(Some(Ok(chunk))) => match body.try_send_data(chunk) {
|
||||||
match body.try_send_data(chunk) {
|
Ok(()) => {
|
||||||
Ok(()) => {
|
self.body_tx = Some(body);
|
||||||
self.body_tx = Some(body);
|
}
|
||||||
},
|
Err(_canceled) => {
|
||||||
Err(_canceled) => {
|
if self.conn.can_read_body() {
|
||||||
if self.conn.can_read_body() {
|
trace!("body receiver dropped before eof, closing");
|
||||||
trace!("body receiver dropped before eof, closing");
|
self.conn.close_read();
|
||||||
self.conn.close_read();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Poll::Ready(None) => {
|
Poll::Ready(None) => {
|
||||||
// just drop, the body will close automatically
|
// just drop, the body will close automatically
|
||||||
},
|
}
|
||||||
Poll::Pending => {
|
Poll::Pending => {
|
||||||
self.body_tx = Some(body);
|
self.body_tx = Some(body);
|
||||||
return Poll::Pending;
|
return Poll::Pending;
|
||||||
@@ -223,14 +242,14 @@ where
|
|||||||
let (tx, rx) = Body::new_channel(other.into_opt());
|
let (tx, rx) = Body::new_channel(other.into_opt());
|
||||||
self.body_tx = Some(tx);
|
self.body_tx = Some(tx);
|
||||||
rx
|
rx
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
if wants_upgrade {
|
if wants_upgrade {
|
||||||
body.set_on_upgrade(self.conn.on_upgrade());
|
body.set_on_upgrade(self.conn.on_upgrade());
|
||||||
}
|
}
|
||||||
self.dispatch.recv_msg(Ok((head, body)))?;
|
self.dispatch.recv_msg(Ok((head, body)))?;
|
||||||
Poll::Ready(Ok(()))
|
Poll::Ready(Ok(()))
|
||||||
},
|
}
|
||||||
Some(Err(err)) => {
|
Some(Err(err)) => {
|
||||||
debug!("read_head error: {}", err);
|
debug!("read_head error: {}", err);
|
||||||
self.dispatch.recv_msg(Err(err))?;
|
self.dispatch.recv_msg(Err(err))?;
|
||||||
@@ -239,7 +258,7 @@ where
|
|||||||
// not as a second error.
|
// not as a second error.
|
||||||
self.close();
|
self.close();
|
||||||
Poll::Ready(Ok(()))
|
Poll::Ready(Ok(()))
|
||||||
},
|
}
|
||||||
None => {
|
None => {
|
||||||
// read eof, the write side will have been closed too unless
|
// read eof, the write side will have been closed too unless
|
||||||
// allow_read_close was set to true, in which case just do
|
// allow_read_close was set to true, in which case just do
|
||||||
@@ -257,7 +276,10 @@ where
|
|||||||
loop {
|
loop {
|
||||||
if self.is_closing {
|
if self.is_closing {
|
||||||
return Poll::Ready(Ok(()));
|
return Poll::Ready(Ok(()));
|
||||||
} else if self.body_rx.is_none() && self.conn.can_write_head() && self.dispatch.should_poll() {
|
} else if self.body_rx.is_none()
|
||||||
|
&& self.conn.can_write_head()
|
||||||
|
&& self.dispatch.should_poll()
|
||||||
|
{
|
||||||
if let Some(msg) = ready!(self.dispatch.poll_msg(cx)) {
|
if let Some(msg) = ready!(self.dispatch.poll_msg(cx)) {
|
||||||
let (head, mut body) = msg.map_err(crate::Error::new_user_service)?;
|
let (head, mut body) = msg.map_err(crate::Error::new_user_service)?;
|
||||||
|
|
||||||
@@ -274,7 +296,9 @@ where
|
|||||||
self.body_rx.set(None);
|
self.body_rx.set(None);
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
let btype = body.size_hint().exact()
|
let btype = body
|
||||||
|
.size_hint()
|
||||||
|
.exact()
|
||||||
.map(BodyLength::Known)
|
.map(BodyLength::Known)
|
||||||
.or_else(|| Some(BodyLength::Unknown));
|
.or_else(|| Some(BodyLength::Unknown));
|
||||||
self.body_rx.set(Some(body));
|
self.body_rx.set(Some(body));
|
||||||
@@ -289,7 +313,9 @@ where
|
|||||||
ready!(self.poll_flush(cx))?;
|
ready!(self.poll_flush(cx))?;
|
||||||
} else {
|
} else {
|
||||||
// A new scope is needed :(
|
// A new scope is needed :(
|
||||||
if let (Some(mut body), clear_body) = OptGuard::new(self.body_rx.as_mut()).guard_mut() {
|
if let (Some(mut body), clear_body) =
|
||||||
|
OptGuard::new(self.body_rx.as_mut()).guard_mut()
|
||||||
|
{
|
||||||
debug_assert!(!*clear_body, "opt guard defaults to keeping body");
|
debug_assert!(!*clear_body, "opt guard defaults to keeping body");
|
||||||
if !self.conn.can_write_body() {
|
if !self.conn.can_write_body() {
|
||||||
trace!(
|
trace!(
|
||||||
@@ -357,8 +383,8 @@ where
|
|||||||
// a client that cannot read may was well be done.
|
// a client that cannot read may was well be done.
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
let write_done = self.conn.is_write_closed() ||
|
let write_done = self.conn.is_write_closed()
|
||||||
(!self.dispatch.should_poll() && self.body_rx.is_none());
|
|| (!self.dispatch.should_poll() && self.body_rx.is_none());
|
||||||
read_done && write_done
|
read_done && write_done
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -366,7 +392,11 @@ where
|
|||||||
|
|
||||||
impl<D, Bs, I, T> Future for Dispatcher<D, Bs, I, T>
|
impl<D, Bs, I, T> Future for Dispatcher<D, Bs, I, T>
|
||||||
where
|
where
|
||||||
D: Dispatch<PollItem=MessageHead<T::Outgoing>, PollBody=Bs, RecvItem=MessageHead<T::Incoming>> + Unpin,
|
D: Dispatch<
|
||||||
|
PollItem = MessageHead<T::Outgoing>,
|
||||||
|
PollBody = Bs,
|
||||||
|
RecvItem = MessageHead<T::Incoming>,
|
||||||
|
> + Unpin,
|
||||||
D::PollError: Into<Box<dyn StdError + Send + Sync>>,
|
D::PollError: Into<Box<dyn StdError + Send + Sync>>,
|
||||||
I: AsyncRead + AsyncWrite + Unpin,
|
I: AsyncRead + AsyncWrite + Unpin,
|
||||||
T: Http1Transaction + Unpin,
|
T: Http1Transaction + Unpin,
|
||||||
@@ -427,7 +457,7 @@ impl<S: HttpService<B>, B> Unpin for Server<S, B> {}
|
|||||||
|
|
||||||
impl<S, Bs> Dispatch for Server<S, Body>
|
impl<S, Bs> Dispatch for Server<S, Body>
|
||||||
where
|
where
|
||||||
S: HttpService<Body, ResBody=Bs>,
|
S: HttpService<Body, ResBody = Bs>,
|
||||||
S::Error: Into<Box<dyn StdError + Send + Sync>>,
|
S::Error: Into<Box<dyn StdError + Send + Sync>>,
|
||||||
Bs: Payload,
|
Bs: Payload,
|
||||||
{
|
{
|
||||||
@@ -436,7 +466,10 @@ where
|
|||||||
type PollError = S::Error;
|
type PollError = S::Error;
|
||||||
type RecvItem = RequestHead;
|
type RecvItem = RequestHead;
|
||||||
|
|
||||||
fn poll_msg(&mut self, cx: &mut task::Context<'_>) -> Poll<Option<Result<(Self::PollItem, Self::PollBody), Self::PollError>>> {
|
fn poll_msg(
|
||||||
|
&mut self,
|
||||||
|
cx: &mut task::Context<'_>,
|
||||||
|
) -> Poll<Option<Result<(Self::PollItem, Self::PollBody), Self::PollError>>> {
|
||||||
let ret = if let Some(ref mut fut) = self.in_flight.as_mut().as_pin_mut() {
|
let ret = if let Some(ref mut fut) = self.in_flight.as_mut().as_pin_mut() {
|
||||||
let resp = ready!(fut.as_mut().poll(cx)?);
|
let resp = ready!(fut.as_mut().poll(cx)?);
|
||||||
let (parts, body) = resp.into_parts();
|
let (parts, body) = resp.into_parts();
|
||||||
@@ -471,11 +504,10 @@ where
|
|||||||
if self.in_flight.is_some() {
|
if self.in_flight.is_some() {
|
||||||
Poll::Pending
|
Poll::Pending
|
||||||
} else {
|
} else {
|
||||||
self.service.poll_ready(cx)
|
self.service.poll_ready(cx).map_err(|_e| {
|
||||||
.map_err(|_e| {
|
// FIXME: return error value.
|
||||||
// FIXME: return error value.
|
trace!("service closed");
|
||||||
trace!("service closed");
|
})
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -486,7 +518,6 @@ where
|
|||||||
|
|
||||||
// ===== impl Client =====
|
// ===== impl Client =====
|
||||||
|
|
||||||
|
|
||||||
impl<B> Client<B> {
|
impl<B> Client<B> {
|
||||||
pub fn new(rx: ClientRx<B>) -> Client<B> {
|
pub fn new(rx: ClientRx<B>) -> Client<B> {
|
||||||
Client {
|
Client {
|
||||||
@@ -506,7 +537,10 @@ where
|
|||||||
type PollError = Never;
|
type PollError = Never;
|
||||||
type RecvItem = ResponseHead;
|
type RecvItem = ResponseHead;
|
||||||
|
|
||||||
fn poll_msg(&mut self, cx: &mut task::Context<'_>) -> Poll<Option<Result<(Self::PollItem, Self::PollBody), Never>>> {
|
fn poll_msg(
|
||||||
|
&mut self,
|
||||||
|
cx: &mut task::Context<'_>,
|
||||||
|
) -> Poll<Option<Result<(Self::PollItem, Self::PollBody), Never>>> {
|
||||||
debug_assert!(!self.rx_closed);
|
debug_assert!(!self.rx_closed);
|
||||||
match self.rx.poll_next(cx) {
|
match self.rx.poll_next(cx) {
|
||||||
Poll::Ready(Some((req, mut cb))) => {
|
Poll::Ready(Some((req, mut cb))) => {
|
||||||
@@ -515,7 +549,7 @@ where
|
|||||||
Poll::Ready(()) => {
|
Poll::Ready(()) => {
|
||||||
trace!("request canceled");
|
trace!("request canceled");
|
||||||
Poll::Ready(None)
|
Poll::Ready(None)
|
||||||
},
|
}
|
||||||
Poll::Pending => {
|
Poll::Pending => {
|
||||||
let (parts, body) = req.into_parts();
|
let (parts, body) = req.into_parts();
|
||||||
let head = RequestHead {
|
let head = RequestHead {
|
||||||
@@ -527,13 +561,13 @@ where
|
|||||||
Poll::Ready(Some(Ok((head, body))))
|
Poll::Ready(Some(Ok((head, body))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
Poll::Ready(None) => {
|
Poll::Ready(None) => {
|
||||||
// user has dropped sender handle
|
// user has dropped sender handle
|
||||||
trace!("client tx closed");
|
trace!("client tx closed");
|
||||||
self.rx_closed = true;
|
self.rx_closed = true;
|
||||||
Poll::Ready(None)
|
Poll::Ready(None)
|
||||||
},
|
}
|
||||||
Poll::Pending => Poll::Pending,
|
Poll::Pending => Poll::Pending,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -554,7 +588,7 @@ where
|
|||||||
// full message!
|
// full message!
|
||||||
Err(crate::Error::new_unexpected_message())
|
Err(crate::Error::new_unexpected_message())
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
if let Some(cb) = self.callback.take() {
|
if let Some(cb) = self.callback.take() {
|
||||||
let _ = cb.send(Err((err, None)));
|
let _ = cb.send(Err((err, None)));
|
||||||
@@ -583,7 +617,7 @@ where
|
|||||||
Poll::Ready(()) => {
|
Poll::Ready(()) => {
|
||||||
trace!("callback receiver has dropped");
|
trace!("callback receiver has dropped");
|
||||||
Poll::Ready(Err(()))
|
Poll::Ready(Err(()))
|
||||||
},
|
}
|
||||||
Poll::Pending => Poll::Ready(Ok(())),
|
Poll::Pending => Poll::Ready(Ok(())),
|
||||||
},
|
},
|
||||||
None => Poll::Ready(Err(())),
|
None => Poll::Ready(Err(())),
|
||||||
@@ -597,18 +631,16 @@ where
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::time::Duration;
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::proto::h1::ClientTransaction;
|
use crate::proto::h1::ClientTransaction;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn client_read_bytes_before_writing_request() {
|
fn client_read_bytes_before_writing_request() {
|
||||||
let _ = pretty_env_logger::try_init();
|
let _ = pretty_env_logger::try_init();
|
||||||
|
|
||||||
tokio_test::task::spawn(()).enter(|cx, _| {
|
tokio_test::task::spawn(()).enter(|cx, _| {
|
||||||
|
let (io, mut handle) = tokio_test::io::Builder::new().build_with_handle();
|
||||||
let (io, mut handle) = tokio_test::io::Builder::new()
|
|
||||||
.build_with_handle();
|
|
||||||
|
|
||||||
// Block at 0 for now, but we will release this response before
|
// Block at 0 for now, but we will release this response before
|
||||||
// the request is ready to write later...
|
// the request is ready to write later...
|
||||||
@@ -624,7 +656,9 @@ mod tests {
|
|||||||
//
|
//
|
||||||
handle.read(b"HTTP/1.1 200 OK\r\n\r\n");
|
handle.read(b"HTTP/1.1 200 OK\r\n\r\n");
|
||||||
|
|
||||||
let mut res_rx = tx.try_send(crate::Request::new(crate::Body::empty())).unwrap();
|
let mut res_rx = tx
|
||||||
|
.try_send(crate::Request::new(crate::Body::empty()))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
tokio_test::assert_ready_ok!(Pin::new(&mut dispatcher).poll(cx));
|
tokio_test::assert_ready_ok!(Pin::new(&mut dispatcher).poll(cx));
|
||||||
let err = tokio_test::assert_ready_ok!(Pin::new(&mut res_rx).poll(cx))
|
let err = tokio_test::assert_ready_ok!(Pin::new(&mut res_rx).poll(cx))
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::io::IoSlice;
|
use std::io::IoSlice;
|
||||||
|
|
||||||
use bytes::Buf;
|
|
||||||
use bytes::buf::ext::{BufExt, Chain, Take};
|
use bytes::buf::ext::{BufExt, Chain, Take};
|
||||||
|
use bytes::Buf;
|
||||||
|
|
||||||
use super::io::WriteBuf;
|
use super::io::WriteBuf;
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ impl Encoder {
|
|||||||
pub fn is_eof(&self) -> bool {
|
pub fn is_eof(&self) -> bool {
|
||||||
match self.kind {
|
match self.kind {
|
||||||
Kind::Length(0) => true,
|
Kind::Length(0) => true,
|
||||||
_ => false
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,7 +105,7 @@ impl Encoder {
|
|||||||
.chain(msg)
|
.chain(msg)
|
||||||
.chain(b"\r\n" as &'static [u8]);
|
.chain(b"\r\n" as &'static [u8]);
|
||||||
BufKind::Chunked(buf)
|
BufKind::Chunked(buf)
|
||||||
},
|
}
|
||||||
Kind::Length(ref mut remaining) => {
|
Kind::Length(ref mut remaining) => {
|
||||||
trace!("sized write, len = {}", len);
|
trace!("sized write, len = {}", len);
|
||||||
if len as u64 > *remaining {
|
if len as u64 > *remaining {
|
||||||
@@ -116,15 +116,13 @@ impl Encoder {
|
|||||||
*remaining -= len as u64;
|
*remaining -= len as u64;
|
||||||
BufKind::Exact(msg)
|
BufKind::Exact(msg)
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
Kind::CloseDelimited => {
|
Kind::CloseDelimited => {
|
||||||
trace!("close delimited write {}B", len);
|
trace!("close delimited write {}B", len);
|
||||||
BufKind::Exact(msg)
|
BufKind::Exact(msg)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
EncodedBuf {
|
EncodedBuf { kind }
|
||||||
kind,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn encode_and_end<B>(&self, msg: B, dst: &mut WriteBuf<EncodedBuf<B>>) -> bool
|
pub(super) fn encode_and_end<B>(&self, msg: B, dst: &mut WriteBuf<EncodedBuf<B>>) -> bool
|
||||||
@@ -142,7 +140,7 @@ impl Encoder {
|
|||||||
.chain(b"\r\n0\r\n\r\n" as &'static [u8]);
|
.chain(b"\r\n0\r\n\r\n" as &'static [u8]);
|
||||||
dst.buffer(buf);
|
dst.buffer(buf);
|
||||||
!self.is_last
|
!self.is_last
|
||||||
},
|
}
|
||||||
Kind::Length(remaining) => {
|
Kind::Length(remaining) => {
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
@@ -151,17 +149,17 @@ impl Encoder {
|
|||||||
Ordering::Equal => {
|
Ordering::Equal => {
|
||||||
dst.buffer(msg);
|
dst.buffer(msg);
|
||||||
!self.is_last
|
!self.is_last
|
||||||
},
|
}
|
||||||
Ordering::Greater => {
|
Ordering::Greater => {
|
||||||
dst.buffer(msg.take(remaining as usize));
|
dst.buffer(msg.take(remaining as usize));
|
||||||
!self.is_last
|
!self.is_last
|
||||||
},
|
}
|
||||||
Ordering::Less => {
|
Ordering::Less => {
|
||||||
dst.buffer(msg);
|
dst.buffer(msg);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
Kind::CloseDelimited => {
|
Kind::CloseDelimited => {
|
||||||
trace!("close delimited write {}B", len);
|
trace!("close delimited write {}B", len);
|
||||||
dst.buffer(msg);
|
dst.buffer(msg);
|
||||||
@@ -180,10 +178,13 @@ impl Encoder {
|
|||||||
B: Buf,
|
B: Buf,
|
||||||
{
|
{
|
||||||
debug_assert!(msg.remaining() > 0, "encode() called with empty buf");
|
debug_assert!(msg.remaining() > 0, "encode() called with empty buf");
|
||||||
debug_assert!(match self.kind {
|
debug_assert!(
|
||||||
Kind::Length(len) => len == msg.remaining() as u64,
|
match self.kind {
|
||||||
_ => true,
|
Kind::Length(len) => len == msg.remaining() as u64,
|
||||||
}, "danger_full_buf length mismatches");
|
_ => true,
|
||||||
|
},
|
||||||
|
"danger_full_buf length mismatches"
|
||||||
|
);
|
||||||
|
|
||||||
match self.kind {
|
match self.kind {
|
||||||
Kind::Chunked => {
|
Kind::Chunked => {
|
||||||
@@ -193,10 +194,10 @@ impl Encoder {
|
|||||||
.chain(msg)
|
.chain(msg)
|
||||||
.chain(b"\r\n0\r\n\r\n" as &'static [u8]);
|
.chain(b"\r\n0\r\n\r\n" as &'static [u8]);
|
||||||
dst.buffer(buf);
|
dst.buffer(buf);
|
||||||
},
|
}
|
||||||
_ => {
|
_ => {
|
||||||
dst.buffer(msg);
|
dst.buffer(msg);
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -246,7 +247,6 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "32")]
|
#[cfg(target_pointer_width = "32")]
|
||||||
const USIZE_BYTES: usize = 4;
|
const USIZE_BYTES: usize = 4;
|
||||||
|
|
||||||
@@ -271,8 +271,7 @@ impl ChunkSize {
|
|||||||
pos: 0,
|
pos: 0,
|
||||||
len: 0,
|
len: 0,
|
||||||
};
|
};
|
||||||
write!(&mut size, "{:X}\r\n", len)
|
write!(&mut size, "{:X}\r\n", len).expect("CHUNK_SIZE_MAX_BYTES should fit any usize");
|
||||||
.expect("CHUNK_SIZE_MAX_BYTES should fit any usize");
|
|
||||||
size
|
size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -285,7 +284,7 @@ impl Buf for ChunkSize {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn bytes(&self) -> &[u8] {
|
fn bytes(&self) -> &[u8] {
|
||||||
&self.bytes[self.pos.into() .. self.len.into()]
|
&self.bytes[self.pos.into()..self.len.into()]
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -307,7 +306,8 @@ impl fmt::Debug for ChunkSize {
|
|||||||
impl fmt::Write for ChunkSize {
|
impl fmt::Write for ChunkSize {
|
||||||
fn write_str(&mut self, num: &str) -> fmt::Result {
|
fn write_str(&mut self, num: &str) -> fmt::Result {
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
(&mut self.bytes[self.len.into()..]).write(num.as_bytes())
|
(&mut self.bytes[self.len.into()..])
|
||||||
|
.write(num.as_bytes())
|
||||||
.expect("&mut [u8].write() cannot error");
|
.expect("&mut [u8].write() cannot error");
|
||||||
self.len += num.len() as u8; // safe because bytes is never bigger than 256
|
self.len += num.len() as u8; // safe because bytes is never bigger than 256
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -340,7 +340,7 @@ impl<B: Buf> From<Chain<Chain<ChunkSize, B>, StaticBuf>> for EncodedBuf<B> {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use bytes::{BufMut};
|
use bytes::BufMut;
|
||||||
|
|
||||||
use super::super::io::Cursor;
|
use super::super::io::Cursor;
|
||||||
use super::Encoder;
|
use super::Encoder;
|
||||||
@@ -364,7 +364,10 @@ mod tests {
|
|||||||
let end = encoder.end::<Cursor<Vec<u8>>>().unwrap().unwrap();
|
let end = encoder.end::<Cursor<Vec<u8>>>().unwrap().unwrap();
|
||||||
dst.put(end);
|
dst.put(end);
|
||||||
|
|
||||||
assert_eq!(dst, b"7\r\nfoo bar\r\nD\r\nbaz quux herp\r\n0\r\n\r\n".as_ref());
|
assert_eq!(
|
||||||
|
dst,
|
||||||
|
b"7\r\nfoo bar\r\nD\r\nbaz quux herp\r\n0\r\n\r\n".as_ref()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -373,12 +376,10 @@ mod tests {
|
|||||||
let mut encoder = Encoder::length(max_len as u64);
|
let mut encoder = Encoder::length(max_len as u64);
|
||||||
let mut dst = Vec::new();
|
let mut dst = Vec::new();
|
||||||
|
|
||||||
|
|
||||||
let msg1 = b"foo bar".as_ref();
|
let msg1 = b"foo bar".as_ref();
|
||||||
let buf1 = encoder.encode(msg1);
|
let buf1 = encoder.encode(msg1);
|
||||||
dst.put(buf1);
|
dst.put(buf1);
|
||||||
|
|
||||||
|
|
||||||
assert_eq!(dst, b"foo bar");
|
assert_eq!(dst, b"foo bar");
|
||||||
assert!(!encoder.is_eof());
|
assert!(!encoder.is_eof());
|
||||||
encoder.end::<()>().unwrap_err();
|
encoder.end::<()>().unwrap_err();
|
||||||
@@ -398,12 +399,10 @@ mod tests {
|
|||||||
let mut encoder = Encoder::close_delimited();
|
let mut encoder = Encoder::close_delimited();
|
||||||
let mut dst = Vec::new();
|
let mut dst = Vec::new();
|
||||||
|
|
||||||
|
|
||||||
let msg1 = b"foo bar".as_ref();
|
let msg1 = b"foo bar".as_ref();
|
||||||
let buf1 = encoder.encode(msg1);
|
let buf1 = encoder.encode(msg1);
|
||||||
dst.put(buf1);
|
dst.put(buf1);
|
||||||
|
|
||||||
|
|
||||||
assert_eq!(dst, b"foo bar");
|
assert_eq!(dst, b"foo bar");
|
||||||
assert!(!encoder.is_eof());
|
assert!(!encoder.is_eof());
|
||||||
encoder.end::<()>().unwrap_err();
|
encoder.end::<()>().unwrap_err();
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ use std::io::{self, IoSlice};
|
|||||||
use bytes::{Buf, BufMut, Bytes, BytesMut};
|
use bytes::{Buf, BufMut, Bytes, BytesMut};
|
||||||
use tokio::io::{AsyncRead, AsyncWrite};
|
use tokio::io::{AsyncRead, AsyncWrite};
|
||||||
|
|
||||||
use crate::common::{Pin, Poll, Unpin, task};
|
|
||||||
use super::{Http1Transaction, ParseContext, ParsedMessage};
|
use super::{Http1Transaction, ParseContext, ParsedMessage};
|
||||||
|
use crate::common::{task, Pin, Poll, Unpin};
|
||||||
|
|
||||||
/// The initial buffer size allocated before trying to read from IO.
|
/// The initial buffer size allocated before trying to read from IO.
|
||||||
pub(crate) const INIT_BUFFER_SIZE: usize = 8192;
|
pub(crate) const INIT_BUFFER_SIZE: usize = 8192;
|
||||||
@@ -140,34 +140,40 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn parse<S>(&mut self, cx: &mut task::Context<'_>, parse_ctx: ParseContext<'_>)
|
pub(super) fn parse<S>(
|
||||||
-> Poll<crate::Result<ParsedMessage<S::Incoming>>>
|
&mut self,
|
||||||
|
cx: &mut task::Context<'_>,
|
||||||
|
parse_ctx: ParseContext<'_>,
|
||||||
|
) -> Poll<crate::Result<ParsedMessage<S::Incoming>>>
|
||||||
where
|
where
|
||||||
S: Http1Transaction,
|
S: Http1Transaction,
|
||||||
{
|
{
|
||||||
loop {
|
loop {
|
||||||
match S::parse(&mut self.read_buf, ParseContext {
|
match S::parse(
|
||||||
cached_headers: parse_ctx.cached_headers,
|
&mut self.read_buf,
|
||||||
req_method: parse_ctx.req_method,
|
ParseContext {
|
||||||
})? {
|
cached_headers: parse_ctx.cached_headers,
|
||||||
|
req_method: parse_ctx.req_method,
|
||||||
|
},
|
||||||
|
)? {
|
||||||
Some(msg) => {
|
Some(msg) => {
|
||||||
debug!("parsed {} headers", msg.head.headers.len());
|
debug!("parsed {} headers", msg.head.headers.len());
|
||||||
return Poll::Ready(Ok(msg));
|
return Poll::Ready(Ok(msg));
|
||||||
},
|
}
|
||||||
None => {
|
None => {
|
||||||
let max = self.read_buf_strategy.max();
|
let max = self.read_buf_strategy.max();
|
||||||
if self.read_buf.len() >= max {
|
if self.read_buf.len() >= max {
|
||||||
debug!("max_buf_size ({}) reached, closing", max);
|
debug!("max_buf_size ({}) reached, closing", max);
|
||||||
return Poll::Ready(Err(crate::Error::new_too_large()));
|
return Poll::Ready(Err(crate::Error::new_too_large()));
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
match ready!(self.poll_read_from_io(cx)).map_err(crate::Error::new_io)? {
|
match ready!(self.poll_read_from_io(cx)).map_err(crate::Error::new_io)? {
|
||||||
0 => {
|
0 => {
|
||||||
trace!("parse eof");
|
trace!("parse eof");
|
||||||
return Poll::Ready(Err(crate::Error::new_incomplete()));
|
return Poll::Ready(Err(crate::Error::new_incomplete()));
|
||||||
}
|
}
|
||||||
_ => {},
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -180,10 +186,10 @@ where
|
|||||||
}
|
}
|
||||||
match Pin::new(&mut self.io).poll_read_buf(cx, &mut self.read_buf) {
|
match Pin::new(&mut self.io).poll_read_buf(cx, &mut self.read_buf) {
|
||||||
Poll::Ready(Ok(n)) => {
|
Poll::Ready(Ok(n)) => {
|
||||||
debug!("read {} bytes", n);
|
debug!("read {} bytes", n);
|
||||||
self.read_buf_strategy.record(n);
|
self.read_buf_strategy.record(n);
|
||||||
Poll::Ready(Ok(n))
|
Poll::Ready(Ok(n))
|
||||||
},
|
}
|
||||||
Poll::Pending => {
|
Poll::Pending => {
|
||||||
self.read_blocked = true;
|
self.read_blocked = true;
|
||||||
Poll::Pending
|
Poll::Pending
|
||||||
@@ -215,12 +221,16 @@ where
|
|||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
loop {
|
loop {
|
||||||
let n = ready!(Pin::new(&mut self.io).poll_write_buf(cx, &mut self.write_buf.auto()))?;
|
let n =
|
||||||
|
ready!(Pin::new(&mut self.io).poll_write_buf(cx, &mut self.write_buf.auto()))?;
|
||||||
debug!("flushed {} bytes", n);
|
debug!("flushed {} bytes", n);
|
||||||
if self.write_buf.remaining() == 0 {
|
if self.write_buf.remaining() == 0 {
|
||||||
break;
|
break;
|
||||||
} else if n == 0 {
|
} else if n == 0 {
|
||||||
trace!("write returned zero, but {} bytes remaining", self.write_buf.remaining());
|
trace!(
|
||||||
|
"write returned zero, but {} bytes remaining",
|
||||||
|
self.write_buf.remaining()
|
||||||
|
);
|
||||||
return Poll::Ready(Err(io::ErrorKind::WriteZero.into()));
|
return Poll::Ready(Err(io::ErrorKind::WriteZero.into()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -241,7 +251,10 @@ where
|
|||||||
self.write_buf.headers.reset();
|
self.write_buf.headers.reset();
|
||||||
break;
|
break;
|
||||||
} else if n == 0 {
|
} else if n == 0 {
|
||||||
trace!("write returned zero, but {} bytes remaining", self.write_buf.remaining());
|
trace!(
|
||||||
|
"write returned zero, but {} bytes remaining",
|
||||||
|
self.write_buf.remaining()
|
||||||
|
);
|
||||||
return Poll::Ready(Err(io::ErrorKind::WriteZero.into()));
|
return Poll::Ready(Err(io::ErrorKind::WriteZero.into()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -283,7 +296,7 @@ enum ReadStrategy {
|
|||||||
Adaptive {
|
Adaptive {
|
||||||
decrease_now: bool,
|
decrease_now: bool,
|
||||||
next: usize,
|
next: usize,
|
||||||
max: usize
|
max: usize,
|
||||||
},
|
},
|
||||||
Exact(usize),
|
Exact(usize),
|
||||||
}
|
}
|
||||||
@@ -313,7 +326,12 @@ impl ReadStrategy {
|
|||||||
|
|
||||||
fn record(&mut self, bytes_read: usize) {
|
fn record(&mut self, bytes_read: usize) {
|
||||||
match *self {
|
match *self {
|
||||||
ReadStrategy::Adaptive { ref mut decrease_now, ref mut next, max, .. } => {
|
ReadStrategy::Adaptive {
|
||||||
|
ref mut decrease_now,
|
||||||
|
ref mut next,
|
||||||
|
max,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
if bytes_read >= *next {
|
if bytes_read >= *next {
|
||||||
*next = cmp::min(incr_power_of_two(*next), max);
|
*next = cmp::min(incr_power_of_two(*next), max);
|
||||||
*decrease_now = false;
|
*decrease_now = false;
|
||||||
@@ -334,7 +352,7 @@ impl ReadStrategy {
|
|||||||
*decrease_now = false;
|
*decrease_now = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -428,7 +446,6 @@ impl<B> WriteBuf<B> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl<B> WriteBuf<B>
|
impl<B> WriteBuf<B>
|
||||||
where
|
where
|
||||||
B: Buf,
|
B: Buf,
|
||||||
@@ -460,22 +477,19 @@ where
|
|||||||
};
|
};
|
||||||
buf.advance(adv);
|
buf.advance(adv);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
WriteStrategy::Auto | WriteStrategy::Queue => {
|
WriteStrategy::Auto | WriteStrategy::Queue => {
|
||||||
self.queue.bufs.push_back(buf.into());
|
self.queue.bufs.push_back(buf.into());
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn can_buffer(&self) -> bool {
|
fn can_buffer(&self) -> bool {
|
||||||
match self.strategy {
|
match self.strategy {
|
||||||
WriteStrategy::Flatten => {
|
WriteStrategy::Flatten => self.remaining() < self.max_buf_size,
|
||||||
self.remaining() < self.max_buf_size
|
|
||||||
},
|
|
||||||
WriteStrategy::Auto | WriteStrategy::Queue => {
|
WriteStrategy::Auto | WriteStrategy::Queue => {
|
||||||
self.queue.bufs.len() < MAX_BUF_LIST_BUFFERS
|
self.queue.bufs.len() < MAX_BUF_LIST_BUFFERS && self.remaining() < self.max_buf_size
|
||||||
&& self.remaining() < self.max_buf_size
|
}
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -587,7 +601,6 @@ impl<'a, B: Buf + 'a> Drop for WriteBufAuto<'a, B> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum WriteStrategy {
|
enum WriteStrategy {
|
||||||
Auto,
|
Auto,
|
||||||
@@ -599,7 +612,6 @@ struct BufDeque<T> {
|
|||||||
bufs: VecDeque<T>,
|
bufs: VecDeque<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl<T> BufDeque<T> {
|
impl<T> BufDeque<T> {
|
||||||
fn new() -> BufDeque<T> {
|
fn new() -> BufDeque<T> {
|
||||||
BufDeque {
|
BufDeque {
|
||||||
@@ -611,9 +623,7 @@ impl<T> BufDeque<T> {
|
|||||||
impl<T: Buf> Buf for BufDeque<T> {
|
impl<T: Buf> Buf for BufDeque<T> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn remaining(&self) -> usize {
|
fn remaining(&self) -> usize {
|
||||||
self.bufs.iter()
|
self.bufs.iter().map(|buf| buf.remaining()).sum()
|
||||||
.map(|buf| buf.remaining())
|
|
||||||
.sum()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -683,9 +693,11 @@ mod tests {
|
|||||||
// First, let's just check that the Mock would normally return an
|
// First, let's just check that the Mock would normally return an
|
||||||
// error on an unexpected write, even if the buffer is empty...
|
// error on an unexpected write, even if the buffer is empty...
|
||||||
let mut mock = Mock::new().build();
|
let mut mock = Mock::new().build();
|
||||||
futures_util::future::poll_fn(|cx| Pin::new(&mut mock).poll_write_buf(cx, &mut Cursor::new(&[])))
|
futures_util::future::poll_fn(|cx| {
|
||||||
.await
|
Pin::new(&mut mock).poll_write_buf(cx, &mut Cursor::new(&[]))
|
||||||
.expect_err("should be a broken pipe");
|
})
|
||||||
|
.await
|
||||||
|
.expect_err("should be a broken pipe");
|
||||||
|
|
||||||
// underlying io will return the logic error upon write,
|
// underlying io will return the logic error upon write,
|
||||||
// so we are testing that the io_buf does not trigger a write
|
// so we are testing that the io_buf does not trigger a write
|
||||||
@@ -716,11 +728,17 @@ mod tests {
|
|||||||
cached_headers: &mut None,
|
cached_headers: &mut None,
|
||||||
req_method: &mut None,
|
req_method: &mut None,
|
||||||
};
|
};
|
||||||
assert!(buffered.parse::<ClientTransaction>(cx, parse_ctx).is_pending());
|
assert!(buffered
|
||||||
|
.parse::<ClientTransaction>(cx, parse_ctx)
|
||||||
|
.is_pending());
|
||||||
Poll::Ready(())
|
Poll::Ready(())
|
||||||
}).await;
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
assert_eq!(buffered.read_buf, b"HTTP/1.1 200 OK\r\nServer: hyper\r\n"[..]);
|
assert_eq!(
|
||||||
|
buffered.read_buf,
|
||||||
|
b"HTTP/1.1 200 OK\r\nServer: hyper\r\n"[..]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -756,12 +774,20 @@ mod tests {
|
|||||||
assert_eq!(strategy.next(), 16384);
|
assert_eq!(strategy.next(), 16384);
|
||||||
|
|
||||||
strategy.record(1);
|
strategy.record(1);
|
||||||
assert_eq!(strategy.next(), 16384, "first smaller record doesn't decrement yet");
|
assert_eq!(
|
||||||
|
strategy.next(),
|
||||||
|
16384,
|
||||||
|
"first smaller record doesn't decrement yet"
|
||||||
|
);
|
||||||
strategy.record(8192);
|
strategy.record(8192);
|
||||||
assert_eq!(strategy.next(), 16384, "record was with range");
|
assert_eq!(strategy.next(), 16384, "record was with range");
|
||||||
|
|
||||||
strategy.record(1);
|
strategy.record(1);
|
||||||
assert_eq!(strategy.next(), 16384, "in-range record should make this the 'first' again");
|
assert_eq!(
|
||||||
|
strategy.next(),
|
||||||
|
16384,
|
||||||
|
"in-range record should make this the 'first' again"
|
||||||
|
);
|
||||||
|
|
||||||
strategy.record(1);
|
strategy.record(1);
|
||||||
assert_eq!(strategy.next(), 8192, "second smaller record decrements");
|
assert_eq!(strategy.next(), 8192, "second smaller record decrements");
|
||||||
@@ -779,10 +805,18 @@ mod tests {
|
|||||||
assert_eq!(strategy.next(), 16384);
|
assert_eq!(strategy.next(), 16384);
|
||||||
|
|
||||||
strategy.record(8193);
|
strategy.record(8193);
|
||||||
assert_eq!(strategy.next(), 16384, "first smaller record doesn't decrement yet");
|
assert_eq!(
|
||||||
|
strategy.next(),
|
||||||
|
16384,
|
||||||
|
"first smaller record doesn't decrement yet"
|
||||||
|
);
|
||||||
|
|
||||||
strategy.record(8193);
|
strategy.record(8193);
|
||||||
assert_eq!(strategy.next(), 16384, "with current step does not decrement");
|
assert_eq!(
|
||||||
|
strategy.next(),
|
||||||
|
16384,
|
||||||
|
"with current step does not decrement"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
use bytes::BytesMut;
|
use bytes::BytesMut;
|
||||||
use http::{HeaderMap, Method};
|
use http::{HeaderMap, Method};
|
||||||
|
|
||||||
use crate::proto::{MessageHead, BodyLength, DecodedLength};
|
use crate::proto::{BodyLength, DecodedLength, MessageHead};
|
||||||
|
|
||||||
pub(crate) use self::conn::Conn;
|
pub(crate) use self::conn::Conn;
|
||||||
pub(crate) use self::dispatch::Dispatcher;
|
|
||||||
pub use self::decode::Decoder;
|
pub use self::decode::Decoder;
|
||||||
|
pub(crate) use self::dispatch::Dispatcher;
|
||||||
pub use self::encode::{EncodedBuf, Encoder};
|
pub use self::encode::{EncodedBuf, Encoder};
|
||||||
pub use self::io::Cursor; //TODO: move out of h1::io
|
pub use self::io::Cursor; //TODO: move out of h1::io
|
||||||
pub use self::io::MINIMUM_MAX_BUFFER_SIZE;
|
pub use self::io::MINIMUM_MAX_BUFFER_SIZE;
|
||||||
@@ -18,7 +18,6 @@ mod encode;
|
|||||||
mod io;
|
mod io;
|
||||||
mod role;
|
mod role;
|
||||||
|
|
||||||
|
|
||||||
pub(crate) type ServerTransaction = role::Server;
|
pub(crate) type ServerTransaction = role::Server;
|
||||||
pub(crate) type ClientTransaction = role::Client;
|
pub(crate) type ClientTransaction = role::Client;
|
||||||
|
|
||||||
@@ -75,4 +74,3 @@ pub(crate) struct Encode<'a, T> {
|
|||||||
req_method: &'a mut Option<Method>,
|
req_method: &'a mut Option<Method>,
|
||||||
title_case_headers: bool,
|
title_case_headers: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
1018
src/proto/h1/role.rs
1018
src/proto/h1/role.rs
File diff suppressed because it is too large
Load Diff
@@ -1,15 +1,15 @@
|
|||||||
use futures_channel::{mpsc, oneshot};
|
use futures_channel::{mpsc, oneshot};
|
||||||
use futures_util::future::{self, FutureExt as _, TryFutureExt as _, Either};
|
use futures_util::future::{self, Either, FutureExt as _, TryFutureExt as _};
|
||||||
use futures_util::stream::StreamExt as _;
|
use futures_util::stream::StreamExt as _;
|
||||||
use h2::client::{Builder, SendRequest};
|
use h2::client::{Builder, SendRequest};
|
||||||
use tokio::io::{AsyncRead, AsyncWrite};
|
use tokio::io::{AsyncRead, AsyncWrite};
|
||||||
|
|
||||||
use crate::headers::content_length_parse_all;
|
|
||||||
use crate::body::Payload;
|
|
||||||
use crate::common::{Exec, Future, Never, Pin, Poll, task};
|
|
||||||
use crate::headers;
|
|
||||||
use crate::proto::Dispatched;
|
|
||||||
use super::{PipeToSendStream, SendBuf};
|
use super::{PipeToSendStream, SendBuf};
|
||||||
|
use crate::body::Payload;
|
||||||
|
use crate::common::{task, Exec, Future, Never, Pin, Poll};
|
||||||
|
use crate::headers;
|
||||||
|
use crate::headers::content_length_parse_all;
|
||||||
|
use crate::proto::Dispatched;
|
||||||
use crate::{Body, Request, Response};
|
use crate::{Body, Request, Response};
|
||||||
|
|
||||||
type ClientRx<B> = crate::client::dispatch::Receiver<Request<B>, Response<Body>>;
|
type ClientRx<B> = crate::client::dispatch::Receiver<Request<B>, Response<Body>>;
|
||||||
@@ -45,13 +45,10 @@ where
|
|||||||
let (conn_drop_ref, rx) = mpsc::channel(1);
|
let (conn_drop_ref, rx) = mpsc::channel(1);
|
||||||
let (cancel_tx, conn_eof) = oneshot::channel();
|
let (cancel_tx, conn_eof) = oneshot::channel();
|
||||||
|
|
||||||
let conn_drop_rx = rx.into_future()
|
let conn_drop_rx = rx.into_future().map(|(item, _rx)| match item {
|
||||||
.map(|(item, _rx)| {
|
Some(never) => match never {},
|
||||||
match item {
|
None => (),
|
||||||
Some(never) => match never {},
|
});
|
||||||
None => (),
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let conn = conn.map_err(|e| debug!("connection error: {}", e));
|
let conn = conn.map_err(|e| debug!("connection error: {}", e));
|
||||||
|
|
||||||
@@ -138,12 +135,11 @@ where
|
|||||||
};
|
};
|
||||||
|
|
||||||
if !eos {
|
if !eos {
|
||||||
let mut pipe = PipeToSendStream::new(body, body_tx)
|
let mut pipe = PipeToSendStream::new(body, body_tx).map(|res| {
|
||||||
.map(|res| {
|
if let Err(e) = res {
|
||||||
if let Err(e) = res {
|
debug!("client request body error: {}", e);
|
||||||
debug!("client request body error: {}", e);
|
}
|
||||||
}
|
});
|
||||||
});
|
|
||||||
|
|
||||||
// eagerly see if the body pipe is ready and
|
// eagerly see if the body pipe is ready and
|
||||||
// can thus skip allocating in the executor
|
// can thus skip allocating in the executor
|
||||||
@@ -152,45 +148,39 @@ where
|
|||||||
Poll::Pending => {
|
Poll::Pending => {
|
||||||
let conn_drop_ref = self.conn_drop_ref.clone();
|
let conn_drop_ref = self.conn_drop_ref.clone();
|
||||||
let pipe = pipe.map(move |x| {
|
let pipe = pipe.map(move |x| {
|
||||||
drop(conn_drop_ref);
|
drop(conn_drop_ref);
|
||||||
x
|
x
|
||||||
});
|
});
|
||||||
self.executor.execute(pipe);
|
self.executor.execute(pipe);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let fut = fut
|
let fut = fut.map(move |result| match result {
|
||||||
.map(move |result| {
|
Ok(res) => {
|
||||||
match result {
|
let content_length = content_length_parse_all(res.headers());
|
||||||
Ok(res) => {
|
let res = res.map(|stream| crate::Body::h2(stream, content_length));
|
||||||
let content_length = content_length_parse_all(res.headers());
|
Ok(res)
|
||||||
let res = res.map(|stream|
|
}
|
||||||
crate::Body::h2(stream, content_length));
|
Err(err) => {
|
||||||
Ok(res)
|
debug!("client response error: {}", err);
|
||||||
},
|
Err((crate::Error::new_h2(err), None))
|
||||||
Err(err) => {
|
}
|
||||||
debug!("client response error: {}", err);
|
});
|
||||||
Err((crate::Error::new_h2(err), None))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
self.executor.execute(cb.send_when(fut));
|
self.executor.execute(cb.send_when(fut));
|
||||||
continue;
|
continue;
|
||||||
},
|
}
|
||||||
|
|
||||||
Poll::Ready(None) => {
|
Poll::Ready(None) => {
|
||||||
trace!("client::dispatch::Sender dropped");
|
trace!("client::dispatch::Sender dropped");
|
||||||
return Poll::Ready(Ok(Dispatched::Shutdown));
|
return Poll::Ready(Ok(Dispatched::Shutdown));
|
||||||
}
|
}
|
||||||
|
|
||||||
Poll::Pending => {
|
Poll::Pending => match ready!(Pin::new(&mut self.conn_eof).poll(cx)) {
|
||||||
match ready!(Pin::new(&mut self.conn_eof).poll(cx)) {
|
Ok(never) => match never {},
|
||||||
Ok(never) => match never {},
|
Err(_conn_is_eof) => {
|
||||||
Err(_conn_is_eof) => {
|
trace!("connection task is closed, closing dispatch task");
|
||||||
trace!("connection task is closed, closing dispatch task");
|
return Poll::Ready(Ok(Dispatched::Shutdown));
|
||||||
return Poll::Ready(Ok(Dispatched::Shutdown));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use bytes::Buf;
|
use bytes::Buf;
|
||||||
use h2::{SendStream};
|
use h2::SendStream;
|
||||||
use http::header::{
|
use http::header::{
|
||||||
HeaderName, CONNECTION, PROXY_AUTHENTICATE, PROXY_AUTHORIZATION, TE, TRAILER,
|
HeaderName, CONNECTION, PROXY_AUTHENTICATE, PROXY_AUTHORIZATION, TE, TRAILER,
|
||||||
TRANSFER_ENCODING, UPGRADE,
|
TRANSFER_ENCODING, UPGRADE,
|
||||||
@@ -7,7 +7,7 @@ use http::header::{
|
|||||||
use http::HeaderMap;
|
use http::HeaderMap;
|
||||||
|
|
||||||
use crate::body::Payload;
|
use crate::body::Payload;
|
||||||
use crate::common::{Future, Pin, Poll, task};
|
use crate::common::{task, Future, Pin, Poll};
|
||||||
|
|
||||||
pub(crate) mod client;
|
pub(crate) mod client;
|
||||||
pub(crate) mod server;
|
pub(crate) mod server;
|
||||||
@@ -38,7 +38,11 @@ fn strip_connection_headers(headers: &mut HeaderMap, is_request: bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if is_request {
|
if is_request {
|
||||||
if headers.get(TE).map(|te_header| te_header != "trailers").unwrap_or(false) {
|
if headers
|
||||||
|
.get(TE)
|
||||||
|
.map(|te_header| te_header != "trailers")
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
warn!("TE headers not set to \"trailers\" are illegal in HTTP/2 requests");
|
warn!("TE headers not set to \"trailers\" are illegal in HTTP/2 requests");
|
||||||
headers.remove(TE);
|
headers.remove(TE);
|
||||||
}
|
}
|
||||||
@@ -123,19 +127,24 @@ where
|
|||||||
if self.body_tx.capacity() == 0 {
|
if self.body_tx.capacity() == 0 {
|
||||||
loop {
|
loop {
|
||||||
match ready!(self.body_tx.poll_capacity(cx)) {
|
match ready!(self.body_tx.poll_capacity(cx)) {
|
||||||
|
Some(Ok(0)) => {}
|
||||||
Some(Ok(0)) => {},
|
|
||||||
Some(Ok(_)) => break,
|
Some(Ok(_)) => break,
|
||||||
Some(Err(e)) => return Poll::Ready(Err(crate::Error::new_body_write(e))) ,
|
Some(Err(e)) => {
|
||||||
|
return Poll::Ready(Err(crate::Error::new_body_write(e)))
|
||||||
|
}
|
||||||
None => return Poll::Ready(Err(crate::Error::new_canceled())),
|
None => return Poll::Ready(Err(crate::Error::new_canceled())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if let Poll::Ready(reason) =
|
if let Poll::Ready(reason) = self
|
||||||
self.body_tx.poll_reset(cx).map_err(crate::Error::new_body_write)?
|
.body_tx
|
||||||
|
.poll_reset(cx)
|
||||||
|
.map_err(crate::Error::new_body_write)?
|
||||||
{
|
{
|
||||||
debug!("stream received RST_STREAM: {:?}", reason);
|
debug!("stream received RST_STREAM: {:?}", reason);
|
||||||
return Poll::Ready(Err(crate::Error::new_body_write(::h2::Error::from(reason))));
|
return Poll::Ready(Err(crate::Error::new_body_write(::h2::Error::from(
|
||||||
|
reason,
|
||||||
|
))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,11 +179,15 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if let Poll::Ready(reason) =
|
if let Poll::Ready(reason) = self
|
||||||
self.body_tx.poll_reset(cx).map_err(|e| crate::Error::new_body_write(e))?
|
.body_tx
|
||||||
|
.poll_reset(cx)
|
||||||
|
.map_err(|e| crate::Error::new_body_write(e))?
|
||||||
{
|
{
|
||||||
debug!("stream received RST_STREAM: {:?}", reason);
|
debug!("stream received RST_STREAM: {:?}", reason);
|
||||||
return Poll::Ready(Err(crate::Error::new_body_write(::h2::Error::from(reason))));
|
return Poll::Ready(Err(crate::Error::new_body_write(::h2::Error::from(
|
||||||
|
reason,
|
||||||
|
))));
|
||||||
}
|
}
|
||||||
|
|
||||||
match ready!(Pin::new(&mut self.stream).poll_trailers(cx)) {
|
match ready!(Pin::new(&mut self.stream).poll_trailers(cx)) {
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
use std::error::Error as StdError;
|
use std::error::Error as StdError;
|
||||||
use std::marker::Unpin;
|
use std::marker::Unpin;
|
||||||
|
|
||||||
use pin_project::{pin_project, project};
|
|
||||||
use h2::Reason;
|
|
||||||
use h2::server::{Builder, Connection, Handshake, SendResponse};
|
use h2::server::{Builder, Connection, Handshake, SendResponse};
|
||||||
|
use h2::Reason;
|
||||||
|
use pin_project::{pin_project, project};
|
||||||
use tokio::io::{AsyncRead, AsyncWrite};
|
use tokio::io::{AsyncRead, AsyncWrite};
|
||||||
|
|
||||||
|
use super::{PipeToSendStream, SendBuf};
|
||||||
use crate::body::Payload;
|
use crate::body::Payload;
|
||||||
use crate::common::exec::H2Exec;
|
use crate::common::exec::H2Exec;
|
||||||
use crate::common::{Future, Pin, Poll, task};
|
use crate::common::{task, Future, Pin, Poll};
|
||||||
use crate::headers;
|
use crate::headers;
|
||||||
use crate::headers::content_length_parse_all;
|
use crate::headers::content_length_parse_all;
|
||||||
use crate::service::HttpService;
|
|
||||||
use crate::proto::Dispatched;
|
use crate::proto::Dispatched;
|
||||||
use super::{PipeToSendStream, SendBuf};
|
use crate::service::HttpService;
|
||||||
|
|
||||||
use crate::{Body, Response};
|
use crate::{Body, Response};
|
||||||
|
|
||||||
@@ -45,11 +45,10 @@ where
|
|||||||
closing: Option<crate::Error>,
|
closing: Option<crate::Error>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl<T, S, B, E> Server<T, S, B, E>
|
impl<T, S, B, E> Server<T, S, B, E>
|
||||||
where
|
where
|
||||||
T: AsyncRead + AsyncWrite + Unpin,
|
T: AsyncRead + AsyncWrite + Unpin,
|
||||||
S: HttpService<Body, ResBody=B>,
|
S: HttpService<Body, ResBody = B>,
|
||||||
S::Error: Into<Box<dyn StdError + Send + Sync>>,
|
S::Error: Into<Box<dyn StdError + Send + Sync>>,
|
||||||
B: Payload,
|
B: Payload,
|
||||||
B::Data: Unpin,
|
B::Data: Unpin,
|
||||||
@@ -69,13 +68,13 @@ where
|
|||||||
match self.state {
|
match self.state {
|
||||||
State::Handshaking(..) => {
|
State::Handshaking(..) => {
|
||||||
// fall-through, to replace state with Closed
|
// fall-through, to replace state with Closed
|
||||||
},
|
}
|
||||||
State::Serving(ref mut srv) => {
|
State::Serving(ref mut srv) => {
|
||||||
if srv.closing.is_none() {
|
if srv.closing.is_none() {
|
||||||
srv.conn.graceful_shutdown();
|
srv.conn.graceful_shutdown();
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
},
|
}
|
||||||
State::Closed => {
|
State::Closed => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -87,7 +86,7 @@ where
|
|||||||
impl<T, S, B, E> Future for Server<T, S, B, E>
|
impl<T, S, B, E> Future for Server<T, S, B, E>
|
||||||
where
|
where
|
||||||
T: AsyncRead + AsyncWrite + Unpin,
|
T: AsyncRead + AsyncWrite + Unpin,
|
||||||
S: HttpService<Body, ResBody=B>,
|
S: HttpService<Body, ResBody = B>,
|
||||||
S::Error: Into<Box<dyn StdError + Send + Sync>>,
|
S::Error: Into<Box<dyn StdError + Send + Sync>>,
|
||||||
B: Payload,
|
B: Payload,
|
||||||
B::Data: Unpin,
|
B::Data: Unpin,
|
||||||
@@ -105,7 +104,7 @@ where
|
|||||||
conn,
|
conn,
|
||||||
closing: None,
|
closing: None,
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
State::Serving(ref mut srv) => {
|
State::Serving(ref mut srv) => {
|
||||||
ready!(srv.poll_server(cx, &mut me.service, &mut me.exec))?;
|
ready!(srv.poll_server(cx, &mut me.service, &mut me.exec))?;
|
||||||
return Poll::Ready(Ok(Dispatched::Shutdown));
|
return Poll::Ready(Ok(Dispatched::Shutdown));
|
||||||
@@ -127,12 +126,14 @@ where
|
|||||||
B: Payload,
|
B: Payload,
|
||||||
B::Data: Unpin,
|
B::Data: Unpin,
|
||||||
{
|
{
|
||||||
fn poll_server<S, E>(&mut self, cx: &mut task::Context<'_>, service: &mut S, exec: &mut E) -> Poll<crate::Result<()>>
|
fn poll_server<S, E>(
|
||||||
|
&mut self,
|
||||||
|
cx: &mut task::Context<'_>,
|
||||||
|
service: &mut S,
|
||||||
|
exec: &mut E,
|
||||||
|
) -> Poll<crate::Result<()>>
|
||||||
where
|
where
|
||||||
S: HttpService<
|
S: HttpService<Body, ResBody = B>,
|
||||||
Body,
|
|
||||||
ResBody=B,
|
|
||||||
>,
|
|
||||||
S::Error: Into<Box<dyn StdError + Send + Sync>>,
|
S::Error: Into<Box<dyn StdError + Send + Sync>>,
|
||||||
E: H2Exec<S::Future, B>,
|
E: H2Exec<S::Future, B>,
|
||||||
{
|
{
|
||||||
@@ -171,25 +172,26 @@ where
|
|||||||
Some(Ok((req, respond))) => {
|
Some(Ok((req, respond))) => {
|
||||||
trace!("incoming request");
|
trace!("incoming request");
|
||||||
let content_length = content_length_parse_all(req.headers());
|
let content_length = content_length_parse_all(req.headers());
|
||||||
let req = req.map(|stream| {
|
let req = req.map(|stream| crate::Body::h2(stream, content_length));
|
||||||
crate::Body::h2(stream, content_length)
|
|
||||||
});
|
|
||||||
let fut = H2Stream::new(service.call(req), respond);
|
let fut = H2Stream::new(service.call(req), respond);
|
||||||
exec.execute_h2stream(fut);
|
exec.execute_h2stream(fut);
|
||||||
},
|
}
|
||||||
Some(Err(e)) => {
|
Some(Err(e)) => {
|
||||||
return Poll::Ready(Err(crate::Error::new_h2(e)));
|
return Poll::Ready(Err(crate::Error::new_h2(e)));
|
||||||
},
|
}
|
||||||
None => {
|
None => {
|
||||||
// no more incoming streams...
|
// no more incoming streams...
|
||||||
trace!("incoming connection complete");
|
trace!("incoming connection complete");
|
||||||
return Poll::Ready(Ok(()));
|
return Poll::Ready(Ok(()));
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_assert!(self.closing.is_some(), "poll_server broke loop without closing");
|
debug_assert!(
|
||||||
|
self.closing.is_some(),
|
||||||
|
"poll_server broke loop without closing"
|
||||||
|
);
|
||||||
|
|
||||||
ready!(self.conn.poll_closed(cx).map_err(crate::Error::new_h2))?;
|
ready!(self.conn.poll_closed(cx).map_err(crate::Error::new_h2))?;
|
||||||
|
|
||||||
@@ -230,7 +232,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! reply {
|
macro_rules! reply {
|
||||||
($me:expr, $res:expr, $eos:expr) => ({
|
($me:expr, $res:expr, $eos:expr) => {{
|
||||||
match $me.reply.send_response($res, $eos) {
|
match $me.reply.send_response($res, $eos) {
|
||||||
Ok(tx) => tx,
|
Ok(tx) => tx,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@@ -239,7 +241,7 @@ macro_rules! reply {
|
|||||||
return Poll::Ready(Err(crate::Error::new_h2(e)));
|
return Poll::Ready(Err(crate::Error::new_h2(e)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F, B, E> H2Stream<F, B>
|
impl<F, B, E> H2Stream<F, B>
|
||||||
@@ -261,8 +263,10 @@ where
|
|||||||
Poll::Pending => {
|
Poll::Pending => {
|
||||||
// Response is not yet ready, so we want to check if the client has sent a
|
// Response is not yet ready, so we want to check if the client has sent a
|
||||||
// RST_STREAM frame which would cancel the current request.
|
// RST_STREAM frame which would cancel the current request.
|
||||||
if let Poll::Ready(reason) =
|
if let Poll::Ready(reason) = me
|
||||||
me.reply.poll_reset(cx).map_err(|e| crate::Error::new_h2(e))?
|
.reply
|
||||||
|
.poll_reset(cx)
|
||||||
|
.map_err(|e| crate::Error::new_h2(e))?
|
||||||
{
|
{
|
||||||
debug!("stream received RST_STREAM: {:?}", reason);
|
debug!("stream received RST_STREAM: {:?}", reason);
|
||||||
return Poll::Ready(Err(crate::Error::new_h2(reason.into())));
|
return Poll::Ready(Err(crate::Error::new_h2(reason.into())));
|
||||||
@@ -274,7 +278,7 @@ where
|
|||||||
warn!("http2 service errored: {}", err);
|
warn!("http2 service errored: {}", err);
|
||||||
me.reply.send_reset(err.h2_reason());
|
me.reply.send_reset(err.h2_reason());
|
||||||
return Poll::Ready(Err(err));
|
return Poll::Ready(Err(err));
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let (head, body) = res.into_parts();
|
let (head, body) = res.into_parts();
|
||||||
@@ -282,13 +286,10 @@ where
|
|||||||
super::strip_connection_headers(res.headers_mut(), false);
|
super::strip_connection_headers(res.headers_mut(), false);
|
||||||
|
|
||||||
// set Date header if it isn't already set...
|
// set Date header if it isn't already set...
|
||||||
res
|
res.headers_mut()
|
||||||
.headers_mut()
|
|
||||||
.entry(::http::header::DATE)
|
.entry(::http::header::DATE)
|
||||||
.or_insert_with(crate::proto::h1::date::update_and_header_value);
|
.or_insert_with(crate::proto::h1::date::update_and_header_value);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// automatically set Content-Length from body...
|
// automatically set Content-Length from body...
|
||||||
if let Some(len) = body.size_hint().exact() {
|
if let Some(len) = body.size_hint().exact() {
|
||||||
headers::set_content_length_if_missing(res.headers_mut(), len);
|
headers::set_content_length_if_missing(res.headers_mut(), len);
|
||||||
@@ -301,7 +302,7 @@ where
|
|||||||
reply!(me, res, true);
|
reply!(me, res, true);
|
||||||
return Poll::Ready(Ok(()));
|
return Poll::Ready(Ok(()));
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
H2StreamState::Body(ref mut pipe) => {
|
H2StreamState::Body(ref mut pipe) => {
|
||||||
return Pin::new(pipe).poll(cx);
|
return Pin::new(pipe).poll(cx);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
//! Pieces pertaining to the HTTP message protocol.
|
//! Pieces pertaining to the HTTP message protocol.
|
||||||
use http::{HeaderMap, Method, StatusCode, Uri, Version};
|
use http::{HeaderMap, Method, StatusCode, Uri, Version};
|
||||||
|
|
||||||
pub(crate) use self::h1::{dispatch, Conn, ServerTransaction};
|
|
||||||
use self::body_length::DecodedLength;
|
use self::body_length::DecodedLength;
|
||||||
|
pub(crate) use self::h1::{dispatch, Conn, ServerTransaction};
|
||||||
|
|
||||||
pub(crate) mod h1;
|
pub(crate) mod h1;
|
||||||
pub(crate) mod h2;
|
pub(crate) mod h2;
|
||||||
@@ -76,9 +76,8 @@ mod body_length {
|
|||||||
/// Converts to an Option<u64> representing a Known or Unknown length.
|
/// Converts to an Option<u64> representing a Known or Unknown length.
|
||||||
pub(crate) fn into_opt(self) -> Option<u64> {
|
pub(crate) fn into_opt(self) -> Option<u64> {
|
||||||
match self {
|
match self {
|
||||||
DecodedLength::CHUNKED |
|
DecodedLength::CHUNKED | DecodedLength::CLOSE_DELIMITED => None,
|
||||||
DecodedLength::CLOSE_DELIMITED => None,
|
DecodedLength(known) => Some(known),
|
||||||
DecodedLength(known) => Some(known)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,10 @@
|
|||||||
#[cfg(feature = "stream")]
|
#[cfg(feature = "stream")]
|
||||||
use futures_core::Stream;
|
use futures_core::Stream;
|
||||||
|
|
||||||
use crate::common::{Pin, task::{self, Poll}};
|
use crate::common::{
|
||||||
|
task::{self, Poll},
|
||||||
|
Pin,
|
||||||
|
};
|
||||||
|
|
||||||
/// Asynchronously accept incoming connections.
|
/// Asynchronously accept incoming connections.
|
||||||
pub trait Accept {
|
pub trait Accept {
|
||||||
@@ -19,8 +22,10 @@ pub trait Accept {
|
|||||||
type Error;
|
type Error;
|
||||||
|
|
||||||
/// Poll to accept the next connection.
|
/// Poll to accept the next connection.
|
||||||
fn poll_accept(self: Pin<&mut Self>, cx: &mut task::Context<'_>)
|
fn poll_accept(
|
||||||
-> Poll<Option<Result<Self::Conn, Self::Error>>>;
|
self: Pin<&mut Self>,
|
||||||
|
cx: &mut task::Context<'_>,
|
||||||
|
) -> Poll<Option<Result<Self::Conn, Self::Error>>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create an `Accept` with a polling function.
|
/// Create an `Accept` with a polling function.
|
||||||
@@ -54,12 +59,11 @@ where
|
|||||||
{
|
{
|
||||||
type Conn = IO;
|
type Conn = IO;
|
||||||
type Error = E;
|
type Error = E;
|
||||||
fn poll_accept(self: Pin<&mut Self>, cx: &mut task::Context<'_>)
|
fn poll_accept(
|
||||||
-> Poll<Option<Result<Self::Conn, Self::Error>>>
|
self: Pin<&mut Self>,
|
||||||
{
|
cx: &mut task::Context<'_>,
|
||||||
unsafe {
|
) -> Poll<Option<Result<Self::Conn, Self::Error>>> {
|
||||||
(self.get_unchecked_mut().0)(cx)
|
unsafe { (self.get_unchecked_mut().0)(cx) }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,13 +89,11 @@ where
|
|||||||
{
|
{
|
||||||
type Conn = IO;
|
type Conn = IO;
|
||||||
type Error = E;
|
type Error = E;
|
||||||
fn poll_accept(self: Pin<&mut Self>, cx: &mut task::Context<'_>)
|
fn poll_accept(
|
||||||
-> Poll<Option<Result<Self::Conn, Self::Error>>>
|
self: Pin<&mut Self>,
|
||||||
{
|
cx: &mut task::Context<'_>,
|
||||||
unsafe {
|
) -> Poll<Option<Result<Self::Conn, Self::Error>>> {
|
||||||
Pin::new_unchecked(&mut self.get_unchecked_mut().0)
|
unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().0).poll_next(cx) }
|
||||||
.poll_next(cx)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,29 +11,31 @@
|
|||||||
use std::error::Error as StdError;
|
use std::error::Error as StdError;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
#[cfg(feature = "tcp")] use std::net::SocketAddr;
|
#[cfg(feature = "tcp")]
|
||||||
|
use std::net::SocketAddr;
|
||||||
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use futures_core::Stream;
|
use futures_core::Stream;
|
||||||
use tokio::io::{AsyncRead, AsyncWrite};
|
|
||||||
use pin_project::{pin_project, project};
|
use pin_project::{pin_project, project};
|
||||||
|
use tokio::io::{AsyncRead, AsyncWrite};
|
||||||
|
|
||||||
|
use super::Accept;
|
||||||
use crate::body::{Body, Payload};
|
use crate::body::{Body, Payload};
|
||||||
use crate::common::exec::{Exec, H2Exec, NewSvcExec};
|
use crate::common::exec::{Exec, H2Exec, NewSvcExec};
|
||||||
use crate::common::io::Rewind;
|
use crate::common::io::Rewind;
|
||||||
use crate::common::{Future, Pin, Poll, Unpin, task};
|
use crate::common::{task, Future, Pin, Poll, Unpin};
|
||||||
use crate::error::{Kind, Parse};
|
use crate::error::{Kind, Parse};
|
||||||
use crate::proto;
|
use crate::proto;
|
||||||
use crate::service::{MakeServiceRef, HttpService};
|
use crate::service::{HttpService, MakeServiceRef};
|
||||||
use crate::upgrade::Upgraded;
|
use crate::upgrade::Upgraded;
|
||||||
use super::Accept;
|
|
||||||
|
|
||||||
pub(super) use self::spawn_all::NoopWatcher;
|
|
||||||
use self::spawn_all::NewSvcTask;
|
use self::spawn_all::NewSvcTask;
|
||||||
|
pub(super) use self::spawn_all::NoopWatcher;
|
||||||
pub(super) use self::spawn_all::Watcher;
|
pub(super) use self::spawn_all::Watcher;
|
||||||
pub(super) use self::upgrades::UpgradeableConnection;
|
pub(super) use self::upgrades::UpgradeableConnection;
|
||||||
|
|
||||||
#[cfg(feature = "tcp")] pub use super::tcp::{AddrIncoming, AddrStream};
|
#[cfg(feature = "tcp")]
|
||||||
|
pub use super::tcp::{AddrIncoming, AddrStream};
|
||||||
|
|
||||||
// Our defaults are chosen for the "majority" case, which usually are not
|
// Our defaults are chosen for the "majority" case, which usually are not
|
||||||
// resource contrained, and so the spec default of 64kb can be too limiting
|
// resource contrained, and so the spec default of 64kb can be too limiting
|
||||||
@@ -161,7 +163,7 @@ impl<E> Unpin for Fallback<E> {}
|
|||||||
/// This allows taking apart a `Connection` at a later time, in order to
|
/// This allows taking apart a `Connection` at a later time, in order to
|
||||||
/// reclaim the IO object, and additional related pieces.
|
/// reclaim the IO object, and additional related pieces.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Parts<T, S> {
|
pub struct Parts<T, S> {
|
||||||
/// The original IO object used in the handshake.
|
/// The original IO object used in the handshake.
|
||||||
pub io: T,
|
pub io: T,
|
||||||
/// A buffer of bytes that have been read but not processed as HTTP.
|
/// A buffer of bytes that have been read but not processed as HTTP.
|
||||||
@@ -274,7 +276,10 @@ impl<E> Http<E> {
|
|||||||
/// Passing `None` will do nothing.
|
/// Passing `None` will do nothing.
|
||||||
///
|
///
|
||||||
/// If not set, hyper will use a default.
|
/// If not set, hyper will use a default.
|
||||||
pub fn http2_initial_connection_window_size(&mut self, sz: impl Into<Option<u32>>) -> &mut Self {
|
pub fn http2_initial_connection_window_size(
|
||||||
|
&mut self,
|
||||||
|
sz: impl Into<Option<u32>>,
|
||||||
|
) -> &mut Self {
|
||||||
if let Some(sz) = sz.into() {
|
if let Some(sz) = sz.into() {
|
||||||
self.h2_builder.initial_connection_window_size(sz);
|
self.h2_builder.initial_connection_window_size(sz);
|
||||||
}
|
}
|
||||||
@@ -374,7 +379,7 @@ impl<E> Http<E> {
|
|||||||
/// ```
|
/// ```
|
||||||
pub fn serve_connection<S, I, Bd>(&self, io: I, service: S) -> Connection<I, S, E>
|
pub fn serve_connection<S, I, Bd>(&self, io: I, service: S) -> Connection<I, S, E>
|
||||||
where
|
where
|
||||||
S: HttpService<Body, ResBody=Bd>,
|
S: HttpService<Body, ResBody = Bd>,
|
||||||
S::Error: Into<Box<dyn StdError + Send + Sync>>,
|
S::Error: Into<Box<dyn StdError + Send + Sync>>,
|
||||||
Bd: Payload,
|
Bd: Payload,
|
||||||
Bd::Data: Unpin,
|
Bd::Data: Unpin,
|
||||||
@@ -402,7 +407,8 @@ impl<E> Http<E> {
|
|||||||
}
|
}
|
||||||
ConnectionMode::H2Only => {
|
ConnectionMode::H2Only => {
|
||||||
let rewind_io = Rewind::new(io);
|
let rewind_io = Rewind::new(io);
|
||||||
let h2 = proto::h2::Server::new(rewind_io, service, &self.h2_builder, self.exec.clone());
|
let h2 =
|
||||||
|
proto::h2::Server::new(rewind_io, service, &self.h2_builder, self.exec.clone());
|
||||||
ProtoServer::H2(h2)
|
ProtoServer::H2(h2)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -419,14 +425,10 @@ impl<E> Http<E> {
|
|||||||
|
|
||||||
pub(super) fn serve<I, IO, IE, S, Bd>(&self, incoming: I, make_service: S) -> Serve<I, S, E>
|
pub(super) fn serve<I, IO, IE, S, Bd>(&self, incoming: I, make_service: S) -> Serve<I, S, E>
|
||||||
where
|
where
|
||||||
I: Accept<Conn=IO, Error=IE>,
|
I: Accept<Conn = IO, Error = IE>,
|
||||||
IE: Into<Box<dyn StdError + Send + Sync>>,
|
IE: Into<Box<dyn StdError + Send + Sync>>,
|
||||||
IO: AsyncRead + AsyncWrite + Unpin,
|
IO: AsyncRead + AsyncWrite + Unpin,
|
||||||
S: MakeServiceRef<
|
S: MakeServiceRef<IO, Body, ResBody = Bd>,
|
||||||
IO,
|
|
||||||
Body,
|
|
||||||
ResBody=Bd,
|
|
||||||
>,
|
|
||||||
S::Error: Into<Box<dyn StdError + Send + Sync>>,
|
S::Error: Into<Box<dyn StdError + Send + Sync>>,
|
||||||
Bd: Payload,
|
Bd: Payload,
|
||||||
E: H2Exec<<S::Service as HttpService<Body>>::Future, Bd>,
|
E: H2Exec<<S::Service as HttpService<Body>>::Future, Bd>,
|
||||||
@@ -439,12 +441,11 @@ impl<E> Http<E> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ===== impl Connection =====
|
// ===== impl Connection =====
|
||||||
|
|
||||||
impl<I, B, S, E> Connection<I, S, E>
|
impl<I, B, S, E> Connection<I, S, E>
|
||||||
where
|
where
|
||||||
S: HttpService<Body, ResBody=B>,
|
S: HttpService<Body, ResBody = B>,
|
||||||
S::Error: Into<Box<dyn StdError + Send + Sync>>,
|
S::Error: Into<Box<dyn StdError + Send + Sync>>,
|
||||||
I: AsyncRead + AsyncWrite + Unpin,
|
I: AsyncRead + AsyncWrite + Unpin,
|
||||||
B: Payload + 'static,
|
B: Payload + 'static,
|
||||||
@@ -459,7 +460,7 @@ where
|
|||||||
match self.project().conn.as_mut().unwrap() {
|
match self.project().conn.as_mut().unwrap() {
|
||||||
ProtoServer::H1(ref mut h1) => {
|
ProtoServer::H1(ref mut h1) => {
|
||||||
h1.disable_keep_alive();
|
h1.disable_keep_alive();
|
||||||
},
|
}
|
||||||
ProtoServer::H2(ref mut h2) => {
|
ProtoServer::H2(ref mut h2) => {
|
||||||
h2.graceful_shutdown();
|
h2.graceful_shutdown();
|
||||||
}
|
}
|
||||||
@@ -476,7 +477,8 @@ where
|
|||||||
/// # Panics
|
/// # Panics
|
||||||
/// This method will panic if this connection is using an h2 protocol.
|
/// This method will panic if this connection is using an h2 protocol.
|
||||||
pub fn into_parts(self) -> Parts<I, S> {
|
pub fn into_parts(self) -> Parts<I, S> {
|
||||||
self.try_into_parts().unwrap_or_else(|| panic!("h2 cannot into_inner"))
|
self.try_into_parts()
|
||||||
|
.unwrap_or_else(|| panic!("h2 cannot into_inner"))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the inner IO object, and additional information, if available.
|
/// Return the inner IO object, and additional information, if available.
|
||||||
@@ -492,7 +494,7 @@ where
|
|||||||
service: dispatch.into_service(),
|
service: dispatch.into_service(),
|
||||||
_inner: (),
|
_inner: (),
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
ProtoServer::H2(_h2) => None,
|
ProtoServer::H2(_h2) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -521,22 +523,20 @@ where
|
|||||||
};
|
};
|
||||||
match ready!(polled) {
|
match ready!(polled) {
|
||||||
Ok(x) => return Poll::Ready(Ok(x)),
|
Ok(x) => return Poll::Ready(Ok(x)),
|
||||||
Err(e) => {
|
Err(e) => match *e.kind() {
|
||||||
match *e.kind() {
|
Kind::Parse(Parse::VersionH2) if self.fallback.to_h2() => {
|
||||||
Kind::Parse(Parse::VersionH2) if self.fallback.to_h2() => {
|
self.upgrade_h2();
|
||||||
self.upgrade_h2();
|
continue;
|
||||||
continue;
|
|
||||||
}
|
|
||||||
_ => return Poll::Ready(Err(e)),
|
|
||||||
}
|
}
|
||||||
}
|
_ => return Poll::Ready(Err(e)),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Prevent shutdown of the underlying IO object at the end of service the request,
|
/// Prevent shutdown of the underlying IO object at the end of service the request,
|
||||||
/// instead run `into_parts`. This is a convenience wrapper over `poll_without_shutdown`.
|
/// instead run `into_parts`. This is a convenience wrapper over `poll_without_shutdown`.
|
||||||
pub fn without_shutdown(self) -> impl Future<Output=crate::Result<Parts<I, S>>>
|
pub fn without_shutdown(self) -> impl Future<Output = crate::Result<Parts<I, S>>>
|
||||||
where
|
where
|
||||||
S: Unpin,
|
S: Unpin,
|
||||||
S::Future: Unpin,
|
S::Future: Unpin,
|
||||||
@@ -554,9 +554,7 @@ where
|
|||||||
let conn = self.conn.take();
|
let conn = self.conn.take();
|
||||||
|
|
||||||
let (io, read_buf, dispatch) = match conn.unwrap() {
|
let (io, read_buf, dispatch) = match conn.unwrap() {
|
||||||
ProtoServer::H1(h1) => {
|
ProtoServer::H1(h1) => h1.into_inner(),
|
||||||
h1.into_inner()
|
|
||||||
},
|
|
||||||
ProtoServer::H2(_h2) => {
|
ProtoServer::H2(_h2) => {
|
||||||
panic!("h2 cannot into_inner");
|
panic!("h2 cannot into_inner");
|
||||||
}
|
}
|
||||||
@@ -567,12 +565,7 @@ where
|
|||||||
Fallback::ToHttp2(ref builder, ref exec) => (builder, exec),
|
Fallback::ToHttp2(ref builder, ref exec) => (builder, exec),
|
||||||
Fallback::Http1Only => unreachable!("upgrade_h2 with Fallback::Http1Only"),
|
Fallback::Http1Only => unreachable!("upgrade_h2 with Fallback::Http1Only"),
|
||||||
};
|
};
|
||||||
let h2 = proto::h2::Server::new(
|
let h2 = proto::h2::Server::new(rewind_io, dispatch.into_service(), builder, exec.clone());
|
||||||
rewind_io,
|
|
||||||
dispatch.into_service(),
|
|
||||||
builder,
|
|
||||||
exec.clone(),
|
|
||||||
);
|
|
||||||
|
|
||||||
debug_assert!(self.conn.is_none());
|
debug_assert!(self.conn.is_none());
|
||||||
self.conn = Some(ProtoServer::H2(h2));
|
self.conn = Some(ProtoServer::H2(h2));
|
||||||
@@ -585,15 +578,13 @@ where
|
|||||||
where
|
where
|
||||||
I: Send,
|
I: Send,
|
||||||
{
|
{
|
||||||
UpgradeableConnection {
|
UpgradeableConnection { inner: self }
|
||||||
inner: self,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I, B, S, E> Future for Connection<I, S, E>
|
impl<I, B, S, E> Future for Connection<I, S, E>
|
||||||
where
|
where
|
||||||
S: HttpService<Body, ResBody=B>,
|
S: HttpService<Body, ResBody = B>,
|
||||||
S::Error: Into<Box<dyn StdError + Send + Sync>>,
|
S::Error: Into<Box<dyn StdError + Send + Sync>>,
|
||||||
I: AsyncRead + AsyncWrite + Unpin + 'static,
|
I: AsyncRead + AsyncWrite + Unpin + 'static,
|
||||||
B: Payload + 'static,
|
B: Payload + 'static,
|
||||||
@@ -614,16 +605,14 @@ where
|
|||||||
pending.manual();
|
pending.manual();
|
||||||
}
|
}
|
||||||
return Poll::Ready(Ok(()));
|
return Poll::Ready(Ok(()));
|
||||||
},
|
|
||||||
Err(e) => {
|
|
||||||
match *e.kind() {
|
|
||||||
Kind::Parse(Parse::VersionH2) if self.fallback.to_h2() => {
|
|
||||||
self.upgrade_h2();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
_ => return Poll::Ready(Err(e)),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Err(e) => match *e.kind() {
|
||||||
|
Kind::Parse(Parse::VersionH2) if self.fallback.to_h2() => {
|
||||||
|
self.upgrade_h2();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
_ => return Poll::Ready(Err(e)),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -634,8 +623,7 @@ where
|
|||||||
S: HttpService<Body>,
|
S: HttpService<Body>,
|
||||||
{
|
{
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.debug_struct("Connection")
|
f.debug_struct("Connection").finish()
|
||||||
.finish()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ===== impl Serve =====
|
// ===== impl Serve =====
|
||||||
@@ -655,25 +643,23 @@ impl<I, S, E> Serve<I, S, E> {
|
|||||||
|
|
||||||
/// Spawn all incoming connections onto the executor in `Http`.
|
/// Spawn all incoming connections onto the executor in `Http`.
|
||||||
pub(super) fn spawn_all(self) -> SpawnAll<I, S, E> {
|
pub(super) fn spawn_all(self) -> SpawnAll<I, S, E> {
|
||||||
SpawnAll {
|
SpawnAll { serve: self }
|
||||||
serve: self,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
impl<I, IO, IE, S, B, E> Serve<I, S, E>
|
impl<I, IO, IE, S, B, E> Serve<I, S, E>
|
||||||
where
|
where
|
||||||
I: Accept<Conn=IO, Error=IE>,
|
I: Accept<Conn = IO, Error = IE>,
|
||||||
IO: AsyncRead + AsyncWrite + Unpin,
|
IO: AsyncRead + AsyncWrite + Unpin,
|
||||||
IE: Into<Box<dyn StdError + Send + Sync>>,
|
IE: Into<Box<dyn StdError + Send + Sync>>,
|
||||||
S: MakeServiceRef<IO, Body, ResBody=B>,
|
S: MakeServiceRef<IO, Body, ResBody = B>,
|
||||||
B: Payload,
|
B: Payload,
|
||||||
E: H2Exec<<S::Service as HttpService<Body>>::Future, B>,
|
E: H2Exec<<S::Service as HttpService<Body>>::Future, B>,
|
||||||
{
|
{
|
||||||
fn poll_next_(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Option<crate::Result<Connecting<IO, S::Future, E>>>> {
|
fn poll_next_(
|
||||||
|
self: Pin<&mut Self>,
|
||||||
|
cx: &mut task::Context<'_>,
|
||||||
|
) -> Poll<Option<crate::Result<Connecting<IO, S::Future, E>>>> {
|
||||||
let me = self.project();
|
let me = self.project();
|
||||||
match ready!(me.make_service.poll_ready_ref(cx)) {
|
match ready!(me.make_service.poll_ready_ref(cx)) {
|
||||||
Ok(()) => (),
|
Ok(()) => (),
|
||||||
@@ -700,10 +686,10 @@ where
|
|||||||
// deprecated
|
// deprecated
|
||||||
impl<I, IO, IE, S, B, E> Stream for Serve<I, S, E>
|
impl<I, IO, IE, S, B, E> Stream for Serve<I, S, E>
|
||||||
where
|
where
|
||||||
I: Accept<Conn=IO, Error=IE>,
|
I: Accept<Conn = IO, Error = IE>,
|
||||||
IO: AsyncRead + AsyncWrite + Unpin,
|
IO: AsyncRead + AsyncWrite + Unpin,
|
||||||
IE: Into<Box<dyn StdError + Send + Sync>>,
|
IE: Into<Box<dyn StdError + Send + Sync>>,
|
||||||
S: MakeServiceRef<IO, Body, ResBody=B>,
|
S: MakeServiceRef<IO, Body, ResBody = B>,
|
||||||
B: Payload,
|
B: Payload,
|
||||||
E: H2Exec<<S::Service as HttpService<Body>>::Future, B>,
|
E: H2Exec<<S::Service as HttpService<Body>>::Future, B>,
|
||||||
{
|
{
|
||||||
@@ -716,12 +702,11 @@ where
|
|||||||
|
|
||||||
// ===== impl Connecting =====
|
// ===== impl Connecting =====
|
||||||
|
|
||||||
|
|
||||||
impl<I, F, S, FE, E, B> Future for Connecting<I, F, E>
|
impl<I, F, S, FE, E, B> Future for Connecting<I, F, E>
|
||||||
where
|
where
|
||||||
I: AsyncRead + AsyncWrite + Unpin,
|
I: AsyncRead + AsyncWrite + Unpin,
|
||||||
F: Future<Output=Result<S, FE>>,
|
F: Future<Output = Result<S, FE>>,
|
||||||
S: HttpService<Body, ResBody=B>,
|
S: HttpService<Body, ResBody = B>,
|
||||||
B: Payload,
|
B: Payload,
|
||||||
B::Data: Unpin,
|
B::Data: Unpin,
|
||||||
E: H2Exec<S::Future, B>,
|
E: H2Exec<S::Future, B>,
|
||||||
@@ -753,18 +738,18 @@ impl<I, S, E> SpawnAll<I, S, E> {
|
|||||||
|
|
||||||
impl<I, IO, IE, S, B, E> SpawnAll<I, S, E>
|
impl<I, IO, IE, S, B, E> SpawnAll<I, S, E>
|
||||||
where
|
where
|
||||||
I: Accept<Conn=IO, Error=IE>,
|
I: Accept<Conn = IO, Error = IE>,
|
||||||
IE: Into<Box<dyn StdError + Send + Sync>>,
|
IE: Into<Box<dyn StdError + Send + Sync>>,
|
||||||
IO: AsyncRead + AsyncWrite + Unpin + Send + 'static,
|
IO: AsyncRead + AsyncWrite + Unpin + Send + 'static,
|
||||||
S: MakeServiceRef<
|
S: MakeServiceRef<IO, Body, ResBody = B>,
|
||||||
IO,
|
|
||||||
Body,
|
|
||||||
ResBody=B,
|
|
||||||
>,
|
|
||||||
B: Payload,
|
B: Payload,
|
||||||
E: H2Exec<<S::Service as HttpService<Body>>::Future, B>,
|
E: H2Exec<<S::Service as HttpService<Body>>::Future, B>,
|
||||||
{
|
{
|
||||||
pub(super) fn poll_watch<W>(self: Pin<&mut Self>, cx: &mut task::Context<'_>, watcher: &W) -> Poll<crate::Result<()>>
|
pub(super) fn poll_watch<W>(
|
||||||
|
self: Pin<&mut Self>,
|
||||||
|
cx: &mut task::Context<'_>,
|
||||||
|
watcher: &W,
|
||||||
|
) -> Poll<crate::Result<()>>
|
||||||
where
|
where
|
||||||
E: NewSvcExec<IO, S::Future, S::Service, E, W>,
|
E: NewSvcExec<IO, S::Future, S::Service, E, W>,
|
||||||
W: Watcher<IO, S::Service, E>,
|
W: Watcher<IO, S::Service, E>,
|
||||||
@@ -773,7 +758,12 @@ where
|
|||||||
loop {
|
loop {
|
||||||
if let Some(connecting) = ready!(me.serve.as_mut().poll_next_(cx)?) {
|
if let Some(connecting) = ready!(me.serve.as_mut().poll_next_(cx)?) {
|
||||||
let fut = NewSvcTask::new(connecting, watcher.clone());
|
let fut = NewSvcTask::new(connecting, watcher.clone());
|
||||||
me.serve.as_mut().project().protocol.exec.execute_new_svc(fut);
|
me.serve
|
||||||
|
.as_mut()
|
||||||
|
.project()
|
||||||
|
.protocol
|
||||||
|
.exec
|
||||||
|
.execute_new_svc(fut);
|
||||||
} else {
|
} else {
|
||||||
return Poll::Ready(Ok(()));
|
return Poll::Ready(Ok(()));
|
||||||
}
|
}
|
||||||
@@ -808,11 +798,11 @@ pub(crate) mod spawn_all {
|
|||||||
use std::error::Error as StdError;
|
use std::error::Error as StdError;
|
||||||
use tokio::io::{AsyncRead, AsyncWrite};
|
use tokio::io::{AsyncRead, AsyncWrite};
|
||||||
|
|
||||||
|
use super::{Connecting, UpgradeableConnection};
|
||||||
use crate::body::{Body, Payload};
|
use crate::body::{Body, Payload};
|
||||||
use crate::common::exec::H2Exec;
|
use crate::common::exec::H2Exec;
|
||||||
use crate::common::{Future, Pin, Poll, Unpin, task};
|
use crate::common::{task, Future, Pin, Poll, Unpin};
|
||||||
use crate::service::HttpService;
|
use crate::service::HttpService;
|
||||||
use super::{Connecting, UpgradeableConnection};
|
|
||||||
use pin_project::{pin_project, project};
|
use pin_project::{pin_project, project};
|
||||||
|
|
||||||
// Used by `SpawnAll` to optionally watch a `Connection` future.
|
// Used by `SpawnAll` to optionally watch a `Connection` future.
|
||||||
@@ -881,9 +871,9 @@ pub(crate) mod spawn_all {
|
|||||||
impl<I, N, S, NE, B, E, W> Future for NewSvcTask<I, N, S, E, W>
|
impl<I, N, S, NE, B, E, W> Future for NewSvcTask<I, N, S, E, W>
|
||||||
where
|
where
|
||||||
I: AsyncRead + AsyncWrite + Unpin + Send + 'static,
|
I: AsyncRead + AsyncWrite + Unpin + Send + 'static,
|
||||||
N: Future<Output=Result<S, NE>>,
|
N: Future<Output = Result<S, NE>>,
|
||||||
NE: Into<Box<dyn StdError + Send + Sync>>,
|
NE: Into<Box<dyn StdError + Send + Sync>>,
|
||||||
S: HttpService<Body, ResBody=B>,
|
S: HttpService<Body, ResBody = B>,
|
||||||
B: Payload,
|
B: Payload,
|
||||||
B::Data: Unpin,
|
B::Data: Unpin,
|
||||||
E: H2Exec<S::Future, B>,
|
E: H2Exec<S::Future, B>,
|
||||||
@@ -914,15 +904,13 @@ pub(crate) mod spawn_all {
|
|||||||
};
|
};
|
||||||
let connected = watcher.watch(conn.with_upgrades());
|
let connected = watcher.watch(conn.with_upgrades());
|
||||||
State::Connected(connected)
|
State::Connected(connected)
|
||||||
},
|
}
|
||||||
State::Connected(future) => {
|
State::Connected(future) => {
|
||||||
return future
|
return future.poll(cx).map(|res| {
|
||||||
.poll(cx)
|
if let Err(err) = res {
|
||||||
.map(|res| {
|
debug!("connection error: {}", err);
|
||||||
if let Err(err) = res {
|
}
|
||||||
debug!("connection error: {}", err);
|
});
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -951,7 +939,7 @@ mod upgrades {
|
|||||||
|
|
||||||
impl<I, B, S, E> UpgradeableConnection<I, S, E>
|
impl<I, B, S, E> UpgradeableConnection<I, S, E>
|
||||||
where
|
where
|
||||||
S: HttpService<Body, ResBody=B>,
|
S: HttpService<Body, ResBody = B>,
|
||||||
S::Error: Into<Box<dyn StdError + Send + Sync>>,
|
S::Error: Into<Box<dyn StdError + Send + Sync>>,
|
||||||
I: AsyncRead + AsyncWrite + Unpin,
|
I: AsyncRead + AsyncWrite + Unpin,
|
||||||
B: Payload + 'static,
|
B: Payload + 'static,
|
||||||
@@ -969,7 +957,7 @@ mod upgrades {
|
|||||||
|
|
||||||
impl<I, B, S, E> Future for UpgradeableConnection<I, S, E>
|
impl<I, B, S, E> Future for UpgradeableConnection<I, S, E>
|
||||||
where
|
where
|
||||||
S: HttpService<Body, ResBody=B>,
|
S: HttpService<Body, ResBody = B>,
|
||||||
S::Error: Into<Box<dyn StdError + Send + Sync>>,
|
S::Error: Into<Box<dyn StdError + Send + Sync>>,
|
||||||
I: AsyncRead + AsyncWrite + Unpin + Send + 'static,
|
I: AsyncRead + AsyncWrite + Unpin + Send + 'static,
|
||||||
B: Payload + 'static,
|
B: Payload + 'static,
|
||||||
@@ -991,19 +979,16 @@ mod upgrades {
|
|||||||
let (io, buf, _) = h1.into_inner();
|
let (io, buf, _) = h1.into_inner();
|
||||||
pending.fulfill(Upgraded::new(Box::new(io), buf));
|
pending.fulfill(Upgraded::new(Box::new(io), buf));
|
||||||
return Poll::Ready(Ok(()));
|
return Poll::Ready(Ok(()));
|
||||||
},
|
|
||||||
Err(e) => {
|
|
||||||
match *e.kind() {
|
|
||||||
Kind::Parse(Parse::VersionH2) if self.inner.fallback.to_h2() => {
|
|
||||||
self.inner.upgrade_h2();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
_ => return Poll::Ready(Err(e)),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Err(e) => match *e.kind() {
|
||||||
|
Kind::Parse(Parse::VersionH2) if self.inner.fallback.to_h2() => {
|
||||||
|
self.inner.upgrade_h2();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
_ => return Poll::Ready(Err(e)),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -51,27 +51,31 @@
|
|||||||
pub mod accept;
|
pub mod accept;
|
||||||
pub mod conn;
|
pub mod conn;
|
||||||
mod shutdown;
|
mod shutdown;
|
||||||
#[cfg(feature = "tcp")] mod tcp;
|
#[cfg(feature = "tcp")]
|
||||||
|
mod tcp;
|
||||||
|
|
||||||
use std::error::Error as StdError;
|
use std::error::Error as StdError;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
#[cfg(feature = "tcp")] use std::net::{SocketAddr, TcpListener as StdTcpListener};
|
#[cfg(feature = "tcp")]
|
||||||
|
use std::net::{SocketAddr, TcpListener as StdTcpListener};
|
||||||
|
|
||||||
#[cfg(feature = "tcp")] use std::time::Duration;
|
#[cfg(feature = "tcp")]
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use tokio::io::{AsyncRead, AsyncWrite};
|
|
||||||
use pin_project::pin_project;
|
use pin_project::pin_project;
|
||||||
|
use tokio::io::{AsyncRead, AsyncWrite};
|
||||||
|
|
||||||
|
use self::accept::Accept;
|
||||||
use crate::body::{Body, Payload};
|
use crate::body::{Body, Payload};
|
||||||
use crate::common::exec::{Exec, H2Exec, NewSvcExec};
|
use crate::common::exec::{Exec, H2Exec, NewSvcExec};
|
||||||
use crate::common::{Future, Pin, Poll, Unpin, task};
|
use crate::common::{task, Future, Pin, Poll, Unpin};
|
||||||
use crate::service::{MakeServiceRef, HttpService};
|
use crate::service::{HttpService, MakeServiceRef};
|
||||||
use self::accept::Accept;
|
|
||||||
// Renamed `Http` as `Http_` for now so that people upgrading don't see an
|
// Renamed `Http` as `Http_` for now so that people upgrading don't see an
|
||||||
// error that `hyper::server::Http` is private...
|
// error that `hyper::server::Http` is private...
|
||||||
use self::conn::{Http as Http_, NoopWatcher, SpawnAll};
|
use self::conn::{Http as Http_, NoopWatcher, SpawnAll};
|
||||||
use self::shutdown::{Graceful, GracefulWatcher};
|
use self::shutdown::{Graceful, GracefulWatcher};
|
||||||
#[cfg(feature = "tcp")] use self::tcp::AddrIncoming;
|
#[cfg(feature = "tcp")]
|
||||||
|
use self::tcp::AddrIncoming;
|
||||||
|
|
||||||
/// A listening HTTP server that accepts connections in both HTTP1 and HTTP2 by default.
|
/// A listening HTTP server that accepts connections in both HTTP1 and HTTP2 by default.
|
||||||
///
|
///
|
||||||
@@ -113,23 +117,20 @@ impl Server<AddrIncoming, ()> {
|
|||||||
/// This method will panic if binding to the address fails. For a method
|
/// 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`.
|
/// to bind to an address and return a `Result`, see `Server::try_bind`.
|
||||||
pub fn bind(addr: &SocketAddr) -> Builder<AddrIncoming> {
|
pub fn bind(addr: &SocketAddr) -> Builder<AddrIncoming> {
|
||||||
let incoming = AddrIncoming::new(addr)
|
let incoming = AddrIncoming::new(addr).unwrap_or_else(|e| {
|
||||||
.unwrap_or_else(|e| {
|
panic!("error binding to {}: {}", addr, e);
|
||||||
panic!("error binding to {}: {}", addr, e);
|
});
|
||||||
});
|
|
||||||
Server::builder(incoming)
|
Server::builder(incoming)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tries to bind to the provided address, and returns a [`Builder`](Builder).
|
/// Tries to bind to the provided address, and returns a [`Builder`](Builder).
|
||||||
pub fn try_bind(addr: &SocketAddr) -> crate::Result<Builder<AddrIncoming>> {
|
pub fn try_bind(addr: &SocketAddr) -> crate::Result<Builder<AddrIncoming>> {
|
||||||
AddrIncoming::new(addr)
|
AddrIncoming::new(addr).map(Server::builder)
|
||||||
.map(Server::builder)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new instance from a `std::net::TcpListener` instance.
|
/// Create a new instance from a `std::net::TcpListener` instance.
|
||||||
pub fn from_tcp(listener: StdTcpListener) -> Result<Builder<AddrIncoming>, crate::Error> {
|
pub fn from_tcp(listener: StdTcpListener) -> Result<Builder<AddrIncoming>, crate::Error> {
|
||||||
AddrIncoming::from_std(listener)
|
AddrIncoming::from_std(listener).map(Server::builder)
|
||||||
.map(Server::builder)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,10 +144,10 @@ impl<S, E> Server<AddrIncoming, S, E> {
|
|||||||
|
|
||||||
impl<I, IO, IE, S, E, B> Server<I, S, E>
|
impl<I, IO, IE, S, E, B> Server<I, S, E>
|
||||||
where
|
where
|
||||||
I: Accept<Conn=IO, Error=IE>,
|
I: Accept<Conn = IO, Error = IE>,
|
||||||
IE: Into<Box<dyn StdError + Send + Sync>>,
|
IE: Into<Box<dyn StdError + Send + Sync>>,
|
||||||
IO: AsyncRead + AsyncWrite + Unpin + Send + 'static,
|
IO: AsyncRead + AsyncWrite + Unpin + Send + 'static,
|
||||||
S: MakeServiceRef<IO, Body, ResBody=B>,
|
S: MakeServiceRef<IO, Body, ResBody = B>,
|
||||||
S::Error: Into<Box<dyn StdError + Send + Sync>>,
|
S::Error: Into<Box<dyn StdError + Send + Sync>>,
|
||||||
B: Payload,
|
B: Payload,
|
||||||
B::Data: Unpin,
|
B::Data: Unpin,
|
||||||
@@ -191,7 +192,7 @@ where
|
|||||||
/// ```
|
/// ```
|
||||||
pub fn with_graceful_shutdown<F>(self, signal: F) -> Graceful<I, S, F, E>
|
pub fn with_graceful_shutdown<F>(self, signal: F) -> Graceful<I, S, F, E>
|
||||||
where
|
where
|
||||||
F: Future<Output=()>
|
F: Future<Output = ()>,
|
||||||
{
|
{
|
||||||
Graceful::new(self.spawn_all, signal)
|
Graceful::new(self.spawn_all, signal)
|
||||||
}
|
}
|
||||||
@@ -199,10 +200,10 @@ where
|
|||||||
|
|
||||||
impl<I, IO, IE, S, B, E> Future for Server<I, S, E>
|
impl<I, IO, IE, S, B, E> Future for Server<I, S, E>
|
||||||
where
|
where
|
||||||
I: Accept<Conn=IO, Error=IE>,
|
I: Accept<Conn = IO, Error = IE>,
|
||||||
IE: Into<Box<dyn StdError + Send + Sync>>,
|
IE: Into<Box<dyn StdError + Send + Sync>>,
|
||||||
IO: AsyncRead + AsyncWrite + Unpin + Send + 'static,
|
IO: AsyncRead + AsyncWrite + Unpin + Send + 'static,
|
||||||
S: MakeServiceRef<IO, Body, ResBody=B>,
|
S: MakeServiceRef<IO, Body, ResBody = B>,
|
||||||
S::Error: Into<Box<dyn StdError + Send + Sync>>,
|
S::Error: Into<Box<dyn StdError + Send + Sync>>,
|
||||||
B: Payload,
|
B: Payload,
|
||||||
B::Data: Unpin,
|
B::Data: Unpin,
|
||||||
@@ -231,10 +232,7 @@ impl<I, E> Builder<I, E> {
|
|||||||
///
|
///
|
||||||
/// For a more convenient constructor, see [`Server::bind`](Server::bind).
|
/// For a more convenient constructor, see [`Server::bind`](Server::bind).
|
||||||
pub fn new(incoming: I, protocol: Http_<E>) -> Self {
|
pub fn new(incoming: I, protocol: Http_<E>) -> Self {
|
||||||
Builder {
|
Builder { incoming, protocol }
|
||||||
incoming,
|
|
||||||
protocol,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets whether to use keep-alive for HTTP/1 connections.
|
/// Sets whether to use keep-alive for HTTP/1 connections.
|
||||||
@@ -245,7 +243,6 @@ impl<I, E> Builder<I, E> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Set whether HTTP/1 connections should support half-closures.
|
/// Set whether HTTP/1 connections should support half-closures.
|
||||||
///
|
///
|
||||||
/// Clients can chose to shutdown their write-side while waiting
|
/// Clients can chose to shutdown their write-side while waiting
|
||||||
@@ -319,7 +316,8 @@ impl<I, E> Builder<I, E> {
|
|||||||
///
|
///
|
||||||
/// If not set, hyper will use a default.
|
/// If not set, hyper will use a default.
|
||||||
pub fn http2_initial_connection_window_size(mut self, sz: impl Into<Option<u32>>) -> Self {
|
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.protocol
|
||||||
|
.http2_initial_connection_window_size(sz.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -387,7 +385,7 @@ impl<I, E> Builder<I, E> {
|
|||||||
I: Accept,
|
I: Accept,
|
||||||
I::Error: Into<Box<dyn StdError + Send + Sync>>,
|
I::Error: Into<Box<dyn StdError + Send + Sync>>,
|
||||||
I::Conn: AsyncRead + AsyncWrite + Unpin + Send + 'static,
|
I::Conn: AsyncRead + AsyncWrite + Unpin + Send + 'static,
|
||||||
S: MakeServiceRef<I::Conn, Body, ResBody=B>,
|
S: MakeServiceRef<I::Conn, Body, ResBody = B>,
|
||||||
S::Error: Into<Box<dyn StdError + Send + Sync>>,
|
S::Error: Into<Box<dyn StdError + Send + Sync>>,
|
||||||
B: Payload,
|
B: Payload,
|
||||||
B::Data: Unpin,
|
B::Data: Unpin,
|
||||||
@@ -396,9 +394,7 @@ impl<I, E> Builder<I, E> {
|
|||||||
{
|
{
|
||||||
let serve = self.protocol.serve(self.incoming, new_service);
|
let serve = self.protocol.serve(self.incoming, new_service);
|
||||||
let spawn_all = serve.spawn_all();
|
let spawn_all = serve.spawn_all();
|
||||||
Server {
|
Server { spawn_all }
|
||||||
spawn_all,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -440,4 +436,3 @@ impl<E> Builder<AddrIncoming, E> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
use std::error::Error as StdError;
|
use std::error::Error as StdError;
|
||||||
|
|
||||||
use tokio::io::{AsyncRead, AsyncWrite};
|
|
||||||
use pin_project::{pin_project, project};
|
use pin_project::{pin_project, project};
|
||||||
|
use tokio::io::{AsyncRead, AsyncWrite};
|
||||||
|
|
||||||
|
use super::conn::{SpawnAll, UpgradeableConnection, Watcher};
|
||||||
|
use super::Accept;
|
||||||
use crate::body::{Body, Payload};
|
use crate::body::{Body, Payload};
|
||||||
use crate::common::drain::{self, Draining, Signal, Watch, Watching};
|
use crate::common::drain::{self, Draining, Signal, Watch, Watching};
|
||||||
use crate::common::exec::{H2Exec, NewSvcExec};
|
use crate::common::exec::{H2Exec, NewSvcExec};
|
||||||
use crate::common::{Future, Pin, Poll, Unpin, task};
|
use crate::common::{task, Future, Pin, Poll, Unpin};
|
||||||
use crate::service::{MakeServiceRef, HttpService};
|
use crate::service::{HttpService, MakeServiceRef};
|
||||||
use super::Accept;
|
|
||||||
use super::conn::{SpawnAll, UpgradeableConnection, Watcher};
|
|
||||||
|
|
||||||
#[allow(missing_debug_implementations)]
|
#[allow(missing_debug_implementations)]
|
||||||
#[pin_project]
|
#[pin_project]
|
||||||
@@ -43,17 +43,16 @@ impl<I, S, F, E> Graceful<I, S, F, E> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl<I, IO, IE, S, B, F, E> Future for Graceful<I, S, F, E>
|
impl<I, IO, IE, S, B, F, E> Future for Graceful<I, S, F, E>
|
||||||
where
|
where
|
||||||
I: Accept<Conn=IO, Error=IE>,
|
I: Accept<Conn = IO, Error = IE>,
|
||||||
IE: Into<Box<dyn StdError + Send + Sync>>,
|
IE: Into<Box<dyn StdError + Send + Sync>>,
|
||||||
IO: AsyncRead + AsyncWrite + Unpin + Send + 'static,
|
IO: AsyncRead + AsyncWrite + Unpin + Send + 'static,
|
||||||
S: MakeServiceRef<IO, Body, ResBody=B>,
|
S: MakeServiceRef<IO, Body, ResBody = B>,
|
||||||
S::Error: Into<Box<dyn StdError + Send + Sync>>,
|
S::Error: Into<Box<dyn StdError + Send + Sync>>,
|
||||||
B: Payload,
|
B: Payload,
|
||||||
B::Data: Unpin,
|
B::Data: Unpin,
|
||||||
F: Future<Output=()>,
|
F: Future<Output = ()>,
|
||||||
E: H2Exec<<S::Service as HttpService<Body>>::Future, B>,
|
E: H2Exec<<S::Service as HttpService<Body>>::Future, B>,
|
||||||
E: NewSvcExec<IO, S::Future, S::Service, E, GracefulWatcher>,
|
E: NewSvcExec<IO, S::Future, S::Service, E, GracefulWatcher>,
|
||||||
{
|
{
|
||||||
@@ -73,20 +72,13 @@ where
|
|||||||
} => match signal.poll(cx) {
|
} => match signal.poll(cx) {
|
||||||
Poll::Ready(()) => {
|
Poll::Ready(()) => {
|
||||||
debug!("signal received, starting graceful shutdown");
|
debug!("signal received, starting graceful shutdown");
|
||||||
let sig = drain
|
let sig = drain.take().expect("drain channel").0;
|
||||||
.take()
|
|
||||||
.expect("drain channel")
|
|
||||||
.0;
|
|
||||||
State::Draining(sig.drain())
|
State::Draining(sig.drain())
|
||||||
},
|
}
|
||||||
Poll::Pending => {
|
Poll::Pending => {
|
||||||
let watch = drain
|
let watch = drain.as_ref().expect("drain channel").1.clone();
|
||||||
.as_ref()
|
|
||||||
.expect("drain channel")
|
|
||||||
.1
|
|
||||||
.clone();
|
|
||||||
return spawn_all.poll_watch(cx, &GracefulWatcher(watch));
|
return spawn_all.poll_watch(cx, &GracefulWatcher(watch));
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
State::Draining(ref mut draining) => {
|
State::Draining(ref mut draining) => {
|
||||||
return Pin::new(draining).poll(cx).map(Ok);
|
return Pin::new(draining).poll(cx).map(Ok);
|
||||||
@@ -109,13 +101,11 @@ where
|
|||||||
<S::ResBody as Payload>::Data: Unpin,
|
<S::ResBody as Payload>::Data: Unpin,
|
||||||
E: H2Exec<S::Future, S::ResBody>,
|
E: H2Exec<S::Future, S::ResBody>,
|
||||||
{
|
{
|
||||||
type Future = Watching<UpgradeableConnection<I, S, E>, fn(Pin<&mut UpgradeableConnection<I, S, E>>)>;
|
type Future =
|
||||||
|
Watching<UpgradeableConnection<I, S, E>, fn(Pin<&mut UpgradeableConnection<I, S, E>>)>;
|
||||||
|
|
||||||
fn watch(&self, conn: UpgradeableConnection<I, S, E>) -> Self::Future {
|
fn watch(&self, conn: UpgradeableConnection<I, S, E>) -> Self::Future {
|
||||||
self
|
self.0.clone().watch(conn, on_drain)
|
||||||
.0
|
|
||||||
.clone()
|
|
||||||
.watch(conn, on_drain)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,4 +120,3 @@ where
|
|||||||
{
|
{
|
||||||
conn.graceful_shutdown()
|
conn.graceful_shutdown()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ use futures_util::FutureExt as _;
|
|||||||
use tokio::net::TcpListener;
|
use tokio::net::TcpListener;
|
||||||
use tokio::time::Delay;
|
use tokio::time::Delay;
|
||||||
|
|
||||||
use crate::common::{Future, Pin, Poll, task};
|
use crate::common::{task, Future, Pin, Poll};
|
||||||
|
|
||||||
use super::Accept;
|
|
||||||
pub use self::addr_stream::AddrStream;
|
pub use self::addr_stream::AddrStream;
|
||||||
|
use super::Accept;
|
||||||
|
|
||||||
/// A stream of connections from binding to an address.
|
/// A stream of connections from binding to an address.
|
||||||
#[must_use = "streams do nothing unless polled"]
|
#[must_use = "streams do nothing unless polled"]
|
||||||
@@ -25,15 +25,13 @@ pub struct AddrIncoming {
|
|||||||
|
|
||||||
impl AddrIncoming {
|
impl AddrIncoming {
|
||||||
pub(super) fn new(addr: &SocketAddr) -> crate::Result<Self> {
|
pub(super) fn new(addr: &SocketAddr) -> crate::Result<Self> {
|
||||||
let std_listener = StdTcpListener::bind(addr)
|
let std_listener = StdTcpListener::bind(addr).map_err(crate::Error::new_listen)?;
|
||||||
.map_err(crate::Error::new_listen)?;
|
|
||||||
|
|
||||||
AddrIncoming::from_std(std_listener)
|
AddrIncoming::from_std(std_listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn from_std(std_listener: StdTcpListener) -> crate::Result<Self> {
|
pub(super) fn from_std(std_listener: StdTcpListener) -> crate::Result<Self> {
|
||||||
let listener = TcpListener::from_std(std_listener)
|
let listener = TcpListener::from_std(std_listener).map_err(crate::Error::new_listen)?;
|
||||||
.map_err(crate::Error::new_listen)?;
|
|
||||||
let addr = listener.local_addr().map_err(crate::Error::new_listen)?;
|
let addr = listener.local_addr().map_err(crate::Error::new_listen)?;
|
||||||
Ok(AddrIncoming {
|
Ok(AddrIncoming {
|
||||||
listener,
|
listener,
|
||||||
@@ -115,7 +113,7 @@ impl AddrIncoming {
|
|||||||
trace!("error trying to set TCP nodelay: {}", e);
|
trace!("error trying to set TCP nodelay: {}", e);
|
||||||
}
|
}
|
||||||
return Poll::Ready(Ok(AddrStream::new(socket, addr)));
|
return Poll::Ready(Ok(AddrStream::new(socket, addr)));
|
||||||
},
|
}
|
||||||
Poll::Pending => return Poll::Pending,
|
Poll::Pending => return Poll::Pending,
|
||||||
Poll::Ready(Err(e)) => {
|
Poll::Ready(Err(e)) => {
|
||||||
// Connection errors can be ignored directly, continue by
|
// Connection errors can be ignored directly, continue by
|
||||||
@@ -134,17 +132,17 @@ impl AddrIncoming {
|
|||||||
match Pin::new(&mut timeout).poll(cx) {
|
match Pin::new(&mut timeout).poll(cx) {
|
||||||
Poll::Ready(()) => {
|
Poll::Ready(()) => {
|
||||||
// Wow, it's been a second already? Ok then...
|
// Wow, it's been a second already? Ok then...
|
||||||
continue
|
continue;
|
||||||
},
|
}
|
||||||
Poll::Pending => {
|
Poll::Pending => {
|
||||||
self.timeout = Some(timeout);
|
self.timeout = Some(timeout);
|
||||||
return Poll::Pending;
|
return Poll::Pending;
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return Poll::Ready(Err(e));
|
return Poll::Ready(Err(e));
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -154,7 +152,10 @@ impl Accept for AddrIncoming {
|
|||||||
type Conn = AddrStream;
|
type Conn = AddrStream;
|
||||||
type Error = io::Error;
|
type Error = io::Error;
|
||||||
|
|
||||||
fn poll_accept(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Option<Result<Self::Conn, Self::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));
|
let result = ready!(self.poll_next_(cx));
|
||||||
Poll::Ready(Some(result))
|
Poll::Ready(Some(result))
|
||||||
}
|
}
|
||||||
@@ -169,9 +170,9 @@ impl Accept for AddrIncoming {
|
|||||||
/// and EMFILE. Otherwise, could enter into tight loop.
|
/// and EMFILE. Otherwise, could enter into tight loop.
|
||||||
fn is_connection_error(e: &io::Error) -> bool {
|
fn is_connection_error(e: &io::Error) -> bool {
|
||||||
match e.kind() {
|
match e.kind() {
|
||||||
io::ErrorKind::ConnectionRefused |
|
io::ErrorKind::ConnectionRefused
|
||||||
io::ErrorKind::ConnectionAborted |
|
| io::ErrorKind::ConnectionAborted
|
||||||
io::ErrorKind::ConnectionReset => true,
|
| io::ErrorKind::ConnectionReset => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -188,14 +189,13 @@ impl fmt::Debug for AddrIncoming {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mod addr_stream {
|
mod addr_stream {
|
||||||
|
use bytes::{Buf, BufMut};
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use bytes::{Buf, BufMut};
|
|
||||||
use tokio::net::TcpStream;
|
|
||||||
use tokio::io::{AsyncRead, AsyncWrite};
|
use tokio::io::{AsyncRead, AsyncWrite};
|
||||||
|
use tokio::net::TcpStream;
|
||||||
|
|
||||||
use crate::common::{Pin, Poll, task};
|
use crate::common::{task, Pin, Poll};
|
||||||
|
|
||||||
|
|
||||||
/// A transport returned yieled by `AddrIncoming`.
|
/// A transport returned yieled by `AddrIncoming`.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -226,29 +226,48 @@ mod addr_stream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AsyncRead for AddrStream {
|
impl AsyncRead for AddrStream {
|
||||||
unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [std::mem::MaybeUninit<u8>]) -> bool {
|
unsafe fn prepare_uninitialized_buffer(
|
||||||
|
&self,
|
||||||
|
buf: &mut [std::mem::MaybeUninit<u8>],
|
||||||
|
) -> bool {
|
||||||
self.inner.prepare_uninitialized_buffer(buf)
|
self.inner.prepare_uninitialized_buffer(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn poll_read(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>, buf: &mut [u8]) -> Poll<io::Result<usize>> {
|
fn poll_read(
|
||||||
|
mut self: Pin<&mut Self>,
|
||||||
|
cx: &mut task::Context<'_>,
|
||||||
|
buf: &mut [u8],
|
||||||
|
) -> Poll<io::Result<usize>> {
|
||||||
Pin::new(&mut self.inner).poll_read(cx, buf)
|
Pin::new(&mut self.inner).poll_read(cx, buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn poll_read_buf<B: BufMut>(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>, buf: &mut B) -> Poll<io::Result<usize>> {
|
fn poll_read_buf<B: BufMut>(
|
||||||
|
mut self: Pin<&mut Self>,
|
||||||
|
cx: &mut task::Context<'_>,
|
||||||
|
buf: &mut B,
|
||||||
|
) -> Poll<io::Result<usize>> {
|
||||||
Pin::new(&mut self.inner).poll_read_buf(cx, buf)
|
Pin::new(&mut self.inner).poll_read_buf(cx, buf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsyncWrite for AddrStream {
|
impl AsyncWrite for AddrStream {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn poll_write(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>, buf: &[u8]) -> Poll<io::Result<usize>> {
|
fn poll_write(
|
||||||
|
mut self: Pin<&mut Self>,
|
||||||
|
cx: &mut task::Context<'_>,
|
||||||
|
buf: &[u8],
|
||||||
|
) -> Poll<io::Result<usize>> {
|
||||||
Pin::new(&mut self.inner).poll_write(cx, buf)
|
Pin::new(&mut self.inner).poll_write(cx, buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn poll_write_buf<B: Buf>(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>, buf: &mut B) -> Poll<io::Result<usize>> {
|
fn poll_write_buf<B: Buf>(
|
||||||
|
mut self: Pin<&mut Self>,
|
||||||
|
cx: &mut task::Context<'_>,
|
||||||
|
buf: &mut B,
|
||||||
|
) -> Poll<io::Result<usize>> {
|
||||||
Pin::new(&mut self.inner).poll_write_buf(cx, buf)
|
Pin::new(&mut self.inner).poll_write_buf(cx, buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -259,7 +278,10 @@ mod addr_stream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<io::Result<()>> {
|
fn poll_shutdown(
|
||||||
|
mut self: Pin<&mut Self>,
|
||||||
|
cx: &mut task::Context<'_>,
|
||||||
|
) -> Poll<io::Result<()>> {
|
||||||
Pin::new(&mut self.inner).poll_shutdown(cx)
|
Pin::new(&mut self.inner).poll_shutdown(cx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use std::error::Error as StdError;
|
use std::error::Error as StdError;
|
||||||
|
|
||||||
use crate::body::Payload;
|
use crate::body::Payload;
|
||||||
use crate::common::{Future, Poll, task};
|
use crate::common::{task, Future, Poll};
|
||||||
use crate::{Request, Response};
|
use crate::{Request, Response};
|
||||||
|
|
||||||
/// An asynchronous function from `Request` to `Response`.
|
/// An asynchronous function from `Request` to `Response`.
|
||||||
@@ -17,7 +17,7 @@ pub trait HttpService<ReqBody>: sealed::Sealed<ReqBody> {
|
|||||||
type Error: Into<Box<dyn StdError + Send + Sync>>;
|
type Error: Into<Box<dyn StdError + Send + Sync>>;
|
||||||
|
|
||||||
/// The `Future` returned by this `Service`.
|
/// The `Future` returned by this `Service`.
|
||||||
type Future: Future<Output=Result<Response<Self::ResBody>, Self::Error>>;
|
type Future: Future<Output = Result<Response<Self::ResBody>, Self::Error>>;
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
fn poll_ready(&mut self, cx: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>>;
|
fn poll_ready(&mut self, cx: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>>;
|
||||||
@@ -50,9 +50,9 @@ impl<T, B1, B2> sealed::Sealed<B1> for T
|
|||||||
where
|
where
|
||||||
T: tower_service::Service<Request<B1>, Response = Response<B2>>,
|
T: tower_service::Service<Request<B1>, Response = Response<B2>>,
|
||||||
B2: Payload,
|
B2: Payload,
|
||||||
{}
|
{
|
||||||
|
}
|
||||||
|
|
||||||
mod sealed {
|
mod sealed {
|
||||||
pub trait Sealed<T> {}
|
pub trait Sealed<T> {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ use std::fmt;
|
|||||||
|
|
||||||
use tokio::io::{AsyncRead, AsyncWrite};
|
use tokio::io::{AsyncRead, AsyncWrite};
|
||||||
|
|
||||||
use crate::body::Payload;
|
|
||||||
use crate::common::{Future, Poll, task};
|
|
||||||
use super::{HttpService, Service};
|
use super::{HttpService, Service};
|
||||||
|
use crate::body::Payload;
|
||||||
|
use crate::common::{task, Future, Poll};
|
||||||
|
|
||||||
// The same "trait alias" as tower::MakeConnection, but inlined to reduce
|
// The same "trait alias" as tower::MakeConnection, but inlined to reduce
|
||||||
// dependencies.
|
// dependencies.
|
||||||
@@ -44,13 +44,9 @@ where
|
|||||||
pub trait MakeServiceRef<Target, ReqBody>: self::sealed::Sealed<(Target, ReqBody)> {
|
pub trait MakeServiceRef<Target, ReqBody>: self::sealed::Sealed<(Target, ReqBody)> {
|
||||||
type ResBody: Payload;
|
type ResBody: Payload;
|
||||||
type Error: Into<Box<dyn StdError + Send + Sync>>;
|
type Error: Into<Box<dyn StdError + Send + Sync>>;
|
||||||
type Service: HttpService<
|
type Service: HttpService<ReqBody, ResBody = Self::ResBody, Error = Self::Error>;
|
||||||
ReqBody,
|
|
||||||
ResBody=Self::ResBody,
|
|
||||||
Error=Self::Error,
|
|
||||||
>;
|
|
||||||
type MakeError: Into<Box<dyn StdError + Send + Sync>>;
|
type MakeError: Into<Box<dyn StdError + Send + Sync>>;
|
||||||
type Future: Future<Output=Result<Self::Service, Self::MakeError>>;
|
type Future: Future<Output = Result<Self::Service, Self::MakeError>>;
|
||||||
|
|
||||||
// Acting like a #[non_exhaustive] for associated types of this trait.
|
// Acting like a #[non_exhaustive] for associated types of this trait.
|
||||||
//
|
//
|
||||||
@@ -70,11 +66,11 @@ pub trait MakeServiceRef<Target, ReqBody>: self::sealed::Sealed<(Target, ReqBody
|
|||||||
|
|
||||||
impl<T, Target, E, ME, S, F, IB, OB> MakeServiceRef<Target, IB> for T
|
impl<T, Target, E, ME, S, F, IB, OB> MakeServiceRef<Target, IB> for T
|
||||||
where
|
where
|
||||||
T: for<'a> Service<&'a Target, Error=ME, Response=S, Future=F>,
|
T: for<'a> Service<&'a Target, Error = ME, Response = S, Future = F>,
|
||||||
E: Into<Box<dyn StdError + Send + Sync>>,
|
E: Into<Box<dyn StdError + Send + Sync>>,
|
||||||
ME: Into<Box<dyn StdError + Send + Sync>>,
|
ME: Into<Box<dyn StdError + Send + Sync>>,
|
||||||
S: HttpService<IB, ResBody=OB, Error=E>,
|
S: HttpService<IB, ResBody = OB, Error = E>,
|
||||||
F: Future<Output=Result<S, ME>>,
|
F: Future<Output = Result<S, ME>>,
|
||||||
IB: Payload,
|
IB: Payload,
|
||||||
OB: Payload,
|
OB: Payload,
|
||||||
{
|
{
|
||||||
@@ -145,9 +141,7 @@ where
|
|||||||
F: FnMut(&Target) -> Ret,
|
F: FnMut(&Target) -> Ret,
|
||||||
Ret: Future,
|
Ret: Future,
|
||||||
{
|
{
|
||||||
MakeServiceFn {
|
MakeServiceFn { f }
|
||||||
f,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not exported from crate as this will likely be replaced with `impl Service`.
|
// Not exported from crate as this will likely be replaced with `impl Service`.
|
||||||
@@ -158,7 +152,7 @@ pub struct MakeServiceFn<F> {
|
|||||||
impl<'t, F, Ret, Target, Svc, MkErr> Service<&'t Target> for MakeServiceFn<F>
|
impl<'t, F, Ret, Target, Svc, MkErr> Service<&'t Target> for MakeServiceFn<F>
|
||||||
where
|
where
|
||||||
F: FnMut(&Target) -> Ret,
|
F: FnMut(&Target) -> Ret,
|
||||||
Ret: Future<Output=Result<Svc, MkErr>>,
|
Ret: Future<Output = Result<Svc, MkErr>>,
|
||||||
MkErr: Into<Box<dyn StdError + Send + Sync>>,
|
MkErr: Into<Box<dyn StdError + Send + Sync>>,
|
||||||
{
|
{
|
||||||
type Error = MkErr;
|
type Error = MkErr;
|
||||||
@@ -176,8 +170,7 @@ where
|
|||||||
|
|
||||||
impl<F> fmt::Debug for MakeServiceFn<F> {
|
impl<F> fmt::Debug for MakeServiceFn<F> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.debug_struct("MakeServiceFn")
|
f.debug_struct("MakeServiceFn").finish()
|
||||||
.finish()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,10 +41,9 @@ mod make;
|
|||||||
mod oneshot;
|
mod oneshot;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
pub(crate) use self::make::{MakeConnection, MakeServiceRef};
|
|
||||||
pub(crate) use self::http::HttpService;
|
pub(crate) use self::http::HttpService;
|
||||||
|
pub(crate) use self::make::{MakeConnection, MakeServiceRef};
|
||||||
pub(crate) use self::oneshot::{oneshot, Oneshot};
|
pub(crate) use self::oneshot::{oneshot, Oneshot};
|
||||||
|
|
||||||
pub use self::make::make_service_fn;
|
pub use self::make::make_service_fn;
|
||||||
pub use self::util::service_fn;
|
pub use self::util::service_fn;
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
// TODO: Eventually to be replaced with tower_util::Oneshot.
|
// TODO: Eventually to be replaced with tower_util::Oneshot.
|
||||||
|
|
||||||
use std::mem;
|
|
||||||
use std::marker::Unpin;
|
use std::marker::Unpin;
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
use tower_service::Service;
|
use tower_service::Service;
|
||||||
|
|
||||||
use crate::common::{Future, Pin, Poll, task};
|
use crate::common::{task, Future, Pin, Poll};
|
||||||
|
|
||||||
pub(crate) fn oneshot<S, Req>(svc: S, req: Req) -> Oneshot<S, Req>
|
pub(crate) fn oneshot<S, Req>(svc: S, req: Req) -> Oneshot<S, Req>
|
||||||
where
|
where
|
||||||
@@ -35,7 +35,8 @@ impl<S, Req> Unpin for Oneshot<S, Req>
|
|||||||
where
|
where
|
||||||
S: Service<Req>,
|
S: Service<Req>,
|
||||||
S::Future: Unpin,
|
S::Future: Unpin,
|
||||||
{}
|
{
|
||||||
|
}
|
||||||
|
|
||||||
impl<S, Req> Future for Oneshot<S, Req>
|
impl<S, Req> Future for Oneshot<S, Req>
|
||||||
where
|
where
|
||||||
@@ -52,17 +53,17 @@ where
|
|||||||
State::NotReady(ref mut svc, _) => {
|
State::NotReady(ref mut svc, _) => {
|
||||||
ready!(svc.poll_ready(cx))?;
|
ready!(svc.poll_ready(cx))?;
|
||||||
// fallthrough out of the match's borrow
|
// fallthrough out of the match's borrow
|
||||||
},
|
}
|
||||||
State::Called(ref mut fut) => {
|
State::Called(ref mut fut) => {
|
||||||
return unsafe { Pin::new_unchecked(fut) }.poll(cx);
|
return unsafe { Pin::new_unchecked(fut) }.poll(cx);
|
||||||
},
|
}
|
||||||
State::Tmp => unreachable!(),
|
State::Tmp => unreachable!(),
|
||||||
}
|
}
|
||||||
|
|
||||||
match mem::replace(&mut me.state, State::Tmp) {
|
match mem::replace(&mut me.state, State::Tmp) {
|
||||||
State::NotReady(mut svc, req) => {
|
State::NotReady(mut svc, req) => {
|
||||||
me.state = State::Called(svc.call(req));
|
me.state = State::Called(svc.call(req));
|
||||||
},
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use std::fmt;
|
|||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use crate::body::Payload;
|
use crate::body::Payload;
|
||||||
use crate::common::{Future, Poll, task};
|
use crate::common::{task, Future, Poll};
|
||||||
use crate::{Request, Response};
|
use crate::{Request, Response};
|
||||||
|
|
||||||
/// Create a `Service` from a function.
|
/// Create a `Service` from a function.
|
||||||
@@ -41,11 +41,12 @@ pub struct ServiceFn<F, R> {
|
|||||||
_req: PhantomData<fn(R)>,
|
_req: PhantomData<fn(R)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F, ReqBody, Ret, ResBody, E> tower_service::Service<crate::Request<ReqBody>> for ServiceFn<F, ReqBody>
|
impl<F, ReqBody, Ret, ResBody, E> tower_service::Service<crate::Request<ReqBody>>
|
||||||
|
for ServiceFn<F, ReqBody>
|
||||||
where
|
where
|
||||||
F: FnMut(Request<ReqBody>) -> Ret,
|
F: FnMut(Request<ReqBody>) -> Ret,
|
||||||
ReqBody: Payload,
|
ReqBody: Payload,
|
||||||
Ret: Future<Output=Result<Response<ResBody>, E>>,
|
Ret: Future<Output = Result<Response<ResBody>, E>>,
|
||||||
E: Into<Box<dyn StdError + Send + Sync>>,
|
E: Into<Box<dyn StdError + Send + Sync>>,
|
||||||
ResBody: Payload,
|
ResBody: Payload,
|
||||||
{
|
{
|
||||||
@@ -64,7 +65,6 @@ where
|
|||||||
|
|
||||||
impl<F, R> fmt::Debug for ServiceFn<F, R> {
|
impl<F, R> fmt::Debug for ServiceFn<F, R> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.debug_struct("impl Service")
|
f.debug_struct("impl Service").finish()
|
||||||
.finish()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,12 +11,12 @@ use std::fmt;
|
|||||||
use std::io;
|
use std::io;
|
||||||
use std::marker::Unpin;
|
use std::marker::Unpin;
|
||||||
|
|
||||||
use bytes::{/*Buf, BufMut, */Bytes};
|
use bytes::Bytes;
|
||||||
use tokio::io::{AsyncRead, AsyncWrite};
|
use tokio::io::{AsyncRead, AsyncWrite};
|
||||||
use tokio::sync::oneshot;
|
use tokio::sync::oneshot;
|
||||||
|
|
||||||
use crate::common::io::Rewind;
|
use crate::common::io::Rewind;
|
||||||
use crate::common::{Future, Pin, Poll, task};
|
use crate::common::{task, Future, Pin, Poll};
|
||||||
|
|
||||||
/// An upgraded HTTP connection.
|
/// An upgraded HTTP connection.
|
||||||
///
|
///
|
||||||
@@ -58,7 +58,7 @@ pub struct Parts<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct Pending {
|
pub(crate) struct Pending {
|
||||||
tx: oneshot::Sender<crate::Result<Upgraded>>
|
tx: oneshot::Sender<crate::Result<Upgraded>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Error cause returned when an upgrade was expected but canceled
|
/// Error cause returned when an upgrade was expected but canceled
|
||||||
@@ -70,14 +70,7 @@ struct UpgradeExpected(());
|
|||||||
|
|
||||||
pub(crate) fn pending() -> (Pending, OnUpgrade) {
|
pub(crate) fn pending() -> (Pending, OnUpgrade) {
|
||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
(
|
(Pending { tx }, OnUpgrade { rx: Some(rx) })
|
||||||
Pending {
|
|
||||||
tx,
|
|
||||||
},
|
|
||||||
OnUpgrade {
|
|
||||||
rx: Some(rx),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) trait Io: AsyncRead + AsyncWrite + Unpin + 'static {
|
pub(crate) trait Io: AsyncRead + AsyncWrite + Unpin + 'static {
|
||||||
@@ -130,7 +123,7 @@ impl Upgraded {
|
|||||||
}),
|
}),
|
||||||
Err(io) => Err(Upgraded {
|
Err(io) => Err(Upgraded {
|
||||||
io: Rewind::new_buffered(io, buf),
|
io: Rewind::new_buffered(io, buf),
|
||||||
})
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -140,13 +133,21 @@ impl AsyncRead for Upgraded {
|
|||||||
self.io.prepare_uninitialized_buffer(buf)
|
self.io.prepare_uninitialized_buffer(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll_read(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>, buf: &mut [u8]) -> Poll<io::Result<usize>> {
|
fn poll_read(
|
||||||
|
mut self: Pin<&mut Self>,
|
||||||
|
cx: &mut task::Context<'_>,
|
||||||
|
buf: &mut [u8],
|
||||||
|
) -> Poll<io::Result<usize>> {
|
||||||
Pin::new(&mut self.io).poll_read(cx, buf)
|
Pin::new(&mut self.io).poll_read(cx, buf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsyncWrite for Upgraded {
|
impl AsyncWrite for Upgraded {
|
||||||
fn poll_write(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>, buf: &[u8]) -> Poll<io::Result<usize>> {
|
fn poll_write(
|
||||||
|
mut self: Pin<&mut Self>,
|
||||||
|
cx: &mut task::Context<'_>,
|
||||||
|
buf: &[u8],
|
||||||
|
) -> Poll<io::Result<usize>> {
|
||||||
Pin::new(&mut self.io).poll_write(cx, buf)
|
Pin::new(&mut self.io).poll_write(cx, buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,8 +162,7 @@ impl AsyncWrite for Upgraded {
|
|||||||
|
|
||||||
impl fmt::Debug for Upgraded {
|
impl fmt::Debug for Upgraded {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.debug_struct("Upgraded")
|
f.debug_struct("Upgraded").finish()
|
||||||
.finish()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,9 +170,7 @@ impl fmt::Debug for Upgraded {
|
|||||||
|
|
||||||
impl OnUpgrade {
|
impl OnUpgrade {
|
||||||
pub(crate) fn none() -> Self {
|
pub(crate) fn none() -> Self {
|
||||||
OnUpgrade {
|
OnUpgrade { rx: None }
|
||||||
rx: None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn is_none(&self) -> bool {
|
pub(crate) fn is_none(&self) -> bool {
|
||||||
@@ -188,9 +186,9 @@ impl Future for OnUpgrade {
|
|||||||
Some(ref mut rx) => Pin::new(rx).poll(cx).map(|res| match res {
|
Some(ref mut rx) => Pin::new(rx).poll(cx).map(|res| match res {
|
||||||
Ok(Ok(upgraded)) => Ok(upgraded),
|
Ok(Ok(upgraded)) => Ok(upgraded),
|
||||||
Ok(Err(err)) => Err(err),
|
Ok(Err(err)) => Err(err),
|
||||||
Err(_oneshot_canceled) => Err(
|
Err(_oneshot_canceled) => {
|
||||||
crate::Error::new_canceled().with(UpgradeExpected(()))
|
Err(crate::Error::new_canceled().with(UpgradeExpected(())))
|
||||||
),
|
}
|
||||||
}),
|
}),
|
||||||
None => Poll::Ready(Err(crate::Error::new_user_no_upgrade())),
|
None => Poll::Ready(Err(crate::Error::new_user_no_upgrade())),
|
||||||
}
|
}
|
||||||
@@ -199,8 +197,7 @@ impl Future for OnUpgrade {
|
|||||||
|
|
||||||
impl fmt::Debug for OnUpgrade {
|
impl fmt::Debug for OnUpgrade {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.debug_struct("OnUpgrade")
|
f.debug_struct("OnUpgrade").finish()
|
||||||
.finish()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,4 +230,3 @@ impl StdError for UpgradeExpected {
|
|||||||
"upgrade expected but not completed"
|
"upgrade expected but not completed"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
499
tests/client.rs
499
tests/client.rs
File diff suppressed because it is too large
Load Diff
@@ -337,4 +337,3 @@ t! {
|
|||||||
http2_parallel_10,
|
http2_parallel_10,
|
||||||
parallel: 0..10
|
parallel: 0..10
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
628
tests/server.rs
628
tests/server.rs
File diff suppressed because it is too large
Load Diff
@@ -1,15 +1,20 @@
|
|||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::sync::{Arc, Mutex, atomic::{AtomicUsize, Ordering}};
|
use std::sync::{
|
||||||
use std::time::{Duration/*, Instant*/};
|
atomic::{AtomicUsize, Ordering},
|
||||||
|
Arc, Mutex,
|
||||||
|
};
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use hyper::{Body, Client, Request, Response, Server, Version};
|
|
||||||
use hyper::client::HttpConnector;
|
use hyper::client::HttpConnector;
|
||||||
use hyper::service::{make_service_fn, service_fn};
|
use hyper::service::{make_service_fn, service_fn};
|
||||||
|
use hyper::{Body, Client, Request, Response, Server, Version};
|
||||||
|
|
||||||
pub use std::net::SocketAddr;
|
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 _,
|
||||||
|
};
|
||||||
pub use hyper::{HeaderMap, StatusCode};
|
pub use hyper::{HeaderMap, StatusCode};
|
||||||
|
pub use std::net::SocketAddr;
|
||||||
|
|
||||||
macro_rules! t {
|
macro_rules! t {
|
||||||
(
|
(
|
||||||
@@ -154,43 +159,43 @@ macro_rules! t {
|
|||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! __internal_map_prop {
|
macro_rules! __internal_map_prop {
|
||||||
(headers: $map:tt) => ({
|
(headers: $map:tt) => {{
|
||||||
#[allow(unused_mut)]
|
#[allow(unused_mut)]
|
||||||
{
|
{
|
||||||
let mut headers = HeaderMap::new();
|
let mut headers = HeaderMap::new();
|
||||||
__internal_headers_map!(headers, $map);
|
__internal_headers_map!(headers, $map);
|
||||||
headers
|
headers
|
||||||
}
|
}
|
||||||
});
|
}};
|
||||||
($name:tt: $val:tt) => ({
|
($name:tt: $val:tt) => {{
|
||||||
__internal_req_res_prop!($name: $val)
|
__internal_req_res_prop!($name: $val)
|
||||||
});
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! __internal_eq_prop {
|
macro_rules! __internal_eq_prop {
|
||||||
(headers: $map:tt) => ({
|
(headers: $map:tt) => {{
|
||||||
#[allow(unused_mut)]
|
#[allow(unused_mut)]
|
||||||
{
|
{
|
||||||
let mut headers = Vec::new();
|
let mut headers = Vec::new();
|
||||||
__internal_headers_eq!(headers, $map);
|
__internal_headers_eq!(headers, $map);
|
||||||
headers
|
headers
|
||||||
}
|
}
|
||||||
});
|
}};
|
||||||
($name:tt: $val:tt) => ({
|
($name:tt: $val:tt) => {{
|
||||||
__internal_req_res_prop!($name: $val)
|
__internal_req_res_prop!($name: $val)
|
||||||
});
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! __internal_req_res_prop {
|
macro_rules! __internal_req_res_prop {
|
||||||
(method: $prop_val:expr) => (
|
(method: $prop_val:expr) => {
|
||||||
$prop_val
|
$prop_val
|
||||||
);
|
};
|
||||||
(status: $prop_val:expr) => (
|
(status: $prop_val:expr) => {
|
||||||
StatusCode::from_u16($prop_val).expect("status code")
|
StatusCode::from_u16($prop_val).expect("status code")
|
||||||
);
|
};
|
||||||
($prop_name:ident: $prop_val:expr) => (
|
($prop_name:ident: $prop_val:expr) => {
|
||||||
From::from($prop_val)
|
From::from($prop_val)
|
||||||
)
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! __internal_headers_map {
|
macro_rules! __internal_headers_map {
|
||||||
@@ -325,9 +330,7 @@ async fn async_test(cfg: __TestConfig) {
|
|||||||
.http2_only(cfg.client_version == 2)
|
.http2_only(cfg.client_version == 2)
|
||||||
.build::<_, Body>(connector);
|
.build::<_, Body>(connector);
|
||||||
|
|
||||||
let serve_handles = Arc::new(Mutex::new(
|
let serve_handles = Arc::new(Mutex::new(cfg.server_msgs));
|
||||||
cfg.server_msgs
|
|
||||||
));
|
|
||||||
|
|
||||||
let expected_connections = cfg.connections;
|
let expected_connections = cfg.connections;
|
||||||
let mut cnt = 0;
|
let mut cnt = 0;
|
||||||
@@ -343,9 +346,7 @@ async fn async_test(cfg: __TestConfig) {
|
|||||||
// Move a clone into the service_fn
|
// Move a clone into the service_fn
|
||||||
let serve_handles = serve_handles.clone();
|
let serve_handles = serve_handles.clone();
|
||||||
future::ok::<_, hyper::Error>(service_fn(move |req: Request<Body>| {
|
future::ok::<_, hyper::Error>(service_fn(move |req: Request<Body>| {
|
||||||
let (sreq, sres) = serve_handles.lock()
|
let (sreq, sres) = serve_handles.lock().unwrap().remove(0);
|
||||||
.unwrap()
|
|
||||||
.remove(0);
|
|
||||||
|
|
||||||
assert_eq!(req.uri().path(), sreq.uri, "client path");
|
assert_eq!(req.uri().path(), sreq.uri, "client path");
|
||||||
assert_eq!(req.method(), &sreq.method, "client method");
|
assert_eq!(req.method(), &sreq.method, "client method");
|
||||||
@@ -354,17 +355,16 @@ async fn async_test(cfg: __TestConfig) {
|
|||||||
func(&req.headers());
|
func(&req.headers());
|
||||||
}
|
}
|
||||||
let sbody = sreq.body;
|
let sbody = sreq.body;
|
||||||
concat(req.into_body())
|
concat(req.into_body()).map_ok(move |body| {
|
||||||
.map_ok(move |body| {
|
assert_eq!(body.as_ref(), sbody.as_slice(), "client body");
|
||||||
assert_eq!(body.as_ref(), sbody.as_slice(), "client body");
|
|
||||||
|
|
||||||
let mut res = Response::builder()
|
let mut res = Response::builder()
|
||||||
.status(sres.status)
|
.status(sres.status)
|
||||||
.body(Body::from(sres.body))
|
.body(Body::from(sres.body))
|
||||||
.expect("Response::build");
|
.expect("Response::build");
|
||||||
*res.headers_mut() = sres.headers;
|
*res.headers_mut() = sres.headers;
|
||||||
res
|
res
|
||||||
})
|
})
|
||||||
}))
|
}))
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -388,34 +388,36 @@ async fn async_test(cfg: __TestConfig) {
|
|||||||
addr = proxy_addr;
|
addr = proxy_addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
let make_request = Arc::new(move |client: &Client<HttpConnector>, creq: __CReq, cres: __CRes| {
|
let make_request = Arc::new(
|
||||||
let uri = format!("http://{}{}", addr, creq.uri);
|
move |client: &Client<HttpConnector>, creq: __CReq, cres: __CRes| {
|
||||||
let mut req = Request::builder()
|
let uri = format!("http://{}{}", addr, creq.uri);
|
||||||
.method(creq.method)
|
let mut req = Request::builder()
|
||||||
.uri(uri)
|
.method(creq.method)
|
||||||
//.headers(creq.headers)
|
.uri(uri)
|
||||||
.body(creq.body.into())
|
//.headers(creq.headers)
|
||||||
.expect("Request::build");
|
.body(creq.body.into())
|
||||||
*req.headers_mut() = creq.headers;
|
.expect("Request::build");
|
||||||
let cstatus = cres.status;
|
*req.headers_mut() = creq.headers;
|
||||||
let cheaders = cres.headers;
|
let cstatus = cres.status;
|
||||||
let cbody = cres.body;
|
let cheaders = cres.headers;
|
||||||
|
let cbody = cres.body;
|
||||||
client.request(req)
|
|
||||||
.and_then(move |res| {
|
|
||||||
assert_eq!(res.status(), cstatus, "server status");
|
|
||||||
assert_eq!(res.version(), version, "server version");
|
|
||||||
for func in &cheaders {
|
|
||||||
func(&res.headers());
|
|
||||||
}
|
|
||||||
concat(res.into_body())
|
|
||||||
})
|
|
||||||
.map_ok(move |body| {
|
|
||||||
assert_eq!(body.as_ref(), cbody.as_slice(), "server body");
|
|
||||||
})
|
|
||||||
.map(|res| res.expect("client error"))
|
|
||||||
});
|
|
||||||
|
|
||||||
|
client
|
||||||
|
.request(req)
|
||||||
|
.and_then(move |res| {
|
||||||
|
assert_eq!(res.status(), cstatus, "server status");
|
||||||
|
assert_eq!(res.version(), version, "server version");
|
||||||
|
for func in &cheaders {
|
||||||
|
func(&res.headers());
|
||||||
|
}
|
||||||
|
concat(res.into_body())
|
||||||
|
})
|
||||||
|
.map_ok(move |body| {
|
||||||
|
assert_eq!(body.as_ref(), cbody.as_slice(), "server body");
|
||||||
|
})
|
||||||
|
.map(|res| res.expect("client error"))
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
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![];
|
||||||
@@ -425,17 +427,14 @@ async fn async_test(cfg: __TestConfig) {
|
|||||||
drop(client);
|
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 = Client<HttpConnector>> + Send>> =
|
||||||
Box::pin(future::ready(client));
|
Box::pin(future::ready(client));
|
||||||
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 = Box::pin(client_futures.then(move |client| {
|
||||||
client_futures
|
let fut = mk_request(&client, creq, cres);
|
||||||
.then(move |client| {
|
fut.map(move |()| client)
|
||||||
let fut = mk_request(&client, creq, cres);
|
}));
|
||||||
fut.map(move |()| client)
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
Box::pin(client_futures.map(|_| ()))
|
Box::pin(client_futures.map(|_| ()))
|
||||||
};
|
};
|
||||||
@@ -459,19 +458,18 @@ fn naive_proxy(cfg: ProxyConfig) -> (SocketAddr, impl Future<Output = ()>) {
|
|||||||
let max_connections = cfg.connections;
|
let max_connections = cfg.connections;
|
||||||
let counter = AtomicUsize::new(0);
|
let counter = AtomicUsize::new(0);
|
||||||
|
|
||||||
let srv = Server::bind(&([127, 0, 0, 1], 0).into())
|
let srv = Server::bind(&([127, 0, 0, 1], 0).into()).serve(make_service_fn(move |_| {
|
||||||
.serve(make_service_fn(move |_| {
|
let prev = counter.fetch_add(1, Ordering::Relaxed);
|
||||||
let prev = counter.fetch_add(1, Ordering::Relaxed);
|
assert!(max_connections >= prev + 1, "proxy max connections");
|
||||||
assert!(max_connections >= prev + 1, "proxy max connections");
|
let client = client.clone();
|
||||||
let client = client.clone();
|
future::ok::<_, hyper::Error>(service_fn(move |mut req| {
|
||||||
future::ok::<_, hyper::Error>(service_fn(move |mut req| {
|
let uri = format!("http://{}{}", dst_addr, req.uri().path())
|
||||||
let uri = format!("http://{}{}", dst_addr, req.uri().path())
|
.parse()
|
||||||
.parse()
|
.expect("proxy new uri parse");
|
||||||
.expect("proxy new uri parse");
|
*req.uri_mut() = uri;
|
||||||
*req.uri_mut() = uri;
|
client.request(req)
|
||||||
client.request(req)
|
}))
|
||||||
}))
|
}));
|
||||||
}));
|
|
||||||
let proxy_addr = srv.local_addr();
|
let proxy_addr = srv.local_addr();
|
||||||
(proxy_addr, srv.map(|res| res.expect("proxy error")))
|
(proxy_addr, srv.map(|res| res.expect("proxy error")))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user