Get a request sent

This commit is contained in:
Carl Lerche
2017-06-26 22:25:25 -07:00
parent ac2959e956
commit 7897b770e9
15 changed files with 296 additions and 125 deletions

View File

@@ -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 } => {