WIP
This commit is contained in:
		
							
								
								
									
										86
									
								
								src/proto/connection.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								src/proto/connection.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,86 @@ | |||||||
|  | use {frame, proto, ConnectionError}; | ||||||
|  | use tokio_io::{AsyncRead, AsyncWrite}; | ||||||
|  | use tokio_io::codec::length_delimited; | ||||||
|  |  | ||||||
|  | use futures::*; | ||||||
|  |  | ||||||
|  | pub struct Connection<T> { | ||||||
|  |     inner: Inner<T>, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type Inner<T> = | ||||||
|  |     proto::Settings< | ||||||
|  |         proto::PingPong< | ||||||
|  |             proto::FramedWrite< | ||||||
|  |                 proto::FramedRead< | ||||||
|  |                     length_delimited::FramedRead<T>>>>>; | ||||||
|  |  | ||||||
|  | impl<T: AsyncRead + AsyncWrite> Connection<T> { | ||||||
|  |     pub fn new(io: T) -> Connection<T> { | ||||||
|  |         // Delimit the frames | ||||||
|  |         let framed_read = length_delimited::Builder::new() | ||||||
|  |             .big_endian() | ||||||
|  |             .length_field_length(3) | ||||||
|  |             .length_adjustment(6) | ||||||
|  |             .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, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl<T: AsyncRead + AsyncWrite> Stream for Connection<T> { | ||||||
|  |     type Item = frame::Frame; | ||||||
|  |     type Error = ConnectionError; | ||||||
|  |  | ||||||
|  |     fn poll(&mut self) -> Poll<Option<frame::Frame>, ConnectionError> { | ||||||
|  |         self.inner.poll() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl<T: AsyncRead + AsyncWrite> Sink for Connection<T> { | ||||||
|  |     type SinkItem = frame::Frame; | ||||||
|  |     type SinkError = ConnectionError; | ||||||
|  |  | ||||||
|  |     fn start_send(&mut self, item: frame::Frame) -> StartSend<frame::Frame, ConnectionError> { | ||||||
|  |         self.inner.start_send(item) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fn poll_complete(&mut self) -> Poll<(), ConnectionError> { | ||||||
|  |         self.inner.poll_complete() | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,65 +1,11 @@ | |||||||
|  | mod connection; | ||||||
| mod framed_read; | mod framed_read; | ||||||
| mod framed_write; | mod framed_write; | ||||||
|  | mod ping_pong; | ||||||
| mod settings; | mod settings; | ||||||
|  |  | ||||||
| use {frame, ConnectionError}; | pub use self::connection::Connection; | ||||||
| use self::framed_read::FramedRead; | pub use self::framed_read::FramedRead; | ||||||
| use self::framed_write::FramedWrite; | pub use self::framed_write::FramedWrite; | ||||||
|  | pub use self::ping_pong::PingPong; | ||||||
| use tokio_io::{AsyncRead, AsyncWrite}; | pub use self::settings::Settings; | ||||||
| use tokio_io::codec::length_delimited; |  | ||||||
|  |  | ||||||
| use futures::*; |  | ||||||
|  |  | ||||||
| pub struct Connection<T> { |  | ||||||
|     inner: Inner<T>, |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type Inner<T> = |  | ||||||
|     FramedWrite< |  | ||||||
|         FramedRead< |  | ||||||
|             length_delimited::FramedRead<T>>>; |  | ||||||
|  |  | ||||||
| impl<T: AsyncRead + AsyncWrite> Connection<T> { |  | ||||||
|     pub fn new(io: T) -> Connection<T> { |  | ||||||
|         // Delimit the frames |  | ||||||
|         let framed_read = length_delimited::Builder::new() |  | ||||||
|             .big_endian() |  | ||||||
|             .length_field_length(3) |  | ||||||
|             .length_adjustment(6) |  | ||||||
|             .num_skip(0) // Don't skip the header |  | ||||||
|             .new_read(io); |  | ||||||
|  |  | ||||||
|         // Map to `Frame` types |  | ||||||
|         let framed_read = FramedRead::new(framed_read); |  | ||||||
|  |  | ||||||
|         // Frame encoder |  | ||||||
|         let framed = FramedWrite::new(framed_read); |  | ||||||
|  |  | ||||||
|         Connection { |  | ||||||
|             inner: framed, |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl<T: AsyncRead + AsyncWrite> Stream for Connection<T> { |  | ||||||
|     type Item = frame::Frame; |  | ||||||
|     type Error = ConnectionError; |  | ||||||
|  |  | ||||||
|     fn poll(&mut self) -> Poll<Option<frame::Frame>, ConnectionError> { |  | ||||||
|         self.inner.poll() |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl<T: AsyncRead + AsyncWrite> Sink for Connection<T> { |  | ||||||
|     type SinkItem = frame::Frame; |  | ||||||
|     type SinkError = ConnectionError; |  | ||||||
|  |  | ||||||
|     fn start_send(&mut self, item: frame::Frame) -> StartSend<frame::Frame, ConnectionError> { |  | ||||||
|         self.inner.start_send(item) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fn poll_complete(&mut self) -> Poll<(), ConnectionError> { |  | ||||||
|         self.inner.poll_complete() |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|   | |||||||
							
								
								
									
										47
									
								
								src/proto/ping_pong.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								src/proto/ping_pong.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | |||||||
|  | use ConnectionError; | ||||||
|  | use frame::Frame; | ||||||
|  |  | ||||||
|  | use futures::*; | ||||||
|  |  | ||||||
|  | pub struct PingPong<T> { | ||||||
|  |     inner: T, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl<T> PingPong<T> | ||||||
|  |     where T: Stream<Item = Frame, Error = ConnectionError>, | ||||||
|  |           T: Sink<SinkItem = Frame, SinkError = ConnectionError>, | ||||||
|  | { | ||||||
|  |     pub fn new(inner: T) -> PingPong<T> { | ||||||
|  |         PingPong { | ||||||
|  |             inner: inner, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl<T> Stream for PingPong<T> | ||||||
|  |     where T: Stream<Item = Frame, Error = ConnectionError>, | ||||||
|  |           T: Sink<SinkItem = Frame, SinkError = ConnectionError>, | ||||||
|  | { | ||||||
|  |     type Item = Frame; | ||||||
|  |     type Error = ConnectionError; | ||||||
|  |  | ||||||
|  |     fn poll(&mut self) -> Poll<Option<Frame>, ConnectionError> { | ||||||
|  |         self.inner.poll() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl<T> Sink for PingPong<T> | ||||||
|  |     where T: Stream<Item = Frame, Error = ConnectionError>, | ||||||
|  |           T: Sink<SinkItem = Frame, SinkError = ConnectionError>, | ||||||
|  | { | ||||||
|  |     type SinkItem = Frame; | ||||||
|  |     type SinkError = ConnectionError; | ||||||
|  |  | ||||||
|  |     fn start_send(&mut self, item: Frame) -> StartSend<Frame, ConnectionError> { | ||||||
|  |         self.inner.start_send(item) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fn poll_complete(&mut self) -> Poll<(), ConnectionError> { | ||||||
|  |         self.inner.poll_complete() | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -15,19 +15,22 @@ pub struct Settings<T> { | |||||||
|     is_dirty: bool, |     is_dirty: bool, | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * TODO: | ||||||
|  |  * - Settings ack timeout for connection error | ||||||
|  |  */ | ||||||
|  |  | ||||||
| impl<T> Settings<T> | impl<T> Settings<T> | ||||||
|     where T: Stream<Item = Frame, Error = ConnectionError>, |     where T: Stream<Item = Frame, Error = ConnectionError>, | ||||||
|           T: Sink<SinkItem = Frame, SinkError = ConnectionError>, |           T: Sink<SinkItem = Frame, SinkError = ConnectionError>, | ||||||
| { | { | ||||||
|     pub fn new(inner: T) -> Settings<T> { |     pub fn new(inner: T, local: frame::SettingSet) -> Settings<T> { | ||||||
|         Settings { |         Settings { | ||||||
|             inner: inner, |             inner: inner, | ||||||
|             local: frame::SettingSet::default(), |             local: local, | ||||||
|             remote: frame::SettingSet::default(), |             remote: frame::SettingSet::default(), | ||||||
|             remaining_acks: 0, |             remaining_acks: 0, | ||||||
|             // Always start in the dirty state as sending the settings frame is |             is_dirty: false, | ||||||
|             // part of the connection handshake |  | ||||||
|             is_dirty: true, |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -37,14 +40,20 @@ impl<T> Settings<T> | |||||||
|  |  | ||||||
|     fn try_send_pending(&mut self) -> Poll<(), ConnectionError> { |     fn try_send_pending(&mut self) -> Poll<(), ConnectionError> { | ||||||
|         if self.is_dirty { |         if self.is_dirty { | ||||||
|             // Create the new frame |  | ||||||
|             let frame = frame::Settings::new(self.local.clone()).into(); |             let frame = frame::Settings::new(self.local.clone()).into(); | ||||||
|             try_ready!(self.try_send(frame)); |             try_ready!(self.try_send(frame)); | ||||||
|  |  | ||||||
|             self.is_dirty = false; |             self.is_dirty = false; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         unimplemented!(); |         while self.remaining_acks > 0 { | ||||||
|  |             let frame = frame::Settings::ack().into(); | ||||||
|  |             try_ready!(self.try_send(frame)); | ||||||
|  |  | ||||||
|  |             self.remaining_acks -= 1; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         Ok(Async::Ready(())) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn try_send(&mut self, item: frame::Frame) -> Poll<(), ConnectionError> { |     fn try_send(&mut self, item: frame::Frame) -> Poll<(), ConnectionError> { | ||||||
| @@ -82,14 +91,9 @@ impl<T> Sink for Settings<T> | |||||||
|         // Settings frames take priority, so `item` cannot be sent if there are |         // Settings frames take priority, so `item` cannot be sent if there are | ||||||
|         // any pending acks OR the local settings have been changed w/o sending |         // any pending acks OR the local settings have been changed w/o sending | ||||||
|         // an associated frame. |         // an associated frame. | ||||||
|         if self.has_pending_sends() { |         if !try!(self.try_send_pending()).is_ready() { | ||||||
|             // Try to flush them |  | ||||||
|             try!(self.poll_complete()); |  | ||||||
|  |  | ||||||
|             if self.has_pending_sends() { |  | ||||||
|             return Ok(AsyncSink::NotReady(item)); |             return Ok(AsyncSink::NotReady(item)); | ||||||
|         } |         } | ||||||
|         } |  | ||||||
|  |  | ||||||
|         self.inner.start_send(item) |         self.inner.start_send(item) | ||||||
|     } |     } | ||||||
| @@ -99,8 +103,8 @@ impl<T> Sink for Settings<T> | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn close(&mut self) -> Poll<(), ConnectionError> { |     fn close(&mut self) -> Poll<(), ConnectionError> { | ||||||
|         if self.has_pending_sends() { |         if !try!(self.try_send_pending()).is_ready() { | ||||||
|             try_ready!(self.poll_complete()); |             return Ok(Async::NotReady); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         self.inner.close() |         self.inner.close() | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user