|
4 | 4 |
|
5 | 5 | //! Types and functions related to HTTP request. |
6 | 6 |
|
7 | | -use http::{header::HeaderName, Method}; |
8 | | -pub use http::{HeaderMap, StatusCode}; |
9 | | -use serde::{Deserialize, Serialize}; |
| 7 | +use http::Method; |
| 8 | +pub use http::StatusCode; |
| 9 | +use serde::{Deserialize, Deserializer, Serialize}; |
10 | 10 | use serde_json::Value; |
11 | 11 | use serde_repr::{Deserialize_repr, Serialize_repr}; |
12 | 12 | use url::Url; |
13 | 13 |
|
14 | 14 | use std::{collections::HashMap, time::Duration}; |
15 | 15 |
|
| 16 | +#[cfg(feature = "reqwest-client")] |
| 17 | +pub use reqwest::header; |
| 18 | + |
| 19 | +#[cfg(not(feature = "reqwest-client"))] |
| 20 | +pub use attohttpc::header; |
| 21 | + |
| 22 | +use header::{HeaderName, HeaderValue}; |
| 23 | + |
16 | 24 | /// The builder of [`Client`]. |
17 | 25 | #[derive(Debug, Clone, Default, Deserialize)] |
18 | 26 | #[serde(rename_all = "camelCase")] |
@@ -107,11 +115,8 @@ impl Client { |
107 | 115 | } |
108 | 116 |
|
109 | 117 | if let Some(headers) = request.headers { |
110 | | - for (header, header_value) in headers.iter() { |
111 | | - request_builder = request_builder.header( |
112 | | - HeaderName::from_bytes(header.as_bytes())?, |
113 | | - header_value.as_bytes(), |
114 | | - ); |
| 118 | + for (name, value) in headers.0.iter() { |
| 119 | + request_builder = request_builder.header(name, value); |
115 | 120 | } |
116 | 121 | } |
117 | 122 |
|
@@ -183,16 +188,12 @@ impl Client { |
183 | 188 | }; |
184 | 189 | } |
185 | 190 |
|
186 | | - let mut http_request = request_builder.build()?; |
187 | 191 | if let Some(headers) = request.headers { |
188 | | - for (header, value) in headers.iter() { |
189 | | - http_request.headers_mut().insert( |
190 | | - HeaderName::from_bytes(header.as_bytes())?, |
191 | | - http::header::HeaderValue::from_bytes(value.as_bytes())?, |
192 | | - ); |
193 | | - } |
| 192 | + request_builder = request_builder.headers(headers.0); |
194 | 193 | } |
195 | 194 |
|
| 195 | + let http_request = request_builder.build()?; |
| 196 | + |
196 | 197 | let response = self.0.execute(http_request).await?; |
197 | 198 |
|
198 | 199 | Ok(Response( |
@@ -252,6 +253,34 @@ pub enum Body { |
252 | 253 | Bytes(Vec<u8>), |
253 | 254 | } |
254 | 255 |
|
| 256 | +/// A set of HTTP headers. |
| 257 | +#[derive(Debug, Default)] |
| 258 | +pub struct HeaderMap(header::HeaderMap); |
| 259 | + |
| 260 | +impl<'de> Deserialize<'de> for HeaderMap { |
| 261 | + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> |
| 262 | + where |
| 263 | + D: Deserializer<'de>, |
| 264 | + { |
| 265 | + let map = HashMap::<String, String>::deserialize(deserializer)?; |
| 266 | + let mut headers = header::HeaderMap::default(); |
| 267 | + for (key, value) in map { |
| 268 | + if let (Ok(key), Ok(value)) = ( |
| 269 | + header::HeaderName::from_bytes(key.as_bytes()), |
| 270 | + header::HeaderValue::from_str(&value), |
| 271 | + ) { |
| 272 | + headers.insert(key, value); |
| 273 | + } else { |
| 274 | + return Err(serde::de::Error::custom(format!( |
| 275 | + "invalid header `{}` `{}`", |
| 276 | + key, value |
| 277 | + ))); |
| 278 | + } |
| 279 | + } |
| 280 | + Ok(Self(headers)) |
| 281 | + } |
| 282 | +} |
| 283 | + |
255 | 284 | /// The builder for a HTTP request. |
256 | 285 | /// |
257 | 286 | /// # Examples |
@@ -281,7 +310,7 @@ pub struct HttpRequestBuilder { |
281 | 310 | /// The request query params |
282 | 311 | pub query: Option<HashMap<String, String>>, |
283 | 312 | /// The request headers |
284 | | - pub headers: Option<HashMap<String, String>>, |
| 313 | + pub headers: Option<HeaderMap>, |
285 | 314 | /// The request body |
286 | 315 | pub body: Option<Body>, |
287 | 316 | /// Timeout for the whole request |
@@ -311,10 +340,28 @@ impl HttpRequestBuilder { |
311 | 340 | self |
312 | 341 | } |
313 | 342 |
|
| 343 | + /// Adds a header. |
| 344 | + pub fn header<K, V>(mut self, key: K, value: V) -> crate::api::Result<Self> |
| 345 | + where |
| 346 | + HeaderName: TryFrom<K>, |
| 347 | + <HeaderName as TryFrom<K>>::Error: Into<http::Error>, |
| 348 | + HeaderValue: TryFrom<V>, |
| 349 | + <HeaderValue as TryFrom<V>>::Error: Into<http::Error>, |
| 350 | + { |
| 351 | + let key: Result<HeaderName, http::Error> = key.try_into().map_err(Into::into); |
| 352 | + let value: Result<HeaderValue, http::Error> = value.try_into().map_err(Into::into); |
| 353 | + self |
| 354 | + .headers |
| 355 | + .get_or_insert_with(Default::default) |
| 356 | + .0 |
| 357 | + .insert(key?, value?); |
| 358 | + Ok(self) |
| 359 | + } |
| 360 | + |
314 | 361 | /// Sets the request headers. |
315 | 362 | #[must_use] |
316 | | - pub fn headers(mut self, headers: HashMap<String, String>) -> Self { |
317 | | - self.headers = Some(headers); |
| 363 | + pub fn headers(mut self, headers: header::HeaderMap) -> Self { |
| 364 | + self.headers.replace(HeaderMap(headers)); |
318 | 365 | self |
319 | 366 | } |
320 | 367 |
|
@@ -356,7 +403,7 @@ impl Response { |
356 | 403 | } |
357 | 404 |
|
358 | 405 | /// Get the headers of this Response. |
359 | | - pub fn headers(&self) -> &HeaderMap { |
| 406 | + pub fn headers(&self) -> &header::HeaderMap { |
360 | 407 | self.1.headers() |
361 | 408 | } |
362 | 409 |
|
|
0 commit comments