feat(http2): add HTTP/2 support for Client and Server
This commit is contained in:
		
							
								
								
									
										144
									
								
								src/proto/h2/client.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								src/proto/h2/client.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,144 @@ | ||||
| use bytes::IntoBuf; | ||||
| use futures::{Async, Future, Poll, Stream}; | ||||
| use futures::future::{self, Either}; | ||||
| use futures::sync::oneshot; | ||||
| use h2::client::{Builder, Handshake, SendRequest}; | ||||
| use tokio_io::{AsyncRead, AsyncWrite}; | ||||
|  | ||||
| use body::Payload; | ||||
| use ::common::{Exec, Never}; | ||||
| use super::{PipeToSendStream, SendBuf}; | ||||
| use ::{Body, Request, Response}; | ||||
|  | ||||
| type ClientRx<B> = ::client::dispatch::Receiver<Request<B>, Response<Body>>; | ||||
|  | ||||
| pub struct Client<T, B> | ||||
| where | ||||
|     B: Payload, | ||||
| { | ||||
|     executor: Exec, | ||||
|     rx: ClientRx<B>, | ||||
|     state: State<T, SendBuf<B::Data>>, | ||||
| } | ||||
|  | ||||
| enum State<T, B> where B: IntoBuf { | ||||
|     Handshaking(Handshake<T, B>), | ||||
|     Ready(SendRequest<B>, oneshot::Sender<Never>), | ||||
| } | ||||
|  | ||||
| impl<T, B> Client<T, B> | ||||
| where | ||||
|     T: AsyncRead + AsyncWrite + Send + 'static, | ||||
|     B: Payload, | ||||
| { | ||||
|     pub(crate) fn new(io: T, rx: ClientRx<B>, exec: Exec) -> Client<T, B> { | ||||
|         let handshake = Builder::new() | ||||
|             // we don't expose PUSH promises yet | ||||
|             .enable_push(false) | ||||
|             .handshake(io); | ||||
|  | ||||
|         Client { | ||||
|             executor: exec, | ||||
|             rx: rx, | ||||
|             state: State::Handshaking(handshake), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T, B> Future for Client<T, B> | ||||
| where | ||||
|     T: AsyncRead + AsyncWrite + Send + 'static, | ||||
|     B: Payload + 'static, | ||||
| { | ||||
|     type Item = (); | ||||
|     type Error = ::Error; | ||||
|  | ||||
|     fn poll(&mut self) -> Poll<Self::Item, Self::Error> { | ||||
|         loop { | ||||
|             let next = match self.state { | ||||
|                 State::Handshaking(ref mut h) => { | ||||
|                     let (request_tx, conn) = try_ready!(h.poll().map_err(::Error::new_h2)); | ||||
|                     // A oneshot channel is used entirely to detect when the | ||||
|                     // 'Client' has been dropped. This is to get around a bug | ||||
|                     // in h2 where dropping all SendRequests won't notify a | ||||
|                     // parked Connection. | ||||
|                     let (tx, rx) = oneshot::channel(); | ||||
|                     let fut = conn | ||||
|                         .map_err(|e| debug!("client h2 connection error: {}", e)) | ||||
|                         .select2(rx) | ||||
|                         .then(|res| match res { | ||||
|                             Ok(Either::A(((), _))) | | ||||
|                             Err(Either::A(((), _))) => { | ||||
|                                 // conn has finished either way | ||||
|                                 Either::A(future::ok(())) | ||||
|                             }, | ||||
|                             Err(Either::B((_, conn))) => { | ||||
|                                 // oneshot has been dropped, hopefully polling | ||||
|                                 // the connection some more should start shutdown | ||||
|                                 // and then close | ||||
|                                 trace!("send_request dropped, starting conn shutdown"); | ||||
|                                 Either::B(conn) | ||||
|                             } | ||||
|                             Ok(Either::B((never, _))) => match never {}, | ||||
|                         }); | ||||
|                     self.executor.execute(fut); | ||||
|                     State::Ready(request_tx, tx) | ||||
|                 }, | ||||
|                 State::Ready(ref mut tx, _) => { | ||||
|                     try_ready!(tx.poll_ready().map_err(::Error::new_h2)); | ||||
|                     match self.rx.poll() { | ||||
|                         Ok(Async::Ready(Some((req, mut cb)))) => { | ||||
|                             // check that future hasn't been canceled already | ||||
|                             if let Async::Ready(()) = cb.poll_cancel().expect("poll_cancel cannot error") { | ||||
|                                 trace!("request canceled"); | ||||
|                                 continue; | ||||
|                             } | ||||
|                             let (head, body) = req.into_parts(); | ||||
|                             let mut req = ::http::Request::from_parts(head, ()); | ||||
|                             super::strip_connection_headers(req.headers_mut()); | ||||
|                             let eos = body.is_end_stream(); | ||||
|                             let (fut, body_tx) = match tx.send_request(req, eos) { | ||||
|                                 Ok(ok) => ok, | ||||
|                                 Err(err) => { | ||||
|                                     debug!("client send request error: {}", err); | ||||
|                                     let _ = cb.send(Err((::Error::new_h2(err), None))); | ||||
|                                     continue; | ||||
|                                 } | ||||
|                             }; | ||||
|                             if !eos { | ||||
|                                 let pipe = PipeToSendStream::new(body, body_tx); | ||||
|                                 self.executor.execute(pipe.map_err(|e| debug!("client request body error: {}", e))); | ||||
|                             } | ||||
|  | ||||
|                             let fut = fut | ||||
|                                 .then(move |result| { | ||||
|                                     match result { | ||||
|                                         Ok(res) => { | ||||
|                                             let res = res.map(::Body::h2); | ||||
|                                             let _ = cb.send(Ok(res)); | ||||
|                                         }, | ||||
|                                         Err(err) => { | ||||
|                                             debug!("client response error: {}", err); | ||||
|                                             let _ = cb.send(Err((::Error::new_h2(err), None))); | ||||
|                                         } | ||||
|                                     } | ||||
|                                     Ok(()) | ||||
|                                 }); | ||||
|                             self.executor.execute(fut); | ||||
|                             continue; | ||||
|                         }, | ||||
|  | ||||
|                         Ok(Async::NotReady) => return Ok(Async::NotReady), | ||||
|  | ||||
|                         Ok(Async::Ready(None)) | | ||||
|                         Err(_) => { | ||||
|                             trace!("client tx dropped"); | ||||
|                             return Ok(Async::Ready(())); | ||||
|                         } | ||||
|                     } | ||||
|                 }, | ||||
|             }; | ||||
|             self.state = next; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1 +1,121 @@ | ||||
| use bytes::Buf; | ||||
| use futures::{Async, Future, Poll}; | ||||
| use h2::{Reason, SendStream}; | ||||
| use http::HeaderMap; | ||||
| use http::header::{CONNECTION, TRANSFER_ENCODING}; | ||||
|  | ||||
| use ::body::Payload; | ||||
| use ::proto::h1::Cursor; | ||||
|  | ||||
| mod client; | ||||
| mod server; | ||||
|  | ||||
| pub(crate) use self::client::Client; | ||||
| pub(crate) use self::server::Server; | ||||
|  | ||||
| fn strip_connection_headers(headers: &mut HeaderMap) { | ||||
|     if headers.remove(TRANSFER_ENCODING).is_some() { | ||||
|         trace!("removed illegal Transfer-Encoding header"); | ||||
|     } | ||||
|     if headers.contains_key(CONNECTION) { | ||||
|         warn!("Connection header illegal in HTTP/2"); | ||||
|         //TODO: actually remove it, after checking the value | ||||
|         //and removing all related headers | ||||
|     } | ||||
| } | ||||
|  | ||||
| // body adapters used by both Client and Server | ||||
|  | ||||
| struct PipeToSendStream<S> | ||||
| where | ||||
|     S: Payload, | ||||
| { | ||||
|     body_tx: SendStream<SendBuf<S::Data>>, | ||||
|     stream: S, | ||||
| } | ||||
|  | ||||
| impl<S> PipeToSendStream<S> | ||||
| where | ||||
|     S: Payload, | ||||
| { | ||||
|     fn new(stream: S, tx: SendStream<SendBuf<S::Data>>) -> PipeToSendStream<S> { | ||||
|         PipeToSendStream { | ||||
|             body_tx: tx, | ||||
|             stream: stream, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<S> Future for PipeToSendStream<S> | ||||
| where | ||||
|     S: Payload, | ||||
| { | ||||
|     type Item = (); | ||||
|     type Error = ::Error; | ||||
|  | ||||
|     fn poll(&mut self) -> Poll<Self::Item, Self::Error> { | ||||
|         loop { | ||||
|             // TODO: make use of flow control on SendStream | ||||
|             // If you're looking at this and thinking of trying to fix this TODO, | ||||
|             // you may want to look at: | ||||
|             // https://docs.rs/h2/0.1.*/h2/struct.SendStream.html | ||||
|             // | ||||
|             // With that doc open, we'd want to do these things: | ||||
|             // - check self.body_tx.capacity() to see if we can send *any* data | ||||
|             // - if > 0: | ||||
|             // -   poll self.stream | ||||
|             // -   reserve chunk.len() more capacity (because its about to be used)? | ||||
|             // -   send the chunk | ||||
|             // - else: | ||||
|             // -   try reserve a smallish amount of capacity | ||||
|             // -   call self.body_tx.poll_capacity(), return if NotReady | ||||
|             match self.stream.poll_data() { | ||||
|                 Ok(Async::Ready(Some(chunk))) => { | ||||
|                     trace!("send body chunk: {}B", chunk.as_ref().len()); | ||||
|                     self.body_tx.send_data(SendBuf(Some(Cursor::new(chunk))), false) | ||||
|                         .map_err(::Error::new_h2)?; | ||||
|                 }, | ||||
|                 Ok(Async::Ready(None)) => { | ||||
|                     trace!("send body eos"); | ||||
|                     self.body_tx.send_data(SendBuf(None), true) | ||||
|                         .map_err(::Error::new_h2)?; | ||||
|                     return Ok(Async::Ready(())); | ||||
|                 }, | ||||
|                 Ok(Async::NotReady) => return Ok(Async::NotReady), | ||||
|                 Err(err) => { | ||||
|                     let err = ::Error::new_user_body(err); | ||||
|                     trace!("send body user stream error: {}", err); | ||||
|                     self.body_tx.send_reset(Reason::INTERNAL_ERROR); | ||||
|                     return Err(err); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| struct SendBuf<B>(Option<Cursor<B>>); | ||||
|  | ||||
| impl<B: AsRef<[u8]>> Buf for SendBuf<B> { | ||||
|     #[inline] | ||||
|     fn remaining(&self) -> usize { | ||||
|         self.0 | ||||
|             .as_ref() | ||||
|             .map(|b| b.remaining()) | ||||
|             .unwrap_or(0) | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     fn bytes(&self) -> &[u8] { | ||||
|         self.0 | ||||
|             .as_ref() | ||||
|             .map(|b| b.bytes()) | ||||
|             .unwrap_or(&[]) | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     fn advance(&mut self, cnt: usize) { | ||||
|         self.0 | ||||
|             .as_mut() | ||||
|             .map(|b| b.advance(cnt)); | ||||
|     } | ||||
| } | ||||
|   | ||||
							
								
								
									
										198
									
								
								src/proto/h2/server.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										198
									
								
								src/proto/h2/server.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,198 @@ | ||||
| use futures::{Async, Future, Poll, Stream}; | ||||
| use h2::Reason; | ||||
| use h2::server::{Builder, Connection, Handshake, SendResponse}; | ||||
| use tokio_io::{AsyncRead, AsyncWrite}; | ||||
|  | ||||
| use ::body::Payload; | ||||
| use ::common::Exec; | ||||
| use ::server::Service; | ||||
| use super::{PipeToSendStream, SendBuf}; | ||||
|  | ||||
| use ::{Body, Request, Response}; | ||||
|  | ||||
| pub(crate) struct Server<T, S, B> | ||||
| where | ||||
|     S: Service, | ||||
|     B: Payload, | ||||
| { | ||||
|     exec: Exec, | ||||
|     service: S, | ||||
|     state: State<T, B>, | ||||
| } | ||||
|  | ||||
| enum State<T, B> | ||||
| where | ||||
|     B: Payload, | ||||
| { | ||||
|     Handshaking(Handshake<T, SendBuf<B::Data>>), | ||||
|     Serving(Serving<T, B>), | ||||
| } | ||||
|  | ||||
| struct Serving<T, B> | ||||
| where | ||||
|     B: Payload, | ||||
| { | ||||
|     conn: Connection<T, SendBuf<B::Data>>, | ||||
| } | ||||
|  | ||||
|  | ||||
| impl<T, S, B> Server<T, S, B> | ||||
| where | ||||
|     T: AsyncRead + AsyncWrite, | ||||
|     S: Service<Request=Request<Body>, Response=Response<B>>, | ||||
|     S::Error: Into<Box<::std::error::Error + Send + Sync>>, | ||||
|     S::Future: Send + 'static, | ||||
|     B: Payload, | ||||
| { | ||||
|     pub(crate) fn new(io: T, service: S, exec: Exec) -> Server<T, S, B> { | ||||
|         let handshake = Builder::new() | ||||
|             .handshake(io); | ||||
|         Server { | ||||
|             exec, | ||||
|             state: State::Handshaking(handshake), | ||||
|             service, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn graceful_shutdown(&mut self) { | ||||
|         unimplemented!("h2 server graceful shutdown"); | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T, S, B> Future for Server<T, S, B> | ||||
| where | ||||
|     T: AsyncRead + AsyncWrite, | ||||
|     S: Service<Request=Request<Body>, Response=Response<B>>, | ||||
|     S::Error: Into<Box<::std::error::Error + Send + Sync>>, | ||||
|     S::Future: Send + 'static, | ||||
|     B: Payload, | ||||
| { | ||||
|     type Item = (); | ||||
|     type Error = ::Error; | ||||
|  | ||||
|     fn poll(&mut self) -> Poll<Self::Item, Self::Error> { | ||||
|         loop { | ||||
|             let next = match self.state { | ||||
|                 State::Handshaking(ref mut h) => { | ||||
|                     let conn = try_ready!(h.poll().map_err(::Error::new_h2)); | ||||
|                     State::Serving(Serving { | ||||
|                         conn: conn, | ||||
|                     }) | ||||
|                 }, | ||||
|                 State::Serving(ref mut srv) => { | ||||
|                     return srv.poll_server(&mut self.service, &self.exec); | ||||
|                 } | ||||
|             }; | ||||
|             self.state = next; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T, B> Serving<T, B> | ||||
| where | ||||
|     T: AsyncRead + AsyncWrite, | ||||
|     B: Payload, | ||||
| { | ||||
|     fn poll_server<S>(&mut self, service: &mut S, exec: &Exec) -> Poll<(), ::Error> | ||||
|     where | ||||
|         S: Service< | ||||
|             Request=Request<Body>, | ||||
|             Response=Response<B>, | ||||
|         >, | ||||
|         S::Error: Into<Box<::std::error::Error + Send + Sync>>, | ||||
|         S::Future: Send + 'static, | ||||
|     { | ||||
|         while let Some((req, respond)) = try_ready!(self.conn.poll().map_err(::Error::new_h2)) { | ||||
|             trace!("incoming request"); | ||||
|             let req = req.map(::Body::h2); | ||||
|             let fut = H2Stream::new(service.call(req), respond); | ||||
|             exec.execute(fut); | ||||
|         } | ||||
|  | ||||
|         // no more incoming streams... | ||||
|         trace!("incoming connection complete"); | ||||
|         Ok(Async::Ready(())) | ||||
|     } | ||||
| } | ||||
|  | ||||
| struct H2Stream<F, B> | ||||
| where | ||||
|     B: Payload, | ||||
| { | ||||
|     reply: SendResponse<SendBuf<B::Data>>, | ||||
|     state: H2StreamState<F, B>, | ||||
| } | ||||
|  | ||||
| enum H2StreamState<F, B> | ||||
| where | ||||
|     B: Payload, | ||||
| { | ||||
|     Service(F), | ||||
|     Body(PipeToSendStream<B>), | ||||
| } | ||||
|  | ||||
| impl<F, B> H2Stream<F, B> | ||||
| where | ||||
|     F: Future<Item=Response<B>>, | ||||
|     F::Error: Into<Box<::std::error::Error + Send + Sync>>, | ||||
|     B: Payload, | ||||
| { | ||||
|     fn new(fut: F, respond: SendResponse<SendBuf<B::Data>>) -> H2Stream<F, B> { | ||||
|         H2Stream { | ||||
|             reply: respond, | ||||
|             state: H2StreamState::Service(fut), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn poll2(&mut self) -> Poll<(), ::Error> { | ||||
|         loop { | ||||
|             let next = match self.state { | ||||
|                 H2StreamState::Service(ref mut h) => { | ||||
|                     let res = try_ready!(h.poll().map_err(::Error::new_user_service)); | ||||
|                     let (head, body) = res.into_parts(); | ||||
|                     let mut res = ::http::Response::from_parts(head, ()); | ||||
|                     super::strip_connection_headers(res.headers_mut()); | ||||
|                     macro_rules! reply { | ||||
|                         ($eos:expr) => ({ | ||||
|                             match self.reply.send_response(res, $eos) { | ||||
|                                 Ok(tx) => tx, | ||||
|                                 Err(e) => { | ||||
|                                     trace!("send response error: {}", e); | ||||
|                                     self.reply.send_reset(Reason::INTERNAL_ERROR); | ||||
|                                     return Err(::Error::new_h2(e)); | ||||
|                                 } | ||||
|                             } | ||||
|                         }) | ||||
|                     } | ||||
|                     if !body.is_end_stream() { | ||||
|                         let body_tx = reply!(false); | ||||
|                         H2StreamState::Body(PipeToSendStream::new(body, body_tx)) | ||||
|                     } else { | ||||
|                         reply!(true); | ||||
|                         return Ok(Async::Ready(())); | ||||
|                     } | ||||
|                 }, | ||||
|                 H2StreamState::Body(ref mut pipe) => { | ||||
|                     return pipe.poll(); | ||||
|                 } | ||||
|             }; | ||||
|             self.state = next; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<F, B> Future for H2Stream<F, B> | ||||
| where | ||||
|     F: Future<Item=Response<B>>, | ||||
|     F::Error: Into<Box<::std::error::Error + Send + Sync>>, | ||||
|     B: Payload, | ||||
| { | ||||
|     type Item = (); | ||||
|     type Error = (); | ||||
|  | ||||
|     fn poll(&mut self) -> Poll<Self::Item, Self::Error> { | ||||
|         self.poll2() | ||||
|             .map_err(|e| debug!("stream error: {}", e)) | ||||
|     } | ||||
| } | ||||
|  | ||||
		Reference in New Issue
	
	Block a user