add some benches with other http libs
This commit is contained in:
@@ -9,3 +9,9 @@ git = "https://github.com/servo/rust-url"
|
|||||||
|
|
||||||
[dependencies.mime]
|
[dependencies.mime]
|
||||||
git = "https://github.com/seanmonstar/mime.rs"
|
git = "https://github.com/seanmonstar/mime.rs"
|
||||||
|
|
||||||
|
[dev-dependencies.curl]
|
||||||
|
git = "https://github.com/carllerche/curl-rust"
|
||||||
|
|
||||||
|
[dev-dependencies.http]
|
||||||
|
git = "https://github.com/chris-morgan/rust-http"
|
||||||
|
|||||||
15
README.md
15
README.md
@@ -4,6 +4,21 @@
|
|||||||
|
|
||||||
An HTTP library for Rust.
|
An HTTP library for Rust.
|
||||||
|
|
||||||
|
## Scientific* Benchmarks
|
||||||
|
|
||||||
|
[Client bench:](./benches/client.rs)
|
||||||
|
|
||||||
|
```
|
||||||
|
running 3 tests
|
||||||
|
test bench_curl ... bench: 346762 ns/iter (+/- 16469)
|
||||||
|
test bench_http ... bench: 310861 ns/iter (+/- 123168)
|
||||||
|
test bench_hyper ... bench: 284916 ns/iter (+/- 65935)
|
||||||
|
|
||||||
|
test result: ok. 0 passed; 0 failed; 0 ignored; 3 measured
|
||||||
|
```
|
||||||
|
|
||||||
|
_* No science was harmed in this benchmark._
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
[MIT](./LICENSE)
|
[MIT](./LICENSE)
|
||||||
|
|||||||
68
benches/client.rs
Normal file
68
benches/client.rs
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
extern crate curl;
|
||||||
|
extern crate http;
|
||||||
|
extern crate hyper;
|
||||||
|
|
||||||
|
extern crate test;
|
||||||
|
|
||||||
|
use std::io::IoResult;
|
||||||
|
use std::time::Duration;
|
||||||
|
use std::io::timer::sleep;
|
||||||
|
use std::io::net::ip::Ipv4Addr;
|
||||||
|
use std::sync::{Once, ONCE_INIT};
|
||||||
|
use hyper::server::{Request, Response, Server};
|
||||||
|
|
||||||
|
static mut SERVER: Once = ONCE_INIT;
|
||||||
|
|
||||||
|
fn listen() {
|
||||||
|
unsafe {
|
||||||
|
SERVER.doit(|| {
|
||||||
|
let server = Server::http(Ipv4Addr(127, 0, 0, 1), 1337);
|
||||||
|
let listening = server.listen(handle).unwrap();
|
||||||
|
spawn(proc() {
|
||||||
|
sleep(Duration::seconds(20));
|
||||||
|
listening.close().unwrap();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle(_req: Request, mut res: Response) -> IoResult<()> {
|
||||||
|
try!(res.write(b"Benchmarking hyper vs others!"));
|
||||||
|
res.end()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_curl(b: &mut test::Bencher) {
|
||||||
|
listen();
|
||||||
|
b.iter(|| {
|
||||||
|
curl::http::handle().get("http://127.0.0.1:1337/").exec().unwrap()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_hyper(b: &mut test::Bencher) {
|
||||||
|
listen();
|
||||||
|
b.iter(|| {
|
||||||
|
hyper::get(hyper::Url::parse("http://127.0.0.1:1337/").unwrap()).unwrap()
|
||||||
|
.send().unwrap()
|
||||||
|
.read_to_string().unwrap()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_http(b: &mut test::Bencher) {
|
||||||
|
listen();
|
||||||
|
b.iter(|| {
|
||||||
|
let req: http::client::RequestWriter = http::client::RequestWriter::new(
|
||||||
|
http::method::Get,
|
||||||
|
hyper::Url::parse("http://127.0.0.1:1337/").unwrap()
|
||||||
|
).unwrap();
|
||||||
|
// cant unwrap because Err contains RequestWriter, which does not implement Show
|
||||||
|
let mut res = match req.read_response() {
|
||||||
|
Ok(res) => res,
|
||||||
|
Err(..) => fail!("http response failed")
|
||||||
|
};
|
||||||
|
res.read_to_string().unwrap();
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -5,17 +5,21 @@ use std::io::{IoResult};
|
|||||||
use std::io::util::copy;
|
use std::io::util::copy;
|
||||||
use std::io::net::ip::Ipv4Addr;
|
use std::io::net::ip::Ipv4Addr;
|
||||||
|
|
||||||
use hyper::method::{Get, Post};
|
use hyper::{Get, Post};
|
||||||
use hyper::server::{Server, Handler, Request, Response};
|
use hyper::server::{Server, Handler, Request, Response};
|
||||||
|
use hyper::header::ContentLength;
|
||||||
|
|
||||||
struct Echo;
|
struct Echo;
|
||||||
|
|
||||||
impl Handler for Echo {
|
impl Handler for Echo {
|
||||||
fn handle(&mut self, mut req: Request, mut res: Response) -> IoResult<()> {
|
fn handle(&mut self, mut req: Request, mut res: Response) -> IoResult<()> {
|
||||||
match &req.uri {
|
match req.uri {
|
||||||
&hyper::uri::AbsolutePath(ref path) => match (&req.method, path.as_slice()) {
|
hyper::uri::AbsolutePath(ref path) => match (&req.method, path.as_slice()) {
|
||||||
(&Get, "/") | (&Get, "/echo") => {
|
(&Get, "/") | (&Get, "/echo") => {
|
||||||
try!(res.write_str("Try POST /echo"));
|
let out = b"Try POST /echo";
|
||||||
|
|
||||||
|
res.headers.set(ContentLength(out.len()));
|
||||||
|
try!(res.write(out));
|
||||||
return res.end();
|
return res.end();
|
||||||
},
|
},
|
||||||
(&Post, "/echo") => (), // fall through, fighting mutable borrows
|
(&Post, "/echo") => (), // fall through, fighting mutable borrows
|
||||||
@@ -27,14 +31,12 @@ impl Handler for Echo {
|
|||||||
_ => return res.end()
|
_ => return res.end()
|
||||||
};
|
};
|
||||||
|
|
||||||
println!("copying...");
|
|
||||||
try!(copy(&mut req, &mut res));
|
try!(copy(&mut req, &mut res));
|
||||||
println!("copied...");
|
|
||||||
res.end()
|
res.end()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let server = Server::http(Ipv4Addr(127, 0, 0, 1), 1337);
|
let server = Server::http(Ipv4Addr(127, 0, 0, 1), 1337);
|
||||||
server.listen(Echo);
|
server.listen(Echo).unwrap();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
//! # Client
|
//! HTTP Client
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use method::{Get, Method};
|
use method::{Get, Head, Post, Delete, Method};
|
||||||
|
|
||||||
pub use self::request::Request;
|
pub use self::request::Request;
|
||||||
pub use self::response::Response;
|
pub use self::response::Response;
|
||||||
@@ -16,6 +16,22 @@ pub fn get(url: Url) -> HttpResult<Request> {
|
|||||||
request(Get, url)
|
request(Get, url)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a HEAD client request.
|
||||||
|
pub fn head(url: Url) -> HttpResult<Request> {
|
||||||
|
request(Head, url)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a POST client request.
|
||||||
|
pub fn post(url: Url) -> HttpResult<Request> {
|
||||||
|
// TODO: should this accept a Body parameter? or just let user `write` to the request?
|
||||||
|
request(Post, url)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a DELETE client request.
|
||||||
|
pub fn delete(url: Url) -> HttpResult<Request> {
|
||||||
|
request(Delete, url)
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a client request.
|
/// Create a client request.
|
||||||
pub fn request(method: Method, url: Url) -> HttpResult<Request> {
|
pub fn request(method: Method, url: Url) -> HttpResult<Request> {
|
||||||
Request::new(method, url)
|
Request::new(method, url)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//! # Client Requests
|
//! Client Requests
|
||||||
use std::io::net::tcp::TcpStream;
|
use std::io::net::tcp::TcpStream;
|
||||||
use std::io::IoResult;
|
use std::io::IoResult;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//! # Client Responses
|
//! Client Responses
|
||||||
use std::io::{Reader, IoResult};
|
use std::io::{Reader, IoResult};
|
||||||
use std::io::net::tcp::TcpStream;
|
use std::io::net::tcp::TcpStream;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//! # Headers
|
//! Headers container, and common header fields.
|
||||||
//!
|
//!
|
||||||
//! hyper has the opinion that Headers should be strongly-typed, because that's
|
//! hyper has the opinion that Headers should be strongly-typed, because that's
|
||||||
//! why we're using Rust in the first place. To set or get any header, an object
|
//! why we're using Rust in the first place. To set or get any header, an object
|
||||||
|
|||||||
@@ -11,7 +11,9 @@ extern crate url;
|
|||||||
pub use std::io::net::ip::{SocketAddr, IpAddr, Ipv4Addr, Ipv6Addr, Port};
|
pub use std::io::net::ip::{SocketAddr, IpAddr, Ipv4Addr, Ipv6Addr, Port};
|
||||||
pub use mimewrapper::mime;
|
pub use mimewrapper::mime;
|
||||||
pub use url::Url;
|
pub use url::Url;
|
||||||
pub use client::{get};
|
pub use client::{get, head, post, delete, request};
|
||||||
|
pub use method::{Get, Head, Post, Delete};
|
||||||
|
pub use status::{Ok, BadRequest, NotFound};
|
||||||
pub use server::Server;
|
pub use server::Server;
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
@@ -30,7 +32,7 @@ struct Trace;
|
|||||||
impl fmt::Show for Trace {
|
impl fmt::Show for Trace {
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||||
let _ = backtrace::write(fmt);
|
let _ = backtrace::write(fmt);
|
||||||
Ok(())
|
::std::result::Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//! # HTTP Method
|
//! The HTTP request method
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::from_str::FromStr;
|
use std::from_str::FromStr;
|
||||||
|
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
//! # Server
|
//! HTTP Server
|
||||||
use std::io::net::tcp::TcpListener;
|
use std::io::net::tcp::{TcpListener, TcpAcceptor};
|
||||||
use std::io::{Acceptor, Listener, IoResult};
|
use std::io::{Acceptor, Listener, IoResult, EndOfFile};
|
||||||
use std::io::net::ip::{IpAddr, Port};
|
use std::io::net::ip::{IpAddr, Port};
|
||||||
|
|
||||||
pub use self::request::Request;
|
pub use self::request::Request;
|
||||||
@@ -30,13 +30,13 @@ impl Server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Binds to a socket, and starts handling connections.
|
/// Binds to a socket, and starts handling connections.
|
||||||
pub fn listen<H: Handler>(&self, mut handler: H) {
|
pub fn listen<H: Handler + 'static>(&self, mut handler: H) -> IoResult<Listening> {
|
||||||
let listener = match TcpListener::bind(self.ip.to_string().as_slice(), self.port) {
|
let listener = try!(TcpListener::bind(self.ip.to_string().as_slice(), self.port));
|
||||||
Ok(listener) => listener,
|
let acceptor = try!(listener.listen());
|
||||||
Err(err) => fail!("Listen failed: {}", err)
|
let worker = acceptor.clone();
|
||||||
};
|
|
||||||
let mut acceptor = listener.listen();
|
|
||||||
|
|
||||||
|
spawn(proc() {
|
||||||
|
let mut acceptor = worker;
|
||||||
for conn in acceptor.incoming() {
|
for conn in acceptor.incoming() {
|
||||||
match conn {
|
match conn {
|
||||||
Ok(stream) => {
|
Ok(stream) => {
|
||||||
@@ -59,24 +59,43 @@ impl Server {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Err(ref e) if e.kind == EndOfFile => break, // server closed
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("Connection failed: {}", e);
|
error!("Connection failed: {}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(Listening {
|
||||||
|
acceptor: acceptor
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A listening server, which can later be closed.
|
||||||
|
pub struct Listening {
|
||||||
|
acceptor: TcpAcceptor
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Listening {
|
||||||
|
/// Stop the server from listening to it's socket address.
|
||||||
|
pub fn close(mut self) -> IoResult<()> {
|
||||||
|
debug!("closing server");
|
||||||
|
self.acceptor.close_accept()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A handler that can handle incoming requests for a server.
|
/// A handler that can handle incoming requests for a server.
|
||||||
pub trait Handler {
|
pub trait Handler: Send {
|
||||||
/// Receives a `Request`/`Response` pair, and should perform some action on them.
|
/// Receives a `Request`/`Response` pair, and should perform some action on them.
|
||||||
///
|
///
|
||||||
/// This could reading from the request, and writing to the response.
|
/// This could reading from the request, and writing to the response.
|
||||||
fn handle(&mut self, req: Request, res: Response) -> IoResult<()>;
|
fn handle(&mut self, req: Request, res: Response) -> IoResult<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Handler for |Request, Response|: 'a -> IoResult<()> {
|
impl Handler for fn(Request, Response) -> IoResult<()> {
|
||||||
fn handle(&mut self, req: Request, res: Response) -> IoResult<()> {
|
fn handle(&mut self, req: Request, res: Response) -> IoResult<()> {
|
||||||
(*self)(req, res)
|
(*self)(req, res)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//! # Server Requests
|
//! Server Requests
|
||||||
//!
|
//!
|
||||||
//! These are requests that a `hyper::Server` receives, and include its method,
|
//! These are requests that a `hyper::Server` receives, and include its method,
|
||||||
//! target URI, headers, and message body.
|
//! target URI, headers, and message body.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//! # Server Responses
|
//! Server Responses
|
||||||
//!
|
//!
|
||||||
//! These are responses sent by a `hyper::Server` to clients, after
|
//! These are responses sent by a `hyper::Server` to clients, after
|
||||||
//! receiving a request.
|
//! receiving a request.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//! # RequestUri
|
//! HTTP RequestUris
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
/// The Request-URI of a Request's StartLine.
|
/// The Request-URI of a Request's StartLine.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//! # HTTP Versions
|
//! HTTP Versions enum
|
||||||
//!
|
//!
|
||||||
//! Instead of relying on typo-prone Strings, use expected HTTP versions as
|
//! Instead of relying on typo-prone Strings, use expected HTTP versions as
|
||||||
//! the `HttpVersion` enum.
|
//! the `HttpVersion` enum.
|
||||||
|
|||||||
Reference in New Issue
Block a user