use std::str;
use json;
use colored::*;
use hyper::header::{Headers, ContentType};
use hyper::status::StatusCode;
use util;
use Options;
use super::{HttpBody, HttpLike, HttpQueryString};
use super::form::{HttpFormDataField, http_form_into_fields};
use super::body::{
ParsedHttpBody,
parse_http_body,
http_body_from_parts,
validate_http_request_body
};
use super::header::validate_http_request_headers;
pub use super::header::http_header_into_tuple;
pub use super::body::http_body_into_parts;
pub fn path_with_query(path: &str, query: HttpQueryString) -> String {
let new_query = query.to_string();
let (path, _) = path.split_at(path.find('#').unwrap_or(path.len()));
let (path, _) = path.split_at(path.find('?').unwrap_or(path.len()));
if new_query.is_empty() {
path.to_string()
} else {
format!("{}?{}", path, new_query)
}
}
pub fn validate_http_request<T: HttpLike>(
result: &mut T,
options: &Options,
context: &str,
response_status: Option<StatusCode>,
expected_status: Option<StatusCode>,
expected_headers: &Headers,
unexpected_headers: &mut Vec<String>,
expected_body: &Option<HttpBody>,
expected_exact_body: bool
) -> Vec<String> {
let mut errors = Vec::new();
if let Some(actual) = response_status {
if let Some(expected) = expected_status {
if actual != expected {
errors.push(format!(
"{} {}\n\n \"{}\"\n\n {}\n\n \"{}\"",
context.yellow(),
"status code does not match value, expected:".yellow(),
format!("{}", expected).green().bold(),
"but got:".yellow(),
format!("{}", actual).red().bold()
));
}
}
}
validate_http_request_headers(
&mut errors, result, context, expected_headers, unexpected_headers
);
if let Some(ref expected_body) = expected_body.as_ref() {
validate_http_request_body(
&mut errors,
options,
result,
context,
expected_body,
expected_exact_body
);
}
errors
}
pub fn dump_http_like<T: HttpLike>(
errors: &mut Vec<String>,
result: &mut T,
context: &str
) {
let headers = format_headers(result);
let body = result.into_http_body();
errors.push(
format!(
"{} {}\n\n {}\n\n {} {}",
context.yellow(),
"headers dump:".yellow(),
headers,
context.yellow(),
format_body(parse_http_body(&body))
)
)
}
fn format_headers<T: HttpLike>(result: &mut T) -> String {
let mut headers = result.headers().iter().map(|header| {
(header.name().to_string(), header.value_string().clone())
}).collect::<Vec<(String, String)>>();
headers.sort();
let max_name_length = headers.iter().map(|h| h.0.len()).max().unwrap_or(0);
headers.into_iter().map(|(name, value)| {
format!("{: >2$}: {}", name.cyan(), value.purple().bold(), max_name_length)
}).collect::<Vec<String>>().join("\n ")
}
fn format_body(body: Result<ParsedHttpBody, String>) -> String {
match body {
Ok(actual) => match actual {
ParsedHttpBody::Text(text) => {
let text = format!("{:?}", text);
format!(
"{}\n\n \"{}\"",
"body dump:".yellow(),
&text[1..text.len() - 1].purple().bold()
)
},
ParsedHttpBody::Json(json) => {
format!(
"{}\n\n {}",
"body dump:".yellow(),
json::stringify_pretty(json, 4)
.lines()
.collect::<Vec<&str>>()
.join("\n ").cyan()
)
},
ParsedHttpBody::Form(form) => {
let fields = http_form_into_fields(form);
let field_count = fields.len();
let fields = fields.into_iter().enumerate().map(|(i, field)| {
match field {
HttpFormDataField::Field(name, value) => {
let value = format!("{:?}", value);
format!(
"{} {} \"{}\" {}\n\n \"{}\"\n",
format!("{:2})", i + 1).cyan().bold(),
"Field".yellow(),
name.cyan(),
"dump:".yellow(),
&value[1..value.len() - 1].purple().bold()
)
},
HttpFormDataField::Array(name, values) => {
format!(
"{} {} \"{}\" ({}) {}\n\n {}\n",
format!("{:2})", i + 1).cyan().bold(),
"Array".yellow(),
name.cyan(),
format!("{} items", values.len()).purple().bold(),
"dump:".yellow(),
values.into_iter().map(|value| {
let value = format!("{:?}", value);
format!("\"{}\"", &value[1..value.len() - 1].purple().bold())
}).collect::<Vec<String>>().join(", ")
)
},
HttpFormDataField::FileVec(name, filename, mime, data) => {
let mut headers = Headers::new();
headers.set(ContentType(mime.clone()));
let body = http_body_from_parts(data, &headers);
let body = format_body(
parse_http_body(&body)
).split('\n').map(|line| {
format!(" {}", line)
}).collect::<Vec<String>>().join("\n");
format!(
"{} {} \"{}\" (\"{}\", {}) {}\n",
format!("{:2})", i + 1).cyan().bold(),
"File".yellow(),
name.cyan(),
filename.purple().bold(),
format!("{}", mime).purple().bold(),
body.trim_left()
)
},
_ => unreachable!()
}
}).collect::<Vec<String>>().join("\n ");
format!(
"{}\n\n {}",
format!(
"{} {}{}",
"form dump with".yellow(),
format!("{} fields", field_count).cyan(),
":".yellow()
),
fields
)
},
ParsedHttpBody::Raw(data) => {
format!(
"{}\n\n [{}]",
format!(
"{} {}{}",
"raw body dump of".yellow(),
format!("{} bytes", data.len()).cyan(),
":".yellow()
),
util::raw::format_purple(data)
)
}
},
Err(err) => err
}
}