diff --git a/README.md b/README.md index 265bcda..8f5361b 100644 --- a/README.md +++ b/README.md @@ -9,8 +9,9 @@ - [x] Header handling - [x] Transfer-Encoding: chunked - [x] Ergonomic JSON handling -- [ ] Auth headers - [ ] Test harness for end-to-end tests +- [ ] Limit read length on Content-Size +- [ ] Auth headers - [ ] Cookie jar in agent - [ ] Forms with application/x-www-form-urlencoded - [ ] multipart/form-data diff --git a/src/conn.rs b/src/conn.rs index 08d19e9..bd8cc9a 100644 --- a/src/conn.rs +++ b/src/conn.rs @@ -33,6 +33,7 @@ impl ConnectionPool { let mut stream = match url.scheme() { "http" => connect_http(request, &url), "https" => connect_https(request, &url), + "test" => connect_test(request, &url), _ => Err(Error::UnknownScheme(url.scheme().to_string())), }?; @@ -191,3 +192,14 @@ where } Ok(()) } + +#[cfg(not(test))] +fn connect_test(_request: &Request, url: &Url) -> Result { + Err(Error::UnknownScheme(url.scheme().to_string())) +} + +#[cfg(test)] +fn connect_test(request: &Request, url: &Url) -> Result { + use test; + test::resolve_handler(request, url) +} diff --git a/src/lib.rs b/src/lib.rs index 3754f1b..545a9ae 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,6 +20,9 @@ mod header; mod stream; mod util; +#[cfg(test)] +mod test; + pub use agent::{Agent, Request, Response}; pub use header::Header; @@ -125,7 +128,7 @@ where mod tests { use super::*; - #[test] + //#[test] fn connect_http_google() { let resp = get("http://www.google.com/").call(); println!("{:?}", resp); @@ -133,7 +136,7 @@ mod tests { assert_eq!("text/html", resp.content_type()); } - #[test] + //#[test] fn connect_https_google() { let resp = get("https://www.google.com/").call(); println!("{:?}", resp); diff --git a/src/stream.rs b/src/stream.rs index 30b46bd..01680ab 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -7,6 +7,7 @@ use std::net::TcpStream; pub enum Stream { Http(TcpStream), Https(rustls::ClientSession, TcpStream), + #[cfg(test)] Test(Box, Box), } impl Read for Stream { @@ -14,6 +15,7 @@ impl Read for Stream { match self { Stream::Http(sock) => sock.read(buf), Stream::Https(sess, sock) => rustls::Stream::new(sess, sock).read(buf), + #[cfg(test)] Stream::Test(reader, _) => reader.read(buf), } } } @@ -23,12 +25,14 @@ impl Write for Stream { match self { Stream::Http(sock) => sock.write(buf), Stream::Https(sess, sock) => rustls::Stream::new(sess, sock).write(buf), + #[cfg(test)] Stream::Test(_, writer) => writer.write(buf), } } fn flush(&mut self) -> Result<()> { match self { Stream::Http(sock) => sock.flush(), Stream::Https(sess, sock) => rustls::Stream::new(sess, sock).flush(), + #[cfg(test)] Stream::Test(_, writer) => writer.flush(), } } } diff --git a/src/test/mod.rs b/src/test/mod.rs new file mode 100644 index 0000000..e5a73ea --- /dev/null +++ b/src/test/mod.rs @@ -0,0 +1,52 @@ +use agent::Request; +use error::Error; +use header::Header; +use std::collections::HashMap; +use std::io::Write; +use std::sync::{Arc, Mutex}; +use stream::Stream; +use url::Url; +use util::vecread::VecRead; + +mod simple; + +type RequestHandler = Fn(&Request, &Url) -> Result + Send + 'static; + +lazy_static! { + pub static ref TEST_HANDLERS: Arc>>> = + { Arc::new(Mutex::new(HashMap::new())) }; +} + +pub fn set_handler(path: &str, handler: H) +where + H: Fn(&Request, &Url) -> Result + Send + 'static, +{ + let mut handlers = TEST_HANDLERS.lock().unwrap(); + handlers.insert(path.to_string(), Box::new(handler)); +} + +pub fn make_stream( + status: u16, + status_text: &str, + headers: Vec<&str>, + mut body: Vec, +) -> Result { + let mut buf: Vec = vec![]; + write!(&mut buf, "HTTP/1.1 {} {}\r\n", status, status_text).ok(); + for hstr in headers.iter() { + let header = hstr.parse::
().unwrap(); + write!(&mut buf, "{}: {}\r\n", header.name(), header.value()).ok(); + } + write!(&mut buf, "\r\n").ok(); + buf.append(&mut body); + let read = VecRead::from_vec(buf); + let write: Vec = vec![]; + Ok(Stream::Test(Box::new(read), Box::new(write))) +} + +pub fn resolve_handler(req: &Request, url: &Url) -> Result { + let mut handlers = TEST_HANDLERS.lock().unwrap(); + let path = url.path(); + let handler = handlers.remove(path).unwrap(); + handler(req, url) +} diff --git a/src/test/simple.rs b/src/test/simple.rs new file mode 100644 index 0000000..7631fb2 --- /dev/null +++ b/src/test/simple.rs @@ -0,0 +1,25 @@ +use super::super::*; +use test; + +#[test] +fn header_passing() { + test::set_handler("/header_passing", |req, _url| { + assert!(req.has("X-Foo")); + assert_eq!(req.get("X-Foo").unwrap(), "bar"); + test::make_stream(200, "OK", vec!["X-Bar: foo"], vec![]) + }); + let resp = get("test://host/header_passing").set("X-Foo", "bar").call(); + assert_eq!(*resp.status(), 200); + assert!(resp.has("X-Bar")); + assert_eq!(resp.get("X-Bar").unwrap(), "foo"); +} + +#[test] +fn body_as_text() { + test::set_handler("/body_as_text", |_req, _url| { + test::make_stream(200, "OK", vec![], "Hello World!".to_string().into_bytes()) + }); + let resp = get("test://host/body_as_text").call(); + let text = resp.into_string().unwrap(); + assert_eq!(text, "Hello World!"); +} diff --git a/src/util/mod.rs b/src/util/mod.rs index c69aa90..2c4b99e 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -1,7 +1,7 @@ #[allow(dead_code)] mod macros; mod serde_macros; -mod vecread; +pub mod vecread; use base64; use mime_guess::get_mime_type_str;