Skip to main content

slinger/
request.rs

1use crate::body::Body;
2use crate::record::CommandRecord;
3use crate::response::parser_headers;
4use crate::{Client, Error, Response, COLON_SPACE, CR_LF, SPACE};
5use bytes::Bytes;
6use http::Request as HttpRequest;
7use http::{HeaderMap, HeaderName, HeaderValue, Method, Version};
8use std::fmt::{Debug, Formatter};
9use tokio::io::{AsyncBufReadExt, AsyncRead, AsyncReadExt, BufReader};
10
11/// Unsafe specifies whether to use raw engine for sending Non RFC-Compliant requests.
12#[derive(Debug, Default, Clone)]
13#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
14#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
15pub struct RawRequest {
16  unsafe_raw: bool,
17  #[cfg_attr(feature = "serde", serde(with = "crate::serde_schema::bytes_serde"))]
18  #[cfg_attr(feature = "schema", schemars(with = "Bytes"))]
19  raw: Bytes,
20}
21
22/// A request which can be executed with `Client::execute()`.
23#[derive(Default, Clone)]
24#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
25#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
26pub struct Request {
27  /// The target URI of the HTTP request
28  #[cfg_attr(feature = "serde", serde(with = "http_serde::uri"))]
29  #[cfg_attr(
30    feature = "schema",
31    schemars(
32      with = "String",
33      title = "request URI",
34      description = "The target URI of the HTTP request including path and query parameters",
35      example = "https://example.com/api/v1/resource?id=123"
36    )
37  )]
38  pub uri: http::Uri,
39  /// The HTTP version used for the request
40  #[cfg_attr(feature = "serde", serde(with = "http_serde::version"))]
41  #[cfg_attr(
42    feature = "schema",
43    schemars(
44      with = "String",
45      title = "HTTP version",
46      description = "The protocol version of HTTP used for this request",
47      example = "HTTP/1.1"
48    )
49  )]
50  pub version: Version,
51  /// The HTTP method indicating the desired action for the resource
52  #[cfg_attr(feature = "serde", serde(with = "http_serde::method"))]
53  #[cfg_attr(
54    feature = "schema",
55    schemars(
56      schema_with = "crate::serde_schema::http_method_schema",
57      title = "HTTP method",
58      description = "The HTTP method indicating the desired action for the resource",
59      example = &"GET",
60    )
61  )]
62  pub method: Method,
63  /// Collection of HTTP headers sent with the request
64  #[cfg_attr(feature = "serde", serde(with = "http_serde::header_map"))]
65  #[cfg_attr(
66    feature = "schema",
67    schemars(
68      with = "std::collections::HashMap<String,String>",
69      title = "HTTP headers",
70      description = "Key-value pairs of HTTP headers included in the request",
71      example = r#"&[("Content-Type", "application/json"), ("Authorization", "Bearer token")]"#
72    )
73  )]
74  pub headers: HeaderMap<HeaderValue>,
75  /// Optional body content sent with the request
76  #[cfg_attr(
77    feature = "schema",
78    schemars(
79      title = "request body",
80      description = "Optional body content sent with the HTTP request",
81      example = r#"&"{\"key\":\"value\"}""#
82    )
83  )]
84  pub body: Option<Body>,
85  /// Optional raw request representation for special cases
86  #[cfg_attr(
87    feature = "schema",
88    schemars(
89      title = "raw request",
90      description = "Optional raw representation of the HTTP request for special handling cases",
91    )
92  )]
93  pub raw_request: Option<RawRequest>,
94}
95
96impl Debug for Request {
97  fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
98    if let Some(raw) = &self.raw_request {
99      f.debug_struct("RawRequest")
100        .field("uri", &self.uri)
101        .field("raw", &format_args!("{}", &raw.raw.escape_ascii()))
102        .field("unsafe_raw", &raw.unsafe_raw)
103        .finish()
104    } else {
105      f.debug_struct("Request")
106        .field("uri", &self.uri)
107        .field("version", &self.version)
108        .field("method", &self.method)
109        .field("headers", &self.headers)
110        .field("body", &self.body)
111        .finish()
112    }
113  }
114}
115impl<T> From<HttpRequest<T>> for Request
116where
117  T: Into<Body>,
118{
119  fn from(value: HttpRequest<T>) -> Self {
120    let (parts, body) = value.into_parts();
121    let body = body.into();
122    Self {
123      uri: parts.uri,
124      version: parts.version,
125      method: parts.method,
126      headers: parts.headers,
127      body: if body.is_empty() { None } else { Some(body) },
128      raw_request: None,
129    }
130  }
131}
132
133impl From<&Request> for Bytes {
134  fn from(value: &Request) -> Self {
135    if let Some(raw) = &value.raw_request {
136      return raw.raw.clone();
137    }
138    let mut http_requests = Vec::new();
139    // 请求头
140    http_requests.extend(value.method.as_str().as_bytes());
141    http_requests.extend(SPACE);
142    // 路径
143    http_requests.extend(value.uri.path().as_bytes());
144    if let Some(q) = value.uri.query() {
145      http_requests.extend([63]);
146      http_requests.extend(q.as_bytes());
147    }
148    http_requests.extend(SPACE);
149    // 版本
150    http_requests.extend(format!("{:?}", value.version).as_bytes());
151    http_requests.extend(CR_LF);
152    // 如果请求头里面没有主机头就先加主机头
153    if value.headers.get(http::header::HOST).is_none() {
154      http_requests.extend(http::header::HOST.as_str().as_bytes());
155      http_requests.extend(COLON_SPACE);
156      http_requests.extend(if let Some(s) = value.uri.authority() {
157        s.as_str().as_bytes()
158      } else {
159        &[]
160      });
161      http_requests.extend(CR_LF);
162    }
163    // 添加请求头
164    let mut headers = value.headers.clone();
165    // 如果有body加入Content-Length请求头
166    if let Some(b) = value.body() {
167      if !b.is_empty() {
168        headers
169          .entry(http::header::CONTENT_LENGTH)
170          .or_insert(HeaderValue::from(b.len()));
171      }
172    }
173    for (k, v) in headers.iter() {
174      http_requests.extend(k.as_str().as_bytes());
175      http_requests.extend(COLON_SPACE);
176      http_requests.extend(v.as_bytes());
177      http_requests.extend(CR_LF);
178    }
179    http_requests.extend(CR_LF);
180    // 添加body
181    if let Some(b) = value.body() {
182      if !b.is_empty() {
183        http_requests.extend(b.as_ref());
184      }
185    }
186    Bytes::from(http_requests)
187  }
188}
189
190impl From<Request> for Bytes {
191  fn from(value: Request) -> Self {
192    Bytes::from(&value)
193  }
194}
195
196impl Request {
197  /// Build a `Request` from an HTTP start-line and buffered stream body.
198  pub async fn from_http_reader<R>(reader: &mut BufReader<R>) -> crate::Result<Self>
199  where
200    R: AsyncRead + Unpin,
201  {
202    let mut request_line = String::new();
203    let n = reader.read_line(&mut request_line).await?;
204    if n == 0 {
205      return Err(Error::invalid_request("Empty request".to_string()));
206    }
207    let parts: Vec<&str> = request_line.split_whitespace().collect();
208    if parts.len() < 3 {
209      return Err(Error::invalid_request("Invalid request line".to_string()));
210    }
211    let method = parts[0];
212    let uri = parts[1];
213    let version = Self::parse_http_version(parts[2])?;
214
215    // Read headers line by line until the blank line that ends them.
216    let mut headers_buf = Vec::new();
217    loop {
218      let mut line = String::new();
219      reader.read_line(&mut line).await?;
220      if line == "\r\n" || line == "\n" {
221        break;
222      }
223      headers_buf.extend_from_slice(line.as_bytes());
224    }
225
226    let mut content_length: usize = 0;
227    let mut parsed_headers: Vec<(String, String)> = Vec::new();
228    for line in String::from_utf8_lossy(&headers_buf).lines() {
229      if let Some(idx) = line.find(':') {
230        let (name, value) = line.split_at(idx);
231        let value = value[1..].trim();
232        parsed_headers.push((name.trim().to_string(), value.to_string()));
233        if name.trim().eq_ignore_ascii_case("content-length") {
234          content_length = value.parse().unwrap_or(0);
235        }
236      }
237    }
238
239    let body = if content_length > 0 {
240      let mut body_buf = vec![0u8; content_length];
241      reader.read_exact(&mut body_buf).await?;
242      Bytes::from(body_buf)
243    } else {
244      Bytes::new()
245    };
246
247    Self::from_http_parts(method, uri, version, parsed_headers, body)
248  }
249
250  fn parse_http_version(version: &str) -> crate::Result<Version> {
251    match version {
252      "HTTP/0.9" => Ok(Version::HTTP_09),
253      "HTTP/1.0" => Ok(Version::HTTP_10),
254      "HTTP/1.1" => Ok(Version::HTTP_11),
255      "HTTP/2.0" | "HTTP/2" => Ok(Version::HTTP_2),
256      "HTTP/3.0" | "HTTP/3" => Ok(Version::HTTP_3),
257      _ => Err(Error::invalid_request(format!(
258        "Unsupported HTTP version: {}",
259        version
260      ))),
261    }
262  }
263
264  /// Build a `Request` from parsed HTTP components.
265  pub fn from_http_parts<I, K, V, B>(
266    method: &str,
267    uri: &str,
268    version: Version,
269    headers: I,
270    body: B,
271  ) -> crate::Result<Self>
272  where
273    I: IntoIterator<Item = (K, V)>,
274    K: AsRef<str>,
275    V: AsRef<str>,
276    B: Into<Body>,
277  {
278    let mut request_builder = http::Request::builder()
279      .method(method)
280      .uri(uri)
281      .version(version);
282
283    for (name, value) in headers {
284      request_builder = request_builder.header(name.as_ref(), value.as_ref());
285    }
286
287    let http_request = request_builder.body(body.into())?;
288    Ok(http_request.into())
289  }
290
291  /// Creates a new builder-style object to manufacture a `Request`
292  ///
293  /// This method returns an instance of `Builder` which can be used to
294  /// create a `Request`.
295  ///
296  /// # Examples
297  ///
298  /// ```
299  /// # use http::*;
300  /// let request = Request::builder()
301  ///     .method("GET")
302  ///     .uri("https://www.rust-lang.org/")
303  ///     .header("X-Custom-Foo", "Bar")
304  ///     .body(())
305  ///     .unwrap();
306  /// ```
307  pub fn builder() -> http::request::Builder {
308    http::request::Builder::new()
309  }
310
311  /// This method return raw_request to create a `Request`
312  /// # Examples
313  ///
314  /// ```
315  /// # use slinger::{Request, RequestBuilder};
316  /// let request: Request = Request::raw(http::Uri::from_static("http://httpbin.org"),"",true);
317  /// assert!(request.raw_request().is_some());
318  ///
319  pub fn raw<U, R>(uri: U, raw: R, unsafe_raw: bool) -> Request
320  where
321    Bytes: From<R>,
322    http::Uri: From<U>,
323  {
324    let raw = RawRequest {
325      unsafe_raw,
326      raw: raw.into(),
327    };
328    Request {
329      uri: uri.into(),
330      raw_request: Some(raw),
331      ..Request::default()
332    }
333  }
334}
335
336impl Request {
337  /// Set the HTTP method for this request.
338  ///
339  /// By default, this is `GET`.
340  ///
341  /// # Examples
342  ///
343  /// ```
344  /// # use http::*;
345  ///
346  /// let req = Request::builder()
347  ///     .method("POST")
348  ///     .body(())
349  ///     .unwrap();
350  /// ```
351  #[inline]
352  pub fn method(&self) -> &Method {
353    &self.method
354  }
355  /// Get the HTTP Method for this request.
356  ///
357  /// By default, this is `GET`. If builder has error, returns None.
358  ///
359  /// # Examples
360  ///
361  /// ```
362  /// # use http::*;
363  ///
364  /// let mut req = Request::builder();
365  /// assert_eq!(req.method_ref(),Some(&Method::GET));
366  ///
367  /// req = req.method("POST");
368  /// assert_eq!(req.method_ref(),Some(&Method::POST));
369  /// ```
370  #[inline]
371  pub fn method_mut(&mut self) -> &mut Method {
372    &mut self.method
373  }
374  /// Set the URI for this request.
375  ///
376  /// By default, this is `/`.
377  ///
378  /// # Examples
379  ///
380  /// ```
381  /// # use http::*;
382  ///
383  /// let req = Request::builder()
384  ///     .uri("https://www.rust-lang.org/")
385  ///     .body(())
386  ///     .unwrap();
387  /// ```
388  #[inline]
389  pub fn uri(&self) -> &http::Uri {
390    &self.uri
391  }
392  /// Get the URI for this request
393  ///
394  /// By default, this is `/`.
395  ///
396  /// # Examples
397  ///
398  /// ```
399  /// # use http::*;
400  ///
401  /// let mut req = Request::builder();
402  /// assert_eq!(req.uri_ref().unwrap(), "/" );
403  ///
404  /// req = req.uri("https://www.rust-lang.org/");
405  /// assert_eq!(req.uri_ref().unwrap(), "https://www.rust-lang.org/" );
406  /// ```
407  #[inline]
408  pub fn uri_mut(&mut self) -> &mut http::Uri {
409    &mut self.uri
410  }
411  /// Appends a header to this request builder.
412  ///
413  /// This function will append the provided key/value as a header to the
414  /// internal `HeaderMap` being constructed. Essentially this is equivalent
415  /// to calling `HeaderMap::append`.
416  ///
417  /// # Examples
418  ///
419  /// ```
420  /// # use http::*;
421  /// # use http::header::HeaderValue;
422  ///
423  /// let req = Request::builder()
424  ///     .header("Accept", "text/html")
425  ///     .header("X-Custom-Foo", "bar")
426  ///     .body(())
427  ///     .unwrap();
428  /// ```
429  #[inline]
430  pub fn headers(&self) -> &HeaderMap {
431    &self.headers
432  }
433  /// Get header on this request builder.
434  /// when builder has error returns None
435  ///
436  /// # Example
437  ///
438  /// ```
439  /// # use http::Request;
440  /// let req = Request::builder()
441  ///     .header("Accept", "text/html")
442  ///     .header("X-Custom-Foo", "bar");
443  /// let headers = req.headers_ref().unwrap();
444  /// assert_eq!( headers["Accept"], "text/html" );
445  /// assert_eq!( headers["X-Custom-Foo"], "bar" );
446  /// ```
447  #[inline]
448  pub fn headers_mut(&mut self) -> &mut HeaderMap {
449    &mut self.headers
450  }
451  /// "Consumes" this builder, using the provided `body` to return a
452  /// constructed `Request`.
453  ///
454  /// # Errors
455  ///
456  /// This function may return an error if any previously configured argument
457  /// failed to parse or get converted to the internal representation. For
458  /// example if an invalid `head` was specified via `header("Foo",
459  /// "Bar\r\n")` the error will be returned when this function is called
460  /// rather than when `header` was called.
461  ///
462  /// # Examples
463  ///
464  /// ```
465  /// # use http::*;
466  ///
467  /// let request = Request::builder()
468  ///     .body(())
469  ///     .unwrap();
470  /// ```
471  #[inline]
472  pub fn body(&self) -> Option<&Body> {
473    self.body.as_ref()
474  }
475  /// Returns the associated version.
476  ///
477  /// # Examples
478  ///
479  /// ```
480  /// # use http::*;
481  /// let request: Request<()> = Request::default();
482  /// assert_eq!(request.version(), Version::HTTP_11);
483  /// ```
484  #[inline]
485  pub fn version(&self) -> Version {
486    self.version
487  }
488  /// Returns a mutable reference to the associated version.
489  ///
490  /// # Examples
491  ///
492  /// ```
493  /// # use http::*;
494  /// let mut request: Request<()> = Request::default();
495  /// *request.version_mut() = Version::HTTP_2;
496  /// assert_eq!(request.version(), Version::HTTP_2);
497  /// ```
498  #[inline]
499  pub fn version_mut(&mut self) -> &mut Version {
500    &mut self.version
501  }
502  /// Returns raw_request.
503  ///
504  /// # Examples
505  ///
506  /// ```
507  /// # use slinger::Request;
508  /// let request: Request = Request::raw(http::Uri::from_static("http://httpbin.org"),"",true);
509  /// assert!(request.raw_request().is_some());
510  /// ```
511  #[inline]
512  pub fn raw_request(&self) -> &Option<RawRequest> {
513    &self.raw_request
514  }
515
516  #[inline]
517  pub(crate) fn raw_request_mut(&mut self) -> &mut Option<RawRequest> {
518    &mut self.raw_request
519  }
520  #[inline]
521  pub(crate) fn is_unsafe(&self) -> bool {
522    match &self.raw_request {
523      None => false,
524      Some(raw) => raw.unsafe_raw,
525    }
526  }
527  /// Returns ncat or curl command to send request.
528  ///
529  /// # Examples
530  ///
531  /// ```
532  /// #
533  /// let req: slinger::Request = slinger::Request::builder()
534  /// .uri("http://httpbin.org/get")
535  /// .header("X", "X")
536  /// .body(bytes::Bytes::from(b"\x7f\x45\x4c\x46\x01\x00\x02\x03".to_vec())).unwrap().into();
537  /// println!("{}", req.get_command());
538  /// ```
539  #[inline]
540  pub fn get_command(&self) -> String {
541    CommandRecord::from(self).command
542  }
543}
544
545/// A builder to construct the properties of a `Request`.
546///
547/// To construct a `RequestBuilder`, refer to the `Client` documentation.
548// #[derive(Debug)]
549#[must_use = "RequestBuilder does nothing until you 'send' it"]
550pub struct RequestBuilder {
551  client: Client,
552  builder: http::request::Builder,
553  body: Body,
554  raw: Option<RawRequest>,
555}
556
557impl Default for RequestBuilder {
558  fn default() -> Self {
559    RequestBuilder {
560      client: Default::default(),
561      builder: http::request::Builder::new(),
562      body: Default::default(),
563      raw: None,
564    }
565  }
566}
567
568impl RequestBuilder {
569  /// Constructs a new request.
570  pub fn new(client: Client, builder: http::request::Builder) -> RequestBuilder {
571    RequestBuilder {
572      client,
573      builder,
574      body: Default::default(),
575      raw: None,
576    }
577  }
578  /// Set `uri` to this Request.
579  pub fn uri<U: Into<http::Uri>>(mut self, uri: U) -> RequestBuilder {
580    self.builder = self.builder.uri(uri);
581    self
582  }
583  /// Add a `Header` to this Request.
584  pub fn header<K, V>(mut self, key: K, value: V) -> RequestBuilder
585  where
586    HeaderName: TryFrom<K>,
587    HeaderValue: TryFrom<V>,
588    <HeaderName as TryFrom<K>>::Error: Into<http::Error>,
589    <HeaderValue as TryFrom<V>>::Error: Into<http::Error>,
590  {
591    self.builder = self.builder.header(key, value);
592    self
593  }
594  /// Add a `Header` from lines to this Request.
595  pub fn header_line<L: Into<String>>(mut self, lines: L) -> RequestBuilder {
596    for line in lines.into().lines() {
597      if let Ok((Some(key), Some(value))) = parser_headers(line.as_bytes()) {
598        self.builder = self.builder.header(key, value);
599      };
600    }
601    self
602  }
603  /// Add a set of Headers to the existing ones on this Request.
604  ///
605  /// The headers will be merged in to any already set.
606  pub fn headers(mut self, headers: HeaderMap) -> RequestBuilder {
607    if let Some(header) = self.builder.headers_mut() {
608      for (key, value) in headers {
609        if let Some(key) = key {
610          header.insert(key, value);
611        }
612      }
613    }
614    self
615  }
616  /// Set the request body.
617  pub fn body<T: Into<Body>>(mut self, body: T) -> RequestBuilder {
618    self.body = body.into();
619    self
620  }
621  /// set raw request
622  pub fn raw<R: Into<Bytes>>(mut self, raw: R, unsafe_raw: bool) -> RequestBuilder {
623    self.raw = Some(RawRequest {
624      unsafe_raw,
625      raw: raw.into(),
626    });
627    self
628  }
629  /// Build a `Request`, which can be inspected, modified and executed with
630  /// `Client::execute()`.
631  pub fn build(self) -> crate::Result<Request> {
632    let mut r: Request = self.builder.body(self.body)?.into();
633    r.raw_request = self.raw;
634    Ok(r)
635  }
636  /// Constructs the Request and sends it to the target URL, returning a
637  /// future Response.
638  ///
639  /// # Errors
640  ///
641  /// This method fails if there was an error while sending request,
642  /// redirect loop was detected or redirect limit was exhausted.
643  ///
644  /// # Example
645  ///
646  /// ```no_run
647  /// # use slinger::Error;
648  /// #
649  /// # async fn run() -> Result<(), Error> {
650  /// let response = slinger::Client::new()
651  ///     .get("https://hyper.rs")
652  ///     .send().await?;
653  /// # Ok(())
654  /// # }
655  /// ```
656  pub async fn send(self) -> crate::Result<Response> {
657    let mut req: Request = self.builder.body(self.body)?.into();
658    *req.raw_request_mut() = self.raw;
659    self.client.execute(req).await
660  }
661}