Much work
This commit is contained in:
		
							
								
								
									
										11
									
								
								Cargo.toml
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								Cargo.toml
									
									
									
									
									
								
							| @@ -8,11 +8,11 @@ futures = "0.1" | |||||||
| tokio-io = "0.1" | tokio-io = "0.1" | ||||||
| tokio-timer = { git = "https://github.com/tokio-rs/tokio-timer" } | tokio-timer = { git = "https://github.com/tokio-rs/tokio-timer" } | ||||||
| bytes = "0.4" | bytes = "0.4" | ||||||
| http = { path = "/Users/carllerche/Code/Oss/Tokio/tower/http" } | http = { git = "https://github.com/carllerche/http" } | ||||||
| byteorder = "1.0" | byteorder = "1.0" | ||||||
| log = "0.3.8" | log = "0.3.8" | ||||||
| # tower = { path = "/Users/carllerche/Code/Oss/Tokio/tower/tower-http" } |  | ||||||
| fnv = "1.0.5" | fnv = "1.0.5" | ||||||
|  | ordermap = "0.2.10" | ||||||
|  |  | ||||||
| [dev-dependencies] | [dev-dependencies] | ||||||
| hex = "0.2.0" | hex = "0.2.0" | ||||||
| @@ -21,3 +21,10 @@ serde = "1.0.0" | |||||||
| serde_json = "1.0.0" | serde_json = "1.0.0" | ||||||
| quickcheck = "0.4.1" | quickcheck = "0.4.1" | ||||||
| rand = "0.3.15" | rand = "0.3.15" | ||||||
|  |  | ||||||
|  | # Akamai example | ||||||
|  | tokio-core = "0.1" | ||||||
|  | openssl = { version = "0.9.14", "features" = ["v102"] } | ||||||
|  | tokio-openssl = "0.1.3" | ||||||
|  | env_logger = "0.4.3" | ||||||
|  | io-dump = { path = "/Users/carllerche/Code/Oss/Tokio/util/io-dump" } | ||||||
|   | |||||||
							
								
								
									
										107
									
								
								src/client.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								src/client.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,107 @@ | |||||||
|  | use {frame, proto, Frame, Peer, ConnectionError, StreamId}; | ||||||
|  |  | ||||||
|  | use http; | ||||||
|  |  | ||||||
|  | use futures::{Future, Poll}; | ||||||
|  |  | ||||||
|  | use tokio_io::{AsyncRead, AsyncWrite}; | ||||||
|  |  | ||||||
|  | /// In progress H2 connection binding | ||||||
|  | pub struct Handshake<T> { | ||||||
|  |     // TODO: unbox | ||||||
|  |     inner: Box<Future<Item = Connection<T>, Error = ConnectionError>>, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Marker type indicating a client peer | ||||||
|  | pub struct Client; | ||||||
|  |  | ||||||
|  | pub type Connection<T> = super::Connection<T, Client>; | ||||||
|  |  | ||||||
|  | /// Bind an H2 client connection. | ||||||
|  | /// | ||||||
|  | /// Returns a future which resolves to the connection value once the H2 | ||||||
|  | /// handshake has been completed. | ||||||
|  | pub fn bind<T>(io: T) -> Handshake<T> | ||||||
|  |     where T: AsyncRead + AsyncWrite + 'static, | ||||||
|  | { | ||||||
|  |     use tokio_io::io; | ||||||
|  |  | ||||||
|  |     debug!("binding client connection"); | ||||||
|  |  | ||||||
|  |     let handshake = io::write_all(io, b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n") | ||||||
|  |         .map(|(io, _)| { | ||||||
|  |             debug!("client connection bound"); | ||||||
|  |             proto::new_connection(io) | ||||||
|  |         }) | ||||||
|  |         .map_err(ConnectionError::from); | ||||||
|  |  | ||||||
|  |     Handshake { inner: Box::new(handshake) } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl Peer for Client { | ||||||
|  |     type Send = http::request::Head; | ||||||
|  |     type Poll = http::response::Head; | ||||||
|  |  | ||||||
|  |     fn check_initiating_id(id: StreamId) -> Result<(), ConnectionError> { | ||||||
|  |         if id % 2 == 0 { | ||||||
|  |             // Client stream identifiers must be odd | ||||||
|  |             unimplemented!(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // TODO: Ensure the `id` doesn't overflow u31 | ||||||
|  |  | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fn convert_send_message( | ||||||
|  |         id: StreamId, | ||||||
|  |         message: Self::Send, | ||||||
|  |         body: bool) -> proto::SendMessage | ||||||
|  |     { | ||||||
|  |         use http::request::Head; | ||||||
|  |  | ||||||
|  |         // Extract the components of the HTTP request | ||||||
|  |         let Head { method, uri, headers, .. } = message; | ||||||
|  |  | ||||||
|  |         // Build the set pseudo header set. All requests will include `method` | ||||||
|  |         // and `path`. | ||||||
|  |         let mut pseudo = frame::Pseudo::request(method, uri.path().into()); | ||||||
|  |  | ||||||
|  |         // If the URI includes a scheme component, add it to the pseudo headers | ||||||
|  |         if let Some(scheme) = uri.scheme() { | ||||||
|  |             pseudo.set_scheme(scheme.into()); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // If the URI includes an authority component, add it to the pseudo | ||||||
|  |         // headers | ||||||
|  |         if let Some(authority) = uri.authority() { | ||||||
|  |             pseudo.set_authority(authority.into()); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // Create the HEADERS frame | ||||||
|  |         let mut frame = frame::Headers::new(id, pseudo, headers); | ||||||
|  |  | ||||||
|  |         // TODO: Factor in trailers | ||||||
|  |         if !body { | ||||||
|  |             frame.set_end_stream(); | ||||||
|  |         } else { | ||||||
|  |             unimplemented!(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // Return the `SendMessage` | ||||||
|  |         proto::SendMessage::new(frame) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fn convert_poll_message(message: proto::PollMessage) -> Frame<Self::Poll> { | ||||||
|  |         unimplemented!(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl<T> Future for Handshake<T> { | ||||||
|  |     type Item = Connection<T>; | ||||||
|  |     type Error = ConnectionError; | ||||||
|  |  | ||||||
|  |     fn poll(&mut self) -> Poll<Self::Item, Self::Error> { | ||||||
|  |         self.inner.poll() | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -66,7 +66,7 @@ impl Head { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     pub fn encode<T: BufMut>(&self, payload_len: usize, dst: &mut T) { |     pub fn encode<T: BufMut>(&self, payload_len: usize, dst: &mut T) { | ||||||
|         debug_assert_eq!(self.encode_len(), dst.remaining_mut()); |         debug_assert!(self.encode_len() <= dst.remaining_mut()); | ||||||
|         debug_assert!(self.stream_id & STREAM_ID_MASK == 0); |         debug_assert!(self.stream_id & STREAM_ID_MASK == 0); | ||||||
|  |  | ||||||
|         dst.put_uint::<BigEndian>(payload_len as u64, 3); |         dst.put_uint::<BigEndian>(payload_len as u64, 3); | ||||||
|   | |||||||
| @@ -34,7 +34,7 @@ pub struct Headers { | |||||||
|     flags: HeadersFlag, |     flags: HeadersFlag, | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Debug, Copy, Clone, Eq, PartialEq, Default)] | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||||||
| pub struct HeadersFlag(u8); | pub struct HeadersFlag(u8); | ||||||
|  |  | ||||||
| #[derive(Debug)] | #[derive(Debug)] | ||||||
| @@ -108,6 +108,16 @@ const ALL: u8 = END_STREAM | |||||||
| // ===== impl Headers ===== | // ===== impl Headers ===== | ||||||
|  |  | ||||||
| impl Headers { | impl Headers { | ||||||
|  |     pub fn new(stream_id: StreamId, pseudo: Pseudo, fields: HeaderMap<HeaderValue>) -> Self { | ||||||
|  |         Headers { | ||||||
|  |             stream_id: stream_id, | ||||||
|  |             stream_dep: None, | ||||||
|  |             fields: fields, | ||||||
|  |             pseudo: pseudo, | ||||||
|  |             flags: HeadersFlag::default(), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     pub fn load(head: Head, src: &mut Cursor<Bytes>, decoder: &mut hpack::Decoder) |     pub fn load(head: Head, src: &mut Cursor<Bytes>, decoder: &mut hpack::Decoder) | ||||||
|         -> Result<Self, Error> |         -> Result<Self, Error> | ||||||
|     { |     { | ||||||
| @@ -162,6 +172,10 @@ impl Headers { | |||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     pub fn is_end_headers(&self) -> bool { | ||||||
|  |         self.flags.is_end_headers() | ||||||
|  |     } | ||||||
|  |  | ||||||
|     pub fn encode(self, encoder: &mut hpack::Encoder, dst: &mut BytesMut) |     pub fn encode(self, encoder: &mut hpack::Encoder, dst: &mut BytesMut) | ||||||
|         -> Option<Continuation> |         -> Option<Continuation> | ||||||
|     { |     { | ||||||
| @@ -210,6 +224,28 @@ impl From<Headers> for Frame { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // ===== impl Pseudo ===== | ||||||
|  |  | ||||||
|  | impl Pseudo { | ||||||
|  |     pub fn request(method: Method, path: ByteStr) -> Self { | ||||||
|  |         Pseudo { | ||||||
|  |             method: Some(method), | ||||||
|  |             scheme: None, | ||||||
|  |             authority: None, | ||||||
|  |             path: Some(path), | ||||||
|  |             status: None, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub fn set_scheme(&mut self, scheme: ByteStr) { | ||||||
|  |         self.scheme = Some(scheme); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub fn set_authority(&mut self, authority: ByteStr) { | ||||||
|  |         self.authority = Some(authority); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| // ===== impl Iter ===== | // ===== impl Iter ===== | ||||||
|  |  | ||||||
| impl Iterator for Iter { | impl Iterator for Iter { | ||||||
| @@ -264,6 +300,10 @@ impl HeadersFlag { | |||||||
|         self.0 & END_STREAM == END_STREAM |         self.0 & END_STREAM == END_STREAM | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     pub fn set_end_stream(&mut self) { | ||||||
|  |         self.0 |= END_STREAM | ||||||
|  |     } | ||||||
|  |  | ||||||
|     pub fn is_end_headers(&self) -> bool { |     pub fn is_end_headers(&self) -> bool { | ||||||
|         self.0 & END_HEADERS == END_HEADERS |         self.0 & END_HEADERS == END_HEADERS | ||||||
|     } |     } | ||||||
| @@ -277,6 +317,13 @@ impl HeadersFlag { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | impl Default for HeadersFlag { | ||||||
|  |     /// Returns a `HeadersFlag` value with `END_HEADERS` set. | ||||||
|  |     fn default() -> Self { | ||||||
|  |         HeadersFlag(END_HEADERS) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| impl From<HeadersFlag> for u8 { | impl From<HeadersFlag> for u8 { | ||||||
|     fn from(src: HeadersFlag) -> u8 { |     fn from(src: HeadersFlag) -> u8 { | ||||||
|         src.0 |         src.0 | ||||||
|   | |||||||
| @@ -34,7 +34,7 @@ mod util; | |||||||
|  |  | ||||||
| pub use self::data::Data; | pub use self::data::Data; | ||||||
| pub use self::head::{Head, Kind, StreamId}; | pub use self::head::{Head, Kind, StreamId}; | ||||||
| pub use self::headers::{Headers, PushPromise, Continuation}; | pub use self::headers::{Headers, PushPromise, Continuation, Pseudo}; | ||||||
| pub use self::settings::{Settings, SettingSet}; | pub use self::settings::{Settings, SettingSet}; | ||||||
|  |  | ||||||
| // Re-export some constants | // Re-export some constants | ||||||
|   | |||||||
| @@ -81,6 +81,7 @@ impl Settings { | |||||||
|  |  | ||||||
|         // Ensure the payload length is correct, each setting is 6 bytes long. |         // Ensure the payload length is correct, each setting is 6 bytes long. | ||||||
|         if payload.len() % 6 != 0 { |         if payload.len() % 6 != 0 { | ||||||
|  |             debug!("invalid settings payload length; len={:?}", payload.len()); | ||||||
|             return Err(Error::InvalidPayloadAckSettings); |             return Err(Error::InvalidPayloadAckSettings); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										43
									
								
								src/lib.rs
									
									
									
									
									
								
							
							
						
						
									
										43
									
								
								src/lib.rs
									
									
									
									
									
								
							| @@ -13,14 +13,17 @@ extern crate http; | |||||||
| // Buffer utilities | // Buffer utilities | ||||||
| extern crate bytes; | extern crate bytes; | ||||||
|  |  | ||||||
| // Hash function used for HPACK encoding | // Hash function used for HPACK encoding and tracking stream states. | ||||||
| extern crate fnv; | extern crate fnv; | ||||||
|  |  | ||||||
|  | extern crate ordermap; | ||||||
|  |  | ||||||
| extern crate byteorder; | extern crate byteorder; | ||||||
|  |  | ||||||
| #[macro_use] | #[macro_use] | ||||||
| extern crate log; | extern crate log; | ||||||
|  |  | ||||||
|  | pub mod client; | ||||||
| pub mod error; | pub mod error; | ||||||
| pub mod hpack; | pub mod hpack; | ||||||
| pub mod proto; | pub mod proto; | ||||||
| @@ -29,4 +32,42 @@ pub mod frame; | |||||||
| mod util; | mod util; | ||||||
|  |  | ||||||
| pub use error::{ConnectionError, StreamError, Reason}; | pub use error::{ConnectionError, StreamError, Reason}; | ||||||
|  | pub use frame::{StreamId}; | ||||||
| pub use proto::Connection; | pub use proto::Connection; | ||||||
|  |  | ||||||
|  | /// An H2 connection frame | ||||||
|  | pub enum Frame<T> { | ||||||
|  |     Message { | ||||||
|  |         id: StreamId, | ||||||
|  |         message: T, | ||||||
|  |         body: bool, | ||||||
|  |     }, | ||||||
|  |     Body { | ||||||
|  |         id: StreamId, | ||||||
|  |         chunk: Option<()>, | ||||||
|  |     }, | ||||||
|  |     Error { | ||||||
|  |         id: StreamId, | ||||||
|  |         error: (), | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Either a Client or a Server | ||||||
|  | pub trait Peer { | ||||||
|  |     /// Message type sent into the transport | ||||||
|  |     type Send; | ||||||
|  |  | ||||||
|  |     /// Message type polled from the transport | ||||||
|  |     type Poll; | ||||||
|  |  | ||||||
|  |     fn check_initiating_id(id: StreamId) -> Result<(), ConnectionError>; | ||||||
|  |  | ||||||
|  |     #[doc(hidden)] | ||||||
|  |     fn convert_send_message( | ||||||
|  |         id: StreamId, | ||||||
|  |         message: Self::Send, | ||||||
|  |         body: bool) -> proto::SendMessage; | ||||||
|  |  | ||||||
|  |     #[doc(hidden)] | ||||||
|  |     fn convert_poll_message(message: proto::PollMessage) -> Frame<Self::Poll>; | ||||||
|  | } | ||||||
|   | |||||||
| @@ -1,11 +1,24 @@ | |||||||
| use {frame, proto, ConnectionError}; | use {frame, Frame, ConnectionError, Peer, StreamId}; | ||||||
|  | use proto::{self, ReadySink, State}; | ||||||
|  |  | ||||||
| use tokio_io::{AsyncRead, AsyncWrite}; | use tokio_io::{AsyncRead, AsyncWrite}; | ||||||
| use tokio_io::codec::length_delimited; | use tokio_io::codec::length_delimited; | ||||||
|  |  | ||||||
|  | use http; | ||||||
|  |  | ||||||
| use futures::*; | use futures::*; | ||||||
|  |  | ||||||
| pub struct Connection<T> { | use ordermap::OrderMap; | ||||||
|  | use fnv::FnvHasher; | ||||||
|  |  | ||||||
|  | use std::marker::PhantomData; | ||||||
|  | use std::hash::BuildHasherDefault; | ||||||
|  |  | ||||||
|  | /// An H2 connection | ||||||
|  | pub struct Connection<T, P> { | ||||||
|     inner: Inner<T>, |     inner: Inner<T>, | ||||||
|  |     streams: StreamMap<State>, | ||||||
|  |     peer: PhantomData<P>, | ||||||
| } | } | ||||||
|  |  | ||||||
| type Inner<T> = | type Inner<T> = | ||||||
| @@ -15,13 +28,19 @@ type Inner<T> = | |||||||
|                 proto::FramedRead< |                 proto::FramedRead< | ||||||
|                     length_delimited::FramedRead<T>>>>>; |                     length_delimited::FramedRead<T>>>>>; | ||||||
|  |  | ||||||
| impl<T: AsyncRead + AsyncWrite> Connection<T> { | type StreamMap<T> = OrderMap<StreamId, T, BuildHasherDefault<FnvHasher>>; | ||||||
|     pub fn new(io: T) -> Connection<T> { |  | ||||||
|  | /// Returns a new `Connection` backed by the given `io`. | ||||||
|  | pub fn new<T, P>(io: T) -> Connection<T, P> | ||||||
|  |     where T: AsyncRead + AsyncWrite, | ||||||
|  |           P: Peer, | ||||||
|  | { | ||||||
|  |  | ||||||
|     // Delimit the frames |     // Delimit the frames | ||||||
|     let framed_read = length_delimited::Builder::new() |     let framed_read = length_delimited::Builder::new() | ||||||
|         .big_endian() |         .big_endian() | ||||||
|         .length_field_length(3) |         .length_field_length(3) | ||||||
|             .length_adjustment(6) |         .length_adjustment(9) | ||||||
|         .num_skip(0) // Don't skip the header |         .num_skip(0) // Don't skip the header | ||||||
|         .new_read(io); |         .new_read(io); | ||||||
|  |  | ||||||
| @@ -59,28 +78,104 @@ impl<T: AsyncRead + AsyncWrite> Connection<T> { | |||||||
|  |  | ||||||
|     Connection { |     Connection { | ||||||
|         inner: connection, |         inner: connection, | ||||||
|         } |         streams: StreamMap::default(), | ||||||
|  |         peer: PhantomData, | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| impl<T: AsyncRead + AsyncWrite> Stream for Connection<T> { | impl<T, P> Connection<T, P> | ||||||
|     type Item = frame::Frame; |     where T: AsyncRead + AsyncWrite, | ||||||
|  |           P: Peer, | ||||||
|  | { | ||||||
|  |     /// Completes when the connection has terminated | ||||||
|  |     pub fn poll_shutdown(&mut self) -> Poll<(), ConnectionError> { | ||||||
|  |         try_ready!(self.poll_complete()); | ||||||
|  |         Ok(Async::NotReady) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl<T, P> Stream for Connection<T, P> | ||||||
|  |     where T: AsyncRead + AsyncWrite, | ||||||
|  |           P: Peer, | ||||||
|  | { | ||||||
|  |     type Item = Frame<P::Poll>; | ||||||
|     type Error = ConnectionError; |     type Error = ConnectionError; | ||||||
|  |  | ||||||
|     fn poll(&mut self) -> Poll<Option<frame::Frame>, ConnectionError> { |     fn poll(&mut self) -> Poll<Option<Self::Item>, ConnectionError> { | ||||||
|         self.inner.poll() |         use frame::Frame::*; | ||||||
|  |  | ||||||
|  |         match try_ready!(self.inner.poll()) { | ||||||
|  |             Some(Headers(v)) => unimplemented!(), | ||||||
|  |             Some(frame) => panic!("unexpected frame; frame={:?}", frame), | ||||||
|  |             None => return Ok(Async::Ready(None)), | ||||||
|  |             _ => unimplemented!(), | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| impl<T: AsyncRead + AsyncWrite> Sink for Connection<T> { | impl<T, P> Sink for Connection<T, P> | ||||||
|     type SinkItem = frame::Frame; |     where T: AsyncRead + AsyncWrite, | ||||||
|  |           P: Peer, | ||||||
|  | { | ||||||
|  |     type SinkItem = Frame<P::Send>; | ||||||
|     type SinkError = ConnectionError; |     type SinkError = ConnectionError; | ||||||
|  |  | ||||||
|     fn start_send(&mut self, item: frame::Frame) -> StartSend<frame::Frame, ConnectionError> { |     fn start_send(&mut self, item: Self::SinkItem) | ||||||
|         self.inner.start_send(item) |         -> StartSend<Self::SinkItem, Self::SinkError> | ||||||
|  |     { | ||||||
|  |         // First ensure that the upstream can process a new item | ||||||
|  |         if !try!(self.poll_ready()).is_ready() { | ||||||
|  |             return Ok(AsyncSink::NotReady(item)); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         match item { | ||||||
|  |             Frame::Message { id, message, body } => { | ||||||
|  |                 // Ensure ID is valid | ||||||
|  |                 try!(P::check_initiating_id(id)); | ||||||
|  |  | ||||||
|  |                 // TODO: Ensure available capacity for a new stream | ||||||
|  |                 // This won't be as simple as self.streams.len() as closed | ||||||
|  |                 // connections should not be factored. | ||||||
|  |  | ||||||
|  |                 // Transition the stream state, creating a new entry if needed | ||||||
|  |                 try!(self.streams.entry(id) | ||||||
|  |                      .or_insert(State::default()) | ||||||
|  |                      .send_headers()); | ||||||
|  |  | ||||||
|  |                 let message = P::convert_send_message(id, message, body); | ||||||
|  |  | ||||||
|  |                 // TODO: Handle trailers and all that jazz | ||||||
|  |  | ||||||
|  |                 // We already ensured that the upstream can handle the frame, so | ||||||
|  |                 // panic if it gets rejected. | ||||||
|  |                 let res = try!(self.inner.start_send(frame::Frame::Headers(message.frame))); | ||||||
|  |  | ||||||
|  |                 // This is a one-way conversion. By checking `poll_ready` first, | ||||||
|  |                 // it's already been determined that the inner `Sink` can accept | ||||||
|  |                 // the item. If the item is rejected, then there is a bug. | ||||||
|  |                 assert!(res.is_ready()); | ||||||
|  |  | ||||||
|  |                 Ok(AsyncSink::Ready) | ||||||
|  |             } | ||||||
|  |             Frame::Body { id, chunk } => { | ||||||
|  |                 unimplemented!(); | ||||||
|  |             } | ||||||
|  |             Frame::Error { id, error } => { | ||||||
|  |                 unimplemented!(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn poll_complete(&mut self) -> Poll<(), ConnectionError> { |     fn poll_complete(&mut self) -> Poll<(), ConnectionError> { | ||||||
|         self.inner.poll_complete() |         self.inner.poll_complete() | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | impl<T, P> ReadySink for Connection<T, P> | ||||||
|  |     where T: AsyncRead + AsyncWrite, | ||||||
|  |           P: Peer, | ||||||
|  | { | ||||||
|  |     fn poll_ready(&mut self) -> Poll<(), Self::SinkError> { | ||||||
|  |         self.inner.poll_ready() | ||||||
|  |     } | ||||||
|  | } | ||||||
|   | |||||||
| @@ -54,6 +54,13 @@ impl<T> FramedRead<T> { | |||||||
|  |  | ||||||
|                 // TODO: Change to drain: carllerche/bytes#130 |                 // TODO: Change to drain: carllerche/bytes#130 | ||||||
|                 let frame = try!(frame::Headers::load(head, &mut buf, &mut self.hpack)); |                 let frame = try!(frame::Headers::load(head, &mut buf, &mut self.hpack)); | ||||||
|  |  | ||||||
|  |                 if !frame.is_end_headers() { | ||||||
|  |                     // Wait for continuation frames | ||||||
|  |                     self.partial = Some(Partial::Headers(frame)); | ||||||
|  |                     return Ok(None); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|                 frame.into() |                 frame.into() | ||||||
|             } |             } | ||||||
|             Kind::Priority => unimplemented!(), |             Kind::Priority => unimplemented!(), | ||||||
| @@ -88,6 +95,7 @@ impl<T> Stream for FramedRead<T> | |||||||
|             }; |             }; | ||||||
|  |  | ||||||
|             if let Some(frame) = try!(self.decode_frame(bytes)) { |             if let Some(frame) = try!(self.decode_frame(bytes)) { | ||||||
|  |                 debug!("poll; frame={:?}", frame); | ||||||
|                 return Ok(Async::Ready(Some(frame))); |                 return Ok(Async::Ready(Some(frame))); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| use {ConnectionError, Reason}; | use {hpack, ConnectionError, Reason}; | ||||||
| use frame::{self, Frame, Error}; | use frame::{self, Frame, Error}; | ||||||
| use hpack; | use proto::ReadySink; | ||||||
|  |  | ||||||
| use futures::*; | use futures::*; | ||||||
| use tokio_io::AsyncWrite; | use tokio_io::AsyncWrite; | ||||||
| @@ -67,7 +67,7 @@ impl<T: AsyncWrite> FramedWrite<T> { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn is_empty(&self) -> bool { |     fn is_empty(&self) -> bool { | ||||||
|         self.next.is_none() && self.buf.has_remaining() |         self.next.is_none() && !self.buf.has_remaining() | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn frame_len(&self, data: &frame::Data) -> usize { |     fn frame_len(&self, data: &frame::Data) -> usize { | ||||||
| @@ -80,14 +80,11 @@ impl<T: AsyncWrite> Sink for FramedWrite<T> { | |||||||
|     type SinkError = ConnectionError; |     type SinkError = ConnectionError; | ||||||
|  |  | ||||||
|     fn start_send(&mut self, item: Frame) -> StartSend<Frame, ConnectionError> { |     fn start_send(&mut self, item: Frame) -> StartSend<Frame, ConnectionError> { | ||||||
|         if self.has_capacity() { |         debug!("start_send; frame={:?}", item); | ||||||
|             // Try flushing |  | ||||||
|             try!(self.poll_complete()); |  | ||||||
|  |  | ||||||
|             if self.has_capacity() { |         if !try!(self.poll_ready()).is_ready() { | ||||||
|             return Ok(AsyncSink::NotReady(item)); |             return Ok(AsyncSink::NotReady(item)); | ||||||
|         } |         } | ||||||
|         } |  | ||||||
|  |  | ||||||
|         match item { |         match item { | ||||||
|             Frame::Data(v) => { |             Frame::Data(v) => { | ||||||
| @@ -117,6 +114,7 @@ impl<T: AsyncWrite> Sink for FramedWrite<T> { | |||||||
|             } |             } | ||||||
|             Frame::Settings(v) => { |             Frame::Settings(v) => { | ||||||
|                 v.encode(self.buf.get_mut()); |                 v.encode(self.buf.get_mut()); | ||||||
|  |                 trace!("encoded settings; rem={:?}", self.buf.remaining()); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -124,6 +122,8 @@ impl<T: AsyncWrite> Sink for FramedWrite<T> { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn poll_complete(&mut self) -> Poll<(), ConnectionError> { |     fn poll_complete(&mut self) -> Poll<(), ConnectionError> { | ||||||
|  |         trace!("FramedWrite::poll_complete"); | ||||||
|  |  | ||||||
|         // TODO: implement |         // TODO: implement | ||||||
|         match self.next { |         match self.next { | ||||||
|             Some(Next::Data { .. }) => unimplemented!(), |             Some(Next::Data { .. }) => unimplemented!(), | ||||||
| @@ -132,9 +132,14 @@ impl<T: AsyncWrite> Sink for FramedWrite<T> { | |||||||
|  |  | ||||||
|         // As long as there is data to write, try to write it! |         // As long as there is data to write, try to write it! | ||||||
|         while !self.is_empty() { |         while !self.is_empty() { | ||||||
|  |             trace!("writing buffer; next={:?}; rem={:?}", self.next, self.buf.remaining()); | ||||||
|             try_ready!(self.inner.write_buf(&mut self.buf)); |             try_ready!(self.inner.write_buf(&mut self.buf)); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         trace!("flushing buffer"); | ||||||
|  |         // Flush the upstream | ||||||
|  |         try_nb!(self.inner.flush()); | ||||||
|  |  | ||||||
|         // Clear internal buffer |         // Clear internal buffer | ||||||
|         self.buf.set_position(0); |         self.buf.set_position(0); | ||||||
|         self.buf.get_mut().clear(); |         self.buf.get_mut().clear(); | ||||||
| @@ -148,6 +153,21 @@ impl<T: AsyncWrite> Sink for FramedWrite<T> { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | impl<T: AsyncWrite> ReadySink for FramedWrite<T> { | ||||||
|  |     fn poll_ready(&mut self) -> Poll<(), Self::SinkError> { | ||||||
|  |         if !self.has_capacity() { | ||||||
|  |             // Try flushing | ||||||
|  |             try!(self.poll_complete()); | ||||||
|  |  | ||||||
|  |             if !self.has_capacity() { | ||||||
|  |                 return Ok(Async::NotReady); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         Ok(Async::Ready(())) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| impl<T: Stream> Stream for FramedWrite<T> { | impl<T: Stream> Stream for FramedWrite<T> { | ||||||
|     type Item = T::Item; |     type Item = T::Item; | ||||||
|     type Error = T::Error; |     type Error = T::Error; | ||||||
|   | |||||||
| @@ -2,10 +2,34 @@ mod connection; | |||||||
| mod framed_read; | mod framed_read; | ||||||
| mod framed_write; | mod framed_write; | ||||||
| mod ping_pong; | mod ping_pong; | ||||||
|  | mod ready; | ||||||
| mod settings; | mod settings; | ||||||
|  | mod state; | ||||||
|  |  | ||||||
| pub use self::connection::Connection; | pub use self::connection::{Connection, new as new_connection}; | ||||||
| pub use self::framed_read::FramedRead; | pub use self::framed_read::FramedRead; | ||||||
| pub use self::framed_write::FramedWrite; | pub use self::framed_write::FramedWrite; | ||||||
| pub use self::ping_pong::PingPong; | pub use self::ping_pong::PingPong; | ||||||
|  | pub use self::ready::ReadySink; | ||||||
| pub use self::settings::Settings; | pub use self::settings::Settings; | ||||||
|  | pub use self::state::State; | ||||||
|  |  | ||||||
|  | use frame; | ||||||
|  |  | ||||||
|  | /// A request or response issued by the current process. | ||||||
|  | pub struct SendMessage { | ||||||
|  |     frame: frame::Headers, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// A request or response received by the current process. | ||||||
|  | pub struct PollMessage { | ||||||
|  |     frame: frame::Headers, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl SendMessage { | ||||||
|  |     pub fn new(frame: frame::Headers) -> Self { | ||||||
|  |         SendMessage { | ||||||
|  |             frame: frame, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|   | |||||||
| @@ -1,5 +1,6 @@ | |||||||
| use ConnectionError; | use ConnectionError; | ||||||
| use frame::Frame; | use frame::Frame; | ||||||
|  | use proto::ReadySink; | ||||||
|  |  | ||||||
| use futures::*; | use futures::*; | ||||||
|  |  | ||||||
| @@ -45,3 +46,13 @@ impl<T> Sink for PingPong<T> | |||||||
|         self.inner.poll_complete() |         self.inner.poll_complete() | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | impl<T> ReadySink for PingPong<T> | ||||||
|  |     where T: Stream<Item = Frame, Error = ConnectionError>, | ||||||
|  |           T: Sink<SinkItem = Frame, SinkError = ConnectionError>, | ||||||
|  |           T: ReadySink, | ||||||
|  | { | ||||||
|  |     fn poll_ready(&mut self) -> Poll<(), ConnectionError> { | ||||||
|  |         self.inner.poll_ready() | ||||||
|  |     } | ||||||
|  | } | ||||||
|   | |||||||
							
								
								
									
										5
									
								
								src/proto/ready.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								src/proto/ready.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | |||||||
|  | use futures::{Sink, Poll}; | ||||||
|  |  | ||||||
|  | pub trait ReadySink: Sink { | ||||||
|  |     fn poll_ready(&mut self) -> Poll<(), Self::SinkError>; | ||||||
|  | } | ||||||
| @@ -1,16 +1,22 @@ | |||||||
| use ConnectionError; | use ConnectionError; | ||||||
| use frame::{self, Frame}; | use frame::{self, Frame}; | ||||||
|  | use proto::ReadySink; | ||||||
|  |  | ||||||
| use futures::*; | use futures::*; | ||||||
|  |  | ||||||
| pub struct Settings<T> { | pub struct Settings<T> { | ||||||
|  |     // Upstream transport | ||||||
|     inner: T, |     inner: T, | ||||||
|  |  | ||||||
|     // Our settings |     // Our settings | ||||||
|     local: frame::SettingSet, |     local: frame::SettingSet, | ||||||
|  |  | ||||||
|     // Peer settings |     // Peer settings | ||||||
|     remote: frame::SettingSet, |     remote: frame::SettingSet, | ||||||
|  |  | ||||||
|     // Number of acks remaining to send to the peer |     // Number of acks remaining to send to the peer | ||||||
|     remaining_acks: usize, |     remaining_acks: usize, | ||||||
|  |  | ||||||
|     // True when the local settings must be flushed to the remote |     // True when the local settings must be flushed to the remote | ||||||
|     is_dirty: bool, |     is_dirty: bool, | ||||||
| } | } | ||||||
| @@ -30,17 +36,13 @@ impl<T> Settings<T> | |||||||
|             local: local, |             local: local, | ||||||
|             remote: frame::SettingSet::default(), |             remote: frame::SettingSet::default(), | ||||||
|             remaining_acks: 0, |             remaining_acks: 0, | ||||||
|             is_dirty: false, |             is_dirty: true, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn has_pending_sends(&self) -> bool { |  | ||||||
|         self.is_dirty || self.remaining_acks > 0 |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fn try_send_pending(&mut self) -> Poll<(), ConnectionError> { |     fn try_send_pending(&mut self) -> Poll<(), ConnectionError> { | ||||||
|         if self.is_dirty { |         if self.is_dirty { | ||||||
|             let frame = frame::Settings::new(self.local.clone()).into(); |             let frame = frame::Settings::new(self.local.clone()); | ||||||
|             try_ready!(self.try_send(frame)); |             try_ready!(self.try_send(frame)); | ||||||
|  |  | ||||||
|             self.is_dirty = false; |             self.is_dirty = false; | ||||||
| @@ -56,8 +58,8 @@ impl<T> Settings<T> | |||||||
|         Ok(Async::Ready(())) |         Ok(Async::Ready(())) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn try_send(&mut self, item: frame::Frame) -> Poll<(), ConnectionError> { |     fn try_send(&mut self, item: frame::Settings) -> Poll<(), ConnectionError> { | ||||||
|         if let AsyncSink::NotReady(_) = try!(self.inner.start_send(item)) { |         if let AsyncSink::NotReady(_) = try!(self.inner.start_send(item.into())) { | ||||||
|             // Ensure that call to `poll_complete` guarantee is called to satisfied |             // Ensure that call to `poll_complete` guarantee is called to satisfied | ||||||
|             try!(self.inner.poll_complete()); |             try!(self.inner.poll_complete()); | ||||||
|  |  | ||||||
| @@ -110,3 +112,17 @@ impl<T> Sink for Settings<T> | |||||||
|         self.inner.close() |         self.inner.close() | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | impl<T> ReadySink for Settings<T> | ||||||
|  |     where T: Stream<Item = Frame, Error = ConnectionError>, | ||||||
|  |           T: Sink<SinkItem = Frame, SinkError = ConnectionError>, | ||||||
|  |           T: ReadySink, | ||||||
|  | { | ||||||
|  |     fn poll_ready(&mut self) -> Poll<(), ConnectionError> { | ||||||
|  |         if try!(self.try_send_pending()).is_ready() { | ||||||
|  |             return self.inner.poll_ready(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         Ok(Async::NotReady) | ||||||
|  |     } | ||||||
|  | } | ||||||
|   | |||||||
							
								
								
									
										72
									
								
								src/proto/state.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								src/proto/state.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,72 @@ | |||||||
|  | use ConnectionError; | ||||||
|  |  | ||||||
|  | /// Represents the state of an H2 stream | ||||||
|  | /// | ||||||
|  | /// ```not_rust | ||||||
|  | ///                              +--------+ | ||||||
|  | ///                      send PP |        | recv PP | ||||||
|  | ///                     ,--------|  idle  |--------. | ||||||
|  | ///                    /         |        |         \ | ||||||
|  | ///                   v          +--------+          v | ||||||
|  | ///            +----------+          |           +----------+ | ||||||
|  | ///            |          |          | send H /  |          | | ||||||
|  | ///     ,------| reserved |          | recv H    | reserved |------. | ||||||
|  | ///     |      | (local)  |          |           | (remote) |      | | ||||||
|  | ///     |      +----------+          v           +----------+      | | ||||||
|  | ///     |          |             +--------+             |          | | ||||||
|  | ///     |          |     recv ES |        | send ES     |          | | ||||||
|  | ///     |   send H |     ,-------|  open  |-------.     | recv H   | | ||||||
|  | ///     |          |    /        |        |        \    |          | | ||||||
|  | ///     |          v   v         +--------+         v   v          | | ||||||
|  | ///     |      +----------+          |           +----------+      | | ||||||
|  | ///     |      |   half   |          |           |   half   |      | | ||||||
|  | ///     |      |  closed  |          | send R /  |  closed  |      | | ||||||
|  | ///     |      | (remote) |          | recv R    | (local)  |      | | ||||||
|  | ///     |      +----------+          |           +----------+      | | ||||||
|  | ///     |           |                |                 |           | | ||||||
|  | ///     |           | send ES /      |       recv ES / |           | | ||||||
|  | ///     |           | send R /       v        send R / |           | | ||||||
|  | ///     |           | recv R     +--------+   recv R   |           | | ||||||
|  | ///     | send R /  `----------->|        |<-----------'  send R / | | ||||||
|  | ///     | recv R                 | closed |               recv R   | | ||||||
|  | ///     `----------------------->|        |<----------------------' | ||||||
|  | ///                              +--------+ | ||||||
|  | /// | ||||||
|  | ///        send:   endpoint sends this frame | ||||||
|  | ///        recv:   endpoint receives this frame | ||||||
|  | /// | ||||||
|  | ///        H:  HEADERS frame (with implied CONTINUATIONs) | ||||||
|  | ///        PP: PUSH_PROMISE frame (with implied CONTINUATIONs) | ||||||
|  | ///        ES: END_STREAM flag | ||||||
|  | ///        R:  RST_STREAM frame | ||||||
|  | /// ``` | ||||||
|  | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||||||
|  | pub enum State { | ||||||
|  |     Idle, | ||||||
|  |     ReservedLocal, | ||||||
|  |     ReservedRemote, | ||||||
|  |     Open, | ||||||
|  |     HalfClosedLocal, | ||||||
|  |     HalfClosedRemote, | ||||||
|  |     Closed, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl State { | ||||||
|  |     /// Transition the state to represent headers being sent. | ||||||
|  |     /// | ||||||
|  |     /// Returns an error if this is an invalid state transition. | ||||||
|  |     pub fn send_headers(&mut self) -> Result<(), ConnectionError> { | ||||||
|  |         if *self != State::Idle { | ||||||
|  |             unimplemented!(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         *self = State::Open; | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl Default for State { | ||||||
|  |     fn default() -> State { | ||||||
|  |         State::Idle | ||||||
|  |     } | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user