http_req/
request.rs

1//! creating and sending HTTP requests
2#[cfg(feature = "wasmedge_rustls")]
3use crate::tls;
4use crate::{
5    error,
6    response::{find_slice, Headers, Response, CR_LF_2},
7    uri::Uri,
8};
9use std::{
10    convert::TryFrom,
11    fmt,
12    io::{self, ErrorKind, Read, Write},
13    path::Path,
14    time::{Duration, Instant},
15};
16
17#[cfg(not(target_arch = "wasm32"))]
18use std::net::TcpStream;
19#[cfg(target_arch = "wasm32")]
20use wasmedge_wasi_socket::{TcpStream, ToSocketAddrs};
21
22const CR_LF: &str = "\r\n";
23const BUF_SIZE: usize = 8 * 1024;
24const SMALL_BUF_SIZE: usize = 8 * 10;
25const TEST_FREQ: usize = 100;
26
27///Every iteration increases `count` by one. When `count` is equal to `stop`, `next()`
28///returns `Some(true)` (and sets `count` to 0), otherwise returns `Some(false)`.
29///Iterator never returns `None`.
30pub struct Counter {
31    count: usize,
32    stop: usize,
33}
34
35impl Counter {
36    pub const fn new(stop: usize) -> Counter {
37        Counter { count: 0, stop }
38    }
39}
40
41impl Iterator for Counter {
42    type Item = bool;
43
44    fn next(&mut self) -> Option<Self::Item> {
45        self.count += 1;
46        let breakpoint = self.count == self.stop;
47
48        if breakpoint {
49            self.count = 0;
50        }
51
52        Some(breakpoint)
53    }
54}
55
56///Copies data from `reader` to `writer` until the `deadline` is reached.
57///Limitations of current implementation may cause exceeding the deadline.
58///Returns how many bytes has been read.
59pub fn copy_with_timeout<R, W>(reader: &mut R, writer: &mut W, deadline: Instant) -> io::Result<u64>
60where
61    R: Read + ?Sized,
62    W: Write + ?Sized,
63{
64    let mut buf = [0; BUF_SIZE];
65    let mut copied = 0;
66    let mut counter = Counter::new(TEST_FREQ);
67
68    loop {
69        let len = match reader.read(&mut buf) {
70            Ok(0) => return Ok(copied),
71            Ok(len) => len,
72            Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
73            Err(e) => return Err(e),
74        };
75        writer.write_all(&buf[..len])?;
76        copied += len as u64;
77
78        if counter.next().unwrap() && Instant::now() >= deadline {
79            return Ok(copied);
80        }
81    }
82}
83
84///Copies a given amount of bytes from `reader` to `writer`.
85pub fn copy_exact<R, W>(reader: &mut R, writer: &mut W, num_bytes: usize) -> io::Result<()>
86where
87    R: Read + ?Sized,
88    W: Write + ?Sized,
89{
90    let mut buf = vec![0u8; num_bytes];
91
92    reader.read_exact(&mut buf)?;
93    writer.write_all(&mut buf)
94}
95
96///Reads data from `reader` and checks for specified `val`ue. When data contains specified value
97///or `deadline` is reached, stops reading. Returns read data as array of two vectors: elements
98///before and after the `val`.
99pub fn copy_until<R>(
100    reader: &mut R,
101    val: &[u8],
102    deadline: Instant,
103) -> Result<[Vec<u8>; 2], io::Error>
104where
105    R: Read + ?Sized,
106{
107    let mut buf = [0; SMALL_BUF_SIZE];
108    let mut writer = Vec::with_capacity(SMALL_BUF_SIZE);
109    let mut counter = Counter::new(TEST_FREQ);
110    let mut split_idx = 0;
111
112    loop {
113        let len = match reader.read(&mut buf) {
114            Ok(0) => break,
115            Ok(len) => len,
116            Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
117            Err(e) => return Err(e),
118        };
119
120        writer.write_all(&buf[..len])?;
121
122        if let Some(i) = find_slice(&writer, val) {
123            split_idx = i;
124            break;
125        }
126
127        if counter.next().unwrap() && Instant::now() >= deadline {
128            split_idx = writer.len();
129            break;
130        }
131    }
132
133    Ok([writer[..split_idx].to_vec(), writer[split_idx..].to_vec()])
134}
135
136///HTTP request methods
137#[derive(Debug, PartialEq, Clone, Copy)]
138pub enum Method {
139    GET,
140    HEAD,
141    POST,
142    PUT,
143    DELETE,
144    OPTIONS,
145    PATCH,
146}
147
148impl fmt::Display for Method {
149    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
150        use self::Method::*;
151
152        let method = match self {
153            GET => "GET",
154            HEAD => "HEAD",
155            POST => "POST",
156            PUT => "PUT",
157            DELETE => "DELETE",
158            OPTIONS => "OPTIONS",
159            PATCH => "PATCH",
160        };
161
162        write!(f, "{}", method)
163    }
164}
165
166///HTTP versions
167#[derive(Debug, PartialEq, Clone, Copy)]
168pub enum HttpVersion {
169    Http10,
170    Http11,
171    Http20,
172}
173
174impl HttpVersion {
175    pub const fn as_str(self) -> &'static str {
176        use self::HttpVersion::*;
177
178        match self {
179            Http10 => "HTTP/1.0",
180            Http11 => "HTTP/1.1",
181            Http20 => "HTTP/2.0",
182        }
183    }
184}
185
186impl fmt::Display for HttpVersion {
187    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
188        write!(f, "{}", self.as_str())
189    }
190}
191
192///Relatively low-level struct for making HTTP requests.
193///
194///It can work with any stream that implements `Read` and `Write`.
195///By default it does not close the connection after completion of the response.
196///
197///# Examples
198///```
199///use std::{net::TcpStream, convert::TryFrom};
200///use http_req::{request::RequestBuilder, tls, uri::Uri, response::StatusCode};
201///
202///let addr: Uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap();
203///let mut writer = Vec::new();
204///
205///let stream = TcpStream::connect((addr.host().unwrap(), addr.corr_port())).unwrap();
206///let mut stream = tls::Config::default()
207///    .connect(addr.host().unwrap_or(""), stream)
208///    .unwrap();
209///
210///let response = RequestBuilder::new(&addr)
211///    .header("Connection", "Close")
212///    .send(&mut stream, &mut writer)
213///    .unwrap();
214///
215///assert_eq!(response.status_code(), StatusCode::new(200));
216///```
217#[derive(Clone, Debug, PartialEq)]
218pub struct RequestBuilder<'a> {
219    uri: &'a Uri<'a>,
220    method: Method,
221    version: HttpVersion,
222    headers: Headers,
223    body: Option<&'a [u8]>,
224    timeout: Option<Duration>,
225}
226
227impl<'a> RequestBuilder<'a> {
228    ///Creates new `RequestBuilder` with default parameters
229    ///
230    ///# Examples
231    ///```
232    ///use std::{net::TcpStream, convert::TryFrom};
233    ///use http_req::{request::RequestBuilder, tls, uri::Uri};
234    ///
235    ///let addr = Uri::try_from("https://www.rust-lang.org/learn").unwrap();
236    ///let mut writer = Vec::new();
237    ///
238    ///let stream = TcpStream::connect((addr.host().unwrap(), addr.corr_port())).unwrap();
239    ///let mut stream = tls::Config::default()
240    ///    .connect(addr.host().unwrap_or(""), stream)
241    ///    .unwrap();
242    ///
243    ///let response = RequestBuilder::new(&addr)
244    ///    .header("Connection", "Close")
245    ///    .send(&mut stream, &mut writer)
246    ///    .unwrap();
247    ///```
248    pub fn new(uri: &'a Uri<'a>) -> RequestBuilder<'a> {
249        RequestBuilder {
250            headers: Headers::default_http(uri),
251            uri,
252            method: Method::GET,
253            version: HttpVersion::Http11,
254            body: None,
255            timeout: None,
256        }
257    }
258
259    ///Sets request method
260    ///
261    ///# Examples
262    ///```
263    ///use std::{net::TcpStream, convert::TryFrom};
264    ///use http_req::{request::{RequestBuilder, Method}, tls, uri::Uri};
265    ///
266    ///let addr= Uri::try_from("https://www.rust-lang.org/learn").unwrap();
267    ///let mut writer = Vec::new();
268    ///
269    ///let stream = TcpStream::connect((addr.host().unwrap(), addr.corr_port())).unwrap();
270    ///let mut stream = tls::Config::default()
271    ///    .connect(addr.host().unwrap_or(""), stream)
272    ///    .unwrap();
273    ///
274    ///let response = RequestBuilder::new(&addr)
275    ///    .method(Method::HEAD)
276    ///    .header("Connection", "Close")
277    ///    .send(&mut stream, &mut writer)
278    ///    .unwrap();
279    ///```
280    pub fn method<T>(&mut self, method: T) -> &mut Self
281    where
282        Method: From<T>,
283    {
284        self.method = Method::from(method);
285        self
286    }
287
288    ///Sets HTTP version
289    ///
290    ///# Examples
291    ///```
292    ///use std::{net::TcpStream, convert::TryFrom};
293    ///use http_req::{request::{RequestBuilder, HttpVersion}, tls, uri::Uri};
294    ///
295    ///let addr = Uri::try_from("https://www.rust-lang.org/learn").unwrap();
296    ///let mut writer = Vec::new();
297    ///
298    ///let stream = TcpStream::connect((addr.host().unwrap(), addr.corr_port())).unwrap();
299    ///let mut stream = tls::Config::default()
300    ///    .connect(addr.host().unwrap_or(""), stream)
301    ///    .unwrap();
302    ///
303    ///let response = RequestBuilder::new(&addr)
304    ///    .version(HttpVersion::Http10)
305    ///    .header("Connection", "Close")
306    ///    .send(&mut stream, &mut writer)
307    ///    .unwrap();
308    ///```
309
310    pub fn version<T>(&mut self, version: T) -> &mut Self
311    where
312        HttpVersion: From<T>,
313    {
314        self.version = HttpVersion::from(version);
315        self
316    }
317
318    ///Replaces all it's headers with headers passed to the function
319    ///
320    ///# Examples
321    ///```
322    ///use std::{net::TcpStream, convert::TryFrom};
323    ///use http_req::{request::{RequestBuilder, Method}, response::Headers, tls, uri::Uri};
324    ///
325    ///let addr = Uri::try_from("https://www.rust-lang.org/learn").unwrap();
326    ///let mut writer = Vec::new();
327    ///let mut headers = Headers::new();
328    ///headers.insert("Accept-Charset", "utf-8");
329    ///headers.insert("Accept-Language", "en-US");
330    ///headers.insert("Host", "rust-lang.org");
331    ///headers.insert("Connection", "Close");
332    ///
333    ///let stream = TcpStream::connect((addr.host().unwrap(), addr.corr_port())).unwrap();
334    ///let mut stream = tls::Config::default()
335    ///    .connect(addr.host().unwrap_or(""), stream)
336    ///    .unwrap();
337    ///
338    ///let response = RequestBuilder::new(&addr)
339    ///    .headers(headers)
340    ///    .send(&mut stream, &mut writer)
341    ///    .unwrap();
342    ///```
343    pub fn headers<T>(&mut self, headers: T) -> &mut Self
344    where
345        Headers: From<T>,
346    {
347        self.headers = Headers::from(headers);
348        self
349    }
350
351    ///Adds new header to existing/default headers
352    ///
353    ///# Examples
354    ///```
355    ///use std::{net::TcpStream, convert::TryFrom};
356    ///use http_req::{request::{RequestBuilder, Method}, tls, uri::Uri};
357    ///
358    ///let addr: Uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap();
359    ///let mut writer = Vec::new();
360    ///
361    ///let stream = TcpStream::connect((addr.host().unwrap(), addr.corr_port())).unwrap();
362    ///let mut stream = tls::Config::default()
363    ///    .connect(addr.host().unwrap_or(""), stream)
364    ///    .unwrap();
365    ///
366    ///let response = RequestBuilder::new(&addr)
367    ///    .header("Connection", "Close")
368    ///    .send(&mut stream, &mut writer)
369    ///    .unwrap();
370    ///```
371    pub fn header<T, U>(&mut self, key: &T, val: &U) -> &mut Self
372    where
373        T: ToString + ?Sized,
374        U: ToString + ?Sized,
375    {
376        self.headers.insert(key, val);
377        self
378    }
379
380    ///Sets body for request
381    ///
382    ///# Examples
383    ///```
384    ///use std::{net::TcpStream, convert::TryFrom};
385    ///use http_req::{request::{RequestBuilder, Method}, tls, uri::Uri};
386    ///
387    ///let addr = Uri::try_from("https://www.rust-lang.org/learn").unwrap();
388    ///const body: &[u8; 27] = b"field1=value1&field2=value2";
389    ///let mut writer = Vec::new();
390    ///
391    ///let stream = TcpStream::connect((addr.host().unwrap(), addr.corr_port())).unwrap();
392    ///let mut stream = tls::Config::default()
393    ///    .connect(addr.host().unwrap_or(""), stream)
394    ///    .unwrap();
395    ///
396    ///let response = RequestBuilder::new(&addr)
397    ///    .method(Method::POST)
398    ///    .body(body)
399    ///    .header("Content-Length", &body.len())
400    ///    .header("Connection", "Close")
401    ///    .send(&mut stream, &mut writer)
402    ///    .unwrap();
403    ///```
404    pub fn body(&mut self, body: &'a [u8]) -> &mut Self {
405        self.body = Some(body);
406        self
407    }
408
409    ///Sets timeout for entire connection.
410    ///
411    ///# Examples
412    ///```
413    ///use std::{net::TcpStream, time::{Duration, Instant}, convert::TryFrom};
414    ///use http_req::{request::RequestBuilder, tls, uri::Uri};
415    ///
416    ///let addr = Uri::try_from("https://www.rust-lang.org/learn").unwrap();
417    ///let mut writer = Vec::new();
418    ///
419    ///let stream = TcpStream::connect((addr.host().unwrap(), addr.corr_port())).unwrap();
420    ///let mut stream = tls::Config::default()
421    ///    .connect(addr.host().unwrap_or(""), stream)
422    ///    .unwrap();
423    ///let timeout = Some(Duration::from_secs(3600));
424    ///
425    ///let response = RequestBuilder::new(&addr)
426    ///    .timeout(timeout)
427    ///    .header("Connection", "Close")
428    ///    .send(&mut stream, &mut writer)
429    ///    .unwrap();
430    ///```
431    pub fn timeout<T>(&mut self, timeout: Option<T>) -> &mut Self
432    where
433        Duration: From<T>,
434    {
435        self.timeout = timeout.map(Duration::from);
436        self
437    }
438
439    ///Sends HTTP request in these steps:
440    ///
441    ///- Writes request message to `stream`.
442    ///- Writes response's body to `writer`.
443    ///- Returns response for this request.
444    ///
445    ///# Examples
446    ///
447    ///HTTP
448    ///```
449    ///use std::{net::TcpStream, convert::TryFrom};
450    ///use http_req::{request::RequestBuilder, uri::Uri};
451    ///
452    /// //This address is automatically redirected to HTTPS, so response code will not ever be 200
453    ///let addr = Uri::try_from("https://www.rust-lang.org/learn").unwrap();
454    ///let mut writer = Vec::new();
455    ///let mut stream = TcpStream::connect((addr.host().unwrap(), addr.corr_port())).unwrap();
456    ///
457    ///let response = RequestBuilder::new(&addr)
458    ///    .header("Connection", "Close")
459    ///    .send(&mut stream, &mut writer)
460    ///    .unwrap();
461    ///```
462    ///
463    ///HTTPS
464    ///```
465    ///use std::{net::TcpStream, convert::TryFrom};
466    ///use http_req::{request::RequestBuilder, tls, uri::Uri};
467    ///
468    ///let addr: Uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap();
469    ///let mut writer = Vec::new();
470    ///
471    ///let stream = TcpStream::connect((addr.host().unwrap(), addr.corr_port())).unwrap();
472    ///let mut stream = tls::Config::default()
473    ///    .connect(addr.host().unwrap_or(""), stream)
474    ///    .unwrap();
475    ///
476    ///let response = RequestBuilder::new(&addr)
477    ///    .header("Connection", "Close")
478    ///    .send(&mut stream, &mut writer)
479    ///    .unwrap();
480    ///```
481    pub fn send<T, U>(&self, stream: &mut T, writer: &mut U) -> Result<Response, error::Error>
482    where
483        T: Write + Read,
484        U: Write,
485    {
486        self.write_msg(stream, &self.parse_msg())?;
487
488        let head_deadline = match self.timeout {
489            Some(t) => Instant::now() + t,
490            None => Instant::now() + Duration::from_secs(360),
491        };
492        let (res, body_part) = self.read_head(stream, head_deadline)?;
493
494        if self.method == Method::HEAD {
495            return Ok(res);
496        }
497
498        if let Some(v) = res.headers().get("Transfer-Encoding") {
499            if *v == "chunked" {
500                let mut dechunked = crate::chunked::Reader::new(body_part.as_slice().chain(stream));
501
502                if let Some(timeout) = self.timeout {
503                    let deadline = Instant::now() + timeout;
504                    copy_with_timeout(&mut dechunked, writer, deadline)?;
505                } else {
506                    io::copy(&mut dechunked, writer)?;
507                }
508
509                return Ok(res);
510            }
511        }
512
513        writer.write_all(&body_part)?;
514
515        if let Some(timeout) = self.timeout {
516            let deadline = Instant::now() + timeout;
517            copy_with_timeout(stream, writer, deadline)?;
518        } else {
519            let num_bytes = res.content_len();
520
521            match num_bytes {
522                Some(0) => {}
523                Some(num_bytes) => {
524                    copy_exact(stream, writer, num_bytes - body_part.len())?;
525                }
526                None => {
527                    io::copy(stream, writer)?;
528                }
529            }
530        }
531
532        Ok(res)
533    }
534
535    ///Writes message to `stream` and flushes it
536    pub fn write_msg<T, U>(&self, stream: &mut T, msg: &U) -> Result<(), io::Error>
537    where
538        T: Write,
539        U: AsRef<[u8]>,
540    {
541        stream.write_all(msg.as_ref())?;
542        stream.flush()?;
543
544        Ok(())
545    }
546
547    ///Reads head of server's response
548    pub fn read_head<T: Read>(
549        &self,
550        stream: &mut T,
551        deadline: Instant,
552    ) -> Result<(Response, Vec<u8>), error::Error> {
553        let [head, body_part] = copy_until(stream, &CR_LF_2, deadline)?;
554
555        Ok((Response::from_head(&head)?, body_part))
556    }
557
558    ///Parses request message for this `RequestBuilder`
559    pub fn parse_msg(&self) -> Vec<u8> {
560        let request_line = format!(
561            "{} {} {}{}",
562            self.method,
563            self.uri.resource(),
564            self.version,
565            CR_LF
566        );
567
568        let headers: String = self
569            .headers
570            .iter()
571            .map(|(k, v)| format!("{}: {}{}", k.as_ref(), v, CR_LF))
572            .collect();
573
574        let mut request_msg = (request_line + &headers + CR_LF).as_bytes().to_vec();
575
576        if let Some(b) = &self.body {
577            request_msg.extend(*b);
578        }
579
580        request_msg
581    }
582
583    ///Consume self to build a `Request` instance.
584    ///
585    ///# Examples
586    ///```
587    ///use http_req::{request::RequestBuilder, uri::Uri};
588    ///
589    ///let addr = Uri::try_from("https://www.rust-lang.org/learn").unwrap();
590    ///let mut writer = Vec::new();
591    ///
592    ///let request = RequestBuilder::new(&addr)
593    ///    .header("Connection", "Close")
594    ///    .build();
595    ///
596    ///let response = request.send(&mut writer);
597    ///```
598    ///
599    pub fn build(self) -> Request<'a> {
600        Request {
601            inner: self,
602            connect_timeout: Some(Duration::from_secs(60)),
603            read_timeout: Some(Duration::from_secs(60)),
604            write_timeout: Some(Duration::from_secs(60)),
605            root_cert_file_pem: None,
606        }
607    }
608}
609
610///Relatively higher-level struct for making HTTP requests.
611///
612///It creates stream (`TcpStream` or `TlsStream`) appropriate for the type of uri (`http`/`https`)
613///By default it closes connection after completion of the response.
614///
615///# Examples
616///```
617///use http_req::{request::Request, uri::Uri, response::StatusCode};
618///use std::convert::TryFrom;
619///
620///let mut writer = Vec::new();
621///let uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap();
622///
623///let response = Request::new(&uri).send(&mut writer).unwrap();;
624///assert_eq!(response.status_code(), StatusCode::new(200));
625///```
626///
627#[derive(Clone, Debug, PartialEq)]
628pub struct Request<'a> {
629    inner: RequestBuilder<'a>,
630    connect_timeout: Option<Duration>,
631    read_timeout: Option<Duration>,
632    write_timeout: Option<Duration>,
633    root_cert_file_pem: Option<&'a Path>,
634}
635
636impl<'a> Request<'a> {
637    ///Creates new `Request` with default parameters
638    ///
639    ///# Examples
640    ///```
641    ///use http_req::{request::Request, uri::Uri};
642    ///use std::convert::TryFrom;
643    ///
644    ///let mut writer = Vec::new();
645    ///let uri: Uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap();
646    ///
647    ///let response = Request::new(&uri).send(&mut writer).unwrap();;
648    ///```
649    pub fn new(uri: &'a Uri) -> Request<'a> {
650        let mut builder = RequestBuilder::new(&uri);
651        builder.header("Connection", "Close");
652
653        Request {
654            inner: builder,
655            connect_timeout: Some(Duration::from_secs(60)),
656            read_timeout: Some(Duration::from_secs(60)),
657            write_timeout: Some(Duration::from_secs(60)),
658            root_cert_file_pem: None,
659        }
660    }
661
662    ///Sets request method
663    ///
664    ///# Examples
665    ///```
666    ///use http_req::{request::{Request, Method}, uri::Uri};
667    ///use std::convert::TryFrom;
668    ///
669    ///let mut writer = Vec::new();
670    ///let uri: Uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap();
671    ///
672    ///let response = Request::new(&uri)
673    ///    .method(Method::HEAD)
674    ///    .send(&mut writer)
675    ///    .unwrap();
676    ///```
677    pub fn method<T>(&mut self, method: T) -> &mut Self
678    where
679        Method: From<T>,
680    {
681        self.inner.method(method);
682        self
683    }
684
685    ///Sets HTTP version
686    ///
687    ///# Examples
688    ///```
689    ///use http_req::{request::{Request, HttpVersion}, uri::Uri};
690    ///use std::convert::TryFrom;
691    ///
692    ///let mut writer = Vec::new();
693    ///let uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap();
694    ///
695    ///let response = Request::new(&uri)
696    ///    .version(HttpVersion::Http10)
697    ///    .send(&mut writer)
698    ///    .unwrap();
699    ///```
700
701    pub fn version<T>(&mut self, version: T) -> &mut Self
702    where
703        HttpVersion: From<T>,
704    {
705        self.inner.version(version);
706        self
707    }
708
709    ///Replaces all it's headers with headers passed to the function
710    ///
711    ///# Examples
712    ///```
713    ///use http_req::{request::Request, uri::Uri, response::Headers};
714    ///use std::convert::TryFrom;
715    ///
716    ///let mut writer = Vec::new();
717    ///let uri: Uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap();
718    ///
719    ///let mut headers = Headers::new();
720    ///headers.insert("Accept-Charset", "utf-8");
721    ///headers.insert("Accept-Language", "en-US");
722    ///headers.insert("Host", "rust-lang.org");
723    ///headers.insert("Connection", "Close");
724    ///
725    ///let response = Request::new(&uri)
726    ///    .headers(headers)
727    ///    .send(&mut writer)
728    ///    .unwrap();;
729    ///```
730    pub fn headers<T>(&mut self, headers: T) -> &mut Self
731    where
732        Headers: From<T>,
733    {
734        self.inner.headers(headers);
735        self
736    }
737
738    ///Adds header to existing/default headers
739    ///
740    ///# Examples
741    ///```
742    ///use http_req::{request::Request, uri::Uri};
743    ///use std::convert::TryFrom;
744    ///
745    ///let mut writer = Vec::new();
746    ///let uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap();
747    ///
748    ///let response = Request::new(&uri)
749    ///    .header("Accept-Language", "en-US")
750    ///    .send(&mut writer)
751    ///    .unwrap();
752    ///```
753    pub fn header<T, U>(&mut self, key: &T, val: &U) -> &mut Self
754    where
755        T: ToString + ?Sized,
756        U: ToString + ?Sized,
757    {
758        self.inner.header(key, val);
759        self
760    }
761
762    ///Sets body for request
763    ///
764    ///# Examples
765    ///```
766    ///use http_req::{request::{Request, Method}, uri::Uri};
767    ///use std::convert::TryFrom;
768    ///
769    ///let mut writer = Vec::new();
770    ///let uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap();
771    ///const body: &[u8; 27] = b"field1=value1&field2=value2";
772    ///
773    ///let response = Request::new(&uri)
774    ///    .method(Method::POST)
775    ///    .header("Content-Length", &body.len())
776    ///    .body(body)
777    ///    .send(&mut writer)
778    ///    .unwrap();
779    ///```
780    pub fn body(&mut self, body: &'a [u8]) -> &mut Self {
781        self.inner.body(body);
782        self
783    }
784
785    ///Sets connection timeout of request.
786    ///
787    ///# Examples
788    ///```
789    ///use std::{time::{Duration, Instant}, convert::TryFrom};
790    ///use http_req::{request::Request, uri::Uri};
791    ///
792    ///let mut writer = Vec::new();
793    ///let uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap();
794    ///const body: &[u8; 27] = b"field1=value1&field2=value2";
795    ///let timeout = Some(Duration::from_secs(3600));
796    ///
797    ///let response = Request::new(&uri)
798    ///    .timeout(timeout)
799    ///    .send(&mut writer)
800    ///    .unwrap();
801    ///```
802    pub fn timeout<T>(&mut self, timeout: Option<T>) -> &mut Self
803    where
804        Duration: From<T>,
805    {
806        self.inner.timeout = timeout.map(Duration::from);
807        self
808    }
809
810    ///Sets connect timeout while using internal `TcpStream` instance
811    ///
812    ///- If there is a timeout, it will be passed to
813    ///  [`TcpStream::connect_timeout`][TcpStream::connect_timeout].
814    ///- If `None` is provided, [`TcpStream::connect`][TcpStream::connect] will
815    ///  be used. A timeout will still be enforced by the operating system, but
816    ///  the exact value depends on the platform.
817    ///
818    ///[TcpStream::connect]: https://doc.rust-lang.org/std/net/struct.TcpStream.html#method.connect
819    ///[TcpStream::connect_timeout]: https://doc.rust-lang.org/std/net/struct.TcpStream.html#method.connect_timeout
820    ///
821    ///# Examples
822    ///```
823    ///use http_req::{request::Request, uri::Uri};
824    ///use std::{time::Duration, convert::TryFrom};
825    ///
826    ///let mut writer = Vec::new();
827    ///let uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap();
828    ///const time: Option<Duration> = Some(Duration::from_secs(10));
829    ///
830    ///let response = Request::new(&uri)
831    ///    .connect_timeout(time)
832    ///    .send(&mut writer)
833    ///    .unwrap();
834    ///```
835    pub fn connect_timeout<T>(&mut self, timeout: Option<T>) -> &mut Self
836    where
837        Duration: From<T>,
838    {
839        self.connect_timeout = timeout.map(Duration::from);
840        self
841    }
842
843    ///Sets read timeout on internal `TcpStream` instance
844    ///
845    ///`timeout` will be passed to
846    ///[`TcpStream::set_read_timeout`][TcpStream::set_read_timeout].
847    ///
848    ///[TcpStream::set_read_timeout]: https://doc.rust-lang.org/std/net/struct.TcpStream.html#method.set_read_timeout
849    ///
850    ///# Examples
851    ///```
852    ///use http_req::{request::Request, uri::Uri};
853    ///use std::{time::Duration, convert::TryFrom};
854    ///
855    ///let mut writer = Vec::new();
856    ///let uri: Uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap();
857    ///const time: Option<Duration> = Some(Duration::from_secs(15));
858    ///
859    ///let response = Request::new(&uri)
860    ///    .read_timeout(time)
861    ///    .send(&mut writer)
862    ///    .unwrap();
863    ///```
864    pub fn read_timeout<T>(&mut self, timeout: Option<T>) -> &mut Self
865    where
866        Duration: From<T>,
867    {
868        self.read_timeout = timeout.map(Duration::from);
869        self
870    }
871
872    ///Sets write timeout on internal `TcpStream` instance
873    ///
874    ///`timeout` will be passed to
875    ///[`TcpStream::set_write_timeout`][TcpStream::set_write_timeout].
876    ///
877    ///[TcpStream::set_write_timeout]: https://doc.rust-lang.org/std/net/struct.TcpStream.html#method.set_write_timeout
878    ///
879    ///# Examples
880    ///```
881    ///use http_req::{request::Request, uri::Uri};
882    ///use std::{time::Duration, convert::TryFrom};
883    ///
884    ///let mut writer = Vec::new();
885    ///let uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap();
886    ///const time: Option<Duration> = Some(Duration::from_secs(5));
887    ///
888    ///let response = Request::new(&uri)
889    ///    .write_timeout(time)
890    ///    .send(&mut writer)
891    ///    .unwrap();
892    ///```
893    pub fn write_timeout<T>(&mut self, timeout: Option<T>) -> &mut Self
894    where
895        Duration: From<T>,
896    {
897        self.write_timeout = timeout.map(Duration::from);
898        self
899    }
900
901    ///Add a file containing the PEM-encoded certificates that should be added in the trusted root store.
902    pub fn root_cert_file_pem(&mut self, file_path: &'a Path) -> &mut Self {
903        self.root_cert_file_pem = Some(file_path);
904        self
905    }
906
907    ///Sends HTTP request.
908    ///
909    ///Creates `TcpStream` (and wraps it with `TlsStream` if needed). Writes request message
910    ///to created stream. Returns response for this request. Writes response's body to `writer`.
911    ///
912    ///# Examples
913    ///```
914    ///use http_req::{request::Request, uri::Uri};
915    ///use std::convert::TryFrom;
916    ///
917    ///let mut writer = Vec::new();
918    ///let uri: Uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap();
919    ///
920    ///let response = Request::new(&uri).send(&mut writer).unwrap();
921    ///```
922    pub fn send<T: Write>(&self, writer: &mut T) -> Result<Response, error::Error> {
923        let host = self
924            .inner
925            .uri
926            .host()
927            .ok_or(error::Error::Parse(error::ParseErr::UriErr))?;
928        let port = self.inner.uri.corr_port();
929
930        #[cfg(target_arch = "wasm32")]
931        let mut stream = {
932            let mut addrs = (host, port).to_socket_addrs()?;
933            let addr = addrs
934                .next()
935                .ok_or(error::Error::Parse(error::ParseErr::UriErr))?;
936            TcpStream::connect(&addr)?
937        };
938
939        #[cfg(not(target_arch = "wasm32"))]
940        let mut stream = TcpStream::connect((host, port))?;
941
942        if self.inner.uri.scheme() == "https" {
943            #[cfg(feature = "wasmedge_rustls")]
944            {
945                let cnf = tls::Config::default();
946                let mut stream = cnf.connect(host, stream)?;
947                self.inner.send(&mut stream, writer)
948            }
949
950            #[cfg(not(feature = "wasmedge_rustls"))]
951            return Err(error::Error::Tls);
952        } else {
953            self.inner.send(&mut stream, writer)
954        }
955    }
956}
957
958///Creates and sends GET request. Returns response for this request.
959///
960///# Examples
961///```
962///use http_req::request;
963///
964///let mut writer = Vec::new();
965///const uri: &str = "https://www.rust-lang.org/learn";
966///
967///let response = request::get(uri, &mut writer).unwrap();
968///```
969pub fn get<T: AsRef<str>, U: Write>(uri: T, writer: &mut U) -> Result<Response, error::Error> {
970    let uri = Uri::try_from(uri.as_ref())?;
971
972    Request::new(&uri).send(writer)
973}
974
975///Creates and sends HEAD request. Returns response for this request.
976///
977///# Examples
978///```
979///use http_req::request;
980///
981///const uri: &str = "https://www.rust-lang.org/learn";
982///let response = request::head(uri).unwrap();
983///```
984pub fn head<T: AsRef<str>>(uri: T) -> Result<Response, error::Error> {
985    let mut writer = Vec::new();
986    let uri = Uri::try_from(uri.as_ref())?;
987
988    Request::new(&uri).method(Method::HEAD).send(&mut writer)
989}
990
991///Creates and sends POST request. Returns response for this request.
992///
993///# Examples
994///```
995///use http_req::request;
996///
997///let mut writer = Vec::new();
998///const uri: &str = "https://www.rust-lang.org/learn";
999///const body: &[u8; 27] = b"field1=value1&field2=value2";
1000///
1001///let response = request::post(uri, body, &mut writer).unwrap();
1002///```
1003pub fn post<T: AsRef<str>, U: Write>(
1004    uri: T,
1005    body: &[u8],
1006    writer: &mut U,
1007) -> Result<Response, error::Error> {
1008    let uri = Uri::try_from(uri.as_ref())?;
1009
1010    Request::new(&uri)
1011        .method(Method::POST)
1012        .header("Content-Length", &body.len())
1013        .body(body)
1014        .send(writer)
1015}
1016
1017#[cfg(test)]
1018mod tests {
1019    use super::*;
1020    use crate::{error::Error, response::StatusCode};
1021    use std::io::Cursor;
1022
1023    const UNSUCCESS_CODE: StatusCode = StatusCode::new(400);
1024    const URI: &str = "http://doc.rust-lang.org/std/string/index.html";
1025    const URI_S: &str = "https://doc.rust-lang.org/std/string/index.html";
1026    const BODY: [u8; 14] = [78, 97, 109, 101, 61, 74, 97, 109, 101, 115, 43, 74, 97, 121];
1027
1028    const RESPONSE: &[u8; 129] = b"HTTP/1.1 200 OK\r\n\
1029                                         Date: Sat, 11 Jan 2003 02:44:04 GMT\r\n\
1030                                         Content-Type: text/html\r\n\
1031                                         Content-Length: 100\r\n\r\n\
1032                                         <html>hello</html>\r\n\r\nhello";
1033
1034    const RESPONSE_H: &[u8; 102] = b"HTTP/1.1 200 OK\r\n\
1035                                           Date: Sat, 11 Jan 2003 02:44:04 GMT\r\n\
1036                                           Content-Type: text/html\r\n\
1037                                           Content-Length: 100\r\n\r\n";
1038
1039    #[test]
1040    fn counter_new() {
1041        let counter = Counter::new(200);
1042
1043        assert_eq!(counter.count, 0);
1044        assert_eq!(counter.stop, 200);
1045    }
1046
1047    #[test]
1048    fn counter_next() {
1049        let mut counter = Counter::new(5);
1050
1051        assert_eq!(counter.next(), Some(false));
1052        assert_eq!(counter.next(), Some(false));
1053        assert_eq!(counter.next(), Some(false));
1054        assert_eq!(counter.next(), Some(false));
1055        assert_eq!(counter.next(), Some(true));
1056        assert_eq!(counter.next(), Some(false));
1057        assert_eq!(counter.next(), Some(false));
1058    }
1059
1060    #[test]
1061    fn copy_data_until() {
1062        let mut reader = Vec::new();
1063        reader.extend(&RESPONSE[..]);
1064
1065        let mut reader = Cursor::new(reader);
1066
1067        let [head, _body] = copy_until(
1068            &mut reader,
1069            &CR_LF_2,
1070            Instant::now() + Duration::from_secs(360),
1071        )
1072        .unwrap();
1073        assert_eq!(&head[..], &RESPONSE_H[..]);
1074    }
1075
1076    #[test]
1077    fn method_display() {
1078        const METHOD: Method = Method::HEAD;
1079        assert_eq!(&format!("{}", METHOD), "HEAD");
1080    }
1081
1082    #[test]
1083    fn request_b_new() {
1084        RequestBuilder::new(&Uri::try_from(URI).unwrap());
1085        RequestBuilder::new(&Uri::try_from(URI_S).unwrap());
1086    }
1087
1088    #[test]
1089    fn request_b_method() {
1090        let uri = Uri::try_from(URI).unwrap();
1091        let mut req = RequestBuilder::new(&uri);
1092        let req = req.method(Method::HEAD);
1093
1094        assert_eq!(req.method, Method::HEAD);
1095    }
1096
1097    #[test]
1098    fn request_b_headers() {
1099        let mut headers = Headers::new();
1100        headers.insert("Accept-Charset", "utf-8");
1101        headers.insert("Accept-Language", "en-US");
1102        headers.insert("Host", "doc.rust-lang.org");
1103        headers.insert("Connection", "Close");
1104
1105        let uri = Uri::try_from(URI).unwrap();
1106        let mut req = RequestBuilder::new(&uri);
1107        let req = req.headers(headers.clone());
1108
1109        assert_eq!(req.headers, headers);
1110    }
1111
1112    #[test]
1113    fn request_b_header() {
1114        let uri = Uri::try_from(URI).unwrap();
1115        let mut req = RequestBuilder::new(&uri);
1116        let k = "Connection";
1117        let v = "Close";
1118
1119        let mut expect_headers = Headers::new();
1120        expect_headers.insert("Host", "doc.rust-lang.org");
1121        expect_headers.insert("Referer", "http://doc.rust-lang.org/std/string/index.html");
1122        expect_headers.insert(k, v);
1123
1124        let req = req.header(k, v);
1125
1126        assert_eq!(req.headers, expect_headers);
1127    }
1128
1129    #[test]
1130    fn request_b_body() {
1131        let uri = Uri::try_from(URI).unwrap();
1132        let mut req = RequestBuilder::new(&uri);
1133        let req = req.body(&BODY);
1134
1135        assert_eq!(req.body, Some(BODY.as_ref()));
1136    }
1137
1138    #[test]
1139    fn request_b_timeout() {
1140        let uri = Uri::try_from(URI).unwrap();
1141        let mut req = RequestBuilder::new(&uri);
1142        let timeout = Some(Duration::from_secs(360));
1143
1144        req.timeout(timeout);
1145        assert_eq!(req.timeout, timeout);
1146    }
1147
1148    #[ignore]
1149    #[test]
1150    fn request_b_send() {
1151        let mut writer = Vec::new();
1152        let uri = Uri::try_from(URI).unwrap();
1153        let mut stream = TcpStream::connect((uri.host().unwrap_or(""), uri.corr_port())).unwrap();
1154
1155        RequestBuilder::new(&Uri::try_from(URI).unwrap())
1156            .header("Connection", "Close")
1157            .send(&mut stream, &mut writer)
1158            .unwrap();
1159    }
1160
1161    // #[ignore]
1162    // #[test]
1163    // fn request_b_send_secure() {
1164    //     let mut writer = Vec::new();
1165    //     let uri = Uri::try_from(URI_S).unwrap();
1166
1167    //     let stream = TcpStream::connect((uri.host().unwrap_or(""), uri.corr_port())).unwrap();
1168    //     let mut secure_stream = tls::Config::default()
1169    //         .connect(uri.host().unwrap_or(""), stream)
1170    //         .unwrap();
1171
1172    //     RequestBuilder::new(&Uri::try_from(URI_S).unwrap())
1173    //         .header("Connection", "Close")
1174    //         .send(&mut secure_stream, &mut writer)
1175    //         .unwrap();
1176    // }
1177
1178    #[test]
1179    fn request_b_parse_msg() {
1180        let uri = Uri::try_from(URI).unwrap();
1181        let req = RequestBuilder::new(&uri);
1182
1183        const DEFAULT_MSG: &str = "GET /std/string/index.html HTTP/1.1\r\n\
1184                                   Referer: http://doc.rust-lang.org/std/string/index.html\r\n\
1185                                   Host: doc.rust-lang.org\r\n\r\n";
1186        let msg = req.parse_msg();
1187        let msg = String::from_utf8_lossy(&msg).into_owned();
1188
1189        for line in DEFAULT_MSG.lines() {
1190            assert!(msg.contains(line));
1191        }
1192
1193        for line in msg.lines() {
1194            assert!(DEFAULT_MSG.contains(line));
1195        }
1196    }
1197
1198    #[test]
1199    fn request_new() {
1200        let uri = Uri::try_from(URI).unwrap();
1201        Request::new(&uri);
1202    }
1203
1204    #[test]
1205    fn request_method() {
1206        let uri = Uri::try_from(URI).unwrap();
1207        let mut req = Request::new(&uri);
1208        req.method(Method::HEAD);
1209
1210        assert_eq!(req.inner.method, Method::HEAD);
1211    }
1212
1213    #[test]
1214    fn request_headers() {
1215        let mut headers = Headers::new();
1216        headers.insert("Accept-Charset", "utf-8");
1217        headers.insert("Accept-Language", "en-US");
1218        headers.insert("Host", "doc.rust-lang.org");
1219        headers.insert("Connection", "Close");
1220
1221        let uri = Uri::try_from(URI).unwrap();
1222        let mut req = Request::new(&uri);
1223        let req = req.headers(headers.clone());
1224
1225        assert_eq!(req.inner.headers, headers);
1226    }
1227
1228    #[test]
1229    fn request_header() {
1230        let uri = Uri::try_from(URI).unwrap();
1231        let mut req = Request::new(&uri);
1232        let k = "Accept-Language";
1233        let v = "en-US";
1234
1235        let mut expect_headers = Headers::new();
1236        expect_headers.insert("Host", "doc.rust-lang.org");
1237        expect_headers.insert("Referer", "http://doc.rust-lang.org/std/string/index.html");
1238        expect_headers.insert("Connection", "Close");
1239        expect_headers.insert(k, v);
1240
1241        let req = req.header(k, v);
1242
1243        assert_eq!(req.inner.headers, expect_headers);
1244    }
1245
1246    #[test]
1247    fn request_body() {
1248        let uri = Uri::try_from(URI).unwrap();
1249        let mut req = Request::new(&uri);
1250        let req = req.body(&BODY);
1251
1252        assert_eq!(req.inner.body, Some(BODY.as_ref()));
1253    }
1254
1255    #[test]
1256    fn request_timeout() {
1257        let uri = Uri::try_from(URI).unwrap();
1258        let mut request = Request::new(&uri);
1259        let timeout = Some(Duration::from_secs(360));
1260
1261        request.timeout(timeout);
1262        assert_eq!(request.inner.timeout, timeout);
1263    }
1264
1265    #[ignore]
1266    #[test]
1267    fn request_connect_timeout() {
1268        let uri = Uri::try_from(URI).unwrap();
1269        let mut request = Request::new(&uri);
1270        request.connect_timeout(Some(Duration::from_nanos(1)));
1271
1272        assert_eq!(request.connect_timeout, Some(Duration::from_nanos(1)));
1273
1274        let err = request.send(&mut io::sink()).unwrap_err();
1275        match err {
1276            Error::IO(err) => assert_eq!(err.kind(), io::ErrorKind::TimedOut),
1277            other => panic!("Expected error to be io::Error, got: {:?}", other),
1278        };
1279    }
1280
1281    #[ignore]
1282    #[test]
1283    fn request_read_timeout() {
1284        let uri = Uri::try_from(URI).unwrap();
1285        let mut request = Request::new(&uri);
1286        request.read_timeout(Some(Duration::from_nanos(1)));
1287
1288        assert_eq!(request.read_timeout, Some(Duration::from_nanos(1)));
1289
1290        let err = request.send(&mut io::sink()).unwrap_err();
1291        match err {
1292            Error::IO(err) => match err.kind() {
1293                io::ErrorKind::WouldBlock | io::ErrorKind::TimedOut => {}
1294                other => panic!(
1295                    "Expected error kind to be one of WouldBlock/TimedOut, got: {:?}",
1296                    other
1297                ),
1298            },
1299            other => panic!("Expected error to be io::Error, got: {:?}", other),
1300        };
1301    }
1302
1303    #[test]
1304    fn request_write_timeout() {
1305        let uri = Uri::try_from(URI).unwrap();
1306        let mut request = Request::new(&uri);
1307        request.write_timeout(Some(Duration::from_nanos(100)));
1308
1309        assert_eq!(request.write_timeout, Some(Duration::from_nanos(100)));
1310    }
1311
1312    #[test]
1313    fn request_send() {
1314        let mut writer = Vec::new();
1315        let uri = Uri::try_from(URI).unwrap();
1316        let res = Request::new(&uri).send(&mut writer).unwrap();
1317
1318        assert_ne!(res.status_code(), UNSUCCESS_CODE);
1319    }
1320
1321    #[ignore]
1322    #[test]
1323    fn request_get() {
1324        let mut writer = Vec::new();
1325        let res = get(URI, &mut writer).unwrap();
1326
1327        assert_ne!(res.status_code(), UNSUCCESS_CODE);
1328
1329        let mut writer = Vec::with_capacity(200);
1330        let res = get(URI_S, &mut writer).unwrap();
1331
1332        assert_ne!(res.status_code(), UNSUCCESS_CODE);
1333    }
1334
1335    #[ignore]
1336    #[test]
1337    fn request_head() {
1338        let res = head(URI).unwrap();
1339        assert_ne!(res.status_code(), UNSUCCESS_CODE);
1340
1341        let res = head(URI_S).unwrap();
1342        assert_ne!(res.status_code(), UNSUCCESS_CODE);
1343    }
1344
1345    #[ignore]
1346    #[test]
1347    fn request_post() {
1348        let mut writer = Vec::new();
1349        let res = post(URI, &BODY, &mut writer).unwrap();
1350
1351        assert_ne!(res.status_code(), UNSUCCESS_CODE);
1352
1353        let mut writer = Vec::with_capacity(200);
1354        let res = post(URI_S, &BODY, &mut writer).unwrap();
1355
1356        assert_ne!(res.status_code(), UNSUCCESS_CODE);
1357    }
1358}