1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
|
use serde_with::DisplayFromStr;
use http::{HeaderMap, HeaderName, HeaderValue, Method};
use reqwest::{Client, Request, Url};
use serde::{Deserialize, Serialize};
use serde_with::serde_as;
use crate::error::{AppError, AppResult};
#[serde_as]
#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct RequestConfig {
#[serde_as(as = "DisplayFromStr")]
#[serde(default = "RequestConfig::default_method")]
method: Method,
#[serde_as(as = "DisplayFromStr")]
url: Url,
#[serde(default)]
headers: Vec<(String, String)>,
}
impl RequestConfig {
pub fn new(method: Method, url: Url, headers: Vec<(String, String)>) -> Self {
Self { method, url, headers }
}
pub fn get(url: Url, headers: Vec<(String, String)>) -> Self {
Self::new(Method::GET, url, headers)
}
pub fn build(&self, client: &Client) -> AppResult<Request> {
let mut builder = client.request(self.method.clone(), self.url.clone());
if !self.headers.is_empty() {
builder = builder.headers(self.header_map()?);
}
Ok(builder.build()?)
}
fn default_method() -> Method {
Method::GET
}
fn header_map(&self) -> AppResult<HeaderMap> {
let mut header_map = HeaderMap::new();
for (name, value) in &self.headers {
let name: HeaderName = name.try_into().map_err(|e| {
AppError::InvalidHttpHeader(format!("[{}] is not a valid HTTP header name ({})", name, e))
})?;
let value: HeaderValue = value.parse().map_err(|e| {
AppError::InvalidHttpHeader(format!("[{}] is not a valid HTTP header value ({})", value, e))
})?;
header_map.insert(name, value);
}
Ok(header_map)
}
}
#[cfg(test)]
mod tests {
use crate::AppError;
use crate::{assert_error, assert_invalid_http_header, test};
use reqwest::ClientBuilder;
use super::*;
test! {
fn simple_get_request() {
let url = Url::parse("https://example.com")?;
let actual = build_get_request(&url, vec![])?;
assert_eq!(actual.method(), Method::GET);
assert_eq!(actual.url(), &url);
}
}
test! {
fn invalid_header_name() {
let url = Url::parse("https://example.com")?;
let invalid_headers = vec![("Content\x00Type".to_string(), "application/json".to_string())];
assert_invalid_http_header!(build_get_request(&url, invalid_headers), "[Content\x00Type] is not a valid HTTP header name");
}
}
test! {
fn invalid_header_value() {
let url = Url::parse("https://example.com")?;
let invalid_headers = vec![("Content-Type".to_string(), "application/json\x00".to_string())];
assert_invalid_http_header!(build_get_request(&url, invalid_headers), "[application/json\x00] is not a valid HTTP header value");
}
}
fn build_get_request(url: &Url, headers: Vec<(String, String)>) -> AppResult<Request> {
let config = RequestConfig::get(url.clone(), headers);
let client = ClientBuilder::new().build()?;
config.build(&client)
}
}
|