11//! Module that provides `h2c` server adapters.
2- //!
3- //! # What is `h2c`?
4- //!
5- //! `h2c` is a http1.1 upgrade token that allows us to accept http2 without
6- //! going through tls/alpn while also accepting regular http1.1 requests. Since,
7- //! our server does not do TLS there is no way to negotiate that an incoming
8- //! connection is going to speak http2 or http1.1 so we must default to http1.1.
9- //!
10- //! # How does it work?
11- //!
12- //! The `H2c` service gets called on every http request that arrives to the
13- //! server and checks if the request has an `upgrade` header set. If this
14- //! header is set to `h2c` then it will start the upgrade process. If this
15- //! header is not set the request continues normally without any upgrades.
16- //!
17- //! The upgrade process is quite simple, if the correct header value is set
18- //! the server will spawn a background task, return status code `101`
19- //! (switching protocols) and will set the same upgrade header with `h2c` as
20- //! the value.
21- //!
22- //! The background task will wait for `hyper::upgrade::on` to complete. At this
23- //! point when `on` completes it returns an `IO` object that we can read/write from.
24- //! We then pass this into hyper's low level server connection type and force http2.
25- //! This means from the point that the client gets back the upgrade headers and correct
26- //! status code the connection will be immediealty speaking http2 and thus the upgrade
27- //! is complete.
28- //!
29- //! ┌───────────────┐ upgrade:h2c ┌──────────────────┐
30- //! │ http::request ├────────────────────────►│ upgrade to http2 │
31- //! └─────┬─────────┘ └────────┬─────────┘
32- //! │ │
33- //! │ │
34- //! │ │
35- //! │ │
36- //! │ │
37- //! │ ┌─────────────────┐ │
38- //! └────────────►│call axum router │◄───────────┘
39- //! └─────────────────┘
402
413use std:: marker:: PhantomData ;
424use std:: pin:: Pin ;
@@ -45,44 +7,49 @@ use axum::body::Body;
457use bytes:: Bytes ;
468use http:: header;
479use http:: { Request , Response } ;
10+ use http_body_util:: BodyExt ;
4811use hyper_util:: rt:: { TokioExecutor , TokioIo } ;
4912use hyper:: server:: conn:: http2:: Builder as Http2Builder ;
5013use tonic:: transport:: server:: TcpConnectInfo ;
5114use tower:: Service ;
5215
16+ type BoxBody = http_body_util:: combinators:: BoxBody < Bytes , Box < dyn std:: error:: Error + Send + Sync > > ;
5317type BoxError = Box < dyn std:: error:: Error + Send + Sync > ;
5418
5519/// A `MakeService` adapter for [`H2c`] that injects connection
5620/// info into the request extensions.
57- #[ derive( Debug , Clone ) ]
58- pub struct H2cMaker < S , B > {
21+ #[ derive( Debug ) ]
22+ pub struct H2cMaker < S > {
5923 s : S ,
60- _pd : PhantomData < fn ( B ) > ,
6124}
6225
63- impl < S , B > H2cMaker < S , B > {
64- pub fn new ( s : S ) -> Self {
26+ impl < S > Clone for H2cMaker < S >
27+ where
28+ S : Clone ,
29+ {
30+ fn clone ( & self ) -> Self {
6531 Self {
66- s,
67- _pd : PhantomData ,
32+ s : self . s . clone ( ) ,
6833 }
6934 }
7035}
7136
72- impl < S , C , B > Service < & C > for H2cMaker < S , B >
37+ impl < S > H2cMaker < S > {
38+ pub fn new ( s : S ) -> Self {
39+ Self { s }
40+ }
41+ }
42+
43+ impl < S , C > Service < & C > for H2cMaker < S >
7344where
74- S : Service < Request < Body > , Response = Response < B > > + Clone + Send + ' static ,
45+ S : Service < Request < Body > > + Clone + Send + ' static ,
7546 S :: Future : Send + ' static ,
7647 S :: Error : Into < BoxError > + Sync + Send + ' static ,
7748 S :: Response : Send + ' static ,
7849 C : crate :: net:: Conn ,
79- B : http_body:: Body < Data = Bytes > + Send + ' static ,
80- B :: Error : Into < BoxError > + Sync + Send + ' static ,
8150{
82- type Response = H2c < S , B > ;
83-
51+ type Response = H2c < S > ;
8452 type Error = BoxError ;
85-
8653 type Future =
8754 Pin < Box < dyn std:: future:: Future < Output = Result < Self :: Response , Self :: Error > > + Send > > ;
8855
@@ -100,32 +67,41 @@ where
10067 Ok ( H2c {
10168 s,
10269 connect_info,
103- _pd : PhantomData ,
10470 } )
10571 } )
10672 }
10773}
10874
109- /// A service that can perform `h2c` upgrades and will
110- /// delegate calls to the inner service once a protocol
111- /// has been selected.
112- #[ derive( Debug , Clone ) ]
113- pub struct H2c < S , B > {
75+ /// A service that can perform `h2c` upgrades.
76+ #[ derive( Debug ) ]
77+ pub struct H2c < S > {
11478 s : S ,
11579 connect_info : TcpConnectInfo ,
116- _pd : PhantomData < fn ( B ) > ,
11780}
11881
119- impl < S , B > Service < Request < Body > > for H2c < S , B >
82+ impl < S > Clone for H2c < S >
83+ where
84+ S : Clone ,
85+ {
86+ fn clone ( & self ) -> Self {
87+ Self {
88+ s : self . s . clone ( ) ,
89+ connect_info : self . connect_info . clone ( ) ,
90+ }
91+ }
92+ }
93+
94+ // Service implementation for hyper 1.0's Incoming body type
95+ impl < S , B > Service < Request < hyper:: body:: Incoming > > for H2c < S >
12096where
12197 S : Service < Request < Body > , Response = Response < B > > + Clone + Send + ' static ,
12298 S :: Future : Send + ' static ,
12399 S :: Error : Into < BoxError > + Sync + Send + ' static ,
124100 S :: Response : Send + ' static ,
125101 B : http_body:: Body < Data = Bytes > + Send + ' static ,
126- B :: Error : Into < BoxError > + Sync + Send + ' static ,
102+ B :: Error : Into < BoxError > + Send + Sync + ' static ,
127103{
128- type Response = Response < Body > ;
104+ type Response = Response < BoxBody > ;
129105 type Error = BoxError ;
130106 type Future =
131107 Pin < Box < dyn std:: future:: Future < Output = Result < Self :: Response , Self :: Error > > + Send > > ;
@@ -137,27 +113,29 @@ where
137113 std:: task:: Poll :: Ready ( Ok ( ( ) ) )
138114 }
139115
140- fn call ( & mut self , mut req : Request < Body > ) -> Self :: Future {
116+ fn call ( & mut self , mut req : Request < hyper :: body :: Incoming > ) -> Self :: Future {
141117 let mut svc = self . s . clone ( ) ;
142118 let connect_info = self . connect_info . clone ( ) ;
143119
144120 Box :: pin ( async move {
145121 req. extensions_mut ( ) . insert ( connect_info. clone ( ) ) ;
146122
147- // Check if this request is a `h2c` upgrade, if it is not pass
148- // the request to the inner service, which in our case is the
149- // axum router.
123+ // Check if this request is a `h2c` upgrade
150124 if req. headers ( ) . get ( header:: UPGRADE ) != Some ( & http:: HeaderValue :: from_static ( "h2c" ) ) {
151- return svc
152- . call ( req)
153- . await
154- . map_err ( Into :: into) ;
125+ // Convert Incoming body to axum Body
126+ let ( parts, incoming) = req. into_parts ( ) ;
127+ let body = Body :: from_stream ( incoming) ;
128+ let req = Request :: from_parts ( parts, body) ;
129+
130+ let res = svc. call ( req) . await . map_err ( Into :: into) ?;
131+ // Box the body to erase type
132+ let ( parts, body) = res. into_parts ( ) ;
133+ return Ok ( Response :: from_parts ( parts, body. boxed ( ) ) ) ;
155134 }
156135
157136 tracing:: debug!( "Got a h2c upgrade request" ) ;
158137
159- // We got a h2c header so lets spawn a task that will wait for the
160- // upgrade to complete and start a http2 connection.
138+ // Spawn the upgrade handling
161139 tokio:: spawn ( async move {
162140 let upgraded_io = match hyper:: upgrade:: on ( & mut req) . await {
163141 Ok ( io) => TokioIo :: new ( io) ,
@@ -172,23 +150,20 @@ where
172150 let executor = TokioExecutor :: new ( ) ;
173151 let conn = Http2Builder :: new ( executor) ;
174152
175- // Create a service that handles incoming HTTP/2 requests
176- let svc = hyper:: service:: service_fn ( move |mut r : Request < hyper:: body:: Incoming > | {
177- r. extensions_mut ( ) . insert ( connect_info. clone ( ) ) ;
178- // Convert the axum service response
153+ // Create a service for HTTP/2
154+ let svc = hyper:: service:: service_fn ( move |r : Request < hyper:: body:: Incoming > | {
179155 let svc_clone = svc. clone ( ) ;
156+ let connect_info = connect_info. clone ( ) ;
180157 async move {
181- // Convert Request<Incoming> to Request<Body> for axum
182- let ( parts, body ) = r. into_parts ( ) ;
183- let body = Body :: from_stream ( body ) ;
184- let req = Request :: from_parts ( parts , body ) ;
158+ // Convert Request<Incoming> to Request<Body>
159+ let ( parts, incoming ) = r. into_parts ( ) ;
160+ let mut req = Request :: from_parts ( parts , Body :: from_stream ( incoming ) ) ;
161+ req. extensions_mut ( ) . insert ( connect_info ) ;
185162
186- svc_clone. call ( req) . await . map ( |res| {
187- // Convert Response<B> to Response<BoxBody>
188- let ( parts, body) = res. into_parts ( ) ;
189- let body = body. boxed_unsync ( ) ;
190- Response :: from_parts ( parts, body)
191- } ) . map_err ( |e| Box :: new ( e) as BoxError )
163+ let res = svc_clone. call ( req) . await . map_err ( |e| Box :: new ( e) as BoxError ) ?;
164+ // Box the body
165+ let ( parts, body) = res. into_parts ( ) ;
166+ Ok :: < _ , BoxError > ( Response :: from_parts ( parts, body. boxed ( ) ) )
192167 }
193168 } ) ;
194169
@@ -197,8 +172,8 @@ where
197172 }
198173 } ) ;
199174
200- // Reply that we are switching protocols to h2
201- let mut res = Response :: new ( Body :: empty ( ) ) ;
175+ // Return 101 Switching Protocols
176+ let mut res = Response :: new ( BoxBody :: default ( ) ) ;
202177 * res. status_mut ( ) = http:: StatusCode :: SWITCHING_PROTOCOLS ;
203178 res. headers_mut ( )
204179 . insert ( header:: UPGRADE , http:: HeaderValue :: from_static ( "h2c" ) ) ;
0 commit comments