hyper_util_wasm/client/legacy/connect/
mod.rs

1//! Connectors used by the `Client`.
2//!
3//! This module contains:
4//!
5//! - A default [`HttpConnector`][] that does DNS resolution and establishes
6//!   connections over TCP.
7//! - Types to build custom connectors.
8//!
9//! # Connectors
10//!
11//! A "connector" is a [`Service`][] that takes a [`Uri`][] destination, and
12//! its `Response` is some type implementing [`Read`][], [`Write`][],
13//! and [`Connection`][].
14//!
15//! ## Custom Connectors
16//!
17//! A simple connector that ignores the `Uri` destination and always returns
18//! a TCP connection to the same address could be written like this:
19//!
20//! ```rust,ignore
21//! let connector = tower::service_fn(|_dst| async {
22//!     tokio::net::TcpStream::connect("127.0.0.1:1337")
23//! })
24//! ```
25//!
26//! Or, fully written out:
27//!
28//! ```
29//! use std::{future::Future, net::SocketAddr, pin::Pin, task::{self, Poll}};
30//! use http::Uri;
31//! use tokio::net::TcpStream;
32//! use tower::Service;
33//!
34//! #[derive(Clone)]
35//! struct LocalConnector;
36//!
37//! impl Service<Uri> for LocalConnector {
38//!     type Response = TcpStream;
39//!     type Error = std::io::Error;
40//!     // We can't "name" an `async` generated future.
41//!     type Future = Pin<Box<
42//!         dyn Future<Output = Result<Self::Response, Self::Error>> + Send
43//!     >>;
44//!
45//!     fn poll_ready(&mut self, _: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>> {
46//!         // This connector is always ready, but others might not be.
47//!         Poll::Ready(Ok(()))
48//!     }
49//!
50//!     fn call(&mut self, _: Uri) -> Self::Future {
51//!         Box::pin(TcpStream::connect(SocketAddr::from(([127, 0, 0, 1], 1337))))
52//!     }
53//! }
54//! ```
55//!
56//! It's worth noting that for `TcpStream`s, the [`HttpConnector`][] is a
57//! better starting place to extend from.
58//!
59//! [`HttpConnector`]: HttpConnector
60//! [`Service`]: tower::Service
61//! [`Uri`]: ::http::Uri
62//! [`Read`]: hyper::rt::Read
63//! [`Write`]: hyper::rt::Write
64//! [`Connection`]: Connection
65use std::fmt;
66
67use ::http::Extensions;
68
69#[cfg(feature = "tokio")]
70pub use self::http::{HttpConnector, HttpInfo};
71
72#[cfg(feature = "tokio")]
73pub mod dns;
74#[cfg(feature = "tokio")]
75mod http;
76
77pub use self::sealed::Connect;
78
79/// Describes a type returned by a connector.
80pub trait Connection {
81    /// Return metadata describing the connection.
82    fn connected(&self) -> Connected;
83}
84
85/// Extra information about the connected transport.
86///
87/// This can be used to inform recipients about things like if ALPN
88/// was used, or if connected to an HTTP proxy.
89#[derive(Debug)]
90pub struct Connected {
91    pub(super) alpn: Alpn,
92    pub(super) is_proxied: bool,
93    pub(super) extra: Option<Extra>,
94}
95
96pub(super) struct Extra(Box<dyn ExtraInner>);
97
98#[derive(Clone, Copy, Debug, PartialEq)]
99pub(super) enum Alpn {
100    H2,
101    None,
102}
103
104impl Connected {
105    /// Create new `Connected` type with empty metadata.
106    pub fn new() -> Connected {
107        Connected {
108            alpn: Alpn::None,
109            is_proxied: false,
110            extra: None,
111        }
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:
118    ///
119    /// - When `proxy(false)`:
120    ///
121    /// ```http
122    /// GET /guide HTTP/1.1
123    /// ```
124    ///
125    /// - When `proxy(true)`:
126    ///
127    /// ```http
128    /// GET http://hyper.rs/guide HTTP/1.1
129    /// ```
130    ///
131    /// Default is `false`.
132    pub fn proxy(mut self, is_proxied: bool) -> Connected {
133        self.is_proxied = is_proxied;
134        self
135    }
136
137    /// Determines if the connected transport is to an HTTP proxy.
138    pub fn is_proxied(&self) -> bool {
139        self.is_proxied
140    }
141
142    /// Set extra connection information to be set in the extensions of every `Response`.
143    pub fn extra<T: Clone + Send + Sync + 'static>(mut self, extra: T) -> Connected {
144        if let Some(prev) = self.extra {
145            self.extra = Some(Extra(Box::new(ExtraChain(prev.0, extra))));
146        } else {
147            self.extra = Some(Extra(Box::new(ExtraEnvelope(extra))));
148        }
149        self
150    }
151
152    /// Copies the extra connection information into an `Extensions` map.
153    pub fn get_extras(&self, extensions: &mut Extensions) {
154        if let Some(extra) = &self.extra {
155            extra.set(extensions);
156        }
157    }
158
159    /// Set that the connected transport negotiated HTTP/2 as its next protocol.
160    pub fn negotiated_h2(mut self) -> Connected {
161        self.alpn = Alpn::H2;
162        self
163    }
164
165    /// Determines if the connected transport negotiated HTTP/2 as its next protocol.
166    pub fn is_negotiated_h2(&self) -> bool {
167        self.alpn == Alpn::H2
168    }
169
170    // Don't public expose that `Connected` is `Clone`, unsure if we want to
171    // keep that contract...
172    #[cfg(feature = "http2")]
173    pub(super) fn clone(&self) -> Connected {
174        Connected {
175            alpn: self.alpn,
176            is_proxied: self.is_proxied,
177            extra: self.extra.clone(),
178        }
179    }
180}
181
182// ===== impl Extra =====
183
184impl Extra {
185    pub(super) fn set(&self, res: &mut Extensions) {
186        self.0.set(res);
187    }
188}
189
190impl Clone for Extra {
191    fn clone(&self) -> Extra {
192        Extra(self.0.clone_box())
193    }
194}
195
196impl fmt::Debug for Extra {
197    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
198        f.debug_struct("Extra").finish()
199    }
200}
201
202trait ExtraInner: Send + Sync {
203    fn clone_box(&self) -> Box<dyn ExtraInner>;
204    fn set(&self, res: &mut Extensions);
205}
206
207// This indirection allows the `Connected` to have a type-erased "extra" value,
208// while that type still knows its inner extra type. This allows the correct
209// TypeId to be used when inserting into `res.extensions_mut()`.
210#[derive(Clone)]
211struct ExtraEnvelope<T>(T);
212
213impl<T> ExtraInner for ExtraEnvelope<T>
214where
215    T: Clone + Send + Sync + 'static,
216{
217    fn clone_box(&self) -> Box<dyn ExtraInner> {
218        Box::new(self.clone())
219    }
220
221    fn set(&self, res: &mut Extensions) {
222        res.insert(self.0.clone());
223    }
224}
225
226struct ExtraChain<T>(Box<dyn ExtraInner>, T);
227
228impl<T: Clone> Clone for ExtraChain<T> {
229    fn clone(&self) -> Self {
230        ExtraChain(self.0.clone_box(), self.1.clone())
231    }
232}
233
234impl<T> ExtraInner for ExtraChain<T>
235where
236    T: Clone + Send + Sync + 'static,
237{
238    fn clone_box(&self) -> Box<dyn ExtraInner> {
239        Box::new(self.clone())
240    }
241
242    fn set(&self, res: &mut Extensions) {
243        self.0.set(res);
244        res.insert(self.1.clone());
245    }
246}
247
248pub(super) mod sealed {
249    use std::error::Error as StdError;
250    use std::future::Future;
251
252    use ::http::Uri;
253    use hyper::rt::{Read, Write};
254
255    use super::Connection;
256
257    /// Connect to a destination, returning an IO transport.
258    ///
259    /// A connector receives a [`Uri`](::http::Uri) and returns a `Future` of the
260    /// ready connection.
261    ///
262    /// # Trait Alias
263    ///
264    /// This is really just an *alias* for the `tower::Service` trait, with
265    /// additional bounds set for convenience *inside* hyper. You don't actually
266    /// implement this trait, but `tower::Service<Uri>` instead.
267    // The `Sized` bound is to prevent creating `dyn Connect`, since they cannot
268    // fit the `Connect` bounds because of the blanket impl for `Service`.
269    pub trait Connect: Sealed + Sized {
270        #[doc(hidden)]
271        type _Svc: ConnectSvc;
272        #[doc(hidden)]
273        fn connect(self, internal_only: Internal, dst: Uri) -> <Self::_Svc as ConnectSvc>::Future;
274    }
275
276    pub trait ConnectSvc {
277        type Connection: Read + Write + Connection + Unpin + Send + 'static;
278        type Error: Into<Box<dyn StdError + Send + Sync>>;
279        type Future: Future<Output = Result<Self::Connection, Self::Error>> + Unpin + Send + 'static;
280
281        fn connect(self, internal_only: Internal, dst: Uri) -> Self::Future;
282    }
283
284    impl<S, T> Connect for S
285    where
286        S: tower_service::Service<Uri, Response = T> + Send + 'static,
287        S::Error: Into<Box<dyn StdError + Send + Sync>>,
288        S::Future: Unpin + Send,
289        T: Read + Write + Connection + Unpin + Send + 'static,
290    {
291        type _Svc = S;
292
293        fn connect(self, _: Internal, dst: Uri) -> tower::util::Oneshot<S, Uri> {
294            tower::util::Oneshot::new(self, dst)
295        }
296    }
297
298    impl<S, T> ConnectSvc for S
299    where
300        S: tower_service::Service<Uri, Response = T> + Send + 'static,
301        S::Error: Into<Box<dyn StdError + Send + Sync>>,
302        S::Future: Unpin + Send,
303        T: Read + Write + Connection + Unpin + Send + 'static,
304    {
305        type Connection = T;
306        type Error = S::Error;
307        type Future = tower::util::Oneshot<S, Uri>;
308
309        fn connect(self, _: Internal, dst: Uri) -> Self::Future {
310            tower::util::Oneshot::new(self, dst)
311        }
312    }
313
314    impl<S, T> Sealed for S
315    where
316        S: tower_service::Service<Uri, Response = T> + Send,
317        S::Error: Into<Box<dyn StdError + Send + Sync>>,
318        S::Future: Unpin + Send,
319        T: Read + Write + Connection + Unpin + Send + 'static,
320    {
321    }
322
323    pub trait Sealed {}
324    #[allow(missing_debug_implementations)]
325    pub struct Internal;
326}
327
328#[cfg(test)]
329mod tests {
330    use super::Connected;
331
332    #[derive(Clone, Debug, PartialEq)]
333    struct Ex1(usize);
334
335    #[derive(Clone, Debug, PartialEq)]
336    struct Ex2(&'static str);
337
338    #[derive(Clone, Debug, PartialEq)]
339    struct Ex3(&'static str);
340
341    #[test]
342    fn test_connected_extra() {
343        let c1 = Connected::new().extra(Ex1(41));
344
345        let mut ex = ::http::Extensions::new();
346
347        assert_eq!(ex.get::<Ex1>(), None);
348
349        c1.extra.as_ref().expect("c1 extra").set(&mut ex);
350
351        assert_eq!(ex.get::<Ex1>(), Some(&Ex1(41)));
352    }
353
354    #[test]
355    fn test_connected_extra_chain() {
356        // If a user composes connectors and at each stage, there's "extra"
357        // info to attach, it shouldn't override the previous extras.
358
359        let c1 = Connected::new()
360            .extra(Ex1(45))
361            .extra(Ex2("zoom"))
362            .extra(Ex3("pew pew"));
363
364        let mut ex1 = ::http::Extensions::new();
365
366        assert_eq!(ex1.get::<Ex1>(), None);
367        assert_eq!(ex1.get::<Ex2>(), None);
368        assert_eq!(ex1.get::<Ex3>(), None);
369
370        c1.extra.as_ref().expect("c1 extra").set(&mut ex1);
371
372        assert_eq!(ex1.get::<Ex1>(), Some(&Ex1(45)));
373        assert_eq!(ex1.get::<Ex2>(), Some(&Ex2("zoom")));
374        assert_eq!(ex1.get::<Ex3>(), Some(&Ex3("pew pew")));
375
376        // Just like extensions, inserting the same type overrides previous type.
377        let c2 = Connected::new()
378            .extra(Ex1(33))
379            .extra(Ex2("hiccup"))
380            .extra(Ex1(99));
381
382        let mut ex2 = ::http::Extensions::new();
383
384        c2.extra.as_ref().expect("c2 extra").set(&mut ex2);
385
386        assert_eq!(ex2.get::<Ex1>(), Some(&Ex1(99)));
387        assert_eq!(ex2.get::<Ex2>(), Some(&Ex2("hiccup")));
388    }
389}