1
+ //! The `Connect` trait, and supporting types.
2
+ //!
3
+ //! This module contains:
4
+ //!
5
+ //! - A default [`HttpConnector`](HttpConnector) that does DNS resolution and
6
+ //! establishes connections over TCP.
7
+ //! - The [`Connect`](Connect) trait and related types to build custom connectors.
1
8
use std:: error:: Error as StdError ;
2
9
use std:: fmt;
3
10
use std:: io;
@@ -14,38 +21,121 @@ use http::uri::Scheme;
14
21
use tokio_io:: { AsyncRead , AsyncWrite } ;
15
22
use tokio:: reactor:: Handle ;
16
23
use tokio:: net:: { TcpStream , TcpStreamNew } ;
17
- use tokio_service:: Service ;
18
24
19
25
use super :: dns;
26
+ use self :: http_connector:: HttpConnectorBlockingTask ;
20
27
21
- /// A connector creates an Io to a remote address. .
28
+ /// Connect to a destination, returning an IO transport .
22
29
///
23
- /// This trait is not implemented directly, and only exists to make
24
- /// the intent clearer. A connector should implement `Service` with
25
- /// `Request=Uri` and `Response: Io` instead.
26
- pub trait Connect : Service < Request =Uri , Error =io:: Error > + ' static {
27
- /// The connected Io Stream.
28
- type Output : AsyncRead + AsyncWrite + ' static ;
29
- /// A Future that will resolve to the connected Stream.
30
- type Future : Future < Item =Self :: Output , Error =io:: Error > + ' static ;
31
- /// Connect to a remote address.
32
- fn connect ( & self , Uri ) -> <Self as Connect >:: Future ;
30
+ /// A connector receives a [`Destination`](Destination) describing how a
31
+ /// connection should be estabilished, and returns a `Future` of the
32
+ /// ready connection.
33
+ pub trait Connect {
34
+ /// The connected IO Stream.
35
+ type Transport : AsyncRead + AsyncWrite + ' static ;
36
+ /// An error occured when trying to connect.
37
+ type Error ;
38
+ /// A Future that will resolve to the connected Transport.
39
+ type Future : Future < Item =( Self :: Transport , Connected ) , Error =Self :: Error > ;
40
+ /// Connect to a destination.
41
+ fn connect ( & self , dst : Destination ) -> Self :: Future ;
33
42
}
34
43
35
- impl < T > Connect for T
36
- where T : Service < Request =Uri , Error =io:: Error > + ' static ,
37
- T :: Response : AsyncRead + AsyncWrite ,
38
- T :: Future : Future < Error =io:: Error > ,
39
- {
40
- type Output = T :: Response ;
41
- type Future = T :: Future ;
44
+ /// A set of properties to describe where and how to try to connect.
45
+ #[ derive( Debug ) ]
46
+ pub struct Destination {
47
+ //pub(super) alpn: Alpn,
48
+ pub ( super ) uri : Uri ,
49
+ }
50
+
51
+ /// Extra information about the connected transport.
52
+ ///
53
+ /// This can be used to inform recipients about things like if ALPN
54
+ /// was used, or if connected to an HTTP proxy.
55
+ #[ derive( Debug ) ]
56
+ pub struct Connected {
57
+ //alpn: Alpn,
58
+ pub ( super ) is_proxied : bool ,
59
+ }
60
+
61
+ /*TODO: when HTTP1 Upgrades to H2 are added, this will be needed
62
+ #[derive(Debug)]
63
+ pub(super) enum Alpn {
64
+ Http1,
65
+ //H2,
66
+ //Http1OrH2
67
+ }
68
+ */
69
+
70
+ impl Destination {
71
+ /// Get the protocol scheme.
72
+ #[ inline]
73
+ pub fn scheme ( & self ) -> & str {
74
+ self . uri
75
+ . scheme_part ( )
76
+ . expect ( "destination uri has scheme" )
77
+ . as_str ( )
78
+ }
79
+
80
+ /// Get the hostname.
81
+ #[ inline]
82
+ pub fn host ( & self ) -> & str {
83
+ self . uri
84
+ . host ( )
85
+ . expect ( "destination uri has host" )
86
+ }
87
+
88
+ /// Get the port, if specified.
89
+ #[ inline]
90
+ pub fn port ( & self ) -> Option < u16 > {
91
+ self . uri . port ( )
92
+ }
93
+
94
+ /*
95
+ /// Returns whether this connection must negotiate HTTP/2 via ALPN.
96
+ pub fn must_h2(&self) -> bool {
97
+ match self.alpn {
98
+ Alpn::Http1 => false,
99
+ Alpn::H2 => true,
100
+ }
101
+ }
102
+ */
103
+ }
42
104
43
- fn connect ( & self , url : Uri ) -> <Self as Connect >:: Future {
44
- self . call ( url)
105
+ impl Connected {
106
+ /// Create new `Connected` type with empty metadata.
107
+ pub fn new ( ) -> Connected {
108
+ Connected {
109
+ //alpn: Alpn::Http1,
110
+ is_proxied : false ,
111
+ }
45
112
}
113
+
114
+ /// Set whether the connected transport is to an HTTP proxy.
115
+ ///
116
+ /// This setting will affect if HTTP/1 requests written on the transport
117
+ /// will have the request-target in absolute-form or origin-form (such as
118
+ /// `GET http://hyper.rs/guide HTTP/1.1` or `GET /guide HTTP/1.1`).
119
+ ///
120
+ /// Default is `false`.
121
+ pub fn proxy ( mut self , is_proxied : bool ) -> Connected {
122
+ self . is_proxied = is_proxied;
123
+ self
124
+ }
125
+
126
+ /*
127
+ /// Set that the connected transport negotiated HTTP/2 as it's
128
+ /// next protocol.
129
+ pub fn h2(mut self) -> Connected {
130
+ self.alpn = Alpn::H2;
131
+ self
132
+ }
133
+ */
46
134
}
47
135
48
136
/// A connector for the `http` scheme.
137
+ ///
138
+ /// Performs DNS resolution in a thread pool, and then connects over TCP.
49
139
#[ derive( Clone ) ]
50
140
pub struct HttpConnector {
51
141
executor : HttpConnectExecutor ,
@@ -109,30 +199,29 @@ impl fmt::Debug for HttpConnector {
109
199
}
110
200
}
111
201
112
- impl Service for HttpConnector {
113
- type Request = Uri ;
114
- type Response = TcpStream ;
202
+ impl Connect for HttpConnector {
203
+ type Transport = TcpStream ;
115
204
type Error = io:: Error ;
116
205
type Future = HttpConnecting ;
117
206
118
- fn call ( & self , uri : Uri ) -> Self :: Future {
119
- trace ! ( "Http::connect({:?})" , uri) ;
207
+ fn connect ( & self , dst : Destination ) -> Self :: Future {
208
+ trace ! ( "Http::connect({:?})" , dst . uri) ;
120
209
121
210
if self . enforce_http {
122
- if uri. scheme_part ( ) != Some ( & Scheme :: HTTP ) {
211
+ if dst . uri . scheme_part ( ) != Some ( & Scheme :: HTTP ) {
123
212
return invalid_url ( InvalidUrl :: NotHttp , & self . handle ) ;
124
213
}
125
- } else if uri. scheme_part ( ) . is_none ( ) {
214
+ } else if dst . uri . scheme_part ( ) . is_none ( ) {
126
215
return invalid_url ( InvalidUrl :: MissingScheme , & self . handle ) ;
127
216
}
128
217
129
- let host = match uri. host ( ) {
218
+ let host = match dst . uri . host ( ) {
130
219
Some ( s) => s,
131
220
None => return invalid_url ( InvalidUrl :: MissingAuthority , & self . handle ) ,
132
221
} ;
133
- let port = match uri. port ( ) {
222
+ let port = match dst . uri . port ( ) {
134
223
Some ( port) => port,
135
- None => if uri. scheme_part ( ) == Some ( & Scheme :: HTTPS ) { 443 } else { 80 } ,
224
+ None => if dst . uri . scheme_part ( ) == Some ( & Scheme :: HTTPS ) { 443 } else { 80 } ,
136
225
} ;
137
226
138
227
HttpConnecting {
@@ -191,7 +280,7 @@ enum State {
191
280
}
192
281
193
282
impl Future for HttpConnecting {
194
- type Item = TcpStream ;
283
+ type Item = ( TcpStream , Connected ) ;
195
284
type Error = io:: Error ;
196
285
197
286
fn poll ( & mut self ) -> Poll < Self :: Item , Self :: Error > {
@@ -230,7 +319,7 @@ impl Future for HttpConnecting {
230
319
sock. set_keepalive ( Some ( dur) ) ?;
231
320
}
232
321
233
- return Ok ( Async :: Ready ( sock) ) ;
322
+ return Ok ( Async :: Ready ( ( sock, Connected :: new ( ) ) ) ) ;
234
323
} ,
235
324
State :: Error ( ref mut e) => return Err ( e. take ( ) . expect ( "polled more than once" ) ) ,
236
325
}
@@ -279,23 +368,27 @@ impl ConnectingTcp {
279
368
}
280
369
}
281
370
282
- /// Blocking task to be executed on a thread pool.
283
- pub struct HttpConnectorBlockingTask {
284
- work : oneshot:: Execute < dns:: Work >
285
- }
371
+ // Make this Future unnameable outside of this crate.
372
+ mod http_connector {
373
+ use super :: * ;
374
+ // Blocking task to be executed on a thread pool.
375
+ pub struct HttpConnectorBlockingTask {
376
+ pub ( super ) work : oneshot:: Execute < dns:: Work >
377
+ }
286
378
287
- impl fmt:: Debug for HttpConnectorBlockingTask {
288
- fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
289
- f. pad ( "HttpConnectorBlockingTask" )
379
+ impl fmt:: Debug for HttpConnectorBlockingTask {
380
+ fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
381
+ f. pad ( "HttpConnectorBlockingTask" )
382
+ }
290
383
}
291
- }
292
384
293
- impl Future for HttpConnectorBlockingTask {
294
- type Item = ( ) ;
295
- type Error = ( ) ;
385
+ impl Future for HttpConnectorBlockingTask {
386
+ type Item = ( ) ;
387
+ type Error = ( ) ;
296
388
297
- fn poll ( & mut self ) -> Poll < ( ) , ( ) > {
298
- self . work . poll ( )
389
+ fn poll ( & mut self ) -> Poll < ( ) , ( ) > {
390
+ self . work . poll ( )
391
+ }
299
392
}
300
393
}
301
394
@@ -311,35 +404,45 @@ impl Executor<oneshot::Execute<dns::Work>> for HttpConnectExecutor {
311
404
312
405
#[ cfg( test) ]
313
406
mod tests {
407
+ #![ allow( deprecated) ]
314
408
use std:: io;
315
409
use tokio:: reactor:: Core ;
316
- use super :: { Connect , HttpConnector } ;
410
+ use super :: { Connect , Destination , HttpConnector } ;
317
411
318
412
#[ test]
319
413
fn test_errors_missing_authority ( ) {
320
414
let mut core = Core :: new ( ) . unwrap ( ) ;
321
- let url = "/foo/bar?baz" . parse ( ) . unwrap ( ) ;
415
+ let uri = "/foo/bar?baz" . parse ( ) . unwrap ( ) ;
416
+ let dst = Destination {
417
+ uri,
418
+ } ;
322
419
let connector = HttpConnector :: new ( 1 , & core. handle ( ) ) ;
323
420
324
- assert_eq ! ( core. run( connector. connect( url ) ) . unwrap_err( ) . kind( ) , io:: ErrorKind :: InvalidInput ) ;
421
+ assert_eq ! ( core. run( connector. connect( dst ) ) . unwrap_err( ) . kind( ) , io:: ErrorKind :: InvalidInput ) ;
325
422
}
326
423
327
424
#[ test]
328
425
fn test_errors_enforce_http ( ) {
329
426
let mut core = Core :: new ( ) . unwrap ( ) ;
330
- let url = "https://example.domain/foo/bar?baz" . parse ( ) . unwrap ( ) ;
427
+ let uri = "https://example.domain/foo/bar?baz" . parse ( ) . unwrap ( ) ;
428
+ let dst = Destination {
429
+ uri,
430
+ } ;
331
431
let connector = HttpConnector :: new ( 1 , & core. handle ( ) ) ;
332
432
333
- assert_eq ! ( core. run( connector. connect( url ) ) . unwrap_err( ) . kind( ) , io:: ErrorKind :: InvalidInput ) ;
433
+ assert_eq ! ( core. run( connector. connect( dst ) ) . unwrap_err( ) . kind( ) , io:: ErrorKind :: InvalidInput ) ;
334
434
}
335
435
336
436
337
437
#[ test]
338
438
fn test_errors_missing_scheme ( ) {
339
439
let mut core = Core :: new ( ) . unwrap ( ) ;
340
- let url = "example.domain" . parse ( ) . unwrap ( ) ;
440
+ let uri = "example.domain" . parse ( ) . unwrap ( ) ;
441
+ let dst = Destination {
442
+ uri,
443
+ } ;
341
444
let connector = HttpConnector :: new ( 1 , & core. handle ( ) ) ;
342
445
343
- assert_eq ! ( core. run( connector. connect( url ) ) . unwrap_err( ) . kind( ) , io:: ErrorKind :: InvalidInput ) ;
446
+ assert_eq ! ( core. run( connector. connect( dst ) ) . unwrap_err( ) . kind( ) , io:: ErrorKind :: InvalidInput ) ;
344
447
}
345
448
}
0 commit comments