Refactor errors (#46)
This patch does a bunch of refactoring, mostly around error types, but it also
paves the way to allow `Codec` to be used standalone.
* `Codec` (and `FramedRead` / `FramedWrite`) is broken out into a codec module.
* An h2-codec crate is created that re-exports the frame and codec modules.
* New error types are introduced in the internals:
  * `RecvError` represents errors caused by trying to receive a frame.
  * `SendError` represents errors caused by trying to send a frame.
  * `UserError` is an enum of potential errors caused by invalid usage
    by the user of the lib.
  * `ProtoError` is either a `Reason` or an `io::Error`. However it doesn't
    specify connection or stream level.
  * `h2::Error` is an opaque error type and is the only error type exposed
    by the public API (used to be `ConnectionError`).
There are misc code changes to enable this as well. The biggest is a new "sink"
API for `Codec`. It provides buffer which queues up a frame followed by flush
which writes everything that is queued. This departs from the `Sink` trait in
order to provide more accurate error values. For example, buffer can never fail
(but it will panic if `poll_ready` is not called first).
			
			
This commit is contained in:
		| @@ -1,9 +1,9 @@ | ||||
| use {frame, HeaderMap, ConnectionError}; | ||||
| use frame::StreamId; | ||||
| use proto::{self, Connection, WindowSize, ProtoError}; | ||||
| use error::Reason::*; | ||||
| use frame::{StreamId, Headers, Pseudo, Settings}; | ||||
| use frame::Reason::*; | ||||
| use codec::{Codec, RecvError}; | ||||
| use proto::{self, Connection, WindowSize}; | ||||
|  | ||||
| use http::{Request, Response}; | ||||
| use http::{Request, Response, HeaderMap}; | ||||
| use futures::{Future, Poll, Sink, Async, AsyncSink, AndThen, MapErr}; | ||||
| use tokio_io::{AsyncRead, AsyncWrite}; | ||||
| use tokio_io::io::WriteAll; | ||||
| @@ -16,9 +16,9 @@ use std::io::Error as IoError; | ||||
| pub struct Handshake<T: AsyncRead + AsyncWrite, B: IntoBuf = Bytes> { | ||||
|     inner: | ||||
|         AndThen< | ||||
|             MapErr<WriteAll<T, &'static [u8]>, fn(IoError) -> ConnectionError>, | ||||
|             Result<Client<T, B>, ConnectionError>, | ||||
|             fn((T, &'static [u8])) -> Result<Client<T, B>, ConnectionError> | ||||
|             MapErr<WriteAll<T, &'static [u8]>, fn(IoError) -> ::Error>, | ||||
|             Result<Client<T, B>, ::Error>, | ||||
|             fn((T, &'static [u8])) -> Result<Client<T, B>, ::Error> | ||||
|         > | ||||
|  | ||||
| } | ||||
| @@ -63,28 +63,31 @@ impl<T, B> Client<T, B> | ||||
|         debug!("binding client connection"); | ||||
|  | ||||
|         let bind: fn((T, &'static [u8])) | ||||
|                     -> Result<Client<T, B>, ConnectionError> = | ||||
|                     -> Result<Client<T, B>, ::Error> = | ||||
|             |(io, _)| { | ||||
|                 debug!("client connection bound"); | ||||
|  | ||||
|                 let mut framed_write = proto::framed_write(io); | ||||
|                 let settings = frame::Settings::default(); | ||||
|                 // Create the codec | ||||
|                 let mut codec = Codec::new(io); | ||||
|  | ||||
|                 // Create the initial SETTINGS frame | ||||
|                 let settings = Settings::default(); | ||||
|  | ||||
|                 // Send initial settings frame | ||||
|                 match framed_write.start_send(settings.into()) { | ||||
|                 match codec.start_send(settings.into()) { | ||||
|                     Ok(AsyncSink::Ready) => { | ||||
|                         let connection = proto::from_framed_write(framed_write); | ||||
|                         let connection = Connection::new(codec); | ||||
|                         Ok(Client { connection }) | ||||
|                     } | ||||
|                     Ok(_) => unreachable!(), | ||||
|                     Err(e) => Err(ConnectionError::from(e)), | ||||
|                     Err(e) => Err(::Error::from(e)), | ||||
|                 } | ||||
|             }; | ||||
|  | ||||
|         let msg: &'static [u8] =  b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"; | ||||
|         let handshake = io::write_all(io, msg) | ||||
|             .map_err(ConnectionError::from as | ||||
|                 fn(IoError) -> ConnectionError | ||||
|             .map_err(::Error::from as | ||||
|                 fn(IoError) -> ::Error | ||||
|             ) | ||||
|             .and_then(bind); | ||||
|  | ||||
| @@ -93,15 +96,16 @@ impl<T, B> Client<T, B> | ||||
|  | ||||
|     /// Returns `Ready` when the connection can initialize a new HTTP 2.0 | ||||
|     /// stream. | ||||
|     pub fn poll_ready(&mut self) -> Poll<(), ConnectionError> { | ||||
|         self.connection.poll_send_request_ready() | ||||
|     pub fn poll_ready(&mut self) -> Poll<(), ::Error> { | ||||
|         Ok(self.connection.poll_send_request_ready()) | ||||
|     } | ||||
|  | ||||
|     /// Send a request on a new HTTP 2.0 stream | ||||
|     pub fn request(&mut self, request: Request<()>, end_of_stream: bool) | ||||
|         -> Result<Stream<B>, ConnectionError> | ||||
|         -> Result<Stream<B>, ::Error> | ||||
|     { | ||||
|         self.connection.send_request(request, end_of_stream) | ||||
|             .map_err(Into::into) | ||||
|             .map(|stream| Stream { | ||||
|                 inner: stream, | ||||
|             }) | ||||
| @@ -114,10 +118,11 @@ impl<T, B> Future for Client<T, B> | ||||
|           B: IntoBuf, | ||||
| { | ||||
|     type Item = (); | ||||
|     type Error = ConnectionError; | ||||
|     type Error = ::Error; | ||||
|  | ||||
|     fn poll(&mut self) -> Poll<(), ConnectionError> { | ||||
|     fn poll(&mut self) -> Poll<(), ::Error> { | ||||
|         self.connection.poll() | ||||
|             .map_err(Into::into) | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -139,7 +144,7 @@ impl<T, B> fmt::Debug for Client<T, B> | ||||
| impl<T, B: IntoBuf> Future for Handshake<T, B> | ||||
| where T: AsyncRead + AsyncWrite { | ||||
|     type Item = Client<T, B>; | ||||
|     type Error = ConnectionError; | ||||
|     type Error = ::Error; | ||||
|  | ||||
|     fn poll(&mut self) -> Poll<Self::Item, Self::Error> { | ||||
|         self.inner.poll() | ||||
| @@ -161,7 +166,7 @@ impl<T, B> fmt::Debug for Handshake<T, B> | ||||
|  | ||||
| impl<B: IntoBuf> Stream<B> { | ||||
|     /// Receive the HTTP/2.0 response, if it is ready. | ||||
|     pub fn poll_response(&mut self) -> Poll<Response<Body<B>>, ConnectionError> { | ||||
|     pub fn poll_response(&mut self) -> Poll<Response<Body<B>>, ::Error> { | ||||
|         let (parts, _) = try_ready!(self.inner.poll_response()).into_parts(); | ||||
|         let body = Body { inner: self.inner.clone() }; | ||||
|  | ||||
| @@ -180,29 +185,31 @@ impl<B: IntoBuf> Stream<B> { | ||||
|     } | ||||
|  | ||||
|     /// Request to be notified when the stream's capacity increases | ||||
|     pub fn poll_capacity(&mut self) -> Poll<Option<usize>, ConnectionError> { | ||||
|     pub fn poll_capacity(&mut self) -> Poll<Option<usize>, ::Error> { | ||||
|         let res = try_ready!(self.inner.poll_capacity()); | ||||
|         Ok(Async::Ready(res.map(|v| v as usize))) | ||||
|     } | ||||
|  | ||||
|     /// Send data | ||||
|     pub fn send_data(&mut self, data: B, end_of_stream: bool) | ||||
|         -> Result<(), ConnectionError> | ||||
|         -> Result<(), ::Error> | ||||
|     { | ||||
|         self.inner.send_data(data.into_buf(), end_of_stream) | ||||
|             .map_err(Into::into) | ||||
|     } | ||||
|  | ||||
|     /// Send trailers | ||||
|     pub fn send_trailers(&mut self, trailers: HeaderMap) | ||||
|         -> Result<(), ConnectionError> | ||||
|         -> Result<(), ::Error> | ||||
|     { | ||||
|         self.inner.send_trailers(trailers) | ||||
|             .map_err(Into::into) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<B: IntoBuf> Future for Stream<B> { | ||||
|     type Item = Response<Body<B>>; | ||||
|     type Error = ConnectionError; | ||||
|     type Error = ::Error; | ||||
|  | ||||
|     fn poll(&mut self) -> Poll<Self::Item, Self::Error> { | ||||
|         self.poll_response() | ||||
| @@ -217,24 +224,27 @@ impl<B: IntoBuf> Body<B> { | ||||
|         self.inner.body_is_empty() | ||||
|     } | ||||
|  | ||||
|     pub fn release_capacity(&mut self, sz: usize) -> Result<(), ConnectionError> { | ||||
|     pub fn release_capacity(&mut self, sz: usize) -> Result<(), ::Error> { | ||||
|         self.inner.release_capacity(sz as proto::WindowSize) | ||||
|             .map_err(Into::into) | ||||
|     } | ||||
|  | ||||
|     /// Poll trailers | ||||
|     /// | ||||
|     /// This function **must** not be called until `Body::poll` returns `None`. | ||||
|     pub fn poll_trailers(&mut self) -> Poll<Option<HeaderMap>, ConnectionError> { | ||||
|     pub fn poll_trailers(&mut self) -> Poll<Option<HeaderMap>, ::Error> { | ||||
|         self.inner.poll_trailers() | ||||
|             .map_err(Into::into) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<B: IntoBuf> ::futures::Stream for Body<B> { | ||||
|     type Item = Bytes; | ||||
|     type Error = ConnectionError; | ||||
|     type Error = ::Error; | ||||
|  | ||||
|     fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> { | ||||
|         self.inner.poll_data() | ||||
|             .map_err(Into::into) | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -251,7 +261,7 @@ impl proto::Peer for Peer { | ||||
|     fn convert_send_message( | ||||
|         id: StreamId, | ||||
|         request: Self::Send, | ||||
|         end_of_stream: bool) -> frame::Headers | ||||
|         end_of_stream: bool) -> Headers | ||||
|     { | ||||
|         use http::request::Parts; | ||||
|  | ||||
| @@ -259,10 +269,10 @@ impl proto::Peer for Peer { | ||||
|  | ||||
|         // Build the set pseudo header set. All requests will include `method` | ||||
|         // and `path`. | ||||
|         let pseudo = frame::Pseudo::request(method, uri); | ||||
|         let pseudo = Pseudo::request(method, uri); | ||||
|  | ||||
|         // Create the HEADERS frame | ||||
|         let mut frame = frame::Headers::new(id, pseudo, headers); | ||||
|         let mut frame = Headers::new(id, pseudo, headers); | ||||
|  | ||||
|         if end_of_stream { | ||||
|             frame.set_end_stream() | ||||
| @@ -271,7 +281,7 @@ impl proto::Peer for Peer { | ||||
|         frame | ||||
|     } | ||||
|  | ||||
|     fn convert_poll_message(headers: frame::Headers) -> Result<Self::Poll, ProtoError> { | ||||
|     fn convert_poll_message(headers: Headers) -> Result<Self::Poll, RecvError> { | ||||
|         let mut b = Response::builder(); | ||||
|  | ||||
|         let stream_id = headers.stream_id(); | ||||
| @@ -286,7 +296,7 @@ impl proto::Peer for Peer { | ||||
|             Err(_) => { | ||||
|                 // TODO: Should there be more specialized handling for different | ||||
|                 // kinds of errors | ||||
|                 return Err(ProtoError::Stream { | ||||
|                 return Err(RecvError::Stream { | ||||
|                     id: stream_id, | ||||
|                     reason: ProtocolError, | ||||
|                 }); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user