Merge pull request #12 from carllerche/stream-api
Restructure API using a handle per stream
This commit is contained in:
@@ -8,7 +8,7 @@ futures = "0.1"
|
||||
tokio-io = "0.1"
|
||||
tokio-timer = "0.1"
|
||||
bytes = "0.4"
|
||||
http = { git = "https://github.com/carllerche/http" }
|
||||
http = { git = "https://github.com/carllerche/http", branch = "uri-try-from-parts" }
|
||||
byteorder = "1.0"
|
||||
log = "0.3.8"
|
||||
fnv = "1.0.5"
|
||||
|
||||
@@ -8,23 +8,29 @@ extern crate openssl;
|
||||
extern crate io_dump;
|
||||
extern crate env_logger;
|
||||
|
||||
use h2::client;
|
||||
|
||||
use http::request;
|
||||
use h2::client::Client;
|
||||
|
||||
use http::{method, Request};
|
||||
use futures::*;
|
||||
|
||||
use tokio_core::reactor;
|
||||
use tokio_core::net::TcpStream;
|
||||
|
||||
use std::net::ToSocketAddrs;
|
||||
|
||||
pub fn main() {
|
||||
let _ = env_logger::init();
|
||||
|
||||
let mut core = reactor::Core::new().unwrap();;
|
||||
// Sync DNS resolution.
|
||||
let addr = "http2.akamai.com:443".to_socket_addrs()
|
||||
.unwrap().next().unwrap();
|
||||
|
||||
let tcp = TcpStream::connect(
|
||||
&"23.39.23.98:443".parse().unwrap(),
|
||||
&core.handle());
|
||||
println!("ADDR: {:?}", addr);
|
||||
|
||||
let mut core = reactor::Core::new().unwrap();;
|
||||
let handle = core.handle();
|
||||
|
||||
let tcp = TcpStream::connect(&addr, &handle);
|
||||
|
||||
let tcp = tcp.then(|res| {
|
||||
use openssl::ssl::{SslMethod, SslConnectorBuilder};
|
||||
@@ -46,24 +52,29 @@ pub fn main() {
|
||||
// Dump output to stdout
|
||||
let tls = io_dump::Dump::to_stdout(tls);
|
||||
|
||||
client::handshake(tls)
|
||||
println!("Starting client handshake");
|
||||
Client::handshake(tls)
|
||||
})
|
||||
.then(|res| {
|
||||
let conn = res.unwrap();
|
||||
let mut h2 = res.unwrap();
|
||||
|
||||
let mut request = request::Head::default();
|
||||
request.uri = "https://http2.akamai.com/".parse().unwrap();
|
||||
// request.version = version::H2;
|
||||
let request = Request::builder()
|
||||
.method(method::GET)
|
||||
.uri("https://http2.akamai.com/")
|
||||
.body(()).unwrap();
|
||||
|
||||
conn.send_request(1.into(), request, true)
|
||||
})
|
||||
.then(|res| {
|
||||
let conn = res.unwrap();
|
||||
// Get the next message
|
||||
conn.for_each(|frame| {
|
||||
println!("RX: {:?}", frame);
|
||||
Ok(())
|
||||
})
|
||||
let stream = h2.request(request, true).unwrap();
|
||||
|
||||
let stream = stream.and_then(|response| {
|
||||
let (_, body) = response.into_parts();
|
||||
|
||||
body.for_each(|chunk| {
|
||||
println!("RX: {:?}", chunk);
|
||||
Ok(())
|
||||
})
|
||||
});
|
||||
|
||||
h2.join(stream)
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/*
|
||||
extern crate h2;
|
||||
extern crate http;
|
||||
extern crate futures;
|
||||
@@ -59,3 +60,6 @@ pub fn main() {
|
||||
|
||||
core.run(tcp).unwrap();
|
||||
}
|
||||
*/
|
||||
|
||||
pub fn main() {}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/*
|
||||
extern crate h2;
|
||||
extern crate http;
|
||||
extern crate futures;
|
||||
@@ -72,3 +73,6 @@ pub fn main() {
|
||||
|
||||
core.run(server).unwrap();
|
||||
}
|
||||
*/
|
||||
|
||||
pub fn main() {}
|
||||
|
||||
253
src/client.rs
253
src/client.rs
@@ -1,7 +1,9 @@
|
||||
use {frame, proto, Peer, ConnectionError, StreamId};
|
||||
use {frame, ConnectionError, StreamId};
|
||||
use proto::{self, Connection};
|
||||
use error::Reason::*;
|
||||
|
||||
use http;
|
||||
use futures::{Future, Poll, Sink, AsyncSink};
|
||||
use http::{self, Request, Response};
|
||||
use futures::{self, Future, Poll, Sink, AsyncSink};
|
||||
use tokio_io::{AsyncRead, AsyncWrite};
|
||||
use bytes::{Bytes, IntoBuf};
|
||||
|
||||
@@ -10,57 +12,201 @@ use std::fmt;
|
||||
/// In progress H2 connection binding
|
||||
pub struct Handshake<T, B: IntoBuf = Bytes> {
|
||||
// TODO: unbox
|
||||
inner: Box<Future<Item = Connection<T, B>, Error = ConnectionError>>,
|
||||
inner: Box<Future<Item = Client<T, B>, Error = ConnectionError>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Peer;
|
||||
|
||||
/// Marker type indicating a client peer
|
||||
#[derive(Debug)]
|
||||
pub struct Client;
|
||||
|
||||
pub type Connection<T, B = Bytes> = super::Connection<T, Client, B>;
|
||||
|
||||
pub fn handshake<T>(io: T) -> Handshake<T, Bytes>
|
||||
where T: AsyncRead + AsyncWrite + 'static,
|
||||
{
|
||||
handshake2(io)
|
||||
pub struct Client<T, B: IntoBuf> {
|
||||
connection: Connection<T, Peer, B>,
|
||||
}
|
||||
|
||||
/// Bind an H2 client connection.
|
||||
///
|
||||
/// Returns a future which resolves to the connection value once the H2
|
||||
/// handshake has been completed.
|
||||
pub fn handshake2<T, B>(io: T) -> Handshake<T, B>
|
||||
#[derive(Debug)]
|
||||
pub struct Stream<B: IntoBuf> {
|
||||
inner: proto::StreamRef<Peer, B::Buf>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Body<B: IntoBuf> {
|
||||
inner: proto::StreamRef<Peer, B::Buf>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Chunk<B: IntoBuf> {
|
||||
inner: proto::Chunk<Peer, B::Buf>,
|
||||
}
|
||||
|
||||
impl<T> Client<T, Bytes>
|
||||
where T: AsyncRead + AsyncWrite + 'static,
|
||||
{
|
||||
pub fn handshake(io: T) -> Handshake<T, Bytes> {
|
||||
Client::handshake2(io)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, B> Client<T, B>
|
||||
// TODO: Get rid of 'static
|
||||
where T: AsyncRead + AsyncWrite + 'static,
|
||||
B: IntoBuf + 'static,
|
||||
{
|
||||
use tokio_io::io;
|
||||
/// Bind an H2 client connection.
|
||||
///
|
||||
/// Returns a future which resolves to the connection value once the H2
|
||||
/// handshake has been completed.
|
||||
pub fn handshake2(io: T) -> Handshake<T, B> {
|
||||
use tokio_io::io;
|
||||
|
||||
debug!("binding client connection");
|
||||
debug!("binding client connection");
|
||||
|
||||
let handshake = io::write_all(io, b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n")
|
||||
.map_err(ConnectionError::from)
|
||||
.and_then(|(io, _)| {
|
||||
debug!("client connection bound");
|
||||
let handshake = io::write_all(io, b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n")
|
||||
.map_err(ConnectionError::from)
|
||||
.and_then(|(io, _)| {
|
||||
debug!("client connection bound");
|
||||
|
||||
let mut framed_write = proto::framed_write(io);
|
||||
let settings = frame::Settings::default();
|
||||
let mut framed_write = proto::framed_write(io);
|
||||
let settings = frame::Settings::default();
|
||||
|
||||
// Send initial settings frame
|
||||
match framed_write.start_send(settings.into()) {
|
||||
Ok(AsyncSink::Ready) => {
|
||||
Ok(proto::from_framed_write(framed_write))
|
||||
// Send initial settings frame
|
||||
match framed_write.start_send(settings.into()) {
|
||||
Ok(AsyncSink::Ready) => {
|
||||
let conn = proto::from_framed_write(framed_write);
|
||||
Ok(Client { connection: conn })
|
||||
}
|
||||
Ok(_) => unreachable!(),
|
||||
Err(e) => Err(ConnectionError::from(e)),
|
||||
}
|
||||
Ok(_) => unreachable!(),
|
||||
Err(e) => Err(ConnectionError::from(e)),
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Handshake { inner: Box::new(handshake) }
|
||||
Handshake { inner: Box::new(handshake) }
|
||||
}
|
||||
|
||||
/// Returns `Ready` when the connection can initialize a new HTTP 2.0
|
||||
/// stream.
|
||||
pub fn poll_ready(&mut self) -> Poll<(), ConnectionError> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
/// Send a request on a new HTTP 2.0 stream
|
||||
pub fn request(&mut self, request: Request<()>, end_of_stream: bool)
|
||||
-> Result<Stream<B>, ConnectionError>
|
||||
{
|
||||
self.connection.send_request(request, end_of_stream)
|
||||
.map(|stream| Stream {
|
||||
inner: stream,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Peer for Client {
|
||||
type Send = http::request::Head;
|
||||
type Poll = http::response::Head;
|
||||
impl<T, B> Future for Client<T, B>
|
||||
// TODO: Get rid of 'static
|
||||
where T: AsyncRead + AsyncWrite + 'static,
|
||||
B: IntoBuf + 'static,
|
||||
{
|
||||
type Item = ();
|
||||
type Error = ConnectionError;
|
||||
|
||||
fn poll(&mut self) -> Poll<(), ConnectionError> {
|
||||
self.connection.poll()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, B> fmt::Debug for Client<T, B>
|
||||
where T: fmt::Debug,
|
||||
B: fmt::Debug + IntoBuf,
|
||||
B::Buf: fmt::Debug + IntoBuf,
|
||||
{
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.debug_struct("Client")
|
||||
.field("connection", &self.connection)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Handshake =====
|
||||
|
||||
impl<T, B: IntoBuf> Future for Handshake<T, B> {
|
||||
type Item = Client<T, B>;
|
||||
type Error = ConnectionError;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
self.inner.poll()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, B> fmt::Debug for Handshake<T, B>
|
||||
where T: fmt::Debug,
|
||||
B: fmt::Debug + IntoBuf,
|
||||
B::Buf: fmt::Debug + IntoBuf,
|
||||
{
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(fmt, "client::Handshake")
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Stream =====
|
||||
|
||||
impl<B: IntoBuf> Stream<B> {
|
||||
/// Receive the HTTP/2.0 response, if it is ready.
|
||||
pub fn poll_response(&mut self) -> Poll<Response<Body<B>>, ConnectionError> {
|
||||
let (parts, _) = try_ready!(self.inner.poll_response()).into_parts();
|
||||
let body = Body { inner: self.inner.clone() };
|
||||
|
||||
Ok(Response::from_parts(parts, body).into())
|
||||
}
|
||||
|
||||
/// Send data
|
||||
pub fn send_data(&mut self, data: B, end_of_stream: bool)
|
||||
-> Result<(), ConnectionError>
|
||||
{
|
||||
self.inner.send_data(data.into_buf(), end_of_stream)
|
||||
}
|
||||
|
||||
/// Send trailers
|
||||
pub fn send_trailers(&mut self, trailers: ())
|
||||
-> Result<(), ConnectionError>
|
||||
{
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: IntoBuf> Future for Stream<B> {
|
||||
type Item = Response<Body<B>>;
|
||||
type Error = ConnectionError;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
self.poll_response()
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Body =====
|
||||
|
||||
impl<B: IntoBuf> futures::Stream for Body<B> {
|
||||
type Item = Chunk<B>;
|
||||
type Error = ConnectionError;
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
||||
let chunk = try_ready!(self.inner.poll_data())
|
||||
.map(|inner| Chunk { inner });
|
||||
|
||||
Ok(chunk.into())
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Chunk =====
|
||||
|
||||
impl<B: IntoBuf> Chunk<B> {
|
||||
pub fn pop_bytes(&mut self) -> Option<Bytes> {
|
||||
self.inner.pop_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Peer =====
|
||||
|
||||
impl proto::Peer for Peer {
|
||||
type Send = Request<()>;
|
||||
type Poll = Response<()>;
|
||||
|
||||
fn is_server() -> bool {
|
||||
false
|
||||
@@ -68,15 +214,12 @@ impl Peer for Client {
|
||||
|
||||
fn convert_send_message(
|
||||
id: StreamId,
|
||||
headers: Self::Send,
|
||||
request: Self::Send,
|
||||
end_of_stream: bool) -> frame::Headers
|
||||
{
|
||||
use http::request::Head;
|
||||
use http::request::Parts;
|
||||
|
||||
// Extract the components of the HTTP request
|
||||
let Head { method, uri, headers, .. } = headers;
|
||||
|
||||
// TODO: Ensure that the version is set to H2
|
||||
let (Parts { method, uri, headers, .. }, _) = request.into_parts();
|
||||
|
||||
// Build the set pseudo header set. All requests will include `method`
|
||||
// and `path`.
|
||||
@@ -92,25 +235,9 @@ impl Peer for Client {
|
||||
frame
|
||||
}
|
||||
|
||||
fn convert_poll_message(headers: frame::Headers) -> Self::Poll {
|
||||
fn convert_poll_message(headers: frame::Headers) -> Result<Self::Poll, ConnectionError> {
|
||||
headers.into_response()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, B: IntoBuf> Future for Handshake<T, B> {
|
||||
type Item = Connection<T, B>;
|
||||
type Error = ConnectionError;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
self.inner.poll()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, B> fmt::Debug for Handshake<T, B>
|
||||
where T: fmt::Debug,
|
||||
B: fmt::Debug + IntoBuf,
|
||||
{
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(fmt, "client::Handshake")
|
||||
// TODO: Is this always a protocol error?
|
||||
.map_err(|_| ProtocolError.into())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,8 @@ use hpack;
|
||||
use frame::{self, Frame, Head, Kind, Error};
|
||||
use HeaderMap;
|
||||
|
||||
use http::{request, response, version, uri, Method, StatusCode, Uri};
|
||||
use http::{self, request, response, version, uri, Method, StatusCode, Uri};
|
||||
use http::{Request, Response};
|
||||
use http::header::{self, HeaderName, HeaderValue};
|
||||
|
||||
use bytes::{BytesMut, Bytes};
|
||||
@@ -46,11 +47,11 @@ pub struct PushPromise {
|
||||
promised_id: StreamId,
|
||||
|
||||
/// The associated flags
|
||||
flags: HeadersFlag,
|
||||
flags: PushPromiseFlag,
|
||||
}
|
||||
|
||||
impl PushPromise {
|
||||
}
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub struct PushPromiseFlag(u8);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Continuation {
|
||||
@@ -199,31 +200,28 @@ impl Headers {
|
||||
self.flags.set_end_stream()
|
||||
}
|
||||
|
||||
pub fn into_response(self) -> response::Head {
|
||||
let mut response = response::Head::default();
|
||||
pub fn into_response(self) -> http::Result<Response<()>> {
|
||||
let mut b = Response::builder();
|
||||
|
||||
if let Some(status) = self.pseudo.status {
|
||||
response.status = status;
|
||||
} else {
|
||||
unimplemented!();
|
||||
b.status(status);
|
||||
}
|
||||
|
||||
response.headers = self.fields;
|
||||
response
|
||||
let mut response = try!(b.body(()));
|
||||
*response.headers_mut() = self.fields;
|
||||
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
pub fn into_request(self) -> request::Head {
|
||||
let mut request = request::Head::default();
|
||||
pub fn into_request(self) -> http::Result<Request<()>> {
|
||||
let mut b = Request::builder();
|
||||
|
||||
// TODO: should we distinguish between HTTP_2 and HTTP_2C?
|
||||
// carllerche/http#42
|
||||
request.version = version::HTTP_2;
|
||||
b.version(version::HTTP_2);
|
||||
|
||||
if let Some(method) = self.pseudo.method {
|
||||
request.method = method;
|
||||
} else {
|
||||
// TODO: invalid request
|
||||
unimplemented!();
|
||||
b.method(method);
|
||||
}
|
||||
|
||||
// Convert the URI
|
||||
@@ -244,12 +242,12 @@ impl Headers {
|
||||
parts.origin_form = Some(uri::OriginForm::try_from_shared(path.into_inner()).unwrap());
|
||||
}
|
||||
|
||||
request.uri = parts.into();
|
||||
b.uri(parts);
|
||||
|
||||
// Set the header fields
|
||||
request.headers = self.fields;
|
||||
let mut request = try!(b.body(()));
|
||||
*request.headers_mut() = self.fields;
|
||||
|
||||
request
|
||||
Ok(request)
|
||||
}
|
||||
|
||||
pub fn into_fields(self) -> HeaderMap {
|
||||
@@ -298,12 +296,46 @@ impl Headers {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Headers> for Frame {
|
||||
fn from(src: Headers) -> Frame {
|
||||
impl<T> From<Headers> for Frame<T> {
|
||||
fn from(src: Headers) -> Self {
|
||||
Frame::Headers(src)
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl PushPromise =====
|
||||
|
||||
impl PushPromise {
|
||||
pub fn load(head: Head, payload: &[u8])
|
||||
-> Result<Self, Error>
|
||||
{
|
||||
let flags = PushPromiseFlag(head.flag());
|
||||
|
||||
// TODO: Handle padding
|
||||
|
||||
let promised_id = StreamId::parse(&payload[..4]);
|
||||
|
||||
Ok(PushPromise {
|
||||
stream_id: head.stream_id(),
|
||||
promised_id: promised_id,
|
||||
flags: flags,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn stream_id(&self) -> StreamId {
|
||||
self.stream_id
|
||||
}
|
||||
|
||||
pub fn promised_id(&self) -> StreamId {
|
||||
self.promised_id
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<PushPromise> for Frame<T> {
|
||||
fn from(src: PushPromise) -> Self {
|
||||
Frame::PushPromise(src)
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Pseudo =====
|
||||
|
||||
impl Pseudo {
|
||||
|
||||
@@ -67,6 +67,15 @@ pub enum Frame<T = Bytes> {
|
||||
}
|
||||
|
||||
impl<T> Frame<T> {
|
||||
/// Returns true if the frame is a DATA frame.
|
||||
pub fn is_data(&self) -> bool {
|
||||
use self::Frame::*;
|
||||
|
||||
match *self {
|
||||
Data(..) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> fmt::Debug for Frame<T> {
|
||||
|
||||
@@ -39,6 +39,10 @@ impl StreamId {
|
||||
pub fn is_zero(&self) -> bool {
|
||||
self.0 == 0
|
||||
}
|
||||
|
||||
pub fn increment(&mut self) {
|
||||
self.0 += 2;
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u32> for StreamId {
|
||||
|
||||
@@ -495,29 +495,29 @@ impl From<Utf8Error> for DecoderError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<header::InvalidValueError> for DecoderError {
|
||||
fn from(_: header::InvalidValueError) -> DecoderError {
|
||||
impl From<header::InvalidHeaderValue> for DecoderError {
|
||||
fn from(_: header::InvalidHeaderValue) -> DecoderError {
|
||||
// TODO: Better error?
|
||||
DecoderError::InvalidUtf8
|
||||
}
|
||||
}
|
||||
|
||||
impl From<method::FromBytesError> for DecoderError {
|
||||
fn from(_: method::FromBytesError) -> DecoderError {
|
||||
impl From<header::InvalidHeaderName> for DecoderError {
|
||||
fn from(_: header::InvalidHeaderName) -> DecoderError {
|
||||
// TODO: Better error
|
||||
DecoderError::InvalidUtf8
|
||||
}
|
||||
}
|
||||
|
||||
impl From<header::FromBytesError> for DecoderError {
|
||||
fn from(_: header::FromBytesError) -> DecoderError {
|
||||
impl From<method::InvalidMethod> for DecoderError {
|
||||
fn from(_: method::InvalidMethod) -> DecoderError {
|
||||
// TODO: Better error
|
||||
DecoderError::InvalidUtf8
|
||||
}
|
||||
}
|
||||
|
||||
impl From<status::FromStrError> for DecoderError {
|
||||
fn from(_: status::FromStrError) -> DecoderError {
|
||||
impl From<status::InvalidStatusCode> for DecoderError {
|
||||
fn from(_: status::InvalidStatusCode) -> DecoderError {
|
||||
// TODO: Better error
|
||||
DecoderError::InvalidUtf8
|
||||
}
|
||||
|
||||
@@ -251,7 +251,6 @@ fn gen_header_name(g: &mut StdRng) -> HeaderName {
|
||||
header::ACCEPT_CHARSET,
|
||||
header::ACCEPT_ENCODING,
|
||||
header::ACCEPT_LANGUAGE,
|
||||
header::ACCEPT_PATCH,
|
||||
header::ACCEPT_RANGES,
|
||||
header::ACCESS_CONTROL_ALLOW_CREDENTIALS,
|
||||
header::ACCESS_CONTROL_ALLOW_HEADERS,
|
||||
@@ -272,7 +271,6 @@ fn gen_header_name(g: &mut StdRng) -> HeaderName {
|
||||
header::CONTENT_LANGUAGE,
|
||||
header::CONTENT_LENGTH,
|
||||
header::CONTENT_LOCATION,
|
||||
header::CONTENT_MD5,
|
||||
header::CONTENT_RANGE,
|
||||
header::CONTENT_SECURITY_POLICY,
|
||||
header::CONTENT_SECURITY_POLICY_REPORT_ONLY,
|
||||
@@ -292,7 +290,6 @@ fn gen_header_name(g: &mut StdRng) -> HeaderName {
|
||||
header::IF_RANGE,
|
||||
header::IF_UNMODIFIED_SINCE,
|
||||
header::LAST_MODIFIED,
|
||||
header::KEEP_ALIVE,
|
||||
header::LINK,
|
||||
header::LOCATION,
|
||||
header::MAX_FORWARDS,
|
||||
@@ -311,10 +308,8 @@ fn gen_header_name(g: &mut StdRng) -> HeaderName {
|
||||
header::SET_COOKIE,
|
||||
header::STRICT_TRANSPORT_SECURITY,
|
||||
header::TE,
|
||||
header::TK,
|
||||
header::TRAILER,
|
||||
header::TRANSFER_ENCODING,
|
||||
header::TSV,
|
||||
header::USER_AGENT,
|
||||
header::UPGRADE,
|
||||
header::UPGRADE_INSECURE_REQUESTS,
|
||||
|
||||
29
src/lib.rs
29
src/lib.rs
@@ -1,5 +1,5 @@
|
||||
// #![allow(warnings)]
|
||||
#![deny(missing_debug_implementations)]
|
||||
#![allow(warnings)]
|
||||
// #![deny(missing_debug_implementations)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate futures;
|
||||
@@ -20,6 +20,8 @@ extern crate fnv;
|
||||
|
||||
extern crate byteorder;
|
||||
|
||||
extern crate slab;
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
@@ -30,11 +32,10 @@ pub mod error;
|
||||
mod hpack;
|
||||
mod proto;
|
||||
mod frame;
|
||||
pub mod server;
|
||||
// pub mod server;
|
||||
|
||||
pub use error::{ConnectionError, Reason};
|
||||
pub use frame::StreamId;
|
||||
pub use proto::Connection;
|
||||
|
||||
use bytes::Bytes;
|
||||
|
||||
@@ -68,23 +69,3 @@ pub enum Frame<T, B = Bytes> {
|
||||
error: Reason,
|
||||
},
|
||||
}
|
||||
|
||||
/// 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 is_server() -> bool;
|
||||
|
||||
#[doc(hidden)]
|
||||
fn convert_send_message(
|
||||
id: StreamId,
|
||||
headers: Self::Send,
|
||||
end_of_stream: bool) -> frame::Headers;
|
||||
|
||||
#[doc(hidden)]
|
||||
fn convert_poll_message(headers: frame::Headers) -> Self::Poll;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
use {ConnectionError, Frame, Peer};
|
||||
use {client, ConnectionError, Frame};
|
||||
use HeaderMap;
|
||||
use frame::{self, StreamId};
|
||||
use client::Client;
|
||||
use server::Server;
|
||||
|
||||
use proto::*;
|
||||
|
||||
use http::{request, response};
|
||||
use http::{Request, Response};
|
||||
use bytes::{Bytes, IntoBuf};
|
||||
use tokio_io::{AsyncRead, AsyncWrite};
|
||||
|
||||
@@ -21,39 +19,34 @@ pub struct Connection<T, P, B: IntoBuf = Bytes> {
|
||||
// TODO: Remove <B>
|
||||
ping_pong: PingPong<B::Buf>,
|
||||
settings: Settings,
|
||||
streams: Streams<P>,
|
||||
streams: Streams<P, B::Buf>,
|
||||
|
||||
_phantom: PhantomData<P>,
|
||||
}
|
||||
|
||||
pub fn new<T, P, B>(codec: Codec<T, B::Buf>)
|
||||
-> Connection<T, P, B>
|
||||
where T: AsyncRead + AsyncWrite,
|
||||
P: Peer,
|
||||
B: IntoBuf,
|
||||
{
|
||||
// TODO: Actually configure
|
||||
let streams = Streams::new(streams::Config {
|
||||
max_remote_initiated: None,
|
||||
init_remote_window_sz: DEFAULT_INITIAL_WINDOW_SIZE,
|
||||
max_local_initiated: None,
|
||||
init_local_window_sz: DEFAULT_INITIAL_WINDOW_SIZE,
|
||||
});
|
||||
|
||||
Connection {
|
||||
codec: codec,
|
||||
ping_pong: PingPong::new(),
|
||||
settings: Settings::new(),
|
||||
streams: streams,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, P, B> Connection<T, P, B>
|
||||
where T: AsyncRead + AsyncWrite,
|
||||
P: Peer,
|
||||
B: IntoBuf,
|
||||
{
|
||||
pub fn new(codec: Codec<T, B::Buf>) -> Connection<T, P, B> {
|
||||
// TODO: Actually configure
|
||||
let streams = Streams::new(streams::Config {
|
||||
max_remote_initiated: None,
|
||||
init_remote_window_sz: DEFAULT_INITIAL_WINDOW_SIZE,
|
||||
max_local_initiated: None,
|
||||
init_local_window_sz: DEFAULT_INITIAL_WINDOW_SIZE,
|
||||
});
|
||||
|
||||
Connection {
|
||||
codec: codec,
|
||||
ping_pong: PingPong::new(),
|
||||
settings: Settings::new(),
|
||||
streams: streams,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Polls for the next update to a remote flow control window.
|
||||
pub fn poll_window_update(&mut self) -> Poll<WindowUpdate, ConnectionError> {
|
||||
self.streams.poll_window_update()
|
||||
@@ -87,6 +80,7 @@ impl<T, P, B> Connection<T, P, B>
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
/// Returns `Ready` when the connection is ready to receive a frame.
|
||||
pub fn poll_ready(&mut self) -> Poll<(), ConnectionError> {
|
||||
try_ready!(self.poll_send_ready());
|
||||
|
||||
@@ -96,6 +90,94 @@ impl<T, P, B> Connection<T, P, B>
|
||||
Ok(().into())
|
||||
}
|
||||
|
||||
/// Advances the internal state of the connection.
|
||||
pub fn poll(&mut self) -> Poll<(), ConnectionError> {
|
||||
match self.poll2() {
|
||||
Err(e) => {
|
||||
self.streams.recv_err(&e);
|
||||
Err(e)
|
||||
}
|
||||
ret => ret,
|
||||
}
|
||||
}
|
||||
|
||||
fn poll2(&mut self) -> Poll<(), ConnectionError> {
|
||||
use frame::Frame::*;
|
||||
|
||||
loop {
|
||||
// First, ensure that the `Connection` is able to receive a frame
|
||||
try_ready!(self.poll_recv_ready());
|
||||
|
||||
trace!("polling codec");
|
||||
|
||||
let frame = match try!(self.codec.poll()) {
|
||||
Async::Ready(frame) => frame,
|
||||
Async::NotReady => {
|
||||
// Flush any pending writes
|
||||
let _ = try!(self.poll_complete());
|
||||
return Ok(Async::NotReady);
|
||||
}
|
||||
};
|
||||
|
||||
match frame {
|
||||
Some(Headers(frame)) => {
|
||||
trace!("recv HEADERS; frame={:?}", frame);
|
||||
|
||||
if let Some(frame) = try!(self.streams.recv_headers(frame)) {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
/*
|
||||
// Update stream state while ensuring that the headers frame
|
||||
// can be received.
|
||||
if let Some(frame) = try!(self.streams.recv_headers(frame)) {
|
||||
let frame = Self::convert_poll_message(frame)?;
|
||||
return Ok(Some(frame).into());
|
||||
}
|
||||
*/
|
||||
}
|
||||
Some(Data(frame)) => {
|
||||
trace!("recv DATA; frame={:?}", frame);
|
||||
try!(self.streams.recv_data(frame));
|
||||
}
|
||||
Some(Reset(frame)) => {
|
||||
trace!("recv RST_STREAM; frame={:?}", frame);
|
||||
try!(self.streams.recv_reset(frame));
|
||||
}
|
||||
Some(PushPromise(frame)) => {
|
||||
trace!("recv PUSH_PROMISE; frame={:?}", frame);
|
||||
self.streams.recv_push_promise(frame)?;
|
||||
}
|
||||
Some(Settings(frame)) => {
|
||||
trace!("recv SETTINGS; frame={:?}", frame);
|
||||
self.settings.recv_settings(frame);
|
||||
|
||||
// TODO: ACK must be sent THEN settings applied.
|
||||
}
|
||||
Some(Ping(frame)) => {
|
||||
unimplemented!();
|
||||
/*
|
||||
trace!("recv PING; frame={:?}", frame);
|
||||
self.ping_pong.recv_ping(frame);
|
||||
*/
|
||||
}
|
||||
Some(WindowUpdate(frame)) => {
|
||||
unimplemented!();
|
||||
/*
|
||||
trace!("recv WINDOW_UPDATE; frame={:?}", frame);
|
||||
try!(self.streams.recv_window_update(frame));
|
||||
*/
|
||||
}
|
||||
None => {
|
||||
// TODO: Is this correct?
|
||||
trace!("codec closed");
|
||||
return Ok(Async::Ready(()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
pub fn send_data(self,
|
||||
id: StreamId,
|
||||
data: B,
|
||||
@@ -119,10 +201,7 @@ impl<T, P, B> Connection<T, P, B>
|
||||
headers,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn start_ping(&mut self, _body: PingPayload) -> StartSend<PingPayload, ConnectionError> {
|
||||
unimplemented!();
|
||||
}
|
||||
*/
|
||||
|
||||
// ===== Private =====
|
||||
|
||||
@@ -146,110 +225,51 @@ impl<T, P, B> Connection<T, P, B>
|
||||
/// This function is currently used by poll_complete, but at some point it
|
||||
/// will probably not be required.
|
||||
fn poll_send_ready(&mut self) -> Poll<(), ConnectionError> {
|
||||
// TODO: Is this function needed?
|
||||
try_ready!(self.poll_recv_ready());
|
||||
|
||||
// Ensure all window updates have been sent.
|
||||
try_ready!(self.streams.send_pending_window_updates(&mut self.codec));
|
||||
|
||||
Ok(().into())
|
||||
}
|
||||
|
||||
/// Try to receive the next frame
|
||||
fn recv_frame(&mut self) -> Poll<Option<Frame<P::Poll>>, ConnectionError> {
|
||||
use frame::Frame::*;
|
||||
fn poll_complete(&mut self) -> Poll<(), ConnectionError> {
|
||||
try_ready!(self.poll_send_ready());
|
||||
|
||||
loop {
|
||||
// First, ensure that the `Connection` is able to receive a frame
|
||||
try_ready!(self.poll_recv_ready());
|
||||
// Ensure all window updates have been sent.
|
||||
try_ready!(self.streams.poll_complete(&mut self.codec));
|
||||
try_ready!(self.codec.poll_complete());
|
||||
|
||||
trace!("polling codec");
|
||||
|
||||
let frame = match try!(self.codec.poll()) {
|
||||
Async::Ready(frame) => frame,
|
||||
Async::NotReady => {
|
||||
// Receiving new frames may depend on ensuring that the write buffer
|
||||
// is clear (e.g. if window updates need to be sent), so `poll_complete`
|
||||
// is called here.
|
||||
let _ = try!(self.poll_complete());
|
||||
return Ok(Async::NotReady);
|
||||
}
|
||||
};
|
||||
|
||||
match frame {
|
||||
Some(Headers(frame)) => {
|
||||
trace!("recv HEADERS; frame={:?}", frame);
|
||||
// Update stream state while ensuring that the headers frame
|
||||
// can be received.
|
||||
if let Some(frame) = try!(self.streams.recv_headers(frame)) {
|
||||
let frame = Self::convert_poll_message(frame);
|
||||
return Ok(Some(frame).into());
|
||||
}
|
||||
}
|
||||
Some(Data(frame)) => {
|
||||
trace!("recv DATA; frame={:?}", frame);
|
||||
try!(self.streams.recv_data(&frame));
|
||||
|
||||
let frame = Frame::Data {
|
||||
id: frame.stream_id(),
|
||||
end_of_stream: frame.is_end_stream(),
|
||||
data: frame.into_payload(),
|
||||
};
|
||||
|
||||
return Ok(Some(frame).into());
|
||||
}
|
||||
Some(Reset(frame)) => {
|
||||
trace!("recv RST_STREAM; frame={:?}", frame);
|
||||
try!(self.streams.recv_reset(&frame));
|
||||
|
||||
let frame = Frame::Reset {
|
||||
id: frame.stream_id(),
|
||||
error: frame.reason(),
|
||||
};
|
||||
|
||||
return Ok(Some(frame).into());
|
||||
}
|
||||
Some(PushPromise(frame)) => {
|
||||
trace!("recv PUSH_PROMISE; frame={:?}", frame);
|
||||
try!(self.streams.recv_push_promise(frame));
|
||||
}
|
||||
Some(Settings(frame)) => {
|
||||
trace!("recv SETTINGS; frame={:?}", frame);
|
||||
self.settings.recv_settings(frame);
|
||||
|
||||
// TODO: ACK must be sent THEN settings applied.
|
||||
}
|
||||
Some(Ping(frame)) => {
|
||||
trace!("recv PING; frame={:?}", frame);
|
||||
self.ping_pong.recv_ping(frame);
|
||||
}
|
||||
Some(WindowUpdate(frame)) => {
|
||||
trace!("recv WINDOW_UPDATE; frame={:?}", frame);
|
||||
try!(self.streams.recv_window_update(frame));
|
||||
}
|
||||
None => {
|
||||
trace!("codec closed");
|
||||
return Ok(Async::Ready(None));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(().into())
|
||||
}
|
||||
|
||||
fn convert_poll_message(frame: frame::Headers) -> Frame<P::Poll> {
|
||||
fn convert_poll_message(frame: frame::Headers) -> Result<Frame<P::Poll>, ConnectionError> {
|
||||
if frame.is_trailers() {
|
||||
Frame::Trailers {
|
||||
Ok(Frame::Trailers {
|
||||
id: frame.stream_id(),
|
||||
headers: frame.into_fields()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
Frame::Headers {
|
||||
Ok(Frame::Headers {
|
||||
id: frame.stream_id(),
|
||||
end_of_stream: frame.is_end_stream(),
|
||||
headers: P::convert_poll_message(frame),
|
||||
}
|
||||
headers: P::convert_poll_message(frame)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, B> Connection<T, client::Peer, B>
|
||||
where T: AsyncRead + AsyncWrite,
|
||||
B: IntoBuf,
|
||||
{
|
||||
/// Initialize a new HTTP/2.0 stream and send the message.
|
||||
pub fn send_request(&mut self, request: Request<()>, end_of_stream: bool)
|
||||
-> Result<StreamRef<client::Peer, B::Buf>, ConnectionError>
|
||||
{
|
||||
self.streams.send_request(request, end_of_stream)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
impl<T, B> Connection<T, Client, B>
|
||||
where T: AsyncRead + AsyncWrite,
|
||||
B: IntoBuf,
|
||||
@@ -296,21 +316,9 @@ impl<T, B> Connection<T, Server, B>
|
||||
})
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
impl<T, P, B> Stream for Connection<T, P, B>
|
||||
where T: AsyncRead + AsyncWrite,
|
||||
P: Peer,
|
||||
B: IntoBuf,
|
||||
{
|
||||
type Item = Frame<P::Poll>;
|
||||
type Error = ConnectionError;
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<Self::Item>, ConnectionError> {
|
||||
// TODO: intercept errors and flag the connection
|
||||
self.recv_frame()
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
impl<T, P, B> Sink for Connection<T, P, B>
|
||||
where T: AsyncRead + AsyncWrite,
|
||||
P: Peer,
|
||||
@@ -384,11 +392,5 @@ impl<T, P, B> Sink for Connection<T, P, B>
|
||||
// Return success
|
||||
Ok(AsyncSink::Ready)
|
||||
}
|
||||
|
||||
fn poll_complete(&mut self) -> Poll<(), ConnectionError> {
|
||||
try_ready!(self.poll_send_ready());
|
||||
try_ready!(self.codec.poll_complete());
|
||||
|
||||
Ok(().into())
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -92,7 +92,9 @@ impl<T> FramedRead<T> {
|
||||
let _todo = try!(frame::GoAway::load(&bytes[frame::HEADER_LEN..]));
|
||||
unimplemented!();
|
||||
}
|
||||
Kind::PushPromise |
|
||||
Kind::PushPromise => {
|
||||
frame::PushPromise::load(head, &bytes[frame::HEADER_LEN..])?.into()
|
||||
}
|
||||
Kind::Priority |
|
||||
Kind::Continuation |
|
||||
Kind::Unknown => {
|
||||
@@ -117,7 +119,7 @@ impl<T: ApplySettings> ApplySettings for FramedRead<T> {
|
||||
}
|
||||
*/
|
||||
|
||||
impl<T> Stream for FramedRead<T>
|
||||
impl<T> futures::Stream for FramedRead<T>
|
||||
where T: AsyncRead,
|
||||
{
|
||||
type Item = Frame;
|
||||
|
||||
@@ -6,22 +6,42 @@ mod settings;
|
||||
mod streams;
|
||||
|
||||
pub use self::connection::Connection;
|
||||
pub use self::streams::{Streams, StreamRef, Chunk};
|
||||
|
||||
use self::framed_read::FramedRead;
|
||||
use self::framed_write::FramedWrite;
|
||||
use self::ping_pong::PingPong;
|
||||
use self::settings::Settings;
|
||||
use self::streams::Streams;
|
||||
|
||||
use {StreamId, Peer};
|
||||
use {StreamId, ConnectionError};
|
||||
use error::Reason;
|
||||
use frame::Frame;
|
||||
use frame::{self, Frame};
|
||||
|
||||
use futures::*;
|
||||
use futures::{self, task, Poll, Async, AsyncSink, Sink, Stream as Stream2};
|
||||
use bytes::{Buf, IntoBuf};
|
||||
use tokio_io::{AsyncRead, AsyncWrite};
|
||||
use tokio_io::codec::length_delimited;
|
||||
|
||||
/// 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 is_server() -> bool;
|
||||
|
||||
#[doc(hidden)]
|
||||
fn convert_send_message(
|
||||
id: StreamId,
|
||||
headers: Self::Send,
|
||||
end_of_stream: bool) -> frame::Headers;
|
||||
|
||||
#[doc(hidden)]
|
||||
fn convert_poll_message(headers: frame::Headers) -> Result<Self::Poll, ConnectionError>;
|
||||
}
|
||||
|
||||
pub type PingPayload = [u8; 8];
|
||||
|
||||
pub type WindowSize = u32;
|
||||
@@ -69,7 +89,7 @@ pub fn from_framed_write<T, P, B>(framed_write: FramedWrite<T, B::Buf>)
|
||||
|
||||
let codec = FramedRead::new(framed);
|
||||
|
||||
connection::new(codec)
|
||||
Connection::new(codec)
|
||||
}
|
||||
|
||||
impl WindowUpdate {
|
||||
|
||||
140
src/proto/streams/buffer.rs
Normal file
140
src/proto/streams/buffer.rs
Normal file
@@ -0,0 +1,140 @@
|
||||
use frame::{self, Frame};
|
||||
|
||||
use slab::Slab;
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
/// Buffers frames for multiple streams.
|
||||
#[derive(Debug)]
|
||||
pub struct Buffer<B> {
|
||||
slab: Slab<Slot<B>>,
|
||||
}
|
||||
|
||||
/// A sequence of frames in a `Buffer`
|
||||
#[derive(Debug)]
|
||||
pub struct Deque<B> {
|
||||
indices: Option<Indices>,
|
||||
_p: PhantomData<B>,
|
||||
}
|
||||
|
||||
/// Tracks the head & tail for a sequence of frames in a `Buffer`.
|
||||
#[derive(Debug, Default, Copy, Clone)]
|
||||
struct Indices {
|
||||
head: usize,
|
||||
tail: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Slot<B> {
|
||||
frame: Frame<B>,
|
||||
next: Option<usize>,
|
||||
}
|
||||
|
||||
impl<B> Buffer<B> {
|
||||
pub fn new() -> Self {
|
||||
Buffer {
|
||||
slab: Slab::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B> Deque<B> {
|
||||
pub fn new() -> Self {
|
||||
Deque {
|
||||
indices: None,
|
||||
_p: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.indices.is_none()
|
||||
}
|
||||
|
||||
pub fn push_back(&mut self, buf: &mut Buffer<B>, frame: Frame<B>) {
|
||||
let key = buf.slab.insert(Slot {
|
||||
frame,
|
||||
next: None,
|
||||
});
|
||||
|
||||
match self.indices {
|
||||
Some(ref mut idxs) => {
|
||||
buf.slab[idxs.tail].next = Some(key);
|
||||
idxs.tail = key;
|
||||
}
|
||||
None => {
|
||||
self.indices = Some(Indices {
|
||||
head: key,
|
||||
tail: key,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pop_front(&mut self, buf: &mut Buffer<B>) -> Option<Frame<B>> {
|
||||
match self.indices {
|
||||
Some(mut idxs) => {
|
||||
let mut slot = buf.slab.remove(idxs.head);
|
||||
|
||||
if idxs.head == idxs.tail {
|
||||
assert!(slot.next.is_none());
|
||||
self.indices = None;
|
||||
} else {
|
||||
idxs.head = slot.next.take().unwrap();
|
||||
self.indices = Some(idxs);
|
||||
}
|
||||
|
||||
return Some(slot.frame);
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn take_while<F>(&mut self, buf: &mut Buffer<B>, mut f: F) -> Self
|
||||
where F: FnMut(&Frame<B>) -> bool
|
||||
{
|
||||
match self.indices {
|
||||
Some(mut idxs) => {
|
||||
if !f(&buf.slab[idxs.head].frame) {
|
||||
return Deque::new();
|
||||
}
|
||||
|
||||
let head = idxs.head;
|
||||
let mut tail = idxs.head;
|
||||
|
||||
loop {
|
||||
let next = match buf.slab[tail].next {
|
||||
Some(next) => next,
|
||||
None => {
|
||||
self.indices = None;
|
||||
return Deque {
|
||||
indices: Some(idxs),
|
||||
_p: PhantomData,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
if !f(&buf.slab[next].frame) {
|
||||
// Split the linked list
|
||||
buf.slab[tail].next = None;
|
||||
|
||||
self.indices = Some(Indices {
|
||||
head: next,
|
||||
tail: idxs.tail,
|
||||
});
|
||||
|
||||
return Deque {
|
||||
indices: Some(Indices {
|
||||
head: head,
|
||||
tail: tail,
|
||||
}),
|
||||
_p: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
tail = next;
|
||||
}
|
||||
}
|
||||
None => Deque::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,43 +1,31 @@
|
||||
mod buffer;
|
||||
mod flow_control;
|
||||
mod prioritize;
|
||||
mod recv;
|
||||
mod send;
|
||||
mod state;
|
||||
mod store;
|
||||
mod stream;
|
||||
mod streams;
|
||||
|
||||
pub use self::streams::{Streams, StreamRef, Chunk};
|
||||
|
||||
use self::buffer::Buffer;
|
||||
use self::flow_control::FlowControl;
|
||||
use self::prioritize::Prioritize;
|
||||
use self::recv::Recv;
|
||||
use self::send::Send;
|
||||
use self::state::State;
|
||||
use self::store::{Store, Entry};
|
||||
use self::stream::Stream;
|
||||
|
||||
use {frame, Peer, StreamId, ConnectionError};
|
||||
use {frame, StreamId, ConnectionError};
|
||||
use proto::*;
|
||||
use error::Reason::*;
|
||||
use error::User::*;
|
||||
|
||||
// TODO: All the VecDeques should become linked lists using the State
|
||||
// values.
|
||||
#[derive(Debug)]
|
||||
pub struct Streams<P> {
|
||||
/// State related to managing the set of streams.
|
||||
inner: Inner<P>,
|
||||
|
||||
/// Streams
|
||||
streams: Store,
|
||||
}
|
||||
|
||||
/// Fields needed to manage state related to managing the set of streams. This
|
||||
/// is mostly split out to make ownership happy.
|
||||
///
|
||||
/// TODO: better name
|
||||
#[derive(Debug)]
|
||||
struct Inner<P> {
|
||||
/// Manages state transitions initiated by receiving frames
|
||||
recv: Recv<P>,
|
||||
|
||||
/// Manages state transitions initiated by sending frames
|
||||
send: Send<P>,
|
||||
}
|
||||
use http::{Request, Response};
|
||||
use bytes::Bytes;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Config {
|
||||
@@ -53,218 +41,3 @@ pub struct Config {
|
||||
/// Initial window size of locally initiated streams
|
||||
pub init_local_window_sz: WindowSize,
|
||||
}
|
||||
|
||||
impl<P: Peer> Streams<P> {
|
||||
pub fn new(config: Config) -> Self {
|
||||
Streams {
|
||||
inner: Inner {
|
||||
recv: Recv::new(&config),
|
||||
send: Send::new(&config),
|
||||
},
|
||||
streams: Store::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn recv_headers(&mut self, frame: frame::Headers)
|
||||
-> Result<Option<frame::Headers>, ConnectionError>
|
||||
{
|
||||
let id = frame.stream_id();
|
||||
|
||||
let state = match self.streams.entry(id) {
|
||||
Entry::Occupied(e) => e.into_mut(),
|
||||
Entry::Vacant(e) => {
|
||||
// Trailers cannot open a stream. Trailers are header frames
|
||||
// that do not contain pseudo headers. Requests MUST contain a
|
||||
// method and responses MUST contain a status. If they do not,t
|
||||
// hey are considered to be malformed.
|
||||
if frame.is_trailers() {
|
||||
return Err(ProtocolError.into());
|
||||
}
|
||||
|
||||
match try!(self.inner.recv.open(id)) {
|
||||
Some(state) => e.insert(state),
|
||||
None => return Ok(None),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if frame.is_trailers() {
|
||||
if !frame.is_end_stream() {
|
||||
// TODO: What error should this return?
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
try!(self.inner.recv.recv_eos(state));
|
||||
} else {
|
||||
try!(self.inner.recv.recv_headers(state, frame.is_end_stream()));
|
||||
}
|
||||
|
||||
if state.is_closed() {
|
||||
self.inner.dec_num_streams(id);
|
||||
}
|
||||
|
||||
Ok(Some(frame))
|
||||
}
|
||||
|
||||
pub fn recv_data(&mut self, frame: &frame::Data)
|
||||
-> Result<(), ConnectionError>
|
||||
{
|
||||
let id = frame.stream_id();
|
||||
|
||||
let state = match self.streams.get_mut(&id) {
|
||||
Some(state) => state,
|
||||
None => return Err(ProtocolError.into()),
|
||||
};
|
||||
|
||||
// Ensure there's enough capacity on the connection before acting on the
|
||||
// stream.
|
||||
try!(self.inner.recv.recv_data(frame, state));
|
||||
|
||||
if state.is_closed() {
|
||||
self.inner.dec_num_streams(id);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn recv_reset(&mut self, _frame: &frame::Reset)
|
||||
-> Result<(), ConnectionError>
|
||||
{
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
pub fn recv_window_update(&mut self, frame: frame::WindowUpdate)
|
||||
-> Result<(), ConnectionError> {
|
||||
let id = frame.stream_id();
|
||||
|
||||
if id.is_zero() {
|
||||
try!(self.inner.send.recv_connection_window_update(frame));
|
||||
} else {
|
||||
// The remote may send window updates for streams that the local now
|
||||
// considers closed. It's ok...
|
||||
if let Some(state) = self.streams.get_mut(&id) {
|
||||
try!(self.inner.send.recv_stream_window_update(frame, state));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn recv_push_promise(&mut self, _frame: frame::PushPromise)
|
||||
-> Result<(), ConnectionError>
|
||||
{
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
pub fn send_headers(&mut self, frame: &frame::Headers)
|
||||
-> Result<(), ConnectionError>
|
||||
{
|
||||
let id = frame.stream_id();
|
||||
|
||||
trace!("send_headers; id={:?}", id);
|
||||
|
||||
let state = match self.streams.entry(id) {
|
||||
Entry::Occupied(e) => e.into_mut(),
|
||||
Entry::Vacant(e) => {
|
||||
// Trailers cannot open a stream. Trailers are header frames
|
||||
// that do not contain pseudo headers. Requests MUST contain a
|
||||
// method and responses MUST contain a status. If they do not,t
|
||||
// hey are considered to be malformed.
|
||||
if frame.is_trailers() {
|
||||
// TODO: Should this be a different error?
|
||||
return Err(UnexpectedFrameType.into());
|
||||
}
|
||||
|
||||
let state = try!(self.inner.send.open(id));
|
||||
e.insert(state)
|
||||
}
|
||||
};
|
||||
|
||||
if frame.is_trailers() {
|
||||
try!(self.inner.send.send_eos(state));
|
||||
} else {
|
||||
try!(self.inner.send.send_headers(state, frame.is_end_stream()));
|
||||
}
|
||||
|
||||
if state.is_closed() {
|
||||
self.inner.dec_num_streams(id);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn send_data<B: Buf>(&mut self, frame: &frame::Data<B>)
|
||||
-> Result<(), ConnectionError>
|
||||
{
|
||||
let id = frame.stream_id();
|
||||
|
||||
let state = match self.streams.get_mut(&id) {
|
||||
Some(state) => state,
|
||||
None => return Err(UnexpectedFrameType.into()),
|
||||
};
|
||||
|
||||
// Ensure there's enough capacity on the connection before acting on the
|
||||
// stream.
|
||||
try!(self.inner.send.send_data(frame, state));
|
||||
|
||||
if state.is_closed() {
|
||||
self.inner.dec_num_streams(id);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn poll_window_update(&mut self)
|
||||
-> Poll<WindowUpdate, ConnectionError>
|
||||
{
|
||||
self.inner.send.poll_window_update(&mut self.streams)
|
||||
}
|
||||
|
||||
pub fn expand_window(&mut self, id: StreamId, sz: WindowSize)
|
||||
-> Result<(), ConnectionError>
|
||||
{
|
||||
if id.is_zero() {
|
||||
try!(self.inner.recv.expand_connection_window(sz));
|
||||
} else {
|
||||
if let Some(state) = self.streams.get_mut(&id) {
|
||||
try!(self.inner.recv.expand_stream_window(id, sz, state));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn send_pending_refusal<T, B>(&mut self, dst: &mut Codec<T, B>)
|
||||
-> Poll<(), ConnectionError>
|
||||
where T: AsyncWrite,
|
||||
B: Buf,
|
||||
{
|
||||
self.inner.recv.send_pending_refusal(dst)
|
||||
}
|
||||
|
||||
pub fn send_pending_window_updates<T, B>(&mut self, dst: &mut Codec<T, B>)
|
||||
-> Poll<(), ConnectionError>
|
||||
where T: AsyncWrite,
|
||||
B: Buf,
|
||||
{
|
||||
try_ready!(self.inner.recv.send_connection_window_update(dst));
|
||||
try_ready!(self.inner.recv.send_stream_window_update(&mut self.streams, dst));
|
||||
|
||||
Ok(().into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Peer> Inner<P> {
|
||||
fn dec_num_streams(&mut self, id: StreamId) {
|
||||
if self.is_local_init(id) {
|
||||
self.send.dec_num_streams();
|
||||
} else {
|
||||
self.recv.dec_num_streams();
|
||||
}
|
||||
}
|
||||
|
||||
fn is_local_init(&self, id: StreamId) -> bool {
|
||||
assert!(!id.is_zero());
|
||||
P::is_server() == id.is_server_initiated()
|
||||
}
|
||||
}
|
||||
|
||||
97
src/proto/streams/prioritize.rs
Normal file
97
src/proto/streams/prioritize.rs
Normal file
@@ -0,0 +1,97 @@
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) struct Prioritize<B> {
|
||||
pending_send: store::List<B>,
|
||||
|
||||
/// Holds frames that are waiting to be written to the socket
|
||||
buffer: Buffer<B>,
|
||||
}
|
||||
|
||||
impl<B> Prioritize<B>
|
||||
where B: Buf,
|
||||
{
|
||||
pub fn new() -> Prioritize<B> {
|
||||
Prioritize {
|
||||
pending_send: store::List::new(),
|
||||
buffer: Buffer::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn queue_frame(&mut self,
|
||||
frame: Frame<B>,
|
||||
stream: &mut store::Ptr<B>)
|
||||
{
|
||||
// queue the frame in the buffer
|
||||
stream.pending_send.push_back(&mut self.buffer, frame);
|
||||
|
||||
if stream.is_pending_send {
|
||||
debug_assert!(!self.pending_send.is_empty());
|
||||
|
||||
// Already queued to have frame processed.
|
||||
return;
|
||||
}
|
||||
|
||||
// Queue the stream
|
||||
self.push_sender(stream);
|
||||
}
|
||||
|
||||
pub fn poll_complete<T>(&mut self,
|
||||
store: &mut Store<B>,
|
||||
dst: &mut Codec<T, B>)
|
||||
-> Poll<(), ConnectionError>
|
||||
where T: AsyncWrite,
|
||||
{
|
||||
loop {
|
||||
// Ensure codec is ready
|
||||
try_ready!(dst.poll_ready());
|
||||
|
||||
match self.pop_frame(store) {
|
||||
Some(frame) => {
|
||||
// TODO: data frames should be handled specially...
|
||||
let res = dst.start_send(frame)?;
|
||||
|
||||
// We already verified that `dst` is ready to accept the
|
||||
// write
|
||||
assert!(res.is_ready());
|
||||
}
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
|
||||
Ok(().into())
|
||||
}
|
||||
|
||||
fn pop_frame(&mut self, store: &mut Store<B>) -> Option<Frame<B>> {
|
||||
match self.pop_sender(store) {
|
||||
Some(mut stream) => {
|
||||
let frame = stream.pending_send.pop_front(&mut self.buffer).unwrap();
|
||||
|
||||
if !stream.pending_send.is_empty() {
|
||||
self.push_sender(&mut stream);
|
||||
}
|
||||
|
||||
Some(frame)
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn push_sender(&mut self, stream: &mut store::Ptr<B>) {
|
||||
debug_assert!(!stream.is_pending_send);
|
||||
|
||||
self.pending_send.push(stream);
|
||||
|
||||
stream.is_pending_send = true;
|
||||
}
|
||||
|
||||
fn pop_sender<'a>(&mut self, store: &'a mut Store<B>) -> Option<store::Ptr<'a, B>> {
|
||||
match self.pending_send.pop(store) {
|
||||
Some(mut stream) => {
|
||||
stream.is_pending_send = false;
|
||||
Some(stream)
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
use {frame, Peer, ConnectionError};
|
||||
use {client, frame, ConnectionError};
|
||||
use proto::*;
|
||||
use super::*;
|
||||
|
||||
@@ -8,7 +8,7 @@ use std::collections::VecDeque;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Recv<P> {
|
||||
pub(super) struct Recv<P, B> {
|
||||
/// Maximum number of remote initiated streams
|
||||
max_streams: Option<usize>,
|
||||
|
||||
@@ -21,15 +21,35 @@ pub struct Recv<P> {
|
||||
/// Connection level flow control governing received data
|
||||
flow_control: FlowControl,
|
||||
|
||||
/// Streams that have pending window updates
|
||||
/// TODO: don't use a VecDeque
|
||||
pending_window_updates: VecDeque<StreamId>,
|
||||
|
||||
/// Holds frames that are waiting to be read
|
||||
buffer: Buffer<Bytes>,
|
||||
|
||||
/// Refused StreamId, this represents a frame that must be sent out.
|
||||
refused: Option<StreamId>,
|
||||
|
||||
_p: PhantomData<P>,
|
||||
_p: PhantomData<(P, B)>,
|
||||
}
|
||||
|
||||
impl<P: Peer> Recv<P> {
|
||||
#[derive(Debug)]
|
||||
pub(super) struct Chunk {
|
||||
/// Data frames pending receival
|
||||
pub pending_recv: buffer::Deque<Bytes>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct Indices {
|
||||
head: store::Key,
|
||||
tail: store::Key,
|
||||
}
|
||||
|
||||
impl<P, B> Recv<P, B>
|
||||
where P: Peer,
|
||||
B: Buf,
|
||||
{
|
||||
pub fn new(config: &Config) -> Self {
|
||||
Recv {
|
||||
max_streams: config.max_remote_initiated,
|
||||
@@ -37,6 +57,7 @@ impl<P: Peer> Recv<P> {
|
||||
init_window_sz: config.init_remote_window_sz,
|
||||
flow_control: FlowControl::new(config.init_remote_window_sz),
|
||||
pending_window_updates: VecDeque::new(),
|
||||
buffer: Buffer::new(),
|
||||
refused: None,
|
||||
_p: PhantomData,
|
||||
}
|
||||
@@ -45,40 +66,58 @@ impl<P: Peer> Recv<P> {
|
||||
/// Update state reflecting a new, remotely opened stream
|
||||
///
|
||||
/// Returns the stream state if successful. `None` if refused
|
||||
pub fn open(&mut self, id: StreamId) -> Result<Option<State>, ConnectionError> {
|
||||
pub fn open(&mut self, id: StreamId) -> Result<Option<Stream<B>>, ConnectionError> {
|
||||
assert!(self.refused.is_none());
|
||||
|
||||
try!(self.ensure_can_open(id));
|
||||
|
||||
if let Some(max) = self.max_streams {
|
||||
if max <= self.num_streams {
|
||||
self.refused = Some(id);
|
||||
return Ok(None);
|
||||
}
|
||||
if !self.can_inc_num_streams() {
|
||||
self.refused = Some(id);
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
// Increment the number of remote initiated streams
|
||||
self.num_streams += 1;
|
||||
|
||||
Ok(Some(State::default()))
|
||||
Ok(Some(Stream::new(id)))
|
||||
}
|
||||
|
||||
/// Transition the stream state based on receiving headers
|
||||
pub fn recv_headers(&mut self, state: &mut State, eos: bool)
|
||||
-> Result<(), ConnectionError>
|
||||
pub fn recv_headers(&mut self,
|
||||
frame: frame::Headers,
|
||||
stream: &mut store::Ptr<B>)
|
||||
-> Result<Option<frame::Headers>, ConnectionError>
|
||||
{
|
||||
state.recv_open(self.init_window_sz, eos)
|
||||
let is_initial = stream.state.recv_open(self.init_window_sz, frame.is_end_stream())?;
|
||||
|
||||
if is_initial {
|
||||
if !self.can_inc_num_streams() {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
// Increment the number of concurrent streams
|
||||
self.inc_num_streams();
|
||||
}
|
||||
|
||||
// Only servers can receive a headers frame that initiates the stream.
|
||||
// This is verified in `Streams` before calling this function.
|
||||
if P::is_server() {
|
||||
Ok(Some(frame))
|
||||
} else {
|
||||
// Push the frame onto the recv buffer
|
||||
stream.pending_recv.push_back(&mut self.buffer, frame.into());
|
||||
stream.notify_recv();
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn recv_eos(&mut self, state: &mut State)
|
||||
pub fn recv_eos(&mut self, stream: &mut Stream<B>)
|
||||
-> Result<(), ConnectionError>
|
||||
{
|
||||
state.recv_close()
|
||||
stream.state.recv_close()
|
||||
}
|
||||
|
||||
pub fn recv_data(&mut self,
|
||||
frame: &frame::Data,
|
||||
state: &mut State)
|
||||
frame: frame::Data,
|
||||
stream: &mut store::Ptr<B>)
|
||||
-> Result<(), ConnectionError>
|
||||
{
|
||||
let sz = frame.payload().len();
|
||||
@@ -89,7 +128,7 @@ impl<P: Peer> Recv<P> {
|
||||
|
||||
let sz = sz as WindowSize;
|
||||
|
||||
match state.recv_flow_control() {
|
||||
match stream.recv_flow_control() {
|
||||
Some(flow) => {
|
||||
// Ensure there's enough capacity on the connection before
|
||||
// acting on the stream.
|
||||
@@ -106,12 +145,97 @@ impl<P: Peer> Recv<P> {
|
||||
}
|
||||
|
||||
if frame.is_end_stream() {
|
||||
try!(state.recv_close());
|
||||
try!(stream.state.recv_close());
|
||||
}
|
||||
|
||||
// Push the frame onto the recv buffer
|
||||
stream.pending_recv.push_back(&mut self.buffer, frame.into());
|
||||
stream.notify_recv();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn recv_push_promise(&mut self, frame: frame::PushPromise, stream: &mut store::Ptr<B>)
|
||||
-> Result<(), ConnectionError>
|
||||
{
|
||||
// First, make sure that the values are legit
|
||||
self.ensure_can_reserve(frame.promised_id())?;
|
||||
|
||||
// Make sure that the stream state is valid
|
||||
stream.state.ensure_recv_open()?;
|
||||
|
||||
// TODO: Streams in the reserved states do not count towards the concurrency
|
||||
// limit. However, it seems like there should be a cap otherwise this
|
||||
// could grow in memory indefinitely.
|
||||
|
||||
/*
|
||||
if !self.inc_num_streams() {
|
||||
self.refused = Some(frame.promised_id());
|
||||
return Ok(());
|
||||
}
|
||||
*/
|
||||
|
||||
// TODO: All earlier stream IDs should be implicitly closed.
|
||||
|
||||
// Now, create a new entry for the stream
|
||||
let mut new_stream = Stream::new(frame.promised_id());
|
||||
new_stream.state.reserve_remote();
|
||||
|
||||
let mut ppp = stream.pending_push_promises.take();
|
||||
|
||||
{
|
||||
// Store the stream
|
||||
let mut new_stream = stream.store()
|
||||
.insert(frame.promised_id(), new_stream);
|
||||
|
||||
ppp.push(&mut new_stream);
|
||||
}
|
||||
|
||||
stream.pending_push_promises = ppp;
|
||||
stream.notify_recv();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn recv_reset(&mut self, frame: frame::Reset, stream: &mut Stream<B>)
|
||||
-> Result<(), ConnectionError>
|
||||
{
|
||||
let err = ConnectionError::Proto(frame.reason());
|
||||
|
||||
// Notify the stream
|
||||
stream.state.recv_err(&err);
|
||||
stream.notify_recv();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn recv_err(&mut self, err: &ConnectionError, stream: &mut Stream<B>) {
|
||||
// Receive an error
|
||||
stream.state.recv_err(err);
|
||||
|
||||
// If a receiver is waiting, notify it
|
||||
stream.notify_recv();
|
||||
}
|
||||
|
||||
/// Returns true if the current stream concurrency can be incremetned
|
||||
fn can_inc_num_streams(&self) -> bool {
|
||||
if let Some(max) = self.max_streams {
|
||||
max > self.num_streams
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// Increments the number of concurrenty streams. Panics on failure as this
|
||||
/// should have been validated before hand.
|
||||
fn inc_num_streams(&mut self) {
|
||||
if !self.can_inc_num_streams() {
|
||||
panic!();
|
||||
}
|
||||
|
||||
// Increment the number of remote initiated streams
|
||||
self.num_streams += 1;
|
||||
}
|
||||
|
||||
pub fn dec_num_streams(&mut self) {
|
||||
self.num_streams -= 1;
|
||||
}
|
||||
@@ -132,11 +256,25 @@ impl<P: Peer> Recv<P> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns true if the remote peer can reserve a stream with the given ID.
|
||||
fn ensure_can_reserve(&self, promised_id: StreamId) -> Result<(), ConnectionError> {
|
||||
// TODO: Are there other rules?
|
||||
if P::is_server() {
|
||||
// The remote is a client and cannot reserve
|
||||
return Err(ProtocolError.into());
|
||||
}
|
||||
|
||||
if !promised_id.is_server_initiated() {
|
||||
return Err(ProtocolError.into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Send any pending refusals.
|
||||
pub fn send_pending_refusal<T, B>(&mut self, dst: &mut Codec<T, B>)
|
||||
pub fn send_pending_refusal<T>(&mut self, dst: &mut Codec<T, B>)
|
||||
-> Poll<(), ConnectionError>
|
||||
where T: AsyncWrite,
|
||||
B: Buf,
|
||||
{
|
||||
if let Some(stream_id) = self.refused.take() {
|
||||
let frame = frame::Reset::new(stream_id, RefusedStream);
|
||||
@@ -168,11 +306,11 @@ impl<P: Peer> Recv<P> {
|
||||
pub fn expand_stream_window(&mut self,
|
||||
id: StreamId,
|
||||
sz: WindowSize,
|
||||
state: &mut State)
|
||||
stream: &mut store::Ptr<B>)
|
||||
-> Result<(), ConnectionError>
|
||||
{
|
||||
// TODO: handle overflow
|
||||
if let Some(flow) = state.recv_flow_control() {
|
||||
if let Some(flow) = stream.recv_flow_control() {
|
||||
flow.expand_window(sz);
|
||||
self.pending_window_updates.push_back(id);
|
||||
}
|
||||
@@ -181,10 +319,9 @@ impl<P: Peer> Recv<P> {
|
||||
}
|
||||
|
||||
/// Send connection level window update
|
||||
pub fn send_connection_window_update<T, B>(&mut self, dst: &mut Codec<T, B>)
|
||||
pub fn send_connection_window_update<T>(&mut self, dst: &mut Codec<T, B>)
|
||||
-> Poll<(), ConnectionError>
|
||||
where T: AsyncWrite,
|
||||
B: Buf,
|
||||
{
|
||||
if let Some(incr) = self.flow_control.peek_window_update() {
|
||||
let frame = frame::WindowUpdate::new(StreamId::zero(), incr);
|
||||
@@ -199,17 +336,47 @@ impl<P: Peer> Recv<P> {
|
||||
Ok(().into())
|
||||
}
|
||||
|
||||
|
||||
pub fn poll_chunk(&mut self, stream: &mut Stream<B>)
|
||||
-> Poll<Option<Chunk>, ConnectionError>
|
||||
{
|
||||
let frames = stream.pending_recv
|
||||
.take_while(&mut self.buffer, |frame| frame.is_data());
|
||||
|
||||
if frames.is_empty() {
|
||||
if stream.state.is_recv_closed() {
|
||||
Ok(None.into())
|
||||
} else {
|
||||
stream.recv_task = Some(task::current());
|
||||
Ok(Async::NotReady)
|
||||
}
|
||||
} else {
|
||||
Ok(Some(Chunk {
|
||||
pending_recv: frames,
|
||||
}).into())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pop_bytes(&mut self, chunk: &mut Chunk) -> Option<Bytes> {
|
||||
match chunk.pending_recv.pop_front(&mut self.buffer) {
|
||||
Some(Frame::Data(frame)) => {
|
||||
Some(frame.into_payload())
|
||||
}
|
||||
None => None,
|
||||
_ => panic!("unexpected frame type"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Send stream level window update
|
||||
pub fn send_stream_window_update<T, B>(&mut self,
|
||||
streams: &mut Store,
|
||||
dst: &mut Codec<T, B>)
|
||||
pub fn send_stream_window_update<T>(&mut self,
|
||||
streams: &mut Store<B>,
|
||||
dst: &mut Codec<T, B>)
|
||||
-> Poll<(), ConnectionError>
|
||||
where T: AsyncWrite,
|
||||
B: Buf,
|
||||
{
|
||||
while let Some(id) = self.pending_window_updates.pop_front() {
|
||||
let flow = streams.get_mut(&id)
|
||||
.and_then(|state| state.recv_flow_control());
|
||||
let flow = streams.find_mut(&id)
|
||||
.and_then(|stream| stream.into_mut().recv_flow_control());
|
||||
|
||||
|
||||
if let Some(flow) = flow {
|
||||
@@ -233,3 +400,27 @@ impl<P: Peer> Recv<P> {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
||||
impl<B> Recv<client::Peer, B>
|
||||
where B: Buf,
|
||||
{
|
||||
pub fn poll_response(&mut self, stream: &mut store::Ptr<B>)
|
||||
-> Poll<Response<()>, ConnectionError> {
|
||||
// If the buffer is not empty, then the first frame must be a HEADERS
|
||||
// frame or the user violated the contract.
|
||||
match stream.pending_recv.pop_front(&mut self.buffer) {
|
||||
Some(Frame::Headers(v)) => {
|
||||
// TODO: This error should probably be caught on receipt of the
|
||||
// frame vs. now.
|
||||
Ok(client::Peer::convert_poll_message(v)?.into())
|
||||
}
|
||||
Some(frame) => unimplemented!(),
|
||||
None => {
|
||||
stream.state.ensure_recv_open()?;
|
||||
|
||||
stream.recv_task = Some(task::current());
|
||||
Ok(Async::NotReady)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use {frame, Peer, ConnectionError};
|
||||
use {frame, ConnectionError};
|
||||
use proto::*;
|
||||
use super::*;
|
||||
|
||||
@@ -10,13 +10,16 @@ use std::collections::VecDeque;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Send<P> {
|
||||
pub(super) struct Send<P, B> {
|
||||
/// Maximum number of locally initiated streams
|
||||
max_streams: Option<usize>,
|
||||
|
||||
/// Current number of locally initiated streams
|
||||
num_streams: usize,
|
||||
|
||||
/// Stream identifier to use for next initialized stream.
|
||||
next_stream_id: StreamId,
|
||||
|
||||
/// Initial window size of locally initiated streams
|
||||
init_window_sz: WindowSize,
|
||||
|
||||
@@ -27,6 +30,8 @@ pub struct Send<P> {
|
||||
// XXX It would be cool if this didn't exist.
|
||||
pending_window_updates: VecDeque<StreamId>,
|
||||
|
||||
prioritize: Prioritize<B>,
|
||||
|
||||
/// When `poll_window_update` is not ready, then the calling task is saved to
|
||||
/// be notified later. Access to poll_window_update must not be shared across tasks,
|
||||
/// as we only track a single task (and *not* i.e. a task per stream id).
|
||||
@@ -35,13 +40,24 @@ pub struct Send<P> {
|
||||
_p: PhantomData<P>,
|
||||
}
|
||||
|
||||
impl<P: Peer> Send<P> {
|
||||
impl<P, B> Send<P, B>
|
||||
where P: Peer,
|
||||
B: Buf,
|
||||
{
|
||||
pub fn new(config: &Config) -> Self {
|
||||
let next_stream_id = if P::is_server() {
|
||||
2
|
||||
} else {
|
||||
1
|
||||
};
|
||||
|
||||
Send {
|
||||
max_streams: config.max_local_initiated,
|
||||
num_streams: 0,
|
||||
next_stream_id: next_stream_id.into(),
|
||||
init_window_sz: config.init_local_window_sz,
|
||||
flow_control: FlowControl::new(config.init_local_window_sz),
|
||||
prioritize: Prioritize::new(),
|
||||
pending_window_updates: VecDeque::new(),
|
||||
blocked: None,
|
||||
_p: PhantomData,
|
||||
@@ -51,8 +67,8 @@ impl<P: Peer> Send<P> {
|
||||
/// Update state reflecting a new, locally opened stream
|
||||
///
|
||||
/// Returns the stream state if successful. `None` if refused
|
||||
pub fn open(&mut self, id: StreamId) -> Result<State, ConnectionError> {
|
||||
try!(self.ensure_can_open(id));
|
||||
pub fn open(&mut self) -> Result<Stream<B>, ConnectionError> {
|
||||
try!(self.ensure_can_open());
|
||||
|
||||
if let Some(max) = self.max_streams {
|
||||
if max <= self.num_streams {
|
||||
@@ -60,27 +76,38 @@ impl<P: Peer> Send<P> {
|
||||
}
|
||||
}
|
||||
|
||||
let ret = Stream::new(self.next_stream_id);
|
||||
|
||||
// Increment the number of locally initiated streams
|
||||
self.num_streams += 1;
|
||||
self.next_stream_id.increment();
|
||||
|
||||
Ok(State::default())
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
pub fn send_headers(&mut self, state: &mut State, eos: bool)
|
||||
pub fn send_headers(&mut self,
|
||||
frame: frame::Headers,
|
||||
stream: &mut store::Ptr<B>)
|
||||
-> Result<(), ConnectionError>
|
||||
{
|
||||
state.send_open(self.init_window_sz, eos)
|
||||
// Update the state
|
||||
stream.state.send_open(self.init_window_sz, frame.is_end_stream())?;
|
||||
|
||||
// Queue the frame for sending
|
||||
self.prioritize.queue_frame(frame.into(), stream);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn send_eos(&mut self, state: &mut State)
|
||||
pub fn send_eos(&mut self, stream: &mut Stream<B>)
|
||||
-> Result<(), ConnectionError>
|
||||
{
|
||||
state.send_close()
|
||||
stream.state.send_close()
|
||||
}
|
||||
|
||||
pub fn send_data<B: Buf>(&mut self,
|
||||
frame: &frame::Data<B>,
|
||||
state: &mut State)
|
||||
pub fn send_data(&mut self,
|
||||
frame: frame::Data<B>,
|
||||
stream: &mut store::Ptr<B>)
|
||||
-> Result<(), ConnectionError>
|
||||
{
|
||||
let sz = frame.payload().remaining();
|
||||
@@ -94,7 +121,7 @@ impl<P: Peer> Send<P> {
|
||||
|
||||
// Make borrow checker happy
|
||||
loop {
|
||||
match state.send_flow_control() {
|
||||
match stream.send_flow_control() {
|
||||
Some(flow) => {
|
||||
try!(self.flow_control.ensure_window(sz, FlowControlViolation));
|
||||
|
||||
@@ -110,7 +137,7 @@ impl<P: Peer> Send<P> {
|
||||
None => {}
|
||||
}
|
||||
|
||||
if state.is_closed() {
|
||||
if stream.state.is_closed() {
|
||||
return Err(InactiveStreamId.into())
|
||||
} else {
|
||||
return Err(UnexpectedFrameType.into())
|
||||
@@ -118,14 +145,25 @@ impl<P: Peer> Send<P> {
|
||||
}
|
||||
|
||||
if frame.is_end_stream() {
|
||||
try!(state.send_close());
|
||||
try!(stream.state.send_close());
|
||||
}
|
||||
|
||||
self.prioritize.queue_frame(frame.into(), stream);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn poll_complete<T>(&mut self,
|
||||
store: &mut Store<B>,
|
||||
dst: &mut Codec<T, B>)
|
||||
-> Poll<(), ConnectionError>
|
||||
where T: AsyncWrite,
|
||||
{
|
||||
self.prioritize.poll_complete(store, dst)
|
||||
}
|
||||
|
||||
/// Get pending window updates
|
||||
pub fn poll_window_update(&mut self, streams: &mut Store)
|
||||
pub fn poll_window_update(&mut self, streams: &mut Store<B>)
|
||||
-> Poll<WindowUpdate, ConnectionError>
|
||||
{
|
||||
// This biases connection window updates, which probably makes sense.
|
||||
@@ -138,8 +176,8 @@ impl<P: Peer> Send<P> {
|
||||
// TODO this should probably account for stream priority?
|
||||
let update = self.pending_window_updates.pop_front()
|
||||
.and_then(|id| {
|
||||
streams.get_mut(&id)
|
||||
.and_then(|state| state.send_flow_control())
|
||||
streams.find_mut(&id)
|
||||
.and_then(|stream| stream.into_mut().send_flow_control())
|
||||
.and_then(|flow| flow.apply_window_update())
|
||||
.map(|incr| WindowUpdate::new(id, incr))
|
||||
});
|
||||
@@ -171,10 +209,10 @@ impl<P: Peer> Send<P> {
|
||||
|
||||
pub fn recv_stream_window_update(&mut self,
|
||||
frame: frame::WindowUpdate,
|
||||
state: &mut State)
|
||||
stream: &mut store::Ptr<B>)
|
||||
-> Result<(), ConnectionError>
|
||||
{
|
||||
if let Some(flow) = state.send_flow_control() {
|
||||
if let Some(flow) = stream.send_flow_control() {
|
||||
// TODO: Handle invalid increment
|
||||
flow.expand_window(frame.size_increment());
|
||||
}
|
||||
@@ -191,15 +229,13 @@ impl<P: Peer> Send<P> {
|
||||
}
|
||||
|
||||
/// Returns true if the local actor can initiate a stream with the given ID.
|
||||
fn ensure_can_open(&self, id: StreamId) -> Result<(), ConnectionError> {
|
||||
fn ensure_can_open(&self) -> Result<(), ConnectionError> {
|
||||
if P::is_server() {
|
||||
// Servers cannot open streams. PushPromise must first be reserved.
|
||||
return Err(UnexpectedFrameType.into());
|
||||
}
|
||||
|
||||
if !id.is_client_initiated() {
|
||||
return Err(InvalidStreamId.into());
|
||||
}
|
||||
// TODO: Handle StreamId overflow
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ enum Inner {
|
||||
Idle,
|
||||
// TODO: these states shouldn't count against concurrency limits:
|
||||
//ReservedLocal,
|
||||
//ReservedRemote,
|
||||
ReservedRemote,
|
||||
Open {
|
||||
local: Peer,
|
||||
remote: Peer,
|
||||
@@ -66,7 +66,7 @@ enum Inner {
|
||||
HalfClosedLocal(Peer), // TODO: explicitly name this value
|
||||
HalfClosedRemote(Peer),
|
||||
// When reset, a reason is provided
|
||||
Closed(Option<Reason>),
|
||||
Closed(Option<Cause>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
@@ -76,6 +76,12 @@ enum Peer {
|
||||
Streaming(FlowControl),
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
enum Cause {
|
||||
Proto(Reason),
|
||||
Io,
|
||||
}
|
||||
|
||||
impl State {
|
||||
/// Opens the send-half of a stream if it is not already open.
|
||||
pub fn send_open(&mut self, sz: WindowSize, eos: bool) -> Result<(), ConnectionError> {
|
||||
@@ -120,11 +126,16 @@ impl State {
|
||||
|
||||
/// Open the receive have of the stream, this action is taken when a HEADERS
|
||||
/// frame is received.
|
||||
pub fn recv_open(&mut self, sz: WindowSize, eos: bool) -> Result<(), ConnectionError> {
|
||||
///
|
||||
/// Returns true if this transitions the state to Open
|
||||
pub fn recv_open(&mut self, sz: WindowSize, eos: bool) -> Result<bool, ConnectionError> {
|
||||
let remote = Peer::streaming(sz);
|
||||
let mut initial = false;
|
||||
|
||||
self.inner = match self.inner {
|
||||
Idle => {
|
||||
initial = true;
|
||||
|
||||
if eos {
|
||||
HalfClosedRemote(AwaitingHeaders)
|
||||
} else {
|
||||
@@ -134,6 +145,18 @@ impl State {
|
||||
}
|
||||
}
|
||||
}
|
||||
ReservedRemote => {
|
||||
initial = true;
|
||||
|
||||
if eos {
|
||||
Closed(None)
|
||||
} else {
|
||||
Open {
|
||||
local: AwaitingHeaders,
|
||||
remote,
|
||||
}
|
||||
}
|
||||
}
|
||||
Open { local, remote: AwaitingHeaders } => {
|
||||
if eos {
|
||||
HalfClosedRemote(local)
|
||||
@@ -157,7 +180,18 @@ impl State {
|
||||
}
|
||||
};
|
||||
|
||||
return Ok(());
|
||||
return Ok(initial);
|
||||
}
|
||||
|
||||
/// Transition from Idle -> ReservedRemote
|
||||
pub fn reserve_remote(&mut self) -> Result<(), ConnectionError> {
|
||||
match self.inner {
|
||||
Idle => {
|
||||
self.inner = ReservedRemote;
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(ProtocolError.into()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Indicates that the remote side will not send more data to the local.
|
||||
@@ -178,6 +212,19 @@ impl State {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn recv_err(&mut self, err: &ConnectionError) {
|
||||
match self.inner {
|
||||
Closed(..) => {}
|
||||
_ => {
|
||||
self.inner = Closed(match *err {
|
||||
ConnectionError::Proto(reason) => Some(Cause::Proto(reason)),
|
||||
ConnectionError::Io(..) => Some(Cause::Io),
|
||||
_ => panic!("cannot terminate stream with user error"),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Indicates that the local side will not send more data to the local.
|
||||
pub fn send_close(&mut self) -> Result<(), ConnectionError> {
|
||||
match self.inner {
|
||||
@@ -196,6 +243,17 @@ impl State {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if a stream with the current state counts against the
|
||||
/// concurrency limit.
|
||||
pub fn is_counted(&self) -> bool {
|
||||
match self.inner {
|
||||
Open { .. } => true,
|
||||
HalfClosedLocal(..) => true,
|
||||
HalfClosedRemote(..) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_closed(&self) -> bool {
|
||||
match self.inner {
|
||||
Closed(_) => true,
|
||||
@@ -203,6 +261,13 @@ impl State {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_recv_closed(&self) -> bool {
|
||||
match self.inner {
|
||||
Closed(..) | HalfClosedRemote(..) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn recv_flow_control(&mut self) -> Option<&mut FlowControl> {
|
||||
match self.inner {
|
||||
Open { ref mut remote, .. } |
|
||||
@@ -218,6 +283,21 @@ impl State {
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ensure_recv_open(&self) -> Result<(), ConnectionError> {
|
||||
use std::io;
|
||||
|
||||
// TODO: Is this correct?
|
||||
match self.inner {
|
||||
Closed(Some(Cause::Proto(reason))) => {
|
||||
Err(ConnectionError::Proto(reason))
|
||||
}
|
||||
Closed(Some(Cause::Io)) => {
|
||||
Err(ConnectionError::Io(io::ErrorKind::BrokenPipe.into()))
|
||||
}
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for State {
|
||||
|
||||
@@ -1,32 +1,59 @@
|
||||
extern crate slab;
|
||||
|
||||
use super::*;
|
||||
|
||||
use slab;
|
||||
|
||||
use std::ops;
|
||||
use std::collections::{HashMap, hash_map};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
/// Storage for streams
|
||||
#[derive(Debug)]
|
||||
pub struct Store {
|
||||
slab: slab::Slab<State>,
|
||||
pub(super) struct Store<B> {
|
||||
slab: slab::Slab<Stream<B>>,
|
||||
ids: HashMap<StreamId, usize>,
|
||||
}
|
||||
|
||||
pub enum Entry<'a> {
|
||||
Occupied(OccupiedEntry<'a>),
|
||||
Vacant(VacantEntry<'a>),
|
||||
/// "Pointer" to an entry in the store
|
||||
pub(super) struct Ptr<'a, B: 'a> {
|
||||
key: Key,
|
||||
store: &'a mut Store<B>,
|
||||
}
|
||||
|
||||
pub struct OccupiedEntry<'a> {
|
||||
/// References an entry in the store.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub(super) struct Key(usize);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) struct List<B> {
|
||||
indices: Option<store::Indices>,
|
||||
_p: PhantomData<B>,
|
||||
}
|
||||
|
||||
/// A linked list
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct Indices {
|
||||
pub head: Key,
|
||||
pub tail: Key,
|
||||
}
|
||||
|
||||
pub(super) enum Entry<'a, B: 'a> {
|
||||
Occupied(OccupiedEntry<'a, B>),
|
||||
Vacant(VacantEntry<'a, B>),
|
||||
}
|
||||
|
||||
pub(super) struct OccupiedEntry<'a, B: 'a> {
|
||||
ids: hash_map::OccupiedEntry<'a, StreamId, usize>,
|
||||
slab: &'a mut slab::Slab<State>,
|
||||
slab: &'a mut slab::Slab<Stream<B>>,
|
||||
}
|
||||
|
||||
pub struct VacantEntry<'a> {
|
||||
pub(super) struct VacantEntry<'a, B: 'a> {
|
||||
ids: hash_map::VacantEntry<'a, StreamId, usize>,
|
||||
slab: &'a mut slab::Slab<State>,
|
||||
slab: &'a mut slab::Slab<Stream<B>>,
|
||||
}
|
||||
|
||||
impl Store {
|
||||
// ===== impl Store =====
|
||||
|
||||
impl<B> Store<B> {
|
||||
pub fn new() -> Self {
|
||||
Store {
|
||||
slab: slab::Slab::new(),
|
||||
@@ -34,15 +61,35 @@ impl Store {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self, id: &StreamId) -> Option<&mut State> {
|
||||
if let Some(handle) = self.ids.get(id) {
|
||||
Some(&mut self.slab[*handle])
|
||||
pub fn resolve(&mut self, key: Key) -> Ptr<B> {
|
||||
Ptr {
|
||||
key: key,
|
||||
store: self,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn find_mut(&mut self, id: &StreamId) -> Option<Ptr<B>> {
|
||||
if let Some(&key) = self.ids.get(id) {
|
||||
Some(Ptr {
|
||||
key: Key(key),
|
||||
store: self,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn entry(&mut self, id: StreamId) -> Entry {
|
||||
pub fn insert(&mut self, id: StreamId, val: Stream<B>) -> Ptr<B> {
|
||||
let key = self.slab.insert(val);
|
||||
assert!(self.ids.insert(id, key).is_none());
|
||||
|
||||
Ptr {
|
||||
key: Key(key),
|
||||
store: self,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn find_entry(&mut self, id: StreamId) -> Entry<B> {
|
||||
use self::hash_map::Entry::*;
|
||||
|
||||
match self.ids.entry(id) {
|
||||
@@ -60,22 +107,145 @@ impl Store {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn for_each<F>(&mut self, mut f: F)
|
||||
where F: FnMut(&mut Stream<B>)
|
||||
{
|
||||
for &id in self.ids.values() {
|
||||
f(&mut self.slab[id])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> OccupiedEntry<'a> {
|
||||
pub fn into_mut(self) -> &'a mut State {
|
||||
// ===== impl List =====
|
||||
|
||||
impl<B> List<B> {
|
||||
pub fn new() -> Self {
|
||||
List {
|
||||
indices: None,
|
||||
_p: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.indices.is_none()
|
||||
}
|
||||
|
||||
pub fn take(&mut self) -> Self {
|
||||
List {
|
||||
indices: self.indices.take(),
|
||||
_p: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push(&mut self, stream: &mut store::Ptr<B>) {
|
||||
// The next pointer shouldn't be set
|
||||
debug_assert!(stream.next.is_none());
|
||||
|
||||
// Queue the stream
|
||||
match self.indices {
|
||||
Some(ref mut idxs) => {
|
||||
// Update the current tail node to point to `stream`
|
||||
stream.resolve(idxs.tail).next = Some(stream.key());
|
||||
|
||||
// Update the tail pointer
|
||||
idxs.tail = stream.key();
|
||||
}
|
||||
None => {
|
||||
self.indices = Some(store::Indices {
|
||||
head: stream.key(),
|
||||
tail: stream.key(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pop<'a>(&mut self, store: &'a mut Store<B>) -> Option<store::Ptr<'a, B>> {
|
||||
if let Some(mut idxs) = self.indices {
|
||||
let mut stream = store.resolve(idxs.head);
|
||||
|
||||
if idxs.head == idxs.tail {
|
||||
assert!(stream.next.is_none());
|
||||
self.indices = None;
|
||||
} else {
|
||||
idxs.head = stream.next.take().unwrap();
|
||||
self.indices = Some(idxs);
|
||||
}
|
||||
|
||||
return Some(stream);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Ptr =====
|
||||
|
||||
impl<'a, B: 'a> Ptr<'a, B> {
|
||||
pub fn key(&self) -> Key {
|
||||
self.key
|
||||
}
|
||||
|
||||
pub fn store(&mut self) -> &mut Store<B> {
|
||||
&mut self.store
|
||||
}
|
||||
|
||||
pub fn resolve(&mut self, key: Key) -> Ptr<B> {
|
||||
Ptr {
|
||||
key: key,
|
||||
store: self.store,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_mut(self) -> &'a mut Stream<B> {
|
||||
&mut self.store.slab[self.key.0]
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, B: 'a> ops::Deref for Ptr<'a, B> {
|
||||
type Target = Stream<B>;
|
||||
|
||||
fn deref(&self) -> &Stream<B> {
|
||||
&self.store.slab[self.key.0]
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, B: 'a> ops::DerefMut for Ptr<'a, B> {
|
||||
fn deref_mut(&mut self) -> &mut Stream<B> {
|
||||
&mut self.store.slab[self.key.0]
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl OccupiedEntry =====
|
||||
|
||||
impl<'a, B> OccupiedEntry<'a, B> {
|
||||
pub fn key(&self) -> Key {
|
||||
Key(*self.ids.get())
|
||||
}
|
||||
|
||||
pub fn get(&self) -> &Stream<B> {
|
||||
&self.slab[*self.ids.get()]
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self) -> &mut Stream<B> {
|
||||
&mut self.slab[*self.ids.get()]
|
||||
}
|
||||
|
||||
pub fn into_mut(self) -> &'a mut Stream<B> {
|
||||
&mut self.slab[*self.ids.get()]
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> VacantEntry<'a> {
|
||||
pub fn insert(self, value: State) -> &'a mut State {
|
||||
// ===== impl VacantEntry =====
|
||||
|
||||
impl<'a, B> VacantEntry<'a, B> {
|
||||
pub fn insert(self, value: Stream<B>) -> Key {
|
||||
// Insert the value in the slab
|
||||
let handle = self.slab.insert(value);
|
||||
let key = self.slab.insert(value);
|
||||
|
||||
// Insert the handle in the ID map
|
||||
self.ids.insert(handle);
|
||||
self.ids.insert(key);
|
||||
|
||||
&mut self.slab[handle]
|
||||
Key(key)
|
||||
}
|
||||
}
|
||||
|
||||
60
src/proto/streams/stream.rs
Normal file
60
src/proto/streams/stream.rs
Normal file
@@ -0,0 +1,60 @@
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) struct Stream<B> {
|
||||
/// The h2 stream identifier
|
||||
pub id: StreamId,
|
||||
|
||||
/// Current state of the stream
|
||||
pub state: State,
|
||||
|
||||
/// Frames pending for this stream to read
|
||||
pub pending_recv: buffer::Deque<Bytes>,
|
||||
|
||||
/// Task tracking receiving frames
|
||||
pub recv_task: Option<task::Task>,
|
||||
|
||||
/// Frames pending for this stream being sent to the socket
|
||||
pub pending_send: buffer::Deque<B>,
|
||||
|
||||
/// Next node in the `Stream` linked list.
|
||||
///
|
||||
/// This field is used in different linked lists depending on the stream
|
||||
/// state.
|
||||
pub next: Option<store::Key>,
|
||||
|
||||
/// The stream's pending push promises
|
||||
pub pending_push_promises: store::List<B>,
|
||||
|
||||
/// True if the stream is currently pending send
|
||||
pub is_pending_send: bool,
|
||||
}
|
||||
|
||||
impl<B> Stream<B> {
|
||||
pub fn new(id: StreamId) -> Stream<B> {
|
||||
Stream {
|
||||
id,
|
||||
state: State::default(),
|
||||
pending_recv: buffer::Deque::new(),
|
||||
recv_task: None,
|
||||
pending_send: buffer::Deque::new(),
|
||||
next: None,
|
||||
pending_push_promises: store::List::new(),
|
||||
is_pending_send: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_flow_control(&mut self) -> Option<&mut FlowControl> {
|
||||
self.state.send_flow_control()
|
||||
}
|
||||
|
||||
pub fn recv_flow_control(&mut self) -> Option<&mut FlowControl> {
|
||||
self.state.recv_flow_control()
|
||||
}
|
||||
|
||||
pub fn notify_recv(&mut self) {
|
||||
if let Some(ref mut task) = self.recv_task {
|
||||
task.notify();
|
||||
}
|
||||
}
|
||||
}
|
||||
446
src/proto/streams/streams.rs
Normal file
446
src/proto/streams/streams.rs
Normal file
@@ -0,0 +1,446 @@
|
||||
use client;
|
||||
use proto::*;
|
||||
use super::*;
|
||||
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
// TODO: All the VecDeques should become linked lists using the State
|
||||
// values.
|
||||
#[derive(Debug)]
|
||||
pub struct Streams<P, B> {
|
||||
inner: Arc<Mutex<Inner<P, B>>>,
|
||||
}
|
||||
|
||||
/// Reference to the stream state
|
||||
#[derive(Debug)]
|
||||
pub struct StreamRef<P, B> {
|
||||
inner: Arc<Mutex<Inner<P, B>>>,
|
||||
key: store::Key,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Chunk<P, B>
|
||||
where P: Peer,
|
||||
B: Buf,
|
||||
{
|
||||
inner: Arc<Mutex<Inner<P, B>>>,
|
||||
recv: recv::Chunk,
|
||||
}
|
||||
|
||||
/// Fields needed to manage state related to managing the set of streams. This
|
||||
/// is mostly split out to make ownership happy.
|
||||
///
|
||||
/// TODO: better name
|
||||
#[derive(Debug)]
|
||||
struct Inner<P, B> {
|
||||
actions: Actions<P, B>,
|
||||
store: Store<B>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Actions<P, B> {
|
||||
/// Manages state transitions initiated by receiving frames
|
||||
recv: Recv<P, B>,
|
||||
|
||||
/// Manages state transitions initiated by sending frames
|
||||
send: Send<P, B>,
|
||||
}
|
||||
|
||||
impl<P, B> Streams<P, B>
|
||||
where P: Peer,
|
||||
B: Buf,
|
||||
{
|
||||
pub fn new(config: Config) -> Self {
|
||||
Streams {
|
||||
inner: Arc::new(Mutex::new(Inner {
|
||||
actions: Actions {
|
||||
recv: Recv::new(&config),
|
||||
send: Send::new(&config),
|
||||
},
|
||||
store: Store::new(),
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
/// Process inbound headers
|
||||
pub fn recv_headers(&mut self, frame: frame::Headers)
|
||||
-> Result<Option<frame::Headers>, ConnectionError>
|
||||
{
|
||||
let id = frame.stream_id();
|
||||
let mut me = self.inner.lock().unwrap();
|
||||
let me = &mut *me;
|
||||
|
||||
let key = match me.store.find_entry(id) {
|
||||
Entry::Occupied(e) => e.key(),
|
||||
Entry::Vacant(e) => {
|
||||
// Trailers cannot open a stream. Trailers are header frames
|
||||
// that do not contain pseudo headers. Requests MUST contain a
|
||||
// method and responses MUST contain a status. If they do not,t
|
||||
// hey are considered to be malformed.
|
||||
if frame.is_trailers() {
|
||||
return Err(ProtocolError.into());
|
||||
}
|
||||
|
||||
match try!(me.actions.recv.open(id)) {
|
||||
Some(stream) => e.insert(stream),
|
||||
None => return Ok(None),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let stream = me.store.resolve(key);
|
||||
|
||||
me.actions.transition(stream, |actions, stream| {
|
||||
if frame.is_trailers() {
|
||||
unimplemented!();
|
||||
/*
|
||||
if !frame.is_end_stream() {
|
||||
// TODO: What error should this return?
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
try!(me.actions.recv.recv_eos(stream));
|
||||
*/
|
||||
} else {
|
||||
actions.recv.recv_headers(frame, stream)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn recv_data(&mut self, frame: frame::Data)
|
||||
-> Result<(), ConnectionError>
|
||||
{
|
||||
let mut me = self.inner.lock().unwrap();
|
||||
let me = &mut *me;
|
||||
|
||||
let id = frame.stream_id();
|
||||
|
||||
let stream = match me.store.find_mut(&id) {
|
||||
Some(stream) => stream,
|
||||
None => return Err(ProtocolError.into()),
|
||||
};
|
||||
|
||||
me.actions.transition(stream, |actions, stream| {
|
||||
actions.recv.recv_data(frame, stream)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn recv_reset(&mut self, frame: frame::Reset)
|
||||
-> Result<(), ConnectionError>
|
||||
{
|
||||
let mut me = self.inner.lock().unwrap();
|
||||
let me = &mut *me;
|
||||
|
||||
let id = frame.stream_id();
|
||||
|
||||
let mut stream = match me.store.find_mut(&id) {
|
||||
Some(stream) => stream,
|
||||
// TODO: should this be an error?
|
||||
None => return Ok(()),
|
||||
};
|
||||
|
||||
me.actions.transition(stream, |actions, stream| {
|
||||
actions.recv.recv_reset(frame, stream)?;
|
||||
assert!(stream.state.is_closed());
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
pub fn recv_err(&mut self, err: &ConnectionError) {
|
||||
let mut me = self.inner.lock().unwrap();
|
||||
let me = &mut *me;
|
||||
|
||||
let actions = &mut me.actions;
|
||||
me.store.for_each(|stream| actions.recv.recv_err(err, stream));
|
||||
}
|
||||
|
||||
pub fn recv_window_update(&mut self, frame: frame::WindowUpdate)
|
||||
-> Result<(), ConnectionError> {
|
||||
let id = frame.stream_id();
|
||||
let mut me = self.inner.lock().unwrap();
|
||||
let me = &mut *me;
|
||||
|
||||
if id.is_zero() {
|
||||
try!(me.actions.send.recv_connection_window_update(frame));
|
||||
} else {
|
||||
// The remote may send window updates for streams that the local now
|
||||
// considers closed. It's ok...
|
||||
if let Some(mut stream) = me.store.find_mut(&id) {
|
||||
try!(me.actions.send.recv_stream_window_update(frame, &mut stream));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn recv_push_promise(&mut self, frame: frame::PushPromise)
|
||||
-> Result<(), ConnectionError>
|
||||
{
|
||||
let mut me = self.inner.lock().unwrap();
|
||||
let me = &mut *me;
|
||||
|
||||
let id = frame.stream_id();
|
||||
|
||||
let mut stream = match me.store.find_mut(&id) {
|
||||
Some(stream) => stream,
|
||||
None => return Err(ProtocolError.into()),
|
||||
};
|
||||
|
||||
me.actions.recv.recv_push_promise(frame, &mut stream)
|
||||
}
|
||||
|
||||
pub fn send_headers(&mut self, headers: frame::Headers)
|
||||
-> Result<(), ConnectionError>
|
||||
{
|
||||
unimplemented!();
|
||||
/*
|
||||
let id = frame.stream_id();
|
||||
let mut me = self.inner.lock().unwrap();
|
||||
let me = &mut *me;
|
||||
|
||||
// let (id, state) = me.actions.send.open());
|
||||
|
||||
|
||||
let state = match me.store.entry(id) {
|
||||
Entry::Occupied(e) => e.into_mut(),
|
||||
Entry::Vacant(e) => {
|
||||
let (id, state) = try!(me.actions.send.open());
|
||||
e.insert(state)
|
||||
}
|
||||
};
|
||||
|
||||
if frame.is_trailers() {
|
||||
try!(me.actions.send.send_eos(state));
|
||||
} else {
|
||||
try!(me.actions.send.send_headers(state, frame.is_end_stream()));
|
||||
}
|
||||
|
||||
if state.is_closed() {
|
||||
me.actions.dec_num_streams(id);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
*/
|
||||
}
|
||||
|
||||
pub fn poll_window_update(&mut self)
|
||||
-> Poll<WindowUpdate, ConnectionError>
|
||||
{
|
||||
let mut me = self.inner.lock().unwrap();
|
||||
let me = &mut *me;
|
||||
me.actions.send.poll_window_update(&mut me.store)
|
||||
}
|
||||
|
||||
pub fn expand_window(&mut self, id: StreamId, sz: WindowSize)
|
||||
-> Result<(), ConnectionError>
|
||||
{
|
||||
let mut me = self.inner.lock().unwrap();
|
||||
let me = &mut *me;
|
||||
|
||||
if id.is_zero() {
|
||||
try!(me.actions.recv.expand_connection_window(sz));
|
||||
} else {
|
||||
if let Some(mut stream) = me.store.find_mut(&id) {
|
||||
try!(me.actions.recv.expand_stream_window(id, sz, &mut stream));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn send_pending_refusal<T>(&mut self, dst: &mut Codec<T, B>)
|
||||
-> Poll<(), ConnectionError>
|
||||
where T: AsyncWrite,
|
||||
{
|
||||
let mut me = self.inner.lock().unwrap();
|
||||
let me = &mut *me;
|
||||
me.actions.recv.send_pending_refusal(dst)
|
||||
}
|
||||
|
||||
pub fn poll_complete<T>(&mut self, dst: &mut Codec<T, B>)
|
||||
-> Poll<(), ConnectionError>
|
||||
where T: AsyncWrite,
|
||||
{
|
||||
let mut me = self.inner.lock().unwrap();
|
||||
let me = &mut *me;
|
||||
|
||||
// TODO: sending window updates should be part of Prioritize
|
||||
/*
|
||||
try_ready!(me.actions.recv.send_connection_window_update(dst));
|
||||
try_ready!(me.actions.recv.send_stream_window_update(&mut me.store, dst));
|
||||
*/
|
||||
|
||||
me.actions.send.poll_complete(&mut me.store, dst)
|
||||
}
|
||||
}
|
||||
|
||||
impl<B> Streams<client::Peer, B>
|
||||
where B: Buf,
|
||||
{
|
||||
pub fn send_request(&mut self, request: Request<()>, end_of_stream: bool)
|
||||
-> Result<StreamRef<client::Peer, B>, ConnectionError>
|
||||
{
|
||||
// TODO: There is a hazard with assigning a stream ID before the
|
||||
// prioritize layer. If prioritization reorders new streams, this
|
||||
// implicitly closes the earlier stream IDs.
|
||||
//
|
||||
// See: carllerche/h2#11
|
||||
let key = {
|
||||
let mut me = self.inner.lock().unwrap();
|
||||
let me = &mut *me;
|
||||
|
||||
// Initialize a new stream. This fails if the connection is at capacity.
|
||||
let mut stream = me.actions.send.open()?;
|
||||
|
||||
// Convert the message
|
||||
let headers = client::Peer::convert_send_message(
|
||||
stream.id, request, end_of_stream);
|
||||
|
||||
let mut stream = me.store.insert(stream.id, stream);
|
||||
|
||||
me.actions.send.send_headers(headers, &mut stream)?;
|
||||
|
||||
// Given that the stream has been initialized, it should not be in the
|
||||
// closed state.
|
||||
debug_assert!(!stream.state.is_closed());
|
||||
|
||||
stream.key()
|
||||
};
|
||||
|
||||
Ok(StreamRef {
|
||||
inner: self.inner.clone(),
|
||||
key: key,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl StreamRef =====
|
||||
|
||||
impl<P, B> StreamRef<P, B>
|
||||
where P: Peer,
|
||||
B: Buf,
|
||||
{
|
||||
pub fn send_data(&mut self, data: B, end_of_stream: bool)
|
||||
-> Result<(), ConnectionError>
|
||||
{
|
||||
let mut me = self.inner.lock().unwrap();
|
||||
let me = &mut *me;
|
||||
|
||||
let stream = me.store.resolve(self.key);
|
||||
|
||||
// Create the data frame
|
||||
let frame = frame::Data::from_buf(stream.id, data, end_of_stream);
|
||||
|
||||
me.actions.transition(stream, |actions, stream| {
|
||||
// Send the data frame
|
||||
actions.send.send_data(frame, stream)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn poll_data(&mut self) -> Poll<Option<Chunk<P, B>>, ConnectionError> {
|
||||
let recv = {
|
||||
let mut me = self.inner.lock().unwrap();
|
||||
let me = &mut *me;
|
||||
|
||||
let mut stream = me.store.resolve(self.key);
|
||||
|
||||
try_ready!(me.actions.recv.poll_chunk(&mut stream))
|
||||
};
|
||||
|
||||
// Convert to a chunk
|
||||
let chunk = recv.map(|recv| {
|
||||
Chunk {
|
||||
inner: self.inner.clone(),
|
||||
recv: recv,
|
||||
}
|
||||
});
|
||||
|
||||
Ok(chunk.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<B> StreamRef<client::Peer, B>
|
||||
where B: Buf,
|
||||
{
|
||||
pub fn poll_response(&mut self) -> Poll<Response<()>, ConnectionError> {
|
||||
let mut me = self.inner.lock().unwrap();
|
||||
let me = &mut *me;
|
||||
|
||||
let mut stream = me.store.resolve(self.key);
|
||||
|
||||
me.actions.recv.poll_response(&mut stream)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
impl<P, B> Clone for StreamRef<P, B> {
|
||||
fn clone(&self) -> Self {
|
||||
StreamRef {
|
||||
inner: self.inner.clone(),
|
||||
key: self.key.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Chunk =====
|
||||
|
||||
impl<P, B> Chunk<P, B>
|
||||
where P: Peer,
|
||||
B: Buf,
|
||||
{
|
||||
// TODO: Come up w/ a better API
|
||||
pub fn pop_bytes(&mut self) -> Option<Bytes> {
|
||||
let mut me = self.inner.lock().unwrap();
|
||||
let me = &mut *me;
|
||||
|
||||
me.actions.recv.pop_bytes(&mut self.recv)
|
||||
}
|
||||
}
|
||||
|
||||
impl<P, B> Drop for Chunk<P, B>
|
||||
where P: Peer,
|
||||
B: Buf,
|
||||
{
|
||||
fn drop(&mut self) {
|
||||
let mut me = self.inner.lock().unwrap();
|
||||
let me = &mut *me;
|
||||
|
||||
while let Some(_) = me.actions.recv.pop_bytes(&mut self.recv) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Actions =====
|
||||
|
||||
impl<P, B> Actions<P, B>
|
||||
where P: Peer,
|
||||
B: Buf,
|
||||
{
|
||||
fn dec_num_streams(&mut self, id: StreamId) {
|
||||
if self.is_local_init(id) {
|
||||
self.send.dec_num_streams();
|
||||
} else {
|
||||
self.recv.dec_num_streams();
|
||||
}
|
||||
}
|
||||
|
||||
fn is_local_init(&self, id: StreamId) -> bool {
|
||||
assert!(!id.is_zero());
|
||||
P::is_server() == id.is_server_initiated()
|
||||
}
|
||||
|
||||
fn transition<F, U>(&mut self, mut stream: store::Ptr<B>, f: F) -> U
|
||||
where F: FnOnce(&mut Self, &mut store::Ptr<B>) -> U,
|
||||
{
|
||||
let is_counted = stream.state.is_counted();
|
||||
|
||||
let ret = f(self, &mut stream);
|
||||
|
||||
if is_counted && stream.state.is_closed() {
|
||||
self.dec_num_streams(stream.id);
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
}
|
||||
@@ -14,78 +14,13 @@ fn handshake() {
|
||||
.write(SETTINGS_ACK)
|
||||
.build();
|
||||
|
||||
let h2 = client::handshake(mock)
|
||||
let h2 = Client::handshake(mock)
|
||||
.wait().unwrap();
|
||||
|
||||
trace!("hands have been shook");
|
||||
|
||||
// At this point, the connection should be closed
|
||||
assert!(Stream::wait(h2).next().is_none());
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn send_request_with_zero_stream_id() {
|
||||
let mock = mock_io::Builder::new()
|
||||
.handshake()
|
||||
.build();
|
||||
|
||||
let h2 = client::handshake(mock)
|
||||
.wait().unwrap();
|
||||
|
||||
// Send the request
|
||||
let mut request = request::Head::default();
|
||||
request.uri = "https://http2.akamai.com/".parse().unwrap();
|
||||
|
||||
let err = h2.send_request(0.into(), request, true).wait().unwrap_err();
|
||||
assert_user_err!(err, InvalidStreamId);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn send_request_with_server_stream_id() {
|
||||
let mock = mock_io::Builder::new()
|
||||
.handshake()
|
||||
.build();
|
||||
|
||||
let h2 = client::handshake(mock)
|
||||
.wait().unwrap();
|
||||
|
||||
// Send the request
|
||||
let mut request = request::Head::default();
|
||||
request.uri = "https://http2.akamai.com/".parse().unwrap();
|
||||
|
||||
let err = h2.send_request(2.into(), request, true).wait().unwrap_err();
|
||||
assert_user_err!(err, InvalidStreamId);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn request_without_scheme() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn request_with_h1_version() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn send_invalid_client_stream_id() {
|
||||
let _ = ::env_logger::init();
|
||||
|
||||
for &id in &[0, 2] {
|
||||
let mock = mock_io::Builder::new()
|
||||
.handshake()
|
||||
.build();
|
||||
|
||||
let h2 = client::handshake(mock)
|
||||
.wait().unwrap();
|
||||
|
||||
// Send the request
|
||||
let mut request = request::Head::default();
|
||||
request.uri = "https://http2.akamai.com/".parse().unwrap();
|
||||
let err = h2.send_request(id.into(), request, true).wait().unwrap_err();
|
||||
|
||||
assert_user_err!(err, InvalidStreamId);
|
||||
}
|
||||
h2.wait().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -104,17 +39,38 @@ fn recv_invalid_server_stream_id() {
|
||||
.read(&[0, 0, 1, 1, 5, 0, 0, 0, 2, 137])
|
||||
.build();
|
||||
|
||||
let h2 = client::handshake(mock)
|
||||
let mut h2 = Client::handshake(mock)
|
||||
.wait().unwrap();
|
||||
|
||||
// Send the request
|
||||
let mut request = request::Head::default();
|
||||
request.uri = "https://http2.akamai.com/".parse().unwrap();
|
||||
let h2 = h2.send_request(1.into(), request, true).wait().unwrap();
|
||||
let request = Request::builder()
|
||||
.uri("https://http2.akamai.com/")
|
||||
.body(()).unwrap();
|
||||
|
||||
// Get the response
|
||||
let (err, _) = h2.into_future().wait().unwrap_err();
|
||||
assert_proto_err!(err, ProtocolError);
|
||||
info!("sending request");
|
||||
let mut stream = h2.request(request, true).unwrap();
|
||||
|
||||
// The connection errors
|
||||
assert_proto_err!(h2.wait().unwrap_err(), ProtocolError);
|
||||
|
||||
// The stream errors
|
||||
assert_proto_err!(stream.wait().unwrap_err(), ProtocolError);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn request_without_scheme() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn request_with_h1_version() {
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn sending_request_on_closed_soket() {
|
||||
}
|
||||
|
||||
const SETTINGS: &'static [u8] = &[0, 0, 0, 4, 0, 0, 0, 0, 0];
|
||||
|
||||
@@ -31,10 +31,10 @@ fn recv_single_ping() {
|
||||
*/
|
||||
.build();
|
||||
|
||||
/*
|
||||
let h2 = client::handshake(mock)
|
||||
.wait().unwrap();
|
||||
|
||||
/*
|
||||
// Send the request
|
||||
let mut request = request::Head::default();
|
||||
request.method = method::POST;
|
||||
|
||||
@@ -29,6 +29,7 @@ fn single_stream_send_large_body() {
|
||||
])
|
||||
.build();
|
||||
|
||||
/*
|
||||
let h2 = client::handshake(mock)
|
||||
.wait().unwrap();
|
||||
|
||||
@@ -65,4 +66,5 @@ fn single_stream_send_large_body() {
|
||||
}
|
||||
|
||||
assert!(Stream::wait(h2).next().is_none());;
|
||||
*/
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ extern crate futures;
|
||||
extern crate mock_io;
|
||||
extern crate env_logger;
|
||||
|
||||
use h2::server;
|
||||
// use h2::server;
|
||||
|
||||
use futures::*;
|
||||
|
||||
@@ -13,6 +13,7 @@ const SETTINGS_ACK: &'static [u8] = &[0, 0, 0, 4, 1, 0, 0, 0, 0];
|
||||
|
||||
#[test]
|
||||
fn read_preface_in_multiple_frames() {
|
||||
/*
|
||||
let _ = ::env_logger::init().unwrap();
|
||||
|
||||
let mock = mock_io::Builder::new()
|
||||
@@ -28,4 +29,5 @@ fn read_preface_in_multiple_frames() {
|
||||
.wait().unwrap();
|
||||
|
||||
assert!(Stream::wait(h2).next().is_none());
|
||||
*/
|
||||
}
|
||||
|
||||
@@ -5,8 +5,6 @@ extern crate log;
|
||||
pub mod support;
|
||||
use support::*;
|
||||
|
||||
use h2::Frame;
|
||||
|
||||
#[test]
|
||||
fn send_recv_headers_only() {
|
||||
let _ = env_logger::init();
|
||||
@@ -23,31 +21,21 @@ fn send_recv_headers_only() {
|
||||
.read(&[0, 0, 1, 1, 5, 0, 0, 0, 1, 0x89])
|
||||
.build();
|
||||
|
||||
let h2 = client::handshake(mock)
|
||||
let mut h2 = Client::handshake(mock)
|
||||
.wait().unwrap();
|
||||
|
||||
// Send the request
|
||||
let mut request = request::Head::default();
|
||||
request.uri = "https://http2.akamai.com/".parse().unwrap();
|
||||
let request = Request::builder()
|
||||
.uri("https://http2.akamai.com/")
|
||||
.body(()).unwrap();
|
||||
|
||||
info!("sending request");
|
||||
let h2 = h2.send_request(1.into(), request, true).wait().unwrap();
|
||||
let mut stream = h2.request(request, true).unwrap();
|
||||
|
||||
// Get the response
|
||||
let resp = h2.run(poll_fn(|| stream.poll_response())).unwrap();
|
||||
assert_eq!(resp.status(), status::NO_CONTENT);
|
||||
|
||||
info!("getting response");
|
||||
let (resp, h2) = h2.into_future().wait().unwrap();
|
||||
|
||||
match resp.unwrap() {
|
||||
Frame::Headers { headers, .. } => {
|
||||
assert_eq!(headers.status, status::NO_CONTENT);
|
||||
}
|
||||
_ => panic!("unexpected frame"),
|
||||
}
|
||||
|
||||
// No more frames
|
||||
info!("ensure no more responses");
|
||||
assert!(Stream::wait(h2).next().is_none());;
|
||||
h2.wait().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -75,46 +63,44 @@ fn send_recv_data() {
|
||||
])
|
||||
.build();
|
||||
|
||||
let h2 = client::handshake(mock).wait().expect("handshake");
|
||||
let mut h2 = Client::handshake2(mock)
|
||||
.wait().unwrap();
|
||||
|
||||
// Send the request
|
||||
let mut request = request::Head::default();
|
||||
request.method = method::POST;
|
||||
request.uri = "https://http2.akamai.com/".parse().unwrap();
|
||||
let h2 = h2.send_request(1.into(), request, false).wait().expect("send request");
|
||||
let request = Request::builder()
|
||||
.method(method::POST)
|
||||
.uri("https://http2.akamai.com/")
|
||||
.body(()).unwrap();
|
||||
|
||||
let b = "hello";
|
||||
info!("sending request");
|
||||
let mut stream = h2.request(request, false).unwrap();
|
||||
|
||||
// Send the data
|
||||
let h2 = h2.send_data(1.into(), b.into(), true).wait().expect("send data");
|
||||
stream.send_data("hello", true).unwrap();
|
||||
|
||||
// Get the response headers
|
||||
let (resp, h2) = h2.into_future().wait().expect("into future");
|
||||
// Get the response
|
||||
let resp = h2.run(poll_fn(|| stream.poll_response())).unwrap();
|
||||
assert_eq!(resp.status(), status::OK);
|
||||
|
||||
match resp.expect("response headers") {
|
||||
Frame::Headers { headers, .. } => {
|
||||
assert_eq!(headers.status, status::OK);
|
||||
}
|
||||
_ => panic!("unexpected frame"),
|
||||
}
|
||||
// Take the body
|
||||
let (_, body) = resp.into_parts();
|
||||
|
||||
// Get the response body
|
||||
let (data, h2) = h2.into_future().wait().expect("into future");
|
||||
// Wait for all the data frames to be received
|
||||
let mut chunks = h2.run(body.collect()).unwrap();
|
||||
|
||||
match data.expect("response data") {
|
||||
Frame::Data { id, data, end_of_stream, .. } => {
|
||||
assert_eq!(id, 1.into());
|
||||
assert_eq!(data, &b"world"[..]);
|
||||
assert!(end_of_stream);
|
||||
}
|
||||
_ => panic!("unexpected frame"),
|
||||
}
|
||||
// Only one chunk since two frames are coalesced.
|
||||
assert_eq!(1, chunks.len());
|
||||
|
||||
assert!(Stream::wait(h2).next().is_none());;
|
||||
let data = chunks[0].pop_bytes().unwrap();
|
||||
assert_eq!(data, &b"world"[..]);
|
||||
|
||||
assert!(chunks[0].pop_bytes().is_none());
|
||||
|
||||
// The H2 connection is closed
|
||||
h2.wait().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn send_headers_recv_data() {
|
||||
fn send_headers_recv_data_single_frame() {
|
||||
let _ = env_logger::init();
|
||||
|
||||
let mock = mock_io::Builder::new()
|
||||
@@ -132,80 +118,42 @@ fn send_headers_recv_data() {
|
||||
])
|
||||
.build();
|
||||
|
||||
let h2 = client::handshake(mock)
|
||||
let mut h2 = Client::handshake(mock)
|
||||
.wait().unwrap();
|
||||
|
||||
// Send the request
|
||||
let mut request = request::Head::default();
|
||||
request.uri = "https://http2.akamai.com/".parse().unwrap();
|
||||
let h2 = h2.send_request(1.into(), request, true).wait().unwrap();
|
||||
let request = Request::builder()
|
||||
.uri("https://http2.akamai.com/")
|
||||
.body(()).unwrap();
|
||||
|
||||
// Get the response headers
|
||||
let (resp, h2) = h2.into_future().wait().unwrap();
|
||||
info!("sending request");
|
||||
let mut stream = h2.request(request, true).unwrap();
|
||||
|
||||
match resp.unwrap() {
|
||||
Frame::Headers { headers, .. } => {
|
||||
assert_eq!(headers.status, status::OK);
|
||||
}
|
||||
_ => panic!("unexpected frame"),
|
||||
}
|
||||
let resp = h2.run(poll_fn(|| stream.poll_response())).unwrap();
|
||||
assert_eq!(resp.status(), status::OK);
|
||||
|
||||
// Get the response body
|
||||
let (data, h2) = h2.into_future().wait().unwrap();
|
||||
// Take the body
|
||||
let (_, body) = resp.into_parts();
|
||||
|
||||
match data.unwrap() {
|
||||
Frame::Data { id, data, end_of_stream, .. } => {
|
||||
assert_eq!(id, 1.into());
|
||||
assert_eq!(data, &b"hello"[..]);
|
||||
assert!(!end_of_stream);
|
||||
}
|
||||
_ => panic!("unexpected frame"),
|
||||
}
|
||||
// Wait for all the data frames to be received
|
||||
let mut chunks = h2.run(body.collect()).unwrap();
|
||||
|
||||
// Get the response body
|
||||
let (data, h2) = h2.into_future().wait().unwrap();
|
||||
// Only one chunk since two frames are coalesced.
|
||||
assert_eq!(1, chunks.len());
|
||||
|
||||
match data.unwrap() {
|
||||
Frame::Data { id, data, end_of_stream, .. } => {
|
||||
assert_eq!(id, 1.into());
|
||||
assert_eq!(data, &b"world"[..]);
|
||||
assert!(end_of_stream);
|
||||
}
|
||||
_ => panic!("unexpected frame"),
|
||||
}
|
||||
let data = chunks[0].pop_bytes().unwrap();
|
||||
assert_eq!(data, &b"hello"[..]);
|
||||
|
||||
assert!(Stream::wait(h2).next().is_none());;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn send_headers_twice_with_same_stream_id() {
|
||||
let _ = env_logger::init();
|
||||
|
||||
let mock = mock_io::Builder::new()
|
||||
.handshake()
|
||||
// Write GET /
|
||||
.write(&[
|
||||
0, 0, 0x10, 1, 5, 0, 0, 0, 1, 0x82, 0x87, 0x41, 0x8B, 0x9D, 0x29,
|
||||
0xAC, 0x4B, 0x8F, 0xA8, 0xE9, 0x19, 0x97, 0x21, 0xE9, 0x84,
|
||||
])
|
||||
.build();
|
||||
|
||||
let h2 = client::handshake(mock)
|
||||
.wait().unwrap();
|
||||
|
||||
// Send the request
|
||||
let mut request = request::Head::default();
|
||||
request.uri = "https://http2.akamai.com/".parse().unwrap();
|
||||
let h2 = h2.send_request(1.into(), request, true).wait().unwrap();
|
||||
|
||||
// Send another request with the same stream ID
|
||||
let mut request = request::Head::default();
|
||||
request.uri = "https://http2.akamai.com/".parse().unwrap();
|
||||
let err = h2.send_request(1.into(), request, true).wait().unwrap_err();
|
||||
|
||||
assert_user_err!(err, UnexpectedFrameType);
|
||||
let data = chunks[0].pop_bytes().unwrap();
|
||||
assert_eq!(data, &b"world"[..]);
|
||||
|
||||
assert!(chunks[0].pop_bytes().is_none());
|
||||
|
||||
// The H2 connection is closed
|
||||
h2.wait().unwrap();
|
||||
}
|
||||
|
||||
/*
|
||||
#[test]
|
||||
fn send_data_after_headers_eos() {
|
||||
let _ = env_logger::init();
|
||||
@@ -238,22 +186,8 @@ fn send_data_after_headers_eos() {
|
||||
assert_user_err!(err, UnexpectedFrameType);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn send_data_without_headers() {
|
||||
let mock = mock_io::Builder::new()
|
||||
.handshake()
|
||||
.build();
|
||||
|
||||
let h2 = client::handshake(mock)
|
||||
.wait().unwrap();
|
||||
|
||||
let b = Bytes::from_static(b"hello world");
|
||||
let err = h2.send_data(1.into(), b, true).wait().unwrap_err();
|
||||
|
||||
assert_user_err!(err, UnexpectedFrameType);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn exceed_max_streams() {
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
pub extern crate bytes;
|
||||
pub extern crate h2;
|
||||
pub extern crate http;
|
||||
pub extern crate tokio_io;
|
||||
pub extern crate futures;
|
||||
pub extern crate mock_io;
|
||||
pub extern crate env_logger;
|
||||
@@ -12,26 +13,30 @@ pub use self::futures::{
|
||||
Sink,
|
||||
Stream,
|
||||
};
|
||||
pub use self::futures::future::poll_fn;
|
||||
|
||||
pub use self::http::{
|
||||
request,
|
||||
response,
|
||||
method,
|
||||
status,
|
||||
Request,
|
||||
Response,
|
||||
};
|
||||
|
||||
pub use self::h2::{
|
||||
client,
|
||||
server,
|
||||
};
|
||||
pub use self::h2::client::{self, Client};
|
||||
// pub use self::h2::server;
|
||||
|
||||
pub use self::bytes::{
|
||||
Buf,
|
||||
BufMut,
|
||||
Bytes,
|
||||
BytesMut,
|
||||
IntoBuf,
|
||||
};
|
||||
|
||||
use tokio_io::{AsyncRead, AsyncWrite};
|
||||
|
||||
pub trait MockH2 {
|
||||
fn handshake(&mut self) -> &mut Self;
|
||||
}
|
||||
@@ -46,6 +51,33 @@ impl MockH2 for mock_io::Builder {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ClientExt {
|
||||
fn run<F: Future>(&mut self, f: F) -> Result<F::Item, F::Error>;
|
||||
}
|
||||
|
||||
impl<T, B> ClientExt for Client<T, B>
|
||||
where T: AsyncRead + AsyncWrite + 'static,
|
||||
B: IntoBuf + 'static,
|
||||
{
|
||||
fn run<F: Future>(&mut self, f: F) -> Result<F::Item, F::Error> {
|
||||
use futures::future::{self, Future};
|
||||
use futures::future::Either::*;
|
||||
|
||||
let res = future::poll_fn(|| self.poll())
|
||||
.select2(f).wait();
|
||||
|
||||
match res {
|
||||
Ok(A((_, b))) => {
|
||||
// Connection is done...
|
||||
b.wait()
|
||||
}
|
||||
Ok(B((v, _))) => return Ok(v),
|
||||
Err(A((e, _))) => panic!("err: {:?}", e),
|
||||
Err(B((e, _))) => return Err(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod frames {
|
||||
//! Some useful frames
|
||||
|
||||
|
||||
Reference in New Issue
Block a user