feat(http): allow specifying custom body streams
This commit is contained in:
		| @@ -6,6 +6,7 @@ | ||||
| use std::cell::RefCell; | ||||
| use std::fmt; | ||||
| use std::io; | ||||
| use std::marker::PhantomData; | ||||
| use std::net::SocketAddr; | ||||
| use std::rc::{Rc, Weak}; | ||||
| use std::time::Duration; | ||||
| @@ -36,29 +37,33 @@ mod response; | ||||
| /// This structure is used to create instances of `Server` or to spawn off tasks | ||||
| /// which handle a connection to an HTTP server. Each instance of `Http` can be | ||||
| /// configured with various protocol-level options such as keepalive. | ||||
| #[derive(Debug, Clone)] | ||||
| pub struct Http { | ||||
| pub struct Http<B> { | ||||
|     keep_alive: bool, | ||||
|     _marker: PhantomData<B>, | ||||
| } | ||||
|  | ||||
| /// An instance of a server created through `Http::bind`. | ||||
| /// | ||||
| /// This server is intended as a convenience for creating a TCP listener on an | ||||
| /// address and then serving TCP connections accepted with the service provided. | ||||
| pub struct Server<S> { | ||||
|     protocol: Http, | ||||
| pub struct Server<S, B> | ||||
| where B: Stream<Error=::Error>, | ||||
|       B::Item: AsRef<[u8]>, | ||||
| { | ||||
|     protocol: Http<B::Item>, | ||||
|     new_service: S, | ||||
|     core: Core, | ||||
|     listener: TcpListener, | ||||
|     shutdown_timeout: Duration, | ||||
| } | ||||
|  | ||||
| impl Http { | ||||
| impl<B: AsRef<[u8]> + 'static> Http<B> { | ||||
|     /// Creates a new instance of the HTTP protocol, ready to spawn a server or | ||||
|     /// start accepting connections. | ||||
|     pub fn new() -> Http { | ||||
|     pub fn new() -> Http<B> { | ||||
|         Http { | ||||
|             keep_alive: true, | ||||
|             _marker: PhantomData, | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -80,9 +85,10 @@ impl Http { | ||||
|     /// | ||||
|     /// The returned `Server` contains one method, `run`, which is used to | ||||
|     /// actually run the server. | ||||
|     pub fn bind<S>(&self, addr: &SocketAddr, new_service: S) -> ::Result<Server<S>> | ||||
|         where S: NewService<Request = Request, Response = Response, Error = ::Error> + | ||||
|     pub fn bind<S, Bd>(&self, addr: &SocketAddr, new_service: S) -> ::Result<Server<S, Bd>> | ||||
|         where S: NewService<Request = Request, Response = Response<Bd>, Error = ::Error> + | ||||
|                     Send + Sync + 'static, | ||||
|               Bd: Stream<Item=B, Error=::Error>, | ||||
|     { | ||||
|         let core = try!(Core::new()); | ||||
|         let handle = core.handle(); | ||||
| @@ -111,12 +117,13 @@ impl Http { | ||||
|     /// used through the `serve` helper method above. This can be useful, | ||||
|     /// however, when writing mocks or accepting sockets from a non-TCP | ||||
|     /// location. | ||||
|     pub fn bind_connection<S, I>(&self, | ||||
|     pub fn bind_connection<S, I, Bd>(&self, | ||||
|                                  handle: &Handle, | ||||
|                                  io: I, | ||||
|                                  remote_addr: SocketAddr, | ||||
|                                  service: S) | ||||
|         where S: Service<Request = Request, Response = Response, Error = ::Error> + 'static, | ||||
|         where S: Service<Request = Request, Response = Response<Bd>, Error = ::Error> + 'static, | ||||
|               Bd: Stream<Item=B, Error=::Error> + 'static, | ||||
|               I: Io + 'static, | ||||
|     { | ||||
|         self.bind_server(handle, io, HttpService { | ||||
| @@ -126,29 +133,48 @@ impl Http { | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[doc(hidden)] | ||||
| #[allow(missing_debug_implementations)] | ||||
| pub struct ProtoRequest(http::RequestHead); | ||||
| #[doc(hidden)] | ||||
| #[allow(missing_debug_implementations)] | ||||
| pub struct ProtoResponse(ResponseHead); | ||||
| #[doc(hidden)] | ||||
| #[allow(missing_debug_implementations)] | ||||
| pub struct ProtoTransport<T>(http::Conn<T, http::ServerTransaction>); | ||||
| #[doc(hidden)] | ||||
| #[allow(missing_debug_implementations)] | ||||
| pub struct ProtoBindTransport<T> { | ||||
|     inner: future::FutureResult<http::Conn<T, http::ServerTransaction>, io::Error>, | ||||
| impl<B> Clone for Http<B> { | ||||
|     fn clone(&self) -> Http<B> { | ||||
|         Http { | ||||
|             ..*self | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T: Io + 'static> ServerProto<T> for Http { | ||||
|     type Request = ProtoRequest; | ||||
| impl<B> fmt::Debug for Http<B> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.debug_struct("Http") | ||||
|             .field("keep_alive", &self.keep_alive) | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[doc(hidden)] | ||||
| #[allow(missing_debug_implementations)] | ||||
| pub struct __ProtoRequest(http::RequestHead); | ||||
| #[doc(hidden)] | ||||
| #[allow(missing_debug_implementations)] | ||||
| pub struct __ProtoResponse(ResponseHead); | ||||
| #[doc(hidden)] | ||||
| #[allow(missing_debug_implementations)] | ||||
| pub struct __ProtoTransport<T, B>(http::Conn<T, B, http::ServerTransaction>); | ||||
| #[doc(hidden)] | ||||
| #[allow(missing_debug_implementations)] | ||||
| pub struct __ProtoBindTransport<T, B> { | ||||
|     inner: future::FutureResult<http::Conn<T, B, http::ServerTransaction>, io::Error>, | ||||
| } | ||||
|  | ||||
| impl<T, B> ServerProto<T> for Http<B> | ||||
| where T: Io + 'static, | ||||
|       B: AsRef<[u8]> + 'static, | ||||
| { | ||||
|     type Request = __ProtoRequest; | ||||
|     type RequestBody = http::Chunk; | ||||
|     type Response = ProtoResponse; | ||||
|     type ResponseBody = http::Chunk; | ||||
|     type Response = __ProtoResponse; | ||||
|     type ResponseBody = B; | ||||
|     type Error = ::Error; | ||||
|     type Transport = ProtoTransport<T>; | ||||
|     type BindTransport = ProtoBindTransport<T>; | ||||
|     type Transport = __ProtoTransport<T, B>; | ||||
|     type BindTransport = __ProtoBindTransport<T, B>; | ||||
|  | ||||
|     fn bind_transport(&self, io: T) -> Self::BindTransport { | ||||
|         let ka = if self.keep_alive { | ||||
| @@ -156,14 +182,17 @@ impl<T: Io + 'static> ServerProto<T> for Http { | ||||
|         } else { | ||||
|             http::KA::Disabled | ||||
|         }; | ||||
|         ProtoBindTransport { | ||||
|         __ProtoBindTransport { | ||||
|             inner: future::ok(http::Conn::new(io, ka)), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T: Io + 'static> Sink for ProtoTransport<T> { | ||||
|     type SinkItem = Frame<ProtoResponse, http::Chunk, ::Error>; | ||||
| impl<T, B> Sink for __ProtoTransport<T, B> | ||||
| where T: Io + 'static, | ||||
|       B: AsRef<[u8]>, | ||||
| { | ||||
|     type SinkItem = Frame<__ProtoResponse, B, ::Error>; | ||||
|     type SinkError = io::Error; | ||||
|  | ||||
|     fn start_send(&mut self, item: Self::SinkItem) | ||||
| @@ -179,7 +208,7 @@ impl<T: Io + 'static> Sink for ProtoTransport<T> { | ||||
|             AsyncSink::Ready => Ok(AsyncSink::Ready), | ||||
|             AsyncSink::NotReady(Frame::Message { message, body }) => { | ||||
|                 Ok(AsyncSink::NotReady(Frame::Message { | ||||
|                     message: ProtoResponse(message), | ||||
|                     message: __ProtoResponse(message), | ||||
|                     body: body, | ||||
|                 })) | ||||
|             } | ||||
| @@ -197,8 +226,8 @@ impl<T: Io + 'static> Sink for ProtoTransport<T> { | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T: Io + 'static> Stream for ProtoTransport<T> { | ||||
|     type Item = Frame<ProtoRequest, http::Chunk, ::Error>; | ||||
| impl<T: Io + 'static, B: AsRef<[u8]>> Stream for __ProtoTransport<T, B> { | ||||
|     type Item = Frame<__ProtoRequest, http::Chunk, ::Error>; | ||||
|     type Error = io::Error; | ||||
|  | ||||
|     fn poll(&mut self) -> Poll<Option<Self::Item>, io::Error> { | ||||
| @@ -208,7 +237,7 @@ impl<T: Io + 'static> Stream for ProtoTransport<T> { | ||||
|         }; | ||||
|         let item = match item { | ||||
|             Frame::Message { message, body } => { | ||||
|                 Frame::Message { message: ProtoRequest(message), body: body } | ||||
|                 Frame::Message { message: __ProtoRequest(message), body: body } | ||||
|             } | ||||
|             Frame::Body { chunk } => Frame::Body { chunk: chunk }, | ||||
|             Frame::Error { error } => Frame::Error { error: error }, | ||||
| @@ -217,7 +246,7 @@ impl<T: Io + 'static> Stream for ProtoTransport<T> { | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T: Io + 'static> Transport for ProtoTransport<T> { | ||||
| impl<T: Io + 'static, B: AsRef<[u8]> + 'static> Transport for __ProtoTransport<T, B> { | ||||
|     fn tick(&mut self) { | ||||
|         self.0.tick() | ||||
|     } | ||||
| @@ -227,12 +256,12 @@ impl<T: Io + 'static> Transport for ProtoTransport<T> { | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T: Io + 'static> Future for ProtoBindTransport<T> { | ||||
|     type Item = ProtoTransport<T>; | ||||
| impl<T: Io + 'static, B> Future for __ProtoBindTransport<T, B> { | ||||
|     type Item = __ProtoTransport<T, B>; | ||||
|     type Error = io::Error; | ||||
|  | ||||
|     fn poll(&mut self) -> Poll<ProtoTransport<T>, io::Error> { | ||||
|         self.inner.poll().map(|a| a.map(ProtoTransport)) | ||||
|     fn poll(&mut self) -> Poll<__ProtoTransport<T, B>, io::Error> { | ||||
|         self.inner.poll().map(|a| a.map(__ProtoTransport)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -241,24 +270,26 @@ struct HttpService<T> { | ||||
|     remote_addr: SocketAddr, | ||||
| } | ||||
|  | ||||
| fn map_response_to_message(res: Response) -> Message<ProtoResponse, http::TokioBody> { | ||||
| fn map_response_to_message<B>(res: Response<B>) -> Message<__ProtoResponse, B> { | ||||
|     let (head, body) = response::split(res); | ||||
|     if let Some(body) = body { | ||||
|         Message::WithBody(ProtoResponse(head), body.into()) | ||||
|         Message::WithBody(__ProtoResponse(head), body.into()) | ||||
|     } else { | ||||
|         Message::WithoutBody(ProtoResponse(head)) | ||||
|         Message::WithoutBody(__ProtoResponse(head)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| type ResponseHead = http::MessageHead<::StatusCode>; | ||||
|  | ||||
| impl<T> Service for HttpService<T> | ||||
|     where T: Service<Request=Request, Response=Response, Error=::Error>, | ||||
| impl<T, B> Service for HttpService<T> | ||||
|     where T: Service<Request=Request, Response=Response<B>, Error=::Error>, | ||||
|           B: Stream<Error=::Error>, | ||||
|           B::Item: AsRef<[u8]>, | ||||
| { | ||||
|     type Request = Message<ProtoRequest, http::TokioBody>; | ||||
|     type Response = Message<ProtoResponse, http::TokioBody>; | ||||
|     type Request = Message<__ProtoRequest, http::TokioBody>; | ||||
|     type Response = Message<__ProtoResponse, B>; | ||||
|     type Error = ::Error; | ||||
|     type Future = Map<T::Future, fn(Response) -> Message<ProtoResponse, http::TokioBody>>; | ||||
|     type Future = Map<T::Future, fn(Response<B>) -> Message<__ProtoResponse, B>>; | ||||
|  | ||||
|     fn call(&self, message: Self::Request) -> Self::Future { | ||||
|         let (head, body) = match message { | ||||
| @@ -270,9 +301,11 @@ impl<T> Service for HttpService<T> | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<S> Server<S> | ||||
|     where S: NewService<Request = Request, Response = Response, Error = ::Error> | ||||
| impl<S, B> Server<S, B> | ||||
|     where S: NewService<Request = Request, Response = Response<B>, Error = ::Error> | ||||
|                 + Send + Sync + 'static, | ||||
|           B: Stream<Error=::Error> + 'static, | ||||
|           B::Item: AsRef<[u8]>, | ||||
| { | ||||
|     /// Returns the local address that this server is bound to. | ||||
|     pub fn local_addr(&self) -> ::Result<SocketAddr> { | ||||
| @@ -370,7 +403,9 @@ impl<S> Server<S> | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<S: fmt::Debug> fmt::Debug for Server<S> { | ||||
| impl<S: fmt::Debug, B: Stream<Error=::Error>> fmt::Debug for Server<S, B> | ||||
| where B::Item: AsRef<[u8]> | ||||
| { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.debug_struct("Server") | ||||
|          .field("core", &"...") | ||||
|   | ||||
| @@ -8,16 +8,15 @@ use version; | ||||
| /// The Response sent to a client after receiving a Request in a Service. | ||||
| /// | ||||
| /// The default `StatusCode` for a `Response` is `200 OK`. | ||||
| #[derive(Default)] | ||||
| pub struct Response { | ||||
| pub struct Response<B = Body> { | ||||
|     head: http::MessageHead<StatusCode>, | ||||
|     body: Option<Body>, | ||||
|     body: Option<B>, | ||||
| } | ||||
|  | ||||
| impl Response { | ||||
| impl<B> Response<B> { | ||||
|     /// Create a new Response. | ||||
|     #[inline] | ||||
|     pub fn new() -> Response { | ||||
|     pub fn new() -> Response<B> { | ||||
|         Response::default() | ||||
|     } | ||||
|  | ||||
| @@ -47,7 +46,7 @@ impl Response { | ||||
|  | ||||
|     /// Set the body. | ||||
|     #[inline] | ||||
|     pub fn set_body<T: Into<Body>>(&mut self, body: T) { | ||||
|     pub fn set_body<T: Into<B>>(&mut self, body: T) { | ||||
|         self.body = Some(body.into()); | ||||
|     } | ||||
|  | ||||
| @@ -82,13 +81,22 @@ impl Response { | ||||
|     /// | ||||
|     /// Useful for the "builder-style" pattern. | ||||
|     #[inline] | ||||
|     pub fn with_body<T: Into<Body>>(mut self, body: T) -> Self { | ||||
|     pub fn with_body<T: Into<B>>(mut self, body: T) -> Self { | ||||
|         self.set_body(body); | ||||
|         self | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl fmt::Debug for Response { | ||||
| impl<B> Default for Response<B> { | ||||
|     fn default() -> Response<B> { | ||||
|         Response { | ||||
|             head: Default::default(), | ||||
|             body: None, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<B> fmt::Debug for Response<B> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.debug_struct("Response") | ||||
|             .field("status", &self.head.subject) | ||||
| @@ -98,6 +106,6 @@ impl fmt::Debug for Response { | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub fn split(res: Response) -> (http::MessageHead<StatusCode>, Option<Body>) { | ||||
| pub fn split<B>(res: Response<B>) -> (http::MessageHead<StatusCode>, Option<B>) { | ||||
|     (res.head, res.body) | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user