Creates query_params method on Url.

This commit is contained in:
Jean-Christophe Amiel 2024-05-16 23:09:12 +02:00
parent fd6463b8bd
commit 3f2a7f528e
No known key found for this signature in database
GPG Key ID: 07FF11CFD55356CC
7 changed files with 70 additions and 70 deletions

View File

@ -284,7 +284,7 @@ impl Client {
let stop_dt = start_dt + duration;
let timings = Timings::new(&mut self.handle, start_dt, stop_dt);
let url = Url::new(&url)?;
let url = Url::try_from(url.as_str())?;
let request = Request::new(
&method.to_string(),
url.clone(),

View File

@ -44,12 +44,23 @@ pub struct RequestCookie {
pub value: String,
}
/// A key/value pair used for query params, form params and multipart-form params.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Param {
pub name: String,
pub value: String,
}
impl Param {
/// Creates a new param pair.
pub fn new(name: &str, value: &str) -> Param {
Param {
name: name.to_string(),
value: value.to_string(),
}
}
}
impl fmt::Display for Cookie {
/// Formats this cookie using Netscape cookie format.
///

View File

@ -81,22 +81,6 @@ impl Request {
}
}
/// Extracts query string params from the url of the request.
pub fn query_string_params(&self) -> Vec<Param> {
// TODO: expose a method on [`http::url::Url`] to get query params
// and remove the call to crate url here.
let u = url::Url::parse(&self.url.to_string()).expect("valid url");
let mut params = vec![];
for (name, value) in u.query_pairs() {
let param = Param {
name: name.to_string(),
value: value.to_string(),
};
params.push(param);
}
params
}
/// Returns a list of request headers cookie.
///
/// see <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cookie>
@ -142,13 +126,13 @@ mod tests {
headers.push(Header::new("Accept", "*/*"));
headers.push(Header::new("User-Agent", "hurl/1.0"));
headers.push(Header::new("content-type", "application/json"));
let url = Url::new("http://localhost:8000/hello").unwrap();
let url = Url::try_from("http://localhost:8000/hello").unwrap();
Request::new("GET", url, headers, vec![])
}
fn query_string_request() -> Request {
let url = Url::new("http://localhost:8000/querystring-params?param1=value1&param2=&param3=a%3Db&param4=1%2C2%2C3").unwrap();
let url = Url::try_from("http://localhost:8000/querystring-params?param1=value1&param2=&param3=a%3Db&param4=1%2C2%2C3").unwrap();
Request::new("GET", url, HeaderVec::new(), vec![])
}
@ -156,36 +140,10 @@ mod tests {
fn cookies_request() -> Request {
let mut headers = HeaderVec::new();
headers.push(Header::new("Cookie", "cookie1=value1; cookie2=value2"));
let url = Url::new("http://localhost:8000/cookies").unwrap();
let url = Url::try_from("http://localhost:8000/cookies").unwrap();
Request::new("GET", url, headers, vec![])
}
#[test]
fn test_query_string() {
assert!(hello_request().query_string_params().is_empty());
assert_eq!(
query_string_request().query_string_params(),
vec![
Param {
name: "param1".to_string(),
value: "value1".to_string(),
},
Param {
name: "param2".to_string(),
value: String::new(),
},
Param {
name: "param3".to_string(),
value: "a=b".to_string(),
},
Param {
name: "param4".to_string(),
value: "1,2,3".to_string(),
},
]
);
}
#[test]
fn test_content_type() {
assert_eq!(
@ -247,7 +205,7 @@ mod tests {
assert_eq!(
Request::new(
"",
Url::new("http://localhost").unwrap(),
Url::try_from("http://localhost").unwrap(),
HeaderVec::new(),
vec![]
)
@ -258,7 +216,7 @@ mod tests {
assert_eq!(
Request::new(
"",
Url::new("http://localhost:8000/redirect-relative").unwrap(),
Url::try_from("http://localhost:8000/redirect-relative").unwrap(),
HeaderVec::new(),
vec![]
)
@ -269,7 +227,7 @@ mod tests {
assert_eq!(
Request::new(
"",
Url::new("https://localhost:8000").unwrap(),
Url::try_from("https://localhost:8000").unwrap(),
HeaderVec::new(),
vec![]
)

View File

@ -15,7 +15,7 @@
* limitations under the License.
*
*/
use crate::http::HttpError;
use crate::http::{HttpError, Param};
use std::fmt;
/// A parsed URL.
@ -26,20 +26,12 @@ pub struct Url {
}
impl Url {
/// Parses an absolute URL from a string.
pub fn new(url: &str) -> Result<Self, HttpError> {
let inner = match url::Url::parse(url) {
Ok(url) => url,
Err(e) => return Err(HttpError::InvalidUrl(url.to_string(), e.to_string())),
};
let scheme = inner.scheme();
if scheme != "http" && scheme != "https" {
return Err(HttpError::InvalidUrl(
url.to_string(),
"Missing protocol http or https".to_string(),
));
}
Ok(Url { inner })
/// Returns a list of query parameters (values are URL decoded).
pub fn query_params(&self) -> Vec<Param> {
self.inner
.query_pairs()
.map(|(k, v)| Param::new(&k, &v))
.collect()
}
/// TODO: Temporary method, will be deleted soon
@ -68,6 +60,26 @@ impl Url {
}
}
impl TryFrom<&str> for Url {
type Error = HttpError;
/// Parses an absolute URL from a string.
fn try_from(value: &str) -> Result<Self, Self::Error> {
let inner = match url::Url::parse(value) {
Ok(url) => url,
Err(e) => return Err(HttpError::InvalidUrl(value.to_string(), e.to_string())),
};
let scheme = inner.scheme();
if scheme != "http" && scheme != "https" {
return Err(HttpError::InvalidUrl(
value.to_string(),
"Missing protocol http or https".to_string(),
));
}
Ok(Url { inner })
}
}
impl fmt::Display for Url {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.inner)
@ -77,6 +89,7 @@ impl fmt::Display for Url {
#[cfg(test)]
mod tests {
use super::Url;
use crate::http::Param;
#[test]
fn parse_url_ok() {
@ -88,7 +101,24 @@ mod tests {
"https://localhost:8000",
];
for url in urls {
assert!(Url::new(url).is_ok());
assert!(Url::try_from(url).is_ok());
}
}
#[test]
fn query_params() {
let url = Url::try_from("http://localhost:8000/hello").unwrap();
assert_eq!(url.query_params(), vec![]);
let url = Url::try_from("http://localhost:8000/querystring-params?param1=value1&param2=&param3=a%3Db&param4=1%2C2%2C3").unwrap();
assert_eq!(
url.query_params(),
vec![
Param::new("param1", "value1"),
Param::new("param2", ""),
Param::new("param3", "a=b"),
Param::new("param4", "1,2,3"),
]
);
}
}

View File

@ -136,7 +136,8 @@ impl Request {
let cookies = self.cookies().iter().map(|e| e.to_json()).collect();
map.insert("cookies".to_string(), serde_json::Value::Array(cookies));
let query_string = self
.query_string_params()
.url
.query_params()
.iter()
.map(|e| e.to_json())
.collect();

View File

@ -135,7 +135,7 @@ mod tests {
source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
calls: vec![Call {
request: Request {
url: Url::new("https://foo.com").unwrap(),
url: Url::try_from("https://foo.com").unwrap(),
method: "GET".to_string(),
headers: HeaderVec::new(),
body: vec![],
@ -154,7 +154,7 @@ mod tests {
source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
calls: vec![Call {
request: Request {
url: Url::new("https://bar.com").unwrap(),
url: Url::try_from("https://bar.com").unwrap(),
method: "GET".to_string(),
headers: HeaderVec::new(),
body: vec![],
@ -173,7 +173,7 @@ mod tests {
source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
calls: vec![Call {
request: Request {
url: Url::new("https://baz.com").unwrap(),
url: Url::try_from("https://baz.com").unwrap(),
method: "GET".to_string(),
headers: HeaderVec::new(),
body: vec![],

View File

@ -52,7 +52,7 @@ fn simple_sample() {
fn check_request(request: &Request) {
assert_eq!(
request.url,
Url::new("http://localhost:8000/hello").unwrap()
Url::try_from("http://localhost:8000/hello").unwrap()
);
assert_eq!(request.method, "GET");
let header_names = request