@@ -11,9 +11,10 @@ use linkerd_app_core::{
1111 addrs:: { ClientAddr , OrigDstAddr , Remote } ,
1212 ServerAddr ,
1313 } ,
14- Error ,
14+ Error , Infallible ,
1515} ;
16- use std:: fmt:: Debug ;
16+ use linkerd_server_policy:: Protocol ;
17+ use std:: { fmt:: Debug , time} ;
1718
1819#[ derive( Clone , Debug , PartialEq , Eq ) ]
1920pub struct Tls {
@@ -22,6 +23,15 @@ pub struct Tls {
2223 permit : Permitted ,
2324}
2425
26+ #[ derive( Clone , Debug ) ]
27+ struct Detect {
28+ timeout : time:: Duration ,
29+ tls : Tls ,
30+ }
31+
32+ #[ derive( Copy , Clone , Debug ) ]
33+ struct ConfigureHttpDetect ;
34+
2535#[ derive( Clone , Debug , PartialEq , Eq ) ]
2636pub struct Http {
2737 tls : Tls ,
@@ -63,22 +73,34 @@ impl<N> Inbound<N> {
6373 const TLS_PORT_SKIPPED : tls:: ConditionalServerTls =
6474 tls:: ConditionalServerTls :: None ( tls:: NoServerTls :: PortSkipped ) ;
6575
66- self . map_stack ( |cfg, rt, tls | {
76+ self . map_stack ( |cfg, rt, detect | {
6777 let detect_timeout = cfg. proxy . detect_protocol_timeout ;
68- tls. check_new_service :: < Tls , tls:: server:: Io < I > > ( )
69- . push_request_filter (
70- |( tls, t) : ( tls:: ConditionalServerTls , T ) | -> Result < Tls , DeniedUnauthorized > {
78+ detect
79+ . push_switch (
80+ // Ensure that the connection is authorized before proceeding with protocol
81+ // detection.
82+ |( tls, t) : ( tls:: ConditionalServerTls , T ) | -> Result < _ , Error > {
7183 let policy: AllowPolicy = t. param ( ) ;
7284 let permit = policy. check_authorized ( tls) ?;
73- Ok ( Tls :: from_params ( & t, permit) )
85+
86+ // If the port is configured to support application TLS, it may have also
87+ // been wrapped in mesh identity. In any case, we don't actually validate
88+ // whether app TLS was employed, but we use this as a signal that we should
89+ // not perform additional protocol detection.
90+ if let Protocol :: Tls = permit. protocol {
91+ return Ok ( svc:: Either :: B ( Tls :: from_params ( & t, permit) ) ) ;
92+ }
93+
94+ Ok ( svc:: Either :: A ( Tls :: from_params ( & t, permit) ) )
7495 } ,
96+ svc:: stack ( forward. clone ( ) )
97+ . push_on_response ( svc:: MapTargetLayer :: new ( io:: BoxedIo :: new) )
98+ . into_inner ( ) ,
7599 )
76- . check_new_service :: < ( tls:: ConditionalServerTls , T ) , tls:: server:: Io < I > > ( )
77100 . push ( tls:: NewDetectTls :: layer ( TlsParams {
78101 timeout : tls:: server:: Timeout ( detect_timeout) ,
79102 identity : rt. identity . clone ( ) ,
80103 } ) )
81- . check_new_service :: < T , I > ( )
82104 . push_switch (
83105 // If this port's policy indicates that authentication is not required and
84106 // detection should be skipped, use the TCP stack directly.
@@ -94,7 +116,6 @@ impl<N> Inbound<N> {
94116 . push_on_response ( svc:: MapTargetLayer :: new ( io:: BoxedIo :: new) )
95117 . into_inner ( ) ,
96118 )
97- . check_new_service :: < T , I > ( )
98119 . push_on_response ( svc:: BoxService :: layer ( ) )
99120 . push ( svc:: BoxNewService :: layer ( ) )
100121 } )
@@ -122,17 +143,36 @@ impl<N> Inbound<N> {
122143 FSvc :: Error : Into < Error > ,
123144 FSvc :: Future : Send ,
124145 {
125- self . map_stack ( |cfg, rt, http| {
126- http. push_map_target ( |( http, tls) | Http { http, tls } )
127- . push ( svc:: UnwrapOr :: layer (
128- // When HTTP detection fails, forward the connection to the application as
129- // an opaque TCP stream.
130- forward. clone ( ) ,
131- ) )
146+ self . map_stack ( |_, rt, http| {
147+ http. clone ( )
132148 . push_on_response ( svc:: MapTargetLayer :: new ( io:: BoxedIo :: new) )
133- . push ( svc:: BoxNewService :: layer ( ) )
134- . push_map_target ( detect:: allow_timeout)
135- . push ( detect:: NewDetectService :: layer ( cfg. proxy . detect_http ( ) ) )
149+ . push_switch (
150+ // If we have a protocol hint, skip detection and just used the hinted HTTP
151+ // version.
152+ |tls : Tls | -> Result < _ , Infallible > {
153+ let http = match tls. permit . protocol {
154+ Protocol :: Detect { timeout } => {
155+ return Ok ( svc:: Either :: B ( Detect { timeout, tls } ) ) ;
156+ }
157+ Protocol :: Http1 => http:: Version :: Http1 ,
158+ Protocol :: Http2 | Protocol :: Grpc => http:: Version :: H2 ,
159+ _ => unreachable ! ( "opaque protocols must not hit the HTTP stack" ) ,
160+ } ;
161+ Ok ( svc:: Either :: A ( Http { http, tls } ) )
162+ } ,
163+ http. push_map_target ( |( http, Detect { tls, .. } ) | Http { http, tls } )
164+ . push ( svc:: UnwrapOr :: layer (
165+ // When HTTP detection fails, forward the connection to the application as
166+ // an opaque TCP stream.
167+ svc:: stack ( forward. clone ( ) )
168+ . push_map_target ( |Detect { tls, .. } | tls)
169+ . into_inner ( ) ,
170+ ) )
171+ . push_on_response ( svc:: MapTargetLayer :: new ( io:: BoxedIo :: new) )
172+ . push ( svc:: BoxNewService :: layer ( ) )
173+ . push_map_target ( detect:: allow_timeout)
174+ . push ( detect:: NewDetectService :: layer ( ConfigureHttpDetect ) ) ,
175+ )
136176 . push ( rt. metrics . transport . layer_accept ( ) )
137177 . push_on_response ( svc:: BoxService :: layer ( ) )
138178 . push ( svc:: BoxNewService :: layer ( ) )
@@ -171,6 +211,14 @@ impl svc::Param<transport::labels::Key> for Tls {
171211 }
172212}
173213
214+ // === impl ConfigureHttpDetect ===
215+
216+ impl svc:: ExtractParam < detect:: Config < http:: DetectHttp > , Detect > for ConfigureHttpDetect {
217+ fn extract_param ( & self , detect : & Detect ) -> detect:: Config < http:: DetectHttp > {
218+ detect:: Config :: from_timeout ( detect. timeout )
219+ }
220+ }
221+
174222// === impl Http ===
175223
176224impl svc:: Param < http:: Version > for Http {
@@ -355,6 +403,35 @@ mod tests {
355403 . expect ( "should succeed" ) ;
356404 }
357405
406+ #[ tokio:: test( flavor = "current_thread" ) ]
407+ async fn hinted_http ( ) {
408+ let _trace = trace:: test:: trace_init ( ) ;
409+
410+ let target = Tls {
411+ client_addr : client_addr ( ) ,
412+ orig_dst_addr : orig_dst_addr ( ) ,
413+ permit : Permitted {
414+ protocol : Protocol :: Http1 ,
415+ labels : None . into_iter ( ) . collect ( ) ,
416+ tls : tls:: ConditionalServerTls :: Some ( tls:: ServerTls :: Established {
417+ client_id : Some ( client_id ( ) ) ,
418+ negotiated_protocol : None ,
419+ } ) ,
420+ } ,
421+ } ;
422+
423+ let ( ior, _) = io:: duplex ( 100 ) ;
424+
425+ inbound ( )
426+ . with_stack ( new_ok ( ) )
427+ . push_detect_http ( new_panic ( "tcp stack must not be used" ) )
428+ . into_inner ( )
429+ . new_service ( target)
430+ . oneshot ( ior)
431+ . await
432+ . expect ( "should succeed" ) ;
433+ }
434+
358435 fn client_id ( ) -> tls:: ClientId {
359436 "testsa.testns.serviceaccount.identity.linkerd.cluster.local"
360437 . parse ( )
0 commit comments