chore(proxy/http): upgrade to hyper 1.x
Signed-off-by: katelyn martin <kate@buoyant.io>
This commit is contained in:
parent
afb351fd14
commit
d12de6d964
|
|
@ -2328,9 +2328,11 @@ dependencies = [
|
|||
"h2",
|
||||
"http 1.2.0",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"httparse",
|
||||
"hyper",
|
||||
"hyper-balance",
|
||||
"hyper-util",
|
||||
"linkerd-detect",
|
||||
"linkerd-duplex",
|
||||
"linkerd-error",
|
||||
|
|
|
|||
|
|
@ -27,6 +27,12 @@ hyper = { workspace = true, features = [
|
|||
"server",
|
||||
] }
|
||||
hyper-balance = { path = "../../../hyper-balance" }
|
||||
hyper-util = { workspace = true, default-features = false, features = [
|
||||
"client",
|
||||
"client-legacy",
|
||||
"http1",
|
||||
"service",
|
||||
] }
|
||||
parking_lot = "0.12"
|
||||
pin-project = "1"
|
||||
rand = "0.9"
|
||||
|
|
@ -54,6 +60,7 @@ linkerd-proxy-balance = { path = "../balance" }
|
|||
linkerd-stack = { path = "../../stack" }
|
||||
|
||||
[dev-dependencies]
|
||||
http-body-util = { workspace = true, features = ["channel"] }
|
||||
tokio-test = "0.4"
|
||||
tower-test = "0.4"
|
||||
linkerd-tracing = { path = "../../tracing", features = ["ansi"] }
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ where
|
|||
C::Connection: Unpin + Send,
|
||||
C::Metadata: Send,
|
||||
C::Future: Unpin + Send + 'static,
|
||||
B: crate::Body + Send + 'static,
|
||||
B: crate::Body + Send + Unpin + 'static,
|
||||
B::Data: Send,
|
||||
B::Error: Into<Error> + Send + Sync,
|
||||
{
|
||||
|
|
@ -123,7 +123,7 @@ where
|
|||
C::Connection: Unpin + Send,
|
||||
C::Future: Unpin + Send + 'static,
|
||||
C::Error: Into<Error>,
|
||||
B: crate::Body + Send + 'static,
|
||||
B: crate::Body + Send + Unpin + 'static,
|
||||
B::Data: Send,
|
||||
B::Error: Into<Error> + Send + Sync,
|
||||
{
|
||||
|
|
|
|||
|
|
@ -33,8 +33,8 @@ pub struct PoolSettings {
|
|||
pub struct Client<C, T, B> {
|
||||
connect: C,
|
||||
target: T,
|
||||
absolute_form: Option<hyper::Client<HyperConnect<C, T>, B>>,
|
||||
origin_form: Option<hyper::Client<HyperConnect<C, T>, B>>,
|
||||
absolute_form: Option<hyper_util::client::legacy::Client<HyperConnect<C, T>, B>>,
|
||||
origin_form: Option<hyper_util::client::legacy::Client<HyperConnect<C, T>, B>>,
|
||||
pool: PoolSettings,
|
||||
}
|
||||
|
||||
|
|
@ -68,9 +68,9 @@ impl<C, T, B> Client<C, T, B>
|
|||
where
|
||||
T: Clone + Send + Sync + 'static,
|
||||
C: MakeConnection<(crate::Variant, T)> + Clone + Send + Sync + 'static,
|
||||
C::Connection: Unpin + Send,
|
||||
C::Connection: Unpin + Send + hyper::rt::Read + hyper::rt::Write,
|
||||
C::Future: Unpin + Send + 'static,
|
||||
B: crate::Body + Send + 'static,
|
||||
B: crate::Body + Send + Unpin + 'static,
|
||||
B::Data: Send,
|
||||
B::Error: Into<Error> + Send + Sync,
|
||||
{
|
||||
|
|
@ -94,10 +94,9 @@ where
|
|||
// ish, so we just build a one-off client for the connection.
|
||||
// There's no real reason to hold the client for re-use.
|
||||
debug!(use_absolute_form, is_missing_host, "Using one-off client");
|
||||
hyper::Client::builder()
|
||||
hyper_util::client::legacy::Client::builder(TracingExecutor)
|
||||
.pool_max_idle_per_host(0)
|
||||
.set_host(use_absolute_form)
|
||||
.executor(TracingExecutor)
|
||||
.build(HyperConnect::new(
|
||||
self.connect.clone(),
|
||||
self.target.clone(),
|
||||
|
|
@ -120,11 +119,10 @@ where
|
|||
if client.is_none() {
|
||||
debug!(use_absolute_form, "Caching new client");
|
||||
*client = Some(
|
||||
hyper::Client::builder()
|
||||
hyper_util::client::legacy::Client::builder(TracingExecutor)
|
||||
.pool_max_idle_per_host(self.pool.max_idle)
|
||||
.pool_idle_timeout(self.pool.idle_timeout)
|
||||
.set_host(use_absolute_form)
|
||||
.executor(TracingExecutor)
|
||||
.build(HyperConnect::new(
|
||||
self.connect.clone(),
|
||||
self.target.clone(),
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ where
|
|||
C::Connection: Send + Unpin + 'static,
|
||||
C::Metadata: Send,
|
||||
C::Future: Send + 'static,
|
||||
B: Body + Send + 'static,
|
||||
B: Body + Send + Unpin + 'static,
|
||||
B::Data: Send,
|
||||
B::Error: Into<Error> + Send + Sync,
|
||||
{
|
||||
|
|
@ -147,7 +147,7 @@ where
|
|||
B::Data: Send,
|
||||
B::Error: Into<Error> + Send + Sync,
|
||||
{
|
||||
type Response = http::Response<hyper::Body>;
|
||||
type Response = http::Response<hyper::body::Incoming>;
|
||||
type Error = hyper::Error;
|
||||
type Future = Pin<Box<dyn Send + Future<Output = Result<Self::Response, Self::Error>>>>;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use super::{h1, h2, Body};
|
||||
use futures::prelude::*;
|
||||
use http::header::{HeaderValue, TRANSFER_ENCODING};
|
||||
use http_body::Frame;
|
||||
use linkerd_error::{Error, Result};
|
||||
use linkerd_http_box::BoxBody;
|
||||
use linkerd_stack::{layer, MakeConnection, Service};
|
||||
|
|
@ -56,7 +57,7 @@ where
|
|||
C: MakeConnection<(crate::Variant, T)> + Clone + Send + Sync + 'static,
|
||||
C::Connection: Unpin + Send,
|
||||
C::Future: Unpin + Send + 'static,
|
||||
B: crate::Body + Send + 'static,
|
||||
B: crate::Body + Send + Unpin + 'static,
|
||||
B::Data: Send,
|
||||
B::Error: Into<Error> + Send + Sync,
|
||||
{
|
||||
|
|
@ -211,23 +212,13 @@ where
|
|||
self.inner.is_end_stream()
|
||||
}
|
||||
|
||||
fn poll_data(
|
||||
fn poll_frame(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
) -> Poll<Option<Result<Self::Data, Self::Error>>> {
|
||||
) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
|
||||
self.project()
|
||||
.inner
|
||||
.poll_data(cx)
|
||||
.map_err(downgrade_h2_error)
|
||||
}
|
||||
|
||||
fn poll_trailers(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
) -> Poll<Result<Option<http::HeaderMap>, Self::Error>> {
|
||||
self.project()
|
||||
.inner
|
||||
.poll_trailers(cx)
|
||||
.poll_frame(cx)
|
||||
.map_err(downgrade_h2_error)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
use crate::{
|
||||
client_handle::SetClientHandle, h2, BoxBody, BoxRequest, ClientHandle, TracingExecutor, Variant,
|
||||
};
|
||||
use crate::{client_handle::SetClientHandle, h2, BoxBody, ClientHandle, TracingExecutor, Variant};
|
||||
use linkerd_error::Error;
|
||||
use linkerd_io::{self as io, PeerAddr};
|
||||
use linkerd_http_upgrade::glue::UpgradeBody;
|
||||
use linkerd_io::PeerAddr;
|
||||
use linkerd_stack::{layer, ExtractParam, NewService};
|
||||
use std::{
|
||||
future::Future,
|
||||
|
|
@ -126,13 +125,22 @@ where
|
|||
|
||||
impl<I, N, S> Service<I> for ServeHttp<N>
|
||||
where
|
||||
I: io::AsyncRead + io::AsyncWrite + PeerAddr + Send + Unpin + 'static,
|
||||
I: hyper::rt::Read + hyper::rt::Write + PeerAddr + Send + Unpin + 'static,
|
||||
N: NewService<ClientHandle, Service = S> + Send + 'static,
|
||||
S: Service<http::Request<BoxBody>, Response = http::Response<BoxBody>, Error = Error>
|
||||
+ Unpin
|
||||
S: Service<
|
||||
http::Request<hyper::body::Incoming>,
|
||||
Response = http::Response<BoxBody>,
|
||||
Error = Error,
|
||||
> + Service<
|
||||
http::Request<UpgradeBody<hyper::body::Incoming>>,
|
||||
Response = http::Response<BoxBody>,
|
||||
Error = Error,
|
||||
> + Clone
|
||||
+ Send
|
||||
+ Unpin
|
||||
+ 'static,
|
||||
S::Future: Send + 'static,
|
||||
<S as Service<http::Request<hyper::body::Incoming>>>::Future: Send + 'static,
|
||||
<S as Service<http::Request<UpgradeBody<hyper::body::Incoming>>>>::Future: Send + 'static,
|
||||
{
|
||||
type Response = ();
|
||||
type Error = Error;
|
||||
|
|
@ -162,10 +170,8 @@ where
|
|||
match version {
|
||||
Variant::Http1 => {
|
||||
// Enable support for HTTP upgrades (CONNECT and websockets).
|
||||
let svc = linkerd_http_upgrade::upgrade::Service::new(
|
||||
BoxRequest::new(svc),
|
||||
drain.clone(),
|
||||
);
|
||||
let svc = linkerd_http_upgrade::upgrade::Service::new(svc, drain.clone());
|
||||
let svc = hyper_util::service::TowerToHyperService::new(svc);
|
||||
let mut conn = http1.serve_connection(io, svc).with_upgrades();
|
||||
|
||||
tokio::select! {
|
||||
|
|
@ -187,7 +193,8 @@ where
|
|||
}
|
||||
|
||||
Variant::H2 => {
|
||||
let mut conn = http2.serve_connection(io, BoxRequest::new(svc));
|
||||
let svc = hyper_util::service::TowerToHyperService::new(svc);
|
||||
let mut conn = http2.serve_connection(io, svc);
|
||||
|
||||
tokio::select! {
|
||||
res = &mut conn => {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ use super::*;
|
|||
use crate::Body;
|
||||
use bytes::Bytes;
|
||||
use futures::FutureExt;
|
||||
use http_body_util::BodyExt;
|
||||
use linkerd_io as io;
|
||||
use linkerd_stack::CloneParam;
|
||||
use tokio::time;
|
||||
use tower::ServiceExt;
|
||||
|
|
@ -75,7 +77,7 @@ async fn h2_connection_window_exhaustion() {
|
|||
.expect("timed out");
|
||||
tokio::select! {
|
||||
_ = time::sleep(time::Duration::from_secs(2)) => {}
|
||||
_ = rx.data() => panic!("unexpected data"),
|
||||
_ = rx.frame() => panic!("unexpected data"),
|
||||
}
|
||||
|
||||
tracing::info!("Dropping one of the retained response bodies frees capacity so that the data can be received");
|
||||
|
|
@ -109,34 +111,52 @@ async fn h2_stream_window_exhaustion() {
|
|||
|
||||
let chunk = (0..CLIENT_STREAM_WINDOW).map(|_| b'a').collect::<Bytes>();
|
||||
tracing::info!(sz = chunk.len(), "Sending chunk");
|
||||
tx.try_send_data(chunk.clone()).expect("send data");
|
||||
tx.send_data(chunk.clone()).await.expect("can send data");
|
||||
tokio::task::yield_now().await;
|
||||
|
||||
tracing::info!(sz = chunk.len(), "Buffering chunk in channel");
|
||||
tx.try_send_data(chunk.clone()).expect("send data");
|
||||
tx.send_data(chunk.clone()).await.expect("can send data");
|
||||
tokio::task::yield_now().await;
|
||||
|
||||
tracing::info!(sz = chunk.len(), "Confirming stream window exhaustion");
|
||||
/*
|
||||
* XXX(kate): this can be reinstate when we have a `poll_ready(cx)` method on the new sender.
|
||||
assert!(
|
||||
timeout(futures::future::poll_fn(|cx| tx.poll_ready(cx)))
|
||||
.await
|
||||
.is_err(),
|
||||
"stream window should be exhausted"
|
||||
);
|
||||
*/
|
||||
|
||||
tracing::info!("Once the pending data is read, the stream window should be replenished");
|
||||
let data = body.data().await.expect("data").expect("data");
|
||||
let data = body
|
||||
.frame()
|
||||
.await
|
||||
.expect("yields a result")
|
||||
.expect("yields a frame")
|
||||
.into_data()
|
||||
.expect("yields data");
|
||||
assert_eq!(data, chunk);
|
||||
let data = body.data().await.expect("data").expect("data");
|
||||
let data = body
|
||||
.frame()
|
||||
.await
|
||||
.expect("yields a result")
|
||||
.expect("yields a frame")
|
||||
.into_data()
|
||||
.expect("yields data");
|
||||
assert_eq!(data, chunk);
|
||||
|
||||
timeout(body.data()).await.expect_err("no more chunks");
|
||||
timeout(body.frame()).await.expect_err("no more chunks");
|
||||
|
||||
tracing::info!(sz = chunk.len(), "Confirming stream window availability");
|
||||
/*
|
||||
* XXX(kate): this can be reinstated when we have a `poll_ready(cx)` method on the new sender.
|
||||
timeout(futures::future::poll_fn(|cx| tx.poll_ready(cx)))
|
||||
.await
|
||||
.expect("timed out")
|
||||
.expect("ready");
|
||||
*/
|
||||
}
|
||||
|
||||
// === Utilities ===
|
||||
|
|
@ -148,12 +168,12 @@ struct TestServer {
|
|||
server: Handle,
|
||||
}
|
||||
|
||||
type Mock = mock::Mock<http::Request<BoxBody>, http::Response<BoxBody>>;
|
||||
type Handle = mock::Handle<http::Request<BoxBody>, http::Response<BoxBody>>;
|
||||
type Mock = mock::Mock<http::Request<hyper::body::Incoming>, http::Response<BoxBody>>;
|
||||
type Handle = mock::Handle<http::Request<hyper::body::Incoming>, http::Response<BoxBody>>;
|
||||
|
||||
/// Allows us to configure a server from the Params type.
|
||||
#[derive(Clone, Debug)]
|
||||
struct NewMock(mock::Mock<http::Request<BoxBody>, http::Response<BoxBody>>);
|
||||
struct NewMock(mock::Mock<http::Request<hyper::body::Incoming>, http::Response<BoxBody>>);
|
||||
|
||||
impl NewService<()> for NewMock {
|
||||
type Service = NewMock;
|
||||
|
|
@ -185,21 +205,28 @@ impl TestServer {
|
|||
#[tracing::instrument(skip_all)]
|
||||
async fn connect_h2(
|
||||
h2: h2::ServerParams,
|
||||
client: &mut hyper::client::conn::http2::Builder,
|
||||
client: &mut hyper::client::conn::http2::Builder<TracingExecutor>,
|
||||
) -> Self {
|
||||
use hyper_util::{rt::tokio::TokioIo, service::TowerToHyperService};
|
||||
let params = Params {
|
||||
drain: drain(),
|
||||
version: Variant::H2,
|
||||
http2: h2,
|
||||
};
|
||||
|
||||
let (sio, cio) = {
|
||||
let (sio, cio) = io::duplex(20 * 1024 * 1024); // 20 MB
|
||||
(TokioIo::new(sio), TokioIo::new(cio))
|
||||
};
|
||||
|
||||
// Build the HTTP server with a mocked inner service so that we can handle
|
||||
// requests.
|
||||
let (mock, server) = mock::pair();
|
||||
let svc = NewServeHttp::new(CloneParam::from(params), NewMock(mock)).new_service(());
|
||||
|
||||
let (sio, cio) = io::duplex(20 * 1024 * 1024); // 20 MB
|
||||
tokio::spawn(svc.oneshot(sio).instrument(info_span!("server")));
|
||||
// fn bound<S: Service<T>, T>(_: &S) {}
|
||||
// bound(&svc);
|
||||
let fut = svc.oneshot(sio).instrument(info_span!("server"));
|
||||
tokio::spawn(fut);
|
||||
|
||||
// Build a real HTTP/2 client using the mocked socket.
|
||||
let (client, task) = client
|
||||
|
|
@ -215,7 +242,12 @@ impl TestServer {
|
|||
/// response. The mocked response body sender and the readable response body are
|
||||
/// returned.
|
||||
#[tracing::instrument(skip(self))]
|
||||
async fn get(&mut self) -> (hyper::body::Sender, hyper::Body) {
|
||||
async fn get(
|
||||
&mut self,
|
||||
) -> (
|
||||
http_body_util::channel::Sender<Bytes>,
|
||||
hyper::body::Incoming,
|
||||
) {
|
||||
self.server.allow(1);
|
||||
let mut call0 = self
|
||||
.client
|
||||
|
|
@ -225,14 +257,14 @@ impl TestServer {
|
|||
_ = (&mut call0) => unreachable!("client cannot receive a response"),
|
||||
next = self.server.next_request() => next.expect("server not dropped"),
|
||||
};
|
||||
let (tx, rx) = hyper::Body::channel();
|
||||
let (tx, rx) = http_body_util::channel::Channel::new(512);
|
||||
next.send_response(http::Response::new(BoxBody::new(rx)));
|
||||
let rsp = call0.await.expect("response");
|
||||
(tx, rsp.into_body())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self))]
|
||||
async fn respond(&mut self, body: Bytes) -> hyper::Body {
|
||||
async fn respond(&mut self, body: Bytes) -> hyper::body::Incoming {
|
||||
let (mut tx, rx) = self.get().await;
|
||||
tx.send_data(body.clone()).await.expect("send data");
|
||||
rx
|
||||
|
|
|
|||
Loading…
Reference in New Issue