Skip to content

Commit 79de26d

Browse files
authored
http: Use ExtractParam to support dynamic detect configuration (#1174)
Following #1169, this change modifies the `detect` middleware to use `ExtractParam`. In doing this, it makes sense to modify the default implementations of `ExtractParam` so that we can provide a static configuration to the module that is cloned for all targets without modification. This change does not alter any functionality, but sets up to make the HTTP detection layer configured per-target.
1 parent fd2c1bb commit 79de26d

File tree

8 files changed

+79
-80
lines changed

8 files changed

+79
-80
lines changed

linkerd/app/admin/src/stack.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,10 +120,7 @@ impl Config {
120120
},
121121
)
122122
.push(svc::BoxNewService::layer())
123-
.push(detect::NewDetectService::layer(
124-
DETECT_TIMEOUT,
125-
http::DetectHttp::default(),
126-
))
123+
.push(detect::NewDetectService::layer(detect::Config::<http::DetectHttp>::from_timeout(DETECT_TIMEOUT)))
127124
.push(metrics.transport.layer_accept())
128125
.push_map_target(|(tls, addrs): (tls::ConditionalServerTls, B::Addrs)| {
129126
// TODO this should use an admin-specific target type.

linkerd/app/core/src/config.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
pub use crate::exp_backoff::ExponentialBackoff;
22
use crate::{
3-
proxy::http::{h1, h2},
3+
proxy::http::{self, h1, h2},
44
svc::Param,
55
transport::{Keepalive, ListenAddr},
66
};
@@ -50,6 +50,14 @@ pub type PortSet = HashSet<u16, BuildHasherDefault<PortHasher>>;
5050
#[derive(Default)]
5151
pub struct PortHasher(u16);
5252

53+
// === impl ProxyConfig ===
54+
55+
impl ProxyConfig {
56+
pub fn detect_http(&self) -> linkerd_detect::Config<http::DetectHttp> {
57+
linkerd_detect::Config::from_timeout(self.detect_protocol_timeout)
58+
}
59+
}
60+
5361
// === impl ServerConfig ===
5462

5563
impl Param<ListenAddr> for ServerConfig {

linkerd/app/gateway/src/lib.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,6 @@ where
9393
let ProxyConfig {
9494
buffer_capacity,
9595
cache_max_idle_age,
96-
detect_protocol_timeout,
9796
dispatch_timeout,
9897
..
9998
} = inbound.config().proxy.clone();
@@ -232,8 +231,7 @@ where
232231
.push(svc::Filter::<ClientInfo, _>::layer(HttpLegacy::try_from))
233232
.push(svc::BoxNewService::layer())
234233
.push(detect::NewDetectService::layer(
235-
detect_protocol_timeout,
236-
http::DetectHttp::default(),
234+
inbound.config().proxy.detect_http(),
237235
));
238236

239237
// When a transported connection is received, use the header's target to

linkerd/app/inbound/src/lib.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -276,10 +276,7 @@ where
276276
))
277277
.push_map_target(detect::allow_timeout)
278278
.push(svc::BoxNewService::layer())
279-
.push(detect::NewDetectService::layer(
280-
detect_timeout,
281-
http::DetectHttp::default(),
282-
))
279+
.push(detect::NewDetectService::layer(cfg.proxy.detect_http()))
283280
.check_new_service::<TcpAccept, _>()
284281
.push_request_filter(require_id)
285282
.push(rt.metrics.transport.layer_accept())

linkerd/app/outbound/src/http/detect.rs

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
use crate::{http, Outbound};
22
use linkerd_app_core::{
3-
config::{ProxyConfig, ServerConfig},
3+
config::ServerConfig,
44
detect, io,
55
svc::{self, Param},
6-
Error,
6+
Error, Infallible,
77
};
88
use tracing::debug_span;
99

@@ -29,11 +29,7 @@ impl<N> Outbound<N> {
2929
U: From<(http::Version, T)> + svc::Param<http::Version> + 'static,
3030
{
3131
self.map_stack(|config, rt, tcp| {
32-
let ProxyConfig {
33-
server: ServerConfig { h2_settings, .. },
34-
detect_protocol_timeout,
35-
..
36-
} = config.proxy;
32+
let ServerConfig { h2_settings, .. } = config.proxy.server;
3733

3834
let skipped = tcp
3935
.clone()
@@ -58,19 +54,15 @@ impl<N> Outbound<N> {
5854
.check_new_service::<(Option<http::Version>, T), _>()
5955
.push_map_target(detect::allow_timeout)
6056
.push(svc::BoxNewService::layer())
61-
.push(detect::NewDetectService::layer(
62-
detect_protocol_timeout,
63-
http::DetectHttp::default(),
64-
))
57+
.push(detect::NewDetectService::layer(config.proxy.detect_http()))
6558
.push_switch(
6659
// When the target is marked as as opaque, we skip HTTP
6760
// detection and just use the TCP stack directly.
68-
|target: T| -> Result<_, Error> {
61+
|target: T| -> Result<_, Infallible> {
6962
if let Some(Skip) = target.param() {
70-
Ok(svc::Either::B(target))
71-
} else {
72-
Ok(svc::Either::A(target))
63+
return Ok(svc::Either::B(target));
7364
}
65+
Ok(svc::Either::A(target))
7466
},
7567
skipped,
7668
)

linkerd/app/outbound/src/ingress.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,14 +70,14 @@ impl Outbound<svc::BoxNewHttp<http::Endpoint>> {
7070

7171
let http_endpoint = self.into_stack();
7272

73+
let detect_http = config.proxy.detect_http();
7374
let Config {
7475
allow_discovery,
7576
proxy:
7677
ProxyConfig {
7778
server: ServerConfig { h2_settings, .. },
7879
dispatch_timeout,
7980
max_in_flight_requests,
80-
detect_protocol_timeout,
8181
buffer_capacity,
8282
cache_max_idle_age,
8383
..
@@ -219,10 +219,7 @@ impl Outbound<svc::BoxNewHttp<http::Endpoint>> {
219219
.push_cache(cache_max_idle_age)
220220
.push_map_target(detect::allow_timeout)
221221
.push(svc::BoxNewService::layer())
222-
.push(detect::NewDetectService::layer(
223-
detect_protocol_timeout,
224-
http::DetectHttp::default(),
225-
))
222+
.push(detect::NewDetectService::layer(detect_http))
226223
.push(rt.metrics.transport.layer_accept())
227224
.instrument(|a: &tcp::Accept| info_span!("ingress", orig_dst = %a.orig_dst))
228225
.push_map_target(|a: T| {

linkerd/detect/src/lib.rs

Lines changed: 55 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
use bytes::BytesMut;
55
use linkerd_error::Error;
66
use linkerd_io as io;
7-
use linkerd_stack::{layer, NewService};
7+
use linkerd_stack::{layer, ExtractParam, NewService};
88
use std::{
99
fmt,
1010
future::Future,
@@ -27,28 +27,30 @@ pub trait Detect<I>: Clone + Send + Sync + 'static {
2727
pub type DetectResult<P> = Result<Option<P>, DetectTimeoutError<P>>;
2828

2929
#[derive(Error)]
30-
#[error("{} protocol detection timed out after {:?}", std::any::type_name::<P>(), .0)]
30+
#[error("{} protocol detection timed out after {0:?}", std::any::type_name::<P>())]
3131
pub struct DetectTimeoutError<P>(time::Duration, std::marker::PhantomData<P>);
3232

3333
#[derive(Copy, Clone, Debug)]
34-
pub struct NewDetectService<D, N> {
34+
pub struct Config<D> {
35+
pub detect: D,
36+
pub capacity: usize,
37+
pub timeout: time::Duration,
38+
}
39+
40+
#[derive(Copy, Clone, Debug)]
41+
pub struct NewDetectService<P, D, N> {
3542
inner: N,
36-
detect: D,
37-
capacity: usize,
38-
timeout: time::Duration,
43+
params: P,
44+
_detect: std::marker::PhantomData<fn() -> D>,
3945
}
4046

4147
#[derive(Copy, Clone, Debug)]
4248
pub struct DetectService<T, D, N> {
4349
target: T,
50+
config: Config<D>,
4451
inner: N,
45-
detect: D,
46-
capacity: usize,
47-
timeout: time::Duration,
4852
}
4953

50-
const BUFFER_CAPACITY: usize = 1024;
51-
5254
pub fn allow_timeout<P, T>((p, t): (DetectResult<P>, T)) -> (Option<P>, T) {
5355
match p {
5456
Ok(p) => (p, t),
@@ -59,52 +61,67 @@ pub fn allow_timeout<P, T>((p, t): (DetectResult<P>, T)) -> (Option<P>, T) {
5961
}
6062
}
6163

64+
// === impl Config ===
65+
66+
impl<D: Default> Config<D> {
67+
const DEFAULT_CAPACITY: usize = 1024;
68+
69+
pub fn from_timeout(timeout: time::Duration) -> Self {
70+
Self {
71+
detect: D::default(),
72+
capacity: Self::DEFAULT_CAPACITY,
73+
timeout,
74+
}
75+
}
76+
}
77+
6278
// === impl NewDetectService ===
6379

64-
impl<D: Clone, N> NewDetectService<D, N> {
65-
pub fn new(timeout: time::Duration, detect: D, inner: N) -> Self {
80+
impl<P, D, N> NewDetectService<P, D, N> {
81+
pub fn new(params: P, inner: N) -> Self {
6682
Self {
67-
detect,
6883
inner,
69-
timeout,
70-
capacity: BUFFER_CAPACITY,
84+
params,
85+
_detect: std::marker::PhantomData,
7186
}
7287
}
7388

74-
pub fn layer(
75-
timeout: time::Duration,
76-
detect: D,
77-
) -> impl layer::Layer<N, Service = Self> + Clone {
78-
layer::mk(move |inner| Self::new(timeout, detect.clone(), inner))
89+
pub fn layer(params: P) -> impl layer::Layer<N, Service = Self> + Clone
90+
where
91+
P: Clone,
92+
{
93+
layer::mk(move |inner| Self::new(params.clone(), inner))
7994
}
8095
}
8196

82-
impl<D: Clone, N: Clone, T> NewService<T> for NewDetectService<D, N> {
97+
impl<T, P, D, N: Clone> NewService<T> for NewDetectService<P, D, N>
98+
where
99+
P: ExtractParam<Config<D>, T>,
100+
{
83101
type Service = DetectService<T, D, N>;
84102

85103
fn new_service(&mut self, target: T) -> DetectService<T, D, N> {
104+
let config = self.params.extract_param(&target);
86105
DetectService {
87106
target,
88-
detect: self.detect.clone(),
107+
config,
89108
inner: self.inner.clone(),
90-
capacity: self.capacity,
91-
timeout: self.timeout,
92109
}
93110
}
94111
}
95112

96113
// === impl DetectService ===
97114

98-
impl<S, T, D, N, I> tower::Service<I> for DetectService<T, D, N>
115+
impl<I, T, D, N, NSvc> tower::Service<I> for DetectService<T, D, N>
99116
where
100117
T: Clone + Send + 'static,
101118
I: Send + 'static,
102119
D: Detect<I>,
103120
D::Protocol: std::fmt::Debug,
104-
N: NewService<(DetectResult<D::Protocol>, T), Service = S> + Clone + Send + 'static,
105-
S: tower::Service<io::PrefixedIo<I>, Response = ()> + Send,
106-
S::Error: Into<Error>,
107-
S::Future: Send,
121+
N: NewService<(DetectResult<D::Protocol>, T), Service = NSvc> + Clone + Send + 'static,
122+
NSvc: tower::Service<io::PrefixedIo<I>, Response = ()> + Send,
123+
NSvc::Error: Into<Error>,
124+
NSvc::Future: Send,
108125
{
109126
type Response = ();
110127
type Error = Error;
@@ -116,15 +133,18 @@ where
116133
}
117134

118135
fn call(&mut self, mut io: I) -> Self::Future {
119-
let mut inner = self.inner.clone();
120-
let mut buf = BytesMut::with_capacity(self.capacity);
121-
let detect = self.detect.clone();
136+
let Config {
137+
detect,
138+
capacity,
139+
timeout,
140+
} = self.config.clone();
122141
let target = self.target.clone();
123-
let timeout = self.timeout;
142+
let mut inner = self.inner.clone();
124143
Box::pin(async move {
125-
trace!("Starting protocol detection");
144+
trace!(%capacity, ?timeout, "Starting protocol detection");
126145
let t0 = time::Instant::now();
127146

147+
let mut buf = BytesMut::with_capacity(capacity);
128148
let detected = match time::timeout(timeout, detect.detect(&mut io, &mut buf)).await {
129149
Ok(Ok(protocol)) => {
130150
debug!(?protocol, elapsed = ?t0.elapsed(), "DetectResult");

linkerd/stack/src/lib.rs

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -84,20 +84,10 @@ impl<T: ToOwned> Param<T::Owned> for T {
8484

8585
/// === ExtractParam ===
8686
87-
impl<P, T: Param<P>> ExtractParam<P, T> for () {
87+
impl<P: ToOwned, T> ExtractParam<P::Owned, T> for P {
8888
#[inline]
89-
fn extract_param(&self, t: &T) -> P {
90-
t.param()
91-
}
92-
}
93-
94-
impl<F, P, T> ExtractParam<P, T> for F
95-
where
96-
F: Fn(&T) -> P,
97-
{
98-
#[inline]
99-
fn extract_param(&self, target: &T) -> P {
100-
(self)(target)
89+
fn extract_param(&self, _: &T) -> P::Owned {
90+
self.to_owned()
10191
}
10292
}
10393

0 commit comments

Comments
 (0)