mirror of
https://github.com/Orange-OpenSource/hurl.git
synced 2024-12-25 20:12:14 +03:00
Merge pull request #202 from Orange-OpenSource/feature/add-real-request-sent
Return "real" HTTP request sent from HTTP client
This commit is contained in:
commit
45a3ab480e
File diff suppressed because one or more lines are too long
@ -56,5 +56,5 @@ span.line:before {
|
|||||||
color: dimgray;
|
color: dimgray;
|
||||||
}
|
}
|
||||||
</style></head><body>
|
</style></head><body>
|
||||||
<div class="hurl-file"><div class="hurl-entry"><div class="request"><span class="line"><span class="method">GET</span> <span class="url">http://localhost:8000/predicate/error/type</span></span></div><div class="response"><span class="line"><span class="version">HTTP/1.0</span> <span class="status">200</span></span><span class="line section-header">[Asserts]</span></span><span class="line"><span class="query-type">jsonpath</span> <span class="string">"$.status"</span> <span class="predicate-type">equals</span> <span class="string">"true"</span></span><span class="line"><span class="comment">#jsonpath "$.count" equals "0"</span></span><span class="line"><span class="query-type">jsonpath</span> <span class="string">"$.count"</span> <span class="predicate-type">equals</span> <span class="number">0</span></span><span class="line"><span class="query-type">jsonpath</span> <span class="string">"$.message"</span> <span class="predicate-type">equals</span> <span class="number">0</span></span><span class="line"><span class="query-type">jsonpath</span> <span class="string">"$.empty"</span> <span class="predicate-type">equals</span> <span class="number">0</span></span><span class="line"><span class="query-type">jsonpath</span> <span class="string">"$.number"</span> <span class="predicate-type">equals</span> <span class="number">1.1</span></span><span class="line"><span class="query-type">jsonpath</span> <span class="string">"$.message"</span> <span class="predicate-type">startsWith</span> <span class="string">"hi"</span></span><span class="line"><span class="query-type">jsonpath</span> <span class="string">"$.message"</span> <span class="predicate-type">contains</span> <span class="string">"hi"</span></span><span class="line"><span class="query-type">jsonpath</span> <span class="string">"$.message"</span> <span class="predicate-type">matches</span> <span class="string">"hi"</span></span><span class="line"><span class="query-type">jsonpath</span> <span class="string">"$.message"</span> <span class="predicate-type">equals</span> <span class="number">1</span></span><span class="line"><span class="query-type">jsonpath</span> <span class="string">"$.toto"</span> <span class="predicate-type">exists</span></span><span class="line"><span class="query-type">jsonpath</span> <span class="string">"$.message"</span> not <span class="predicate-type">exists</span></span></div></div></div>
|
<div class="hurl-file"><div class="hurl-entry"><div class="request"><span class="line"><span class="method">GET</span> <span class="url">http://localhost:8000/predicate/error/type</span></span></div><div class="response"><span class="line"><span class="version">HTTP/1.0</span> <span class="status">200</span></span><span class="line section-header">[Asserts]</span></span><span class="line"><span class="query-type">jsonpath</span> <span class="string">"$.status"</span> <span class="predicate-type">equals</span> <span class="string">"true"</span></span><span class="line"><span class="comment">#jsonpath "$.count" equals "0"</span></span><span class="line"><span class="query-type">jsonpath</span> <span class="string">"$.count"</span> <span class="predicate-type">equals</span> <span class="number">0</span></span><span class="line"><span class="query-type">jsonpath</span> <span class="string">"$.message"</span> <span class="predicate-type">equals</span> <span class="number">0</span></span><span class="line"><span class="query-type">jsonpath</span> <span class="string">"$.empty"</span> <span class="predicate-type">equals</span> <span class="number">0</span></span><span class="line"><span class="query-type">jsonpath</span> <span class="string">"$.number"</span> <span class="predicate-type">equals</span> <span class="number">1.1</span></span><span class="line"><span class="query-type">jsonpath</span> <span class="string">"$.message"</span> <span class="predicate-type">startsWith</span> <span class="string">"hi"</span></span><span class="line"><span class="query-type">jsonpath</span> <span class="string">"$.message"</span> <span class="predicate-type">contains</span> <span class="string">"hi"</span></span><span class="line"><span class="query-type">jsonpath</span> <span class="string">"$.message"</span> <span class="predicate-type">matches</span> <span class="string">"hi"</span></span><span class="line"><span class="query-type">jsonpath</span> <span class="string">"$.message"</span> <span class="predicate-type">equals</span> <span class="number">1</span></span><span class="line"><span class="query-type">jsonpath</span> <span class="string">"$.toto"</span> <span class="predicate-type">exists</span></span><span class="line"><span class="query-type">jsonpath</span> <span class="string">"$.message"</span> not <span class="predicate-type">exists</span></span><span class="line"><span class="query-type">jsonpath</span> <span class="string">"$.list"</span> <span class="predicate-type">equals</span> <span class="number">2</span></span></div></div></div>
|
||||||
</body></html>
|
</body></html>
|
File diff suppressed because it is too large
Load Diff
@ -26,6 +26,7 @@ use std::time::Instant;
|
|||||||
use super::core::*;
|
use super::core::*;
|
||||||
use super::options::ClientOptions;
|
use super::options::ClientOptions;
|
||||||
use super::request::*;
|
use super::request::*;
|
||||||
|
use super::request_spec::*;
|
||||||
use super::response::*;
|
use super::response::*;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
@ -51,7 +52,7 @@ pub struct Client {
|
|||||||
pub redirect_count: usize,
|
pub redirect_count: usize,
|
||||||
// unfortunately, follow-location feature from libcurl can not be used
|
// unfortunately, follow-location feature from libcurl can not be used
|
||||||
// libcurl returns a single list of headers for the 2 responses
|
// libcurl returns a single list of headers for the 2 responses
|
||||||
// hurl needs the return the headers only for the second (last) response)
|
// hurl needs to keep everything
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
@ -85,15 +86,50 @@ impl Client {
|
|||||||
///
|
///
|
||||||
/// Execute an http request
|
/// Execute an http request
|
||||||
///
|
///
|
||||||
pub fn execute(
|
pub fn execute_with_redirect(
|
||||||
&mut self,
|
&mut self,
|
||||||
request: &Request,
|
request: &RequestSpec,
|
||||||
redirect_count: usize,
|
) -> Result<Vec<(Request, Response)>, HttpError> {
|
||||||
) -> Result<Response, HttpError> {
|
let mut calls = vec![];
|
||||||
|
|
||||||
|
let mut request_spec = request.clone();
|
||||||
|
self.redirect_count = 0;
|
||||||
|
loop {
|
||||||
|
let (request, response) = self.execute(&request_spec)?;
|
||||||
|
calls.push((request, response.clone()));
|
||||||
|
if let Some(url) = self.get_follow_location(response.clone()) {
|
||||||
|
request_spec = RequestSpec {
|
||||||
|
method: Method::Get,
|
||||||
|
url,
|
||||||
|
headers: vec![],
|
||||||
|
querystring: vec![],
|
||||||
|
form: vec![],
|
||||||
|
multipart: vec![],
|
||||||
|
cookies: vec![],
|
||||||
|
body: Body::Binary(vec![]),
|
||||||
|
content_type: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.redirect_count += 1;
|
||||||
|
if let Some(max_redirect) = self.options.max_redirect {
|
||||||
|
if self.redirect_count > max_redirect {
|
||||||
|
return Err(HttpError::TooManyRedirect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(calls)
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Execute an http request
|
||||||
|
///
|
||||||
|
pub fn execute(&mut self, request: &RequestSpec) -> Result<(Request, Response), HttpError> {
|
||||||
// set handle attributes
|
// set handle attributes
|
||||||
// that have not been set or reset
|
// that have not been set or reset
|
||||||
|
self.handle.verbose(true).unwrap();
|
||||||
self.handle.verbose(self.options.verbose).unwrap();
|
|
||||||
self.handle.ssl_verify_host(!self.options.insecure).unwrap();
|
self.handle.ssl_verify_host(!self.options.insecure).unwrap();
|
||||||
self.handle.ssl_verify_peer(!self.options.insecure).unwrap();
|
self.handle.ssl_verify_peer(!self.options.insecure).unwrap();
|
||||||
if let Some(proxy) = self.options.proxy.clone() {
|
if let Some(proxy) = self.options.proxy.clone() {
|
||||||
@ -107,7 +143,8 @@ impl Client {
|
|||||||
.connect_timeout(self.options.connect_timeout)
|
.connect_timeout(self.options.connect_timeout)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
self.set_url(&request.url, &request.querystring);
|
let url = self.generate_url(&request.url, &request.querystring);
|
||||||
|
self.handle.url(url.as_str()).unwrap();
|
||||||
self.set_method(&request.method);
|
self.set_method(&request.method);
|
||||||
|
|
||||||
self.set_cookies(&request.cookies);
|
self.set_cookies(&request.cookies);
|
||||||
@ -120,23 +157,8 @@ impl Client {
|
|||||||
|
|
||||||
self.set_headers(&request);
|
self.set_headers(&request);
|
||||||
|
|
||||||
self.handle
|
let verbose = self.options.verbose;
|
||||||
.debug_function(|info_type, data| match info_type {
|
let mut request_headers: Vec<Header> = vec![];
|
||||||
// return all request headers (not one by one)
|
|
||||||
easy::InfoType::HeaderOut => {
|
|
||||||
let lines = split_lines(data);
|
|
||||||
for line in lines {
|
|
||||||
eprintln!("> {}", line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
easy::InfoType::HeaderIn => {
|
|
||||||
if let Some(s) = decode_header(data) {
|
|
||||||
eprint!("< {}", s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
let mut status_lines = vec![];
|
let mut status_lines = vec![];
|
||||||
@ -149,7 +171,35 @@ impl Client {
|
|||||||
.read_function(|buf| Ok(data.read(buf).unwrap_or(0)))
|
.read_function(|buf| Ok(data.read(buf).unwrap_or(0)))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
transfer
|
||||||
|
.debug_function(|info_type, data| match info_type {
|
||||||
|
// return all request headers (not one by one)
|
||||||
|
easy::InfoType::HeaderOut => {
|
||||||
|
let mut lines = split_lines(data);
|
||||||
|
if verbose {
|
||||||
|
for line in lines.clone() {
|
||||||
|
eprintln!("> {}", line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lines.pop().unwrap();
|
||||||
|
lines.remove(0); // method/url
|
||||||
|
for line in lines {
|
||||||
|
if let Some(header) = Header::parse(line) {
|
||||||
|
request_headers.push(header);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
easy::InfoType::HeaderIn => {
|
||||||
|
if let Some(s) = decode_header(data) {
|
||||||
|
if verbose {
|
||||||
|
eprint!("< {}", s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
transfer
|
transfer
|
||||||
.header_function(|h| {
|
.header_function(|h| {
|
||||||
if let Some(s) = decode_header(h) {
|
if let Some(s) = decode_header(h) {
|
||||||
@ -196,46 +246,28 @@ impl Client {
|
|||||||
Some(status_line) => self.parse_response_version(status_line.clone())?,
|
Some(status_line) => self.parse_response_version(status_line.clone())?,
|
||||||
};
|
};
|
||||||
let headers = self.parse_response_headers(&headers);
|
let headers = self.parse_response_headers(&headers);
|
||||||
|
|
||||||
if let Some(url) = self.get_follow_location(headers.clone()) {
|
|
||||||
let request = Request {
|
|
||||||
method: Method::Get,
|
|
||||||
url,
|
|
||||||
headers: vec![],
|
|
||||||
querystring: vec![],
|
|
||||||
form: vec![],
|
|
||||||
multipart: vec![],
|
|
||||||
cookies: vec![],
|
|
||||||
body: Body::Binary(vec![]),
|
|
||||||
content_type: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let redirect_count = redirect_count + 1;
|
|
||||||
if let Some(max_redirect) = self.options.max_redirect {
|
|
||||||
if redirect_count > max_redirect {
|
|
||||||
return Err(HttpError::TooManyRedirect);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return self.execute(&request, redirect_count);
|
|
||||||
}
|
|
||||||
let duration = start.elapsed();
|
let duration = start.elapsed();
|
||||||
self.redirect_count = redirect_count;
|
|
||||||
self.handle.reset();
|
self.handle.reset();
|
||||||
|
|
||||||
Ok(Response {
|
let request = Request {
|
||||||
|
url,
|
||||||
|
method: (&request.method).to_string(),
|
||||||
|
headers: request_headers,
|
||||||
|
};
|
||||||
|
let response = Response {
|
||||||
version,
|
version,
|
||||||
status,
|
status,
|
||||||
headers,
|
headers,
|
||||||
body,
|
body,
|
||||||
duration,
|
duration,
|
||||||
})
|
};
|
||||||
|
Ok((request, response))
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// set url
|
/// generate url
|
||||||
///
|
///
|
||||||
fn set_url(&mut self, url: &str, params: &[Param]) {
|
fn generate_url(&mut self, url: &str, params: &[Param]) -> String {
|
||||||
let url = if params.is_empty() {
|
let url = if params.is_empty() {
|
||||||
url.to_string()
|
url.to_string()
|
||||||
} else {
|
} else {
|
||||||
@ -249,7 +281,7 @@ impl Client {
|
|||||||
let s = self.encode_params(params);
|
let s = self.encode_params(params);
|
||||||
format!("{}{}", url, s)
|
format!("{}{}", url, s)
|
||||||
};
|
};
|
||||||
self.handle.url(url.as_str()).unwrap();
|
url
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
@ -272,7 +304,7 @@ impl Client {
|
|||||||
///
|
///
|
||||||
/// set request headers
|
/// set request headers
|
||||||
///
|
///
|
||||||
fn set_headers(&mut self, request: &Request) {
|
fn set_headers(&mut self, request: &RequestSpec) {
|
||||||
let mut list = easy::List::new();
|
let mut list = easy::List::new();
|
||||||
|
|
||||||
for header in request.headers.clone() {
|
for header in request.headers.clone() {
|
||||||
@ -426,17 +458,15 @@ impl Client {
|
|||||||
/// 2. a 3xx response code
|
/// 2. a 3xx response code
|
||||||
/// 3. a header Location
|
/// 3. a header Location
|
||||||
///
|
///
|
||||||
fn get_follow_location(&mut self, headers: Vec<Header>) -> Option<String> {
|
fn get_follow_location(&mut self, response: Response) -> Option<String> {
|
||||||
if !self.options.follow_location {
|
if !self.options.follow_location {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
let response_code = response.status;
|
||||||
let response_code = self.handle.response_code().unwrap();
|
|
||||||
if !(300..400).contains(&response_code) {
|
if !(300..400).contains(&response_code) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
let location = match get_header_values(response.headers, "Location".to_string()).get(0) {
|
||||||
let location = match get_header_values(headers, "Location".to_string()).get(0) {
|
|
||||||
None => return None,
|
None => return None,
|
||||||
Some(value) => value.clone(),
|
Some(value) => value.clone(),
|
||||||
};
|
};
|
||||||
@ -490,7 +520,7 @@ impl Client {
|
|||||||
///
|
///
|
||||||
/// return curl command-line for the http request run by the client
|
/// return curl command-line for the http request run by the client
|
||||||
///
|
///
|
||||||
pub fn curl_command_line(&mut self, http_request: &Request) -> String {
|
pub fn curl_command_line(&mut self, http_request: &RequestSpec) -> String {
|
||||||
let mut arguments = vec!["curl".to_string()];
|
let mut arguments = vec!["curl".to_string()];
|
||||||
arguments.append(&mut http_request.curl_args(self.options.context_dir.clone()));
|
arguments.append(&mut http_request.curl_args(self.options.context_dir.clone()));
|
||||||
|
|
||||||
@ -514,7 +544,7 @@ impl Client {
|
|||||||
///
|
///
|
||||||
/// return cookies from both cookies from the cookie storage and the request
|
/// return cookies from both cookies from the cookie storage and the request
|
||||||
///
|
///
|
||||||
pub fn all_cookies(cookie_storage: Vec<Cookie>, request: &Request) -> Vec<RequestCookie> {
|
pub fn all_cookies(cookie_storage: Vec<Cookie>, request: &RequestSpec) -> Vec<RequestCookie> {
|
||||||
let mut cookies = request.cookies.clone();
|
let mut cookies = request.cookies.clone();
|
||||||
cookies.append(
|
cookies.append(
|
||||||
&mut cookie_storage
|
&mut cookie_storage
|
||||||
|
@ -37,6 +37,18 @@ pub struct Cookie {
|
|||||||
pub http_only: bool,
|
pub http_only: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub struct RequestCookie {
|
||||||
|
pub name: String,
|
||||||
|
pub value: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub struct Param {
|
||||||
|
pub name: String,
|
||||||
|
pub value: String,
|
||||||
|
}
|
||||||
|
|
||||||
impl fmt::Display for Header {
|
impl fmt::Display for Header {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "{}: {}", self.name, self.value)
|
write!(f, "{}: {}", self.name, self.value)
|
||||||
@ -60,6 +72,18 @@ impl fmt::Display for Cookie {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for RequestCookie {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{}={}", self.name, self.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Param {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{}: {}", self.name, self.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct ParseCookieError {}
|
pub struct ParseCookieError {}
|
||||||
|
|
||||||
|
@ -17,11 +17,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
pub use self::client::{Client, HttpError};
|
pub use self::client::{Client, HttpError};
|
||||||
pub use self::core::{Cookie, Header};
|
pub use self::core::{Cookie, Header, Param, RequestCookie};
|
||||||
pub use self::options::ClientOptions;
|
pub use self::options::ClientOptions;
|
||||||
|
pub use self::request::Request;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub use self::request::tests::*;
|
pub use self::request_spec::tests::*;
|
||||||
pub use self::request::{Body, FileParam, Method, MultipartParam, Param, Request, RequestCookie};
|
pub use self::request_spec::{Body, FileParam, Method, MultipartParam, RequestSpec};
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub use self::response::tests::*;
|
pub use self::response::tests::*;
|
||||||
pub use self::response::{Response, Version};
|
pub use self::response::{Response, Version};
|
||||||
@ -30,4 +31,5 @@ mod client;
|
|||||||
mod core;
|
mod core;
|
||||||
mod options;
|
mod options;
|
||||||
mod request;
|
mod request;
|
||||||
|
mod request_spec;
|
||||||
mod response;
|
mod response;
|
||||||
|
@ -16,564 +16,170 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use core::fmt;
|
|
||||||
|
|
||||||
use super::core::*;
|
use super::core::*;
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct Request {
|
pub struct Request {
|
||||||
pub method: Method,
|
|
||||||
pub url: String,
|
pub url: String,
|
||||||
|
pub method: String,
|
||||||
pub headers: Vec<Header>,
|
pub headers: Vec<Header>,
|
||||||
pub querystring: Vec<Param>,
|
|
||||||
pub form: Vec<Param>,
|
|
||||||
pub multipart: Vec<MultipartParam>,
|
|
||||||
pub cookies: Vec<RequestCookie>,
|
|
||||||
pub body: Body,
|
|
||||||
pub content_type: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub enum Method {
|
|
||||||
Get,
|
|
||||||
Head,
|
|
||||||
Post,
|
|
||||||
Put,
|
|
||||||
Delete,
|
|
||||||
Connect,
|
|
||||||
Options,
|
|
||||||
Trace,
|
|
||||||
Patch,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct Param {
|
|
||||||
pub name: String,
|
|
||||||
pub value: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub enum MultipartParam {
|
|
||||||
Param(Param),
|
|
||||||
FileParam(FileParam),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct FileParam {
|
|
||||||
pub name: String,
|
|
||||||
pub filename: String,
|
|
||||||
pub data: Vec<u8>,
|
|
||||||
pub content_type: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct RequestCookie {
|
|
||||||
pub name: String,
|
|
||||||
pub value: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub enum Body {
|
|
||||||
Text(String),
|
|
||||||
Binary(Vec<u8>),
|
|
||||||
File(Vec<u8>, String),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Body {
|
|
||||||
pub fn bytes(&self) -> Vec<u8> {
|
|
||||||
match self {
|
|
||||||
Body::Text(s) => s.as_bytes().to_vec(),
|
|
||||||
Body::Binary(bs) => bs.clone(),
|
|
||||||
Body::File(bs, _) => bs.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Method {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
let value = match self {
|
|
||||||
Method::Get => "GET",
|
|
||||||
Method::Head => "HEAD",
|
|
||||||
Method::Post => "POST",
|
|
||||||
Method::Put => "PUT",
|
|
||||||
Method::Delete => "DELETE",
|
|
||||||
Method::Connect => "CONNECT",
|
|
||||||
Method::Options => "OPTIONS",
|
|
||||||
Method::Trace => "TRACE",
|
|
||||||
Method::Patch => "PATCH",
|
|
||||||
};
|
|
||||||
write!(f, "{}", value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Param {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
write!(f, "{}: {}", self.name, self.value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for MultipartParam {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
MultipartParam::Param(param) => write!(f, "{}", param.to_string()),
|
|
||||||
MultipartParam::FileParam(param) => write!(f, "{}", param.to_string()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for FileParam {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"{}: file,{}; {}",
|
|
||||||
self.name, self.filename, self.content_type
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for RequestCookie {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
write!(f, "{}={}", self.name, self.value)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Request {
|
impl Request {
|
||||||
///
|
pub fn query_string_params(self) -> Vec<Param> {
|
||||||
/// return request as curl arguments
|
let u = Url::parse(self.url.as_str()).expect("valid url");
|
||||||
/// It does not contain the requests cookies (they will be accessed from the client)
|
let mut params = vec![];
|
||||||
///
|
for (name, value) in u.query_pairs() {
|
||||||
pub fn curl_args(&self, context_dir: String) -> Vec<String> {
|
let param = Param {
|
||||||
let querystring = if self.querystring.is_empty() {
|
name: name.to_string(),
|
||||||
"".to_string()
|
value: value.to_string(),
|
||||||
} else {
|
};
|
||||||
let params = self
|
params.push(param);
|
||||||
.querystring
|
|
||||||
.iter()
|
|
||||||
.map(|p| p.curl_arg_escape())
|
|
||||||
.collect::<Vec<String>>();
|
|
||||||
params.join("&")
|
|
||||||
};
|
|
||||||
let url = if querystring.as_str() == "" {
|
|
||||||
self.url.to_string()
|
|
||||||
} else if self.url.to_string().contains('?') {
|
|
||||||
format!("{}&{}", self.url.to_string(), querystring)
|
|
||||||
} else {
|
|
||||||
format!("{}?{}", self.url.to_string(), querystring)
|
|
||||||
};
|
|
||||||
let mut arguments = vec![format!("'{}'", url)];
|
|
||||||
|
|
||||||
let data =
|
|
||||||
!self.multipart.is_empty() || !self.form.is_empty() || !self.body.bytes().is_empty();
|
|
||||||
arguments.append(&mut self.method.curl_args(data));
|
|
||||||
|
|
||||||
for header in self.headers.clone() {
|
|
||||||
arguments.append(&mut header.curl_args());
|
|
||||||
}
|
}
|
||||||
|
params
|
||||||
|
}
|
||||||
|
|
||||||
let has_explicit_content_type = self
|
pub fn cookies(self) -> Vec<RequestCookie> {
|
||||||
.headers
|
self.headers
|
||||||
.iter()
|
.iter()
|
||||||
.map(|h| h.name.clone())
|
.filter(|h| h.name.as_str() == "Cookie")
|
||||||
.any(|n| n.as_str() == "Content-Type");
|
.flat_map(|h| parse_cookies(h.value.as_str().trim()))
|
||||||
if !has_explicit_content_type {
|
.collect()
|
||||||
if let Some(content_type) = self.content_type.clone() {
|
|
||||||
if content_type.as_str() != "application/x-www-form-urlencoded"
|
|
||||||
&& content_type.as_str() != "multipart/form-data"
|
|
||||||
{
|
|
||||||
arguments.push("-H".to_string());
|
|
||||||
arguments.push(format!("'Content-Type: {}'", content_type));
|
|
||||||
}
|
|
||||||
} else if !self.body.bytes().is_empty() {
|
|
||||||
match self.body.clone() {
|
|
||||||
Body::Text(_) => {
|
|
||||||
arguments.push("-H".to_string());
|
|
||||||
arguments.push("'Content-Type:'".to_string())
|
|
||||||
}
|
|
||||||
Body::Binary(_) => {
|
|
||||||
arguments.push("-H".to_string());
|
|
||||||
arguments.push("'Content-Type: application/octet-stream'".to_string())
|
|
||||||
}
|
|
||||||
Body::File(_, _) => {
|
|
||||||
arguments.push("-H".to_string());
|
|
||||||
arguments.push("'Content-Type:'".to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for param in self.form.clone() {
|
|
||||||
arguments.push("--data".to_string());
|
|
||||||
arguments.push(format!("'{}'", param.curl_arg_escape()));
|
|
||||||
}
|
|
||||||
for param in self.multipart.clone() {
|
|
||||||
arguments.push("-F".to_string());
|
|
||||||
arguments.push(format!("'{}'", param.curl_arg(context_dir.clone())));
|
|
||||||
}
|
|
||||||
|
|
||||||
if !self.body.bytes().is_empty() {
|
|
||||||
arguments.push("--data".to_string());
|
|
||||||
match self.body.clone() {
|
|
||||||
Body::Text(s) => {
|
|
||||||
let prefix = if s.contains('\n') { "$" } else { "" };
|
|
||||||
arguments.push(format!("{}'{}'", prefix, s.replace("\n", "\\n")))
|
|
||||||
}
|
|
||||||
Body::Binary(bytes) => arguments.push(format!("$'{}'", encode_bytes(bytes))),
|
|
||||||
Body::File(_, filename) => {
|
|
||||||
let prefix = if context_dir.as_str() == "." {
|
|
||||||
"".to_string()
|
|
||||||
} else {
|
|
||||||
format!("{}/", context_dir)
|
|
||||||
};
|
|
||||||
arguments.push(format!("'@{}{}'", prefix, filename))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
arguments
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn encode_byte(b: u8) -> String {
|
fn parse_cookies(s: &str) -> Vec<RequestCookie> {
|
||||||
format!("\\x{:02x}", b)
|
s.split(';').map(|t| parse_cookie(t.trim())).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn encode_bytes(b: Vec<u8>) -> String {
|
fn parse_cookie(s: &str) -> RequestCookie {
|
||||||
b.iter().map(|b| encode_byte(*b)).collect()
|
match s.find('=') {
|
||||||
}
|
Some(i) => RequestCookie {
|
||||||
|
name: s.split_at(i).0.to_string(),
|
||||||
impl Method {
|
value: s.split_at(i + 1).1.to_string(),
|
||||||
pub fn curl_args(&self, data: bool) -> Vec<String> {
|
},
|
||||||
match self {
|
None => RequestCookie {
|
||||||
Method::Get => {
|
name: s.to_string(),
|
||||||
if data {
|
value: "".to_string(),
|
||||||
vec!["-X".to_string(), "GET".to_string()]
|
},
|
||||||
} else {
|
|
||||||
vec![]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Method::Head => vec!["-X".to_string(), "HEAD".to_string()],
|
|
||||||
Method::Post => {
|
|
||||||
if data {
|
|
||||||
vec![]
|
|
||||||
} else {
|
|
||||||
vec!["-X".to_string(), "POST".to_string()]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Method::Put => vec!["-X".to_string(), "PUT".to_string()],
|
|
||||||
Method::Delete => vec!["-X".to_string(), "DELETE".to_string()],
|
|
||||||
Method::Connect => vec!["-X".to_string(), "CONNECT".to_string()],
|
|
||||||
Method::Options => vec!["-X".to_string(), "OPTIONS".to_string()],
|
|
||||||
Method::Trace => vec!["-X".to_string(), "TRACE".to_string()],
|
|
||||||
Method::Patch => vec!["-X".to_string(), "PATCH".to_string()],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Header {
|
|
||||||
pub fn curl_args(&self) -> Vec<String> {
|
|
||||||
let name = self.name.clone();
|
|
||||||
let value = self.value.clone();
|
|
||||||
vec![
|
|
||||||
"-H".to_string(),
|
|
||||||
encode_value(format!("{}: {}", name, value)),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Param {
|
|
||||||
pub fn curl_arg_escape(&self) -> String {
|
|
||||||
let name = self.name.clone();
|
|
||||||
let value = escape_url(self.value.clone());
|
|
||||||
format!("{}={}", name, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn curl_arg(&self) -> String {
|
|
||||||
let name = self.name.clone();
|
|
||||||
let value = self.value.clone();
|
|
||||||
format!("{}={}", name, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MultipartParam {
|
|
||||||
pub fn curl_arg(&self, context_dir: String) -> String {
|
|
||||||
match self {
|
|
||||||
MultipartParam::Param(param) => param.curl_arg(),
|
|
||||||
MultipartParam::FileParam(FileParam {
|
|
||||||
name,
|
|
||||||
filename,
|
|
||||||
content_type,
|
|
||||||
..
|
|
||||||
}) => {
|
|
||||||
let prefix = if context_dir.as_str() == "." {
|
|
||||||
"".to_string()
|
|
||||||
} else {
|
|
||||||
format!("{}/", context_dir)
|
|
||||||
};
|
|
||||||
let value = format!("@{}{};type={}", prefix, filename, content_type);
|
|
||||||
format!("{}={}", name, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn escape_single_quote(s: String) -> String {
|
|
||||||
s.chars()
|
|
||||||
.map(|c| {
|
|
||||||
if c == '\'' {
|
|
||||||
"\\'".to_string()
|
|
||||||
} else {
|
|
||||||
c.to_string()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect::<Vec<String>>()
|
|
||||||
.join("")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn escape_url(s: String) -> String {
|
|
||||||
percent_encoding::percent_encode(s.as_bytes(), percent_encoding::NON_ALPHANUMERIC).to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
// special encoding for the shell
|
|
||||||
// $'...'
|
|
||||||
fn encode_value(s: String) -> String {
|
|
||||||
if s.contains('\'') {
|
|
||||||
let s = escape_single_quote(s);
|
|
||||||
format!("$'{}'", s)
|
|
||||||
} else {
|
|
||||||
format!("'{}'", s)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod tests {
|
pub mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::http::RequestCookie;
|
||||||
|
|
||||||
pub fn hello_http_request() -> Request {
|
pub fn hello_request() -> Request {
|
||||||
Request {
|
Request {
|
||||||
method: Method::Get,
|
method: "GET".to_string(),
|
||||||
url: "http://localhost:8000/hello".to_string(),
|
url: "http://localhost:8000/hello".to_string(),
|
||||||
querystring: vec![],
|
|
||||||
headers: vec![],
|
|
||||||
cookies: vec![],
|
|
||||||
body: Body::Binary(vec![]),
|
|
||||||
multipart: vec![],
|
|
||||||
form: vec![],
|
|
||||||
content_type: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn custom_http_request() -> Request {
|
|
||||||
Request {
|
|
||||||
method: Method::Get,
|
|
||||||
url: "http://localhost/custom".to_string(),
|
|
||||||
querystring: vec![],
|
|
||||||
headers: vec![
|
headers: vec![
|
||||||
Header {
|
Header {
|
||||||
name: String::from("User-Agent"),
|
name: "Host".to_string(),
|
||||||
value: String::from("iPhone"),
|
value: "localhost:8000".to_string(),
|
||||||
},
|
},
|
||||||
Header {
|
Header {
|
||||||
name: String::from("Foo"),
|
name: "Accept".to_string(),
|
||||||
value: String::from("Bar"),
|
value: "*/*".to_string(),
|
||||||
|
},
|
||||||
|
Header {
|
||||||
|
name: "User-Agent".to_string(),
|
||||||
|
value: "hurl/1.0".to_string(),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
cookies: vec![
|
|
||||||
RequestCookie {
|
|
||||||
name: String::from("theme"),
|
|
||||||
value: String::from("light"),
|
|
||||||
},
|
|
||||||
RequestCookie {
|
|
||||||
name: String::from("sessionToken"),
|
|
||||||
value: String::from("abc123"),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
body: Body::Binary(vec![]),
|
|
||||||
multipart: vec![],
|
|
||||||
form: vec![],
|
|
||||||
content_type: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn query_http_request() -> Request {
|
pub fn query_string_request() -> Request {
|
||||||
Request {
|
Request {
|
||||||
method: Method::Get,
|
method: "GET".to_string(),
|
||||||
url: "http://localhost:8000/querystring-params".to_string(),
|
url: "http://localhost:8000/querystring-params?param1=value1¶m2=¶m3=a%3Db¶m4=1%2C2%2C3".to_string(),
|
||||||
querystring: vec![
|
|
||||||
Param {
|
|
||||||
name: String::from("param1"),
|
|
||||||
value: String::from("value1"),
|
|
||||||
},
|
|
||||||
Param {
|
|
||||||
name: String::from("param2"),
|
|
||||||
value: String::from("a b"),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
headers: vec![],
|
headers: vec![],
|
||||||
cookies: vec![],
|
|
||||||
body: Body::Binary(vec![]),
|
|
||||||
multipart: vec![],
|
|
||||||
form: vec![],
|
|
||||||
content_type: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn form_http_request() -> Request {
|
pub fn cookies_request() -> Request {
|
||||||
Request {
|
Request {
|
||||||
method: Method::Post,
|
method: "GET".to_string(),
|
||||||
url: "http://localhost/form-params".to_string(),
|
url: "http://localhost:8000/cookies".to_string(),
|
||||||
querystring: vec![],
|
|
||||||
headers: vec![Header {
|
headers: vec![Header {
|
||||||
name: String::from("Content-Type"),
|
name: "Cookie".to_string(),
|
||||||
value: String::from("application/x-www-form-urlencoded"),
|
value: "cookie1=value1; cookie2=value2".to_string(),
|
||||||
}],
|
}],
|
||||||
cookies: vec![],
|
|
||||||
body: Body::Binary(vec![]),
|
|
||||||
multipart: vec![],
|
|
||||||
form: vec![
|
|
||||||
Param {
|
|
||||||
name: String::from("param1"),
|
|
||||||
value: String::from("value1"),
|
|
||||||
},
|
|
||||||
Param {
|
|
||||||
name: String::from("param2"),
|
|
||||||
value: String::from("a b"),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
content_type: Some("multipart/form-data".to_string()),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_encode_byte() {
|
fn test_query_string() {
|
||||||
assert_eq!(encode_byte(1), "\\x01".to_string());
|
assert!(hello_request().query_string_params().is_empty());
|
||||||
assert_eq!(encode_byte(32), "\\x20".to_string());
|
assert_eq!(
|
||||||
|
query_string_request().query_string_params(),
|
||||||
|
vec![
|
||||||
|
Param {
|
||||||
|
name: "param1".to_string(),
|
||||||
|
value: "value1".to_string()
|
||||||
|
},
|
||||||
|
Param {
|
||||||
|
name: "param2".to_string(),
|
||||||
|
value: "".to_string()
|
||||||
|
},
|
||||||
|
Param {
|
||||||
|
name: "param3".to_string(),
|
||||||
|
value: "a=b".to_string()
|
||||||
|
},
|
||||||
|
Param {
|
||||||
|
name: "param4".to_string(),
|
||||||
|
value: "1,2,3".to_string()
|
||||||
|
}
|
||||||
|
]
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn method_curl_args() {
|
fn test_cookies() {
|
||||||
assert!(Method::Get.curl_args(false).is_empty());
|
assert!(hello_request().cookies().is_empty());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Method::Get.curl_args(true),
|
cookies_request().cookies(),
|
||||||
vec!["-X".to_string(), "GET".to_string()]
|
vec![
|
||||||
);
|
RequestCookie {
|
||||||
|
name: "cookie1".to_string(),
|
||||||
assert_eq!(
|
value: "value1".to_string()
|
||||||
Method::Post.curl_args(false),
|
},
|
||||||
vec!["-X".to_string(), "POST".to_string()]
|
RequestCookie {
|
||||||
);
|
name: "cookie2".to_string(),
|
||||||
assert!(Method::Post.curl_args(true).is_empty());
|
value: "value2".to_string()
|
||||||
|
}
|
||||||
assert_eq!(
|
]
|
||||||
Method::Put.curl_args(false),
|
)
|
||||||
vec!["-X".to_string(), "PUT".to_string()]
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
Method::Put.curl_args(true),
|
|
||||||
vec!["-X".to_string(), "PUT".to_string()]
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn header_curl_args() {
|
fn test_parse_cookies() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Header {
|
parse_cookies("cookie1=value1; cookie2=value2"),
|
||||||
name: "Host".to_string(),
|
vec![
|
||||||
value: "example.com".to_string()
|
RequestCookie {
|
||||||
}
|
name: "cookie1".to_string(),
|
||||||
.curl_args(),
|
value: "value1".to_string()
|
||||||
vec!["-H".to_string(), "'Host: example.com'".to_string()]
|
},
|
||||||
);
|
RequestCookie {
|
||||||
assert_eq!(
|
name: "cookie2".to_string(),
|
||||||
Header {
|
value: "value2".to_string()
|
||||||
name: "If-Match".to_string(),
|
}
|
||||||
value: "\"e0023aa4e\"".to_string()
|
]
|
||||||
}
|
)
|
||||||
.curl_args(),
|
|
||||||
vec!["-H".to_string(), "'If-Match: \"e0023aa4e\"'".to_string()]
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn param_curl_args() {
|
fn test_parse_cookie() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Param {
|
parse_cookie("cookie1=value1"),
|
||||||
name: "param1".to_string(),
|
RequestCookie {
|
||||||
|
name: "cookie1".to_string(),
|
||||||
value: "value1".to_string()
|
value: "value1".to_string()
|
||||||
}
|
},
|
||||||
.curl_arg(),
|
)
|
||||||
"param1=value1".to_string()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
Param {
|
|
||||||
name: "param2".to_string(),
|
|
||||||
value: "".to_string()
|
|
||||||
}
|
|
||||||
.curl_arg(),
|
|
||||||
"param2=".to_string()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
Param {
|
|
||||||
name: "param3".to_string(),
|
|
||||||
value: "a=b".to_string()
|
|
||||||
}
|
|
||||||
.curl_arg_escape(),
|
|
||||||
"param3=a%3Db".to_string()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
Param {
|
|
||||||
name: "param4".to_string(),
|
|
||||||
value: "1,2,3".to_string()
|
|
||||||
}
|
|
||||||
.curl_arg_escape(),
|
|
||||||
"param4=1%2C2%2C3".to_string()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn requests_curl_args() {
|
|
||||||
assert_eq!(
|
|
||||||
hello_http_request().curl_args(".".to_string()),
|
|
||||||
vec!["'http://localhost:8000/hello'".to_string()]
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
custom_http_request().curl_args(".".to_string()),
|
|
||||||
vec![
|
|
||||||
"'http://localhost/custom'".to_string(),
|
|
||||||
"-H".to_string(),
|
|
||||||
"'User-Agent: iPhone'".to_string(),
|
|
||||||
"-H".to_string(),
|
|
||||||
"'Foo: Bar'".to_string(),
|
|
||||||
]
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
query_http_request().curl_args(".".to_string()),
|
|
||||||
vec![
|
|
||||||
"'http://localhost:8000/querystring-params?param1=value1¶m2=a%20b'".to_string()
|
|
||||||
]
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
form_http_request().curl_args(".".to_string()),
|
|
||||||
vec![
|
|
||||||
"'http://localhost/form-params'".to_string(),
|
|
||||||
"-H".to_string(),
|
|
||||||
"'Content-Type: application/x-www-form-urlencoded'".to_string(),
|
|
||||||
"--data".to_string(),
|
|
||||||
"'param1=value1'".to_string(),
|
|
||||||
"--data".to_string(),
|
|
||||||
"'param2=a%20b'".to_string(),
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_encode_value() {
|
|
||||||
assert_eq!(
|
|
||||||
encode_value("Header1: x".to_string()),
|
|
||||||
"'Header1: x'".to_string()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
encode_value("Header1: '".to_string()),
|
|
||||||
"$'Header1: \\''".to_string()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
555
packages/hurl/src/http/request_spec.rs
Normal file
555
packages/hurl/src/http/request_spec.rs
Normal file
@ -0,0 +1,555 @@
|
|||||||
|
/*
|
||||||
|
* hurl (https://hurl.dev)
|
||||||
|
* Copyright (C) 2020 Orange
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
use core::fmt;
|
||||||
|
|
||||||
|
use super::core::*;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub struct RequestSpec {
|
||||||
|
pub method: Method,
|
||||||
|
pub url: String,
|
||||||
|
pub headers: Vec<Header>,
|
||||||
|
pub querystring: Vec<Param>,
|
||||||
|
pub form: Vec<Param>,
|
||||||
|
pub multipart: Vec<MultipartParam>,
|
||||||
|
pub cookies: Vec<RequestCookie>,
|
||||||
|
pub body: Body,
|
||||||
|
pub content_type: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub enum Method {
|
||||||
|
Get,
|
||||||
|
Head,
|
||||||
|
Post,
|
||||||
|
Put,
|
||||||
|
Delete,
|
||||||
|
Connect,
|
||||||
|
Options,
|
||||||
|
Trace,
|
||||||
|
Patch,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub enum MultipartParam {
|
||||||
|
Param(Param),
|
||||||
|
FileParam(FileParam),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub struct FileParam {
|
||||||
|
pub name: String,
|
||||||
|
pub filename: String,
|
||||||
|
pub data: Vec<u8>,
|
||||||
|
pub content_type: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub enum Body {
|
||||||
|
Text(String),
|
||||||
|
Binary(Vec<u8>),
|
||||||
|
File(Vec<u8>, String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Body {
|
||||||
|
pub fn bytes(&self) -> Vec<u8> {
|
||||||
|
match self {
|
||||||
|
Body::Text(s) => s.as_bytes().to_vec(),
|
||||||
|
Body::Binary(bs) => bs.clone(),
|
||||||
|
Body::File(bs, _) => bs.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Method {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
let value = match self {
|
||||||
|
Method::Get => "GET",
|
||||||
|
Method::Head => "HEAD",
|
||||||
|
Method::Post => "POST",
|
||||||
|
Method::Put => "PUT",
|
||||||
|
Method::Delete => "DELETE",
|
||||||
|
Method::Connect => "CONNECT",
|
||||||
|
Method::Options => "OPTIONS",
|
||||||
|
Method::Trace => "TRACE",
|
||||||
|
Method::Patch => "PATCH",
|
||||||
|
};
|
||||||
|
write!(f, "{}", value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for MultipartParam {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
MultipartParam::Param(param) => write!(f, "{}", param.to_string()),
|
||||||
|
MultipartParam::FileParam(param) => write!(f, "{}", param.to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for FileParam {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{}: file,{}; {}",
|
||||||
|
self.name, self.filename, self.content_type
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RequestSpec {
|
||||||
|
///
|
||||||
|
/// return request as curl arguments
|
||||||
|
/// It does not contain the requests cookies (they will be accessed from the client)
|
||||||
|
///
|
||||||
|
pub fn curl_args(&self, context_dir: String) -> Vec<String> {
|
||||||
|
let querystring = if self.querystring.is_empty() {
|
||||||
|
"".to_string()
|
||||||
|
} else {
|
||||||
|
let params = self
|
||||||
|
.querystring
|
||||||
|
.iter()
|
||||||
|
.map(|p| p.curl_arg_escape())
|
||||||
|
.collect::<Vec<String>>();
|
||||||
|
params.join("&")
|
||||||
|
};
|
||||||
|
let url = if querystring.as_str() == "" {
|
||||||
|
self.url.to_string()
|
||||||
|
} else if self.url.to_string().contains('?') {
|
||||||
|
format!("{}&{}", self.url.to_string(), querystring)
|
||||||
|
} else {
|
||||||
|
format!("{}?{}", self.url.to_string(), querystring)
|
||||||
|
};
|
||||||
|
let mut arguments = vec![format!("'{}'", url)];
|
||||||
|
|
||||||
|
let data =
|
||||||
|
!self.multipart.is_empty() || !self.form.is_empty() || !self.body.bytes().is_empty();
|
||||||
|
arguments.append(&mut self.method.curl_args(data));
|
||||||
|
|
||||||
|
for header in self.headers.clone() {
|
||||||
|
arguments.append(&mut header.curl_args());
|
||||||
|
}
|
||||||
|
|
||||||
|
let has_explicit_content_type = self
|
||||||
|
.headers
|
||||||
|
.iter()
|
||||||
|
.map(|h| h.name.clone())
|
||||||
|
.any(|n| n.as_str() == "Content-Type");
|
||||||
|
if !has_explicit_content_type {
|
||||||
|
if let Some(content_type) = self.content_type.clone() {
|
||||||
|
if content_type.as_str() != "application/x-www-form-urlencoded"
|
||||||
|
&& content_type.as_str() != "multipart/form-data"
|
||||||
|
{
|
||||||
|
arguments.push("-H".to_string());
|
||||||
|
arguments.push(format!("'Content-Type: {}'", content_type));
|
||||||
|
}
|
||||||
|
} else if !self.body.bytes().is_empty() {
|
||||||
|
match self.body.clone() {
|
||||||
|
Body::Text(_) => {
|
||||||
|
arguments.push("-H".to_string());
|
||||||
|
arguments.push("'Content-Type:'".to_string())
|
||||||
|
}
|
||||||
|
Body::Binary(_) => {
|
||||||
|
arguments.push("-H".to_string());
|
||||||
|
arguments.push("'Content-Type: application/octet-stream'".to_string())
|
||||||
|
}
|
||||||
|
Body::File(_, _) => {
|
||||||
|
arguments.push("-H".to_string());
|
||||||
|
arguments.push("'Content-Type:'".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for param in self.form.clone() {
|
||||||
|
arguments.push("--data".to_string());
|
||||||
|
arguments.push(format!("'{}'", param.curl_arg_escape()));
|
||||||
|
}
|
||||||
|
for param in self.multipart.clone() {
|
||||||
|
arguments.push("-F".to_string());
|
||||||
|
arguments.push(format!("'{}'", param.curl_arg(context_dir.clone())));
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.body.bytes().is_empty() {
|
||||||
|
arguments.push("--data".to_string());
|
||||||
|
match self.body.clone() {
|
||||||
|
Body::Text(s) => {
|
||||||
|
let prefix = if s.contains('\n') { "$" } else { "" };
|
||||||
|
arguments.push(format!("{}'{}'", prefix, s.replace("\n", "\\n")))
|
||||||
|
}
|
||||||
|
Body::Binary(bytes) => arguments.push(format!("$'{}'", encode_bytes(bytes))),
|
||||||
|
Body::File(_, filename) => {
|
||||||
|
let prefix = if context_dir.as_str() == "." {
|
||||||
|
"".to_string()
|
||||||
|
} else {
|
||||||
|
format!("{}/", context_dir)
|
||||||
|
};
|
||||||
|
arguments.push(format!("'@{}{}'", prefix, filename))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
arguments
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encode_byte(b: u8) -> String {
|
||||||
|
format!("\\x{:02x}", b)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encode_bytes(b: Vec<u8>) -> String {
|
||||||
|
b.iter().map(|b| encode_byte(*b)).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Method {
|
||||||
|
pub fn curl_args(&self, data: bool) -> Vec<String> {
|
||||||
|
match self {
|
||||||
|
Method::Get => {
|
||||||
|
if data {
|
||||||
|
vec!["-X".to_string(), "GET".to_string()]
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Method::Head => vec!["-X".to_string(), "HEAD".to_string()],
|
||||||
|
Method::Post => {
|
||||||
|
if data {
|
||||||
|
vec![]
|
||||||
|
} else {
|
||||||
|
vec!["-X".to_string(), "POST".to_string()]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Method::Put => vec!["-X".to_string(), "PUT".to_string()],
|
||||||
|
Method::Delete => vec!["-X".to_string(), "DELETE".to_string()],
|
||||||
|
Method::Connect => vec!["-X".to_string(), "CONNECT".to_string()],
|
||||||
|
Method::Options => vec!["-X".to_string(), "OPTIONS".to_string()],
|
||||||
|
Method::Trace => vec!["-X".to_string(), "TRACE".to_string()],
|
||||||
|
Method::Patch => vec!["-X".to_string(), "PATCH".to_string()],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Header {
|
||||||
|
pub fn curl_args(&self) -> Vec<String> {
|
||||||
|
let name = self.name.clone();
|
||||||
|
let value = self.value.clone();
|
||||||
|
vec![
|
||||||
|
"-H".to_string(),
|
||||||
|
encode_value(format!("{}: {}", name, value)),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Param {
|
||||||
|
pub fn curl_arg_escape(&self) -> String {
|
||||||
|
let name = self.name.clone();
|
||||||
|
let value = escape_url(self.value.clone());
|
||||||
|
format!("{}={}", name, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn curl_arg(&self) -> String {
|
||||||
|
let name = self.name.clone();
|
||||||
|
let value = self.value.clone();
|
||||||
|
format!("{}={}", name, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MultipartParam {
|
||||||
|
pub fn curl_arg(&self, context_dir: String) -> String {
|
||||||
|
match self {
|
||||||
|
MultipartParam::Param(param) => param.curl_arg(),
|
||||||
|
MultipartParam::FileParam(FileParam {
|
||||||
|
name,
|
||||||
|
filename,
|
||||||
|
content_type,
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
|
let prefix = if context_dir.as_str() == "." {
|
||||||
|
"".to_string()
|
||||||
|
} else {
|
||||||
|
format!("{}/", context_dir)
|
||||||
|
};
|
||||||
|
let value = format!("@{}{};type={}", prefix, filename, content_type);
|
||||||
|
format!("{}={}", name, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn escape_single_quote(s: String) -> String {
|
||||||
|
s.chars()
|
||||||
|
.map(|c| {
|
||||||
|
if c == '\'' {
|
||||||
|
"\\'".to_string()
|
||||||
|
} else {
|
||||||
|
c.to_string()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join("")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn escape_url(s: String) -> String {
|
||||||
|
percent_encoding::percent_encode(s.as_bytes(), percent_encoding::NON_ALPHANUMERIC).to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
// special encoding for the shell
|
||||||
|
// $'...'
|
||||||
|
fn encode_value(s: String) -> String {
|
||||||
|
if s.contains('\'') {
|
||||||
|
let s = escape_single_quote(s);
|
||||||
|
format!("$'{}'", s)
|
||||||
|
} else {
|
||||||
|
format!("'{}'", s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub fn hello_http_request() -> RequestSpec {
|
||||||
|
RequestSpec {
|
||||||
|
method: Method::Get,
|
||||||
|
url: "http://localhost:8000/hello".to_string(),
|
||||||
|
querystring: vec![],
|
||||||
|
headers: vec![],
|
||||||
|
cookies: vec![],
|
||||||
|
body: Body::Binary(vec![]),
|
||||||
|
multipart: vec![],
|
||||||
|
form: vec![],
|
||||||
|
content_type: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn custom_http_request() -> RequestSpec {
|
||||||
|
RequestSpec {
|
||||||
|
method: Method::Get,
|
||||||
|
url: "http://localhost/custom".to_string(),
|
||||||
|
querystring: vec![],
|
||||||
|
headers: vec![
|
||||||
|
Header {
|
||||||
|
name: String::from("User-Agent"),
|
||||||
|
value: String::from("iPhone"),
|
||||||
|
},
|
||||||
|
Header {
|
||||||
|
name: String::from("Foo"),
|
||||||
|
value: String::from("Bar"),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
cookies: vec![
|
||||||
|
RequestCookie {
|
||||||
|
name: String::from("theme"),
|
||||||
|
value: String::from("light"),
|
||||||
|
},
|
||||||
|
RequestCookie {
|
||||||
|
name: String::from("sessionToken"),
|
||||||
|
value: String::from("abc123"),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
body: Body::Binary(vec![]),
|
||||||
|
multipart: vec![],
|
||||||
|
form: vec![],
|
||||||
|
content_type: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn query_http_request() -> RequestSpec {
|
||||||
|
RequestSpec {
|
||||||
|
method: Method::Get,
|
||||||
|
url: "http://localhost:8000/querystring-params".to_string(),
|
||||||
|
querystring: vec![
|
||||||
|
Param {
|
||||||
|
name: String::from("param1"),
|
||||||
|
value: String::from("value1"),
|
||||||
|
},
|
||||||
|
Param {
|
||||||
|
name: String::from("param2"),
|
||||||
|
value: String::from("a b"),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
headers: vec![],
|
||||||
|
cookies: vec![],
|
||||||
|
body: Body::Binary(vec![]),
|
||||||
|
multipart: vec![],
|
||||||
|
form: vec![],
|
||||||
|
content_type: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn form_http_request() -> RequestSpec {
|
||||||
|
RequestSpec {
|
||||||
|
method: Method::Post,
|
||||||
|
url: "http://localhost/form-params".to_string(),
|
||||||
|
querystring: vec![],
|
||||||
|
headers: vec![Header {
|
||||||
|
name: String::from("Content-Type"),
|
||||||
|
value: String::from("application/x-www-form-urlencoded"),
|
||||||
|
}],
|
||||||
|
cookies: vec![],
|
||||||
|
body: Body::Binary(vec![]),
|
||||||
|
multipart: vec![],
|
||||||
|
form: vec![
|
||||||
|
Param {
|
||||||
|
name: String::from("param1"),
|
||||||
|
value: String::from("value1"),
|
||||||
|
},
|
||||||
|
Param {
|
||||||
|
name: String::from("param2"),
|
||||||
|
value: String::from("a b"),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
content_type: Some("multipart/form-data".to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_encode_byte() {
|
||||||
|
assert_eq!(encode_byte(1), "\\x01".to_string());
|
||||||
|
assert_eq!(encode_byte(32), "\\x20".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn method_curl_args() {
|
||||||
|
assert!(Method::Get.curl_args(false).is_empty());
|
||||||
|
assert_eq!(
|
||||||
|
Method::Get.curl_args(true),
|
||||||
|
vec!["-X".to_string(), "GET".to_string()]
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
Method::Post.curl_args(false),
|
||||||
|
vec!["-X".to_string(), "POST".to_string()]
|
||||||
|
);
|
||||||
|
assert!(Method::Post.curl_args(true).is_empty());
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
Method::Put.curl_args(false),
|
||||||
|
vec!["-X".to_string(), "PUT".to_string()]
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Method::Put.curl_args(true),
|
||||||
|
vec!["-X".to_string(), "PUT".to_string()]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn header_curl_args() {
|
||||||
|
assert_eq!(
|
||||||
|
Header {
|
||||||
|
name: "Host".to_string(),
|
||||||
|
value: "example.com".to_string()
|
||||||
|
}
|
||||||
|
.curl_args(),
|
||||||
|
vec!["-H".to_string(), "'Host: example.com'".to_string()]
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Header {
|
||||||
|
name: "If-Match".to_string(),
|
||||||
|
value: "\"e0023aa4e\"".to_string()
|
||||||
|
}
|
||||||
|
.curl_args(),
|
||||||
|
vec!["-H".to_string(), "'If-Match: \"e0023aa4e\"'".to_string()]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn param_curl_args() {
|
||||||
|
assert_eq!(
|
||||||
|
Param {
|
||||||
|
name: "param1".to_string(),
|
||||||
|
value: "value1".to_string()
|
||||||
|
}
|
||||||
|
.curl_arg(),
|
||||||
|
"param1=value1".to_string()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Param {
|
||||||
|
name: "param2".to_string(),
|
||||||
|
value: "".to_string()
|
||||||
|
}
|
||||||
|
.curl_arg(),
|
||||||
|
"param2=".to_string()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Param {
|
||||||
|
name: "param3".to_string(),
|
||||||
|
value: "a=b".to_string()
|
||||||
|
}
|
||||||
|
.curl_arg_escape(),
|
||||||
|
"param3=a%3Db".to_string()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Param {
|
||||||
|
name: "param4".to_string(),
|
||||||
|
value: "1,2,3".to_string()
|
||||||
|
}
|
||||||
|
.curl_arg_escape(),
|
||||||
|
"param4=1%2C2%2C3".to_string()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn requests_curl_args() {
|
||||||
|
assert_eq!(
|
||||||
|
hello_http_request().curl_args(".".to_string()),
|
||||||
|
vec!["'http://localhost:8000/hello'".to_string()]
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
custom_http_request().curl_args(".".to_string()),
|
||||||
|
vec![
|
||||||
|
"'http://localhost/custom'".to_string(),
|
||||||
|
"-H".to_string(),
|
||||||
|
"'User-Agent: iPhone'".to_string(),
|
||||||
|
"-H".to_string(),
|
||||||
|
"'Foo: Bar'".to_string(),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
query_http_request().curl_args(".".to_string()),
|
||||||
|
vec![
|
||||||
|
"'http://localhost:8000/querystring-params?param1=value1¶m2=a%20b'".to_string()
|
||||||
|
]
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
form_http_request().curl_args(".".to_string()),
|
||||||
|
vec![
|
||||||
|
"'http://localhost/form-params'".to_string(),
|
||||||
|
"-H".to_string(),
|
||||||
|
"'Content-Type: application/x-www-form-urlencoded'".to_string(),
|
||||||
|
"--data".to_string(),
|
||||||
|
"'param1=value1'".to_string(),
|
||||||
|
"--data".to_string(),
|
||||||
|
"'param2=a%20b'".to_string(),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_encode_value() {
|
||||||
|
assert_eq!(
|
||||||
|
encode_value("Header1: x".to_string()),
|
||||||
|
"'Header1: x'".to_string()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
encode_value("Header1: '".to_string()),
|
||||||
|
"$'Header1: \\''".to_string()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -29,6 +29,7 @@ use super::value::Value;
|
|||||||
use crate::runner::request::{cookie_storage_clear, cookie_storage_set};
|
use crate::runner::request::{cookie_storage_clear, cookie_storage_set};
|
||||||
|
|
||||||
/// Run an entry with the hurl http client
|
/// Run an entry with the hurl http client
|
||||||
|
/// Return one or more EntryResults (if following redirect)
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
@ -54,18 +55,18 @@ pub fn run(
|
|||||||
context_dir: String,
|
context_dir: String,
|
||||||
log_verbose: &impl Fn(&str),
|
log_verbose: &impl Fn(&str),
|
||||||
log_error_message: &impl Fn(bool, &str),
|
log_error_message: &impl Fn(bool, &str),
|
||||||
) -> EntryResult {
|
) -> Vec<EntryResult> {
|
||||||
let http_request = match eval_request(entry.request.clone(), variables, context_dir.clone()) {
|
let http_request = match eval_request(entry.request.clone(), variables, context_dir.clone()) {
|
||||||
Ok(r) => r,
|
Ok(r) => r,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
return EntryResult {
|
return vec![EntryResult {
|
||||||
request: None,
|
request: None,
|
||||||
response: None,
|
response: None,
|
||||||
captures: vec![],
|
captures: vec![],
|
||||||
asserts: vec![],
|
asserts: vec![],
|
||||||
errors: vec![error],
|
errors: vec![error],
|
||||||
time_in_ms: 0,
|
time_in_ms: 0,
|
||||||
};
|
}];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -106,8 +107,8 @@ pub fn run(
|
|||||||
.as_str(),
|
.as_str(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let http_response = match http_client.execute(&http_request, 0) {
|
let calls = match http_client.execute_with_redirect(&http_request) {
|
||||||
Ok(response) => response,
|
Ok(calls) => calls,
|
||||||
Err(http_error) => {
|
Err(http_error) => {
|
||||||
let runner_error = match http_error {
|
let runner_error = match http_error {
|
||||||
HttpError::CouldNotResolveProxyName => RunnerError::CouldNotResolveProxyName,
|
HttpError::CouldNotResolveProxyName => RunnerError::CouldNotResolveProxyName,
|
||||||
@ -129,87 +130,100 @@ pub fn run(
|
|||||||
url: http_request.url.clone(),
|
url: http_request.url.clone(),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
return EntryResult {
|
return vec![EntryResult {
|
||||||
request: Some(http_request.clone()),
|
request: None,
|
||||||
response: None,
|
response: None,
|
||||||
captures: vec![],
|
captures: vec![],
|
||||||
asserts: vec![],
|
asserts: vec![],
|
||||||
errors: vec![Error {
|
errors: vec![Error {
|
||||||
source_info: SourceInfo {
|
source_info: SourceInfo {
|
||||||
start: entry.clone().request.url.source_info.start,
|
start: entry.request.url.source_info.start,
|
||||||
end: entry.clone().request.url.source_info.end,
|
end: entry.request.url.source_info.end,
|
||||||
},
|
},
|
||||||
inner: runner_error,
|
inner: runner_error,
|
||||||
assert: false,
|
assert: false,
|
||||||
}],
|
}],
|
||||||
time_in_ms: 0,
|
time_in_ms: 0,
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut entry_results = vec![];
|
||||||
|
for (i, (http_request, http_response)) in calls.iter().enumerate() {
|
||||||
|
let mut captures = vec![];
|
||||||
|
let mut asserts = vec![];
|
||||||
|
let mut errors = vec![];
|
||||||
|
let time_in_ms = http_response.duration.as_millis();
|
||||||
|
|
||||||
|
// Last call
|
||||||
|
if i == calls.len() - 1 {
|
||||||
|
captures = match entry.response.clone() {
|
||||||
|
None => vec![],
|
||||||
|
Some(response) => match eval_captures(response, http_response, variables) {
|
||||||
|
Ok(captures) => captures,
|
||||||
|
Err(e) => {
|
||||||
|
return vec![EntryResult {
|
||||||
|
request: Some(http_request.clone()),
|
||||||
|
response: Some(http_response.clone()),
|
||||||
|
captures: vec![],
|
||||||
|
asserts: vec![],
|
||||||
|
errors: vec![e],
|
||||||
|
time_in_ms,
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
// update variables now!
|
||||||
};
|
for capture_result in captures.clone() {
|
||||||
|
variables.insert(capture_result.name, capture_result.value);
|
||||||
let time_in_ms = http_response.duration.as_millis();
|
|
||||||
log_verbose(format!("Response Time: {}ms", time_in_ms.to_string()).as_str());
|
|
||||||
|
|
||||||
let captures = match entry.response.clone() {
|
|
||||||
None => vec![],
|
|
||||||
Some(response) => match eval_captures(response, http_response.clone(), variables) {
|
|
||||||
Ok(captures) => captures,
|
|
||||||
Err(e) => {
|
|
||||||
return EntryResult {
|
|
||||||
request: Some(http_request),
|
|
||||||
response: Some(http_response),
|
|
||||||
captures: vec![],
|
|
||||||
asserts: vec![],
|
|
||||||
errors: vec![e],
|
|
||||||
time_in_ms,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
},
|
asserts = match entry.response.clone() {
|
||||||
};
|
None => vec![],
|
||||||
|
Some(response) => eval_asserts(
|
||||||
|
response,
|
||||||
|
variables,
|
||||||
|
http_response.clone(),
|
||||||
|
context_dir.clone(),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
errors = asserts
|
||||||
|
.iter()
|
||||||
|
.filter_map(|assert| assert.clone().error())
|
||||||
|
.map(
|
||||||
|
|Error {
|
||||||
|
source_info, inner, ..
|
||||||
|
}| Error {
|
||||||
|
source_info,
|
||||||
|
inner,
|
||||||
|
assert: true,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.collect();
|
||||||
|
|
||||||
// update variables now!
|
if !captures.is_empty() {
|
||||||
for capture_result in captures.clone() {
|
log_verbose("Captures");
|
||||||
variables.insert(capture_result.name, capture_result.value);
|
for capture in captures.clone() {
|
||||||
}
|
log_verbose(format!("{}: {}", capture.name, capture.value).as_str());
|
||||||
|
}
|
||||||
let asserts = match entry.response {
|
}
|
||||||
None => vec![],
|
log_verbose("");
|
||||||
Some(response) => eval_asserts(response, variables, http_response.clone(), context_dir),
|
|
||||||
};
|
|
||||||
let errors = asserts
|
|
||||||
.iter()
|
|
||||||
.filter_map(|assert| assert.clone().error())
|
|
||||||
.map(
|
|
||||||
|Error {
|
|
||||||
source_info, inner, ..
|
|
||||||
}| Error {
|
|
||||||
source_info,
|
|
||||||
inner,
|
|
||||||
assert: true,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
if !captures.is_empty() {
|
|
||||||
log_verbose("Captures");
|
|
||||||
for capture in captures.clone() {
|
|
||||||
log_verbose(format!("{}: {}", capture.name, capture.value).as_str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let entry_result = EntryResult {
|
||||||
|
request: Some(http_request.clone()),
|
||||||
|
response: Some(http_response.clone()),
|
||||||
|
captures,
|
||||||
|
asserts,
|
||||||
|
errors,
|
||||||
|
time_in_ms,
|
||||||
|
};
|
||||||
|
entry_results.push(entry_result);
|
||||||
}
|
}
|
||||||
|
|
||||||
log_verbose("");
|
entry_results
|
||||||
|
|
||||||
EntryResult {
|
|
||||||
request: Some(http_request),
|
|
||||||
response: Some(http_response),
|
|
||||||
captures,
|
|
||||||
asserts,
|
|
||||||
errors,
|
|
||||||
time_in_ms,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn log_request(log_verbose: impl Fn(&str), request: &http::Request) {
|
pub fn log_request(log_verbose: impl Fn(&str), request: &http::RequestSpec) {
|
||||||
log_verbose("Request");
|
log_verbose("Request");
|
||||||
log_verbose(format!("{} {}", request.method, request.url).as_str());
|
log_verbose(format!("{} {}", request.method, request.url).as_str());
|
||||||
for header in request.headers.clone() {
|
for header in request.headers.clone() {
|
||||||
|
@ -124,7 +124,7 @@ pub fn run(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
let entry_result = entry::run(
|
let entry_results = entry::run(
|
||||||
entry,
|
entry,
|
||||||
http_client,
|
http_client,
|
||||||
entry_index,
|
entry_index,
|
||||||
@ -133,12 +133,15 @@ pub fn run(
|
|||||||
&log_verbose,
|
&log_verbose,
|
||||||
&log_error_message,
|
&log_error_message,
|
||||||
);
|
);
|
||||||
entries.push(entry_result.clone());
|
|
||||||
for e in entry_result.errors.clone() {
|
for entry_result in entry_results.clone() {
|
||||||
log_error(&e, false);
|
for e in entry_result.errors.clone() {
|
||||||
|
log_error(&e, false);
|
||||||
|
}
|
||||||
|
entries.push(entry_result.clone());
|
||||||
}
|
}
|
||||||
let exit = (options.post_entry)();
|
let exit = (options.post_entry)();
|
||||||
if exit || (options.fail_fast && !entry_result.errors.is_empty()) {
|
if exit || (options.fail_fast && !entry_results.last().unwrap().errors.is_empty()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use crate::http;
|
|
||||||
use crate::http::*;
|
use crate::http::*;
|
||||||
|
|
||||||
use super::cookie::*;
|
use super::cookie::*;
|
||||||
@ -99,11 +98,90 @@ fn parse_entry_result(value: serde_json::Value) -> Result<EntryResult, String> {
|
|||||||
time_in_ms: 0,
|
time_in_ms: 0,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
// pub fn _parse_request(value: serde_json::Value) -> Result<Request, ParseError> {
|
||||||
|
// if let serde_json::Value::Object(map) = value {
|
||||||
|
// let method = match map.get("method") {
|
||||||
|
// Some(serde_json::Value::String(s)) => parse_method(s.clone())?,
|
||||||
|
// _ => return Err("expecting a string for the method".to_string()),
|
||||||
|
// };
|
||||||
|
// let url = match map.get("url") {
|
||||||
|
// Some(serde_json::Value::String(s)) => s.to_string(),
|
||||||
|
// _ => return Err("expecting a string for the url".to_string()),
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// let headers = match map.get("headers") {
|
||||||
|
// Some(serde_json::Value::Array(values)) => {
|
||||||
|
// let mut headers = vec![];
|
||||||
|
// for value in values {
|
||||||
|
// let header = parse_header(value.clone())?;
|
||||||
|
// headers.push(header);
|
||||||
|
// }
|
||||||
|
// headers
|
||||||
|
// }
|
||||||
|
// _ => vec![],
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// let querystring = match map.get("queryString") {
|
||||||
|
// Some(serde_json::Value::Array(values)) => {
|
||||||
|
// let mut params = vec![];
|
||||||
|
// for value in values {
|
||||||
|
// let param = parse_param(value.clone())?;
|
||||||
|
// params.push(param);
|
||||||
|
// }
|
||||||
|
// params
|
||||||
|
// }
|
||||||
|
// _ => vec![],
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// let form = match map.get("form") {
|
||||||
|
// Some(serde_json::Value::Array(values)) => {
|
||||||
|
// let mut params = vec![];
|
||||||
|
// for value in values {
|
||||||
|
// let param = parse_param(value.clone())?;
|
||||||
|
// params.push(param);
|
||||||
|
// }
|
||||||
|
// params
|
||||||
|
// }
|
||||||
|
// _ => vec![],
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// let cookies = match map.get("cookies") {
|
||||||
|
// Some(serde_json::Value::Array(values)) => {
|
||||||
|
// let mut headers = vec![];
|
||||||
|
// for value in values {
|
||||||
|
// let header = parse_request_cookie(value.clone())?;
|
||||||
|
// headers.push(header);
|
||||||
|
// }
|
||||||
|
// headers
|
||||||
|
// }
|
||||||
|
// _ => vec![],
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// // TODO
|
||||||
|
// let multipart = vec![];
|
||||||
|
// let body = http::Body::Binary(vec![]);
|
||||||
|
// let content_type = None;
|
||||||
|
//
|
||||||
|
// Ok(Request {
|
||||||
|
// method,
|
||||||
|
// url,
|
||||||
|
// headers,
|
||||||
|
// querystring,
|
||||||
|
// form,
|
||||||
|
// multipart,
|
||||||
|
// cookies,
|
||||||
|
// body,
|
||||||
|
// content_type,
|
||||||
|
// })
|
||||||
|
// } else {
|
||||||
|
// Err("expecting an object for the request".to_string())
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
pub fn parse_request(value: serde_json::Value) -> Result<Request, ParseError> {
|
pub fn parse_request(value: serde_json::Value) -> Result<Request, ParseError> {
|
||||||
if let serde_json::Value::Object(map) = value {
|
if let serde_json::Value::Object(map) = value {
|
||||||
let method = match map.get("method") {
|
let method = match map.get("method") {
|
||||||
Some(serde_json::Value::String(s)) => parse_method(s.clone())?,
|
Some(serde_json::Value::String(s)) => parse_method(s.clone())?.to_string(),
|
||||||
_ => return Err("expecting a string for the method".to_string()),
|
_ => return Err("expecting a string for the method".to_string()),
|
||||||
};
|
};
|
||||||
let url = match map.get("url") {
|
let url = match map.get("url") {
|
||||||
@ -123,57 +201,10 @@ pub fn parse_request(value: serde_json::Value) -> Result<Request, ParseError> {
|
|||||||
_ => vec![],
|
_ => vec![],
|
||||||
};
|
};
|
||||||
|
|
||||||
let querystring = match map.get("queryString") {
|
|
||||||
Some(serde_json::Value::Array(values)) => {
|
|
||||||
let mut params = vec![];
|
|
||||||
for value in values {
|
|
||||||
let param = parse_param(value.clone())?;
|
|
||||||
params.push(param);
|
|
||||||
}
|
|
||||||
params
|
|
||||||
}
|
|
||||||
_ => vec![],
|
|
||||||
};
|
|
||||||
|
|
||||||
let form = match map.get("form") {
|
|
||||||
Some(serde_json::Value::Array(values)) => {
|
|
||||||
let mut params = vec![];
|
|
||||||
for value in values {
|
|
||||||
let param = parse_param(value.clone())?;
|
|
||||||
params.push(param);
|
|
||||||
}
|
|
||||||
params
|
|
||||||
}
|
|
||||||
_ => vec![],
|
|
||||||
};
|
|
||||||
|
|
||||||
let cookies = match map.get("cookies") {
|
|
||||||
Some(serde_json::Value::Array(values)) => {
|
|
||||||
let mut headers = vec![];
|
|
||||||
for value in values {
|
|
||||||
let header = parse_request_cookie(value.clone())?;
|
|
||||||
headers.push(header);
|
|
||||||
}
|
|
||||||
headers
|
|
||||||
}
|
|
||||||
_ => vec![],
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO
|
|
||||||
let multipart = vec![];
|
|
||||||
let body = http::Body::Binary(vec![]);
|
|
||||||
let content_type = None;
|
|
||||||
|
|
||||||
Ok(Request {
|
Ok(Request {
|
||||||
method,
|
|
||||||
url,
|
url,
|
||||||
|
method,
|
||||||
headers,
|
headers,
|
||||||
querystring,
|
|
||||||
form,
|
|
||||||
multipart,
|
|
||||||
cookies,
|
|
||||||
body,
|
|
||||||
content_type,
|
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Err("expecting an object for the request".to_string())
|
Err("expecting an object for the request".to_string())
|
||||||
@ -256,37 +287,37 @@ fn parse_header(value: serde_json::Value) -> Result<Header, ParseError> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_param(value: serde_json::Value) -> Result<Param, ParseError> {
|
// pub fn parse_param(value: serde_json::Value) -> Result<Param, ParseError> {
|
||||||
if let serde_json::Value::Object(map) = value {
|
// if let serde_json::Value::Object(map) = value {
|
||||||
let name = match map.get("name") {
|
// let name = match map.get("name") {
|
||||||
Some(serde_json::Value::String(s)) => s.to_string(),
|
// Some(serde_json::Value::String(s)) => s.to_string(),
|
||||||
_ => return Err("expecting a string for the cookie name".to_string()),
|
// _ => return Err("expecting a string for the cookie name".to_string()),
|
||||||
};
|
// };
|
||||||
let value = match map.get("value") {
|
// let value = match map.get("value") {
|
||||||
Some(serde_json::Value::String(s)) => s.to_string(),
|
// Some(serde_json::Value::String(s)) => s.to_string(),
|
||||||
_ => return Err("expecting a string for the cookie value".to_string()),
|
// _ => return Err("expecting a string for the cookie value".to_string()),
|
||||||
};
|
// };
|
||||||
Ok(Param { name, value })
|
// Ok(Param { name, value })
|
||||||
} else {
|
// } else {
|
||||||
Err("Expecting object for the param".to_string())
|
// Err("Expecting object for the param".to_string())
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
pub fn parse_request_cookie(value: serde_json::Value) -> Result<RequestCookie, ParseError> {
|
// pub fn parse_request_cookie(value: serde_json::Value) -> Result<RequestCookie, ParseError> {
|
||||||
if let serde_json::Value::Object(map) = value {
|
// if let serde_json::Value::Object(map) = value {
|
||||||
let name = match map.get("name") {
|
// let name = match map.get("name") {
|
||||||
Some(serde_json::Value::String(s)) => s.to_string(),
|
// Some(serde_json::Value::String(s)) => s.to_string(),
|
||||||
_ => return Err("expecting a string for the cookie name".to_string()),
|
// _ => return Err("expecting a string for the cookie name".to_string()),
|
||||||
};
|
// };
|
||||||
let value = match map.get("value") {
|
// let value = match map.get("value") {
|
||||||
Some(serde_json::Value::String(s)) => s.to_string(),
|
// Some(serde_json::Value::String(s)) => s.to_string(),
|
||||||
_ => return Err("expecting a string for the cookie value".to_string()),
|
// _ => return Err("expecting a string for the cookie value".to_string()),
|
||||||
};
|
// };
|
||||||
Ok(RequestCookie { name, value })
|
// Ok(RequestCookie { name, value })
|
||||||
} else {
|
// } else {
|
||||||
Err("Expecting object for the request cookie".to_string())
|
// Err("Expecting object for the request cookie".to_string())
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn parse_response_cookie(value: serde_json::Value) -> Result<ResponseCookie, ParseError> {
|
pub fn parse_response_cookie(value: serde_json::Value) -> Result<ResponseCookie, ParseError> {
|
||||||
@ -383,59 +414,59 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::runner::value::Value;
|
use crate::runner::value::Value;
|
||||||
|
|
||||||
#[test]
|
// #[test]
|
||||||
fn test_parse_request() {
|
// fn test_parse_request() {
|
||||||
let v: serde_json::Value = serde_json::from_str(
|
// let v: serde_json::Value = serde_json::from_str(
|
||||||
r#"{
|
// r#"{
|
||||||
"method": "GET",
|
// "method": "GET",
|
||||||
"url": "http://localhost:8000/hello",
|
// "url": "http://localhost:8000/hello",
|
||||||
"headers": []
|
// "headers": []
|
||||||
}"#,
|
// }"#,
|
||||||
)
|
// )
|
||||||
.unwrap();
|
// .unwrap();
|
||||||
assert_eq!(parse_request(v).unwrap(), hello_http_request());
|
// assert_eq!(_parse_request(v).unwrap(), hello_http_request());
|
||||||
|
//
|
||||||
let v: serde_json::Value = serde_json::from_str(
|
// let v: serde_json::Value = serde_json::from_str(
|
||||||
r#"{
|
// r#"{
|
||||||
"method": "GET",
|
// "method": "GET",
|
||||||
"url": "http://localhost:8000/querystring-params?param1=value1¶m2=a%20b",
|
// "url": "http://localhost:8000/querystring-params?param1=value1¶m2=a%20b",
|
||||||
"headers": []
|
// "headers": []
|
||||||
}"#,
|
// }"#,
|
||||||
)
|
// )
|
||||||
.unwrap();
|
// .unwrap();
|
||||||
assert_eq!(
|
// assert_eq!(
|
||||||
parse_request(v).unwrap(),
|
// _parse_request(v).unwrap(),
|
||||||
http::Request {
|
// http::Request {
|
||||||
method: http::Method::Get,
|
// method: http::Method::Get,
|
||||||
url: "http://localhost:8000/querystring-params?param1=value1¶m2=a%20b"
|
// url: "http://localhost:8000/querystring-params?param1=value1¶m2=a%20b"
|
||||||
.to_string(),
|
// .to_string(),
|
||||||
querystring: vec![],
|
// querystring: vec![],
|
||||||
headers: vec![],
|
// headers: vec![],
|
||||||
cookies: vec![],
|
// cookies: vec![],
|
||||||
body: http::Body::Binary(vec![]),
|
// body: http::Body::Binary(vec![]),
|
||||||
form: vec![],
|
// form: vec![],
|
||||||
multipart: vec![],
|
// multipart: vec![],
|
||||||
content_type: None,
|
// content_type: None,
|
||||||
}
|
// }
|
||||||
);
|
// );
|
||||||
|
//
|
||||||
let v: serde_json::Value = serde_json::from_str(
|
// let v: serde_json::Value = serde_json::from_str(
|
||||||
r#"{
|
// r#"{
|
||||||
"method": "GET",
|
// "method": "GET",
|
||||||
"url": "http://localhost/custom",
|
// "url": "http://localhost/custom",
|
||||||
"headers": [
|
// "headers": [
|
||||||
{"name": "User-Agent", "value": "iPhone"},
|
// {"name": "User-Agent", "value": "iPhone"},
|
||||||
{"name": "Foo", "value": "Bar"}
|
// {"name": "Foo", "value": "Bar"}
|
||||||
],
|
// ],
|
||||||
"cookies": [
|
// "cookies": [
|
||||||
{"name": "theme", "value": "light"},
|
// {"name": "theme", "value": "light"},
|
||||||
{"name": "sessionToken", "value": "abc123"}
|
// {"name": "sessionToken", "value": "abc123"}
|
||||||
]
|
// ]
|
||||||
}"#,
|
// }"#,
|
||||||
)
|
// )
|
||||||
.unwrap();
|
// .unwrap();
|
||||||
assert_eq!(parse_request(v).unwrap(), custom_http_request());
|
// assert_eq!(_parse_request(v).unwrap(), custom_http_request());
|
||||||
}
|
// }
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_response() {
|
fn test_parse_response() {
|
||||||
|
@ -94,20 +94,12 @@ impl Serialize for Request {
|
|||||||
where
|
where
|
||||||
S: Serializer,
|
S: Serializer,
|
||||||
{
|
{
|
||||||
// 3 is the number of fields in the struct.
|
let mut state = serializer.serialize_struct("Request", 5)?;
|
||||||
let mut state = serializer.serialize_struct("??", 3)?;
|
state.serialize_field("method", &self.clone().method)?;
|
||||||
state.serialize_field("method", &self.clone().method.to_string())?;
|
|
||||||
state.serialize_field("url", &self.clone().url)?;
|
state.serialize_field("url", &self.clone().url)?;
|
||||||
state.serialize_field("queryString", &self.clone().querystring)?;
|
|
||||||
state.serialize_field("headers", &self.clone().headers)?;
|
state.serialize_field("headers", &self.clone().headers)?;
|
||||||
state.serialize_field("cookies", &self.clone().cookies)?;
|
state.serialize_field("cookies", &self.clone().cookies())?;
|
||||||
state.serialize_field("multipartFormData", &self.clone().multipart)?;
|
state.serialize_field("queryString", &self.clone().query_string_params())?;
|
||||||
|
|
||||||
if !self.clone().form.is_empty() {
|
|
||||||
state.serialize_field("form", &self.clone().form)?;
|
|
||||||
}
|
|
||||||
state.serialize_field("body", &base64::encode(&self.body.bytes()))?;
|
|
||||||
|
|
||||||
state.end()
|
state.end()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -118,7 +110,7 @@ impl Serialize for Response {
|
|||||||
S: Serializer,
|
S: Serializer,
|
||||||
{
|
{
|
||||||
// 3 is the number of fields in the struct.
|
// 3 is the number of fields in the struct.
|
||||||
let mut state = serializer.serialize_struct("??", 3)?;
|
let mut state = serializer.serialize_struct("Response", 3)?;
|
||||||
state.serialize_field("httpVersion", &self.clone().version)?;
|
state.serialize_field("httpVersion", &self.clone().version)?;
|
||||||
state.serialize_field("status", &self.clone().status)?;
|
state.serialize_field("status", &self.clone().status)?;
|
||||||
state.serialize_field("cookies", &self.clone().cookies())?;
|
state.serialize_field("cookies", &self.clone().cookies())?;
|
||||||
|
@ -36,7 +36,7 @@ pub fn eval_request(
|
|||||||
request: Request,
|
request: Request,
|
||||||
variables: &HashMap<String, Value>,
|
variables: &HashMap<String, Value>,
|
||||||
context_dir: String,
|
context_dir: String,
|
||||||
) -> Result<http::Request, Error> {
|
) -> Result<http::RequestSpec, Error> {
|
||||||
let method = eval_method(request.method.clone());
|
let method = eval_method(request.method.clone());
|
||||||
|
|
||||||
let url = eval_template(request.clone().url, &variables)?;
|
let url = eval_template(request.clone().url, &variables)?;
|
||||||
@ -121,7 +121,7 @@ pub fn eval_request(
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
Ok(http::Request {
|
Ok(http::RequestSpec {
|
||||||
method,
|
method,
|
||||||
url,
|
url,
|
||||||
headers,
|
headers,
|
||||||
|
@ -212,7 +212,7 @@ pub fn eval_asserts(
|
|||||||
|
|
||||||
pub fn eval_captures(
|
pub fn eval_captures(
|
||||||
response: Response,
|
response: Response,
|
||||||
http_response: http::Response,
|
http_response: &http::Response,
|
||||||
variables: &HashMap<String, Value>,
|
variables: &HashMap<String, Value>,
|
||||||
) -> Result<Vec<CaptureResult>, Error> {
|
) -> Result<Vec<CaptureResult>, Error> {
|
||||||
let mut captures = vec![];
|
let mut captures = vec![];
|
||||||
@ -321,7 +321,7 @@ mod tests {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
eval_captures(
|
eval_captures(
|
||||||
user_response(),
|
user_response(),
|
||||||
http::xml_two_users_http_response(),
|
&http::xml_two_users_http_response(),
|
||||||
&variables,
|
&variables,
|
||||||
)
|
)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
|
@ -14,8 +14,8 @@ fn default_client() -> Client {
|
|||||||
Client::init(options)
|
Client::init(options)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_get_request(url: String) -> Request {
|
fn default_get_request(url: String) -> RequestSpec {
|
||||||
Request {
|
RequestSpec {
|
||||||
method: Method::Get,
|
method: Method::Get,
|
||||||
url,
|
url,
|
||||||
headers: vec![],
|
headers: vec![],
|
||||||
@ -33,13 +33,25 @@ fn default_get_request(url: String) -> Request {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_hello() {
|
fn test_hello() {
|
||||||
let mut client = default_client();
|
let mut client = default_client();
|
||||||
let request = default_get_request("http://localhost:8000/hello".to_string());
|
let request_spec = default_get_request("http://localhost:8000/hello".to_string());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client.curl_command_line(&request),
|
client.curl_command_line(&request_spec),
|
||||||
"curl 'http://localhost:8000/hello'".to_string()
|
"curl 'http://localhost:8000/hello'".to_string()
|
||||||
);
|
);
|
||||||
|
|
||||||
let response = client.execute(&request, 0).unwrap();
|
let (request, response) = client.execute(&request_spec).unwrap();
|
||||||
|
assert_eq!(request.method, "GET".to_string());
|
||||||
|
assert_eq!(request.url, "http://localhost:8000/hello".to_string());
|
||||||
|
assert_eq!(request.headers.len(), 3);
|
||||||
|
assert!(request.headers.contains(&Header {
|
||||||
|
name: "Host".to_string(),
|
||||||
|
value: "localhost:8000".to_string()
|
||||||
|
}));
|
||||||
|
assert!(request.headers.contains(&Header {
|
||||||
|
name: "Accept".to_string(),
|
||||||
|
value: "*/*".to_string()
|
||||||
|
}));
|
||||||
|
|
||||||
assert_eq!(response.version, Version::Http10);
|
assert_eq!(response.version, Version::Http10);
|
||||||
assert_eq!(response.status, 200);
|
assert_eq!(response.status, 200);
|
||||||
assert_eq!(response.body, b"Hello World!".to_vec());
|
assert_eq!(response.body, b"Hello World!".to_vec());
|
||||||
@ -63,7 +75,7 @@ fn test_hello() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_put() {
|
fn test_put() {
|
||||||
let mut client = default_client();
|
let mut client = default_client();
|
||||||
let request = Request {
|
let request_spec = RequestSpec {
|
||||||
method: Method::Put,
|
method: Method::Put,
|
||||||
url: "http://localhost:8000/put".to_string(),
|
url: "http://localhost:8000/put".to_string(),
|
||||||
headers: vec![],
|
headers: vec![],
|
||||||
@ -75,11 +87,22 @@ fn test_put() {
|
|||||||
content_type: None,
|
content_type: None,
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client.curl_command_line(&request),
|
client.curl_command_line(&request_spec),
|
||||||
"curl 'http://localhost:8000/put' -X PUT".to_string()
|
"curl 'http://localhost:8000/put' -X PUT".to_string()
|
||||||
);
|
);
|
||||||
|
|
||||||
let response = client.execute(&request, 0).unwrap();
|
let (request, response) = client.execute(&request_spec).unwrap();
|
||||||
|
assert_eq!(request.method, "PUT".to_string());
|
||||||
|
assert_eq!(request.url, "http://localhost:8000/put".to_string());
|
||||||
|
assert!(request.headers.contains(&Header {
|
||||||
|
name: "Host".to_string(),
|
||||||
|
value: "localhost:8000".to_string()
|
||||||
|
}));
|
||||||
|
assert!(request.headers.contains(&Header {
|
||||||
|
name: "Accept".to_string(),
|
||||||
|
value: "*/*".to_string()
|
||||||
|
}));
|
||||||
|
|
||||||
assert_eq!(response.status, 200);
|
assert_eq!(response.status, 200);
|
||||||
assert!(response.body.is_empty());
|
assert!(response.body.is_empty());
|
||||||
}
|
}
|
||||||
@ -87,7 +110,7 @@ fn test_put() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_patch() {
|
fn test_patch() {
|
||||||
let mut client = default_client();
|
let mut client = default_client();
|
||||||
let request = Request {
|
let request_spec = RequestSpec {
|
||||||
method: Method::Patch,
|
method: Method::Patch,
|
||||||
url: "http://localhost:8000/patch/file.txt".to_string(),
|
url: "http://localhost:8000/patch/file.txt".to_string(),
|
||||||
headers: vec![
|
headers: vec![
|
||||||
@ -112,11 +135,25 @@ fn test_patch() {
|
|||||||
content_type: None,
|
content_type: None,
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client.curl_command_line(&request),
|
client.curl_command_line(&request_spec),
|
||||||
"curl 'http://localhost:8000/patch/file.txt' -X PATCH -H 'Host: www.example.com' -H 'Content-Type: application/example' -H 'If-Match: \"e0023aa4e\"'".to_string()
|
"curl 'http://localhost:8000/patch/file.txt' -X PATCH -H 'Host: www.example.com' -H 'Content-Type: application/example' -H 'If-Match: \"e0023aa4e\"'".to_string()
|
||||||
);
|
);
|
||||||
|
|
||||||
let response = client.execute(&request, 0).unwrap();
|
let (request, response) = client.execute(&request_spec).unwrap();
|
||||||
|
assert_eq!(request.method, "PATCH".to_string());
|
||||||
|
assert_eq!(
|
||||||
|
request.url,
|
||||||
|
"http://localhost:8000/patch/file.txt".to_string()
|
||||||
|
);
|
||||||
|
assert!(request.headers.contains(&Header {
|
||||||
|
name: "Host".to_string(),
|
||||||
|
value: "www.example.com".to_string()
|
||||||
|
}));
|
||||||
|
assert!(request.headers.contains(&Header {
|
||||||
|
name: "Content-Type".to_string(),
|
||||||
|
value: "application/example".to_string()
|
||||||
|
}));
|
||||||
|
|
||||||
assert_eq!(response.status, 204);
|
assert_eq!(response.status, 204);
|
||||||
assert!(response.body.is_empty());
|
assert!(response.body.is_empty());
|
||||||
}
|
}
|
||||||
@ -128,7 +165,7 @@ fn test_patch() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_custom_headers() {
|
fn test_custom_headers() {
|
||||||
let mut client = default_client();
|
let mut client = default_client();
|
||||||
let request = Request {
|
let request_spec = RequestSpec {
|
||||||
method: Method::Get,
|
method: Method::Get,
|
||||||
url: "http://localhost:8000/custom-headers".to_string(),
|
url: "http://localhost:8000/custom-headers".to_string(),
|
||||||
headers: vec![
|
headers: vec![
|
||||||
@ -147,11 +184,20 @@ fn test_custom_headers() {
|
|||||||
};
|
};
|
||||||
assert!(client.options.curl_args().is_empty());
|
assert!(client.options.curl_args().is_empty());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client.curl_command_line(&request),
|
client.curl_command_line(&request_spec),
|
||||||
"curl 'http://localhost:8000/custom-headers' -H 'Fruit: Raspberry' -H 'Fruit: Apple' -H 'Fruit: Banana' -H 'Fruit: Grape' -H 'Color: Green'".to_string()
|
"curl 'http://localhost:8000/custom-headers' -H 'Fruit: Raspberry' -H 'Fruit: Apple' -H 'Fruit: Banana' -H 'Fruit: Grape' -H 'Color: Green'".to_string()
|
||||||
);
|
);
|
||||||
|
|
||||||
let response = client.execute(&request, 0).unwrap();
|
let (request, response) = client.execute(&request_spec).unwrap();
|
||||||
|
assert_eq!(request.method, "GET".to_string());
|
||||||
|
assert_eq!(
|
||||||
|
request.url,
|
||||||
|
"http://localhost:8000/custom-headers".to_string()
|
||||||
|
);
|
||||||
|
assert!(request.headers.contains(&Header {
|
||||||
|
name: "Fruit".to_string(),
|
||||||
|
value: "Raspberry".to_string()
|
||||||
|
}));
|
||||||
assert_eq!(response.status, 200);
|
assert_eq!(response.status, 200);
|
||||||
assert!(response.body.is_empty());
|
assert!(response.body.is_empty());
|
||||||
}
|
}
|
||||||
@ -163,7 +209,7 @@ fn test_custom_headers() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_querystring_params() {
|
fn test_querystring_params() {
|
||||||
let mut client = default_client();
|
let mut client = default_client();
|
||||||
let request = Request {
|
let request_spec = RequestSpec {
|
||||||
method: Method::Get,
|
method: Method::Get,
|
||||||
url: "http://localhost:8000/querystring-params".to_string(),
|
url: "http://localhost:8000/querystring-params".to_string(),
|
||||||
headers: vec![],
|
headers: vec![],
|
||||||
@ -192,10 +238,14 @@ fn test_querystring_params() {
|
|||||||
content_type: None,
|
content_type: None,
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client.curl_command_line(&request),
|
client.curl_command_line(&request_spec),
|
||||||
"curl 'http://localhost:8000/querystring-params?param1=value1¶m2=¶m3=a%3Db¶m4=1%2C2%2C3'".to_string()
|
"curl 'http://localhost:8000/querystring-params?param1=value1¶m2=¶m3=a%3Db¶m4=1%2C2%2C3'".to_string()
|
||||||
);
|
);
|
||||||
let response = client.execute(&request, 0).unwrap();
|
let (request, response) = client.execute(&request_spec).unwrap();
|
||||||
|
assert_eq!(request.method, "GET".to_string());
|
||||||
|
assert_eq!(request.url, "http://localhost:8000/querystring-params?param1=value1¶m2=¶m3=a%3Db¶m4=1%2C2%2C3".to_string());
|
||||||
|
assert_eq!(request.headers.len(), 3);
|
||||||
|
|
||||||
assert_eq!(response.status, 200);
|
assert_eq!(response.status, 200);
|
||||||
assert!(response.body.is_empty());
|
assert!(response.body.is_empty());
|
||||||
}
|
}
|
||||||
@ -207,7 +257,7 @@ fn test_querystring_params() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_form_params() {
|
fn test_form_params() {
|
||||||
let mut client = default_client();
|
let mut client = default_client();
|
||||||
let request = Request {
|
let request_spec = RequestSpec {
|
||||||
method: Method::Post,
|
method: Method::Post,
|
||||||
url: "http://localhost:8000/form-params".to_string(),
|
url: "http://localhost:8000/form-params".to_string(),
|
||||||
headers: vec![],
|
headers: vec![],
|
||||||
@ -236,17 +286,27 @@ fn test_form_params() {
|
|||||||
content_type: Some("application/x-www-form-urlencoded".to_string()),
|
content_type: Some("application/x-www-form-urlencoded".to_string()),
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client.curl_command_line(&request),
|
client.curl_command_line(&request_spec),
|
||||||
"curl 'http://localhost:8000/form-params' --data 'param1=value1' --data 'param2=' --data 'param3=a%3Db' --data 'param4=a%253db'".to_string()
|
"curl 'http://localhost:8000/form-params' --data 'param1=value1' --data 'param2=' --data 'param3=a%3Db' --data 'param4=a%253db'".to_string()
|
||||||
);
|
);
|
||||||
|
|
||||||
let response = client.execute(&request, 0).unwrap();
|
let (request, response) = client.execute(&request_spec).unwrap();
|
||||||
|
assert_eq!(request.method, "POST".to_string());
|
||||||
|
assert_eq!(request.url, "http://localhost:8000/form-params".to_string());
|
||||||
|
assert!(request.headers.contains(&Header {
|
||||||
|
name: "Content-Type".to_string(),
|
||||||
|
value: "application/x-www-form-urlencoded".to_string()
|
||||||
|
}));
|
||||||
|
|
||||||
assert_eq!(response.status, 200);
|
assert_eq!(response.status, 200);
|
||||||
assert!(response.body.is_empty());
|
assert!(response.body.is_empty());
|
||||||
|
|
||||||
// make sure you can reuse client for other request
|
// make sure you can reuse client for other request
|
||||||
let request = default_get_request("http://localhost:8000/hello".to_string());
|
let request = default_get_request("http://localhost:8000/hello".to_string());
|
||||||
let response = client.execute(&request, 0).unwrap();
|
let (request, response) = client.execute(&request).unwrap();
|
||||||
|
assert_eq!(request.method, "GET".to_string());
|
||||||
|
assert_eq!(request.url, "http://localhost:8000/hello".to_string());
|
||||||
|
assert_eq!(request.headers.len(), 3);
|
||||||
assert_eq!(response.status, 200);
|
assert_eq!(response.status, 200);
|
||||||
assert_eq!(response.body, b"Hello World!".to_vec());
|
assert_eq!(response.body, b"Hello World!".to_vec());
|
||||||
}
|
}
|
||||||
@ -256,11 +316,15 @@ fn test_form_params() {
|
|||||||
// region redirect
|
// region redirect
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_follow_location() {
|
fn test_redirect() {
|
||||||
let request = default_get_request("http://localhost:8000/redirect".to_string());
|
let request_spec = default_get_request("http://localhost:8000/redirect".to_string());
|
||||||
|
|
||||||
let mut client = default_client();
|
let mut client = default_client();
|
||||||
let response = client.execute(&request, 0).unwrap();
|
let (request, response) = client.execute(&request_spec).unwrap();
|
||||||
|
assert_eq!(request.method, "GET".to_string());
|
||||||
|
assert_eq!(request.url, "http://localhost:8000/redirect".to_string());
|
||||||
|
assert_eq!(request.headers.len(), 3);
|
||||||
|
|
||||||
assert_eq!(response.status, 302);
|
assert_eq!(response.status, 302);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
response
|
response
|
||||||
@ -270,6 +334,11 @@ fn test_follow_location() {
|
|||||||
"http://localhost:8000/redirected"
|
"http://localhost:8000/redirected"
|
||||||
);
|
);
|
||||||
assert_eq!(client.redirect_count, 0);
|
assert_eq!(client.redirect_count, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_follow_location() {
|
||||||
|
let request_spec = default_get_request("http://localhost:8000/redirect".to_string());
|
||||||
|
|
||||||
let options = ClientOptions {
|
let options = ClientOptions {
|
||||||
follow_location: true,
|
follow_location: true,
|
||||||
@ -288,24 +357,35 @@ fn test_follow_location() {
|
|||||||
let mut client = Client::init(options);
|
let mut client = Client::init(options);
|
||||||
assert_eq!(client.options.curl_args(), vec!["-L".to_string(),]);
|
assert_eq!(client.options.curl_args(), vec!["-L".to_string(),]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client.curl_command_line(&request),
|
client.curl_command_line(&request_spec),
|
||||||
"curl 'http://localhost:8000/redirect' -L".to_string()
|
"curl 'http://localhost:8000/redirect' -L".to_string()
|
||||||
);
|
);
|
||||||
|
|
||||||
let response = client.execute(&request, 0).unwrap();
|
let calls = client.execute_with_redirect(&request_spec).unwrap();
|
||||||
assert_eq!(response.status, 200);
|
assert_eq!(calls.len(), 2);
|
||||||
assert_eq!(
|
|
||||||
response
|
let (request1, response1) = calls.get(0).unwrap();
|
||||||
.get_header_values("Content-Length".to_string())
|
assert_eq!(request1.method, "GET".to_string());
|
||||||
.get(0)
|
assert_eq!(request1.url, "http://localhost:8000/redirect".to_string());
|
||||||
.unwrap(),
|
assert_eq!(request1.headers.len(), 3);
|
||||||
"0"
|
assert_eq!(response1.status, 302);
|
||||||
);
|
assert!(response1.headers.contains(&Header {
|
||||||
|
name: "Location".to_string(),
|
||||||
|
value: "http://localhost:8000/redirected".to_string()
|
||||||
|
}));
|
||||||
|
|
||||||
|
let (request2, response2) = calls.get(1).unwrap();
|
||||||
|
assert_eq!(request2.method, "GET".to_string());
|
||||||
|
assert_eq!(request2.url, "http://localhost:8000/redirected".to_string());
|
||||||
|
assert_eq!(request2.headers.len(), 3);
|
||||||
|
assert_eq!(response2.status, 200);
|
||||||
|
|
||||||
assert_eq!(client.redirect_count, 1);
|
assert_eq!(client.redirect_count, 1);
|
||||||
|
|
||||||
// make sure that the redirect count is reset to 0
|
// make sure that the redirect count is reset to 0
|
||||||
let request = default_get_request("http://localhost:8000/hello".to_string());
|
let request = default_get_request("http://localhost:8000/hello".to_string());
|
||||||
let response = client.execute(&request, 0).unwrap();
|
let calls = client.execute_with_redirect(&request).unwrap();
|
||||||
|
let (_, response) = calls.get(0).unwrap();
|
||||||
assert_eq!(response.status, 200);
|
assert_eq!(response.status, 200);
|
||||||
assert_eq!(response.body, b"Hello World!".to_vec());
|
assert_eq!(response.body, b"Hello World!".to_vec());
|
||||||
assert_eq!(client.redirect_count, 0);
|
assert_eq!(client.redirect_count, 0);
|
||||||
@ -328,18 +408,25 @@ fn test_max_redirect() {
|
|||||||
context_dir: ".".to_string(),
|
context_dir: ".".to_string(),
|
||||||
};
|
};
|
||||||
let mut client = Client::init(options);
|
let mut client = Client::init(options);
|
||||||
let request = default_get_request("http://localhost:8000/redirect".to_string());
|
|
||||||
|
let request_spec = default_get_request("http://localhost:8000/redirect/15".to_string());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client.curl_command_line(&request),
|
client.curl_command_line(&request_spec),
|
||||||
"curl 'http://localhost:8000/redirect' -L --max-redirs 10".to_string()
|
"curl 'http://localhost:8000/redirect/15' -L --max-redirs 10".to_string()
|
||||||
);
|
);
|
||||||
|
let error = client.execute_with_redirect(&request_spec).err().unwrap();
|
||||||
let response = client.execute(&request, 5).unwrap();
|
|
||||||
assert_eq!(response.status, 200);
|
|
||||||
assert_eq!(client.redirect_count, 6);
|
|
||||||
|
|
||||||
let error = client.execute(&request, 11).err().unwrap();
|
|
||||||
assert_eq!(error, HttpError::TooManyRedirect);
|
assert_eq!(error, HttpError::TooManyRedirect);
|
||||||
|
|
||||||
|
let request_spec = default_get_request("http://localhost:8000/redirect/8".to_string());
|
||||||
|
assert_eq!(
|
||||||
|
client.curl_command_line(&request_spec),
|
||||||
|
"curl 'http://localhost:8000/redirect/8' -L --max-redirs 10".to_string()
|
||||||
|
);
|
||||||
|
let calls = client.execute_with_redirect(&request_spec).unwrap();
|
||||||
|
let (request, response) = calls.last().unwrap();
|
||||||
|
assert_eq!(request.url, "http://localhost:8000/redirect/0".to_string());
|
||||||
|
assert_eq!(response.status, 200);
|
||||||
|
assert_eq!(client.redirect_count, 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
@ -349,7 +436,7 @@ fn test_max_redirect() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_multipart_form_data() {
|
fn test_multipart_form_data() {
|
||||||
let mut client = default_client();
|
let mut client = default_client();
|
||||||
let request = Request {
|
let request_spec = RequestSpec {
|
||||||
method: Method::Post,
|
method: Method::Post,
|
||||||
url: "http://localhost:8000/multipart-form-data".to_string(),
|
url: "http://localhost:8000/multipart-form-data".to_string(),
|
||||||
headers: vec![],
|
headers: vec![],
|
||||||
@ -384,17 +471,22 @@ fn test_multipart_form_data() {
|
|||||||
content_type: Some("multipart/form-data".to_string()),
|
content_type: Some("multipart/form-data".to_string()),
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client.curl_command_line(&request),
|
client.curl_command_line(&request_spec),
|
||||||
"curl 'http://localhost:8000/multipart-form-data' -F 'key1=value1' -F 'upload1=@data.txt;type=text/plain' -F 'upload2=@data.html;type=text/html' -F 'upload3=@data.txt;type=text/html'".to_string()
|
"curl 'http://localhost:8000/multipart-form-data' -F 'key1=value1' -F 'upload1=@data.txt;type=text/plain' -F 'upload2=@data.html;type=text/html' -F 'upload3=@data.txt;type=text/html'".to_string()
|
||||||
);
|
);
|
||||||
|
|
||||||
let response = client.execute(&request, 0).unwrap();
|
let (request, response) = client.execute(&request_spec).unwrap();
|
||||||
|
assert!(request.headers.contains(&Header {
|
||||||
|
name: "Content-Length".to_string(),
|
||||||
|
value: "616".to_string(),
|
||||||
|
}));
|
||||||
assert_eq!(response.status, 200);
|
assert_eq!(response.status, 200);
|
||||||
assert!(response.body.is_empty());
|
assert!(response.body.is_empty());
|
||||||
|
|
||||||
// make sure you can reuse client for other request
|
// make sure you can reuse client for other request
|
||||||
let request = default_get_request("http://localhost:8000/hello".to_string());
|
let request_spec = default_get_request("http://localhost:8000/hello".to_string());
|
||||||
let response = client.execute(&request, 0).unwrap();
|
let (request, response) = client.execute(&request_spec).unwrap();
|
||||||
|
assert_eq!(request.method, "GET".to_string());
|
||||||
assert_eq!(response.status, 200);
|
assert_eq!(response.status, 200);
|
||||||
assert_eq!(response.body, b"Hello World!".to_vec());
|
assert_eq!(response.body, b"Hello World!".to_vec());
|
||||||
}
|
}
|
||||||
@ -406,7 +498,7 @@ fn test_multipart_form_data() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_post_bytes() {
|
fn test_post_bytes() {
|
||||||
let mut client = default_client();
|
let mut client = default_client();
|
||||||
let request = Request {
|
let request_spec = RequestSpec {
|
||||||
method: Method::Post,
|
method: Method::Post,
|
||||||
url: "http://localhost:8000/post-base64".to_string(),
|
url: "http://localhost:8000/post-base64".to_string(),
|
||||||
headers: vec![],
|
headers: vec![],
|
||||||
@ -418,10 +510,15 @@ fn test_post_bytes() {
|
|||||||
content_type: None,
|
content_type: None,
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client.curl_command_line(&request),
|
client.curl_command_line(&request_spec),
|
||||||
"curl 'http://localhost:8000/post-base64' -H 'Content-Type: application/octet-stream' --data $'\\x48\\x65\\x6c\\x6c\\x6f\\x20\\x57\\x6f\\x72\\x6c\\x64\\x21'".to_string()
|
"curl 'http://localhost:8000/post-base64' -H 'Content-Type: application/octet-stream' --data $'\\x48\\x65\\x6c\\x6c\\x6f\\x20\\x57\\x6f\\x72\\x6c\\x64\\x21'".to_string()
|
||||||
);
|
);
|
||||||
let response = client.execute(&request, 0).unwrap();
|
let (request, response) = client.execute(&request_spec).unwrap();
|
||||||
|
assert!(request.headers.contains(&Header {
|
||||||
|
name: "Content-Length".to_string(),
|
||||||
|
value: "12".to_string(),
|
||||||
|
}));
|
||||||
|
|
||||||
assert_eq!(response.status, 200);
|
assert_eq!(response.status, 200);
|
||||||
assert!(response.body.is_empty());
|
assert!(response.body.is_empty());
|
||||||
}
|
}
|
||||||
@ -431,7 +528,7 @@ fn test_post_bytes() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_expect() {
|
fn test_expect() {
|
||||||
let mut client = default_client();
|
let mut client = default_client();
|
||||||
let request = Request {
|
let request_spec = RequestSpec {
|
||||||
method: Method::Post,
|
method: Method::Post,
|
||||||
url: "http://localhost:8000/expect".to_string(),
|
url: "http://localhost:8000/expect".to_string(),
|
||||||
headers: vec![Header {
|
headers: vec![Header {
|
||||||
@ -446,11 +543,15 @@ fn test_expect() {
|
|||||||
content_type: None,
|
content_type: None,
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client.curl_command_line(&request),
|
client.curl_command_line(&request_spec),
|
||||||
"curl 'http://localhost:8000/expect' -H 'Expect: 100-continue' -H 'Content-Type:' --data 'data'".to_string()
|
"curl 'http://localhost:8000/expect' -H 'Expect: 100-continue' -H 'Content-Type:' --data 'data'".to_string()
|
||||||
);
|
);
|
||||||
|
|
||||||
let response = client.execute(&request, 0).unwrap();
|
let (request, response) = client.execute(&request_spec).unwrap();
|
||||||
|
assert!(request.headers.contains(&Header {
|
||||||
|
name: "Expect".to_string(),
|
||||||
|
value: "100-continue".to_string(),
|
||||||
|
}));
|
||||||
assert_eq!(response.status, 200);
|
assert_eq!(response.status, 200);
|
||||||
assert_eq!(response.version, Version::Http10);
|
assert_eq!(response.version, Version::Http10);
|
||||||
assert!(response.body.is_empty());
|
assert!(response.body.is_empty());
|
||||||
@ -473,7 +574,7 @@ fn test_basic_authentication() {
|
|||||||
context_dir: ".".to_string(),
|
context_dir: ".".to_string(),
|
||||||
};
|
};
|
||||||
let mut client = Client::init(options);
|
let mut client = Client::init(options);
|
||||||
let request = Request {
|
let request_spec = RequestSpec {
|
||||||
method: Method::Get,
|
method: Method::Get,
|
||||||
url: "http://localhost:8000/basic-authentication".to_string(),
|
url: "http://localhost:8000/basic-authentication".to_string(),
|
||||||
headers: vec![],
|
headers: vec![],
|
||||||
@ -485,16 +586,20 @@ fn test_basic_authentication() {
|
|||||||
content_type: None,
|
content_type: None,
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client.curl_command_line(&request),
|
client.curl_command_line(&request_spec),
|
||||||
"curl 'http://localhost:8000/basic-authentication' --user 'bob:secret'".to_string()
|
"curl 'http://localhost:8000/basic-authentication' --user 'bob:secret'".to_string()
|
||||||
);
|
);
|
||||||
let response = client.execute(&request, 0).unwrap();
|
let (request, response) = client.execute(&request_spec).unwrap();
|
||||||
|
assert!(request.headers.contains(&Header {
|
||||||
|
name: "Authorization".to_string(),
|
||||||
|
value: "Basic Ym9iOnNlY3JldA==".to_string(),
|
||||||
|
}));
|
||||||
assert_eq!(response.status, 200);
|
assert_eq!(response.status, 200);
|
||||||
assert_eq!(response.version, Version::Http10);
|
assert_eq!(response.version, Version::Http10);
|
||||||
assert_eq!(response.body, b"You are authenticated".to_vec());
|
assert_eq!(response.body, b"You are authenticated".to_vec());
|
||||||
|
|
||||||
let mut client = default_client();
|
let mut client = default_client();
|
||||||
let request = Request {
|
let request_spec = RequestSpec {
|
||||||
method: Method::Get,
|
method: Method::Get,
|
||||||
url: "http://bob:secret@localhost:8000/basic-authentication".to_string(),
|
url: "http://bob:secret@localhost:8000/basic-authentication".to_string(),
|
||||||
headers: vec![],
|
headers: vec![],
|
||||||
@ -506,10 +611,14 @@ fn test_basic_authentication() {
|
|||||||
content_type: None,
|
content_type: None,
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
request.curl_args(".".to_string()),
|
request_spec.curl_args(".".to_string()),
|
||||||
vec!["'http://bob:secret@localhost:8000/basic-authentication'".to_string()]
|
vec!["'http://bob:secret@localhost:8000/basic-authentication'".to_string()]
|
||||||
);
|
);
|
||||||
let response = client.execute(&request, 0).unwrap();
|
let (request, response) = client.execute(&request_spec).unwrap();
|
||||||
|
assert!(request.headers.contains(&Header {
|
||||||
|
name: "Authorization".to_string(),
|
||||||
|
value: "Basic Ym9iOnNlY3JldA==".to_string(),
|
||||||
|
}));
|
||||||
assert_eq!(response.status, 200);
|
assert_eq!(response.status, 200);
|
||||||
assert_eq!(response.version, Version::Http10);
|
assert_eq!(response.version, Version::Http10);
|
||||||
assert_eq!(response.body, b"You are authenticated".to_vec());
|
assert_eq!(response.body, b"You are authenticated".to_vec());
|
||||||
@ -521,7 +630,7 @@ fn test_basic_authentication() {
|
|||||||
fn test_error_could_not_resolve_host() {
|
fn test_error_could_not_resolve_host() {
|
||||||
let mut client = default_client();
|
let mut client = default_client();
|
||||||
let request = default_get_request("http://unknown".to_string());
|
let request = default_get_request("http://unknown".to_string());
|
||||||
let error = client.execute(&request, 0).err().unwrap();
|
let error = client.execute(&request).err().unwrap();
|
||||||
|
|
||||||
assert_eq!(error, HttpError::CouldNotResolveHost("unknown".to_string()));
|
assert_eq!(error, HttpError::CouldNotResolveHost("unknown".to_string()));
|
||||||
}
|
}
|
||||||
@ -529,8 +638,8 @@ fn test_error_could_not_resolve_host() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_error_fail_to_connect() {
|
fn test_error_fail_to_connect() {
|
||||||
let mut client = default_client();
|
let mut client = default_client();
|
||||||
let request = default_get_request("http://localhost:9999".to_string());
|
let request_spec = default_get_request("http://localhost:9999".to_string());
|
||||||
let error = client.execute(&request, 0).err().unwrap();
|
let error = client.execute(&request_spec).err().unwrap();
|
||||||
assert_eq!(error, HttpError::FailToConnect);
|
assert_eq!(error, HttpError::FailToConnect);
|
||||||
|
|
||||||
let options = ClientOptions {
|
let options = ClientOptions {
|
||||||
@ -549,7 +658,7 @@ fn test_error_fail_to_connect() {
|
|||||||
};
|
};
|
||||||
let mut client = Client::init(options);
|
let mut client = Client::init(options);
|
||||||
let request = default_get_request("http://localhost:8000/hello".to_string());
|
let request = default_get_request("http://localhost:8000/hello".to_string());
|
||||||
let error = client.execute(&request, 0).err().unwrap();
|
let error = client.execute(&request).err().unwrap();
|
||||||
assert_eq!(error, HttpError::FailToConnect);
|
assert_eq!(error, HttpError::FailToConnect);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -570,8 +679,8 @@ fn test_error_could_not_resolve_proxy_name() {
|
|||||||
context_dir: ".".to_string(),
|
context_dir: ".".to_string(),
|
||||||
};
|
};
|
||||||
let mut client = Client::init(options);
|
let mut client = Client::init(options);
|
||||||
let request = default_get_request("http://localhost:8000/hello".to_string());
|
let request_spec = default_get_request("http://localhost:8000/hello".to_string());
|
||||||
let error = client.execute(&request, 0).err().unwrap();
|
let error = client.execute(&request_spec).err().unwrap();
|
||||||
assert_eq!(error, HttpError::CouldNotResolveProxyName);
|
assert_eq!(error, HttpError::CouldNotResolveProxyName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -592,8 +701,8 @@ fn test_error_ssl() {
|
|||||||
context_dir: ".".to_string(),
|
context_dir: ".".to_string(),
|
||||||
};
|
};
|
||||||
let mut client = Client::init(options);
|
let mut client = Client::init(options);
|
||||||
let request = default_get_request("https://localhost:8001/hello".to_string());
|
let request_spec = default_get_request("https://localhost:8001/hello".to_string());
|
||||||
let error = client.execute(&request, 0).err().unwrap();
|
let error = client.execute(&request_spec).err().unwrap();
|
||||||
let message = if cfg!(windows) {
|
let message = if cfg!(windows) {
|
||||||
"schannel: SEC_E_UNTRUSTED_ROOT (0x80090325) - The certificate chain was issued by an authority that is not trusted.".to_string()
|
"schannel: SEC_E_UNTRUSTED_ROOT (0x80090325) - The certificate chain was issued by an authority that is not trusted.".to_string()
|
||||||
} else {
|
} else {
|
||||||
@ -619,8 +728,8 @@ fn test_timeout() {
|
|||||||
context_dir: ".".to_string(),
|
context_dir: ".".to_string(),
|
||||||
};
|
};
|
||||||
let mut client = Client::init(options);
|
let mut client = Client::init(options);
|
||||||
let request = default_get_request("http://localhost:8000/timeout".to_string());
|
let request_spec = default_get_request("http://localhost:8000/timeout".to_string());
|
||||||
let error = client.execute(&request, 0).err().unwrap();
|
let error = client.execute(&request_spec).err().unwrap();
|
||||||
assert_eq!(error, HttpError::Timeout);
|
assert_eq!(error, HttpError::Timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -642,7 +751,7 @@ fn test_accept_encoding() {
|
|||||||
};
|
};
|
||||||
let mut client = Client::init(options);
|
let mut client = Client::init(options);
|
||||||
|
|
||||||
let request = Request {
|
let request_spec = RequestSpec {
|
||||||
method: Method::Get,
|
method: Method::Get,
|
||||||
url: "http://localhost:8000/compressed/gzip".to_string(),
|
url: "http://localhost:8000/compressed/gzip".to_string(),
|
||||||
headers: vec![],
|
headers: vec![],
|
||||||
@ -653,7 +762,11 @@ fn test_accept_encoding() {
|
|||||||
body: Body::Binary(vec![]),
|
body: Body::Binary(vec![]),
|
||||||
content_type: None,
|
content_type: None,
|
||||||
};
|
};
|
||||||
let response = client.execute(&request, 0).unwrap();
|
let (request, response) = client.execute(&request_spec).unwrap();
|
||||||
|
assert!(request.headers.contains(&Header {
|
||||||
|
name: "Accept-Encoding".to_string(),
|
||||||
|
value: "gzip, deflate, br".to_string()
|
||||||
|
}));
|
||||||
assert_eq!(response.status, 200);
|
assert_eq!(response.status, 200);
|
||||||
assert!(response.headers.contains(&Header {
|
assert!(response.headers.contains(&Header {
|
||||||
name: "Content-Length".to_string(),
|
name: "Content-Length".to_string(),
|
||||||
@ -678,12 +791,12 @@ fn test_connect_timeout() {
|
|||||||
context_dir: ".".to_string(),
|
context_dir: ".".to_string(),
|
||||||
};
|
};
|
||||||
let mut client = Client::init(options);
|
let mut client = Client::init(options);
|
||||||
let request = default_get_request("http://10.0.0.0".to_string());
|
let request_spec = default_get_request("http://10.0.0.0".to_string());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client.curl_command_line(&request),
|
client.curl_command_line(&request_spec),
|
||||||
"curl 'http://10.0.0.0' --connect-timeout 1".to_string()
|
"curl 'http://10.0.0.0' --connect-timeout 1".to_string()
|
||||||
);
|
);
|
||||||
let error = client.execute(&request, 0).err().unwrap();
|
let error = client.execute(&request_spec).err().unwrap();
|
||||||
if cfg!(target_os = "macos") {
|
if cfg!(target_os = "macos") {
|
||||||
assert_eq!(error, HttpError::FailToConnect);
|
assert_eq!(error, HttpError::FailToConnect);
|
||||||
} else {
|
} else {
|
||||||
@ -697,7 +810,7 @@ fn test_connect_timeout() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_cookie() {
|
fn test_cookie() {
|
||||||
let mut client = default_client();
|
let mut client = default_client();
|
||||||
let request = Request {
|
let request_spec = RequestSpec {
|
||||||
method: Method::Get,
|
method: Method::Get,
|
||||||
url: "http://localhost:8000/cookies/set-request-cookie1-valueA".to_string(),
|
url: "http://localhost:8000/cookies/set-request-cookie1-valueA".to_string(),
|
||||||
headers: vec![],
|
headers: vec![],
|
||||||
@ -712,18 +825,22 @@ fn test_cookie() {
|
|||||||
content_type: None,
|
content_type: None,
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client.curl_command_line(&request),
|
client.curl_command_line(&request_spec),
|
||||||
"curl 'http://localhost:8000/cookies/set-request-cookie1-valueA' --cookie 'cookie1=valueA'"
|
"curl 'http://localhost:8000/cookies/set-request-cookie1-valueA' --cookie 'cookie1=valueA'"
|
||||||
.to_string()
|
.to_string()
|
||||||
);
|
);
|
||||||
|
|
||||||
//assert_eq!(request.cookies(), vec!["cookie1=valueA".to_string(),]);
|
//assert_eq!(request.cookies(), vec!["cookie1=valueA".to_string(),]);
|
||||||
|
|
||||||
let response = client.execute(&request, 0).unwrap();
|
let (request, response) = client.execute(&request_spec).unwrap();
|
||||||
|
assert!(request.headers.contains(&Header {
|
||||||
|
name: "Cookie".to_string(),
|
||||||
|
value: "cookie1=valueA".to_string()
|
||||||
|
}));
|
||||||
assert_eq!(response.status, 200);
|
assert_eq!(response.status, 200);
|
||||||
assert!(response.body.is_empty());
|
assert!(response.body.is_empty());
|
||||||
|
|
||||||
let request = Request {
|
let request_spec = RequestSpec {
|
||||||
method: Method::Get,
|
method: Method::Get,
|
||||||
url: "http://localhost:8000/cookies/assert-that-cookie1-is-not-in-session".to_string(),
|
url: "http://localhost:8000/cookies/assert-that-cookie1-is-not-in-session".to_string(),
|
||||||
headers: vec![],
|
headers: vec![],
|
||||||
@ -734,14 +851,14 @@ fn test_cookie() {
|
|||||||
body: Body::Binary(vec![]),
|
body: Body::Binary(vec![]),
|
||||||
content_type: None,
|
content_type: None,
|
||||||
};
|
};
|
||||||
let response = client.execute(&request, 0).unwrap();
|
let (_request, response) = client.execute(&request_spec).unwrap();
|
||||||
assert_eq!(response.status, 200);
|
assert_eq!(response.status, 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_multiple_request_cookies() {
|
fn test_multiple_request_cookies() {
|
||||||
let mut client = default_client();
|
let mut client = default_client();
|
||||||
let request = Request {
|
let request_spec = RequestSpec {
|
||||||
method: Method::Get,
|
method: Method::Get,
|
||||||
url: "http://localhost:8000/cookies/set-multiple-request-cookies".to_string(),
|
url: "http://localhost:8000/cookies/set-multiple-request-cookies".to_string(),
|
||||||
headers: vec![],
|
headers: vec![],
|
||||||
@ -762,11 +879,15 @@ fn test_multiple_request_cookies() {
|
|||||||
content_type: None,
|
content_type: None,
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client.curl_command_line(&request),
|
client.curl_command_line(&request_spec),
|
||||||
"curl 'http://localhost:8000/cookies/set-multiple-request-cookies' --cookie 'user1=Bob; user2=Bill'".to_string()
|
"curl 'http://localhost:8000/cookies/set-multiple-request-cookies' --cookie 'user1=Bob; user2=Bill'".to_string()
|
||||||
);
|
);
|
||||||
|
|
||||||
let response = client.execute(&request, 0).unwrap();
|
let (request, response) = client.execute(&request_spec).unwrap();
|
||||||
|
assert!(request.headers.contains(&Header {
|
||||||
|
name: "Cookie".to_string(),
|
||||||
|
value: "user1=Bob; user2=Bill".to_string()
|
||||||
|
}));
|
||||||
assert_eq!(response.status, 200);
|
assert_eq!(response.status, 200);
|
||||||
assert!(response.body.is_empty());
|
assert!(response.body.is_empty());
|
||||||
}
|
}
|
||||||
@ -774,9 +895,13 @@ fn test_multiple_request_cookies() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_cookie_storage() {
|
fn test_cookie_storage() {
|
||||||
let mut client = default_client();
|
let mut client = default_client();
|
||||||
let request =
|
let request_spec =
|
||||||
default_get_request("http://localhost:8000/cookies/set-session-cookie2-valueA".to_string());
|
default_get_request("http://localhost:8000/cookies/set-session-cookie2-valueA".to_string());
|
||||||
let response = client.execute(&request, 0).unwrap();
|
let (request, response) = client.execute(&request_spec).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
request.url,
|
||||||
|
"http://localhost:8000/cookies/set-session-cookie2-valueA".to_string()
|
||||||
|
);
|
||||||
assert_eq!(response.status, 200);
|
assert_eq!(response.status, 200);
|
||||||
assert!(response.body.is_empty());
|
assert!(response.body.is_empty());
|
||||||
|
|
||||||
@ -791,14 +916,18 @@ fn test_cookie_storage() {
|
|||||||
expires: "0".to_string(),
|
expires: "0".to_string(),
|
||||||
name: "cookie2".to_string(),
|
name: "cookie2".to_string(),
|
||||||
value: "valueA".to_string(),
|
value: "valueA".to_string(),
|
||||||
http_only: false
|
http_only: false,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
let request = default_get_request(
|
let request_spec = default_get_request(
|
||||||
"http://localhost:8000/cookies/assert-that-cookie2-is-valueA".to_string(),
|
"http://localhost:8000/cookies/assert-that-cookie2-is-valueA".to_string(),
|
||||||
);
|
);
|
||||||
let response = client.execute(&request, 0).unwrap();
|
let (request, response) = client.execute(&request_spec).unwrap();
|
||||||
|
assert!(request.headers.contains(&Header {
|
||||||
|
name: "Cookie".to_string(),
|
||||||
|
value: "cookie2=valueA".to_string()
|
||||||
|
}));
|
||||||
assert_eq!(response.status, 200);
|
assert_eq!(response.status, 200);
|
||||||
assert!(response.body.is_empty());
|
assert!(response.body.is_empty());
|
||||||
}
|
}
|
||||||
@ -820,15 +949,24 @@ fn test_cookie_file() {
|
|||||||
context_dir: ".".to_string(),
|
context_dir: ".".to_string(),
|
||||||
};
|
};
|
||||||
let mut client = Client::init(options);
|
let mut client = Client::init(options);
|
||||||
let request = default_get_request(
|
let request_spec = default_get_request(
|
||||||
"http://localhost:8000/cookies/assert-that-cookie2-is-valueA".to_string(),
|
"http://localhost:8000/cookies/assert-that-cookie2-is-valueA".to_string(),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client.curl_command_line(&request),
|
client.curl_command_line(&request_spec),
|
||||||
"curl 'http://localhost:8000/cookies/assert-that-cookie2-is-valueA' --cookie tests/cookies.txt".to_string()
|
"curl 'http://localhost:8000/cookies/assert-that-cookie2-is-valueA' --cookie tests/cookies.txt".to_string()
|
||||||
);
|
);
|
||||||
|
|
||||||
let response = client.execute(&request, 0).unwrap();
|
let (request, response) = client.execute(&request_spec).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
request.url,
|
||||||
|
"http://localhost:8000/cookies/assert-that-cookie2-is-valueA"
|
||||||
|
);
|
||||||
|
assert!(request.headers.contains(&Header {
|
||||||
|
name: "Cookie".to_string(),
|
||||||
|
value: "cookie2=valueA".to_string()
|
||||||
|
}));
|
||||||
|
|
||||||
assert_eq!(response.status, 200);
|
assert_eq!(response.status, 200);
|
||||||
assert!(response.body.is_empty());
|
assert!(response.body.is_empty());
|
||||||
}
|
}
|
||||||
@ -855,12 +993,13 @@ fn test_proxy() {
|
|||||||
context_dir: ".".to_string(),
|
context_dir: ".".to_string(),
|
||||||
};
|
};
|
||||||
let mut client = Client::init(options);
|
let mut client = Client::init(options);
|
||||||
let request = default_get_request("http://localhost:8000/proxy".to_string());
|
let request_spec = default_get_request("http://localhost:8000/proxy".to_string());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client.curl_command_line(&request),
|
client.curl_command_line(&request_spec),
|
||||||
"curl 'http://localhost:8000/proxy' --proxy 'localhost:8888'".to_string()
|
"curl 'http://localhost:8000/proxy' --proxy 'localhost:8888'".to_string()
|
||||||
);
|
);
|
||||||
let response = client.execute(&request, 0).unwrap();
|
let (request, response) = client.execute(&request_spec).unwrap();
|
||||||
|
assert_eq!(request.url, "http://localhost:8000/proxy");
|
||||||
assert_eq!(response.status, 200);
|
assert_eq!(response.status, 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -884,15 +1023,13 @@ fn test_insecure() {
|
|||||||
};
|
};
|
||||||
let mut client = Client::init(options);
|
let mut client = Client::init(options);
|
||||||
assert_eq!(client.options.curl_args(), vec!["--insecure".to_string()]);
|
assert_eq!(client.options.curl_args(), vec!["--insecure".to_string()]);
|
||||||
let request = default_get_request("https://localhost:8001/hello".to_string());
|
let request_spec = default_get_request("https://localhost:8001/hello".to_string());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client.curl_command_line(&request),
|
client.curl_command_line(&request_spec),
|
||||||
"curl 'https://localhost:8001/hello' --insecure".to_string()
|
"curl 'https://localhost:8001/hello' --insecure".to_string()
|
||||||
);
|
);
|
||||||
|
|
||||||
let response = client.execute(&request, 0).unwrap();
|
let (request, response) = client.execute(&request_spec).unwrap();
|
||||||
assert_eq!(response.status, 200);
|
assert_eq!(request.url, "https://localhost:8001/hello");
|
||||||
|
|
||||||
let response = client.execute(&request, 0).unwrap();
|
|
||||||
assert_eq!(response.status, 200);
|
assert_eq!(response.status, 200);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user