Skip to content

Commit b59172a

Browse files
authored
http: Simplify stacks and target types (#656)
Currently, various properties of HTTP requests are exposed into the stack target type, meaning that stacks (including their resolutions) cannot be shared across requests that vary by any of these settings and, worse, that per-request determinations must be made to determine which stack to use. In order to bind stacks according to connection-level metadata, where these request-level parameters cannot be known, we need to remove this metadata from the stack target. The HTTP server now applies URI normalization as an internal implementation detail (because it's a detail of how hyper clients and servers differ). This eliminates the `ShouldNormalizeUri` trait, as that determination can be made entirely based on the properties of the request without any additional stack-specific metadata. The HTTP client now takes an updated `Settings` type that explicitly handles the orig-proto upgrade logic (removing this from the client stack). Stack targets now hold an `http::Version` on the accept-side, and map that to a per-endpoint `http::client::Settings` for use by the http client.
1 parent 9e7fd5a commit b59172a

File tree

21 files changed

+474
-662
lines changed

21 files changed

+474
-662
lines changed

linkerd/app/core/src/control.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ impl fmt::Display for ControlAddr {
3434
}
3535

3636
type BalanceBody =
37-
http::balance::PendingUntilFirstDataBody<tower::load::peak_ewma::Handle, hyper::Body>;
37+
http::balance::PendingUntilFirstDataBody<tower::load::peak_ewma::Handle, http::Payload>;
3838

3939
type RspBody = linkerd2_http_metrics::requests::ResponseBody<BalanceBody, classify::Eos>;
4040

linkerd/app/gateway/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ mod test {
153153
dst: dst_name
154154
.map(|n| NameAddr::from_str(n).unwrap().into())
155155
.unwrap_or_else(|| socket_addr.into()),
156-
http_settings: http::Settings::Http2,
156+
http_version: http::Version::Http1,
157157
tls_client_id: peer_id,
158158
};
159159
assert_ready_ok!(make_gateway.poll_ready());

linkerd/app/gateway/src/make.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ where
7171
fn call(&mut self, target: inbound::Target) -> Self::Future {
7272
let inbound::Target {
7373
dst,
74-
http_settings,
74+
http_version,
7575
tls_client_id,
7676
..
7777
} = target;
@@ -120,7 +120,7 @@ where
120120
let endpoint = outbound::HttpLogical {
121121
dst: dst_name.clone().into(),
122122
orig_dst: dst_addr,
123-
settings: http_settings,
123+
version: http_version,
124124
require_identity: None,
125125
};
126126

linkerd/app/inbound/src/endpoint.rs

Lines changed: 14 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,14 @@ use linkerd2_app_core::{
77
transport::{listen, tls},
88
Addr, Conditional, CANONICAL_DST_HEADER, DST_OVERRIDE_HEADER,
99
};
10-
use std::fmt;
11-
use std::net::SocketAddr;
12-
use std::sync::Arc;
10+
use std::{convert::TryInto, fmt, net::SocketAddr, sync::Arc};
1311
use tracing::debug;
1412

1513
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1614
pub struct Target {
1715
pub dst: Addr,
1816
pub socket_addr: SocketAddr,
19-
pub http_settings: http::Settings,
17+
pub http_version: http::Version,
2018
pub tls_client_id: tls::PeerIdentity,
2119
}
2220

@@ -29,7 +27,7 @@ pub struct Logical {
2927
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3028
pub struct HttpEndpoint {
3129
pub port: u16,
32-
pub settings: http::Settings,
30+
pub settings: http::client::Settings,
3331
}
3432

3533
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
@@ -53,17 +51,17 @@ impl Into<SocketAddr> for HttpEndpoint {
5351
}
5452
}
5553

56-
impl AsRef<http::Settings> for HttpEndpoint {
57-
fn as_ref(&self) -> &http::Settings {
58-
&self.settings
54+
impl Into<http::client::Settings> for &'_ HttpEndpoint {
55+
fn into(self) -> http::client::Settings {
56+
self.settings
5957
}
6058
}
6159

6260
impl From<Target> for HttpEndpoint {
6361
fn from(target: Target) -> Self {
6462
Self {
6563
port: target.socket_addr.port(),
66-
settings: target.http_settings,
64+
settings: target.http_version.into(),
6765
}
6866
}
6967
}
@@ -122,25 +120,6 @@ impl AsRef<Addr> for Target {
122120
}
123121
}
124122

125-
impl http::normalize_uri::ShouldNormalizeUri for Target {
126-
fn should_normalize_uri(&self) -> Option<http::uri::Authority> {
127-
if let http::Settings::Http1 {
128-
was_absolute_form: false,
129-
..
130-
} = self.http_settings
131-
{
132-
return Some(self.dst.to_http_authority());
133-
}
134-
None
135-
}
136-
}
137-
138-
impl AsRef<http::Settings> for Target {
139-
fn as_ref(&self) -> &http::Settings {
140-
&self.http_settings
141-
}
142-
}
143-
144123
impl tls::HasPeerIdentity for Target {
145124
fn peer_identity(&self) -> tls::PeerIdentity {
146125
Conditional::None(tls::ReasonForNoPeerName::Loopback.into())
@@ -219,8 +198,8 @@ impl stack_tracing::GetSpan<()> for Target {
219198
fn get_span(&self, _: &()) -> tracing::Span {
220199
use tracing::info_span;
221200

222-
match self.http_settings {
223-
http::Settings::Http2 => match self.dst.name_addr() {
201+
match self.http_version {
202+
http::Version::H2 => match self.dst.name_addr() {
224203
None => info_span!(
225204
"http2",
226205
port = %self.socket_addr.port(),
@@ -231,25 +210,15 @@ impl stack_tracing::GetSpan<()> for Target {
231210
port = %self.socket_addr.port(),
232211
),
233212
},
234-
http::Settings::Http1 {
235-
keep_alive,
236-
wants_h1_upgrade,
237-
was_absolute_form,
238-
} => match self.dst.name_addr() {
213+
http::Version::Http1 => match self.dst.name_addr() {
239214
None => info_span!(
240215
"http1",
241216
port = %self.socket_addr.port(),
242-
keep_alive,
243-
wants_h1_upgrade,
244-
was_absolute_form,
245217
),
246218
Some(name) => info_span!(
247219
"http1",
248220
%name,
249221
port = %self.socket_addr.port(),
250-
keep_alive,
251-
wants_h1_upgrade,
252-
was_absolute_form,
253222
),
254223
},
255224
}
@@ -295,7 +264,10 @@ impl<A> router::Recognize<http::Request<A>> for RequestTarget {
295264
dst,
296265
socket_addr: self.accept.addrs.target_addr(),
297266
tls_client_id: self.accept.peer_identity.clone(),
298-
http_settings: http::Settings::from_request(req),
267+
http_version: req
268+
.version()
269+
.try_into()
270+
.expect("HTTP version must be valid"),
299271
}
300272
}
301273
}

linkerd/app/inbound/src/lib.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use linkerd2_app_core::{
1616
opencensus::proto::trace::v1 as oc,
1717
profiles,
1818
proxy::{
19-
http::{self, normalize_uri, orig_proto, strip_header, DetectHttp},
19+
http::{self, orig_proto, strip_header, DetectHttp},
2020
identity, tap, tcp, SkipDetect,
2121
},
2222
reconnect, router, serve,
@@ -179,7 +179,7 @@ impl Config {
179179

180180
// Creates HTTP clients for each inbound port & HTTP settings.
181181
let endpoint = svc::stack(tcp_connect)
182-
.push(http::MakeClientLayer::new(connect.h2_settings))
182+
.push(http::client::layer(connect.h2_settings))
183183
.push(reconnect::layer({
184184
let backoff = connect.backoff.clone();
185185
move |_| Ok(backoff.stream())
@@ -199,9 +199,6 @@ impl Config {
199199

200200
let target = endpoint
201201
.push_map_target(HttpEndpoint::from)
202-
// Normalizes the URI, i.e. if it was originally in
203-
// absolute-form on the outbound side.
204-
.push(normalize_uri::layer())
205202
.push(observe)
206203
.into_new_service()
207204
.check_new_service::<Target, http::Request<_>>();

linkerd/app/outbound/src/endpoint.rs

Lines changed: 18 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use linkerd2_app_core::{
77
proxy::{
88
api_resolve::{Metadata, ProtocolHint},
99
http::override_authority::CanOverrideAuthority,
10-
http::{self, identity_from_header, Settings},
10+
http::{self, identity_from_header},
1111
identity,
1212
resolve::map_endpoint::MapEndpoint,
1313
tap,
@@ -16,8 +16,7 @@ use linkerd2_app_core::{
1616
transport::{listen, tls},
1717
Addr, Conditional, L5D_REQUIRE_ID,
1818
};
19-
use std::net::SocketAddr;
20-
use std::sync::Arc;
19+
use std::{convert::TryInto, net::SocketAddr, sync::Arc};
2120

2221
#[derive(Copy, Clone, Debug)]
2322
pub struct FromMetadata;
@@ -26,7 +25,7 @@ pub struct FromMetadata;
2625
pub struct HttpLogical {
2726
pub dst: Addr,
2827
pub orig_dst: SocketAddr,
29-
pub settings: http::Settings,
28+
pub version: http::Version,
3029
pub require_identity: Option<identity::Name>,
3130
}
3231

@@ -48,7 +47,7 @@ pub struct Profile {
4847
#[derive(Clone, Debug, Eq, PartialEq)]
4948
pub struct HttpEndpoint {
5049
pub addr: SocketAddr,
51-
pub settings: http::Settings,
50+
pub settings: http::client::Settings,
5251
pub identity: tls::PeerIdentity,
5352
pub metadata: Metadata,
5453
pub concrete: HttpConcrete,
@@ -130,7 +129,7 @@ impl From<HttpLogical> for HttpEndpoint {
130129
fn from(logical: HttpLogical) -> Self {
131130
Self {
132131
addr: logical.orig_dst,
133-
settings: logical.settings,
132+
settings: logical.version.into(),
134133
identity: logical
135134
.require_identity
136135
.clone()
@@ -146,22 +145,6 @@ impl From<HttpLogical> for HttpEndpoint {
146145
}
147146
}
148147

149-
impl HttpEndpoint {
150-
pub fn can_use_orig_proto(&self) -> bool {
151-
if let ProtocolHint::Unknown = self.metadata.protocol_hint() {
152-
return false;
153-
}
154-
155-
// Look at the original settings, ignoring any authority overrides.
156-
match self.settings {
157-
http::Settings::Http2 => false,
158-
http::Settings::Http1 {
159-
wants_h1_upgrade, ..
160-
} => !wants_h1_upgrade,
161-
}
162-
}
163-
}
164-
165148
impl std::hash::Hash for HttpEndpoint {
166149
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
167150
self.addr.hash(state);
@@ -182,22 +165,9 @@ impl Into<SocketAddr> for HttpEndpoint {
182165
}
183166
}
184167

185-
impl AsRef<http::Settings> for HttpEndpoint {
186-
fn as_ref(&self) -> &http::Settings {
187-
&self.settings
188-
}
189-
}
190-
191-
impl http::normalize_uri::ShouldNormalizeUri for HttpEndpoint {
192-
fn should_normalize_uri(&self) -> Option<http::uri::Authority> {
193-
if let http::Settings::Http1 {
194-
was_absolute_form: false,
195-
..
196-
} = self.settings
197-
{
198-
return Some(self.concrete.logical.dst.to_http_authority());
199-
}
200-
None
168+
impl Into<http::client::Settings> for &'_ HttpEndpoint {
169+
fn into(self) -> http::client::Settings {
170+
self.settings
201171
}
202172
}
203173

@@ -260,18 +230,12 @@ impl MapEndpoint<HttpConcrete, Metadata> for FromMetadata {
260230
Conditional::None(tls::ReasonForNoPeerName::NotProvidedByServiceDiscovery.into())
261231
});
262232

263-
let settings = match concrete.logical.settings {
264-
Settings::Http1 {
265-
keep_alive,
266-
wants_h1_upgrade,
267-
was_absolute_form,
268-
} => Settings::Http1 {
269-
keep_alive,
270-
wants_h1_upgrade,
271-
// Always use absolute form when an onverride is present.
272-
was_absolute_form: metadata.authority_override().is_some() || was_absolute_form,
233+
let settings = match concrete.logical.version {
234+
http::Version::H2 => http::client::Settings::H2,
235+
http::Version::Http1 => match metadata.protocol_hint() {
236+
ProtocolHint::Unknown => http::client::Settings::Http1,
237+
ProtocolHint::Http2 => http::client::Settings::OrigProtoUpgrade,
273238
},
274-
settings => settings,
275239
};
276240

277241
HttpEndpoint {
@@ -406,17 +370,18 @@ impl<B> router::Recognize<http::Request<B>> for LogicalPerRequest {
406370
addr.into()
407371
});
408372

409-
let settings = http::Settings::from_request(req);
410-
411-
tracing::debug!(headers = ?req.headers(), uri = %req.uri(), dst = %dst, http.settings = ?settings, "Setting target for request");
373+
tracing::debug!(headers = ?req.headers(), uri = %req.uri(), dst = %dst, version = ?req.version(), "Setting target for request");
412374

413375
let require_identity = identity_from_header(req, L5D_REQUIRE_ID);
414376

415377
HttpLogical {
416378
dst,
417379
orig_dst: self.0.target_addr(),
418-
settings,
419380
require_identity,
381+
version: req
382+
.version()
383+
.try_into()
384+
.expect("HTTP version must be valid"),
420385
}
421386
}
422387
}

linkerd/app/outbound/src/lib.rs

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,9 @@ use tokio::sync::mpsc;
3434
use tracing::{info, info_span};
3535

3636
pub mod endpoint;
37-
mod orig_proto_upgrade;
3837
mod prevent_loop;
3938
mod require_identity_on_endpoint;
4039

41-
use self::orig_proto_upgrade::OrigProtoUpgradeLayer;
4240
use self::prevent_loop::PreventLoop;
4341
use self::require_identity_on_endpoint::MakeRequireIdentityLayer;
4442

@@ -166,7 +164,7 @@ impl Config {
166164
// Initiates an HTTP client on the underlying transport. Prior-knowledge HTTP/2
167165
// is typically used (i.e. when communicating with other proxies); though
168166
// HTTP/1.x fallback is supported as needed.
169-
.push(http::MakeClientLayer::new(self.proxy.connect.h2_settings))
167+
.push(http::client::layer(self.proxy.connect.h2_settings))
170168
// Re-establishes a connection when the client fails.
171169
.push(reconnect::layer({
172170
let backoff = self.proxy.connect.backoff.clone();
@@ -179,13 +177,6 @@ impl Config {
179177
HOST.as_str(),
180178
CANONICAL_DST_HEADER,
181179
]))
182-
// Ensures that the request's URI is in the proper form.
183-
.push(http::normalize_uri::layer())
184-
// Upgrades HTTP/1 requests to be transported over HTTP/2 connections.
185-
//
186-
// This sets headers so that the inbound proxy can downgrade the request
187-
// properly.
188-
.push(OrigProtoUpgradeLayer::new())
189180
.push_on_response(svc::layers().box_http_response())
190181
.check_service::<HttpEndpoint>()
191182
.instrument(|e: &HttpEndpoint| info_span!("endpoint", peer.addr = %e.addr))

0 commit comments

Comments
 (0)