feat(http): add Decoder.try_read and Encoder.try_write

This commit is contained in:
Sean McArthur
2016-07-14 10:01:57 -07:00
parent 40745c5671
commit 85894bc123
3 changed files with 78 additions and 45 deletions

View File

@@ -3,11 +3,8 @@ extern crate hyper;
extern crate env_logger; extern crate env_logger;
extern crate num_cpus; extern crate num_cpus;
use std::io::Write; use hyper::{Decoder, Encoder, Next, HttpStream};
use hyper::server::{Server, Handler, Request, Response, HttpListener};
use hyper::{Decoder, Encoder, Next};
use hyper::net::{HttpStream, HttpListener};
use hyper::server::{Server, Handler, Request, Response};
static PHRASE: &'static [u8] = b"Hello World!"; static PHRASE: &'static [u8] = b"Hello World!";

View File

@@ -4,11 +4,8 @@ extern crate env_logger;
#[macro_use] #[macro_use]
extern crate log; extern crate log;
use std::io::{self, Read, Write}; use hyper::{Get, Post, StatusCode, RequestUri, Decoder, Encoder, HttpStream, Next};
use hyper::{Get, Post, StatusCode, RequestUri, Decoder, Encoder, Next};
use hyper::header::ContentLength; use hyper::header::ContentLength;
use hyper::net::HttpStream;
use hyper::server::{Server, Handler, Request, Response}; use hyper::server::{Server, Handler, Request, Response};
struct Echo { struct Echo {
@@ -78,13 +75,13 @@ impl Handler<HttpStream> for Echo {
match self.route { match self.route {
Route::Echo(ref body) => { Route::Echo(ref body) => {
if self.read_pos < self.buf.len() { if self.read_pos < self.buf.len() {
match transport.read(&mut self.buf[self.read_pos..]) { match transport.try_read(&mut self.buf[self.read_pos..]) {
Ok(0) => { Ok(Some(0)) => {
debug!("Read 0, eof"); debug!("Read 0, eof");
self.eof = true; self.eof = true;
Next::write() Next::write()
}, },
Ok(n) => { Ok(Some(n)) => {
self.read_pos += n; self.read_pos += n;
match *body { match *body {
Body::Len(max) if max <= self.read_pos as u64 => { Body::Len(max) if max <= self.read_pos as u64 => {
@@ -94,12 +91,10 @@ impl Handler<HttpStream> for Echo {
_ => Next::read_and_write() _ => Next::read_and_write()
} }
} }
Err(e) => match e.kind() { Ok(None) => Next::read_and_write(),
io::ErrorKind::WouldBlock => Next::read_and_write(), Err(e) => {
_ => { println!("read error {:?}", e);
println!("read error {:?}", e); Next::end()
Next::end()
}
} }
} }
} else { } else {
@@ -137,18 +132,16 @@ impl Handler<HttpStream> for Echo {
} }
Route::Echo(..) => { Route::Echo(..) => {
if self.write_pos < self.read_pos { if self.write_pos < self.read_pos {
match transport.write(&self.buf[self.write_pos..self.read_pos]) { match transport.try_write(&self.buf[self.write_pos..self.read_pos]) {
Ok(0) => panic!("write ZERO"), Ok(Some(0)) => panic!("write ZERO"),
Ok(n) => { Ok(Some(n)) => {
self.write_pos += n; self.write_pos += n;
Next::write() Next::write()
} }
Err(e) => match e.kind() { Ok(None) => Next::write(),
io::ErrorKind::WouldBlock => Next::write(), Err(e) => {
_ => { println!("write error {:?}", e);
println!("write error {:?}", e); Next::end()
Next::end()
}
} }
} }
} else if !self.eof { } else if !self.eof {

View File

@@ -72,6 +72,31 @@ impl<'a, T: Read> Decoder<'a, T> {
Decoder(DecoderImpl::H1(decoder, transport)) Decoder(DecoderImpl::H1(decoder, transport))
} }
/// Read from the `Transport`.
#[inline]
pub fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
match self.0 {
DecoderImpl::H1(ref mut decoder, ref mut transport) => {
decoder.decode(transport, buf)
}
}
}
/// Try to read from the `Transport`.
///
/// This method looks for the `WouldBlock` error. If the read did not block,
/// a return value would be `Ok(Some(x))`. If the read would block,
/// this method would return `Ok(None)`.
#[inline]
pub fn try_read(&mut self, buf: &mut [u8]) -> io::Result<Option<usize>> {
match self.read(buf) {
Ok(n) => Ok(Some(n)),
Err(e) => match e.kind() {
io::ErrorKind::WouldBlock => Ok(None),
_ => Err(e)
}
}
}
/// Get a reference to the transport. /// Get a reference to the transport.
pub fn get_ref(&self) -> &T { pub fn get_ref(&self) -> &T {
@@ -86,9 +111,42 @@ impl<'a, T: Transport> Encoder<'a, T> {
Encoder(EncoderImpl::H1(encoder, transport)) Encoder(EncoderImpl::H1(encoder, transport))
} }
/// Write to the `Transport`.
#[inline]
pub fn write(&mut self, data: &[u8]) -> io::Result<usize> {
if data.is_empty() {
return Ok(0);
}
match self.0 {
EncoderImpl::H1(ref mut encoder, ref mut transport) => {
if encoder.is_closed() {
Ok(0)
} else {
encoder.encode(*transport, data)
}
}
}
}
/// Try to write to the `Transport`.
///
/// This method looks for the `WouldBlock` error. If the write did not block,
/// a return value would be `Ok(Some(x))`. If the write would block,
/// this method would return `Ok(None)`.
#[inline]
pub fn try_write(&mut self, data: &[u8]) -> io::Result<Option<usize>> {
match self.write(data) {
Ok(n) => Ok(Some(n)),
Err(e) => match e.kind() {
io::ErrorKind::WouldBlock => Ok(None),
_ => Err(e)
}
}
}
/// Closes an encoder, signaling that no more writing will occur. /// Closes an encoder, signaling that no more writing will occur.
/// ///
/// This is needed for encodings that don't know length of the content /// This is needed for encodings that don't know the length of the content
/// beforehand. Most common instance would be usage of /// beforehand. Most common instance would be usage of
/// `Transfer-Enciding: chunked`. You would call `close()` to signal /// `Transfer-Enciding: chunked`. You would call `close()` to signal
/// the `Encoder` should write the end chunk, or `0\r\n\r\n`. /// the `Encoder` should write the end chunk, or `0\r\n\r\n`.
@@ -109,29 +167,14 @@ impl<'a, T: Transport> Encoder<'a, T> {
impl<'a, T: Read> Read for Decoder<'a, T> { impl<'a, T: Read> Read for Decoder<'a, T> {
#[inline] #[inline]
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
match self.0 { self.read(buf)
DecoderImpl::H1(ref mut decoder, ref mut transport) => {
decoder.decode(transport, buf)
}
}
} }
} }
impl<'a, T: Transport> Write for Encoder<'a, T> { impl<'a, T: Transport> Write for Encoder<'a, T> {
#[inline] #[inline]
fn write(&mut self, data: &[u8]) -> io::Result<usize> { fn write(&mut self, data: &[u8]) -> io::Result<usize> {
if data.is_empty() { self.write(data)
return Ok(0);
}
match self.0 {
EncoderImpl::H1(ref mut encoder, ref mut transport) => {
if encoder.is_closed() {
Ok(0)
} else {
encoder.encode(*transport, data)
}
}
}
} }
#[inline] #[inline]