use {frame, Frame, ConnectionError, Peer, StreamId}; use proto::{self, ReadySink, State}; use tokio_io::{AsyncRead, AsyncWrite}; use tokio_io::codec::length_delimited; use http; use futures::*; use ordermap::OrderMap; use fnv::FnvHasher; use std::marker::PhantomData; use std::hash::BuildHasherDefault; /// An H2 connection pub struct Connection { inner: Inner, streams: StreamMap, peer: PhantomData

, } type Inner = proto::Settings< proto::PingPong< proto::FramedWrite< proto::FramedRead< length_delimited::FramedRead>>>>; type StreamMap = OrderMap>; /// Returns a new `Connection` backed by the given `io`. pub fn new(io: T) -> Connection 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, } } impl Connection 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 Stream for Connection where T: AsyncRead + AsyncWrite, P: Peer, { type Item = Frame; type Error = ConnectionError; fn poll(&mut self) -> Poll, ConnectionError> { 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 Sink for Connection where T: AsyncRead + AsyncWrite, P: Peer, { type SinkItem = Frame; type SinkError = ConnectionError; fn start_send(&mut self, item: Self::SinkItem) -> StartSend { // 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> { self.inner.poll_complete() } } impl ReadySink for Connection where T: AsyncRead + AsyncWrite, P: Peer, { fn poll_ready(&mut self) -> Poll<(), Self::SinkError> { self.inner.poll_ready() } }