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
///
@@ -46,8 +47,12 @@ pub struct Destination {
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.
@@ -259,6 +273,54 @@ impl Connected {
259
273
self
260
274
}
261
275
*/
276
+
277
+ // Don't public expose that `Connected` is `Clone`, unsure if we want to
278
+ // keep that contract...
279
+ pub ( super ) fn clone ( & self ) -> Connected {
280
+ Connected {
281
+ is_proxied : self . is_proxied ,
282
+ extra : self . extra . clone ( ) ,
283
+ }
284
+ }
285
+ }
286
+
287
+ // ===== impl Extra =====
288
+
289
+ impl Extra {
290
+ pub ( super ) fn set ( & self , res : & mut Response < :: Body > ) {
291
+ self . 0 . call ( res) ;
292
+ }
293
+ }
294
+
295
+ impl Clone for Extra {
296
+ fn clone ( & self ) -> Extra {
297
+ Extra ( self . 0 . clone_fn ( ) )
298
+ }
299
+ }
300
+
301
+ impl fmt:: Debug for Extra {
302
+ fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
303
+ f. debug_struct ( "Extra" )
304
+ . finish ( )
305
+ }
306
+ }
307
+
308
+ trait FnClone : Send + Sync {
309
+ fn clone_fn ( & self ) -> Box < FnClone > ;
310
+ fn call ( & self , res : & mut Response < :: Body > ) ;
311
+ }
312
+
313
+ impl < F > FnClone for F
314
+ where
315
+ F : Fn ( & mut Response < :: Body > ) + Clone + Send + Sync + ' static
316
+ {
317
+ fn clone_fn ( & self ) -> Box < FnClone > {
318
+ Box :: new ( self . clone ( ) )
319
+ }
320
+
321
+ fn call ( & self , res : & mut Response < :: Body > ) {
322
+ ( * self ) ( res)
323
+ }
262
324
}
263
325
264
326
#[ cfg( test) ]
@@ -436,14 +498,50 @@ mod http {
436
498
/// A connector for the `http` scheme.
437
499
///
438
500
/// Performs DNS resolution in a thread pool, and then connects over TCP.
501
+ ///
502
+ /// # Note
503
+ ///
504
+ /// Sets the [`HttpInfo`](HttpInfo) value on responses, which includes
505
+ /// transport information such as the remote socket address used.
439
506
#[ derive( Clone ) ]
440
507
pub struct HttpConnector {
441
508
executor : HttpConnectExecutor ,
442
509
enforce_http : bool ,
443
510
handle : Option < Handle > ,
444
511
keep_alive_timeout : Option < Duration > ,
445
- nodelay : bool ,
446
512
local_address : Option < IpAddr > ,
513
+ nodelay : bool ,
514
+ }
515
+
516
+ /// Extra information about the transport when an HttpConnector is used.
517
+ ///
518
+ /// # Example
519
+ ///
520
+ /// ```rust
521
+ /// use hyper::client::{Client, connect::HttpInfo};
522
+ /// use hyper::rt::Future;
523
+ ///
524
+ /// let client = Client::new();
525
+ ///
526
+ /// let fut = client.get("http://example.local".parse().unwrap())
527
+ /// .inspect(|resp| {
528
+ /// let info = resp
529
+ /// .extensions()
530
+ /// .get::<HttpInfo>()
531
+ /// .expect("HttpConnector sets HttpInfo");
532
+ ///
533
+ /// println!("remote addr = {}", info.remote_addr());
534
+ /// });
535
+ /// ```
536
+ ///
537
+ /// # Note
538
+ ///
539
+ /// If a different connector is used besides [`HttpConnector`](HttpConnector),
540
+ /// this value will not exist in the extensions. Consult that specific
541
+ /// connector to see what "extra" information it might provide to responses.
542
+ #[ derive( Clone , Debug ) ]
543
+ pub struct HttpInfo {
544
+ remote_addr : SocketAddr ,
447
545
}
448
546
449
547
impl HttpConnector {
@@ -600,6 +698,7 @@ mod http {
600
698
}
601
699
}
602
700
}
701
+
603
702
/// A Future representing work to connect to a URL.
604
703
#[ must_use = "futures do nothing unless polled" ]
605
704
pub struct HttpConnecting {
@@ -640,16 +739,12 @@ mod http {
640
739
}
641
740
} ,
642
741
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
- } ;
742
+ let addrs = try_ready ! ( future. poll( ) ) ;
743
+ state = State :: Connecting ( ConnectingTcp {
744
+ addrs : addrs,
745
+ local_addr : local_addr,
746
+ current : None ,
747
+ } ) ;
653
748
} ,
654
749
State :: Connecting ( ref mut c) => {
655
750
let sock = try_ready ! ( c. poll( & self . handle) ) ;
@@ -660,7 +755,13 @@ mod http {
660
755
661
756
sock. set_nodelay ( self . nodelay ) ?;
662
757
663
- return Ok ( Async :: Ready ( ( sock, Connected :: new ( ) ) ) ) ;
758
+ let extra = HttpInfo {
759
+ remote_addr : sock. peer_addr ( ) ?,
760
+ } ;
761
+ let connected = Connected :: new ( )
762
+ . extra ( extra) ;
763
+
764
+ return Ok ( Async :: Ready ( ( sock, connected) ) ) ;
664
765
} ,
665
766
State :: Error ( ref mut e) => return Err ( e. take ( ) . expect ( "polled more than once" ) ) ,
666
767
}
@@ -710,6 +811,13 @@ mod http {
710
811
}
711
812
}
712
813
814
+ impl HttpInfo {
815
+ /// Get the remote address of the transport used.
816
+ pub fn remote_addr ( & self ) -> SocketAddr {
817
+ self . remote_addr
818
+ }
819
+ }
820
+
713
821
// Make this Future unnameable outside of this crate.
714
822
mod http_connector {
715
823
use super :: * ;
0 commit comments