diff --git a/benches/client.rs b/benches/client.rs index 805b3daa..59380b56 100644 --- a/benches/client.rs +++ b/benches/client.rs @@ -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] diff --git a/benches/server.rs b/benches/server.rs index d3ce62f4..5d5f0c11 100644 --- a/benches/server.rs +++ b/benches/server.rs @@ -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) { diff --git a/examples/client.rs b/examples/client.rs index f436eacb..e728cd6c 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -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()) { diff --git a/src/client/mod.rs b/src/client/mod.rs index 4a8d0e3b..1b24c7b7 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -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(Get, url) -} - -/// Create a HEAD client request. -pub fn head(url: Url) -> HttpResult { - request(Head, url) -} - -/// Create a POST client request. -pub fn post(url: Url) -> HttpResult { - // 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(Delete, url) -} - -/// Create a client request. -pub fn request(method: Method, url: Url) -> HttpResult { - Request::new(method, url) -} diff --git a/src/client/request.rs b/src/client/request.rs index 89ea0131..7b575631 100644 --- a/src/client/request.rs +++ b/src/client/request.rs @@ -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 { /// The target URI for this request. pub url: Url, + /// The HTTP version of this request. pub version: version::HttpVersion, - headers_written: bool, + body: BufferedWriter>, + headers: Headers, + method: method::Method, } -impl Request { +impl Request { + /// 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 { /// Create a new client request. - pub fn new(method: method::Method, url: Url) -> HttpResult { + pub fn new(method: method::Method, url: Url) -> HttpResult> { 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::new(Get, url) } + /// Create a new POST request. + #[inline] + pub fn post(url: Url) -> HttpResult> { Request::new(Post, url) } + + /// Create a new DELETE request. + #[inline] + pub fn delete(url: Url) -> HttpResult> { Request::new(Delete, url) } + + /// Create a new PUT request. + #[inline] + pub fn put(url: Url) -> HttpResult> { Request::new(Put, url) } + + /// Create a new PATCH request. + #[inline] + pub fn patch(url: Url) -> HttpResult> { Request::new(Patch, url) } + + /// Create a new HEAD request. + #[inline] + pub fn head(url: Url) -> HttpResult> { Request::new(Head, url) } + + /// Create a new OPTIONS request. + #[inline] + pub fn options(url: Url) -> HttpResult> { Request::new(Options, url) } + + /// Consume a Fresh Request, writing the headers and method, + /// returning a Streaming Request. + pub fn start(mut self) -> HttpResult> { 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 { /// 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 { 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() } } diff --git a/src/lib.rs b/src/lib.rs index c74b3c61..a4d26cba 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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;