1use std::convert::TryFrom;
2use std::fmt;
3use std::io::Write;
4
5use base64::write::EncoderWriter as Base64Encoder;
6use bytes::Bytes;
7use http::{request::Parts, Method, Request as HttpRequest};
8use serde::Serialize;
9#[cfg(feature = "json")]
10use serde_json;
11use url::Url;
12use web_sys::RequestCredentials;
13
14use super::{Body, Client, Response};
15use crate::header::{HeaderMap, HeaderName, HeaderValue, CONTENT_TYPE};
16
17pub struct Request {
19 method: Method,
20 url: Url,
21 headers: HeaderMap,
22 body: Option<Body>,
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 cors: true,
43 credentials: None,
44 }
45 }
46
47 #[inline]
49 pub fn method(&self) -> &Method {
50 &self.method
51 }
52
53 #[inline]
55 pub fn method_mut(&mut self) -> &mut Method {
56 &mut self.method
57 }
58
59 #[inline]
61 pub fn url(&self) -> &Url {
62 &self.url
63 }
64
65 #[inline]
67 pub fn url_mut(&mut self) -> &mut Url {
68 &mut self.url
69 }
70
71 #[inline]
73 pub fn headers(&self) -> &HeaderMap {
74 &self.headers
75 }
76
77 #[inline]
79 pub fn headers_mut(&mut self) -> &mut HeaderMap {
80 &mut self.headers
81 }
82
83 #[inline]
85 pub fn body(&self) -> Option<&Body> {
86 self.body.as_ref()
87 }
88
89 #[inline]
91 pub fn body_mut(&mut self) -> &mut Option<Body> {
92 &mut self.body
93 }
94
95 pub fn try_clone(&self) -> Option<Request> {
99 let body = match self.body.as_ref() {
100 Some(body) => Some(body.try_clone()?),
101 None => None,
102 };
103
104 Some(Self {
105 method: self.method.clone(),
106 url: self.url.clone(),
107 headers: self.headers.clone(),
108 body,
109 cors: self.cors,
110 credentials: self.credentials,
111 })
112 }
113}
114
115impl RequestBuilder {
116 pub(super) fn new(client: Client, request: crate::Result<Request>) -> RequestBuilder {
117 RequestBuilder { client, request }
118 }
119
120 pub fn query<T: Serialize + ?Sized>(mut self, query: &T) -> RequestBuilder {
139 let mut error = None;
140 if let Ok(ref mut req) = self.request {
141 let url = req.url_mut();
142 let mut pairs = url.query_pairs_mut();
143 let serializer = serde_urlencoded::Serializer::new(&mut pairs);
144
145 if let Err(err) = query.serialize(serializer) {
146 error = Some(crate::error::builder(err));
147 }
148 }
149 if let Ok(ref mut req) = self.request {
150 if let Some("") = req.url().query() {
151 req.url_mut().set_query(None);
152 }
153 }
154 if let Some(err) = error {
155 self.request = Err(err);
156 }
157 self
158 }
159
160 pub fn form<T: Serialize + ?Sized>(mut self, form: &T) -> RequestBuilder {
171 let mut error = None;
172 if let Ok(ref mut req) = self.request {
173 match serde_urlencoded::to_string(form) {
174 Ok(body) => {
175 req.headers_mut().insert(
176 CONTENT_TYPE,
177 HeaderValue::from_static("application/x-www-form-urlencoded"),
178 );
179 *req.body_mut() = Some(body.into());
180 }
181 Err(err) => error = Some(crate::error::builder(err)),
182 }
183 }
184 if let Some(err) = error {
185 self.request = Err(err);
186 }
187 self
188 }
189
190 #[cfg(feature = "json")]
191 #[cfg_attr(docsrs, doc(cfg(feature = "json")))]
192 pub fn json<T: Serialize + ?Sized>(mut self, json: &T) -> RequestBuilder {
194 let mut error = None;
195 if let Ok(ref mut req) = self.request {
196 match serde_json::to_vec(json) {
197 Ok(body) => {
198 req.headers_mut()
199 .insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
200 *req.body_mut() = Some(body.into());
201 }
202 Err(err) => error = Some(crate::error::builder(err)),
203 }
204 }
205 if let Some(err) = error {
206 self.request = Err(err);
207 }
208 self
209 }
210
211 pub fn basic_auth<U, P>(self, username: U, password: Option<P>) -> RequestBuilder
213 where
214 U: fmt::Display,
215 P: fmt::Display,
216 {
217 let mut header_value = b"Basic ".to_vec();
218 {
219 let mut encoder = Base64Encoder::new(&mut header_value, base64::STANDARD);
220 write!(encoder, "{}:", username).unwrap();
222 if let Some(password) = password {
223 write!(encoder, "{}", password).unwrap();
224 }
225 }
226
227 self.header(crate::header::AUTHORIZATION, header_value)
228 }
229
230 pub fn bearer_auth<T>(self, token: T) -> RequestBuilder
232 where
233 T: fmt::Display,
234 {
235 let header_value = format!("Bearer {}", token);
236 self.header(crate::header::AUTHORIZATION, header_value)
237 }
238
239 pub fn body<T: Into<Body>>(mut self, body: T) -> RequestBuilder {
241 if let Ok(ref mut req) = self.request {
242 req.body = Some(body.into());
243 }
244 self
245 }
246
247 #[cfg(feature = "multipart")]
249 #[cfg_attr(docsrs, doc(cfg(feature = "multipart")))]
250 pub fn multipart(mut self, multipart: super::multipart::Form) -> RequestBuilder {
251 if let Ok(ref mut req) = self.request {
252 *req.body_mut() = Some(Body::from_form(multipart))
253 }
254 self
255 }
256
257 pub fn header<K, V>(mut self, key: K, value: V) -> RequestBuilder
259 where
260 HeaderName: TryFrom<K>,
261 <HeaderName as TryFrom<K>>::Error: Into<http::Error>,
262 HeaderValue: TryFrom<V>,
263 <HeaderValue as TryFrom<V>>::Error: Into<http::Error>,
264 {
265 let mut error = None;
266 if let Ok(ref mut req) = self.request {
267 match <HeaderName as TryFrom<K>>::try_from(key) {
268 Ok(key) => match <HeaderValue as TryFrom<V>>::try_from(value) {
269 Ok(value) => {
270 req.headers_mut().append(key, value);
271 }
272 Err(e) => error = Some(crate::error::builder(e.into())),
273 },
274 Err(e) => error = Some(crate::error::builder(e.into())),
275 };
276 }
277 if let Some(err) = error {
278 self.request = Err(err);
279 }
280 self
281 }
282
283 pub fn headers(mut self, headers: crate::header::HeaderMap) -> RequestBuilder {
287 if let Ok(ref mut req) = self.request {
288 crate::util::replace_headers(req.headers_mut(), headers);
289 }
290 self
291 }
292
293 pub fn fetch_mode_no_cors(mut self) -> RequestBuilder {
303 if let Ok(ref mut req) = self.request {
304 req.cors = false;
305 }
306 self
307 }
308
309 pub fn fetch_credentials_same_origin(mut self) -> RequestBuilder {
319 if let Ok(ref mut req) = self.request {
320 req.credentials = Some(RequestCredentials::SameOrigin);
321 }
322 self
323 }
324
325 pub fn fetch_credentials_include(mut self) -> RequestBuilder {
335 if let Ok(ref mut req) = self.request {
336 req.credentials = Some(RequestCredentials::Include);
337 }
338 self
339 }
340
341 pub fn fetch_credentials_omit(mut self) -> RequestBuilder {
351 if let Ok(ref mut req) = self.request {
352 req.credentials = Some(RequestCredentials::Omit);
353 }
354 self
355 }
356
357 pub fn build(self) -> crate::Result<Request> {
360 self.request
361 }
362
363 pub async fn send(self) -> crate::Result<Response> {
384 let req = self.request?;
385 self.client.execute_request(req).await
386 }
387
388 pub fn try_clone(&self) -> Option<RequestBuilder> {
407 self.request
408 .as_ref()
409 .ok()
410 .and_then(|req| req.try_clone())
411 .map(|req| RequestBuilder {
412 client: self.client.clone(),
413 request: Ok(req),
414 })
415 }
416}
417
418impl fmt::Debug for Request {
419 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
420 fmt_request_fields(&mut f.debug_struct("Request"), self).finish()
421 }
422}
423
424impl fmt::Debug for RequestBuilder {
425 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
426 let mut builder = f.debug_struct("RequestBuilder");
427 match self.request {
428 Ok(ref req) => fmt_request_fields(&mut builder, req).finish(),
429 Err(ref err) => builder.field("error", err).finish(),
430 }
431 }
432}
433
434fn fmt_request_fields<'a, 'b>(
435 f: &'a mut fmt::DebugStruct<'a, 'b>,
436 req: &Request,
437) -> &'a mut fmt::DebugStruct<'a, 'b> {
438 f.field("method", &req.method)
439 .field("url", &req.url)
440 .field("headers", &req.headers)
441}
442
443impl<T> TryFrom<HttpRequest<T>> for Request
444where
445 T: Into<Body>,
446{
447 type Error = crate::Error;
448
449 fn try_from(req: HttpRequest<T>) -> crate::Result<Self> {
450 let (parts, body) = req.into_parts();
451 let Parts {
452 method,
453 uri,
454 headers,
455 ..
456 } = parts;
457 let url = Url::parse(&uri.to_string()).map_err(crate::error::builder)?;
458 Ok(Request {
459 method,
460 url,
461 headers,
462 body: Some(body.into()),
463 cors: true,
464 credentials: None,
465 })
466 }
467}
468
469impl TryFrom<Request> for HttpRequest<Body> {
470 type Error = crate::Error;
471
472 fn try_from(req: Request) -> crate::Result<Self> {
473 let Request {
474 method,
475 url,
476 headers,
477 body,
478 ..
479 } = req;
480
481 let mut req = HttpRequest::builder()
482 .method(method)
483 .uri(url.as_str())
484 .body(body.unwrap_or_else(|| Body::from(Bytes::default())))
485 .map_err(crate::error::builder)?;
486
487 *req.headers_mut() = headers;
488 Ok(req)
489 }
490}