mirror of
https://github.com/Orange-OpenSource/hurl.git
synced 2024-12-23 11:02:43 +03:00
Merge pull request #20 from Orange-OpenSource/feature/log_serialize_deserialize
Serialize/Deserialize new http request/response
This commit is contained in:
commit
1daf83dc99
@ -34,7 +34,7 @@ use hurl::http;
|
||||
use hurl::parser;
|
||||
use hurl::runner;
|
||||
use hurl::runner::core::*;
|
||||
use hurl::runner::log;
|
||||
use hurl::runner::{log_deserialize};
|
||||
use hurl::format;
|
||||
|
||||
|
||||
@ -207,7 +207,7 @@ fn json_file(matches: ArgMatches, logger: format::logger::Logger) -> (Vec<HurlRe
|
||||
std::process::exit(127);
|
||||
}
|
||||
};
|
||||
match log::parse_results(v) {
|
||||
match log_deserialize::parse_results(v) {
|
||||
Err(msg) => {
|
||||
logger.error_message(format!("Existing Hurl json can not be parsed! - {}", msg));
|
||||
std::process::exit(127);
|
||||
|
@ -52,6 +52,23 @@ pub enum Method {
|
||||
Patch,
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Version {
|
||||
Http10,
|
||||
@ -171,4 +188,45 @@ pub mod tests {
|
||||
form: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn custom_http_request() -> Request {
|
||||
Request {
|
||||
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: vec![],
|
||||
multipart: vec![],
|
||||
form: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn form_http_request() -> Request {
|
||||
Request {
|
||||
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: "param1=value1¶m2=¶m3=a%3db¶m4=a%253db".to_string().into_bytes(),
|
||||
multipart: vec![],
|
||||
form: vec![]
|
||||
}
|
||||
}
|
||||
}
|
@ -25,6 +25,18 @@
|
||||
/// and not by the http client.
|
||||
///
|
||||
|
||||
use crate::http::libcurl::core::Response;
|
||||
|
||||
impl Response {
|
||||
pub fn cookies(&self) -> Vec<ResponseCookie> {
|
||||
self.headers
|
||||
.iter()
|
||||
.filter(|&h| h.name.to_lowercase() == "set-cookie")
|
||||
.filter_map(|h| ResponseCookie::parse(h.value.clone()))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Cookie return from HTTP Response
|
||||
/// It contains arbitrary attributes.
|
||||
|
@ -1,180 +0,0 @@
|
||||
/*
|
||||
* 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 serde::ser::{Serializer, SerializeStruct};
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::http;
|
||||
|
||||
use super::core::*;
|
||||
|
||||
impl Serialize for HurlResult {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let mut state = serializer.serialize_struct("??", 3)?;
|
||||
state.serialize_field("filename", &self.clone().filename)?;
|
||||
state.serialize_field("entries", &self.clone().entries)?;
|
||||
state.serialize_field("success", &self.clone().success)?;
|
||||
state.serialize_field("time", &self.time_in_ms)?;
|
||||
state.serialize_field("cookies", &self.cookies)?;
|
||||
state.end()
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for EntryResult {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let mut state = serializer.serialize_struct("EntryResult", 3)?;
|
||||
if let Some(request) = &self.request {
|
||||
state.serialize_field("request", request)?;
|
||||
}
|
||||
if let Some(response) = &self.response {
|
||||
state.serialize_field("response", response)?;
|
||||
}
|
||||
state.serialize_field("captures", &self.captures)?;
|
||||
state.serialize_field("asserts", &self.asserts)?;
|
||||
state.serialize_field("time", &self.time_in_ms)?;
|
||||
state.end()
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for AssertResult {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let mut state = serializer.serialize_struct("??", 3)?;
|
||||
if let AssertResult::Version { source_info, actual, expected } = self {
|
||||
state.serialize_field("source_info", source_info)?;
|
||||
state.serialize_field("actual", actual)?;
|
||||
state.serialize_field("expected", expected)?;
|
||||
};
|
||||
state.end()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Serialize for CaptureResult {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let mut state = serializer.serialize_struct("CaptureResult", 3)?;
|
||||
state.serialize_field("name", self.name.as_str())?;
|
||||
//let (_type, value) = self.value.to_json_value();
|
||||
//state.serialize_field("type", &_type)?;
|
||||
state.serialize_field("value", &self.value)?;
|
||||
state.end()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// http-specific
|
||||
|
||||
|
||||
type ParseError = String;
|
||||
|
||||
|
||||
pub fn parse_results(value: serde_json::Value) -> Result<Vec<HurlResult>, ParseError> {
|
||||
if let serde_json::Value::Array(values) = value {
|
||||
let mut results = vec![];
|
||||
for value in values {
|
||||
let result = parse_result(value)?;
|
||||
results.push(result);
|
||||
}
|
||||
Ok(results)
|
||||
} else {
|
||||
Err("expecting an array of session".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn parse_result(value: serde_json::Value) -> Result<HurlResult, ParseError> {
|
||||
if let serde_json::Value::Object(map) = value.clone() {
|
||||
let filename = map.get("filename").unwrap().as_str().unwrap().to_string();
|
||||
let mut entries = vec![];
|
||||
let entries = if let Some(serde_json::Value::Array(values)) = map.get("entries") {
|
||||
for value in values {
|
||||
let entry = parse_entry_result(value.clone())?;
|
||||
entries.push(entry);
|
||||
}
|
||||
entries
|
||||
} else {
|
||||
return Err("expecting an array of entries".to_string());
|
||||
};
|
||||
let time_in_ms = match value.get("time") {
|
||||
Some(serde_json::Value::Number(n)) => {
|
||||
match n.as_u64() {
|
||||
Some(x) => x as u128,
|
||||
None => return Err("expecting an integer for the time".to_string()),
|
||||
}
|
||||
}
|
||||
_ => return Err("expecting an integer for the time".to_string()),
|
||||
};
|
||||
let success = match value.get("success") {
|
||||
Some(serde_json::Value::Bool(v)) => *v,
|
||||
_ => return Err("expecting a bool for the status".to_string()),
|
||||
};
|
||||
let cookies = match value.get("cookies") {
|
||||
None => vec![],
|
||||
Some(serde_json::Value::Array(values)) => {
|
||||
let mut cookies: Vec<http::cookie::Cookie> = vec![];
|
||||
for value in values {
|
||||
let cookie = http::import::parse_cookie(value.clone())?;
|
||||
cookies.push(cookie);
|
||||
}
|
||||
cookies
|
||||
}
|
||||
Some(_) => return Err("expecting list for cookies".to_string()),
|
||||
};
|
||||
|
||||
Ok(HurlResult { filename, entries, time_in_ms, success, cookies })
|
||||
} else {
|
||||
Err("expecting an object for the result".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_entry_result(value: serde_json::Value) -> Result<EntryResult, String> {
|
||||
let request = match value.get("request") {
|
||||
None => None,
|
||||
Some(v) => {
|
||||
let r = http::import::parse_request(v.clone())?;
|
||||
Some(r)
|
||||
}
|
||||
};
|
||||
let response = match value.get("response") {
|
||||
None => None,
|
||||
Some(v) => {
|
||||
let r = http::import::parse_response(v.clone())?;
|
||||
Some(r)
|
||||
}
|
||||
};
|
||||
Ok(EntryResult {
|
||||
request,
|
||||
response,
|
||||
captures: vec![],
|
||||
asserts: vec![],
|
||||
errors: vec![],
|
||||
time_in_ms: 0,
|
||||
})
|
||||
}
|
||||
|
||||
|
455
src/runner/log_deserialize.rs
Normal file
455
src/runner/log_deserialize.rs
Normal file
@ -0,0 +1,455 @@
|
||||
/*
|
||||
* 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 crate::http;
|
||||
use crate::http::libcurl::core::*;
|
||||
|
||||
use super::cookie::*;
|
||||
use super::core::*;
|
||||
|
||||
type ParseError = String;
|
||||
|
||||
|
||||
pub fn parse_results(value: serde_json::Value) -> Result<Vec<HurlResult>, ParseError> {
|
||||
if let serde_json::Value::Array(values) = value {
|
||||
let mut results = vec![];
|
||||
for value in values {
|
||||
let result = parse_result(value)?;
|
||||
results.push(result);
|
||||
}
|
||||
Ok(results)
|
||||
} else {
|
||||
Err("expecting an array of session".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn parse_result(value: serde_json::Value) -> Result<HurlResult, ParseError> {
|
||||
if let serde_json::Value::Object(map) = value.clone() {
|
||||
let filename = map.get("filename").unwrap().as_str().unwrap().to_string();
|
||||
let mut entries = vec![];
|
||||
let entries = if let Some(serde_json::Value::Array(values)) = map.get("entries") {
|
||||
for value in values {
|
||||
let entry = parse_entry_result(value.clone())?;
|
||||
entries.push(entry);
|
||||
}
|
||||
entries
|
||||
} else {
|
||||
return Err("expecting an array of entries".to_string());
|
||||
};
|
||||
let time_in_ms = match value.get("time") {
|
||||
Some(serde_json::Value::Number(n)) => {
|
||||
match n.as_u64() {
|
||||
Some(x) => x as u128,
|
||||
None => return Err("expecting an integer for the time".to_string()),
|
||||
}
|
||||
}
|
||||
_ => return Err("expecting an integer for the time".to_string()),
|
||||
};
|
||||
let success = match value.get("success") {
|
||||
Some(serde_json::Value::Bool(v)) => *v,
|
||||
_ => return Err("expecting a bool for the status".to_string()),
|
||||
};
|
||||
let cookies = vec![];
|
||||
Ok(HurlResult { filename, entries, time_in_ms, success, cookies })
|
||||
} else {
|
||||
Err("expecting an object for the result".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_entry_result(value: serde_json::Value) -> Result<EntryResult, String> {
|
||||
let request = match value.get("request") {
|
||||
None => None,
|
||||
Some(v) => {
|
||||
let r = http::import::parse_request(v.clone())?;
|
||||
Some(r)
|
||||
}
|
||||
};
|
||||
let response = match value.get("response") {
|
||||
None => None,
|
||||
Some(v) => {
|
||||
let r = http::import::parse_response(v.clone())?;
|
||||
Some(r)
|
||||
}
|
||||
};
|
||||
Ok(EntryResult {
|
||||
request,
|
||||
response,
|
||||
captures: vec![],
|
||||
asserts: vec![],
|
||||
errors: vec![],
|
||||
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 = vec![];
|
||||
|
||||
Ok(Request {
|
||||
method,
|
||||
url,
|
||||
querystring,
|
||||
headers,
|
||||
cookies,
|
||||
body,
|
||||
multipart,
|
||||
form,
|
||||
})
|
||||
} else {
|
||||
Err("expecting an object for the request".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_response(value: serde_json::Value) -> Result<Response, ParseError> {
|
||||
if let serde_json::Value::Object(map) = value {
|
||||
let status = match map.get("status") {
|
||||
Some(serde_json::Value::Number(x)) => if let Some(x) = x.as_u64() {
|
||||
x as u32
|
||||
} else {
|
||||
return Err("expecting a integer for the status".to_string());
|
||||
},
|
||||
_ => return Err("expecting a number for the status".to_string()),
|
||||
};
|
||||
|
||||
let version = match map.get("httpVersion") {
|
||||
Some(serde_json::Value::String(s)) => parse_version(s.clone())?,
|
||||
_ => return Err("expecting a string for the version".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![],
|
||||
};
|
||||
|
||||
Ok(Response {
|
||||
version,
|
||||
status,
|
||||
headers,
|
||||
body: vec![],
|
||||
})
|
||||
} else {
|
||||
Err("expecting an object for the response".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_method(s: String) -> Result<Method, ParseError> {
|
||||
match s.as_str() {
|
||||
"GET" => Ok(Method::Get),
|
||||
"HEAD" => Ok(Method::Head),
|
||||
"POST" => Ok(Method::Post),
|
||||
"PUT" => Ok(Method::Put),
|
||||
"DELETE" => Ok(Method::Delete),
|
||||
"CONNECT" => Ok(Method::Connect),
|
||||
"OPTIONS" => Ok(Method::Options),
|
||||
"TRACE" => Ok(Method::Trace),
|
||||
"PATCH" => Ok(Method::Patch),
|
||||
_ => Err(format!("Invalid method <{}>", s))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn parse_header(value: serde_json::Value) -> Result<Header, ParseError> {
|
||||
if let serde_json::Value::Object(map) = value {
|
||||
let name = match map.get("name") {
|
||||
Some(serde_json::Value::String(s)) => s.to_string(),
|
||||
_ => return Err("expecting a string for the header name".to_string()),
|
||||
};
|
||||
let value = match map.get("value") {
|
||||
Some(serde_json::Value::String(s)) => s.to_string(),
|
||||
_ => return Err("expecting a string for the header value".to_string()),
|
||||
};
|
||||
Ok(Header { name, value })
|
||||
} else {
|
||||
Err("Expecting object for one header".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_param(value: serde_json::Value) -> Result<Param, ParseError> {
|
||||
if let serde_json::Value::Object(map) = value {
|
||||
let name = match map.get("name") {
|
||||
Some(serde_json::Value::String(s)) => s.to_string(),
|
||||
_ => return Err("expecting a string for the cookie name".to_string()),
|
||||
};
|
||||
let value = match map.get("value") {
|
||||
Some(serde_json::Value::String(s)) => s.to_string(),
|
||||
_ => return Err("expecting a string for the cookie value".to_string()),
|
||||
};
|
||||
Ok(Param { name, value })
|
||||
} else {
|
||||
Err("Expecting object for the param".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_request_cookie(value: serde_json::Value) -> Result<RequestCookie, ParseError> {
|
||||
if let serde_json::Value::Object(map) = value {
|
||||
let name = match map.get("name") {
|
||||
Some(serde_json::Value::String(s)) => s.to_string(),
|
||||
_ => return Err("expecting a string for the cookie name".to_string()),
|
||||
};
|
||||
let value = match map.get("value") {
|
||||
Some(serde_json::Value::String(s)) => s.to_string(),
|
||||
_ => return Err("expecting a string for the cookie value".to_string()),
|
||||
};
|
||||
Ok(RequestCookie { name, value })
|
||||
} else {
|
||||
Err("Expecting object for the request cookie".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_response_cookie(value: serde_json::Value) -> Result<ResponseCookie, ParseError> {
|
||||
if let serde_json::Value::Object(map) = value {
|
||||
let name = match map.get("name") {
|
||||
Some(serde_json::Value::String(s)) => s.to_string(),
|
||||
_ => return Err("expecting a string for the cookie name".to_string()),
|
||||
};
|
||||
let value = match map.get("value") {
|
||||
Some(serde_json::Value::String(s)) => s.to_string(),
|
||||
_ => return Err("expecting a string for the cookie value".to_string()),
|
||||
};
|
||||
let mut attributes = vec![];
|
||||
|
||||
match map.get("expires") {
|
||||
None => {}
|
||||
Some(serde_json::Value::String(s)) => attributes.push(CookieAttribute { name: "Expires".to_string(), value: Some(s.to_string()) }),
|
||||
_ => return Err("expecting a string for the cookie expires".to_string()),
|
||||
};
|
||||
match map.get("max_age") {
|
||||
None => {}
|
||||
Some(serde_json::Value::Number(n)) => attributes.push(CookieAttribute { name: "Max-Age".to_string(), value: Some(n.to_string()) }),
|
||||
_ => return Err("expecting an integer for the cookie max_age".to_string()),
|
||||
};
|
||||
match map.get("domain") {
|
||||
None => {}
|
||||
Some(serde_json::Value::String(s)) => attributes.push(CookieAttribute { name: "Domain".to_string(), value: Some(s.to_string()) }),
|
||||
_ => return Err("expecting a string for the cookie domain".to_string()),
|
||||
};
|
||||
match map.get("path") {
|
||||
None => {}
|
||||
Some(serde_json::Value::String(s)) => attributes.push(CookieAttribute { name: "Path".to_string(), value: Some(s.to_string()) }),
|
||||
_ => return Err("expecting a string for the cookie path".to_string()),
|
||||
};
|
||||
|
||||
match map.get("secure") {
|
||||
None => {}
|
||||
Some(serde_json::Value::Bool(true)) => attributes.push(CookieAttribute { name: "Secure".to_string(), value: None }),
|
||||
_ => return Err("expecting a true for the cookie secure flag".to_string()),
|
||||
};
|
||||
match map.get("http_only") {
|
||||
None => {}
|
||||
Some(serde_json::Value::Bool(true)) => attributes.push(CookieAttribute { name: "HttpOnly".to_string(), value: None }),
|
||||
_ => return Err("expecting a true for the cookie http_only flag".to_string()),
|
||||
};
|
||||
match map.get("same_site") {
|
||||
None => {}
|
||||
Some(serde_json::Value::String(s)) => attributes.push(CookieAttribute { name: "SameSite".to_string(), value: Some(s.to_string()) }),
|
||||
_ => return Err("expecting a string for the cookie same_site".to_string()),
|
||||
};
|
||||
|
||||
Ok(ResponseCookie { name, value, attributes })
|
||||
} else {
|
||||
Err("Expecting object for one cookie".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn parse_version(s: String) -> Result<Version, ParseError> {
|
||||
match s.as_str() {
|
||||
"HTTP/1.0" => Ok(Version::Http10),
|
||||
"HTTP/1.1" => Ok(Version::Http11),
|
||||
"HTTP/2" => Ok(Version::Http2),
|
||||
_ => Err("Expecting version HTTP/1.0, HTTP/1.2 or HTTP/2".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::http::libcurl::core::tests::*;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_parse_request() {
|
||||
let v: serde_json::Value = serde_json::from_str(r#"{
|
||||
"method": "GET",
|
||||
"url": "http://localhost:8000/hello",
|
||||
"headers": []
|
||||
}"#).unwrap();
|
||||
assert_eq!(parse_request(v).unwrap(), hello_http_request());
|
||||
|
||||
let v: serde_json::Value = serde_json::from_str(r#"{
|
||||
"method": "GET",
|
||||
"url": "http://localhost:8000/querystring-params?param1=value1¶m2=a%20b",
|
||||
"headers": []
|
||||
}"#).unwrap();
|
||||
assert_eq!(parse_request(v).unwrap(), Request {
|
||||
method: Method::Get,
|
||||
url: "http://localhost:8000/querystring-params?param1=value1¶m2=a%20b".to_string(),
|
||||
querystring: vec![],
|
||||
headers: vec![],
|
||||
cookies: vec![],
|
||||
body: vec![],
|
||||
form: vec![],
|
||||
multipart: vec![],
|
||||
});
|
||||
|
||||
|
||||
let v: serde_json::Value = serde_json::from_str(r#"{
|
||||
"method": "GET",
|
||||
"url": "http://localhost/custom",
|
||||
"headers": [
|
||||
{"name": "User-Agent", "value": "iPhone"},
|
||||
{"name": "Foo", "value": "Bar"}
|
||||
],
|
||||
"cookies": [
|
||||
{"name": "theme", "value": "light"},
|
||||
{"name": "sessionToken", "value": "abc123"}
|
||||
]
|
||||
}"#).unwrap();
|
||||
assert_eq!(parse_request(v).unwrap(), custom_http_request());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_response() {
|
||||
let v: serde_json::Value = serde_json::from_str(r#"{
|
||||
"status": 200,
|
||||
"httpVersion": "HTTP/1.0",
|
||||
"headers": [
|
||||
{"name": "Content-Type", "value": "text/html; charset=utf-8" },
|
||||
{"name": "Content-Length", "value": "12" }
|
||||
|
||||
]
|
||||
}"#).unwrap();
|
||||
assert_eq!(parse_response(v).unwrap(), Response {
|
||||
version: Version::Http10,
|
||||
status: 200,
|
||||
headers: vec![
|
||||
Header { name: String::from("Content-Type"), value: String::from("text/html; charset=utf-8") },
|
||||
Header { name: String::from("Content-Length"), value: String::from("12") },
|
||||
],
|
||||
body: vec![],
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_method() {
|
||||
assert_eq!(parse_method("GET".to_string()).unwrap(), Method::Get);
|
||||
|
||||
let error = parse_method("x".to_string()).err().unwrap();
|
||||
assert_eq!(error, "Invalid method <x>");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_header() {
|
||||
let v: serde_json::Value = serde_json::from_str(r#"{
|
||||
"name": "name1",
|
||||
"value": "value1"
|
||||
}"#).unwrap();
|
||||
assert_eq!(parse_header(v).unwrap(), Header { name: "name1".to_string(), value: "value1".to_string() });
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_response_cookie() {
|
||||
let v: serde_json::Value = serde_json::from_str(r#"{
|
||||
"name": "name1",
|
||||
"value": "value1"
|
||||
}"#).unwrap();
|
||||
assert_eq!(parse_response_cookie(v).unwrap(),
|
||||
ResponseCookie {
|
||||
name: "name1".to_string(),
|
||||
value: "value1".to_string(),
|
||||
attributes: vec![],
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_version() {
|
||||
assert_eq!(parse_version("HTTP/1.0".to_string()).unwrap(), Version::Http10);
|
||||
}
|
||||
}
|
237
src/runner/log_serialize.rs
Normal file
237
src/runner/log_serialize.rs
Normal file
@ -0,0 +1,237 @@
|
||||
/*
|
||||
* 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 serde::ser::{Serializer, SerializeStruct};
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::http::libcurl::core::*;
|
||||
|
||||
use super::cookie::*;
|
||||
use super::core::*;
|
||||
|
||||
impl Serialize for HurlResult {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let mut state = serializer.serialize_struct("??", 3)?;
|
||||
state.serialize_field("filename", &self.clone().filename)?;
|
||||
state.serialize_field("entries", &self.clone().entries)?;
|
||||
state.serialize_field("success", &self.clone().success)?;
|
||||
state.serialize_field("time", &self.time_in_ms)?;
|
||||
state.serialize_field("cookies", &self.cookies)?;
|
||||
state.end()
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for EntryResult {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let mut state = serializer.serialize_struct("EntryResult", 3)?;
|
||||
if let Some(request) = &self.request {
|
||||
state.serialize_field("request", request)?;
|
||||
}
|
||||
if let Some(response) = &self.response {
|
||||
state.serialize_field("response", response)?;
|
||||
}
|
||||
state.serialize_field("captures", &self.captures)?;
|
||||
state.serialize_field("asserts", &self.asserts)?;
|
||||
state.serialize_field("time", &self.time_in_ms)?;
|
||||
state.end()
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for AssertResult {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let mut state = serializer.serialize_struct("??", 3)?;
|
||||
if let AssertResult::Version { source_info, actual, expected } = self {
|
||||
state.serialize_field("source_info", source_info)?;
|
||||
state.serialize_field("actual", actual)?;
|
||||
state.serialize_field("expected", expected)?;
|
||||
};
|
||||
state.end()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Serialize for CaptureResult {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let mut state = serializer.serialize_struct("CaptureResult", 3)?;
|
||||
state.serialize_field("name", self.name.as_str())?;
|
||||
state.serialize_field("value", &self.value)?;
|
||||
state.end()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Serialize for Request {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
// 3 is the number of fields in the struct.
|
||||
let mut state = serializer.serialize_struct("??", 3)?;
|
||||
state.serialize_field("method", &self.clone().method.to_string())?;
|
||||
state.serialize_field("url", &self.clone().url)?;
|
||||
state.serialize_field("queryString", &self.clone().querystring)?;
|
||||
state.serialize_field("headers", &self.clone().headers)?;
|
||||
state.serialize_field("cookies", &self.clone().cookies)?;
|
||||
|
||||
if !self.clone().form.is_empty() {
|
||||
state.serialize_field("form", &self.clone().form)?;
|
||||
}
|
||||
state.serialize_field("body", &base64::encode(&self.body))?;
|
||||
|
||||
state.end()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Serialize for Response {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
// 3 is the number of fields in the struct.
|
||||
let mut state = serializer.serialize_struct("??", 3)?;
|
||||
state.serialize_field("httpVersion", &self.clone().version)?;
|
||||
state.serialize_field("status", &self.clone().status)?;
|
||||
state.serialize_field("cookies", &self.clone().cookies())?;
|
||||
state.serialize_field("headers", &self.clone().headers)?;
|
||||
|
||||
// TODO Serialize body
|
||||
// let content_type = self.get_header("content_type", true);
|
||||
// if let Some(value) = content_type.first() {
|
||||
// if value.as_str() == "application/json; charset=UTF-8" {
|
||||
// let s = String::from_utf8(self.body.clone()).expect("Found invalid UTF-8");
|
||||
// let result: Result<serde_json::Value, serde_json::Error> = serde_json::from_str(s.as_str());
|
||||
// if let Ok(v) = result {
|
||||
// state.serialize_field("json", &v)?;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
state.end()
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Header {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let mut state = serializer.serialize_struct("??", 3)?;
|
||||
state.serialize_field("name", &self.name)?;
|
||||
state.serialize_field("value", &self.value)?;
|
||||
state.end()
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Param {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let mut state = serializer.serialize_struct("??", 3)?;
|
||||
state.serialize_field("name", &self.name)?;
|
||||
state.serialize_field("value", &self.value)?;
|
||||
state.end()
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for RequestCookie {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let mut state = serializer.serialize_struct("??", 2)?;
|
||||
state.serialize_field("name", &self.name)?;
|
||||
state.serialize_field("value", &self.value)?;
|
||||
state.end()
|
||||
}
|
||||
}
|
||||
|
||||
//impl Serialize for Cookie {
|
||||
// fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
// where
|
||||
// S: Serializer,
|
||||
// {
|
||||
// let mut state = serializer.serialize_struct("InternalCookie", 3)?;
|
||||
// state.serialize_field("name", &self.clone().name)?;
|
||||
// state.serialize_field("value", &self.clone().value)?;
|
||||
// state.serialize_field("domain", &self.clone().domain)?;
|
||||
// state.serialize_field("path", &self.clone().path)?;
|
||||
// state.serialize_field("include_subdomain", &self.clone().subdomains)?;
|
||||
// state.end()
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
impl Serialize for Version {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
match self {
|
||||
Version::Http10 => serializer.serialize_str("HTTP/1.0"),
|
||||
Version::Http11 => serializer.serialize_str("HTTP/1.1"),
|
||||
Version::Http2 => serializer.serialize_str("HTTP/2"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for ResponseCookie {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let mut state = serializer.serialize_struct("ResponseCookie", 3)?;
|
||||
state.serialize_field("name", &self.clone().name)?;
|
||||
state.serialize_field("value", &self.clone().value)?;
|
||||
if let Some(expires) = &self.clone().expires() {
|
||||
state.serialize_field("expires", expires)?;
|
||||
}
|
||||
if let Some(max_age) = &self.clone().max_age() {
|
||||
state.serialize_field("max_age", max_age)?;
|
||||
}
|
||||
if let Some(domain) = &self.clone().domain() {
|
||||
state.serialize_field("domain", domain)?;
|
||||
}
|
||||
if let Some(path) = &self.clone().path() {
|
||||
state.serialize_field("path", path)?;
|
||||
}
|
||||
if self.clone().has_secure() {
|
||||
state.serialize_field("secure", &true)?;
|
||||
}
|
||||
if self.clone().has_httponly() {
|
||||
state.serialize_field("httponly", &true)?;
|
||||
}
|
||||
if let Some(samesite) = &self.clone().samesite() {
|
||||
state.serialize_field("samesite", samesite)?;
|
||||
}
|
||||
state.end()
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,8 @@ mod entry;
|
||||
pub mod file;
|
||||
mod http_response;
|
||||
mod json;
|
||||
pub mod log;
|
||||
pub mod log_serialize;
|
||||
pub mod log_deserialize;
|
||||
mod predicate;
|
||||
mod query;
|
||||
pub mod request;
|
||||
|
Loading…
Reference in New Issue
Block a user