//! Connectors used by the `Client`. //! //! This module contains: //! //! - A default [`HttpConnector`][] that does DNS resolution and establishes //! connections over TCP. //! - Types to build custom connectors. //! //! # Connectors //! //! A "connector" is a [`Service`][] that takes a [`Uri`][] destination, and //! its `Response` is some type implementing [`AsyncRead`][], [`AsyncWrite`][], //! and [`Connection`][]. //! //! ## Custom Connectors //! //! A simple connector that ignores the `Uri` destination and always returns //! a TCP connection to the same address could be written like this: //! //! ```rust,ignore //! let connector = tower::service_fn(|_dst| async { //! tokio::net::TcpStream::connect("127.0.0.1:1337") //! }) //! ``` //! //! Or, fully written out: //! //! ``` //! # #[cfg(feature = "runtime")] //! # mod rt { //! use std::{future::Future, net::SocketAddr, pin::Pin, task::{self, Poll}}; //! use hyper::{service::Service, Uri}; //! use tokio::net::TcpStream; //! //! #[derive(Clone)] //! struct LocalConnector; //! //! impl Service for LocalConnector { //! type Response = TcpStream; //! type Error = std::io::Error; //! // We can't "name" an `async` generated future. //! type Future = Pin> + Send //! >>; //! //! fn poll_ready(&mut self, _: &mut task::Context<'_>) -> Poll> { //! // This connector is always ready, but others might not be. //! Poll::Ready(Ok(())) //! } //! //! fn call(&mut self, _: Uri) -> Self::Future { //! Box::pin(TcpStream::connect(SocketAddr::from(([127, 0, 0, 1], 1337)))) //! } //! } //! # } //! ``` //! //! It's worth noting that for `TcpStream`s, the [`HttpConnector`][] is a //! better starting place to extend from. //! //! Using either of the above connector examples, it can be used with the //! `Client` like this: //! //! ``` //! # #[cfg(feature = "runtime")] //! # fn rt () { //! # let connector = hyper::client::HttpConnector::new(); //! // let connector = ... //! //! let client = hyper::Client::builder() //! .build::<_, hyper::Body>(connector); //! # } //! ``` //! //! //! [`HttpConnector`]: HttpConnector //! [`Service`]: crate::service::Service //! [`Uri`]: ::http::Uri //! [`AsyncRead`]: tokio::io::AsyncRead //! [`AsyncWrite`]: tokio::io::AsyncWrite //! [`Connection`]: Connection use std::fmt; use ::http::Extensions; cfg_feature! { #![feature = "tcp"] pub use self::http::{HttpConnector, HttpInfo}; pub mod dns; mod http; } cfg_feature! { #![any(feature = "http1", feature = "http2")] pub use self::sealed::Connect; } /// Describes a type returned by a connector. pub trait Connection { /// Return metadata describing the connection. fn connected(&self) -> Connected; } /// Extra information about the connected transport. /// /// This can be used to inform recipients about things like if ALPN /// was used, or if connected to an HTTP proxy. #[derive(Debug)] pub struct Connected { pub(super) alpn: Alpn, pub(super) is_proxied: bool, pub(super) extra: Option, } pub(super) struct Extra(Box); #[derive(Clone, Copy, Debug, PartialEq)] pub(super) enum Alpn { H2, None, } impl Connected { /// Create new `Connected` type with empty metadata. pub fn new() -> Connected { Connected { alpn: Alpn::None, is_proxied: false, extra: None, } } /// Set whether the connected transport is to an HTTP proxy. /// /// This setting will affect if HTTP/1 requests written on the transport /// will have the request-target in absolute-form or origin-form: /// /// - When `proxy(false)`: /// /// ```http /// GET /guide HTTP/1.1 /// ``` /// /// - When `proxy(true)`: /// /// ```http /// GET http://hyper.rs/guide HTTP/1.1 /// ``` /// /// Default is `false`. pub fn proxy(mut self, is_proxied: bool) -> Connected { self.is_proxied = is_proxied; self } /// Determines if the connected transport is to an HTTP proxy. pub fn is_proxied(&self) -> bool { self.is_proxied } /// Set extra connection information to be set in the extensions of every `Response`. pub fn extra(mut self, extra: T) -> Connected { if let Some(prev) = self.extra { self.extra = Some(Extra(Box::new(ExtraChain(prev.0, extra)))); } else { self.extra = Some(Extra(Box::new(ExtraEnvelope(extra)))); } self } /// Copies the extra connection information into an `Extensions` map. pub fn get_extras(&self, extensions: &mut Extensions) { if let Some(extra) = &self.extra { extra.set(extensions); } } /// Set that the connected transport negotiated HTTP/2 as its next protocol. pub fn negotiated_h2(mut self) -> Connected { self.alpn = Alpn::H2; self } /// Determines if the connected transport negotiated HTTP/2 as its next protocol. pub fn is_negotiated_h2(&self) -> bool { self.alpn == Alpn::H2 } // Don't public expose that `Connected` is `Clone`, unsure if we want to // keep that contract... #[cfg(feature = "http2")] pub(super) fn clone(&self) -> Connected { Connected { alpn: self.alpn.clone(), is_proxied: self.is_proxied, extra: self.extra.clone(), } } } // ===== impl Extra ===== impl Extra { pub(super) fn set(&self, res: &mut Extensions) { self.0.set(res); } } impl Clone for Extra { fn clone(&self) -> Extra { Extra(self.0.clone_box()) } } impl fmt::Debug for Extra { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Extra").finish() } } trait ExtraInner: Send + Sync { fn clone_box(&self) -> Box; fn set(&self, res: &mut Extensions); } // This indirection allows the `Connected` to have a type-erased "extra" value, // while that type still knows its inner extra type. This allows the correct // TypeId to be used when inserting into `res.extensions_mut()`. #[derive(Clone)] struct ExtraEnvelope(T); impl ExtraInner for ExtraEnvelope where T: Clone + Send + Sync + 'static, { fn clone_box(&self) -> Box { Box::new(self.clone()) } fn set(&self, res: &mut Extensions) { res.insert(self.0.clone()); } } struct ExtraChain(Box, T); impl Clone for ExtraChain { fn clone(&self) -> Self { ExtraChain(self.0.clone_box(), self.1.clone()) } } impl ExtraInner for ExtraChain where T: Clone + Send + Sync + 'static, { fn clone_box(&self) -> Box { Box::new(self.clone()) } fn set(&self, res: &mut Extensions) { self.0.set(res); res.insert(self.1.clone()); } } #[cfg(any(feature = "http1", feature = "http2"))] pub(super) mod sealed { use std::error::Error as StdError; use ::http::Uri; use tokio::io::{AsyncRead, AsyncWrite}; use super::Connection; use crate::common::{Future, Unpin}; /// Connect to a destination, returning an IO transport. /// /// A connector receives a [`Uri`](::http::Uri) and returns a `Future` of the /// ready connection. /// /// # Trait Alias /// /// This is really just an *alias* for the `tower::Service` trait, with /// additional bounds set for convenience *inside* hyper. You don't actually /// implement this trait, but `tower::Service` instead. // The `Sized` bound is to prevent creating `dyn Connect`, since they cannot // fit the `Connect` bounds because of the blanket impl for `Service`. pub trait Connect: Sealed + Sized { #[doc(hidden)] type _Svc: ConnectSvc; #[doc(hidden)] fn connect(self, internal_only: Internal, dst: Uri) -> ::Future; } pub trait ConnectSvc { type Connection: AsyncRead + AsyncWrite + Connection + Unpin + Send + 'static; type Error: Into>; type Future: Future> + Unpin + Send + 'static; fn connect(self, internal_only: Internal, dst: Uri) -> Self::Future; } impl Connect for S where S: tower_service::Service + Send + 'static, S::Error: Into>, S::Future: Unpin + Send, T: AsyncRead + AsyncWrite + Connection + Unpin + Send + 'static, { type _Svc = S; fn connect(self, _: Internal, dst: Uri) -> crate::service::Oneshot { crate::service::oneshot(self, dst) } } impl ConnectSvc for S where S: tower_service::Service + Send + 'static, S::Error: Into>, S::Future: Unpin + Send, T: AsyncRead + AsyncWrite + Connection + Unpin + Send + 'static, { type Connection = T; type Error = S::Error; type Future = crate::service::Oneshot; fn connect(self, _: Internal, dst: Uri) -> Self::Future { crate::service::oneshot(self, dst) } } impl Sealed for S where S: tower_service::Service + Send, S::Error: Into>, S::Future: Unpin + Send, T: AsyncRead + AsyncWrite + Connection + Unpin + Send + 'static, { } pub trait Sealed {} #[allow(missing_debug_implementations)] pub struct Internal; } #[cfg(test)] mod tests { use super::Connected; #[derive(Clone, Debug, PartialEq)] struct Ex1(usize); #[derive(Clone, Debug, PartialEq)] struct Ex2(&'static str); #[derive(Clone, Debug, PartialEq)] struct Ex3(&'static str); #[test] fn test_connected_extra() { let c1 = Connected::new().extra(Ex1(41)); let mut ex = ::http::Extensions::new(); assert_eq!(ex.get::(), None); c1.extra.as_ref().expect("c1 extra").set(&mut ex); assert_eq!(ex.get::(), Some(&Ex1(41))); } #[test] fn test_connected_extra_chain() { // If a user composes connectors and at each stage, there's "extra" // info to attach, it shouldn't override the previous extras. let c1 = Connected::new() .extra(Ex1(45)) .extra(Ex2("zoom")) .extra(Ex3("pew pew")); let mut ex1 = ::http::Extensions::new(); assert_eq!(ex1.get::(), None); assert_eq!(ex1.get::(), None); assert_eq!(ex1.get::(), None); c1.extra.as_ref().expect("c1 extra").set(&mut ex1); assert_eq!(ex1.get::(), Some(&Ex1(45))); assert_eq!(ex1.get::(), Some(&Ex2("zoom"))); assert_eq!(ex1.get::(), Some(&Ex3("pew pew"))); // Just like extensions, inserting the same type overrides previous type. let c2 = Connected::new() .extra(Ex1(33)) .extra(Ex2("hiccup")) .extra(Ex1(99)); let mut ex2 = ::http::Extensions::new(); c2.extra.as_ref().expect("c2 extra").set(&mut ex2); assert_eq!(ex2.get::(), Some(&Ex1(99))); assert_eq!(ex2.get::(), Some(&Ex2("hiccup"))); } }