feat(lib): add support to disable tokio-proto internals
For now, this adds `client::Config::no_proto`, `server::Http::no_proto`, and `server::Server::no_proto` to skip tokio-proto implementations, and use an internal dispatch system instead. `Http::no_proto` is similar to `Http::bind_connection`, but returns a `Connection` that is a `Future` to drive HTTP with the provided service. Any errors prior to parsing a request, and after delivering a response (but before flush the response body) will be returned from this future. See #1342 for more.
This commit is contained in:
		| @@ -66,6 +66,7 @@ where B: Stream<Error=::Error>, | ||||
|     core: Core, | ||||
|     listener: TcpListener, | ||||
|     shutdown_timeout: Duration, | ||||
|     no_proto: bool, | ||||
| } | ||||
|  | ||||
| impl<B: AsRef<[u8]> + 'static> Http<B> { | ||||
| @@ -121,6 +122,7 @@ impl<B: AsRef<[u8]> + 'static> Http<B> { | ||||
|             listener: listener, | ||||
|             protocol: self.clone(), | ||||
|             shutdown_timeout: Duration::new(1, 0), | ||||
|             no_proto: false, | ||||
|         }) | ||||
|     } | ||||
|  | ||||
| @@ -165,6 +167,30 @@ impl<B: AsRef<[u8]> + 'static> Http<B> { | ||||
|         }) | ||||
|     } | ||||
|  | ||||
|     /// Bind a connection together with a Service. | ||||
|     /// | ||||
|     /// This returns a Future that must be polled in order for HTTP to be | ||||
|     /// driven on the connection. | ||||
|     /// | ||||
|     /// This additionally skips the tokio-proto infrastructure internally. | ||||
|     pub fn no_proto<S, I, Bd>(&self, io: I, service: S) -> Connection<I, Bd, S> | ||||
|         where S: Service<Request = Request, Response = Response<Bd>, Error = ::Error> + 'static, | ||||
|               Bd: Stream<Item=B, Error=::Error> + 'static, | ||||
|               I: AsyncRead + AsyncWrite + 'static, | ||||
|  | ||||
|     { | ||||
|         let ka = if self.keep_alive { | ||||
|             proto::KA::Busy | ||||
|         } else { | ||||
|             proto::KA::Disabled | ||||
|         }; | ||||
|         let mut conn = proto::Conn::new(io, ka); | ||||
|         conn.set_flush_pipeline(self.pipeline); | ||||
|         Connection { | ||||
|             conn: proto::dispatch::Dispatcher::new(proto::dispatch::Server::new(service), conn), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// Bind a `Service` using types from the `http` crate. | ||||
|     /// | ||||
|     /// See `Http::bind_connection`. | ||||
| @@ -185,6 +211,67 @@ impl<B: AsRef<[u8]> + 'static> Http<B> { | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// A future binding a connection with a Service. | ||||
| /// | ||||
| /// Polling this future will drive HTTP forward. | ||||
| #[must_use = "futures do nothing unless polled"] | ||||
| pub struct Connection<I, B, S> | ||||
| where S: Service, | ||||
|       B: Stream<Error=::Error>, | ||||
|       B::Item: AsRef<[u8]>, | ||||
| { | ||||
|     conn: proto::dispatch::Dispatcher<proto::dispatch::Server<S>, B, I, B::Item, proto::ServerTransaction, proto::KA>, | ||||
| } | ||||
|  | ||||
| impl<I, B, S> Future for Connection<I, B, S> | ||||
| where S: Service<Request = Request, Response = Response<B>, Error = ::Error> + 'static, | ||||
|       I: AsyncRead + AsyncWrite + 'static, | ||||
|       B: Stream<Error=::Error> + 'static, | ||||
|       B::Item: AsRef<[u8]>, | ||||
| { | ||||
|     type Item = self::unnameable::Opaque; | ||||
|     type Error = ::Error; | ||||
|  | ||||
|     fn poll(&mut self) -> Poll<Self::Item, Self::Error> { | ||||
|         try_ready!(self.conn.poll()); | ||||
|         Ok(self::unnameable::opaque().into()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<I, B, S> fmt::Debug for Connection<I, B, S>  | ||||
| where S: Service, | ||||
|       B: Stream<Error=::Error>, | ||||
|       B::Item: AsRef<[u8]>, | ||||
| { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.debug_struct("Connection") | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
|  | ||||
| mod unnameable { | ||||
|     // This type is specifically not exported outside the crate, | ||||
|     // so no one can actually name the type. With no methods, we make no | ||||
|     // promises about this type. | ||||
|     // | ||||
|     // All of that to say we can eventually replace the type returned | ||||
|     // to something else, and it would not be a breaking change. | ||||
|     // | ||||
|     // We may want to eventually yield the `T: AsyncRead + AsyncWrite`, which | ||||
|     // doesn't have a `Debug` bound. So, this type can't implement `Debug` | ||||
|     // either, so the type change doesn't break people. | ||||
|     #[allow(missing_debug_implementations)] | ||||
|     pub struct Opaque { | ||||
|         _inner: (), | ||||
|     } | ||||
|  | ||||
|     pub fn opaque() -> Opaque { | ||||
|         Opaque { | ||||
|             _inner: (), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<B> Clone for Http<B> { | ||||
|     fn clone(&self) -> Http<B> { | ||||
|         Http { | ||||
| @@ -207,7 +294,7 @@ impl<B> fmt::Debug for Http<B> { | ||||
| pub struct __ProtoRequest(proto::RequestHead); | ||||
| #[doc(hidden)] | ||||
| #[allow(missing_debug_implementations)] | ||||
| pub struct __ProtoResponse(ResponseHead); | ||||
| pub struct __ProtoResponse(proto::MessageHead<::StatusCode>); | ||||
| #[doc(hidden)] | ||||
| #[allow(missing_debug_implementations)] | ||||
| pub struct __ProtoTransport<T, B>(proto::Conn<T, B, proto::ServerTransaction>); | ||||
| @@ -368,8 +455,6 @@ struct HttpService<T> { | ||||
|     remote_addr: SocketAddr, | ||||
| } | ||||
|  | ||||
| type ResponseHead = proto::MessageHead<::StatusCode>; | ||||
|  | ||||
| impl<T, B> Service for HttpService<T> | ||||
|     where T: Service<Request=Request, Response=Response<B>, Error=::Error>, | ||||
|           B: Stream<Error=::Error>, | ||||
| @@ -420,6 +505,12 @@ impl<S, B> Server<S, B> | ||||
|         self | ||||
|     } | ||||
|  | ||||
|     /// Configure this server to not use tokio-proto infrastructure internally. | ||||
|     pub fn no_proto(&mut self) -> &mut Self { | ||||
|         self.no_proto = true; | ||||
|         self | ||||
|     } | ||||
|  | ||||
|     /// Execute this server infinitely. | ||||
|     /// | ||||
|     /// This method does not currently return, but it will return an error if | ||||
| @@ -444,7 +535,7 @@ impl<S, B> Server<S, B> | ||||
|     pub fn run_until<F>(self, shutdown_signal: F) -> ::Result<()> | ||||
|         where F: Future<Item = (), Error = ()>, | ||||
|     { | ||||
|         let Server { protocol, new_service, mut core, listener, shutdown_timeout } = self; | ||||
|         let Server { protocol, new_service, mut core, listener, shutdown_timeout, no_proto } = self; | ||||
|         let handle = core.handle(); | ||||
|  | ||||
|         // Mini future to track the number of active services | ||||
| @@ -460,7 +551,14 @@ impl<S, B> Server<S, B> | ||||
|                 info: Rc::downgrade(&info), | ||||
|             }; | ||||
|             info.borrow_mut().active += 1; | ||||
|             protocol.bind_connection(&handle, socket, addr, s); | ||||
|             if no_proto { | ||||
|                 let fut = protocol.no_proto(socket, s) | ||||
|                     .map(|_| ()) | ||||
|                     .map_err(|err| error!("no_proto error: {}", err)); | ||||
|                 handle.spawn(fut); | ||||
|             } else { | ||||
|                 protocol.bind_connection(&handle, socket, addr, s); | ||||
|             } | ||||
|             Ok(()) | ||||
|         }); | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user