use iron::{
response::{Response, WriteBody},
headers::{Header, HeaderFormat, ContentType, Cookie},
mime::{Mime, TopLevel, SubLevel},
Headers,
typemap::{Key, TypeMap},
status::Status,
};
use std::{
error::Error,
fmt::{Display, Formatter, Result as FmtResult},
string::FromUtf8Error,
};
pub struct ResponseExamine {
body: Option<Vec<u8>>,
response: Response,
}
impl ResponseExamine {
pub fn new(mut response: Response) -> ResponseExamine {
let body = response.body.as_mut().map(|mut body| {
let mut data = Vec::new();
body.write_body(&mut data).expect("write_all");
data.shrink_to_fit();
data
});
ResponseExamine {
body,
response,
}
}
pub fn get_bytes(&self) -> Option<&[u8]> {
if let Some(b) = &self.body { Some(b.as_slice()) } else { None }
}
fn get_body_error(&self) -> Result<&Vec<u8>, ResponseError> {
self.body.as_ref().ok_or(ResponseError::NoBody)
}
pub fn get_string(&self) -> Result<String, ResponseError> {
self.get_body_error().and_then(|v| Ok(String::from_utf8(v.clone())?))
}
pub fn get_header<H: Header + HeaderFormat>(&self) -> Option<&H> {
self.response.headers.get::<H>()
}
pub fn get_headers(&self) -> &Headers {
&self.response.headers
}
pub fn get_extension<K: Key>(&self) -> Option<&K::Value> {
self.response.extensions.get::<K>()
}
pub fn get_extensions(&self) -> &TypeMap {
&self.response.extensions
}
pub fn get_status(&self) -> Option<&Status> {
self.response.status.as_ref()
}
pub fn is_success(&self) -> bool {
self.response.status.map_or(false, |s| s.is_success())
}
#[cfg(feature = "json")]
pub fn get_json<T: serde::de::DeserializeOwned>(&self) -> Result<T, ResponseError> {
self.get_header::<ContentType>().map_or(Ok(()), |got| {
let expected = ContentType::json();
if *got == expected { Ok(()) }
else { Err(ResponseError::BadContentType { expected, got: got.clone() }) }
})?;
self.get_body_error().and_then(|v| Ok(serde_json::from_slice(v.as_slice())?))
}
#[cfg(feature = "jsonapi")]
pub fn get_document(&self) -> Result<japi::Document, ResponseError> {
self.get_header::<ContentType>().map_or(Err(ResponseError::NoContentType), |got| {
let expected = ContentType(Mime(
TopLevel::Application,
SubLevel::Ext(String::from("vnd.api+json")),
Vec::new()));
if *got == expected { Ok(()) }
else { Err(ResponseError::BadContentType { expected, got: got.clone() }) }
})?;
self.get_body_error().and_then(|v| Ok(serde_json::from_slice(v.as_slice())?))
}
#[cfg(feature = "jsonapi")]
pub fn get_primary_data(&self) -> Result<japi::GenericObject, ResponseError> {
use japi::OptionalVec;
let document = self.get_document()?;
match document.data {
OptionalVec::One(Some(d)) => Ok(d),
OptionalVec::Many(_) => Err(ResponseError::MultipleData),
_ => Err(ResponseError::NoData),
}
}
#[cfg(feature = "cookies")]
pub fn get_cookies(&self) -> Result<cookie::CookieJar, ResponseError> {
let mut jar = cookie::CookieJar::new();
if let Some(cookies) = self.get_header::<Cookie>() {
for c in cookies.iter() {
jar.add(cookie::Cookie::parse(c)?.into_owned());
}
Ok(jar)
} else { Err(ResponseError::NoCookies) }
}
}
#[derive(Debug)]
pub enum ResponseError {
NoBody,
StringConversion(FromUtf8Error),
#[cfg(any(feature = "json", feature = "jsonapi"))]
JsonError(serde_json::error::Error),
#[cfg(any(feature = "json", feature = "jsonapi"))]
BadContentType{ expected: ContentType, got: ContentType },
#[cfg(feature = "jsonapi")]
NoContentType,
#[cfg(feature = "jsonapi")]
JsonApiError(japi::ObjectConversionError),
#[cfg(feature = "jsonapi")]
MultipleData,
#[cfg(feature = "jsonapi")]
NoData,
#[cfg(feature = "cookies")]
CookieError(cookie::ParseError),
#[cfg(feature = "cookies")]
NoCookies,
}
impl Error for ResponseError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
ResponseError::StringConversion(e) => Some(e),
#[cfg(feature = "json")]
ResponseError::JsonError(e) => Some(e),
#[cfg(feature = "jsonapi")]
ResponseError::JsonApiError(e) => Some(e),
#[cfg(feature = "cookies")]
ResponseError::CookieError(e) => Some(e),
_ => None,
}
}
}
impl Display for ResponseError {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
write!(f, "Response Error: ")?;
match self {
ResponseError::NoBody => write!(f, "the response has no body"),
ResponseError::StringConversion(e) =>
write!(f, "couldn't convert the body to a string ({})", e),
#[cfg(any(feature = "json", feature = "jsonapi"))]
ResponseError::JsonError(e) =>
write!(f, "couldn't convert the body to a json object ({})", e),
#[cfg(any(feature = "json", feature = "jsonapi"))]
ResponseError::BadContentType{ expected, got } =>
write!(f, "bad content type (expected '{}', got '{}')", expected, got),
#[cfg(feature = "jsonapi")]
ResponseError::NoContentType => write!(f, "there was no content type header"),
#[cfg(feature = "jsonapi")]
ResponseError::JsonApiError(e) =>
write!(f, "couldn't convert the body to a json:api object ({})", e),
#[cfg(feature = "jsonapi")]
ResponseError::MultipleData =>
write!(f, "the document contains multiple primary data entries"),
#[cfg(feature = "jsonapi")]
ResponseError::NoData => write!(f, "the document contains no primary data"),
#[cfg(feature = "cookies")]
ResponseError::CookieError(e) =>
write!(f, "couldn't convert cookies ({})", e),
#[cfg(feature = "cookies")]
ResponseError::NoCookies =>
write!(f, "the 'Cookie' header was not set"),
}
}
}
impl From<FromUtf8Error> for ResponseError {
fn from(e: FromUtf8Error) -> Self {
ResponseError::StringConversion(e)
}
}
#[cfg(any(feature = "json", feature = "jsonapi"))]
impl From<serde_json::error::Error> for ResponseError {
fn from(e: serde_json::error::Error) -> Self {
ResponseError::JsonError(e)
}
}
#[cfg(feature = "jsonapi")]
impl From<japi::ObjectConversionError> for ResponseError {
fn from(e: japi::ObjectConversionError) -> Self {
ResponseError::JsonApiError(e)
}
}
#[cfg(feature = "cookies")]
impl From<cookie::ParseError> for ResponseError {
fn from(e: cookie::ParseError) -> Self {
ResponseError::CookieError(e)
}
}
mod test {
use crate::RequestBuilder;
use iron::{
method::Method,
headers::Expect,
prelude::*,
modifiers::Header as ModHead,
};
use super::*;
pub struct TestExtension;
impl Key for TestExtension { type Value = u8; }
#[test]
fn success() {
let resp = RequestBuilder::new(Method::Get, "http://127.0.0.1:8080/").unwrap()
.request_response(|_| {
let mut resp =
Response::with((Status::Ok, "this is a test", ModHead(Expect::Continue)));
resp.extensions.insert::<TestExtension>(1);
Ok(resp)
}).unwrap();
assert_eq!(resp.get_bytes().unwrap(), b"this is a test");
assert_eq!(resp.get_string().unwrap(), "this is a test");
assert_eq!(resp.get_headers().get::<Expect>(), Some(&Expect::Continue));
assert_eq!(resp.get_header::<Expect>(), Some(&Expect::Continue));
assert_eq!(resp.get_extensions().len(), 1);
assert_eq!(resp.get_extension::<TestExtension>(), Some(&1));
assert_eq!(resp.get_status(), Some(&Status::Ok));
assert!(resp.is_success());
}
#[test]
fn fail() {
let resp = RequestBuilder::new(Method::Get, "http://127.0.0.1:8080/").unwrap()
.request_response(|_| {
Ok(Response::with(Status::ImATeapot))
}).unwrap();
assert!(!resp.is_success());
}
#[test]
fn no_status() {
let resp = RequestBuilder::new(Method::Get, "http://127.0.0.1:8080/").unwrap()
.request_response(|_| {
Ok(Response::with(""))
}).unwrap();
assert!(!resp.is_success());
}
#[cfg(feature = "json")]
#[test]
fn json_no_content_type() {
use serde_json::{json, Value};
let resp = RequestBuilder::new(Method::Get, "http://127.0.0.1:8080/").unwrap()
.request_response(|_| {
Ok(Response::with("{\"test\":1}"))
}).unwrap();
assert_eq!(resp.get_json::<Value>().unwrap(), json!({"test":1}));
}
#[cfg(feature = "json")]
#[test]
fn json() {
use serde_json::{json, Value};
let resp = RequestBuilder::new(Method::Get, "http://127.0.0.1:8080/").unwrap()
.request_response(|_| {
Ok(Response::with(("{\"test\":1}", ModHead(ContentType::json()))))
}).unwrap();
assert_eq!(resp.get_json::<Value>().unwrap(), json!({"test":1}));
}
#[cfg(feature = "json")]
#[test]
fn json_wrong_content_type() {
use serde_json::{json, Value};
let resp = RequestBuilder::new(Method::Get, "http://127.0.0.1:8080/").unwrap()
.request_response(|_| {
Ok(Response::with(("{\"test\":1}", ModHead(ContentType::jpeg()))))
}).unwrap();
assert!(resp.get_json::<Value>().is_err());
}
#[cfg(feature = "jsonapi")]
#[test]
fn jsonapi() {
use japi::*;
use serde_json;
let go : GenericObject = Identifier::new("a".into(), "b".into()).into();
let d = Document {
data: OptionalVec::One(Some(go.clone())),
..Default::default()
};
let content_type = ContentType(Mime(
TopLevel::Application,
SubLevel::Ext(String::from("vnd.api+json")),
Vec::new()));
let resp = RequestBuilder::new(Method::Get, "http://127.0.0.1:8080/").unwrap()
.request_response(|_| {
Ok(Response::with((serde_json::to_string(&d).unwrap(), ModHead(content_type.clone()))))
}).unwrap();
assert_eq!(resp.get_document().unwrap(), d);
assert_eq!(resp.get_primary_data().unwrap(), go);
}
#[cfg(feature = "jsonapi")]
#[test]
fn jsonapi_no_content_type() {
use japi::*;
use serde_json;
let go : GenericObject = Identifier::new("a".into(), "b".into()).into();
let d = Document {
data: OptionalVec::One(Some(go.clone())),
..Default::default()
};
let resp = RequestBuilder::new(Method::Get, "http://127.0.0.1:8080/").unwrap()
.request_response(|_| {
Ok(Response::with(serde_json::to_string(&d).unwrap()))
}).unwrap();
assert!(resp.get_document().is_err());
assert!(resp.get_primary_data().is_err());
}
#[cfg(feature = "jsonapi")]
#[test]
fn jsonapi_wrong_content_type() {
use japi::*;
use serde_json;
let go : GenericObject = Identifier::new("a".into(), "b".into()).into();
let d = Document {
data: OptionalVec::One(Some(go.clone())),
..Default::default()
};
let resp = RequestBuilder::new(Method::Get, "http://127.0.0.1:8080/").unwrap()
.request_response(|_| {
Ok(Response::with((serde_json::to_string(&d).unwrap(), ModHead(ContentType::json()))))
}).unwrap();
assert!(resp.get_document().is_err());
assert!(resp.get_primary_data().is_err());
}
#[cfg(feature = "cookies")]
#[test]
fn cookies() {
let resp = RequestBuilder::new(Method::Get, "http://127.0.0.1:8080/").unwrap()
.request_response(|_| {
Ok(Response::with(ModHead(Cookie(vec![
String::from("foo=bar"), String::from("bar=baz")]))))
}).unwrap();
let jar = resp.get_cookies().unwrap();
assert_eq!(jar.get("foo").unwrap().value(), "bar");
assert_eq!(jar.get("bar").unwrap().value(), "baz");
assert_eq!(jar.iter().count(), 2);
}
#[cfg(feature = "cookies")]
#[test]
fn no_cookies() {
let resp = RequestBuilder::new(Method::Get, "http://127.0.0.1:8080/").unwrap()
.request_response(|_| {
Ok(Response::with("oops no cookies"))
}).unwrap();
assert!(resp.get_cookies().is_err());
}
}