refactor to body
This commit is contained in:
70
src/agent.rs
70
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::<Response>().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::<Response>().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::<Response>().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::<Response>().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::<Response>().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::<Response>().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::<Response>().unwrap();
|
||||
let v = resp.into_json().unwrap();
|
||||
assert_eq!(
|
||||
v,
|
||||
"{\"hello\":\"world\"}"
|
||||
.parse::<serde_json::Value>()
|
||||
.unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_borked_header() {
|
||||
let s = format!("HTTP/1.1 BORKED\r\n");
|
||||
let resp: Response = s.parse::<Response>().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");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
98
src/body.rs
Normal file
98
src/body.rs
Normal file
@@ -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<Read + 'static>),
|
||||
}
|
||||
|
||||
impl Default for Payload {
|
||||
fn default() -> Payload {
|
||||
Payload::Empty
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SizedReader {
|
||||
pub size: Option<usize>,
|
||||
pub reader: Box<Read + 'static>,
|
||||
}
|
||||
|
||||
impl SizedReader {
|
||||
fn new(size: Option<usize>, reader: Box<Read + 'static>) -> 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<R, W>(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(())
|
||||
}
|
||||
@@ -107,6 +107,7 @@ extern crate native_tls;
|
||||
extern crate serde_json;
|
||||
|
||||
mod agent;
|
||||
mod body;
|
||||
mod conn;
|
||||
mod error;
|
||||
mod header;
|
||||
|
||||
@@ -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<Read + 'static>),
|
||||
}
|
||||
|
||||
impl Default for Payload {
|
||||
fn default() -> Payload {
|
||||
Payload::Empty
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SizedReader {
|
||||
pub size: Option<usize>,
|
||||
pub reader: Box<Read + 'static>,
|
||||
}
|
||||
|
||||
impl SizedReader {
|
||||
fn new(size: Option<usize>, reader: Box<Read + 'static>) -> 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 {
|
||||
|
||||
@@ -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<Stream, Error> {
|
||||
pub fn connect_https(unit: &Unit) -> Result<Stream, Error> {
|
||||
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<R, W>(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(())
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user