feat(client,server): remove tcp feature and code (#2929)
This removes the `tcp` feature from hyper's `Cargo.toml`, and the code it enabled: - `HttpConnector` - `GaiResolver` - `AddrStream` And parts of `Client` and `Server` that used those types. Alternatives will be available in the `hyper-util` crate. Closes #2856 Co-authored-by: MrGunflame <mrgunflame@protonmail.com>
This commit is contained in:
323
tests/client.rs
323
tests/client.rs
@@ -5,6 +5,7 @@
|
||||
extern crate matches;
|
||||
|
||||
use std::convert::Infallible;
|
||||
use std::fmt;
|
||||
use std::io::{Read, Write};
|
||||
use std::net::{SocketAddr, TcpListener};
|
||||
use std::pin::Pin;
|
||||
@@ -12,9 +13,11 @@ use std::task::{Context, Poll};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use http::uri::PathAndQuery;
|
||||
use http_body_util::{BodyExt, StreamBody};
|
||||
use hyper::body::to_bytes as concat;
|
||||
use hyper::{Body, Client, Method, Request, StatusCode};
|
||||
use hyper::header::HeaderValue;
|
||||
use hyper::{Body, Method, Request, StatusCode, Uri, Version};
|
||||
|
||||
use bytes::Bytes;
|
||||
use futures_channel::oneshot;
|
||||
@@ -31,6 +34,71 @@ fn tcp_connect(addr: &SocketAddr) -> impl Future<Output = std::io::Result<TcpStr
|
||||
TcpStream::connect(*addr)
|
||||
}
|
||||
|
||||
struct HttpInfo {
|
||||
remote_addr: SocketAddr,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Error {
|
||||
Io(std::io::Error),
|
||||
Hyper(hyper::Error),
|
||||
AbsoluteUriRequired,
|
||||
UnsupportedVersion,
|
||||
}
|
||||
|
||||
impl Error {
|
||||
fn is_incomplete_message(&self) -> bool {
|
||||
match self {
|
||||
Self::Hyper(err) => err.is_incomplete_message(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_parse(&self) -> bool {
|
||||
match self {
|
||||
Self::Hyper(err) => err.is_parse(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_parse_too_large(&self) -> bool {
|
||||
match self {
|
||||
Self::Hyper(err) => err.is_parse_too_large(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_parse_status(&self) -> bool {
|
||||
match self {
|
||||
Self::Hyper(err) => err.is_parse_status(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Io(err) => err.fmt(fmt),
|
||||
Self::Hyper(err) => err.fmt(fmt),
|
||||
Self::AbsoluteUriRequired => write!(fmt, "client requires absolute-form URIs"),
|
||||
Self::UnsupportedVersion => write!(fmt, "request has unsupported HTTP version"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for Error {
|
||||
fn from(err: std::io::Error) -> Self {
|
||||
Self::Io(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<hyper::Error> for Error {
|
||||
fn from(err: hyper::Error) -> Self {
|
||||
Self::Hyper(err)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! test {
|
||||
(
|
||||
name: $name:ident,
|
||||
@@ -110,7 +178,7 @@ macro_rules! test {
|
||||
let _ = pretty_env_logger::try_init();
|
||||
let rt = support::runtime();
|
||||
|
||||
let err: ::hyper::Error = test! {
|
||||
let err: Error = test! {
|
||||
INNER;
|
||||
name: $name,
|
||||
runtime: &rt,
|
||||
@@ -123,7 +191,7 @@ macro_rules! test {
|
||||
)*},
|
||||
}.unwrap_err();
|
||||
|
||||
fn infer_closure<F: FnOnce(&::hyper::Error) -> bool>(f: F) -> F { f }
|
||||
fn infer_closure<F: FnOnce(&Error) -> bool>(f: F) -> F { f }
|
||||
|
||||
let closure = infer_closure($err);
|
||||
if !closure(&err) {
|
||||
@@ -151,22 +219,123 @@ macro_rules! test {
|
||||
let addr = server.local_addr().expect("local_addr");
|
||||
let rt = $runtime;
|
||||
|
||||
let connector = ::hyper::client::HttpConnector::new();
|
||||
let client = Client::builder()
|
||||
$($(.$c_opt_prop($c_opt_val))*)?
|
||||
.build(connector);
|
||||
|
||||
#[allow(unused_assignments, unused_mut)]
|
||||
let mut body = BodyExt::boxed(http_body_util::Empty::<bytes::Bytes>::new());
|
||||
let mut req_builder = Request::builder();
|
||||
$(
|
||||
test!(@client_request; req_builder, body, addr, $c_req_prop: $c_req_val);
|
||||
)*
|
||||
let req = req_builder
|
||||
let mut req = req_builder
|
||||
.body(body)
|
||||
.expect("request builder");
|
||||
|
||||
let res = client.request(req);
|
||||
let res = async move {
|
||||
// Wrapper around hyper::client::conn::Builder with set_host field to mimic
|
||||
// hyper::client::Builder.
|
||||
struct Builder {
|
||||
inner: hyper::client::conn::Builder,
|
||||
set_host: bool,
|
||||
http09_responses: bool,
|
||||
http2_only: bool,
|
||||
}
|
||||
|
||||
impl Builder {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
inner: hyper::client::conn::Builder::new(),
|
||||
set_host: true,
|
||||
http09_responses: false,
|
||||
http2_only: false,
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
fn set_host(&mut self, val: bool) -> &mut Self {
|
||||
self.set_host = val;
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
fn http09_responses(&mut self, val: bool) -> &mut Self {
|
||||
self.http09_responses = val;
|
||||
self.inner.http09_responses(val);
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
fn http2_only(&mut self, val: bool) -> &mut Self {
|
||||
self.http2_only = val;
|
||||
self.inner.http2_only(val);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for Builder {
|
||||
type Target = hyper::client::conn::Builder;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::DerefMut for Builder {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused_mut)]
|
||||
let mut builder = Builder::new();
|
||||
$(builder$(.$c_opt_prop($c_opt_val))*;)?
|
||||
|
||||
|
||||
if req.version() == Version::HTTP_09 && !builder.http09_responses {
|
||||
return Err(Error::UnsupportedVersion);
|
||||
}
|
||||
|
||||
if req.version() == Version::HTTP_2 && !builder.http2_only {
|
||||
return Err(Error::UnsupportedVersion);
|
||||
}
|
||||
|
||||
let host = req.uri().host().ok_or(Error::AbsoluteUriRequired)?;
|
||||
let port = req.uri().port_u16().unwrap_or(80);
|
||||
|
||||
let stream = TcpStream::connect(format!("{}:{}", host, port)).await?;
|
||||
|
||||
let extra = HttpInfo {
|
||||
remote_addr: stream.peer_addr().unwrap(),
|
||||
};
|
||||
|
||||
if builder.set_host {
|
||||
let host = req.uri().host().expect("no host in uri");
|
||||
let port = req.uri().port_u16().expect("no port in uri");
|
||||
|
||||
let host = format!("{}:{}", host, port);
|
||||
|
||||
req.headers_mut().append("Host", HeaderValue::from_str(&host).unwrap());
|
||||
}
|
||||
|
||||
let (mut sender, conn) = builder.handshake(stream).await?;
|
||||
|
||||
tokio::task::spawn(async move {
|
||||
if let Err(err) = conn.await {
|
||||
panic!("{}", err);
|
||||
}
|
||||
});
|
||||
|
||||
let mut builder = Uri::builder();
|
||||
if req.method() == Method::CONNECT {
|
||||
builder = builder.path_and_query(format!("{}:{}", req.uri().host().unwrap(), req.uri().port_u16().unwrap()));
|
||||
} else {
|
||||
builder = builder.path_and_query(req.uri().path_and_query().cloned().unwrap_or(PathAndQuery::from_static("/")));
|
||||
}
|
||||
*req.uri_mut() = builder.build().unwrap();
|
||||
|
||||
let mut resp = sender.send_request(req).await?;
|
||||
|
||||
resp.extensions_mut().insert(extra);
|
||||
Ok(resp)
|
||||
};
|
||||
|
||||
let (tx, rx) = oneshot::channel();
|
||||
|
||||
@@ -188,7 +357,7 @@ macro_rules! test {
|
||||
assert_eq!(s(&buf[..n]), expected);
|
||||
|
||||
inc.write_all($server_reply.as_ref()).expect("write_all");
|
||||
let _ = tx.send(Ok::<_, hyper::Error>(()));
|
||||
let _ = tx.send(Ok::<_, Error>(()));
|
||||
}).expect("thread spawn");
|
||||
|
||||
let rx = rx.expect("thread panicked");
|
||||
@@ -197,10 +366,10 @@ macro_rules! test {
|
||||
// Always check that HttpConnector has set the "extra" info...
|
||||
let extra = resp
|
||||
.extensions_mut()
|
||||
.remove::<::hyper::client::connect::HttpInfo>()
|
||||
.remove::<HttpInfo>()
|
||||
.expect("HttpConnector should set HttpInfo");
|
||||
|
||||
assert_eq!(extra.remote_addr(), addr, "HttpInfo should have server addr");
|
||||
assert_eq!(extra.remote_addr, addr, "HttpInfo should have server addr");
|
||||
|
||||
resp
|
||||
})
|
||||
@@ -1174,7 +1343,7 @@ mod dispatch_impl {
|
||||
|
||||
use super::support;
|
||||
use hyper::body::HttpBody;
|
||||
use hyper::client::connect::{Connected, Connection, HttpConnector};
|
||||
use hyper::client::connect::{Connected, Connection};
|
||||
use hyper::Client;
|
||||
|
||||
#[test]
|
||||
@@ -1186,10 +1355,7 @@ mod dispatch_impl {
|
||||
let addr = server.local_addr().unwrap();
|
||||
let rt = support::runtime();
|
||||
let (closes_tx, closes) = mpsc::channel(10);
|
||||
let client = Client::builder().build(DebugConnector::with_http_and_closes(
|
||||
HttpConnector::new(),
|
||||
closes_tx,
|
||||
));
|
||||
let client = Client::builder().build(DebugConnector::with_closes(closes_tx));
|
||||
|
||||
let (tx1, rx1) = oneshot::channel();
|
||||
|
||||
@@ -1259,10 +1425,7 @@ mod dispatch_impl {
|
||||
});
|
||||
|
||||
let res = {
|
||||
let client = Client::builder().build(DebugConnector::with_http_and_closes(
|
||||
HttpConnector::new(),
|
||||
closes_tx,
|
||||
));
|
||||
let client = Client::builder().build(DebugConnector::with_closes(closes_tx));
|
||||
|
||||
let req = Request::builder()
|
||||
.uri(&*format!("http://{}/a", addr))
|
||||
@@ -1322,10 +1485,7 @@ mod dispatch_impl {
|
||||
support::runtime().block_on(client_drop_rx.into_future())
|
||||
});
|
||||
|
||||
let client = Client::builder().build(DebugConnector::with_http_and_closes(
|
||||
HttpConnector::new(),
|
||||
closes_tx,
|
||||
));
|
||||
let client = Client::builder().build(DebugConnector::with_closes(closes_tx));
|
||||
|
||||
let req = Request::builder()
|
||||
.uri(&*format!("http://{}/a", addr))
|
||||
@@ -1385,10 +1545,7 @@ mod dispatch_impl {
|
||||
});
|
||||
|
||||
let res = {
|
||||
let client = Client::builder().build(DebugConnector::with_http_and_closes(
|
||||
HttpConnector::new(),
|
||||
closes_tx,
|
||||
));
|
||||
let client = Client::builder().build(DebugConnector::with_closes(closes_tx));
|
||||
|
||||
let req = Request::builder()
|
||||
.uri(&*format!("http://{}/a", addr))
|
||||
@@ -1438,10 +1595,7 @@ mod dispatch_impl {
|
||||
|
||||
let rx = rx1.expect("thread panicked");
|
||||
let res = {
|
||||
let client = Client::builder().build(DebugConnector::with_http_and_closes(
|
||||
HttpConnector::new(),
|
||||
closes_tx,
|
||||
));
|
||||
let client = Client::builder().build(DebugConnector::with_closes(closes_tx));
|
||||
|
||||
let req = Request::builder()
|
||||
.uri(&*format!("http://{}/a", addr))
|
||||
@@ -1490,9 +1644,9 @@ mod dispatch_impl {
|
||||
let _ = rx2.recv();
|
||||
});
|
||||
|
||||
let client = Client::builder().pool_max_idle_per_host(0).build(
|
||||
DebugConnector::with_http_and_closes(HttpConnector::new(), closes_tx),
|
||||
);
|
||||
let client = Client::builder()
|
||||
.pool_max_idle_per_host(0)
|
||||
.build(DebugConnector::with_closes(closes_tx));
|
||||
|
||||
let req = Request::builder()
|
||||
.uri(&*format!("http://{}/a", addr))
|
||||
@@ -1536,10 +1690,7 @@ mod dispatch_impl {
|
||||
let _ = tx1.send(());
|
||||
});
|
||||
|
||||
let client = Client::builder().build(DebugConnector::with_http_and_closes(
|
||||
HttpConnector::new(),
|
||||
closes_tx,
|
||||
));
|
||||
let client = Client::builder().build(DebugConnector::with_closes(closes_tx));
|
||||
|
||||
let req = Request::builder()
|
||||
.uri(&*format!("http://{}/a", addr))
|
||||
@@ -2085,7 +2236,6 @@ mod dispatch_impl {
|
||||
|
||||
#[derive(Clone)]
|
||||
struct DebugConnector {
|
||||
http: HttpConnector,
|
||||
closes: mpsc::Sender<()>,
|
||||
connects: Arc<AtomicUsize>,
|
||||
is_proxy: bool,
|
||||
@@ -2094,14 +2244,12 @@ mod dispatch_impl {
|
||||
|
||||
impl DebugConnector {
|
||||
fn new() -> DebugConnector {
|
||||
let http = HttpConnector::new();
|
||||
let (tx, _) = mpsc::channel(10);
|
||||
DebugConnector::with_http_and_closes(http, tx)
|
||||
DebugConnector::with_closes(tx)
|
||||
}
|
||||
|
||||
fn with_http_and_closes(http: HttpConnector, closes: mpsc::Sender<()>) -> DebugConnector {
|
||||
fn with_closes(closes: mpsc::Sender<()>) -> DebugConnector {
|
||||
DebugConnector {
|
||||
http,
|
||||
closes,
|
||||
connects: Arc::new(AtomicUsize::new(0)),
|
||||
is_proxy: false,
|
||||
@@ -2117,12 +2265,11 @@ mod dispatch_impl {
|
||||
|
||||
impl hyper::service::Service<Uri> for DebugConnector {
|
||||
type Response = DebugStream;
|
||||
type Error = <HttpConnector as hyper::service::Service<Uri>>::Error;
|
||||
type Error = std::io::Error;
|
||||
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
|
||||
|
||||
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
// don't forget to check inner service is ready :)
|
||||
hyper::service::Service::<Uri>::poll_ready(&mut self.http, cx)
|
||||
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn call(&mut self, dst: Uri) -> Self::Future {
|
||||
@@ -2130,12 +2277,20 @@ mod dispatch_impl {
|
||||
let closes = self.closes.clone();
|
||||
let is_proxy = self.is_proxy;
|
||||
let is_alpn_h2 = self.alpn_h2;
|
||||
Box::pin(self.http.call(dst).map_ok(move |tcp| DebugStream {
|
||||
tcp,
|
||||
on_drop: closes,
|
||||
is_alpn_h2,
|
||||
is_proxy,
|
||||
}))
|
||||
|
||||
Box::pin(async move {
|
||||
let host = dst.host().expect("no host in uri");
|
||||
let port = dst.port_u16().expect("no port in uri");
|
||||
|
||||
let stream = TcpStream::connect(format!("{}:{}", host, port)).await?;
|
||||
|
||||
Ok(DebugStream {
|
||||
tcp: stream,
|
||||
on_drop: closes,
|
||||
is_alpn_h2,
|
||||
is_proxy,
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2188,7 +2343,7 @@ mod dispatch_impl {
|
||||
|
||||
impl Connection for DebugStream {
|
||||
fn connected(&self) -> Connected {
|
||||
let connected = self.tcp.connected().proxy(self.is_proxy);
|
||||
let connected = Connected::new().proxy(self.is_proxy);
|
||||
|
||||
if self.is_alpn_h2 {
|
||||
connected.negotiated_h2()
|
||||
@@ -2744,27 +2899,45 @@ mod conn {
|
||||
#[tokio::test]
|
||||
async fn http2_detect_conn_eof() {
|
||||
use futures_util::future;
|
||||
use hyper::service::{make_service_fn, service_fn};
|
||||
use hyper::{Response, Server};
|
||||
|
||||
let _ = pretty_env_logger::try_init();
|
||||
|
||||
let server = Server::bind(&([127, 0, 0, 1], 0).into())
|
||||
.http2_only(true)
|
||||
.serve(make_service_fn(|_| async move {
|
||||
Ok::<_, hyper::Error>(service_fn(|_req| {
|
||||
future::ok::<_, hyper::Error>(Response::new(Body::empty()))
|
||||
}))
|
||||
}));
|
||||
let addr = server.local_addr();
|
||||
let (shdn_tx, shdn_rx) = oneshot::channel();
|
||||
let addr = SocketAddr::from(([127, 0, 0, 1], 0));
|
||||
let listener = TkTcpListener::bind(addr).await.unwrap();
|
||||
|
||||
let addr = listener.local_addr().unwrap();
|
||||
let (shdn_tx, mut shdn_rx) = tokio::sync::watch::channel(false);
|
||||
tokio::task::spawn(async move {
|
||||
server
|
||||
.with_graceful_shutdown(async move {
|
||||
let _ = shdn_rx.await;
|
||||
})
|
||||
.await
|
||||
.expect("server")
|
||||
use hyper::server::conn::Http;
|
||||
use hyper::service::service_fn;
|
||||
|
||||
loop {
|
||||
tokio::select! {
|
||||
res = listener.accept() => {
|
||||
let (stream, _) = res.unwrap();
|
||||
|
||||
let service = service_fn(|_:Request<Body>| future::ok::<Response<Body>, hyper::Error>(Response::new(Body::empty())));
|
||||
|
||||
let mut shdn_rx = shdn_rx.clone();
|
||||
tokio::task::spawn(async move {
|
||||
let mut conn = Http::new().http2_only(true).serve_connection(stream, service);
|
||||
|
||||
tokio::select! {
|
||||
res = &mut conn => {
|
||||
res.unwrap();
|
||||
}
|
||||
_ = shdn_rx.changed() => {
|
||||
Pin::new(&mut conn).graceful_shutdown();
|
||||
conn.await.unwrap();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
_ = shdn_rx.changed() => {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let io = tcp_connect(&addr).await.expect("tcp connect");
|
||||
@@ -2796,7 +2969,7 @@ mod conn {
|
||||
.expect("client poll ready after");
|
||||
|
||||
// Trigger the server shutdown...
|
||||
let _ = shdn_tx.send(());
|
||||
let _ = shdn_tx.send(true);
|
||||
|
||||
// Allow time for graceful shutdown roundtrips...
|
||||
tokio::time::sleep(Duration::from_millis(100)).await;
|
||||
|
||||
Reference in New Issue
Block a user