@@ -14,6 +14,7 @@ use linkerd_app_core::{
1414 Error , Infallible ,
1515} ;
1616use std:: { fmt:: Debug , time} ;
17+ use tracing:: info;
1718
1819#[ derive( Clone , Debug , PartialEq , Eq ) ]
1920pub ( crate ) struct Forward {
@@ -106,7 +107,6 @@ impl<N> Inbound<N> {
106107
107108 let detect_timeout = cfg. proxy . detect_protocol_timeout ;
108109 detect
109- . check_new_service :: < Tls , _ > ( )
110110 . push_switch (
111111 // Ensure that the connection is authorized before proceeding with protocol
112112 // detection.
@@ -135,12 +135,10 @@ impl<N> Inbound<N> {
135135 . push_on_service ( svc:: MapTargetLayer :: new ( io:: BoxedIo :: new) )
136136 . into_inner ( ) ,
137137 )
138- . check_new_service :: < ( tls:: ConditionalServerTls , T ) , _ > ( )
139138 . push ( tls:: NewDetectTls :: layer ( TlsParams {
140139 timeout : tls:: server:: Timeout ( detect_timeout) ,
141140 identity : rt. identity . clone ( ) ,
142141 } ) )
143- . check_new_service :: < T , I > ( )
144142 . push_switch (
145143 // If this port's policy indicates that authentication is not required and
146144 // detection should be skipped, use the TCP stack directly.
@@ -162,7 +160,6 @@ impl<N> Inbound<N> {
162160 . push_on_service ( svc:: MapTargetLayer :: new ( io:: BoxedIo :: new) )
163161 . into_inner ( ) ,
164162 )
165- . check_new_service :: < T , I > ( )
166163 . push_on_service ( svc:: BoxService :: layer ( ) )
167164 . push ( svc:: ArcNewService :: layer ( ) )
168165 } )
@@ -193,14 +190,39 @@ impl<N> Inbound<N> {
193190 . push ( transport:: metrics:: NewServer :: layer (
194191 rt. metrics . proxy . transport . clone ( ) ,
195192 ) )
196- . check_new_service :: < Http , io:: PrefixedIo < I > > ( )
197193 . push_switch (
198- |( http, Detect { tls, .. } ) | -> Result < _ , Infallible > {
199- match http {
200- Some ( http) => Ok ( svc:: Either :: A ( Http { http, tls } ) ) ,
194+ |( detected, Detect { tls, .. } ) | -> Result < _ , Infallible > {
195+ match detected {
196+ Ok ( Some ( http) ) => Ok ( svc:: Either :: A ( Http { http, tls } ) ) ,
197+ Ok ( None ) => Ok ( svc:: Either :: B ( tls) ) ,
201198 // When HTTP detection fails, forward the connection to the application as
202199 // an opaque TCP stream.
203- None => Ok ( svc:: Either :: B ( tls) ) ,
200+ Err ( timeout) => match tls. policy . protocol ( ) {
201+ Protocol :: Http1 => {
202+ // If the protocol was hinted to be HTTP/1.1 but detection
203+ // failed, we'll usually be handling HTTP/1, but we may actually
204+ // be handling HTTP/2 via protocol upgrade. Our options are:
205+ // handle the connection as HTTP/1, assuming it will be rare for
206+ // a proxy to initiate TLS, etc and not send the 16B of
207+ // connection header; or we can handle it as opaque--but there's
208+ // no chance the server will be able to handle the H2 protocol
209+ // upgrade. So, it seems best to assume it's HTTP/1 and let the
210+ // proxy handle the protocol error if we're in an edge case.
211+ info ! ( %timeout, "Handling connection as HTTP/1 due to policy" ) ;
212+ Ok ( svc:: Either :: A ( Http {
213+ http : http:: Version :: Http1 ,
214+ tls,
215+ } ) )
216+ }
217+ // Otherwise, the protocol hint must have been `Detect` or the
218+ // protocol was updated after detection was initiated, otherwise we
219+ // would have avoided detection below. Continue handling the
220+ // connection as if it were opaque.
221+ _ => {
222+ info ! ( %timeout, "Handling connection as opaque" ) ;
223+ Ok ( svc:: Either :: B ( tls) )
224+ }
225+ } ,
204226 }
205227 } ,
206228 svc:: stack ( forward)
@@ -212,16 +234,12 @@ impl<N> Inbound<N> {
212234 . push ( policy:: NewAuthorizeTcp :: layer ( rt. metrics . tcp_authz . clone ( ) ) )
213235 . into_inner ( ) ,
214236 )
215- . push ( svc:: ArcNewService :: layer ( ) )
216- . push_map_target ( detect:: allow_timeout)
217- . push ( detect:: NewDetectService :: layer ( ConfigureHttpDetect ) )
218- . check_new_service :: < Detect , I > ( ) ;
237+ . push ( detect:: NewDetectService :: layer ( ConfigureHttpDetect ) ) ;
219238
220239 http. push_on_service ( svc:: MapTargetLayer :: new ( io:: BoxedIo :: new) )
221240 . push ( transport:: metrics:: NewServer :: layer (
222241 rt. metrics . proxy . transport . clone ( ) ,
223242 ) )
224- . check_new_service :: < Http , I > ( )
225243 . push_switch (
226244 // If we have a protocol hint, skip detection and just used the hinted HTTP
227245 // version.
@@ -230,14 +248,20 @@ impl<N> Inbound<N> {
230248 Protocol :: Detect { timeout } => {
231249 return Ok ( svc:: Either :: B ( Detect { timeout, tls } ) ) ;
232250 }
233- Protocol :: Http1 => {
234- // HTTP/1 services may actually be transported over HTTP/2
235- // connections between proxies, so we have to do detection.
251+ // Meshed HTTP/1 services may actually be transported over HTTP/2 connections
252+ // between proxies, so we have to do detection.
253+ //
254+ // TODO(ver) outbound clients should hint this with ALPN so we don't
255+ // have to detect this situation.
256+ Protocol :: Http1 if tls. status . is_some ( ) => {
236257 return Ok ( svc:: Either :: B ( Detect {
237258 timeout : detect_timeout,
238259 tls,
239260 } ) ) ;
240261 }
262+ // Unmeshed services don't use protocol upgrading, so we can use the
263+ // hint without further detection.
264+ Protocol :: Http1 => http:: Version :: Http1 ,
241265 Protocol :: Http2 | Protocol :: Grpc => http:: Version :: H2 ,
242266 _ => unreachable ! ( "opaque protocols must not hit the HTTP stack" ) ,
243267 } ;
@@ -247,7 +271,6 @@ impl<N> Inbound<N> {
247271 )
248272 . push_on_service ( svc:: BoxService :: layer ( ) )
249273 . push ( svc:: ArcNewService :: layer ( ) )
250- . check_new_service :: < Tls , I > ( )
251274 } )
252275 }
253276}
0 commit comments