use hyper::{
self,
buffer::BufReader,
client::{
Body,
Client,
IntoUrl,
},
error::ParseError,
net::NetworkStream,
Url,
};
use iron::{
method::Method,
middleware::BeforeMiddleware,
headers::{
self,
Header,
HeaderFormat
},
Headers,
Request,
Response,
IronResult,
request::HttpRequest,
Protocol,
};
use std::{
io::Cursor,
net::{
IpAddr,
Ipv4Addr,
SocketAddr,
},
};
use url::Host;
mod transfer_connector;
use transfer_connector::TransferConnector;
mod response_examine;
pub use response_examine::ResponseExamine;
pub struct RequestBuilder {
body: Option<Vec<u8>>,
headers: Headers,
method: Method,
url: Url,
client_address: SocketAddr,
chain: Vec<Box<dyn BeforeMiddleware>>,
}
impl Default for RequestBuilder {
fn default() -> Self {
Self::new(Method::Get, "http://127.0.0.1:8080/").unwrap()
}
}
impl RequestBuilder {
pub fn new<U: IntoUrl>(method: Method, url: U) -> Result<RequestBuilder, ParseError> {
let mut h = Headers::new();
h.set(headers::UserAgent("anneal".into()));
Ok(RequestBuilder {
body: None,
headers: h,
method,
url: url.into_url()?,
client_address: "127.0.0.1:3000".parse().unwrap(),
chain: Vec::new(),
})
}
pub fn get<U: IntoUrl>(url: U) -> Result<RequestBuilder, ParseError> {
Self::new(Method::Get, url)
}
pub fn post<U: IntoUrl>(url: U) -> Result<RequestBuilder, ParseError> {
Self::new(Method::Post, url)
}
pub fn options<U: IntoUrl>(url: U) -> Result<RequestBuilder, ParseError> {
Self::new(Method::Options, url)
}
pub fn put<U: IntoUrl>(url: U) -> Result<RequestBuilder, ParseError> {
Self::new(Method::Put, url)
}
pub fn delete<U: IntoUrl>(url: U) -> Result<RequestBuilder, ParseError> {
Self::new(Method::Delete, url)
}
pub fn head<U: IntoUrl>(url: U) -> Result<RequestBuilder, ParseError> {
Self::new(Method::Head, url)
}
pub fn trace<U: IntoUrl>(url: U) -> Result<RequestBuilder, ParseError> {
Self::new(Method::Trace, url)
}
pub fn connect<U: IntoUrl>(url: U) -> Result<RequestBuilder, ParseError> {
Self::new(Method::Connect, url)
}
pub fn patch<U: IntoUrl>(url: U) -> Result<RequestBuilder, ParseError> {
Self::new(Method::Patch, url)
}
pub fn default_post() -> Self {
Self::new(Method::Post, "http://127.0.0.1:8080/").unwrap()
}
pub fn set_header<H: Header + HeaderFormat>(&mut self, header: H) -> &mut Self {
self.headers.set(header);
self
}
pub fn set_headers(&mut self, headers: Headers) -> &mut Self {
self.headers = headers;
self
}
pub fn set_url<U: IntoUrl>(&mut self, url: U) -> Result<&mut Self, ParseError> {
self.url = url.into_url()?;
Ok(self)
}
pub fn set_method(&mut self, method: Method) -> &mut Self {
self.method = method;
self
}
pub fn set_bytes(&mut self, body: Vec<u8>) -> &mut Self {
self.body = Some(body);
self
}
pub fn set_string(&mut self, body: &str) -> &mut Self {
self.body = Some(body.as_bytes().to_vec());
self
}
pub fn add_middleware<B: BeforeMiddleware + 'static>(&mut self, middleware: B) -> &mut Self {
self.chain.push(Box::new(middleware));
self
}
pub fn set_chain(&mut self, chain: Vec<Box<dyn BeforeMiddleware>>) -> &mut Self {
self.chain = chain;
self
}
#[cfg(feature = "cookies")]
pub fn set_cookies(&mut self, cookies: &cookie::CookieJar) -> &mut Self {
let cookies : Vec<String> = cookies.iter()
.map(|c| c.to_string())
.collect();
if cookies.len() != 0 { self.headers.set::<headers::Cookie>(headers::Cookie(cookies)); }
self
}
#[cfg(feature = "json")]
pub fn set_json<T: serde::Serialize>(&mut self, json: &T) -> &mut Self {
self.body = Some(serde_json::to_vec(json).unwrap());
self.headers.set::<headers::ContentType>(headers::ContentType::json());
self
}
#[cfg(feature = "jsonapi")]
fn jsonapi_mime() -> iron::mime::Mime {
use iron::mime;
mime::Mime(
mime::TopLevel::Application,
mime::SubLevel::Ext(String::from("vnd.api+json")),
Vec::new())
}
#[cfg(feature = "jsonapi")]
pub fn set_document(&mut self, doc: &japi::Document) -> &mut Self {
self.body = Some(serde_json::to_vec(&doc).unwrap());
self.headers.set::<headers::ContentType>(headers::ContentType(Self::jsonapi_mime()));
self
}
#[cfg(feature = "jsonapi")]
pub fn set_primary_data(&mut self, obj: japi::GenericObject) -> &mut Self {
let document = japi::Document {
data: japi::OptionalVec::One(Some(obj)),
..Default::default()
};
self.body = Some(serde_json::to_vec(&document).unwrap());
self.headers.set::<headers::ContentType>(headers::ContentType(Self::jsonapi_mime()));
self
}
pub fn request<T, H: FnOnce(Request) -> T>(&self, handler: H) -> T {
let tc = TransferConnector::new("127.0.0.1:3000".parse().unwrap());
let client = Client::with_connector(tc.clone());
let rb = client.request(self.method.clone(), self.url.clone())
.headers(self.headers.clone());
let rb = if let Some(b) = &self.body {
rb.body(Body::BufBody(&b[..], b.len()))
} else { rb };
rb.send();
let proto = match tc.scheme.lock().expect("proto scheme lock")
.as_ref().expect("proto scheme not set").as_str() {
"http" => Protocol::http(),
"https" => Protocol::https(),
s => panic!("Unknown Scheme: {}", s),
};
let mut serv_stream = tc.server_stream();
let mut buff_read = BufReader::new(&mut serv_stream as &mut NetworkStream);
let http_request = HttpRequest::new(&mut buff_read, tc.client_address)
.expect("build http request");
let mut r = Request::from_http(
http_request,
tc.server_address.lock().expect("server address lock").clone()
.expect("server address not set"),
&proto).expect("build request");
for middleware in &self.chain { middleware.before(&mut r).unwrap(); }
handler(r)
}
pub fn request_response<H: FnOnce(Request) -> IronResult<Response>>(&self, handler: H)
-> IronResult<ResponseExamine> {
self.request(handler).map(|r| ResponseExamine::new(r))
}
}
#[cfg(test)]
mod test {
use super::*;
use hyper::version::HttpVersion;
use std::io::Read;
#[test]
fn base() {
RequestBuilder::new(Method::Get, "http://127.0.0.1:8080/").unwrap().request(|req| {
let mut headers = Headers::new();
headers.set(headers::Host{hostname: "127.0.0.1".into(), port: Some(8080)});
headers.set(headers::UserAgent("anneal".into()));
assert_eq!(req.headers, headers);
assert_eq!(req.method, Method::Get);
assert_eq!(req.url, "http://127.0.0.1:8080/".parse().unwrap());
assert_eq!(req.local_addr, "127.0.0.1:8080".parse().unwrap());
assert_eq!(req.remote_addr, "127.0.0.1:3000".parse().unwrap());
assert_eq!(req.body.bytes().count(), 0);
assert_eq!(req.version, HttpVersion::Http11);
});
}
#[test]
fn full() {
RequestBuilder::new(Method::Post, "https://127.0.0.1:8080/").unwrap()
.set_header(headers::ContentType::json())
.set_string("this is a body".into())
.request(|mut req| {
let mut headers = Headers::new();
headers.set(headers::ContentLength(14));
headers.set(headers::UserAgent("anneal".into()));
headers.set(headers::Host{hostname: "127.0.0.1".into(), port: Some(8080)});
headers.set(headers::ContentType::json());
assert_eq!(req.headers, headers);
assert_eq!(req.method, Method::Post);
assert_eq!(req.url, "https://127.0.0.1:8080/".parse().unwrap());
assert_eq!(req.local_addr, "127.0.0.1:8080".parse().unwrap());
assert_eq!(req.remote_addr, "127.0.0.1:3000".parse().unwrap());
assert_eq!(req.version, HttpVersion::Http11);
let mut s = String::new();
req.body.read_to_string(&mut s).unwrap();
assert_eq!(s, "this is a body");
})
}
#[test]
fn constructors() {
fn assert_kind(rb: RequestBuilder, method: Method) {
rb.request(|req| {
assert_eq!(req.url, "http://127.0.0.1:8080/".parse().unwrap());
assert_eq!(req.method, method);
});
}
assert_kind(RequestBuilder::default(), Method::Get);
assert_kind(RequestBuilder::default_post(), Method::Post);
assert_kind(RequestBuilder::options("http://127.0.0.1:8080/").unwrap(), Method::Options);
assert_kind(RequestBuilder::get("http://127.0.0.1:8080/").unwrap(), Method::Get);
assert_kind(RequestBuilder::post("http://127.0.0.1:8080/").unwrap(), Method::Post);
assert_kind(RequestBuilder::put("http://127.0.0.1:8080/").unwrap(), Method::Put);
assert_kind(RequestBuilder::delete("http://127.0.0.1:8080/").unwrap(), Method::Delete);
assert_kind(RequestBuilder::head("http://127.0.0.1:8080/").unwrap(), Method::Head);
assert_kind(RequestBuilder::trace("http://127.0.0.1:8080/").unwrap(), Method::Trace);
assert_kind(RequestBuilder::connect("http://127.0.0.1:8080/").unwrap(), Method::Connect);
assert_kind(RequestBuilder::patch("http://127.0.0.1:8080/").unwrap(), Method::Patch);
}
#[test]
fn middleware() {
pub struct TestMiddleware(u8);
impl iron::typemap::Key for TestMiddleware { type Value = Vec<u8>; }
impl BeforeMiddleware for TestMiddleware {
fn before(&self, req: &mut Request) -> IronResult<()> {
if let Some(mut tm) = req.extensions.get_mut::<TestMiddleware>() {
tm.push(self.0);
} else { req.extensions.insert::<TestMiddleware>(vec![self.0]); }
Ok(())
}
}
RequestBuilder::default()
.add_middleware(TestMiddleware(0))
.set_chain(vec![Box::new(TestMiddleware(1)), Box::new(TestMiddleware(2))])
.add_middleware(TestMiddleware(3))
.request(|req| {
assert_eq!(req.extensions.get::<TestMiddleware>().unwrap(), &vec![1,2,3]);
});
}
#[cfg(feature = "cookies")]
#[test]
fn cookies() {
use cookie::{CookieJar, Cookie};
let mut jar = CookieJar::new();
jar.add(Cookie::new("a", "b"));
jar.add(Cookie::new("c", "d"));
RequestBuilder::new(Method::Get, "https://127.0.0.1:8080/").unwrap()
.set_cookies(&jar)
.request(|req| {
let cookies = req.headers.get::<headers::Cookie>().unwrap();
assert_eq!(cookies.len(), 2);
for cookie in cookies.iter() {
let c = Cookie::parse(cookie).unwrap();
assert_eq!(&c, jar.get(c.name()).unwrap());
}
})
}
#[cfg(feature = "json")]
#[test]
fn json() {
RequestBuilder::new(Method::Post, "https://127.0.0.1:8080/").unwrap()
.set_json(&serde_json::json!({"a": "b"}))
.request(|mut req| {
assert_eq!(req.headers.get::<headers::ContentType>().unwrap(),
&headers::ContentType::json());
let mut s = String::new();
req.body.read_to_string(&mut s).unwrap();
assert_eq!(s, "{\"a\":\"b\"}");
})
}
#[cfg(feature = "jsonapi")]
#[test]
fn jsonapi_document() {
use japi::{
Document,
OptionalVec,
Identifier,
};
RequestBuilder::new(Method::Post, "https://127.0.0.1:8080/").unwrap()
.set_document(&Document {
data: OptionalVec::One(Some(Identifier::new("a".into(), "b".into()).into())),
..Default::default()
})
.request(|mut req| {
assert_eq!(req.headers.get::<headers::ContentType>().unwrap(),
&headers::ContentType(RequestBuilder::jsonapi_mime()));
let mut s = String::new();
req.body.read_to_string(&mut s).unwrap();
assert_eq!(s, "{\"data\":{\"id\":\"a\",\"type\":\"b\"}}");
})
}
#[cfg(feature = "jsonapi")]
#[test]
fn jsonapi_object() {
RequestBuilder::new(Method::Post, "https://127.0.0.1:8080/").unwrap()
.set_primary_data(japi::Identifier::new("a".into(), "b".into()).into())
.request(|mut req| {
assert_eq!(req.headers.get::<headers::ContentType>().unwrap(),
&headers::ContentType(RequestBuilder::jsonapi_mime()));
let mut s = String::new();
req.body.read_to_string(&mut s).unwrap();
assert_eq!(s, "{\"data\":{\"id\":\"a\",\"type\":\"b\"}}");
})
}
}