1use std::convert::TryFrom;
2use std::fmt;
3use std::time::Duration;
4
5use bytes::Bytes;
6use http::{request::Parts, Method, Request as HttpRequest};
7use serde::Serialize;
8#[cfg(feature = "json")]
9use serde_json;
10use url::Url;
11use web_sys::RequestCredentials;
12
13use super::{Body, Client, Response};
14use crate::header::{HeaderMap, HeaderName, HeaderValue, CONTENT_TYPE};
15
16pub struct Request {
18 method: Method,
19 url: Url,
20 headers: HeaderMap,
21 body: Option<Body>,
22 timeout: Option<Duration>,
23 pub(super) cors: bool,
24 pub(super) credentials: Option<RequestCredentials>,
25}
26
27pub struct RequestBuilder {
29 client: Client,
30 request: crate::Result<Request>,
31}
32
33impl Request {
34 #[inline]
36 pub fn new(method: Method, url: Url) -> Self {
37 Request {
38 method,
39 url,
40 headers: HeaderMap::new(),
41 body: None,
42 timeout: None,
43 cors: true,
44 credentials: None,
45 }
46 }
47
48 #[inline]
50 pub fn method(&self) -> &Method {
51 &self.method
52 }
53
54 #[inline]
56 pub fn method_mut(&mut self) -> &mut Method {
57 &mut self.method
58 }
59
60 #[inline]
62 pub fn url(&self) -> &Url {
63 &self.url
64 }
65
66 #[inline]
68 pub fn url_mut(&mut self) -> &mut Url {
69 &mut self.url
70 }
71
72 #[inline]
74 pub fn headers(&self) -> &HeaderMap {
75 &self.headers
76 }
77
78 #[inline]
80 pub fn headers_mut(&mut self) -> &mut HeaderMap {
81 &mut self.headers
82 }
83
84 #[inline]
86 pub fn body(&self) -> Option<&Body> {
87 self.body.as_ref()
88 }
89
90 #[inline]
92 pub fn body_mut(&mut self) -> &mut Option<Body> {
93 &mut self.body
94 }
95
96 #[inline]
98 pub fn timeout(&self) -> Option<&Duration> {
99 self.timeout.as_ref()
100 }
101
102 #[inline]
104 pub fn timeout_mut(&mut self) -> &mut Option<Duration> {
105 &mut self.timeout
106 }
107
108 pub fn try_clone(&self) -> Option<Request> {
112 let body = match self.body.as_ref() {
113 Some(body) => Some(body.try_clone()?),
114 None => None,
115 };
116
117 Some(Self {
118 method: self.method.clone(),
119 url: self.url.clone(),
120 headers: self.headers.clone(),
121 body,
122 timeout: self.timeout,
123 cors: self.cors,
124 credentials: self.credentials,
125 })
126 }
127}
128
129impl RequestBuilder {
130 pub(super) fn new(client: Client, request: crate::Result<Request>) -> RequestBuilder {
131 RequestBuilder { client, request }
132 }
133
134 pub fn from_parts(client: crate::Client, request: crate::Request) -> crate::RequestBuilder {
136 crate::RequestBuilder {
137 client,
138 request: crate::Result::Ok(request),
139 }
140 }
141
142 pub fn query<T: Serialize + ?Sized>(mut self, query: &T) -> RequestBuilder {
161 let mut error = None;
162 if let Ok(ref mut req) = self.request {
163 let url = req.url_mut();
164 let mut pairs = url.query_pairs_mut();
165 let serializer = serde_urlencoded::Serializer::new(&mut pairs);
166
167 if let Err(err) = query.serialize(serializer) {
168 error = Some(crate::error::builder(err));
169 }
170 }
171 if let Ok(ref mut req) = self.request {
172 if let Some("") = req.url().query() {
173 req.url_mut().set_query(None);
174 }
175 }
176 if let Some(err) = error {
177 self.request = Err(err);
178 }
179 self
180 }
181
182 pub fn form<T: Serialize + ?Sized>(mut self, form: &T) -> RequestBuilder {
193 let mut error = None;
194 if let Ok(ref mut req) = self.request {
195 match serde_urlencoded::to_string(form) {
196 Ok(body) => {
197 req.headers_mut().insert(
198 CONTENT_TYPE,
199 HeaderValue::from_static("application/x-www-form-urlencoded"),
200 );
201 *req.body_mut() = Some(body.into());
202 }
203 Err(err) => error = Some(crate::error::builder(err)),
204 }
205 }
206 if let Some(err) = error {
207 self.request = Err(err);
208 }
209 self
210 }
211
212 #[cfg(feature = "json")]
213 #[cfg_attr(docsrs, doc(cfg(feature = "json")))]
214 pub fn json<T: Serialize + ?Sized>(mut self, json: &T) -> RequestBuilder {
216 let mut error = None;
217 if let Ok(ref mut req) = self.request {
218 match serde_json::to_vec(json) {
219 Ok(body) => {
220 req.headers_mut()
221 .insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
222 *req.body_mut() = Some(body.into());
223 }
224 Err(err) => error = Some(crate::error::builder(err)),
225 }
226 }
227 if let Some(err) = error {
228 self.request = Err(err);
229 }
230 self
231 }
232
233 pub fn basic_auth<U, P>(self, username: U, password: Option<P>) -> RequestBuilder
235 where
236 U: fmt::Display,
237 P: fmt::Display,
238 {
239 let header_value = crate::util::basic_auth(username, password);
240 self.header(crate::header::AUTHORIZATION, header_value)
241 }
242
243 pub fn bearer_auth<T>(self, token: T) -> RequestBuilder
245 where
246 T: fmt::Display,
247 {
248 let header_value = format!("Bearer {token}");
249 self.header(crate::header::AUTHORIZATION, header_value)
250 }
251
252 pub fn body<T: Into<Body>>(mut self, body: T) -> RequestBuilder {
254 if let Ok(ref mut req) = self.request {
255 req.body = Some(body.into());
256 }
257 self
258 }
259
260 pub fn timeout(mut self, timeout: Duration) -> RequestBuilder {
262 if let Ok(ref mut req) = self.request {
263 *req.timeout_mut() = Some(timeout);
264 }
265 self
266 }
267
268 #[cfg(feature = "multipart")]
270 #[cfg_attr(docsrs, doc(cfg(feature = "multipart")))]
271 pub fn multipart(mut self, multipart: super::multipart::Form) -> RequestBuilder {
272 if let Ok(ref mut req) = self.request {
273 *req.body_mut() = Some(Body::from_form(multipart))
274 }
275 self
276 }
277
278 pub fn header<K, V>(mut self, key: K, value: V) -> RequestBuilder
280 where
281 HeaderName: TryFrom<K>,
282 <HeaderName as TryFrom<K>>::Error: Into<http::Error>,
283 HeaderValue: TryFrom<V>,
284 <HeaderValue as TryFrom<V>>::Error: Into<http::Error>,
285 {
286 let mut error = None;
287 if let Ok(ref mut req) = self.request {
288 match <HeaderName as TryFrom<K>>::try_from(key) {
289 Ok(key) => match <HeaderValue as TryFrom<V>>::try_from(value) {
290 Ok(value) => {
291 req.headers_mut().append(key, value);
292 }
293 Err(e) => error = Some(crate::error::builder(e.into())),
294 },
295 Err(e) => error = Some(crate::error::builder(e.into())),
296 };
297 }
298 if let Some(err) = error {
299 self.request = Err(err);
300 }
301 self
302 }
303
304 pub fn headers(mut self, headers: crate::header::HeaderMap) -> RequestBuilder {
308 if let Ok(ref mut req) = self.request {
309 crate::util::replace_headers(req.headers_mut(), headers);
310 }
311 self
312 }
313
314 pub fn fetch_mode_no_cors(mut self) -> RequestBuilder {
324 if let Ok(ref mut req) = self.request {
325 req.cors = false;
326 }
327 self
328 }
329
330 pub fn fetch_credentials_same_origin(mut self) -> RequestBuilder {
340 if let Ok(ref mut req) = self.request {
341 req.credentials = Some(RequestCredentials::SameOrigin);
342 }
343 self
344 }
345
346 pub fn fetch_credentials_include(mut self) -> RequestBuilder {
356 if let Ok(ref mut req) = self.request {
357 req.credentials = Some(RequestCredentials::Include);
358 }
359 self
360 }
361
362 pub fn fetch_credentials_omit(mut self) -> RequestBuilder {
372 if let Ok(ref mut req) = self.request {
373 req.credentials = Some(RequestCredentials::Omit);
374 }
375 self
376 }
377
378 pub fn build(self) -> crate::Result<Request> {
381 self.request
382 }
383
384 pub fn build_split(self) -> (Client, crate::Result<Request>) {
390 (self.client, self.request)
391 }
392
393 pub async fn send(self) -> crate::Result<Response> {
414 let req = self.request?;
415 self.client.execute_request(req).await
416 }
417
418 pub fn try_clone(&self) -> Option<RequestBuilder> {
437 self.request
438 .as_ref()
439 .ok()
440 .and_then(|req| req.try_clone())
441 .map(|req| RequestBuilder {
442 client: self.client.clone(),
443 request: Ok(req),
444 })
445 }
446}
447
448impl fmt::Debug for Request {
449 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
450 fmt_request_fields(&mut f.debug_struct("Request"), self).finish()
451 }
452}
453
454impl fmt::Debug for RequestBuilder {
455 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
456 let mut builder = f.debug_struct("RequestBuilder");
457 match self.request {
458 Ok(ref req) => fmt_request_fields(&mut builder, req).finish(),
459 Err(ref err) => builder.field("error", err).finish(),
460 }
461 }
462}
463
464fn fmt_request_fields<'a, 'b>(
465 f: &'a mut fmt::DebugStruct<'a, 'b>,
466 req: &Request,
467) -> &'a mut fmt::DebugStruct<'a, 'b> {
468 f.field("method", &req.method)
469 .field("url", &req.url)
470 .field("headers", &req.headers)
471}
472
473impl<T> TryFrom<HttpRequest<T>> for Request
474where
475 T: Into<Body>,
476{
477 type Error = crate::Error;
478
479 fn try_from(req: HttpRequest<T>) -> crate::Result<Self> {
480 let (parts, body) = req.into_parts();
481 let Parts {
482 method,
483 uri,
484 headers,
485 ..
486 } = parts;
487 let url = Url::parse(&uri.to_string()).map_err(crate::error::builder)?;
488 Ok(Request {
489 method,
490 url,
491 headers,
492 body: Some(body.into()),
493 timeout: None,
494 cors: true,
495 credentials: None,
496 })
497 }
498}
499
500impl TryFrom<Request> for HttpRequest<Body> {
501 type Error = crate::Error;
502
503 fn try_from(req: Request) -> crate::Result<Self> {
504 let Request {
505 method,
506 url,
507 headers,
508 body,
509 ..
510 } = req;
511
512 let mut req = HttpRequest::builder()
513 .method(method)
514 .uri(url.as_str())
515 .body(body.unwrap_or_else(|| Body::from(Bytes::default())))
516 .map_err(crate::error::builder)?;
517
518 *req.headers_mut() = headers;
519 Ok(req)
520 }
521}