refactor(lib): convert usage of tokio_core::io to tokio_io

This commit updates to the most recent versions (released today) of the various
Tokio libraries in use. Namely the `tokio_core::io` module has now been
deprecated in favor of an external `tokio-io` crate. This commit pulls in that
crate and uses the `AsyncRead + AsyncWrite` abstraction instead of `Io` from
tokio-core.

BREAKING CHANGE: Any external types that were using that had implemented `Io` will need to 
  implement `AsyncRead + AsyncWrite` from tokio_io.
This commit is contained in:
Alex Crichton
2017-03-17 19:31:44 -05:00
committed by Sean McArthur
parent 34509ef51a
commit 8554904dc9
11 changed files with 118 additions and 96 deletions

View File

@@ -22,17 +22,17 @@ include = [
[dependencies] [dependencies]
base64 = "0.4" base64 = "0.4"
bytes = "0.4" bytes = "0.4"
futures = "0.1.7" futures = "0.1.11"
futures-cpupool = "0.1" futures-cpupool = "0.1"
httparse = "1.0" httparse = "1.0"
language-tags = "0.2" language-tags = "0.2"
log = "0.3" log = "0.3"
mime = "0.2" mime = "0.2"
relay = "0.1"
time = "0.1" time = "0.1"
tokio-core = "0.1" tokio-core = "0.1.6"
tokio-proto = "0.1" tokio-proto = "0.1"
tokio-service = "0.1" tokio-service = "0.1"
tokio-io = "0.1"
unicase = "1.0" unicase = "1.0"
url = "1.0" url = "1.0"

View File

@@ -3,7 +3,7 @@ use std::io;
//use std::net::SocketAddr; //use std::net::SocketAddr;
use futures::{Future, Poll, Async}; use futures::{Future, Poll, Async};
use tokio::io::Io; use tokio_io::{AsyncRead, AsyncWrite};
use tokio::reactor::Handle; use tokio::reactor::Handle;
use tokio::net::{TcpStream, TcpStreamNew}; use tokio::net::{TcpStream, TcpStreamNew};
use tokio_service::Service; use tokio_service::Service;
@@ -18,7 +18,7 @@ use super::dns;
/// `Request=Url` and `Response: Io` instead. /// `Request=Url` and `Response: Io` instead.
pub trait Connect: Service<Request=Url, Error=io::Error> + 'static { pub trait Connect: Service<Request=Url, Error=io::Error> + 'static {
/// The connected Io Stream. /// The connected Io Stream.
type Output: Io + 'static; type Output: AsyncRead + AsyncWrite + 'static;
/// A Future that will resolve to the connected Stream. /// A Future that will resolve to the connected Stream.
type Future: Future<Item=Self::Output, Error=io::Error> + 'static; type Future: Future<Item=Self::Output, Error=io::Error> + 'static;
/// Connect to a remote address. /// Connect to a remote address.
@@ -27,7 +27,7 @@ pub trait Connect: Service<Request=Url, Error=io::Error> + 'static {
impl<T> Connect for T impl<T> Connect for T
where T: Service<Request=Url, Error=io::Error> + 'static, where T: Service<Request=Url, Error=io::Error> + 'static,
T::Response: Io, T::Response: AsyncRead + AsyncWrite,
T::Future: Future<Error=io::Error>, T::Future: Future<Error=io::Error>,
{ {
type Output = T::Response; type Output = T::Response;

View File

@@ -11,8 +11,8 @@ use std::rc::Rc;
use std::time::Duration; use std::time::Duration;
use futures::{Poll, Async, Future, Stream}; use futures::{Poll, Async, Future, Stream};
use relay; use futures::unsync::oneshot;
use tokio::io::Io; use tokio_io::{AsyncRead, AsyncWrite};
use tokio::reactor::Handle; use tokio::reactor::Handle;
use tokio_proto::BindClient; use tokio_proto::BindClient;
use tokio_proto::streaming::Message; use tokio_proto::streaming::Message;
@@ -149,12 +149,12 @@ where C: Connect,
let pool_key = Rc::new(url[..::url::Position::BeforePath].to_owned()); let pool_key = Rc::new(url[..::url::Position::BeforePath].to_owned());
self.connector.connect(url) self.connector.connect(url)
.map(move |io| { .map(move |io| {
let (tx, rx) = relay::channel(); let (tx, rx) = oneshot::channel();
let client = HttpClient { let client = HttpClient {
client_rx: RefCell::new(Some(rx)), client_rx: RefCell::new(Some(rx)),
}.bind_client(&handle, io); }.bind_client(&handle, io);
let pooled = pool.pooled(pool_key, client); let pooled = pool.pooled(pool_key, client);
tx.complete(pooled.clone()); drop(tx.send(pooled.clone()));
pooled pooled
}) })
}; };
@@ -207,11 +207,11 @@ impl<C, B> fmt::Debug for Client<C, B> {
type TokioClient<B> = ClientProxy<Message<http::RequestHead, B>, Message<http::ResponseHead, TokioBody>, ::Error>; type TokioClient<B> = ClientProxy<Message<http::RequestHead, B>, Message<http::ResponseHead, TokioBody>, ::Error>;
struct HttpClient<B> { struct HttpClient<B> {
client_rx: RefCell<Option<relay::Receiver<Pooled<TokioClient<B>>>>>, client_rx: RefCell<Option<oneshot::Receiver<Pooled<TokioClient<B>>>>>,
} }
impl<T, B> ClientProto<T> for HttpClient<B> impl<T, B> ClientProto<T> for HttpClient<B>
where T: Io + 'static, where T: AsyncRead + AsyncWrite + 'static,
B: Stream<Error=::Error> + 'static, B: Stream<Error=::Error> + 'static,
B::Item: AsRef<[u8]>, B::Item: AsRef<[u8]>,
{ {
@@ -232,12 +232,12 @@ where T: Io + 'static,
} }
struct BindingClient<T, B> { struct BindingClient<T, B> {
rx: relay::Receiver<Pooled<TokioClient<B>>>, rx: oneshot::Receiver<Pooled<TokioClient<B>>>,
io: Option<T>, io: Option<T>,
} }
impl<T, B> Future for BindingClient<T, B> impl<T, B> Future for BindingClient<T, B>
where T: Io + 'static, where T: AsyncRead + AsyncWrite + 'static,
B: Stream<Error=::Error>, B: Stream<Error=::Error>,
B::Item: AsRef<[u8]>, B::Item: AsRef<[u8]>,
{ {

View File

@@ -7,7 +7,7 @@ use std::rc::Rc;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use futures::{Future, Async, Poll}; use futures::{Future, Async, Poll};
use relay; use futures::unsync::oneshot;
use http::{KeepAlive, KA}; use http::{KeepAlive, KA};
@@ -18,7 +18,7 @@ pub struct Pool<T> {
struct PoolInner<T> { struct PoolInner<T> {
enabled: bool, enabled: bool,
idle: HashMap<Rc<String>, Vec<Entry<T>>>, idle: HashMap<Rc<String>, Vec<Entry<T>>>,
parked: HashMap<Rc<String>, VecDeque<relay::Sender<Entry<T>>>>, parked: HashMap<Rc<String>, VecDeque<oneshot::Sender<Entry<T>>>>,
timeout: Option<Duration>, timeout: Option<Duration>,
} }
@@ -44,32 +44,34 @@ impl<T: Clone> Pool<T> {
fn put(&mut self, key: Rc<String>, entry: Entry<T>) { fn put(&mut self, key: Rc<String>, entry: Entry<T>) {
trace!("Pool::put {:?}", key); trace!("Pool::put {:?}", key);
let mut inner = self.inner.borrow_mut();
//let inner = &mut *inner;
let mut remove_parked = false; let mut remove_parked = false;
let tx = self.inner.borrow_mut().parked.get_mut(&key).and_then(|parked| { let mut entry = Some(entry);
let mut ret = None; if let Some(parked) = inner.parked.get_mut(&key) {
while let Some(tx) = parked.pop_front() { while let Some(tx) = parked.pop_front() {
if !tx.is_canceled() { match tx.send(entry.take().unwrap()) {
ret = Some(tx); Ok(()) => break,
break; Err(e) => {
}
trace!("Pool::put removing canceled parked {:?}", key); trace!("Pool::put removing canceled parked {:?}", key);
entry = Some(e);
}
}
} }
remove_parked = parked.is_empty(); remove_parked = parked.is_empty();
ret }
});
if remove_parked { if remove_parked {
self.inner.borrow_mut().parked.remove(&key); inner.parked.remove(&key);
} }
if let Some(tx) = tx { match entry {
trace!("Pool::put found parked {:?}", key); Some(entry) => {
tx.complete(entry); inner.idle.entry(key)
} else {
self.inner.borrow_mut()
.idle.entry(key)
.or_insert(Vec::new()) .or_insert(Vec::new())
.push(entry); .push(entry);
} }
None => trace!("Pool::put found parked {:?}", key),
}
} }
pub fn pooled(&self, key: Rc<String>, value: T) -> Pooled<T> { pub fn pooled(&self, key: Rc<String>, value: T) -> Pooled<T> {
@@ -100,7 +102,7 @@ impl<T: Clone> Pool<T> {
} }
} }
fn park(&mut self, key: Rc<String>, tx: relay::Sender<Entry<T>>) { fn park(&mut self, key: Rc<String>, tx: oneshot::Sender<Entry<T>>) {
trace!("Pool::park {:?}", key); trace!("Pool::park {:?}", key);
self.inner.borrow_mut() self.inner.borrow_mut()
.parked.entry(key) .parked.entry(key)
@@ -191,7 +193,7 @@ struct Entry<T> {
pub struct Checkout<T> { pub struct Checkout<T> {
key: Rc<String>, key: Rc<String>,
pool: Pool<T>, pool: Pool<T>,
parked: Option<relay::Receiver<Entry<T>>>, parked: Option<oneshot::Receiver<Entry<T>>>,
} }
impl<T: Clone> Future for Checkout<T> { impl<T: Clone> Future for Checkout<T> {
@@ -247,7 +249,7 @@ impl<T: Clone> Future for Checkout<T> {
Some(entry) => Ok(Async::Ready(self.pool.reuse(self.key.clone(), entry))), Some(entry) => Ok(Async::Ready(self.pool.reuse(self.key.clone(), entry))),
None => { None => {
if self.parked.is_none() { if self.parked.is_none() {
let (tx, mut rx) = relay::channel(); let (tx, mut rx) = oneshot::channel();
let _ = rx.poll(); // park this task let _ = rx.poll(); // park this task
self.pool.park(self.key.clone(), tx); self.pool.park(self.key.clone(), tx);
self.parked = Some(rx); self.parked = Some(rx);
@@ -279,6 +281,7 @@ mod tests {
use std::rc::Rc; use std::rc::Rc;
use std::time::Duration; use std::time::Duration;
use futures::{Async, Future}; use futures::{Async, Future};
use futures::future;
use http::KeepAlive; use http::KeepAlive;
use super::Pool; use super::Pool;
@@ -297,7 +300,7 @@ mod tests {
#[test] #[test]
fn test_pool_checkout_returns_none_if_expired() { fn test_pool_checkout_returns_none_if_expired() {
::futures::lazy(|| { future::lazy(|| {
let pool = Pool::new(true, Some(Duration::from_secs(1))); let pool = Pool::new(true, Some(Duration::from_secs(1)));
let key = Rc::new("foo".to_string()); let key = Rc::new("foo".to_string());
let mut pooled = pool.pooled(key.clone(), 41); let mut pooled = pool.pooled(key.clone(), 41);
@@ -339,7 +342,7 @@ mod tests {
let pooled1 = pool.pooled(key.clone(), 41); let pooled1 = pool.pooled(key.clone(), 41);
let mut pooled = pooled1.clone(); let mut pooled = pooled1.clone();
let checkout = pool.checkout(&key).join(::futures::lazy(move || { let checkout = pool.checkout(&key).join(future::lazy(move || {
// the checkout future will park first, // the checkout future will park first,
// and then this lazy future will be polled, which will insert // and then this lazy future will be polled, which will insert
// the pooled back into the pool // the pooled back into the pool

View File

@@ -5,7 +5,7 @@ use std::time::Instant;
use futures::{Poll, Async, AsyncSink, Stream, Sink, StartSend}; use futures::{Poll, Async, AsyncSink, Stream, Sink, StartSend};
use futures::task::Task; use futures::task::Task;
use tokio::io::Io; use tokio_io::{AsyncRead, AsyncWrite};
use tokio_proto::streaming::pipeline::{Frame, Transport}; use tokio_proto::streaming::pipeline::{Frame, Transport};
use header::{ContentLength, TransferEncoding}; use header::{ContentLength, TransferEncoding};
@@ -16,7 +16,7 @@ use version::HttpVersion;
/// This handles a connection, which will have been established over an /// This handles a connection, which will have been established over an
/// `Io` (like a socket), and will likely include multiple /// `AsyncRead + AsyncWrite` (like a socket), and will likely include multiple
/// `Transaction`s over HTTP. /// `Transaction`s over HTTP.
/// ///
/// The connection will determine when a message begins and ends as well as /// The connection will determine when a message begins and ends as well as
@@ -29,7 +29,7 @@ pub struct Conn<I, B, T, K = KA> {
} }
impl<I, B, T, K> Conn<I, B, T, K> impl<I, B, T, K> Conn<I, B, T, K>
where I: Io, where I: AsyncRead + AsyncWrite,
B: AsRef<[u8]>, B: AsRef<[u8]>,
T: Http1Transaction, T: Http1Transaction,
K: KeepAlive K: KeepAlive
@@ -155,7 +155,7 @@ where I: Io,
} }
fn maybe_park_read(&mut self) { fn maybe_park_read(&mut self) {
if self.io.poll_read().is_ready() { if !self.io.is_read_blocked() {
// the Io object is ready to read, which means it will never alert // the Io object is ready to read, which means it will never alert
// us that it is ready until we drain it. However, we're currently // us that it is ready until we drain it. However, we're currently
// finished reading, so we need to park the task to be able to // finished reading, so we need to park the task to be able to
@@ -350,7 +350,7 @@ where I: Io,
} }
impl<I, B, T, K> Stream for Conn<I, B, T, K> impl<I, B, T, K> Stream for Conn<I, B, T, K>
where I: Io, where I: AsyncRead + AsyncWrite,
B: AsRef<[u8]>, B: AsRef<[u8]>,
T: Http1Transaction, T: Http1Transaction,
K: KeepAlive, K: KeepAlive,
@@ -385,7 +385,7 @@ where I: Io,
} }
impl<I, B, T, K> Sink for Conn<I, B, T, K> impl<I, B, T, K> Sink for Conn<I, B, T, K>
where I: Io, where I: AsyncRead + AsyncWrite,
B: AsRef<[u8]>, B: AsRef<[u8]>,
T: Http1Transaction, T: Http1Transaction,
K: KeepAlive, K: KeepAlive,
@@ -450,10 +450,15 @@ where I: Io,
trace!("Conn::flush = {:?}", ret); trace!("Conn::flush = {:?}", ret);
ret ret
} }
fn close(&mut self) -> Poll<(), Self::SinkError> {
try_ready!(self.poll_complete());
self.io.io_mut().shutdown()
}
} }
impl<I, B, T, K> Transport for Conn<I, B, T, K> impl<I, B, T, K> Transport for Conn<I, B, T, K>
where I: Io + 'static, where I: AsyncRead + AsyncWrite + 'static,
B: AsRef<[u8]> + 'static, B: AsRef<[u8]> + 'static,
T: Http1Transaction + 'static, T: Http1Transaction + 'static,
K: KeepAlive + 'static, K: KeepAlive + 'static,
@@ -665,6 +670,7 @@ impl<'a, T: fmt::Debug + 'a, B: AsRef<[u8]> + 'a> fmt::Debug for DebugFrame<'a,
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use futures::{Async, Future, Stream, Sink}; use futures::{Async, Future, Stream, Sink};
use futures::future;
use tokio_proto::streaming::pipeline::Frame; use tokio_proto::streaming::pipeline::Frame;
use http::{self, MessageHead, ServerTransaction}; use http::{self, MessageHead, ServerTransaction};
@@ -705,7 +711,7 @@ mod tests {
#[test] #[test]
fn test_conn_parse_partial() { fn test_conn_parse_partial() {
let _: Result<(), ()> = ::futures::lazy(|| { let _: Result<(), ()> = future::lazy(|| {
let good_message = b"GET / HTTP/1.1\r\nHost: foo.bar\r\n\r\n".to_vec(); let good_message = b"GET / HTTP/1.1\r\nHost: foo.bar\r\n\r\n".to_vec();
let io = AsyncIo::new_buf(good_message, 10); let io = AsyncIo::new_buf(good_message, 10);
let mut conn = Conn::<_, http::Chunk, ServerTransaction>::new(io, Default::default()); let mut conn = Conn::<_, http::Chunk, ServerTransaction>::new(io, Default::default());
@@ -772,7 +778,7 @@ mod tests {
#[test] #[test]
fn test_conn_body_write_length() { fn test_conn_body_write_length() {
let _: Result<(), ()> = ::futures::lazy(|| { let _: Result<(), ()> = future::lazy(|| {
let io = AsyncIo::new_buf(vec![], 0); let io = AsyncIo::new_buf(vec![], 0);
let mut conn = Conn::<_, http::Chunk, ServerTransaction>::new(io, Default::default()); let mut conn = Conn::<_, http::Chunk, ServerTransaction>::new(io, Default::default());
let max = ::http::io::MAX_BUFFER_SIZE + 4096; let max = ::http::io::MAX_BUFFER_SIZE + 4096;
@@ -800,7 +806,7 @@ mod tests {
#[test] #[test]
fn test_conn_body_write_chunked() { fn test_conn_body_write_chunked() {
let _: Result<(), ()> = ::futures::lazy(|| { let _: Result<(), ()> = future::lazy(|| {
let io = AsyncIo::new_buf(vec![], 4096); let io = AsyncIo::new_buf(vec![], 4096);
let mut conn = Conn::<_, http::Chunk, ServerTransaction>::new(io, Default::default()); let mut conn = Conn::<_, http::Chunk, ServerTransaction>::new(io, Default::default());
conn.state.writing = Writing::Body(Encoder::chunked(), None); conn.state.writing = Writing::Body(Encoder::chunked(), None);
@@ -813,7 +819,7 @@ mod tests {
#[test] #[test]
fn test_conn_body_flush() { fn test_conn_body_flush() {
let _: Result<(), ()> = ::futures::lazy(|| { let _: Result<(), ()> = future::lazy(|| {
let io = AsyncIo::new_buf(vec![], 1024 * 1024 * 5); let io = AsyncIo::new_buf(vec![], 1024 * 1024 * 5);
let mut conn = Conn::<_, http::Chunk, ServerTransaction>::new(io, Default::default()); let mut conn = Conn::<_, http::Chunk, ServerTransaction>::new(io, Default::default());
conn.state.writing = Writing::Body(Encoder::length(1024 * 1024), None); conn.state.writing = Writing::Body(Encoder::length(1024 * 1024), None);
@@ -829,7 +835,7 @@ mod tests {
#[test] #[test]
fn test_conn_parking() { fn test_conn_parking() {
use std::sync::Arc; use std::sync::Arc;
use futures::task::Unpark; use futures::executor::Unpark;
struct Car { struct Car {
permit: bool, permit: bool,
@@ -847,7 +853,7 @@ mod tests {
} }
// test that once writing is done, unparks // test that once writing is done, unparks
let f = ::futures::lazy(|| { let f = future::lazy(|| {
let io = AsyncIo::new_buf(vec![], 4096); let io = AsyncIo::new_buf(vec![], 4096);
let mut conn = Conn::<_, http::Chunk, ServerTransaction>::new(io, Default::default()); let mut conn = Conn::<_, http::Chunk, ServerTransaction>::new(io, Default::default());
conn.state.reading = Reading::KeepAlive; conn.state.reading = Reading::KeepAlive;
@@ -861,7 +867,7 @@ mod tests {
// test that flushing when not waiting on read doesn't unpark // test that flushing when not waiting on read doesn't unpark
let f = ::futures::lazy(|| { let f = future::lazy(|| {
let io = AsyncIo::new_buf(vec![], 4096); let io = AsyncIo::new_buf(vec![], 4096);
let mut conn = Conn::<_, http::Chunk, ServerTransaction>::new(io, Default::default()); let mut conn = Conn::<_, http::Chunk, ServerTransaction>::new(io, Default::default());
conn.state.writing = Writing::KeepAlive; conn.state.writing = Writing::KeepAlive;
@@ -872,7 +878,7 @@ mod tests {
// test that flushing and writing isn't done doesn't unpark // test that flushing and writing isn't done doesn't unpark
let f = ::futures::lazy(|| { let f = future::lazy(|| {
let io = AsyncIo::new_buf(vec![], 4096); let io = AsyncIo::new_buf(vec![], 4096);
let mut conn = Conn::<_, http::Chunk, ServerTransaction>::new(io, Default::default()); let mut conn = Conn::<_, http::Chunk, ServerTransaction>::new(io, Default::default());
conn.state.reading = Reading::KeepAlive; conn.state.reading = Reading::KeepAlive;

View File

@@ -295,7 +295,7 @@ mod tests {
let (a, b) = self.split_at(n); let (a, b) = self.split_at(n);
let mut buf = BytesMut::from(a); let mut buf = BytesMut::from(a);
*self = b; *self = b;
Ok(buf.drain_to(n).freeze()) Ok(buf.split_to(n).freeze())
} else { } else {
Ok(Bytes::new()) Ok(Bytes::new())
} }

View File

@@ -55,7 +55,7 @@ impl Http1Transaction for ServerTransaction {
}; };
let mut headers = Headers::with_capacity(headers_len); let mut headers = Headers::with_capacity(headers_len);
let slice = buf.drain_to(len).freeze(); let slice = buf.split_to(len).freeze();
let path = slice.slice(path.0, path.1); let path = slice.slice(path.0, path.1);
// path was found to be utf8 by httparse // path was found to be utf8 by httparse
let path = unsafe { ByteStr::from_utf8_unchecked(path) }; let path = unsafe { ByteStr::from_utf8_unchecked(path) };
@@ -171,7 +171,7 @@ impl Http1Transaction for ClientTransaction {
}; };
let mut headers = Headers::with_capacity(headers_len); let mut headers = Headers::with_capacity(headers_len);
let slice = buf.drain_to(len).freeze(); let slice = buf.split_to(len).freeze();
headers.extend(HeadersAsBytesIter { headers.extend(HeadersAsBytesIter {
headers: headers_indices[..headers_len].iter(), headers: headers_indices[..headers_len].iter(),
slice: slice, slice: slice,

View File

@@ -3,8 +3,7 @@ use std::fmt;
use std::io::{self, Write}; use std::io::{self, Write};
use std::ptr; use std::ptr;
use futures::Async; use tokio_io::{AsyncRead, AsyncWrite};
use tokio::io::Io;
use http::{Http1Transaction, h1, MessageHead, ParseResult, DebugTruncate}; use http::{Http1Transaction, h1, MessageHead, ParseResult, DebugTruncate};
use bytes::{BytesMut, Bytes}; use bytes::{BytesMut, Bytes};
@@ -14,6 +13,7 @@ pub const MAX_BUFFER_SIZE: usize = 8192 + 4096 * 100;
pub struct Buffered<T> { pub struct Buffered<T> {
io: T, io: T,
read_blocked: bool,
read_buf: BytesMut, read_buf: BytesMut,
write_buf: WriteBuf, write_buf: WriteBuf,
} }
@@ -27,12 +27,13 @@ impl<T> fmt::Debug for Buffered<T> {
} }
} }
impl<T: Io> Buffered<T> { impl<T: AsyncRead + AsyncWrite> Buffered<T> {
pub fn new(io: T) -> Buffered<T> { pub fn new(io: T) -> Buffered<T> {
Buffered { Buffered {
io: io, io: io,
read_buf: BytesMut::with_capacity(0), read_buf: BytesMut::with_capacity(0),
write_buf: WriteBuf::new(), write_buf: WriteBuf::new(),
read_blocked: false,
} }
} }
@@ -49,14 +50,10 @@ impl<T: Io> Buffered<T> {
_ => break, _ => break,
} }
} }
self.read_buf.drain_to(i); self.read_buf.split_to(i);
} }
} }
pub fn poll_read(&mut self) -> Async<()> {
self.io.poll_read()
}
pub fn parse<S: Http1Transaction>(&mut self) -> ::Result<Option<MessageHead<S::Incoming>>> { pub fn parse<S: Http1Transaction>(&mut self) -> ::Result<Option<MessageHead<S::Incoming>>> {
self.reserve_read_buf(); self.reserve_read_buf();
match self.read_from_io() { match self.read_from_io() {
@@ -88,8 +85,17 @@ impl<T: Io> Buffered<T> {
fn read_from_io(&mut self) -> io::Result<usize> { fn read_from_io(&mut self) -> io::Result<usize> {
use bytes::BufMut; use bytes::BufMut;
self.read_blocked = false;
unsafe { unsafe {
let n = try!(self.io.read(self.read_buf.bytes_mut())); let n = match self.io.read(self.read_buf.bytes_mut()) {
Ok(n) => n,
Err(e) => {
if e.kind() == io::ErrorKind::WouldBlock {
self.read_blocked = true;
}
return Err(e)
}
};
self.read_buf.advance_mut(n); self.read_buf.advance_mut(n);
Ok(n) Ok(n)
} }
@@ -112,10 +118,13 @@ impl<T: Io> Buffered<T> {
self.write_buf.buffer(buf.as_ref()) self.write_buf.buffer(buf.as_ref())
} }
#[cfg(test)]
pub fn io_mut(&mut self) -> &mut T { pub fn io_mut(&mut self) -> &mut T {
&mut self.io &mut self.io
} }
pub fn is_read_blocked(&self) -> bool {
self.read_blocked
}
} }
impl<T: Write> Write for Buffered<T> { impl<T: Write> Write for Buffered<T> {
@@ -146,17 +155,17 @@ pub trait MemRead {
fn read_mem(&mut self, len: usize) -> io::Result<Bytes>; fn read_mem(&mut self, len: usize) -> io::Result<Bytes>;
} }
impl<T: Io> MemRead for Buffered<T> { impl<T: AsyncRead + AsyncWrite> MemRead for Buffered<T> {
fn read_mem(&mut self, len: usize) -> io::Result<Bytes> { fn read_mem(&mut self, len: usize) -> io::Result<Bytes> {
trace!("Buffered.read_mem read_buf={}, wanted={}", self.read_buf.len(), len); trace!("Buffered.read_mem read_buf={}, wanted={}", self.read_buf.len(), len);
if !self.read_buf.is_empty() { if !self.read_buf.is_empty() {
let n = ::std::cmp::min(len, self.read_buf.len()); let n = ::std::cmp::min(len, self.read_buf.len());
trace!("Buffered.read_mem read_buf is not empty, slicing {}", n); trace!("Buffered.read_mem read_buf is not empty, slicing {}", n);
Ok(self.read_buf.drain_to(n).freeze()) Ok(self.read_buf.split_to(n).freeze())
} else { } else {
self.reserve_read_buf(); self.reserve_read_buf();
let n = try!(self.read_from_io()); let n = try!(self.read_from_io());
Ok(self.read_buf.drain_to(::std::cmp::min(len, n)).freeze()) Ok(self.read_buf.split_to(::std::cmp::min(len, n)).freeze())
} }
} }
} }

View File

@@ -21,10 +21,10 @@ extern crate httparse;
#[cfg_attr(test, macro_use)] extern crate language_tags; #[cfg_attr(test, macro_use)] extern crate language_tags;
#[macro_use] extern crate log; #[macro_use] extern crate log;
#[macro_use] pub extern crate mime; #[macro_use] pub extern crate mime;
extern crate relay;
extern crate base64; extern crate base64;
extern crate time; extern crate time;
#[macro_use] extern crate tokio_core as tokio; #[macro_use] extern crate tokio_core as tokio;
extern crate tokio_io;
extern crate tokio_proto; extern crate tokio_proto;
extern crate tokio_service; extern crate tokio_service;
extern crate unicase; extern crate unicase;

View File

@@ -1,8 +1,8 @@
use std::cmp; use std::cmp;
use std::io::{self, Read, Write}; use std::io::{self, Read, Write};
use futures::Async; use futures::Poll;
use tokio::io::Io; use tokio_io::{AsyncRead, AsyncWrite};
#[derive(Debug)] #[derive(Debug)]
pub struct Buf { pub struct Buf {
@@ -123,21 +123,12 @@ impl<T: Write> Write for AsyncIo<T> {
} }
} }
impl<T: Read + Write> Io for AsyncIo<T> { impl<T: Read + Write> AsyncRead for AsyncIo<T> {
fn poll_read(&mut self) -> Async<()> { }
if self.bytes_until_block == 0 {
Async::NotReady
} else {
Async::Ready(())
}
}
fn poll_write(&mut self) -> Async<()> { impl<T: Read + Write> AsyncWrite for AsyncIo<T> {
if self.bytes_until_block == 0 { fn shutdown(&mut self) -> Poll<(), io::Error> {
Async::NotReady Ok(().into())
} else {
Async::Ready(())
}
} }
} }

View File

@@ -13,9 +13,10 @@ use std::time::Duration;
use futures::future; use futures::future;
use futures::task::{self, Task}; use futures::task::{self, Task};
use futures::{Future, Map, Stream, Poll, Async, Sink, StartSend, AsyncSink}; use futures::{Future, Stream, Poll, Async, Sink, StartSend, AsyncSink};
use futures::future::Map;
use tokio::io::Io; use tokio_io::{AsyncRead, AsyncWrite};
use tokio::reactor::{Core, Handle, Timeout}; use tokio::reactor::{Core, Handle, Timeout};
use tokio::net::TcpListener; use tokio::net::TcpListener;
use tokio_proto::BindServer; use tokio_proto::BindServer;
@@ -124,7 +125,7 @@ impl<B: AsRef<[u8]> + 'static> Http<B> {
service: S) service: S)
where S: Service<Request = Request, Response = Response<Bd>, Error = ::Error> + 'static, where S: Service<Request = Request, Response = Response<Bd>, Error = ::Error> + 'static,
Bd: Stream<Item=B, Error=::Error> + 'static, Bd: Stream<Item=B, Error=::Error> + 'static,
I: Io + 'static, I: AsyncRead + AsyncWrite + 'static,
{ {
self.bind_server(handle, io, HttpService { self.bind_server(handle, io, HttpService {
inner: service, inner: service,
@@ -165,7 +166,7 @@ pub struct __ProtoBindTransport<T, B> {
} }
impl<T, B> ServerProto<T> for Http<B> impl<T, B> ServerProto<T> for Http<B>
where T: Io + 'static, where T: AsyncRead + AsyncWrite + 'static,
B: AsRef<[u8]> + 'static, B: AsRef<[u8]> + 'static,
{ {
type Request = __ProtoRequest; type Request = __ProtoRequest;
@@ -189,8 +190,8 @@ where T: Io + 'static,
} }
impl<T, B> Sink for __ProtoTransport<T, B> impl<T, B> Sink for __ProtoTransport<T, B>
where T: Io + 'static, where T: AsyncRead + AsyncWrite + 'static,
B: AsRef<[u8]>, B: AsRef<[u8]> + 'static,
{ {
type SinkItem = Frame<__ProtoResponse, B, ::Error>; type SinkItem = Frame<__ProtoResponse, B, ::Error>;
type SinkError = io::Error; type SinkError = io::Error;
@@ -224,9 +225,16 @@ where T: Io + 'static,
fn poll_complete(&mut self) -> Poll<(), io::Error> { fn poll_complete(&mut self) -> Poll<(), io::Error> {
self.0.poll_complete() self.0.poll_complete()
} }
fn close(&mut self) -> Poll<(), io::Error> {
self.0.close()
}
} }
impl<T: Io + 'static, B: AsRef<[u8]>> Stream for __ProtoTransport<T, B> { impl<T, B> Stream for __ProtoTransport<T, B>
where T: AsyncRead + AsyncWrite + 'static,
B: AsRef<[u8]> + 'static,
{
type Item = Frame<__ProtoRequest, http::Chunk, ::Error>; type Item = Frame<__ProtoRequest, http::Chunk, ::Error>;
type Error = io::Error; type Error = io::Error;
@@ -246,7 +254,10 @@ impl<T: Io + 'static, B: AsRef<[u8]>> Stream for __ProtoTransport<T, B> {
} }
} }
impl<T: Io + 'static, B: AsRef<[u8]> + 'static> Transport for __ProtoTransport<T, B> { impl<T, B> Transport for __ProtoTransport<T, B>
where T: AsyncRead + AsyncWrite + 'static,
B: AsRef<[u8]> + 'static,
{
fn tick(&mut self) { fn tick(&mut self) {
self.0.tick() self.0.tick()
} }
@@ -256,7 +267,9 @@ impl<T: Io + 'static, B: AsRef<[u8]> + 'static> Transport for __ProtoTransport<T
} }
} }
impl<T: Io + 'static, B> Future for __ProtoBindTransport<T, B> { impl<T, B> Future for __ProtoBindTransport<T, B>
where T: AsyncRead + AsyncWrite + 'static,
{
type Item = __ProtoTransport<T, B>; type Item = __ProtoTransport<T, B>;
type Error = io::Error; type Error = io::Error;