feat(client): provide tower::Service support for clients (#1915)
This commit is contained in:
		
				
					committed by
					
						 Sean McArthur
						Sean McArthur
					
				
			
			
				
	
			
			
			
						parent
						
							eef407d60e
						
					
				
				
					commit
					eee2a72879
				
			| @@ -14,6 +14,7 @@ use std::sync::Arc; | ||||
| use bytes::Bytes; | ||||
| use futures_util::future::{self, Either, FutureExt as _}; | ||||
| use tokio_io::{AsyncRead, AsyncWrite}; | ||||
| use tower_service::Service; | ||||
|  | ||||
| use crate::body::Payload; | ||||
| use crate::common::{Exec, Future, Pin, Poll, task}; | ||||
| @@ -242,18 +243,21 @@ where | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* TODO(0.12.0): when we change from tokio-service to tower. | ||||
| impl<T, B> Service for SendRequest<T, B> { | ||||
|     type Request = Request<B>; | ||||
|     type Response = Response; | ||||
|     type Error = ::Error; | ||||
| impl<B> Service<Request<B>> for SendRequest<B> | ||||
| where | ||||
|     B: Payload + 'static, { | ||||
|     type Response = Response<Body>; | ||||
|     type Error = crate::Error; | ||||
|     type Future = ResponseFuture; | ||||
|  | ||||
|     fn call(&self, req: Self::Request) -> Self::Future { | ||||
|     fn poll_ready(&mut self, cx: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>> { | ||||
|         self.poll_ready(cx) | ||||
|     } | ||||
|  | ||||
|     fn call(&mut self, req: Request<B>) -> Self::Future { | ||||
|         self.send_request(req) | ||||
|     } | ||||
| } | ||||
| */ | ||||
|  | ||||
| impl<B> fmt::Debug for SendRequest<B> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|   | ||||
| @@ -5,7 +5,8 @@ use std::mem; | ||||
| use std::net::{IpAddr, SocketAddr}; | ||||
| use std::time::Duration; | ||||
|  | ||||
| use http::uri::Scheme; | ||||
| use http::uri::{Scheme, Uri}; | ||||
| use futures_util::{TryFutureExt, FutureExt}; | ||||
| use net2::TcpBuilder; | ||||
| use tokio_net::driver::Handle; | ||||
| use tokio_net::tcp::TcpStream; | ||||
| @@ -248,6 +249,63 @@ where | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<R> tower_service::Service<Uri> for HttpConnector<R> | ||||
| where | ||||
|     R: Resolve + Clone + Send + Sync + 'static, | ||||
|     R::Future: Send, | ||||
| { | ||||
|     type Response  = TcpStream; | ||||
|     type Error = io::Error; | ||||
|     type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send + 'static>>; | ||||
|  | ||||
|     fn poll_ready(&mut self, _cx: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>> { | ||||
|         Ok(()).into() | ||||
|     } | ||||
|  | ||||
|     fn call(&mut self, dst: Uri) -> Self::Future { | ||||
|         // TODO: return error here | ||||
|         let dst = Destination::try_from_uri(dst).unwrap(); | ||||
|  | ||||
|         trace!( | ||||
|             "Http::connect; scheme={}, host={}, port={:?}", | ||||
|             dst.scheme(), | ||||
|             dst.host(), | ||||
|             dst.port(), | ||||
|         ); | ||||
|  | ||||
|         if self.enforce_http { | ||||
|             if dst.uri.scheme_part() != Some(&Scheme::HTTP) { | ||||
|                 return invalid_url::<R>(InvalidUrl::NotHttp, &self.handle).map_ok(|(s, _)| s).boxed(); | ||||
|             } | ||||
|         } else if dst.uri.scheme_part().is_none() { | ||||
|             return invalid_url::<R>(InvalidUrl::MissingScheme, &self.handle).map_ok(|(s, _)| s).boxed(); | ||||
|         } | ||||
|  | ||||
|         let host = match dst.uri.host() { | ||||
|             Some(s) => s, | ||||
|             None => return invalid_url::<R>(InvalidUrl::MissingAuthority, &self.handle).map_ok(|(s, _)| s).boxed(), | ||||
|         }; | ||||
|         let port = match dst.uri.port_part() { | ||||
|             Some(port) => port.as_u16(), | ||||
|             None => if dst.uri.scheme_part() == Some(&Scheme::HTTPS) { 443 } else { 80 }, | ||||
|         }; | ||||
|  | ||||
|         let fut = HttpConnecting { | ||||
|             state: State::Lazy(self.resolver.clone(), host.into(), self.local_address), | ||||
|             handle: self.handle.clone(), | ||||
|             happy_eyeballs_timeout: self.happy_eyeballs_timeout, | ||||
|             keep_alive_timeout: self.keep_alive_timeout, | ||||
|             nodelay: self.nodelay, | ||||
|             port, | ||||
|             reuse_address: self.reuse_address, | ||||
|             send_buffer_size: self.send_buffer_size, | ||||
|             recv_buffer_size: self.recv_buffer_size, | ||||
|         }; | ||||
|  | ||||
|         fut.map_ok(|(s, _)| s).boxed() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl HttpInfo { | ||||
|     /// Get the remote address of the transport used. | ||||
|     pub fn remote_addr(&self) -> SocketAddr { | ||||
|   | ||||
| @@ -81,6 +81,7 @@ pub mod conn; | ||||
| pub mod connect; | ||||
| pub(crate) mod dispatch; | ||||
| mod pool; | ||||
| pub mod service; | ||||
| #[cfg(test)] | ||||
| mod tests; | ||||
|  | ||||
|   | ||||
							
								
								
									
										85
									
								
								src/client/service.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								src/client/service.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,85 @@ | ||||
| //! Utilities used to interact with the Tower ecosystem. | ||||
| //! | ||||
| //! This module provides exports of `Service`, `MakeService` and `Connect` which | ||||
| //! all provide hook-ins into the Tower ecosystem. | ||||
|  | ||||
| use super::conn::{SendRequest, Builder}; | ||||
| use std::marker::PhantomData; | ||||
| use crate::{common::{Poll, task, Pin}, body::Payload}; | ||||
| use std::future::Future; | ||||
| use std::error::Error as StdError; | ||||
| use tower_make::MakeConnection; | ||||
|  | ||||
| pub use tower_service::Service; | ||||
| pub use tower_make::MakeService; | ||||
|  | ||||
| /// Creates a connection via `SendRequest`. | ||||
| /// | ||||
| /// This accepts a `hyper::client::conn::Builder` and provides | ||||
| /// a `MakeService` implementation to create connections from some | ||||
| /// target `T`. | ||||
| #[derive(Debug)] | ||||
| pub struct Connect<C, B, T> { | ||||
|     inner: C, | ||||
|     builder: Builder, | ||||
|     _pd: PhantomData<fn(T, B)> | ||||
| } | ||||
|  | ||||
| impl<C, B, T> Connect<C, B, T> { | ||||
|     /// Create a new `Connect` with some inner connector `C` and a connection | ||||
|     /// builder. | ||||
|     pub fn new(inner: C, builder: Builder) -> Self { | ||||
|         Self { | ||||
|             inner, | ||||
|             builder, | ||||
|             _pd: PhantomData | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<C, B, T> Service<T> for Connect<C, B, T> | ||||
| where | ||||
|     C: MakeConnection<T>, | ||||
|     C::Connection: Unpin + Send + 'static, | ||||
|     C::Future: Send + 'static, | ||||
|     C::Error: Into<Box<dyn StdError + Send + Sync>> + Send, | ||||
|     B: Payload + Unpin + 'static, | ||||
|     B::Data: Unpin, | ||||
| { | ||||
|     type Response = SendRequest<B>; | ||||
|     type Error = crate::Error; | ||||
|     type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send + 'static>>; | ||||
|  | ||||
|     fn poll_ready(&mut self, cx: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>> { | ||||
|         self.inner.poll_ready(cx).map_err(|e| crate::Error::new(crate::error::Kind::Connect).with(e.into())) | ||||
|     } | ||||
|  | ||||
|     fn call(&mut self, req: T) -> Self::Future { | ||||
|         let builder = self.builder.clone(); | ||||
|         let io = self.inner.make_connection(req); | ||||
|  | ||||
|         let fut = async move { | ||||
|             match io.await { | ||||
|                 Ok(io) => { | ||||
|                     match builder.handshake(io).await { | ||||
|                         Ok((sr, conn)) => { | ||||
|                             builder.exec.execute(async move { | ||||
|                                 if let Err(e) = conn.await { | ||||
|                                     debug!("connection error: {:?}", e); | ||||
|                                 } | ||||
|                             })?; | ||||
|                             Ok(sr) | ||||
|                         }, | ||||
|                         Err(e) => Err(e) | ||||
|                     } | ||||
|                 }, | ||||
|                 Err(e) => { | ||||
|                     let err = crate::Error::new(crate::error::Kind::Connect).with(e.into()); | ||||
|                     Err(err) | ||||
|                 } | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         Box::pin(fut) | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user