diff --git a/src/agent.rs b/src/agent.rs index ebd0dfe..8c4c4c0 100644 --- a/src/agent.rs +++ b/src/agent.rs @@ -357,74 +357,4 @@ mod tests { }); } - //////////////////// RESPONSE TESTS ///////////////////////////// - - #[test] - fn content_type_without_charset() { - let s = "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\nOK"; - let resp = s.parse::().unwrap(); - assert_eq!("application/json", resp.content_type()); - } - - #[test] - fn content_type_with_charset() { - let s = "HTTP/1.1 200 OK\r\nContent-Type: application/json; charset=iso-8859-4\r\n\r\nOK"; - let resp = s.parse::().unwrap(); - assert_eq!("application/json", resp.content_type()); - } - - #[test] - fn content_type_default() { - let s = "HTTP/1.1 200 OK\r\n\r\nOK"; - let resp = s.parse::().unwrap(); - assert_eq!("text/plain", resp.content_type()); - } - - #[test] - fn charset() { - let s = "HTTP/1.1 200 OK\r\nContent-Type: application/json; charset=iso-8859-4\r\n\r\nOK"; - let resp = s.parse::().unwrap(); - assert_eq!("iso-8859-4", resp.charset()); - } - - #[test] - fn charset_default() { - let s = "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\nOK"; - let resp = s.parse::().unwrap(); - assert_eq!("utf-8", resp.charset()); - } - - #[test] - fn chunked_transfer() { - let s = "HTTP/1.1 200 OK\r\nTransfer-Encoding: Chunked\r\n\r\n3\r\nhel\r\nb\r\nlo world!!!\r\n0\r\n\r\n"; - let resp = s.parse::().unwrap(); - assert_eq!("hello world!!!", resp.into_string().unwrap()); - } - - #[test] - #[cfg(feature = "json")] - fn parse_simple_json() { - let s = format!("HTTP/1.1 200 OK\r\n\r\n{{\"hello\":\"world\"}}"); - let resp = s.parse::().unwrap(); - let v = resp.into_json().unwrap(); - assert_eq!( - v, - "{\"hello\":\"world\"}" - .parse::() - .unwrap() - ); - } - - #[test] - fn parse_borked_header() { - let s = format!("HTTP/1.1 BORKED\r\n"); - let resp: Response = s.parse::().unwrap_err().into(); - assert_eq!(resp.http_version(), "HTTP/1.1"); - assert_eq!(*resp.status(), 500); - assert_eq!(resp.status_text(), "Bad Status"); - assert_eq!(resp.content_type(), "text/plain"); - let v = resp.into_string().unwrap(); - assert_eq!(v, "Bad Status\n"); - } - } diff --git a/src/body.rs b/src/body.rs new file mode 100644 index 0000000..522514a --- /dev/null +++ b/src/body.rs @@ -0,0 +1,98 @@ +use chunked_transfer; +use std::io::{empty, Cursor, Read, Result as IoResult, Write}; +use stream::Stream; + +#[cfg(feature = "charset")] +use encoding::label::encoding_from_whatwg_label; +#[cfg(feature = "charset")] +use encoding::EncoderTrap; +#[cfg(feature = "charset")] +use response::DEFAULT_CHARACTER_SET; + +#[cfg(feature = "json")] +use super::SerdeValue; +#[cfg(feature = "json")] +use serde_json; + +const CHUNK_SIZE: usize = 1024 * 1024; + +pub enum Payload { + Empty, + Text(String, String), + #[cfg(feature = "json")] + JSON(SerdeValue), + Reader(Box), +} + +impl Default for Payload { + fn default() -> Payload { + Payload::Empty + } +} + +pub struct SizedReader { + pub size: Option, + pub reader: Box, +} + +impl SizedReader { + fn new(size: Option, reader: Box) -> Self { + SizedReader { size, reader } + } +} + +impl Payload { + pub fn into_read(self) -> SizedReader { + match self { + Payload::Empty => SizedReader::new(None, Box::new(empty())), + Payload::Text(text, _charset) => { + #[cfg(feature = "charset")] + let bytes = { + let encoding = encoding_from_whatwg_label(&_charset) + .or_else(|| encoding_from_whatwg_label(DEFAULT_CHARACTER_SET)) + .unwrap(); + encoding.encode(&text, EncoderTrap::Replace).unwrap() + }; + #[cfg(not(feature = "charset"))] + let bytes = text.into_bytes(); + let len = bytes.len(); + let cursor = Cursor::new(bytes); + SizedReader::new(Some(len), Box::new(cursor)) + } + #[cfg(feature = "json")] + Payload::JSON(v) => { + let bytes = serde_json::to_vec(&v).expect("Bad JSON in payload"); + let len = bytes.len(); + let cursor = Cursor::new(bytes); + SizedReader::new(Some(len), Box::new(cursor)) + } + Payload::Reader(read) => SizedReader::new(None, read), + } + } +} + +pub fn send_body(body: SizedReader, do_chunk: bool, stream: &mut Stream) -> IoResult<()> { + if do_chunk { + pipe(body.reader, chunked_transfer::Encoder::new(stream))?; + } else { + pipe(body.reader, stream)?; + } + + Ok(()) +} + +fn pipe(mut reader: R, mut writer: W) -> IoResult<()> +where + R: Read, + W: Write, +{ + let mut buf = [0_u8; CHUNK_SIZE]; + loop { + let len = reader.read(&mut buf)?; + if len == 0 { + break; + } + writer.write_all(&buf[0..len])?; + } + Ok(()) +} diff --git a/src/lib.rs b/src/lib.rs index 3b7b712..bdc709d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -107,6 +107,7 @@ extern crate native_tls; extern crate serde_json; mod agent; +mod body; mod conn; mod error; mod header; diff --git a/src/request.rs b/src/request.rs index 4491a96..8b29b41 100644 --- a/src/request.rs +++ b/src/request.rs @@ -1,21 +1,9 @@ use qstring::QString; -use std::io::empty; -use std::io::Cursor; use std::io::Read; use std::sync::Arc; - #[cfg(feature = "json")] use super::SerdeValue; -#[cfg(feature = "json")] -use serde_json; - -#[cfg(feature = "charset")] -use encoding::label::encoding_from_whatwg_label; -#[cfg(feature = "charset")] -use encoding::EncoderTrap; -#[cfg(feature = "charset")] -use response::DEFAULT_CHARACTER_SET; lazy_static! { static ref URL_BASE: Url = { Url::parse("http://localhost/").expect("Failed to parse URL_BASE") }; @@ -60,59 +48,6 @@ impl ::std::fmt::Debug for Request { } } -enum Payload { - Empty, - Text(String, String), - #[cfg(feature = "json")] JSON(SerdeValue), - Reader(Box), -} - -impl Default for Payload { - fn default() -> Payload { - Payload::Empty - } -} - -pub struct SizedReader { - pub size: Option, - pub reader: Box, -} - -impl SizedReader { - fn new(size: Option, reader: Box) -> Self { - SizedReader { size, reader } - } -} - -impl Payload { - fn into_read(self) -> SizedReader { - match self { - Payload::Empty => SizedReader::new(None, Box::new(empty())), - Payload::Text(text, _charset) => { - #[cfg(feature = "charset")] - let bytes = { - let encoding = encoding_from_whatwg_label(&_charset) - .or_else(|| encoding_from_whatwg_label(DEFAULT_CHARACTER_SET)) - .unwrap(); - encoding.encode(&text, EncoderTrap::Replace).unwrap() - }; - #[cfg(not(feature = "charset"))] - let bytes = text.into_bytes(); - let len = bytes.len(); - let cursor = Cursor::new(bytes); - SizedReader::new(Some(len), Box::new(cursor)) - } - #[cfg(feature = "json")] - Payload::JSON(v) => { - let bytes = serde_json::to_vec(&v).expect("Bad JSON in payload"); - let len = bytes.len(); - let cursor = Cursor::new(bytes); - SizedReader::new(Some(len), Box::new(cursor)) - } - Payload::Reader(read) => SizedReader::new(None, read), - } - } -} impl Request { fn new(agent: &Agent, method: String, path: String) -> Request { diff --git a/src/stream.rs b/src/stream.rs index 8c5641e..4da1e87 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -1,5 +1,4 @@ -use agent::{SizedReader, Unit}; -use chunked_transfer; +use agent::Unit; use error::Error; use std::io::{Cursor, Read, Result as IoResult, Write}; use std::net::SocketAddr; @@ -10,8 +9,6 @@ use std::time::Duration; #[cfg(feature = "tls")] use native_tls::TlsStream; -const CHUNK_SIZE: usize = 1024 * 1024; - pub enum Stream { Http(TcpStream), #[cfg(feature = "tls")] @@ -139,29 +136,3 @@ pub fn connect_test(unit: &Unit) -> Result { pub fn connect_https(unit: &Unit) -> Result { Err(Error::UnknownScheme(unit.url.scheme().to_string())) } - -pub fn send_body(body: SizedReader, do_chunk: bool, stream: &mut Stream) -> IoResult<()> { - if do_chunk { - pipe(body.reader, chunked_transfer::Encoder::new(stream))?; - } else { - pipe(body.reader, stream)?; - } - - Ok(()) -} - -fn pipe(mut reader: R, mut writer: W) -> IoResult<()> -where - R: Read, - W: Write, -{ - let mut buf = [0_u8; CHUNK_SIZE]; - loop { - let len = reader.read(&mut buf)?; - if len == 0 { - break; - } - writer.write_all(&buf[0..len])?; - } - Ok(()) -} diff --git a/src/unit.rs b/src/unit.rs index b61f8d2..d70ccf3 100644 --- a/src/unit.rs +++ b/src/unit.rs @@ -1,6 +1,7 @@ use url::Url; -use stream::{connect_http, connect_https, connect_test, send_body}; +use stream::{connect_http, connect_https, connect_test}; use std::io::Write; +use body::{Payload, SizedReader, send_body}; // pub struct Unit {