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}