feat(lib): redesign API to use Futures and Tokio
There are many changes involved with this, but let's just talk about
user-facing changes.
- Creating a `Client` and `Server` now needs a Tokio `Core` event loop
to attach to.
- `Request` and `Response` both no longer implement the
`std::io::{Read,Write}` traits, but instead represent their bodies as a
`futures::Stream` of items, where each item is a `Chunk`.
- The `Client.request` method now takes a `Request`, instead of being
used as a builder, and returns a `Future` that resolves to `Response`.
- The `Handler` trait for servers is no more, and instead the Tokio
`Service` trait is used. This allows interoperability with generic
middleware.
BREAKING CHANGE: A big sweeping set of breaking changes.
This commit is contained in:
97
src/http/body.rs
Normal file
97
src/http/body.rs
Normal file
@@ -0,0 +1,97 @@
|
||||
use std::convert::From;
|
||||
use std::sync::Arc;
|
||||
|
||||
use tokio_proto;
|
||||
use http::Chunk;
|
||||
use futures::{Poll, Stream};
|
||||
use futures::sync::mpsc;
|
||||
|
||||
pub type TokioBody = tokio_proto::streaming::Body<Chunk, ::Error>;
|
||||
|
||||
/// A `Stream` for `Chunk`s used in requests and responses.
|
||||
#[derive(Debug)]
|
||||
pub struct Body(TokioBody);
|
||||
|
||||
impl Body {
|
||||
/// Return an empty body stream
|
||||
pub fn empty() -> Body {
|
||||
Body(TokioBody::empty())
|
||||
}
|
||||
|
||||
/// Return a body stream with an associated sender half
|
||||
pub fn pair() -> (mpsc::Sender<Result<Chunk, ::Error>>, Body) {
|
||||
let (tx, rx) = TokioBody::pair();
|
||||
let rx = Body(rx);
|
||||
(tx, rx)
|
||||
}
|
||||
}
|
||||
|
||||
impl Stream for Body {
|
||||
type Item = Chunk;
|
||||
type Error = ::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<Chunk>, ::Error> {
|
||||
self.0.poll()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Body> for tokio_proto::streaming::Body<Chunk, ::Error> {
|
||||
fn from(b: Body) -> tokio_proto::streaming::Body<Chunk, ::Error> {
|
||||
b.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<tokio_proto::streaming::Body<Chunk, ::Error>> for Body {
|
||||
fn from(tokio_body: tokio_proto::streaming::Body<Chunk, ::Error>) -> Body {
|
||||
Body(tokio_body)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<mpsc::Receiver<Result<Chunk, ::Error>>> for Body {
|
||||
fn from(src: mpsc::Receiver<Result<Chunk, ::Error>>) -> Body {
|
||||
Body(src.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Chunk> for Body {
|
||||
fn from (chunk: Chunk) -> Body {
|
||||
Body(TokioBody::from(chunk))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for Body {
|
||||
fn from (vec: Vec<u8>) -> Body {
|
||||
Body(TokioBody::from(Chunk::from(vec)))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Arc<Vec<u8>>> for Body {
|
||||
fn from (vec: Arc<Vec<u8>>) -> Body {
|
||||
Body(TokioBody::from(Chunk::from(vec)))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&'static [u8]> for Body {
|
||||
fn from (slice: &'static [u8]) -> Body {
|
||||
Body(TokioBody::from(Chunk::from(slice)))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for Body {
|
||||
fn from (s: String) -> Body {
|
||||
Body(TokioBody::from(Chunk::from(s.into_bytes())))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&'static str> for Body {
|
||||
fn from (slice: &'static str) -> Body {
|
||||
Body(TokioBody::from(Chunk::from(slice.as_bytes())))
|
||||
}
|
||||
}
|
||||
|
||||
fn _assert_send() {
|
||||
fn _assert<T: Send>() {}
|
||||
|
||||
_assert::<Body>();
|
||||
_assert::<Chunk>();
|
||||
}
|
||||
@@ -1,16 +1,16 @@
|
||||
use std::cmp;
|
||||
use std::io::{self, Read};
|
||||
use std::io::{self, Read, Write};
|
||||
use std::ptr;
|
||||
|
||||
|
||||
const INIT_BUFFER_SIZE: usize = 4096;
|
||||
const MAX_BUFFER_SIZE: usize = 8192 + 4096 * 100;
|
||||
pub const MAX_BUFFER_SIZE: usize = 8192 + 4096 * 100;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Buffer {
|
||||
vec: Vec<u8>,
|
||||
read_pos: usize,
|
||||
write_pos: usize,
|
||||
tail: usize,
|
||||
head: usize,
|
||||
}
|
||||
|
||||
impl Buffer {
|
||||
@@ -24,7 +24,17 @@ impl Buffer {
|
||||
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
self.read_pos - self.write_pos
|
||||
self.tail - self.head
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn available(&self) -> usize {
|
||||
self.vec.len() - self.tail
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_max_size(&self) -> bool {
|
||||
self.len() >= MAX_BUFFER_SIZE
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -34,45 +44,88 @@ impl Buffer {
|
||||
|
||||
#[inline]
|
||||
pub fn bytes(&self) -> &[u8] {
|
||||
&self.vec[self.write_pos..self.read_pos]
|
||||
&self.vec[self.head..self.tail]
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn consume(&mut self, pos: usize) {
|
||||
debug_assert!(self.read_pos >= self.write_pos + pos);
|
||||
self.write_pos += pos;
|
||||
if self.write_pos == self.read_pos {
|
||||
self.write_pos = 0;
|
||||
self.read_pos = 0;
|
||||
debug_assert!(self.tail >= self.head + pos);
|
||||
self.head += pos;
|
||||
if self.head == self.tail {
|
||||
self.head = 0;
|
||||
self.tail = 0;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn consume_leading_lines(&mut self) {
|
||||
while !self.is_empty() {
|
||||
match self.vec[self.head] {
|
||||
b'\r' | b'\n' => {
|
||||
self.consume(1);
|
||||
},
|
||||
_ => return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_from<R: Read>(&mut self, r: &mut R) -> io::Result<usize> {
|
||||
self.maybe_reserve();
|
||||
let n = try!(r.read(&mut self.vec[self.read_pos..]));
|
||||
self.read_pos += n;
|
||||
self.maybe_reserve(1);
|
||||
let n = try!(r.read(&mut self.vec[self.tail..]));
|
||||
self.tail += n;
|
||||
self.maybe_reset();
|
||||
Ok(n)
|
||||
}
|
||||
|
||||
pub fn write_into<W: Write>(&mut self, w: &mut W) -> io::Result<usize> {
|
||||
if self.is_empty() {
|
||||
Ok(0)
|
||||
} else {
|
||||
let n = try!(w.write(&mut self.vec[self.head..self.tail]));
|
||||
self.head += n;
|
||||
self.maybe_reset();
|
||||
Ok(n)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write(&mut self, data: &[u8]) -> usize {
|
||||
trace!("Buffer::write len = {:?}", data.len());
|
||||
self.maybe_reserve(data.len());
|
||||
let len = cmp::min(self.available(), data.len());
|
||||
assert!(self.available() >= len);
|
||||
unsafe {
|
||||
// in rust 1.9, we could use slice::copy_from_slice
|
||||
ptr::copy(
|
||||
data.as_ptr(),
|
||||
self.vec.as_mut_ptr().offset(self.tail as isize),
|
||||
len
|
||||
);
|
||||
}
|
||||
self.tail += len;
|
||||
len
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn maybe_reserve(&mut self) {
|
||||
fn maybe_reserve(&mut self, needed: usize) {
|
||||
let cap = self.vec.len();
|
||||
if cap == 0 {
|
||||
trace!("reserving initial {}", INIT_BUFFER_SIZE);
|
||||
self.vec = vec![0; INIT_BUFFER_SIZE];
|
||||
} else if self.write_pos > 0 && self.read_pos == cap {
|
||||
let count = self.read_pos - self.write_pos;
|
||||
// first reserve
|
||||
let init = cmp::max(INIT_BUFFER_SIZE, needed);
|
||||
trace!("reserving initial {}", init);
|
||||
self.vec = vec![0; init];
|
||||
} else if self.head > 0 && self.tail == cap && self.head >= needed {
|
||||
// there is space to shift over
|
||||
let count = self.tail - self.head;
|
||||
trace!("moving buffer bytes over by {}", count);
|
||||
unsafe {
|
||||
ptr::copy(
|
||||
self.vec.as_ptr().offset(self.write_pos as isize),
|
||||
self.vec.as_ptr().offset(self.head as isize),
|
||||
self.vec.as_mut_ptr(),
|
||||
count
|
||||
);
|
||||
}
|
||||
self.read_pos -= count;
|
||||
self.write_pos = 0;
|
||||
} else if self.read_pos == cap && cap < MAX_BUFFER_SIZE {
|
||||
self.tail -= count;
|
||||
self.head = 0;
|
||||
} else if self.tail == cap && cap < MAX_BUFFER_SIZE {
|
||||
self.vec.reserve(cmp::min(cap * 4, MAX_BUFFER_SIZE) - cap);
|
||||
let new = self.vec.capacity() - cap;
|
||||
trace!("reserved {}", new);
|
||||
@@ -80,36 +133,11 @@ impl Buffer {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wrap<'a, 'b: 'a, R: io::Read>(&'a mut self, reader: &'b mut R) -> BufReader<'a, R> {
|
||||
BufReader {
|
||||
buf: self,
|
||||
reader: reader
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BufReader<'a, R: io::Read + 'a> {
|
||||
buf: &'a mut Buffer,
|
||||
reader: &'a mut R
|
||||
}
|
||||
|
||||
impl<'a, R: io::Read + 'a> BufReader<'a, R> {
|
||||
pub fn get_ref(&self) -> &R {
|
||||
self.reader
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, R: io::Read> Read for BufReader<'a, R> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
trace!("BufReader.read self={}, buf={}", self.buf.len(), buf.len());
|
||||
let n = try!(self.buf.bytes().read(buf));
|
||||
self.buf.consume(n);
|
||||
if n == 0 {
|
||||
self.buf.reset();
|
||||
self.reader.read(&mut buf[n..])
|
||||
} else {
|
||||
Ok(n)
|
||||
#[inline]
|
||||
fn maybe_reset(&mut self) {
|
||||
if self.tail != 0 && self.tail == self.head {
|
||||
self.tail = 0;
|
||||
self.head = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
use std::fmt;
|
||||
use std::sync::{Arc, mpsc};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use ::rotor;
|
||||
|
||||
pub use std::sync::mpsc::TryRecvError;
|
||||
|
||||
pub fn new<T>(notify: rotor::Notifier) -> (Sender<T>, Receiver<T>) {
|
||||
let b = Arc::new(AtomicBool::new(false));
|
||||
let (tx, rx) = mpsc::channel();
|
||||
(Sender {
|
||||
awake: b.clone(),
|
||||
notify: notify,
|
||||
tx: tx,
|
||||
},
|
||||
Receiver {
|
||||
awake: b,
|
||||
rx: rx,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn share<T, U>(other: &Sender<U>) -> (Sender<T>, Receiver<T>) {
|
||||
let (tx, rx) = mpsc::channel();
|
||||
let notify = other.notify.clone();
|
||||
let b = other.awake.clone();
|
||||
(Sender {
|
||||
awake: b.clone(),
|
||||
notify: notify,
|
||||
tx: tx,
|
||||
},
|
||||
Receiver {
|
||||
awake: b,
|
||||
rx: rx,
|
||||
})
|
||||
}
|
||||
|
||||
pub struct Sender<T> {
|
||||
awake: Arc<AtomicBool>,
|
||||
notify: rotor::Notifier,
|
||||
tx: mpsc::Sender<T>,
|
||||
}
|
||||
|
||||
impl<T: Send> Sender<T> {
|
||||
pub fn send(&self, val: T) -> Result<(), SendError<T>> {
|
||||
try!(self.tx.send(val));
|
||||
if !self.awake.swap(true, Ordering::SeqCst) {
|
||||
try!(self.notify.wakeup());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for Sender<T> {
|
||||
fn clone(&self) -> Sender<T> {
|
||||
Sender {
|
||||
awake: self.awake.clone(),
|
||||
notify: self.notify.clone(),
|
||||
tx: self.tx.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> fmt::Debug for Sender<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("Sender")
|
||||
.field("notify", &self.notify)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SendError<T>(pub Option<T>);
|
||||
|
||||
impl<T> From<mpsc::SendError<T>> for SendError<T> {
|
||||
fn from(e: mpsc::SendError<T>) -> SendError<T> {
|
||||
SendError(Some(e.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<rotor::WakeupError> for SendError<T> {
|
||||
fn from(_e: rotor::WakeupError) -> SendError<T> {
|
||||
SendError(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Receiver<T> {
|
||||
awake: Arc<AtomicBool>,
|
||||
rx: mpsc::Receiver<T>,
|
||||
}
|
||||
|
||||
impl<T: Send> Receiver<T> {
|
||||
pub fn try_recv(&self) -> Result<T, mpsc::TryRecvError> {
|
||||
self.awake.store(false, Ordering::Relaxed);
|
||||
self.rx.try_recv()
|
||||
}
|
||||
}
|
||||
77
src/http/chunk.rs
Normal file
77
src/http/chunk.rs
Normal file
@@ -0,0 +1,77 @@
|
||||
use std::borrow::Borrow;
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// A piece of a message body.
|
||||
pub struct Chunk(Inner);
|
||||
|
||||
enum Inner {
|
||||
Owned(Vec<u8>),
|
||||
Referenced(Arc<Vec<u8>>),
|
||||
Static(&'static [u8]),
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for Chunk {
|
||||
#[inline]
|
||||
fn from(v: Vec<u8>) -> Chunk {
|
||||
Chunk(Inner::Owned(v))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Arc<Vec<u8>>> for Chunk {
|
||||
#[inline]
|
||||
fn from(v: Arc<Vec<u8>>) -> Chunk {
|
||||
Chunk(Inner::Referenced(v))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&'static [u8]> for Chunk {
|
||||
#[inline]
|
||||
fn from(slice: &'static [u8]) -> Chunk {
|
||||
Chunk(Inner::Static(slice))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for Chunk {
|
||||
#[inline]
|
||||
fn from(s: String) -> Chunk {
|
||||
s.into_bytes().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&'static str> for Chunk {
|
||||
#[inline]
|
||||
fn from(slice: &'static str) -> Chunk {
|
||||
slice.as_bytes().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::ops::Deref for Chunk {
|
||||
type Target = [u8];
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for Chunk {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
match self.0 {
|
||||
Inner::Owned(ref vec) => vec,
|
||||
Inner::Referenced(ref vec) => {
|
||||
let v: &Vec<u8> = vec.borrow();
|
||||
v.as_slice()
|
||||
}
|
||||
Inner::Static(slice) => slice,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Chunk {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt::Debug::fmt(self.as_ref(), f)
|
||||
}
|
||||
}
|
||||
1607
src/http/conn.rs
1607
src/http/conn.rs
File diff suppressed because it is too large
Load Diff
@@ -277,7 +277,7 @@ mod tests {
|
||||
use std::io::Write;
|
||||
use super::Decoder;
|
||||
use super::ChunkedState;
|
||||
use mock::Async;
|
||||
use mock::AsyncIo;
|
||||
|
||||
#[test]
|
||||
fn test_read_chunk_size() {
|
||||
@@ -422,7 +422,7 @@ mod tests {
|
||||
-> String {
|
||||
let content_len = content.len();
|
||||
let mock_buf = io::Cursor::new(content.clone());
|
||||
let mut ins = Async::new(mock_buf, block_at);
|
||||
let mut ins = AsyncIo::new(mock_buf, block_at);
|
||||
let mut outs = vec![];
|
||||
loop {
|
||||
let mut buf = vec![0; read_buffer_size];
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
use std::borrow::Cow;
|
||||
use std::cmp;
|
||||
use std::io::{self, Write};
|
||||
|
||||
use http::internal::{AtomicWrite, WriteBuf};
|
||||
use http::io::AtomicWrite;
|
||||
|
||||
/// Encoders to handle different Transfer-Encodings.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Encoder {
|
||||
kind: Kind,
|
||||
prefix: Prefix,
|
||||
is_closed: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
@@ -26,27 +23,16 @@ impl Encoder {
|
||||
pub fn chunked() -> Encoder {
|
||||
Encoder {
|
||||
kind: Kind::Chunked(Chunked::Init),
|
||||
prefix: Prefix(None),
|
||||
is_closed: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn length(len: u64) -> Encoder {
|
||||
Encoder {
|
||||
kind: Kind::Length(len),
|
||||
prefix: Prefix(None),
|
||||
is_closed: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn prefix(&mut self, prefix: WriteBuf<Vec<u8>>) {
|
||||
self.prefix.0 = Some(prefix);
|
||||
}
|
||||
|
||||
pub fn is_eof(&self) -> bool {
|
||||
if self.prefix.0.is_some() {
|
||||
return false;
|
||||
}
|
||||
match self.kind {
|
||||
Kind::Length(0) |
|
||||
Kind::Chunked(Chunked::End) => true,
|
||||
@@ -54,71 +40,26 @@ impl Encoder {
|
||||
}
|
||||
}
|
||||
|
||||
/// User has called `encoder.close()` in a `Handler`.
|
||||
pub fn is_closed(&self) -> bool {
|
||||
self.is_closed
|
||||
}
|
||||
|
||||
pub fn close(&mut self) {
|
||||
self.is_closed = true;
|
||||
}
|
||||
|
||||
pub fn finish(self) -> Option<WriteBuf<Cow<'static, [u8]>>> {
|
||||
let trailer = self.trailer();
|
||||
let buf = self.prefix.0;
|
||||
|
||||
match (buf, trailer) {
|
||||
(Some(mut buf), Some(trailer)) => {
|
||||
buf.bytes.extend_from_slice(trailer);
|
||||
Some(WriteBuf {
|
||||
bytes: Cow::Owned(buf.bytes),
|
||||
pos: buf.pos,
|
||||
})
|
||||
},
|
||||
(Some(buf), None) => Some(WriteBuf {
|
||||
bytes: Cow::Owned(buf.bytes),
|
||||
pos: buf.pos
|
||||
}),
|
||||
(None, Some(trailer)) => {
|
||||
Some(WriteBuf {
|
||||
bytes: Cow::Borrowed(trailer),
|
||||
pos: 0,
|
||||
})
|
||||
},
|
||||
(None, None) => None
|
||||
}
|
||||
}
|
||||
|
||||
fn trailer(&self) -> Option<&'static [u8]> {
|
||||
match self.kind {
|
||||
Kind::Chunked(Chunked::Init) => {
|
||||
Some(b"0\r\n\r\n")
|
||||
}
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn encode<W: AtomicWrite>(&mut self, w: &mut W, msg: &[u8]) -> io::Result<usize> {
|
||||
match self.kind {
|
||||
Kind::Chunked(ref mut chunked) => {
|
||||
chunked.encode(w, &mut self.prefix, msg)
|
||||
chunked.encode(w, msg)
|
||||
},
|
||||
Kind::Length(ref mut remaining) => {
|
||||
let mut n = {
|
||||
let n = {
|
||||
let max = cmp::min(*remaining as usize, msg.len());
|
||||
trace!("sized write, len = {}", max);
|
||||
let slice = &msg[..max];
|
||||
|
||||
let prefix = self.prefix.0.as_ref().map(|buf| &buf.bytes[buf.pos..]).unwrap_or(b"");
|
||||
|
||||
try!(w.write_atomic(&[prefix, slice]))
|
||||
try!(w.write_atomic(&[slice]))
|
||||
};
|
||||
|
||||
n = self.prefix.update(n);
|
||||
if n == 0 {
|
||||
return Err(io::Error::new(io::ErrorKind::WouldBlock, "would block"));
|
||||
}
|
||||
|
||||
*remaining -= n as u64;
|
||||
trace!("sized write complete, remaining = {}", remaining);
|
||||
Ok(n)
|
||||
},
|
||||
}
|
||||
@@ -138,7 +79,7 @@ enum Chunked {
|
||||
}
|
||||
|
||||
impl Chunked {
|
||||
fn encode<W: AtomicWrite>(&mut self, w: &mut W, prefix: &mut Prefix, msg: &[u8]) -> io::Result<usize> {
|
||||
fn encode<W: AtomicWrite>(&mut self, w: &mut W, msg: &[u8]) -> io::Result<usize> {
|
||||
match *self {
|
||||
Chunked::Init => {
|
||||
let mut size = ChunkSize {
|
||||
@@ -158,28 +99,24 @@ impl Chunked {
|
||||
let pieces = match *self {
|
||||
Chunked::Init => unreachable!("Chunked::Init should have become Chunked::Size"),
|
||||
Chunked::Size(ref size) => [
|
||||
prefix.0.as_ref().map(|buf| &buf.bytes[buf.pos..]).unwrap_or(b""),
|
||||
&size.bytes[size.pos.into() .. size.len.into()],
|
||||
&b"\r\n"[..],
|
||||
msg,
|
||||
&b"\r\n"[..],
|
||||
],
|
||||
Chunked::SizeCr => [
|
||||
&b""[..],
|
||||
&b""[..],
|
||||
&b"\r\n"[..],
|
||||
msg,
|
||||
&b"\r\n"[..],
|
||||
],
|
||||
Chunked::SizeLf => [
|
||||
&b""[..],
|
||||
&b""[..],
|
||||
&b"\n"[..],
|
||||
msg,
|
||||
&b"\r\n"[..],
|
||||
],
|
||||
Chunked::Body(pos) => [
|
||||
&b""[..],
|
||||
&b""[..],
|
||||
&b""[..],
|
||||
&msg[pos..],
|
||||
@@ -189,14 +126,12 @@ impl Chunked {
|
||||
&b""[..],
|
||||
&b""[..],
|
||||
&b""[..],
|
||||
&b""[..],
|
||||
&b"\r\n"[..],
|
||||
],
|
||||
Chunked::BodyLf => [
|
||||
&b""[..],
|
||||
&b""[..],
|
||||
&b""[..],
|
||||
&b""[..],
|
||||
&b"\n"[..],
|
||||
],
|
||||
Chunked::End => unreachable!("Chunked::End shouldn't write more")
|
||||
@@ -204,9 +139,6 @@ impl Chunked {
|
||||
try!(w.write_atomic(&pieces))
|
||||
};
|
||||
|
||||
if n > 0 {
|
||||
n = prefix.update(n);
|
||||
}
|
||||
while n > 0 {
|
||||
match *self {
|
||||
Chunked::Init => unreachable!("Chunked::Init should have become Chunked::Size"),
|
||||
@@ -321,30 +253,10 @@ impl io::Write for ChunkSize {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct Prefix(Option<WriteBuf<Vec<u8>>>);
|
||||
|
||||
impl Prefix {
|
||||
fn update(&mut self, n: usize) -> usize {
|
||||
if let Some(mut buf) = self.0.take() {
|
||||
if buf.bytes.len() - buf.pos > n {
|
||||
buf.pos += n;
|
||||
self.0 = Some(buf);
|
||||
0
|
||||
} else {
|
||||
let nbuf = buf.bytes.len() - buf.pos;
|
||||
n - nbuf
|
||||
}
|
||||
} else {
|
||||
n
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Encoder;
|
||||
use mock::{Async, Buf};
|
||||
use mock::{AsyncIo, Buf};
|
||||
|
||||
#[test]
|
||||
fn test_chunked_encode_sync() {
|
||||
@@ -359,7 +271,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_chunked_encode_async() {
|
||||
let mut dst = Async::new(Buf::new(), 7);
|
||||
let mut dst = AsyncIo::new(Buf::new(), 7);
|
||||
let mut encoder = Encoder::chunked();
|
||||
|
||||
assert!(encoder.encode(&mut dst, b"foo bar").is_err());
|
||||
|
||||
@@ -1,21 +1,3 @@
|
||||
/*
|
||||
use std::fmt;
|
||||
use std::io::{self, Write};
|
||||
use std::marker::PhantomData;
|
||||
use std::sync::mpsc;
|
||||
|
||||
use url::Url;
|
||||
use tick;
|
||||
use time::now_utc;
|
||||
|
||||
use header::{self, Headers};
|
||||
use http::{self, conn};
|
||||
use method::Method;
|
||||
use net::{Fresh, Streaming};
|
||||
use status::StatusCode;
|
||||
use version::HttpVersion;
|
||||
*/
|
||||
|
||||
pub use self::decode::Decoder;
|
||||
pub use self::encode::Encoder;
|
||||
|
||||
@@ -23,7 +5,7 @@ pub use self::parse::parse;
|
||||
|
||||
mod decode;
|
||||
mod encode;
|
||||
mod parse;
|
||||
pub mod parse;
|
||||
|
||||
/*
|
||||
fn should_have_response_body(method: &Method, status: u16) -> bool {
|
||||
|
||||
@@ -4,7 +4,7 @@ use std::fmt::{self, Write};
|
||||
use httparse;
|
||||
|
||||
use header::{self, Headers, ContentLength, TransferEncoding};
|
||||
use http::{MessageHead, RawStatus, Http1Message, ParseResult, ServerMessage, ClientMessage, RequestLine};
|
||||
use http::{MessageHead, RawStatus, Http1Transaction, ParseResult, ServerTransaction, ClientTransaction, RequestLine};
|
||||
use http::h1::{Encoder, Decoder};
|
||||
use method::Method;
|
||||
use status::StatusCode;
|
||||
@@ -13,17 +13,15 @@ use version::HttpVersion::{Http10, Http11};
|
||||
const MAX_HEADERS: usize = 100;
|
||||
const AVERAGE_HEADER_SIZE: usize = 30; // totally scientific
|
||||
|
||||
pub fn parse<T: Http1Message<Incoming=I>, I>(buf: &[u8]) -> ParseResult<I> {
|
||||
pub fn parse<T: Http1Transaction<Incoming=I>, I>(buf: &[u8]) -> ParseResult<I> {
|
||||
if buf.len() == 0 {
|
||||
return Ok(None);
|
||||
}
|
||||
trace!("parse({:?})", buf);
|
||||
<T as Http1Message>::parse(buf)
|
||||
<T as Http1Transaction>::parse(buf)
|
||||
}
|
||||
|
||||
|
||||
|
||||
impl Http1Message for ServerMessage {
|
||||
impl Http1Transaction for ServerTransaction {
|
||||
type Incoming = RequestLine;
|
||||
type Outgoing = StatusCode;
|
||||
|
||||
@@ -60,7 +58,7 @@ impl Http1Message for ServerMessage {
|
||||
}
|
||||
|
||||
|
||||
fn encode(mut head: MessageHead<Self::Outgoing>, dst: &mut Vec<u8>) -> Encoder {
|
||||
fn encode(head: &mut MessageHead<Self::Outgoing>, dst: &mut Vec<u8>) -> Encoder {
|
||||
use ::header;
|
||||
trace!("writing head: {:?}", head);
|
||||
|
||||
@@ -103,9 +101,14 @@ impl Http1Message for ServerMessage {
|
||||
}
|
||||
body
|
||||
}
|
||||
|
||||
fn should_set_length(_head: &MessageHead<Self::Outgoing>) -> bool {
|
||||
//TODO: pass method, check if method == HEAD
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl Http1Message for ClientMessage {
|
||||
impl Http1Transaction for ClientTransaction {
|
||||
type Incoming = RawStatus;
|
||||
type Outgoing = RequestLine;
|
||||
|
||||
@@ -162,7 +165,7 @@ impl Http1Message for ClientMessage {
|
||||
}
|
||||
}
|
||||
|
||||
fn encode(mut head: MessageHead<Self::Outgoing>, dst: &mut Vec<u8>) -> Encoder {
|
||||
fn encode(head: &mut MessageHead<Self::Outgoing>, dst: &mut Vec<u8>) -> Encoder {
|
||||
trace!("writing head: {:?}", head);
|
||||
|
||||
|
||||
@@ -203,6 +206,14 @@ impl Http1Message for ClientMessage {
|
||||
|
||||
body
|
||||
}
|
||||
|
||||
|
||||
fn should_set_length(head: &MessageHead<Self::Outgoing>) -> bool {
|
||||
match &head.subject.0 {
|
||||
&Method::Get | &Method::Head => false,
|
||||
_ => true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct FastWrite<'a>(&'a mut Vec<u8>);
|
||||
@@ -238,17 +249,17 @@ mod tests {
|
||||
#[test]
|
||||
fn test_parse_request() {
|
||||
let raw = b"GET /echo HTTP/1.1\r\nHost: hyper.rs\r\n\r\n";
|
||||
parse::<http::ServerMessage, _>(raw).unwrap();
|
||||
parse::<http::ServerTransaction, _>(raw).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_raw_status() {
|
||||
let raw = b"HTTP/1.1 200 OK\r\n\r\n";
|
||||
let (res, _) = parse::<http::ClientMessage, _>(raw).unwrap().unwrap();
|
||||
let (res, _) = parse::<http::ClientTransaction, _>(raw).unwrap().unwrap();
|
||||
assert_eq!(res.subject.1, "OK");
|
||||
|
||||
let raw = b"HTTP/1.1 200 Howdy\r\n\r\n";
|
||||
let (res, _) = parse::<http::ClientMessage, _>(raw).unwrap().unwrap();
|
||||
let (res, _) = parse::<http::ClientTransaction, _>(raw).unwrap().unwrap();
|
||||
assert_eq!(res.subject.1, "Howdy");
|
||||
}
|
||||
|
||||
@@ -260,7 +271,7 @@ mod tests {
|
||||
fn bench_parse_incoming(b: &mut Bencher) {
|
||||
let raw = b"GET /echo HTTP/1.1\r\nHost: hyper.rs\r\n\r\n";
|
||||
b.iter(|| {
|
||||
parse::<http::ServerMessage, _>(raw).unwrap()
|
||||
parse::<http::ServerTransaction, _>(raw).unwrap()
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
207
src/http/io.rs
Normal file
207
src/http/io.rs
Normal file
@@ -0,0 +1,207 @@
|
||||
use std::fmt;
|
||||
use std::io::{self, Read, Write};
|
||||
|
||||
use futures::Async;
|
||||
use tokio::io::Io;
|
||||
|
||||
use http::{Http1Transaction, h1, MessageHead, ParseResult};
|
||||
use http::buffer::Buffer;
|
||||
|
||||
pub struct Buffered<T> {
|
||||
io: T,
|
||||
read_buf: Buffer,
|
||||
write_buf: Buffer,
|
||||
}
|
||||
|
||||
impl<T> fmt::Debug for Buffered<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("Buffered")
|
||||
.field("read_buf", &self.read_buf)
|
||||
.field("write_buf", &self.write_buf)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Io> Buffered<T> {
|
||||
pub fn new(io: T) -> Buffered<T> {
|
||||
Buffered {
|
||||
io: io,
|
||||
read_buf: Buffer::new(),
|
||||
write_buf: Buffer::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_buf(&self) -> &[u8] {
|
||||
self.read_buf.bytes()
|
||||
}
|
||||
|
||||
pub fn consume_leading_lines(&mut self) {
|
||||
self.read_buf.consume_leading_lines();
|
||||
}
|
||||
|
||||
pub fn poll_read(&mut self) -> Async<()> {
|
||||
self.io.poll_read()
|
||||
}
|
||||
|
||||
pub fn parse<S: Http1Transaction>(&mut self) -> ::Result<Option<MessageHead<S::Incoming>>> {
|
||||
match self.read_buf.read_from(&mut self.io) {
|
||||
Ok(0) => {
|
||||
trace!("parse eof");
|
||||
return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "parse eof").into());
|
||||
}
|
||||
Ok(_) => {},
|
||||
Err(e) => match e.kind() {
|
||||
io::ErrorKind::WouldBlock => {},
|
||||
_ => return Err(e.into())
|
||||
}
|
||||
}
|
||||
match try!(parse::<S, _>(self.read_buf.bytes())) {
|
||||
Some((head, len)) => {
|
||||
trace!("parsed {} bytes out of {}", len, self.read_buf.len());
|
||||
self.read_buf.consume(len);
|
||||
Ok(Some(head))
|
||||
},
|
||||
None => {
|
||||
if self.read_buf.is_max_size() {
|
||||
debug!("MAX_BUFFER_SIZE reached, closing");
|
||||
Err(::Error::TooLarge)
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn buffer<B: AsRef<[u8]>>(&mut self, buf: B) {
|
||||
self.write_buf.write(buf.as_ref());
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn io_mut(&mut self) -> &mut T {
|
||||
&mut self.io
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Read> Read for Buffered<T> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
trace!("Buffered.read self={}, buf={}", self.read_buf.len(), buf.len());
|
||||
let n = try!(self.read_buf.bytes().read(buf));
|
||||
self.read_buf.consume(n);
|
||||
if n == 0 {
|
||||
self.read_buf.reset();
|
||||
self.io.read(&mut buf[n..])
|
||||
} else {
|
||||
Ok(n)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Write> Write for Buffered<T> {
|
||||
fn write(&mut self, data: &[u8]) -> io::Result<usize> {
|
||||
Ok(self.write_buf.write(data))
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.write_buf.write_into(&mut self.io).and_then(|_n| {
|
||||
if self.write_buf.is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::WouldBlock, "wouldblock"))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
fn parse<T: Http1Transaction<Incoming=I>, I>(rdr: &[u8]) -> ParseResult<I> {
|
||||
h1::parse::<T, I>(rdr)
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Cursor<T: AsRef<[u8]>> {
|
||||
bytes: T,
|
||||
pos: usize,
|
||||
}
|
||||
|
||||
impl<T: AsRef<[u8]>> Cursor<T> {
|
||||
pub fn new(bytes: T) -> Cursor<T> {
|
||||
Cursor {
|
||||
bytes: bytes,
|
||||
pos: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_written(&self) -> bool {
|
||||
trace!("Cursor::is_written pos = {}, len = {}", self.pos, self.bytes.as_ref().len());
|
||||
self.pos >= self.bytes.as_ref().len()
|
||||
}
|
||||
|
||||
/*
|
||||
pub fn write_to<W: Write>(&mut self, dst: &mut W) -> io::Result<usize> {
|
||||
dst.write(&self.bytes.as_ref()[self.pos..]).map(|n| {
|
||||
self.pos += n;
|
||||
n
|
||||
})
|
||||
}
|
||||
*/
|
||||
|
||||
#[inline]
|
||||
pub fn buf(&self) -> &[u8] {
|
||||
&self.bytes.as_ref()[self.pos..]
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn consume(&mut self, num: usize) {
|
||||
trace!("Cursor::consume({})", num);
|
||||
self.pos = ::std::cmp::min(self.bytes.as_ref().len(), self.pos + num);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsRef<[u8]>> fmt::Debug for Cursor<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let bytes = self.buf();
|
||||
let reasonable_max = ::std::cmp::min(bytes.len(), 32);
|
||||
write!(f, "Cursor({:?})", &bytes[..reasonable_max])
|
||||
}
|
||||
}
|
||||
|
||||
pub trait AtomicWrite {
|
||||
fn write_atomic(&mut self, data: &[&[u8]]) -> io::Result<usize>;
|
||||
}
|
||||
|
||||
/*
|
||||
#[cfg(not(windows))]
|
||||
impl<T: Write + ::vecio::Writev> AtomicWrite for T {
|
||||
|
||||
fn write_atomic(&mut self, bufs: &[&[u8]]) -> io::Result<usize> {
|
||||
self.writev(bufs)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
*/
|
||||
impl<T: Write> AtomicWrite for T {
|
||||
fn write_atomic(&mut self, bufs: &[&[u8]]) -> io::Result<usize> {
|
||||
if cfg!(not(windows)) {
|
||||
warn!("write_atomic not using writev");
|
||||
}
|
||||
let vec = bufs.concat();
|
||||
self.write(&vec)
|
||||
}
|
||||
}
|
||||
//}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_iobuf_write_empty_slice() {
|
||||
use mock::{AsyncIo, Buf as MockBuf};
|
||||
|
||||
let mut mock = AsyncIo::new(MockBuf::new(), 256);
|
||||
mock.error(io::Error::new(io::ErrorKind::Other, "logic error"));
|
||||
|
||||
let mut io_buf = Buffered::new(mock);
|
||||
|
||||
// underlying io will return the logic error upon write,
|
||||
// so we are testing that the io_buf does not trigger a write
|
||||
// when there is nothing to flush
|
||||
io_buf.flush().expect("should short-circuit flush");
|
||||
}
|
||||
397
src/http/mod.rs
397
src/http/mod.rs
@@ -1,227 +1,44 @@
|
||||
//! Pieces pertaining to the HTTP message protocol.
|
||||
use std::borrow::Cow;
|
||||
use std::fmt;
|
||||
use std::io::{self, Read, Write};
|
||||
use std::time::Duration;
|
||||
|
||||
use header::Connection;
|
||||
use header::ConnectionOption::{KeepAlive, Close};
|
||||
use header::{Connection, ConnectionOption};
|
||||
use header::Headers;
|
||||
use method::Method;
|
||||
use net::Transport;
|
||||
use status::StatusCode;
|
||||
use uri::RequestUri;
|
||||
use version::HttpVersion;
|
||||
use version::HttpVersion::{Http10, Http11};
|
||||
|
||||
#[cfg(feature = "serde-serialization")]
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
pub use self::conn::{Conn, MessageHandler, MessageHandlerFactory, Seed, Key, ReadyResult};
|
||||
pub use self::conn::{Conn, KeepAlive, KA};
|
||||
pub use self::body::{Body, TokioBody};
|
||||
pub use self::chunk::Chunk;
|
||||
|
||||
mod body;
|
||||
//mod buf;
|
||||
mod buffer;
|
||||
pub mod channel;
|
||||
mod chunk;
|
||||
mod conn;
|
||||
mod io;
|
||||
mod h1;
|
||||
//mod h2;
|
||||
|
||||
/// Wraps a `Transport` to provide HTTP decoding when reading.
|
||||
#[derive(Debug)]
|
||||
pub struct Decoder<'a, T: Read + 'a>(DecoderImpl<'a, T>);
|
||||
|
||||
/// Wraps a `Transport` to provide HTTP encoding when writing.
|
||||
#[derive(Debug)]
|
||||
pub struct Encoder<'a, T: Transport + 'a>(EncoderImpl<'a, T>);
|
||||
|
||||
#[derive(Debug)]
|
||||
enum DecoderImpl<'a, T: Read + 'a> {
|
||||
H1(&'a mut h1::Decoder, Trans<'a, T>),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Trans<'a, T: Read + 'a> {
|
||||
Port(&'a mut T),
|
||||
Buf(self::buffer::BufReader<'a, T>)
|
||||
}
|
||||
|
||||
impl<'a, T: Read + 'a> Trans<'a, T> {
|
||||
fn get_ref(&self) -> &T {
|
||||
match *self {
|
||||
Trans::Port(ref t) => &*t,
|
||||
Trans::Buf(ref buf) => buf.get_ref()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Read + 'a> Read for Trans<'a, T> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
match *self {
|
||||
Trans::Port(ref mut t) => t.read(buf),
|
||||
Trans::Buf(ref mut b) => b.read(buf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum EncoderImpl<'a, T: Transport + 'a> {
|
||||
H1(&'a mut h1::Encoder, &'a mut T),
|
||||
}
|
||||
|
||||
impl<'a, T: Read> Decoder<'a, T> {
|
||||
fn h1(decoder: &'a mut h1::Decoder, transport: Trans<'a, T>) -> Decoder<'a, T> {
|
||||
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) {
|
||||
/*
|
||||
macro_rules! nonblocking {
|
||||
($e:expr) => ({
|
||||
match $e {
|
||||
Ok(n) => Ok(Some(n)),
|
||||
Err(e) => match e.kind() {
|
||||
io::ErrorKind::WouldBlock => Ok(None),
|
||||
stdio::ErrorKind::WouldBlock => Ok(None),
|
||||
_ => Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a reference to the transport.
|
||||
pub fn get_ref(&self) -> &T {
|
||||
match self.0 {
|
||||
DecoderImpl::H1(_, ref transport) => transport.get_ref()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Transport> Encoder<'a, T> {
|
||||
fn h1(encoder: &'a mut h1::Encoder, transport: &'a mut T) -> Encoder<'a, T> {
|
||||
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.
|
||||
///
|
||||
/// This is needed for encodings that don't know the length of the content
|
||||
/// beforehand. Most common instance would be usage of
|
||||
/// `Transfer-Enciding: chunked`. You would call `close()` to signal
|
||||
/// the `Encoder` should write the end chunk, or `0\r\n\r\n`.
|
||||
pub fn close(&mut self) {
|
||||
match self.0 {
|
||||
EncoderImpl::H1(ref mut encoder, _) => encoder.close()
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a reference to the transport.
|
||||
pub fn get_ref(&self) -> &T {
|
||||
match self.0 {
|
||||
EncoderImpl::H1(_, ref transport) => &*transport
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Read> Read for Decoder<'a, T> {
|
||||
#[inline]
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.read(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Transport> Write for Encoder<'a, T> {
|
||||
#[inline]
|
||||
fn write(&mut self, data: &[u8]) -> io::Result<usize> {
|
||||
self.write(data)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
match self.0 {
|
||||
EncoderImpl::H1(_, ref mut transport) => {
|
||||
transport.flush()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Because privacy rules. Reasons.
|
||||
/// https://github.com/rust-lang/rust/issues/30905
|
||||
mod internal {
|
||||
use std::io::{self, Write};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct WriteBuf<T: AsRef<[u8]>> {
|
||||
pub bytes: T,
|
||||
pub pos: usize,
|
||||
}
|
||||
|
||||
pub trait AtomicWrite {
|
||||
fn write_atomic(&mut self, data: &[&[u8]]) -> io::Result<usize>;
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
impl<T: Write + ::vecio::Writev> AtomicWrite for T {
|
||||
|
||||
fn write_atomic(&mut self, bufs: &[&[u8]]) -> io::Result<usize> {
|
||||
self.writev(bufs)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
impl<T: Write> AtomicWrite for T {
|
||||
fn write_atomic(&mut self, bufs: &[&[u8]]) -> io::Result<usize> {
|
||||
let vec = bufs.concat();
|
||||
self.write(&vec)
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
*/
|
||||
|
||||
/// An Incoming Message head. Includes request/status line, and headers.
|
||||
#[derive(Debug, Default)]
|
||||
#[derive(Debug, Default, PartialEq)]
|
||||
pub struct MessageHead<S> {
|
||||
/// HTTP version of the message.
|
||||
pub version: HttpVersion,
|
||||
@@ -234,7 +51,7 @@ pub struct MessageHead<S> {
|
||||
/// An incoming request message.
|
||||
pub type RequestHead = MessageHead<RequestLine>;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
#[derive(Debug, Default, PartialEq)]
|
||||
pub struct RequestLine(pub Method, pub RequestUri);
|
||||
|
||||
impl fmt::Display for RequestLine {
|
||||
@@ -274,18 +91,13 @@ impl Default for RawStatus {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde-serialization")]
|
||||
impl Serialize for RawStatus {
|
||||
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer {
|
||||
(self.0, &self.1).serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde-serialization")]
|
||||
impl Deserialize for RawStatus {
|
||||
fn deserialize<D>(deserializer: &mut D) -> Result<RawStatus, D::Error> where D: Deserializer {
|
||||
let representation: (u16, String) = try!(Deserialize::deserialize(deserializer));
|
||||
Ok(RawStatus(representation.0, Cow::Owned(representation.1)))
|
||||
impl From<MessageHead<::StatusCode>> for MessageHead<RawStatus> {
|
||||
fn from(head: MessageHead<::StatusCode>) -> MessageHead<RawStatus> {
|
||||
MessageHead {
|
||||
subject: head.subject.into(),
|
||||
version: head.version,
|
||||
headers: head.headers,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,174 +106,31 @@ impl Deserialize for RawStatus {
|
||||
pub fn should_keep_alive(version: HttpVersion, headers: &Headers) -> bool {
|
||||
let ret = match (version, headers.get::<Connection>()) {
|
||||
(Http10, None) => false,
|
||||
(Http10, Some(conn)) if !conn.contains(&KeepAlive) => false,
|
||||
(Http11, Some(conn)) if conn.contains(&Close) => false,
|
||||
(Http10, Some(conn)) if !conn.contains(&ConnectionOption::KeepAlive) => false,
|
||||
(Http11, Some(conn)) if conn.contains(&ConnectionOption::Close) => false,
|
||||
_ => true
|
||||
};
|
||||
trace!("should_keep_alive(version={:?}, header={:?}) = {:?}", version, headers.get::<Connection>(), ret);
|
||||
ret
|
||||
}
|
||||
|
||||
pub type ParseResult<T> = ::Result<Option<(MessageHead<T>, usize)>>;
|
||||
|
||||
pub fn parse<T: Http1Message<Incoming=I>, I>(rdr: &[u8]) -> ParseResult<I> {
|
||||
h1::parse::<T, I>(rdr)
|
||||
}
|
||||
|
||||
// These 2 enums are not actually dead_code. They are used in the server and
|
||||
// and client modules, respectively. However, their being used as associated
|
||||
// types doesn't mark them as used, so the dead_code linter complains.
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug)]
|
||||
pub enum ServerMessage {}
|
||||
pub enum ServerTransaction {}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug)]
|
||||
pub enum ClientMessage {}
|
||||
pub enum ClientTransaction {}
|
||||
|
||||
pub trait Http1Message {
|
||||
pub trait Http1Transaction {
|
||||
type Incoming;
|
||||
type Outgoing: Default;
|
||||
//type KeepAlive: KeepAlive;
|
||||
fn parse(bytes: &[u8]) -> ParseResult<Self::Incoming>;
|
||||
fn decoder(head: &MessageHead<Self::Incoming>) -> ::Result<h1::Decoder>;
|
||||
fn encode(head: MessageHead<Self::Outgoing>, dst: &mut Vec<u8>) -> h1::Encoder;
|
||||
fn encode(head: &mut MessageHead<Self::Outgoing>, dst: &mut Vec<u8>) -> h1::Encoder;
|
||||
fn should_set_length(head: &MessageHead<Self::Outgoing>) -> bool;
|
||||
}
|
||||
|
||||
/// Used to signal desired events when working with asynchronous IO.
|
||||
#[must_use]
|
||||
#[derive(Clone)]
|
||||
pub struct Next {
|
||||
interest: Next_,
|
||||
timeout: Option<Duration>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for Next {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
try!(write!(f, "Next::{:?}", &self.interest));
|
||||
match self.timeout {
|
||||
Some(ref d) => write!(f, "({:?})", d),
|
||||
None => Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Internal enum for `Next`
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum Next_ {
|
||||
Read,
|
||||
Write,
|
||||
ReadWrite,
|
||||
Wait,
|
||||
End,
|
||||
Remove,
|
||||
}
|
||||
|
||||
// An enum representing all the possible actions to taken when registering
|
||||
// with the event loop.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum Reg {
|
||||
Read,
|
||||
Write,
|
||||
ReadWrite,
|
||||
Wait,
|
||||
Remove
|
||||
}
|
||||
|
||||
/// A notifier to wakeup a socket after having used `Next::wait()`
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Control {
|
||||
tx: self::channel::Sender<Next>,
|
||||
}
|
||||
|
||||
impl Control {
|
||||
/// Wakeup a waiting socket to listen for a certain event.
|
||||
pub fn ready(&self, next: Next) -> Result<(), ControlError> {
|
||||
//TODO: assert!( next.interest != Next_::Wait ) ?
|
||||
self.tx.send(next).map_err(|_| ControlError(()))
|
||||
}
|
||||
}
|
||||
|
||||
/// An error occured trying to tell a Control it is ready.
|
||||
#[derive(Debug)]
|
||||
pub struct ControlError(());
|
||||
|
||||
impl ::std::error::Error for ControlError {
|
||||
fn description(&self) -> &str {
|
||||
"Cannot wakeup event loop: loop is closed"
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ControlError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.write_str(::std::error::Error::description(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl Next {
|
||||
fn new(interest: Next_) -> Next {
|
||||
Next {
|
||||
interest: interest,
|
||||
timeout: None,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
fn reg(&self) -> Reg {
|
||||
self.interest.register()
|
||||
}
|
||||
*/
|
||||
|
||||
/// Signals the desire to read from the transport.
|
||||
pub fn read() -> Next {
|
||||
Next::new(Next_::Read)
|
||||
}
|
||||
|
||||
/// Signals the desire to write to the transport.
|
||||
pub fn write() -> Next {
|
||||
Next::new(Next_::Write)
|
||||
}
|
||||
|
||||
/// Signals the desire to read and write to the transport.
|
||||
pub fn read_and_write() -> Next {
|
||||
Next::new(Next_::ReadWrite)
|
||||
}
|
||||
|
||||
/// Signals the desire to end the current HTTP message.
|
||||
pub fn end() -> Next {
|
||||
Next::new(Next_::End)
|
||||
}
|
||||
|
||||
/// Signals the desire to abruptly remove the current transport from the
|
||||
/// event loop.
|
||||
pub fn remove() -> Next {
|
||||
Next::new(Next_::Remove)
|
||||
}
|
||||
|
||||
/// Signals the desire to wait until some future time before acting again.
|
||||
pub fn wait() -> Next {
|
||||
Next::new(Next_::Wait)
|
||||
}
|
||||
|
||||
/// Signals a maximum duration to be waited for the desired event.
|
||||
pub fn timeout(mut self, dur: Duration) -> Next {
|
||||
self.timeout = Some(dur);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Next_ {
|
||||
fn register(&self) -> Reg {
|
||||
match *self {
|
||||
Next_::Read => Reg::Read,
|
||||
Next_::Write => Reg::Write,
|
||||
Next_::ReadWrite => Reg::ReadWrite,
|
||||
Next_::Wait => Reg::Wait,
|
||||
Next_::End => Reg::Remove,
|
||||
Next_::Remove => Reg::Remove,
|
||||
}
|
||||
}
|
||||
}
|
||||
type ParseResult<T> = ::Result<Option<(MessageHead<T>, usize)>>;
|
||||
|
||||
#[test]
|
||||
fn test_should_keep_alive() {
|
||||
|
||||
Reference in New Issue
Block a user