Refactor framing to use Streams and Sinks

- Gets rid of `easy` module, instead providing framing support directly
  in the `io` module.

- In particular, adds a framing adapter directly to the `Io` trait,
  which gives you a Stream + Sink object. That object can then be
  `split` into separate `Stream` and `Sink` objects if needed.

- Deprecates the `FramedIo` trait; that's now just Stream + Sink.

- Updates the line framing test to use the stream/sink combinators.
This commit is contained in:
Aaron Turon
2016-11-08 15:47:13 -08:00
parent 3eac142e5f
commit 36e3dbf418
4 changed files with 223 additions and 210 deletions
+13 -68
View File
@@ -5,85 +5,30 @@ extern crate futures;
use std::io;
use std::net::Shutdown;
use futures::{Future, Async, Poll};
use futures::stream::Stream;
use tokio_core::io::{FramedIo, write_all, read};
use tokio_core::easy::{Encode, Decode, EasyFramed, EasyBuf};
use futures::{Future, Stream, Sink};
use tokio_core::io::{write_all, read, Encode, Decode, EasyBuf, Io};
use tokio_core::net::{TcpListener, TcpStream};
use tokio_core::reactor::Core;
pub struct LineDecoder;
pub struct LineEncoder;
pub struct Line(EasyBuf);
impl Decode for LineDecoder {
type Out = EasyBuf;
fn decode(&mut self, buf: &mut EasyBuf) -> Result<Option<EasyBuf>, io::Error> {
impl Decode for Line {
fn decode(buf: &mut EasyBuf) -> Result<Option<Line>, io::Error> {
match buf.as_slice().iter().position(|&b| b == b'\n') {
Some(i) => Ok(Some(buf.drain_to(i + 1).into())),
Some(i) => Ok(Some(Line(buf.drain_to(i + 1).into()))),
None => Ok(None),
}
}
fn done(&mut self, buf: &mut EasyBuf) -> io::Result<EasyBuf> {
fn done(buf: &mut EasyBuf) -> io::Result<Line> {
let amt = buf.len();
Ok(buf.drain_to(amt))
Ok(Line(buf.drain_to(amt)))
}
}
impl Encode for LineEncoder {
type In = EasyBuf;
fn encode(&mut self, msg: EasyBuf, into: &mut Vec<u8>) {
into.extend_from_slice(msg.as_slice());
}
}
pub struct EchoFramed<T> {
inner: T,
eof: bool,
}
impl<T, U> Future for EchoFramed<T>
where T: FramedIo<In = U, Out = Option<U>>,
{
type Item = ();
type Error = ();
fn poll(&mut self) -> Poll<(), ()> {
// Try to write out any buffered messages if we have them.
if self.inner.flush().expect("flush error").is_not_ready() {
return Ok(Async::NotReady)
}
// Wait until we can simultaneously read and write a message
while !self.eof &&
self.inner.poll_read().is_ready() &&
self.inner.poll_write().is_ready() {
let frame = match self.inner.read() {
Ok(Async::Ready(Some(frame))) => frame,
Ok(Async::Ready(None)) => {
self.eof = true;
break
}
Ok(Async::NotReady) => break,
Err(e) => panic!("error in read: {}", e),
};
match self.inner.write(frame) {
Ok(Async::Ready(())) => {}
Ok(Async::NotReady) => break,
Err(e) => panic!("error in write: {}", e),
}
}
// If we wrote some frames try to flush again. Ignore whether this is
// ready to finish or not as we're going to continue to return NotReady
if self.inner.flush().expect("flush error").is_ready() && self.eof {
Ok(().into())
} else {
Ok(Async::NotReady)
}
impl Encode for Line {
fn encode(self, into: &mut Vec<u8>) {
into.extend_from_slice(self.0.as_slice());
}
}
@@ -97,8 +42,8 @@ fn echo() {
let listener = TcpListener::bind(&"127.0.0.1:0".parse().unwrap(), &handle).unwrap();
let addr = listener.local_addr().unwrap();
let srv = listener.incoming().for_each(move |(socket, _)| {
let framed = EasyFramed::new(socket, LineDecoder, LineEncoder);
handle.spawn(EchoFramed { inner: framed, eof: false });
let (stream, sink) = socket.framed::<Line, Line>().split();
handle.spawn(sink.send_all(stream).map(|_| ()).map_err(|_| ()));
Ok(())
});