6
6
//! establishes connections over TCP.
7
7
//! - The [`Connect`](Connect) trait and related types to build custom connectors.
8
8
use std:: error:: Error as StdError ;
9
+ use std:: fmt;
9
10
use std:: mem;
10
11
11
12
use bytes:: { BufMut , BytesMut } ;
12
13
use futures:: Future ;
13
- use http:: { uri, Uri } ;
14
+ use http:: { uri, Response , Uri } ;
14
15
use tokio_io:: { AsyncRead , AsyncWrite } ;
15
16
16
- #[ cfg( feature = "runtime" ) ] pub use self :: http:: HttpConnector ;
17
+ #[ cfg( feature = "runtime" ) ] pub use self :: http:: { HttpConnector , HttpInfo } ;
17
18
18
19
/// Connect to a destination, returning an IO transport.
19
20
///
@@ -42,12 +43,16 @@ pub struct Destination {
42
43
///
43
44
/// This can be used to inform recipients about things like if ALPN
44
45
/// was used, or if connected to an HTTP proxy.
45
- #[ derive( Debug ) ]
46
+ #[ derive( Clone , Debug ) ]
46
47
pub struct Connected {
47
48
//alpn: Alpn,
48
49
pub ( super ) is_proxied : bool ,
50
+ pub ( super ) extra : Option < Extra > ,
49
51
}
50
52
53
+ pub ( super ) struct Extra ( Box < FnClone > ) ;
54
+
55
+
51
56
/*TODO: when HTTP1 Upgrades to H2 are added, this will be needed
52
57
#[derive(Debug)]
53
58
pub(super) enum Alpn {
@@ -234,8 +239,8 @@ impl Connected {
234
239
/// Create new `Connected` type with empty metadata.
235
240
pub fn new ( ) -> Connected {
236
241
Connected {
237
- //alpn: Alpn::Http1,
238
242
is_proxied : false ,
243
+ extra : None ,
239
244
}
240
245
}
241
246
@@ -251,6 +256,15 @@ impl Connected {
251
256
self
252
257
}
253
258
259
+ /// Set extra connection information to be set in the extensions of every `Response`.
260
+ pub fn extra < T : Clone + Send + Sync + ' static > ( mut self , extra : T ) -> Connected {
261
+ self . extra = Some ( Extra ( Box :: new ( move |res : & mut Response < :: Body > | {
262
+ let e = extra. clone ( ) ;
263
+ res. extensions_mut ( ) . insert ( e) ;
264
+ } ) ) ) ;
265
+ self
266
+ }
267
+
254
268
/*
255
269
/// Set that the connected transport negotiated HTTP/2 as it's
256
270
/// next protocol.
@@ -261,6 +275,45 @@ impl Connected {
261
275
*/
262
276
}
263
277
278
+ // ===== impl Extra =====
279
+
280
+ impl Extra {
281
+ pub ( super ) fn set ( & self , res : & mut Response < :: Body > ) {
282
+ self . 0 . call ( res) ;
283
+ }
284
+ }
285
+
286
+ impl Clone for Extra {
287
+ fn clone ( & self ) -> Extra {
288
+ Extra ( self . 0 . clone_fn ( ) )
289
+ }
290
+ }
291
+
292
+ impl fmt:: Debug for Extra {
293
+ fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
294
+ f. debug_struct ( "Extra" )
295
+ . finish ( )
296
+ }
297
+ }
298
+
299
+ trait FnClone : Send + Sync {
300
+ fn clone_fn ( & self ) -> Box < FnClone > ;
301
+ fn call ( & self , res : & mut Response < :: Body > ) ;
302
+ }
303
+
304
+ impl < F > FnClone for F
305
+ where
306
+ F : Fn ( & mut Response < :: Body > ) + Clone + Send + Sync + ' static
307
+ {
308
+ fn clone_fn ( & self ) -> Box < FnClone > {
309
+ Box :: new ( self . clone ( ) )
310
+ }
311
+
312
+ fn call ( & self , res : & mut Response < :: Body > ) {
313
+ ( * self ) ( res)
314
+ }
315
+ }
316
+
264
317
#[ cfg( test) ]
265
318
mod tests {
266
319
use super :: Destination ;
@@ -436,14 +489,50 @@ mod http {
436
489
/// A connector for the `http` scheme.
437
490
///
438
491
/// Performs DNS resolution in a thread pool, and then connects over TCP.
492
+ ///
493
+ /// # Note
494
+ ///
495
+ /// Sets the [`HttpInfo`](HttpInfo) value on responses, which includes
496
+ /// transport information such as the remote socket address used.
439
497
#[ derive( Clone ) ]
440
498
pub struct HttpConnector {
441
499
executor : HttpConnectExecutor ,
442
500
enforce_http : bool ,
443
501
handle : Option < Handle > ,
444
502
keep_alive_timeout : Option < Duration > ,
445
- nodelay : bool ,
446
503
local_address : Option < IpAddr > ,
504
+ nodelay : bool ,
505
+ }
506
+
507
+ /// Extra information about the transport when an HttpConnector is used.
508
+ ///
509
+ /// # Example
510
+ ///
511
+ /// ```rust
512
+ /// use hyper::client::{Client, connect::HttpInfo};
513
+ /// use hyper::rt::Future;
514
+ ///
515
+ /// let client = Client::new();
516
+ ///
517
+ /// let fut = client.get("http://example.local".parse().unwrap())
518
+ /// .inspect(|resp| {
519
+ /// let info = resp
520
+ /// .extensions()
521
+ /// .get::<HttpInfo>()
522
+ /// .expect("HttpConnector sets HttpInfo");
523
+ ///
524
+ /// println!("remote addr = {}", info.remote_addr());
525
+ /// });
526
+ /// ```
527
+ ///
528
+ /// # Note
529
+ ///
530
+ /// If a different connector is used besides [`HttpConnector`](HttpConnector),
531
+ /// this value will not exist in the extensions. Consult that specific
532
+ /// connector to see what "extra" information it might provide to responses.
533
+ #[ derive( Clone , Debug ) ]
534
+ pub struct HttpInfo {
535
+ remote_addr : SocketAddr ,
447
536
}
448
537
449
538
impl HttpConnector {
@@ -600,6 +689,7 @@ mod http {
600
689
}
601
690
}
602
691
}
692
+
603
693
/// A Future representing work to connect to a URL.
604
694
#[ must_use = "futures do nothing unless polled" ]
605
695
pub struct HttpConnecting {
@@ -640,16 +730,12 @@ mod http {
640
730
}
641
731
} ,
642
732
State :: Resolving ( ref mut future, local_addr) => {
643
- match try!( future. poll ( ) ) {
644
- Async :: NotReady => return Ok ( Async :: NotReady ) ,
645
- Async :: Ready ( addrs) => {
646
- state = State :: Connecting ( ConnectingTcp {
647
- addrs : addrs,
648
- local_addr : local_addr,
649
- current : None ,
650
- } )
651
- }
652
- } ;
733
+ let addrs = try_ready ! ( future. poll( ) ) ;
734
+ state = State :: Connecting ( ConnectingTcp {
735
+ addrs : addrs,
736
+ local_addr : local_addr,
737
+ current : None ,
738
+ } ) ;
653
739
} ,
654
740
State :: Connecting ( ref mut c) => {
655
741
let sock = try_ready ! ( c. poll( & self . handle) ) ;
@@ -660,7 +746,13 @@ mod http {
660
746
661
747
sock. set_nodelay ( self . nodelay ) ?;
662
748
663
- return Ok ( Async :: Ready ( ( sock, Connected :: new ( ) ) ) ) ;
749
+ let extra = HttpInfo {
750
+ remote_addr : sock. peer_addr ( ) ?,
751
+ } ;
752
+ let connected = Connected :: new ( )
753
+ . extra ( extra) ;
754
+
755
+ return Ok ( Async :: Ready ( ( sock, connected) ) ) ;
664
756
} ,
665
757
State :: Error ( ref mut e) => return Err ( e. take ( ) . expect ( "polled more than once" ) ) ,
666
758
}
@@ -710,6 +802,13 @@ mod http {
710
802
}
711
803
}
712
804
805
+ impl HttpInfo {
806
+ /// Get the remote address of the transport used.
807
+ pub fn remote_addr ( & self ) -> SocketAddr {
808
+ self . remote_addr
809
+ }
810
+ }
811
+
713
812
// Make this Future unnameable outside of this crate.
714
813
mod http_connector {
715
814
use super :: * ;
0 commit comments