Update the client API to statically track headers and move constructors
The client Request now uses the same system as a server Response to track the write status of headers, and the API has been updated accordingly. Additionally, the Request constructors have been moved onto the Request object instead of being top-level hyper functions, as this better namespaces the client and Server.
This commit is contained in:
committed by
Sean McArthur
parent
38a47889d9
commit
d5c6f33c34
@@ -66,14 +66,14 @@ fn bench_hyper(b: &mut test::Bencher) {
|
||||
let s = format!("http://{}/", listening.sockets[0]);
|
||||
let url = s.as_slice();
|
||||
b.iter(|| {
|
||||
let mut req = hyper::get(hyper::Url::parse(url).unwrap()).unwrap();
|
||||
req.headers.set(Foo);
|
||||
let mut req = hyper::client::Request::get(hyper::Url::parse(url).unwrap()).unwrap();
|
||||
req.headers_mut().set(Foo);
|
||||
|
||||
req
|
||||
.send().unwrap()
|
||||
.read_to_string().unwrap()
|
||||
req.start().unwrap()
|
||||
.send().unwrap()
|
||||
.read_to_string().unwrap()
|
||||
});
|
||||
listening.close().unwrap()
|
||||
listening.close().unwrap()
|
||||
}
|
||||
|
||||
#[bench]
|
||||
|
||||
@@ -13,8 +13,8 @@ use http::server::Server;
|
||||
static phrase: &'static [u8] = b"Benchmarking hyper vs others!";
|
||||
|
||||
fn request(url: hyper::Url) {
|
||||
let req = hyper::get(url).unwrap();
|
||||
req.send().unwrap().read_to_string().unwrap();
|
||||
let req = hyper::client::Request::get(url).unwrap();
|
||||
req.start().unwrap().send().unwrap().read_to_string().unwrap();
|
||||
}
|
||||
|
||||
fn hyper_handle(mut incoming: hyper::server::Incoming) {
|
||||
|
||||
@@ -5,6 +5,7 @@ use std::io::stdout;
|
||||
use std::io::util::copy;
|
||||
|
||||
use hyper::Url;
|
||||
use hyper::client::Request;
|
||||
|
||||
fn main() {
|
||||
let args = os::args();
|
||||
@@ -25,12 +26,15 @@ fn main() {
|
||||
};
|
||||
|
||||
|
||||
let req = match hyper::get(url) {
|
||||
let req = match Request::get(url) {
|
||||
Ok(req) => req,
|
||||
Err(err) => fail!("Failed to connect: {}", err)
|
||||
};
|
||||
let mut res = req.send().unwrap();
|
||||
|
||||
|
||||
let mut res = req
|
||||
.start().ok().expect("Error writing Headers.")
|
||||
.send().ok().expect("Error sending Request.");
|
||||
|
||||
println!("Response: {}", res.status);
|
||||
println!("{}", res.headers);
|
||||
match copy(&mut res, &mut stdout()) {
|
||||
|
||||
@@ -1,38 +1,7 @@
|
||||
//! HTTP Client
|
||||
use url::Url;
|
||||
|
||||
use method::{Get, Head, Post, Delete, Method};
|
||||
|
||||
pub use self::request::Request;
|
||||
pub use self::response::Response;
|
||||
use {HttpResult};
|
||||
|
||||
pub mod request;
|
||||
pub mod response;
|
||||
|
||||
|
||||
/// Create a GET client request.
|
||||
pub fn get(url: Url) -> HttpResult<Request> {
|
||||
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.
|
||||
pub fn request(method: Method, url: Url) -> HttpResult<Request> {
|
||||
Request::new(method, url)
|
||||
}
|
||||
|
||||
@@ -3,34 +3,58 @@ use std::io::{BufferedWriter, IoResult};
|
||||
|
||||
use url::Url;
|
||||
|
||||
use method;
|
||||
use method::{mod, Get, Post, Delete, Put, Patch, Head, Options};
|
||||
use header::Headers;
|
||||
use header::common::Host;
|
||||
use net::{NetworkStream, HttpStream};
|
||||
use http::LINE_ENDING;
|
||||
use version;
|
||||
use {HttpResult, HttpUriError};
|
||||
use super::{Response};
|
||||
use super::Response;
|
||||
|
||||
/// The write-status of a Request that has not had its headers written.
|
||||
pub struct Fresh;
|
||||
|
||||
/// The write-status of a Request that has had its headers written.
|
||||
pub struct Streaming;
|
||||
|
||||
/// The write-status of a Request
|
||||
pub trait WriteStatus: Private {}
|
||||
impl WriteStatus for Fresh {}
|
||||
impl WriteStatus for Streaming {}
|
||||
|
||||
// Only Fresh and Streaming can be WriteStatus
|
||||
#[doc(hidden)]
|
||||
trait Private {}
|
||||
impl Private for Fresh {}
|
||||
impl Private for Streaming {}
|
||||
|
||||
/// A client request to a remote server.
|
||||
pub struct Request {
|
||||
/// The method of this request.
|
||||
pub method: method::Method,
|
||||
/// The headers that will be sent with this request.
|
||||
pub headers: Headers,
|
||||
pub struct Request<W: WriteStatus> {
|
||||
/// The target URI for this request.
|
||||
pub url: Url,
|
||||
|
||||
/// The HTTP version of this request.
|
||||
pub version: version::HttpVersion,
|
||||
headers_written: bool,
|
||||
|
||||
body: BufferedWriter<Box<NetworkStream + Send>>,
|
||||
headers: Headers,
|
||||
method: method::Method,
|
||||
}
|
||||
|
||||
impl Request {
|
||||
impl<W: WriteStatus> Request<W> {
|
||||
/// Read the Request headers.
|
||||
#[inline]
|
||||
pub fn headers(&self) -> &Headers { &self.headers }
|
||||
|
||||
/// Read the Request method.
|
||||
#[inline]
|
||||
pub fn method(&self) -> method::Method { self.method.clone() }
|
||||
}
|
||||
|
||||
impl Request<Fresh> {
|
||||
/// Create a new client request.
|
||||
pub fn new(method: method::Method, url: Url) -> HttpResult<Request> {
|
||||
pub fn new(method: method::Method, url: Url) -> HttpResult<Request<Fresh>> {
|
||||
debug!("{} {}", method, url);
|
||||
let host = match url.serialize_host() {
|
||||
Some(host) => host,
|
||||
@@ -47,38 +71,76 @@ impl Request {
|
||||
let stream = BufferedWriter::new(stream.abstract());
|
||||
let mut headers = Headers::new();
|
||||
headers.set(Host(host));
|
||||
|
||||
Ok(Request {
|
||||
method: method,
|
||||
headers: headers,
|
||||
url: url,
|
||||
version: version::Http11,
|
||||
headers_written: false,
|
||||
body: stream
|
||||
})
|
||||
}
|
||||
|
||||
fn write_head(&mut self) -> IoResult<()> {
|
||||
if self.headers_written {
|
||||
debug!("headers previsouly written, nooping");
|
||||
return Ok(());
|
||||
}
|
||||
self.headers_written = true;
|
||||
/// Create a new GET request.
|
||||
#[inline]
|
||||
pub fn get(url: Url) -> HttpResult<Request<Fresh>> { Request::new(Get, url) }
|
||||
|
||||
/// Create a new POST request.
|
||||
#[inline]
|
||||
pub fn post(url: Url) -> HttpResult<Request<Fresh>> { Request::new(Post, url) }
|
||||
|
||||
/// Create a new DELETE request.
|
||||
#[inline]
|
||||
pub fn delete(url: Url) -> HttpResult<Request<Fresh>> { Request::new(Delete, url) }
|
||||
|
||||
/// Create a new PUT request.
|
||||
#[inline]
|
||||
pub fn put(url: Url) -> HttpResult<Request<Fresh>> { Request::new(Put, url) }
|
||||
|
||||
/// Create a new PATCH request.
|
||||
#[inline]
|
||||
pub fn patch(url: Url) -> HttpResult<Request<Fresh>> { Request::new(Patch, url) }
|
||||
|
||||
/// Create a new HEAD request.
|
||||
#[inline]
|
||||
pub fn head(url: Url) -> HttpResult<Request<Fresh>> { Request::new(Head, url) }
|
||||
|
||||
/// Create a new OPTIONS request.
|
||||
#[inline]
|
||||
pub fn options(url: Url) -> HttpResult<Request<Fresh>> { Request::new(Options, url) }
|
||||
|
||||
/// Consume a Fresh Request, writing the headers and method,
|
||||
/// returning a Streaming Request.
|
||||
pub fn start(mut self) -> HttpResult<Request<Streaming>> {
|
||||
let uri = self.url.serialize_path().unwrap();
|
||||
debug!("writing head: {} {} {}", self.method, uri, self.version);
|
||||
try!(write!(self.body, "{} {} {}", self.method, uri, self.version))
|
||||
try!(self.body.write(LINE_ENDING));
|
||||
try_io!(write!(self.body, "{} {} {}", self.method, uri, self.version))
|
||||
try_io!(self.body.write(LINE_ENDING));
|
||||
|
||||
debug!("{}", self.headers);
|
||||
|
||||
for (name, header) in self.headers.iter() {
|
||||
try!(write!(self.body, "{}: {}", name, header));
|
||||
try!(self.body.write(LINE_ENDING));
|
||||
try_io!(write!(self.body, "{}: {}", name, header));
|
||||
try_io!(self.body.write(LINE_ENDING));
|
||||
}
|
||||
|
||||
self.body.write(LINE_ENDING)
|
||||
try_io!(self.body.write(LINE_ENDING));
|
||||
|
||||
Ok(Request {
|
||||
method: self.method,
|
||||
headers: self.headers,
|
||||
url: self.url,
|
||||
version: self.version,
|
||||
body: self.body
|
||||
})
|
||||
}
|
||||
|
||||
/// Get a mutable reference to the Request headers.
|
||||
#[inline]
|
||||
pub fn headers_mut(&mut self) -> &mut Headers { &mut self.headers }
|
||||
}
|
||||
|
||||
impl Request<Streaming> {
|
||||
/// Completes writing the request, and returns a response to read from.
|
||||
///
|
||||
/// Consumes the Request.
|
||||
@@ -89,19 +151,12 @@ impl Request {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Writer for Request {
|
||||
impl Writer for Request<Streaming> {
|
||||
fn write(&mut self, msg: &[u8]) -> IoResult<()> {
|
||||
if !self.headers_written {
|
||||
try!(self.write_head());
|
||||
}
|
||||
self.body.write(msg)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> IoResult<()> {
|
||||
if !self.headers_written {
|
||||
try!(self.write_head());
|
||||
}
|
||||
self.body.flush()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ extern crate intertwine;
|
||||
pub use std::io::net::ip::{SocketAddr, IpAddr, Ipv4Addr, Ipv6Addr, Port};
|
||||
pub use mimewrapper::mime;
|
||||
pub use url::Url;
|
||||
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;
|
||||
|
||||
Reference in New Issue
Block a user