Skip to content

Commit dcfc21c

Browse files
authored
gateway: Fix URI form for gatewayed HTTP/1 requests (#2241)
A recent change broke how gateways handle the URI of upgraded HTTP/1.1 requests. This problem was caused by normaling the URI twice (once in the inbound HTTP server and once in the outbound HTTP server). To fix this, the outbound HTTP server has been removed from the cached stack so that it is not included in gatewayed services. Furthermore, to test this, this change splits `push_http_client` from the `Outbound::push_http_endpoint` stack and `push_http_tcp_server` from the `Inbound::push_http_server` stack. This allows us to write tests that mock HTTP-level services.
1 parent ed394d9 commit dcfc21c

File tree

26 files changed

+489
-198
lines changed

26 files changed

+489
-198
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -859,6 +859,7 @@ dependencies = [
859859
"linkerd-app-inbound",
860860
"linkerd-app-outbound",
861861
"linkerd-app-test",
862+
"linkerd-proxy-server-policy",
862863
"thiserror",
863864
"tokio",
864865
"tokio-test",

linkerd/app/gateway/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ tower = { version = "0.4", default-features = false }
1818
tracing = "0.1"
1919

2020
[dev-dependencies]
21+
linkerd-app-inbound = { path = "../inbound", features = ["test-util"] }
22+
linkerd-app-outbound = { path = "../outbound", features = ["test-util"] }
23+
linkerd-proxy-server-policy = { path = "../../proxy/server-policy" }
2124
tokio = { version = "1", features = ["rt", "macros"] }
2225
tokio-test = "0.4"
2326
tower = { version = "0.4", default-features = false, features = ["util"] }

linkerd/app/gateway/src/http.rs

Lines changed: 72 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,18 @@
1-
use super::{server::Http, Gateway};
1+
use super::Gateway;
22
use inbound::{GatewayAddr, GatewayDomainInvalid};
3-
use linkerd_app_core::{identity, io, profiles, proxy::http, svc, tls, transport::addrs::*, Error};
3+
use linkerd_app_core::{
4+
identity,
5+
metrics::ServerLabel,
6+
profiles,
7+
proxy::{
8+
api_resolve::{ConcreteAddr, Metadata},
9+
core::Resolve,
10+
http,
11+
},
12+
svc, tls,
13+
transport::addrs::*,
14+
Error,
15+
};
416
use linkerd_app_inbound as inbound;
517
use linkerd_app_outbound as outbound;
618
use std::{
@@ -34,34 +46,61 @@ pub struct Target<T = ()> {
3446
struct ByRequestVersion<T>(Target<T>);
3547

3648
impl Gateway {
37-
/// Wrap the provided outbound HTTP stack with an HTTP server, inbound
38-
/// authorization, and gateway request routing.
39-
pub fn http<T, I, N, NSvc>(&self, inner: N) -> svc::Stack<svc::ArcNewTcp<Http<T>, I>>
49+
/// Wrap the provided outbound HTTP client with the inbound HTTP server,
50+
/// inbound authorization, tagged-transport gateway routing, and the
51+
/// outbound router.
52+
pub fn http<T, N, R, NSvc>(
53+
&self,
54+
inner: N,
55+
resolve: R,
56+
) -> svc::Stack<
57+
svc::ArcNewService<
58+
T,
59+
impl svc::Service<
60+
http::Request<http::BoxBody>,
61+
Response = http::Response<http::BoxBody>,
62+
Error = Error,
63+
Future = impl Send,
64+
> + Clone,
65+
>,
66+
>
4067
where
4168
// Target describing an inbound gateway connection.
4269
T: svc::Param<GatewayAddr>,
4370
T: svc::Param<OrigDstAddr>,
4471
T: svc::Param<Remote<ClientAddr>>,
72+
T: svc::Param<ServerLabel>,
4573
T: svc::Param<tls::ConditionalServerTls>,
4674
T: svc::Param<tls::ClientId>,
4775
T: svc::Param<inbound::policy::AllowPolicy>,
48-
T: svc::Param<profiles::LookupAddr>,
76+
T: svc::Param<Option<profiles::Receiver>>,
77+
T: svc::Param<http::Version>,
78+
T: svc::Param<http::normalize_uri::DefaultAuthority>,
4979
T: Clone + Send + Sync + Unpin + 'static,
50-
// Server-side socket.
51-
I: io::AsyncRead + io::AsyncWrite + io::PeerAddr,
52-
I: Send + Unpin + 'static,
80+
// Endpoint resolution.
81+
R: Resolve<ConcreteAddr, Endpoint = Metadata, Error = Error>,
5382
// HTTP outbound stack.
54-
N: svc::NewService<Target, Service = NSvc>,
83+
N: svc::NewService<
84+
outbound::http::concrete::Endpoint<
85+
outbound::http::logical::Concrete<outbound::http::Http>,
86+
>,
87+
Service = NSvc,
88+
>,
5589
N: Clone + Send + Sync + Unpin + 'static,
5690
NSvc: svc::Service<
5791
http::Request<http::BoxBody>,
5892
Response = http::Response<http::BoxBody>,
5993
Error = Error,
6094
>,
6195
NSvc: Send + Unpin + 'static,
62-
NSvc::Future: Send + 'static,
96+
NSvc::Future: Send + Unpin + 'static,
6397
{
64-
let http = svc::stack(inner)
98+
let http = self
99+
.outbound
100+
.clone()
101+
.with_stack(inner)
102+
.push_http_cached(resolve)
103+
.into_stack()
65104
// Discard `T` and its associated client-specific metadata.
66105
.push_map_target(Target::discard_parent)
67106
// Add headers to prevent loops.
@@ -77,29 +116,27 @@ impl Gateway {
77116
}))
78117
// Only permit gateway traffic to endpoints for which we have
79118
// discovery information.
80-
.push_filter(
81-
|(_, parent): (_, Http<T>)| -> Result<_, GatewayDomainInvalid> {
82-
let target = {
83-
let profile = svc::Param::<Option<profiles::Receiver>>::param(&parent)
84-
.ok_or(GatewayDomainInvalid)?;
85-
86-
if let Some(profiles::LogicalAddr(addr)) = profile.logical_addr() {
87-
outbound::http::Logical::Route(addr, profile)
88-
} else if let Some((addr, metadata)) = profile.endpoint() {
89-
outbound::http::Logical::Forward(Remote(ServerAddr(addr)), metadata)
90-
} else {
91-
return Err(GatewayDomainInvalid);
92-
}
93-
};
94-
95-
Ok(Target {
96-
target,
97-
addr: (*parent).param(),
98-
version: svc::Param::param(&parent),
99-
parent: (**parent).clone(),
100-
})
101-
},
102-
)
119+
.push_filter(|(_, parent): (_, T)| -> Result<_, GatewayDomainInvalid> {
120+
let target = {
121+
let profile = svc::Param::<Option<profiles::Receiver>>::param(&parent)
122+
.ok_or(GatewayDomainInvalid)?;
123+
124+
if let Some(profiles::LogicalAddr(addr)) = profile.logical_addr() {
125+
outbound::http::Logical::Route(addr, profile)
126+
} else if let Some((addr, metadata)) = profile.endpoint() {
127+
outbound::http::Logical::Forward(Remote(ServerAddr(addr)), metadata)
128+
} else {
129+
return Err(GatewayDomainInvalid);
130+
}
131+
};
132+
133+
Ok(Target {
134+
target,
135+
addr: parent.param(),
136+
version: parent.param(),
137+
parent,
138+
})
139+
})
103140
// Authorize requests to the gateway.
104141
.push(self.inbound.authorize_http());
105142

0 commit comments

Comments
 (0)