@@ -86,12 +86,12 @@ use futures::{Async, Future, Poll};
86
86
use futures:: future:: { self , Either , Executor } ;
87
87
use futures:: sync:: oneshot;
88
88
use http:: { Method , Request , Response , Uri , Version } ;
89
- use http:: header:: { Entry , HeaderValue , HOST } ;
89
+ use http:: header:: { HeaderValue , HOST } ;
90
90
use http:: uri:: Scheme ;
91
91
92
92
use body:: { Body , Payload } ;
93
93
use common:: { Exec , lazy as hyper_lazy, Lazy } ;
94
- use self :: connect:: { Connect , Connected , Destination } ;
94
+ use self :: connect:: { Alpn , Connect , Connected , Destination } ;
95
95
use self :: pool:: { Key as PoolKey , Pool , Poolable , Pooled , Reservation } ;
96
96
97
97
#[ cfg( feature = "runtime" ) ] pub use self :: connect:: HttpConnector ;
@@ -192,23 +192,19 @@ where C: Connect + Sync + 'static,
192
192
193
193
/// Send a constructed Request using this Client.
194
194
pub fn request ( & self , mut req : Request < B > ) -> ResponseFuture {
195
- let is_http_11 = self . ver == Ver :: Http1 && match req. version ( ) {
196
- Version :: HTTP_11 => true ,
197
- Version :: HTTP_10 => false ,
198
- other => {
195
+ let is_http_connect = req. method ( ) == & Method :: CONNECT ;
196
+ match req. version ( ) {
197
+ Version :: HTTP_11 => ( ) ,
198
+ Version :: HTTP_10 => if is_http_connect {
199
+ debug ! ( "CONNECT is not allowed for HTTP/1.0" ) ;
200
+ return ResponseFuture :: new ( Box :: new ( future:: err ( :: Error :: new_user_unsupported_request_method ( ) ) ) ) ;
201
+ } ,
202
+ other => if self . ver != Ver :: Http2 {
199
203
error ! ( "Request has unsupported version \" {:?}\" " , other) ;
200
204
return ResponseFuture :: new ( Box :: new ( future:: err ( :: Error :: new_user_unsupported_version ( ) ) ) ) ;
201
205
}
202
206
} ;
203
207
204
- let is_http_connect = req. method ( ) == & Method :: CONNECT ;
205
-
206
- if !is_http_11 && is_http_connect {
207
- debug ! ( "client does not support CONNECT requests for {:?}" , req. version( ) ) ;
208
- return ResponseFuture :: new ( Box :: new ( future:: err ( :: Error :: new_user_unsupported_request_method ( ) ) ) ) ;
209
- }
210
-
211
-
212
208
let uri = req. uri ( ) . clone ( ) ;
213
209
let domain = match ( uri. scheme_part ( ) , uri. authority_part ( ) ) {
214
210
( Some ( scheme) , Some ( auth) ) => {
@@ -233,21 +229,7 @@ where C: Connect + Sync + 'static,
233
229
}
234
230
} ;
235
231
236
- if self . set_host && self . ver == Ver :: Http1 {
237
- if let Entry :: Vacant ( entry) = req. headers_mut ( ) . entry ( HOST ) . expect ( "HOST is always valid header name" ) {
238
- let hostname = uri. host ( ) . expect ( "authority implies host" ) ;
239
- let host = if let Some ( port) = uri. port ( ) {
240
- let s = format ! ( "{}:{}" , hostname, port) ;
241
- HeaderValue :: from_str ( & s)
242
- } else {
243
- HeaderValue :: from_str ( hostname)
244
- } . expect ( "uri host is valid header value" ) ;
245
- entry. insert ( host) ;
246
- }
247
- }
248
-
249
-
250
- let pool_key = ( Arc :: new ( domain. to_string ( ) ) , self . ver ) ;
232
+ let pool_key = Arc :: new ( domain. to_string ( ) ) ;
251
233
ResponseFuture :: new ( Box :: new ( self . retryably_send_request ( req, pool_key) ) )
252
234
}
253
235
@@ -283,23 +265,38 @@ where C: Connect + Sync + 'static,
283
265
fn send_request ( & self , mut req : Request < B > , pool_key : PoolKey ) -> impl Future < Item =Response < Body > , Error =ClientError < B > > {
284
266
let conn = self . connection_for ( req. uri ( ) . clone ( ) , pool_key) ;
285
267
286
- let ver = self . ver ;
268
+ let set_host = self . set_host ;
287
269
let executor = self . executor . clone ( ) ;
288
270
conn. and_then ( move |mut pooled| {
289
- if ver == Ver :: Http1 {
290
- // CONNECT always sends origin-form, so check it first...
271
+ if pooled. is_http1 ( ) {
272
+ if set_host {
273
+ let uri = req. uri ( ) . clone ( ) ;
274
+ req
275
+ . headers_mut ( )
276
+ . entry ( HOST )
277
+ . expect ( "HOST is always valid header name" )
278
+ . or_insert_with ( || {
279
+ let hostname = uri. host ( ) . expect ( "authority implies host" ) ;
280
+ if let Some ( port) = uri. port ( ) {
281
+ let s = format ! ( "{}:{}" , hostname, port) ;
282
+ HeaderValue :: from_str ( & s)
283
+ } else {
284
+ HeaderValue :: from_str ( hostname)
285
+ } . expect ( "uri host is valid header value" )
286
+ } ) ;
287
+ }
288
+
289
+ // CONNECT always sends authority-form, so check it first...
291
290
if req. method ( ) == & Method :: CONNECT {
292
291
authority_form ( req. uri_mut ( ) ) ;
293
292
} else if pooled. conn_info . is_proxied {
294
293
absolute_form ( req. uri_mut ( ) ) ;
295
294
} else {
296
295
origin_form ( req. uri_mut ( ) ) ;
297
296
} ;
298
- } else {
299
- debug_assert ! (
300
- req. method( ) != & Method :: CONNECT ,
301
- "Client should have returned Error for HTTP2 CONNECT"
302
- ) ;
297
+ } else if req. method ( ) == & Method :: CONNECT {
298
+ debug ! ( "client does not support CONNECT requests over HTTP2" ) ;
299
+ return Either :: A ( future:: err ( ClientError :: Normal ( :: Error :: new_user_unsupported_request_method ( ) ) ) ) ;
303
300
}
304
301
305
302
let fut = pooled. send_request_retryable ( req)
@@ -322,10 +319,10 @@ where C: Connect + Sync + 'static,
322
319
// To counteract this, we must check if our senders 'want' channel
323
320
// has been closed after having tried to send. If so, error out...
324
321
if pooled. is_closed ( ) {
325
- return Either :: A ( fut) ;
322
+ return Either :: B ( Either :: A ( fut) ) ;
326
323
}
327
324
328
- Either :: B ( fut
325
+ Either :: B ( Either :: B ( fut
329
326
. and_then ( move |mut res| {
330
327
// If pooled is HTTP/2, we can toss this reference immediately.
331
328
//
@@ -337,7 +334,7 @@ where C: Connect + Sync + 'static,
337
334
// for a new request to start.
338
335
//
339
336
// It won't be ready if there is a body to stream.
340
- if ver == Ver :: Http2 || !pooled. is_pool_enabled ( ) || pooled. is_ready ( ) {
337
+ if pooled . is_http2 ( ) || !pooled. is_pool_enabled ( ) || pooled. is_ready ( ) {
341
338
drop ( pooled) ;
342
339
} else if !res. body ( ) . is_end_stream ( ) {
343
340
let ( delayed_tx, delayed_rx) = oneshot:: channel ( ) ;
@@ -370,7 +367,7 @@ where C: Connect + Sync + 'static,
370
367
}
371
368
}
372
369
Ok ( res)
373
- } ) )
370
+ } ) ) )
374
371
} )
375
372
}
376
373
@@ -463,8 +460,9 @@ where C: Connect + Sync + 'static,
463
460
let pool = self . pool . clone ( ) ;
464
461
let h1_writev = self . h1_writev ;
465
462
let h1_title_case_headers = self . h1_title_case_headers ;
463
+ let ver = self . ver ;
464
+ let is_ver_h2 = self . ver == Ver :: Http2 ;
466
465
let connector = self . connector . clone ( ) ;
467
- let ver = pool_key. 1 ;
468
466
let dst = Destination {
469
467
uri,
470
468
} ;
@@ -474,7 +472,7 @@ where C: Connect + Sync + 'static,
474
472
// If the pool_key is for HTTP/2, and there is already a
475
473
// connection being estabalished, then this can't take a
476
474
// second lock. The "connect_to" future is Canceled.
477
- let connecting = match pool. connecting ( & pool_key) {
475
+ let connecting = match pool. connecting ( & pool_key, ver ) {
478
476
Some ( lock) => lock,
479
477
None => {
480
478
let canceled = :: Error :: new_canceled ( Some ( "HTTP/2 connection in progress" ) ) ;
@@ -484,11 +482,31 @@ where C: Connect + Sync + 'static,
484
482
Either :: A ( connector. connect ( dst)
485
483
. map_err ( :: Error :: new_connect)
486
484
. and_then ( move |( io, connected) | {
487
- conn:: Builder :: new ( )
485
+ // If ALPN is h2 and we aren't http2_only already,
486
+ // then we need to convert our pool checkout into
487
+ // a single HTTP2 one.
488
+ let connecting = if connected. alpn == Alpn :: H2 && !is_ver_h2 {
489
+ match connecting. alpn_h2 ( & pool) {
490
+ Some ( lock) => {
491
+ trace ! ( "ALPN negotiated h2, updating pool" ) ;
492
+ lock
493
+ } ,
494
+ None => {
495
+ // Another connection has already upgraded,
496
+ // the pool checkout should finish up for us.
497
+ let canceled = :: Error :: new_canceled ( Some ( "ALPN upgraded to HTTP/2" ) ) ;
498
+ return Either :: B ( future:: err ( canceled) ) ;
499
+ }
500
+ }
501
+ } else {
502
+ connecting
503
+ } ;
504
+ let is_h2 = is_ver_h2 || connected. alpn == Alpn :: H2 ;
505
+ Either :: A ( conn:: Builder :: new ( )
488
506
. exec ( executor. clone ( ) )
489
507
. h1_writev ( h1_writev)
490
508
. h1_title_case_headers ( h1_title_case_headers)
491
- . http2_only ( pool_key . 1 == Ver :: Http2 )
509
+ . http2_only ( is_h2 )
492
510
. handshake ( io)
493
511
. and_then ( move |( tx, conn) | {
494
512
let bg = executor. execute ( conn. map_err ( |e| {
@@ -509,12 +527,13 @@ where C: Connect + Sync + 'static,
509
527
. map ( move |tx| {
510
528
pool. pooled ( connecting, PoolClient {
511
529
conn_info : connected,
512
- tx : match ver {
513
- Ver :: Http1 => PoolTx :: Http1 ( tx) ,
514
- Ver :: Http2 => PoolTx :: Http2 ( tx. into_http2 ( ) ) ,
530
+ tx : if is_h2 {
531
+ PoolTx :: Http2 ( tx. into_http2 ( ) )
532
+ } else {
533
+ PoolTx :: Http1 ( tx)
515
534
} ,
516
535
} )
517
- } )
536
+ } ) )
518
537
} ) )
519
538
} )
520
539
}
@@ -591,6 +610,17 @@ impl<B> PoolClient<B> {
591
610
}
592
611
}
593
612
613
+ fn is_http1 ( & self ) -> bool {
614
+ !self . is_http2 ( )
615
+ }
616
+
617
+ fn is_http2 ( & self ) -> bool {
618
+ match self . tx {
619
+ PoolTx :: Http1 ( _) => false ,
620
+ PoolTx :: Http2 ( _) => true ,
621
+ }
622
+ }
623
+
594
624
fn is_ready ( & self ) -> bool {
595
625
match self . tx {
596
626
PoolTx :: Http1 ( ref tx) => tx. is_ready ( ) ,
@@ -650,6 +680,10 @@ where
650
680
}
651
681
}
652
682
}
683
+
684
+ fn can_share ( & self ) -> bool {
685
+ self . is_http2 ( )
686
+ }
653
687
}
654
688
655
689
// FIXME: allow() required due to `impl Trait` leaking types to this lint
0 commit comments