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:
Sean McArthur
2016-11-17 17:31:42 -08:00
parent e23689122a
commit 2d2d5574a6
43 changed files with 2775 additions and 5033 deletions

View File

@@ -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()
});
}