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
|
||||
|
||||
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:
|
||||
name: Test ${{ matrix.rust }} on ${{ matrix.os }}
|
||||
|
||||
needs: [style]
|
||||
strategy:
|
||||
matrix:
|
||||
rust:
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
disable_all_formatting = true
|
||||
@@ -3,11 +3,11 @@
|
||||
|
||||
extern crate test;
|
||||
|
||||
use http::Uri;
|
||||
use hyper::client::connect::HttpConnector;
|
||||
use hyper::service::Service;
|
||||
use std::net::SocketAddr;
|
||||
use tokio::net::TcpListener;
|
||||
use hyper::client::connect::{HttpConnector};
|
||||
use hyper::service::Service;
|
||||
use http::Uri;
|
||||
|
||||
#[bench]
|
||||
fn http_connector(b: &mut test::Bencher) {
|
||||
@@ -17,7 +17,9 @@ fn http_connector(b: &mut test::Bencher) {
|
||||
.basic_scheduler()
|
||||
.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 dst: Uri = format!("http://{}/", addr).parse().expect("uri parse");
|
||||
let mut connector = HttpConnector::new();
|
||||
@@ -28,7 +30,6 @@ fn http_connector(b: &mut test::Bencher) {
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
b.iter(|| {
|
||||
rt.block_on(async {
|
||||
connector.call(dst.clone()).await.expect("connect");
|
||||
|
||||
@@ -7,15 +7,14 @@ use std::net::SocketAddr;
|
||||
|
||||
use futures_util::future::join_all;
|
||||
|
||||
use hyper::{body::HttpBody as _, Body, Method, Request, Response, Server};
|
||||
use hyper::client::HttpConnector;
|
||||
use hyper::{body::HttpBody as _, Body, Method, Request, Response, Server};
|
||||
|
||||
// HTTP1
|
||||
|
||||
#[bench]
|
||||
fn http1_get(b: &mut test::Bencher) {
|
||||
opts()
|
||||
.bench(b)
|
||||
opts().bench(b)
|
||||
}
|
||||
|
||||
#[bench]
|
||||
@@ -48,9 +47,7 @@ fn http1_body_both_10mb(b: &mut test::Bencher) {
|
||||
|
||||
#[bench]
|
||||
fn http1_parallel_x10_empty(b: &mut test::Bencher) {
|
||||
opts()
|
||||
.parallel(10)
|
||||
.bench(b)
|
||||
opts().parallel(10).bench(b)
|
||||
}
|
||||
|
||||
#[bench]
|
||||
@@ -76,19 +73,13 @@ fn http1_parallel_x10_req_10kb_100_chunks(b: &mut test::Bencher) {
|
||||
#[bench]
|
||||
fn http1_parallel_x10_res_1mb(b: &mut test::Bencher) {
|
||||
let body = &[b'x'; 1024 * 1024 * 1];
|
||||
opts()
|
||||
.parallel(10)
|
||||
.response_body(body)
|
||||
.bench(b)
|
||||
opts().parallel(10).response_body(body).bench(b)
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn http1_parallel_x10_res_10mb(b: &mut test::Bencher) {
|
||||
let body = &[b'x'; 1024 * 1024 * 10];
|
||||
opts()
|
||||
.parallel(10)
|
||||
.response_body(body)
|
||||
.bench(b)
|
||||
opts().parallel(10).response_body(body).bench(b)
|
||||
}
|
||||
|
||||
// HTTP2
|
||||
@@ -97,9 +88,7 @@ const HTTP2_MAX_WINDOW: u32 = std::u32::MAX >> 1;
|
||||
|
||||
#[bench]
|
||||
fn http2_get(b: &mut test::Bencher) {
|
||||
opts()
|
||||
.http2()
|
||||
.bench(b)
|
||||
opts().http2().bench(b)
|
||||
}
|
||||
|
||||
#[bench]
|
||||
@@ -123,10 +112,7 @@ fn http2_req_100kb(b: &mut test::Bencher) {
|
||||
|
||||
#[bench]
|
||||
fn http2_parallel_x10_empty(b: &mut test::Bencher) {
|
||||
opts()
|
||||
.http2()
|
||||
.parallel(10)
|
||||
.bench(b)
|
||||
opts().http2().parallel(10).bench(b)
|
||||
}
|
||||
|
||||
#[bench]
|
||||
@@ -293,9 +279,10 @@ impl Opts {
|
||||
let make_request = || {
|
||||
let chunk_cnt = self.request_chunks;
|
||||
let body = if chunk_cnt > 0 {
|
||||
|
||||
let (mut tx, body) = Body::channel();
|
||||
let chunk = self.request_body.expect("request_chunks means request_body");
|
||||
let chunk = self
|
||||
.request_body
|
||||
.expect("request_chunks means request_body");
|
||||
exec.spawn(async move {
|
||||
for _ in 0..chunk_cnt {
|
||||
tx.send_data(chunk.into()).await.expect("send_data");
|
||||
@@ -303,8 +290,7 @@ impl Opts {
|
||||
});
|
||||
body
|
||||
} else {
|
||||
self
|
||||
.request_body
|
||||
self.request_body
|
||||
.map(Body::from)
|
||||
.unwrap_or_else(|| Body::empty())
|
||||
};
|
||||
@@ -328,11 +314,9 @@ impl Opts {
|
||||
let req = make_request();
|
||||
rt.block_on(send_request(req));
|
||||
});
|
||||
|
||||
} else {
|
||||
b.iter(|| {
|
||||
let futs = (0..self.parallel_cnt)
|
||||
.map(|_| {
|
||||
let futs = (0..self.parallel_cnt).map(|_| {
|
||||
let req = make_request();
|
||||
send_request(req)
|
||||
});
|
||||
@@ -353,12 +337,16 @@ fn spawn_server(rt: &mut tokio::runtime::Runtime, opts: &Opts) -> SocketAddr {
|
||||
.http2_only(opts.http2)
|
||||
.http2_initial_stream_window_size(opts.http2_stream_window)
|
||||
.http2_initial_connection_window_size(opts.http2_conn_window)
|
||||
.serve(make_service_fn( move |_| async move {
|
||||
Ok::<_, hyper::Error>(service_fn(move |req: Request<Body>| async move {
|
||||
.serve(make_service_fn(move |_| {
|
||||
async move {
|
||||
Ok::<_, hyper::Error>(service_fn(move |req: Request<Body>| {
|
||||
async move {
|
||||
let mut req_body = req.into_body();
|
||||
while let Some(_chunk) = req_body.data().await {}
|
||||
Ok::<_, hyper::Error>(Response::new(Body::from(body)))
|
||||
}
|
||||
}))
|
||||
}
|
||||
}))
|
||||
});
|
||||
let addr = srv.local_addr();
|
||||
@@ -367,5 +355,5 @@ fn spawn_server(rt: &mut tokio::runtime::Runtime, opts: &Opts) -> SocketAddr {
|
||||
panic!("server error: {}", err);
|
||||
}
|
||||
});
|
||||
return addr
|
||||
return addr;
|
||||
}
|
||||
|
||||
@@ -4,14 +4,14 @@
|
||||
extern crate test;
|
||||
|
||||
use std::io::{Read, Write};
|
||||
use std::net::{TcpStream};
|
||||
use std::net::TcpStream;
|
||||
use std::sync::mpsc;
|
||||
use std::time::Duration;
|
||||
|
||||
use tokio::sync::oneshot;
|
||||
|
||||
use hyper::{Body, Response, Server};
|
||||
use hyper::service::{make_service_fn, service_fn};
|
||||
use hyper::{Body, Response, Server};
|
||||
|
||||
const PIPELINED_REQUESTS: usize = 16;
|
||||
|
||||
@@ -25,10 +25,12 @@ fn hello_world(b: &mut test::Bencher) {
|
||||
std::thread::spawn(move || {
|
||||
let addr = "127.0.0.1:0".parse().unwrap();
|
||||
|
||||
let make_svc = make_service_fn(|_| async {
|
||||
Ok::<_, hyper::Error>(service_fn(|_| async {
|
||||
Ok::<_, hyper::Error>(Response::new(Body::from("Hello, World!")))
|
||||
let make_svc = make_service_fn(|_| {
|
||||
async {
|
||||
Ok::<_, hyper::Error>(service_fn(|_| {
|
||||
async { Ok::<_, hyper::Error>(Response::new(Body::from("Hello, World!"))) }
|
||||
}))
|
||||
}
|
||||
});
|
||||
|
||||
let mut rt = tokio::runtime::Builder::new()
|
||||
@@ -44,8 +46,7 @@ fn hello_world(b: &mut test::Bencher) {
|
||||
|
||||
addr_tx.send(srv.local_addr()).unwrap();
|
||||
|
||||
let graceful = srv
|
||||
.with_graceful_shutdown(async {
|
||||
let graceful = srv.with_graceful_shutdown(async {
|
||||
until_rx.await.ok();
|
||||
});
|
||||
|
||||
@@ -66,7 +67,8 @@ fn hello_world(b: &mut test::Bencher) {
|
||||
|
||||
let total_bytes = {
|
||||
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();
|
||||
tcp.read_to_end(&mut buf).unwrap()
|
||||
} * PIPELINED_REQUESTS;
|
||||
@@ -85,4 +87,3 @@ fn hello_world(b: &mut test::Bencher) {
|
||||
assert_eq!(sum, total_bytes);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -11,26 +11,31 @@ use std::time::Duration;
|
||||
use futures_util::{stream, StreamExt};
|
||||
use tokio::sync::oneshot;
|
||||
|
||||
use hyper::{Body, Response, Server};
|
||||
use hyper::service::{make_service_fn, service_fn};
|
||||
use hyper::{Body, Response, Server};
|
||||
|
||||
macro_rules! bench_server {
|
||||
($b:ident, $header:expr, $body:expr) => ({
|
||||
($b:ident, $header:expr, $body:expr) => {{
|
||||
let _ = pretty_env_logger::try_init();
|
||||
let (_until_tx, until_rx) = oneshot::channel::<()>();
|
||||
let addr = {
|
||||
let (addr_tx, addr_rx) = mpsc::channel();
|
||||
std::thread::spawn(move || {
|
||||
let addr = "127.0.0.1:0".parse().unwrap();
|
||||
let make_svc = make_service_fn(|_| async {
|
||||
Ok::<_, hyper::Error>(service_fn(|_| async {
|
||||
Ok::<_, hyper::Error>(Response::builder()
|
||||
let make_svc = make_service_fn(|_| {
|
||||
async {
|
||||
Ok::<_, hyper::Error>(service_fn(|_| {
|
||||
async {
|
||||
Ok::<_, hyper::Error>(
|
||||
Response::builder()
|
||||
.header($header.0, $header.1)
|
||||
.header("content-type", "text/plain")
|
||||
.body($body())
|
||||
.unwrap()
|
||||
.unwrap(),
|
||||
)
|
||||
}
|
||||
}))
|
||||
}
|
||||
});
|
||||
|
||||
let mut rt = tokio::runtime::Builder::new()
|
||||
@@ -39,15 +44,11 @@ macro_rules! bench_server {
|
||||
.build()
|
||||
.expect("rt build");
|
||||
|
||||
let srv = rt.block_on(async move {
|
||||
Server::bind(&addr)
|
||||
.serve(make_svc)
|
||||
});
|
||||
let srv = rt.block_on(async move { Server::bind(&addr).serve(make_svc) });
|
||||
|
||||
addr_tx.send(srv.local_addr()).unwrap();
|
||||
|
||||
let graceful = srv
|
||||
.with_graceful_shutdown(async {
|
||||
let graceful = srv.with_graceful_shutdown(async {
|
||||
until_rx.await.ok();
|
||||
});
|
||||
rt.block_on(async move {
|
||||
@@ -62,7 +63,8 @@ macro_rules! bench_server {
|
||||
|
||||
let total_bytes = {
|
||||
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();
|
||||
tcp.read_to_end(&mut buf).unwrap()
|
||||
};
|
||||
@@ -73,14 +75,15 @@ macro_rules! bench_server {
|
||||
|
||||
$b.bytes = 35 + total_bytes as u64;
|
||||
$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;
|
||||
while sum < total_bytes {
|
||||
sum += tcp.read(&mut buf).unwrap();
|
||||
}
|
||||
assert_eq!(sum, total_bytes);
|
||||
});
|
||||
})
|
||||
}};
|
||||
}
|
||||
|
||||
fn body(b: &'static [u8]) -> hyper::Body {
|
||||
@@ -94,7 +97,9 @@ fn throughput_fixedsize_small_payload(b: &mut test::Bencher) {
|
||||
|
||||
#[bench]
|
||||
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]
|
||||
@@ -107,12 +112,16 @@ fn throughput_fixedsize_many_chunks(b: &mut test::Bencher) {
|
||||
|
||||
#[bench]
|
||||
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]
|
||||
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]
|
||||
@@ -134,14 +143,17 @@ fn raw_tcp_throughput_small_payload(b: &mut test::Bencher) {
|
||||
let mut buf = [0u8; 8192];
|
||||
while rx.try_recv().is_err() {
|
||||
sock.read(&mut buf).unwrap();
|
||||
sock.write_all(b"\
|
||||
sock.write_all(
|
||||
b"\
|
||||
HTTP/1.1 200 OK\r\n\
|
||||
Content-Length: 13\r\n\
|
||||
Content-Type: text/plain; charset=utf-8\r\n\
|
||||
Date: Fri, 12 May 2017 18:21:45 GMT\r\n\
|
||||
\r\n\
|
||||
Hello, World!\
|
||||
").unwrap();
|
||||
",
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -150,7 +162,8 @@ fn raw_tcp_throughput_small_payload(b: &mut test::Bencher) {
|
||||
|
||||
b.bytes = 130 + 35;
|
||||
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();
|
||||
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.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;
|
||||
while sum < expect_read {
|
||||
sum += tcp.read(&mut buf).unwrap();
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#![warn(rust_2018_idioms)]
|
||||
use std::env;
|
||||
|
||||
use hyper::{Client, body::HttpBody as _};
|
||||
use hyper::{body::HttpBody as _, Client};
|
||||
use tokio::io::{self, AsyncWriteExt as _};
|
||||
|
||||
// A simple type alias so as to DRY.
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
use hyper::Client;
|
||||
use futures_util::StreamExt;
|
||||
use hyper::Client;
|
||||
|
||||
// A simple type alias so as to DRY.
|
||||
type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
|
||||
|
||||
@@ -1,22 +1,20 @@
|
||||
//#![deny(warnings)]
|
||||
|
||||
use hyper::{Body, Method, Request, Response, Server, StatusCode};
|
||||
use hyper::service::{make_service_fn, service_fn};
|
||||
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
|
||||
/// path, and returns a Future of a Response.
|
||||
async fn echo(mut req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
|
||||
match (req.method(), req.uri().path()) {
|
||||
// Serve some instructions at /
|
||||
(&Method::GET, "/") => {
|
||||
Ok(Response::new(Body::from("Try POSTing data to /echo such as: `curl localhost:3000/echo -XPOST -d 'hello world'`")))
|
||||
}
|
||||
(&Method::GET, "/") => Ok(Response::new(Body::from(
|
||||
"Try POSTing data to /echo such as: `curl localhost:3000/echo -XPOST -d 'hello world'`",
|
||||
))),
|
||||
|
||||
// Simply echo the body back to the client.
|
||||
(&Method::POST, "/echo") => {
|
||||
Ok(Response::new(req.into_body()))
|
||||
}
|
||||
(&Method::POST, "/echo") => Ok(Response::new(req.into_body())),
|
||||
|
||||
// Convert to uppercase before sending back to client using a stream.
|
||||
(&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?);
|
||||
}
|
||||
|
||||
let reversed_body = whole_body
|
||||
.iter()
|
||||
.rev()
|
||||
.cloned()
|
||||
.collect::<Vec<u8>>();
|
||||
let reversed_body = whole_body.iter().rev().cloned().collect::<Vec<u8>>();
|
||||
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]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
let addr = ([127, 0, 0, 1], 3000).into();
|
||||
|
||||
let service = make_service_fn(|_| {
|
||||
async {
|
||||
Ok::<_, hyper::Error>(service_fn(echo))
|
||||
}
|
||||
});
|
||||
let service = make_service_fn(|_| async { Ok::<_, hyper::Error>(service_fn(echo)) });
|
||||
|
||||
let server = Server::bind(&addr)
|
||||
.serve(service);
|
||||
let server = Server::bind(&addr).serve(service);
|
||||
|
||||
println!("Listening on http://{}", addr);
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
use std::convert::Infallible;
|
||||
|
||||
use hyper::{Body, Request, Response, Server};
|
||||
use hyper::service::{make_service_fn, service_fn};
|
||||
use hyper::{Body, Request, Response, Server};
|
||||
|
||||
async fn hello(_: Request<Body>) -> Result<Response<Body>, Infallible> {
|
||||
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.
|
||||
// `service_fn` is a helper to convert a function that
|
||||
// returns a Response into a `Service`.
|
||||
async {
|
||||
Ok::<_, Infallible>(service_fn(hello))
|
||||
}
|
||||
async { Ok::<_, Infallible>(service_fn(hello)) }
|
||||
});
|
||||
|
||||
let addr = ([127, 0, 0, 1], 3000).into();
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#![deny(warnings)]
|
||||
#![warn(rust_2018_idioms)]
|
||||
|
||||
use hyper::{Body, Request, Response, Server};
|
||||
use hyper::service::{service_fn, make_service_fn};
|
||||
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 INDEX2: &'static [u8] = b"The 2nd service!";
|
||||
@@ -23,18 +23,12 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
let addr1 = ([127, 0, 0, 1], 1337).into();
|
||||
let addr2 = ([127, 0, 0, 1], 1338).into();
|
||||
|
||||
let srv1 = Server::bind(&addr1)
|
||||
.serve(make_service_fn(|_| {
|
||||
async {
|
||||
Ok::<_, hyper::Error>(service_fn(index1))
|
||||
}
|
||||
let srv1 = Server::bind(&addr1).serve(make_service_fn(|_| {
|
||||
async { Ok::<_, hyper::Error>(service_fn(index1)) }
|
||||
}));
|
||||
|
||||
let srv2 = Server::bind(&addr2)
|
||||
.serve(make_service_fn(|_| {
|
||||
async {
|
||||
Ok::<_, hyper::Error>(service_fn(index2))
|
||||
}
|
||||
let srv2 = Server::bind(&addr2).serve(make_service_fn(|_| {
|
||||
async { Ok::<_, hyper::Error>(service_fn(index2)) }
|
||||
}));
|
||||
|
||||
println!("Listening on http://{} and http://{}", addr1, addr2);
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
// #![deny(warnings)] // FIXME: https://github.com/rust-lang/rust/issues/62411
|
||||
#![warn(rust_2018_idioms)]
|
||||
|
||||
use hyper::service::{make_service_fn, service_fn};
|
||||
use hyper::{Body, Method, Request, Response, Server, StatusCode};
|
||||
use hyper::service::{service_fn, make_service_fn};
|
||||
|
||||
use futures_util::StreamExt;
|
||||
use std::collections::HashMap;
|
||||
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 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`.
|
||||
async fn param_example(mut req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
|
||||
match (req.method(), req.uri().path()) {
|
||||
(&Method::GET, "/") | (&Method::GET, "/post") => {
|
||||
Ok(Response::new(INDEX.into()))
|
||||
},
|
||||
(&Method::GET, "/") | (&Method::GET, "/post") => Ok(Response::new(INDEX.into())),
|
||||
(&Method::POST, "/post") => {
|
||||
// Concatenate the body...
|
||||
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
|
||||
// HashMap<String, Vec<String>>. However in this
|
||||
// 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
|
||||
// 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.
|
||||
let body = format!("Hello {}, your number is {}", name, number);
|
||||
Ok(Response::new(body.into()))
|
||||
},
|
||||
_ => {
|
||||
Ok(Response::builder()
|
||||
}
|
||||
_ => Ok(Response::builder()
|
||||
.status(StatusCode::NOT_FOUND)
|
||||
.body(Body::empty())
|
||||
.unwrap())
|
||||
}
|
||||
.unwrap()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,11 +85,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
|
||||
let addr = ([127, 0, 0, 1], 1337).into();
|
||||
|
||||
let server = Server::bind(&addr)
|
||||
.serve(make_service_fn(|_| {
|
||||
async {
|
||||
Ok::<_, hyper::Error>(service_fn(param_example))
|
||||
}
|
||||
let server = Server::bind(&addr).serve(make_service_fn(|_| {
|
||||
async { Ok::<_, hyper::Error>(service_fn(param_example)) }
|
||||
}));
|
||||
|
||||
println!("Listening on http://{}", addr);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#![deny(warnings)]
|
||||
|
||||
use hyper::{Client, Error, Server};
|
||||
use hyper::service::{make_service_fn, service_fn};
|
||||
use hyper::{Client, Error, Server};
|
||||
use std::net::SocketAddr;
|
||||
|
||||
#[tokio::main]
|
||||
@@ -25,9 +25,11 @@ async fn main() {
|
||||
// `service_fn` is a helper to convert a function that
|
||||
// returns a Response into a `Service`.
|
||||
Ok::<_, Error>(service_fn(move |mut req| {
|
||||
let uri_string = format!("http://{}/{}",
|
||||
let uri_string = format!(
|
||||
"http://{}/{}",
|
||||
out_addr_clone,
|
||||
req.uri().path_and_query().map(|x| x.as_str()).unwrap_or(""));
|
||||
req.uri().path_and_query().map(|x| x.as_str()).unwrap_or("")
|
||||
);
|
||||
let uri = uri_string.parse().unwrap();
|
||||
*req.uri_mut() = uri;
|
||||
client.request(req)
|
||||
@@ -35,8 +37,7 @@ async fn main() {
|
||||
}
|
||||
});
|
||||
|
||||
let server = Server::bind(&in_addr)
|
||||
.serve(make_service);
|
||||
let server = Server::bind(&in_addr).serve(make_service);
|
||||
|
||||
println!("Listening on http://{}", in_addr);
|
||||
println!("Proxying on http://{}", out_addr);
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#![deny(warnings)]
|
||||
|
||||
use tokio::io::AsyncReadExt;
|
||||
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::{Body, Method, Request, Response, Result, Server, StatusCode};
|
||||
|
||||
static INDEX: &str = "examples/send_file_index.html";
|
||||
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 make_service = make_service_fn(|_| async {
|
||||
Ok::<_, hyper::Error>(service_fn(response_examples))
|
||||
});
|
||||
let make_service =
|
||||
make_service_fn(|_| async { Ok::<_, hyper::Error>(service_fn(response_examples)) });
|
||||
|
||||
let server = Server::bind(&addr)
|
||||
.serve(make_service);
|
||||
let server = Server::bind(&addr).serve(make_service);
|
||||
|
||||
println!("Listening on http://{}", addr);
|
||||
|
||||
@@ -32,16 +30,14 @@ async fn main() {
|
||||
|
||||
async fn response_examples(req: Request<Body>) -> Result<Response<Body>> {
|
||||
match (req.method(), req.uri().path()) {
|
||||
(&Method::GET, "/") |
|
||||
(&Method::GET, "/index.html") |
|
||||
(&Method::GET, "/big_file.html") => {
|
||||
(&Method::GET, "/") | (&Method::GET, "/index.html") | (&Method::GET, "/big_file.html") => {
|
||||
simple_file_send(INDEX).await
|
||||
}
|
||||
(&Method::GET, "/no_file.html") => {
|
||||
// Test what happens when file cannot be be found
|
||||
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::rc::Rc;
|
||||
|
||||
use hyper::{Body, Error, Response, Server};
|
||||
use hyper::service::{make_service_fn, service_fn};
|
||||
use hyper::{Body, Error, Response, Server};
|
||||
|
||||
fn main() {
|
||||
pretty_env_logger::init();
|
||||
@@ -22,7 +22,6 @@ fn main() {
|
||||
}
|
||||
|
||||
async fn run() {
|
||||
|
||||
let addr = ([127, 0, 0, 1], 3000).into();
|
||||
|
||||
// Using a !Send request counter is fine on 1 thread...
|
||||
@@ -37,18 +36,12 @@ async fn run() {
|
||||
let prev = cnt.get();
|
||||
cnt.set(prev + 1);
|
||||
let value = cnt.get();
|
||||
async move {
|
||||
Ok::<_, Error>(Response::new(Body::from(
|
||||
format!("Request #{}", value)
|
||||
)))
|
||||
}
|
||||
async move { Ok::<_, Error>(Response::new(Body::from(format!("Request #{}", value)))) }
|
||||
}))
|
||||
}
|
||||
});
|
||||
|
||||
let server = Server::bind(&addr)
|
||||
.executor(LocalExec)
|
||||
.serve(make_service);
|
||||
let server = Server::bind(&addr).executor(LocalExec).serve(make_service);
|
||||
|
||||
println!("Listening on http://{}", addr);
|
||||
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
#![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::{Body, Error, Response, Server};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
@@ -34,17 +37,12 @@ async fn main() {
|
||||
// Get the current count, and also increment by 1, in a single
|
||||
// atomic operation.
|
||||
let count = counter.fetch_add(1, Ordering::AcqRel);
|
||||
async move {
|
||||
Ok::<_, Error>(
|
||||
Response::new(Body::from(format!("Request #{}", count)))
|
||||
)
|
||||
}
|
||||
async move { Ok::<_, Error>(Response::new(Body::from(format!("Request #{}", count)))) }
|
||||
}))
|
||||
}
|
||||
});
|
||||
|
||||
let server = Server::bind(&addr)
|
||||
.serve(make_service);
|
||||
let server = Server::bind(&addr).serve(make_service);
|
||||
|
||||
println!("Listening on http://{}", addr);
|
||||
|
||||
@@ -52,4 +50,3 @@ async fn main() {
|
||||
eprintln!("server error: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#![deny(warnings)]
|
||||
|
||||
use hyper::client::service::Connect;
|
||||
use hyper::client::conn::Builder;
|
||||
use hyper::client::connect::HttpConnector;
|
||||
use hyper::client::service::Connect;
|
||||
use hyper::service::Service;
|
||||
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 mut svc = mk_svc.call(uri.clone()).await?;
|
||||
|
||||
let body = Body::empty();
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
use futures_util::future;
|
||||
use hyper::{Body, Request, Response, Server};
|
||||
use hyper::service::Service;
|
||||
use hyper::{Body, Request, Response, Server};
|
||||
|
||||
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 server = Server::bind(&addr)
|
||||
.serve(MakeSvc);
|
||||
let server = Server::bind(&addr).serve(MakeSvc);
|
||||
|
||||
println!("Listening on http://{}", addr);
|
||||
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
// Note: `hyper::upgrade` docs link to this upgrade.
|
||||
use std::str;
|
||||
|
||||
use tokio::sync::oneshot;
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tokio::sync::oneshot;
|
||||
|
||||
use hyper::{Body, Client, Request, Response, Server, StatusCode};
|
||||
use hyper::header::{UPGRADE, HeaderValue};
|
||||
use hyper::header::{HeaderValue, UPGRADE};
|
||||
use hyper::service::{make_service_fn, service_fn};
|
||||
use hyper::upgrade::Upgraded;
|
||||
use hyper::{Body, Client, Request, Response, Server, StatusCode};
|
||||
use std::net::SocketAddr;
|
||||
|
||||
// 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)
|
||||
};
|
||||
}
|
||||
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
|
||||
// made-up 'foobar' protocol.
|
||||
*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)
|
||||
}
|
||||
|
||||
@@ -102,7 +103,7 @@ async fn client_upgrade_request(addr: SocketAddr) -> Result<()> {
|
||||
eprintln!("client foobar io error: {}", e)
|
||||
};
|
||||
}
|
||||
Err(e) => eprintln!("upgrade error: {}", e)
|
||||
Err(e) => eprintln!("upgrade error: {}", e),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -115,12 +116,10 @@ async fn main() {
|
||||
// unused port.
|
||||
let addr = ([127, 0, 0, 1], 0).into();
|
||||
|
||||
let make_service = make_service_fn(|_| async {
|
||||
Ok::<_, hyper::Error>(service_fn(server_upgrade))
|
||||
});
|
||||
let make_service =
|
||||
make_service_fn(|_| async { Ok::<_, hyper::Error>(service_fn(server_upgrade)) });
|
||||
|
||||
let server = Server::bind(&addr)
|
||||
.serve(make_service);
|
||||
let server = Server::bind(&addr).serve(make_service);
|
||||
|
||||
// We need the assigned address for the client to send it messages.
|
||||
let addr = server.local_addr();
|
||||
@@ -128,8 +127,7 @@ async fn main() {
|
||||
// For this example, a oneshot is used to signal that after 1 request,
|
||||
// the server should be shutdown.
|
||||
let (tx, rx) = oneshot::channel::<()>();
|
||||
let server = server
|
||||
.with_graceful_shutdown(async move {
|
||||
let server = server.with_graceful_shutdown(async move {
|
||||
rx.await.ok();
|
||||
});
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#![deny(warnings)]
|
||||
|
||||
use hyper::{Body, Chunk, Client, Method, Request, Response, Server, StatusCode, header};
|
||||
use futures_util::{StreamExt, TryStreamExt};
|
||||
use hyper::client::HttpConnector;
|
||||
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 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 URL: &str = "http://127.0.0.1:1337/json_api";
|
||||
|
||||
async fn client_request_response(
|
||||
client: &Client<HttpConnector>
|
||||
) -> Result<Response<Body>> {
|
||||
async fn client_request_response(client: &Client<HttpConnector>) -> Result<Response<Body>> {
|
||||
let req = Request::builder()
|
||||
.method(Method::POST)
|
||||
.uri(URL)
|
||||
@@ -27,9 +25,11 @@ async fn client_request_response(
|
||||
let web_res = client.request(req).await?;
|
||||
// Compare the JSON we sent (before) with what we received (after):
|
||||
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!(
|
||||
"<b>POST request body</b>: {}<br><b>Response</b>: {}",
|
||||
POST_DATA,
|
||||
std::str::from_utf8(&b).unwrap()))
|
||||
std::str::from_utf8(&b).unwrap()
|
||||
))
|
||||
}));
|
||||
|
||||
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>> {
|
||||
let data = vec!["foo", "bar"];
|
||||
let res = match serde_json::to_string(&data) {
|
||||
Ok(json) => {
|
||||
Response::builder()
|
||||
Ok(json) => Response::builder()
|
||||
.header(header::CONTENT_TYPE, "application/json")
|
||||
.body(Body::from(json))
|
||||
.unwrap()
|
||||
}
|
||||
Err(_) => {
|
||||
Response::builder()
|
||||
.unwrap(),
|
||||
Err(_) => Response::builder()
|
||||
.status(StatusCode::INTERNAL_SERVER_ERROR)
|
||||
.body(INTERNAL_SERVER_ERROR.into())
|
||||
.unwrap()
|
||||
}
|
||||
.unwrap(),
|
||||
};
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
async fn response_examples(
|
||||
req: Request<Body>,
|
||||
client: Client<HttpConnector>
|
||||
client: Client<HttpConnector>,
|
||||
) -> Result<Response<Body>> {
|
||||
match (req.method(), req.uri().path()) {
|
||||
(&Method::GET, "/") |
|
||||
(&Method::GET, "/index.html") => {
|
||||
Ok(Response::new(INDEX.into()))
|
||||
},
|
||||
(&Method::GET, "/test.html") => {
|
||||
client_request_response(&client).await
|
||||
},
|
||||
(&Method::POST, "/json_api") => {
|
||||
api_post_response(req).await
|
||||
},
|
||||
(&Method::GET, "/json_api") => {
|
||||
api_get_response().await
|
||||
}
|
||||
(&Method::GET, "/") | (&Method::GET, "/index.html") => Ok(Response::new(INDEX.into())),
|
||||
(&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.
|
||||
Ok(Response::builder()
|
||||
@@ -121,8 +108,7 @@ async fn main() -> Result<()> {
|
||||
}
|
||||
});
|
||||
|
||||
let server = Server::bind(&addr)
|
||||
.serve(new_service);
|
||||
let server = Server::bind(&addr).serve(new_service);
|
||||
|
||||
println!("Listening on http://{}", addr);
|
||||
|
||||
|
||||
@@ -4,15 +4,15 @@ use std::error::Error as StdError;
|
||||
use std::fmt;
|
||||
|
||||
use bytes::Bytes;
|
||||
use futures_core::Stream; // for mpsc::Receiver
|
||||
use futures_channel::{mpsc, oneshot};
|
||||
use futures_core::Stream; // for mpsc::Receiver
|
||||
#[cfg(feature = "stream")]
|
||||
use futures_util::TryStreamExt;
|
||||
use http_body::{SizeHint, Body as HttpBody};
|
||||
use http::HeaderMap;
|
||||
use http_body::{Body as HttpBody, SizeHint};
|
||||
|
||||
use crate::common::{Future, Never, Pin, Poll, task};
|
||||
use super::Chunk;
|
||||
use crate::common::{task, Future, Never, Pin, Poll};
|
||||
use crate::upgrade::OnUpgrade;
|
||||
|
||||
type BodySender = mpsc::Sender<Result<Chunk, crate::Error>>;
|
||||
@@ -44,7 +44,9 @@ enum Kind {
|
||||
//
|
||||
// See https://github.com/rust-lang/rust/issues/57017
|
||||
#[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 {
|
||||
@@ -161,8 +163,7 @@ impl Body {
|
||||
///
|
||||
/// See [the `upgrade` module](::upgrade) for more.
|
||||
pub fn on_upgrade(self) -> OnUpgrade {
|
||||
self
|
||||
.extra
|
||||
self.extra
|
||||
.map(|ex| ex.on_upgrade)
|
||||
.unwrap_or_else(OnUpgrade::none)
|
||||
}
|
||||
@@ -193,54 +194,44 @@ impl Body {
|
||||
}
|
||||
|
||||
fn take_delayed_eof(&mut self) -> Option<DelayEof> {
|
||||
self
|
||||
.extra
|
||||
self.extra
|
||||
.as_mut()
|
||||
.and_then(|extra| extra.delayed_eof.take())
|
||||
}
|
||||
|
||||
fn extra_mut(&mut self) -> &mut Extra {
|
||||
self
|
||||
.extra
|
||||
.get_or_insert_with(|| Box::new(Extra {
|
||||
self.extra.get_or_insert_with(|| {
|
||||
Box::new(Extra {
|
||||
delayed_eof: None,
|
||||
on_upgrade: OnUpgrade::none(),
|
||||
}))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn poll_eof(&mut self, cx: &mut task::Context<'_>) -> Poll<Option<crate::Result<Chunk>>> {
|
||||
match self.take_delayed_eof() {
|
||||
Some(DelayEof::NotEof(mut delay)) => {
|
||||
match self.poll_inner(cx) {
|
||||
ok @ Poll::Ready(Some(Ok(..))) |
|
||||
ok @ Poll::Pending => {
|
||||
Some(DelayEof::NotEof(mut delay)) => match self.poll_inner(cx) {
|
||||
ok @ Poll::Ready(Some(Ok(..))) | ok @ Poll::Pending => {
|
||||
self.extra_mut().delayed_eof = Some(DelayEof::NotEof(delay));
|
||||
ok
|
||||
},
|
||||
}
|
||||
Poll::Ready(None) => match Pin::new(&mut delay).poll(cx) {
|
||||
Poll::Ready(Ok(never)) => match never {},
|
||||
Poll::Pending => {
|
||||
self.extra_mut().delayed_eof = Some(DelayEof::Eof(delay));
|
||||
Poll::Pending
|
||||
},
|
||||
Poll::Ready(Err(_done)) => {
|
||||
Poll::Ready(None)
|
||||
},
|
||||
}
|
||||
Poll::Ready(Err(_done)) => 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) {
|
||||
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)
|
||||
},
|
||||
}
|
||||
Poll::Ready(Err(_done)) => Poll::Ready(None),
|
||||
},
|
||||
None => self.poll_inner(cx),
|
||||
}
|
||||
@@ -268,25 +259,23 @@ impl Body {
|
||||
}
|
||||
None => Poll::Ready(None),
|
||||
}
|
||||
},
|
||||
}
|
||||
Kind::H2 {
|
||||
recv: ref mut h2, ..
|
||||
} => match ready!(h2.poll_data(cx)) {
|
||||
Some(Ok(bytes)) => {
|
||||
let _ = h2.flow_control().release_capacity(bytes.len());
|
||||
Poll::Ready(Some(Ok(Chunk::from(bytes))))
|
||||
},
|
||||
}
|
||||
Some(Err(e)) => Poll::Ready(Some(Err(crate::Error::new_body(e)))),
|
||||
None => Poll::Ready(None),
|
||||
},
|
||||
|
||||
#[cfg(feature = "stream")]
|
||||
Kind::Wrapped(ref mut s) => {
|
||||
match ready!(s.as_mut().poll_next(cx)) {
|
||||
Kind::Wrapped(ref mut s) => match ready!(s.as_mut().poll_next(cx)) {
|
||||
Some(res) => Poll::Ready(Some(res.map_err(crate::Error::new_body))),
|
||||
None => Poll::Ready(None),
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -311,13 +300,21 @@ impl HttpBody for Body {
|
||||
type Data = Chunk;
|
||||
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)
|
||||
}
|
||||
|
||||
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 {
|
||||
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)),
|
||||
Err(e) => Poll::Ready(Err(crate::Error::new_h2(e))),
|
||||
},
|
||||
@@ -341,10 +338,8 @@ impl HttpBody for Body {
|
||||
let mut hint = SizeHint::default();
|
||||
hint.set_exact(val.len() as u64);
|
||||
hint
|
||||
},
|
||||
Kind::Once(None) => {
|
||||
SizeHint::default()
|
||||
},
|
||||
}
|
||||
Kind::Once(None) => SizeHint::default(),
|
||||
#[cfg(feature = "stream")]
|
||||
Kind::Wrapped(..) => SizeHint::default(),
|
||||
Kind::Chan { content_length, .. } | Kind::H2 { content_length, .. } => {
|
||||
@@ -355,7 +350,7 @@ impl HttpBody for Body {
|
||||
}
|
||||
|
||||
hint
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -393,14 +388,12 @@ impl Stream for Body {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// # Optional
|
||||
///
|
||||
/// This function requires enabling the `stream` feature in your
|
||||
/// `Cargo.toml`.
|
||||
#[cfg(feature = "stream")]
|
||||
impl
|
||||
From<Box<dyn Stream<Item = Result<Chunk, Box<dyn StdError + Send + Sync>>> + Send + Sync>>
|
||||
impl From<Box<dyn Stream<Item = Result<Chunk, Box<dyn StdError + Send + Sync>>> + Send + Sync>>
|
||||
for Body
|
||||
{
|
||||
#[inline]
|
||||
@@ -487,13 +480,17 @@ impl Sender {
|
||||
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.
|
||||
pub async fn send_data(&mut self, chunk: Chunk) -> crate::Result<()> {
|
||||
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.
|
||||
|
||||
@@ -19,7 +19,6 @@ pub struct IntoIter {
|
||||
inner: <Bytes as IntoIterator>::IntoIter,
|
||||
}
|
||||
|
||||
|
||||
impl Chunk {
|
||||
/// 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 {
|
||||
#[inline]
|
||||
fn from(bytes: Bytes) -> Chunk {
|
||||
Chunk {
|
||||
bytes: bytes,
|
||||
}
|
||||
Chunk { bytes: bytes }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,4 +173,3 @@ mod tests {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -59,4 +59,3 @@ fn _assert_send_sync() {
|
||||
_assert_send::<Chunk>();
|
||||
_assert_sync::<Chunk>();
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::error::Error as StdError;
|
||||
use bytes::Buf;
|
||||
use http::HeaderMap;
|
||||
|
||||
use crate::common::{Pin, Poll, task};
|
||||
use crate::common::{task, Pin, Poll};
|
||||
use http_body::{Body as HttpBody, SizeHint};
|
||||
|
||||
/// 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
|
||||
/// 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.
|
||||
///
|
||||
/// This should **only** be called after `poll_data` has ended.
|
||||
///
|
||||
/// 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);
|
||||
Poll::Ready(Ok(None))
|
||||
}
|
||||
@@ -61,7 +67,6 @@ pub trait Payload: sealed::Sealed + Send + 'static {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<T> Payload for T
|
||||
where
|
||||
T: HttpBody + Send + 'static,
|
||||
@@ -71,11 +76,17 @@ where
|
||||
type Data = T::Data;
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -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 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 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};
|
||||
|
||||
type Http1Dispatcher<T, B, R> = proto::dispatch::Dispatcher<
|
||||
proto::dispatch::Client<B>,
|
||||
B,
|
||||
T,
|
||||
R,
|
||||
>;
|
||||
type Http1Dispatcher<T, B, R> = proto::dispatch::Dispatcher<proto::dispatch::Client<B>, B, T, R>;
|
||||
|
||||
#[pin_project]
|
||||
enum ProtoClient<T, B>
|
||||
@@ -46,18 +41,16 @@ where
|
||||
const DEFAULT_HTTP2_CONN_WINDOW: u32 = 1024 * 1024 * 5; // 5mb
|
||||
const DEFAULT_HTTP2_STREAM_WINDOW: u32 = 1024 * 1024 * 2; // 2mb
|
||||
|
||||
|
||||
|
||||
/// Returns a handshake future over some 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
|
||||
T: AsyncRead + AsyncWrite + Unpin + Send + 'static,
|
||||
{
|
||||
Builder::new()
|
||||
.handshake(io)
|
||||
.await
|
||||
Builder::new().handshake(io).await
|
||||
}
|
||||
|
||||
/// The sender side of an established connection.
|
||||
@@ -65,7 +58,6 @@ pub struct SendRequest<B> {
|
||||
dispatch: dispatch::Sender<Request<B>, Response<Body>>,
|
||||
}
|
||||
|
||||
|
||||
/// 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
|
||||
@@ -79,7 +71,6 @@ where
|
||||
inner: Option<ProtoClient<T, B>>,
|
||||
}
|
||||
|
||||
|
||||
/// A builder to configure an HTTP connection.
|
||||
///
|
||||
/// After setting options, the builder is used to create a handshake future.
|
||||
@@ -99,7 +90,7 @@ pub struct Builder {
|
||||
/// Yields a `Response` if successful.
|
||||
#[must_use = "futures do nothing unless polled"]
|
||||
pub struct ResponseFuture {
|
||||
inner: ResponseFutureState
|
||||
inner: ResponseFutureState,
|
||||
}
|
||||
|
||||
enum ResponseFutureState {
|
||||
@@ -139,8 +130,7 @@ pub(super) struct Http2SendRequest<B> {
|
||||
|
||||
// ===== impl SendRequest
|
||||
|
||||
impl<B> SendRequest<B>
|
||||
{
|
||||
impl<B> SendRequest<B> {
|
||||
/// Polls to determine whether this sender can be used yet for a request.
|
||||
///
|
||||
/// If the associated connection is closed, this returns an Error.
|
||||
@@ -148,7 +138,7 @@ impl<B> SendRequest<B>
|
||||
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);
|
||||
future::poll_fn(move |cx| {
|
||||
ready!(me.as_mut().unwrap().poll_ready(cx))?;
|
||||
@@ -218,9 +208,7 @@ where
|
||||
/// ```
|
||||
pub fn send_request(&mut self, req: Request<B>) -> ResponseFuture {
|
||||
let inner = match self.dispatch.send(req) {
|
||||
Ok(rx) => {
|
||||
ResponseFutureState::Waiting(rx)
|
||||
},
|
||||
Ok(rx) => ResponseFutureState::Waiting(rx),
|
||||
Err(_req) => {
|
||||
debug!("connection was not ready");
|
||||
let err = crate::Error::new_canceled().with("connection was not ready");
|
||||
@@ -228,12 +216,13 @@ where
|
||||
}
|
||||
};
|
||||
|
||||
ResponseFuture {
|
||||
inner,
|
||||
}
|
||||
ResponseFuture { 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
|
||||
B: Send,
|
||||
{
|
||||
@@ -247,7 +236,7 @@ where
|
||||
Err(_) => panic!("dispatch dropped without returning error"),
|
||||
}
|
||||
}))
|
||||
},
|
||||
}
|
||||
Err(req) => {
|
||||
debug!("connection was not ready");
|
||||
let err = crate::Error::new_canceled().with("connection was not ready");
|
||||
@@ -259,7 +248,8 @@ where
|
||||
|
||||
impl<B> Service<Request<B>> for SendRequest<B>
|
||||
where
|
||||
B: Payload + 'static, {
|
||||
B: Payload + 'static,
|
||||
{
|
||||
type Response = Response<Body>;
|
||||
type Error = crate::Error;
|
||||
type Future = ResponseFuture;
|
||||
@@ -275,8 +265,7 @@ where
|
||||
|
||||
impl<B> fmt::Debug for SendRequest<B> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("SendRequest")
|
||||
.finish()
|
||||
f.debug_struct("SendRequest").finish()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -296,7 +285,10 @@ impl<B> Http2SendRequest<B>
|
||||
where
|
||||
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
|
||||
B: Send,
|
||||
{
|
||||
@@ -310,7 +302,7 @@ where
|
||||
Err(_) => panic!("dispatch dropped without returning error"),
|
||||
}
|
||||
}))
|
||||
},
|
||||
}
|
||||
Err(req) => {
|
||||
debug!("connection was not ready");
|
||||
let err = crate::Error::new_canceled().with("connection was not ready");
|
||||
@@ -322,8 +314,7 @@ where
|
||||
|
||||
impl<B> fmt::Debug for Http2SendRequest<B> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Http2SendRequest")
|
||||
.finish()
|
||||
f.debug_struct("Http2SendRequest").finish()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -374,18 +365,14 @@ where
|
||||
/// 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<()>> {
|
||||
match self.inner.as_mut().expect("already upgraded") {
|
||||
&mut ProtoClient::H1(ref mut h1) => {
|
||||
h1.poll_without_shutdown(cx)
|
||||
},
|
||||
&mut ProtoClient::H2(ref mut h2) => {
|
||||
Pin::new(h2).poll(cx).map_ok(|_| ())
|
||||
}
|
||||
&mut ProtoClient::H1(ref mut h1) => h1.poll_without_shutdown(cx),
|
||||
&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,
|
||||
/// 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);
|
||||
future::poll_fn(move |cx| -> Poll<crate::Result<Parts<T>>> {
|
||||
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> {
|
||||
match ready!(Pin::new(self.inner.as_mut().unwrap()).poll(cx))? {
|
||||
proto::Dispatched::Shutdown => {
|
||||
Poll::Ready(Ok(()))
|
||||
},
|
||||
proto::Dispatched::Shutdown => Poll::Ready(Ok(())),
|
||||
proto::Dispatched::Upgrade(pending) => {
|
||||
let h1 = match mem::replace(&mut self.inner, None) {
|
||||
Some(ProtoClient::H1(h1)) => h1,
|
||||
@@ -427,8 +412,7 @@ where
|
||||
B: Payload + 'static,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Connection")
|
||||
.finish()
|
||||
f.debug_struct("Connection").finish()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -519,7 +503,10 @@ impl Builder {
|
||||
/// Passing `None` will do nothing.
|
||||
///
|
||||
/// 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() {
|
||||
self.h2_builder.initial_connection_window_size(sz);
|
||||
}
|
||||
@@ -527,7 +514,10 @@ impl Builder {
|
||||
}
|
||||
|
||||
/// 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
|
||||
T: AsyncRead + AsyncWrite + Unpin + Send + 'static,
|
||||
B: Payload + 'static,
|
||||
@@ -563,12 +553,8 @@ impl Builder {
|
||||
};
|
||||
|
||||
Ok((
|
||||
SendRequest {
|
||||
dispatch: tx,
|
||||
},
|
||||
Connection {
|
||||
inner: Some(proto),
|
||||
},
|
||||
SendRequest { dispatch: tx },
|
||||
Connection { inner: Some(proto) },
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -588,7 +574,7 @@ impl Future for ResponseFuture {
|
||||
// this is definite bug if it happens, but it shouldn't happen!
|
||||
Err(_canceled) => panic!("dispatch dropped without returning error"),
|
||||
})
|
||||
},
|
||||
}
|
||||
ResponseFutureState::Error(ref mut err) => {
|
||||
Poll::Ready(Err(err.take().expect("polled after ready")))
|
||||
}
|
||||
@@ -598,8 +584,7 @@ impl Future for ResponseFuture {
|
||||
|
||||
impl fmt::Debug for ResponseFuture {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("ResponseFuture")
|
||||
.finish()
|
||||
f.debug_struct("ResponseFuture").finish()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -628,7 +613,6 @@ where
|
||||
trait AssertSend: Send {}
|
||||
trait AssertSendSync: Send + Sync {}
|
||||
|
||||
|
||||
#[doc(hidden)]
|
||||
impl<B: Send> AssertSendSync for SendRequest<B> {}
|
||||
|
||||
@@ -637,7 +621,8 @@ impl<T: Send, B: Send> AssertSend for Connection<T, B>
|
||||
where
|
||||
T: AsyncRead + AsyncWrite + Send + 'static,
|
||||
B: Payload + 'static,
|
||||
{}
|
||||
{
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl<T: Send + Sync, B: Send + Sync> AssertSendSync for Connection<T, B>
|
||||
@@ -645,11 +630,11 @@ where
|
||||
T: AsyncRead + AsyncWrite + Send + 'static,
|
||||
B: Payload + 'static,
|
||||
B::Data: Send + Sync + 'static,
|
||||
{}
|
||||
{
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl AssertSendSync for Builder {}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl AssertSend for ResponseFuture {}
|
||||
|
||||
|
||||
@@ -22,11 +22,11 @@
|
||||
//! });
|
||||
//! ```
|
||||
use std::error::Error;
|
||||
use std::future::Future;
|
||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs};
|
||||
use std::pin::Pin;
|
||||
use std::str::FromStr;
|
||||
use std::task::{self, Poll};
|
||||
use std::pin::Pin;
|
||||
use std::future::Future;
|
||||
use std::{fmt, io, vec};
|
||||
|
||||
use tokio::task::JoinHandle;
|
||||
@@ -394,7 +394,8 @@ mod tests {
|
||||
let dst = ::http::Uri::from_static("http://[::1]:8080/");
|
||||
|
||||
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");
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
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::future::Future;
|
||||
use std::io;
|
||||
use std::marker::PhantomData;
|
||||
use std::net::{IpAddr, SocketAddr};
|
||||
use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
use std::task::{self, Poll};
|
||||
use std::time::Duration;
|
||||
|
||||
use futures_util::future::Either;
|
||||
@@ -20,7 +20,6 @@ use super::dns::{self, resolve, GaiResolver, Resolve};
|
||||
use super::{Connected, Connection};
|
||||
//#[cfg(feature = "runtime")] use super::dns::TokioThreadpoolGaiResolver;
|
||||
|
||||
|
||||
/// A connector for the `http` scheme.
|
||||
///
|
||||
/// Performs DNS resolution in a thread pool, and then connects over TCP.
|
||||
@@ -256,10 +255,7 @@ impl<R> HttpConnector<R>
|
||||
where
|
||||
R: Resolve,
|
||||
{
|
||||
async fn call_async(
|
||||
&mut self,
|
||||
dst: Uri,
|
||||
) -> Result<TcpStream, ConnectError> {
|
||||
async fn call_async(&mut self, dst: Uri) -> Result<TcpStream, ConnectError> {
|
||||
trace!(
|
||||
"Http::connect; scheme={:?}, host={:?}, port={:?}",
|
||||
dst.scheme(),
|
||||
@@ -292,7 +288,13 @@ where
|
||||
};
|
||||
let port = match dst.port() {
|
||||
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;
|
||||
@@ -348,9 +350,7 @@ impl Connection for TcpStream {
|
||||
fn connected(&self) -> Connected {
|
||||
let connected = Connected::new();
|
||||
if let Ok(remote_addr) = self.peer_addr() {
|
||||
connected.extra(HttpInfo {
|
||||
remote_addr,
|
||||
})
|
||||
connected.extra(HttpInfo { remote_addr })
|
||||
} else {
|
||||
connected
|
||||
}
|
||||
@@ -389,7 +389,6 @@ impl<R: Resolve> Future for HttpConnecting<R> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Not publicly exported (so missing_docs doesn't trigger).
|
||||
pub struct ConnectError {
|
||||
msg: Box<str>,
|
||||
@@ -531,14 +530,7 @@ impl ConnectingTcpRemote {
|
||||
let mut err = None;
|
||||
for addr in &mut self.addrs {
|
||||
debug!("connecting to {}", addr);
|
||||
match connect(
|
||||
&addr,
|
||||
local_addr,
|
||||
reuse_address,
|
||||
self.connect_timeout,
|
||||
)?
|
||||
.await
|
||||
{
|
||||
match connect(&addr, local_addr, reuse_address, self.connect_timeout)?.await {
|
||||
Ok(tcp) => {
|
||||
debug!("connected to {:?}", tcp.peer_addr().ok());
|
||||
return Ok(tcp);
|
||||
@@ -606,11 +598,7 @@ impl ConnectingTcp {
|
||||
..
|
||||
} = self;
|
||||
match self.fallback {
|
||||
None => {
|
||||
self.preferred
|
||||
.connect(local_addr, reuse_address)
|
||||
.await
|
||||
}
|
||||
None => self.preferred.connect(local_addr, reuse_address).await,
|
||||
Some(mut fallback) => {
|
||||
let preferred_fut = self.preferred.connect(local_addr, reuse_address);
|
||||
futures_util::pin_mut!(preferred_fut);
|
||||
@@ -652,10 +640,7 @@ mod tests {
|
||||
use super::super::sealed::Connect;
|
||||
use super::HttpConnector;
|
||||
|
||||
async fn connect<C>(
|
||||
connector: C,
|
||||
dst: Uri,
|
||||
) -> Result<C::Transport, C::Error>
|
||||
async fn connect<C>(connector: C, dst: Uri) -> Result<C::Transport, C::Error>
|
||||
where
|
||||
C: Connect,
|
||||
{
|
||||
@@ -795,7 +780,6 @@ mod tests {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
let (start, stream) = rt
|
||||
.block_on(async move {
|
||||
let addrs = hosts
|
||||
|
||||
@@ -7,11 +7,14 @@
|
||||
//! - Types to build custom connectors.
|
||||
use std::fmt;
|
||||
|
||||
use ::http::{Response};
|
||||
use ::http::Response;
|
||||
|
||||
#[cfg(feature = "tcp")] pub mod dns;
|
||||
#[cfg(feature = "tcp")] mod http;
|
||||
#[cfg(feature = "tcp")] pub use self::http::{HttpConnector, HttpInfo};
|
||||
#[cfg(feature = "tcp")]
|
||||
pub mod dns;
|
||||
#[cfg(feature = "tcp")]
|
||||
mod http;
|
||||
#[cfg(feature = "tcp")]
|
||||
pub use self::http::{HttpConnector, HttpInfo};
|
||||
|
||||
/// Describes a type returned by a connector.
|
||||
pub trait Connection {
|
||||
@@ -115,8 +118,7 @@ impl Clone for Extra {
|
||||
|
||||
impl fmt::Debug for Extra {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Extra")
|
||||
.finish()
|
||||
f.debug_struct("Extra").finish()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,7 +135,7 @@ struct ExtraEnvelope<T>(T);
|
||||
|
||||
impl<T> ExtraInner for ExtraEnvelope<T>
|
||||
where
|
||||
T: Clone + Send + Sync + 'static
|
||||
T: Clone + Send + Sync + 'static,
|
||||
{
|
||||
fn clone_box(&self) -> Box<dyn ExtraInner> {
|
||||
Box::new(self.clone())
|
||||
@@ -154,7 +156,7 @@ impl<T: Clone> Clone for ExtraChain<T> {
|
||||
|
||||
impl<T> ExtraInner for ExtraChain<T>
|
||||
where
|
||||
T: Clone + Send + Sync + 'static
|
||||
T: Clone + Send + Sync + 'static,
|
||||
{
|
||||
fn clone_box(&self) -> Box<dyn ExtraInner> {
|
||||
Box::new(self.clone())
|
||||
@@ -172,8 +174,8 @@ pub(super) mod sealed {
|
||||
use ::http::Uri;
|
||||
use tokio::io::{AsyncRead, AsyncWrite};
|
||||
|
||||
use super::Connection;
|
||||
use crate::common::{Future, Unpin};
|
||||
use super::{Connection};
|
||||
|
||||
/// Connect to a destination, returning an IO transport.
|
||||
///
|
||||
@@ -193,14 +195,14 @@ pub(super) mod sealed {
|
||||
/// An error occured when trying to connect.
|
||||
type Error: Into<Box<dyn StdError + Send + Sync>>;
|
||||
/// 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)]
|
||||
fn connect(self, internal_only: Internal, dst: Uri) -> Self::Future;
|
||||
}
|
||||
|
||||
impl<S, T> Connect for S
|
||||
where
|
||||
S: tower_service::Service<Uri, Response=T> + Send,
|
||||
S: tower_service::Service<Uri, Response = T> + Send,
|
||||
S::Error: Into<Box<dyn StdError + Send + Sync>>,
|
||||
S::Future: Unpin + Send,
|
||||
T: AsyncRead + AsyncWrite + Connection + Unpin + Send + 'static,
|
||||
@@ -215,11 +217,12 @@ pub(super) mod sealed {
|
||||
|
||||
impl<S, T> Sealed for S
|
||||
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::Future: Unpin + Send,
|
||||
T: AsyncRead + AsyncWrite + Connection + Unpin + Send + 'static,
|
||||
{}
|
||||
{
|
||||
}
|
||||
|
||||
pub trait Sealed {}
|
||||
#[allow(missing_debug_implementations)]
|
||||
@@ -228,7 +231,7 @@ pub(super) mod sealed {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{Connected};
|
||||
use super::Connected;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
struct Ex1(usize);
|
||||
@@ -241,18 +244,13 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_connected_extra() {
|
||||
let c1 = Connected::new()
|
||||
.extra(Ex1(41));
|
||||
let c1 = Connected::new().extra(Ex1(41));
|
||||
|
||||
let mut res1 = crate::Response::new(crate::Body::empty());
|
||||
|
||||
assert_eq!(res1.extensions().get::<Ex1>(), None);
|
||||
|
||||
c1
|
||||
.extra
|
||||
.as_ref()
|
||||
.expect("c1 extra")
|
||||
.set(&mut res1);
|
||||
c1.extra.as_ref().expect("c1 extra").set(&mut res1);
|
||||
|
||||
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::<Ex3>(), None);
|
||||
|
||||
c1
|
||||
.extra
|
||||
.as_ref()
|
||||
.expect("c1 extra")
|
||||
.set(&mut res1);
|
||||
c1.extra.as_ref().expect("c1 extra").set(&mut res1);
|
||||
|
||||
assert_eq!(res1.extensions().get::<Ex1>(), Some(&Ex1(45)));
|
||||
assert_eq!(res1.extensions().get::<Ex2>(), Some(&Ex2("zoom")));
|
||||
@@ -291,11 +285,7 @@ mod tests {
|
||||
|
||||
let mut res2 = crate::Response::new(crate::Body::empty());
|
||||
|
||||
c2
|
||||
.extra
|
||||
.as_ref()
|
||||
.expect("c2 extra")
|
||||
.set(&mut res2);
|
||||
c2.extra.as_ref().expect("c2 extra").set(&mut res2);
|
||||
|
||||
assert_eq!(res2.extensions().get::<Ex1>(), Some(&Ex1(99)));
|
||||
assert_eq!(res2.extensions().get::<Ex2>(), Some(&Ex2("hiccup")));
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use futures_core::Stream;
|
||||
use futures_channel::{mpsc, oneshot};
|
||||
use futures_core::Stream;
|
||||
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 Promise<T> = oneshot::Receiver<Result<T, crate::Error>>;
|
||||
@@ -52,7 +52,8 @@ pub struct UnboundedSender<T, U> {
|
||||
|
||||
impl<T, U> Sender<T, U> {
|
||||
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())
|
||||
}
|
||||
|
||||
@@ -82,7 +83,8 @@ impl<T, U> Sender<T, U> {
|
||||
return Err(val);
|
||||
}
|
||||
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_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);
|
||||
}
|
||||
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_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> {
|
||||
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_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> {
|
||||
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) {
|
||||
Poll::Ready(item) => Poll::Ready(item.map(|mut env| {
|
||||
env.0.take().expect("envelope not dropped")
|
||||
})),
|
||||
Poll::Ready(item) => {
|
||||
Poll::Ready(item.map(|mut env| env.0.take().expect("envelope not dropped")))
|
||||
}
|
||||
Poll::Pending => {
|
||||
self.taker.want();
|
||||
Poll::Pending
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,7 +183,10 @@ struct Envelope<T, U>(Option<(T, Callback<T, U>)>);
|
||||
impl<T, U> Drop for Envelope<T, U> {
|
||||
fn drop(&mut self) {
|
||||
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 {
|
||||
Callback::Retry(tx) => {
|
||||
let _ = tx.send(val);
|
||||
},
|
||||
}
|
||||
Callback::NoRetry(tx) => {
|
||||
let _ = tx.send(val.map_err(|e| e.0));
|
||||
}
|
||||
@@ -214,29 +224,25 @@ impl<T, U> Callback<T, U> {
|
||||
|
||||
pub(crate) fn send_when(
|
||||
self,
|
||||
mut when: impl Future<Output=Result<U, (crate::Error, Option<T>)>> + Unpin,
|
||||
) -> impl Future<Output=()> {
|
||||
mut when: impl Future<Output = Result<U, (crate::Error, Option<T>)>> + Unpin,
|
||||
) -> impl Future<Output = ()> {
|
||||
let mut cb = Some(self);
|
||||
|
||||
// "select" on this callback being canceled, and the future completing
|
||||
future::poll_fn(move |cx| {
|
||||
match Pin::new(&mut when).poll(cx) {
|
||||
Poll::Ready(Ok(res)) => {
|
||||
cb.take()
|
||||
.expect("polled after complete")
|
||||
.send(Ok(res));
|
||||
cb.take().expect("polled after complete").send(Ok(res));
|
||||
Poll::Ready(())
|
||||
},
|
||||
}
|
||||
Poll::Pending => {
|
||||
// check if the callback is canceled
|
||||
ready!(cb.as_mut().unwrap().poll_canceled(cx));
|
||||
trace!("send_when canceled");
|
||||
Poll::Ready(())
|
||||
},
|
||||
}
|
||||
Poll::Ready(Err(err)) => {
|
||||
cb.take()
|
||||
.expect("polled after complete")
|
||||
.send(Err(err));
|
||||
cb.take().expect("polled after complete").send(Err(err));
|
||||
Poll::Ready(())
|
||||
}
|
||||
}
|
||||
@@ -253,7 +259,7 @@ mod tests {
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
use super::{Callback, channel, Receiver};
|
||||
use super::{channel, Callback, Receiver};
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Custom(i32);
|
||||
@@ -271,14 +277,14 @@ mod tests {
|
||||
|
||||
impl<F, T> Future for PollOnce<'_, F>
|
||||
where
|
||||
F: Future<Output = T> + Unpin
|
||||
F: Future<Output = T> + Unpin,
|
||||
{
|
||||
type Output = Option<()>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
match Pin::new(&mut self.0).poll(cx) {
|
||||
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 opt = poll_once.await;
|
||||
if opt.is_none() {
|
||||
break
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -64,17 +64,18 @@ use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use futures_channel::oneshot;
|
||||
use futures_util::future::{self, FutureExt as _, TryFutureExt as _, Either};
|
||||
use http::{Method, Request, Response, Uri, Version};
|
||||
use futures_util::future::{self, Either, FutureExt as _, TryFutureExt as _};
|
||||
use http::header::{HeaderValue, HOST};
|
||||
use http::uri::Scheme;
|
||||
use http::{Method, Request, Response, Uri, Version};
|
||||
|
||||
use crate::body::{Body, Payload};
|
||||
use crate::common::{lazy as hyper_lazy, BoxSendFuture, Executor, Lazy, Future, Pin, Poll, task};
|
||||
use self::connect::{Alpn, sealed::Connect, Connected, Connection};
|
||||
use self::connect::{sealed::Connect, Alpn, Connected, Connection};
|
||||
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 connect;
|
||||
@@ -104,7 +105,7 @@ struct Config {
|
||||
/// This is returned by `Client::request` (and `Client::get`).
|
||||
#[must_use = "futures do nothing unless polled"]
|
||||
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 =====
|
||||
@@ -157,7 +158,8 @@ impl Client<(), Body> {
|
||||
}
|
||||
|
||||
impl<C, B> Client<C, B>
|
||||
where C: Connect + Clone + Send + Sync + 'static,
|
||||
where
|
||||
C: Connect + Clone + Send + Sync + 'static,
|
||||
C::Transport: Unpin + Send + 'static,
|
||||
C::Future: Unpin + Send + 'static,
|
||||
B: Payload + Unpin + Send + 'static,
|
||||
@@ -223,13 +225,19 @@ where C: Connect + Clone + Send + Sync + 'static,
|
||||
let is_http_connect = req.method() == &Method::CONNECT;
|
||||
match req.version() {
|
||||
Version::HTTP_11 => (),
|
||||
Version::HTTP_10 => if is_http_connect {
|
||||
Version::HTTP_10 => {
|
||||
if is_http_connect {
|
||||
warn!("CONNECT is not allowed for HTTP/1.0");
|
||||
return ResponseFuture::new(Box::new(future::err(crate::Error::new_user_unsupported_request_method())));
|
||||
},
|
||||
other_h2 @ Version::HTTP_2 => if self.config.ver != Ver::Http2 {
|
||||
return ResponseFuture::new(Box::new(future::err(
|
||||
crate::Error::new_user_unsupported_request_method(),
|
||||
)));
|
||||
}
|
||||
}
|
||||
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)!
|
||||
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)))
|
||||
}
|
||||
|
||||
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 uri = req.uri().clone();
|
||||
|
||||
@@ -265,7 +277,10 @@ where C: Connect + Clone + Send + Sync + 'static,
|
||||
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();
|
||||
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 set_host = self.config.set_host;
|
||||
@@ -282,17 +301,15 @@ where C: Connect + Clone + Send + Sync + 'static,
|
||||
if pooled.is_http1() {
|
||||
if set_host {
|
||||
let uri = req.uri().clone();
|
||||
req
|
||||
.headers_mut()
|
||||
.entry(HOST)
|
||||
.or_insert_with(|| {
|
||||
req.headers_mut().entry(HOST).or_insert_with(|| {
|
||||
let hostname = uri.host().expect("authority implies host");
|
||||
if let Some(port) = uri.port() {
|
||||
let s = format!("{}:{}", hostname, port);
|
||||
HeaderValue::from_str(&s)
|
||||
} else {
|
||||
HeaderValue::from_str(hostname)
|
||||
}.expect("uri host is valid header value")
|
||||
}
|
||||
.expect("uri host is valid header value")
|
||||
});
|
||||
}
|
||||
|
||||
@@ -306,10 +323,13 @@ where C: Connect + Clone + Send + Sync + 'static,
|
||||
};
|
||||
} else if req.method() == &Method::CONNECT {
|
||||
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()));
|
||||
|
||||
// If the Connector included 'extra' info, add to Response...
|
||||
@@ -332,8 +352,7 @@ where C: Connect + Clone + Send + Sync + 'static,
|
||||
return Either::Right(Either::Left(fut));
|
||||
}
|
||||
|
||||
Either::Right(Either::Right(fut
|
||||
.map_ok(move |mut res| {
|
||||
Either::Right(Either::Right(fut.map_ok(move |mut res| {
|
||||
// If pooled is HTTP/2, we can toss this reference immediately.
|
||||
//
|
||||
// when pooled is dropped, it will try to insert back into the
|
||||
@@ -349,10 +368,7 @@ where C: Connect + Clone + Send + Sync + 'static,
|
||||
} else if !res.body().is_end_stream() {
|
||||
let (delayed_tx, delayed_rx) = oneshot::channel();
|
||||
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| pooled.poll_ready(cx)).map(move |_| {
|
||||
// At this point, `pooled` is dropped, and had a chance
|
||||
// to insert into the pool (if conn was idle)
|
||||
drop(delayed_tx);
|
||||
@@ -362,10 +378,7 @@ where C: Connect + Clone + Send + Sync + 'static,
|
||||
} else {
|
||||
// There's no body to delay, but the connection isn't
|
||||
// ready yet. Only re-insert when it's ready
|
||||
let on_idle = future::poll_fn(move |cx| {
|
||||
pooled.poll_ready(cx)
|
||||
})
|
||||
.map(|_| ());
|
||||
let on_idle = future::poll_fn(move |cx| pooled.poll_ready(cx)).map(|_| ());
|
||||
|
||||
executor.execute(on_idle);
|
||||
}
|
||||
@@ -374,9 +387,11 @@ where C: Connect + Clone + Send + Sync + 'static,
|
||||
})
|
||||
}
|
||||
|
||||
fn connection_for(&self, uri: Uri, pool_key: PoolKey)
|
||||
-> impl Future<Output=Result<Pooled<PoolClient<B>>, ClientError<B>>>
|
||||
{
|
||||
fn connection_for(
|
||||
&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
|
||||
// connection the fastest, and to reduce connection churn.
|
||||
//
|
||||
@@ -395,8 +410,7 @@ where C: Connect + Clone + Send + Sync + 'static,
|
||||
|
||||
let executor = self.conn_builder.exec.clone();
|
||||
// The order of the `select` is depended on below...
|
||||
future::select(checkout, connect)
|
||||
.then(move |either| match either {
|
||||
future::select(checkout, connect).then(move |either| match either {
|
||||
// Checkout won, connect future may have been started or not.
|
||||
//
|
||||
// If it has, let it finish and insert back into the pool,
|
||||
@@ -423,11 +437,9 @@ where C: Connect + Clone + Send + Sync + 'static,
|
||||
let _ = executor.execute(bg);
|
||||
}
|
||||
Either::Left(future::ok(checked_out))
|
||||
},
|
||||
}
|
||||
// Connect won, checkout can just be dropped.
|
||||
Either::Right((Ok(connected), _checkout)) => {
|
||||
Either::Left(future::ok(connected))
|
||||
},
|
||||
Either::Right((Ok(connected), _checkout)) => Either::Left(future::ok(connected)),
|
||||
// Either checkout or connect could get canceled:
|
||||
//
|
||||
// 1. Connect is canceled if this is HTTP/2 and there is
|
||||
@@ -453,9 +465,11 @@ where C: Connect + Clone + Send + Sync + 'static,
|
||||
})
|
||||
}
|
||||
|
||||
fn connect_to(&self, uri: Uri, pool_key: PoolKey)
|
||||
-> impl Lazy<Output=crate::Result<Pooled<PoolClient<B>>>> + Unpin
|
||||
{
|
||||
fn connect_to(
|
||||
&self,
|
||||
uri: Uri,
|
||||
pool_key: PoolKey,
|
||||
) -> impl Lazy<Output = crate::Result<Pooled<PoolClient<B>>>> + Unpin {
|
||||
let executor = self.conn_builder.exec.clone();
|
||||
let pool = self.pool.clone();
|
||||
let mut conn_builder = self.conn_builder.clone();
|
||||
@@ -472,11 +486,14 @@ where C: Connect + Clone + Send + Sync + 'static,
|
||||
let connecting = match pool.connecting(&pool_key, ver) {
|
||||
Some(lock) => lock,
|
||||
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));
|
||||
}
|
||||
};
|
||||
Either::Left(connector.connect(connect::sealed::Internal, dst)
|
||||
Either::Left(
|
||||
connector
|
||||
.connect(connect::sealed::Internal, dst)
|
||||
.map_err(crate::Error::new_connect)
|
||||
.and_then(move |io| {
|
||||
let connected = io.connected();
|
||||
@@ -488,11 +505,12 @@ where C: Connect + Clone + Send + Sync + 'static,
|
||||
Some(lock) => {
|
||||
trace!("ALPN negotiated h2, updating pool");
|
||||
lock
|
||||
},
|
||||
}
|
||||
None => {
|
||||
// Another connection has already upgraded,
|
||||
// the pool checkout should finish up for us.
|
||||
let canceled = crate::Error::new_canceled().with("ALPN upgraded to HTTP/2");
|
||||
let canceled = crate::Error::new_canceled()
|
||||
.with("ALPN upgraded to HTTP/2");
|
||||
return Either::Right(future::err(canceled));
|
||||
}
|
||||
}
|
||||
@@ -500,36 +518,46 @@ where C: Connect + Clone + Send + Sync + 'static,
|
||||
connecting
|
||||
};
|
||||
let is_h2 = is_ver_h2 || connected.alpn == Alpn::H2;
|
||||
Either::Left(Box::pin(conn_builder
|
||||
Either::Left(Box::pin(
|
||||
conn_builder
|
||||
.http2_only(is_h2)
|
||||
.handshake(io)
|
||||
.and_then(move |(tx, conn)| {
|
||||
trace!("handshake complete, spawning background dispatcher task");
|
||||
executor.execute(conn.map_err(|e| {
|
||||
debug!("client connection error: {}", e)
|
||||
}).map(|_| ()));
|
||||
trace!(
|
||||
"handshake complete, spawning background dispatcher task"
|
||||
);
|
||||
executor.execute(
|
||||
conn.map_err(|e| debug!("client connection error: {}", e))
|
||||
.map(|_| ()),
|
||||
);
|
||||
|
||||
// Wait for 'conn' to ready up before we
|
||||
// declare this tx as usable
|
||||
tx.when_ready()
|
||||
})
|
||||
.map_ok(move |tx| {
|
||||
pool.pooled(connecting, PoolClient {
|
||||
pool.pooled(
|
||||
connecting,
|
||||
PoolClient {
|
||||
conn_info: connected,
|
||||
tx: if is_h2 {
|
||||
PoolTx::Http2(tx.into_http2())
|
||||
} else {
|
||||
PoolTx::Http1(tx)
|
||||
},
|
||||
})
|
||||
})))
|
||||
}))
|
||||
},
|
||||
)
|
||||
}),
|
||||
))
|
||||
}),
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<C, B> tower_service::Service<Request<B>> for Client<C, B>
|
||||
where C: Connect + Clone + Send + Sync + 'static,
|
||||
where
|
||||
C: Connect + Clone + Send + Sync + 'static,
|
||||
C::Transport: Unpin + Send + 'static,
|
||||
C::Future: Unpin + Send + 'static,
|
||||
B: Payload + Unpin + Send + 'static,
|
||||
@@ -561,23 +589,22 @@ impl<C: Clone, B> Clone for Client<C, B> {
|
||||
|
||||
impl<C, B> fmt::Debug for Client<C, B> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Client")
|
||||
.finish()
|
||||
f.debug_struct("Client").finish()
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl ResponseFuture =====
|
||||
|
||||
impl ResponseFuture {
|
||||
fn new(fut: Box<dyn Future<Output=crate::Result<Response<Body>>> + Send>) -> Self {
|
||||
Self {
|
||||
inner: fut.into(),
|
||||
}
|
||||
fn new(fut: Box<dyn Future<Output = crate::Result<Response<Body>>> + Send>) -> Self {
|
||||
Self { inner: fut.into() }
|
||||
}
|
||||
|
||||
fn error_version(ver: Version) -> Self {
|
||||
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> {
|
||||
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
|
||||
B: Send,
|
||||
{
|
||||
@@ -668,12 +698,10 @@ where
|
||||
|
||||
fn reserve(self) -> Reservation<Self> {
|
||||
match self.tx {
|
||||
PoolTx::Http1(tx) => {
|
||||
Reservation::Unique(PoolClient {
|
||||
PoolTx::Http1(tx) => Reservation::Unique(PoolClient {
|
||||
conn_info: self.conn_info,
|
||||
tx: PoolTx::Http1(tx),
|
||||
})
|
||||
},
|
||||
}),
|
||||
PoolTx::Http2(tx) => {
|
||||
let b = PoolClient {
|
||||
conn_info: self.conn_info.clone(),
|
||||
@@ -703,13 +731,11 @@ enum ClientError<B> {
|
||||
connection_reused: bool,
|
||||
req: Request<B>,
|
||||
reason: crate::Error,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
impl<B> ClientError<B> {
|
||||
fn map_with_reused(conn_reused: bool)
|
||||
-> impl Fn((crate::Error, Option<Request<B>>)) -> Self
|
||||
{
|
||||
fn map_with_reused(conn_reused: bool) -> impl Fn((crate::Error, Option<Request<B>>)) -> Self {
|
||||
move |(err, orig_req)| {
|
||||
if let Some(req) = orig_req {
|
||||
ClientError::Canceled {
|
||||
@@ -737,7 +763,7 @@ fn origin_form(uri: &mut Uri) {
|
||||
let mut parts = ::http::uri::Parts::default();
|
||||
parts.path_and_query = Some(path.clone());
|
||||
Uri::from_parts(parts).expect("path is valid uri")
|
||||
},
|
||||
}
|
||||
_none_or_just_slash => {
|
||||
debug_assert!(Uri::default() == "/");
|
||||
Uri::default()
|
||||
@@ -748,7 +774,10 @@ fn origin_form(uri: &mut Uri) {
|
||||
|
||||
fn absolute_form(uri: &mut Uri) {
|
||||
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,
|
||||
// then it *should* have tunneled, and so we don't want to send
|
||||
// absolute-form in that case.
|
||||
@@ -763,10 +792,7 @@ fn authority_form(uri: &mut Uri) {
|
||||
// `https://hyper.rs` would parse with `/` path, don't
|
||||
// annoy people about that...
|
||||
if path != "/" {
|
||||
warn!(
|
||||
"HTTP/1.1 CONNECT request stripping path: {:?}",
|
||||
path
|
||||
);
|
||||
warn!("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();
|
||||
parts.authority = Some(auth.clone());
|
||||
Uri::from_parts(parts).expect("authority is valid")
|
||||
},
|
||||
}
|
||||
None => {
|
||||
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> {
|
||||
let uri_clone = uri.clone();
|
||||
match (uri_clone.scheme(), uri_clone.authority()) {
|
||||
(Some(scheme), Some(auth)) => {
|
||||
Ok(format!("{}://{}", scheme, auth))
|
||||
}
|
||||
(Some(scheme), Some(auth)) => Ok(format!("{}://{}", scheme, auth)),
|
||||
(None, Some(auth)) if is_http_connect => {
|
||||
let scheme = match auth.port_u16() {
|
||||
Some(443) => {
|
||||
@@ -797,10 +821,10 @@ fn extract_domain(uri: &mut Uri, is_http_connect: bool) -> crate::Result<String>
|
||||
_ => {
|
||||
set_scheme(uri, Scheme::HTTP);
|
||||
"http"
|
||||
},
|
||||
}
|
||||
};
|
||||
Ok(format!("{}://{}", scheme, auth))
|
||||
},
|
||||
}
|
||||
_ => {
|
||||
debug!("Client requires absolute-form URIs, received: {:?}", uri);
|
||||
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) {
|
||||
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 mut parts: ::http::uri::Parts = old.into();
|
||||
parts.scheme = Some(scheme);
|
||||
@@ -946,11 +973,7 @@ impl Builder {
|
||||
///
|
||||
/// Default is false.
|
||||
pub fn http2_only(&mut self, val: bool) -> &mut Self {
|
||||
self.client_config.ver = if val {
|
||||
Ver::Http2
|
||||
} else {
|
||||
Ver::Auto
|
||||
};
|
||||
self.client_config.ver = if val { Ver::Http2 } else { Ver::Auto };
|
||||
self
|
||||
}
|
||||
|
||||
@@ -963,7 +986,8 @@ impl Builder {
|
||||
///
|
||||
/// [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 {
|
||||
self.conn_builder.http2_initial_stream_window_size(sz.into());
|
||||
self.conn_builder
|
||||
.http2_initial_stream_window_size(sz.into());
|
||||
self
|
||||
}
|
||||
|
||||
@@ -972,8 +996,12 @@ impl Builder {
|
||||
/// Passing `None` will do nothing.
|
||||
///
|
||||
/// If not set, hyper will use a default.
|
||||
pub fn http2_initial_connection_window_size(&mut self, sz: impl Into<Option<u32>>) -> &mut Self {
|
||||
self.conn_builder.http2_initial_connection_window_size(sz.into());
|
||||
pub fn http2_initial_connection_window_size(
|
||||
&mut self,
|
||||
sz: impl Into<Option<u32>>,
|
||||
) -> &mut Self {
|
||||
self.conn_builder
|
||||
.http2_initial_connection_window_size(sz.into());
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
@@ -10,8 +10,8 @@ use futures_channel::oneshot;
|
||||
#[cfg(feature = "runtime")]
|
||||
use tokio::time::{Duration, Instant, Interval};
|
||||
|
||||
use crate::common::{Exec, Future, Pin, Poll, Unpin, task};
|
||||
use super::Ver;
|
||||
use crate::common::{task, Exec, Future, Pin, Poll, Unpin};
|
||||
|
||||
// FIXME: allow() required due to `impl Trait` leaking types to this lint
|
||||
#[allow(missing_debug_implementations)]
|
||||
@@ -111,9 +111,7 @@ impl<T> Pool<T> {
|
||||
None
|
||||
};
|
||||
|
||||
Pool {
|
||||
inner,
|
||||
}
|
||||
Pool { inner }
|
||||
}
|
||||
|
||||
fn is_enabled(&self) -> bool {
|
||||
@@ -174,12 +172,7 @@ impl<T: Poolable> Pool<T> {
|
||||
|
||||
#[cfg(test)]
|
||||
fn locked(&self) -> ::std::sync::MutexGuard<'_, PoolInner<T>> {
|
||||
self
|
||||
.inner
|
||||
.as_ref()
|
||||
.expect("enabled")
|
||||
.lock()
|
||||
.expect("lock")
|
||||
self.inner.as_ref().expect("enabled").lock().expect("lock")
|
||||
}
|
||||
|
||||
/* Used in client/tests.rs...
|
||||
@@ -216,13 +209,13 @@ impl<T: Poolable> Pool<T> {
|
||||
// Shared reservations don't need a reference to the pool,
|
||||
// since the pool always keeps a copy.
|
||||
(to_return, WeakOpt::none())
|
||||
},
|
||||
}
|
||||
Reservation::Unique(value) => {
|
||||
// Unique reservations must take a reference to the pool
|
||||
// since they hope to reinsert once the reservation is
|
||||
// completed
|
||||
(value, WeakOpt::downgrade(enabled))
|
||||
},
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If pool is not enabled, skip all the things...
|
||||
@@ -236,7 +229,7 @@ impl<T: Poolable> Pool<T> {
|
||||
key: connecting.key.clone(),
|
||||
is_reused: false,
|
||||
pool: pool_ref,
|
||||
value: Some(value)
|
||||
value: Some(value),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -299,10 +292,8 @@ impl<'a, T: Poolable + 'a> IdlePopper<'a, T> {
|
||||
value: to_reinsert,
|
||||
});
|
||||
to_checkout
|
||||
},
|
||||
Reservation::Unique(unique) => {
|
||||
unique
|
||||
}
|
||||
Reservation::Unique(unique) => unique,
|
||||
};
|
||||
|
||||
return Some(Idle {
|
||||
@@ -332,7 +323,7 @@ impl<T: Poolable> PoolInner<T> {
|
||||
Reservation::Shared(to_keep, to_send) => {
|
||||
value = Some(to_keep);
|
||||
to_send
|
||||
},
|
||||
}
|
||||
Reservation::Unique(uniq) => uniq,
|
||||
};
|
||||
match tx.send(reserved) {
|
||||
@@ -342,7 +333,7 @@ impl<T: Poolable> PoolInner<T> {
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
},
|
||||
}
|
||||
Err(e) => {
|
||||
value = Some(e);
|
||||
}
|
||||
@@ -361,10 +352,7 @@ impl<T: Poolable> PoolInner<T> {
|
||||
Some(value) => {
|
||||
// borrow-check scope...
|
||||
{
|
||||
let idle_list = self
|
||||
.idle
|
||||
.entry(key.clone())
|
||||
.or_insert(Vec::new());
|
||||
let idle_list = self.idle.entry(key.clone()).or_insert(Vec::new());
|
||||
if self.max_idle_per_host <= idle_list.len() {
|
||||
trace!("max idle per host for {:?}, dropping connection", key);
|
||||
return;
|
||||
@@ -390,10 +378,7 @@ impl<T: Poolable> PoolInner<T> {
|
||||
/// but the lock is going away, so clean up.
|
||||
fn connected(&mut self, key: &Key) {
|
||||
let existed = self.connecting.remove(key);
|
||||
debug_assert!(
|
||||
existed,
|
||||
"Connecting dropped, key not in pool.connecting"
|
||||
);
|
||||
debug_assert!(existed, "Connecting dropped, key not in pool.connecting");
|
||||
// cancel any waiters. if there are any, it's because
|
||||
// this Connecting task didn't complete successfully.
|
||||
// those waiters would never receive a connection.
|
||||
@@ -412,7 +397,7 @@ impl<T: Poolable> PoolInner<T> {
|
||||
self.idle_interval_ref = Some(tx);
|
||||
(dur, rx)
|
||||
} else {
|
||||
return
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -434,9 +419,7 @@ impl<T> PoolInner<T> {
|
||||
fn clean_waiters(&mut self, key: &Key) {
|
||||
let mut remove_waiters = false;
|
||||
if let Some(waiters) = self.waiters.get_mut(key) {
|
||||
waiters.retain(|tx| {
|
||||
!tx.is_canceled()
|
||||
});
|
||||
waiters.retain(|tx| !tx.is_canceled());
|
||||
remove_waiters = waiters.is_empty();
|
||||
}
|
||||
if remove_waiters {
|
||||
@@ -547,9 +530,7 @@ impl<T: Poolable> Drop for Pooled<T> {
|
||||
|
||||
impl<T: Poolable> fmt::Debug for Pooled<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Pooled")
|
||||
.field("key", &self.key)
|
||||
.finish()
|
||||
f.debug_struct("Pooled").field("key", &self.key).finish()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -567,7 +548,10 @@ pub(super) struct 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";
|
||||
if let Some(mut rx) = self.waiter.take() {
|
||||
match Pin::new(&mut rx).poll(cx) {
|
||||
@@ -577,12 +561,14 @@ impl<T: Poolable> Checkout<T> {
|
||||
} else {
|
||||
Poll::Ready(Some(Err(crate::Error::new_canceled().with(CANCELED))))
|
||||
}
|
||||
},
|
||||
}
|
||||
Poll::Pending => {
|
||||
self.waiter = Some(rx);
|
||||
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 {
|
||||
Poll::Ready(None)
|
||||
@@ -593,8 +579,7 @@ impl<T: Poolable> Checkout<T> {
|
||||
let entry = {
|
||||
let mut inner = self.pool.inner.as_ref()?.lock().unwrap();
|
||||
let expiration = Expiration::new(inner.timeout);
|
||||
let maybe_entry = inner.idle.get_mut(&self.key)
|
||||
.and_then(|list| {
|
||||
let maybe_entry = inner.idle.get_mut(&self.key).and_then(|list| {
|
||||
trace!("take? {:?}: expiration = {:?}", self.key, expiration.0);
|
||||
// A block to end the mutable borrow on list,
|
||||
// so the map below can check is_empty()
|
||||
@@ -764,9 +749,7 @@ impl<T> WeakOpt<T> {
|
||||
}
|
||||
|
||||
fn upgrade(&self) -> Option<Arc<T>> {
|
||||
self.0
|
||||
.as_ref()
|
||||
.and_then(Weak::upgrade)
|
||||
self.0.as_ref().and_then(Weak::upgrade)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -776,8 +759,8 @@ mod tests {
|
||||
use std::task::Poll;
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::common::{Exec, Future, Pin, task};
|
||||
use super::{Connecting, Key, Poolable, Pool, Reservation, WeakOpt};
|
||||
use super::{Connecting, Key, Pool, Poolable, Reservation, WeakOpt};
|
||||
use crate::common::{task, Exec, Future, Pin};
|
||||
|
||||
/// Test unique reservations.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
@@ -809,7 +792,8 @@ mod tests {
|
||||
}
|
||||
|
||||
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,
|
||||
keep_alive_timeout: Some(Duration::from_millis(100)),
|
||||
max_idle_per_host: max_idle,
|
||||
@@ -838,7 +822,8 @@ mod tests {
|
||||
struct PollOnce<'a, F>(&'a mut 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<()>;
|
||||
|
||||
@@ -846,7 +831,7 @@ mod tests {
|
||||
match Pin::new(&mut self.0).poll(cx) {
|
||||
Poll::Ready(Ok(_)) => 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(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;
|
||||
|
||||
let mut checkout = pool.checkout(key.clone());
|
||||
@@ -895,7 +883,10 @@ mod tests {
|
||||
pool.pooled(c(key.clone()), Uniq(99));
|
||||
|
||||
// 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")]
|
||||
@@ -904,7 +895,8 @@ mod tests {
|
||||
let _ = pretty_env_logger::try_init();
|
||||
tokio::time::pause();
|
||||
|
||||
let pool = Pool::new(super::Config {
|
||||
let pool = Pool::new(
|
||||
super::Config {
|
||||
enabled: true,
|
||||
keep_alive_timeout: Some(Duration::from_millis(10)),
|
||||
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(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...
|
||||
tokio::time::advance(Duration::from_millis(30)).await;
|
||||
@@ -937,17 +932,15 @@ mod tests {
|
||||
let key = Arc::new("foo".to_string());
|
||||
let pooled = pool.pooled(c(key.clone()), Uniq(41));
|
||||
|
||||
let checkout = join(
|
||||
pool.checkout(key),
|
||||
async {
|
||||
let checkout = join(pool.checkout(key), async {
|
||||
// the checkout future will park first,
|
||||
// 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);
|
||||
},
|
||||
).map(|(entry, _)| entry);
|
||||
})
|
||||
.map(|(entry, _)| entry);
|
||||
|
||||
assert_eq!(*checkout.await.unwrap(), Uniq(41));
|
||||
}
|
||||
@@ -1001,10 +994,13 @@ mod tests {
|
||||
fn pooled_drop_if_closed_doesnt_reinsert() {
|
||||
let pool = pool_no_timer();
|
||||
let key = Arc::new("localhost:12345".to_string());
|
||||
pool.pooled(c(key.clone()), CanClose {
|
||||
pool.pooled(
|
||||
c(key.clone()),
|
||||
CanClose {
|
||||
val: 57,
|
||||
closed: true,
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
assert!(!pool.locked().idle.contains_key(&key));
|
||||
}
|
||||
|
||||
@@ -2,12 +2,16 @@
|
||||
//!
|
||||
//! 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::future::Future;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use crate::{common::{Poll, task, Pin}, body::Payload, service::{MakeConnection, Service}};
|
||||
use super::conn::{SendRequest, Builder};
|
||||
use super::conn::{Builder, SendRequest};
|
||||
use crate::{
|
||||
body::Payload,
|
||||
common::{task, Pin, Poll},
|
||||
service::{MakeConnection, Service},
|
||||
};
|
||||
|
||||
/// Creates a connection via `SendRequest`.
|
||||
///
|
||||
@@ -18,7 +22,7 @@ use super::conn::{SendRequest, Builder};
|
||||
pub struct Connect<C, B, T> {
|
||||
inner: C,
|
||||
builder: Builder,
|
||||
_pd: PhantomData<fn(T, B)>
|
||||
_pd: PhantomData<fn(T, B)>,
|
||||
}
|
||||
|
||||
impl<C, B, T> Connect<C, B, T> {
|
||||
@@ -28,7 +32,7 @@ impl<C, B, T> Connect<C, B, T> {
|
||||
Self {
|
||||
inner,
|
||||
builder,
|
||||
_pd: PhantomData
|
||||
_pd: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,10 +48,13 @@ where
|
||||
{
|
||||
type Response = SendRequest<B>;
|
||||
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>> {
|
||||
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 {
|
||||
@@ -56,8 +63,7 @@ where
|
||||
|
||||
let fut = async move {
|
||||
match io.await {
|
||||
Ok(io) => {
|
||||
match builder.handshake(io).await {
|
||||
Ok(io) => match builder.handshake(io).await {
|
||||
Ok((sr, conn)) => {
|
||||
builder.exec.execute(async move {
|
||||
if let Err(e) = conn.await {
|
||||
@@ -65,9 +71,8 @@ where
|
||||
}
|
||||
});
|
||||
Ok(sr)
|
||||
},
|
||||
Err(e) => Err(e)
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
},
|
||||
Err(e) => {
|
||||
let err = crate::Error::new(crate::error::Kind::Connect).with(e.into());
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use std::mem;
|
||||
|
||||
use tokio::sync::{mpsc, watch};
|
||||
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
|
||||
#[derive(Clone, Copy)]
|
||||
@@ -21,10 +21,7 @@ pub fn channel() -> (Signal, Watch) {
|
||||
drained_rx,
|
||||
_tx: tx,
|
||||
},
|
||||
Watch {
|
||||
drained_tx,
|
||||
rx,
|
||||
},
|
||||
Watch { drained_tx, rx },
|
||||
)
|
||||
}
|
||||
|
||||
@@ -107,17 +104,14 @@ where
|
||||
Poll::Ready(None) => {
|
||||
// Drain has been triggered!
|
||||
on_drain(me.future.as_mut());
|
||||
},
|
||||
Poll::Ready(Some(_/*State::Open*/)) |
|
||||
Poll::Pending => {
|
||||
}
|
||||
Poll::Ready(Some(_ /*State::Open*/)) | Poll::Pending => {
|
||||
*me.state = State::Watch(on_drain);
|
||||
return me.future.poll(cx);
|
||||
},
|
||||
}
|
||||
},
|
||||
State::Draining => {
|
||||
return me.future.poll(cx)
|
||||
},
|
||||
}
|
||||
}
|
||||
State::Draining => 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::sync::Arc;
|
||||
|
||||
use crate::body::{Payload, Body};
|
||||
use crate::body::{Body, Payload};
|
||||
use crate::proto::h2::server::H2Stream;
|
||||
use crate::server::conn::spawn_all::{NewSvcTask, Watcher};
|
||||
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>);
|
||||
}
|
||||
|
||||
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
|
||||
// `tokio::spawn`.
|
||||
@@ -37,7 +37,7 @@ pub enum Exec {
|
||||
impl Exec {
|
||||
pub(crate) fn execute<F>(&self, fut: F)
|
||||
where
|
||||
F: Future<Output=()> + Send + 'static,
|
||||
F: Future<Output = ()> + Send + 'static,
|
||||
{
|
||||
match *self {
|
||||
Exec::Default => {
|
||||
@@ -50,22 +50,20 @@ impl Exec {
|
||||
// If no runtime, we need an executor!
|
||||
panic!("executor must be set")
|
||||
}
|
||||
},
|
||||
}
|
||||
Exec::Executor(ref e) => {
|
||||
e.execute(Box::pin(fut));
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Exec {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Exec")
|
||||
.finish()
|
||||
f.debug_struct("Exec").finish()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<F, B> H2Exec<F, B> for Exec
|
||||
where
|
||||
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
|
||||
where
|
||||
NewSvcTask<I, N, S, E, W>: Future<Output=()> + Send + 'static,
|
||||
NewSvcTask<I, N, S, E, W>: Future<Output = ()> + Send + 'static,
|
||||
S: HttpService<Body>,
|
||||
W: Watcher<I, S, E>,
|
||||
{
|
||||
@@ -92,7 +90,7 @@ where
|
||||
impl<E, F, B> H2Exec<F, B> for E
|
||||
where
|
||||
E: Executor<H2Stream<F, B>> + Clone,
|
||||
H2Stream<F, B>: Future<Output=()>,
|
||||
H2Stream<F, B>: Future<Output = ()>,
|
||||
B: Payload,
|
||||
{
|
||||
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
|
||||
where
|
||||
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>,
|
||||
W: Watcher<I, S, E>,
|
||||
{
|
||||
@@ -111,4 +109,3 @@ where
|
||||
self.execute(fut)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use std::{cmp, io};
|
||||
use std::marker::Unpin;
|
||||
use std::{cmp, io};
|
||||
|
||||
use bytes::{Buf, Bytes};
|
||||
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.
|
||||
#[derive(Debug)]
|
||||
@@ -47,7 +47,11 @@ where
|
||||
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 there are no remaining bytes, let the bytes get dropped.
|
||||
if prefix.len() > 0 {
|
||||
@@ -69,7 +73,11 @@ impl<T> AsyncWrite for Rewind<T>
|
||||
where
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -82,7 +90,11 @@ where
|
||||
}
|
||||
|
||||
#[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)
|
||||
}
|
||||
}
|
||||
@@ -91,36 +103,27 @@ where
|
||||
mod tests {
|
||||
// FIXME: re-implement tests with `async/await`, this import should
|
||||
// trigger a warning to remind us
|
||||
use super::Rewind;
|
||||
use bytes::Bytes;
|
||||
use tokio::io::AsyncReadExt;
|
||||
use super::Rewind;
|
||||
|
||||
#[tokio::test]
|
||||
async fn partial_rewind() {
|
||||
let underlying = [104, 101, 108, 108, 111];
|
||||
|
||||
let mock = tokio_test::io::Builder::new()
|
||||
.read(&underlying)
|
||||
.build();
|
||||
let mock = tokio_test::io::Builder::new().read(&underlying).build();
|
||||
|
||||
let mut stream = Rewind::new(mock);
|
||||
|
||||
// Read off some bytes, ensure we filled o1
|
||||
let mut buf = [0; 2];
|
||||
stream
|
||||
.read_exact(&mut buf)
|
||||
.await
|
||||
.expect("read1");
|
||||
|
||||
stream.read_exact(&mut buf).await.expect("read1");
|
||||
|
||||
// Rewind the stream so that it is as if we never read in the first place.
|
||||
stream.rewind(Bytes::copy_from_slice(&buf[..]));
|
||||
|
||||
let mut buf = [0; 5];
|
||||
stream
|
||||
.read_exact(&mut buf)
|
||||
.await
|
||||
.expect("read1");
|
||||
stream.read_exact(&mut buf).await.expect("read1");
|
||||
|
||||
// At this point we should have read everything that was in the MockStream
|
||||
assert_eq!(&buf, &underlying);
|
||||
@@ -130,26 +133,17 @@ mod tests {
|
||||
async fn full_rewind() {
|
||||
let underlying = [104, 101, 108, 108, 111];
|
||||
|
||||
let mock = tokio_test::io::Builder::new()
|
||||
.read(&underlying)
|
||||
.build();
|
||||
let mock = tokio_test::io::Builder::new().read(&underlying).build();
|
||||
|
||||
let mut stream = Rewind::new(mock);
|
||||
|
||||
let mut buf = [0; 5];
|
||||
stream
|
||||
.read_exact(&mut buf)
|
||||
.await
|
||||
.expect("read1");
|
||||
|
||||
stream.read_exact(&mut buf).await.expect("read1");
|
||||
|
||||
// Rewind the stream so that it is as if we never read in the first place.
|
||||
stream.rewind(Bytes::copy_from_slice(&buf[..]));
|
||||
|
||||
let mut buf = [0; 5];
|
||||
stream
|
||||
.read_exact(&mut buf)
|
||||
.await
|
||||
.expect("read1");
|
||||
stream.read_exact(&mut buf).await.expect("read1");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::mem;
|
||||
|
||||
use super::{Future, Pin, Poll, task};
|
||||
use super::{task, Future, Pin, Poll};
|
||||
|
||||
pub(crate) trait Started: Future {
|
||||
fn started(&self) -> bool;
|
||||
@@ -19,7 +19,7 @@ where
|
||||
// FIXME: allow() required due to `impl Trait` leaking types to this lint
|
||||
#[allow(missing_debug_implementations)]
|
||||
pub(crate) struct Lazy<F, R> {
|
||||
inner: Inner<F, R>
|
||||
inner: Inner<F, R>,
|
||||
}
|
||||
|
||||
enum Inner<F, R> {
|
||||
@@ -36,8 +36,7 @@ where
|
||||
fn started(&self) -> bool {
|
||||
match self.inner {
|
||||
Inner::Init(_) => false,
|
||||
Inner::Fut(_) |
|
||||
Inner::Empty => true,
|
||||
Inner::Fut(_) | Inner::Empty => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -61,7 +60,7 @@ where
|
||||
let ret = Pin::new(&mut fut).poll(cx);
|
||||
self.inner = Inner::Fut(fut);
|
||||
ret
|
||||
},
|
||||
}
|
||||
_ => unreachable!("lazy state wrong"),
|
||||
}
|
||||
}
|
||||
@@ -69,4 +68,3 @@ where
|
||||
|
||||
// The closure `F` is never pinned
|
||||
impl<F, R: Unpin> Unpin for Lazy<F, R> {}
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
macro_rules! ready {
|
||||
($e:expr) => (
|
||||
($e:expr) => {
|
||||
match $e {
|
||||
::std::task::Poll::Ready(v) => v,
|
||||
::std::task::Poll::Pending => return ::std::task::Poll::Pending,
|
||||
}
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) mod drain;
|
||||
@@ -14,16 +14,11 @@ mod lazy;
|
||||
mod never;
|
||||
pub(crate) mod task;
|
||||
|
||||
pub(crate) use self::exec::{BoxSendFuture, Exec};
|
||||
pub use self::exec::Executor;
|
||||
pub(crate) use self::exec::{BoxSendFuture, Exec};
|
||||
pub(crate) use self::lazy::{lazy, Started as Lazy};
|
||||
pub use self::never::Never;
|
||||
pub(crate) use self::task::Poll;
|
||||
|
||||
// group up types normally needed for `Future`
|
||||
pub(crate) use std::{
|
||||
future::Future,
|
||||
marker::Unpin,
|
||||
pin::Pin,
|
||||
};
|
||||
|
||||
pub(crate) use std::{future::Future, marker::Unpin, pin::Pin};
|
||||
|
||||
@@ -19,4 +19,3 @@ impl Error for Never {
|
||||
match *self {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
pub(crate) use std::task::{Context, Poll};
|
||||
use super::Never;
|
||||
pub(crate) use std::task::{Context, Poll};
|
||||
|
||||
/// 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 {
|
||||
Error {
|
||||
inner: Box::new(ErrorImpl {
|
||||
kind,
|
||||
cause: None,
|
||||
}),
|
||||
inner: Box::new(ErrorImpl { kind, cause: None }),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,9 +159,7 @@ impl Error {
|
||||
let mut cause = self.source();
|
||||
while let Some(err) = cause {
|
||||
if let Some(h2_err) = err.downcast_ref::<h2::Error>() {
|
||||
return h2_err
|
||||
.reason()
|
||||
.unwrap_or(h2::Reason::INTERNAL_ERROR);
|
||||
return h2_err.reason().unwrap_or(h2::Reason::INTERNAL_ERROR);
|
||||
}
|
||||
cause = err.source();
|
||||
}
|
||||
@@ -335,7 +330,9 @@ impl StdError for Error {
|
||||
Kind::User(User::UnexpectedHeader) => "user sent unexpected header",
|
||||
Kind::User(User::UnsupportedVersion) => "request has unsupported HTTP version",
|
||||
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::NoUpgrade) => "no upgrade available",
|
||||
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)> {
|
||||
self
|
||||
.inner
|
||||
self.inner
|
||||
.cause
|
||||
.as_ref()
|
||||
.map(|cause| &**cause as &(dyn StdError + 'static))
|
||||
@@ -361,10 +357,10 @@ impl From<Parse> for Error {
|
||||
impl From<httparse::Error> for Parse {
|
||||
fn from(err: httparse::Error) -> Parse {
|
||||
match err {
|
||||
httparse::Error::HeaderName |
|
||||
httparse::Error::HeaderValue |
|
||||
httparse::Error::NewLine |
|
||||
httparse::Error::Token => Parse::Header,
|
||||
httparse::Error::HeaderName
|
||||
| httparse::Error::HeaderValue
|
||||
| httparse::Error::NewLine
|
||||
| httparse::Error::Token => Parse::Header,
|
||||
httparse::Error::Status => Parse::Status,
|
||||
httparse::Error::TooManyHeaders => Parse::TooLarge,
|
||||
httparse::Error::Version => Parse::Version,
|
||||
@@ -403,8 +399,8 @@ impl AssertSendSync for Error {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::mem;
|
||||
use super::*;
|
||||
use std::mem;
|
||||
|
||||
#[test]
|
||||
fn error_size_of() {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use bytes::BytesMut;
|
||||
use http::HeaderMap;
|
||||
use http::header::{CONTENT_LENGTH, TRANSFER_ENCODING};
|
||||
use http::header::{HeaderValue, OccupiedEntry, ValueIter};
|
||||
use http::header::{CONTENT_LENGTH, TRANSFER_ENCODING};
|
||||
use http::HeaderMap;
|
||||
|
||||
pub fn connection_keep_alive(value: &HeaderValue) -> bool {
|
||||
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> {
|
||||
value
|
||||
.to_str()
|
||||
.ok()
|
||||
.and_then(|s| s.parse().ok())
|
||||
value.to_str().ok().and_then(|s| s.parse().ok())
|
||||
}
|
||||
|
||||
pub fn content_length_parse_all(headers: &HeaderMap) -> Option<u64> {
|
||||
@@ -38,21 +35,18 @@ pub fn content_length_parse_all_values(values: ValueIter<'_, HeaderValue>) -> Op
|
||||
// be alright if they all contain the same value, and all parse
|
||||
// correctly. If not, then it's an error.
|
||||
|
||||
let folded = values
|
||||
.fold(None, |prev, line| match prev {
|
||||
Some(Ok(prev)) => {
|
||||
Some(line
|
||||
.to_str()
|
||||
let folded = values.fold(None, |prev, line| match prev {
|
||||
Some(Ok(prev)) => Some(
|
||||
line.to_str()
|
||||
.map_err(|_| ())
|
||||
.and_then(|s| s.parse().map_err(|_| ()))
|
||||
.and_then(|n| if prev == n { Ok(n) } else { Err(()) }))
|
||||
},
|
||||
None => {
|
||||
Some(line
|
||||
.to_str()
|
||||
.and_then(|n| if prev == n { Ok(n) } else { Err(()) }),
|
||||
),
|
||||
None => Some(
|
||||
line.to_str()
|
||||
.map_err(|_| ())
|
||||
.and_then(|s| s.parse().map_err(|_| ())))
|
||||
},
|
||||
.and_then(|s| s.parse().map_err(|_| ())),
|
||||
),
|
||||
Some(Err(())) => Some(Err(())),
|
||||
});
|
||||
|
||||
|
||||
27
src/lib.rs
27
src/lib.rs
@@ -29,38 +29,31 @@
|
||||
//! TCP (using tokio).
|
||||
//! - `stream` (*enabled by default*): Provides `futures::Stream` capabilities.
|
||||
|
||||
#[doc(hidden)] pub use http;
|
||||
#[macro_use] extern crate log;
|
||||
#[doc(hidden)]
|
||||
pub use http;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
#[cfg(all(test, feature = "nightly"))]
|
||||
extern crate test;
|
||||
|
||||
pub use http::{
|
||||
header,
|
||||
HeaderMap,
|
||||
Method,
|
||||
Request,
|
||||
Response,
|
||||
StatusCode,
|
||||
Uri,
|
||||
Version,
|
||||
};
|
||||
pub use http::{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::client::Client;
|
||||
pub use crate::error::{Error, Result};
|
||||
pub use crate::server::Server;
|
||||
|
||||
#[macro_use]
|
||||
mod common;
|
||||
#[cfg(test)]
|
||||
mod mock;
|
||||
pub mod body;
|
||||
pub mod client;
|
||||
pub mod error;
|
||||
mod headers;
|
||||
#[cfg(test)]
|
||||
mod mock;
|
||||
mod proto;
|
||||
pub mod rt;
|
||||
pub mod server;
|
||||
pub mod service;
|
||||
pub mod rt;
|
||||
pub mod upgrade;
|
||||
|
||||
@@ -3,16 +3,16 @@ use std::io::{self};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use bytes::{Buf, Bytes};
|
||||
use http::{HeaderMap, Method, Version};
|
||||
use http::header::{HeaderValue, CONNECTION};
|
||||
use http::{HeaderMap, Method, Version};
|
||||
use tokio::io::{AsyncRead, AsyncWrite};
|
||||
|
||||
use crate::Chunk;
|
||||
use crate::common::{Pin, Poll, Unpin, task};
|
||||
use crate::proto::{BodyLength, DecodedLength, MessageHead};
|
||||
use super::io::Buffered;
|
||||
use super::{/*Decode,*/ Decoder, Encode, EncodedBuf, Encoder, Http1Transaction, ParseContext,};
|
||||
use crate::common::{task, Pin, Poll, Unpin};
|
||||
use crate::headers::connection_keep_alive;
|
||||
use super::io::{Buffered};
|
||||
use super::{EncodedBuf, Encode, Encoder, /*Decode,*/ Decoder, Http1Transaction, ParseContext};
|
||||
use crate::proto::{BodyLength, DecodedLength, MessageHead};
|
||||
use crate::Chunk;
|
||||
|
||||
const H2_PREFACE: &'static [u8] = b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
|
||||
|
||||
@@ -26,11 +26,12 @@ 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> {
|
||||
io: Buffered<I, EncodedBuf<B>>,
|
||||
state: State,
|
||||
_marker: PhantomData<fn(T)>
|
||||
_marker: PhantomData<fn(T)>,
|
||||
}
|
||||
|
||||
impl<I, B, T> Conn<I, B, T>
|
||||
where I: AsyncRead + AsyncWrite + Unpin,
|
||||
where
|
||||
I: AsyncRead + AsyncWrite + Unpin,
|
||||
B: Buf,
|
||||
T: Http1Transaction,
|
||||
{
|
||||
@@ -107,7 +108,7 @@ where I: AsyncRead + AsyncWrite + Unpin,
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@@ -129,14 +130,20 @@ where I: AsyncRead + AsyncWrite + Unpin,
|
||||
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());
|
||||
trace!("Conn::read_head");
|
||||
|
||||
let msg = match ready!(self.io.parse::<T>(cx, ParseContext {
|
||||
let msg = match ready!(self.io.parse::<T>(
|
||||
cx,
|
||||
ParseContext {
|
||||
cached_headers: &mut self.state.cached_headers,
|
||||
req_method: &mut self.state.method,
|
||||
})) {
|
||||
}
|
||||
)) {
|
||||
Ok(msg) => msg,
|
||||
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();
|
||||
if was_mid_parse || must_error {
|
||||
// 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) {
|
||||
Ok(()) => Poll::Pending, // XXX: wat?
|
||||
Err(e) => Poll::Ready(Some(Err(e))),
|
||||
|
||||
}
|
||||
} else {
|
||||
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());
|
||||
|
||||
let (reading, ret) = match self.state.reading {
|
||||
@@ -201,11 +214,14 @@ where I: AsyncRead + AsyncWrite + Unpin,
|
||||
Poll::Ready(Ok(slice)) => {
|
||||
let (reading, chunk) = if decoder.is_eof() {
|
||||
debug!("incoming body completed");
|
||||
(Reading::KeepAlive, if !slice.is_empty() {
|
||||
(
|
||||
Reading::KeepAlive,
|
||||
if !slice.is_empty() {
|
||||
Some(Ok(Chunk::from(slice)))
|
||||
} else {
|
||||
None
|
||||
})
|
||||
},
|
||||
)
|
||||
} else if slice.is_empty() {
|
||||
error!("incoming body unexpectedly ended");
|
||||
// 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))));
|
||||
};
|
||||
(reading, Poll::Ready(chunk))
|
||||
},
|
||||
}
|
||||
Poll::Pending => return Poll::Pending,
|
||||
Poll::Ready(Err(e)) => {
|
||||
debug!("incoming body decode error: {}", e);
|
||||
(Reading::Closed, Poll::Ready(Some(Err(e))))
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
_ => unreachable!("read_body invalid state: {:?}", self.state.reading),
|
||||
};
|
||||
|
||||
@@ -287,7 +303,10 @@ where I: AsyncRead + AsyncWrite + Unpin,
|
||||
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()))
|
||||
}
|
||||
|
||||
@@ -321,27 +340,20 @@ where I: AsyncRead + AsyncWrite + Unpin,
|
||||
}))
|
||||
}
|
||||
|
||||
|
||||
fn maybe_notify(&mut self, cx: &mut task::Context<'_>) {
|
||||
// its possible that we returned NotReady from poll() without having
|
||||
// exhausted the underlying Io. We would have done this when we
|
||||
// determined we couldn't keep reading until we knew how writing
|
||||
// would finish.
|
||||
|
||||
|
||||
|
||||
match self.state.reading {
|
||||
Reading::Body(..) |
|
||||
Reading::KeepAlive |
|
||||
Reading::Closed => return,
|
||||
Reading::Body(..) | Reading::KeepAlive | Reading::Closed => return,
|
||||
Reading::Init => (),
|
||||
};
|
||||
|
||||
match self.state.writing {
|
||||
Writing::Body(..) => return,
|
||||
Writing::Init |
|
||||
Writing::KeepAlive |
|
||||
Writing::Closed => (),
|
||||
Writing::Init | Writing::KeepAlive | Writing::Closed => (),
|
||||
}
|
||||
|
||||
if !self.io.is_read_blocked() {
|
||||
@@ -357,11 +369,11 @@ where I: AsyncRead + AsyncWrite + Unpin,
|
||||
}
|
||||
return;
|
||||
}
|
||||
},
|
||||
}
|
||||
Poll::Pending => {
|
||||
trace!("maybe_notify; read_from_io blocked");
|
||||
return
|
||||
},
|
||||
return;
|
||||
}
|
||||
Poll::Ready(Err(e)) => {
|
||||
trace!("maybe_notify; read_from_io error: {}", e);
|
||||
self.state.close();
|
||||
@@ -382,21 +394,19 @@ where I: AsyncRead + AsyncWrite + Unpin,
|
||||
if !T::should_read_first() {
|
||||
match self.state.reading {
|
||||
Reading::Closed => return false,
|
||||
_ => {},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
match self.state.writing {
|
||||
Writing::Init => true,
|
||||
_ => false
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn can_write_body(&self) -> bool {
|
||||
match self.state.writing {
|
||||
Writing::Body(..) => true,
|
||||
Writing::Init |
|
||||
Writing::KeepAlive |
|
||||
Writing::Closed => false,
|
||||
Writing::Init | 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) {
|
||||
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();
|
||||
// Make sure we don't write a body if we weren't actually allowed
|
||||
// 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());
|
||||
|
||||
if !T::should_read_first() {
|
||||
@@ -442,24 +458,27 @@ where I: AsyncRead + AsyncWrite + Unpin,
|
||||
self.enforce_version(&mut head);
|
||||
|
||||
let buf = self.io.headers_buf();
|
||||
match T::encode(Encode {
|
||||
match T::encode(
|
||||
Encode {
|
||||
head: &mut head,
|
||||
body,
|
||||
keep_alive: self.state.wants_keep_alive(),
|
||||
req_method: &mut self.state.method,
|
||||
title_case_headers: self.state.title_case_headers,
|
||||
}, buf) {
|
||||
},
|
||||
buf,
|
||||
) {
|
||||
Ok(encoder) => {
|
||||
debug_assert!(self.state.cached_headers.is_none());
|
||||
debug_assert!(head.headers.is_empty());
|
||||
self.state.cached_headers = Some(head.headers);
|
||||
Some(encoder)
|
||||
},
|
||||
}
|
||||
Err(err) => {
|
||||
self.state.error = Some(err);
|
||||
self.state.writing = Writing::Closed;
|
||||
None
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -478,10 +497,12 @@ where I: AsyncRead + AsyncWrite + Unpin,
|
||||
Version::HTTP_10 => self.state.disable_keep_alive(),
|
||||
// If response is version 1.1 and keep-alive is wanted, add
|
||||
// Connection: keep-alive header when not present
|
||||
Version::HTTP_11 => if self.state.wants_keep_alive() {
|
||||
Version::HTTP_11 => {
|
||||
if self.state.wants_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
|
||||
// to work with our older peer.
|
||||
fn enforce_version(&mut self, head: &mut MessageHead<T::Outgoing>) {
|
||||
|
||||
match self.state.version {
|
||||
Version::HTTP_10 => {
|
||||
// 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
|
||||
// to do only speak HTTP/1.0 as well.
|
||||
head.version = Version::HTTP_10;
|
||||
},
|
||||
}
|
||||
_ => {
|
||||
// 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
|
||||
@@ -525,7 +545,7 @@ where I: AsyncRead + AsyncWrite + Unpin,
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
},
|
||||
}
|
||||
_ => unreachable!("write_body invalid state: {:?}", self.state.writing),
|
||||
};
|
||||
|
||||
@@ -545,7 +565,7 @@ where I: AsyncRead + AsyncWrite + Unpin,
|
||||
} else {
|
||||
Writing::Closed
|
||||
}
|
||||
},
|
||||
}
|
||||
_ => unreachable!("write_body invalid state: {:?}", self.state.writing),
|
||||
};
|
||||
|
||||
@@ -568,15 +588,14 @@ where I: AsyncRead + AsyncWrite + Unpin,
|
||||
} else {
|
||||
Writing::KeepAlive
|
||||
}
|
||||
},
|
||||
}
|
||||
Err(_not_eof) => Writing::Closed,
|
||||
}
|
||||
},
|
||||
}
|
||||
_ => return,
|
||||
};
|
||||
|
||||
self.state.writing = state;
|
||||
|
||||
}
|
||||
|
||||
// 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
|
||||
// - 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<()> {
|
||||
|
||||
match self.state.writing {
|
||||
Writing::Init => {
|
||||
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) {
|
||||
// Drop the cached headers so as to not trigger a debug
|
||||
@@ -619,7 +637,7 @@ where I: AsyncRead + AsyncWrite + Unpin,
|
||||
Ok(()) => {
|
||||
trace!("shut down IO complete");
|
||||
Poll::Ready(Ok(()))
|
||||
},
|
||||
}
|
||||
Err(e) => {
|
||||
debug!("error shutting down IO: {}", e);
|
||||
Poll::Ready(Err(e))
|
||||
@@ -741,9 +759,7 @@ impl fmt::Debug for Writing {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match *self {
|
||||
Writing::Init => f.write_str("Init"),
|
||||
Writing::Body(ref enc) => f.debug_tuple("Body")
|
||||
.field(enc)
|
||||
.finish(),
|
||||
Writing::Body(ref enc) => f.debug_tuple("Body").field(enc).finish(),
|
||||
Writing::KeepAlive => f.write_str("KeepAlive"),
|
||||
Writing::Closed => f.write_str("Closed"),
|
||||
}
|
||||
@@ -824,15 +840,18 @@ impl State {
|
||||
if let KA::Busy = self.keep_alive.status() {
|
||||
self.idle::<T>();
|
||||
} 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();
|
||||
}
|
||||
},
|
||||
(&Reading::Closed, &Writing::KeepAlive) |
|
||||
(&Reading::KeepAlive, &Writing::Closed) => {
|
||||
}
|
||||
(&Reading::Closed, &Writing::KeepAlive) | (&Reading::KeepAlive, &Writing::Closed) => {
|
||||
self.close()
|
||||
}
|
||||
_ => ()
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -880,14 +899,14 @@ impl State {
|
||||
fn is_read_closed(&self) -> bool {
|
||||
match self.reading {
|
||||
Reading::Closed => true,
|
||||
_ => false
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_write_closed(&self) -> bool {
|
||||
match self.writing {
|
||||
Writing::Closed => true,
|
||||
_ => false
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -929,10 +948,9 @@ mod tests {
|
||||
let mut headers = x.0.headers;
|
||||
headers.clear();
|
||||
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);
|
||||
unsafe {
|
||||
|
||||
@@ -24,8 +24,7 @@ pub(crate) fn update_and_header_value() -> HeaderValue {
|
||||
CACHED.with(|cache| {
|
||||
let mut cache = cache.borrow_mut();
|
||||
cache.check();
|
||||
HeaderValue::from_bytes(cache.buffer())
|
||||
.expect("Date format should be valid HeaderValue")
|
||||
HeaderValue::from_bytes(cache.buffer()).expect("Date format should be valid HeaderValue")
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
use std::error::Error as StdError;
|
||||
use std::fmt;
|
||||
use std::usize;
|
||||
use std::io;
|
||||
use std::usize;
|
||||
|
||||
use bytes::Bytes;
|
||||
|
||||
use crate::common::{Poll, task};
|
||||
use crate::common::{task, Poll};
|
||||
|
||||
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.
|
||||
///
|
||||
@@ -64,15 +64,21 @@ impl Decoder {
|
||||
// constructors
|
||||
|
||||
pub fn length(x: u64) -> Decoder {
|
||||
Decoder { kind: Kind::Length(x) }
|
||||
Decoder {
|
||||
kind: Kind::Length(x),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn chunked() -> Decoder {
|
||||
Decoder { kind: Kind::Chunked(ChunkedState::Size, 0) }
|
||||
Decoder {
|
||||
kind: Kind::Chunked(ChunkedState::Size, 0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eof() -> Decoder {
|
||||
Decoder { kind: Kind::Eof(false) }
|
||||
Decoder {
|
||||
kind: Kind::Eof(false),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn new(len: DecodedLength) -> Self {
|
||||
@@ -87,14 +93,16 @@ impl Decoder {
|
||||
|
||||
pub fn is_eof(&self) -> bool {
|
||||
match self.kind {
|
||||
Length(0) |
|
||||
Chunked(ChunkedState::End, _) |
|
||||
Eof(true) => true,
|
||||
Length(0) | Chunked(ChunkedState::End, _) | Eof(true) => true,
|
||||
_ => 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);
|
||||
match self.kind {
|
||||
Length(ref mut remaining) => {
|
||||
@@ -107,7 +115,10 @@ impl Decoder {
|
||||
if num > *remaining {
|
||||
*remaining = 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 {
|
||||
*remaining -= num;
|
||||
}
|
||||
@@ -146,13 +157,10 @@ impl Decoder {
|
||||
|
||||
#[cfg(test)]
|
||||
async fn decode_fut<R: MemRead>(&mut self, body: &mut R) -> Result<Bytes, io::Error> {
|
||||
futures_util::future::poll_fn(move |cx| {
|
||||
self.decode(cx, body)
|
||||
}).await
|
||||
futures_util::future::poll_fn(move |cx| self.decode(cx, body)).await
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl fmt::Debug for Decoder {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Debug::fmt(&self.kind, f)
|
||||
@@ -172,12 +180,13 @@ macro_rules! byte (
|
||||
);
|
||||
|
||||
impl ChunkedState {
|
||||
fn step<R: MemRead>(&self,
|
||||
fn step<R: MemRead>(
|
||||
&self,
|
||||
cx: &mut task::Context<'_>,
|
||||
body: &mut R,
|
||||
size: &mut u64,
|
||||
buf: &mut Option<Bytes>)
|
||||
-> Poll<Result<ChunkedState, io::Error>> {
|
||||
buf: &mut Option<Bytes>,
|
||||
) -> Poll<Result<ChunkedState, io::Error>> {
|
||||
use self::ChunkedState::*;
|
||||
match *self {
|
||||
Size => ChunkedState::read_size(cx, body, size),
|
||||
@@ -192,7 +201,11 @@ impl ChunkedState {
|
||||
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");
|
||||
let radix = 16;
|
||||
match byte!(rdr, cx) {
|
||||
@@ -212,33 +225,45 @@ impl ChunkedState {
|
||||
b';' => return Poll::Ready(Ok(ChunkedState::Extension)),
|
||||
b'\r' => return Poll::Ready(Ok(ChunkedState::SizeLf)),
|
||||
_ => {
|
||||
return Poll::Ready(Err(io::Error::new(io::ErrorKind::InvalidInput,
|
||||
"Invalid chunk size line: Invalid Size")));
|
||||
return Poll::Ready(Err(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
"Invalid chunk size line: Invalid 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");
|
||||
match byte!(rdr, cx) {
|
||||
// LWS can follow the chunk size, but no more digits can come
|
||||
b'\t' | b' ' => Poll::Ready(Ok(ChunkedState::SizeLws)),
|
||||
b';' => Poll::Ready(Ok(ChunkedState::Extension)),
|
||||
b'\r' => Poll::Ready(Ok(ChunkedState::SizeLf)),
|
||||
_ => {
|
||||
Poll::Ready(Err(io::Error::new(io::ErrorKind::InvalidInput,
|
||||
"Invalid chunk size linear white space")))
|
||||
_ => Poll::Ready(Err(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
"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");
|
||||
match byte!(rdr, cx) {
|
||||
b'\r' => Poll::Ready(Ok(ChunkedState::SizeLf)),
|
||||
_ => 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);
|
||||
match byte!(rdr, cx) {
|
||||
b'\n' => {
|
||||
@@ -248,15 +273,20 @@ impl ChunkedState {
|
||||
debug!("incoming chunked header: {0:#X} ({0} bytes)", size);
|
||||
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>(
|
||||
cx: &mut task::Context<'_>,
|
||||
rdr: &mut R,
|
||||
rem: &mut u64,
|
||||
buf: &mut Option<Bytes>)
|
||||
-> Poll<Result<ChunkedState, io::Error>> {
|
||||
buf: &mut Option<Bytes>,
|
||||
) -> Poll<Result<ChunkedState, io::Error>> {
|
||||
trace!("Chunked read, remaining={:?}", rem);
|
||||
|
||||
// cap remaining bytes at the max capacity of usize
|
||||
@@ -271,7 +301,10 @@ impl ChunkedState {
|
||||
|
||||
if count == 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);
|
||||
*rem -= count as u64;
|
||||
@@ -282,29 +315,53 @@ impl ChunkedState {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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)]
|
||||
mod tests {
|
||||
use std::time::Duration;
|
||||
use std::pin::Pin;
|
||||
use tokio::io::AsyncRead;
|
||||
use super::*;
|
||||
use std::pin::Pin;
|
||||
use std::time::Duration;
|
||||
use tokio::io::AsyncRead;
|
||||
|
||||
impl<'a> MemRead for &'a [u8] {
|
||||
fn read_mem(&mut self, _: &mut task::Context<'_>, len: usize) -> Poll<io::Result<Bytes>> {
|
||||
@@ -363,19 +420,18 @@ mod tests {
|
||||
use crate::mock::AsyncIo;
|
||||
*/
|
||||
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_read_chunk_size() {
|
||||
use std::io::ErrorKind::{UnexpectedEof, InvalidInput};
|
||||
use std::io::ErrorKind::{InvalidInput, UnexpectedEof};
|
||||
|
||||
async fn read(s: &str) -> u64 {
|
||||
let mut state = ChunkedState::Size;
|
||||
let rdr = &mut s.as_bytes();
|
||||
let mut size = 0;
|
||||
loop {
|
||||
let result = futures_util::future::poll_fn(|cx| {
|
||||
state.step(cx, rdr, &mut size, &mut None)
|
||||
}).await;
|
||||
let result =
|
||||
futures_util::future::poll_fn(|cx| state.step(cx, rdr, &mut size, &mut None))
|
||||
.await;
|
||||
let desc = format!("read_size failed for {:?}", s);
|
||||
state = result.expect(desc.as_str());
|
||||
if state == ChunkedState::Body || state == ChunkedState::EndCr {
|
||||
@@ -390,14 +446,19 @@ mod tests {
|
||||
let rdr = &mut s.as_bytes();
|
||||
let mut size = 0;
|
||||
loop {
|
||||
let result = futures_util::future::poll_fn(|cx| {
|
||||
state.step(cx, rdr, &mut size, &mut None)
|
||||
}).await;
|
||||
let result =
|
||||
futures_util::future::poll_fn(|cx| state.step(cx, rdr, &mut size, &mut None))
|
||||
.await;
|
||||
state = match result {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
assert!(expected_err == e.kind(), "Reading {:?}, expected {:?}, but got {:?}",
|
||||
s, expected_err, e.kind());
|
||||
assert!(
|
||||
expected_err == e.kind(),
|
||||
"Reading {:?}, expected {:?}, but got {:?}",
|
||||
s,
|
||||
expected_err,
|
||||
e.kind()
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
@@ -462,7 +523,10 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn test_read_chunked_single_read() {
|
||||
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());
|
||||
let result = String::from_utf8(buf.as_ref().to_vec()).expect("decode String");
|
||||
assert_eq!("1234567890abcdef", &result);
|
||||
@@ -490,10 +554,7 @@ mod tests {
|
||||
|
||||
// perform an async read using a custom buffer size and causing a blocking
|
||||
// read at the specified byte
|
||||
async fn read_async(mut decoder: Decoder,
|
||||
content: &[u8],
|
||||
block_at: usize)
|
||||
-> String {
|
||||
async fn read_async(mut decoder: Decoder, content: &[u8], block_at: usize) -> String {
|
||||
let mut outs = Vec::new();
|
||||
|
||||
let mut ins = if block_at == 0 {
|
||||
|
||||
@@ -4,10 +4,13 @@ use bytes::{Buf, Bytes};
|
||||
use http::{Request, Response, StatusCode};
|
||||
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 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;
|
||||
|
||||
pub(crate) struct Dispatcher<D, Bs: Payload, I, T> {
|
||||
@@ -23,7 +26,10 @@ pub(crate) trait Dispatch {
|
||||
type PollBody;
|
||||
type PollError;
|
||||
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 poll_ready(&mut self, cx: &mut task::Context<'_>) -> Poll<Result<(), ()>>;
|
||||
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>
|
||||
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>>,
|
||||
I: AsyncRead + AsyncWrite + Unpin,
|
||||
T: Http1Transaction + Unpin,
|
||||
@@ -77,7 +87,10 @@ where
|
||||
///
|
||||
/// This is useful for old-style HTTP upgrades, but ignores
|
||||
/// 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
|
||||
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| {
|
||||
// An error means we're shutting down either way.
|
||||
// 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();
|
||||
|
||||
ready!(self.poll_loop(cx))?;
|
||||
@@ -161,7 +182,7 @@ where
|
||||
Poll::Pending => {
|
||||
self.body_tx = Some(body);
|
||||
return Poll::Pending;
|
||||
},
|
||||
}
|
||||
Poll::Ready(Err(_canceled)) => {
|
||||
// user doesn't care about the body
|
||||
// so we should stop reading
|
||||
@@ -171,22 +192,20 @@ where
|
||||
}
|
||||
}
|
||||
match self.conn.poll_read_body(cx) {
|
||||
Poll::Ready(Some(Ok(chunk))) => {
|
||||
match body.try_send_data(chunk) {
|
||||
Poll::Ready(Some(Ok(chunk))) => match body.try_send_data(chunk) {
|
||||
Ok(()) => {
|
||||
self.body_tx = Some(body);
|
||||
},
|
||||
}
|
||||
Err(_canceled) => {
|
||||
if self.conn.can_read_body() {
|
||||
trace!("body receiver dropped before eof, closing");
|
||||
self.conn.close_read();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Poll::Ready(None) => {
|
||||
// just drop, the body will close automatically
|
||||
},
|
||||
}
|
||||
Poll::Pending => {
|
||||
self.body_tx = Some(body);
|
||||
return Poll::Pending;
|
||||
@@ -223,14 +242,14 @@ where
|
||||
let (tx, rx) = Body::new_channel(other.into_opt());
|
||||
self.body_tx = Some(tx);
|
||||
rx
|
||||
},
|
||||
}
|
||||
};
|
||||
if wants_upgrade {
|
||||
body.set_on_upgrade(self.conn.on_upgrade());
|
||||
}
|
||||
self.dispatch.recv_msg(Ok((head, body)))?;
|
||||
Poll::Ready(Ok(()))
|
||||
},
|
||||
}
|
||||
Some(Err(err)) => {
|
||||
debug!("read_head error: {}", err);
|
||||
self.dispatch.recv_msg(Err(err))?;
|
||||
@@ -239,7 +258,7 @@ where
|
||||
// not as a second error.
|
||||
self.close();
|
||||
Poll::Ready(Ok(()))
|
||||
},
|
||||
}
|
||||
None => {
|
||||
// read eof, the write side will have been closed too unless
|
||||
// allow_read_close was set to true, in which case just do
|
||||
@@ -257,7 +276,10 @@ where
|
||||
loop {
|
||||
if self.is_closing {
|
||||
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)) {
|
||||
let (head, mut body) = msg.map_err(crate::Error::new_user_service)?;
|
||||
|
||||
@@ -274,7 +296,9 @@ where
|
||||
self.body_rx.set(None);
|
||||
None
|
||||
} else {
|
||||
let btype = body.size_hint().exact()
|
||||
let btype = body
|
||||
.size_hint()
|
||||
.exact()
|
||||
.map(BodyLength::Known)
|
||||
.or_else(|| Some(BodyLength::Unknown));
|
||||
self.body_rx.set(Some(body));
|
||||
@@ -289,7 +313,9 @@ where
|
||||
ready!(self.poll_flush(cx))?;
|
||||
} else {
|
||||
// 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");
|
||||
if !self.conn.can_write_body() {
|
||||
trace!(
|
||||
@@ -357,8 +383,8 @@ where
|
||||
// a client that cannot read may was well be done.
|
||||
true
|
||||
} else {
|
||||
let write_done = self.conn.is_write_closed() ||
|
||||
(!self.dispatch.should_poll() && self.body_rx.is_none());
|
||||
let write_done = self.conn.is_write_closed()
|
||||
|| (!self.dispatch.should_poll() && self.body_rx.is_none());
|
||||
read_done && write_done
|
||||
}
|
||||
}
|
||||
@@ -366,7 +392,11 @@ where
|
||||
|
||||
impl<D, Bs, I, T> Future for Dispatcher<D, Bs, I, T>
|
||||
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>>,
|
||||
I: AsyncRead + AsyncWrite + 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>
|
||||
where
|
||||
S: HttpService<Body, ResBody=Bs>,
|
||||
S: HttpService<Body, ResBody = Bs>,
|
||||
S::Error: Into<Box<dyn StdError + Send + Sync>>,
|
||||
Bs: Payload,
|
||||
{
|
||||
@@ -436,7 +466,10 @@ where
|
||||
type PollError = S::Error;
|
||||
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 resp = ready!(fut.as_mut().poll(cx)?);
|
||||
let (parts, body) = resp.into_parts();
|
||||
@@ -471,8 +504,7 @@ where
|
||||
if self.in_flight.is_some() {
|
||||
Poll::Pending
|
||||
} else {
|
||||
self.service.poll_ready(cx)
|
||||
.map_err(|_e| {
|
||||
self.service.poll_ready(cx).map_err(|_e| {
|
||||
// FIXME: return error value.
|
||||
trace!("service closed");
|
||||
})
|
||||
@@ -486,7 +518,6 @@ where
|
||||
|
||||
// ===== impl Client =====
|
||||
|
||||
|
||||
impl<B> Client<B> {
|
||||
pub fn new(rx: ClientRx<B>) -> Client<B> {
|
||||
Client {
|
||||
@@ -506,7 +537,10 @@ where
|
||||
type PollError = Never;
|
||||
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);
|
||||
match self.rx.poll_next(cx) {
|
||||
Poll::Ready(Some((req, mut cb))) => {
|
||||
@@ -515,7 +549,7 @@ where
|
||||
Poll::Ready(()) => {
|
||||
trace!("request canceled");
|
||||
Poll::Ready(None)
|
||||
},
|
||||
}
|
||||
Poll::Pending => {
|
||||
let (parts, body) = req.into_parts();
|
||||
let head = RequestHead {
|
||||
@@ -527,13 +561,13 @@ where
|
||||
Poll::Ready(Some(Ok((head, body))))
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
Poll::Ready(None) => {
|
||||
// user has dropped sender handle
|
||||
trace!("client tx closed");
|
||||
self.rx_closed = true;
|
||||
Poll::Ready(None)
|
||||
},
|
||||
}
|
||||
Poll::Pending => Poll::Pending,
|
||||
}
|
||||
}
|
||||
@@ -554,7 +588,7 @@ where
|
||||
// full message!
|
||||
Err(crate::Error::new_unexpected_message())
|
||||
}
|
||||
},
|
||||
}
|
||||
Err(err) => {
|
||||
if let Some(cb) = self.callback.take() {
|
||||
let _ = cb.send(Err((err, None)));
|
||||
@@ -583,7 +617,7 @@ where
|
||||
Poll::Ready(()) => {
|
||||
trace!("callback receiver has dropped");
|
||||
Poll::Ready(Err(()))
|
||||
},
|
||||
}
|
||||
Poll::Pending => Poll::Ready(Ok(())),
|
||||
},
|
||||
None => Poll::Ready(Err(())),
|
||||
@@ -597,18 +631,16 @@ where
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::time::Duration;
|
||||
use super::*;
|
||||
use crate::proto::h1::ClientTransaction;
|
||||
use std::time::Duration;
|
||||
|
||||
#[test]
|
||||
fn client_read_bytes_before_writing_request() {
|
||||
let _ = pretty_env_logger::try_init();
|
||||
|
||||
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
|
||||
// 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");
|
||||
|
||||
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));
|
||||
let err = tokio_test::assert_ready_ok!(Pin::new(&mut res_rx).poll(cx))
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use std::fmt;
|
||||
use std::io::IoSlice;
|
||||
|
||||
use bytes::Buf;
|
||||
use bytes::buf::ext::{BufExt, Chain, Take};
|
||||
use bytes::Buf;
|
||||
|
||||
use super::io::WriteBuf;
|
||||
|
||||
@@ -68,7 +68,7 @@ impl Encoder {
|
||||
pub fn is_eof(&self) -> bool {
|
||||
match self.kind {
|
||||
Kind::Length(0) => true,
|
||||
_ => false
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ impl Encoder {
|
||||
.chain(msg)
|
||||
.chain(b"\r\n" as &'static [u8]);
|
||||
BufKind::Chunked(buf)
|
||||
},
|
||||
}
|
||||
Kind::Length(ref mut remaining) => {
|
||||
trace!("sized write, len = {}", len);
|
||||
if len as u64 > *remaining {
|
||||
@@ -116,15 +116,13 @@ impl Encoder {
|
||||
*remaining -= len as u64;
|
||||
BufKind::Exact(msg)
|
||||
}
|
||||
},
|
||||
}
|
||||
Kind::CloseDelimited => {
|
||||
trace!("close delimited write {}B", len);
|
||||
BufKind::Exact(msg)
|
||||
}
|
||||
};
|
||||
EncodedBuf {
|
||||
kind,
|
||||
}
|
||||
EncodedBuf { kind }
|
||||
}
|
||||
|
||||
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]);
|
||||
dst.buffer(buf);
|
||||
!self.is_last
|
||||
},
|
||||
}
|
||||
Kind::Length(remaining) => {
|
||||
use std::cmp::Ordering;
|
||||
|
||||
@@ -151,17 +149,17 @@ impl Encoder {
|
||||
Ordering::Equal => {
|
||||
dst.buffer(msg);
|
||||
!self.is_last
|
||||
},
|
||||
}
|
||||
Ordering::Greater => {
|
||||
dst.buffer(msg.take(remaining as usize));
|
||||
!self.is_last
|
||||
},
|
||||
}
|
||||
Ordering::Less => {
|
||||
dst.buffer(msg);
|
||||
false
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
Kind::CloseDelimited => {
|
||||
trace!("close delimited write {}B", len);
|
||||
dst.buffer(msg);
|
||||
@@ -180,10 +178,13 @@ impl Encoder {
|
||||
B: Buf,
|
||||
{
|
||||
debug_assert!(msg.remaining() > 0, "encode() called with empty buf");
|
||||
debug_assert!(match self.kind {
|
||||
debug_assert!(
|
||||
match self.kind {
|
||||
Kind::Length(len) => len == msg.remaining() as u64,
|
||||
_ => true,
|
||||
}, "danger_full_buf length mismatches");
|
||||
},
|
||||
"danger_full_buf length mismatches"
|
||||
);
|
||||
|
||||
match self.kind {
|
||||
Kind::Chunked => {
|
||||
@@ -193,10 +194,10 @@ impl Encoder {
|
||||
.chain(msg)
|
||||
.chain(b"\r\n0\r\n\r\n" as &'static [u8]);
|
||||
dst.buffer(buf);
|
||||
},
|
||||
}
|
||||
_ => {
|
||||
dst.buffer(msg);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -246,7 +247,6 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
const USIZE_BYTES: usize = 4;
|
||||
|
||||
@@ -271,8 +271,7 @@ impl ChunkSize {
|
||||
pos: 0,
|
||||
len: 0,
|
||||
};
|
||||
write!(&mut size, "{:X}\r\n", len)
|
||||
.expect("CHUNK_SIZE_MAX_BYTES should fit any usize");
|
||||
write!(&mut size, "{:X}\r\n", len).expect("CHUNK_SIZE_MAX_BYTES should fit any usize");
|
||||
size
|
||||
}
|
||||
}
|
||||
@@ -285,7 +284,7 @@ impl Buf for ChunkSize {
|
||||
|
||||
#[inline]
|
||||
fn bytes(&self) -> &[u8] {
|
||||
&self.bytes[self.pos.into() .. self.len.into()]
|
||||
&self.bytes[self.pos.into()..self.len.into()]
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -307,7 +306,8 @@ impl fmt::Debug for ChunkSize {
|
||||
impl fmt::Write for ChunkSize {
|
||||
fn write_str(&mut self, num: &str) -> fmt::Result {
|
||||
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");
|
||||
self.len += num.len() as u8; // safe because bytes is never bigger than 256
|
||||
Ok(())
|
||||
@@ -340,7 +340,7 @@ impl<B: Buf> From<Chain<Chain<ChunkSize, B>, StaticBuf>> for EncodedBuf<B> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use bytes::{BufMut};
|
||||
use bytes::BufMut;
|
||||
|
||||
use super::super::io::Cursor;
|
||||
use super::Encoder;
|
||||
@@ -364,7 +364,10 @@ mod tests {
|
||||
let end = encoder.end::<Cursor<Vec<u8>>>().unwrap().unwrap();
|
||||
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]
|
||||
@@ -373,12 +376,10 @@ mod tests {
|
||||
let mut encoder = Encoder::length(max_len as u64);
|
||||
let mut dst = Vec::new();
|
||||
|
||||
|
||||
let msg1 = b"foo bar".as_ref();
|
||||
let buf1 = encoder.encode(msg1);
|
||||
dst.put(buf1);
|
||||
|
||||
|
||||
assert_eq!(dst, b"foo bar");
|
||||
assert!(!encoder.is_eof());
|
||||
encoder.end::<()>().unwrap_err();
|
||||
@@ -398,12 +399,10 @@ mod tests {
|
||||
let mut encoder = Encoder::close_delimited();
|
||||
let mut dst = Vec::new();
|
||||
|
||||
|
||||
let msg1 = b"foo bar".as_ref();
|
||||
let buf1 = encoder.encode(msg1);
|
||||
dst.put(buf1);
|
||||
|
||||
|
||||
assert_eq!(dst, b"foo bar");
|
||||
assert!(!encoder.is_eof());
|
||||
encoder.end::<()>().unwrap_err();
|
||||
|
||||
@@ -7,8 +7,8 @@ use std::io::{self, IoSlice};
|
||||
use bytes::{Buf, BufMut, Bytes, BytesMut};
|
||||
use tokio::io::{AsyncRead, AsyncWrite};
|
||||
|
||||
use crate::common::{Pin, Poll, Unpin, task};
|
||||
use super::{Http1Transaction, ParseContext, ParsedMessage};
|
||||
use crate::common::{task, Pin, Poll, Unpin};
|
||||
|
||||
/// The initial buffer size allocated before trying to read from IO.
|
||||
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<'_>)
|
||||
-> Poll<crate::Result<ParsedMessage<S::Incoming>>>
|
||||
pub(super) fn parse<S>(
|
||||
&mut self,
|
||||
cx: &mut task::Context<'_>,
|
||||
parse_ctx: ParseContext<'_>,
|
||||
) -> Poll<crate::Result<ParsedMessage<S::Incoming>>>
|
||||
where
|
||||
S: Http1Transaction,
|
||||
{
|
||||
loop {
|
||||
match S::parse(&mut self.read_buf, ParseContext {
|
||||
match S::parse(
|
||||
&mut self.read_buf,
|
||||
ParseContext {
|
||||
cached_headers: parse_ctx.cached_headers,
|
||||
req_method: parse_ctx.req_method,
|
||||
})? {
|
||||
},
|
||||
)? {
|
||||
Some(msg) => {
|
||||
debug!("parsed {} headers", msg.head.headers.len());
|
||||
return Poll::Ready(Ok(msg));
|
||||
},
|
||||
}
|
||||
None => {
|
||||
let max = self.read_buf_strategy.max();
|
||||
if self.read_buf.len() >= max {
|
||||
debug!("max_buf_size ({}) reached, closing", max);
|
||||
return Poll::Ready(Err(crate::Error::new_too_large()));
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
match ready!(self.poll_read_from_io(cx)).map_err(crate::Error::new_io)? {
|
||||
0 => {
|
||||
trace!("parse eof");
|
||||
return Poll::Ready(Err(crate::Error::new_incomplete()));
|
||||
}
|
||||
_ => {},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -183,7 +189,7 @@ where
|
||||
debug!("read {} bytes", n);
|
||||
self.read_buf_strategy.record(n);
|
||||
Poll::Ready(Ok(n))
|
||||
},
|
||||
}
|
||||
Poll::Pending => {
|
||||
self.read_blocked = true;
|
||||
Poll::Pending
|
||||
@@ -215,12 +221,16 @@ where
|
||||
_ => (),
|
||||
}
|
||||
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);
|
||||
if self.write_buf.remaining() == 0 {
|
||||
break;
|
||||
} 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()));
|
||||
}
|
||||
}
|
||||
@@ -241,7 +251,10 @@ where
|
||||
self.write_buf.headers.reset();
|
||||
break;
|
||||
} 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()));
|
||||
}
|
||||
}
|
||||
@@ -283,7 +296,7 @@ enum ReadStrategy {
|
||||
Adaptive {
|
||||
decrease_now: bool,
|
||||
next: usize,
|
||||
max: usize
|
||||
max: usize,
|
||||
},
|
||||
Exact(usize),
|
||||
}
|
||||
@@ -313,7 +326,12 @@ impl ReadStrategy {
|
||||
|
||||
fn record(&mut self, bytes_read: usize) {
|
||||
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 {
|
||||
*next = cmp::min(incr_power_of_two(*next), max);
|
||||
*decrease_now = false;
|
||||
@@ -334,7 +352,7 @@ impl ReadStrategy {
|
||||
*decrease_now = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
@@ -428,7 +446,6 @@ impl<B> WriteBuf<B> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<B> WriteBuf<B>
|
||||
where
|
||||
B: Buf,
|
||||
@@ -460,22 +477,19 @@ where
|
||||
};
|
||||
buf.advance(adv);
|
||||
}
|
||||
},
|
||||
}
|
||||
WriteStrategy::Auto | WriteStrategy::Queue => {
|
||||
self.queue.bufs.push_back(buf.into());
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn can_buffer(&self) -> bool {
|
||||
match self.strategy {
|
||||
WriteStrategy::Flatten => {
|
||||
self.remaining() < self.max_buf_size
|
||||
},
|
||||
WriteStrategy::Flatten => self.remaining() < self.max_buf_size,
|
||||
WriteStrategy::Auto | WriteStrategy::Queue => {
|
||||
self.queue.bufs.len() < MAX_BUF_LIST_BUFFERS
|
||||
&& self.remaining() < self.max_buf_size
|
||||
},
|
||||
self.queue.bufs.len() < MAX_BUF_LIST_BUFFERS && self.remaining() < self.max_buf_size
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -587,7 +601,6 @@ impl<'a, B: Buf + 'a> Drop for WriteBufAuto<'a, B> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
enum WriteStrategy {
|
||||
Auto,
|
||||
@@ -599,7 +612,6 @@ struct BufDeque<T> {
|
||||
bufs: VecDeque<T>,
|
||||
}
|
||||
|
||||
|
||||
impl<T> BufDeque<T> {
|
||||
fn new() -> BufDeque<T> {
|
||||
BufDeque {
|
||||
@@ -611,9 +623,7 @@ impl<T> BufDeque<T> {
|
||||
impl<T: Buf> Buf for BufDeque<T> {
|
||||
#[inline]
|
||||
fn remaining(&self) -> usize {
|
||||
self.bufs.iter()
|
||||
.map(|buf| buf.remaining())
|
||||
.sum()
|
||||
self.bufs.iter().map(|buf| buf.remaining()).sum()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -683,7 +693,9 @@ mod tests {
|
||||
// First, let's just check that the Mock would normally return an
|
||||
// error on an unexpected write, even if the buffer is empty...
|
||||
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| {
|
||||
Pin::new(&mut mock).poll_write_buf(cx, &mut Cursor::new(&[]))
|
||||
})
|
||||
.await
|
||||
.expect_err("should be a broken pipe");
|
||||
|
||||
@@ -716,11 +728,17 @@ mod tests {
|
||||
cached_headers: &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(())
|
||||
}).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]
|
||||
@@ -756,12 +774,20 @@ mod tests {
|
||||
assert_eq!(strategy.next(), 16384);
|
||||
|
||||
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);
|
||||
assert_eq!(strategy.next(), 16384, "record was with range");
|
||||
|
||||
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);
|
||||
assert_eq!(strategy.next(), 8192, "second smaller record decrements");
|
||||
@@ -779,10 +805,18 @@ mod tests {
|
||||
assert_eq!(strategy.next(), 16384);
|
||||
|
||||
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);
|
||||
assert_eq!(strategy.next(), 16384, "with current step does not decrement");
|
||||
assert_eq!(
|
||||
strategy.next(),
|
||||
16384,
|
||||
"with current step does not decrement"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use bytes::BytesMut;
|
||||
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::dispatch::Dispatcher;
|
||||
pub use self::decode::Decoder;
|
||||
pub(crate) use self::dispatch::Dispatcher;
|
||||
pub use self::encode::{EncodedBuf, Encoder};
|
||||
pub use self::io::Cursor; //TODO: move out of h1::io
|
||||
pub use self::io::MINIMUM_MAX_BUFFER_SIZE;
|
||||
@@ -18,7 +18,6 @@ mod encode;
|
||||
mod io;
|
||||
mod role;
|
||||
|
||||
|
||||
pub(crate) type ServerTransaction = role::Server;
|
||||
pub(crate) type ClientTransaction = role::Client;
|
||||
|
||||
@@ -75,4 +74,3 @@ pub(crate) struct Encode<'a, T> {
|
||||
req_method: &'a mut Option<Method>,
|
||||
title_case_headers: bool,
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,15 +1,15 @@
|
||||
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 h2::client::{Builder, SendRequest};
|
||||
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 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};
|
||||
|
||||
type ClientRx<B> = crate::client::dispatch::Receiver<Request<B>, Response<Body>>;
|
||||
@@ -45,12 +45,9 @@ where
|
||||
let (conn_drop_ref, rx) = mpsc::channel(1);
|
||||
let (cancel_tx, conn_eof) = oneshot::channel();
|
||||
|
||||
let conn_drop_rx = rx.into_future()
|
||||
.map(|(item, _rx)| {
|
||||
match item {
|
||||
let conn_drop_rx = rx.into_future().map(|(item, _rx)| match item {
|
||||
Some(never) => match never {},
|
||||
None => (),
|
||||
}
|
||||
});
|
||||
|
||||
let conn = conn.map_err(|e| debug!("connection error: {}", e));
|
||||
@@ -138,8 +135,7 @@ where
|
||||
};
|
||||
|
||||
if !eos {
|
||||
let mut pipe = PipeToSendStream::new(body, body_tx)
|
||||
.map(|res| {
|
||||
let mut pipe = PipeToSendStream::new(body, body_tx).map(|res| {
|
||||
if let Err(e) = res {
|
||||
debug!("client request body error: {}", e);
|
||||
}
|
||||
@@ -160,38 +156,32 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
let fut = fut
|
||||
.map(move |result| {
|
||||
match result {
|
||||
let fut = fut.map(move |result| match result {
|
||||
Ok(res) => {
|
||||
let content_length = content_length_parse_all(res.headers());
|
||||
let res = res.map(|stream|
|
||||
crate::Body::h2(stream, content_length));
|
||||
let res = res.map(|stream| crate::Body::h2(stream, content_length));
|
||||
Ok(res)
|
||||
},
|
||||
}
|
||||
Err(err) => {
|
||||
debug!("client response error: {}", err);
|
||||
Err((crate::Error::new_h2(err), None))
|
||||
}
|
||||
}
|
||||
});
|
||||
self.executor.execute(cb.send_when(fut));
|
||||
continue;
|
||||
},
|
||||
}
|
||||
|
||||
Poll::Ready(None) => {
|
||||
trace!("client::dispatch::Sender dropped");
|
||||
return Poll::Ready(Ok(Dispatched::Shutdown));
|
||||
}
|
||||
|
||||
Poll::Pending => {
|
||||
match ready!(Pin::new(&mut self.conn_eof).poll(cx)) {
|
||||
Poll::Pending => match ready!(Pin::new(&mut self.conn_eof).poll(cx)) {
|
||||
Ok(never) => match never {},
|
||||
Err(_conn_is_eof) => {
|
||||
trace!("connection task is closed, closing dispatch task");
|
||||
return Poll::Ready(Ok(Dispatched::Shutdown));
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use bytes::Buf;
|
||||
use h2::{SendStream};
|
||||
use h2::SendStream;
|
||||
use http::header::{
|
||||
HeaderName, CONNECTION, PROXY_AUTHENTICATE, PROXY_AUTHORIZATION, TE, TRAILER,
|
||||
TRANSFER_ENCODING, UPGRADE,
|
||||
@@ -7,7 +7,7 @@ use http::header::{
|
||||
use http::HeaderMap;
|
||||
|
||||
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 server;
|
||||
@@ -38,7 +38,11 @@ fn strip_connection_headers(headers: &mut HeaderMap, is_request: bool) {
|
||||
}
|
||||
|
||||
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");
|
||||
headers.remove(TE);
|
||||
}
|
||||
@@ -123,19 +127,24 @@ where
|
||||
if self.body_tx.capacity() == 0 {
|
||||
loop {
|
||||
match ready!(self.body_tx.poll_capacity(cx)) {
|
||||
|
||||
Some(Ok(0)) => {},
|
||||
Some(Ok(0)) => {}
|
||||
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())),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if let Poll::Ready(reason) =
|
||||
self.body_tx.poll_reset(cx).map_err(crate::Error::new_body_write)?
|
||||
if let Poll::Ready(reason) = self
|
||||
.body_tx
|
||||
.poll_reset(cx)
|
||||
.map_err(crate::Error::new_body_write)?
|
||||
{
|
||||
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 {
|
||||
if let Poll::Ready(reason) =
|
||||
self.body_tx.poll_reset(cx).map_err(|e| crate::Error::new_body_write(e))?
|
||||
if let Poll::Ready(reason) = self
|
||||
.body_tx
|
||||
.poll_reset(cx)
|
||||
.map_err(|e| crate::Error::new_body_write(e))?
|
||||
{
|
||||
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)) {
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
use std::error::Error as StdError;
|
||||
use std::marker::Unpin;
|
||||
|
||||
use pin_project::{pin_project, project};
|
||||
use h2::Reason;
|
||||
use h2::server::{Builder, Connection, Handshake, SendResponse};
|
||||
use h2::Reason;
|
||||
use pin_project::{pin_project, project};
|
||||
use tokio::io::{AsyncRead, AsyncWrite};
|
||||
|
||||
use super::{PipeToSendStream, SendBuf};
|
||||
use crate::body::Payload;
|
||||
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::content_length_parse_all;
|
||||
use crate::service::HttpService;
|
||||
use crate::proto::Dispatched;
|
||||
use super::{PipeToSendStream, SendBuf};
|
||||
use crate::service::HttpService;
|
||||
|
||||
use crate::{Body, Response};
|
||||
|
||||
@@ -45,11 +45,10 @@ where
|
||||
closing: Option<crate::Error>,
|
||||
}
|
||||
|
||||
|
||||
impl<T, S, B, E> Server<T, S, B, E>
|
||||
where
|
||||
T: AsyncRead + AsyncWrite + Unpin,
|
||||
S: HttpService<Body, ResBody=B>,
|
||||
S: HttpService<Body, ResBody = B>,
|
||||
S::Error: Into<Box<dyn StdError + Send + Sync>>,
|
||||
B: Payload,
|
||||
B::Data: Unpin,
|
||||
@@ -69,13 +68,13 @@ where
|
||||
match self.state {
|
||||
State::Handshaking(..) => {
|
||||
// fall-through, to replace state with Closed
|
||||
},
|
||||
}
|
||||
State::Serving(ref mut srv) => {
|
||||
if srv.closing.is_none() {
|
||||
srv.conn.graceful_shutdown();
|
||||
}
|
||||
return;
|
||||
},
|
||||
}
|
||||
State::Closed => {
|
||||
return;
|
||||
}
|
||||
@@ -87,7 +86,7 @@ where
|
||||
impl<T, S, B, E> Future for Server<T, S, B, E>
|
||||
where
|
||||
T: AsyncRead + AsyncWrite + Unpin,
|
||||
S: HttpService<Body, ResBody=B>,
|
||||
S: HttpService<Body, ResBody = B>,
|
||||
S::Error: Into<Box<dyn StdError + Send + Sync>>,
|
||||
B: Payload,
|
||||
B::Data: Unpin,
|
||||
@@ -105,7 +104,7 @@ where
|
||||
conn,
|
||||
closing: None,
|
||||
})
|
||||
},
|
||||
}
|
||||
State::Serving(ref mut srv) => {
|
||||
ready!(srv.poll_server(cx, &mut me.service, &mut me.exec))?;
|
||||
return Poll::Ready(Ok(Dispatched::Shutdown));
|
||||
@@ -127,12 +126,14 @@ where
|
||||
B: Payload,
|
||||
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
|
||||
S: HttpService<
|
||||
Body,
|
||||
ResBody=B,
|
||||
>,
|
||||
S: HttpService<Body, ResBody = B>,
|
||||
S::Error: Into<Box<dyn StdError + Send + Sync>>,
|
||||
E: H2Exec<S::Future, B>,
|
||||
{
|
||||
@@ -171,25 +172,26 @@ where
|
||||
Some(Ok((req, respond))) => {
|
||||
trace!("incoming request");
|
||||
let content_length = content_length_parse_all(req.headers());
|
||||
let req = req.map(|stream| {
|
||||
crate::Body::h2(stream, content_length)
|
||||
});
|
||||
let req = req.map(|stream| crate::Body::h2(stream, content_length));
|
||||
let fut = H2Stream::new(service.call(req), respond);
|
||||
exec.execute_h2stream(fut);
|
||||
},
|
||||
}
|
||||
Some(Err(e)) => {
|
||||
return Poll::Ready(Err(crate::Error::new_h2(e)));
|
||||
},
|
||||
}
|
||||
None => {
|
||||
// no more incoming streams...
|
||||
trace!("incoming connection complete");
|
||||
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))?;
|
||||
|
||||
@@ -230,7 +232,7 @@ where
|
||||
}
|
||||
|
||||
macro_rules! reply {
|
||||
($me:expr, $res:expr, $eos:expr) => ({
|
||||
($me:expr, $res:expr, $eos:expr) => {{
|
||||
match $me.reply.send_response($res, $eos) {
|
||||
Ok(tx) => tx,
|
||||
Err(e) => {
|
||||
@@ -239,7 +241,7 @@ macro_rules! reply {
|
||||
return Poll::Ready(Err(crate::Error::new_h2(e)));
|
||||
}
|
||||
}
|
||||
})
|
||||
}};
|
||||
}
|
||||
|
||||
impl<F, B, E> H2Stream<F, B>
|
||||
@@ -261,8 +263,10 @@ where
|
||||
Poll::Pending => {
|
||||
// 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.
|
||||
if let Poll::Ready(reason) =
|
||||
me.reply.poll_reset(cx).map_err(|e| crate::Error::new_h2(e))?
|
||||
if let Poll::Ready(reason) = me
|
||||
.reply
|
||||
.poll_reset(cx)
|
||||
.map_err(|e| crate::Error::new_h2(e))?
|
||||
{
|
||||
debug!("stream received RST_STREAM: {:?}", reason);
|
||||
return Poll::Ready(Err(crate::Error::new_h2(reason.into())));
|
||||
@@ -274,7 +278,7 @@ where
|
||||
warn!("http2 service errored: {}", err);
|
||||
me.reply.send_reset(err.h2_reason());
|
||||
return Poll::Ready(Err(err));
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
let (head, body) = res.into_parts();
|
||||
@@ -282,13 +286,10 @@ where
|
||||
super::strip_connection_headers(res.headers_mut(), false);
|
||||
|
||||
// set Date header if it isn't already set...
|
||||
res
|
||||
.headers_mut()
|
||||
res.headers_mut()
|
||||
.entry(::http::header::DATE)
|
||||
.or_insert_with(crate::proto::h1::date::update_and_header_value);
|
||||
|
||||
|
||||
|
||||
// automatically set Content-Length from body...
|
||||
if let Some(len) = body.size_hint().exact() {
|
||||
headers::set_content_length_if_missing(res.headers_mut(), len);
|
||||
@@ -301,7 +302,7 @@ where
|
||||
reply!(me, res, true);
|
||||
return Poll::Ready(Ok(()));
|
||||
}
|
||||
},
|
||||
}
|
||||
H2StreamState::Body(ref mut pipe) => {
|
||||
return Pin::new(pipe).poll(cx);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
//! Pieces pertaining to the HTTP message protocol.
|
||||
use http::{HeaderMap, Method, StatusCode, Uri, Version};
|
||||
|
||||
pub(crate) use self::h1::{dispatch, Conn, ServerTransaction};
|
||||
use self::body_length::DecodedLength;
|
||||
pub(crate) use self::h1::{dispatch, Conn, ServerTransaction};
|
||||
|
||||
pub(crate) mod h1;
|
||||
pub(crate) mod h2;
|
||||
@@ -76,9 +76,8 @@ mod body_length {
|
||||
/// Converts to an Option<u64> representing a Known or Unknown length.
|
||||
pub(crate) fn into_opt(self) -> Option<u64> {
|
||||
match self {
|
||||
DecodedLength::CHUNKED |
|
||||
DecodedLength::CLOSE_DELIMITED => None,
|
||||
DecodedLength(known) => Some(known)
|
||||
DecodedLength::CHUNKED | DecodedLength::CLOSE_DELIMITED => None,
|
||||
DecodedLength(known) => Some(known),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,10 @@
|
||||
#[cfg(feature = "stream")]
|
||||
use futures_core::Stream;
|
||||
|
||||
use crate::common::{Pin, task::{self, Poll}};
|
||||
use crate::common::{
|
||||
task::{self, Poll},
|
||||
Pin,
|
||||
};
|
||||
|
||||
/// Asynchronously accept incoming connections.
|
||||
pub trait Accept {
|
||||
@@ -19,8 +22,10 @@ pub trait Accept {
|
||||
type Error;
|
||||
|
||||
/// Poll to accept the next connection.
|
||||
fn poll_accept(self: Pin<&mut Self>, cx: &mut task::Context<'_>)
|
||||
-> Poll<Option<Result<Self::Conn, Self::Error>>>;
|
||||
fn poll_accept(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut task::Context<'_>,
|
||||
) -> Poll<Option<Result<Self::Conn, Self::Error>>>;
|
||||
}
|
||||
|
||||
/// Create an `Accept` with a polling function.
|
||||
@@ -54,12 +59,11 @@ where
|
||||
{
|
||||
type Conn = IO;
|
||||
type Error = E;
|
||||
fn poll_accept(self: Pin<&mut Self>, cx: &mut task::Context<'_>)
|
||||
-> Poll<Option<Result<Self::Conn, Self::Error>>>
|
||||
{
|
||||
unsafe {
|
||||
(self.get_unchecked_mut().0)(cx)
|
||||
}
|
||||
fn poll_accept(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut task::Context<'_>,
|
||||
) -> Poll<Option<Result<Self::Conn, Self::Error>>> {
|
||||
unsafe { (self.get_unchecked_mut().0)(cx) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,13 +89,11 @@ where
|
||||
{
|
||||
type Conn = IO;
|
||||
type Error = E;
|
||||
fn poll_accept(self: Pin<&mut Self>, cx: &mut task::Context<'_>)
|
||||
-> Poll<Option<Result<Self::Conn, Self::Error>>>
|
||||
{
|
||||
unsafe {
|
||||
Pin::new_unchecked(&mut self.get_unchecked_mut().0)
|
||||
.poll_next(cx)
|
||||
}
|
||||
fn poll_accept(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut task::Context<'_>,
|
||||
) -> Poll<Option<Result<Self::Conn, Self::Error>>> {
|
||||
unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().0).poll_next(cx) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,29 +11,31 @@
|
||||
use std::error::Error as StdError;
|
||||
use std::fmt;
|
||||
use std::mem;
|
||||
#[cfg(feature = "tcp")] use std::net::SocketAddr;
|
||||
#[cfg(feature = "tcp")]
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use bytes::Bytes;
|
||||
use futures_core::Stream;
|
||||
use tokio::io::{AsyncRead, AsyncWrite};
|
||||
use pin_project::{pin_project, project};
|
||||
use tokio::io::{AsyncRead, AsyncWrite};
|
||||
|
||||
use super::Accept;
|
||||
use crate::body::{Body, Payload};
|
||||
use crate::common::exec::{Exec, H2Exec, NewSvcExec};
|
||||
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::proto;
|
||||
use crate::service::{MakeServiceRef, HttpService};
|
||||
use crate::service::{HttpService, MakeServiceRef};
|
||||
use crate::upgrade::Upgraded;
|
||||
use super::Accept;
|
||||
|
||||
pub(super) use self::spawn_all::NoopWatcher;
|
||||
use self::spawn_all::NewSvcTask;
|
||||
pub(super) use self::spawn_all::NoopWatcher;
|
||||
pub(super) use self::spawn_all::Watcher;
|
||||
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
|
||||
// resource contrained, and so the spec default of 64kb can be too limiting
|
||||
@@ -274,7 +276,10 @@ impl<E> Http<E> {
|
||||
/// Passing `None` will do nothing.
|
||||
///
|
||||
/// 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() {
|
||||
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>
|
||||
where
|
||||
S: HttpService<Body, ResBody=Bd>,
|
||||
S: HttpService<Body, ResBody = Bd>,
|
||||
S::Error: Into<Box<dyn StdError + Send + Sync>>,
|
||||
Bd: Payload,
|
||||
Bd::Data: Unpin,
|
||||
@@ -402,7 +407,8 @@ impl<E> Http<E> {
|
||||
}
|
||||
ConnectionMode::H2Only => {
|
||||
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)
|
||||
}
|
||||
};
|
||||
@@ -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>
|
||||
where
|
||||
I: Accept<Conn=IO, Error=IE>,
|
||||
I: Accept<Conn = IO, Error = IE>,
|
||||
IE: Into<Box<dyn StdError + Send + Sync>>,
|
||||
IO: AsyncRead + AsyncWrite + Unpin,
|
||||
S: MakeServiceRef<
|
||||
IO,
|
||||
Body,
|
||||
ResBody=Bd,
|
||||
>,
|
||||
S: MakeServiceRef<IO, Body, ResBody = Bd>,
|
||||
S::Error: Into<Box<dyn StdError + Send + Sync>>,
|
||||
Bd: Payload,
|
||||
E: H2Exec<<S::Service as HttpService<Body>>::Future, Bd>,
|
||||
@@ -439,12 +441,11 @@ impl<E> Http<E> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ===== impl Connection =====
|
||||
|
||||
impl<I, B, S, E> Connection<I, S, E>
|
||||
where
|
||||
S: HttpService<Body, ResBody=B>,
|
||||
S: HttpService<Body, ResBody = B>,
|
||||
S::Error: Into<Box<dyn StdError + Send + Sync>>,
|
||||
I: AsyncRead + AsyncWrite + Unpin,
|
||||
B: Payload + 'static,
|
||||
@@ -459,7 +460,7 @@ where
|
||||
match self.project().conn.as_mut().unwrap() {
|
||||
ProtoServer::H1(ref mut h1) => {
|
||||
h1.disable_keep_alive();
|
||||
},
|
||||
}
|
||||
ProtoServer::H2(ref mut h2) => {
|
||||
h2.graceful_shutdown();
|
||||
}
|
||||
@@ -476,7 +477,8 @@ where
|
||||
/// # Panics
|
||||
/// This method will panic if this connection is using an h2 protocol.
|
||||
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.
|
||||
@@ -492,7 +494,7 @@ where
|
||||
service: dispatch.into_service(),
|
||||
_inner: (),
|
||||
})
|
||||
},
|
||||
}
|
||||
ProtoServer::H2(_h2) => None,
|
||||
}
|
||||
}
|
||||
@@ -521,22 +523,20 @@ where
|
||||
};
|
||||
match ready!(polled) {
|
||||
Ok(x) => return Poll::Ready(Ok(x)),
|
||||
Err(e) => {
|
||||
match *e.kind() {
|
||||
Err(e) => match *e.kind() {
|
||||
Kind::Parse(Parse::VersionH2) if self.fallback.to_h2() => {
|
||||
self.upgrade_h2();
|
||||
continue;
|
||||
}
|
||||
_ => return Poll::Ready(Err(e)),
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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`.
|
||||
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
|
||||
S: Unpin,
|
||||
S::Future: Unpin,
|
||||
@@ -554,9 +554,7 @@ where
|
||||
let conn = self.conn.take();
|
||||
|
||||
let (io, read_buf, dispatch) = match conn.unwrap() {
|
||||
ProtoServer::H1(h1) => {
|
||||
h1.into_inner()
|
||||
},
|
||||
ProtoServer::H1(h1) => h1.into_inner(),
|
||||
ProtoServer::H2(_h2) => {
|
||||
panic!("h2 cannot into_inner");
|
||||
}
|
||||
@@ -567,12 +565,7 @@ where
|
||||
Fallback::ToHttp2(ref builder, ref exec) => (builder, exec),
|
||||
Fallback::Http1Only => unreachable!("upgrade_h2 with Fallback::Http1Only"),
|
||||
};
|
||||
let h2 = proto::h2::Server::new(
|
||||
rewind_io,
|
||||
dispatch.into_service(),
|
||||
builder,
|
||||
exec.clone(),
|
||||
);
|
||||
let h2 = proto::h2::Server::new(rewind_io, dispatch.into_service(), builder, exec.clone());
|
||||
|
||||
debug_assert!(self.conn.is_none());
|
||||
self.conn = Some(ProtoServer::H2(h2));
|
||||
@@ -585,15 +578,13 @@ where
|
||||
where
|
||||
I: Send,
|
||||
{
|
||||
UpgradeableConnection {
|
||||
inner: self,
|
||||
}
|
||||
UpgradeableConnection { inner: self }
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, B, S, E> Future for Connection<I, S, E>
|
||||
where
|
||||
S: HttpService<Body, ResBody=B>,
|
||||
S: HttpService<Body, ResBody = B>,
|
||||
S::Error: Into<Box<dyn StdError + Send + Sync>>,
|
||||
I: AsyncRead + AsyncWrite + Unpin + 'static,
|
||||
B: Payload + 'static,
|
||||
@@ -614,16 +605,14 @@ where
|
||||
pending.manual();
|
||||
}
|
||||
return Poll::Ready(Ok(()));
|
||||
},
|
||||
Err(e) => {
|
||||
match *e.kind() {
|
||||
}
|
||||
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>,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Connection")
|
||||
.finish()
|
||||
f.debug_struct("Connection").finish()
|
||||
}
|
||||
}
|
||||
// ===== impl Serve =====
|
||||
@@ -655,25 +643,23 @@ impl<I, S, E> Serve<I, S, E> {
|
||||
|
||||
/// Spawn all incoming connections onto the executor in `Http`.
|
||||
pub(super) fn spawn_all(self) -> SpawnAll<I, S, E> {
|
||||
SpawnAll {
|
||||
serve: self,
|
||||
}
|
||||
SpawnAll { serve: self }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
impl<I, IO, IE, S, B, E> Serve<I, S, E>
|
||||
where
|
||||
I: Accept<Conn=IO, Error=IE>,
|
||||
I: Accept<Conn = IO, Error = IE>,
|
||||
IO: AsyncRead + AsyncWrite + Unpin,
|
||||
IE: Into<Box<dyn StdError + Send + Sync>>,
|
||||
S: MakeServiceRef<IO, Body, ResBody=B>,
|
||||
S: MakeServiceRef<IO, Body, ResBody = B>,
|
||||
B: Payload,
|
||||
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();
|
||||
match ready!(me.make_service.poll_ready_ref(cx)) {
|
||||
Ok(()) => (),
|
||||
@@ -700,10 +686,10 @@ where
|
||||
// deprecated
|
||||
impl<I, IO, IE, S, B, E> Stream for Serve<I, S, E>
|
||||
where
|
||||
I: Accept<Conn=IO, Error=IE>,
|
||||
I: Accept<Conn = IO, Error = IE>,
|
||||
IO: AsyncRead + AsyncWrite + Unpin,
|
||||
IE: Into<Box<dyn StdError + Send + Sync>>,
|
||||
S: MakeServiceRef<IO, Body, ResBody=B>,
|
||||
S: MakeServiceRef<IO, Body, ResBody = B>,
|
||||
B: Payload,
|
||||
E: H2Exec<<S::Service as HttpService<Body>>::Future, B>,
|
||||
{
|
||||
@@ -716,12 +702,11 @@ where
|
||||
|
||||
// ===== impl Connecting =====
|
||||
|
||||
|
||||
impl<I, F, S, FE, E, B> Future for Connecting<I, F, E>
|
||||
where
|
||||
I: AsyncRead + AsyncWrite + Unpin,
|
||||
F: Future<Output=Result<S, FE>>,
|
||||
S: HttpService<Body, ResBody=B>,
|
||||
F: Future<Output = Result<S, FE>>,
|
||||
S: HttpService<Body, ResBody = B>,
|
||||
B: Payload,
|
||||
B::Data: Unpin,
|
||||
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>
|
||||
where
|
||||
I: Accept<Conn=IO, Error=IE>,
|
||||
I: Accept<Conn = IO, Error = IE>,
|
||||
IE: Into<Box<dyn StdError + Send + Sync>>,
|
||||
IO: AsyncRead + AsyncWrite + Unpin + Send + 'static,
|
||||
S: MakeServiceRef<
|
||||
IO,
|
||||
Body,
|
||||
ResBody=B,
|
||||
>,
|
||||
S: MakeServiceRef<IO, Body, ResBody = B>,
|
||||
B: Payload,
|
||||
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
|
||||
E: NewSvcExec<IO, S::Future, S::Service, E, W>,
|
||||
W: Watcher<IO, S::Service, E>,
|
||||
@@ -773,7 +758,12 @@ where
|
||||
loop {
|
||||
if let Some(connecting) = ready!(me.serve.as_mut().poll_next_(cx)?) {
|
||||
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 {
|
||||
return Poll::Ready(Ok(()));
|
||||
}
|
||||
@@ -808,11 +798,11 @@ pub(crate) mod spawn_all {
|
||||
use std::error::Error as StdError;
|
||||
use tokio::io::{AsyncRead, AsyncWrite};
|
||||
|
||||
use super::{Connecting, UpgradeableConnection};
|
||||
use crate::body::{Body, Payload};
|
||||
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 super::{Connecting, UpgradeableConnection};
|
||||
use pin_project::{pin_project, project};
|
||||
|
||||
// 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>
|
||||
where
|
||||
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>>,
|
||||
S: HttpService<Body, ResBody=B>,
|
||||
S: HttpService<Body, ResBody = B>,
|
||||
B: Payload,
|
||||
B::Data: Unpin,
|
||||
E: H2Exec<S::Future, B>,
|
||||
@@ -914,11 +904,9 @@ pub(crate) mod spawn_all {
|
||||
};
|
||||
let connected = watcher.watch(conn.with_upgrades());
|
||||
State::Connected(connected)
|
||||
},
|
||||
}
|
||||
State::Connected(future) => {
|
||||
return future
|
||||
.poll(cx)
|
||||
.map(|res| {
|
||||
return future.poll(cx).map(|res| {
|
||||
if let Err(err) = res {
|
||||
debug!("connection error: {}", err);
|
||||
}
|
||||
@@ -951,7 +939,7 @@ mod upgrades {
|
||||
|
||||
impl<I, B, S, E> UpgradeableConnection<I, S, E>
|
||||
where
|
||||
S: HttpService<Body, ResBody=B>,
|
||||
S: HttpService<Body, ResBody = B>,
|
||||
S::Error: Into<Box<dyn StdError + Send + Sync>>,
|
||||
I: AsyncRead + AsyncWrite + Unpin,
|
||||
B: Payload + 'static,
|
||||
@@ -969,7 +957,7 @@ mod upgrades {
|
||||
|
||||
impl<I, B, S, E> Future for UpgradeableConnection<I, S, E>
|
||||
where
|
||||
S: HttpService<Body, ResBody=B>,
|
||||
S: HttpService<Body, ResBody = B>,
|
||||
S::Error: Into<Box<dyn StdError + Send + Sync>>,
|
||||
I: AsyncRead + AsyncWrite + Unpin + Send + 'static,
|
||||
B: Payload + 'static,
|
||||
@@ -991,19 +979,16 @@ mod upgrades {
|
||||
let (io, buf, _) = h1.into_inner();
|
||||
pending.fulfill(Upgraded::new(Box::new(io), buf));
|
||||
return Poll::Ready(Ok(()));
|
||||
},
|
||||
Err(e) => {
|
||||
match *e.kind() {
|
||||
}
|
||||
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 conn;
|
||||
mod shutdown;
|
||||
#[cfg(feature = "tcp")] mod tcp;
|
||||
#[cfg(feature = "tcp")]
|
||||
mod tcp;
|
||||
|
||||
use std::error::Error as StdError;
|
||||
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 tokio::io::{AsyncRead, AsyncWrite};
|
||||
|
||||
use self::accept::Accept;
|
||||
use crate::body::{Body, Payload};
|
||||
use crate::common::exec::{Exec, H2Exec, NewSvcExec};
|
||||
use crate::common::{Future, Pin, Poll, Unpin, task};
|
||||
use crate::service::{MakeServiceRef, HttpService};
|
||||
use self::accept::Accept;
|
||||
use crate::common::{task, Future, Pin, Poll, Unpin};
|
||||
use crate::service::{HttpService, MakeServiceRef};
|
||||
// Renamed `Http` as `Http_` for now so that people upgrading don't see an
|
||||
// error that `hyper::server::Http` is private...
|
||||
use self::conn::{Http as Http_, NoopWatcher, SpawnAll};
|
||||
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.
|
||||
///
|
||||
@@ -113,8 +117,7 @@ impl Server<AddrIncoming, ()> {
|
||||
/// This method will panic if binding to the address fails. For a method
|
||||
/// to bind to an address and return a `Result`, see `Server::try_bind`.
|
||||
pub fn bind(addr: &SocketAddr) -> Builder<AddrIncoming> {
|
||||
let incoming = AddrIncoming::new(addr)
|
||||
.unwrap_or_else(|e| {
|
||||
let incoming = AddrIncoming::new(addr).unwrap_or_else(|e| {
|
||||
panic!("error binding to {}: {}", addr, e);
|
||||
});
|
||||
Server::builder(incoming)
|
||||
@@ -122,14 +125,12 @@ impl Server<AddrIncoming, ()> {
|
||||
|
||||
/// Tries to bind to the provided address, and returns a [`Builder`](Builder).
|
||||
pub fn try_bind(addr: &SocketAddr) -> crate::Result<Builder<AddrIncoming>> {
|
||||
AddrIncoming::new(addr)
|
||||
.map(Server::builder)
|
||||
AddrIncoming::new(addr).map(Server::builder)
|
||||
}
|
||||
|
||||
/// Create a new instance from a `std::net::TcpListener` instance.
|
||||
pub fn from_tcp(listener: StdTcpListener) -> Result<Builder<AddrIncoming>, crate::Error> {
|
||||
AddrIncoming::from_std(listener)
|
||||
.map(Server::builder)
|
||||
AddrIncoming::from_std(listener).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>
|
||||
where
|
||||
I: Accept<Conn=IO, Error=IE>,
|
||||
I: Accept<Conn = IO, Error = IE>,
|
||||
IE: Into<Box<dyn StdError + Send + Sync>>,
|
||||
IO: AsyncRead + AsyncWrite + Unpin + Send + 'static,
|
||||
S: MakeServiceRef<IO, Body, ResBody=B>,
|
||||
S: MakeServiceRef<IO, Body, ResBody = B>,
|
||||
S::Error: Into<Box<dyn StdError + Send + Sync>>,
|
||||
B: Payload,
|
||||
B::Data: Unpin,
|
||||
@@ -191,7 +192,7 @@ where
|
||||
/// ```
|
||||
pub fn with_graceful_shutdown<F>(self, signal: F) -> Graceful<I, S, F, E>
|
||||
where
|
||||
F: Future<Output=()>
|
||||
F: Future<Output = ()>,
|
||||
{
|
||||
Graceful::new(self.spawn_all, signal)
|
||||
}
|
||||
@@ -199,10 +200,10 @@ where
|
||||
|
||||
impl<I, IO, IE, S, B, E> Future for Server<I, S, E>
|
||||
where
|
||||
I: Accept<Conn=IO, Error=IE>,
|
||||
I: Accept<Conn = IO, Error = IE>,
|
||||
IE: Into<Box<dyn StdError + Send + Sync>>,
|
||||
IO: AsyncRead + AsyncWrite + Unpin + Send + 'static,
|
||||
S: MakeServiceRef<IO, Body, ResBody=B>,
|
||||
S: MakeServiceRef<IO, Body, ResBody = B>,
|
||||
S::Error: Into<Box<dyn StdError + Send + Sync>>,
|
||||
B: Payload,
|
||||
B::Data: Unpin,
|
||||
@@ -231,10 +232,7 @@ impl<I, E> Builder<I, E> {
|
||||
///
|
||||
/// For a more convenient constructor, see [`Server::bind`](Server::bind).
|
||||
pub fn new(incoming: I, protocol: Http_<E>) -> Self {
|
||||
Builder {
|
||||
incoming,
|
||||
protocol,
|
||||
}
|
||||
Builder { incoming, protocol }
|
||||
}
|
||||
|
||||
/// Sets whether to use keep-alive for HTTP/1 connections.
|
||||
@@ -245,7 +243,6 @@ impl<I, E> Builder<I, E> {
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
/// Set whether HTTP/1 connections should support half-closures.
|
||||
///
|
||||
/// 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.
|
||||
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
|
||||
}
|
||||
|
||||
@@ -387,7 +385,7 @@ impl<I, E> Builder<I, E> {
|
||||
I: Accept,
|
||||
I::Error: Into<Box<dyn StdError + Send + Sync>>,
|
||||
I::Conn: AsyncRead + AsyncWrite + Unpin + Send + 'static,
|
||||
S: MakeServiceRef<I::Conn, Body, ResBody=B>,
|
||||
S: MakeServiceRef<I::Conn, Body, ResBody = B>,
|
||||
S::Error: Into<Box<dyn StdError + Send + Sync>>,
|
||||
B: Payload,
|
||||
B::Data: Unpin,
|
||||
@@ -396,9 +394,7 @@ impl<I, E> Builder<I, E> {
|
||||
{
|
||||
let serve = self.protocol.serve(self.incoming, new_service);
|
||||
let spawn_all = serve.spawn_all();
|
||||
Server {
|
||||
spawn_all,
|
||||
}
|
||||
Server { spawn_all }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -440,4 +436,3 @@ impl<E> Builder<AddrIncoming, E> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
use std::error::Error as StdError;
|
||||
|
||||
use tokio::io::{AsyncRead, AsyncWrite};
|
||||
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::common::drain::{self, Draining, Signal, Watch, Watching};
|
||||
use crate::common::exec::{H2Exec, NewSvcExec};
|
||||
use crate::common::{Future, Pin, Poll, Unpin, task};
|
||||
use crate::service::{MakeServiceRef, HttpService};
|
||||
use super::Accept;
|
||||
use super::conn::{SpawnAll, UpgradeableConnection, Watcher};
|
||||
use crate::common::{task, Future, Pin, Poll, Unpin};
|
||||
use crate::service::{HttpService, MakeServiceRef};
|
||||
|
||||
#[allow(missing_debug_implementations)]
|
||||
#[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>
|
||||
where
|
||||
I: Accept<Conn=IO, Error=IE>,
|
||||
I: Accept<Conn = IO, Error = IE>,
|
||||
IE: Into<Box<dyn StdError + Send + Sync>>,
|
||||
IO: AsyncRead + AsyncWrite + Unpin + Send + 'static,
|
||||
S: MakeServiceRef<IO, Body, ResBody=B>,
|
||||
S: MakeServiceRef<IO, Body, ResBody = B>,
|
||||
S::Error: Into<Box<dyn StdError + Send + Sync>>,
|
||||
B: Payload,
|
||||
B::Data: Unpin,
|
||||
F: Future<Output=()>,
|
||||
F: Future<Output = ()>,
|
||||
E: H2Exec<<S::Service as HttpService<Body>>::Future, B>,
|
||||
E: NewSvcExec<IO, S::Future, S::Service, E, GracefulWatcher>,
|
||||
{
|
||||
@@ -73,20 +72,13 @@ where
|
||||
} => match signal.poll(cx) {
|
||||
Poll::Ready(()) => {
|
||||
debug!("signal received, starting graceful shutdown");
|
||||
let sig = drain
|
||||
.take()
|
||||
.expect("drain channel")
|
||||
.0;
|
||||
let sig = drain.take().expect("drain channel").0;
|
||||
State::Draining(sig.drain())
|
||||
},
|
||||
}
|
||||
Poll::Pending => {
|
||||
let watch = drain
|
||||
.as_ref()
|
||||
.expect("drain channel")
|
||||
.1
|
||||
.clone();
|
||||
let watch = drain.as_ref().expect("drain channel").1.clone();
|
||||
return spawn_all.poll_watch(cx, &GracefulWatcher(watch));
|
||||
},
|
||||
}
|
||||
},
|
||||
State::Draining(ref mut draining) => {
|
||||
return Pin::new(draining).poll(cx).map(Ok);
|
||||
@@ -109,13 +101,11 @@ where
|
||||
<S::ResBody as Payload>::Data: Unpin,
|
||||
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 {
|
||||
self
|
||||
.0
|
||||
.clone()
|
||||
.watch(conn, on_drain)
|
||||
self.0.clone().watch(conn, on_drain)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,4 +120,3 @@ where
|
||||
{
|
||||
conn.graceful_shutdown()
|
||||
}
|
||||
|
||||
|
||||
@@ -7,10 +7,10 @@ use futures_util::FutureExt as _;
|
||||
use tokio::net::TcpListener;
|
||||
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;
|
||||
use super::Accept;
|
||||
|
||||
/// A stream of connections from binding to an address.
|
||||
#[must_use = "streams do nothing unless polled"]
|
||||
@@ -25,15 +25,13 @@ pub struct AddrIncoming {
|
||||
|
||||
impl AddrIncoming {
|
||||
pub(super) fn new(addr: &SocketAddr) -> crate::Result<Self> {
|
||||
let std_listener = StdTcpListener::bind(addr)
|
||||
.map_err(crate::Error::new_listen)?;
|
||||
let std_listener = StdTcpListener::bind(addr).map_err(crate::Error::new_listen)?;
|
||||
|
||||
AddrIncoming::from_std(std_listener)
|
||||
}
|
||||
|
||||
pub(super) fn from_std(std_listener: StdTcpListener) -> crate::Result<Self> {
|
||||
let listener = TcpListener::from_std(std_listener)
|
||||
.map_err(crate::Error::new_listen)?;
|
||||
let listener = TcpListener::from_std(std_listener).map_err(crate::Error::new_listen)?;
|
||||
let addr = listener.local_addr().map_err(crate::Error::new_listen)?;
|
||||
Ok(AddrIncoming {
|
||||
listener,
|
||||
@@ -115,7 +113,7 @@ impl AddrIncoming {
|
||||
trace!("error trying to set TCP nodelay: {}", e);
|
||||
}
|
||||
return Poll::Ready(Ok(AddrStream::new(socket, addr)));
|
||||
},
|
||||
}
|
||||
Poll::Pending => return Poll::Pending,
|
||||
Poll::Ready(Err(e)) => {
|
||||
// Connection errors can be ignored directly, continue by
|
||||
@@ -134,17 +132,17 @@ impl AddrIncoming {
|
||||
match Pin::new(&mut timeout).poll(cx) {
|
||||
Poll::Ready(()) => {
|
||||
// Wow, it's been a second already? Ok then...
|
||||
continue
|
||||
},
|
||||
continue;
|
||||
}
|
||||
Poll::Pending => {
|
||||
self.timeout = Some(timeout);
|
||||
return Poll::Pending;
|
||||
},
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Poll::Ready(Err(e));
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -154,7 +152,10 @@ impl Accept for AddrIncoming {
|
||||
type Conn = AddrStream;
|
||||
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));
|
||||
Poll::Ready(Some(result))
|
||||
}
|
||||
@@ -169,9 +170,9 @@ impl Accept for AddrIncoming {
|
||||
/// and EMFILE. Otherwise, could enter into tight loop.
|
||||
fn is_connection_error(e: &io::Error) -> bool {
|
||||
match e.kind() {
|
||||
io::ErrorKind::ConnectionRefused |
|
||||
io::ErrorKind::ConnectionAborted |
|
||||
io::ErrorKind::ConnectionReset => true,
|
||||
io::ErrorKind::ConnectionRefused
|
||||
| io::ErrorKind::ConnectionAborted
|
||||
| io::ErrorKind::ConnectionReset => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@@ -188,14 +189,13 @@ impl fmt::Debug for AddrIncoming {
|
||||
}
|
||||
|
||||
mod addr_stream {
|
||||
use bytes::{Buf, BufMut};
|
||||
use std::io;
|
||||
use std::net::SocketAddr;
|
||||
use bytes::{Buf, BufMut};
|
||||
use tokio::net::TcpStream;
|
||||
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`.
|
||||
#[derive(Debug)]
|
||||
@@ -226,29 +226,48 @@ mod addr_stream {
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
#[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)
|
||||
}
|
||||
|
||||
#[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)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncWrite for AddrStream {
|
||||
#[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)
|
||||
}
|
||||
|
||||
#[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)
|
||||
}
|
||||
|
||||
@@ -259,7 +278,10 @@ mod addr_stream {
|
||||
}
|
||||
|
||||
#[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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::error::Error as StdError;
|
||||
|
||||
use crate::body::Payload;
|
||||
use crate::common::{Future, Poll, task};
|
||||
use crate::common::{task, Future, Poll};
|
||||
use crate::{Request, 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>>;
|
||||
|
||||
/// 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)]
|
||||
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
|
||||
T: tower_service::Service<Request<B1>, Response = Response<B2>>,
|
||||
B2: Payload,
|
||||
{}
|
||||
{
|
||||
}
|
||||
|
||||
mod sealed {
|
||||
pub trait Sealed<T> {}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,9 +3,9 @@ use std::fmt;
|
||||
|
||||
use tokio::io::{AsyncRead, AsyncWrite};
|
||||
|
||||
use crate::body::Payload;
|
||||
use crate::common::{Future, Poll, task};
|
||||
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
|
||||
// dependencies.
|
||||
@@ -44,13 +44,9 @@ where
|
||||
pub trait MakeServiceRef<Target, ReqBody>: self::sealed::Sealed<(Target, ReqBody)> {
|
||||
type ResBody: Payload;
|
||||
type Error: Into<Box<dyn StdError + Send + Sync>>;
|
||||
type Service: HttpService<
|
||||
ReqBody,
|
||||
ResBody=Self::ResBody,
|
||||
Error=Self::Error,
|
||||
>;
|
||||
type Service: HttpService<ReqBody, ResBody = Self::ResBody, Error = Self::Error>;
|
||||
type MakeError: Into<Box<dyn StdError + Send + Sync>>;
|
||||
type Future: Future<Output=Result<Self::Service, Self::MakeError>>;
|
||||
type Future: Future<Output = Result<Self::Service, Self::MakeError>>;
|
||||
|
||||
// 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
|
||||
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>>,
|
||||
ME: Into<Box<dyn StdError + Send + Sync>>,
|
||||
S: HttpService<IB, ResBody=OB, Error=E>,
|
||||
F: Future<Output=Result<S, ME>>,
|
||||
S: HttpService<IB, ResBody = OB, Error = E>,
|
||||
F: Future<Output = Result<S, ME>>,
|
||||
IB: Payload,
|
||||
OB: Payload,
|
||||
{
|
||||
@@ -145,9 +141,7 @@ where
|
||||
F: FnMut(&Target) -> Ret,
|
||||
Ret: Future,
|
||||
{
|
||||
MakeServiceFn {
|
||||
f,
|
||||
}
|
||||
MakeServiceFn { f }
|
||||
}
|
||||
|
||||
// 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>
|
||||
where
|
||||
F: FnMut(&Target) -> Ret,
|
||||
Ret: Future<Output=Result<Svc, MkErr>>,
|
||||
Ret: Future<Output = Result<Svc, MkErr>>,
|
||||
MkErr: Into<Box<dyn StdError + Send + Sync>>,
|
||||
{
|
||||
type Error = MkErr;
|
||||
@@ -176,8 +170,7 @@ where
|
||||
|
||||
impl<F> fmt::Debug for MakeServiceFn<F> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("MakeServiceFn")
|
||||
.finish()
|
||||
f.debug_struct("MakeServiceFn").finish()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,10 +41,9 @@ mod make;
|
||||
mod oneshot;
|
||||
mod util;
|
||||
|
||||
pub(crate) use self::make::{MakeConnection, MakeServiceRef};
|
||||
pub(crate) use self::http::HttpService;
|
||||
pub(crate) use self::make::{MakeConnection, MakeServiceRef};
|
||||
pub(crate) use self::oneshot::{oneshot, Oneshot};
|
||||
|
||||
pub use self::make::make_service_fn;
|
||||
pub use self::util::service_fn;
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// TODO: Eventually to be replaced with tower_util::Oneshot.
|
||||
|
||||
use std::mem;
|
||||
use std::marker::Unpin;
|
||||
use std::mem;
|
||||
|
||||
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>
|
||||
where
|
||||
@@ -35,7 +35,8 @@ impl<S, Req> Unpin for Oneshot<S, Req>
|
||||
where
|
||||
S: Service<Req>,
|
||||
S::Future: Unpin,
|
||||
{}
|
||||
{
|
||||
}
|
||||
|
||||
impl<S, Req> Future for Oneshot<S, Req>
|
||||
where
|
||||
@@ -52,17 +53,17 @@ where
|
||||
State::NotReady(ref mut svc, _) => {
|
||||
ready!(svc.poll_ready(cx))?;
|
||||
// fallthrough out of the match's borrow
|
||||
},
|
||||
}
|
||||
State::Called(ref mut fut) => {
|
||||
return unsafe { Pin::new_unchecked(fut) }.poll(cx);
|
||||
},
|
||||
}
|
||||
State::Tmp => unreachable!(),
|
||||
}
|
||||
|
||||
match mem::replace(&mut me.state, State::Tmp) {
|
||||
State::NotReady(mut svc, req) => {
|
||||
me.state = State::Called(svc.call(req));
|
||||
},
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::fmt;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use crate::body::Payload;
|
||||
use crate::common::{Future, Poll, task};
|
||||
use crate::common::{task, Future, Poll};
|
||||
use crate::{Request, Response};
|
||||
|
||||
/// Create a `Service` from a function.
|
||||
@@ -41,11 +41,12 @@ pub struct ServiceFn<F, 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
|
||||
F: FnMut(Request<ReqBody>) -> Ret,
|
||||
ReqBody: Payload,
|
||||
Ret: Future<Output=Result<Response<ResBody>, E>>,
|
||||
Ret: Future<Output = Result<Response<ResBody>, E>>,
|
||||
E: Into<Box<dyn StdError + Send + Sync>>,
|
||||
ResBody: Payload,
|
||||
{
|
||||
@@ -64,7 +65,6 @@ where
|
||||
|
||||
impl<F, R> fmt::Debug for ServiceFn<F, R> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("impl Service")
|
||||
.finish()
|
||||
f.debug_struct("impl Service").finish()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,12 +11,12 @@ use std::fmt;
|
||||
use std::io;
|
||||
use std::marker::Unpin;
|
||||
|
||||
use bytes::{/*Buf, BufMut, */Bytes};
|
||||
use bytes::Bytes;
|
||||
use tokio::io::{AsyncRead, AsyncWrite};
|
||||
use tokio::sync::oneshot;
|
||||
|
||||
use crate::common::io::Rewind;
|
||||
use crate::common::{Future, Pin, Poll, task};
|
||||
use crate::common::{task, Future, Pin, Poll};
|
||||
|
||||
/// An upgraded HTTP connection.
|
||||
///
|
||||
@@ -58,7 +58,7 @@ pub struct Parts<T> {
|
||||
}
|
||||
|
||||
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
|
||||
@@ -70,14 +70,7 @@ struct UpgradeExpected(());
|
||||
|
||||
pub(crate) fn pending() -> (Pending, OnUpgrade) {
|
||||
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 {
|
||||
@@ -130,7 +123,7 @@ impl Upgraded {
|
||||
}),
|
||||
Err(io) => Err(Upgraded {
|
||||
io: Rewind::new_buffered(io, buf),
|
||||
})
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -140,13 +133,21 @@ impl AsyncRead for Upgraded {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -161,8 +162,7 @@ impl AsyncWrite for Upgraded {
|
||||
|
||||
impl fmt::Debug for Upgraded {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Upgraded")
|
||||
.finish()
|
||||
f.debug_struct("Upgraded").finish()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,9 +170,7 @@ impl fmt::Debug for Upgraded {
|
||||
|
||||
impl OnUpgrade {
|
||||
pub(crate) fn none() -> Self {
|
||||
OnUpgrade {
|
||||
rx: None,
|
||||
}
|
||||
OnUpgrade { rx: None }
|
||||
}
|
||||
|
||||
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 {
|
||||
Ok(Ok(upgraded)) => Ok(upgraded),
|
||||
Ok(Err(err)) => Err(err),
|
||||
Err(_oneshot_canceled) => Err(
|
||||
crate::Error::new_canceled().with(UpgradeExpected(()))
|
||||
),
|
||||
Err(_oneshot_canceled) => {
|
||||
Err(crate::Error::new_canceled().with(UpgradeExpected(())))
|
||||
}
|
||||
}),
|
||||
None => Poll::Ready(Err(crate::Error::new_user_no_upgrade())),
|
||||
}
|
||||
@@ -199,8 +197,7 @@ impl Future for OnUpgrade {
|
||||
|
||||
impl fmt::Debug for OnUpgrade {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("OnUpgrade")
|
||||
.finish()
|
||||
f.debug_struct("OnUpgrade").finish()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -233,4 +230,3 @@ impl StdError for UpgradeExpected {
|
||||
"upgrade expected but not completed"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
473
tests/client.rs
473
tests/client.rs
File diff suppressed because it is too large
Load Diff
@@ -337,4 +337,3 @@ t! {
|
||||
http2_parallel_10,
|
||||
parallel: 0..10
|
||||
}
|
||||
|
||||
|
||||
550
tests/server.rs
550
tests/server.rs
File diff suppressed because it is too large
Load Diff
@@ -1,15 +1,20 @@
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::sync::{Arc, Mutex, atomic::{AtomicUsize, Ordering}};
|
||||
use std::time::{Duration/*, Instant*/};
|
||||
use std::sync::{
|
||||
atomic::{AtomicUsize, Ordering},
|
||||
Arc, Mutex,
|
||||
};
|
||||
use std::time::Duration;
|
||||
|
||||
use hyper::{Body, Client, Request, Response, Server, Version};
|
||||
use hyper::client::HttpConnector;
|
||||
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::{future, FutureExt as _, StreamExt as _, TryFutureExt as _, TryStreamExt as _};
|
||||
pub use futures_util::{
|
||||
future, FutureExt as _, StreamExt as _, TryFutureExt as _, TryStreamExt as _,
|
||||
};
|
||||
pub use hyper::{HeaderMap, StatusCode};
|
||||
pub use std::net::SocketAddr;
|
||||
|
||||
macro_rules! t {
|
||||
(
|
||||
@@ -154,43 +159,43 @@ macro_rules! t {
|
||||
}
|
||||
|
||||
macro_rules! __internal_map_prop {
|
||||
(headers: $map:tt) => ({
|
||||
(headers: $map:tt) => {{
|
||||
#[allow(unused_mut)]
|
||||
{
|
||||
let mut headers = HeaderMap::new();
|
||||
__internal_headers_map!(headers, $map);
|
||||
headers
|
||||
}
|
||||
});
|
||||
($name:tt: $val:tt) => ({
|
||||
}};
|
||||
($name:tt: $val:tt) => {{
|
||||
__internal_req_res_prop!($name: $val)
|
||||
});
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! __internal_eq_prop {
|
||||
(headers: $map:tt) => ({
|
||||
(headers: $map:tt) => {{
|
||||
#[allow(unused_mut)]
|
||||
{
|
||||
let mut headers = Vec::new();
|
||||
__internal_headers_eq!(headers, $map);
|
||||
headers
|
||||
}
|
||||
});
|
||||
($name:tt: $val:tt) => ({
|
||||
}};
|
||||
($name:tt: $val:tt) => {{
|
||||
__internal_req_res_prop!($name: $val)
|
||||
});
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! __internal_req_res_prop {
|
||||
(method: $prop_val:expr) => (
|
||||
(method: $prop_val:expr) => {
|
||||
$prop_val
|
||||
);
|
||||
(status: $prop_val:expr) => (
|
||||
};
|
||||
(status: $prop_val:expr) => {
|
||||
StatusCode::from_u16($prop_val).expect("status code")
|
||||
);
|
||||
($prop_name:ident: $prop_val:expr) => (
|
||||
};
|
||||
($prop_name:ident: $prop_val:expr) => {
|
||||
From::from($prop_val)
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! __internal_headers_map {
|
||||
@@ -325,9 +330,7 @@ async fn async_test(cfg: __TestConfig) {
|
||||
.http2_only(cfg.client_version == 2)
|
||||
.build::<_, Body>(connector);
|
||||
|
||||
let serve_handles = Arc::new(Mutex::new(
|
||||
cfg.server_msgs
|
||||
));
|
||||
let serve_handles = Arc::new(Mutex::new(cfg.server_msgs));
|
||||
|
||||
let expected_connections = cfg.connections;
|
||||
let mut cnt = 0;
|
||||
@@ -343,9 +346,7 @@ async fn async_test(cfg: __TestConfig) {
|
||||
// Move a clone into the service_fn
|
||||
let serve_handles = serve_handles.clone();
|
||||
future::ok::<_, hyper::Error>(service_fn(move |req: Request<Body>| {
|
||||
let (sreq, sres) = serve_handles.lock()
|
||||
.unwrap()
|
||||
.remove(0);
|
||||
let (sreq, sres) = serve_handles.lock().unwrap().remove(0);
|
||||
|
||||
assert_eq!(req.uri().path(), sreq.uri, "client path");
|
||||
assert_eq!(req.method(), &sreq.method, "client method");
|
||||
@@ -354,8 +355,7 @@ async fn async_test(cfg: __TestConfig) {
|
||||
func(&req.headers());
|
||||
}
|
||||
let sbody = sreq.body;
|
||||
concat(req.into_body())
|
||||
.map_ok(move |body| {
|
||||
concat(req.into_body()).map_ok(move |body| {
|
||||
assert_eq!(body.as_ref(), sbody.as_slice(), "client body");
|
||||
|
||||
let mut res = Response::builder()
|
||||
@@ -388,7 +388,8 @@ async fn async_test(cfg: __TestConfig) {
|
||||
addr = proxy_addr;
|
||||
}
|
||||
|
||||
let make_request = Arc::new(move |client: &Client<HttpConnector>, creq: __CReq, cres: __CRes| {
|
||||
let make_request = Arc::new(
|
||||
move |client: &Client<HttpConnector>, creq: __CReq, cres: __CRes| {
|
||||
let uri = format!("http://{}{}", addr, creq.uri);
|
||||
let mut req = Request::builder()
|
||||
.method(creq.method)
|
||||
@@ -401,7 +402,8 @@ async fn async_test(cfg: __TestConfig) {
|
||||
let cheaders = cres.headers;
|
||||
let cbody = cres.body;
|
||||
|
||||
client.request(req)
|
||||
client
|
||||
.request(req)
|
||||
.and_then(move |res| {
|
||||
assert_eq!(res.status(), cstatus, "server status");
|
||||
assert_eq!(res.version(), version, "server version");
|
||||
@@ -414,8 +416,8 @@ async fn async_test(cfg: __TestConfig) {
|
||||
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 mut client_futures = vec![];
|
||||
@@ -425,17 +427,14 @@ async fn async_test(cfg: __TestConfig) {
|
||||
drop(client);
|
||||
Box::pin(future::join_all(client_futures).map(|_| ()))
|
||||
} 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));
|
||||
for (creq, cres) in cfg.client_msgs {
|
||||
let mk_request = make_request.clone();
|
||||
client_futures = Box::pin(
|
||||
client_futures
|
||||
.then(move |client| {
|
||||
client_futures = Box::pin(client_futures.then(move |client| {
|
||||
let fut = mk_request(&client, creq, cres);
|
||||
fut.map(move |()| client)
|
||||
})
|
||||
);
|
||||
}));
|
||||
}
|
||||
Box::pin(client_futures.map(|_| ()))
|
||||
};
|
||||
@@ -459,8 +458,7 @@ fn naive_proxy(cfg: ProxyConfig) -> (SocketAddr, impl Future<Output = ()>) {
|
||||
let max_connections = cfg.connections;
|
||||
let counter = AtomicUsize::new(0);
|
||||
|
||||
let srv = Server::bind(&([127, 0, 0, 1], 0).into())
|
||||
.serve(make_service_fn(move |_| {
|
||||
let srv = Server::bind(&([127, 0, 0, 1], 0).into()).serve(make_service_fn(move |_| {
|
||||
let prev = counter.fetch_add(1, Ordering::Relaxed);
|
||||
assert!(max_connections >= prev + 1, "proxy max connections");
|
||||
let client = client.clone();
|
||||
|
||||
Reference in New Issue
Block a user