Create http::header module.

This commit is contained in:
jcamiel 2022-06-21 18:33:02 +02:00 committed by Fabrice Reix
parent 3b801a782a
commit 451f6e8200
11 changed files with 85 additions and 76 deletions

View File

@ -28,7 +28,7 @@ use super::options::ClientOptions;
use super::request::*; use super::request::*;
use super::request_spec::*; use super::request_spec::*;
use super::response::*; use super::response::*;
use crate::http::{HttpError, Verbosity}; use super::{Header, HttpError, Verbosity};
use std::str::FromStr; use std::str::FromStr;
use url::Url; use url::Url;
@ -310,7 +310,7 @@ impl Client {
.unwrap(); .unwrap();
} }
if get_header_values(&request.headers, "Content-Type").is_empty() { if request.get_header_values("Content-Type").is_empty() {
if let Some(ref s) = request.content_type { if let Some(ref s) = request.content_type {
list.append(format!("Content-Type: {}", s).as_str()) list.append(format!("Content-Type: {}", s).as_str())
.unwrap(); .unwrap();
@ -319,11 +319,11 @@ impl Client {
} }
} }
if get_header_values(&request.headers, "Expect").is_empty() { if request.get_header_values("Expect").is_empty() {
list.append("Expect:").unwrap(); // remove header Expect list.append("Expect:").unwrap(); // remove header Expect
} }
if get_header_values(&request.headers, "User-Agent").is_empty() { if request.get_header_values("User-Agent").is_empty() {
let user_agent = match self.options.user_agent { let user_agent = match self.options.user_agent {
Some(ref u) => u.clone(), Some(ref u) => u.clone(),
None => format!("hurl/{}", clap::crate_version!()), None => format!("hurl/{}", clap::crate_version!()),
@ -334,14 +334,12 @@ impl Client {
if let Some(ref user) = self.options.user { if let Some(ref user) = self.options.user {
let authorization = base64::encode(user.as_bytes()); let authorization = base64::encode(user.as_bytes());
if get_header_values(&request.headers, "Authorization").is_empty() { if request.get_header_values("Authorization").is_empty() {
list.append(format!("Authorization: Basic {}", authorization).as_str()) list.append(format!("Authorization: Basic {}", authorization).as_str())
.unwrap(); .unwrap();
} }
} }
if self.options.compressed if self.options.compressed && request.get_header_values("Accept-Encoding").is_empty() {
&& get_header_values(&request.headers, "Accept-Encoding").is_empty()
{
list.append("Accept-Encoding: gzip, deflate, br").unwrap(); list.append("Accept-Encoding: gzip, deflate, br").unwrap();
} }
@ -468,7 +466,7 @@ impl Client {
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").get(0) { let location = match response.get_header_values("Location").get(0) {
None => return None, None => return None,
Some(value) => value.clone(), Some(value) => value.clone(),
}; };

View File

@ -19,12 +19,6 @@
use core::fmt; use core::fmt;
use std::str::FromStr; use std::str::FromStr;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Header {
pub name: String,
pub value: String,
}
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub struct Cookie { pub struct Cookie {
pub domain: String, pub domain: String,
@ -49,12 +43,6 @@ pub struct Param {
pub value: String, pub value: String,
} }
impl fmt::Display for Header {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}: {}", self.name, self.value)
}
}
impl fmt::Display for Cookie { impl fmt::Display for Cookie {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!( write!(
@ -144,22 +132,6 @@ impl FromStr for Cookie {
} }
} }
///
/// Return a list of headers values for the given header name.
///
pub fn get_header_values(headers: &[Header], expected_name: &str) -> Vec<String> {
headers
.iter()
.filter_map(|Header { name, value }| {
if name.to_lowercase() == expected_name.to_lowercase() {
Some(value.to_string())
} else {
None
}
})
.collect()
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View File

@ -0,0 +1,50 @@
/*
* Hurl (https://hurl.dev)
* Copyright (C) 2022 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;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Header {
pub name: String,
pub value: String,
}
impl fmt::Display for Header {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}: {}", self.name, self.value)
}
}
/// Returns all header values for given name
///
/// # Arguments
///
/// * `headers` - A list of HTTP headers
/// * `name` - A name to filter header (case insensitively)
pub fn get_values(headers: &[Header], name: &str) -> Vec<String> {
headers
.iter()
.filter_map(|Header { name: key, value }| {
if key.to_lowercase() == name.to_lowercase() {
Some(value.to_string())
} else {
None
}
})
.collect()
}

View File

@ -18,8 +18,9 @@
pub use self::client::Client; pub use self::client::Client;
pub use self::cookie::{CookieAttribute, ResponseCookie}; pub use self::cookie::{CookieAttribute, ResponseCookie};
pub use self::core::{Cookie, Header, Param, RequestCookie}; pub use self::core::{Cookie, Param, RequestCookie};
pub use self::error::HttpError; pub use self::error::HttpError;
pub use self::header::Header;
pub use self::options::{ClientOptions, Verbosity}; pub use self::options::{ClientOptions, Verbosity};
pub use self::request::Request; pub use self::request::Request;
#[cfg(test)] #[cfg(test)]
@ -34,6 +35,7 @@ mod client;
mod cookie; mod cookie;
mod core; mod core;
mod error; mod error;
mod header;
mod options; mod options;
mod request; mod request;
mod request_spec; mod request_spec;

View File

@ -17,6 +17,7 @@
*/ */
use super::core::*; use super::core::*;
use super::Header;
use url::Url; use url::Url;
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]

View File

@ -16,6 +16,7 @@
* *
*/ */
use super::{header, Header};
use core::fmt; use core::fmt;
use super::core::*; use super::core::*;
@ -77,6 +78,13 @@ impl Body {
} }
} }
impl RequestSpec {
/// Returns all header values.
pub fn get_header_values(&self, name: &str) -> Vec<String> {
header::get_values(&self.headers, name)
}
}
impl fmt::Display for Method { impl fmt::Display for Method {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let value = match self { let value = match self {

View File

@ -16,11 +16,10 @@
* *
*/ */
use super::{header, Header};
use core::fmt; use core::fmt;
use std::time::Duration; use std::time::Duration;
use super::core::*;
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub struct Response { pub struct Response {
pub version: Version, pub version: Version,
@ -49,34 +48,16 @@ impl fmt::Display for Version {
} }
impl Response { impl Response {
/// /// Returns all header values.
/// return a list of headers values for the given header name pub fn get_header_values(&self, name: &str) -> Vec<String> {
/// header::get_values(&self.headers, name)
pub fn get_header_values(&self, expected_name: String) -> Vec<String> {
get_header_values(&self.headers, &expected_name)
} }
/// /// Returns optional Content-type header value.
///
///
pub fn get_header(&self, name: String) -> Vec<String> {
self.headers
.iter()
.filter(|&h| h.name.to_lowercase() == name.to_lowercase())
.map(|h| h.value.clone())
.collect()
}
///
/// Return optional Content-type header value
///
pub fn content_type(&self) -> Option<String> { pub fn content_type(&self) -> Option<String> {
for header in self.headers.clone() { header::get_values(&self.headers, "Content-Type")
if header.name.to_lowercase().as_str() == "content-type" { .get(0)
return Some(header.value); .cloned()
}
}
None
} }
} }
@ -253,9 +234,9 @@ xxx
duration: Default::default(), duration: Default::default(),
}; };
assert_eq!( assert_eq!(
response.get_header_values("Content-Length".to_string()), response.get_header_values("Content-Length"),
vec!["12".to_string()] vec!["12".to_string()]
); );
assert!(response.get_header_values("Unknown".to_string()).is_empty()); assert!(response.get_header_values("Unknown").is_empty());
} }
} }

View File

@ -15,8 +15,8 @@
* limitations under the License. * limitations under the License.
* *
*/ */
use crate::http::Response; use super::Response;
use crate::http::ResponseCookie; use super::ResponseCookie;
impl Response { impl Response {
pub fn cookies(&self) -> Vec<ResponseCookie> { pub fn cookies(&self) -> Vec<ResponseCookie> {

View File

@ -60,7 +60,7 @@ pub fn eval_query_value(
QueryValue::Status {} => Ok(Some(Value::Integer(i64::from(http_response.status)))), QueryValue::Status {} => Ok(Some(Value::Integer(i64::from(http_response.status)))),
QueryValue::Header { name, .. } => { QueryValue::Header { name, .. } => {
let header_name = eval_template(&name, variables)?; let header_name = eval_template(&name, variables)?;
let values = http_response.get_header(header_name); let values = http_response.get_header_values(&header_name);
if values.is_empty() { if values.is_empty() {
Ok(None) Ok(None)
} else if values.len() == 1 { } else if values.len() == 1 {

View File

@ -72,7 +72,7 @@ pub fn eval_asserts(
} }
Ok(expected) => { Ok(expected) => {
let header_name = header.key.value.clone(); let header_name = header.key.value.clone();
let actuals = http_response.get_header(header_name); let actuals = http_response.get_header_values(&header_name);
if actuals.is_empty() { if actuals.is_empty() {
asserts.push(AssertResult::Header { asserts.push(AssertResult::Header {
actual: Err(Error { actual: Err(Error {

View File

@ -67,7 +67,7 @@ fn test_hello() {
name: "Content-Type".to_string(), name: "Content-Type".to_string(),
value: "text/html; charset=utf-8".to_string(), value: "text/html; charset=utf-8".to_string(),
})); }));
assert_eq!(response.get_header_values("Date".to_string()).len(), 1); assert_eq!(response.get_header_values("Date").len(), 1);
} }
// endregion // endregion
@ -337,10 +337,7 @@ fn test_redirect() {
assert_eq!(response.status, 302); assert_eq!(response.status, 302);
assert_eq!( assert_eq!(
response response.get_header_values("Location").get(0).unwrap(),
.get_header_values("Location".to_string())
.get(0)
.unwrap(),
"http://localhost:8000/redirected" "http://localhost:8000/redirected"
); );
assert_eq!(client.redirect_count, 0); assert_eq!(client.redirect_count, 0);