Get a request sent
This commit is contained in:
		| @@ -1,10 +1,10 @@ | ||||
| use {frame, Frame, ConnectionError, Peer, StreamId}; | ||||
| use client::Client; | ||||
| use proto::{self, ReadySink, State}; | ||||
|  | ||||
| use tokio_io::{AsyncRead, AsyncWrite}; | ||||
| use tokio_io::codec::length_delimited; | ||||
|  | ||||
| use http; | ||||
| use http::{self, request, response}; | ||||
|  | ||||
| use futures::*; | ||||
|  | ||||
| @@ -15,74 +15,28 @@ use std::marker::PhantomData; | ||||
| use std::hash::BuildHasherDefault; | ||||
|  | ||||
| /// An H2 connection | ||||
| #[derive(Debug)] | ||||
| pub struct Connection<T, P> { | ||||
|     inner: Inner<T>, | ||||
|     inner: proto::Inner<T>, | ||||
|     streams: StreamMap<State>, | ||||
|     peer: PhantomData<P>, | ||||
| } | ||||
|  | ||||
| type Inner<T> = | ||||
|     proto::Settings< | ||||
|         proto::PingPong< | ||||
|             proto::FramedWrite< | ||||
|                 proto::FramedRead< | ||||
|                     length_delimited::FramedRead<T>>>>>; | ||||
|  | ||||
| type StreamMap<T> = OrderMap<StreamId, T, BuildHasherDefault<FnvHasher>>; | ||||
|  | ||||
| /// Returns a new `Connection` backed by the given `io`. | ||||
| pub fn new<T, P>(io: T) -> Connection<T, P> | ||||
| impl<T, P> From<proto::Inner<T>> for Connection<T, P> | ||||
|     where T: AsyncRead + AsyncWrite, | ||||
|           P: Peer, | ||||
| { | ||||
|  | ||||
|     // Delimit the frames | ||||
|     let framed_read = length_delimited::Builder::new() | ||||
|         .big_endian() | ||||
|         .length_field_length(3) | ||||
|         .length_adjustment(9) | ||||
|         .num_skip(0) // Don't skip the header | ||||
|         .new_read(io); | ||||
|  | ||||
|     // Map to `Frame` types | ||||
|     let framed_read = proto::FramedRead::new(framed_read); | ||||
|  | ||||
|     // Frame encoder | ||||
|     let mut framed = proto::FramedWrite::new(framed_read); | ||||
|  | ||||
|     // Ok, so this is a **little** hacky, but it works for now. | ||||
|     // | ||||
|     // The ping/pong behavior SHOULD be given highest priority (6.7). | ||||
|     // However, the connection handshake requires the settings frame to be | ||||
|     // sent as the very first one. This needs special handling because | ||||
|     // otherwise there is a race condition where the peer could send its | ||||
|     // settings frame followed immediately by a Ping, in which case, we | ||||
|     // don't want to accidentally send the pong before finishing the | ||||
|     // connection hand shake. | ||||
|     // | ||||
|     // So, to ensure correct ordering, we write the settings frame here | ||||
|     // before fully constructing the connection struct. Technically, `Async` | ||||
|     // operations should not be performed in `new` because this might not | ||||
|     // happen on a task, however we have full control of the I/O and we know | ||||
|     // that the settings frame will get buffered and not actually perform an | ||||
|     // I/O op. | ||||
|     let initial_settings = frame::SettingSet::default(); | ||||
|     let frame = frame::Settings::new(initial_settings.clone()); | ||||
|     assert!(framed.start_send(frame.into()).unwrap().is_ready()); | ||||
|  | ||||
|     // Add ping/pong handler | ||||
|     let ping_pong = proto::PingPong::new(framed); | ||||
|  | ||||
|     // Add settings handler | ||||
|     let connection = proto::Settings::new(ping_pong, initial_settings); | ||||
|  | ||||
|     Connection { | ||||
|         inner: connection, | ||||
|         streams: StreamMap::default(), | ||||
|         peer: PhantomData, | ||||
|     fn from(src: proto::Inner<T>) -> Self { | ||||
|         Connection { | ||||
|             inner: src, | ||||
|             streams: StreamMap::default(), | ||||
|             peer: PhantomData, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| type StreamMap<T> = OrderMap<StreamId, T, BuildHasherDefault<FnvHasher>>; | ||||
|  | ||||
| impl<T, P> Connection<T, P> | ||||
|     where T: AsyncRead + AsyncWrite, | ||||
|           P: Peer, | ||||
| @@ -94,6 +48,23 @@ impl<T, P> Connection<T, P> | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T> Connection<T, Client> | ||||
|     where T: AsyncRead + AsyncWrite, | ||||
| { | ||||
|     pub fn send_request(self, | ||||
|                         id: StreamId, // TODO: Generate one internally? | ||||
|                         request: request::Head, | ||||
|                         end_of_stream: bool) | ||||
|         -> sink::Send<Self> | ||||
|     { | ||||
|         self.send(Frame::Headers { | ||||
|             id: id, | ||||
|             headers: request, | ||||
|             end_of_stream: end_of_stream, | ||||
|         }) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T, P> Stream for Connection<T, P> | ||||
|     where T: AsyncRead + AsyncWrite, | ||||
|           P: Peer, | ||||
| @@ -104,8 +75,15 @@ impl<T, P> Stream for Connection<T, P> | ||||
|     fn poll(&mut self) -> Poll<Option<Self::Item>, ConnectionError> { | ||||
|         use frame::Frame::*; | ||||
|  | ||||
|         // Because receiving new frames may depend on ensuring that the write | ||||
|         // buffer is clear, `poll_complete` is called here. | ||||
|         let _ = try!(self.poll_complete()); | ||||
|  | ||||
|         match try_ready!(self.inner.poll()) { | ||||
|             Some(Headers(v)) => unimplemented!(), | ||||
|             Some(Headers(v)) => { | ||||
|                 debug!("poll; frame={:?}", v); | ||||
|                 unimplemented!(); | ||||
|             } | ||||
|             Some(frame) => panic!("unexpected frame; frame={:?}", frame), | ||||
|             None => return Ok(Async::Ready(None)), | ||||
|             _ => unimplemented!(), | ||||
| @@ -129,7 +107,7 @@ impl<T, P> Sink for Connection<T, P> | ||||
|         } | ||||
|  | ||||
|         match item { | ||||
|             Frame::Message { id, message, body } => { | ||||
|             Frame::Headers { id, headers, end_of_stream } => { | ||||
|                 // Ensure ID is valid | ||||
|                 try!(P::check_initiating_id(id)); | ||||
|  | ||||
| @@ -138,17 +116,18 @@ impl<T, P> Sink for Connection<T, P> | ||||
|                 // connections should not be factored. | ||||
|  | ||||
|                 // Transition the stream state, creating a new entry if needed | ||||
|                 // | ||||
|                 // TODO: Response can send multiple headers frames before body | ||||
|                 // (1xx responses). | ||||
|                 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 | ||||
|                 let frame = P::convert_send_message(id, headers, end_of_stream); | ||||
|  | ||||
|                 // 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))); | ||||
|                 let res = try!(self.inner.start_send(frame::Frame::Headers(frame))); | ||||
|  | ||||
|                 // This is a one-way conversion. By checking `poll_ready` first, | ||||
|                 // it's already been determined that the inner `Sink` can accept | ||||
| @@ -157,7 +136,13 @@ impl<T, P> Sink for Connection<T, P> | ||||
|  | ||||
|                 Ok(AsyncSink::Ready) | ||||
|             } | ||||
|             Frame::Body { id, chunk } => { | ||||
|             Frame::Trailers { id, headers } => { | ||||
|                 unimplemented!(); | ||||
|             } | ||||
|             Frame::Body { id, chunk, end_of_stream } => { | ||||
|                 unimplemented!(); | ||||
|             } | ||||
|             Frame::PushPromise { id, promise } => { | ||||
|                 unimplemented!(); | ||||
|             } | ||||
|             Frame::Error { id, error } => { | ||||
|   | ||||
| @@ -9,6 +9,7 @@ use bytes::{Bytes, BytesMut, Buf}; | ||||
|  | ||||
| use std::io::{self, Write, Cursor}; | ||||
|  | ||||
| #[derive(Debug)] | ||||
| pub struct FramedRead<T> { | ||||
|     inner: T, | ||||
|  | ||||
| @@ -19,6 +20,7 @@ pub struct FramedRead<T> { | ||||
| } | ||||
|  | ||||
| /// Partially loaded headers frame | ||||
| #[derive(Debug)] | ||||
| enum Partial { | ||||
|     Headers(frame::Headers), | ||||
|     PushPromise(frame::PushPromise), | ||||
| @@ -71,9 +73,15 @@ impl<T> FramedRead<T> { | ||||
|             } | ||||
|             Kind::PushPromise => unimplemented!(), | ||||
|             Kind::Ping => unimplemented!(), | ||||
|             Kind::GoAway => unimplemented!(), | ||||
|             Kind::GoAway => { | ||||
|                 let frame = try!(frame::GoAway::load(&bytes[frame::HEADER_LEN..])); | ||||
|                 debug!("decoded; frame={:?}", frame); | ||||
|                 unimplemented!(); | ||||
|             } | ||||
|             Kind::WindowUpdate => unimplemented!(), | ||||
|             Kind::Continuation => unimplemented!(), | ||||
|             Kind::Continuation => { | ||||
|                 unimplemented!(); | ||||
|             } | ||||
|             Kind::Unknown => return Ok(None), | ||||
|         }; | ||||
|  | ||||
|   | ||||
							
								
								
									
										131
									
								
								src/proto/handshake.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								src/proto/handshake.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,131 @@ | ||||
| use {ConnectionError, Peer}; | ||||
| use frame::{self, Frame}; | ||||
| use proto::{self, Connection}; | ||||
|  | ||||
| use tokio_io::{AsyncRead, AsyncWrite}; | ||||
| use tokio_io::codec::length_delimited; | ||||
|  | ||||
| use futures::{Future, Sink, Stream, Poll, Async, AsyncSink}; | ||||
|  | ||||
| use std::marker::PhantomData; | ||||
|  | ||||
| /// Implements the settings component of the initial H2 handshake | ||||
| pub struct Handshake<T, P> { | ||||
|     // Upstream transport | ||||
|     inner: Option<Inner<T>>, | ||||
|  | ||||
|     // True when the local settings have been sent | ||||
|     settings_sent: bool, | ||||
|  | ||||
|     // Peer | ||||
|     peer: PhantomData<P>, | ||||
| } | ||||
|  | ||||
| struct Inner<T> { | ||||
|     // Upstream transport | ||||
|     framed: proto::Framed<T>, | ||||
|  | ||||
|     // Our settings | ||||
|     local: frame::SettingSet, | ||||
| } | ||||
|  | ||||
| impl<T, P> Handshake<T, P> | ||||
|     where T: AsyncRead + AsyncWrite, | ||||
| { | ||||
|     /// Initiate an HTTP/2.0 handshake. | ||||
|     pub fn new(io: T, local: frame::SettingSet) -> Self { | ||||
|         // Delimit the frames | ||||
|         let framed_read = length_delimited::Builder::new() | ||||
|             .big_endian() | ||||
|             .length_field_length(3) | ||||
|             .length_adjustment(9) | ||||
|             .num_skip(0) // Don't skip the header | ||||
|             .new_read(io); | ||||
|  | ||||
|         // Map to `Frame` types | ||||
|         let framed_read = proto::FramedRead::new(framed_read); | ||||
|  | ||||
|         // Frame encoder | ||||
|         let mut framed = proto::FramedWrite::new(framed_read); | ||||
|  | ||||
|         Handshake { | ||||
|             inner: Some(Inner { | ||||
|                 framed: framed, | ||||
|                 local: local, | ||||
|             }), | ||||
|             settings_sent: false, | ||||
|             peer: PhantomData, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// Returns a reference to the local settings. | ||||
|     /// | ||||
|     /// # Panics | ||||
|     /// | ||||
|     /// Panics if `HandshakeInner` has already been consumed. | ||||
|     fn local(&self) -> &frame::SettingSet { | ||||
|         &self.inner.as_ref().unwrap().local | ||||
|     } | ||||
|  | ||||
|     /// Returns a mutable reference to `HandshakeInner`. | ||||
|     /// | ||||
|     /// # Panics | ||||
|     /// | ||||
|     /// Panics if `HandshakeInner` has already been consumed. | ||||
|     fn inner_mut(&mut self) -> &mut proto::Framed<T> { | ||||
|         &mut self.inner.as_mut().unwrap().framed | ||||
|     } | ||||
| } | ||||
|  | ||||
| // Either a client or server. satisfied when we have sent a SETTINGS frame and | ||||
| // have sent an ACK for the remote's settings. | ||||
| impl<T, P> Future for Handshake<T, P> | ||||
|     where T: AsyncRead + AsyncWrite, | ||||
|           P: Peer, | ||||
| { | ||||
|     type Item = Connection<T, P>; | ||||
|     type Error = ConnectionError; | ||||
|  | ||||
|     fn poll(&mut self) -> Poll<Self::Item, Self::Error> { | ||||
|         if !self.settings_sent { | ||||
|             let frame = frame::Settings::new(self.local().clone()).into(); | ||||
|  | ||||
|             if let AsyncSink::NotReady(_) = try!(self.inner_mut().start_send(frame)) { | ||||
|                 // This shouldn't really happen, but if it does, try again | ||||
|                 // later. | ||||
|                 return Ok(Async::NotReady); | ||||
|             } | ||||
|  | ||||
|             // Try flushing... | ||||
|             try!(self.inner_mut().poll_complete()); | ||||
|  | ||||
|             self.settings_sent = true; | ||||
|         } | ||||
|  | ||||
|         match try_ready!(self.inner_mut().poll()) { | ||||
|             Some(Frame::Settings(v)) => { | ||||
|                 if v.is_ack() { | ||||
|                     // TODO: unexpected ACK, protocol error | ||||
|                     unimplemented!(); | ||||
|                 } else { | ||||
|                     let remote = v.into_set(); | ||||
|                     let inner = self.inner.take().unwrap(); | ||||
|  | ||||
|                     // Add ping/pong handler | ||||
|                     let ping_pong = proto::PingPong::new(inner.framed); | ||||
|  | ||||
|                     // Add settings handler | ||||
|                     let settings = proto::Settings::new( | ||||
|                         ping_pong, inner.local, remote); | ||||
|  | ||||
|                     // Finally, convert to the `Connection` | ||||
|                     let connection = settings.into(); | ||||
|  | ||||
|                     return Ok(Async::Ready(connection)); | ||||
|                 } | ||||
|             } | ||||
|             // TODO: handle handshake failure | ||||
|             _ => unimplemented!(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,35 +1,30 @@ | ||||
| mod connection; | ||||
| mod framed_read; | ||||
| mod framed_write; | ||||
| mod handshake; | ||||
| mod ping_pong; | ||||
| mod ready; | ||||
| mod settings; | ||||
| mod state; | ||||
|  | ||||
| pub use self::connection::{Connection, new as new_connection}; | ||||
| pub use self::connection::{Connection}; | ||||
| pub use self::framed_read::FramedRead; | ||||
| pub use self::framed_write::FramedWrite; | ||||
| pub use self::handshake::Handshake; | ||||
| pub use self::ping_pong::PingPong; | ||||
| pub use self::ready::ReadySink; | ||||
| pub use self::settings::Settings; | ||||
| pub use self::state::State; | ||||
|  | ||||
| use frame; | ||||
| use tokio_io::codec::length_delimited; | ||||
|  | ||||
| /// A request or response issued by the current process. | ||||
| pub struct SendMessage { | ||||
|     frame: frame::Headers, | ||||
| } | ||||
| /// Base HTTP/2.0 transport. Only handles framing. | ||||
| type Framed<T> = | ||||
|     FramedWrite< | ||||
|         FramedRead< | ||||
|             length_delimited::FramedRead<T>>>; | ||||
|  | ||||
| /// 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, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| type Inner<T> = | ||||
|     Settings< | ||||
|         PingPong< | ||||
|             Framed<T>>>; | ||||
|   | ||||
| @@ -4,6 +4,7 @@ use proto::ReadySink; | ||||
|  | ||||
| use futures::*; | ||||
|  | ||||
| #[derive(Debug)] | ||||
| pub struct PingPong<T> { | ||||
|     inner: T, | ||||
| } | ||||
|   | ||||
| @@ -4,6 +4,7 @@ use proto::ReadySink; | ||||
|  | ||||
| use futures::*; | ||||
|  | ||||
| #[derive(Debug)] | ||||
| pub struct Settings<T> { | ||||
|     // Upstream transport | ||||
|     inner: T, | ||||
| @@ -21,22 +22,17 @@ pub struct Settings<T> { | ||||
|     is_dirty: bool, | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * TODO: | ||||
|  * - Settings ack timeout for connection error | ||||
|  */ | ||||
|  | ||||
| impl<T> Settings<T> | ||||
|     where T: Stream<Item = Frame, Error = ConnectionError>, | ||||
|           T: Sink<SinkItem = Frame, SinkError = ConnectionError>, | ||||
| { | ||||
|     pub fn new(inner: T, local: frame::SettingSet) -> Settings<T> { | ||||
|     pub fn new(inner: T, local: frame::SettingSet, remote: frame::SettingSet) -> Settings<T> { | ||||
|         Settings { | ||||
|             inner: inner, | ||||
|             local: local, | ||||
|             remote: frame::SettingSet::default(), | ||||
|             remaining_acks: 0, | ||||
|             is_dirty: true, | ||||
|             remote: remote, | ||||
|             remaining_acks: 1, | ||||
|             is_dirty: false, | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -60,7 +56,8 @@ impl<T> Settings<T> | ||||
|  | ||||
|     fn try_send(&mut self, item: frame::Settings) -> Poll<(), ConnectionError> { | ||||
|         if let AsyncSink::NotReady(_) = try!(self.inner.start_send(item.into())) { | ||||
|             // Ensure that call to `poll_complete` guarantee is called to satisfied | ||||
|             // TODO: I don't think this is needed actually... It was originally | ||||
|             // done to "satisfy the start_send" contract... | ||||
|             try!(self.inner.poll_complete()); | ||||
|  | ||||
|             return Ok(Async::NotReady); | ||||
| @@ -117,14 +114,12 @@ impl<T> Sink for Settings<T> | ||||
|     } | ||||
|  | ||||
|     fn poll_complete(&mut self) -> Poll<(), ConnectionError> { | ||||
|         try_ready!(self.try_send_pending()); | ||||
|         self.inner.poll_complete() | ||||
|     } | ||||
|  | ||||
|     fn close(&mut self) -> Poll<(), ConnectionError> { | ||||
|         if !try!(self.try_send_pending()).is_ready() { | ||||
|             return Ok(Async::NotReady); | ||||
|         } | ||||
|  | ||||
|         try_ready!(self.try_send_pending()); | ||||
|         self.inner.close() | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user