From eba0471369662667c27c0a46541b8cb464c8edc0 Mon Sep 17 00:00:00 2001 From: Jakub Date: Fri, 11 Dec 2020 23:27:35 +0100 Subject: [PATCH 01/47] Update Travis CI link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7809830..7d77ab0 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # http_req -[![Build Status](https://travis-ci.org/jayjamesjay/http_req.svg?branch=master)](https://travis-ci.org/jayjamesjay/http_req) +[![Build Status](https://travis-ci.com/jayjamesjay/http_req.svg?branch=master)](https://travis-ci.com/jayjamesjay/http_req) [![Crates.io](https://img.shields.io/badge/crates.io-v0.7.2-orange.svg?longCache=true)](https://crates.io/crates/http_req) [![Docs.rs](https://docs.rs/http_req/badge.svg)](https://docs.rs/http_req/0.7.2/http_req/) From a174a5987bf35592e10e72dba75a657e92c795bf Mon Sep 17 00:00:00 2001 From: jayjamesjay Date: Sat, 12 Dec 2020 21:16:26 +0100 Subject: [PATCH 02/47] basic const fn --- src/request.rs | 8 ++++---- src/response.rs | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/request.rs b/src/request.rs index c82c872..7048e1b 100644 --- a/src/request.rs +++ b/src/request.rs @@ -27,7 +27,7 @@ pub struct Counter { } impl Counter { - pub fn new(stop: usize) -> Counter { + pub const fn new(stop: usize) -> Counter { Counter { count: 0, stop } } } @@ -98,7 +98,7 @@ where R: Read + ?Sized, { let mut buf = [0; SMALL_BUF_SIZE]; - let mut writer = Vec::new(); + let mut writer = Vec::with_capacity(SMALL_BUF_SIZE); let mut counter = Counter::new(TEST_FREQ); let mut split_idx = 0; @@ -165,7 +165,7 @@ pub enum HttpVersion { } impl HttpVersion { - pub fn as_str(self) -> &'static str { + pub const fn as_str(self) -> &'static str { use self::HttpVersion::*; match self { @@ -1329,4 +1329,4 @@ mod tests { assert_ne!(res.status_code(), UNSUCCESS_CODE); } -} +} \ No newline at end of file diff --git a/src/response.rs b/src/response.rs index 2520f86..73122e0 100644 --- a/src/response.rs +++ b/src/response.rs @@ -419,7 +419,7 @@ impl StatusCode { ///const code: StatusCode = StatusCode::new(101); ///assert!(code.is_info()) ///``` - pub fn is_info(self) -> bool { + pub const fn is_info(self) -> bool { self.0 >= 100 && self.0 < 200 } @@ -432,7 +432,7 @@ impl StatusCode { ///const code: StatusCode = StatusCode::new(204); ///assert!(code.is_success()) ///``` - pub fn is_success(self) -> bool { + pub const fn is_success(self) -> bool { self.0 >= 200 && self.0 < 300 } @@ -445,7 +445,7 @@ impl StatusCode { ///const code: StatusCode = StatusCode::new(301); ///assert!(code.is_redirect()) ///``` - pub fn is_redirect(self) -> bool { + pub const fn is_redirect(self) -> bool { self.0 >= 300 && self.0 < 400 } @@ -458,7 +458,7 @@ impl StatusCode { ///const code: StatusCode = StatusCode::new(400); ///assert!(code.is_client_err()) ///``` - pub fn is_client_err(self) -> bool { + pub const fn is_client_err(self) -> bool { self.0 >= 400 && self.0 < 500 } @@ -471,7 +471,7 @@ impl StatusCode { ///const code: StatusCode = StatusCode::new(503); ///assert!(code.is_server_err()) ///``` - pub fn is_server_err(self) -> bool { + pub const fn is_server_err(self) -> bool { self.0 >= 500 && self.0 < 600 } @@ -497,7 +497,7 @@ impl StatusCode { ///const code: StatusCode = StatusCode::new(200); ///assert_eq!(code.reason(), Some("OK")) ///``` - pub fn reason(self) -> Option<&'static str> { + pub const fn reason(self) -> Option<&'static str> { let reason = match self.0 { 100 => "Continue", 101 => "Switching Protocols", From 39c25c24a0ef0849ffa10a8d17d2744e0a3acde2 Mon Sep 17 00:00:00 2001 From: jayjamesjay Date: Sat, 12 Dec 2020 22:02:43 +0100 Subject: [PATCH 03/47] Status fn new --- src/request.rs | 4 ++-- src/response.rs | 8 +++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/request.rs b/src/request.rs index 7048e1b..05fed33 100644 --- a/src/request.rs +++ b/src/request.rs @@ -976,7 +976,7 @@ pub fn head>(uri: T) -> Result { ///let response = request::post(uri, body, &mut writer).unwrap(); ///``` pub fn post, U: Write>( - uri: T, + uri: T, body: &[u8], writer: &mut U, ) -> Result { @@ -1329,4 +1329,4 @@ mod tests { assert_ne!(res.status_code(), UNSUCCESS_CODE); } -} \ No newline at end of file +} diff --git a/src/response.rs b/src/response.rs index 73122e0..dab3c14 100644 --- a/src/response.rs +++ b/src/response.rs @@ -92,7 +92,7 @@ impl Response { ///let response = Response::try_from(RESPONSE, &mut body).unwrap(); ///assert_eq!(response.status_code(), StatusCode::new(200)); ///``` - pub fn status_code(&self) -> StatusCode { + pub const fn status_code(&self) -> StatusCode { self.status.code } @@ -188,6 +188,12 @@ pub struct Status { reason: String, } +impl Status { + pub fn new(version: &str, code: StatusCode, reason: &str) -> Status { + Status::from((version, code, reason)) + } +} + impl From<(T, U, V)> for Status where T: ToString, From b760ba78a445b1331e84a12f18b89982ba6ceaf5 Mon Sep 17 00:00:00 2001 From: jayjamesjay Date: Sat, 12 Dec 2020 23:30:12 +0100 Subject: [PATCH 04/47] replace `FromStr` with `TryFrom` for Uri & Authority --- benches/bench.rs | 6 +- examples/request_builder_get.rs | 4 +- src/request.rs | 135 +++++++++++++++------------- src/response.rs | 8 +- src/uri.rs | 154 ++++++++++++++++++-------------- 5 files changed, 169 insertions(+), 138 deletions(-) diff --git a/benches/bench.rs b/benches/bench.rs index 5b6e411..c877159 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -3,7 +3,7 @@ extern crate http_req; extern crate test; use http_req::{request::Request, response::Response, uri::Uri}; -use std::{fs::File, io::Read, time::Duration}; +use std::{convert::TryFrom, fs::File, io::Read, time::Duration}; use test::Bencher; #[bench] @@ -23,7 +23,7 @@ const URI: &str = "https://www.rust-lang.org/"; #[bench] fn request_send(b: &mut Bencher) { b.iter(|| { - let uri = URI.parse::().unwrap(); + let uri = Uri::try_from(URI).unwrap(); let timeout = Some(Duration::from_secs(6)); let mut writer = Vec::new(); @@ -38,5 +38,5 @@ fn request_send(b: &mut Bencher) { #[bench] fn parse_uri(b: &mut Bencher) { - b.iter(|| URI.parse::()); + b.iter(|| Uri::try_from(URI)); } diff --git a/examples/request_builder_get.rs b/examples/request_builder_get.rs index 5745571..52457d7 100644 --- a/examples/request_builder_get.rs +++ b/examples/request_builder_get.rs @@ -1,9 +1,9 @@ use http_req::{request::RequestBuilder, tls, uri::Uri}; -use std::net::TcpStream; +use std::{convert::TryFrom, net::TcpStream}; fn main() { //Parse uri and assign it to variable `addr` - let addr: Uri = "https://doc.rust-lang.org/".parse().unwrap(); + let addr: Uri = Uri::try_from("https://doc.rust-lang.org/").unwrap(); //Connect to remote host let stream = TcpStream::connect((addr.host().unwrap(), addr.corr_port())).unwrap(); diff --git a/src/request.rs b/src/request.rs index 05fed33..364823c 100644 --- a/src/request.rs +++ b/src/request.rs @@ -6,6 +6,7 @@ use crate::{ uri::Uri, }; use std::{ + convert::TryFrom, fmt, io::{self, ErrorKind, Read, Write}, net::{TcpStream, ToSocketAddrs}, @@ -189,10 +190,10 @@ impl fmt::Display for HttpVersion { /// ///# Examples ///``` -///use std::net::TcpStream; +///use std::{net::TcpStream, convert::TryFrom}; ///use http_req::{request::RequestBuilder, tls, uri::Uri, response::StatusCode}; /// -///let addr: Uri = "https://www.rust-lang.org/learn".parse().unwrap(); +///let addr: Uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); ///let mut writer = Vec::new(); /// ///let stream = TcpStream::connect((addr.host().unwrap(), addr.corr_port())).unwrap(); @@ -209,7 +210,7 @@ impl fmt::Display for HttpVersion { ///``` #[derive(Clone, Debug, PartialEq)] pub struct RequestBuilder<'a> { - uri: &'a Uri, + uri: &'a Uri<'a>, method: Method, version: HttpVersion, headers: Headers, @@ -222,10 +223,10 @@ impl<'a> RequestBuilder<'a> { /// ///# Examples ///``` - ///use std::net::TcpStream; + ///use std::{net::TcpStream, convert::TryFrom}; ///use http_req::{request::RequestBuilder, tls, uri::Uri}; /// - ///let addr: Uri = "https://www.rust-lang.org/learn".parse().unwrap(); + ///let addr = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); ///let mut writer = Vec::new(); /// ///let stream = TcpStream::connect((addr.host().unwrap(), addr.corr_port())).unwrap(); @@ -238,7 +239,7 @@ impl<'a> RequestBuilder<'a> { /// .send(&mut stream, &mut writer) /// .unwrap(); ///``` - pub fn new(uri: &'a Uri) -> RequestBuilder<'a> { + pub fn new(uri: &'a Uri<'a>) -> RequestBuilder<'a> { RequestBuilder { headers: Headers::default_http(uri), uri, @@ -253,10 +254,10 @@ impl<'a> RequestBuilder<'a> { /// ///# Examples ///``` - ///use std::net::TcpStream; + ///use std::{net::TcpStream, convert::TryFrom}; ///use http_req::{request::{RequestBuilder, Method}, tls, uri::Uri}; /// - ///let addr: Uri = "https://www.rust-lang.org/learn".parse().unwrap(); + ///let addr= Uri::try_from("https://www.rust-lang.org/learn").unwrap(); ///let mut writer = Vec::new(); /// ///let stream = TcpStream::connect((addr.host().unwrap(), addr.corr_port())).unwrap(); @@ -282,10 +283,10 @@ impl<'a> RequestBuilder<'a> { /// ///# Examples ///``` - ///use std::net::TcpStream; + ///use std::{net::TcpStream, convert::TryFrom}; ///use http_req::{request::{RequestBuilder, HttpVersion}, tls, uri::Uri}; /// - ///let addr: Uri = "https://www.rust-lang.org/learn".parse().unwrap(); + ///let addr = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); ///let mut writer = Vec::new(); /// ///let stream = TcpStream::connect((addr.host().unwrap(), addr.corr_port())).unwrap(); @@ -312,10 +313,10 @@ impl<'a> RequestBuilder<'a> { /// ///# Examples ///``` - ///use std::net::TcpStream; + ///use std::{net::TcpStream, convert::TryFrom}; ///use http_req::{request::{RequestBuilder, Method}, response::Headers, tls, uri::Uri}; /// - ///let addr: Uri = "https://www.rust-lang.org/learn".parse().unwrap(); + ///let addr = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); ///let mut writer = Vec::new(); ///let mut headers = Headers::new(); ///headers.insert("Accept-Charset", "utf-8"); @@ -345,10 +346,10 @@ impl<'a> RequestBuilder<'a> { /// ///# Examples ///``` - ///use std::net::TcpStream; + ///use std::{net::TcpStream, convert::TryFrom}; ///use http_req::{request::{RequestBuilder, Method}, tls, uri::Uri}; /// - ///let addr: Uri = "https://www.rust-lang.org/learn".parse().unwrap(); + ///let addr: Uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); ///let mut writer = Vec::new(); /// ///let stream = TcpStream::connect((addr.host().unwrap(), addr.corr_port())).unwrap(); @@ -374,10 +375,10 @@ impl<'a> RequestBuilder<'a> { /// ///# Examples ///``` - ///use std::net::TcpStream; + ///use std::{net::TcpStream, convert::TryFrom}; ///use http_req::{request::{RequestBuilder, Method}, tls, uri::Uri}; /// - ///let addr: Uri = "https://www.rust-lang.org/learn".parse().unwrap(); + ///let addr = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); ///const body: &[u8; 27] = b"field1=value1&field2=value2"; ///let mut writer = Vec::new(); /// @@ -403,10 +404,10 @@ impl<'a> RequestBuilder<'a> { /// ///# Examples ///``` - ///use std::{net::TcpStream, time::{Duration, Instant}}; + ///use std::{net::TcpStream, time::{Duration, Instant}, convert::TryFrom}; ///use http_req::{request::RequestBuilder, tls, uri::Uri}; /// - ///let addr: Uri = "https://www.rust-lang.org/learn".parse().unwrap(); + ///let addr = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); ///let mut writer = Vec::new(); /// ///let stream = TcpStream::connect((addr.host().unwrap(), addr.corr_port())).unwrap(); @@ -439,11 +440,11 @@ impl<'a> RequestBuilder<'a> { /// ///HTTP ///``` - ///use std::net::TcpStream; + ///use std::{net::TcpStream, convert::TryFrom}; ///use http_req::{request::RequestBuilder, uri::Uri}; /// /// //This address is automatically redirected to HTTPS, so response code will not ever be 200 - ///let addr: Uri = "http://www.rust-lang.org/learn".parse().unwrap(); + ///let addr = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); ///let mut writer = Vec::new(); ///let mut stream = TcpStream::connect((addr.host().unwrap(), addr.corr_port())).unwrap(); /// @@ -455,10 +456,10 @@ impl<'a> RequestBuilder<'a> { /// ///HTTPS ///``` - ///use std::net::TcpStream; + ///use std::{net::TcpStream, convert::TryFrom}; ///use http_req::{request::RequestBuilder, tls, uri::Uri}; /// - ///let addr: Uri = "https://www.rust-lang.org/learn".parse().unwrap(); + ///let addr: Uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); ///let mut writer = Vec::new(); /// ///let stream = TcpStream::connect((addr.host().unwrap(), addr.corr_port())).unwrap(); @@ -578,9 +579,10 @@ impl<'a> RequestBuilder<'a> { ///# Examples ///``` ///use http_req::{request::Request, uri::Uri, response::StatusCode}; +///use std::convert::TryFrom; /// ///let mut writer = Vec::new(); -///let uri: Uri = "https://www.rust-lang.org/learn".parse().unwrap(); +///let uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); /// ///let response = Request::new(&uri).send(&mut writer).unwrap();; ///assert_eq!(response.status_code(), StatusCode::new(200)); @@ -601,9 +603,10 @@ impl<'a> Request<'a> { ///# Examples ///``` ///use http_req::{request::Request, uri::Uri}; + ///use std::convert::TryFrom; /// ///let mut writer = Vec::new(); - ///let uri: Uri = "https://www.rust-lang.org/learn".parse().unwrap(); + ///let uri: Uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); /// ///let response = Request::new(&uri).send(&mut writer).unwrap();; ///``` @@ -625,9 +628,10 @@ impl<'a> Request<'a> { ///# Examples ///``` ///use http_req::{request::{Request, Method}, uri::Uri}; + ///use std::convert::TryFrom; /// ///let mut writer = Vec::new(); - ///let uri: Uri = "https://www.rust-lang.org/learn".parse().unwrap(); + ///let uri: Uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); /// ///let response = Request::new(&uri) /// .method(Method::HEAD) @@ -647,9 +651,10 @@ impl<'a> Request<'a> { ///# Examples ///``` ///use http_req::{request::{Request, HttpVersion}, uri::Uri}; + ///use std::convert::TryFrom; /// ///let mut writer = Vec::new(); - ///let uri: Uri = "https://www.rust-lang.org/learn".parse().unwrap(); + ///let uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); /// ///let response = Request::new(&uri) /// .version(HttpVersion::Http10) @@ -670,9 +675,10 @@ impl<'a> Request<'a> { ///# Examples ///``` ///use http_req::{request::Request, uri::Uri, response::Headers}; + ///use std::convert::TryFrom; /// ///let mut writer = Vec::new(); - ///let uri: Uri = "https://www.rust-lang.org/learn".parse().unwrap(); + ///let uri: Uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); /// ///let mut headers = Headers::new(); ///headers.insert("Accept-Charset", "utf-8"); @@ -698,9 +704,10 @@ impl<'a> Request<'a> { ///# Examples ///``` ///use http_req::{request::Request, uri::Uri}; + ///use std::convert::TryFrom; /// ///let mut writer = Vec::new(); - ///let uri: Uri = "https://www.rust-lang.org/learn".parse().unwrap(); + ///let uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); /// ///let response = Request::new(&uri) /// .header("Accept-Language", "en-US") @@ -721,9 +728,10 @@ impl<'a> Request<'a> { ///# Examples ///``` ///use http_req::{request::{Request, Method}, uri::Uri}; + ///use std::convert::TryFrom; /// ///let mut writer = Vec::new(); - ///let uri: Uri = "https://www.rust-lang.org/learn".parse().unwrap(); + ///let uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); ///const body: &[u8; 27] = b"field1=value1&field2=value2"; /// ///let response = Request::new(&uri) @@ -742,11 +750,11 @@ impl<'a> Request<'a> { /// ///# Examples ///``` - ///use std::time::{Duration, Instant}; + ///use std::{time::{Duration, Instant}, convert::TryFrom}; ///use http_req::{request::Request, uri::Uri}; /// ///let mut writer = Vec::new(); - ///let uri: Uri = "https://www.rust-lang.org/learn".parse().unwrap(); + ///let uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); ///const body: &[u8; 27] = b"field1=value1&field2=value2"; ///let timeout = Some(Duration::from_secs(3600)); /// @@ -777,10 +785,10 @@ impl<'a> Request<'a> { ///# Examples ///``` ///use http_req::{request::Request, uri::Uri}; - ///use std::time::Duration; + ///use std::{time::Duration, convert::TryFrom}; /// ///let mut writer = Vec::new(); - ///let uri: Uri = "https://www.rust-lang.org/learn".parse().unwrap(); + ///let uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); ///const time: Option = Some(Duration::from_secs(10)); /// ///let response = Request::new(&uri) @@ -806,10 +814,10 @@ impl<'a> Request<'a> { ///# Examples ///``` ///use http_req::{request::Request, uri::Uri}; - ///use std::time::Duration; + ///use std::{time::Duration, convert::TryFrom}; /// ///let mut writer = Vec::new(); - ///let uri: Uri = "https://www.rust-lang.org/learn".parse().unwrap(); + ///let uri: Uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); ///const time: Option = Some(Duration::from_secs(15)); /// ///let response = Request::new(&uri) @@ -835,10 +843,10 @@ impl<'a> Request<'a> { ///# Examples ///``` ///use http_req::{request::Request, uri::Uri}; - ///use std::time::Duration; + ///use std::{time::Duration, convert::TryFrom}; /// ///let mut writer = Vec::new(); - ///let uri: Uri = "https://www.rust-lang.org/learn".parse().unwrap(); + ///let uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); ///const time: Option = Some(Duration::from_secs(5)); /// ///let response = Request::new(&uri) @@ -868,9 +876,10 @@ impl<'a> Request<'a> { ///# Examples ///``` ///use http_req::{request::Request, uri::Uri}; + ///use std::convert::TryFrom; /// ///let mut writer = Vec::new(); - ///let uri: Uri = "https://www.rust-lang.org/learn".parse().unwrap(); + ///let uri: Uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); /// ///let response = Request::new(&uri).send(&mut writer).unwrap(); ///``` @@ -942,7 +951,7 @@ where ///let response = request::get(uri, &mut writer).unwrap(); ///``` pub fn get, U: Write>(uri: T, writer: &mut U) -> Result { - let uri = uri.as_ref().parse::()?; + let uri = Uri::try_from(uri.as_ref())?; Request::new(&uri).send(writer) } @@ -958,7 +967,7 @@ pub fn get, U: Write>(uri: T, writer: &mut U) -> Result>(uri: T) -> Result { let mut writer = Vec::new(); - let uri = uri.as_ref().parse::()?; + let uri = Uri::try_from(uri.as_ref())?; Request::new(&uri).method(Method::HEAD).send(&mut writer) } @@ -980,7 +989,7 @@ pub fn post, U: Write>( body: &[u8], writer: &mut U, ) -> Result { - let uri = uri.as_ref().parse::()?; + let uri = Uri::try_from(uri.as_ref())?; Request::new(&uri) .method(Method::POST) @@ -1056,13 +1065,13 @@ mod tests { #[test] fn request_b_new() { - RequestBuilder::new(&URI.parse().unwrap()); - RequestBuilder::new(&URI_S.parse().unwrap()); + RequestBuilder::new(&Uri::try_from(URI).unwrap()); + RequestBuilder::new(&Uri::try_from(URI_S).unwrap()); } #[test] fn request_b_method() { - let uri: Uri = URI.parse().unwrap(); + let uri = Uri::try_from(URI).unwrap(); let mut req = RequestBuilder::new(&uri); let req = req.method(Method::HEAD); @@ -1077,7 +1086,7 @@ mod tests { headers.insert("Host", "doc.rust-lang.org"); headers.insert("Connection", "Close"); - let uri: Uri = URI.parse().unwrap(); + let uri = Uri::try_from(URI).unwrap(); let mut req = RequestBuilder::new(&uri); let req = req.headers(headers.clone()); @@ -1086,7 +1095,7 @@ mod tests { #[test] fn request_b_header() { - let uri: Uri = URI.parse().unwrap(); + let uri = Uri::try_from(URI).unwrap(); let mut req = RequestBuilder::new(&uri); let k = "Connection"; let v = "Close"; @@ -1103,7 +1112,7 @@ mod tests { #[test] fn request_b_body() { - let uri: Uri = URI.parse().unwrap(); + let uri = Uri::try_from(URI).unwrap(); let mut req = RequestBuilder::new(&uri); let req = req.body(&BODY); @@ -1112,7 +1121,7 @@ mod tests { #[test] fn request_b_timeout() { - let uri = URI.parse().unwrap(); + let uri = Uri::try_from(URI).unwrap(); let mut req = RequestBuilder::new(&uri); let timeout = Some(Duration::from_secs(360)); @@ -1124,10 +1133,10 @@ mod tests { #[test] fn request_b_send() { let mut writer = Vec::new(); - let uri: Uri = URI.parse().unwrap(); + let uri = Uri::try_from(URI).unwrap(); let mut stream = TcpStream::connect((uri.host().unwrap_or(""), uri.corr_port())).unwrap(); - RequestBuilder::new(&URI.parse().unwrap()) + RequestBuilder::new(&Uri::try_from(URI).unwrap()) .header("Connection", "Close") .send(&mut stream, &mut writer) .unwrap(); @@ -1137,14 +1146,14 @@ mod tests { #[test] fn request_b_send_secure() { let mut writer = Vec::new(); - let uri: Uri = URI_S.parse().unwrap(); + let uri = Uri::try_from(URI_S).unwrap(); let stream = TcpStream::connect((uri.host().unwrap_or(""), uri.corr_port())).unwrap(); let mut secure_stream = tls::Config::default() .connect(uri.host().unwrap_or(""), stream) .unwrap(); - RequestBuilder::new(&URI_S.parse().unwrap()) + RequestBuilder::new(&Uri::try_from(URI_S).unwrap()) .header("Connection", "Close") .send(&mut secure_stream, &mut writer) .unwrap(); @@ -1152,7 +1161,7 @@ mod tests { #[test] fn request_b_parse_msg() { - let uri = URI.parse().unwrap(); + let uri = Uri::try_from(URI).unwrap(); let req = RequestBuilder::new(&uri); const DEFAULT_MSG: &str = "GET /std/string/index.html HTTP/1.1\r\n\ @@ -1172,13 +1181,13 @@ mod tests { #[test] fn request_new() { - let uri = URI.parse().unwrap(); + let uri = Uri::try_from(URI).unwrap(); Request::new(&uri); } #[test] fn request_method() { - let uri = URI.parse().unwrap(); + let uri = Uri::try_from(URI).unwrap(); let mut req = Request::new(&uri); req.method(Method::HEAD); @@ -1193,7 +1202,7 @@ mod tests { headers.insert("Host", "doc.rust-lang.org"); headers.insert("Connection", "Close"); - let uri: Uri = URI.parse().unwrap(); + let uri = Uri::try_from(URI).unwrap(); let mut req = Request::new(&uri); let req = req.headers(headers.clone()); @@ -1202,7 +1211,7 @@ mod tests { #[test] fn request_header() { - let uri: Uri = URI.parse().unwrap(); + let uri = Uri::try_from(URI).unwrap(); let mut req = Request::new(&uri); let k = "Accept-Language"; let v = "en-US"; @@ -1220,7 +1229,7 @@ mod tests { #[test] fn request_body() { - let uri = URI.parse().unwrap(); + let uri = Uri::try_from(URI).unwrap(); let mut req = Request::new(&uri); let req = req.body(&BODY); @@ -1229,7 +1238,7 @@ mod tests { #[test] fn request_timeout() { - let uri = URI.parse().unwrap(); + let uri = Uri::try_from(URI).unwrap(); let mut request = Request::new(&uri); let timeout = Some(Duration::from_secs(360)); @@ -1239,7 +1248,7 @@ mod tests { #[test] fn request_connect_timeout() { - let uri = URI.parse().unwrap(); + let uri = Uri::try_from(URI).unwrap(); let mut request = Request::new(&uri); request.connect_timeout(Some(Duration::from_nanos(1))); @@ -1255,7 +1264,7 @@ mod tests { #[ignore] #[test] fn request_read_timeout() { - let uri = URI.parse().unwrap(); + let uri = Uri::try_from(URI).unwrap(); let mut request = Request::new(&uri); request.read_timeout(Some(Duration::from_nanos(1))); @@ -1276,7 +1285,7 @@ mod tests { #[test] fn request_write_timeout() { - let uri = URI.parse().unwrap(); + let uri = Uri::try_from(URI).unwrap(); let mut request = Request::new(&uri); request.write_timeout(Some(Duration::from_nanos(100))); @@ -1286,7 +1295,7 @@ mod tests { #[test] fn request_send() { let mut writer = Vec::new(); - let uri = URI.parse().unwrap(); + let uri = Uri::try_from(URI).unwrap(); let res = Request::new(&uri).send(&mut writer).unwrap(); assert_ne!(res.status_code(), UNSUCCESS_CODE); diff --git a/src/response.rs b/src/response.rs index dab3c14..30bac49 100644 --- a/src/response.rs +++ b/src/response.rs @@ -331,8 +331,9 @@ impl Headers { ///# Examples ///``` ///use http_req::{response::Headers, uri::Uri}; + ///use std::convert::TryFrom; /// - ///let uri: Uri = "https://www.rust-lang.org/learn".parse().unwrap(); + ///let uri: Uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); ///let headers = Headers::default_http(&uri); ///``` pub fn default_http(uri: &Uri) -> Headers { @@ -621,6 +622,7 @@ where #[cfg(test)] mod tests { use super::*; + use std::convert::TryFrom; const RESPONSE: &[u8; 129] = b"HTTP/1.1 200 OK\r\n\ Date: Sat, 11 Jan 2003 02:44:04 GMT\r\n\ @@ -793,9 +795,7 @@ mod tests { #[test] fn headers_default_http() { - let uri = "http://doc.rust-lang.org/std/string/index.html" - .parse() - .unwrap(); + let uri = Uri::try_from("http://doc.rust-lang.org/std/string/index.html").unwrap(); let mut headers = Headers::with_capacity(4); headers.insert("Host", "doc.rust-lang.org"); diff --git a/src/uri.rs b/src/uri.rs index 08220c7..378a602 100644 --- a/src/uri.rs +++ b/src/uri.rs @@ -1,6 +1,7 @@ //! uri operations use crate::error::{Error, ParseErr}; use std::{ + convert::TryFrom, fmt, ops::{Index, Range}, str, @@ -40,6 +41,15 @@ impl From for Range { } } +impl Index for str { + type Output = str; + + #[inline] + fn index(&self, index: RangeC) -> &str { + &self[..][Range::from(index)] + } +} + impl Index for String { type Output = str; @@ -54,28 +64,30 @@ impl Index for String { ///# Example ///``` ///use http_req::uri::Uri; +///use std::convert::TryFrom; /// -///let uri: Uri = "https://user:info@foo.com:12/bar/baz?query#fragment".parse().unwrap(); +///let uri: Uri = Uri::try_from("https://user:info@foo.com:12/bar/baz?query#fragment").unwrap();; ///assert_eq!(uri.host(), Some("foo.com")); ///``` #[derive(Clone, Debug, PartialEq)] -pub struct Uri { - inner: String, +pub struct Uri<'a> { + inner: &'a str, scheme: RangeC, - authority: Option, + authority: Option>, path: Option, query: Option, fragment: Option, } -impl Uri { +impl<'a> Uri<'a> { ///Returns scheme of this `Uri`. /// ///# Example ///``` ///use http_req::uri::Uri; + ///use std::convert::TryFrom; /// - ///let uri: Uri = "https://user:info@foo.com:12/bar/baz?query#fragment".parse().unwrap(); + ///let uri: Uri = Uri::try_from("https://user:info@foo.com:12/bar/baz?query#fragment").unwrap();; ///assert_eq!(uri.scheme(), "https"); ///``` pub fn scheme(&self) -> &str { @@ -87,8 +99,9 @@ impl Uri { ///# Example ///``` ///use http_req::uri::Uri; + ///use std::convert::TryFrom; /// - ///let uri: Uri = "https://user:info@foo.com:12/bar/baz?query#fragment".parse().unwrap(); + ///let uri: Uri = Uri::try_from("https://user:info@foo.com:12/bar/baz?query#fragment").unwrap();; ///assert_eq!(uri.user_info(), Some("user:info")); ///``` pub fn user_info(&self) -> Option<&str> { @@ -100,8 +113,9 @@ impl Uri { ///# Example ///``` ///use http_req::uri::Uri; + ///use std::convert::TryFrom; /// - ///let uri: Uri = "https://user:info@foo.com:12/bar/baz?query#fragment".parse().unwrap(); + ///let uri: Uri = Uri::try_from("https://user:info@foo.com:12/bar/baz?query#fragment").unwrap();; ///assert_eq!(uri.host(), Some("foo.com")); ///``` pub fn host(&self) -> Option<&str> { @@ -113,8 +127,9 @@ impl Uri { ///# Example ///``` ///use http_req::uri::Uri; + ///use std::convert::TryFrom; /// - ///let uri: Uri = "https://user:info@foo.com:12/bar/baz?query#fragment".parse().unwrap(); + ///let uri: Uri = Uri::try_from("https://user:info@foo.com:12/bar/baz?query#fragment").unwrap();; ///assert_eq!(uri.host_header(), Some("foo.com:12".to_string())); ///``` pub fn host_header(&self) -> Option { @@ -129,8 +144,9 @@ impl Uri { ///# Example ///``` ///use http_req::uri::Uri; + ///use std::convert::TryFrom; /// - ///let uri: Uri = "https://user:info@foo.com:12/bar/baz?query#fragment".parse().unwrap(); + ///let uri: Uri = Uri::try_from("https://user:info@foo.com:12/bar/baz?query#fragment").unwrap();; ///assert_eq!(uri.port(), Some(12)); ///``` pub fn port(&self) -> Option { @@ -143,8 +159,9 @@ impl Uri { ///# Example ///``` ///use http_req::uri::Uri; + ///use std::convert::TryFrom; /// - ///let uri: Uri = "https://user:info@foo.com:12/bar/baz?query#fragment".parse().unwrap(); + ///let uri: Uri = Uri::try_from("https://user:info@foo.com:12/bar/baz?query#fragment").unwrap();; ///assert_eq!(uri.corr_port(), 12); ///``` pub fn corr_port(&self) -> u16 { @@ -164,8 +181,9 @@ impl Uri { ///# Example ///``` ///use http_req::uri::Uri; + ///use std::convert::TryFrom; /// - ///let uri: Uri = "https://user:info@foo.com:12/bar/baz?query#fragment".parse().unwrap(); + ///let uri: Uri = Uri::try_from("https://user:info@foo.com:12/bar/baz?query#fragment").unwrap();; ///assert_eq!(uri.path(), Some("/bar/baz")); ///``` pub fn path(&self) -> Option<&str> { @@ -177,8 +195,9 @@ impl Uri { ///# Example ///``` ///use http_req::uri::Uri; + ///use std::convert::TryFrom; /// - ///let uri: Uri = "https://user:info@foo.com:12/bar/baz?query#fragment".parse().unwrap(); + ///let uri: Uri = Uri::try_from("https://user:info@foo.com:12/bar/baz?query#fragment").unwrap();; ///assert_eq!(uri.query(), Some("query")); ///``` pub fn query(&self) -> Option<&str> { @@ -190,8 +209,9 @@ impl Uri { ///# Example ///``` ///use http_req::uri::Uri; + ///use std::convert::TryFrom; /// - ///let uri: Uri = "https://user:info@foo.com:12/bar/baz?query#fragment".parse().unwrap(); + ///let uri: Uri = Uri::try_from("https://user:info@foo.com:12/bar/baz?query#fragment").unwrap();; ///assert_eq!(uri.fragment(), Some("fragment")); ///``` pub fn fragment(&self) -> Option<&str> { @@ -203,8 +223,9 @@ impl Uri { ///# Example ///``` ///use http_req::uri::Uri; + ///use std::convert::TryFrom; /// - ///let uri: Uri = "https://user:info@foo.com:12/bar/baz?query#fragment".parse().unwrap(); + ///let uri: Uri = Uri::try_from("https://user:info@foo.com:12/bar/baz?query#fragment").unwrap();; ///assert_eq!(uri.resource(), "/bar/baz?query#fragment"); ///``` pub fn resource(&self) -> &str { @@ -221,7 +242,7 @@ impl Uri { } } -impl fmt::Display for Uri { +impl<'a> fmt::Display for Uri<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let uri = if let Some(auth) = &self.authority { let mut uri = self.inner.to_string(); @@ -238,13 +259,10 @@ impl fmt::Display for Uri { } } -impl str::FromStr for Uri { - type Err = Error; - - fn from_str(s: &str) -> Result { - let mut s = s.to_string(); - remove_spaces(&mut s); +impl<'a> TryFrom<&'a str> for Uri<'a> { + type Error = Error; + fn try_from(s: &'a str) -> Result { let (scheme, mut uri_part) = get_chunks(&s, Some(RangeC::new(0, s.len())), ":"); let scheme = scheme.ok_or(ParseErr::UriErr)?; @@ -255,7 +273,7 @@ impl str::FromStr for Uri { let (auth, part) = get_chunks(&s, Some(RangeC::new(u.start + 2, u.end)), "/"); authority = if let Some(a) = auth { - Some(s[a].parse()?) + Some(Authority::try_from(&s[a])?) } else { None }; @@ -288,30 +306,32 @@ impl str::FromStr for Uri { ///# Example ///``` ///use http_req::uri::Authority; +///use std::convert::TryFrom; /// -///let auth: Authority = "user:info@foo.com:443".parse().unwrap(); +///let auth: Authority = Authority::try_from("user:info@foo.com:443").unwrap(); ///assert_eq!(auth.host(), "foo.com"); ///``` #[derive(Clone, Debug, PartialEq)] -pub struct Authority { - inner: String, +pub struct Authority<'a> { + inner: &'a str, username: Option, password: Option, host: RangeC, port: Option, } -impl Authority { +impl<'a> Authority<'a> { ///Returns username of this `Authority` /// ///# Example ///``` ///use http_req::uri::Authority; + ///use std::convert::TryFrom; /// - ///let auth: Authority = "user:info@foo.com:443".parse().unwrap(); + ///let auth: Authority = Authority::try_from("user:info@foo.com:443").unwrap(); ///assert_eq!(auth.username(), Some("user")); ///``` - pub fn username(&self) -> Option<&str> { + pub fn username(&self) -> Option<&'a str> { self.username.map(|r| &self.inner[r]) } @@ -320,8 +340,9 @@ impl Authority { ///# Example ///``` ///use http_req::uri::Authority; + ///use std::convert::TryFrom; /// - ///let auth: Authority = "user:info@foo.com:443".parse().unwrap(); + ///let auth: Authority = Authority::try_from("user:info@foo.com:443").unwrap(); ///assert_eq!(auth.password(), Some("info")); ///``` pub fn password(&self) -> Option<&str> { @@ -333,8 +354,9 @@ impl Authority { ///# Example ///``` ///use http_req::uri::Authority; + ///use std::convert::TryFrom; /// - ///let auth: Authority = "user:info@foo.com:443".parse().unwrap(); + ///let auth: Authority = Authority::try_from("user:info@foo.com:443").unwrap(); ///assert_eq!(auth.user_info(), Some("user:info")); ///``` pub fn user_info(&self) -> Option<&str> { @@ -350,8 +372,9 @@ impl Authority { ///# Example ///``` ///use http_req::uri::Authority; + ///use std::convert::TryFrom; /// - ///let auth: Authority = "user:info@foo.com:443".parse().unwrap(); + ///let auth: Authority = Authority::try_from("user:info@foo.com:443").unwrap(); ///assert_eq!(auth.host(), "foo.com"); ///``` pub fn host(&self) -> &str { @@ -363,8 +386,9 @@ impl Authority { ///# Example ///``` ///use http_req::uri::Authority; + ///use std::convert::TryFrom; /// - ///let auth: Authority = "user:info@foo.com:443".parse().unwrap(); + ///let auth: Authority = Authority::try_from("user:info@foo.com:443").unwrap(); ///assert_eq!(auth.port(), Some(443)); ///``` pub fn port(&self) -> Option { @@ -372,12 +396,10 @@ impl Authority { } } -impl str::FromStr for Authority { - type Err = ParseErr; - - fn from_str(s: &str) -> Result { - let inner = s.to_string(); +impl<'a> TryFrom<&'a str> for Authority<'a> { + type Error = ParseErr; + fn try_from(s: &'a str) -> Result { let mut username = None; let mut password = None; @@ -402,13 +424,13 @@ impl str::FromStr for Authority { let host = host.ok_or(ParseErr::UriErr)?; if let Some(p) = port { - if inner[p].parse::().is_err() { + if s[p].parse::().is_err() { return Err(ParseErr::UriErr); } } Ok(Authority { - inner, + inner: s, username, password, host, @@ -417,7 +439,7 @@ impl str::FromStr for Authority { } } -impl fmt::Display for Authority { +impl<'a> fmt::Display for Authority<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let auth = if let Some(pass) = self.password { let range = Range::from(pass); @@ -436,7 +458,7 @@ impl fmt::Display for Authority { } //Removes whitespace from `text` -fn remove_spaces(text: &mut String) { +pub fn remove_spaces(text: &mut String) { text.retain(|c| !c.is_whitespace()); } @@ -502,9 +524,10 @@ mod tests { #[test] fn uri_full_parse() { - let uri = "abc://username:password@example.com:123/path/data?key=value&key2=value2#fragid1" - .parse::() - .unwrap(); + let uri = Uri::try_from( + "abc://username:password@example.com:123/path/data?key=value&key2=value2#fragid1", + ) + .unwrap(); assert_eq!(uri.scheme(), "abc"); assert_eq!(uri.user_info(), Some("username:password")); @@ -519,7 +542,7 @@ mod tests { #[test] fn uri_parse() { for uri in TEST_URIS.iter() { - uri.parse::().unwrap(); + Uri::try_from(*uri).unwrap(); } } @@ -527,7 +550,7 @@ mod tests { fn uri_scheme() { let uris: Vec<_> = TEST_URIS .iter() - .map(|uri| uri.parse::().unwrap()) + .map(|uri| Uri::try_from(*uri).unwrap()) .collect(); assert_eq!(uris[0].scheme(), "https"); @@ -541,7 +564,7 @@ mod tests { fn uri_uesr_info() { let uris: Vec<_> = TEST_URIS .iter() - .map(|uri| uri.parse::().unwrap()) + .map(|uri| Uri::try_from(*uri).unwrap()) .collect(); assert_eq!(uris[0].user_info(), Some("user:info")); @@ -555,7 +578,7 @@ mod tests { fn uri_host() { let uris: Vec<_> = TEST_URIS .iter() - .map(|uri| uri.parse::().unwrap()) + .map(|uri| Uri::try_from(*uri).unwrap()) .collect(); assert_eq!(uris[0].host(), Some("foo.com")); @@ -567,12 +590,11 @@ mod tests { #[test] fn uri_host_header() { - let uri_def: Uri = "https://en.wikipedia.org:443/wiki/Hypertext_Transfer_Protocol" - .parse() - .unwrap(); + let uri_def: Uri = + Uri::try_from("https://en.wikipedia.org:443/wiki/Hypertext_Transfer_Protocol").unwrap(); let uris: Vec<_> = TEST_URIS .iter() - .map(|uri| uri.parse::().unwrap()) + .map(|uri| Uri::try_from(*uri).unwrap()) .collect(); assert_eq!(uris[0].host_header(), Some("foo.com:12".to_string())); @@ -584,7 +606,7 @@ mod tests { fn uri_port() { let uris: Vec<_> = TEST_URIS .iter() - .map(|uri| uri.parse::().unwrap()) + .map(|uri| Uri::try_from(*uri).unwrap()) .collect(); assert_eq!(uris[0].port(), Some(12)); @@ -599,7 +621,7 @@ mod tests { fn uri_corr_port() { let uris: Vec<_> = TEST_URIS .iter() - .map(|uri| uri.parse::().unwrap()) + .map(|uri| Uri::try_from(*uri).unwrap()) .collect(); assert_eq!(uris[0].corr_port(), 12); @@ -613,7 +635,7 @@ mod tests { fn uri_path() { let uris: Vec<_> = TEST_URIS .iter() - .map(|uri| uri.parse::().unwrap()) + .map(|uri| Uri::try_from(*uri).unwrap()) .collect(); assert_eq!(uris[0].path(), Some("/bar/baz")); @@ -630,7 +652,7 @@ mod tests { fn uri_query() { let uris: Vec<_> = TEST_URIS .iter() - .map(|uri| uri.parse::().unwrap()) + .map(|uri| Uri::try_from(*uri).unwrap()) .collect(); assert_eq!(uris[0].query(), Some("query")); @@ -644,7 +666,7 @@ mod tests { fn uri_fragment() { let uris: Vec<_> = TEST_URIS .iter() - .map(|uri| uri.parse::().unwrap()) + .map(|uri| Uri::try_from(*uri).unwrap()) .collect(); assert_eq!(uris[0].fragment(), Some("fragment")); @@ -658,7 +680,7 @@ mod tests { fn uri_resource() { let uris: Vec<_> = TEST_URIS .iter() - .map(|uri| uri.parse::().unwrap()) + .map(|uri| Uri::try_from(*uri).unwrap()) .collect(); assert_eq!(uris[0].resource(), "/bar/baz?query#fragment"); @@ -672,7 +694,7 @@ mod tests { fn uri_display() { let uris: Vec<_> = TEST_URIS .iter() - .map(|uri| uri.parse::().unwrap()) + .map(|uri| Uri::try_from(*uri).unwrap()) .collect(); assert_eq!( @@ -690,7 +712,7 @@ mod tests { fn authority_username() { let auths: Vec<_> = TEST_AUTH .iter() - .map(|auth| auth.parse::().unwrap()) + .map(|auth| Authority::try_from(*auth).unwrap()) .collect(); assert_eq!(auths[0].username(), Some("user")); @@ -703,7 +725,7 @@ mod tests { fn authority_password() { let auths: Vec<_> = TEST_AUTH .iter() - .map(|auth| auth.parse::().unwrap()) + .map(|auth| Authority::try_from(*auth).unwrap()) .collect(); assert_eq!(auths[0].password(), Some("info")); @@ -716,7 +738,7 @@ mod tests { fn authority_host() { let auths: Vec<_> = TEST_AUTH .iter() - .map(|auth| auth.parse::().unwrap()) + .map(|auth| Authority::try_from(*auth).unwrap()) .collect(); assert_eq!(auths[0].host(), "foo.com"); @@ -729,7 +751,7 @@ mod tests { fn authority_port() { let auths: Vec<_> = TEST_AUTH .iter() - .map(|auth| auth.parse::().unwrap()) + .map(|auth| Authority::try_from(*auth).unwrap()) .collect(); assert_eq!(auths[0].port(), Some(12)); @@ -741,7 +763,7 @@ mod tests { #[test] fn authority_from_str() { for auth in TEST_AUTH.iter() { - auth.parse::().unwrap(); + Authority::try_from(*auth).unwrap(); } } @@ -749,7 +771,7 @@ mod tests { fn authority_display() { let auths: Vec<_> = TEST_AUTH .iter() - .map(|auth| auth.parse::().unwrap()) + .map(|auth| Authority::try_from(*auth).unwrap()) .collect(); assert_eq!("user:****@foo.com:12", auths[0].to_string()); From 7d4cee59f6282a08853967dac01354d1652024a5 Mon Sep 17 00:00:00 2001 From: jayjamesjay Date: Wed, 30 Jun 2021 17:38:12 +0200 Subject: [PATCH 05/47] update deps --- .gitignore | 2 +- Cargo.lock | 22 +++++++++++++++++----- Cargo.toml | 4 ++-- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index b309da6..a008342 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ /target **/*.rs.bk -/.idea \ No newline at end of file +.DS_store \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 20f0585..acfd7c7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,7 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] name = "autocfg" version = "1.0.1" @@ -91,7 +93,7 @@ dependencies = [ "native-tls", "rustls", "unicase", - "webpki", + "webpki 0.22.0", "webpki-roots", ] @@ -293,7 +295,7 @@ dependencies = [ "log", "ring", "sct", - "webpki", + "webpki 0.21.4", ] [[package]] @@ -483,13 +485,23 @@ dependencies = [ "untrusted", ] +[[package]] +name = "webpki" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "webpki-roots" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82015b7e0b8bad8185994674a13a93306bea76cf5a16c5a181382fd3a5ec2376" +checksum = "b533367e7546a31c15093b12a273004b79eb3b57b15359af710460f9aee94cf8" dependencies = [ - "webpki", + "webpki 0.22.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index dfa43e1..c2faf9e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,11 +26,11 @@ version = "^0.19" optional = true [dependencies.webpki] -version = "^0.21" +version = "^0.22" optional = true [dependencies.webpki-roots] -version = "^0.21" +version = "^0.22" optional = true [badges] From 95b9e01579a904a4c7e165ebf5dd88c909350b69 Mon Sep 17 00:00:00 2001 From: jayjamesjay Date: Thu, 1 Jul 2021 13:25:52 +0200 Subject: [PATCH 06/47] v0.8.0 --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index acfd7c7..045e50c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -88,7 +88,7 @@ dependencies = [ [[package]] name = "http_req" -version = "0.7.2" +version = "0.8.0" dependencies = [ "native-tls", "rustls", diff --git a/Cargo.toml b/Cargo.toml index c2faf9e..97337a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "http_req" -version = "0.7.2" +version = "0.8.0" license = "MIT" description = "simple and lightweight HTTP client with built-in HTTPS support" repository = "https://github.com/jayjamesjay/http_req" From 1dff4d71db991dfb35a75d52eb0928b1d0976345 Mon Sep 17 00:00:00 2001 From: jayjamesjay Date: Thu, 1 Jul 2021 13:37:26 +0200 Subject: [PATCH 07/47] update docs --- README.md | 4 ++-- src/request.rs | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7d77ab0..2444dc4 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # http_req [![Build Status](https://travis-ci.com/jayjamesjay/http_req.svg?branch=master)](https://travis-ci.com/jayjamesjay/http_req) -[![Crates.io](https://img.shields.io/badge/crates.io-v0.7.2-orange.svg?longCache=true)](https://crates.io/crates/http_req) +[![Crates.io](https://img.shields.io/badge/crates.io-v0.8.0-orange.svg?longCache=true)](https://crates.io/crates/http_req) [![Docs.rs](https://docs.rs/http_req/badge.svg)](https://docs.rs/http_req/0.7.2/http_req/) Simple and lightweight HTTP client with built-in HTTPS support. @@ -27,7 +27,7 @@ fn main() { In order to use `http_req` with `rustls` in your project, add following lines to `Cargo.toml`: ```toml [dependencies] -http_req = {version="^0.7", default-features = false, features = ["rust-tls"]} +http_req = {version="^0.8", default-features = false, features = ["rust-tls"]} ``` ## License diff --git a/src/request.rs b/src/request.rs index 364823c..274a878 100644 --- a/src/request.rs +++ b/src/request.rs @@ -49,6 +49,7 @@ impl Iterator for Counter { } ///Copies data from `reader` to `writer` until the `deadline` is reached. +///Limitations of current implementation may cause exceeding the deadline. ///Returns how many bytes has been read. pub fn copy_with_timeout(reader: &mut R, writer: &mut W, deadline: Instant) -> io::Result where From 386d56f6ce18a20aa3e9a764b24c7c985ec44111 Mon Sep 17 00:00:00 2001 From: jayjamesjay Date: Sun, 4 Jul 2021 00:56:32 +0200 Subject: [PATCH 08/47] replace Travis CI with Github Actions --- .travis.yml | 14 -------------- Cargo.toml | 3 --- LICENSE | 2 +- README.md | 2 +- 4 files changed, 2 insertions(+), 19 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index fa1b5d1..0000000 --- a/.travis.yml +++ /dev/null @@ -1,14 +0,0 @@ -language: rust -rust: - - stable - - beta - - nightly -matrix: - allow_failures: - - rust: nightly -os: - - linux - - osx -script: - - cargo test - - cargo test --features rust-tls --no-default-features diff --git a/Cargo.toml b/Cargo.toml index 97337a3..96076ab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,3 @@ optional = true [dependencies.webpki-roots] version = "^0.22" optional = true - -[badges] -travis-ci = { repository = "jayjamesjay/http_req"} diff --git a/LICENSE b/LICENSE index 65f6868..a62e670 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2018-2019 jayjamesjay +Copyright (c) 2018-2021 jayjamesjay Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 2444dc4..4672a71 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # http_req -[![Build Status](https://travis-ci.com/jayjamesjay/http_req.svg?branch=master)](https://travis-ci.com/jayjamesjay/http_req) +[![Rust](https://github.com/jayjamesjay/http_req/actions/workflows/rust.yml/badge.svg)](https://github.com/jayjamesjay/http_req/actions/workflows/rust.yml) [![Crates.io](https://img.shields.io/badge/crates.io-v0.8.0-orange.svg?longCache=true)](https://crates.io/crates/http_req) [![Docs.rs](https://docs.rs/http_req/badge.svg)](https://docs.rs/http_req/0.7.2/http_req/) From 29fc1f01bb09e75594976e18c086d7e1b91e2636 Mon Sep 17 00:00:00 2001 From: jayjamesjay Date: Thu, 8 Jul 2021 00:16:26 +0200 Subject: [PATCH 09/47] fix rustls issue --- Cargo.lock | 22 ++++++---------------- Cargo.toml | 6 +++--- README.md | 2 +- 3 files changed, 10 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 045e50c..3756e71 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -88,12 +88,12 @@ dependencies = [ [[package]] name = "http_req" -version = "0.8.0" +version = "0.8.1" dependencies = [ "native-tls", "rustls", "unicase", - "webpki 0.22.0", + "webpki", "webpki-roots", ] @@ -295,7 +295,7 @@ dependencies = [ "log", "ring", "sct", - "webpki 0.21.4", + "webpki", ] [[package]] @@ -485,23 +485,13 @@ dependencies = [ "untrusted", ] -[[package]] -name = "webpki" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" -dependencies = [ - "ring", - "untrusted", -] - [[package]] name = "webpki-roots" -version = "0.22.0" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b533367e7546a31c15093b12a273004b79eb3b57b15359af710460f9aee94cf8" +checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940" dependencies = [ - "webpki 0.22.0", + "webpki", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 96076ab..4b61bf3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "http_req" -version = "0.8.0" +version = "0.8.1" license = "MIT" description = "simple and lightweight HTTP client with built-in HTTPS support" repository = "https://github.com/jayjamesjay/http_req" @@ -26,9 +26,9 @@ version = "^0.19" optional = true [dependencies.webpki] -version = "^0.22" +version = "^0.21" optional = true [dependencies.webpki-roots] -version = "^0.22" +version = "^0.21" optional = true diff --git a/README.md b/README.md index 4672a71..8d88f3b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # http_req [![Rust](https://github.com/jayjamesjay/http_req/actions/workflows/rust.yml/badge.svg)](https://github.com/jayjamesjay/http_req/actions/workflows/rust.yml) -[![Crates.io](https://img.shields.io/badge/crates.io-v0.8.0-orange.svg?longCache=true)](https://crates.io/crates/http_req) +[![Crates.io](https://img.shields.io/badge/crates.io-v0.8.1-orange.svg?longCache=true)](https://crates.io/crates/http_req) [![Docs.rs](https://docs.rs/http_req/badge.svg)](https://docs.rs/http_req/0.7.2/http_req/) Simple and lightweight HTTP client with built-in HTTPS support. From 55abf2a41251019e5ba0d649d7a969a066ef2b6e Mon Sep 17 00:00:00 2001 From: jayjamesjay Date: Fri, 9 Jul 2021 11:40:52 +0200 Subject: [PATCH 10/47] update gh actions --- .github/workflows/rust.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index fec17ad..3f9b12a 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -11,5 +11,7 @@ jobs: - uses: actions/checkout@v1 - name: Build run: cargo build --verbose + - name: Build with Rusttls + run: cargo build --verbose --features rusttls - name: Run tests run: cargo test --verbose From eff5b9d0393c8f8d4929a8280b671985aca6dc8d Mon Sep 17 00:00:00 2001 From: jayjamesjay Date: Fri, 9 Jul 2021 11:44:25 +0200 Subject: [PATCH 11/47] fix command in gh actions --- .github/workflows/rust.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 3f9b12a..2ca91c8 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -12,6 +12,6 @@ jobs: - name: Build run: cargo build --verbose - name: Build with Rusttls - run: cargo build --verbose --features rusttls + run: cargo build --verbose --no-default-features --features rust-tls - name: Run tests run: cargo test --verbose From 5a85a07086f1577c85e0b3de8a366b456a9baa40 Mon Sep 17 00:00:00 2001 From: jayjamesjay Date: Sat, 19 Nov 2022 21:20:26 +0100 Subject: [PATCH 12/47] change default headers & migrate to rust 2021 --- CONTRIBUTING.md | 3 +- Cargo.lock | 323 ++++++++++++++++++++++++------------------------ Cargo.toml | 10 +- LICENSE | 2 +- README.md | 4 +- src/request.rs | 4 - src/response.rs | 3 - 7 files changed, 170 insertions(+), 179 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 58b6320..30fe887 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,5 +1,4 @@ # Contributing Code and documentation PRs are always welcome. Just remember to list out introduced changes. -If you want to suggest some improvements, report a bug, discuss a functionality, etc., -feel free to open an issue. +If you want to suggest some improvements, report a bug, discuss a functionality, etc., feel free to open an issue. diff --git a/Cargo.lock b/Cargo.lock index 3756e71..5e51703 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,39 +4,27 @@ version = 3 [[package]] name = "autocfg" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" - -[[package]] -name = "base64" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "bitflags" -version = "1.2.1" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bumpalo" -version = "3.4.0" +version = "3.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820" +checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" [[package]] name = "cc" -version = "1.0.66" +version = "1.0.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48" - -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +checksum = "76a284da2e6fe2092f2353e51713435363112dfd60030e22add80be333fb928f" [[package]] name = "cfg-if" @@ -46,9 +34,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "core-foundation" -version = "0.9.1" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" dependencies = [ "core-foundation-sys", "libc", @@ -56,9 +44,18 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + +[[package]] +name = "fastrand" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +dependencies = [ + "instant", +] [[package]] name = "foreign-types" @@ -75,20 +72,9 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" -[[package]] -name = "getrandom" -version = "0.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6" -dependencies = [ - "cfg-if 0.1.10", - "libc", - "wasi", -] - [[package]] name = "http_req" -version = "0.8.1" +version = "0.9.0" dependencies = [ "native-tls", "rustls", @@ -97,11 +83,20 @@ dependencies = [ "webpki-roots", ] +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + [[package]] name = "js-sys" -version = "0.3.46" +version = "0.3.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf3d7383929f7c9c7c2d0fa596f325832df98c3704f2c60553080f7127a58175" +checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" dependencies = [ "wasm-bindgen", ] @@ -114,24 +109,24 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.81" +version = "0.2.137" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1482821306169ec4d07f6aca392a4681f66c75c9918aa49641a2595db64053cb" +checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" [[package]] name = "log" -version = "0.4.11" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ - "cfg-if 0.1.10", + "cfg-if", ] [[package]] name = "native-tls" -version = "0.2.6" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fcc7939b5edc4e4f86b1b4a04bb1498afaaf871b1a6691838ed06fcb48d3a3f" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" dependencies = [ "lazy_static", "libc", @@ -147,35 +142,47 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.5.2" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0" +checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" [[package]] name = "openssl" -version = "0.10.31" +version = "0.10.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d008f51b1acffa0d3450a68606e6a51c123012edaacb0f4e1426bd978869187" +checksum = "12fc0523e3bd51a692c8850d075d74dc062ccf251c0110668cbd921917118a13" dependencies = [ "bitflags", - "cfg-if 1.0.0", + "cfg-if", "foreign-types", - "lazy_static", "libc", + "once_cell", + "openssl-macros", "openssl-sys", ] +[[package]] +name = "openssl-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "openssl-probe" -version = "0.1.2" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.59" +version = "0.9.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de52d8eabd217311538a39bba130d7dea1f1e118010fee7a033d966845e7d5fe" +checksum = "b03b84c3b2d099b81f0953422b4d4ad58761589d0229b5506356afca05a3670a" dependencies = [ "autocfg", "cc", @@ -186,81 +193,37 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.19" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" - -[[package]] -name = "ppv-lite86" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" [[package]] name = "proc-macro2" -version = "1.0.24" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" dependencies = [ - "unicode-xid", + "unicode-ident", ] [[package]] name = "quote" -version = "1.0.7" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" dependencies = [ "proc-macro2", ] [[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom", - "libc", - "rand_chacha", - "rand_core", - "rand_hc", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" +name = "redox_syscall" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ - "rand_core", + "bitflags", ] -[[package]] -name = "redox_syscall" -version = "0.1.57" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" - [[package]] name = "remove_dir_all" version = "0.5.3" @@ -272,9 +235,9 @@ dependencies = [ [[package]] name = "ring" -version = "0.16.19" +version = "0.16.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "024a1e66fea74c66c66624ee5622a7ff0e4b73a13b4f5c326ddb50c708944226" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" dependencies = [ "cc", "libc", @@ -287,11 +250,10 @@ dependencies = [ [[package]] name = "rustls" -version = "0.19.0" +version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "064fd21ff87c6e87ed4506e68beb42459caa4a0e2eb144932e6776768556980b" +checksum = "539a2bfe908f471bfa933876bd1eb6a19cf2176d375f82ef7f99530a40e48c2c" dependencies = [ - "base64", "log", "ring", "sct", @@ -300,19 +262,19 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.19" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" +checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" dependencies = [ "lazy_static", - "winapi", + "windows-sys", ] [[package]] name = "sct" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3042af939fca8c3453b7af0f1c66e533a15a86169e39de2657310ade8f98d3c" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" dependencies = [ "ring", "untrusted", @@ -320,9 +282,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.0.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1759c2e3c8580017a484a7ac56d3abc5a6c1feadf88db2f3633f12ae4268c69" +checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c" dependencies = [ "bitflags", "core-foundation", @@ -333,9 +295,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.0.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f99b9d5e26d2a71633cc4f2ebae7cc9f874044e0c351a27e17892d76dce5678b" +checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" dependencies = [ "core-foundation-sys", "libc", @@ -349,24 +311,24 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "syn" -version = "1.0.54" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2af957a63d6bd42255c359c93d9bfdb97076bd3b820897ce55ffbfbf107f44" +checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d" dependencies = [ "proc-macro2", "quote", - "unicode-xid", + "unicode-ident", ] [[package]] name = "tempfile" -version = "3.1.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" dependencies = [ - "cfg-if 0.1.10", + "cfg-if", + "fastrand", "libc", - "rand", "redox_syscall", "remove_dir_all", "winapi", @@ -382,10 +344,10 @@ dependencies = [ ] [[package]] -name = "unicode-xid" -version = "0.2.1" +name = "unicode-ident" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" +checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" [[package]] name = "untrusted" @@ -395,41 +357,35 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "vcpkg" -version = "0.2.11" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b00bca6106a5e23f3eee943593759b7fcddb00554332e856d990c893966879fb" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "version_check" -version = "0.9.2" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" - -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "wasm-bindgen" -version = "0.2.69" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cd364751395ca0f68cafb17666eee36b63077fb5ecd972bbcd74c90c4bf736e" +checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.69" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1114f89ab1f4106e5b55e688b828c0ab0ea593a1ea7c094b141b14cbaaec2d62" +checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" dependencies = [ "bumpalo", - "lazy_static", "log", + "once_cell", "proc-macro2", "quote", "syn", @@ -438,9 +394,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.69" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6ac8995ead1f084a8dea1e65f194d0973800c7f571f6edd70adf06ecf77084" +checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -448,9 +404,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.69" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5a48c72f299d80557c7c62e37e7225369ecc0c963964059509fbafe917c7549" +checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" dependencies = [ "proc-macro2", "quote", @@ -461,15 +417,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.69" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e7811dd7f9398f14cc76efd356f98f03aa30419dea46aa810d71e819fc97158" +checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" [[package]] name = "web-sys" -version = "0.3.46" +version = "0.3.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "222b1ef9334f92a21d3fb53dc3fd80f30836959a90f9274a626d7e06315ba3c3" +checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" dependencies = [ "js-sys", "wasm-bindgen", @@ -477,9 +433,9 @@ dependencies = [ [[package]] name = "webpki" -version = "0.21.4" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" +checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" dependencies = [ "ring", "untrusted", @@ -487,9 +443,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.21.1" +version = "0.22.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940" +checksum = "368bfe657969fb01238bb756d351dcade285e0f6fcbd36dcb23359a5169975be" dependencies = [ "webpki", ] @@ -515,3 +471,46 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +dependencies = [ + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" + +[[package]] +name = "windows_i686_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" + +[[package]] +name = "windows_i686_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" diff --git a/Cargo.toml b/Cargo.toml index 4b61bf3..71481a0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "http_req" -version = "0.8.1" +version = "0.9.0" license = "MIT" description = "simple and lightweight HTTP client with built-in HTTPS support" repository = "https://github.com/jayjamesjay/http_req" @@ -8,7 +8,7 @@ authors = ["jayjamesjay"] readme = "README.md" categories = ["web-programming::http-client", "network-programming"] keywords = ["http", "client", "request"] -edition = "2018" +edition = "2021" [dependencies] unicase = "^2.6" @@ -22,13 +22,13 @@ version = "^0.2" optional = true [dependencies.rustls] -version = "^0.19" +version = "^0.20" optional = true [dependencies.webpki] -version = "^0.21" +version = "^0.22" optional = true [dependencies.webpki-roots] -version = "^0.21" +version = "^0.22" optional = true diff --git a/LICENSE b/LICENSE index a62e670..22cffd8 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2018-2021 jayjamesjay +Copyright (c) 2018-2022 jayjamesjay Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 8d88f3b..472b944 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # http_req [![Rust](https://github.com/jayjamesjay/http_req/actions/workflows/rust.yml/badge.svg)](https://github.com/jayjamesjay/http_req/actions/workflows/rust.yml) -[![Crates.io](https://img.shields.io/badge/crates.io-v0.8.1-orange.svg?longCache=true)](https://crates.io/crates/http_req) +[![Crates.io](https://img.shields.io/badge/crates.io-v0.9.0-orange.svg?longCache=true)](https://crates.io/crates/http_req) [![Docs.rs](https://docs.rs/http_req/badge.svg)](https://docs.rs/http_req/0.7.2/http_req/) Simple and lightweight HTTP client with built-in HTTPS support. @@ -27,7 +27,7 @@ fn main() { In order to use `http_req` with `rustls` in your project, add following lines to `Cargo.toml`: ```toml [dependencies] -http_req = {version="^0.8", default-features = false, features = ["rust-tls"]} +http_req = {version="^0.9", default-features = false, features = ["rust-tls"]} ``` ## License diff --git a/src/request.rs b/src/request.rs index 274a878..02f7cfe 100644 --- a/src/request.rs +++ b/src/request.rs @@ -6,7 +6,6 @@ use crate::{ uri::Uri, }; use std::{ - convert::TryFrom, fmt, io::{self, ErrorKind, Read, Write}, net::{TcpStream, ToSocketAddrs}, @@ -1103,7 +1102,6 @@ mod tests { let mut expect_headers = Headers::new(); expect_headers.insert("Host", "doc.rust-lang.org"); - expect_headers.insert("Referer", "http://doc.rust-lang.org/std/string/index.html"); expect_headers.insert(k, v); let req = req.header(k, v); @@ -1166,7 +1164,6 @@ mod tests { let req = RequestBuilder::new(&uri); const DEFAULT_MSG: &str = "GET /std/string/index.html HTTP/1.1\r\n\ - Referer: http://doc.rust-lang.org/std/string/index.html\r\n\ Host: doc.rust-lang.org\r\n\r\n"; let msg = req.parse_msg(); let msg = String::from_utf8_lossy(&msg).into_owned(); @@ -1219,7 +1216,6 @@ mod tests { let mut expect_headers = Headers::new(); expect_headers.insert("Host", "doc.rust-lang.org"); - expect_headers.insert("Referer", "http://doc.rust-lang.org/std/string/index.html"); expect_headers.insert("Connection", "Close"); expect_headers.insert(k, v); diff --git a/src/response.rs b/src/response.rs index 30bac49..d40c095 100644 --- a/src/response.rs +++ b/src/response.rs @@ -338,9 +338,7 @@ impl Headers { ///``` pub fn default_http(uri: &Uri) -> Headers { let mut headers = Headers::with_capacity(4); - headers.insert("Host", &uri.host_header().unwrap_or_default()); - headers.insert("Referer", &uri); headers } @@ -799,7 +797,6 @@ mod tests { let mut headers = Headers::with_capacity(4); headers.insert("Host", "doc.rust-lang.org"); - headers.insert("Referer", "http://doc.rust-lang.org/std/string/index.html"); assert_eq!(Headers::default_http(&uri), headers); } From 7fe6ac648c9a31d3bc21d0a0979082841db9b6e5 Mon Sep 17 00:00:00 2001 From: jayjamesjay Date: Sat, 19 Nov 2022 22:05:04 +0100 Subject: [PATCH 13/47] restore rustls 0.19 --- Cargo.lock | 23 +++++++++++++++-------- Cargo.toml | 6 +++--- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5e51703..dc04a0e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + [[package]] name = "bitflags" version = "1.3.2" @@ -250,10 +256,11 @@ dependencies = [ [[package]] name = "rustls" -version = "0.20.7" +version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "539a2bfe908f471bfa933876bd1eb6a19cf2176d375f82ef7f99530a40e48c2c" +checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" dependencies = [ + "base64", "log", "ring", "sct", @@ -272,9 +279,9 @@ dependencies = [ [[package]] name = "sct" -version = "0.7.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" dependencies = [ "ring", "untrusted", @@ -433,9 +440,9 @@ dependencies = [ [[package]] name = "webpki" -version = "0.22.0" +version = "0.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" dependencies = [ "ring", "untrusted", @@ -443,9 +450,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.22.5" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368bfe657969fb01238bb756d351dcade285e0f6fcbd36dcb23359a5169975be" +checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940" dependencies = [ "webpki", ] diff --git a/Cargo.toml b/Cargo.toml index 71481a0..3f7ff85 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,13 +22,13 @@ version = "^0.2" optional = true [dependencies.rustls] -version = "^0.20" +version = "^0.19" optional = true [dependencies.webpki] -version = "^0.22" +version = "^0.21" optional = true [dependencies.webpki-roots] -version = "^0.22" +version = "^0.21" optional = true From b8576a18d74aa0f756562f6bc46617825c1b3e5f Mon Sep 17 00:00:00 2001 From: jayjamesjay Date: Sat, 25 Mar 2023 09:11:45 +0100 Subject: [PATCH 14/47] security update --- Cargo.lock | 225 ++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 155 insertions(+), 70 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dc04a0e..ed87a16 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -22,15 +22,15 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bumpalo" -version = "3.11.1" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" [[package]] name = "cc" -version = "1.0.76" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76a284da2e6fe2092f2353e51713435363112dfd60030e22add80be333fb928f" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" [[package]] name = "cfg-if" @@ -54,11 +54,32 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +[[package]] +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "fastrand" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" dependencies = [ "instant", ] @@ -78,6 +99,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + [[package]] name = "http_req" version = "0.9.0" @@ -98,11 +125,22 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "io-lifetimes" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09270fd4fa1111bc614ed2246c7ef56239a3063d5be0d1ec3b589c505d400aeb" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.45.0", +] + [[package]] name = "js-sys" -version = "0.3.60" +version = "0.3.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" dependencies = [ "wasm-bindgen", ] @@ -115,9 +153,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.137" +version = "0.2.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" + +[[package]] +name = "linux-raw-sys" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" [[package]] name = "log" @@ -148,15 +192,15 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.16.0" +version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] name = "openssl" -version = "0.10.42" +version = "0.10.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12fc0523e3bd51a692c8850d075d74dc062ccf251c0110668cbd921917118a13" +checksum = "518915b97df115dd36109bfa429a48b8f737bd05508cf9588977b599648926d2" dependencies = [ "bitflags", "cfg-if", @@ -186,9 +230,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.77" +version = "0.9.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b03b84c3b2d099b81f0953422b4d4ad58761589d0229b5506356afca05a3670a" +checksum = "666416d899cf077260dac8698d60a60b435a46d57e82acb1be3d0dad87284e5b" dependencies = [ "autocfg", "cc", @@ -205,18 +249,18 @@ checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" [[package]] name = "proc-macro2" -version = "1.0.47" +version = "1.0.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" +checksum = "ba466839c78239c09faf015484e5cc04860f88242cff4d03eb038f04b4699b73" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.21" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" dependencies = [ "proc-macro2", ] @@ -230,15 +274,6 @@ dependencies = [ "bitflags", ] -[[package]] -name = "remove_dir_all" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi", -] - [[package]] name = "ring" version = "0.16.20" @@ -254,6 +289,20 @@ dependencies = [ "winapi", ] +[[package]] +name = "rustix" +version = "0.36.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db4165c9963ab29e422d6c26fbc1d37f15bace6b2810221f9d925023480fcf0e" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.45.0", +] + [[package]] name = "rustls" version = "0.19.1" @@ -269,12 +318,11 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.20" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" +checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" dependencies = [ - "lazy_static", - "windows-sys", + "windows-sys 0.42.0", ] [[package]] @@ -289,9 +337,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.7.0" +version = "2.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c" +checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" dependencies = [ "bitflags", "core-foundation", @@ -302,9 +350,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.6.1" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" +checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" dependencies = [ "core-foundation-sys", "libc", @@ -318,9 +366,9 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "syn" -version = "1.0.103" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", @@ -329,16 +377,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95" dependencies = [ "cfg-if", "fastrand", - "libc", "redox_syscall", - "remove_dir_all", - "winapi", + "rustix", + "windows-sys 0.42.0", ] [[package]] @@ -352,9 +399,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.5" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" [[package]] name = "untrusted" @@ -376,9 +423,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "wasm-bindgen" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -386,9 +433,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" dependencies = [ "bumpalo", "log", @@ -401,9 +448,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -411,9 +458,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ "proc-macro2", "quote", @@ -424,15 +471,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" [[package]] name = "web-sys" -version = "0.3.60" +version = "0.3.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" +checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" dependencies = [ "js-sys", "wasm-bindgen", @@ -481,43 +528,81 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" -version = "0.36.1" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ + "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_msvc", "windows_x86_64_gnu", + "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + [[package]] name = "windows_aarch64_msvc" -version = "0.36.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" [[package]] name = "windows_i686_gnu" -version = "0.36.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" [[package]] name = "windows_i686_msvc" -version = "0.36.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] name = "windows_x86_64_gnu" -version = "0.36.1" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" [[package]] name = "windows_x86_64_msvc" -version = "0.36.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" From a4c6b9e06a3c52f3199e38741f65d3905741afac Mon Sep 17 00:00:00 2001 From: jayjamesjay Date: Sat, 25 Mar 2023 09:24:25 +0100 Subject: [PATCH 15/47] update docs --- Cargo.toml | 2 +- LICENSE | 2 +- README.md | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3f7ff85..a2957a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "http_req" -version = "0.9.0" +version = "0.9.1" license = "MIT" description = "simple and lightweight HTTP client with built-in HTTPS support" repository = "https://github.com/jayjamesjay/http_req" diff --git a/LICENSE b/LICENSE index 22cffd8..c972bbd 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2018-2022 jayjamesjay +Copyright (c) 2018-2023 jayjamesjay Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 472b944..ef8d2a2 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # http_req [![Rust](https://github.com/jayjamesjay/http_req/actions/workflows/rust.yml/badge.svg)](https://github.com/jayjamesjay/http_req/actions/workflows/rust.yml) -[![Crates.io](https://img.shields.io/badge/crates.io-v0.9.0-orange.svg?longCache=true)](https://crates.io/crates/http_req) -[![Docs.rs](https://docs.rs/http_req/badge.svg)](https://docs.rs/http_req/0.7.2/http_req/) +[![Crates.io](https://img.shields.io/badge/crates.io-v0.9.1-orange.svg?longCache=true)](https://crates.io/crates/http_req) +[![Docs.rs](https://docs.rs/http_req/badge.svg)](https://docs.rs/http_req/0.9.1/http_req/) Simple and lightweight HTTP client with built-in HTTPS support. From 9783b62152616f7052e761464bc5354ce135cb8c Mon Sep 17 00:00:00 2001 From: jayjamesjay Date: Fri, 7 Jul 2023 20:40:01 +0200 Subject: [PATCH 16/47] update dependencies --- Cargo.lock | 188 ++++++++++++++++++++++++----------------------------- Cargo.toml | 2 +- README.md | 4 +- 3 files changed, 88 insertions(+), 106 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ed87a16..15cbef4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -22,9 +22,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bumpalo" -version = "3.12.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" [[package]] name = "cc" @@ -50,19 +50,19 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "errno" -version = "0.2.8" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" dependencies = [ "errno-dragonfly", "libc", - "winapi", + "windows-sys", ] [[package]] @@ -101,13 +101,13 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "hermit-abi" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" [[package]] name = "http_req" -version = "0.9.0" +version = "0.9.1" dependencies = [ "native-tls", "rustls", @@ -127,20 +127,20 @@ dependencies = [ [[package]] name = "io-lifetimes" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09270fd4fa1111bc614ed2246c7ef56239a3063d5be0d1ec3b589c505d400aeb" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.45.0", + "windows-sys", ] [[package]] name = "js-sys" -version = "0.3.61" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" dependencies = [ "wasm-bindgen", ] @@ -153,24 +153,21 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.140" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "linux-raw-sys" -version = "0.1.4" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "log" -version = "0.4.17" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" [[package]] name = "native-tls" @@ -192,15 +189,15 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.17.1" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "openssl" -version = "0.10.48" +version = "0.10.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "518915b97df115dd36109bfa429a48b8f737bd05508cf9588977b599648926d2" +checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d" dependencies = [ "bitflags", "cfg-if", @@ -213,9 +210,9 @@ dependencies = [ [[package]] name = "openssl-macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", @@ -230,11 +227,10 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.83" +version = "0.9.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "666416d899cf077260dac8698d60a60b435a46d57e82acb1be3d0dad87284e5b" +checksum = "374533b0e45f3a7ced10fcaeccca020e66656bc03dac384f852e4e5a7a8104a6" dependencies = [ - "autocfg", "cc", "libc", "pkg-config", @@ -243,33 +239,33 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "proc-macro2" -version = "1.0.53" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba466839c78239c09faf015484e5cc04860f88242cff4d03eb038f04b4699b73" +checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.26" +version = "1.0.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" dependencies = [ "proc-macro2", ] [[package]] name = "redox_syscall" -version = "0.2.16" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ "bitflags", ] @@ -291,16 +287,16 @@ dependencies = [ [[package]] name = "rustix" -version = "0.36.11" +version = "0.37.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db4165c9963ab29e422d6c26fbc1d37f15bace6b2810221f9d925023480fcf0e" +checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06" dependencies = [ "bitflags", "errno", "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys 0.45.0", + "windows-sys", ] [[package]] @@ -318,11 +314,11 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" dependencies = [ - "windows-sys 0.42.0", + "windows-sys", ] [[package]] @@ -337,9 +333,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.8.2" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" +checksum = "1fc758eb7bffce5b308734e9b0c1468893cae9ff70ebf13e7090be8dcbcc83a8" dependencies = [ "bitflags", "core-foundation", @@ -350,9 +346,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" +checksum = "f51d0c0d83bec45f16480d0ce0058397a69e48fcdc52d1dc8855fb68acbd31a7" dependencies = [ "core-foundation-sys", "libc", @@ -366,9 +362,9 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "syn" -version = "1.0.109" +version = "2.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +checksum = "59fb7d6d8281a51045d62b8eb3a7d1ce347b76f312af50cd3dc0af39c87c1737" dependencies = [ "proc-macro2", "quote", @@ -377,15 +373,16 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.4.0" +version = "3.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95" +checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6" dependencies = [ + "autocfg", "cfg-if", "fastrand", "redox_syscall", "rustix", - "windows-sys 0.42.0", + "windows-sys", ] [[package]] @@ -399,9 +396,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.8" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" [[package]] name = "untrusted" @@ -423,9 +420,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "wasm-bindgen" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -433,9 +430,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" dependencies = [ "bumpalo", "log", @@ -448,9 +445,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -458,9 +455,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", @@ -471,15 +468,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "web-sys" -version = "0.3.61" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" dependencies = [ "js-sys", "wasm-bindgen", @@ -528,33 +525,18 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows-sys" -version = "0.45.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" -version = "0.42.2" +version = "0.48.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", @@ -567,42 +549,42 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.2" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" [[package]] name = "windows_aarch64_msvc" -version = "0.42.2" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" [[package]] name = "windows_i686_gnu" -version = "0.42.2" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" [[package]] name = "windows_i686_msvc" -version = "0.42.2" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" [[package]] name = "windows_x86_64_gnu" -version = "0.42.2" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.2" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" [[package]] name = "windows_x86_64_msvc" -version = "0.42.2" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" diff --git a/Cargo.toml b/Cargo.toml index a2957a3..592f2d2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "http_req" -version = "0.9.1" +version = "0.9.2" license = "MIT" description = "simple and lightweight HTTP client with built-in HTTPS support" repository = "https://github.com/jayjamesjay/http_req" diff --git a/README.md b/README.md index ef8d2a2..bc3e2d4 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # http_req [![Rust](https://github.com/jayjamesjay/http_req/actions/workflows/rust.yml/badge.svg)](https://github.com/jayjamesjay/http_req/actions/workflows/rust.yml) -[![Crates.io](https://img.shields.io/badge/crates.io-v0.9.1-orange.svg?longCache=true)](https://crates.io/crates/http_req) -[![Docs.rs](https://docs.rs/http_req/badge.svg)](https://docs.rs/http_req/0.9.1/http_req/) +[![Crates.io](https://img.shields.io/badge/crates.io-v0.9.2-orange.svg?longCache=true)](https://crates.io/crates/http_req) +[![Docs.rs](https://docs.rs/http_req/badge.svg)](https://docs.rs/http_req/0.9.2/http_req/) Simple and lightweight HTTP client with built-in HTTPS support. From 1c8cba149b4684993d0f35c11b94073f14bf66b1 Mon Sep 17 00:00:00 2001 From: jayjamesjay Date: Sun, 17 Sep 2023 20:55:15 +0200 Subject: [PATCH 17/47] update deps --- Cargo.lock | 160 ++++++++++++++++++++++------------------------------- Cargo.toml | 2 +- README.md | 4 +- 3 files changed, 69 insertions(+), 97 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 15cbef4..7508fdc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,12 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - [[package]] name = "base64" version = "0.13.1" @@ -20,17 +14,26 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" + [[package]] name = "bumpalo" -version = "3.13.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] name = "cc" -version = "1.0.79" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] [[package]] name = "cfg-if" @@ -56,9 +59,9 @@ checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "errno" -version = "0.3.1" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd" dependencies = [ "errno-dragonfly", "libc", @@ -77,12 +80,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "1.9.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] +checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" [[package]] name = "foreign-types" @@ -99,15 +99,9 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" -[[package]] -name = "hermit-abi" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" - [[package]] name = "http_req" -version = "0.9.1" +version = "0.9.3" dependencies = [ "native-tls", "rustls", @@ -116,26 +110,6 @@ dependencies = [ "webpki-roots", ] -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "io-lifetimes" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" -dependencies = [ - "hermit-abi", - "libc", - "windows-sys", -] - [[package]] name = "js-sys" version = "0.3.64" @@ -153,21 +127,21 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.147" +version = "0.2.148" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" [[package]] name = "linux-raw-sys" -version = "0.3.8" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" +checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128" [[package]] name = "log" -version = "0.4.19" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "native-tls" @@ -195,11 +169,11 @@ checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "openssl" -version = "0.10.55" +version = "0.10.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d" +checksum = "bac25ee399abb46215765b1cb35bc0212377e58a061560d8b29b024fd0430e7c" dependencies = [ - "bitflags", + "bitflags 2.4.0", "cfg-if", "foreign-types", "libc", @@ -227,9 +201,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.90" +version = "0.9.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "374533b0e45f3a7ced10fcaeccca020e66656bc03dac384f852e4e5a7a8104a6" +checksum = "db4d56a4c0478783083cfafcc42493dd4a981d41669da64b4572a2a089b51b1d" dependencies = [ "cc", "libc", @@ -245,18 +219,18 @@ checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "proc-macro2" -version = "1.0.63" +version = "1.0.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb" +checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.29" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] @@ -267,7 +241,7 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -287,13 +261,12 @@ dependencies = [ [[package]] name = "rustix" -version = "0.37.23" +version = "0.38.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06" +checksum = "d7db8590df6dfcd144d22afd1b83b36c21a18d7cbc1dc4bb5295a8712e9eb662" dependencies = [ - "bitflags", + "bitflags 2.4.0", "errno", - "io-lifetimes", "libc", "linux-raw-sys", "windows-sys", @@ -333,11 +306,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.9.1" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc758eb7bffce5b308734e9b0c1468893cae9ff70ebf13e7090be8dcbcc83a8" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "core-foundation-sys", "libc", @@ -346,9 +319,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.9.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f51d0c0d83bec45f16480d0ce0058397a69e48fcdc52d1dc8855fb68acbd31a7" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" dependencies = [ "core-foundation-sys", "libc", @@ -362,9 +335,9 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "syn" -version = "2.0.23" +version = "2.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59fb7d6d8281a51045d62b8eb3a7d1ce347b76f312af50cd3dc0af39c87c1737" +checksum = "91e02e55d62894af2a08aca894c6577281f76769ba47c94d5756bec8ac6e7373" dependencies = [ "proc-macro2", "quote", @@ -373,11 +346,10 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.6.0" +version = "3.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6" +checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" dependencies = [ - "autocfg", "cfg-if", "fastrand", "redox_syscall", @@ -387,18 +359,18 @@ dependencies = [ [[package]] name = "unicase" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" dependencies = [ "version_check", ] [[package]] name = "unicode-ident" -version = "1.0.10" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "untrusted" @@ -534,9 +506,9 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.48.1" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", @@ -549,42 +521,42 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_i686_gnu" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_x86_64_gnu" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" diff --git a/Cargo.toml b/Cargo.toml index 592f2d2..f2ad32a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "http_req" -version = "0.9.2" +version = "0.9.3" license = "MIT" description = "simple and lightweight HTTP client with built-in HTTPS support" repository = "https://github.com/jayjamesjay/http_req" diff --git a/README.md b/README.md index bc3e2d4..52f01b7 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # http_req [![Rust](https://github.com/jayjamesjay/http_req/actions/workflows/rust.yml/badge.svg)](https://github.com/jayjamesjay/http_req/actions/workflows/rust.yml) -[![Crates.io](https://img.shields.io/badge/crates.io-v0.9.2-orange.svg?longCache=true)](https://crates.io/crates/http_req) -[![Docs.rs](https://docs.rs/http_req/badge.svg)](https://docs.rs/http_req/0.9.2/http_req/) +[![Crates.io](https://img.shields.io/badge/crates.io-v0.9.3-orange.svg?longCache=true)](https://crates.io/crates/http_req) +[![Docs.rs](https://docs.rs/http_req/badge.svg)](https://docs.rs/http_req/0.9.3/http_req/) Simple and lightweight HTTP client with built-in HTTPS support. From 40d7814cc4191f3df25c5262afe1395ca173148c Mon Sep 17 00:00:00 2001 From: Damien Voreiter Date: Thu, 21 Sep 2023 16:28:10 +0200 Subject: [PATCH 18/47] Update rustls 0.21.7 and adapt code --- Cargo.lock | 54 ++++++++++++++++++++++++++++++++++------------------ Cargo.toml | 7 ++++--- src/error.rs | 9 +++++++++ src/tls.rs | 38 ++++++++++++++++++------------------ 4 files changed, 67 insertions(+), 41 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7508fdc..b748aff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "base64" -version = "0.13.1" +version = "0.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" [[package]] name = "bitflags" @@ -105,6 +105,7 @@ version = "0.9.3" dependencies = [ "native-tls", "rustls", + "rustls-pemfile", "unicase", "webpki", "webpki-roots", @@ -261,9 +262,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.13" +version = "0.38.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7db8590df6dfcd144d22afd1b83b36c21a18d7cbc1dc4bb5295a8712e9eb662" +checksum = "747c788e9ce8e92b12cd485c49ddf90723550b654b32508f979b71a7b1ecda4f" dependencies = [ "bitflags 2.4.0", "errno", @@ -274,15 +275,33 @@ dependencies = [ [[package]] name = "rustls" -version = "0.19.1" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" +checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8" dependencies = [ - "base64", "log", "ring", + "rustls-webpki", "sct", - "webpki", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" +dependencies = [ + "base64", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45a27e3b59326c16e23d30aeb7a36a24cc0d29e71d68ff611cdfb4a01d013bed" +dependencies = [ + "ring", + "untrusted", ] [[package]] @@ -296,9 +315,9 @@ dependencies = [ [[package]] name = "sct" -version = "0.6.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" dependencies = [ "ring", "untrusted", @@ -335,9 +354,9 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "syn" -version = "2.0.36" +version = "2.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e02e55d62894af2a08aca894c6577281f76769ba47c94d5756bec8ac6e7373" +checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8" dependencies = [ "proc-macro2", "quote", @@ -456,9 +475,9 @@ dependencies = [ [[package]] name = "webpki" -version = "0.21.4" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" +checksum = "f0e74f82d49d545ad128049b7e88f6576df2da6b02e9ce565c6f533be576957e" dependencies = [ "ring", "untrusted", @@ -466,12 +485,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.21.1" +version = "0.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940" -dependencies = [ - "webpki", -] +checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc" [[package]] name = "winapi" diff --git a/Cargo.toml b/Cargo.toml index f2ad32a..e2a089d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ keywords = ["http", "client", "request"] edition = "2021" [dependencies] +rustls-pemfile = "^1" unicase = "^2.6" [features] @@ -22,13 +23,13 @@ version = "^0.2" optional = true [dependencies.rustls] -version = "^0.19" +version = "^0.21" optional = true [dependencies.webpki] -version = "^0.21" +version = "^0" optional = true [dependencies.webpki-roots] -version = "^0.21" +version = "^0" optional = true diff --git a/src/error.rs b/src/error.rs index 55bf752..8bb27b0 100644 --- a/src/error.rs +++ b/src/error.rs @@ -5,6 +5,7 @@ use std::{error, fmt, io, num, str}; pub enum ParseErr { Utf8(str::Utf8Error), Int(num::ParseIntError), + Rustls(rustls::Error), StatusErr, HeadersErr, UriErr, @@ -19,6 +20,7 @@ impl error::Error for ParseErr { match self { Utf8(e) => Some(e), Int(e) => Some(e), + Rustls(e) => Some(e), StatusErr | HeadersErr | UriErr | Invalid | Empty => None, } } @@ -31,6 +33,7 @@ impl fmt::Display for ParseErr { let err = match self { Utf8(_) => "invalid character", Int(_) => "cannot parse number", + Rustls(_) => "rustls error", Invalid => "invalid value", Empty => "nothing to parse", StatusErr => "status line contains invalid values", @@ -41,6 +44,12 @@ impl fmt::Display for ParseErr { } } +impl From for ParseErr { + fn from(e: rustls::Error) -> Self { + ParseErr::Rustls(e) + } +} + impl From for ParseErr { fn from(e: num::ParseIntError) -> Self { ParseErr::Int(e) diff --git a/src/tls.rs b/src/tls.rs index ebb5b91..00d81d6 100644 --- a/src/tls.rs +++ b/src/tls.rs @@ -23,7 +23,7 @@ pub struct Conn { stream: native_tls::TlsStream, #[cfg(feature = "rust-tls")] - stream: rustls::StreamOwned, + stream: rustls::StreamOwned, } impl io::Read for Conn { @@ -63,7 +63,7 @@ pub struct Config { #[cfg(feature = "native-tls")] extra_root_certs: Vec, #[cfg(feature = "rust-tls")] - client_config: std::sync::Arc, + root_certs: std::sync::Arc, } impl Default for Config { @@ -76,13 +76,16 @@ impl Default for Config { #[cfg(feature = "rust-tls")] fn default() -> Self { - let mut config = rustls::ClientConfig::new(); - config - .root_store - .add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS); - + let mut root_store = rustls::RootCertStore::empty(); + root_store.add_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.iter().map(|ta| { + rustls::OwnedTrustAnchor::from_subject_spki_name_constraints( + ta.subject, + ta.spki, + ta.name_constraints, + ) + })); Config { - client_config: std::sync::Arc::new(config), + root_certs: std::sync::Arc::new(root_store), } } } @@ -127,11 +130,8 @@ impl Config { pub fn add_root_cert_file_pem(&mut self, file_path: &Path) -> Result<&mut Self, HttpError> { let f = File::open(file_path)?; let mut f = BufReader::new(f); - let config = std::sync::Arc::make_mut(&mut self.client_config); - let _ = config - .root_store - .add_pem_file(&mut f) - .map_err(|_| HttpError::from(ParseErr::Invalid))?; + let root_certs = std::sync::Arc::make_mut(&mut self.root_certs); + root_certs.add_parsable_certificates(&rustls_pemfile::certs(&mut f)?); Ok(self) } @@ -141,13 +141,13 @@ impl Config { H: AsRef, S: io::Read + io::Write, { - use rustls::{ClientSession, StreamOwned}; + use rustls::{ClientConnection, StreamOwned}; - let session = ClientSession::new( - &self.client_config, - webpki::DNSNameRef::try_from_ascii_str(hostname.as_ref()) - .map_err(|_| HttpError::Tls)?, - ); + let client_config = rustls::ClientConfig::builder().with_safe_defaults().with_root_certificates(self.root_certs.clone()).with_no_client_auth(); + let session = ClientConnection::new( + std::sync::Arc::new(client_config), + hostname.as_ref().try_into().map_err(|_| HttpError::Tls)?, + ).map_err(|e| ParseErr::Rustls(e))?; let stream = StreamOwned::new(session, stream); Ok(Conn { stream }) From 626fe4b3e87608338e0218d3d5cf5e4fce295a8e Mon Sep 17 00:00:00 2001 From: jayjamesjay Date: Thu, 21 Sep 2023 22:10:44 +0200 Subject: [PATCH 19/47] add config flags --- Cargo.toml | 4 ++-- src/error.rs | 8 ++++++-- src/tls.rs | 8 ++++++-- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e2a089d..f4f71f8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,9 +27,9 @@ version = "^0.21" optional = true [dependencies.webpki] -version = "^0" +version = "^0.22" optional = true [dependencies.webpki-roots] -version = "^0" +version = "^0.25" optional = true diff --git a/src/error.rs b/src/error.rs index 8bb27b0..477d4bd 100644 --- a/src/error.rs +++ b/src/error.rs @@ -5,12 +5,13 @@ use std::{error, fmt, io, num, str}; pub enum ParseErr { Utf8(str::Utf8Error), Int(num::ParseIntError), - Rustls(rustls::Error), StatusErr, HeadersErr, UriErr, Invalid, Empty, + #[cfg(feature = "rust-tls")] + Rustls(rustls::Error), } impl error::Error for ParseErr { @@ -20,6 +21,7 @@ impl error::Error for ParseErr { match self { Utf8(e) => Some(e), Int(e) => Some(e), + #[cfg(feature = "rust-tls")] Rustls(e) => Some(e), StatusErr | HeadersErr | UriErr | Invalid | Empty => None, } @@ -33,17 +35,19 @@ impl fmt::Display for ParseErr { let err = match self { Utf8(_) => "invalid character", Int(_) => "cannot parse number", - Rustls(_) => "rustls error", Invalid => "invalid value", Empty => "nothing to parse", StatusErr => "status line contains invalid values", HeadersErr => "headers contain invalid values", UriErr => "uri contains invalid characters", + #[cfg(feature = "rust-tls")] + Rustls(_) => "rustls error", }; write!(f, "ParseErr: {}", err) } } +#[cfg(feature = "rust-tls")] impl From for ParseErr { fn from(e: rustls::Error) -> Self { ParseErr::Rustls(e) diff --git a/src/tls.rs b/src/tls.rs index 00d81d6..5d6a713 100644 --- a/src/tls.rs +++ b/src/tls.rs @@ -143,11 +143,15 @@ impl Config { { use rustls::{ClientConnection, StreamOwned}; - let client_config = rustls::ClientConfig::builder().with_safe_defaults().with_root_certificates(self.root_certs.clone()).with_no_client_auth(); + let client_config = rustls::ClientConfig::builder() + .with_safe_defaults() + .with_root_certificates(self.root_certs.clone()) + .with_no_client_auth(); let session = ClientConnection::new( std::sync::Arc::new(client_config), hostname.as_ref().try_into().map_err(|_| HttpError::Tls)?, - ).map_err(|e| ParseErr::Rustls(e))?; + ) + .map_err(|e| ParseErr::Rustls(e))?; let stream = StreamOwned::new(session, stream); Ok(Conn { stream }) From 9b7c55209236fa3ea06d629e25ca32cf805b931b Mon Sep 17 00:00:00 2001 From: jayjamesjay Date: Fri, 22 Sep 2023 08:19:31 +0200 Subject: [PATCH 20/47] set rustls-pemfile as optional dependency --- Cargo.toml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f4f71f8..6810584 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,12 +11,11 @@ keywords = ["http", "client", "request"] edition = "2021" [dependencies] -rustls-pemfile = "^1" unicase = "^2.6" [features] default = ["native-tls"] -rust-tls = ["rustls", "webpki", "webpki-roots"] +rust-tls = ["rustls", "webpki", "webpki-roots", "rustls-pemfile"] [dependencies.native-tls] version = "^0.2" @@ -26,6 +25,10 @@ optional = true version = "^0.21" optional = true +[dependencies.rustls-pemfile] +version = "^1.0" +optional = true + [dependencies.webpki] version = "^0.22" optional = true From 7a1a4099890f2d31542d7fbfcd659b2feccdd2b3 Mon Sep 17 00:00:00 2001 From: jayjamesjay Date: Sat, 23 Sep 2023 12:09:37 +0200 Subject: [PATCH 21/47] improve examples --- examples/get.rs | 12 ++++++++---- examples/head.rs | 4 +++- examples/post.rs | 16 +++++++++++----- examples/request_builder_get.rs | 13 +++++++------ 4 files changed, 29 insertions(+), 16 deletions(-) diff --git a/examples/get.rs b/examples/get.rs index 1fbf882..a14c8a7 100644 --- a/examples/get.rs +++ b/examples/get.rs @@ -1,10 +1,14 @@ use http_req::request; fn main() { - let mut writer = Vec::new(); //container for body of a response - let res = request::get("https://www.rust-lang.org/learn", &mut writer).unwrap(); + //Container for body of a response. + let mut body = Vec::new(); + //Sends a HTTP GET request and processes the response. Saves body of the response to `body` variable. + let res = request::get("https://www.rust-lang.org/learn", &mut body).unwrap(); + + //Prints details about the response. println!("Status: {} {}", res.status_code(), res.reason()); - println!("Headers {}", res.headers()); - //println!("{}", String::from_utf8_lossy(&writer)); + println!("Headers: {}", res.headers()); + //println!("{}", String::from_utf8_lossy(&body)); } diff --git a/examples/head.rs b/examples/head.rs index eb2f816..922e278 100644 --- a/examples/head.rs +++ b/examples/head.rs @@ -1,8 +1,10 @@ use http_req::request; fn main() { + //Sends a HTTP HEAD request and processes the response. let res = request::head("https://www.rust-lang.org/learn").unwrap(); + //Prints details about the response. println!("Status: {} {}", res.status_code(), res.reason()); - println!("{:?}", res.headers()); + println!("Headers: {}", res.headers()); } diff --git a/examples/post.rs b/examples/post.rs index 982fd32..fadfb9d 100644 --- a/examples/post.rs +++ b/examples/post.rs @@ -1,11 +1,17 @@ use http_req::request; fn main() { - let mut writer = Vec::new(); //container for body of a response - const BODY: &[u8; 27] = b"field1=value1&field2=value2"; - let res = request::post("https://httpbin.org/post", BODY, &mut writer).unwrap(); + //Container for body of a response. + let mut res_body = Vec::new(); + //Body of a request. + const REQ_BODY: &[u8; 27] = b"field1=value1&field2=value2"; + + //Sends a HTTP POST request and processes the response. + let res = request::post("https://httpbin.org/post", REQ_BODY, &mut res_body).unwrap(); + + //Prints details about the response. println!("Status: {} {}", res.status_code(), res.reason()); - println!("Headers {}", res.headers()); - //println!("{}", String::from_utf8_lossy(&writer)); + println!("Headers: {}", res.headers()); + println!("{}", String::from_utf8_lossy(&res_body)); } diff --git a/examples/request_builder_get.rs b/examples/request_builder_get.rs index 52457d7..7c237d5 100644 --- a/examples/request_builder_get.rs +++ b/examples/request_builder_get.rs @@ -2,26 +2,27 @@ use http_req::{request::RequestBuilder, tls, uri::Uri}; use std::{convert::TryFrom, net::TcpStream}; fn main() { - //Parse uri and assign it to variable `addr` - let addr: Uri = Uri::try_from("https://doc.rust-lang.org/").unwrap(); + //Parses a URI and assigns it to a variable `addr`. + let addr: Uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); - //Connect to remote host + //Connects to a remote host. Uses information from `addr`. let stream = TcpStream::connect((addr.host().unwrap(), addr.corr_port())).unwrap(); - //Open secure connection over TlsStream, because of `addr` (https) + //Opens a secure connection over TlsStream. This is required due to use of `https` protocol. let mut stream = tls::Config::default() .connect(addr.host().unwrap_or(""), stream) .unwrap(); - //Container for response's body + //Container for a response's body. let mut writer = Vec::new(); - //Add header `Connection: Close` + //Adds a header `Connection: Close`. let response = RequestBuilder::new(&addr) .header("Connection", "Close") .send(&mut stream, &mut writer) .unwrap(); println!("Status: {} {}", response.status_code(), response.reason()); + println!("Headers: {}", response.headers()); //println!("{}", String::from_utf8_lossy(&writer)); } From 84c7030456a688bf75da87ed4bd5591cdba48699 Mon Sep 17 00:00:00 2001 From: jayjamesjay Date: Sat, 23 Sep 2023 12:11:52 +0200 Subject: [PATCH 22/47] update readme --- Cargo.lock | 2 +- Cargo.toml | 4 ++-- README.md | 16 +++++++++------- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b748aff..4038d1a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -101,7 +101,7 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "http_req" -version = "0.9.3" +version = "0.10.0" dependencies = [ "native-tls", "rustls", diff --git a/Cargo.toml b/Cargo.toml index 6810584..adb0af1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "http_req" -version = "0.9.3" +version = "0.10.0" license = "MIT" description = "simple and lightweight HTTP client with built-in HTTPS support" repository = "https://github.com/jayjamesjay/http_req" @@ -11,7 +11,7 @@ keywords = ["http", "client", "request"] edition = "2021" [dependencies] -unicase = "^2.6" +unicase = "^2.7" [features] default = ["native-tls"] diff --git a/README.md b/README.md index 52f01b7..00a6163 100644 --- a/README.md +++ b/README.md @@ -1,33 +1,35 @@ # http_req [![Rust](https://github.com/jayjamesjay/http_req/actions/workflows/rust.yml/badge.svg)](https://github.com/jayjamesjay/http_req/actions/workflows/rust.yml) -[![Crates.io](https://img.shields.io/badge/crates.io-v0.9.3-orange.svg?longCache=true)](https://crates.io/crates/http_req) -[![Docs.rs](https://docs.rs/http_req/badge.svg)](https://docs.rs/http_req/0.9.3/http_req/) +[![Crates.io](https://img.shields.io/badge/crates.io-v0.10.0-orange.svg?longCache=true)](https://crates.io/crates/http_req) +[![Docs.rs](https://docs.rs/http_req/badge.svg)](https://docs.rs/http_req/0.10.0/http_req/) Simple and lightweight HTTP client with built-in HTTPS support. ## Requirements http_req by default uses [rust-native-tls](https://github.com/sfackler/rust-native-tls), -which uses TLS framework provided by OS on Windows and macOS, and OpenSSL +which relies on TLS framework provided by OS on Windows and macOS, and OpenSSL on all other platforms. But it also supports [rus-tls](https://crates.io/crates/rustls). ## Example -Basic GET request +Basic HTTP GET request ```rust use http_req::request; fn main() { - let mut writer = Vec::new(); //container for body of a response - let res = request::get("https://doc.rust-lang.org/", &mut writer).unwrap(); + let mut body = Vec::new(); //Container for body of a response. + let res = request::get("https://doc.rust-lang.org/", &mut body).unwrap(); println!("Status: {} {}", res.status_code(), res.reason()); } ``` +Take a look at [more examples](https://github.com/jayjamesjay/http_req/tree/master/examples) + ## How to use with `rustls`: In order to use `http_req` with `rustls` in your project, add following lines to `Cargo.toml`: ```toml [dependencies] -http_req = {version="^0.9", default-features = false, features = ["rust-tls"]} +http_req = {version="^0.10", default-features = false, features = ["rust-tls"]} ``` ## License From 07772307ab82243c7315078432418ffe76337ec8 Mon Sep 17 00:00:00 2001 From: jayjamesjay Date: Sun, 24 Sep 2023 21:47:55 +0200 Subject: [PATCH 23/47] update Uri parser --- src/uri.rs | 182 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 112 insertions(+), 70 deletions(-) diff --git a/src/uri.rs b/src/uri.rs index 378a602..b7d549f 100644 --- a/src/uri.rs +++ b/src/uri.rs @@ -229,16 +229,10 @@ impl<'a> Uri<'a> { ///assert_eq!(uri.resource(), "/bar/baz?query#fragment"); ///``` pub fn resource(&self) -> &str { - let mut result = "/"; - - for v in &[self.path, self.query, self.fragment] { - if let Some(r) = v { - result = &self.inner[r.start..]; - break; - } + match self.path { + Some(p) => &self.inner[p.start..], + None => "/", } - - result } } @@ -265,30 +259,38 @@ impl<'a> TryFrom<&'a str> for Uri<'a> { fn try_from(s: &'a str) -> Result { let (scheme, mut uri_part) = get_chunks(&s, Some(RangeC::new(0, s.len())), ":"); let scheme = scheme.ok_or(ParseErr::UriErr)?; + let (mut authority, mut query, mut fragment) = (None, None, None); - let mut authority = None; - - if let Some(u) = &uri_part { - if s[*u].contains("//") { + if let Some(u) = uri_part { + if s[u].contains("//") { let (auth, part) = get_chunks(&s, Some(RangeC::new(u.start + 2, u.end)), "/"); - authority = if let Some(a) = auth { - Some(Authority::try_from(&s[a])?) - } else { - None + if let Some(a) = auth { + authority = Some(Authority::try_from(&s[a])?) }; uri_part = part; } } - let (mut path, uri_part) = get_chunks(&s, uri_part, "?"); - - if authority.is_some() || &s[scheme] == "file" { - path = path.map(|p| RangeC::new(p.start - 1, p.end)); + if let Some(u) = uri_part { + if &s[u.start - 1..u.start] == "/" { + uri_part = Some(RangeC::new(u.start - 1, u.end)); + } } - let (query, fragment) = get_chunks(&s, uri_part, "#"); + let mut path = uri_part; + + if let Some(u) = uri_part { + if s[u].contains("?") && s[u].contains("#") { + (path, uri_part) = get_chunks(&s, uri_part, "?"); + (query, fragment) = get_chunks(&s, uri_part, "#"); + } else if s[u].contains("?") { + (path, query) = get_chunks(&s, uri_part, "?"); + } else if s[u].contains("#") { + (path, fragment) = get_chunks(&s, uri_part, "#"); + } + } Ok(Uri { inner: s, @@ -405,17 +407,14 @@ impl<'a> TryFrom<&'a str> for Authority<'a> { let uri_part = if s.contains('@') { let (info, part) = get_chunks(&s, Some(RangeC::new(0, s.len())), "@"); - let (name, pass) = get_chunks(&s, info, ":"); - - username = name; - password = pass; + (username, password) = get_chunks(&s, info, ":"); part } else { Some(RangeC::new(0, s.len())) }; - let split_by = if s.contains(']') && s.contains('[') { + let split_by = if s.contains('[') && s.contains(']') { "]:" } else { ":" @@ -498,12 +497,14 @@ fn get_chunks<'a>( mod tests { use super::*; - const TEST_URIS: [&str; 5] = [ + const TEST_URIS: [&str; 7] = [ "https://user:info@foo.com:12/bar/baz?query#fragment", "file:///C:/Users/User/Pictures/screenshot.png", "https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol", "mailto:John.Doe@example.com", "https://[4b10:bbb0:0:d0::ba7:8001]:443/", + "http://example.com/?query=val", + "https://example.com/#fragment", ]; const TEST_AUTH: [&str; 4] = [ @@ -552,12 +553,11 @@ mod tests { .iter() .map(|uri| Uri::try_from(*uri).unwrap()) .collect(); + const RESULT: [&str; 7] = ["https", "file", "https", "mailto", "https", "http", "https"]; - assert_eq!(uris[0].scheme(), "https"); - assert_eq!(uris[1].scheme(), "file"); - assert_eq!(uris[2].scheme(), "https"); - assert_eq!(uris[3].scheme(), "mailto"); - assert_eq!(uris[4].scheme(), "https"); + for i in 0..RESULT.len() { + assert_eq!(uris[i].scheme(), RESULT[i]); + } } #[test] @@ -566,12 +566,11 @@ mod tests { .iter() .map(|uri| Uri::try_from(*uri).unwrap()) .collect(); + const RESULT: [Option<&str>; 7] = [Some("user:info"), None, None, None, None, None, None]; - assert_eq!(uris[0].user_info(), Some("user:info")); - assert_eq!(uris[1].user_info(), None); - assert_eq!(uris[2].user_info(), None); - assert_eq!(uris[3].user_info(), None); - assert_eq!(uris[4].user_info(), None); + for i in 0..RESULT.len() { + assert_eq!(uris[i].user_info(), RESULT[i]); + } } #[test] @@ -581,11 +580,19 @@ mod tests { .map(|uri| Uri::try_from(*uri).unwrap()) .collect(); - assert_eq!(uris[0].host(), Some("foo.com")); - assert_eq!(uris[1].host(), None); - assert_eq!(uris[2].host(), Some("en.wikipedia.org")); - assert_eq!(uris[3].host(), None); - assert_eq!(uris[4].host(), Some("[4b10:bbb0:0:d0::ba7:8001]")); + const RESULT: [Option<&str>; 7] = [ + Some("foo.com"), + None, + Some("en.wikipedia.org"), + None, + Some("[4b10:bbb0:0:d0::ba7:8001]"), + Some("example.com"), + Some("example.com"), + ]; + + for i in 0..RESULT.len() { + assert_eq!(uris[i].host(), RESULT[i]); + } } #[test] @@ -612,7 +619,11 @@ mod tests { assert_eq!(uris[0].port(), Some(12)); assert_eq!(uris[4].port(), Some(443)); - for i in 1..3 { + for i in 1..4 { + assert_eq!(uris[i].port(), None); + } + + for i in 5..7 { assert_eq!(uris[i].port(), None); } } @@ -624,11 +635,13 @@ mod tests { .map(|uri| Uri::try_from(*uri).unwrap()) .collect(); - assert_eq!(uris[0].corr_port(), 12); - assert_eq!(uris[1].corr_port(), HTTP_PORT); - assert_eq!(uris[2].corr_port(), HTTPS_PORT); - assert_eq!(uris[3].corr_port(), HTTP_PORT); - assert_eq!(uris[4].corr_port(), HTTPS_PORT); + const RESULT: [u16; 7] = [ + 12, HTTP_PORT, HTTPS_PORT, HTTP_PORT, HTTPS_PORT, HTTP_PORT, HTTPS_PORT, + ]; + + for i in 0..RESULT.len() { + assert_eq!(uris[i].corr_port(), RESULT[i]); + } } #[test] @@ -638,14 +651,19 @@ mod tests { .map(|uri| Uri::try_from(*uri).unwrap()) .collect(); - assert_eq!(uris[0].path(), Some("/bar/baz")); - assert_eq!( - uris[1].path(), - Some("/C:/Users/User/Pictures/screenshot.png") - ); - assert_eq!(uris[2].path(), Some("/wiki/Hypertext_Transfer_Protocol")); - assert_eq!(uris[3].path(), Some("John.Doe@example.com")); - assert_eq!(uris[4].path(), None); + const RESULT: [Option<&str>; 7] = [ + Some("/bar/baz"), + Some("/C:/Users/User/Pictures/screenshot.png"), + Some("/wiki/Hypertext_Transfer_Protocol"), + Some("John.Doe@example.com"), + None, + Some("/"), + Some("/"), + ]; + + for i in 0..RESULT.len() { + assert_eq!(uris[i].path(), RESULT[i]); + } } #[test] @@ -655,10 +673,18 @@ mod tests { .map(|uri| Uri::try_from(*uri).unwrap()) .collect(); - assert_eq!(uris[0].query(), Some("query")); - - for i in 1..4 { - assert_eq!(uris[i].query(), None); + const RESULT: [Option<&str>; 7] = [ + Some("query"), + None, + None, + None, + None, + Some("query=val"), + None, + ]; + + for i in 0..RESULT.len() { + assert_eq!(uris[i].query(), RESULT[i]); } } @@ -669,10 +695,18 @@ mod tests { .map(|uri| Uri::try_from(*uri).unwrap()) .collect(); - assert_eq!(uris[0].fragment(), Some("fragment")); - - for i in 1..4 { - assert_eq!(uris[i].fragment(), None); + const RESULT: [Option<&str>; 7] = [ + Some("fragment"), + None, + None, + None, + None, + None, + Some("fragment"), + ]; + + for i in 0..RESULT.len() { + assert_eq!(uris[i].fragment(), RESULT[i]); } } @@ -683,11 +717,19 @@ mod tests { .map(|uri| Uri::try_from(*uri).unwrap()) .collect(); - assert_eq!(uris[0].resource(), "/bar/baz?query#fragment"); - assert_eq!(uris[1].resource(), "/C:/Users/User/Pictures/screenshot.png"); - assert_eq!(uris[2].resource(), "/wiki/Hypertext_Transfer_Protocol"); - assert_eq!(uris[3].resource(), "John.Doe@example.com"); - assert_eq!(uris[4].resource(), "/"); + const RESULT: [&str; 7] = [ + "/bar/baz?query#fragment", + "/C:/Users/User/Pictures/screenshot.png", + "/wiki/Hypertext_Transfer_Protocol", + "John.Doe@example.com", + "/", + "/?query=val", + "/#fragment" + ]; + + for i in 0..RESULT.len() { + assert_eq!(uris[i].resource(), RESULT[i]); + } } #[test] From 55bb7c06058a592e33d98a38dc48852ed62d8391 Mon Sep 17 00:00:00 2001 From: jayjamesjay Date: Mon, 25 Sep 2023 19:44:28 +0200 Subject: [PATCH 24/47] refactor uri --- src/error.rs | 2 +- src/lib.rs | 5 +++-- src/uri.rs | 60 ++++++++++++++++++++++------------------------------ 3 files changed, 29 insertions(+), 38 deletions(-) diff --git a/src/error.rs b/src/error.rs index 477d4bd..5c16e68 100644 --- a/src/error.rs +++ b/src/error.rs @@ -21,9 +21,9 @@ impl error::Error for ParseErr { match self { Utf8(e) => Some(e), Int(e) => Some(e), + StatusErr | HeadersErr | UriErr | Invalid | Empty => None, #[cfg(feature = "rust-tls")] Rustls(e) => Some(e), - StatusErr | HeadersErr | UriErr | Invalid | Empty => None, } } } diff --git a/src/lib.rs b/src/lib.rs index 27bd641..d07d71f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,8 +7,9 @@ //!use http_req::request; //! //!fn main() { -//! let mut writer = Vec::new(); //container for body of a response -//! let res = request::get("https://doc.rust-lang.org/", &mut writer).unwrap(); +//! //Container for body of a response +//! let mut body = Vec::new(); +//! let res = request::get("https://doc.rust-lang.org/", &mut body).unwrap(); //! //! println!("Status: {} {}", res.status_code(), res.reason()); //!} diff --git a/src/uri.rs b/src/uri.rs index b7d549f..a3e9301 100644 --- a/src/uri.rs +++ b/src/uri.rs @@ -238,16 +238,14 @@ impl<'a> Uri<'a> { impl<'a> fmt::Display for Uri<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let uri = if let Some(auth) = &self.authority { - let mut uri = self.inner.to_string(); + let mut uri = self.inner.to_string(); + + if let Some(auth) = &self.authority { let auth = auth.to_string(); let start = self.scheme.end + 3; uri.replace_range(start..(start + auth.len()), &auth); - uri - } else { - self.inner.to_string() - }; + } write!(f, "{}", uri) } @@ -402,8 +400,7 @@ impl<'a> TryFrom<&'a str> for Authority<'a> { type Error = ParseErr; fn try_from(s: &'a str) -> Result { - let mut username = None; - let mut password = None; + let (mut username, mut password) = (None, None); let uri_part = if s.contains('@') { let (info, part) = get_chunks(&s, Some(RangeC::new(0, s.len())), "@"); @@ -440,17 +437,14 @@ impl<'a> TryFrom<&'a str> for Authority<'a> { impl<'a> fmt::Display for Authority<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let auth = if let Some(pass) = self.password { - let range = Range::from(pass); + let mut auth = self.inner.to_string(); + if let Some(pass) = self.password { + let range = Range::from(pass); let hidden_pass = "*".repeat(range.len()); - let mut auth = self.inner.to_string(); - auth.replace_range(range, &hidden_pass); - auth - } else { - self.inner.to_string() - }; + auth.replace_range(range, &hidden_pass); + } write!(f, "{}", auth) } @@ -469,28 +463,24 @@ fn get_chunks<'a>( range: Option, separator: &'a str, ) -> (Option, Option) { - if let Some(r) = range { - let range = Range::from(r); + let (mut before, mut after) = (None, None); - match s[range.clone()].find(separator) { + if let Some(range) = range { + match s[range].find(separator) { Some(i) => { - let mid = r.start + i + separator.len(); - let before = Some(RangeC::new(r.start, mid - 1)).filter(|r| r.start != r.end); - let after = Some(RangeC::new(mid, r.end)).filter(|r| r.start != r.end); - - (before, after) + let mid = range.start + i + separator.len(); + before = Some(RangeC::new(range.start, mid - 1)).filter(|r| r.start != r.end); + after = Some(RangeC::new(mid, range.end)).filter(|r| r.start != r.end); } None => { if !s[range].is_empty() { - (Some(r), None) - } else { - (None, None) + before = Some(range); } } } - } else { - (None, None) } + + (before, after) } #[cfg(test)] @@ -718,13 +708,13 @@ mod tests { .collect(); const RESULT: [&str; 7] = [ - "/bar/baz?query#fragment", - "/C:/Users/User/Pictures/screenshot.png", + "/bar/baz?query#fragment", + "/C:/Users/User/Pictures/screenshot.png", "/wiki/Hypertext_Transfer_Protocol", - "John.Doe@example.com", - "/", - "/?query=val", - "/#fragment" + "John.Doe@example.com", + "/", + "/?query=val", + "/#fragment", ]; for i in 0..RESULT.len() { From 33e79124e5a428f46bfb815cd08e768cd10fbb0b Mon Sep 17 00:00:00 2001 From: jayjamesjay Date: Mon, 2 Oct 2023 19:33:56 +0200 Subject: [PATCH 25/47] update dependencies --- Cargo.lock | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4038d1a..03afe1e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -59,9 +59,9 @@ checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "errno" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd" +checksum = "add4f07d43996f76ef320709726a556a9d4f965d9410d8d0271132d2f8293480" dependencies = [ "errno-dragonfly", "libc", @@ -80,9 +80,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" [[package]] name = "foreign-types" @@ -134,9 +134,9 @@ checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" [[package]] name = "linux-raw-sys" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128" +checksum = "3852614a3bd9ca9804678ba6be5e3b8ce76dfc902cae004e3e0c44051b6e88db" [[package]] name = "log" @@ -262,9 +262,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.14" +version = "0.38.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "747c788e9ce8e92b12cd485c49ddf90723550b654b32508f979b71a7b1ecda4f" +checksum = "d2f9da0cbd88f9f09e7814e388301c8414c51c62aa6ce1e4b5c551d49d96e531" dependencies = [ "bitflags 2.4.0", "errno", @@ -296,9 +296,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.101.5" +version = "0.101.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45a27e3b59326c16e23d30aeb7a36a24cc0d29e71d68ff611cdfb4a01d013bed" +checksum = "3c7d5dece342910d9ba34d259310cae3e0154b873b35408b787b59bce53d34fe" dependencies = [ "ring", "untrusted", @@ -475,9 +475,9 @@ dependencies = [ [[package]] name = "webpki" -version = "0.22.1" +version = "0.22.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0e74f82d49d545ad128049b7e88f6576df2da6b02e9ce565c6f533be576957e" +checksum = "07ecc0cd7cac091bf682ec5efa18b1cff79d617b84181f38b3951dbe135f607f" dependencies = [ "ring", "untrusted", From db6631e2a266fea697eb48db82d2cfd0be58b603 Mon Sep 17 00:00:00 2001 From: jayjamesjay Date: Sun, 29 Oct 2023 17:56:58 +0100 Subject: [PATCH 26/47] update dependencies --- Cargo.lock | 202 ++++++++++++++--------------------------------------- Cargo.toml | 2 +- README.md | 4 +- 3 files changed, 56 insertions(+), 152 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 03afe1e..2672997 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "base64" -version = "0.21.4" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" +checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" [[package]] name = "bitflags" @@ -16,15 +16,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" - -[[package]] -name = "bumpalo" -version = "3.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" [[package]] name = "cc" @@ -59,25 +53,14 @@ checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "errno" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "add4f07d43996f76ef320709726a556a9d4f965d9410d8d0271132d2f8293480" +checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" dependencies = [ - "errno-dragonfly", "libc", "windows-sys", ] -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - [[package]] name = "fastrand" version = "2.0.1" @@ -99,9 +82,20 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "http_req" -version = "0.10.0" +version = "0.10.1" dependencies = [ "native-tls", "rustls", @@ -111,15 +105,6 @@ dependencies = [ "webpki-roots", ] -[[package]] -name = "js-sys" -version = "0.3.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" -dependencies = [ - "wasm-bindgen", -] - [[package]] name = "lazy_static" version = "1.4.0" @@ -128,15 +113,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.148" +version = "0.2.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" +checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" [[package]] name = "linux-raw-sys" -version = "0.4.8" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3852614a3bd9ca9804678ba6be5e3b8ce76dfc902cae004e3e0c44051b6e88db" +checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" [[package]] name = "log" @@ -174,7 +159,7 @@ version = "0.10.57" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bac25ee399abb46215765b1cb35bc0212377e58a061560d8b29b024fd0430e7c" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.1", "cfg-if", "foreign-types", "libc", @@ -220,9 +205,9 @@ checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "proc-macro2" -version = "1.0.67" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] @@ -238,35 +223,34 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.3.5" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ "bitflags 1.3.2", ] [[package]] name = "ring" -version = "0.16.20" +version = "0.17.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +checksum = "fb0205304757e5d899b9c2e448b867ffd03ae7f988002e47cd24954391394d0b" dependencies = [ "cc", + "getrandom", "libc", - "once_cell", "spin", "untrusted", - "web-sys", - "winapi", + "windows-sys", ] [[package]] name = "rustix" -version = "0.38.15" +version = "0.38.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2f9da0cbd88f9f09e7814e388301c8414c51c62aa6ce1e4b5c551d49d96e531" +checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.1", "errno", "libc", "linux-raw-sys", @@ -275,9 +259,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.21.7" +version = "0.21.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8" +checksum = "446e14c5cda4f3f30fe71863c34ec70f5ac79d6087097ad0bb433e1be5edf04c" dependencies = [ "log", "ring", @@ -296,9 +280,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.101.6" +version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c7d5dece342910d9ba34d259310cae3e0154b873b35408b787b59bce53d34fe" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ "ring", "untrusted", @@ -315,9 +299,9 @@ dependencies = [ [[package]] name = "sct" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ "ring", "untrusted", @@ -348,15 +332,15 @@ dependencies = [ [[package]] name = "spin" -version = "0.5.2" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" [[package]] name = "syn" -version = "2.0.37" +version = "2.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8" +checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" dependencies = [ "proc-macro2", "quote", @@ -365,9 +349,9 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.8.0" +version = "3.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" +checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" dependencies = [ "cfg-if", "fastrand", @@ -393,9 +377,9 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "untrusted" -version = "0.7.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "vcpkg" @@ -410,74 +394,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] -name = "wasm-bindgen" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.87" +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" - -[[package]] -name = "web-sys" -version = "0.3.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" -dependencies = [ - "js-sys", - "wasm-bindgen", -] +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "webpki" -version = "0.22.2" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07ecc0cd7cac091bf682ec5efa18b1cff79d617b84181f38b3951dbe135f607f" +checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53" dependencies = [ "ring", "untrusted", @@ -489,28 +415,6 @@ version = "0.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc" -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - [[package]] name = "windows-sys" version = "0.48.0" diff --git a/Cargo.toml b/Cargo.toml index adb0af1..30a176b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "http_req" -version = "0.10.0" +version = "0.10.1" license = "MIT" description = "simple and lightweight HTTP client with built-in HTTPS support" repository = "https://github.com/jayjamesjay/http_req" diff --git a/README.md b/README.md index 00a6163..5e72055 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # http_req [![Rust](https://github.com/jayjamesjay/http_req/actions/workflows/rust.yml/badge.svg)](https://github.com/jayjamesjay/http_req/actions/workflows/rust.yml) -[![Crates.io](https://img.shields.io/badge/crates.io-v0.10.0-orange.svg?longCache=true)](https://crates.io/crates/http_req) -[![Docs.rs](https://docs.rs/http_req/badge.svg)](https://docs.rs/http_req/0.10.0/http_req/) +[![Crates.io](https://img.shields.io/badge/crates.io-v0.10.1-orange.svg?longCache=true)](https://crates.io/crates/http_req) +[![Docs.rs](https://docs.rs/http_req/badge.svg)](https://docs.rs/http_req/0.10.1/http_req/) Simple and lightweight HTTP client with built-in HTTPS support. From 2abc8fbb642bb08abe0ad8f0e1be4ecbdf1e5f71 Mon Sep 17 00:00:00 2001 From: jayjamesjay Date: Fri, 1 Dec 2023 20:53:21 +0100 Subject: [PATCH 27/47] update dependencies --- Cargo.lock | 154 ++++++++++++++++++++++++++++++++++++++--------------- Cargo.toml | 2 +- README.md | 6 +-- 3 files changed, 114 insertions(+), 48 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2672997..bfc9ce8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -37,9 +37,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "core-foundation" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ "core-foundation-sys", "libc", @@ -47,18 +47,18 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "errno" -version = "0.3.5" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -84,9 +84,9 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "getrandom" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" dependencies = [ "cfg-if", "libc", @@ -95,7 +95,7 @@ dependencies = [ [[package]] name = "http_req" -version = "0.10.1" +version = "0.10.2" dependencies = [ "native-tls", "rustls", @@ -113,15 +113,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.149" +version = "0.2.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" [[package]] name = "linux-raw-sys" -version = "0.4.10" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" +checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" [[package]] name = "log" @@ -155,9 +155,9 @@ checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "openssl" -version = "0.10.57" +version = "0.10.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bac25ee399abb46215765b1cb35bc0212377e58a061560d8b29b024fd0430e7c" +checksum = "79a4c6c3a2b158f7f8f2a2fc5a969fa3a068df6fc9dbb4a43845436e3af7c800" dependencies = [ "bitflags 2.4.1", "cfg-if", @@ -187,9 +187,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.93" +version = "0.9.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db4d56a4c0478783083cfafcc42493dd4a981d41669da64b4572a2a089b51b1d" +checksum = "3812c071ba60da8b5677cc12bcb1d42989a65553772897a7e0355545a819838f" dependencies = [ "cc", "libc", @@ -205,9 +205,9 @@ checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "proc-macro2" -version = "1.0.69" +version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" dependencies = [ "unicode-ident", ] @@ -232,36 +232,36 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.5" +version = "0.17.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb0205304757e5d899b9c2e448b867ffd03ae7f988002e47cd24954391394d0b" +checksum = "684d5e6e18f669ccebf64a92236bb7db9a34f07be010e3627368182027180866" dependencies = [ "cc", "getrandom", "libc", "spin", "untrusted", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] name = "rustix" -version = "0.38.21" +version = "0.38.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3" +checksum = "9470c4bf8246c8daf25f9598dca807fb6510347b1e1cfa55749113850c79d88a" dependencies = [ "bitflags 2.4.1", "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] name = "rustls" -version = "0.21.8" +version = "0.21.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "446e14c5cda4f3f30fe71863c34ec70f5ac79d6087097ad0bb433e1be5edf04c" +checksum = "629648aced5775d558af50b2b4c7b02983a04b312126d45eeead26e7caa498b9" dependencies = [ "log", "ring", @@ -271,9 +271,9 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" dependencies = [ "base64", ] @@ -294,7 +294,7 @@ version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" dependencies = [ - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -338,9 +338,9 @@ checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" [[package]] name = "syn" -version = "2.0.38" +version = "2.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" dependencies = [ "proc-macro2", "quote", @@ -357,7 +357,7 @@ dependencies = [ "fastrand", "redox_syscall", "rustix", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -411,9 +411,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.25.2" +version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc" +checksum = "1778a42e8b3b90bff8d0f5032bf22250792889a5cdc752aa0020c84abe3aaf10" [[package]] name = "windows-sys" @@ -421,7 +421,16 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", ] [[package]] @@ -430,13 +439,28 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", ] [[package]] @@ -445,38 +469,80 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" diff --git a/Cargo.toml b/Cargo.toml index 30a176b..4350d9c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "http_req" -version = "0.10.1" +version = "0.10.2" license = "MIT" description = "simple and lightweight HTTP client with built-in HTTPS support" repository = "https://github.com/jayjamesjay/http_req" diff --git a/README.md b/README.md index 5e72055..36d5b85 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # http_req [![Rust](https://github.com/jayjamesjay/http_req/actions/workflows/rust.yml/badge.svg)](https://github.com/jayjamesjay/http_req/actions/workflows/rust.yml) -[![Crates.io](https://img.shields.io/badge/crates.io-v0.10.1-orange.svg?longCache=true)](https://crates.io/crates/http_req) -[![Docs.rs](https://docs.rs/http_req/badge.svg)](https://docs.rs/http_req/0.10.1/http_req/) +[![Crates.io](https://img.shields.io/badge/crates.io-v0.10.2-orange.svg?longCache=true)](https://crates.io/crates/http_req) +[![Docs.rs](https://docs.rs/http_req/badge.svg)](https://docs.rs/http_req/0.10.2/http_req/) Simple and lightweight HTTP client with built-in HTTPS support. @@ -26,7 +26,7 @@ fn main() { Take a look at [more examples](https://github.com/jayjamesjay/http_req/tree/master/examples) ## How to use with `rustls`: -In order to use `http_req` with `rustls` in your project, add following lines to `Cargo.toml`: +In order to use `http_req` with `rustls` in your project, add the following lines to `Cargo.toml`: ```toml [dependencies] http_req = {version="^0.10", default-features = false, features = ["rust-tls"]} From 7f50f7f8d9dd48240fdc483c9afbc29198e5a4ee Mon Sep 17 00:00:00 2001 From: jayjamesjay Date: Sun, 21 Apr 2024 16:04:02 +0200 Subject: [PATCH 28/47] update dependencies --- Cargo.lock | 235 +++++++++++++++++++---------------------------------- Cargo.toml | 2 +- README.md | 4 +- 3 files changed, 85 insertions(+), 156 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bfc9ce8..990f50e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "base64" -version = "0.21.5" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "bitflags" @@ -16,18 +16,15 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "cc" -version = "1.0.83" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" -dependencies = [ - "libc", -] +checksum = "d32a725bc159af97c3e629873bb9f88fb8cf8a4867175f76dc987815ea07c83b" [[package]] name = "cfg-if" @@ -58,14 +55,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] name = "fastrand" -version = "2.0.1" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" [[package]] name = "foreign-types" @@ -84,9 +81,9 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "getrandom" -version = "0.2.11" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" dependencies = [ "cfg-if", "libc", @@ -113,21 +110,21 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.150" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "linux-raw-sys" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "log" -version = "0.4.20" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "native-tls" @@ -149,17 +146,17 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "openssl" -version = "0.10.60" +version = "0.10.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79a4c6c3a2b158f7f8f2a2fc5a969fa3a068df6fc9dbb4a43845436e3af7c800" +checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.5.0", "cfg-if", "foreign-types", "libc", @@ -187,9 +184,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.96" +version = "0.9.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3812c071ba60da8b5677cc12bcb1d42989a65553772897a7e0355545a819838f" +checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" dependencies = [ "cc", "libc", @@ -199,69 +196,61 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.27" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "proc-macro2" -version = "1.0.70" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" +checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.33" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] -[[package]] -name = "redox_syscall" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "ring" -version = "0.17.6" +version = "0.17.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "684d5e6e18f669ccebf64a92236bb7db9a34f07be010e3627368182027180866" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", + "cfg-if", "getrandom", "libc", "spin", "untrusted", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] name = "rustix" -version = "0.38.26" +version = "0.38.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9470c4bf8246c8daf25f9598dca807fb6510347b1e1cfa55749113850c79d88a" +checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.5.0", "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] name = "rustls" -version = "0.21.9" +version = "0.21.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "629648aced5775d558af50b2b4c7b02983a04b312126d45eeead26e7caa498b9" +checksum = "7fecbfb7b1444f477b345853b1fce097a2c6fb637b2bfb87e6bc5db0f043fae4" dependencies = [ "log", "ring", @@ -290,11 +279,11 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" dependencies = [ - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -309,9 +298,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.9.2" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6" dependencies = [ "bitflags 1.3.2", "core-foundation", @@ -322,9 +311,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.9.1" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +checksum = "41f3cc463c0ef97e11c3461a9d3787412d30e8e7eb907c79180c4a57bf7c04ef" dependencies = [ "core-foundation-sys", "libc", @@ -338,9 +327,9 @@ checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" [[package]] name = "syn" -version = "2.0.39" +version = "2.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" dependencies = [ "proc-macro2", "quote", @@ -349,15 +338,14 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.8.1" +version = "3.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", "fastrand", - "redox_syscall", "rustix", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -411,18 +399,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.25.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1778a42e8b3b90bff8d0f5032bf22250792889a5cdc752aa0020c84abe3aaf10" - -[[package]] -name = "windows-sys" -version = "0.48.0" +version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" [[package]] name = "windows-sys" @@ -430,119 +409,69 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.0", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", + "windows-targets", ] [[package]] name = "windows-targets" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" [[package]] -name = "windows_i686_msvc" -version = "0.48.5" +name = "windows_i686_gnullvm" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" diff --git a/Cargo.toml b/Cargo.toml index 4350d9c..0bc6220 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "http_req" -version = "0.10.2" +version = "0.10.3" license = "MIT" description = "simple and lightweight HTTP client with built-in HTTPS support" repository = "https://github.com/jayjamesjay/http_req" diff --git a/README.md b/README.md index 36d5b85..ea6611c 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # http_req [![Rust](https://github.com/jayjamesjay/http_req/actions/workflows/rust.yml/badge.svg)](https://github.com/jayjamesjay/http_req/actions/workflows/rust.yml) -[![Crates.io](https://img.shields.io/badge/crates.io-v0.10.2-orange.svg?longCache=true)](https://crates.io/crates/http_req) -[![Docs.rs](https://docs.rs/http_req/badge.svg)](https://docs.rs/http_req/0.10.2/http_req/) +[![Crates.io](https://img.shields.io/badge/crates.io-v0.10.3-orange.svg?longCache=true)](https://crates.io/crates/http_req) +[![Docs.rs](https://docs.rs/http_req/badge.svg)](https://docs.rs/http_req/0.10.3/http_req/) Simple and lightweight HTTP client with built-in HTTPS support. From 8000c69a216a9ff198e8396c7aa49160553befae Mon Sep 17 00:00:00 2001 From: Dmitry Date: Mon, 22 Apr 2024 22:18:57 +0300 Subject: [PATCH 29/47] Fix for incorrect Content-Length: 0 handling If the Content-Length header is set to 0, the client must not read the content from the stream, otherwise it hangs waiting for data. Redirect responses (302, 307) usally have no content and have Content-Length: 0 --- src/request.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/request.rs b/src/request.rs index 02f7cfe..25eed96 100644 --- a/src/request.rs +++ b/src/request.rs @@ -510,10 +510,10 @@ impl<'a> RequestBuilder<'a> { let deadline = Instant::now() + timeout; copy_with_timeout(stream, writer, deadline)?; } else { - let num_bytes = res.content_len().unwrap_or(0); - - if num_bytes > 0 { - copy_exact(stream, writer, num_bytes - body_part.len())?; + if let Some(num_bytes) = res.content_len() { + if num_bytes > 0 { + copy_exact(stream, writer, num_bytes - body_part.len())?; + } } else { io::copy(stream, writer)?; } From d510cbcde19292fab599dc781acf999cea6a45e2 Mon Sep 17 00:00:00 2001 From: jayjamesjay Date: Thu, 20 Jun 2024 14:22:16 +0200 Subject: [PATCH 30/47] remove current timeout implementation --- Cargo.lock | 2 +- benches/bench.rs | 4 +- src/request.rs | 177 +++++------------------------------------------ 3 files changed, 20 insertions(+), 163 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 990f50e..fe0958c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -92,7 +92,7 @@ dependencies = [ [[package]] name = "http_req" -version = "0.10.2" +version = "0.10.3" dependencies = [ "native-tls", "rustls", diff --git a/benches/bench.rs b/benches/bench.rs index c877159..643357a 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -3,7 +3,7 @@ extern crate http_req; extern crate test; use http_req::{request::Request, response::Response, uri::Uri}; -use std::{convert::TryFrom, fs::File, io::Read, time::Duration}; +use std::{convert::TryFrom, fs::File, io::Read}; use test::Bencher; #[bench] @@ -24,11 +24,9 @@ const URI: &str = "https://www.rust-lang.org/"; fn request_send(b: &mut Bencher) { b.iter(|| { let uri = Uri::try_from(URI).unwrap(); - let timeout = Some(Duration::from_secs(6)); let mut writer = Vec::new(); let res = Request::new(&uri) - .timeout(timeout) .send(&mut writer) .unwrap(); diff --git a/src/request.rs b/src/request.rs index 25eed96..ff91b0b 100644 --- a/src/request.rs +++ b/src/request.rs @@ -1,5 +1,6 @@ //! creating and sending HTTP requests use crate::{ + chunked::Reader, error, response::{find_slice, Headers, Response, CR_LF_2}, tls, @@ -10,13 +11,11 @@ use std::{ io::{self, ErrorKind, Read, Write}, net::{TcpStream, ToSocketAddrs}, path::Path, - time::{Duration, Instant}, + time::Duration, }; const CR_LF: &str = "\r\n"; -const BUF_SIZE: usize = 8 * 1024; const SMALL_BUF_SIZE: usize = 8 * 10; -const TEST_FREQ: usize = 100; ///Every iteration increases `count` by one. When `count` is equal to `stop`, `next()` ///returns `Some(true)` (and sets `count` to 0), otherwise returns `Some(false)`. @@ -47,34 +46,6 @@ impl Iterator for Counter { } } -///Copies data from `reader` to `writer` until the `deadline` is reached. -///Limitations of current implementation may cause exceeding the deadline. -///Returns how many bytes has been read. -pub fn copy_with_timeout(reader: &mut R, writer: &mut W, deadline: Instant) -> io::Result -where - R: Read + ?Sized, - W: Write + ?Sized, -{ - let mut buf = [0; BUF_SIZE]; - let mut copied = 0; - let mut counter = Counter::new(TEST_FREQ); - - loop { - let len = match reader.read(&mut buf) { - Ok(0) => return Ok(copied), - Ok(len) => len, - Err(ref e) if e.kind() == ErrorKind::Interrupted => continue, - Err(e) => return Err(e), - }; - writer.write_all(&buf[..len])?; - copied += len as u64; - - if counter.next().unwrap() && Instant::now() >= deadline { - return Ok(copied); - } - } -} - ///Copies a given amount of bytes from `reader` to `writer`. pub fn copy_exact(reader: &mut R, writer: &mut W, num_bytes: usize) -> io::Result<()> where @@ -90,17 +61,12 @@ where ///Reads data from `reader` and checks for specified `val`ue. When data contains specified value ///or `deadline` is reached, stops reading. Returns read data as array of two vectors: elements ///before and after the `val`. -pub fn copy_until( - reader: &mut R, - val: &[u8], - deadline: Instant, -) -> Result<[Vec; 2], io::Error> +pub fn copy_until(reader: &mut R, val: &[u8]) -> Result<[Vec; 2], io::Error> where R: Read + ?Sized, { let mut buf = [0; SMALL_BUF_SIZE]; let mut writer = Vec::with_capacity(SMALL_BUF_SIZE); - let mut counter = Counter::new(TEST_FREQ); let mut split_idx = 0; loop { @@ -117,11 +83,6 @@ where split_idx = i; break; } - - if counter.next().unwrap() && Instant::now() >= deadline { - split_idx = writer.len(); - break; - } } Ok([writer[..split_idx].to_vec(), writer[split_idx..].to_vec()]) @@ -215,7 +176,6 @@ pub struct RequestBuilder<'a> { version: HttpVersion, headers: Headers, body: Option<&'a [u8]>, - timeout: Option, } impl<'a> RequestBuilder<'a> { @@ -246,7 +206,6 @@ impl<'a> RequestBuilder<'a> { method: Method::GET, version: HttpVersion::Http11, body: None, - timeout: None, } } @@ -400,36 +359,6 @@ impl<'a> RequestBuilder<'a> { self } - ///Sets timeout for entire connection. - /// - ///# Examples - ///``` - ///use std::{net::TcpStream, time::{Duration, Instant}, convert::TryFrom}; - ///use http_req::{request::RequestBuilder, tls, uri::Uri}; - /// - ///let addr = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); - ///let mut writer = Vec::new(); - /// - ///let stream = TcpStream::connect((addr.host().unwrap(), addr.corr_port())).unwrap(); - ///let mut stream = tls::Config::default() - /// .connect(addr.host().unwrap_or(""), stream) - /// .unwrap(); - ///let timeout = Some(Duration::from_secs(3600)); - /// - ///let response = RequestBuilder::new(&addr) - /// .timeout(timeout) - /// .header("Connection", "Close") - /// .send(&mut stream, &mut writer) - /// .unwrap(); - ///``` - pub fn timeout(&mut self, timeout: Option) -> &mut Self - where - Duration: From, - { - self.timeout = timeout.map(Duration::from); - self - } - ///Sends HTTP request in these steps: /// ///- Writes request message to `stream`. @@ -479,44 +408,28 @@ impl<'a> RequestBuilder<'a> { { self.write_msg(stream, &self.parse_msg())?; - let head_deadline = match self.timeout { - Some(t) => Instant::now() + t, - None => Instant::now() + Duration::from_secs(360), - }; - let (res, body_part) = self.read_head(stream, head_deadline)?; - + let (res, body_part) = self.read_head(stream)?; if self.method == Method::HEAD { return Ok(res); } + writer.write_all(&body_part)?; + if let Some(v) = res.headers().get("Transfer-Encoding") { if *v == "chunked" { - let mut dechunked = crate::chunked::Reader::new(body_part.as_slice().chain(stream)); - - if let Some(timeout) = self.timeout { - let deadline = Instant::now() + timeout; - copy_with_timeout(&mut dechunked, writer, deadline)?; - } else { - io::copy(&mut dechunked, writer)?; - } - - return Ok(res); + let mut dechunked = Reader::new(body_part.as_slice().chain(stream)); + io::copy(&mut dechunked, writer)?; } - } - writer.write_all(&body_part)?; + return Ok(res); + } - if let Some(timeout) = self.timeout { - let deadline = Instant::now() + timeout; - copy_with_timeout(stream, writer, deadline)?; - } else { - if let Some(num_bytes) = res.content_len() { - if num_bytes > 0 { - copy_exact(stream, writer, num_bytes - body_part.len())?; - } - } else { - io::copy(stream, writer)?; + if let Some(num_bytes) = res.content_len() { + if num_bytes > 0 { + copy_exact(stream, writer, num_bytes - body_part.len())?; } + } else { + io::copy(stream, writer)?; } Ok(res) @@ -535,12 +448,8 @@ impl<'a> RequestBuilder<'a> { } ///Reads head of server's response - pub fn read_head( - &self, - stream: &mut T, - deadline: Instant, - ) -> Result<(Response, Vec), error::Error> { - let [head, body_part] = copy_until(stream, &CR_LF_2, deadline)?; + pub fn read_head(&self, stream: &mut T) -> Result<(Response, Vec), error::Error> { + let [head, body_part] = copy_until(stream, &CR_LF_2)?; Ok((Response::from_head(&head)?, body_part)) } @@ -746,31 +655,6 @@ impl<'a> Request<'a> { self } - ///Sets connection timeout of request. - /// - ///# Examples - ///``` - ///use std::{time::{Duration, Instant}, convert::TryFrom}; - ///use http_req::{request::Request, uri::Uri}; - /// - ///let mut writer = Vec::new(); - ///let uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); - ///const body: &[u8; 27] = b"field1=value1&field2=value2"; - ///let timeout = Some(Duration::from_secs(3600)); - /// - ///let response = Request::new(&uri) - /// .timeout(timeout) - /// .send(&mut writer) - /// .unwrap(); - ///``` - pub fn timeout(&mut self, timeout: Option) -> &mut Self - where - Duration: From, - { - self.inner.timeout = timeout.map(Duration::from); - self - } - ///Sets connect timeout while using internal `TcpStream` instance /// ///- If there is a timeout, it will be passed to @@ -1048,12 +932,7 @@ mod tests { let mut reader = Cursor::new(reader); - let [head, _body] = copy_until( - &mut reader, - &CR_LF_2, - Instant::now() + Duration::from_secs(360), - ) - .unwrap(); + let [head, _body] = copy_until(&mut reader, &CR_LF_2).unwrap(); assert_eq!(&head[..], &RESPONSE_H[..]); } @@ -1118,16 +997,6 @@ mod tests { assert_eq!(req.body, Some(BODY.as_ref())); } - #[test] - fn request_b_timeout() { - let uri = Uri::try_from(URI).unwrap(); - let mut req = RequestBuilder::new(&uri); - let timeout = Some(Duration::from_secs(360)); - - req.timeout(timeout); - assert_eq!(req.timeout, timeout); - } - #[ignore] #[test] fn request_b_send() { @@ -1233,16 +1102,6 @@ mod tests { assert_eq!(req.inner.body, Some(BODY.as_ref())); } - #[test] - fn request_timeout() { - let uri = Uri::try_from(URI).unwrap(); - let mut request = Request::new(&uri); - let timeout = Some(Duration::from_secs(360)); - - request.timeout(timeout); - assert_eq!(request.inner.timeout, timeout); - } - #[test] fn request_connect_timeout() { let uri = Uri::try_from(URI).unwrap(); From 47e81c4368354280bfb856f1b1633a5be6c60516 Mon Sep 17 00:00:00 2001 From: jayjamesjay Date: Fri, 21 Jun 2024 11:03:05 +0200 Subject: [PATCH 31/47] timeout - new impl - init --- benches/bench.rs | 4 +-- src/error.rs | 11 +++++++- src/lib.rs | 1 + src/request.rs | 68 ++++++++++++++++++++++++++++++++++++++++++------ src/stream.rs | 51 ++++++++++++++++++++++++++++++++++++ 5 files changed, 123 insertions(+), 12 deletions(-) create mode 100644 src/stream.rs diff --git a/benches/bench.rs b/benches/bench.rs index 643357a..bb6f26a 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -26,9 +26,7 @@ fn request_send(b: &mut Bencher) { let uri = Uri::try_from(URI).unwrap(); let mut writer = Vec::new(); - let res = Request::new(&uri) - .send(&mut writer) - .unwrap(); + let res = Request::new(&uri).send(&mut writer).unwrap(); res }); diff --git a/src/error.rs b/src/error.rs index 5c16e68..454a579 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,5 +1,5 @@ //!error system -use std::{error, fmt, io, num, str}; +use std::{error, fmt, io, num, str, sync::mpsc}; #[derive(Debug, PartialEq)] pub enum ParseErr { @@ -70,6 +70,7 @@ impl From for ParseErr { pub enum Error { IO(io::Error), Parse(ParseErr), + Timeout(mpsc::RecvTimeoutError), Tls, } @@ -80,6 +81,7 @@ impl error::Error for Error { match self { IO(e) => Some(e), Parse(e) => Some(e), + Timeout(e) => Some(e), Tls => None, } } @@ -92,6 +94,7 @@ impl fmt::Display for Error { let err = match self { IO(_) => "IO error", Tls => "TLS error", + Timeout(_) => "Timeout error", Parse(err) => return err.fmt(f), }; write!(f, "Error: {}", err) @@ -129,3 +132,9 @@ impl From for Error { Error::Parse(ParseErr::Utf8(e)) } } + +impl From for Error { + fn from(e: mpsc::RecvTimeoutError) -> Self { + Error::Timeout(e) + } +} diff --git a/src/lib.rs b/src/lib.rs index d07d71f..b954e1c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,6 +17,7 @@ pub mod error; pub mod request; pub mod response; +pub mod stream; pub mod tls; pub mod uri; diff --git a/src/request.rs b/src/request.rs index ff91b0b..b901a8f 100644 --- a/src/request.rs +++ b/src/request.rs @@ -3,6 +3,7 @@ use crate::{ chunked::Reader, error, response::{find_slice, Headers, Response, CR_LF_2}, + stream::Stream, tls, uri::Uri, }; @@ -11,10 +12,13 @@ use std::{ io::{self, ErrorKind, Read, Write}, net::{TcpStream, ToSocketAddrs}, path::Path, - time::Duration, + sync::mpsc, + thread, + time::{Duration, Instant}, }; const CR_LF: &str = "\r\n"; +const BUF_SIZE: usize = 8 * 1024; const SMALL_BUF_SIZE: usize = 8 * 10; ///Every iteration increases `count` by one. When `count` is equal to `stop`, `next()` @@ -770,9 +774,15 @@ impl<'a> Request<'a> { pub fn send(&self, writer: &mut T) -> Result { let host = self.inner.uri.host().unwrap_or(""); let port = self.inner.uri.corr_port(); - let mut stream = match self.connect_timeout { - Some(timeout) => connect_timeout(host, port, timeout)?, - None => TcpStream::connect((host, port))?, + let init_msg = self.inner.parse_msg(); + let timeout = Duration::from_secs(10); + + let (sender, receiver) = mpsc::channel(); + let mut raw_response_head: Vec = Vec::new(); + + let mut stream: Stream = match self.connect_timeout { + Some(timeout) => Stream::Http(connect_timeout(host, port, timeout)?), + None => Stream::Http(TcpStream::connect((host, port))?), }; stream.set_read_timeout(self.read_timeout)?; @@ -784,11 +794,53 @@ impl<'a> Request<'a> { Some(p) => cnf.add_root_cert_file_pem(p)?, None => &mut cnf, }; - let mut stream = cnf.connect(host, stream)?; - self.inner.send(&mut stream, writer) - } else { - self.inner.send(&mut stream, writer) + + if let Stream::Http(inner_stream) = stream { + stream = Stream::Https(cnf.connect(host, inner_stream)?); + } } + + stream.write_all(&init_msg)?; + + thread::spawn(move || loop { + let mut buf = [0; BUF_SIZE]; + + match stream.read(&mut buf) { + Ok(0) => break, + Ok(_) => sender.send(buf).unwrap(), + Err(_) => break, + } + }); + + let start_time = Instant::now(); + loop { + let now = Instant::now(); + + if start_time + timeout > now { + let data_read = receiver.recv_timeout(timeout)?; + + if let Some(i) = find_slice(&data_read, &CR_LF_2) { + raw_response_head.write_all(&data_read[..i])?; + writer.write_all(&data_read[i..])?; + break; + } else { + raw_response_head.write_all(&data_read)?; + } + } + } + + loop { + let now = Instant::now(); + + if start_time + timeout > now { + match receiver.recv_timeout(timeout) { + Ok(data_read) => writer.write_all(&data_read)?, + Err(_) => break, + } + } + } + + Response::from_head(&raw_response_head) } } diff --git a/src/stream.rs b/src/stream.rs new file mode 100644 index 0000000..368f537 --- /dev/null +++ b/src/stream.rs @@ -0,0 +1,51 @@ +use crate::error::Error; +use std::io; +use std::net::TcpStream; +use std::time::Duration; + +use crate::tls::Conn; + +pub enum Stream { + Http(TcpStream), + Https(Conn), +} + +impl Stream { + pub fn set_read_timeout(&mut self, dur: Option) -> Result<(), Error> { + match self { + Stream::Http(stream) => Ok(stream.set_read_timeout(dur)?), + Stream::Https(_) => Err(Error::Tls), + } + } + + pub fn set_write_timeout(&mut self, dur: Option) -> Result<(), Error> { + match self { + Stream::Http(stream) => Ok(stream.set_write_timeout(dur)?), + Stream::Https(_) => Err(Error::Tls), + } + } +} + +impl io::Read for Stream { + fn read(&mut self, buf: &mut [u8]) -> Result { + match self { + Stream::Http(stream) => stream.read(buf), + Stream::Https(stream) => stream.read(buf), + } + } +} + +impl io::Write for Stream { + fn write(&mut self, buf: &[u8]) -> Result { + match self { + Stream::Http(stream) => stream.write(buf), + Stream::Https(stream) => stream.write(buf), + } + } + fn flush(&mut self) -> Result<(), io::Error> { + match self { + Stream::Http(stream) => stream.flush(), + Stream::Https(stream) => stream.flush(), + } + } +} From 424fb46ad44519aac26ef3641ed651b1be5a4cce Mon Sep 17 00:00:00 2001 From: jayjamesjay Date: Fri, 21 Jun 2024 16:13:56 +0200 Subject: [PATCH 32/47] move more functionalities to Stream --- Cargo.lock | 2 +- Cargo.toml | 2 +- examples/get.rs | 22 +- examples/request_builder_get.rs | 12 +- src/request.rs | 367 +++++--------------------------- src/stream.rs | 107 +++++++++- 6 files changed, 186 insertions(+), 326 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fe0958c..f971aaf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -92,7 +92,7 @@ dependencies = [ [[package]] name = "http_req" -version = "0.10.3" +version = "0.11.0" dependencies = [ "native-tls", "rustls", diff --git a/Cargo.toml b/Cargo.toml index 0bc6220..f5d04a6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "http_req" -version = "0.10.3" +version = "0.11.0" license = "MIT" description = "simple and lightweight HTTP client with built-in HTTPS support" repository = "https://github.com/jayjamesjay/http_req" diff --git a/examples/get.rs b/examples/get.rs index a14c8a7..22f6abb 100644 --- a/examples/get.rs +++ b/examples/get.rs @@ -1,14 +1,28 @@ use http_req::request; +use http_req::{request::Request, response::StatusCode, uri::Uri}; +use std::convert::TryFrom; +use std::fs::File; fn main() { //Container for body of a response. - let mut body = Vec::new(); + //let mut body = Vec::new(); //Sends a HTTP GET request and processes the response. Saves body of the response to `body` variable. - let res = request::get("https://www.rust-lang.org/learn", &mut body).unwrap(); + //let res = request::get("https://drivers.amd.com/drivers/installer/23.40/whql/amd-software-adrenalin-edition-24.5.1-minimalsetup-240514_web.exe", &mut body).unwrap(); //Prints details about the response. - println!("Status: {} {}", res.status_code(), res.reason()); - println!("Headers: {}", res.headers()); + //println!("Status: {} {}", res.status_code(), res.reason()); + //println!("Headers: {}", res.headers()); //println!("{}", String::from_utf8_lossy(&body)); + + let mut writer = File::create("boo.txt").unwrap(); + let uri = Uri::try_from("https://drivers.amd.com/drivers/whql-amd-software-adrenalin-edition-24.5.1-win10-win11-may15-rdna.exe").unwrap(); + + let response = Request::new(&uri) + .header("Referer", &uri) + .send(&mut writer) + .unwrap(); + + println!("Status: {} {}", response.status_code(), response.reason()); + println!("Headers: {}", response.headers()); } diff --git a/examples/request_builder_get.rs b/examples/request_builder_get.rs index 7c237d5..75c65a4 100644 --- a/examples/request_builder_get.rs +++ b/examples/request_builder_get.rs @@ -14,15 +14,15 @@ fn main() { .unwrap(); //Container for a response's body. - let mut writer = Vec::new(); + //let mut writer = Vec::new(); //Adds a header `Connection: Close`. let response = RequestBuilder::new(&addr) - .header("Connection", "Close") - .send(&mut stream, &mut writer) - .unwrap(); + .header("Connection", "Close"); + // .send(&mut stream, &mut writer) + // .unwrap(); - println!("Status: {} {}", response.status_code(), response.reason()); - println!("Headers: {}", response.headers()); + //println!("Status: {} {}", response.status_code(), response.reason()); + //println!("Headers: {}", response.headers()); //println!("{}", String::from_utf8_lossy(&writer)); } diff --git a/src/request.rs b/src/request.rs index b901a8f..51f5a78 100644 --- a/src/request.rs +++ b/src/request.rs @@ -1,16 +1,13 @@ //! creating and sending HTTP requests use crate::{ - chunked::Reader, error, response::{find_slice, Headers, Response, CR_LF_2}, stream::Stream, - tls, uri::Uri, }; use std::{ fmt, - io::{self, ErrorKind, Read, Write}, - net::{TcpStream, ToSocketAddrs}, + io::{Read, Write}, path::Path, sync::mpsc, thread, @@ -18,79 +15,9 @@ use std::{ }; const CR_LF: &str = "\r\n"; -const BUF_SIZE: usize = 8 * 1024; -const SMALL_BUF_SIZE: usize = 8 * 10; - -///Every iteration increases `count` by one. When `count` is equal to `stop`, `next()` -///returns `Some(true)` (and sets `count` to 0), otherwise returns `Some(false)`. -///Iterator never returns `None`. -pub struct Counter { - count: usize, - stop: usize, -} - -impl Counter { - pub const fn new(stop: usize) -> Counter { - Counter { count: 0, stop } - } -} - -impl Iterator for Counter { - type Item = bool; - - fn next(&mut self) -> Option { - self.count += 1; - let breakpoint = self.count == self.stop; - - if breakpoint { - self.count = 0; - } - - Some(breakpoint) - } -} - -///Copies a given amount of bytes from `reader` to `writer`. -pub fn copy_exact(reader: &mut R, writer: &mut W, num_bytes: usize) -> io::Result<()> -where - R: Read + ?Sized, - W: Write + ?Sized, -{ - let mut buf = vec![0u8; num_bytes]; - - reader.read_exact(&mut buf)?; - writer.write_all(&mut buf) -} - -///Reads data from `reader` and checks for specified `val`ue. When data contains specified value -///or `deadline` is reached, stops reading. Returns read data as array of two vectors: elements -///before and after the `val`. -pub fn copy_until(reader: &mut R, val: &[u8]) -> Result<[Vec; 2], io::Error> -where - R: Read + ?Sized, -{ - let mut buf = [0; SMALL_BUF_SIZE]; - let mut writer = Vec::with_capacity(SMALL_BUF_SIZE); - let mut split_idx = 0; - - loop { - let len = match reader.read(&mut buf) { - Ok(0) => break, - Ok(len) => len, - Err(ref e) if e.kind() == ErrorKind::Interrupted => continue, - Err(e) => return Err(e), - }; - - writer.write_all(&buf[..len])?; - - if let Some(i) = find_slice(&writer, val) { - split_idx = i; - break; - } - } - - Ok([writer[..split_idx].to_vec(), writer[split_idx..].to_vec()]) -} +const BUF_SIZE: usize = 24 * 1024; +const RECEIVING_TIMEOUT: u64 = 60; +const DEFAULT_REQ_TIMEOUT: u64 = 12 * 60 * 60; ///HTTP request methods #[derive(Debug, PartialEq, Clone, Copy)] @@ -363,101 +290,6 @@ impl<'a> RequestBuilder<'a> { self } - ///Sends HTTP request in these steps: - /// - ///- Writes request message to `stream`. - ///- Writes response's body to `writer`. - ///- Returns response for this request. - /// - ///# Examples - /// - ///HTTP - ///``` - ///use std::{net::TcpStream, convert::TryFrom}; - ///use http_req::{request::RequestBuilder, uri::Uri}; - /// - /// //This address is automatically redirected to HTTPS, so response code will not ever be 200 - ///let addr = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); - ///let mut writer = Vec::new(); - ///let mut stream = TcpStream::connect((addr.host().unwrap(), addr.corr_port())).unwrap(); - /// - ///let response = RequestBuilder::new(&addr) - /// .header("Connection", "Close") - /// .send(&mut stream, &mut writer) - /// .unwrap(); - ///``` - /// - ///HTTPS - ///``` - ///use std::{net::TcpStream, convert::TryFrom}; - ///use http_req::{request::RequestBuilder, tls, uri::Uri}; - /// - ///let addr: Uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); - ///let mut writer = Vec::new(); - /// - ///let stream = TcpStream::connect((addr.host().unwrap(), addr.corr_port())).unwrap(); - ///let mut stream = tls::Config::default() - /// .connect(addr.host().unwrap_or(""), stream) - /// .unwrap(); - /// - ///let response = RequestBuilder::new(&addr) - /// .header("Connection", "Close") - /// .send(&mut stream, &mut writer) - /// .unwrap(); - ///``` - pub fn send(&self, stream: &mut T, writer: &mut U) -> Result - where - T: Write + Read, - U: Write, - { - self.write_msg(stream, &self.parse_msg())?; - - let (res, body_part) = self.read_head(stream)?; - if self.method == Method::HEAD { - return Ok(res); - } - - writer.write_all(&body_part)?; - - if let Some(v) = res.headers().get("Transfer-Encoding") { - if *v == "chunked" { - let mut dechunked = Reader::new(body_part.as_slice().chain(stream)); - io::copy(&mut dechunked, writer)?; - } - - return Ok(res); - } - - if let Some(num_bytes) = res.content_len() { - if num_bytes > 0 { - copy_exact(stream, writer, num_bytes - body_part.len())?; - } - } else { - io::copy(stream, writer)?; - } - - Ok(res) - } - - ///Writes message to `stream` and flushes it - pub fn write_msg(&self, stream: &mut T, msg: &U) -> Result<(), io::Error> - where - T: Write, - U: AsRef<[u8]>, - { - stream.write_all(msg.as_ref())?; - stream.flush()?; - - Ok(()) - } - - ///Reads head of server's response - pub fn read_head(&self, stream: &mut T) -> Result<(Response, Vec), error::Error> { - let [head, body_part] = copy_until(stream, &CR_LF_2)?; - - Ok((Response::from_head(&head)?, body_part)) - } - ///Parses request message for this `RequestBuilder` pub fn parse_msg(&self) -> Vec { let request_line = format!( @@ -507,6 +339,7 @@ pub struct Request<'a> { connect_timeout: Option, read_timeout: Option, write_timeout: Option, + timeout: Duration, root_cert_file_pem: Option<&'a Path>, } @@ -532,6 +365,7 @@ impl<'a> Request<'a> { connect_timeout: Some(Duration::from_secs(60)), read_timeout: Some(Duration::from_secs(60)), write_timeout: Some(Duration::from_secs(60)), + timeout: Duration::from_secs(DEFAULT_REQ_TIMEOUT), root_cert_file_pem: None, } } @@ -750,6 +584,30 @@ impl<'a> Request<'a> { self } + ///Sets timeout on entire request + /// + ///# Examples + ///``` + ///use http_req::{request::Request, uri::Uri}; + ///use std::{time::Duration, convert::TryFrom}; + /// + ///let mut writer = Vec::new(); + ///let uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); + ///const time: Duration = Duration::from_secs(5); + /// + ///let response = Request::new(&uri) + /// .timeout(time) + /// .send(&mut writer) + /// .unwrap(); + ///``` + pub fn timeout(&mut self, timeout: T) -> &mut Self + where + Duration: From, + { + self.timeout = Duration::from(timeout); + self + } + ///Add a file containing the PEM-encoded certificates that should be added in the trusted root store. pub fn root_cert_file_pem(&mut self, file_path: &'a Path) -> &mut Self { self.root_cert_file_pem = Some(file_path); @@ -772,38 +630,21 @@ impl<'a> Request<'a> { ///let response = Request::new(&uri).send(&mut writer).unwrap(); ///``` pub fn send(&self, writer: &mut T) -> Result { - let host = self.inner.uri.host().unwrap_or(""); - let port = self.inner.uri.corr_port(); - let init_msg = self.inner.parse_msg(); - let timeout = Duration::from_secs(10); - let (sender, receiver) = mpsc::channel(); + let init_msg = self.inner.parse_msg(); let mut raw_response_head: Vec = Vec::new(); - let mut stream: Stream = match self.connect_timeout { - Some(timeout) => Stream::Http(connect_timeout(host, port, timeout)?), - None => Stream::Http(TcpStream::connect((host, port))?), - }; - - stream.set_read_timeout(self.read_timeout)?; - stream.set_write_timeout(self.write_timeout)?; - - if self.inner.uri.scheme() == "https" { - let mut cnf = tls::Config::default(); - let cnf = match self.root_cert_file_pem { - Some(p) => cnf.add_root_cert_file_pem(p)?, - None => &mut cnf, - }; - - if let Stream::Http(inner_stream) = stream { - stream = Stream::Https(cnf.connect(host, inner_stream)?); - } - } - + let mut stream = Stream::default( + self.inner.uri, + self.connect_timeout, + self.read_timeout, + self.write_timeout, + self.root_cert_file_pem, + )?; stream.write_all(&init_msg)?; thread::spawn(move || loop { - let mut buf = [0; BUF_SIZE]; + let mut buf = vec![0; BUF_SIZE]; match stream.read(&mut buf) { Ok(0) => break, @@ -813,29 +654,28 @@ impl<'a> Request<'a> { }); let start_time = Instant::now(); - loop { - let now = Instant::now(); - - if start_time + timeout > now { - let data_read = receiver.recv_timeout(timeout)?; - - if let Some(i) = find_slice(&data_read, &CR_LF_2) { - raw_response_head.write_all(&data_read[..i])?; - writer.write_all(&data_read[i..])?; - break; - } else { - raw_response_head.write_all(&data_read)?; - } - } - } + let reciving_timeout = Duration::from_secs(RECEIVING_TIMEOUT); + let mut is_head = true; loop { let now = Instant::now(); - if start_time + timeout > now { - match receiver.recv_timeout(timeout) { - Ok(data_read) => writer.write_all(&data_read)?, + if start_time + self.timeout > now { + let data_read = match receiver.recv_timeout(reciving_timeout) { + Ok(data) => data, Err(_) => break, + }; + + if is_head { + if let Some(i) = find_slice(&data_read, &CR_LF_2) { + raw_response_head.write_all(&data_read[..i])?; + writer.write_all(&data_read[i..])?; + is_head = false; + } else { + raw_response_head.write_all(&data_read)?; + } + } else { + writer.write_all(&data_read)?; } } } @@ -844,37 +684,6 @@ impl<'a> Request<'a> { } } -///Connects to target host with a timeout -pub fn connect_timeout(host: T, port: u16, timeout: U) -> io::Result -where - Duration: From, - T: AsRef, -{ - let host = host.as_ref(); - let timeout = Duration::from(timeout); - let addrs: Vec<_> = (host, port).to_socket_addrs()?.collect(); - let count = addrs.len(); - - for (idx, addr) in addrs.into_iter().enumerate() { - match TcpStream::connect_timeout(&addr, timeout) { - Ok(stream) => return Ok(stream), - Err(err) => match err.kind() { - io::ErrorKind::TimedOut => return Err(err), - _ => { - if idx + 1 == count { - return Err(err); - } - } - }, - }; - } - - Err(io::Error::new( - io::ErrorKind::AddrNotAvailable, - format!("Could not resolve address for {:?}", host), - )) -} - ///Creates and sends GET request. Returns response for this request. /// ///# Examples @@ -938,7 +747,7 @@ pub fn post, U: Write>( mod tests { use super::*; use crate::{error::Error, response::StatusCode}; - use std::io::Cursor; + use std::io; const UNSUCCESS_CODE: StatusCode = StatusCode::new(400); const URI: &str = "http://doc.rust-lang.org/std/string/index.html"; @@ -956,38 +765,6 @@ mod tests { Content-Type: text/html\r\n\ Content-Length: 100\r\n\r\n"; - #[test] - fn counter_new() { - let counter = Counter::new(200); - - assert_eq!(counter.count, 0); - assert_eq!(counter.stop, 200); - } - - #[test] - fn counter_next() { - let mut counter = Counter::new(5); - - assert_eq!(counter.next(), Some(false)); - assert_eq!(counter.next(), Some(false)); - assert_eq!(counter.next(), Some(false)); - assert_eq!(counter.next(), Some(false)); - assert_eq!(counter.next(), Some(true)); - assert_eq!(counter.next(), Some(false)); - assert_eq!(counter.next(), Some(false)); - } - - #[test] - fn copy_data_until() { - let mut reader = Vec::new(); - reader.extend(&RESPONSE[..]); - - let mut reader = Cursor::new(reader); - - let [head, _body] = copy_until(&mut reader, &CR_LF_2).unwrap(); - assert_eq!(&head[..], &RESPONSE_H[..]); - } - #[test] fn method_display() { const METHOD: Method = Method::HEAD; @@ -1049,36 +826,6 @@ mod tests { assert_eq!(req.body, Some(BODY.as_ref())); } - #[ignore] - #[test] - fn request_b_send() { - let mut writer = Vec::new(); - let uri = Uri::try_from(URI).unwrap(); - let mut stream = TcpStream::connect((uri.host().unwrap_or(""), uri.corr_port())).unwrap(); - - RequestBuilder::new(&Uri::try_from(URI).unwrap()) - .header("Connection", "Close") - .send(&mut stream, &mut writer) - .unwrap(); - } - - #[ignore] - #[test] - fn request_b_send_secure() { - let mut writer = Vec::new(); - let uri = Uri::try_from(URI_S).unwrap(); - - let stream = TcpStream::connect((uri.host().unwrap_or(""), uri.corr_port())).unwrap(); - let mut secure_stream = tls::Config::default() - .connect(uri.host().unwrap_or(""), stream) - .unwrap(); - - RequestBuilder::new(&Uri::try_from(URI_S).unwrap()) - .header("Connection", "Close") - .send(&mut secure_stream, &mut writer) - .unwrap(); - } - #[test] fn request_b_parse_msg() { let uri = Uri::try_from(URI).unwrap(); diff --git a/src/stream.rs b/src/stream.rs index 368f537..a4835ff 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -1,7 +1,14 @@ -use crate::error::Error; -use std::io; -use std::net::TcpStream; -use std::time::Duration; +use crate::{ + error::Error, + tls, + uri::Uri, +}; +use std::{ + io, + net::{TcpStream, ToSocketAddrs}, + path::Path, + time::Duration, +}; use crate::tls::Conn; @@ -11,6 +18,42 @@ pub enum Stream { } impl Stream { + pub fn default( + uri: &Uri, + connect_timeout: Option, + read_timeout: Option, + write_timeout: Option, + root_cert_file_pem: Option<&Path>, + ) -> Result { + let host = uri.host().unwrap_or(""); + let port = uri.corr_port(); + let scheme = uri.scheme(); + + let mut stream = new_http(host, port, connect_timeout)?; + stream.set_read_timeout(read_timeout)?; + stream.set_write_timeout(write_timeout)?; + + if scheme == "https" { + if let Stream::Http(inner_stream) = stream { + stream = to_https(inner_stream, host, root_cert_file_pem)?; + }; + }; + + Ok(stream) + } + + /*pub fn new(uri: &Uri, connect_timeout: Option) -> Result { + let host = uri.host().unwrap_or(""); + let port = uri.corr_port(); + + let stream = match connect_timeout { + Some(timeout) => connect_with_timeout(host, port, timeout)?, + None => TcpStream::connect((host, port))?, + }; + + Ok(Stream::Http(stream)) + }*/ + pub fn set_read_timeout(&mut self, dur: Option) -> Result<(), Error> { match self { Stream::Http(stream) => Ok(stream.set_read_timeout(dur)?), @@ -49,3 +92,59 @@ impl io::Write for Stream { } } } + +pub fn new_http(host: &str, port: u16, connect_timeout: Option) -> Result { + let stream = match connect_timeout { + Some(timeout) => connect_with_timeout(host, port, timeout)?, + None => TcpStream::connect((host, port))?, + }; + + Ok(Stream::Http(stream)) +} + +pub fn to_https( + http_stream: TcpStream, + host: &str, + root_cert_file_pem: Option<&Path>, +) -> Result { + let mut cnf = tls::Config::default(); + + let cnf = match root_cert_file_pem { + Some(p) => cnf.add_root_cert_file_pem(p)?, + None => &mut cnf, + }; + + let stream = cnf.connect(host, http_stream)?; + Ok(Stream::Https(stream)) +} + +///Connects to target host with a timeout +pub fn connect_with_timeout(host: T, port: u16, timeout: U) -> io::Result +where + Duration: From, + T: AsRef, +{ + let host = host.as_ref(); + let timeout = Duration::from(timeout); + let addrs: Vec<_> = (host, port).to_socket_addrs()?.collect(); + let count = addrs.len(); + + for (idx, addr) in addrs.into_iter().enumerate() { + match TcpStream::connect_timeout(&addr, timeout) { + Ok(stream) => return Ok(stream), + Err(err) => match err.kind() { + io::ErrorKind::TimedOut => return Err(err), + _ => { + if idx + 1 == count { + return Err(err); + } + } + }, + }; + } + + Err(io::Error::new( + io::ErrorKind::AddrNotAvailable, + format!("Could not resolve address for {:?}", host), + )) +} From 9f48e923ce1adcee83de91d6c3e2adf11931aee4 Mon Sep 17 00:00:00 2001 From: jayjamesjay Date: Sat, 22 Jun 2024 08:09:27 +0200 Subject: [PATCH 33/47] update stream & fix examples --- examples/get.rs | 22 +--- examples/request_builder_get.rs | 50 ++++++--- src/request.rs | 177 ++++++++++---------------------- src/stream.rs | 86 ++++++---------- 4 files changed, 119 insertions(+), 216 deletions(-) diff --git a/examples/get.rs b/examples/get.rs index 22f6abb..a14c8a7 100644 --- a/examples/get.rs +++ b/examples/get.rs @@ -1,28 +1,14 @@ use http_req::request; -use http_req::{request::Request, response::StatusCode, uri::Uri}; -use std::convert::TryFrom; -use std::fs::File; fn main() { //Container for body of a response. - //let mut body = Vec::new(); + let mut body = Vec::new(); //Sends a HTTP GET request and processes the response. Saves body of the response to `body` variable. - //let res = request::get("https://drivers.amd.com/drivers/installer/23.40/whql/amd-software-adrenalin-edition-24.5.1-minimalsetup-240514_web.exe", &mut body).unwrap(); + let res = request::get("https://www.rust-lang.org/learn", &mut body).unwrap(); //Prints details about the response. - //println!("Status: {} {}", res.status_code(), res.reason()); - //println!("Headers: {}", res.headers()); + println!("Status: {} {}", res.status_code(), res.reason()); + println!("Headers: {}", res.headers()); //println!("{}", String::from_utf8_lossy(&body)); - - let mut writer = File::create("boo.txt").unwrap(); - let uri = Uri::try_from("https://drivers.amd.com/drivers/whql-amd-software-adrenalin-edition-24.5.1-win10-win11-may15-rdna.exe").unwrap(); - - let response = Request::new(&uri) - .header("Referer", &uri) - .send(&mut writer) - .unwrap(); - - println!("Status: {} {}", response.status_code(), response.reason()); - println!("Headers: {}", response.headers()); } diff --git a/examples/request_builder_get.rs b/examples/request_builder_get.rs index 75c65a4..a31a85e 100644 --- a/examples/request_builder_get.rs +++ b/examples/request_builder_get.rs @@ -1,28 +1,44 @@ -use http_req::{request::RequestBuilder, tls, uri::Uri}; -use std::{convert::TryFrom, net::TcpStream}; +use http_req::{ + request::RequestBuilder, + response::{find_slice, Response}, + stream::Stream, + uri::Uri, +}; +use std::{ + convert::TryFrom, + io::{Read, Write}, + time::Duration, +}; fn main() { //Parses a URI and assigns it to a variable `addr`. let addr: Uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); + //Prepare a request + let mut request_builder = RequestBuilder::new(&addr); + request_builder.header("Connection", "Close"); + + //Container for a server's response. + let mut writer = Vec::new(); + //Connects to a remote host. Uses information from `addr`. - let stream = TcpStream::connect((addr.host().unwrap(), addr.corr_port())).unwrap(); + let mut stream = Stream::new(&addr, Some(Duration::from_secs(60))).unwrap(); + stream = Stream::try_to_https(stream, &addr, None).unwrap(); - //Opens a secure connection over TlsStream. This is required due to use of `https` protocol. - let mut stream = tls::Config::default() - .connect(addr.host().unwrap_or(""), stream) - .unwrap(); + //Generate a request (message) and send it to server. + let request_msg = request_builder.parse_msg(); + stream.write_all(&request_msg).unwrap(); - //Container for a response's body. - //let mut writer = Vec::new(); + //Read response from the server and save it to writer + stream.read_to_end(&mut writer).unwrap(); - //Adds a header `Connection: Close`. - let response = RequestBuilder::new(&addr) - .header("Connection", "Close"); - // .send(&mut stream, &mut writer) - // .unwrap(); + //Parse and process response. + let pos = find_slice(&writer, &[13, 10, 13, 10].to_owned()).unwrap(); + let response = Response::from_head(&writer[..pos]).unwrap(); + let body = writer[pos..].to_vec(); - //println!("Status: {} {}", response.status_code(), response.reason()); - //println!("Headers: {}", response.headers()); - //println!("{}", String::from_utf8_lossy(&writer)); + //Print infromation about the response. + println!("Status: {} {}", response.status_code(), response.reason()); + println!("Headers: {}", response.headers()); + //println!("{}", String::from_utf8_lossy(&body)); } diff --git a/src/request.rs b/src/request.rs index 51f5a78..2f95658 100644 --- a/src/request.rs +++ b/src/request.rs @@ -82,21 +82,24 @@ impl fmt::Display for HttpVersion { /// ///# Examples ///``` -///use std::{net::TcpStream, convert::TryFrom}; -///use http_req::{request::RequestBuilder, tls, uri::Uri, response::StatusCode}; +///use http_req::{request::RequestBuilder, response::{find_slice, Response, StatusCode}, stream::Stream, uri::Uri}; +///use std::{convert::TryFrom, io::{Read, Write}, time::Duration}; /// ///let addr: Uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); ///let mut writer = Vec::new(); /// -///let stream = TcpStream::connect((addr.host().unwrap(), addr.corr_port())).unwrap(); -///let mut stream = tls::Config::default() -/// .connect(addr.host().unwrap_or(""), stream) -/// .unwrap(); +///let mut request_builder = RequestBuilder::new(&addr); +///request_builder.header("Connection", "Close"); +///let request_msg = request_builder.parse_msg(); /// -///let response = RequestBuilder::new(&addr) -/// .header("Connection", "Close") -/// .send(&mut stream, &mut writer) -/// .unwrap(); +///let mut stream = Stream::new(&addr, Some(Duration::from_secs(60))).unwrap(); +///stream = Stream::try_to_https(stream, &addr, None).unwrap(); +///stream.write_all(&request_msg).unwrap(); +///stream.read_to_end(&mut writer).unwrap(); +/// +///let pos = find_slice(&writer, &[13, 10, 13, 10].to_owned()).unwrap(); +///let response = Response::from_head(&writer[..pos]).unwrap(); +///let body = writer[pos..].to_vec(); /// ///assert_eq!(response.status_code(), StatusCode::new(200)); ///``` @@ -114,21 +117,13 @@ impl<'a> RequestBuilder<'a> { /// ///# Examples ///``` - ///use std::{net::TcpStream, convert::TryFrom}; - ///use http_req::{request::RequestBuilder, tls, uri::Uri}; + ///use std::convert::TryFrom; + ///use http_req::{request::RequestBuilder, uri::Uri}; /// ///let addr = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); - ///let mut writer = Vec::new(); /// - ///let stream = TcpStream::connect((addr.host().unwrap(), addr.corr_port())).unwrap(); - ///let mut stream = tls::Config::default() - /// .connect(addr.host().unwrap_or(""), stream) - /// .unwrap(); - /// - ///let response = RequestBuilder::new(&addr) - /// .header("Connection", "Close") - /// .send(&mut stream, &mut writer) - /// .unwrap(); + ///let request_builder = RequestBuilder::new(&addr) + /// .header("Connection", "Close"); ///``` pub fn new(uri: &'a Uri<'a>) -> RequestBuilder<'a> { RequestBuilder { @@ -144,22 +139,13 @@ impl<'a> RequestBuilder<'a> { /// ///# Examples ///``` - ///use std::{net::TcpStream, convert::TryFrom}; - ///use http_req::{request::{RequestBuilder, Method}, tls, uri::Uri}; - /// - ///let addr= Uri::try_from("https://www.rust-lang.org/learn").unwrap(); - ///let mut writer = Vec::new(); + ///use std::convert::TryFrom; + ///use http_req::{request::{RequestBuilder, Method}, uri::Uri}; /// - ///let stream = TcpStream::connect((addr.host().unwrap(), addr.corr_port())).unwrap(); - ///let mut stream = tls::Config::default() - /// .connect(addr.host().unwrap_or(""), stream) - /// .unwrap(); + ///let addr = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); /// - ///let response = RequestBuilder::new(&addr) - /// .method(Method::HEAD) - /// .header("Connection", "Close") - /// .send(&mut stream, &mut writer) - /// .unwrap(); + ///let request_builder = RequestBuilder::new(&addr) + /// .method(Method::HEAD); ///``` pub fn method(&mut self, method: T) -> &mut Self where @@ -173,24 +159,14 @@ impl<'a> RequestBuilder<'a> { /// ///# Examples ///``` - ///use std::{net::TcpStream, convert::TryFrom}; - ///use http_req::{request::{RequestBuilder, HttpVersion}, tls, uri::Uri}; + ///use std::convert::TryFrom; + ///use http_req::{request::{RequestBuilder, HttpVersion}, uri::Uri}; /// ///let addr = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); - ///let mut writer = Vec::new(); - /// - ///let stream = TcpStream::connect((addr.host().unwrap(), addr.corr_port())).unwrap(); - ///let mut stream = tls::Config::default() - /// .connect(addr.host().unwrap_or(""), stream) - /// .unwrap(); /// - ///let response = RequestBuilder::new(&addr) - /// .version(HttpVersion::Http10) - /// .header("Connection", "Close") - /// .send(&mut stream, &mut writer) - /// .unwrap(); + ///let request_builder = RequestBuilder::new(&addr) + /// .version(HttpVersion::Http10); ///``` - pub fn version(&mut self, version: T) -> &mut Self where HttpVersion: From, @@ -203,26 +179,19 @@ impl<'a> RequestBuilder<'a> { /// ///# Examples ///``` - ///use std::{net::TcpStream, convert::TryFrom}; - ///use http_req::{request::{RequestBuilder, Method}, response::Headers, tls, uri::Uri}; + ///use std::convert::TryFrom; + ///use http_req::{request::RequestBuilder, response::Headers, uri::Uri}; /// ///let addr = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); - ///let mut writer = Vec::new(); + /// ///let mut headers = Headers::new(); ///headers.insert("Accept-Charset", "utf-8"); ///headers.insert("Accept-Language", "en-US"); ///headers.insert("Host", "rust-lang.org"); ///headers.insert("Connection", "Close"); /// - ///let stream = TcpStream::connect((addr.host().unwrap(), addr.corr_port())).unwrap(); - ///let mut stream = tls::Config::default() - /// .connect(addr.host().unwrap_or(""), stream) - /// .unwrap(); - /// - ///let response = RequestBuilder::new(&addr) - /// .headers(headers) - /// .send(&mut stream, &mut writer) - /// .unwrap(); + ///let request_builder = RequestBuilder::new(&addr) + /// .headers(headers); ///``` pub fn headers(&mut self, headers: T) -> &mut Self where @@ -236,21 +205,13 @@ impl<'a> RequestBuilder<'a> { /// ///# Examples ///``` - ///use std::{net::TcpStream, convert::TryFrom}; - ///use http_req::{request::{RequestBuilder, Method}, tls, uri::Uri}; - /// - ///let addr: Uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); - ///let mut writer = Vec::new(); + ///use std::convert::TryFrom; + ///use http_req::{request::RequestBuilder, response::Headers, uri::Uri}; /// - ///let stream = TcpStream::connect((addr.host().unwrap(), addr.corr_port())).unwrap(); - ///let mut stream = tls::Config::default() - /// .connect(addr.host().unwrap_or(""), stream) - /// .unwrap(); + ///let addr = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); /// - ///let response = RequestBuilder::new(&addr) - /// .header("Connection", "Close") - /// .send(&mut stream, &mut writer) - /// .unwrap(); + ///let request_builder = RequestBuilder::new(&addr) + /// .header("Connection", "Close"); ///``` pub fn header(&mut self, key: &T, val: &U) -> &mut Self where @@ -265,25 +226,17 @@ impl<'a> RequestBuilder<'a> { /// ///# Examples ///``` - ///use std::{net::TcpStream, convert::TryFrom}; - ///use http_req::{request::{RequestBuilder, Method}, tls, uri::Uri}; + ///use std::convert::TryFrom; + ///use http_req::{request::{RequestBuilder, Method}, response::Headers, uri::Uri}; /// ///let addr = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); - ///const body: &[u8; 27] = b"field1=value1&field2=value2"; - ///let mut writer = Vec::new(); + ///const BODY: &[u8; 27] = b"field1=value1&field2=value2"; /// - ///let stream = TcpStream::connect((addr.host().unwrap(), addr.corr_port())).unwrap(); - ///let mut stream = tls::Config::default() - /// .connect(addr.host().unwrap_or(""), stream) - /// .unwrap(); - /// - ///let response = RequestBuilder::new(&addr) + ///let request_builder = RequestBuilder::new(&addr) /// .method(Method::POST) - /// .body(body) - /// .header("Content-Length", &body.len()) - /// .header("Connection", "Close") - /// .send(&mut stream, &mut writer) - /// .unwrap(); + /// .body(BODY) + /// .header("Content-Length", &BODY.len()) + /// .header("Connection", "Close"); ///``` pub fn body(&mut self, body: &'a [u8]) -> &mut Self { self.body = Some(body); @@ -631,16 +584,14 @@ impl<'a> Request<'a> { ///``` pub fn send(&self, writer: &mut T) -> Result { let (sender, receiver) = mpsc::channel(); - let init_msg = self.inner.parse_msg(); let mut raw_response_head: Vec = Vec::new(); + let init_msg = self.inner.parse_msg(); - let mut stream = Stream::default( - self.inner.uri, - self.connect_timeout, - self.read_timeout, - self.write_timeout, - self.root_cert_file_pem, - )?; + let mut stream = Stream::new(self.inner.uri, self.connect_timeout)?; + stream.set_read_timeout(self.read_timeout)?; + stream.set_write_timeout(self.write_timeout)?; + + stream = Stream::try_to_https(stream, self.inner.uri, self.root_cert_file_pem)?; stream.write_all(&init_msg)?; thread::spawn(move || loop { @@ -648,7 +599,7 @@ impl<'a> Request<'a> { match stream.read(&mut buf) { Ok(0) => break, - Ok(_) => sender.send(buf).unwrap(), + Ok(count) => sender.send(buf[..count].to_owned()).unwrap(), Err(_) => break, } }); @@ -754,17 +705,6 @@ mod tests { const URI_S: &str = "https://doc.rust-lang.org/std/string/index.html"; const BODY: [u8; 14] = [78, 97, 109, 101, 61, 74, 97, 109, 101, 115, 43, 74, 97, 121]; - const RESPONSE: &[u8; 129] = b"HTTP/1.1 200 OK\r\n\ - Date: Sat, 11 Jan 2003 02:44:04 GMT\r\n\ - Content-Type: text/html\r\n\ - Content-Length: 100\r\n\r\n\ - hello\r\n\r\nhello"; - - const RESPONSE_H: &[u8; 102] = b"HTTP/1.1 200 OK\r\n\ - Date: Sat, 11 Jan 2003 02:44:04 GMT\r\n\ - Content-Type: text/html\r\n\ - Content-Length: 100\r\n\r\n"; - #[test] fn method_display() { const METHOD: Method = Method::HEAD; @@ -916,26 +856,13 @@ mod tests { }; } - #[ignore] #[test] fn request_read_timeout() { let uri = Uri::try_from(URI).unwrap(); let mut request = Request::new(&uri); - request.read_timeout(Some(Duration::from_nanos(1))); - - assert_eq!(request.read_timeout, Some(Duration::from_nanos(1))); + request.read_timeout(Some(Duration::from_nanos(100))); - let err = request.send(&mut io::sink()).unwrap_err(); - match err { - Error::IO(err) => match err.kind() { - io::ErrorKind::WouldBlock | io::ErrorKind::TimedOut => {} - other => panic!( - "Expected error kind to be one of WouldBlock/TimedOut, got: {:?}", - other - ), - }, - other => panic!("Expected error to be io::Error, got: {:?}", other), - }; + assert_eq!(request.read_timeout, Some(Duration::from_nanos(100))); } #[test] diff --git a/src/stream.rs b/src/stream.rs index a4835ff..e9b99ab 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -1,8 +1,4 @@ -use crate::{ - error::Error, - tls, - uri::Uri, -}; +use crate::{error::Error, tls, uri::Uri}; use std::{ io, net::{TcpStream, ToSocketAddrs}, @@ -15,34 +11,11 @@ use crate::tls::Conn; pub enum Stream { Http(TcpStream), Https(Conn), + //Custom(R) } impl Stream { - pub fn default( - uri: &Uri, - connect_timeout: Option, - read_timeout: Option, - write_timeout: Option, - root_cert_file_pem: Option<&Path>, - ) -> Result { - let host = uri.host().unwrap_or(""); - let port = uri.corr_port(); - let scheme = uri.scheme(); - - let mut stream = new_http(host, port, connect_timeout)?; - stream.set_read_timeout(read_timeout)?; - stream.set_write_timeout(write_timeout)?; - - if scheme == "https" { - if let Stream::Http(inner_stream) = stream { - stream = to_https(inner_stream, host, root_cert_file_pem)?; - }; - }; - - Ok(stream) - } - - /*pub fn new(uri: &Uri, connect_timeout: Option) -> Result { + pub fn new(uri: &Uri, connect_timeout: Option) -> Result { let host = uri.host().unwrap_or(""); let port = uri.corr_port(); @@ -52,7 +25,33 @@ impl Stream { }; Ok(Stream::Http(stream)) - }*/ + } + + pub fn try_to_https( + stream: Stream, + uri: &Uri, + root_cert_file_pem: Option<&Path>, + ) -> Result { + match stream { + Stream::Http(http_stream) => { + if uri.scheme() == "https" { + let host = uri.host().unwrap_or(""); + let mut cnf = tls::Config::default(); + + let cnf = match root_cert_file_pem { + Some(p) => cnf.add_root_cert_file_pem(p)?, + None => &mut cnf, + }; + + let stream = cnf.connect(host, http_stream)?; + Ok(Stream::Https(stream)) + } else { + Ok(Stream::Http(http_stream)) + } + } + Stream::Https(_) => Ok(stream), + } + } pub fn set_read_timeout(&mut self, dur: Option) -> Result<(), Error> { match self { @@ -93,31 +92,6 @@ impl io::Write for Stream { } } -pub fn new_http(host: &str, port: u16, connect_timeout: Option) -> Result { - let stream = match connect_timeout { - Some(timeout) => connect_with_timeout(host, port, timeout)?, - None => TcpStream::connect((host, port))?, - }; - - Ok(Stream::Http(stream)) -} - -pub fn to_https( - http_stream: TcpStream, - host: &str, - root_cert_file_pem: Option<&Path>, -) -> Result { - let mut cnf = tls::Config::default(); - - let cnf = match root_cert_file_pem { - Some(p) => cnf.add_root_cert_file_pem(p)?, - None => &mut cnf, - }; - - let stream = cnf.connect(host, http_stream)?; - Ok(Stream::Https(stream)) -} - ///Connects to target host with a timeout pub fn connect_with_timeout(host: T, port: u16, timeout: U) -> io::Result where From 038f10dc60d4888dfdfaa6656fa90e40058b1a69 Mon Sep 17 00:00:00 2001 From: jayjamesjay Date: Sat, 22 Jun 2024 20:51:56 +0200 Subject: [PATCH 34/47] improve reading from stream --- src/request.rs | 91 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 64 insertions(+), 27 deletions(-) diff --git a/src/request.rs b/src/request.rs index 2f95658..07c14fe 100644 --- a/src/request.rs +++ b/src/request.rs @@ -1,13 +1,13 @@ //! creating and sending HTTP requests use crate::{ error, - response::{find_slice, Headers, Response, CR_LF_2}, + response::{Headers, Response}, stream::Stream, uri::Uri, }; use std::{ fmt, - io::{Read, Write}, + io::{BufRead, BufReader, Read, Write}, path::Path, sync::mpsc, thread, @@ -15,7 +15,7 @@ use std::{ }; const CR_LF: &str = "\r\n"; -const BUF_SIZE: usize = 24 * 1024; +const BUF_SIZE: usize = 1024 * 1024; const RECEIVING_TIMEOUT: u64 = 60; const DEFAULT_REQ_TIMEOUT: u64 = 12 * 60 * 60; @@ -583,55 +583,92 @@ impl<'a> Request<'a> { ///let response = Request::new(&uri).send(&mut writer).unwrap(); ///``` pub fn send(&self, writer: &mut T) -> Result { + //Set up stream let (sender, receiver) = mpsc::channel(); let mut raw_response_head: Vec = Vec::new(); - let init_msg = self.inner.parse_msg(); + let request_msg = self.inner.parse_msg(); let mut stream = Stream::new(self.inner.uri, self.connect_timeout)?; stream.set_read_timeout(self.read_timeout)?; stream.set_write_timeout(self.write_timeout)?; - stream = Stream::try_to_https(stream, self.inner.uri, self.root_cert_file_pem)?; - stream.write_all(&init_msg)?; + stream.write_all(&request_msg)?; - thread::spawn(move || loop { - let mut buf = vec![0; BUF_SIZE]; + //Read the response + let mut buf_reader = BufReader::new(stream); + let deadline = Instant::now() + self.timeout; + let reciving_timeout = Duration::from_secs(RECEIVING_TIMEOUT); - match stream.read(&mut buf) { - Ok(0) => break, - Ok(count) => sender.send(buf[..count].to_owned()).unwrap(), - Err(_) => break, + thread::spawn(move || { + loop { + let mut buf = Vec::new(); + match buf_reader.read_until(0xA, &mut buf) { + Ok(0) => break, + Ok(len) => { + let filled_buf = buf[..len].to_owned(); + sender.send(filled_buf).unwrap(); + + if len == 2 && buf == CR_LF.as_bytes() { + break; + } + } + Err(_) => break, + } } - }); - let start_time = Instant::now(); - let reciving_timeout = Duration::from_secs(RECEIVING_TIMEOUT); - let mut is_head = true; + loop { + let mut buf = vec![0; BUF_SIZE]; + match buf_reader.read(&mut buf) { + Ok(0) => break, + Ok(len) => { + let filled_buf = buf[..len].to_owned(); + sender.send(filled_buf).unwrap(); + } + Err(_) => break, + } + } + }); loop { let now = Instant::now(); - if start_time + self.timeout > now { + if deadline > now { let data_read = match receiver.recv_timeout(reciving_timeout) { Ok(data) => data, Err(_) => break, }; - if is_head { - if let Some(i) = find_slice(&data_read, &CR_LF_2) { - raw_response_head.write_all(&data_read[..i])?; - writer.write_all(&data_read[i..])?; - is_head = false; - } else { - raw_response_head.write_all(&data_read)?; - } - } else { + if data_read == CR_LF.as_bytes() { + break; + } + + raw_response_head.write_all(&data_read)?; + } else { + break; + } + } + + let response = Response::from_head(&raw_response_head)?; + let content_len = response.content_len().unwrap_or(1); + + if content_len > 0 { + loop { + let now = Instant::now(); + + if deadline > now { + let data_read = match receiver.recv_timeout(reciving_timeout) { + Ok(data) => data, + Err(_) => break, + }; + writer.write_all(&data_read)?; + } else { + break; } } } - Response::from_head(&raw_response_head) + Ok(response) } } From 666c218d3b8164214ca2fc776076001aade4999f Mon Sep 17 00:00:00 2001 From: jayjamesjay Date: Mon, 24 Jun 2024 09:28:05 +0200 Subject: [PATCH 35/47] refactor and document request module --- examples/chunked.rs | 12 ++ examples/request_builder_get.rs | 58 ++++--- src/chunked.rs | 49 ++++-- src/lib.rs | 7 +- src/request.rs | 260 ++++++++++++++------------------ src/response.rs | 4 +- src/stream.rs | 115 +++++++++++++- 7 files changed, 308 insertions(+), 197 deletions(-) create mode 100644 examples/chunked.rs diff --git a/examples/chunked.rs b/examples/chunked.rs new file mode 100644 index 0000000..99c490a --- /dev/null +++ b/examples/chunked.rs @@ -0,0 +1,12 @@ +use http_req::request; + +fn main() { + //Sends a HTTP GET request and processes the response. + let mut body = Vec::new(); + let res = request::get("https://jigsaw.w3.org/HTTP/ChunkedScript", &mut body).unwrap(); + + //Prints details about the response. + println!("Status: {} {}", res.status_code(), res.reason()); + println!("Headers: {}", res.headers()); + //println!("{}", String::from_utf8_lossy(&body)); +} diff --git a/examples/request_builder_get.rs b/examples/request_builder_get.rs index a31a85e..ac72597 100644 --- a/examples/request_builder_get.rs +++ b/examples/request_builder_get.rs @@ -1,12 +1,7 @@ -use http_req::{ - request::RequestBuilder, - response::{find_slice, Response}, - stream::Stream, - uri::Uri, -}; +use http_req::{request::RequestBuilder, response::Response, stream::Stream, uri::Uri}; use std::{ convert::TryFrom, - io::{Read, Write}, + io::{BufRead, BufReader, Read, Write}, time::Duration, }; @@ -14,30 +9,45 @@ fn main() { //Parses a URI and assigns it to a variable `addr`. let addr: Uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); - //Prepare a request - let mut request_builder = RequestBuilder::new(&addr); - request_builder.header("Connection", "Close"); + //Containers for a server's response. + let mut raw_head = Vec::new(); + let mut body = Vec::new(); + + //Prepares a request message. + let request_msg = RequestBuilder::new(&addr) + .header("Connection", "Close") + .parse(); - //Container for a server's response. - let mut writer = Vec::new(); + println!("{:?}", String::from_utf8(request_msg.clone())); - //Connects to a remote host. Uses information from `addr`. + //Connects to a server. Uses information from `addr`. let mut stream = Stream::new(&addr, Some(Duration::from_secs(60))).unwrap(); stream = Stream::try_to_https(stream, &addr, None).unwrap(); - //Generate a request (message) and send it to server. - let request_msg = request_builder.parse_msg(); + //Makes a request to server - sends a prepared message. stream.write_all(&request_msg).unwrap(); - //Read response from the server and save it to writer - stream.read_to_end(&mut writer).unwrap(); - - //Parse and process response. - let pos = find_slice(&writer, &[13, 10, 13, 10].to_owned()).unwrap(); - let response = Response::from_head(&writer[..pos]).unwrap(); - let body = writer[pos..].to_vec(); - - //Print infromation about the response. + //Wraps the stream in BufReader to make it easier to read from it. + //Reads a response from the server and saves the head to `raw_head`, and the body to `body`. + let mut stream = BufReader::new(stream); + loop { + match stream.read_until(0xA, &mut raw_head) { + Ok(0) | Err(_) => break, + Ok(len) => { + let full_len = raw_head.len(); + + if len == 2 && &raw_head[full_len - 2..] == b"\r\n" { + break; + } + } + } + } + stream.read_to_end(&mut body).unwrap(); + + //Parses and processes the response. + let response = Response::from_head(&raw_head).unwrap(); + + //Prints infromation about the response. println!("Status: {} {}", response.status_code(), response.reason()); println!("Headers: {}", response.headers()); //println!("{}", String::from_utf8_lossy(&body)); diff --git a/src/chunked.rs b/src/chunked.rs index ffc3e4d..c17d6f1 100644 --- a/src/chunked.rs +++ b/src/chunked.rs @@ -1,15 +1,16 @@ -//! module chunked implements the wire protocol for HTTP's "chunked" Transfer-Encoding. -//! And it's a rust version of the reference implementation in [Go][1]. -//! -//! [1]: https://golang.google.cn/src/net/http/internal/chunked.go -//! +//!implements the wire protocol for HTTP's "chunked" Transfer-Encoding. +/// +///It's a Rust version of the reference implementation in [Go][1]. +/// +///[1]: https://golang.google.cn/src/net/http/internal/chunked.go +/// use std::io::{self, BufRead, BufReader, Error, ErrorKind, Read}; const MAX_LINE_LENGTH: usize = 4096; const CR_LF: [u8; 2] = [b'\r', b'\n']; -pub struct Reader { +pub struct ChunkReader { check_end: bool, eof: bool, err: Option, @@ -17,7 +18,7 @@ pub struct Reader { reader: BufReader, } -impl Read for Reader +impl Read for ChunkReader where R: Read, { @@ -93,7 +94,29 @@ where } } -impl Reader +impl BufRead for ChunkReader { + fn fill_buf(&mut self) -> io::Result<&[u8]> { + self.reader.fill_buf() + } + + fn consume(&mut self, amt: usize) { + self.reader.consume(amt) + } +} + +impl From> for ChunkReader { + fn from(value: BufReader) -> Self { + ChunkReader { + check_end: false, + eof: false, + err: None, + n: 0, + reader: value, + } + } +} + +impl ChunkReader where R: Read, { @@ -216,7 +239,7 @@ mod tests { #[test] fn read() { let data: &[u8] = b"7\r\nhello, \r\n17\r\nworld! 0123456789abcdef\r\n0\r\n"; - let mut reader = Reader::new(data); + let mut reader = ChunkReader::new(data); let mut writer = vec![]; io::copy(&mut reader, &mut writer).expect("failed to dechunk"); @@ -226,7 +249,7 @@ mod tests { fn read_multiple() { { let data: &[u8] = b"3\r\nfoo\r\n3\r\nbar\r\n0\r\n"; - let mut reader = Reader::new(data); + let mut reader = ChunkReader::new(data); let mut writer = vec![0u8; 10]; let n = reader.read(&mut writer).expect("unexpect error"); @@ -235,7 +258,7 @@ mod tests { } { let data: &[u8] = b"3\r\nfoo\r\n0\r\n"; - let mut reader = Reader::new(data); + let mut reader = ChunkReader::new(data); let mut writer = vec![0u8; 3]; let n = reader.read(&mut writer).expect("unexpect error"); @@ -246,7 +269,7 @@ mod tests { #[test] fn read_partial() { let data: &[u8] = b"7\r\n1234567"; - let mut reader = Reader::new(data); + let mut reader = ChunkReader::new(data); let mut writer = vec![]; io::copy(&mut reader, &mut writer).expect("failed to dechunk"); @@ -260,7 +283,7 @@ mod tests { + "world! 0123456789abcdef\r\n" + "0;someextension=sometoken\r\n"; let data = data_str.as_bytes(); - let mut reader = Reader::new(data); + let mut reader = ChunkReader::new(data); let mut writer = vec![]; reader.read_to_end(&mut writer).expect("failed to dechunk"); diff --git a/src/lib.rs b/src/lib.rs index b954e1c..780c359 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,11 +14,10 @@ //! println!("Status: {} {}", res.status_code(), res.reason()); //!} //!``` -pub mod error; +pub mod uri; pub mod request; pub mod response; pub mod stream; +pub mod chunked; pub mod tls; -pub mod uri; - -mod chunked; +pub mod error; diff --git a/src/request.rs b/src/request.rs index 07c14fe..2368b3b 100644 --- a/src/request.rs +++ b/src/request.rs @@ -1,13 +1,14 @@ //! creating and sending HTTP requests use crate::{ + chunked::ChunkReader, error, response::{Headers, Response}, - stream::Stream, + stream::{Stream, ThreadReceive, ThreadSend}, uri::Uri, }; use std::{ fmt, - io::{BufRead, BufReader, Read, Write}, + io::{BufReader, Write}, path::Path, sync::mpsc, thread, @@ -15,8 +16,6 @@ use std::{ }; const CR_LF: &str = "\r\n"; -const BUF_SIZE: usize = 1024 * 1024; -const RECEIVING_TIMEOUT: u64 = 60; const DEFAULT_REQ_TIMEOUT: u64 = 12 * 60 * 60; ///HTTP request methods @@ -75,33 +74,18 @@ impl fmt::Display for HttpVersion { } } -///Relatively low-level struct for making HTTP requests. -/// -///It can work with any stream that implements `Read` and `Write`. -///By default it does not close the connection after completion of the response. +///Raw HTTP request that can be sent to any stream /// ///# Examples ///``` -///use http_req::{request::RequestBuilder, response::{find_slice, Response, StatusCode}, stream::Stream, uri::Uri}; -///use std::{convert::TryFrom, io::{Read, Write}, time::Duration}; +///use std::convert::TryFrom; +///use http_req::{request::RequestBuilder, uri::Uri}; /// ///let addr: Uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); -///let mut writer = Vec::new(); -/// -///let mut request_builder = RequestBuilder::new(&addr); -///request_builder.header("Connection", "Close"); -///let request_msg = request_builder.parse_msg(); -/// -///let mut stream = Stream::new(&addr, Some(Duration::from_secs(60))).unwrap(); -///stream = Stream::try_to_https(stream, &addr, None).unwrap(); -///stream.write_all(&request_msg).unwrap(); -///stream.read_to_end(&mut writer).unwrap(); /// -///let pos = find_slice(&writer, &[13, 10, 13, 10].to_owned()).unwrap(); -///let response = Response::from_head(&writer[..pos]).unwrap(); -///let body = writer[pos..].to_vec(); -/// -///assert_eq!(response.status_code(), StatusCode::new(200)); +///let mut request_msg = RequestBuilder::new(&addr) +/// .header("Connection", "Close") +/// .parse(); ///``` #[derive(Clone, Debug, PartialEq)] pub struct RequestBuilder<'a> { @@ -244,7 +228,22 @@ impl<'a> RequestBuilder<'a> { } ///Parses request message for this `RequestBuilder` - pub fn parse_msg(&self) -> Vec { + /// + ///# Examples + ///``` + ///use std::convert::TryFrom; + ///use http_req::{request::RequestBuilder, uri::Uri}; + /// + ///let addr: Uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); + /// + ///let mut request_msg = RequestBuilder::new(&addr) + /// .header("Connection", "Close") + /// .parse(); + /// + ///let expected_msg = "GET /learn HTTP/1.1\r\nHost: www.rust-lang.org\r\nConnection: Close\r\n\r\n"; + ///assert_eq!(String::from_utf8(request_msg).unwrap(), expected_msg); + ///``` + pub fn parse(&self) -> Vec { let request_line = format!( "{} {} {}{}", self.method, @@ -269,9 +268,9 @@ impl<'a> RequestBuilder<'a> { } } -///Relatively higher-level struct for making HTTP requests. +///Allows for making HTTP requests based on specified parameters. /// -///It creates stream (`TcpStream` or `TlsStream`) appropriate for the type of uri (`http`/`https`) +///It creates stream (`TcpStream` or `TlsStream`) appropriate for the type of uri (`http`/`https`). ///By default it closes connection after completion of the response. /// ///# Examples @@ -297,7 +296,7 @@ pub struct Request<'a> { } impl<'a> Request<'a> { - ///Creates new `Request` with default parameters + ///Creates new `Request` with default parameters. /// ///# Examples ///``` @@ -323,20 +322,17 @@ impl<'a> Request<'a> { } } - ///Sets request method + ///Sets request method. /// ///# Examples ///``` ///use http_req::{request::{Request, Method}, uri::Uri}; ///use std::convert::TryFrom; /// - ///let mut writer = Vec::new(); ///let uri: Uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); /// ///let response = Request::new(&uri) - /// .method(Method::HEAD) - /// .send(&mut writer) - /// .unwrap(); + /// .method(Method::HEAD); ///``` pub fn method(&mut self, method: T) -> &mut Self where @@ -346,20 +342,17 @@ impl<'a> Request<'a> { self } - ///Sets HTTP version + ///Sets HTTP version. /// ///# Examples ///``` ///use http_req::{request::{Request, HttpVersion}, uri::Uri}; ///use std::convert::TryFrom; /// - ///let mut writer = Vec::new(); ///let uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); /// ///let response = Request::new(&uri) - /// .version(HttpVersion::Http10) - /// .send(&mut writer) - /// .unwrap(); + /// .version(HttpVersion::Http10); ///``` pub fn version(&mut self, version: T) -> &mut Self @@ -370,14 +363,13 @@ impl<'a> Request<'a> { self } - ///Replaces all it's headers with headers passed to the function + ///Replaces all it's headers with headers passed to the function. /// ///# Examples ///``` ///use http_req::{request::Request, uri::Uri, response::Headers}; ///use std::convert::TryFrom; /// - ///let mut writer = Vec::new(); ///let uri: Uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); /// ///let mut headers = Headers::new(); @@ -387,9 +379,7 @@ impl<'a> Request<'a> { ///headers.insert("Connection", "Close"); /// ///let response = Request::new(&uri) - /// .headers(headers) - /// .send(&mut writer) - /// .unwrap();; + /// .headers(headers); ///``` pub fn headers(&mut self, headers: T) -> &mut Self where @@ -399,20 +389,17 @@ impl<'a> Request<'a> { self } - ///Adds header to existing/default headers + ///Adds header to existing/default headers. /// ///# Examples ///``` ///use http_req::{request::Request, uri::Uri}; ///use std::convert::TryFrom; /// - ///let mut writer = Vec::new(); ///let uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); /// ///let response = Request::new(&uri) - /// .header("Accept-Language", "en-US") - /// .send(&mut writer) - /// .unwrap(); + /// .header("Accept-Language", "en-US"); ///``` pub fn header(&mut self, key: &T, val: &U) -> &mut Self where @@ -423,30 +410,27 @@ impl<'a> Request<'a> { self } - ///Sets body for request + ///Sets body for request. /// ///# Examples ///``` ///use http_req::{request::{Request, Method}, uri::Uri}; ///use std::convert::TryFrom; /// - ///let mut writer = Vec::new(); ///let uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); ///const body: &[u8; 27] = b"field1=value1&field2=value2"; /// ///let response = Request::new(&uri) /// .method(Method::POST) /// .header("Content-Length", &body.len()) - /// .body(body) - /// .send(&mut writer) - /// .unwrap(); + /// .body(body); ///``` pub fn body(&mut self, body: &'a [u8]) -> &mut Self { self.inner.body(body); self } - ///Sets connect timeout while using internal `TcpStream` instance + ///Sets connect timeout while using internal `TcpStream` instance. /// ///- If there is a timeout, it will be passed to /// [`TcpStream::connect_timeout`][TcpStream::connect_timeout]. @@ -462,14 +446,11 @@ impl<'a> Request<'a> { ///use http_req::{request::Request, uri::Uri}; ///use std::{time::Duration, convert::TryFrom}; /// - ///let mut writer = Vec::new(); ///let uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); ///const time: Option = Some(Duration::from_secs(10)); /// ///let response = Request::new(&uri) - /// .connect_timeout(time) - /// .send(&mut writer) - /// .unwrap(); + /// .connect_timeout(time); ///``` pub fn connect_timeout(&mut self, timeout: Option) -> &mut Self where @@ -479,7 +460,7 @@ impl<'a> Request<'a> { self } - ///Sets read timeout on internal `TcpStream` instance + ///Sets read timeout on internal `TcpStream` instance. /// ///`timeout` will be passed to ///[`TcpStream::set_read_timeout`][TcpStream::set_read_timeout]. @@ -491,14 +472,11 @@ impl<'a> Request<'a> { ///use http_req::{request::Request, uri::Uri}; ///use std::{time::Duration, convert::TryFrom}; /// - ///let mut writer = Vec::new(); ///let uri: Uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); ///const time: Option = Some(Duration::from_secs(15)); /// ///let response = Request::new(&uri) - /// .read_timeout(time) - /// .send(&mut writer) - /// .unwrap(); + /// .read_timeout(time); ///``` pub fn read_timeout(&mut self, timeout: Option) -> &mut Self where @@ -508,7 +486,7 @@ impl<'a> Request<'a> { self } - ///Sets write timeout on internal `TcpStream` instance + ///Sets write timeout on internal `TcpStream` instance. /// ///`timeout` will be passed to ///[`TcpStream::set_write_timeout`][TcpStream::set_write_timeout]. @@ -520,14 +498,11 @@ impl<'a> Request<'a> { ///use http_req::{request::Request, uri::Uri}; ///use std::{time::Duration, convert::TryFrom}; /// - ///let mut writer = Vec::new(); ///let uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); ///const time: Option = Some(Duration::from_secs(5)); /// ///let response = Request::new(&uri) - /// .write_timeout(time) - /// .send(&mut writer) - /// .unwrap(); + /// .write_timeout(time); ///``` pub fn write_timeout(&mut self, timeout: Option) -> &mut Self where @@ -537,21 +512,19 @@ impl<'a> Request<'a> { self } - ///Sets timeout on entire request + ///Sets timeout on entire request. + ///Data is read from a stream until the timeout is reached or there is no more data to read. /// ///# Examples ///``` ///use http_req::{request::Request, uri::Uri}; ///use std::{time::Duration, convert::TryFrom}; /// - ///let mut writer = Vec::new(); ///let uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); ///const time: Duration = Duration::from_secs(5); /// ///let response = Request::new(&uri) - /// .timeout(time) - /// .send(&mut writer) - /// .unwrap(); + /// .timeout(time); ///``` pub fn timeout(&mut self, timeout: T) -> &mut Self where @@ -562,12 +535,24 @@ impl<'a> Request<'a> { } ///Add a file containing the PEM-encoded certificates that should be added in the trusted root store. + /// + ///# Examples + ///``` + ///use http_req::{request::Request, uri::Uri}; + ///use std::{time::Duration, convert::TryFrom, path::Path}; + /// + ///let uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); + ///let path = Path::new("./foo/bar.txt"); + /// + ///let response = Request::new(&uri) + /// .root_cert_file_pem(&path); + ///``` pub fn root_cert_file_pem(&mut self, file_path: &'a Path) -> &mut Self { self.root_cert_file_pem = Some(file_path); self } - ///Sends HTTP request. + ///Sends HTTP request and returns `Response`. /// ///Creates `TcpStream` (and wraps it with `TlsStream` if needed). Writes request message ///to created stream. Returns response for this request. Writes response's body to `writer`. @@ -582,90 +567,65 @@ impl<'a> Request<'a> { /// ///let response = Request::new(&uri).send(&mut writer).unwrap(); ///``` - pub fn send(&self, writer: &mut T) -> Result { - //Set up stream - let (sender, receiver) = mpsc::channel(); - let mut raw_response_head: Vec = Vec::new(); - let request_msg = self.inner.parse_msg(); - + pub fn send(&self, writer: &mut T) -> Result + where + T: Write, + { + //Set up stream. let mut stream = Stream::new(self.inner.uri, self.connect_timeout)?; stream.set_read_timeout(self.read_timeout)?; stream.set_write_timeout(self.write_timeout)?; stream = Stream::try_to_https(stream, self.inner.uri, self.root_cert_file_pem)?; + + //Send request message to stream. + let request_msg = self.inner.parse(); stream.write_all(&request_msg)?; - //Read the response - let mut buf_reader = BufReader::new(stream); + //Set up variables let deadline = Instant::now() + self.timeout; - let reciving_timeout = Duration::from_secs(RECEIVING_TIMEOUT); + let (sender, receiver) = mpsc::channel(); + let (sender_supp, receiver_supp) = mpsc::channel(); + let mut raw_response_head: Vec = Vec::new(); + let mut buf_reader = BufReader::new(stream); + //Read from stream and send over data via `sender`. thread::spawn(move || { - loop { - let mut buf = Vec::new(); - match buf_reader.read_until(0xA, &mut buf) { - Ok(0) => break, - Ok(len) => { - let filled_buf = buf[..len].to_owned(); - sender.send(filled_buf).unwrap(); - - if len == 2 && buf == CR_LF.as_bytes() { - break; - } - } - Err(_) => break, - } - } + buf_reader.read_head(&sender); - loop { - let mut buf = vec![0; BUF_SIZE]; - match buf_reader.read(&mut buf) { - Ok(0) => break, - Ok(len) => { - let filled_buf = buf[..len].to_owned(); - sender.send(filled_buf).unwrap(); - } - Err(_) => break, + let params: Vec<&str> = receiver_supp.recv().unwrap(); + if params.contains(&"non-empty") { + if params.contains(&"chunked") { + let mut buf_reader = ChunkReader::from(buf_reader); + buf_reader.read_body(&sender); + } else { + buf_reader.read_body(&sender); } } }); - loop { - let now = Instant::now(); + //Receive and process `head` of the response. + raw_response_head.write_head(&receiver, deadline); - if deadline > now { - let data_read = match receiver.recv_timeout(reciving_timeout) { - Ok(data) => data, - Err(_) => break, - }; - - if data_read == CR_LF.as_bytes() { - break; - } + let response = Response::from_head(&raw_response_head)?; + let content_len = response.content_len().unwrap_or(1); + let encoding = response.headers().get("Transfer-Encoding"); + let mut params = Vec::with_capacity(5); - raw_response_head.write_all(&data_read)?; - } else { - break; + if let Some(encode) = encoding { + if encode == "chunked" { + params.push("chunked"); } } - let response = Response::from_head(&raw_response_head)?; - let content_len = response.content_len().unwrap_or(1); - - if content_len > 0 { - loop { - let now = Instant::now(); + if content_len > 0 && self.inner.method != Method::HEAD { + params.push("non-empty"); + } - if deadline > now { - let data_read = match receiver.recv_timeout(reciving_timeout) { - Ok(data) => data, - Err(_) => break, - }; + sender_supp.send(params).unwrap(); - writer.write_all(&data_read)?; - } else { - break; - } - } + //Receive and process `body`` of the response. + if content_len > 0 { + writer.write_body(&receiver, deadline); } Ok(response) @@ -683,9 +643,12 @@ impl<'a> Request<'a> { /// ///let response = request::get(uri, &mut writer).unwrap(); ///``` -pub fn get, U: Write>(uri: T, writer: &mut U) -> Result { +pub fn get(uri: T, writer: &mut U) -> Result +where + T: AsRef, + U: Write, +{ let uri = Uri::try_from(uri.as_ref())?; - Request::new(&uri).send(writer) } @@ -698,7 +661,10 @@ pub fn get, U: Write>(uri: T, writer: &mut U) -> Result>(uri: T) -> Result { +pub fn head(uri: T) -> Result +where + T: AsRef, +{ let mut writer = Vec::new(); let uri = Uri::try_from(uri.as_ref())?; @@ -717,11 +683,11 @@ pub fn head>(uri: T) -> Result { /// ///let response = request::post(uri, body, &mut writer).unwrap(); ///``` -pub fn post, U: Write>( - uri: T, - body: &[u8], - writer: &mut U, -) -> Result { +pub fn post(uri: T, body: &[u8], writer: &mut U) -> Result +where + T: AsRef, + U: Write, +{ let uri = Uri::try_from(uri.as_ref())?; Request::new(&uri) @@ -810,7 +776,7 @@ mod tests { const DEFAULT_MSG: &str = "GET /std/string/index.html HTTP/1.1\r\n\ Host: doc.rust-lang.org\r\n\r\n"; - let msg = req.parse_msg(); + let msg = req.parse(); let msg = String::from_utf8_lossy(&msg).into_owned(); for line in DEFAULT_MSG.lines() { diff --git a/src/response.rs b/src/response.rs index d40c095..857f7d5 100644 --- a/src/response.rs +++ b/src/response.rs @@ -226,7 +226,7 @@ impl str::FromStr for Status { } } -///Wrapper around HashMap, String> with additional functionality for parsing HTTP headers +///Wrapper around `HashMap, String>` with additional functionality for parsing HTTP headers /// ///# Example ///``` @@ -272,7 +272,7 @@ impl Headers { } ///An iterator visiting all key-value pairs in arbitrary order. - ///The iterator's element type is (&Ascii, &String). + ///The iterator's element type is `(&Ascii, &String)`. /// ///# Examples ///``` diff --git a/src/stream.rs b/src/stream.rs index e9b99ab..de19204 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -1,17 +1,19 @@ -use crate::{error::Error, tls, uri::Uri}; +use crate::{error::Error, tls, tls::Conn, uri::Uri}; use std::{ - io, + io::{self, BufRead, Read, Write}, net::{TcpStream, ToSocketAddrs}, path::Path, - time::Duration, + sync::mpsc::{Receiver, Sender}, + time::{Duration, Instant}, }; -use crate::tls::Conn; +const CR_LF: &str = "\r\n"; +const BUF_SIZE: usize = 1024 * 1024; +const RECEIVING_TIMEOUT: Duration = Duration::from_secs(60); pub enum Stream { Http(TcpStream), Https(Conn), - //Custom(R) } impl Stream { @@ -68,7 +70,7 @@ impl Stream { } } -impl io::Read for Stream { +impl Read for Stream { fn read(&mut self, buf: &mut [u8]) -> Result { match self { Stream::Http(stream) => stream.read(buf), @@ -77,7 +79,7 @@ impl io::Read for Stream { } } -impl io::Write for Stream { +impl Write for Stream { fn write(&mut self, buf: &[u8]) -> Result { match self { Stream::Http(stream) => stream.write(buf), @@ -92,6 +94,92 @@ impl io::Write for Stream { } } +pub trait ThreadSend { + fn read_head(&mut self, sender: &Sender>); + fn read_body(&mut self, sender: &Sender>); +} + +impl ThreadSend for T +where + T: BufRead, +{ + fn read_head(&mut self, sender: &Sender>) { + loop { + let mut buf = Vec::new(); + + match self.read_until(0xA, &mut buf) { + Ok(0) | Err(_) => break, + Ok(len) => { + let filled_buf = buf[..len].to_owned(); + sender.send(filled_buf).unwrap(); + + if len == 2 && buf == CR_LF.as_bytes() { + break; + } + } + } + } + } + + fn read_body(&mut self, sender: &Sender>) { + loop { + let mut buf = vec![0; BUF_SIZE]; + + match self.read(&mut buf) { + Ok(0) | Err(_) => break, + Ok(len) => { + let filled_buf = buf[..len].to_owned(); + sender.send(filled_buf).unwrap(); + } + } + } + } +} + +pub trait ThreadReceive { + fn write_head(&mut self, receiver: &Receiver>, deadline: Instant); + fn write_body(&mut self, receiver: &Receiver>, deadline: Instant); +} + +impl ThreadReceive for T +where + T: Write, +{ + fn write_head(&mut self, receiver: &Receiver>, deadline: Instant) { + execute_with_deadline(deadline, || { + let mut continue_reading = true; + + let data_read = match receiver.recv_timeout(RECEIVING_TIMEOUT) { + Ok(data) => data, + Err(_) => return false, + }; + + if data_read == CR_LF.as_bytes() { + continue_reading = false; + } + + self.write_all(&data_read).unwrap(); + + continue_reading + }); + } + + fn write_body(&mut self, receiver: &Receiver>, deadline: Instant) { + execute_with_deadline(deadline, || { + let continue_reading = true; + + let data_read = match receiver.recv_timeout(RECEIVING_TIMEOUT) { + Ok(data) => data, + Err(_) => return false, + }; + + self.write_all(&data_read).unwrap(); + + continue_reading + }); + } +} + ///Connects to target host with a timeout pub fn connect_with_timeout(host: T, port: u16, timeout: U) -> io::Result where @@ -122,3 +210,16 @@ where format!("Could not resolve address for {:?}", host), )) } + +pub fn execute_with_deadline(deadline: Instant, mut func: F) +where + F: FnMut() -> bool, +{ + loop { + let now = Instant::now(); + + if deadline < now || func() == false { + break; + } + } +} From 76f0536eaaf4d1186fe9c34a310baa7ccdf7eaff Mon Sep 17 00:00:00 2001 From: jayjamesjay Date: Tue, 25 Jun 2024 09:57:10 +0200 Subject: [PATCH 36/47] improve docs and refactor stream --- LICENSE | 2 +- README.md | 9 +- examples/request_builder_get.rs | 26 +- src/chunked.rs | 13 +- src/error.rs | 2 +- src/lib.rs | 35 ++- src/request.rs | 537 ++++++++++++++++---------------- src/response.rs | 430 ++++++++++++------------- src/stream.rs | 123 +++++--- src/tls.rs | 34 +- src/uri.rs | 314 +++++++++---------- 11 files changed, 787 insertions(+), 738 deletions(-) diff --git a/LICENSE b/LICENSE index c972bbd..78d3280 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2018-2023 jayjamesjay +Copyright (c) 2018-2024 jayjamesjay Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index ea6611c..e2cf333 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,10 @@ # http_req +> [!CAUTION] +> V0.11.0 introduces major changes to design of `RequestBuilder` and `Request`. Please review [documentation](https://docs.rs/http_req/0.11.0/http_req/) before migrating from previous versions. + [![Rust](https://github.com/jayjamesjay/http_req/actions/workflows/rust.yml/badge.svg)](https://github.com/jayjamesjay/http_req/actions/workflows/rust.yml) -[![Crates.io](https://img.shields.io/badge/crates.io-v0.10.3-orange.svg?longCache=true)](https://crates.io/crates/http_req) -[![Docs.rs](https://docs.rs/http_req/badge.svg)](https://docs.rs/http_req/0.10.3/http_req/) +[![Crates.io](https://img.shields.io/badge/crates.io-v0.11.0-orange.svg?longCache=true)](https://crates.io/crates/http_req) +[![Docs.rs](https://docs.rs/http_req/badge.svg)](https://docs.rs/http_req/0.11.0/http_req/) Simple and lightweight HTTP client with built-in HTTPS support. @@ -29,7 +32,7 @@ Take a look at [more examples](https://github.com/jayjamesjay/http_req/tree/mast In order to use `http_req` with `rustls` in your project, add the following lines to `Cargo.toml`: ```toml [dependencies] -http_req = {version="^0.10", default-features = false, features = ["rust-tls"]} +http_req = {version="^0.11", default-features = false, features = ["rust-tls"]} ``` ## License diff --git a/examples/request_builder_get.rs b/examples/request_builder_get.rs index ac72597..da13231 100644 --- a/examples/request_builder_get.rs +++ b/examples/request_builder_get.rs @@ -1,7 +1,12 @@ -use http_req::{request::RequestBuilder, response::Response, stream::Stream, uri::Uri}; +use http_req::{ + request::RequestBuilder, + response::Response, + stream::{self, Stream}, + uri::Uri, +}; use std::{ convert::TryFrom, - io::{BufRead, BufReader, Read, Write}, + io::{BufReader, Read, Write}, time::Duration, }; @@ -10,7 +15,7 @@ fn main() { let addr: Uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); //Containers for a server's response. - let mut raw_head = Vec::new(); + let raw_head; let mut body = Vec::new(); //Prepares a request message. @@ -18,8 +23,6 @@ fn main() { .header("Connection", "Close") .parse(); - println!("{:?}", String::from_utf8(request_msg.clone())); - //Connects to a server. Uses information from `addr`. let mut stream = Stream::new(&addr, Some(Duration::from_secs(60))).unwrap(); stream = Stream::try_to_https(stream, &addr, None).unwrap(); @@ -30,18 +33,7 @@ fn main() { //Wraps the stream in BufReader to make it easier to read from it. //Reads a response from the server and saves the head to `raw_head`, and the body to `body`. let mut stream = BufReader::new(stream); - loop { - match stream.read_until(0xA, &mut raw_head) { - Ok(0) | Err(_) => break, - Ok(len) => { - let full_len = raw_head.len(); - - if len == 2 && &raw_head[full_len - 2..] == b"\r\n" { - break; - } - } - } - } + raw_head = stream::read_head(&mut stream); stream.read_to_end(&mut body).unwrap(); //Parses and processes the response. diff --git a/src/chunked.rs b/src/chunked.rs index c17d6f1..44121fb 100644 --- a/src/chunked.rs +++ b/src/chunked.rs @@ -1,14 +1,13 @@ -//!implements the wire protocol for HTTP's "chunked" Transfer-Encoding. -/// -///It's a Rust version of the reference implementation in [Go][1]. +//! implements the wire protocol for HTTP's "chunked" Transfer-Encoding. +use crate::CR_LF; /// -///[1]: https://golang.google.cn/src/net/http/internal/chunked.go +/// It's a Rust version of the reference implementation in [Go][1]. +/// +/// [1]: https://golang.google.cn/src/net/http/internal/chunked.go /// - use std::io::{self, BufRead, BufReader, Error, ErrorKind, Read}; const MAX_LINE_LENGTH: usize = 4096; -const CR_LF: [u8; 2] = [b'\r', b'\n']; pub struct ChunkReader { check_end: bool, @@ -37,7 +36,7 @@ where } if let Ok(_) = self.reader.read_exact(&mut footer) { - if footer != CR_LF { + if &footer != CR_LF { self.err = Some(error_malformed_chunked_encoding()); break; } diff --git a/src/error.rs b/src/error.rs index 454a579..f653c81 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,4 +1,4 @@ -//!error system +//! error system used around the library. use std::{error, fmt, io, num, str, sync::mpsc}; #[derive(Debug, PartialEq)] diff --git a/src/lib.rs b/src/lib.rs index 780c359..3f806de 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,23 +1,26 @@ -//!Simple HTTP client with built-in HTTPS support. -//!Currently it's in heavy development and may frequently change. +//! Simple HTTP client with built-in HTTPS support. +//! Currently it's in heavy development and may frequently change. //! -//!## Example -//!Basic GET request -//!``` -//!use http_req::request; +//! ## Example +//! Basic GET request +//! ``` +//! use http_req::request; //! -//!fn main() { -//! //Container for body of a response -//! let mut body = Vec::new(); -//! let res = request::get("https://doc.rust-lang.org/", &mut body).unwrap(); +//! fn main() { +//! //Container for body of a response +//! let mut body = Vec::new(); +//! let res = request::get("https://doc.rust-lang.org/", &mut body).unwrap(); //! -//! println!("Status: {} {}", res.status_code(), res.reason()); -//!} -//!``` -pub mod uri; +//! println!("Status: {} {}", res.status_code(), res.reason()); +//! } +//! ``` +pub mod chunked; +pub mod error; pub mod request; pub mod response; pub mod stream; -pub mod chunked; pub mod tls; -pub mod error; +pub mod uri; + +pub(crate) const CR_LF: &[u8; 2] = b"\r\n"; +pub(crate) const LF: u8 = 0xA; diff --git a/src/request.rs b/src/request.rs index 2368b3b..246048f 100644 --- a/src/request.rs +++ b/src/request.rs @@ -18,7 +18,7 @@ use std::{ const CR_LF: &str = "\r\n"; const DEFAULT_REQ_TIMEOUT: u64 = 12 * 60 * 60; -///HTTP request methods +/// HTTP request methods #[derive(Debug, PartialEq, Clone, Copy)] pub enum Method { GET, @@ -48,7 +48,7 @@ impl fmt::Display for Method { } } -///HTTP versions +/// HTTP versions #[derive(Debug, PartialEq, Clone, Copy)] pub enum HttpVersion { Http10, @@ -74,19 +74,19 @@ impl fmt::Display for HttpVersion { } } -///Raw HTTP request that can be sent to any stream +/// Raw HTTP request that can be sent to any stream /// -///# Examples -///``` -///use std::convert::TryFrom; -///use http_req::{request::RequestBuilder, uri::Uri}; +/// # Examples +/// ``` +/// use std::convert::TryFrom; +/// use http_req::{request::RequestBuilder, uri::Uri}; /// -///let addr: Uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); +/// let addr: Uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); /// -///let mut request_msg = RequestBuilder::new(&addr) -/// .header("Connection", "Close") -/// .parse(); -///``` +/// let mut request_msg = RequestBuilder::new(&addr) +/// .header("Connection", "Close") +/// .parse(); +/// ``` #[derive(Clone, Debug, PartialEq)] pub struct RequestBuilder<'a> { uri: &'a Uri<'a>, @@ -97,18 +97,18 @@ pub struct RequestBuilder<'a> { } impl<'a> RequestBuilder<'a> { - ///Creates new `RequestBuilder` with default parameters + /// Creates a new `RequestBuilder` with default parameters /// - ///# Examples - ///``` - ///use std::convert::TryFrom; - ///use http_req::{request::RequestBuilder, uri::Uri}; + /// # Examples + /// ``` + /// use std::convert::TryFrom; + /// use http_req::{request::RequestBuilder, uri::Uri}; /// - ///let addr = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); + /// let addr = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); /// - ///let request_builder = RequestBuilder::new(&addr) - /// .header("Connection", "Close"); - ///``` + /// let request_builder = RequestBuilder::new(&addr) + /// .header("Connection", "Close"); + /// ``` pub fn new(uri: &'a Uri<'a>) -> RequestBuilder<'a> { RequestBuilder { headers: Headers::default_http(uri), @@ -119,18 +119,18 @@ impl<'a> RequestBuilder<'a> { } } - ///Sets request method + /// Sets the request method /// - ///# Examples - ///``` - ///use std::convert::TryFrom; - ///use http_req::{request::{RequestBuilder, Method}, uri::Uri}; + /// # Examples + /// ``` + /// use std::convert::TryFrom; + /// use http_req::{request::{RequestBuilder, Method}, uri::Uri}; /// - ///let addr = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); + /// let addr = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); /// - ///let request_builder = RequestBuilder::new(&addr) - /// .method(Method::HEAD); - ///``` + /// let request_builder = RequestBuilder::new(&addr) + /// .method(Method::HEAD); + /// ``` pub fn method(&mut self, method: T) -> &mut Self where Method: From, @@ -139,18 +139,18 @@ impl<'a> RequestBuilder<'a> { self } - ///Sets HTTP version + /// Sets the HTTP version /// - ///# Examples - ///``` - ///use std::convert::TryFrom; - ///use http_req::{request::{RequestBuilder, HttpVersion}, uri::Uri}; + /// # Examples + /// ``` + /// use std::convert::TryFrom; + /// use http_req::{request::{RequestBuilder, HttpVersion}, uri::Uri}; /// - ///let addr = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); + /// let addr = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); /// - ///let request_builder = RequestBuilder::new(&addr) - /// .version(HttpVersion::Http10); - ///``` + /// let request_builder = RequestBuilder::new(&addr) + /// .version(HttpVersion::Http10); + /// ``` pub fn version(&mut self, version: T) -> &mut Self where HttpVersion: From, @@ -159,24 +159,24 @@ impl<'a> RequestBuilder<'a> { self } - ///Replaces all it's headers with headers passed to the function + /// Replaces all it's headers with headers passed to the function /// - ///# Examples - ///``` - ///use std::convert::TryFrom; - ///use http_req::{request::RequestBuilder, response::Headers, uri::Uri}; + /// # Examples + /// ``` + /// use std::convert::TryFrom; + /// use http_req::{request::RequestBuilder, response::Headers, uri::Uri}; /// - ///let addr = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); + /// let addr = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); /// - ///let mut headers = Headers::new(); - ///headers.insert("Accept-Charset", "utf-8"); - ///headers.insert("Accept-Language", "en-US"); - ///headers.insert("Host", "rust-lang.org"); - ///headers.insert("Connection", "Close"); + /// let mut headers = Headers::new(); + /// headers.insert("Accept-Charset", "utf-8"); + /// headers.insert("Accept-Language", "en-US"); + /// headers.insert("Host", "rust-lang.org"); + /// headers.insert("Connection", "Close"); /// - ///let request_builder = RequestBuilder::new(&addr) - /// .headers(headers); - ///``` + /// let request_builder = RequestBuilder::new(&addr) + /// .headers(headers); + /// ``` pub fn headers(&mut self, headers: T) -> &mut Self where Headers: From, @@ -185,18 +185,18 @@ impl<'a> RequestBuilder<'a> { self } - ///Adds new header to existing/default headers + /// Adds a new header to existing/default headers /// - ///# Examples - ///``` - ///use std::convert::TryFrom; - ///use http_req::{request::RequestBuilder, response::Headers, uri::Uri}; + /// # Examples + /// ``` + /// use std::convert::TryFrom; + /// use http_req::{request::RequestBuilder, response::Headers, uri::Uri}; /// - ///let addr = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); + /// let addr = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); /// - ///let request_builder = RequestBuilder::new(&addr) - /// .header("Connection", "Close"); - ///``` + /// let request_builder = RequestBuilder::new(&addr) + /// .header("Connection", "Close"); + /// ``` pub fn header(&mut self, key: &T, val: &U) -> &mut Self where T: ToString + ?Sized, @@ -206,43 +206,40 @@ impl<'a> RequestBuilder<'a> { self } - ///Sets body for request + /// Sets the body for request /// - ///# Examples - ///``` - ///use std::convert::TryFrom; - ///use http_req::{request::{RequestBuilder, Method}, response::Headers, uri::Uri}; + /// # Examples + /// ``` + /// use std::convert::TryFrom; + /// use http_req::{request::{RequestBuilder, Method}, response::Headers, uri::Uri}; /// - ///let addr = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); - ///const BODY: &[u8; 27] = b"field1=value1&field2=value2"; + /// let addr = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); + /// const BODY: &[u8; 27] = b"field1=value1&field2=value2"; /// - ///let request_builder = RequestBuilder::new(&addr) - /// .method(Method::POST) - /// .body(BODY) - /// .header("Content-Length", &BODY.len()) - /// .header("Connection", "Close"); - ///``` + /// let request_builder = RequestBuilder::new(&addr) + /// .method(Method::POST) + /// .body(BODY) + /// .header("Content-Length", &BODY.len()) + /// .header("Connection", "Close"); + /// ``` pub fn body(&mut self, body: &'a [u8]) -> &mut Self { self.body = Some(body); self } - ///Parses request message for this `RequestBuilder` + /// Parses the request message for this `RequestBuilder` /// - ///# Examples - ///``` - ///use std::convert::TryFrom; - ///use http_req::{request::RequestBuilder, uri::Uri}; + /// # Examples + /// ``` + /// use std::convert::TryFrom; + /// use http_req::{request::RequestBuilder, uri::Uri}; /// - ///let addr: Uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); + /// let addr: Uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); /// - ///let mut request_msg = RequestBuilder::new(&addr) - /// .header("Connection", "Close") - /// .parse(); - /// - ///let expected_msg = "GET /learn HTTP/1.1\r\nHost: www.rust-lang.org\r\nConnection: Close\r\n\r\n"; - ///assert_eq!(String::from_utf8(request_msg).unwrap(), expected_msg); - ///``` + /// let mut request_msg = RequestBuilder::new(&addr) + /// .header("Connection", "Close") + /// .parse(); + /// ``` pub fn parse(&self) -> Vec { let request_line = format!( "{} {} {}{}", @@ -268,22 +265,22 @@ impl<'a> RequestBuilder<'a> { } } -///Allows for making HTTP requests based on specified parameters. +/// Allows for making HTTP requests based on specified parameters. /// -///It creates stream (`TcpStream` or `TlsStream`) appropriate for the type of uri (`http`/`https`). -///By default it closes connection after completion of the response. +/// It creates a stream (`TcpStream` or `TlsStream`) appropriate for the type of uri (`http`/`https`). +/// By default it closes connection after completion of the response. /// -///# Examples -///``` -///use http_req::{request::Request, uri::Uri, response::StatusCode}; -///use std::convert::TryFrom; +/// # Examples +/// ``` +/// use http_req::{request::Request, uri::Uri, response::StatusCode}; +/// use std::convert::TryFrom; /// -///let mut writer = Vec::new(); -///let uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); +/// let mut writer = Vec::new(); +/// let uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); /// -///let response = Request::new(&uri).send(&mut writer).unwrap();; -///assert_eq!(response.status_code(), StatusCode::new(200)); -///``` +/// let response = Request::new(&uri).send(&mut writer).unwrap();; +/// assert_eq!(response.status_code(), StatusCode::new(200)); +/// ``` /// #[derive(Clone, Debug, PartialEq)] pub struct Request<'a> { @@ -296,18 +293,18 @@ pub struct Request<'a> { } impl<'a> Request<'a> { - ///Creates new `Request` with default parameters. + /// Creates a new `Request` with default parameters. /// - ///# Examples - ///``` - ///use http_req::{request::Request, uri::Uri}; - ///use std::convert::TryFrom; + /// # Examples + /// ``` + /// use http_req::{request::Request, uri::Uri}; + /// use std::convert::TryFrom; /// - ///let mut writer = Vec::new(); - ///let uri: Uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); + /// let mut writer = Vec::new(); + /// let uri: Uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); /// - ///let response = Request::new(&uri).send(&mut writer).unwrap();; - ///``` + /// let response = Request::new(&uri).send(&mut writer).unwrap();; + /// ``` pub fn new(uri: &'a Uri) -> Request<'a> { let mut builder = RequestBuilder::new(&uri); builder.header("Connection", "Close"); @@ -322,18 +319,18 @@ impl<'a> Request<'a> { } } - ///Sets request method. + /// Sets the request method. /// - ///# Examples - ///``` - ///use http_req::{request::{Request, Method}, uri::Uri}; - ///use std::convert::TryFrom; + /// # Examples + /// ``` + /// use http_req::{request::{Request, Method}, uri::Uri}; + /// use std::convert::TryFrom; /// - ///let uri: Uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); + /// let uri: Uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); /// - ///let response = Request::new(&uri) - /// .method(Method::HEAD); - ///``` + /// let response = Request::new(&uri) + /// .method(Method::HEAD); + /// ``` pub fn method(&mut self, method: T) -> &mut Self where Method: From, @@ -342,18 +339,18 @@ impl<'a> Request<'a> { self } - ///Sets HTTP version. + /// Sets the HTTP version. /// - ///# Examples - ///``` - ///use http_req::{request::{Request, HttpVersion}, uri::Uri}; - ///use std::convert::TryFrom; + /// # Examples + /// ``` + /// use http_req::{request::{Request, HttpVersion}, uri::Uri}; + /// use std::convert::TryFrom; /// - ///let uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); + /// let uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); /// - ///let response = Request::new(&uri) - /// .version(HttpVersion::Http10); - ///``` + /// let response = Request::new(&uri) + /// .version(HttpVersion::Http10); + /// ``` pub fn version(&mut self, version: T) -> &mut Self where @@ -363,24 +360,24 @@ impl<'a> Request<'a> { self } - ///Replaces all it's headers with headers passed to the function. + /// Replaces all it's headers with headers passed to the function. /// - ///# Examples - ///``` - ///use http_req::{request::Request, uri::Uri, response::Headers}; - ///use std::convert::TryFrom; + /// # Examples + /// ``` + /// use http_req::{request::Request, uri::Uri, response::Headers}; + /// use std::convert::TryFrom; /// - ///let uri: Uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); + /// let uri: Uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); /// - ///let mut headers = Headers::new(); - ///headers.insert("Accept-Charset", "utf-8"); - ///headers.insert("Accept-Language", "en-US"); - ///headers.insert("Host", "rust-lang.org"); - ///headers.insert("Connection", "Close"); + /// let mut headers = Headers::new(); + /// headers.insert("Accept-Charset", "utf-8"); + /// headers.insert("Accept-Language", "en-US"); + /// headers.insert("Host", "rust-lang.org"); + /// headers.insert("Connection", "Close"); /// - ///let response = Request::new(&uri) - /// .headers(headers); - ///``` + /// let response = Request::new(&uri) + /// .headers(headers); + /// ``` pub fn headers(&mut self, headers: T) -> &mut Self where Headers: From, @@ -389,18 +386,18 @@ impl<'a> Request<'a> { self } - ///Adds header to existing/default headers. + /// Adds the header to existing/default headers. /// - ///# Examples - ///``` - ///use http_req::{request::Request, uri::Uri}; - ///use std::convert::TryFrom; + /// # Examples + /// ``` + /// use http_req::{request::Request, uri::Uri}; + /// use std::convert::TryFrom; /// - ///let uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); + /// let uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); /// - ///let response = Request::new(&uri) - /// .header("Accept-Language", "en-US"); - ///``` + /// let response = Request::new(&uri) + /// .header("Accept-Language", "en-US"); + /// ``` pub fn header(&mut self, key: &T, val: &U) -> &mut Self where T: ToString + ?Sized, @@ -410,48 +407,48 @@ impl<'a> Request<'a> { self } - ///Sets body for request. + /// Sets the body for request. /// - ///# Examples - ///``` - ///use http_req::{request::{Request, Method}, uri::Uri}; - ///use std::convert::TryFrom; + /// # Examples + /// ``` + /// use http_req::{request::{Request, Method}, uri::Uri}; + /// use std::convert::TryFrom; /// - ///let uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); - ///const body: &[u8; 27] = b"field1=value1&field2=value2"; + /// let uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); + /// const body: &[u8; 27] = b"field1=value1&field2=value2"; /// - ///let response = Request::new(&uri) - /// .method(Method::POST) - /// .header("Content-Length", &body.len()) - /// .body(body); - ///``` + /// let response = Request::new(&uri) + /// .method(Method::POST) + /// .header("Content-Length", &body.len()) + /// .body(body); + /// ``` pub fn body(&mut self, body: &'a [u8]) -> &mut Self { self.inner.body(body); self } - ///Sets connect timeout while using internal `TcpStream` instance. + /// Sets the connect timeout while using internal `TcpStream` instance. /// - ///- If there is a timeout, it will be passed to - /// [`TcpStream::connect_timeout`][TcpStream::connect_timeout]. - ///- If `None` is provided, [`TcpStream::connect`][TcpStream::connect] will - /// be used. A timeout will still be enforced by the operating system, but - /// the exact value depends on the platform. + /// - If there is a timeout, it will be passed to + /// [`TcpStream::connect_timeout`][TcpStream::connect_timeout]. + /// - If `None` is provided, [`TcpStream::connect`][TcpStream::connect] will + /// be used. A timeout will still be enforced by the operating system, but + /// the exact value depends on the platform. /// - ///[TcpStream::connect]: https://doc.rust-lang.org/std/net/struct.TcpStream.html#method.connect - ///[TcpStream::connect_timeout]: https://doc.rust-lang.org/std/net/struct.TcpStream.html#method.connect_timeout + /// [TcpStream::connect]: https://doc.rust-lang.org/std/net/struct.TcpStream.html#method.connect + /// [TcpStream::connect_timeout]: https://doc.rust-lang.org/std/net/struct.TcpStream.html#method.connect_timeout /// - ///# Examples - ///``` - ///use http_req::{request::Request, uri::Uri}; - ///use std::{time::Duration, convert::TryFrom}; + /// # Examples + /// ``` + /// use http_req::{request::Request, uri::Uri}; + /// use std::{time::Duration, convert::TryFrom}; /// - ///let uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); - ///const time: Option = Some(Duration::from_secs(10)); + /// let uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); + /// const time: Option = Some(Duration::from_secs(10)); /// - ///let response = Request::new(&uri) - /// .connect_timeout(time); - ///``` + /// let response = Request::new(&uri) + /// .connect_timeout(time); + /// ``` pub fn connect_timeout(&mut self, timeout: Option) -> &mut Self where Duration: From, @@ -460,24 +457,24 @@ impl<'a> Request<'a> { self } - ///Sets read timeout on internal `TcpStream` instance. + /// Sets the read timeout on internal `TcpStream` instance. /// - ///`timeout` will be passed to - ///[`TcpStream::set_read_timeout`][TcpStream::set_read_timeout]. + /// `timeout` will be passed to + /// [`TcpStream::set_read_timeout`][TcpStream::set_read_timeout]. /// - ///[TcpStream::set_read_timeout]: https://doc.rust-lang.org/std/net/struct.TcpStream.html#method.set_read_timeout + /// [TcpStream::set_read_timeout]: https://doc.rust-lang.org/std/net/struct.TcpStream.html#method.set_read_timeout /// - ///# Examples - ///``` - ///use http_req::{request::Request, uri::Uri}; - ///use std::{time::Duration, convert::TryFrom}; + /// # Examples + /// ``` + /// use http_req::{request::Request, uri::Uri}; + /// use std::{time::Duration, convert::TryFrom}; /// - ///let uri: Uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); - ///const time: Option = Some(Duration::from_secs(15)); + /// let uri: Uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); + /// const time: Option = Some(Duration::from_secs(15)); /// - ///let response = Request::new(&uri) - /// .read_timeout(time); - ///``` + /// let response = Request::new(&uri) + /// .read_timeout(time); + /// ``` pub fn read_timeout(&mut self, timeout: Option) -> &mut Self where Duration: From, @@ -486,24 +483,24 @@ impl<'a> Request<'a> { self } - ///Sets write timeout on internal `TcpStream` instance. + /// Sets the write timeout on internal `TcpStream` instance. /// - ///`timeout` will be passed to - ///[`TcpStream::set_write_timeout`][TcpStream::set_write_timeout]. + /// `timeout` will be passed to + /// [`TcpStream::set_write_timeout`][TcpStream::set_write_timeout]. /// - ///[TcpStream::set_write_timeout]: https://doc.rust-lang.org/std/net/struct.TcpStream.html#method.set_write_timeout + /// [TcpStream::set_write_timeout]: https://doc.rust-lang.org/std/net/struct.TcpStream.html#method.set_write_timeout /// - ///# Examples - ///``` - ///use http_req::{request::Request, uri::Uri}; - ///use std::{time::Duration, convert::TryFrom}; + /// # Examples + /// ``` + /// use http_req::{request::Request, uri::Uri}; + /// use std::{time::Duration, convert::TryFrom}; /// - ///let uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); - ///const time: Option = Some(Duration::from_secs(5)); + /// let uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); + /// const time: Option = Some(Duration::from_secs(5)); /// - ///let response = Request::new(&uri) - /// .write_timeout(time); - ///``` + /// let response = Request::new(&uri) + /// .write_timeout(time); + /// ``` pub fn write_timeout(&mut self, timeout: Option) -> &mut Self where Duration: From, @@ -512,20 +509,20 @@ impl<'a> Request<'a> { self } - ///Sets timeout on entire request. - ///Data is read from a stream until the timeout is reached or there is no more data to read. + /// Sets the timeout on entire request. + /// Data is read from a stream until there is no more data to read or the timeout is exceeded. /// - ///# Examples - ///``` - ///use http_req::{request::Request, uri::Uri}; - ///use std::{time::Duration, convert::TryFrom}; + /// # Examples + /// ``` + /// use http_req::{request::Request, uri::Uri}; + /// use std::{time::Duration, convert::TryFrom}; /// - ///let uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); - ///const time: Duration = Duration::from_secs(5); + /// let uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); + /// const time: Duration = Duration::from_secs(5); /// - ///let response = Request::new(&uri) - /// .timeout(time); - ///``` + /// let response = Request::new(&uri) + /// .timeout(time); + /// ``` pub fn timeout(&mut self, timeout: T) -> &mut Self where Duration: From, @@ -534,50 +531,50 @@ impl<'a> Request<'a> { self } - ///Add a file containing the PEM-encoded certificates that should be added in the trusted root store. + /// Adds the file containing the PEM-encoded certificates that should be added in the trusted root store. /// - ///# Examples - ///``` - ///use http_req::{request::Request, uri::Uri}; - ///use std::{time::Duration, convert::TryFrom, path::Path}; + /// # Examples + /// ``` + /// use http_req::{request::Request, uri::Uri}; + /// use std::{time::Duration, convert::TryFrom, path::Path}; /// - ///let uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); - ///let path = Path::new("./foo/bar.txt"); + /// let uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); + /// let path = Path::new("./foo/bar.txt"); /// - ///let response = Request::new(&uri) - /// .root_cert_file_pem(&path); - ///``` + /// let response = Request::new(&uri) + /// .root_cert_file_pem(&path); + /// ``` pub fn root_cert_file_pem(&mut self, file_path: &'a Path) -> &mut Self { self.root_cert_file_pem = Some(file_path); self } - ///Sends HTTP request and returns `Response`. + /// Sends the HTTP request and returns `Response`. /// - ///Creates `TcpStream` (and wraps it with `TlsStream` if needed). Writes request message - ///to created stream. Returns response for this request. Writes response's body to `writer`. + /// Creates `TcpStream` (and wraps it with `TlsStream` if needed). Writes request message + /// to created stream. Returns response for this request. Writes response's body to `writer`. /// - ///# Examples - ///``` - ///use http_req::{request::Request, uri::Uri}; - ///use std::convert::TryFrom; + /// # Examples + /// ``` + /// use http_req::{request::Request, uri::Uri}; + /// use std::convert::TryFrom; /// - ///let mut writer = Vec::new(); - ///let uri: Uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); + /// let mut writer = Vec::new(); + /// let uri: Uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); /// - ///let response = Request::new(&uri).send(&mut writer).unwrap(); - ///``` + /// let response = Request::new(&uri).send(&mut writer).unwrap(); + /// ``` pub fn send(&self, writer: &mut T) -> Result where T: Write, { - //Set up stream. + //Set up a stream. let mut stream = Stream::new(self.inner.uri, self.connect_timeout)?; stream.set_read_timeout(self.read_timeout)?; stream.set_write_timeout(self.write_timeout)?; stream = Stream::try_to_https(stream, self.inner.uri, self.root_cert_file_pem)?; - //Send request message to stream. + //Send the request message to stream. let request_msg = self.inner.parse(); stream.write_all(&request_msg)?; @@ -588,23 +585,23 @@ impl<'a> Request<'a> { let mut raw_response_head: Vec = Vec::new(); let mut buf_reader = BufReader::new(stream); - //Read from stream and send over data via `sender`. + //Read from the stream and send over data via `sender`. thread::spawn(move || { - buf_reader.read_head(&sender); + buf_reader.send_head(&sender); let params: Vec<&str> = receiver_supp.recv().unwrap(); if params.contains(&"non-empty") { if params.contains(&"chunked") { let mut buf_reader = ChunkReader::from(buf_reader); - buf_reader.read_body(&sender); + buf_reader.send_all(&sender); } else { - buf_reader.read_body(&sender); + buf_reader.send_all(&sender); } } }); //Receive and process `head` of the response. - raw_response_head.write_head(&receiver, deadline); + raw_response_head.receive(&receiver, deadline); let response = Response::from_head(&raw_response_head)?; let content_len = response.content_len().unwrap_or(1); @@ -625,24 +622,24 @@ impl<'a> Request<'a> { //Receive and process `body`` of the response. if content_len > 0 { - writer.write_body(&receiver, deadline); + writer.receive_all(&receiver, deadline); } Ok(response) } } -///Creates and sends GET request. Returns response for this request. +/// Creates and sends GET request. Returns response for this request. /// -///# Examples -///``` -///use http_req::request; +/// # Examples +/// ``` +/// use http_req::request; /// -///let mut writer = Vec::new(); -///const uri: &str = "https://www.rust-lang.org/learn"; +/// let mut writer = Vec::new(); +/// const uri: &str = "https://www.rust-lang.org/learn"; /// -///let response = request::get(uri, &mut writer).unwrap(); -///``` +/// let response = request::get(uri, &mut writer).unwrap(); +/// ``` pub fn get(uri: T, writer: &mut U) -> Result where T: AsRef, @@ -652,15 +649,15 @@ where Request::new(&uri).send(writer) } -///Creates and sends HEAD request. Returns response for this request. +/// Creates and sends HEAD request. Returns response for this request. /// -///# Examples -///``` -///use http_req::request; +/// # Examples +/// ``` +/// use http_req::request; /// -///const uri: &str = "https://www.rust-lang.org/learn"; -///let response = request::head(uri).unwrap(); -///``` +/// const uri: &str = "https://www.rust-lang.org/learn"; +/// let response = request::head(uri).unwrap(); +/// ``` pub fn head(uri: T) -> Result where T: AsRef, @@ -671,18 +668,18 @@ where Request::new(&uri).method(Method::HEAD).send(&mut writer) } -///Creates and sends POST request. Returns response for this request. +/// Creates and sends POST request. Returns response for this request. /// -///# Examples -///``` -///use http_req::request; +/// # Examples +/// ``` +/// use http_req::request; /// -///let mut writer = Vec::new(); -///const uri: &str = "https://www.rust-lang.org/learn"; -///const body: &[u8; 27] = b"field1=value1&field2=value2"; +/// let mut writer = Vec::new(); +/// const uri: &str = "https://www.rust-lang.org/learn"; +/// const body: &[u8; 27] = b"field1=value1&field2=value2"; /// -///let response = request::post(uri, body, &mut writer).unwrap(); -///``` +/// let response = request::post(uri, body, &mut writer).unwrap(); +/// ``` pub fn post(uri: T, body: &[u8], writer: &mut U) -> Result where T: AsRef, @@ -770,7 +767,7 @@ mod tests { } #[test] - fn request_b_parse_msg() { + fn request_b_parse() { let uri = Uri::try_from(URI).unwrap(); let req = RequestBuilder::new(&uri); diff --git a/src/response.rs b/src/response.rs index 857f7d5..895e6d6 100644 --- a/src/response.rs +++ b/src/response.rs @@ -13,9 +13,9 @@ use unicase::Ascii; pub(crate) const CR_LF_2: [u8; 4] = [13, 10, 13, 10]; -///Represents an HTTP response. +/// Represents an HTTP response. /// -///It contains `Headers` and `Status` parsed from response. +/// It contains `Headers` and `Status` parsed from response. #[derive(Debug, PartialEq, Clone)] pub struct Response { status: Status, @@ -23,19 +23,19 @@ pub struct Response { } impl Response { - ///Creates new `Response` with head - status and headers - parsed from a slice of bytes + /// Creates new `Response` with head - status and headers - parsed from a slice of bytes /// - ///# Examples - ///``` - ///use http_req::response::Response; + /// # Examples + /// ``` + /// use http_req::response::Response; /// - ///const HEAD: &[u8; 102] = b"HTTP/1.1 200 OK\r\n\ - /// Date: Sat, 11 Jan 2003 02:44:04 GMT\r\n\ - /// Content-Type: text/html\r\n\ - /// Content-Length: 100\r\n\r\n"; + /// const HEAD: &[u8; 102] = b"HTTP/1.1 200 OK\r\n\ + /// Date: Sat, 11 Jan 2003 02:44:04 GMT\r\n\ + /// Content-Type: text/html\r\n\ + /// Content-Length: 100\r\n\r\n"; /// - ///let response = Response::from_head(HEAD).unwrap(); - ///``` + /// let response = Response::from_head(HEAD).unwrap(); + /// ``` pub fn from_head(head: &[u8]) -> Result { let mut head = str::from_utf8(head)?.splitn(2, '\n'); @@ -45,21 +45,21 @@ impl Response { Ok(Response { status, headers }) } - ///Parses `Response` from slice of bytes. Writes it's body to `writer`. + /// Parses `Response` from slice of bytes. Writes it's body to `writer`. /// - ///# Examples - ///``` - ///use http_req::response::Response; + /// # Examples + /// ``` + /// use http_req::response::Response; /// - ///const RESPONSE: &[u8; 129] = b"HTTP/1.1 200 OK\r\n\ - /// Date: Sat, 11 Jan 2003 02:44:04 GMT\r\n\ - /// Content-Type: text/html\r\n\ - /// Content-Length: 100\r\n\r\n\ - /// hello\r\n\r\nhello"; - ///let mut body = Vec::new(); + /// const RESPONSE: &[u8; 129] = b"HTTP/1.1 200 OK\r\n\ + /// Date: Sat, 11 Jan 2003 02:44:04 GMT\r\n\ + /// Content-Type: text/html\r\n\ + /// Content-Length: 100\r\n\r\n\ + /// hello\r\n\r\nhello"; + /// let mut body = Vec::new(); /// - ///let response = Response::try_from(RESPONSE, &mut body).unwrap(); - ///``` + /// let response = Response::try_from(RESPONSE, &mut body).unwrap(); + /// ``` pub fn try_from(res: &[u8], writer: &mut T) -> Result { if res.is_empty() { Err(Error::Parse(ParseErr::Empty)) @@ -76,103 +76,103 @@ impl Response { } } - ///Returns status code of this `Response`. + /// Returns status code of this `Response`. /// - ///# Examples - ///``` - ///use http_req::response::{Response, StatusCode}; + /// # Examples + /// ``` + /// use http_req::response::{Response, StatusCode}; /// - ///const RESPONSE: &[u8; 129] = b"HTTP/1.1 200 OK\r\n\ - /// Date: Sat, 11 Jan 2003 02:44:04 GMT\r\n\ - /// Content-Type: text/html\r\n\ - /// Content-Length: 100\r\n\r\n\ - /// hello\r\n\r\nhello"; - ///let mut body = Vec::new(); + /// const RESPONSE: &[u8; 129] = b"HTTP/1.1 200 OK\r\n\ + /// Date: Sat, 11 Jan 2003 02:44:04 GMT\r\n\ + /// Content-Type: text/html\r\n\ + /// Content-Length: 100\r\n\r\n\ + /// hello\r\n\r\nhello"; + /// let mut body = Vec::new(); /// - ///let response = Response::try_from(RESPONSE, &mut body).unwrap(); - ///assert_eq!(response.status_code(), StatusCode::new(200)); - ///``` + /// let response = Response::try_from(RESPONSE, &mut body).unwrap(); + /// assert_eq!(response.status_code(), StatusCode::new(200)); + /// ``` pub const fn status_code(&self) -> StatusCode { self.status.code } - ///Returns HTTP version of this `Response`. + /// Returns HTTP version of this `Response`. /// - ///# Examples - ///``` - ///use http_req::response::Response; + /// # Examples + /// ``` + /// use http_req::response::Response; /// - ///const RESPONSE: &[u8; 129] = b"HTTP/1.1 200 OK\r\n\ - /// Date: Sat, 11 Jan 2003 02:44:04 GMT\r\n\ - /// Content-Type: text/html\r\n\ - /// Content-Length: 100\r\n\r\n\ - /// hello\r\n\r\nhello"; - ///let mut body = Vec::new(); + /// const RESPONSE: &[u8; 129] = b"HTTP/1.1 200 OK\r\n\ + /// Date: Sat, 11 Jan 2003 02:44:04 GMT\r\n\ + /// Content-Type: text/html\r\n\ + /// Content-Length: 100\r\n\r\n\ + /// hello\r\n\r\nhello"; + /// let mut body = Vec::new(); /// - ///let response = Response::try_from(RESPONSE, &mut body).unwrap(); - ///assert_eq!(response.version(), "HTTP/1.1"); - ///``` + /// let response = Response::try_from(RESPONSE, &mut body).unwrap(); + /// assert_eq!(response.version(), "HTTP/1.1"); + /// ``` pub fn version(&self) -> &str { &self.status.version } - ///Returns reason of this `Response`. + /// Returns reason of this `Response`. /// - ///# Examples - ///``` - ///use http_req::response::Response; + /// # Examples + /// ``` + /// use http_req::response::Response; /// - ///const RESPONSE: &[u8; 129] = b"HTTP/1.1 200 OK\r\n\ - /// Date: Sat, 11 Jan 2003 02:44:04 GMT\r\n\ - /// Content-Type: text/html\r\n\ - /// Content-Length: 100\r\n\r\n\ - /// hello\r\n\r\nhello"; - ///let mut body = Vec::new(); + /// const RESPONSE: &[u8; 129] = b"HTTP/1.1 200 OK\r\n\ + /// Date: Sat, 11 Jan 2003 02:44:04 GMT\r\n\ + /// Content-Type: text/html\r\n\ + /// Content-Length: 100\r\n\r\n\ + /// hello\r\n\r\nhello"; + /// let mut body = Vec::new(); /// - ///let response = Response::try_from(RESPONSE, &mut body).unwrap(); - ///assert_eq!(response.reason(), "OK"); - ///``` + /// let response = Response::try_from(RESPONSE, &mut body).unwrap(); + /// assert_eq!(response.reason(), "OK"); + /// ``` pub fn reason(&self) -> &str { &self.status.reason } - ///Returns headers of this `Response`. + /// Returns headers of this `Response`. /// - ///# Examples - ///``` - ///use http_req::response::Response; + /// # Examples + /// ``` + /// use http_req::response::Response; /// - ///const RESPONSE: &[u8; 129] = b"HTTP/1.1 200 OK\r\n\ - /// Date: Sat, 11 Jan 2003 02:44:04 GMT\r\n\ - /// Content-Type: text/html\r\n\ - /// Content-Length: 100\r\n\r\n\ - /// hello\r\n\r\nhello"; - ///let mut body = Vec::new(); + /// const RESPONSE: &[u8; 129] = b"HTTP/1.1 200 OK\r\n\ + /// Date: Sat, 11 Jan 2003 02:44:04 GMT\r\n\ + /// Content-Type: text/html\r\n\ + /// Content-Length: 100\r\n\r\n\ + /// hello\r\n\r\nhello"; + /// let mut body = Vec::new(); /// - ///let response = Response::try_from(RESPONSE, &mut body).unwrap(); - ///let headers = response.headers(); - ///``` + /// let response = Response::try_from(RESPONSE, &mut body).unwrap(); + /// let headers = response.headers(); + /// ``` pub fn headers(&self) -> &Headers { &self.headers } - ///Returns length of the content of this `Response` as a `Option`, according to information - ///included in headers. If there is no such an information, returns `None`. + /// Returns length of the content of this `Response` as a `Option`, according to information + /// included in headers. If there is no such an information, returns `None`. /// - ///# Examples - ///``` - ///use http_req::response::Response; + /// # Examples + /// ``` + /// use http_req::response::Response; /// - ///const RESPONSE: &[u8; 129] = b"HTTP/1.1 200 OK\r\n\ - /// Date: Sat, 11 Jan 2003 02:44:04 GMT\r\n\ - /// Content-Type: text/html\r\n\ - /// Content-Length: 100\r\n\r\n\ - /// hello\r\n\r\nhello"; - ///let mut body = Vec::new(); + /// const RESPONSE: &[u8; 129] = b"HTTP/1.1 200 OK\r\n\ + /// Date: Sat, 11 Jan 2003 02:44:04 GMT\r\n\ + /// Content-Type: text/html\r\n\ + /// Content-Length: 100\r\n\r\n\ + /// hello\r\n\r\nhello"; + /// let mut body = Vec::new(); /// - ///let response = Response::try_from(RESPONSE, &mut body).unwrap(); - ///assert_eq!(response.content_len().unwrap(), 100); - ///``` + /// let response = Response::try_from(RESPONSE, &mut body).unwrap(); + /// assert_eq!(response.content_len().unwrap(), 100); + /// ``` pub fn content_len(&self) -> Option { self.headers() .get("Content-Length") @@ -180,7 +180,7 @@ impl Response { } } -///Status of HTTP response +/// Status of HTTP response #[derive(PartialEq, Debug, Clone)] pub struct Status { version: String, @@ -226,98 +226,98 @@ impl str::FromStr for Status { } } -///Wrapper around `HashMap, String>` with additional functionality for parsing HTTP headers +/// Wrapper around `HashMap, String>` with additional functionality for parsing HTTP headers /// -///# Example -///``` -///use http_req::response::Headers; +/// # Example +/// ``` +/// use http_req::response::Headers; /// -///let mut headers = Headers::new(); -///headers.insert("Connection", "Close"); +/// let mut headers = Headers::new(); +/// headers.insert("Connection", "Close"); /// -///assert_eq!(headers.get("Connection"), Some(&"Close".to_string())) -///``` +/// assert_eq!(headers.get("Connection"), Some(&"Close".to_string())) +/// ``` #[derive(Debug, PartialEq, Clone, Default)] pub struct Headers(HashMap, String>); impl Headers { - ///Creates an empty `Headers`. + /// Creates an empty `Headers`. /// - ///The headers are initially created with a capacity of 0, so they will not allocate until - ///it is first inserted into. + /// The headers are initially created with a capacity of 0, so they will not allocate until + /// it is first inserted into. /// - ///# Examples - ///``` - ///use http_req::response::Headers; + /// # Examples + /// ``` + /// use http_req::response::Headers; /// - ///let mut headers = Headers::new(); - ///``` + /// let mut headers = Headers::new(); + /// ``` pub fn new() -> Headers { Headers(HashMap::new()) } - ///Creates empty `Headers` with the specified capacity. + /// Creates empty `Headers` with the specified capacity. /// - ///The headers will be able to hold at least capacity elements without reallocating. - ///If capacity is 0, the headers will not allocate. + /// The headers will be able to hold at least capacity elements without reallocating. + /// If capacity is 0, the headers will not allocate. /// - ///# Examples - ///``` - ///use http_req::response::Headers; + /// # Examples + /// ``` + /// use http_req::response::Headers; /// - ///let mut headers = Headers::with_capacity(200); - ///``` + /// let mut headers = Headers::with_capacity(200); + /// ``` pub fn with_capacity(capacity: usize) -> Headers { Headers(HashMap::with_capacity(capacity)) } - ///An iterator visiting all key-value pairs in arbitrary order. - ///The iterator's element type is `(&Ascii, &String)`. + /// An iterator visiting all key-value pairs in arbitrary order. + /// The iterator's element type is `(&Ascii, &String)`. /// - ///# Examples - ///``` - ///use http_req::response::Headers; + /// # Examples + /// ``` + /// use http_req::response::Headers; /// - ///let mut headers = Headers::new(); - ///headers.insert("Accept-Charset", "utf-8"); - ///headers.insert("Accept-Language", "en-US"); - ///headers.insert("Connection", "Close"); + /// let mut headers = Headers::new(); + /// headers.insert("Accept-Charset", "utf-8"); + /// headers.insert("Accept-Language", "en-US"); + /// headers.insert("Connection", "Close"); /// - ///let mut iterator = headers.iter(); - ///``` + /// let mut iterator = headers.iter(); + /// ``` pub fn iter(&self) -> hash_map::Iter, String> { self.0.iter() } - ///Returns a reference to the value corresponding to the key. + /// Returns a reference to the value corresponding to the key. /// - ///# Examples - ///``` - ///use http_req::response::Headers; + /// # Examples + /// ``` + /// use http_req::response::Headers; /// - ///let mut headers = Headers::new(); - ///headers.insert("Accept-Charset", "utf-8"); + /// let mut headers = Headers::new(); + /// headers.insert("Accept-Charset", "utf-8"); /// - ///assert_eq!(headers.get("Accept-Charset"), Some(&"utf-8".to_string())) - ///``` + /// assert_eq!(headers.get("Accept-Charset"), Some(&"utf-8".to_string())) + /// ``` pub fn get(&self, k: &T) -> Option<&std::string::String> { self.0.get(&Ascii::new(k.to_string())) } - ///Inserts a key-value pair into the headers. + /// Inserts a key-value pair into the headers. /// - ///If the headers did not have this key present, None is returned. + /// If the headers did not have this key present, None is returned. /// - ///If the headers did have this key present, the value is updated, and the old value is returned. - ///The key is not updated, though; this matters for types that can be == without being identical. + /// If the headers did have this key present, the value is updated, and the old value is returned. + /// The key is not updated, though; this matters for types that can be == without being identical. /// - ///# Examples - ///``` - ///use http_req::response::Headers; + /// # Examples + /// ``` + /// use http_req::response::Headers; /// - ///let mut headers = Headers::new(); - ///headers.insert("Accept-Language", "en-US"); - ///``` + /// let mut headers = Headers::new(); + /// headers.insert("Accept-Language", "en-US"); + /// ``` pub fn insert(&mut self, key: &T, val: &U) -> Option where T: ToString + ?Sized, @@ -326,16 +326,16 @@ impl Headers { self.0.insert(Ascii::new(key.to_string()), val.to_string()) } - ///Creates default headers for a HTTP request + /// Creates default headers for a HTTP request /// - ///# Examples - ///``` - ///use http_req::{response::Headers, uri::Uri}; - ///use std::convert::TryFrom; + /// # Examples + /// ``` + /// use http_req::{response::Headers, uri::Uri}; + /// use std::convert::TryFrom; /// - ///let uri: Uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); - ///let headers = Headers::default_http(&uri); - ///``` + /// let uri: Uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); + /// let headers = Headers::default_http(&uri); + /// ``` pub fn default_http(uri: &Uri) -> Headers { let mut headers = Headers::with_capacity(4); headers.insert("Host", &uri.host_header().unwrap_or_default()); @@ -390,118 +390,118 @@ impl fmt::Display for Headers { } } -///Code sent by a server in response to a client's request. +/// Code sent by a server in response to a client's request. /// -///# Example -///``` -///use http_req::response::StatusCode; +/// # Example +/// ``` +/// use http_req::response::StatusCode; /// -///const code: StatusCode = StatusCode::new(200); -///assert!(code.is_success()) -///``` +/// const code: StatusCode = StatusCode::new(200); +/// assert!(code.is_success()) +/// ``` #[derive(Debug, PartialEq, Clone, Copy)] pub struct StatusCode(u16); impl StatusCode { - ///Creates new StatusCode from `u16` value. + /// Creates new StatusCode from `u16` value. /// - ///# Examples - ///``` - ///use http_req::response::StatusCode; + /// # Examples + /// ``` + /// use http_req::response::StatusCode; /// - ///const code: StatusCode = StatusCode::new(200); - ///``` + /// const code: StatusCode = StatusCode::new(200); + /// ``` pub const fn new(code: u16) -> StatusCode { StatusCode(code) } - ///Checks if this `StatusCode` is within 100-199, which indicates that it's Informational. + /// Checks if this `StatusCode` is within 100-199, which indicates that it's Informational. /// - ///# Examples - ///``` - ///use http_req::response::StatusCode; + /// # Examples + /// ``` + /// use http_req::response::StatusCode; /// - ///const code: StatusCode = StatusCode::new(101); - ///assert!(code.is_info()) - ///``` + /// const code: StatusCode = StatusCode::new(101); + /// assert!(code.is_info()) + /// ``` pub const fn is_info(self) -> bool { self.0 >= 100 && self.0 < 200 } - ///Checks if this `StatusCode` is within 200-299, which indicates that it's Successful. + /// Checks if this `StatusCode` is within 200-299, which indicates that it's Successful. /// - ///# Examples - ///``` - ///use http_req::response::StatusCode; + /// # Examples + /// ``` + /// use http_req::response::StatusCode; /// - ///const code: StatusCode = StatusCode::new(204); - ///assert!(code.is_success()) - ///``` + /// const code: StatusCode = StatusCode::new(204); + /// assert!(code.is_success()) + /// ``` pub const fn is_success(self) -> bool { self.0 >= 200 && self.0 < 300 } - ///Checks if this `StatusCode` is within 300-399, which indicates that it's Redirection. + /// Checks if this `StatusCode` is within 300-399, which indicates that it's Redirection. /// - ///# Examples - ///``` - ///use http_req::response::StatusCode; + /// # Examples + /// ``` + /// use http_req::response::StatusCode; /// - ///const code: StatusCode = StatusCode::new(301); - ///assert!(code.is_redirect()) - ///``` + /// const code: StatusCode = StatusCode::new(301); + /// assert!(code.is_redirect()) + /// ``` pub const fn is_redirect(self) -> bool { self.0 >= 300 && self.0 < 400 } - ///Checks if this `StatusCode` is within 400-499, which indicates that it's Client Error. + /// Checks if this `StatusCode` is within 400-499, which indicates that it's Client Error. /// - ///# Examples - ///``` - ///use http_req::response::StatusCode; + /// # Examples + /// ``` + /// use http_req::response::StatusCode; /// - ///const code: StatusCode = StatusCode::new(400); - ///assert!(code.is_client_err()) - ///``` + /// const code: StatusCode = StatusCode::new(400); + /// assert!(code.is_client_err()) + /// ``` pub const fn is_client_err(self) -> bool { self.0 >= 400 && self.0 < 500 } - ///Checks if this `StatusCode` is within 500-599, which indicates that it's Server Error. + /// Checks if this `StatusCode` is within 500-599, which indicates that it's Server Error. /// - ///# Examples - ///``` - ///use http_req::response::StatusCode; + /// # Examples + /// ``` + /// use http_req::response::StatusCode; /// - ///const code: StatusCode = StatusCode::new(503); - ///assert!(code.is_server_err()) - ///``` + /// const code: StatusCode = StatusCode::new(503); + /// assert!(code.is_server_err()) + /// ``` pub const fn is_server_err(self) -> bool { self.0 >= 500 && self.0 < 600 } - ///Checks this `StatusCode` using closure `f` + /// Checks this `StatusCode` using closure `f` /// - ///# Examples - ///``` - ///use http_req::response::StatusCode; + /// # Examples + /// ``` + /// use http_req::response::StatusCode; /// - ///const code: StatusCode = StatusCode::new(203); - ///assert!(code.is(|i| i > 199 && i < 250)) - ///``` + /// const code: StatusCode = StatusCode::new(203); + /// assert!(code.is(|i| i > 199 && i < 250)) + /// ``` pub fn is bool>(self, f: F) -> bool { f(self.0) } - ///Returns `Reason-Phrase` corresponding to this `StatusCode` + /// Returns `Reason-Phrase` corresponding to this `StatusCode` /// - ///# Examples - ///``` - ///use http_req::response::StatusCode; + /// # Examples + /// ``` + /// use http_req::response::StatusCode; /// - ///const code: StatusCode = StatusCode::new(200); - ///assert_eq!(code.reason(), Some("OK")) - ///``` + /// const code: StatusCode = StatusCode::new(200); + /// assert_eq!(code.reason(), Some("OK")) + /// ``` pub const fn reason(self) -> Option<&'static str> { let reason = match self.0 { 100 => "Continue", @@ -601,7 +601,7 @@ impl str::FromStr for StatusCode { } } -///Finds elements slice `e` inside slice `data`. Returns position of the end of first match. +/// Finds elements slice `e` inside slice `data`. Returns position of the end of first match. pub fn find_slice(data: &[T], e: &[T]) -> Option where [T]: PartialEq, diff --git a/src/stream.rs b/src/stream.rs index de19204..aa37301 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -1,4 +1,6 @@ -use crate::{error::Error, tls, tls::Conn, uri::Uri}; +//! TCP stream + +use crate::{error::Error, tls, tls::Conn, uri::Uri, CR_LF, LF}; use std::{ io::{self, BufRead, Read, Write}, net::{TcpStream, ToSocketAddrs}, @@ -7,16 +9,17 @@ use std::{ time::{Duration, Instant}, }; -const CR_LF: &str = "\r\n"; const BUF_SIZE: usize = 1024 * 1024; -const RECEIVING_TIMEOUT: Duration = Duration::from_secs(60); +/// Wrapper around TCP stream for HTTP and HTTPS protocols. +/// Allows to perform common operations on underlying stream. pub enum Stream { Http(TcpStream), Https(Conn), } impl Stream { + /// Opens a TCP connection to a remote host with a connection timeout (if specified). pub fn new(uri: &Uri, connect_timeout: Option) -> Result { let host = uri.host().unwrap_or(""); let port = uri.corr_port(); @@ -29,6 +32,11 @@ impl Stream { Ok(Stream::Http(stream)) } + /// Tries to establish a secure connection over TLS. + /// + /// Checks if `uri` scheme denotes a HTTPS protocol: + /// - If yes, attemps to establish a secure connection + /// - Otherwise, returns the `stream` without any modification pub fn try_to_https( stream: Stream, uri: &Uri, @@ -55,17 +63,19 @@ impl Stream { } } + /// Sets the read timeout on the underlying TCP stream. pub fn set_read_timeout(&mut self, dur: Option) -> Result<(), Error> { match self { Stream::Http(stream) => Ok(stream.set_read_timeout(dur)?), - Stream::Https(_) => Err(Error::Tls), + Stream::Https(conn) => Ok(conn.get_mut().set_read_timeout(dur)?), } } + /// Sets the write timeout on the underlying TCP stream. pub fn set_write_timeout(&mut self, dur: Option) -> Result<(), Error> { match self { Stream::Http(stream) => Ok(stream.set_write_timeout(dur)?), - Stream::Https(_) => Err(Error::Tls), + Stream::Https(conn) => Ok(conn.get_mut().set_write_timeout(dur)?), } } } @@ -95,33 +105,23 @@ impl Write for Stream { } pub trait ThreadSend { - fn read_head(&mut self, sender: &Sender>); - fn read_body(&mut self, sender: &Sender>); + /// Reads `head` of the response and sends it via `sender` + fn send_head(&mut self, sender: &Sender>); + + /// Reads all bytes until EOF and sends them via `sender` + fn send_all(&mut self, sender: &Sender>); } impl ThreadSend for T where T: BufRead, { - fn read_head(&mut self, sender: &Sender>) { - loop { - let mut buf = Vec::new(); - - match self.read_until(0xA, &mut buf) { - Ok(0) | Err(_) => break, - Ok(len) => { - let filled_buf = buf[..len].to_owned(); - sender.send(filled_buf).unwrap(); - - if len == 2 && buf == CR_LF.as_bytes() { - break; - } - } - } - } + fn send_head(&mut self, sender: &Sender>) { + let buf = read_head(self); + sender.send(buf).unwrap(); } - fn read_body(&mut self, sender: &Sender>) { + fn send_all(&mut self, sender: &Sender>) { loop { let mut buf = vec![0; BUF_SIZE]; @@ -137,50 +137,48 @@ where } pub trait ThreadReceive { - fn write_head(&mut self, receiver: &Receiver>, deadline: Instant); - fn write_body(&mut self, receiver: &Receiver>, deadline: Instant); + /// Receives data from `receiver` and writes them into this writer. + /// Fails if `deadline` is exceeded. + fn receive(&mut self, receiver: &Receiver>, deadline: Instant); + + /// Continuosly receives data from `receiver` until there is no more data + /// or `deadline` is exceeded. Writes received data into this writer. + fn receive_all(&mut self, receiver: &Receiver>, deadline: Instant); } impl ThreadReceive for T where T: Write, { - fn write_head(&mut self, receiver: &Receiver>, deadline: Instant) { - execute_with_deadline(deadline, || { - let mut continue_reading = true; - - let data_read = match receiver.recv_timeout(RECEIVING_TIMEOUT) { + fn receive(&mut self, receiver: &Receiver>, deadline: Instant) { + execute_with_deadline(deadline, |remaining_time| { + let data_read = match receiver.recv_timeout(remaining_time) { Ok(data) => data, - Err(_) => return false, + Err(_) => return true, }; - if data_read == CR_LF.as_bytes() { - continue_reading = false; - } - self.write_all(&data_read).unwrap(); - - continue_reading + true }); } - fn write_body(&mut self, receiver: &Receiver>, deadline: Instant) { - execute_with_deadline(deadline, || { - let continue_reading = true; + fn receive_all(&mut self, receiver: &Receiver>, deadline: Instant) { + execute_with_deadline(deadline, |remaining_time| { + let is_complete = false; - let data_read = match receiver.recv_timeout(RECEIVING_TIMEOUT) { + let data_read = match receiver.recv_timeout(remaining_time) { Ok(data) => data, - Err(_) => return false, + Err(_) => return true, }; self.write_all(&data_read).unwrap(); - continue_reading + is_complete }); } } -///Connects to target host with a timeout +/// Connects to the target host with a specified timeout. pub fn connect_with_timeout(host: T, port: u16, timeout: U) -> io::Result where Duration: From, @@ -211,15 +209,46 @@ where )) } +/// Exexcutes a function in a loop until operation is completed +/// or deadline is reached. +/// +/// Function `func` needs to return `true` when the operation is complete. pub fn execute_with_deadline(deadline: Instant, mut func: F) where - F: FnMut() -> bool, + F: FnMut(Duration) -> bool, { loop { let now = Instant::now(); + let remaining_time = deadline - now; - if deadline < now || func() == false { + if deadline < now || func(remaining_time) == true { break; } } } + +/// Reads the head of HTTP response from `reader`. +/// +/// Reads from `reader` (line by line) until a blank line is found +/// indicating that all meta-information for the request has been sent. +pub fn read_head(reader: &mut B) -> Vec +where + B: BufRead, +{ + let mut buf = Vec::with_capacity(BUF_SIZE); + + loop { + match reader.read_until(LF, &mut buf) { + Ok(0) | Err(_) => break, + Ok(len) => { + let full_len = buf.len(); + + if len == 2 && &buf[full_len - 2..] == CR_LF { + break; + } + } + } + } + + buf +} diff --git a/src/tls.rs b/src/tls.rs index 5d6a713..343191d 100644 --- a/src/tls.rs +++ b/src/tls.rs @@ -1,4 +1,4 @@ -//!secure connection over TLS +//! secure connection over TLS use crate::error::Error as HttpError; use std::{ @@ -16,8 +16,9 @@ use crate::error::ParseErr; #[cfg(not(any(feature = "native-tls", feature = "rust-tls")))] compile_error!("one of the `native-tls` or `rust-tls` features must be enabled"); -///wrapper around TLS Stream, -///depends on selected TLS library +/// Wrapper around TLS Stream, depends on selected TLS library (`S: io::Read + io::Write`): +/// - native_tls: `TlsStream` +/// - rustls: `StreamOwned` pub struct Conn { #[cfg(feature = "native-tls")] stream: native_tls::TlsStream, @@ -26,6 +27,21 @@ pub struct Conn { stream: rustls::StreamOwned, } +impl Conn +where + S: io::Read + io::Write, +{ + /// Returns a reference to the underlying socket + pub fn get_ref(&self) -> &S { + self.stream.get_ref() + } + + /// Returns a mutable reference to the underlying socket + pub fn get_mut(&mut self) -> &mut S { + self.stream.get_mut() + } +} + impl io::Read for Conn { fn read(&mut self, buf: &mut [u8]) -> Result { let len = self.stream.read(buf); @@ -58,7 +74,7 @@ impl io::Write for Conn { } } -///client configuration +/// Client configuration for TLS connection. pub struct Config { #[cfg(feature = "native-tls")] extra_root_certs: Vec, @@ -84,6 +100,7 @@ impl Default for Config { ta.name_constraints, ) })); + Config { root_certs: std::sync::Arc::new(root_store), } @@ -96,17 +113,20 @@ impl Config { let f = File::open(file_path)?; let f = BufReader::new(f); let mut pem_crt = vec![]; + for line in f.lines() { let line = line?; let is_end_cert = line.contains("-----END"); pem_crt.append(&mut line.into_bytes()); pem_crt.push(b'\n'); + if is_end_cert { let crt = native_tls::Certificate::from_pem(&pem_crt)?; self.extra_root_certs.push(crt); pem_crt.clear(); } } + Ok(self) } @@ -117,9 +137,11 @@ impl Config { S: io::Read + io::Write, { let mut connector_builder = native_tls::TlsConnector::builder(); + for crt in self.extra_root_certs.iter() { connector_builder.add_root_certificate((*crt).clone()); } + let connector = connector_builder.build()?; let stream = connector.connect(hostname.as_ref(), stream)?; @@ -130,8 +152,10 @@ impl Config { pub fn add_root_cert_file_pem(&mut self, file_path: &Path) -> Result<&mut Self, HttpError> { let f = File::open(file_path)?; let mut f = BufReader::new(f); + let root_certs = std::sync::Arc::make_mut(&mut self.root_certs); root_certs.add_parsable_certificates(&rustls_pemfile::certs(&mut f)?); + Ok(self) } @@ -147,11 +171,13 @@ impl Config { .with_safe_defaults() .with_root_certificates(self.root_certs.clone()) .with_no_client_auth(); + let session = ClientConnection::new( std::sync::Arc::new(client_config), hostname.as_ref().try_into().map_err(|_| HttpError::Tls)?, ) .map_err(|e| ParseErr::Rustls(e))?; + let stream = StreamOwned::new(session, stream); Ok(Conn { stream }) diff --git a/src/uri.rs b/src/uri.rs index a3e9301..ca76801 100644 --- a/src/uri.rs +++ b/src/uri.rs @@ -11,7 +11,7 @@ use std::{ const HTTP_PORT: u16 = 80; const HTTPS_PORT: u16 = 443; -///A (half-open) range bounded inclusively below and exclusively above (start..end) with `Copy`. +/// A (half-open) range bounded inclusively below and exclusively above (start..end) with `Copy`. #[derive(Copy, Clone, Debug, PartialOrd, PartialEq)] pub struct RangeC { pub start: usize, @@ -19,14 +19,14 @@ pub struct RangeC { } impl RangeC { - ///Creates new `RangeC` with `start` and `end`. + /// Creates new `RangeC` with `start` and `end`. /// - ///# Exmaples - ///``` - ///use http_req::uri::RangeC; + /// # Exmaples + /// ``` + /// use http_req::uri::RangeC; /// - ///const range: RangeC = RangeC::new(0, 20); - ///``` + /// const range: RangeC = RangeC::new(0, 20); + /// ``` pub const fn new(start: usize, end: usize) -> RangeC { RangeC { start, end } } @@ -59,16 +59,16 @@ impl Index for String { } } -///Representation of Uniform Resource Identifier +/// Representation of Uniform Resource Identifier /// -///# Example -///``` -///use http_req::uri::Uri; -///use std::convert::TryFrom; +/// # Example +/// ``` +/// use http_req::uri::Uri; +/// use std::convert::TryFrom; /// -///let uri: Uri = Uri::try_from("https://user:info@foo.com:12/bar/baz?query#fragment").unwrap();; -///assert_eq!(uri.host(), Some("foo.com")); -///``` +/// let uri: Uri = Uri::try_from("https://user:info@foo.com:12/bar/baz?query#fragment").unwrap();; +/// assert_eq!(uri.host(), Some("foo.com")); +/// ``` #[derive(Clone, Debug, PartialEq)] pub struct Uri<'a> { inner: &'a str, @@ -80,58 +80,58 @@ pub struct Uri<'a> { } impl<'a> Uri<'a> { - ///Returns scheme of this `Uri`. + /// Returns scheme of this `Uri`. /// - ///# Example - ///``` - ///use http_req::uri::Uri; - ///use std::convert::TryFrom; + /// # Example + /// ``` + /// use http_req::uri::Uri; + /// use std::convert::TryFrom; /// - ///let uri: Uri = Uri::try_from("https://user:info@foo.com:12/bar/baz?query#fragment").unwrap();; - ///assert_eq!(uri.scheme(), "https"); - ///``` + /// let uri: Uri = Uri::try_from("https://user:info@foo.com:12/bar/baz?query#fragment").unwrap();; + /// assert_eq!(uri.scheme(), "https"); + /// ``` pub fn scheme(&self) -> &str { &self.inner[self.scheme] } - ///Returns information about the user included in this `Uri`. - /// - ///# Example - ///``` - ///use http_req::uri::Uri; - ///use std::convert::TryFrom; + /// Returns information about the user included in this `Uri`. + /// + /// # Example + /// ``` + /// use http_req::uri::Uri; + /// use std::convert::TryFrom; /// - ///let uri: Uri = Uri::try_from("https://user:info@foo.com:12/bar/baz?query#fragment").unwrap();; - ///assert_eq!(uri.user_info(), Some("user:info")); - ///``` + /// let uri: Uri = Uri::try_from("https://user:info@foo.com:12/bar/baz?query#fragment").unwrap();; + /// assert_eq!(uri.user_info(), Some("user:info")); + /// ``` pub fn user_info(&self) -> Option<&str> { self.authority.as_ref().and_then(|a| a.user_info()) } - ///Returns host of this `Uri`. - /// - ///# Example - ///``` - ///use http_req::uri::Uri; - ///use std::convert::TryFrom; + /// Returns host of this `Uri`. + /// + /// # Example + /// ``` + /// use http_req::uri::Uri; + /// use std::convert::TryFrom; /// - ///let uri: Uri = Uri::try_from("https://user:info@foo.com:12/bar/baz?query#fragment").unwrap();; - ///assert_eq!(uri.host(), Some("foo.com")); - ///``` + /// let uri: Uri = Uri::try_from("https://user:info@foo.com:12/bar/baz?query#fragment").unwrap();; + /// assert_eq!(uri.host(), Some("foo.com")); + /// ``` pub fn host(&self) -> Option<&str> { self.authority.as_ref().map(|a| a.host()) } - ///Returns host of this `Uri` to use in a header. - /// - ///# Example - ///``` - ///use http_req::uri::Uri; - ///use std::convert::TryFrom; + /// Returns host of this `Uri` to use in a header. + /// + /// # Example + /// ``` + /// use http_req::uri::Uri; + /// use std::convert::TryFrom; /// - ///let uri: Uri = Uri::try_from("https://user:info@foo.com:12/bar/baz?query#fragment").unwrap();; - ///assert_eq!(uri.host_header(), Some("foo.com:12".to_string())); - ///``` + /// let uri: Uri = Uri::try_from("https://user:info@foo.com:12/bar/baz?query#fragment").unwrap();; + /// assert_eq!(uri.host_header(), Some("foo.com:12".to_string())); + /// ``` pub fn host_header(&self) -> Option { self.host().map(|h| match self.corr_port() { HTTP_PORT | HTTPS_PORT => h.to_string(), @@ -139,31 +139,31 @@ impl<'a> Uri<'a> { }) } - ///Returns port of this `Uri` - /// - ///# Example - ///``` - ///use http_req::uri::Uri; - ///use std::convert::TryFrom; + /// Returns port of this `Uri` + /// + /// # Example + /// ``` + /// use http_req::uri::Uri; + /// use std::convert::TryFrom; /// - ///let uri: Uri = Uri::try_from("https://user:info@foo.com:12/bar/baz?query#fragment").unwrap();; - ///assert_eq!(uri.port(), Some(12)); - ///``` + /// let uri: Uri = Uri::try_from("https://user:info@foo.com:12/bar/baz?query#fragment").unwrap();; + /// assert_eq!(uri.port(), Some(12)); + /// ``` pub fn port(&self) -> Option { self.authority.as_ref().and_then(|a| a.port()) } - ///Returns port corresponding to this `Uri`. - ///Returns default port if it hasn't been set in the uri. - /// - ///# Example - ///``` - ///use http_req::uri::Uri; - ///use std::convert::TryFrom; + /// Returns port corresponding to this `Uri`. + /// Returns default port if it hasn't been set in the uri. + /// + /// # Example + /// ``` + /// use http_req::uri::Uri; + /// use std::convert::TryFrom; /// - ///let uri: Uri = Uri::try_from("https://user:info@foo.com:12/bar/baz?query#fragment").unwrap();; - ///assert_eq!(uri.corr_port(), 12); - ///``` + /// let uri: Uri = Uri::try_from("https://user:info@foo.com:12/bar/baz?query#fragment").unwrap();; + /// assert_eq!(uri.corr_port(), 12); + /// ``` pub fn corr_port(&self) -> u16 { let default_port = match self.scheme() { "https" => HTTPS_PORT, @@ -176,58 +176,58 @@ impl<'a> Uri<'a> { } } - ///Returns path of this `Uri`. - /// - ///# Example - ///``` - ///use http_req::uri::Uri; - ///use std::convert::TryFrom; + /// Returns path of this `Uri`. + /// + /// # Example + /// ``` + /// use http_req::uri::Uri; + /// use std::convert::TryFrom; /// - ///let uri: Uri = Uri::try_from("https://user:info@foo.com:12/bar/baz?query#fragment").unwrap();; - ///assert_eq!(uri.path(), Some("/bar/baz")); - ///``` + /// let uri: Uri = Uri::try_from("https://user:info@foo.com:12/bar/baz?query#fragment").unwrap();; + /// assert_eq!(uri.path(), Some("/bar/baz")); + /// ``` pub fn path(&self) -> Option<&str> { self.path.map(|r| &self.inner[r]) } - ///Returns query of this `Uri`. - /// - ///# Example - ///``` - ///use http_req::uri::Uri; - ///use std::convert::TryFrom; + /// Returns query of this `Uri`. + /// + /// # Example + /// ``` + /// use http_req::uri::Uri; + /// use std::convert::TryFrom; /// - ///let uri: Uri = Uri::try_from("https://user:info@foo.com:12/bar/baz?query#fragment").unwrap();; - ///assert_eq!(uri.query(), Some("query")); - ///``` + /// let uri: Uri = Uri::try_from("https://user:info@foo.com:12/bar/baz?query#fragment").unwrap();; + /// assert_eq!(uri.query(), Some("query")); + /// ``` pub fn query(&self) -> Option<&str> { self.query.map(|r| &self.inner[r]) } - ///Returns fragment of this `Uri`. - /// - ///# Example - ///``` - ///use http_req::uri::Uri; - ///use std::convert::TryFrom; + /// Returns fragment of this `Uri`. + /// + /// # Example + /// ``` + /// use http_req::uri::Uri; + /// use std::convert::TryFrom; /// - ///let uri: Uri = Uri::try_from("https://user:info@foo.com:12/bar/baz?query#fragment").unwrap();; - ///assert_eq!(uri.fragment(), Some("fragment")); - ///``` + /// let uri: Uri = Uri::try_from("https://user:info@foo.com:12/bar/baz?query#fragment").unwrap();; + /// assert_eq!(uri.fragment(), Some("fragment")); + /// ``` pub fn fragment(&self) -> Option<&str> { self.fragment.map(|r| &self.inner[r]) } - ///Returns resource `Uri` points to. - /// - ///# Example - ///``` - ///use http_req::uri::Uri; - ///use std::convert::TryFrom; + /// Returns resource `Uri` points to. + /// + /// # Example + /// ``` + /// use http_req::uri::Uri; + /// use std::convert::TryFrom; /// - ///let uri: Uri = Uri::try_from("https://user:info@foo.com:12/bar/baz?query#fragment").unwrap();; - ///assert_eq!(uri.resource(), "/bar/baz?query#fragment"); - ///``` + /// let uri: Uri = Uri::try_from("https://user:info@foo.com:12/bar/baz?query#fragment").unwrap();; + /// assert_eq!(uri.resource(), "/bar/baz?query#fragment"); + /// ``` pub fn resource(&self) -> &str { match self.path { Some(p) => &self.inner[p.start..], @@ -301,16 +301,16 @@ impl<'a> TryFrom<&'a str> for Uri<'a> { } } -///Authority of Uri +/// Authority of Uri /// -///# Example -///``` -///use http_req::uri::Authority; -///use std::convert::TryFrom; +/// # Example +/// ``` +/// use http_req::uri::Authority; +/// use std::convert::TryFrom; /// -///let auth: Authority = Authority::try_from("user:info@foo.com:443").unwrap(); -///assert_eq!(auth.host(), "foo.com"); -///``` +/// let auth: Authority = Authority::try_from("user:info@foo.com:443").unwrap(); +/// assert_eq!(auth.host(), "foo.com"); +/// ``` #[derive(Clone, Debug, PartialEq)] pub struct Authority<'a> { inner: &'a str, @@ -321,44 +321,44 @@ pub struct Authority<'a> { } impl<'a> Authority<'a> { - ///Returns username of this `Authority` + /// Returns username of this `Authority` /// - ///# Example - ///``` - ///use http_req::uri::Authority; - ///use std::convert::TryFrom; + /// # Example + /// ``` + /// use http_req::uri::Authority; + /// use std::convert::TryFrom; /// - ///let auth: Authority = Authority::try_from("user:info@foo.com:443").unwrap(); - ///assert_eq!(auth.username(), Some("user")); - ///``` + /// let auth: Authority = Authority::try_from("user:info@foo.com:443").unwrap(); + /// assert_eq!(auth.username(), Some("user")); + /// ``` pub fn username(&self) -> Option<&'a str> { self.username.map(|r| &self.inner[r]) } - ///Returns password of this `Authority` + /// Returns password of this `Authority` /// - ///# Example - ///``` - ///use http_req::uri::Authority; - ///use std::convert::TryFrom; + /// # Example + /// ``` + /// use http_req::uri::Authority; + /// use std::convert::TryFrom; /// - ///let auth: Authority = Authority::try_from("user:info@foo.com:443").unwrap(); - ///assert_eq!(auth.password(), Some("info")); - ///``` + /// let auth: Authority = Authority::try_from("user:info@foo.com:443").unwrap(); + /// assert_eq!(auth.password(), Some("info")); + /// ``` pub fn password(&self) -> Option<&str> { self.password.map(|r| &self.inner[r]) } - ///Returns information about the user + /// Returns information about the user /// - ///# Example - ///``` - ///use http_req::uri::Authority; - ///use std::convert::TryFrom; + /// # Example + /// ``` + /// use http_req::uri::Authority; + /// use std::convert::TryFrom; /// - ///let auth: Authority = Authority::try_from("user:info@foo.com:443").unwrap(); - ///assert_eq!(auth.user_info(), Some("user:info")); - ///``` + /// let auth: Authority = Authority::try_from("user:info@foo.com:443").unwrap(); + /// assert_eq!(auth.user_info(), Some("user:info")); + /// ``` pub fn user_info(&self) -> Option<&str> { match (&self.username, &self.password) { (Some(u), Some(p)) => Some(&self.inner[u.start..p.end]), @@ -367,30 +367,30 @@ impl<'a> Authority<'a> { } } - ///Returns host of this `Authority` + /// Returns host of this `Authority` /// - ///# Example - ///``` - ///use http_req::uri::Authority; - ///use std::convert::TryFrom; + /// # Example + /// ``` + /// use http_req::uri::Authority; + /// use std::convert::TryFrom; /// - ///let auth: Authority = Authority::try_from("user:info@foo.com:443").unwrap(); - ///assert_eq!(auth.host(), "foo.com"); - ///``` + /// let auth: Authority = Authority::try_from("user:info@foo.com:443").unwrap(); + /// assert_eq!(auth.host(), "foo.com"); + /// ``` pub fn host(&self) -> &str { &self.inner[self.host] } - ///Returns port of this `Authority` + /// Returns port of this `Authority` /// - ///# Example - ///``` - ///use http_req::uri::Authority; - ///use std::convert::TryFrom; + /// # Example + /// ``` + /// use http_req::uri::Authority; + /// use std::convert::TryFrom; /// - ///let auth: Authority = Authority::try_from("user:info@foo.com:443").unwrap(); - ///assert_eq!(auth.port(), Some(443)); - ///``` + /// let auth: Authority = Authority::try_from("user:info@foo.com:443").unwrap(); + /// assert_eq!(auth.port(), Some(443)); + /// ``` pub fn port(&self) -> Option { self.port.as_ref().map(|p| self.inner[*p].parse().unwrap()) } @@ -450,14 +450,14 @@ impl<'a> fmt::Display for Authority<'a> { } } -//Removes whitespace from `text` +/// Removes whitespace from `text` pub fn remove_spaces(text: &mut String) { text.retain(|c| !c.is_whitespace()); } -//Splits `s` by `separator`. If `separator` is found inside `s`, it will return two `Some` values -//consisting `RangeC` of each `&str`. If `separator` is at the end of `s` or it's not found, -//it will return tuple consisting `Some` with `RangeC` of entire `s` inside and None. +/// Splits `s` by `separator`. If `separator` is found inside `s`, it will return two `Some` values +/// consisting `RangeC` of each `&str`. If `separator` is at the end of `s` or it's not found, +/// it will return tuple consisting `Some` with `RangeC` of entire `s` inside and None. fn get_chunks<'a>( s: &'a str, range: Option, From 1f1a17124522764374d2f9826e3ddb292ae8935b Mon Sep 17 00:00:00 2001 From: jayjamesjay Date: Wed, 26 Jun 2024 12:01:28 +0200 Subject: [PATCH 37/47] tests for stream --- Cargo.lock | 4 +- README.md | 2 +- src/chunked.rs | 17 +-- src/error.rs | 24 ++-- src/request.rs | 22 +++- src/stream.rs | 292 ++++++++++++++++++++++++++++++++++++++++++++++--- src/tls.rs | 5 +- 7 files changed, 313 insertions(+), 53 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f971aaf..e24d19e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -248,9 +248,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.21.11" +version = "0.21.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fecbfb7b1444f477b345853b1fce097a2c6fb637b2bfb87e6bc5db0f043fae4" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", "ring", diff --git a/README.md b/README.md index e2cf333..b5d6db8 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # http_req > [!CAUTION] -> V0.11.0 introduces major changes to design of `RequestBuilder` and `Request`. Please review [documentation](https://docs.rs/http_req/0.11.0/http_req/) before migrating from previous versions. +> v0.11.0 introduces major changes to design of `RequestBuilder` and `Request`. Please review [documentation](https://docs.rs/http_req/0.11.0/http_req/) before migrating from previous versions. [![Rust](https://github.com/jayjamesjay/http_req/actions/workflows/rust.yml/badge.svg)](https://github.com/jayjamesjay/http_req/actions/workflows/rust.yml) [![Crates.io](https://img.shields.io/badge/crates.io-v0.11.0-orange.svg?longCache=true)](https://crates.io/crates/http_req) diff --git a/src/chunked.rs b/src/chunked.rs index 44121fb..8cc4487 100644 --- a/src/chunked.rs +++ b/src/chunked.rs @@ -1,14 +1,13 @@ -//! implements the wire protocol for HTTP's "chunked" Transfer-Encoding. +//! support for Transfer-Encoding: chunked use crate::CR_LF; -/// -/// It's a Rust version of the reference implementation in [Go][1]. -/// -/// [1]: https://golang.google.cn/src/net/http/internal/chunked.go -/// use std::io::{self, BufRead, BufReader, Error, ErrorKind, Read}; const MAX_LINE_LENGTH: usize = 4096; +/// Implements the wire protocol for HTTP's Transfer-Encoding: chunked. +/// +/// It's a Rust version of the [reference implementation in Go](https://golang.google.cn/src/net/http/internal/chunked.go) +/// pub struct ChunkReader { check_end: bool, eof: bool, @@ -103,7 +102,10 @@ impl BufRead for ChunkReader { } } -impl From> for ChunkReader { +impl From> for ChunkReader +where + R: Read, +{ fn from(value: BufReader) -> Self { ChunkReader { check_end: false, @@ -119,6 +121,7 @@ impl ChunkReader where R: Read, { + /// Creates a new `ChunkReader` from `reader` pub fn new(reader: R) -> Self where R: Read, diff --git a/src/error.rs b/src/error.rs index f653c81..7168732 100644 --- a/src/error.rs +++ b/src/error.rs @@ -10,8 +10,6 @@ pub enum ParseErr { UriErr, Invalid, Empty, - #[cfg(feature = "rust-tls")] - Rustls(rustls::Error), } impl error::Error for ParseErr { @@ -22,8 +20,6 @@ impl error::Error for ParseErr { Utf8(e) => Some(e), Int(e) => Some(e), StatusErr | HeadersErr | UriErr | Invalid | Empty => None, - #[cfg(feature = "rust-tls")] - Rustls(e) => Some(e), } } } @@ -40,20 +36,11 @@ impl fmt::Display for ParseErr { StatusErr => "status line contains invalid values", HeadersErr => "headers contain invalid values", UriErr => "uri contains invalid characters", - #[cfg(feature = "rust-tls")] - Rustls(_) => "rustls error", }; write!(f, "ParseErr: {}", err) } } -#[cfg(feature = "rust-tls")] -impl From for ParseErr { - fn from(e: rustls::Error) -> Self { - ParseErr::Rustls(e) - } -} - impl From for ParseErr { fn from(e: num::ParseIntError) -> Self { ParseErr::Int(e) @@ -93,9 +80,9 @@ impl fmt::Display for Error { let err = match self { IO(_) => "IO error", - Tls => "TLS error", - Timeout(_) => "Timeout error", Parse(err) => return err.fmt(f), + Timeout(_) => "Timeout error", + Tls => "TLS error", }; write!(f, "Error: {}", err) } @@ -138,3 +125,10 @@ impl From for Error { Error::Timeout(e) } } + +#[cfg(feature = "rust-tls")] +impl From for Error { + fn from(_e: rustls::Error) -> Self { + Error::Tls + } +} diff --git a/src/request.rs b/src/request.rs index 246048f..cb42f3b 100644 --- a/src/request.rs +++ b/src/request.rs @@ -16,7 +16,7 @@ use std::{ }; const CR_LF: &str = "\r\n"; -const DEFAULT_REQ_TIMEOUT: u64 = 12 * 60 * 60; +const DEFAULT_REQ_TIMEOUT: u64 = 60 * 60; /// HTTP request methods #[derive(Debug, PartialEq, Clone, Copy)] @@ -601,7 +601,7 @@ impl<'a> Request<'a> { }); //Receive and process `head` of the response. - raw_response_head.receive(&receiver, deadline); + raw_response_head.receive(&receiver, deadline)?; let response = Response::from_head(&raw_response_head)?; let content_len = response.content_len().unwrap_or(1); @@ -622,7 +622,7 @@ impl<'a> Request<'a> { //Receive and process `body`` of the response. if content_len > 0 { - writer.receive_all(&receiver, deadline); + writer.receive_all(&receiver, deadline)?; } Ok(response) @@ -874,6 +874,16 @@ mod tests { assert_eq!(request.write_timeout, Some(Duration::from_nanos(100))); } + #[test] + fn request_timeout() { + let uri = Uri::try_from(URI).unwrap(); + let mut request = Request::new(&uri); + let timeout = Duration::from_secs(360); + + request.timeout(timeout); + assert_eq!(request.timeout, timeout); + } + #[test] fn request_send() { let mut writer = Vec::new(); @@ -885,7 +895,7 @@ mod tests { #[ignore] #[test] - fn request_get() { + fn fn_get() { let mut writer = Vec::new(); let res = get(URI, &mut writer).unwrap(); @@ -899,7 +909,7 @@ mod tests { #[ignore] #[test] - fn request_head() { + fn fn_head() { let res = head(URI).unwrap(); assert_ne!(res.status_code(), UNSUCCESS_CODE); @@ -909,7 +919,7 @@ mod tests { #[ignore] #[test] - fn request_post() { + fn fn_post() { let mut writer = Vec::new(); let res = post(URI, &BODY, &mut writer).unwrap(); diff --git a/src/stream.rs b/src/stream.rs index aa37301..bf2c773 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -5,7 +5,7 @@ use std::{ io::{self, BufRead, Read, Write}, net::{TcpStream, ToSocketAddrs}, path::Path, - sync::mpsc::{Receiver, Sender}, + sync::mpsc::{Receiver, RecvTimeoutError, Sender}, time::{Duration, Instant}, }; @@ -104,6 +104,7 @@ impl Write for Stream { } } +/// Trait that allows to send data from readers to other threads pub trait ThreadSend { /// Reads `head` of the response and sends it via `sender` fn send_head(&mut self, sender: &Sender>); @@ -136,45 +137,58 @@ where } } +/// Trait that allows to receive data from receivers pub trait ThreadReceive { /// Receives data from `receiver` and writes them into this writer. /// Fails if `deadline` is exceeded. - fn receive(&mut self, receiver: &Receiver>, deadline: Instant); + fn receive(&mut self, receiver: &Receiver>, deadline: Instant) -> Result<(), Error>; /// Continuosly receives data from `receiver` until there is no more data /// or `deadline` is exceeded. Writes received data into this writer. - fn receive_all(&mut self, receiver: &Receiver>, deadline: Instant); + fn receive_all(&mut self, receiver: &Receiver>, deadline: Instant) + -> Result<(), Error>; } impl ThreadReceive for T where T: Write, { - fn receive(&mut self, receiver: &Receiver>, deadline: Instant) { - execute_with_deadline(deadline, |remaining_time| { - let data_read = match receiver.recv_timeout(remaining_time) { - Ok(data) => data, - Err(_) => return true, - }; + fn receive(&mut self, receiver: &Receiver>, deadline: Instant) -> Result<(), Error> { + let now = Instant::now(); + let data_read = receiver.recv_timeout(deadline - now)?; - self.write_all(&data_read).unwrap(); - true - }); + Ok(self.write_all(&data_read)?) } - fn receive_all(&mut self, receiver: &Receiver>, deadline: Instant) { + fn receive_all( + &mut self, + receiver: &Receiver>, + deadline: Instant, + ) -> Result<(), Error> { + let mut result = Ok(()); + execute_with_deadline(deadline, |remaining_time| { - let is_complete = false; + let mut is_complete = false; let data_read = match receiver.recv_timeout(remaining_time) { Ok(data) => data, - Err(_) => return true, + Err(e) => { + if e == RecvTimeoutError::Timeout { + result = Err(Error::Timeout(RecvTimeoutError::Timeout)); + } + return true; + } }; - self.write_all(&data_read).unwrap(); + if let Err(e) = self.write_all(&data_read).map_err(|e| Error::IO(e)) { + result = Err(e); + is_complete = true; + } is_complete }); + + Ok(result?) } } @@ -209,10 +223,14 @@ where )) } -/// Exexcutes a function in a loop until operation is completed -/// or deadline is reached. +/// Exexcutes a function in a loop until operation is completed or deadline is exceeded. +/// +/// It checks if a timeout was exceeded every iteration, therefore it limits +/// how many time a specific function can be called before deadline. +/// However a deadline may be exceeded if a single function call takes too much time. /// /// Function `func` needs to return `true` when the operation is complete. +/// pub fn execute_with_deadline(deadline: Instant, mut func: F) where F: FnMut(Duration) -> bool, @@ -252,3 +270,241 @@ where buf } + +#[cfg(test)] +mod tests { + use super::*; + use std::{io::BufReader, sync::mpsc, thread}; + + const URI: &str = "http://doc.rust-lang.org/std/string/index.html"; + const URI_S: &str = "https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol"; + const TIMEOUT: Duration = Duration::from_secs(3); + const RESPONSE: &[u8; 129] = b"HTTP/1.1 200 OK\r\n\ + Date: Sat, 11 Jan 2003 02:44:04 GMT\r\n\ + Content-Type: text/html\r\n\ + Content-Length: 100\r\n\r\n\ + hello\r\n\r\nhello"; + + const RESPONSE_H: &[u8; 102] = b"HTTP/1.1 200 OK\r\n\ + Date: Sat, 11 Jan 2003 02:44:04 GMT\r\n\ + Content-Type: text/html\r\n\ + Content-Length: 100\r\n\r\n"; + + #[test] + fn stream_new() { + { + let uri = Uri::try_from(URI).unwrap(); + let stream = Stream::new(&uri, None); + + assert!(stream.is_ok()); + } + { + let uri = Uri::try_from(URI).unwrap(); + let stream = Stream::new(&uri, Some(TIMEOUT)); + + assert!(stream.is_ok()); + } + } + + #[test] + fn stream_try_to_https() { + { + let uri = Uri::try_from(URI_S).unwrap(); + let stream = Stream::new(&uri, None).unwrap(); + let https_stream = Stream::try_to_https(stream, &uri, None); + + assert!(https_stream.is_ok()); + + // Scheme is `https`, therefore stream should be converted into HTTPS variant + match https_stream.unwrap() { + Stream::Http(_) => assert!(false), + Stream::Https(_) => assert!(true), + } + } + { + let uri = Uri::try_from(URI).unwrap(); + let stream = Stream::new(&uri, None).unwrap(); + let https_stream = Stream::try_to_https(stream, &uri, None); + + assert!(https_stream.is_ok()); + + // Scheme is `http`, therefore stream should returned without changes + match https_stream.unwrap() { + Stream::Http(_) => assert!(true), + Stream::Https(_) => assert!(false), + } + } + } + + #[test] + fn stream_set_read_timeot() { + { + let uri = Uri::try_from(URI).unwrap(); + let mut stream = Stream::new(&uri, None).unwrap(); + stream.set_read_timeout(Some(TIMEOUT)).unwrap(); + + let inner_read_timeout = if let Stream::Http(inner) = stream { + inner.read_timeout().unwrap() + } else { + None + }; + + assert_eq!(inner_read_timeout, Some(TIMEOUT)); + } + { + let uri = Uri::try_from(URI_S).unwrap(); + let mut stream = Stream::new(&uri, None).unwrap(); + stream = Stream::try_to_https(stream, &uri, None).unwrap(); + stream.set_read_timeout(Some(TIMEOUT)).unwrap(); + + let inner_read_timeout = if let Stream::Https(inner) = stream { + inner.get_ref().read_timeout().unwrap() + } else { + None + }; + + assert_eq!(inner_read_timeout, Some(TIMEOUT)); + } + } + + #[test] + fn stream_set_write_timeot() { + { + let uri = Uri::try_from(URI).unwrap(); + let mut stream = Stream::new(&uri, None).unwrap(); + stream.set_write_timeout(Some(TIMEOUT)).unwrap(); + + let inner_read_timeout = if let Stream::Http(inner) = stream { + inner.write_timeout().unwrap() + } else { + None + }; + + assert_eq!(inner_read_timeout, Some(TIMEOUT)); + } + { + let uri = Uri::try_from(URI_S).unwrap(); + let mut stream = Stream::new(&uri, None).unwrap(); + stream = Stream::try_to_https(stream, &uri, None).unwrap(); + stream.set_write_timeout(Some(TIMEOUT)).unwrap(); + + let inner_read_timeout = if let Stream::Https(inner) = stream { + inner.get_ref().write_timeout().unwrap() + } else { + None + }; + + assert_eq!(inner_read_timeout, Some(TIMEOUT)); + } + } + + #[test] + fn thread_send_send_head() { + let (sender, receiver) = mpsc::channel(); + + thread::spawn(move || { + let mut reader = BufReader::new(RESPONSE.as_slice()); + reader.send_head(&sender); + }); + + let raw_head = receiver.recv().unwrap(); + assert_eq!(raw_head, RESPONSE_H); + } + + #[test] + fn thread_send_send_all() { + let (sender, receiver) = mpsc::channel(); + + thread::spawn(move || { + let mut reader = BufReader::new(RESPONSE.as_slice()); + reader.send_all(&sender); + }); + + let raw_head = receiver.recv().unwrap(); + assert_eq!(raw_head, RESPONSE); + } + + #[test] + fn thread_receive_receive() { + let (sender, receiver) = mpsc::channel(); + let deadline = Instant::now() + TIMEOUT; + + thread::spawn(move || { + let res = [RESPONSE[..50].to_vec(), RESPONSE[50..].to_vec()]; + + for part in res { + sender.send(part).unwrap(); + } + }); + + let mut buf = Vec::with_capacity(BUF_SIZE); + buf.receive(&receiver, deadline).unwrap(); + + assert_eq!(buf, RESPONSE[..50]); + } + + #[test] + fn thread_receive_receive_all() { + let (sender, receiver) = mpsc::channel(); + let deadline = Instant::now() + TIMEOUT; + + thread::spawn(move || { + let res = [RESPONSE[..50].to_vec(), RESPONSE[50..].to_vec()]; + + for part in res { + sender.send(part).unwrap(); + } + }); + + let mut buf = Vec::with_capacity(BUF_SIZE); + buf.receive_all(&receiver, deadline).unwrap(); + + assert_eq!(buf, RESPONSE); + } + + #[ignore] + #[test] + fn fn_execute_with_deadline() { + { + let star_time = Instant::now(); + let deadline = star_time + TIMEOUT; + + execute_with_deadline(deadline, |_| { + let sleep_time = Duration::from_millis(500); + thread::sleep(sleep_time); + + false + }); + + let end_time = Instant::now(); + let total_time = end_time.duration_since(star_time).as_secs(); + + assert_eq!(total_time, TIMEOUT.as_secs()); + } + { + let star_time = Instant::now(); + let deadline = star_time + TIMEOUT; + + execute_with_deadline(deadline, |_| { + let sleep_time = Duration::from_secs(1); + thread::sleep(sleep_time); + + true + }); + + let end_time = Instant::now(); + let total_time = end_time.duration_since(star_time).as_secs(); + + assert_eq!(total_time, 1); + } + } + + #[test] + fn fn_read_head() { + let reader = RESPONSE.as_slice(); + let mut buf_reader = BufReader::new(reader); + let raw_head = read_head(&mut buf_reader); + + assert_eq!(raw_head, RESPONSE_H); + } +} diff --git a/src/tls.rs b/src/tls.rs index 343191d..f7ec4dd 100644 --- a/src/tls.rs +++ b/src/tls.rs @@ -10,9 +10,6 @@ use std::{ #[cfg(feature = "native-tls")] use std::io::prelude::*; -#[cfg(feature = "rust-tls")] -use crate::error::ParseErr; - #[cfg(not(any(feature = "native-tls", feature = "rust-tls")))] compile_error!("one of the `native-tls` or `rust-tls` features must be enabled"); @@ -176,7 +173,7 @@ impl Config { std::sync::Arc::new(client_config), hostname.as_ref().try_into().map_err(|_| HttpError::Tls)?, ) - .map_err(|e| ParseErr::Rustls(e))?; + .map_err(|_| HttpError::Tls)?; let stream = StreamOwned::new(session, stream); From 44ab978c7ec6b22858ca38fa42151bc68c9653bf Mon Sep 17 00:00:00 2001 From: jayjamesjay Date: Wed, 26 Jun 2024 19:55:55 +0200 Subject: [PATCH 38/47] update rustls dependencies --- Cargo.lock | 333 +++++++++++++++++++++++++++++++++++++++++++++---- Cargo.toml | 13 +- src/chunked.rs | 6 +- src/request.rs | 12 +- src/stream.rs | 6 +- src/tls.rs | 47 ++++--- 6 files changed, 365 insertions(+), 52 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e24d19e..46d9c60 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,11 +2,70 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "aws-lc-rs" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf7d844e282b4b56750b2d4e893b2205581ded8709fddd2b6aa5418c150ca877" +dependencies = [ + "aws-lc-sys", + "mirai-annotations", + "paste", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3a2c29203f6bf296d01141cc8bb9dbd5ecd4c27843f2ee0767bcd5985a927da" +dependencies = [ + "bindgen", + "cc", + "cmake", + "dunce", + "fs_extra", + "libc", + "paste", +] + [[package]] name = "base64" -version = "0.21.7" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bindgen" +version = "0.69.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" +dependencies = [ + "bitflags 2.5.0", + "cexpr", + "clang-sys", + "itertools", + "lazy_static", + "lazycell", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", + "which", +] [[package]] name = "bitflags" @@ -25,6 +84,20 @@ name = "cc" version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d32a725bc159af97c3e629873bb9f88fb8cf8a4867175f76dc987815ea07c83b" +dependencies = [ + "jobserver", + "libc", + "once_cell", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] [[package]] name = "cfg-if" @@ -32,6 +105,26 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "cmake" +version = "0.1.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130" +dependencies = [ + "cc", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -48,6 +141,18 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +[[package]] +name = "dunce" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + [[package]] name = "errno" version = "0.3.8" @@ -79,6 +184,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + [[package]] name = "getrandom" version = "0.2.14" @@ -90,6 +201,21 @@ dependencies = [ "wasi", ] +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys", +] + [[package]] name = "http_req" version = "0.11.0" @@ -97,23 +223,58 @@ dependencies = [ "native-tls", "rustls", "rustls-pemfile", + "rustls-pki-types", "unicase", "webpki", "webpki-roots", ] +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "jobserver" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" +dependencies = [ + "libc", +] + [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "libc" version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +[[package]] +name = "libloading" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e310b3a6b5907f99202fcdb4960ff45b93735d7c7d96b760fcff8db2dc0e103d" +dependencies = [ + "cfg-if", + "windows-targets", +] + [[package]] name = "linux-raw-sys" version = "0.4.13" @@ -126,6 +287,24 @@ version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "mirai-annotations" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9be0862c1b3f26a88803c4a49de6889c10e608b3ee9344e6ef5b45fb37ad3d1" + [[package]] name = "native-tls" version = "0.2.11" @@ -144,6 +323,16 @@ dependencies = [ "tempfile", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "once_cell" version = "1.19.0" @@ -194,12 +383,28 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + [[package]] name = "pkg-config" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +[[package]] +name = "prettyplease" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" +dependencies = [ + "proc-macro2", + "syn", +] + [[package]] name = "proc-macro2" version = "1.0.81" @@ -218,6 +423,35 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "regex" +version = "1.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + [[package]] name = "ring" version = "0.17.8" @@ -233,6 +467,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustix" version = "0.38.32" @@ -248,32 +488,44 @@ dependencies = [ [[package]] name = "rustls" -version = "0.21.12" +version = "0.23.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +checksum = "05cff451f60db80f490f3c182b77c35260baace73209e9cdbbe526bfe3a4d402" dependencies = [ + "aws-lc-rs", "log", - "ring", + "once_cell", + "rustls-pki-types", "rustls-webpki", - "sct", + "subtle", + "zeroize", ] [[package]] name = "rustls-pemfile" -version = "1.0.4" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" dependencies = [ "base64", + "rustls-pki-types", ] +[[package]] +name = "rustls-pki-types" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" + [[package]] name = "rustls-webpki" -version = "0.101.7" +version = "0.102.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +checksum = "ff448f7e92e913c4b7d4c6d8e4540a1724b319b4152b8aef6d4cf8339712b33e" dependencies = [ + "aws-lc-rs", "ring", + "rustls-pki-types", "untrusted", ] @@ -286,16 +538,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "sct" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" -dependencies = [ - "ring", - "untrusted", -] - [[package]] name = "security-framework" version = "2.10.0" @@ -319,12 +561,24 @@ dependencies = [ "libc", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "spin" version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "syn" version = "2.0.60" @@ -399,9 +653,24 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.25.4" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd7c23921eeb1713a4e851530e9b9756e4fb0e89978582942612524cf09f01cd" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "which" +version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] [[package]] name = "windows-sys" @@ -475,3 +744,23 @@ name = "windows_x86_64_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml index f5d04a6..bb5d947 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,18 +15,18 @@ unicase = "^2.7" [features] default = ["native-tls"] -rust-tls = ["rustls", "webpki", "webpki-roots", "rustls-pemfile"] +rust-tls = ["rustls", "rustls-pki-types", "webpki", "webpki-roots", "rustls-pemfile"] [dependencies.native-tls] version = "^0.2" optional = true [dependencies.rustls] -version = "^0.21" +version = "^0.23" optional = true [dependencies.rustls-pemfile] -version = "^1.0" +version = "^2.1" optional = true [dependencies.webpki] @@ -34,5 +34,10 @@ version = "^0.22" optional = true [dependencies.webpki-roots] -version = "^0.25" +version = "^0.26" optional = true + +[dependencies.rustls-pki-types] +version = "^1.7" +features = ["alloc"] +optional = true \ No newline at end of file diff --git a/src/chunked.rs b/src/chunked.rs index 8cc4487..9b4616e 100644 --- a/src/chunked.rs +++ b/src/chunked.rs @@ -7,7 +7,6 @@ const MAX_LINE_LENGTH: usize = 4096; /// Implements the wire protocol for HTTP's Transfer-Encoding: chunked. /// /// It's a Rust version of the [reference implementation in Go](https://golang.google.cn/src/net/http/internal/chunked.go) -/// pub struct ChunkReader { check_end: bool, eof: bool, @@ -92,7 +91,10 @@ where } } -impl BufRead for ChunkReader { +impl BufRead for ChunkReader +where + R: Read, +{ fn fill_buf(&mut self) -> io::Result<&[u8]> { self.reader.fill_buf() } diff --git a/src/request.rs b/src/request.rs index cb42f3b..d41f40d 100644 --- a/src/request.rs +++ b/src/request.rs @@ -568,24 +568,24 @@ impl<'a> Request<'a> { where T: Write, { - //Set up a stream. + // Set up a stream. let mut stream = Stream::new(self.inner.uri, self.connect_timeout)?; stream.set_read_timeout(self.read_timeout)?; stream.set_write_timeout(self.write_timeout)?; stream = Stream::try_to_https(stream, self.inner.uri, self.root_cert_file_pem)?; - //Send the request message to stream. + // Send the request message to stream. let request_msg = self.inner.parse(); stream.write_all(&request_msg)?; - //Set up variables + // Set up variables let deadline = Instant::now() + self.timeout; let (sender, receiver) = mpsc::channel(); let (sender_supp, receiver_supp) = mpsc::channel(); let mut raw_response_head: Vec = Vec::new(); let mut buf_reader = BufReader::new(stream); - //Read from the stream and send over data via `sender`. + // Read from the stream and send over data via `sender`. thread::spawn(move || { buf_reader.send_head(&sender); @@ -600,7 +600,7 @@ impl<'a> Request<'a> { } }); - //Receive and process `head` of the response. + // Receive and process `head` of the response. raw_response_head.receive(&receiver, deadline)?; let response = Response::from_head(&raw_response_head)?; @@ -620,7 +620,7 @@ impl<'a> Request<'a> { sender_supp.send(params).unwrap(); - //Receive and process `body`` of the response. + // Receive and process `body` of the response. if content_len > 0 { writer.receive_all(&receiver, deadline)?; } diff --git a/src/stream.rs b/src/stream.rs index bf2c773..f0c8d87 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -9,7 +9,7 @@ use std::{ time::{Duration, Instant}, }; -const BUF_SIZE: usize = 1024 * 1024; +const BUF_SIZE: usize = 16 * 1000; /// Wrapper around TCP stream for HTTP and HTTPS protocols. /// Allows to perform common operations on underlying stream. @@ -124,12 +124,12 @@ where fn send_all(&mut self, sender: &Sender>) { loop { - let mut buf = vec![0; BUF_SIZE]; + let mut buf = [0; BUF_SIZE]; match self.read(&mut buf) { Ok(0) | Err(_) => break, Ok(len) => { - let filled_buf = buf[..len].to_owned(); + let filled_buf = buf[..len].to_vec(); sender.send(filled_buf).unwrap(); } } diff --git a/src/tls.rs b/src/tls.rs index f7ec4dd..9cd771e 100644 --- a/src/tls.rs +++ b/src/tls.rs @@ -10,6 +10,11 @@ use std::{ #[cfg(feature = "native-tls")] use std::io::prelude::*; +#[cfg(feature = "rust-tls")] +use rustls::{ClientConnection, StreamOwned}; +#[cfg(feature = "rust-tls")] +use rustls_pki_types::ServerName; + #[cfg(not(any(feature = "native-tls", feature = "rust-tls")))] compile_error!("one of the `native-tls` or `rust-tls` features must be enabled"); @@ -28,18 +33,21 @@ impl Conn where S: io::Read + io::Write, { - /// Returns a reference to the underlying socket + /// Returns a reference to the underlying socket. pub fn get_ref(&self) -> &S { self.stream.get_ref() } - /// Returns a mutable reference to the underlying socket + /// Returns a mutable reference to the underlying socket. pub fn get_mut(&mut self) -> &mut S { self.stream.get_mut() } } -impl io::Read for Conn { +impl io::Read for Conn +where + S: io::Read + io::Write, +{ fn read(&mut self, buf: &mut [u8]) -> Result { let len = self.stream.read(buf); @@ -89,14 +97,9 @@ impl Default for Config { #[cfg(feature = "rust-tls")] fn default() -> Self { - let mut root_store = rustls::RootCertStore::empty(); - root_store.add_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.iter().map(|ta| { - rustls::OwnedTrustAnchor::from_subject_spki_name_constraints( - ta.subject, - ta.spki, - ta.name_constraints, - ) - })); + let root_store = rustls::RootCertStore { + roots: webpki_roots::TLS_SERVER_ROOTS.iter().cloned().collect(), + }; Config { root_certs: std::sync::Arc::new(root_store), @@ -105,6 +108,7 @@ impl Default for Config { } impl Config { + /// Adds root certificates (X.509) from PEM file. #[cfg(feature = "native-tls")] pub fn add_root_cert_file_pem(&mut self, file_path: &Path) -> Result<&mut Self, HttpError> { let f = File::open(file_path)?; @@ -127,6 +131,7 @@ impl Config { Ok(self) } + /// Establishes a secure connection. #[cfg(feature = "native-tls")] pub fn connect(&self, hostname: H, stream: S) -> Result, HttpError> where @@ -145,33 +150,45 @@ impl Config { Ok(Conn { stream }) } + /// Adds root certificates (X.509) from a PEM file. #[cfg(feature = "rust-tls")] pub fn add_root_cert_file_pem(&mut self, file_path: &Path) -> Result<&mut Self, HttpError> { let f = File::open(file_path)?; let mut f = BufReader::new(f); let root_certs = std::sync::Arc::make_mut(&mut self.root_certs); - root_certs.add_parsable_certificates(&rustls_pemfile::certs(&mut f)?); + let mut file_certs = Vec::new(); + + for cert in rustls_pemfile::certs(&mut f) { + match cert { + Ok(item) => { + file_certs.push(item); + } + Err(e) => return Err(HttpError::IO(e)), + } + } + + root_certs.add_parsable_certificates(file_certs); Ok(self) } + /// Establishes a secure connection. #[cfg(feature = "rust-tls")] pub fn connect(&self, hostname: H, stream: S) -> Result, HttpError> where H: AsRef, S: io::Read + io::Write, { - use rustls::{ClientConnection, StreamOwned}; + let hostname = hostname.as_ref().to_string(); let client_config = rustls::ClientConfig::builder() - .with_safe_defaults() .with_root_certificates(self.root_certs.clone()) .with_no_client_auth(); let session = ClientConnection::new( std::sync::Arc::new(client_config), - hostname.as_ref().try_into().map_err(|_| HttpError::Tls)?, + ServerName::try_from(hostname).map_err(|_| HttpError::Tls)?, ) .map_err(|_| HttpError::Tls)?; From 3cbb03e1f96919dac61f4a0a0f64f5c6e643fa1d Mon Sep 17 00:00:00 2001 From: jayjamesjay Date: Thu, 27 Jun 2024 10:06:50 +0200 Subject: [PATCH 39/47] improve docs --- examples/chunked.rs | 4 ++-- examples/get.rs | 4 ++-- examples/head.rs | 4 ++-- examples/post.rs | 10 +++++----- examples/request_builder_get.rs | 18 +++++++++--------- src/lib.rs | 5 ++++- src/request.rs | 27 +++++++++++++++------------ src/stream.rs | 17 ++++++++++------- 8 files changed, 49 insertions(+), 40 deletions(-) diff --git a/examples/chunked.rs b/examples/chunked.rs index 99c490a..8e258b2 100644 --- a/examples/chunked.rs +++ b/examples/chunked.rs @@ -1,11 +1,11 @@ use http_req::request; fn main() { - //Sends a HTTP GET request and processes the response. + // Sends a HTTP GET request and processes the response. let mut body = Vec::new(); let res = request::get("https://jigsaw.w3.org/HTTP/ChunkedScript", &mut body).unwrap(); - //Prints details about the response. + // Prints details about the response. println!("Status: {} {}", res.status_code(), res.reason()); println!("Headers: {}", res.headers()); //println!("{}", String::from_utf8_lossy(&body)); diff --git a/examples/get.rs b/examples/get.rs index a14c8a7..a1d4856 100644 --- a/examples/get.rs +++ b/examples/get.rs @@ -1,10 +1,10 @@ use http_req::request; fn main() { - //Container for body of a response. + // Container for body of a response. let mut body = Vec::new(); - //Sends a HTTP GET request and processes the response. Saves body of the response to `body` variable. + // Sends a HTTP GET request and processes the response. Saves body of the response to `body` variable. let res = request::get("https://www.rust-lang.org/learn", &mut body).unwrap(); //Prints details about the response. diff --git a/examples/head.rs b/examples/head.rs index 922e278..c20e80a 100644 --- a/examples/head.rs +++ b/examples/head.rs @@ -1,10 +1,10 @@ use http_req::request; fn main() { - //Sends a HTTP HEAD request and processes the response. + // Sends a HTTP HEAD request and processes the response. let res = request::head("https://www.rust-lang.org/learn").unwrap(); - //Prints details about the response. + // Prints the details about the response. println!("Status: {} {}", res.status_code(), res.reason()); println!("Headers: {}", res.headers()); } diff --git a/examples/post.rs b/examples/post.rs index fadfb9d..e1f498b 100644 --- a/examples/post.rs +++ b/examples/post.rs @@ -1,17 +1,17 @@ use http_req::request; fn main() { - //Container for body of a response. + // Container for body of a response. let mut res_body = Vec::new(); - //Body of a request. + // Body of a request. const REQ_BODY: &[u8; 27] = b"field1=value1&field2=value2"; - //Sends a HTTP POST request and processes the response. + // Sends a HTTP POST request and processes the response. let res = request::post("https://httpbin.org/post", REQ_BODY, &mut res_body).unwrap(); - //Prints details about the response. + // Prints details about the response. println!("Status: {} {}", res.status_code(), res.reason()); println!("Headers: {}", res.headers()); - println!("{}", String::from_utf8_lossy(&res_body)); + //println!("{}", String::from_utf8_lossy(&res_body)); } diff --git a/examples/request_builder_get.rs b/examples/request_builder_get.rs index da13231..e52be18 100644 --- a/examples/request_builder_get.rs +++ b/examples/request_builder_get.rs @@ -11,35 +11,35 @@ use std::{ }; fn main() { - //Parses a URI and assigns it to a variable `addr`. + // Parses a URI and assigns it to a variable `addr`. let addr: Uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); - //Containers for a server's response. + // Containers for a server's response. let raw_head; let mut body = Vec::new(); - //Prepares a request message. + // Prepares a request message. let request_msg = RequestBuilder::new(&addr) .header("Connection", "Close") .parse(); - //Connects to a server. Uses information from `addr`. + // Connects to a server. Uses information from `addr`. let mut stream = Stream::new(&addr, Some(Duration::from_secs(60))).unwrap(); stream = Stream::try_to_https(stream, &addr, None).unwrap(); - //Makes a request to server - sends a prepared message. + // Makes a request to server. Sends the prepared message. stream.write_all(&request_msg).unwrap(); - //Wraps the stream in BufReader to make it easier to read from it. - //Reads a response from the server and saves the head to `raw_head`, and the body to `body`. + // Wraps the stream in BufReader to make it easier to read from it. + // Reads a response from the server and saves the head to `raw_head`, and the body to `body`. let mut stream = BufReader::new(stream); raw_head = stream::read_head(&mut stream); stream.read_to_end(&mut body).unwrap(); - //Parses and processes the response. + // Parses and processes the response. let response = Response::from_head(&raw_head).unwrap(); - //Prints infromation about the response. + // Prints infromation about the response. println!("Status: {} {}", response.status_code(), response.reason()); println!("Headers: {}", response.headers()); //println!("{}", String::from_utf8_lossy(&body)); diff --git a/src/lib.rs b/src/lib.rs index 3f806de..2de2c9e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,8 @@ //! Simple HTTP client with built-in HTTPS support. -//! Currently it's in heavy development and may frequently change. +//! +//! By default uses [rust-native-tls](https://github.com/sfackler/rust-native-tls), +//! which relies on TLS framework provided by OS on Windows and macOS, and OpenSSL +//! on all other platforms. But it also supports [rus-tls](https://crates.io/crates/rustls). //! //! ## Example //! Basic GET request diff --git a/src/request.rs b/src/request.rs index d41f40d..c079892 100644 --- a/src/request.rs +++ b/src/request.rs @@ -26,7 +26,9 @@ pub enum Method { POST, PUT, DELETE, + CONNECT, OPTIONS, + TRACE, PATCH, } @@ -40,7 +42,9 @@ impl fmt::Display for Method { POST => "POST", PUT => "PUT", DELETE => "DELETE", + CONNECT => "CONNECT", OPTIONS => "OPTIONS", + TRACE => "TRACE", PATCH => "PATCH", }; @@ -300,10 +304,9 @@ impl<'a> Request<'a> { /// use http_req::{request::Request, uri::Uri}; /// use std::convert::TryFrom; /// - /// let mut writer = Vec::new(); /// let uri: Uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); /// - /// let response = Request::new(&uri).send(&mut writer).unwrap();; + /// let request = Request::new(&uri); /// ``` pub fn new(uri: &'a Uri) -> Request<'a> { let mut builder = RequestBuilder::new(&uri); @@ -328,7 +331,7 @@ impl<'a> Request<'a> { /// /// let uri: Uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); /// - /// let response = Request::new(&uri) + /// let request = Request::new(&uri) /// .method(Method::HEAD); /// ``` pub fn method(&mut self, method: T) -> &mut Self @@ -348,7 +351,7 @@ impl<'a> Request<'a> { /// /// let uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); /// - /// let response = Request::new(&uri) + /// let request = Request::new(&uri) /// .version(HttpVersion::Http10); /// ``` @@ -375,7 +378,7 @@ impl<'a> Request<'a> { /// headers.insert("Host", "rust-lang.org"); /// headers.insert("Connection", "Close"); /// - /// let response = Request::new(&uri) + /// let request = Request::new(&uri) /// .headers(headers); /// ``` pub fn headers(&mut self, headers: T) -> &mut Self @@ -395,7 +398,7 @@ impl<'a> Request<'a> { /// /// let uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); /// - /// let response = Request::new(&uri) + /// let request = Request::new(&uri) /// .header("Accept-Language", "en-US"); /// ``` pub fn header(&mut self, key: &T, val: &U) -> &mut Self @@ -417,7 +420,7 @@ impl<'a> Request<'a> { /// let uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); /// const body: &[u8; 27] = b"field1=value1&field2=value2"; /// - /// let response = Request::new(&uri) + /// let request = Request::new(&uri) /// .method(Method::POST) /// .header("Content-Length", &body.len()) /// .body(body); @@ -446,7 +449,7 @@ impl<'a> Request<'a> { /// let uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); /// const time: Option = Some(Duration::from_secs(10)); /// - /// let response = Request::new(&uri) + /// let request = Request::new(&uri) /// .connect_timeout(time); /// ``` pub fn connect_timeout(&mut self, timeout: Option) -> &mut Self @@ -472,7 +475,7 @@ impl<'a> Request<'a> { /// let uri: Uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); /// const time: Option = Some(Duration::from_secs(15)); /// - /// let response = Request::new(&uri) + /// let request = Request::new(&uri) /// .read_timeout(time); /// ``` pub fn read_timeout(&mut self, timeout: Option) -> &mut Self @@ -498,7 +501,7 @@ impl<'a> Request<'a> { /// let uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); /// const time: Option = Some(Duration::from_secs(5)); /// - /// let response = Request::new(&uri) + /// let request = Request::new(&uri) /// .write_timeout(time); /// ``` pub fn write_timeout(&mut self, timeout: Option) -> &mut Self @@ -520,7 +523,7 @@ impl<'a> Request<'a> { /// let uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); /// const time: Duration = Duration::from_secs(5); /// - /// let response = Request::new(&uri) + /// let request = Request::new(&uri) /// .timeout(time); /// ``` pub fn timeout(&mut self, timeout: T) -> &mut Self @@ -541,7 +544,7 @@ impl<'a> Request<'a> { /// let uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); /// let path = Path::new("./foo/bar.txt"); /// - /// let response = Request::new(&uri) + /// let request = Request::new(&uri) /// .root_cert_file_pem(&path); /// ``` pub fn root_cert_file_pem(&mut self, file_path: &'a Path) -> &mut Self { diff --git a/src/stream.rs b/src/stream.rs index f0c8d87..1afe877 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -226,11 +226,14 @@ where /// Exexcutes a function in a loop until operation is completed or deadline is exceeded. /// /// It checks if a timeout was exceeded every iteration, therefore it limits -/// how many time a specific function can be called before deadline. -/// However a deadline may be exceeded if a single function call takes too much time. -/// -/// Function `func` needs to return `true` when the operation is complete. -/// +/// how many time a specific function can be called before deadline. +/// For the `execute_with_deadline` to meet the deadline, each call +/// to `func` needs finish before the deadline. +/// +/// Key information about function `func`: +/// - is provided with information about remaining time +/// - must ensure that its execution will not take more time than specified in `remaining_time` +/// - needs to return `true` when the operation is complete pub fn execute_with_deadline(deadline: Instant, mut func: F) where F: FnMut(Duration) -> bool, @@ -247,8 +250,8 @@ where /// Reads the head of HTTP response from `reader`. /// -/// Reads from `reader` (line by line) until a blank line is found -/// indicating that all meta-information for the request has been sent. +/// Reads from `reader` (line by line) until a blank line is identified, +/// which indicates that all meta-information has been read, pub fn read_head(reader: &mut B) -> Vec where B: BufRead, From 3cca3f82c99cc6f22be28c93ea1b346e14df2010 Mon Sep 17 00:00:00 2001 From: jayjamesjay Date: Sun, 28 Jul 2024 09:03:31 +0200 Subject: [PATCH 40/47] RedirectPolicy init --- Cargo.lock | 2 +- Cargo.toml | 2 +- README.md | 11 +- ...builder_get.rs => advanced_request_get.rs} | 4 +- src/lib.rs | 2 +- src/request.rs | 147 +++++++++++------- src/response.rs | 14 +- src/stream.rs | 11 +- src/tls.rs | 8 +- 9 files changed, 125 insertions(+), 76 deletions(-) rename examples/{request_builder_get.rs => advanced_request_get.rs} (94%) diff --git a/Cargo.lock b/Cargo.lock index 46d9c60..b8c6ab2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -218,7 +218,7 @@ dependencies = [ [[package]] name = "http_req" -version = "0.11.0" +version = "0.12.0" dependencies = [ "native-tls", "rustls", diff --git a/Cargo.toml b/Cargo.toml index bb5d947..b5b37e5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "http_req" -version = "0.11.0" +version = "0.12.0" license = "MIT" description = "simple and lightweight HTTP client with built-in HTTPS support" repository = "https://github.com/jayjamesjay/http_req" diff --git a/README.md b/README.md index b5d6db8..f23c88d 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,15 @@ # http_req > [!CAUTION] -> v0.11.0 introduces major changes to design of `RequestBuilder` and `Request`. Please review [documentation](https://docs.rs/http_req/0.11.0/http_req/) before migrating from previous versions. +> v0.12.0 replaces `RequestBuilder` with `RequestMessage`. Please review [documentation](https://docs.rs/http_req/0.12.0/http_req/) before migrating from previous versions. [![Rust](https://github.com/jayjamesjay/http_req/actions/workflows/rust.yml/badge.svg)](https://github.com/jayjamesjay/http_req/actions/workflows/rust.yml) -[![Crates.io](https://img.shields.io/badge/crates.io-v0.11.0-orange.svg?longCache=true)](https://crates.io/crates/http_req) -[![Docs.rs](https://docs.rs/http_req/badge.svg)](https://docs.rs/http_req/0.11.0/http_req/) +[![Crates.io](https://img.shields.io/badge/crates.io-v0.12.0-orange.svg?longCache=true)](https://crates.io/crates/http_req) +[![Docs.rs](https://docs.rs/http_req/badge.svg)](https://docs.rs/http_req/0.12.0/http_req/) Simple and lightweight HTTP client with built-in HTTPS support. +- HTTP and HTTPS via [rust-native-tls](https://github.com/sfackler/rust-native-tls) (or optionally [rus-tls](https://crates.io/crates/rustls)) +- Small binary size (less than 0.7 MB for basic GET request) +- Minimal amount of dependencies ## Requirements http_req by default uses [rust-native-tls](https://github.com/sfackler/rust-native-tls), @@ -32,7 +35,7 @@ Take a look at [more examples](https://github.com/jayjamesjay/http_req/tree/mast In order to use `http_req` with `rustls` in your project, add the following lines to `Cargo.toml`: ```toml [dependencies] -http_req = {version="^0.11", default-features = false, features = ["rust-tls"]} +http_req = {version="^0.12", default-features = false, features = ["rust-tls"]} ``` ## License diff --git a/examples/request_builder_get.rs b/examples/advanced_request_get.rs similarity index 94% rename from examples/request_builder_get.rs rename to examples/advanced_request_get.rs index e52be18..abf6788 100644 --- a/examples/request_builder_get.rs +++ b/examples/advanced_request_get.rs @@ -1,5 +1,5 @@ use http_req::{ - request::RequestBuilder, + request::RequestMessage, response::Response, stream::{self, Stream}, uri::Uri, @@ -19,7 +19,7 @@ fn main() { let mut body = Vec::new(); // Prepares a request message. - let request_msg = RequestBuilder::new(&addr) + let request_msg = RequestMessage::new(&addr) .header("Connection", "Close") .parse(); diff --git a/src/lib.rs b/src/lib.rs index 2de2c9e..77ad98e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,5 @@ //! Simple HTTP client with built-in HTTPS support. -//! +//! //! By default uses [rust-native-tls](https://github.com/sfackler/rust-native-tls), //! which relies on TLS framework provided by OS on Windows and macOS, and OpenSSL //! on all other platforms. But it also supports [rus-tls](https://crates.io/crates/rustls). diff --git a/src/request.rs b/src/request.rs index c079892..17e31b8 100644 --- a/src/request.rs +++ b/src/request.rs @@ -78,21 +78,55 @@ impl fmt::Display for HttpVersion { } } -/// Raw HTTP request that can be sent to any stream +pub struct RequestBuilder {} + +#[deprecated( + since = "0.12.0", + note = "RequestBuilder was replaced with RequestMessage" +)] +impl<'a> RequestBuilder { + pub fn new(uri: &'a Uri<'a>) -> RequestMessage<'a> { + RequestMessage::new(uri) + } +} + +/// Allows to control redirects +#[derive(Debug, PartialEq, Clone, Copy)] +pub enum RedirectPolicy bool> { + /// Follows redirect if limit is greater than 0. + Limit(usize), + /// Runs functions `F` to determine if redirect should be followed. + Custom(F), +} + +impl bool> RedirectPolicy { + /// Checks the policy againt specified conditions. + /// Returns `true` if redirect should be followed. + pub fn follow(&self) -> bool { + use self::RedirectPolicy::*; + + match self { + Limit(limit) => *limit > 0, + Custom(func) => func(), + } + } +} + +/// Raw HTTP request message that can be sent to any stream /// /// # Examples /// ``` /// use std::convert::TryFrom; -/// use http_req::{request::RequestBuilder, uri::Uri}; +/// use http_req::{request::RequestMessage, uri::Uri}; /// /// let addr: Uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); /// -/// let mut request_msg = RequestBuilder::new(&addr) +/// let mut request_msg = RequestMessage::new(&addr) /// .header("Connection", "Close") /// .parse(); /// ``` #[derive(Clone, Debug, PartialEq)] -pub struct RequestBuilder<'a> { +pub struct RequestMessage<'a> { uri: &'a Uri<'a>, method: Method, version: HttpVersion, @@ -100,21 +134,21 @@ pub struct RequestBuilder<'a> { body: Option<&'a [u8]>, } -impl<'a> RequestBuilder<'a> { - /// Creates a new `RequestBuilder` with default parameters +impl<'a> RequestMessage<'a> { + /// Creates a new `RequestMessage` with default parameters /// /// # Examples /// ``` /// use std::convert::TryFrom; - /// use http_req::{request::RequestBuilder, uri::Uri}; + /// use http_req::{request::RequestMessage, uri::Uri}; /// /// let addr = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); /// - /// let request_builder = RequestBuilder::new(&addr) + /// let request_msg = RequestMessage::new(&addr) /// .header("Connection", "Close"); /// ``` - pub fn new(uri: &'a Uri<'a>) -> RequestBuilder<'a> { - RequestBuilder { + pub fn new(uri: &'a Uri<'a>) -> RequestMessage<'a> { + RequestMessage { headers: Headers::default_http(uri), uri, method: Method::GET, @@ -128,11 +162,11 @@ impl<'a> RequestBuilder<'a> { /// # Examples /// ``` /// use std::convert::TryFrom; - /// use http_req::{request::{RequestBuilder, Method}, uri::Uri}; + /// use http_req::{request::{RequestMessage, Method}, uri::Uri}; /// /// let addr = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); /// - /// let request_builder = RequestBuilder::new(&addr) + /// let request_msg = RequestMessage::new(&addr) /// .method(Method::HEAD); /// ``` pub fn method(&mut self, method: T) -> &mut Self @@ -148,11 +182,11 @@ impl<'a> RequestBuilder<'a> { /// # Examples /// ``` /// use std::convert::TryFrom; - /// use http_req::{request::{RequestBuilder, HttpVersion}, uri::Uri}; + /// use http_req::{request::{RequestMessage, HttpVersion}, uri::Uri}; /// /// let addr = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); /// - /// let request_builder = RequestBuilder::new(&addr) + /// let request_msg = RequestMessage::new(&addr) /// .version(HttpVersion::Http10); /// ``` pub fn version(&mut self, version: T) -> &mut Self @@ -168,7 +202,7 @@ impl<'a> RequestBuilder<'a> { /// # Examples /// ``` /// use std::convert::TryFrom; - /// use http_req::{request::RequestBuilder, response::Headers, uri::Uri}; + /// use http_req::{request::RequestMessage, response::Headers, uri::Uri}; /// /// let addr = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); /// @@ -178,7 +212,7 @@ impl<'a> RequestBuilder<'a> { /// headers.insert("Host", "rust-lang.org"); /// headers.insert("Connection", "Close"); /// - /// let request_builder = RequestBuilder::new(&addr) + /// let request_msg = RequestMessage::new(&addr) /// .headers(headers); /// ``` pub fn headers(&mut self, headers: T) -> &mut Self @@ -194,11 +228,11 @@ impl<'a> RequestBuilder<'a> { /// # Examples /// ``` /// use std::convert::TryFrom; - /// use http_req::{request::RequestBuilder, response::Headers, uri::Uri}; + /// use http_req::{request::RequestMessage, response::Headers, uri::Uri}; /// /// let addr = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); /// - /// let request_builder = RequestBuilder::new(&addr) + /// let request_msg = RequestMessage::new(&addr) /// .header("Connection", "Close"); /// ``` pub fn header(&mut self, key: &T, val: &U) -> &mut Self @@ -215,12 +249,12 @@ impl<'a> RequestBuilder<'a> { /// # Examples /// ``` /// use std::convert::TryFrom; - /// use http_req::{request::{RequestBuilder, Method}, response::Headers, uri::Uri}; + /// use http_req::{request::{RequestMessage, Method}, response::Headers, uri::Uri}; /// /// let addr = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); /// const BODY: &[u8; 27] = b"field1=value1&field2=value2"; /// - /// let request_builder = RequestBuilder::new(&addr) + /// let request_msg = RequestMessage::new(&addr) /// .method(Method::POST) /// .body(BODY) /// .header("Content-Length", &BODY.len()) @@ -231,16 +265,16 @@ impl<'a> RequestBuilder<'a> { self } - /// Parses the request message for this `RequestBuilder` + /// Parses the request message for this `RequestMessage` /// /// # Examples /// ``` /// use std::convert::TryFrom; - /// use http_req::{request::RequestBuilder, uri::Uri}; + /// use http_req::{request::RequestMessage, uri::Uri}; /// /// let addr: Uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); /// - /// let mut request_msg = RequestBuilder::new(&addr) + /// let mut request_msg = RequestMessage::new(&addr) /// .header("Connection", "Close") /// .parse(); /// ``` @@ -288,7 +322,7 @@ impl<'a> RequestBuilder<'a> { /// #[derive(Clone, Debug, PartialEq)] pub struct Request<'a> { - inner: RequestBuilder<'a>, + messsage: RequestMessage<'a>, connect_timeout: Option, read_timeout: Option, write_timeout: Option, @@ -309,11 +343,11 @@ impl<'a> Request<'a> { /// let request = Request::new(&uri); /// ``` pub fn new(uri: &'a Uri) -> Request<'a> { - let mut builder = RequestBuilder::new(&uri); - builder.header("Connection", "Close"); + let mut message = RequestMessage::new(&uri); + message.header("Connection", "Close"); Request { - inner: builder, + messsage: message, connect_timeout: Some(Duration::from_secs(60)), read_timeout: Some(Duration::from_secs(60)), write_timeout: Some(Duration::from_secs(60)), @@ -338,7 +372,7 @@ impl<'a> Request<'a> { where Method: From, { - self.inner.method(method); + self.messsage.method(method); self } @@ -359,7 +393,7 @@ impl<'a> Request<'a> { where HttpVersion: From, { - self.inner.version(version); + self.messsage.version(version); self } @@ -385,7 +419,7 @@ impl<'a> Request<'a> { where Headers: From, { - self.inner.headers(headers); + self.messsage.headers(headers); self } @@ -406,7 +440,7 @@ impl<'a> Request<'a> { T: ToString + ?Sized, U: ToString + ?Sized, { - self.inner.header(key, val); + self.messsage.header(key, val); self } @@ -426,7 +460,7 @@ impl<'a> Request<'a> { /// .body(body); /// ``` pub fn body(&mut self, body: &'a [u8]) -> &mut Self { - self.inner.body(body); + self.messsage.body(body); self } @@ -572,13 +606,13 @@ impl<'a> Request<'a> { T: Write, { // Set up a stream. - let mut stream = Stream::new(self.inner.uri, self.connect_timeout)?; + let mut stream = Stream::new(self.messsage.uri, self.connect_timeout)?; stream.set_read_timeout(self.read_timeout)?; stream.set_write_timeout(self.write_timeout)?; - stream = Stream::try_to_https(stream, self.inner.uri, self.root_cert_file_pem)?; + stream = Stream::try_to_https(stream, self.messsage.uri, self.root_cert_file_pem)?; // Send the request message to stream. - let request_msg = self.inner.parse(); + let request_msg = self.messsage.parse(); stream.write_all(&request_msg)?; // Set up variables @@ -608,16 +642,13 @@ impl<'a> Request<'a> { let response = Response::from_head(&raw_response_head)?; let content_len = response.content_len().unwrap_or(1); - let encoding = response.headers().get("Transfer-Encoding"); let mut params = Vec::with_capacity(5); - if let Some(encode) = encoding { - if encode == "chunked" { - params.push("chunked"); - } + if response.is_chunked() { + params.push("chunked"); } - if content_len > 0 && self.inner.method != Method::HEAD { + if content_len > 0 && self.messsage.method != Method::HEAD { params.push("non-empty"); } @@ -715,22 +746,22 @@ mod tests { } #[test] - fn request_b_new() { - RequestBuilder::new(&Uri::try_from(URI).unwrap()); - RequestBuilder::new(&Uri::try_from(URI_S).unwrap()); + fn request_m_new() { + RequestMessage::new(&Uri::try_from(URI).unwrap()); + RequestMessage::new(&Uri::try_from(URI_S).unwrap()); } #[test] - fn request_b_method() { + fn request_m_method() { let uri = Uri::try_from(URI).unwrap(); - let mut req = RequestBuilder::new(&uri); + let mut req = RequestMessage::new(&uri); let req = req.method(Method::HEAD); assert_eq!(req.method, Method::HEAD); } #[test] - fn request_b_headers() { + fn request_m_headers() { let mut headers = Headers::new(); headers.insert("Accept-Charset", "utf-8"); headers.insert("Accept-Language", "en-US"); @@ -738,16 +769,16 @@ mod tests { headers.insert("Connection", "Close"); let uri = Uri::try_from(URI).unwrap(); - let mut req = RequestBuilder::new(&uri); + let mut req = RequestMessage::new(&uri); let req = req.headers(headers.clone()); assert_eq!(req.headers, headers); } #[test] - fn request_b_header() { + fn request_m_header() { let uri = Uri::try_from(URI).unwrap(); - let mut req = RequestBuilder::new(&uri); + let mut req = RequestMessage::new(&uri); let k = "Connection"; let v = "Close"; @@ -761,18 +792,18 @@ mod tests { } #[test] - fn request_b_body() { + fn request_m_body() { let uri = Uri::try_from(URI).unwrap(); - let mut req = RequestBuilder::new(&uri); + let mut req = RequestMessage::new(&uri); let req = req.body(&BODY); assert_eq!(req.body, Some(BODY.as_ref())); } #[test] - fn request_b_parse() { + fn request_m_parse() { let uri = Uri::try_from(URI).unwrap(); - let req = RequestBuilder::new(&uri); + let req = RequestMessage::new(&uri); const DEFAULT_MSG: &str = "GET /std/string/index.html HTTP/1.1\r\n\ Host: doc.rust-lang.org\r\n\r\n"; @@ -800,7 +831,7 @@ mod tests { let mut req = Request::new(&uri); req.method(Method::HEAD); - assert_eq!(req.inner.method, Method::HEAD); + assert_eq!(req.messsage.method, Method::HEAD); } #[test] @@ -815,7 +846,7 @@ mod tests { let mut req = Request::new(&uri); let req = req.headers(headers.clone()); - assert_eq!(req.inner.headers, headers); + assert_eq!(req.messsage.headers, headers); } #[test] @@ -832,7 +863,7 @@ mod tests { let req = req.header(k, v); - assert_eq!(req.inner.headers, expect_headers); + assert_eq!(req.messsage.headers, expect_headers); } #[test] @@ -841,7 +872,7 @@ mod tests { let mut req = Request::new(&uri); let req = req.body(&BODY); - assert_eq!(req.inner.body, Some(BODY.as_ref())); + assert_eq!(req.messsage.body, Some(BODY.as_ref())); } #[test] diff --git a/src/response.rs b/src/response.rs index 895e6d6..6295c0d 100644 --- a/src/response.rs +++ b/src/response.rs @@ -60,7 +60,10 @@ impl Response { /// /// let response = Response::try_from(RESPONSE, &mut body).unwrap(); /// ``` - pub fn try_from(res: &[u8], writer: &mut T) -> Result { + pub fn try_from(res: &[u8], writer: &mut T) -> Result + where + T: Write, + { if res.is_empty() { Err(Error::Parse(ParseErr::Empty)) } else { @@ -152,7 +155,7 @@ impl Response { /// let response = Response::try_from(RESPONSE, &mut body).unwrap(); /// let headers = response.headers(); /// ``` - pub fn headers(&self) -> &Headers { + pub const fn headers(&self) -> &Headers { &self.headers } @@ -178,6 +181,13 @@ impl Response { .get("Content-Length") .and_then(|len| len.parse().ok()) } + + /// Checks if Transfer-Encoding includes "chunked". + pub fn is_chunked(&self) -> bool { + self.headers() + .get("Transfer-Encoding") + .is_some_and(|encodings| encodings.contains("chunked")) + } } /// Status of HTTP response diff --git a/src/stream.rs b/src/stream.rs index 1afe877..27b0e60 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -13,6 +13,7 @@ const BUF_SIZE: usize = 16 * 1000; /// Wrapper around TCP stream for HTTP and HTTPS protocols. /// Allows to perform common operations on underlying stream. +#[derive(Debug)] pub enum Stream { Http(TcpStream), Https(Conn), @@ -226,10 +227,10 @@ where /// Exexcutes a function in a loop until operation is completed or deadline is exceeded. /// /// It checks if a timeout was exceeded every iteration, therefore it limits -/// how many time a specific function can be called before deadline. -/// For the `execute_with_deadline` to meet the deadline, each call -/// to `func` needs finish before the deadline. -/// +/// how many time a specific function can be called before deadline. +/// For the `execute_with_deadline` to meet the deadline, each call +/// to `func` needs finish before the deadline. +/// /// Key information about function `func`: /// - is provided with information about remaining time /// - must ensure that its execution will not take more time than specified in `remaining_time` @@ -250,7 +251,7 @@ where /// Reads the head of HTTP response from `reader`. /// -/// Reads from `reader` (line by line) until a blank line is identified, +/// Reads from `reader` (line by line) until a blank line is identified, /// which indicates that all meta-information has been read, pub fn read_head(reader: &mut B) -> Vec where diff --git a/src/tls.rs b/src/tls.rs index 9cd771e..edb5732 100644 --- a/src/tls.rs +++ b/src/tls.rs @@ -18,9 +18,10 @@ use rustls_pki_types::ServerName; #[cfg(not(any(feature = "native-tls", feature = "rust-tls")))] compile_error!("one of the `native-tls` or `rust-tls` features must be enabled"); -/// Wrapper around TLS Stream, depends on selected TLS library (`S: io::Read + io::Write`): +/// Wrapper around TLS Stream, depends on selected TLS library: /// - native_tls: `TlsStream` /// - rustls: `StreamOwned` +#[derive(Debug)] pub struct Conn { #[cfg(feature = "native-tls")] stream: native_tls::TlsStream, @@ -70,7 +71,10 @@ where } } -impl io::Write for Conn { +impl io::Write for Conn +where + S: io::Read + io::Write, +{ fn write(&mut self, buf: &[u8]) -> Result { self.stream.write(buf) } From 6c91c2e05eeb0a6a1f5e7d5bf3c5fe45dd884618 Mon Sep 17 00:00:00 2001 From: jayjamesjay Date: Sun, 28 Jul 2024 09:10:11 +0200 Subject: [PATCH 41/47] update dependencies --- Cargo.lock | 150 +++++++++++++++++++++++++---------------------------- Cargo.toml | 12 +++-- README.md | 4 +- 3 files changed, 82 insertions(+), 84 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 46d9c60..a7515f0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,9 +13,9 @@ dependencies = [ [[package]] name = "aws-lc-rs" -version = "1.7.3" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf7d844e282b4b56750b2d4e893b2205581ded8709fddd2b6aa5418c150ca877" +checksum = "4ae74d9bd0a7530e8afd1770739ad34b36838829d6ad61818f9230f683f5ad77" dependencies = [ "aws-lc-sys", "mirai-annotations", @@ -25,9 +25,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.18.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3a2c29203f6bf296d01141cc8bb9dbd5ecd4c27843f2ee0767bcd5985a927da" +checksum = "2e89b6941c2d1a7045538884d6e760ccfffdf8e1ffc2613d8efa74305e1f3752" dependencies = [ "bindgen", "cc", @@ -50,7 +50,7 @@ version = "0.69.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" dependencies = [ - "bitflags 2.5.0", + "bitflags", "cexpr", "clang-sys", "itertools", @@ -69,25 +69,18 @@ dependencies = [ [[package]] name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "cc" -version = "1.0.95" +version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d32a725bc159af97c3e629873bb9f88fb8cf8a4867175f76dc987815ea07c83b" +checksum = "2aba8f4e9906c7ce3c73463f62a7f0c65183ada1a2d47e397cc8810827f9694f" dependencies = [ "jobserver", "libc", - "once_cell", ] [[package]] @@ -155,9 +148,9 @@ checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "errno" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", "windows-sys", @@ -165,9 +158,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.2" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" [[package]] name = "foreign-types" @@ -192,9 +185,9 @@ checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" [[package]] name = "getrandom" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", @@ -218,7 +211,7 @@ dependencies = [ [[package]] name = "http_req" -version = "0.11.0" +version = "0.11.1" dependencies = [ "native-tls", "rustls", @@ -240,18 +233,18 @@ dependencies = [ [[package]] name = "jobserver" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" dependencies = [ "libc", ] [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "lazycell" @@ -261,15 +254,15 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.153" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "libloading" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e310b3a6b5907f99202fcdb4960ff45b93735d7c7d96b760fcff8db2dc0e103d" +checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", "windows-targets", @@ -277,15 +270,15 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "memchr" @@ -307,11 +300,10 @@ checksum = "c9be0862c1b3f26a88803c4a49de6889c10e608b3ee9344e6ef5b45fb37ad3d1" [[package]] name = "native-tls" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" dependencies = [ - "lazy_static", "libc", "log", "openssl", @@ -341,11 +333,11 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "openssl" -version = "0.10.64" +version = "0.10.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" +checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" dependencies = [ - "bitflags 2.5.0", + "bitflags", "cfg-if", "foreign-types", "libc", @@ -373,9 +365,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.102" +version = "0.9.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" +checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" dependencies = [ "cc", "libc", @@ -407,9 +399,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.81" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] @@ -475,11 +467,11 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustix" -version = "0.38.32" +version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.5.0", + "bitflags", "errno", "libc", "linux-raw-sys", @@ -488,9 +480,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.10" +version = "0.23.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05cff451f60db80f490f3c182b77c35260baace73209e9cdbbe526bfe3a4d402" +checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044" dependencies = [ "aws-lc-rs", "log", @@ -519,9 +511,9 @@ checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" [[package]] name = "rustls-webpki" -version = "0.102.4" +version = "0.102.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff448f7e92e913c4b7d4c6d8e4540a1724b319b4152b8aef6d4cf8339712b33e" +checksum = "8e6b52d4fda176fd835fdc55a835d4a89b8499cad995885a21149d5ad62f852e" dependencies = [ "aws-lc-rs", "ring", @@ -540,11 +532,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.10.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 1.3.2", + "bitflags", "core-foundation", "core-foundation-sys", "libc", @@ -553,9 +545,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.10.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f3cc463c0ef97e11c3461a9d3787412d30e8e7eb907c79180c4a57bf7c04ef" +checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" dependencies = [ "core-foundation-sys", "libc", @@ -581,9 +573,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.60" +version = "2.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" +checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" dependencies = [ "proc-macro2", "quote", @@ -631,9 +623,9 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "wasi" @@ -683,9 +675,9 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", @@ -699,51 +691,51 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "zeroize" diff --git a/Cargo.toml b/Cargo.toml index bb5d947..6f5991e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "http_req" -version = "0.11.0" +version = "0.11.1" license = "MIT" description = "simple and lightweight HTTP client with built-in HTTPS support" repository = "https://github.com/jayjamesjay/http_req" @@ -15,7 +15,13 @@ unicase = "^2.7" [features] default = ["native-tls"] -rust-tls = ["rustls", "rustls-pki-types", "webpki", "webpki-roots", "rustls-pemfile"] +rust-tls = [ + "rustls", + "rustls-pki-types", + "webpki", + "webpki-roots", + "rustls-pemfile", +] [dependencies.native-tls] version = "^0.2" @@ -40,4 +46,4 @@ optional = true [dependencies.rustls-pki-types] version = "^1.7" features = ["alloc"] -optional = true \ No newline at end of file +optional = true diff --git a/README.md b/README.md index b5d6db8..ed16c20 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,8 @@ > v0.11.0 introduces major changes to design of `RequestBuilder` and `Request`. Please review [documentation](https://docs.rs/http_req/0.11.0/http_req/) before migrating from previous versions. [![Rust](https://github.com/jayjamesjay/http_req/actions/workflows/rust.yml/badge.svg)](https://github.com/jayjamesjay/http_req/actions/workflows/rust.yml) -[![Crates.io](https://img.shields.io/badge/crates.io-v0.11.0-orange.svg?longCache=true)](https://crates.io/crates/http_req) -[![Docs.rs](https://docs.rs/http_req/badge.svg)](https://docs.rs/http_req/0.11.0/http_req/) +[![Crates.io](https://img.shields.io/badge/crates.io-v0.11.1-orange.svg?longCache=true)](https://crates.io/crates/http_req) +[![Docs.rs](https://docs.rs/http_req/badge.svg)](https://docs.rs/http_req/0.11.1/http_req/) Simple and lightweight HTTP client with built-in HTTPS support. From f46dd1c1d0f086ae6fa26a8c469113b75102037f Mon Sep 17 00:00:00 2001 From: jayjamesjay Date: Sun, 28 Jul 2024 20:51:19 +0200 Subject: [PATCH 42/47] basic redirect support --- Cargo.lock | 148 +++++++++++++++++++++++------------------------- src/request.rs | 66 ++++++++++++++++----- src/response.rs | 21 ++++++- src/uri.rs | 45 ++++++++++++--- 4 files changed, 178 insertions(+), 102 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b8c6ab2..a141821 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,9 +13,9 @@ dependencies = [ [[package]] name = "aws-lc-rs" -version = "1.7.3" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf7d844e282b4b56750b2d4e893b2205581ded8709fddd2b6aa5418c150ca877" +checksum = "4ae74d9bd0a7530e8afd1770739ad34b36838829d6ad61818f9230f683f5ad77" dependencies = [ "aws-lc-sys", "mirai-annotations", @@ -25,9 +25,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.18.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3a2c29203f6bf296d01141cc8bb9dbd5ecd4c27843f2ee0767bcd5985a927da" +checksum = "2e89b6941c2d1a7045538884d6e760ccfffdf8e1ffc2613d8efa74305e1f3752" dependencies = [ "bindgen", "cc", @@ -50,7 +50,7 @@ version = "0.69.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" dependencies = [ - "bitflags 2.5.0", + "bitflags", "cexpr", "clang-sys", "itertools", @@ -69,25 +69,18 @@ dependencies = [ [[package]] name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "cc" -version = "1.0.95" +version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d32a725bc159af97c3e629873bb9f88fb8cf8a4867175f76dc987815ea07c83b" +checksum = "2aba8f4e9906c7ce3c73463f62a7f0c65183ada1a2d47e397cc8810827f9694f" dependencies = [ "jobserver", "libc", - "once_cell", ] [[package]] @@ -155,9 +148,9 @@ checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "errno" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", "windows-sys", @@ -165,9 +158,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.2" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" [[package]] name = "foreign-types" @@ -192,9 +185,9 @@ checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" [[package]] name = "getrandom" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", @@ -240,18 +233,18 @@ dependencies = [ [[package]] name = "jobserver" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" dependencies = [ "libc", ] [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "lazycell" @@ -261,15 +254,15 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.153" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "libloading" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e310b3a6b5907f99202fcdb4960ff45b93735d7c7d96b760fcff8db2dc0e103d" +checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", "windows-targets", @@ -277,15 +270,15 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "memchr" @@ -307,11 +300,10 @@ checksum = "c9be0862c1b3f26a88803c4a49de6889c10e608b3ee9344e6ef5b45fb37ad3d1" [[package]] name = "native-tls" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" dependencies = [ - "lazy_static", "libc", "log", "openssl", @@ -341,11 +333,11 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "openssl" -version = "0.10.64" +version = "0.10.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" +checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" dependencies = [ - "bitflags 2.5.0", + "bitflags", "cfg-if", "foreign-types", "libc", @@ -373,9 +365,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.102" +version = "0.9.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" +checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" dependencies = [ "cc", "libc", @@ -407,9 +399,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.81" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] @@ -475,11 +467,11 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustix" -version = "0.38.32" +version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.5.0", + "bitflags", "errno", "libc", "linux-raw-sys", @@ -488,9 +480,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.10" +version = "0.23.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05cff451f60db80f490f3c182b77c35260baace73209e9cdbbe526bfe3a4d402" +checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044" dependencies = [ "aws-lc-rs", "log", @@ -519,9 +511,9 @@ checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" [[package]] name = "rustls-webpki" -version = "0.102.4" +version = "0.102.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff448f7e92e913c4b7d4c6d8e4540a1724b319b4152b8aef6d4cf8339712b33e" +checksum = "8e6b52d4fda176fd835fdc55a835d4a89b8499cad995885a21149d5ad62f852e" dependencies = [ "aws-lc-rs", "ring", @@ -540,11 +532,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.10.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 1.3.2", + "bitflags", "core-foundation", "core-foundation-sys", "libc", @@ -553,9 +545,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.10.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f3cc463c0ef97e11c3461a9d3787412d30e8e7eb907c79180c4a57bf7c04ef" +checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" dependencies = [ "core-foundation-sys", "libc", @@ -581,9 +573,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.60" +version = "2.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" +checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" dependencies = [ "proc-macro2", "quote", @@ -631,9 +623,9 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "wasi" @@ -683,9 +675,9 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", @@ -699,51 +691,51 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "zeroize" diff --git a/src/request.rs b/src/request.rs index 17e31b8..2ed0a90 100644 --- a/src/request.rs +++ b/src/request.rs @@ -7,6 +7,7 @@ use crate::{ uri::Uri, }; use std::{ + convert::TryFrom, fmt, io::{BufReader, Write}, path::Path, @@ -61,7 +62,7 @@ pub enum HttpVersion { } impl HttpVersion { - pub const fn as_str(self) -> &'static str { + pub const fn as_str(&self) -> &str { use self::HttpVersion::*; match self { @@ -92,7 +93,7 @@ impl<'a> RequestBuilder { /// Allows to control redirects #[derive(Debug, PartialEq, Clone, Copy)] -pub enum RedirectPolicy bool> { +pub enum RedirectPolicy { /// Follows redirect if limit is greater than 0. Limit(usize), /// Runs functions `F` to determine if redirect should be followed. @@ -102,16 +103,28 @@ pub enum RedirectPolicy bool> { impl bool> RedirectPolicy { /// Checks the policy againt specified conditions. /// Returns `true` if redirect should be followed. - pub fn follow(&self) -> bool { + pub fn follow(&mut self) -> bool { use self::RedirectPolicy::*; match self { - Limit(limit) => *limit > 0, + Limit(limit) => match limit { + 0 => false, + _ => { + *limit = *limit - 1; + true + } + }, Custom(func) => func(), } } } +impl bool> Default for RedirectPolicy { + fn default() -> Self { + RedirectPolicy::Limit(5) + } +} + /// Raw HTTP request message that can be sent to any stream /// /// # Examples @@ -323,6 +336,7 @@ impl<'a> RequestMessage<'a> { #[derive(Clone, Debug, PartialEq)] pub struct Request<'a> { messsage: RequestMessage<'a>, + redirect_policy: RedirectPolicy bool>, connect_timeout: Option, read_timeout: Option, write_timeout: Option, @@ -348,6 +362,7 @@ impl<'a> Request<'a> { Request { messsage: message, + redirect_policy: RedirectPolicy::default(), connect_timeout: Some(Duration::from_secs(60)), read_timeout: Some(Duration::from_secs(60)), write_timeout: Some(Duration::from_secs(60)), @@ -586,6 +601,23 @@ impl<'a> Request<'a> { self } + /// Sets the redirect policy for the request. + /// + /// # Examples + /// ``` + /// use http_req::{request::{Request, RedirectPolicy}, uri::Uri}; + /// use std::{time::Duration, convert::TryFrom, path::Path}; + /// + /// let uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); + /// + /// let request = Request::new(&uri) + /// .redirect_policy(RedirectPolicy::Limit(5)); + /// ``` + pub fn redirect_policy(&mut self, policy: RedirectPolicy bool>) -> &mut Self { + self.redirect_policy = policy; + self + } + /// Sends the HTTP request and returns `Response`. /// /// Creates `TcpStream` (and wraps it with `TlsStream` if needed). Writes request message @@ -601,7 +633,7 @@ impl<'a> Request<'a> { /// /// let response = Request::new(&uri).send(&mut writer).unwrap(); /// ``` - pub fn send(&self, writer: &mut T) -> Result + pub fn send(&mut self, writer: &mut T) -> Result where T: Write, { @@ -626,7 +658,7 @@ impl<'a> Request<'a> { thread::spawn(move || { buf_reader.send_head(&sender); - let params: Vec<&str> = receiver_supp.recv().unwrap(); + let params: Vec<&str> = receiver_supp.recv().unwrap_or(Vec::new()); if params.contains(&"non-empty") { if params.contains(&"chunked") { let mut buf_reader = ChunkReader::from(buf_reader); @@ -639,22 +671,28 @@ impl<'a> Request<'a> { // Receive and process `head` of the response. raw_response_head.receive(&receiver, deadline)?; - let response = Response::from_head(&raw_response_head)?; - let content_len = response.content_len().unwrap_or(1); - let mut params = Vec::with_capacity(5); - if response.is_chunked() { - params.push("chunked"); - } + if response.status_code().is_redirect() && self.redirect_policy.follow() { + if let Some(location) = response.headers().get("Location") { + let mut raw_uri = location.to_string(); + let uri = if Uri::is_relative(&raw_uri) { + self.messsage.uri.from_relative(&mut raw_uri) + } else { + Uri::try_from(raw_uri.as_str()) + }?; - if content_len > 0 && self.messsage.method != Method::HEAD { - params.push("non-empty"); + return Request::new(&uri) + .redirect_policy(self.redirect_policy) + .send(writer); + } } + let params = response.basic_info(&self.messsage.method).to_vec(); sender_supp.send(params).unwrap(); // Receive and process `body` of the response. + let content_len = response.content_len().unwrap_or(1); if content_len > 0 { writer.receive_all(&receiver, deadline)?; } diff --git a/src/response.rs b/src/response.rs index 6295c0d..68aff81 100644 --- a/src/response.rs +++ b/src/response.rs @@ -1,6 +1,7 @@ //! parsing server response use crate::{ error::{Error, ParseErr}, + request::Method, uri::Uri, }; use std::{ @@ -188,6 +189,24 @@ impl Response { .get("Transfer-Encoding") .is_some_and(|encodings| encodings.contains("chunked")) } + + /// Returns basic information about the response as an array, including: + /// - chunked -> Transfer-Encoding includes "chunked" + /// - non-empty -> Content-Length is greater than 0 (or unknown) and method is not HEAD + pub fn basic_info<'a>(&self, method: &Method) -> [&'a str; 2] { + let mut params = [""; 2]; + let content_len = self.content_len().unwrap_or(1); + + if self.is_chunked() { + params[0] = "chunked"; + } + + if content_len > 0 && method != &Method::HEAD { + params[1] = "non-empty"; + } + + params + } } /// Status of HTTP response @@ -512,7 +531,7 @@ impl StatusCode { /// const code: StatusCode = StatusCode::new(200); /// assert_eq!(code.reason(), Some("OK")) /// ``` - pub const fn reason(self) -> Option<&'static str> { + pub const fn reason(&self) -> Option<&str> { let reason = match self.0 { 100 => "Continue", 101 => "Switching Protocols", diff --git a/src/uri.rs b/src/uri.rs index ca76801..1877547 100644 --- a/src/uri.rs +++ b/src/uri.rs @@ -80,6 +80,11 @@ pub struct Uri<'a> { } impl<'a> Uri<'a> { + /// Returns a reference to the underlying &str. + pub fn get_ref(&self) -> &str { + self.inner + } + /// Returns scheme of this `Uri`. /// /// # Example @@ -95,7 +100,7 @@ impl<'a> Uri<'a> { } /// Returns information about the user included in this `Uri`. - /// + /// /// # Example /// ``` /// use http_req::uri::Uri; @@ -109,7 +114,7 @@ impl<'a> Uri<'a> { } /// Returns host of this `Uri`. - /// + /// /// # Example /// ``` /// use http_req::uri::Uri; @@ -123,7 +128,7 @@ impl<'a> Uri<'a> { } /// Returns host of this `Uri` to use in a header. - /// + /// /// # Example /// ``` /// use http_req::uri::Uri; @@ -140,7 +145,7 @@ impl<'a> Uri<'a> { } /// Returns port of this `Uri` - /// + /// /// # Example /// ``` /// use http_req::uri::Uri; @@ -155,7 +160,7 @@ impl<'a> Uri<'a> { /// Returns port corresponding to this `Uri`. /// Returns default port if it hasn't been set in the uri. - /// + /// /// # Example /// ``` /// use http_req::uri::Uri; @@ -177,7 +182,7 @@ impl<'a> Uri<'a> { } /// Returns path of this `Uri`. - /// + /// /// # Example /// ``` /// use http_req::uri::Uri; @@ -191,7 +196,7 @@ impl<'a> Uri<'a> { } /// Returns query of this `Uri`. - /// + /// /// # Example /// ``` /// use http_req::uri::Uri; @@ -205,7 +210,7 @@ impl<'a> Uri<'a> { } /// Returns fragment of this `Uri`. - /// + /// /// # Example /// ``` /// use http_req::uri::Uri; @@ -219,7 +224,7 @@ impl<'a> Uri<'a> { } /// Returns resource `Uri` points to. - /// + /// /// # Example /// ``` /// use http_req::uri::Uri; @@ -234,6 +239,28 @@ impl<'a> Uri<'a> { None => "/", } } + + /// Checks if &str is an relative uri. + pub fn is_relative(raw_uri: &str) -> bool { + raw_uri.starts_with("/") + || raw_uri.starts_with("?") + || raw_uri.starts_with("#") + || raw_uri.starts_with("@") + } + + /// Creates a new `Uri` from current uri and relative uri. + /// Transforms the relative uri into an absolute uri. + pub fn from_relative(&'a self, relative_uri: &'a mut String) -> Result, Error> { + let inner_uri = self.inner; + let mut relative_part = relative_uri.as_ref(); + + if inner_uri.ends_with("/") && relative_uri.starts_with("/") { + relative_part = relative_uri.trim_start_matches("/"); + } + + *relative_uri = inner_uri.to_owned() + relative_part; + Uri::try_from(relative_uri.as_str()) + } } impl<'a> fmt::Display for Uri<'a> { From ce69208a239725757ee83a6b92cf2a63efadff7f Mon Sep 17 00:00:00 2001 From: jayjamesjay Date: Thu, 15 Aug 2024 17:42:44 +0200 Subject: [PATCH 43/47] improve parsing relative uri --- Cargo.lock | 63 +++++++++++-------- examples/advanced_request_get.rs | 2 +- src/chunked.rs | 2 +- src/error.rs | 61 ++++++++++-------- src/request.rs | 22 ++++--- src/stream.rs | 28 +++++---- src/uri.rs | 105 ++++++++++++++++++++++++++++--- 7 files changed, 199 insertions(+), 84 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a141821..e3ade9a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -25,9 +25,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.20.0" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e89b6941c2d1a7045538884d6e760ccfffdf8e1ffc2613d8efa74305e1f3752" +checksum = "0f0e249228c6ad2d240c2dc94b714d711629d52bad946075d8e9b2f5391f0703" dependencies = [ "bindgen", "cc", @@ -75,12 +75,13 @@ checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "cc" -version = "1.1.6" +version = "1.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aba8f4e9906c7ce3c73463f62a7f0c65183ada1a2d47e397cc8810827f9694f" +checksum = "68064e60dbf1f17005c2fde4d07c16d8baa506fd7ffed8ccab702d93617975c7" dependencies = [ "jobserver", "libc", + "shlex", ] [[package]] @@ -111,9 +112,9 @@ dependencies = [ [[package]] name = "cmake" -version = "0.1.50" +version = "0.1.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130" +checksum = "fb1e43aa7fd152b1f968787f7dbcdeb306d1867ff373c69955211876c053f91a" dependencies = [ "cc", ] @@ -130,15 +131,15 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "dunce" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" [[package]] name = "either" @@ -153,7 +154,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -206,7 +207,7 @@ version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" dependencies = [ - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -417,9 +418,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.5" +version = "1.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" dependencies = [ "aho-corasick", "memchr", @@ -456,7 +457,7 @@ dependencies = [ "libc", "spin", "untrusted", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -475,7 +476,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -495,9 +496,9 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "2.1.2" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" +checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" dependencies = [ "base64", "rustls-pki-types", @@ -505,9 +506,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" +checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" [[package]] name = "rustls-webpki" @@ -527,7 +528,7 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" dependencies = [ - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -573,9 +574,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.72" +version = "2.0.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" +checksum = "1fceb41e3d546d0bd83421d3409b1460cc7444cd389341a4c880fe7a042cb3d7" dependencies = [ "proc-macro2", "quote", @@ -584,14 +585,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.10.1" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" dependencies = [ "cfg-if", "fastrand", + "once_cell", "rustix", - "windows-sys", + "windows-sys 0.59.0", ] [[package]] @@ -673,6 +675,15 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-targets" version = "0.52.6" diff --git a/examples/advanced_request_get.rs b/examples/advanced_request_get.rs index abf6788..6b60897 100644 --- a/examples/advanced_request_get.rs +++ b/examples/advanced_request_get.rs @@ -24,7 +24,7 @@ fn main() { .parse(); // Connects to a server. Uses information from `addr`. - let mut stream = Stream::new(&addr, Some(Duration::from_secs(60))).unwrap(); + let mut stream = Stream::connect(&addr, Some(Duration::from_secs(60))).unwrap(); stream = Stream::try_to_https(stream, &addr, None).unwrap(); // Makes a request to server. Sends the prepared message. diff --git a/src/chunked.rs b/src/chunked.rs index 9b4616e..25ac72d 100644 --- a/src/chunked.rs +++ b/src/chunked.rs @@ -175,7 +175,7 @@ fn is_ascii_space(b: u8) -> bool { } } -fn parse_hex_uint(data: Vec) -> Result { +fn parse_hex_uint<'a>(data: Vec) -> Result { let mut n = 0usize; for (i, v) in data.iter().enumerate() { if i == 16 { diff --git a/src/error.rs b/src/error.rs index 7168732..4e54570 100644 --- a/src/error.rs +++ b/src/error.rs @@ -29,13 +29,13 @@ impl fmt::Display for ParseErr { use self::ParseErr::*; let err = match self { - Utf8(_) => "invalid character", - Int(_) => "cannot parse number", - Invalid => "invalid value", - Empty => "nothing to parse", - StatusErr => "status line contains invalid values", - HeadersErr => "headers contain invalid values", - UriErr => "uri contains invalid characters", + Utf8(_) => "Invalid character", + Int(_) => "Cannot parse number", + Invalid => "Invalid value", + Empty => "Nothing to parse", + StatusErr => "Status line contains invalid values", + HeadersErr => "Headers contain invalid values", + UriErr => "URI contains invalid characters", }; write!(f, "ParseErr: {}", err) } @@ -57,8 +57,9 @@ impl From for ParseErr { pub enum Error { IO(io::Error), Parse(ParseErr), - Timeout(mpsc::RecvTimeoutError), + Timeout, Tls, + Thread, } impl error::Error for Error { @@ -68,8 +69,7 @@ impl error::Error for Error { match self { IO(e) => Some(e), Parse(e) => Some(e), - Timeout(e) => Some(e), - Tls => None, + Timeout | Tls | Thread => None, } } } @@ -81,27 +81,14 @@ impl fmt::Display for Error { let err = match self { IO(_) => "IO error", Parse(err) => return err.fmt(f), - Timeout(_) => "Timeout error", + Timeout => "Timeout error", Tls => "TLS error", + Thread => "Thread communication error", }; write!(f, "Error: {}", err) } } -#[cfg(feature = "native-tls")] -impl From for Error { - fn from(_e: native_tls::Error) -> Self { - Error::Tls - } -} - -#[cfg(feature = "native-tls")] -impl From> for Error { - fn from(_e: native_tls::HandshakeError) -> Self { - Error::Tls - } -} - impl From for Error { fn from(e: io::Error) -> Self { Error::IO(e) @@ -121,8 +108,8 @@ impl From for Error { } impl From for Error { - fn from(e: mpsc::RecvTimeoutError) -> Self { - Error::Timeout(e) + fn from(_e: mpsc::RecvTimeoutError) -> Self { + Error::Timeout } } @@ -132,3 +119,23 @@ impl From for Error { Error::Tls } } + +#[cfg(feature = "native-tls")] +impl From for Error { + fn from(_e: native_tls::Error) -> Self { + Error::Tls + } +} + +#[cfg(feature = "native-tls")] +impl From> for Error { + fn from(_e: native_tls::HandshakeError) -> Self { + Error::Tls + } +} + +impl From> for Error { + fn from(_e: mpsc::SendError) -> Self { + Error::Thread + } +} diff --git a/src/request.rs b/src/request.rs index 2ed0a90..3add606 100644 --- a/src/request.rs +++ b/src/request.rs @@ -18,6 +18,7 @@ use std::{ const CR_LF: &str = "\r\n"; const DEFAULT_REQ_TIMEOUT: u64 = 60 * 60; +const DEFAULT_CALL_TIMEOUT: u64 = 60; /// HTTP request methods #[derive(Debug, PartialEq, Clone, Copy)] @@ -308,8 +309,8 @@ impl<'a> RequestMessage<'a> { let mut request_msg = (request_line + &headers + CR_LF).as_bytes().to_vec(); - if let Some(b) = &self.body { - request_msg.extend(*b); + if let Some(b) = self.body { + request_msg.extend(b); } request_msg @@ -363,9 +364,9 @@ impl<'a> Request<'a> { Request { messsage: message, redirect_policy: RedirectPolicy::default(), - connect_timeout: Some(Duration::from_secs(60)), - read_timeout: Some(Duration::from_secs(60)), - write_timeout: Some(Duration::from_secs(60)), + connect_timeout: Some(Duration::from_secs(DEFAULT_CALL_TIMEOUT)), + read_timeout: Some(Duration::from_secs(DEFAULT_CALL_TIMEOUT)), + write_timeout: Some(Duration::from_secs(DEFAULT_CALL_TIMEOUT)), timeout: Duration::from_secs(DEFAULT_REQ_TIMEOUT), root_cert_file_pem: None, } @@ -613,8 +614,11 @@ impl<'a> Request<'a> { /// let request = Request::new(&uri) /// .redirect_policy(RedirectPolicy::Limit(5)); /// ``` - pub fn redirect_policy(&mut self, policy: RedirectPolicy bool>) -> &mut Self { - self.redirect_policy = policy; + pub fn redirect_policy(&mut self, policy: T) -> &mut Self + where + RedirectPolicy bool>: From, + { + self.redirect_policy = RedirectPolicy::from(policy); self } @@ -638,7 +642,7 @@ impl<'a> Request<'a> { T: Write, { // Set up a stream. - let mut stream = Stream::new(self.messsage.uri, self.connect_timeout)?; + let mut stream = Stream::connect(self.messsage.uri, self.connect_timeout)?; stream.set_read_timeout(self.read_timeout)?; stream.set_write_timeout(self.write_timeout)?; stream = Stream::try_to_https(stream, self.messsage.uri, self.root_cert_file_pem)?; @@ -689,7 +693,7 @@ impl<'a> Request<'a> { } let params = response.basic_info(&self.messsage.method).to_vec(); - sender_supp.send(params).unwrap(); + sender_supp.send(params)?; // Receive and process `body` of the response. let content_len = response.content_len().unwrap_or(1); diff --git a/src/stream.rs b/src/stream.rs index 27b0e60..6b60879 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -21,7 +21,7 @@ pub enum Stream { impl Stream { /// Opens a TCP connection to a remote host with a connection timeout (if specified). - pub fn new(uri: &Uri, connect_timeout: Option) -> Result { + pub fn connect(uri: &Uri, connect_timeout: Option) -> Result { let host = uri.host().unwrap_or(""); let port = uri.corr_port(); @@ -120,7 +120,7 @@ where { fn send_head(&mut self, sender: &Sender>) { let buf = read_head(self); - sender.send(buf).unwrap(); + sender.send(buf).unwrap_or(()); } fn send_all(&mut self, sender: &Sender>) { @@ -131,7 +131,9 @@ where Ok(0) | Err(_) => break, Ok(len) => { let filled_buf = buf[..len].to_vec(); - sender.send(filled_buf).unwrap(); + if let Err(_) = sender.send(filled_buf) { + break; + } } } } @@ -175,7 +177,7 @@ where Ok(data) => data, Err(e) => { if e == RecvTimeoutError::Timeout { - result = Err(Error::Timeout(RecvTimeoutError::Timeout)); + result = Err(Error::Timeout); } return true; } @@ -189,7 +191,7 @@ where is_complete }); - Ok(result?) + result } } @@ -298,13 +300,13 @@ mod tests { fn stream_new() { { let uri = Uri::try_from(URI).unwrap(); - let stream = Stream::new(&uri, None); + let stream = Stream::connect(&uri, None); assert!(stream.is_ok()); } { let uri = Uri::try_from(URI).unwrap(); - let stream = Stream::new(&uri, Some(TIMEOUT)); + let stream = Stream::connect(&uri, Some(TIMEOUT)); assert!(stream.is_ok()); } @@ -314,7 +316,7 @@ mod tests { fn stream_try_to_https() { { let uri = Uri::try_from(URI_S).unwrap(); - let stream = Stream::new(&uri, None).unwrap(); + let stream = Stream::connect(&uri, None).unwrap(); let https_stream = Stream::try_to_https(stream, &uri, None); assert!(https_stream.is_ok()); @@ -327,7 +329,7 @@ mod tests { } { let uri = Uri::try_from(URI).unwrap(); - let stream = Stream::new(&uri, None).unwrap(); + let stream = Stream::connect(&uri, None).unwrap(); let https_stream = Stream::try_to_https(stream, &uri, None); assert!(https_stream.is_ok()); @@ -344,7 +346,7 @@ mod tests { fn stream_set_read_timeot() { { let uri = Uri::try_from(URI).unwrap(); - let mut stream = Stream::new(&uri, None).unwrap(); + let mut stream = Stream::connect(&uri, None).unwrap(); stream.set_read_timeout(Some(TIMEOUT)).unwrap(); let inner_read_timeout = if let Stream::Http(inner) = stream { @@ -357,7 +359,7 @@ mod tests { } { let uri = Uri::try_from(URI_S).unwrap(); - let mut stream = Stream::new(&uri, None).unwrap(); + let mut stream = Stream::connect(&uri, None).unwrap(); stream = Stream::try_to_https(stream, &uri, None).unwrap(); stream.set_read_timeout(Some(TIMEOUT)).unwrap(); @@ -375,7 +377,7 @@ mod tests { fn stream_set_write_timeot() { { let uri = Uri::try_from(URI).unwrap(); - let mut stream = Stream::new(&uri, None).unwrap(); + let mut stream = Stream::connect(&uri, None).unwrap(); stream.set_write_timeout(Some(TIMEOUT)).unwrap(); let inner_read_timeout = if let Stream::Http(inner) = stream { @@ -388,7 +390,7 @@ mod tests { } { let uri = Uri::try_from(URI_S).unwrap(); - let mut stream = Stream::new(&uri, None).unwrap(); + let mut stream = Stream::connect(&uri, None).unwrap(); stream = Stream::try_to_https(stream, &uri, None).unwrap(); stream.set_write_timeout(Some(TIMEOUT)).unwrap(); diff --git a/src/uri.rs b/src/uri.rs index 1877547..7930dc0 100644 --- a/src/uri.rs +++ b/src/uri.rs @@ -240,27 +240,59 @@ impl<'a> Uri<'a> { } } - /// Checks if &str is an relative uri. + /// Checks if &str is a relative uri. pub fn is_relative(raw_uri: &str) -> bool { raw_uri.starts_with("/") || raw_uri.starts_with("?") || raw_uri.starts_with("#") - || raw_uri.starts_with("@") + || !raw_uri.contains(":") } /// Creates a new `Uri` from current uri and relative uri. /// Transforms the relative uri into an absolute uri. pub fn from_relative(&'a self, relative_uri: &'a mut String) -> Result, Error> { let inner_uri = self.inner; - let mut relative_part = relative_uri.as_ref(); + let mut resource = self.resource().to_string(); + + resource = match &relative_uri.get(..1) { + Some("#") => Uri::add_part(&resource, relative_uri, "#"), + Some("?") => Uri::add_part(&self.path().unwrap_or("/"), relative_uri, "?"), + Some("/") => Uri::add_part(&resource, relative_uri, "/"), + Some(_) | None => { + let part_idx = resource.rfind("/"); + + match part_idx { + Some(idx) => resource[..idx].to_string() + relative_uri, + None => { + if resource.starts_with("/") { + resource.to_string() + relative_uri + } else { + resource.to_string() + "/" + relative_uri + } + } + } + } + }; - if inner_uri.ends_with("/") && relative_uri.starts_with("/") { - relative_part = relative_uri.trim_start_matches("/"); - } + *relative_uri = if let Some(p) = self.path { + inner_uri[..p.start].to_string() + &resource + } else { + inner_uri.trim_end_matches("/").to_string() + &resource + }; - *relative_uri = inner_uri.to_owned() + relative_part; Uri::try_from(relative_uri.as_str()) } + + /// Adds a part to a base at the position definied by a separator. + /// If the separator is not found, concatenates 2 strings together. + fn add_part(base: &str, part: &str, separator: &str) -> String { + let part_idx = base.find(separator); + + match part_idx { + Some(idx) => base[..idx].to_string() + part, + None => base.to_string() + part, + } + } } impl<'a> fmt::Display for Uri<'a> { @@ -531,6 +563,16 @@ mod tests { "[4b10:bbb0:0:d0::ba7:8001]:443", ]; + const TEST_PARTS: [&str; 7] = [ + "?query123", + "/path", + "#fragment", + "other-path", + "#paragraph", + "/foo/bar/buz", + "?users#1551", + ]; + #[test] fn remove_space() { let mut text = String::from("Hello World !"); @@ -749,6 +791,55 @@ mod tests { } } + #[test] + fn uri_is_relative() { + for i in 0..TEST_URIS.len() { + assert!(!Uri::is_relative(TEST_URIS[i])); + } + + for i in 0..TEST_PARTS.len() { + assert!(Uri::is_relative(TEST_PARTS[i])); + } + } + + #[test] + fn uri_from_relative() { + let uris: Vec<_> = TEST_URIS + .iter() + .map(|&uri| Uri::try_from(uri).unwrap()) + .collect(); + + const RESULT: [&str; 7] = [ + "https://user:info@foo.com:12/bar/baz?query123", + "file:///path", + "https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#fragment", + "mailto:John.Doe@example.com/other-path", + "https://[4b10:bbb0:0:d0::ba7:8001]:443/#paragraph", + "http://example.com/foo/bar/buz", + "https://example.com/?users#1551", + ]; + + for i in 0..RESULT.len() { + let mut uri_part = TEST_PARTS[i].to_string(); + + println!("{}", uris[i].resource()); + assert_eq!( + uris[i].from_relative(&mut uri_part).unwrap().inner, + RESULT[i] + ); + } + } + + #[test] + fn uri_add_part() { + const BASES: [&str; 2] = ["/bar/baz?query", "/bar/baz?query#some-fragment"]; + const RESULT: &str = "/bar/baz?query#another-fragment"; + + for i in 0..BASES.len() { + assert_eq!(Uri::add_part(BASES[i], "#another-fragment", "#"), RESULT); + } + } + #[test] fn uri_display() { let uris: Vec<_> = TEST_URIS From b3f828cb6782e1ff7184693a996660d9189fb2ec Mon Sep 17 00:00:00 2001 From: jayjamesjay Date: Sat, 17 Aug 2024 14:00:26 +0200 Subject: [PATCH 44/47] improve timeout error handling & relative uri support --- Cargo.lock | 8 ++-- src/stream.rs | 63 ++++++++++++++++-------------- src/uri.rs | 103 ++++++++++++++++++++++++++++++++++++-------------- 3 files changed, 114 insertions(+), 60 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e3ade9a..5bc715d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -75,9 +75,9 @@ checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "cc" -version = "1.1.12" +version = "1.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68064e60dbf1f17005c2fde4d07c16d8baa506fd7ffed8ccab702d93617975c7" +checksum = "72db2f7947ecee9b03b510377e8bb9077afa27176fdbff55c51027e976fdcc48" dependencies = [ "jobserver", "libc", @@ -255,9 +255,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.155" +version = "0.2.156" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "a5f43f184355eefb8d17fc948dbecf6c13be3c141f20d834ae842193a448c72a" [[package]] name = "libloading" diff --git a/src/stream.rs b/src/stream.rs index 6b60879..e9840c6 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -20,6 +20,15 @@ pub enum Stream { } impl Stream { + /// Opens a TCP connection to a remote host with a connection timeout (if specified). + #[deprecated( + since = "0.12.0", + note = "Stream::new(uri, connect_timeout) was replaced with Stream::connect(uri, connect_timeout)" + )] + pub fn new(uri: &Uri, connect_timeout: Option) -> Result { + Stream::connect(uri, connect_timeout) + } + /// Opens a TCP connection to a remote host with a connection timeout (if specified). pub fn connect(uri: &Uri, connect_timeout: Option) -> Result { let host = uri.host().unwrap_or(""); @@ -168,30 +177,18 @@ where receiver: &Receiver>, deadline: Instant, ) -> Result<(), Error> { - let mut result = Ok(()); - execute_with_deadline(deadline, |remaining_time| { - let mut is_complete = false; - let data_read = match receiver.recv_timeout(remaining_time) { Ok(data) => data, - Err(e) => { - if e == RecvTimeoutError::Timeout { - result = Err(Error::Timeout); - } - return true; - } + Err(e) => match e { + RecvTimeoutError::Timeout => return Err(Error::Timeout), + RecvTimeoutError::Disconnected => return Ok(true), + }, }; - if let Err(e) = self.write_all(&data_read).map_err(|e| Error::IO(e)) { - result = Err(e); - is_complete = true; - } - - is_complete - }); - - result + self.write_all(&data_read).map_err(|e| Error::IO(e))?; + Ok(false) + }) } } @@ -236,19 +233,27 @@ where /// Key information about function `func`: /// - is provided with information about remaining time /// - must ensure that its execution will not take more time than specified in `remaining_time` -/// - needs to return `true` when the operation is complete -pub fn execute_with_deadline(deadline: Instant, mut func: F) +/// - needs to return `Some(true)` when the operation is complete, and `Some(false)` - when operation is in progress +pub fn execute_with_deadline(deadline: Instant, mut func: F) -> Result<(), Error> where - F: FnMut(Duration) -> bool, + F: FnMut(Duration) -> Result, { loop { let now = Instant::now(); let remaining_time = deadline - now; - if deadline < now || func(remaining_time) == true { - break; + if deadline < now { + return Err(Error::Timeout); + } + + match func(remaining_time) { + Ok(true) => break, + Ok(false) => continue, + Err(e) => return Err(e), } } + + Ok(()) } /// Reads the head of HTTP response from `reader`. @@ -475,17 +480,18 @@ mod tests { let star_time = Instant::now(); let deadline = star_time + TIMEOUT; - execute_with_deadline(deadline, |_| { + let timeout_err = execute_with_deadline(deadline, |_| { let sleep_time = Duration::from_millis(500); thread::sleep(sleep_time); - false + Ok(false) }); let end_time = Instant::now(); let total_time = end_time.duration_since(star_time).as_secs(); assert_eq!(total_time, TIMEOUT.as_secs()); + assert!(timeout_err.is_err()); } { let star_time = Instant::now(); @@ -495,8 +501,9 @@ mod tests { let sleep_time = Duration::from_secs(1); thread::sleep(sleep_time); - true - }); + Ok(true) + }) + .unwrap(); let end_time = Instant::now(); let total_time = end_time.duration_since(star_time).as_secs(); diff --git a/src/uri.rs b/src/uri.rs index 7930dc0..c4a65ce 100644 --- a/src/uri.rs +++ b/src/uri.rs @@ -255,23 +255,10 @@ impl<'a> Uri<'a> { let mut resource = self.resource().to_string(); resource = match &relative_uri.get(..1) { - Some("#") => Uri::add_part(&resource, relative_uri, "#"), - Some("?") => Uri::add_part(&self.path().unwrap_or("/"), relative_uri, "?"), - Some("/") => Uri::add_part(&resource, relative_uri, "/"), - Some(_) | None => { - let part_idx = resource.rfind("/"); - - match part_idx { - Some(idx) => resource[..idx].to_string() + relative_uri, - None => { - if resource.starts_with("/") { - resource.to_string() + relative_uri - } else { - resource.to_string() + "/" + relative_uri - } - } - } - } + Some("#") => Uri::add_part_start(&resource, relative_uri, "#"), + Some("?") => Uri::add_part_start(&self.path().unwrap_or("/"), relative_uri, "?"), + Some("/") => Uri::add_part_start(&resource, relative_uri, "/"), + Some(_) | None => Uri::add_part_end(&resource, relative_uri, "/"), }; *relative_uri = if let Some(p) = self.path { @@ -283,15 +270,43 @@ impl<'a> Uri<'a> { Uri::try_from(relative_uri.as_str()) } - /// Adds a part to a base at the position definied by a separator. - /// If the separator is not found, concatenates 2 strings together. - fn add_part(base: &str, part: &str, separator: &str) -> String { - let part_idx = base.find(separator); + /// Adds a part at the beggining of the base. + /// Finds the first occurance of a separator in a base and the first occurance of a separator in a part. + /// Joins all chars before the separator from the base, separator and all chars after the separator from the part. + fn add_part_start(base: &str, part: &str, separator: &str) -> String { + let base_idx = base.find(separator); + Uri::add_part(base, part, separator, base_idx) + } + + /// Adds a part at the end of the base. + /// Finds the last occurance of a separator in a base and the first occurance of a separator in a part. + /// Joins all chars before the separator from the base, separator and all chars after the separator from the part. + fn add_part_end(base: &str, part: &str, separator: &str) -> String { + let base_idx = base.rfind(separator); + Uri::add_part(base, part, separator, base_idx) + } + + /// Adds a part to the base with separator in between. + /// Base index defines where part should be added. + fn add_part(base: &str, part: &str, separator: &str, base_idx: Option) -> String { + let mut output = String::new(); + let part_idx = part.find(separator); + + if let Some(idx) = base_idx { + output += &base[..idx]; + } else { + output += base; + } + + output += separator; - match part_idx { - Some(idx) => base[..idx].to_string() + part, - None => base.to_string() + part, + if let Some(idx) = part_idx { + output += &part[idx + 1..]; + } else { + output += part; } + + output } } @@ -569,7 +584,7 @@ mod tests { "#fragment", "other-path", "#paragraph", - "/foo/bar/buz", + "./foo/bar/buz", "?users#1551", ]; @@ -832,11 +847,43 @@ mod tests { #[test] fn uri_add_part() { - const BASES: [&str; 2] = ["/bar/baz?query", "/bar/baz?query#some-fragment"]; - const RESULT: &str = "/bar/baz?query#another-fragment"; + const BASES: [&str; 2] = ["/bar/baz/fizz?query", "/bar/baz?query#some-fragment"]; + const RESULT: [&str; 2] = [ + "/bar/baz/fizz?query#another-fragment", + "/bar/baz?query#some-fragment#another-fragment", + ]; + + for i in 0..BASES.len() { + assert_eq!( + Uri::add_part(BASES[i], "#another-fragment", "#", Some(BASES[i].len())), + RESULT[i] + ); + } + } + + #[test] + fn uri_add_part_start() { + const BASES: [&str; 2] = ["/bar/baz/fizz?query", "/bar/baz?query#some-fragment"]; + const RESULT: [&str; 2] = [ + "/bar/baz/fizz?query#another-fragment", + "/bar/baz?query#another-fragment", + ]; + + for i in 0..BASES.len() { + assert_eq!( + Uri::add_part_start(BASES[i], "#another-fragment", "#"), + RESULT[i] + ); + } + } + + #[test] + fn uri_add_part_end() { + const BASES: [&str; 2] = ["/bar/baz/fizz?query", "/bar/baz?query#some-fragment"]; + const RESULT: [&str; 2] = ["/bar/baz/another", "/bar/another"]; for i in 0..BASES.len() { - assert_eq!(Uri::add_part(BASES[i], "#another-fragment", "#"), RESULT); + assert_eq!(Uri::add_part_end(BASES[i], "./another", "/"), RESULT[i]); } } From 62e47e3dfc2b58d69d336c38b4ae3c000c5d9d31 Mon Sep 17 00:00:00 2001 From: jayjamesjay Date: Sat, 24 Aug 2024 17:31:33 +0200 Subject: [PATCH 45/47] improve RequestMessage, pass uri to RedirectPolicy --- Cargo.lock | 22 +++++------ Cargo.toml | 2 +- README.md | 26 ++++++++++--- benches/bench.rs | 41 +++++++++++---------- src/request.rs | 96 ++++++++++++++++++++++++------------------------ src/response.rs | 4 +- src/stream.rs | 27 +++++++------- src/tls.rs | 1 - src/uri.rs | 2 +- 9 files changed, 120 insertions(+), 101 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5bc715d..16e0ae8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -75,9 +75,9 @@ checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "cc" -version = "1.1.13" +version = "1.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72db2f7947ecee9b03b510377e8bb9077afa27176fdbff55c51027e976fdcc48" +checksum = "50d2eb3cd3d1bf4529e31c215ee6f93ec5a3d536d9f578f93d9d33ee19562932" dependencies = [ "jobserver", "libc", @@ -159,9 +159,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" [[package]] name = "foreign-types" @@ -212,7 +212,7 @@ dependencies = [ [[package]] name = "http_req" -version = "0.12.0" +version = "0.13.0" dependencies = [ "native-tls", "rustls", @@ -255,9 +255,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.156" +version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5f43f184355eefb8d17fc948dbecf6c13be3c141f20d834ae842193a448c72a" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" [[package]] name = "libloading" @@ -409,9 +409,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -574,9 +574,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.74" +version = "2.0.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fceb41e3d546d0bd83421d3409b1460cc7444cd389341a4c880fe7a042cb3d7" +checksum = "578e081a14e0cefc3279b0472138c513f37b41a08d5a3cca9b6e4e8ceb6cd525" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index ee22a3e..de4d345 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "http_req" -version = "0.12.0" +version = "0.13.0" license = "MIT" description = "simple and lightweight HTTP client with built-in HTTPS support" repository = "https://github.com/jayjamesjay/http_req" diff --git a/README.md b/README.md index f23c88d..01212d9 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,25 @@ # http_req -> [!CAUTION] -> v0.12.0 replaces `RequestBuilder` with `RequestMessage`. Please review [documentation](https://docs.rs/http_req/0.12.0/http_req/) before migrating from previous versions. [![Rust](https://github.com/jayjamesjay/http_req/actions/workflows/rust.yml/badge.svg)](https://github.com/jayjamesjay/http_req/actions/workflows/rust.yml) [![Crates.io](https://img.shields.io/badge/crates.io-v0.12.0-orange.svg?longCache=true)](https://crates.io/crates/http_req) [![Docs.rs](https://docs.rs/http_req/badge.svg)](https://docs.rs/http_req/0.12.0/http_req/) Simple and lightweight HTTP client with built-in HTTPS support. + - HTTP and HTTPS via [rust-native-tls](https://github.com/sfackler/rust-native-tls) (or optionally [rus-tls](https://crates.io/crates/rustls)) -- Small binary size (less than 0.7 MB for basic GET request) +- Small binary size (0.7 MB for basic GET request in default configuration) - Minimal amount of dependencies ## Requirements + http_req by default uses [rust-native-tls](https://github.com/sfackler/rust-native-tls), which relies on TLS framework provided by OS on Windows and macOS, and OpenSSL on all other platforms. But it also supports [rus-tls](https://crates.io/crates/rustls). ## Example + Basic HTTP GET request + ```rust use http_req::request; @@ -31,12 +33,26 @@ fn main() { Take a look at [more examples](https://github.com/jayjamesjay/http_req/tree/master/examples) -## How to use with `rustls`: +## Usage + +### Default configuration + +In order to use `http_req` with default configuration, add the following lines to `Cargo.toml`: + +```toml +[dependencies] +http_req = "^0.13" +``` + +### Rustls + In order to use `http_req` with `rustls` in your project, add the following lines to `Cargo.toml`: + ```toml [dependencies] -http_req = {version="^0.12", default-features = false, features = ["rust-tls"]} +http_req = { version="^0.13", default-features = false, features = ["rust-tls"] } ``` ## License + Licensed under [MIT](https://github.com/jayjamesjay/http_req/blob/master/LICENSE). diff --git a/benches/bench.rs b/benches/bench.rs index bb6f26a..8ba3b9a 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -2,10 +2,30 @@ extern crate http_req; extern crate test; -use http_req::{request::Request, response::Response, uri::Uri}; +use http_req::{request::RequestMessage, response::Response, uri::Uri}; use std::{convert::TryFrom, fs::File, io::Read}; use test::Bencher; +const URI: &str = "https://www.rust-lang.org/"; +const BODY: [u8; 14] = [78, 97, 109, 101, 61, 74, 97, 109, 101, 115, 43, 74, 97, 121]; + +#[bench] +fn parse_uri(b: &mut Bencher) { + b.iter(|| Uri::try_from(URI)); +} + +#[bench] +fn parse_request(b: &mut Bencher) { + let uri = Uri::try_from(URI).unwrap(); + + b.iter(|| { + RequestMessage::new(&uri) + .header("Accept", "*/*") + .body(&BODY) + .parse(); + }); +} + #[bench] fn parse_response(b: &mut Bencher) { let mut content = Vec::new(); @@ -17,22 +37,3 @@ fn parse_response(b: &mut Bencher) { Response::try_from(&content, &mut body) }); } - -const URI: &str = "https://www.rust-lang.org/"; - -#[bench] -fn request_send(b: &mut Bencher) { - b.iter(|| { - let uri = Uri::try_from(URI).unwrap(); - let mut writer = Vec::new(); - - let res = Request::new(&uri).send(&mut writer).unwrap(); - - res - }); -} - -#[bench] -fn parse_uri(b: &mut Bencher) { - b.iter(|| Uri::try_from(URI)); -} diff --git a/src/request.rs b/src/request.rs index 3add606..4db9ba2 100644 --- a/src/request.rs +++ b/src/request.rs @@ -17,6 +17,7 @@ use std::{ }; const CR_LF: &str = "\r\n"; +const DEFAULT_REDIRECT_LIMIT: usize = 5; const DEFAULT_REQ_TIMEOUT: u64 = 60 * 60; const DEFAULT_CALL_TIMEOUT: u64 = 60; @@ -34,11 +35,11 @@ pub enum Method { PATCH, } -impl fmt::Display for Method { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +impl Method { + pub const fn as_str(&self) -> &str { use self::Method::*; - let method = match self { + match self { GET => "GET", HEAD => "HEAD", POST => "POST", @@ -48,9 +49,13 @@ impl fmt::Display for Method { OPTIONS => "OPTIONS", TRACE => "TRACE", PATCH => "PATCH", - }; + } + } +} - write!(f, "{}", method) +impl fmt::Display for Method { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.as_str()) } } @@ -80,18 +85,6 @@ impl fmt::Display for HttpVersion { } } -pub struct RequestBuilder {} - -#[deprecated( - since = "0.12.0", - note = "RequestBuilder was replaced with RequestMessage" -)] -impl<'a> RequestBuilder { - pub fn new(uri: &'a Uri<'a>) -> RequestMessage<'a> { - RequestMessage::new(uri) - } -} - /// Allows to control redirects #[derive(Debug, PartialEq, Clone, Copy)] pub enum RedirectPolicy { @@ -101,10 +94,14 @@ pub enum RedirectPolicy { Custom(F), } -impl bool> RedirectPolicy { +impl RedirectPolicy +where + F: Fn(&str) -> bool, +{ /// Checks the policy againt specified conditions. + /// `uri` is passed into the function in case of case of `Custom` policy. /// Returns `true` if redirect should be followed. - pub fn follow(&mut self) -> bool { + pub fn follow(&mut self, uri: &str) -> bool { use self::RedirectPolicy::*; match self { @@ -115,14 +112,17 @@ impl bool> RedirectPolicy { true } }, - Custom(func) => func(), + Custom(func) => func(uri), } } } -impl bool> Default for RedirectPolicy { +impl Default for RedirectPolicy +where + F: Fn(&str) -> bool, +{ fn default() -> Self { - RedirectPolicy::Limit(5) + RedirectPolicy::Limit(DEFAULT_REDIRECT_LIMIT) } } @@ -270,12 +270,11 @@ impl<'a> RequestMessage<'a> { /// /// let request_msg = RequestMessage::new(&addr) /// .method(Method::POST) - /// .body(BODY) - /// .header("Content-Length", &BODY.len()) - /// .header("Connection", "Close"); + /// .body(BODY); /// ``` pub fn body(&mut self, body: &'a [u8]) -> &mut Self { self.body = Some(body); + self.header("Content-Length", &body.len()); self } @@ -293,7 +292,7 @@ impl<'a> RequestMessage<'a> { /// .parse(); /// ``` pub fn parse(&self) -> Vec { - let request_line = format!( + let mut request_msg = format!( "{} {} {}{}", self.method, self.uri.resource(), @@ -301,14 +300,11 @@ impl<'a> RequestMessage<'a> { CR_LF ); - let headers: String = self - .headers - .iter() - .map(|(k, v)| format!("{}: {}{}", k, v, CR_LF)) - .collect(); - - let mut request_msg = (request_line + &headers + CR_LF).as_bytes().to_vec(); + for (key, val) in self.headers.iter() { + request_msg = request_msg + key + ": " + val + CR_LF; + } + let mut request_msg = (request_msg + CR_LF).as_bytes().to_vec(); if let Some(b) = self.body { request_msg.extend(b); } @@ -337,7 +333,7 @@ impl<'a> RequestMessage<'a> { #[derive(Clone, Debug, PartialEq)] pub struct Request<'a> { messsage: RequestMessage<'a>, - redirect_policy: RedirectPolicy bool>, + redirect_policy: RedirectPolicy bool>, connect_timeout: Option, read_timeout: Option, write_timeout: Option, @@ -616,7 +612,7 @@ impl<'a> Request<'a> { /// ``` pub fn redirect_policy(&mut self, policy: T) -> &mut Self where - RedirectPolicy bool>: From, + RedirectPolicy bool>: From, { self.redirect_policy = RedirectPolicy::from(policy); self @@ -677,18 +673,20 @@ impl<'a> Request<'a> { raw_response_head.receive(&receiver, deadline)?; let response = Response::from_head(&raw_response_head)?; - if response.status_code().is_redirect() && self.redirect_policy.follow() { + if response.status_code().is_redirect() { if let Some(location) = response.headers().get("Location") { - let mut raw_uri = location.to_string(); - let uri = if Uri::is_relative(&raw_uri) { - self.messsage.uri.from_relative(&mut raw_uri) - } else { - Uri::try_from(raw_uri.as_str()) - }?; - - return Request::new(&uri) - .redirect_policy(self.redirect_policy) - .send(writer); + if self.redirect_policy.follow(&location) { + let mut raw_uri = location.to_string(); + let uri = if Uri::is_relative(&raw_uri) { + self.messsage.uri.from_relative(&mut raw_uri) + } else { + Uri::try_from(raw_uri.as_str()) + }?; + + return Request::new(&uri) + .redirect_policy(self.redirect_policy) + .send(writer); + } } } @@ -765,7 +763,6 @@ where Request::new(&uri) .method(Method::POST) - .header("Content-Length", &body.len()) .body(body) .send(writer) } @@ -826,6 +823,7 @@ mod tests { let mut expect_headers = Headers::new(); expect_headers.insert("Host", "doc.rust-lang.org"); + expect_headers.insert("User-Agent", "http_req/0.13.0"); expect_headers.insert(k, v); let req = req.header(k, v); @@ -848,7 +846,8 @@ mod tests { let req = RequestMessage::new(&uri); const DEFAULT_MSG: &str = "GET /std/string/index.html HTTP/1.1\r\n\ - Host: doc.rust-lang.org\r\n\r\n"; + Host: doc.rust-lang.org\r\n\ + User-Agent: http_req/0.13.0\r\n\r\n"; let msg = req.parse(); let msg = String::from_utf8_lossy(&msg).into_owned(); @@ -901,6 +900,7 @@ mod tests { let mut expect_headers = Headers::new(); expect_headers.insert("Host", "doc.rust-lang.org"); expect_headers.insert("Connection", "Close"); + expect_headers.insert("User-Agent", "http_req/0.13.0"); expect_headers.insert(k, v); let req = req.header(k, v); diff --git a/src/response.rs b/src/response.rs index 68aff81..81b9e32 100644 --- a/src/response.rs +++ b/src/response.rs @@ -366,8 +366,9 @@ impl Headers { /// let headers = Headers::default_http(&uri); /// ``` pub fn default_http(uri: &Uri) -> Headers { - let mut headers = Headers::with_capacity(4); + let mut headers = Headers::with_capacity(10); headers.insert("Host", &uri.host_header().unwrap_or_default()); + headers.insert("User-Agent", "http_req/0.13.0"); headers } @@ -826,6 +827,7 @@ mod tests { let mut headers = Headers::with_capacity(4); headers.insert("Host", "doc.rust-lang.org"); + headers.insert("User-Agent", "http_req/0.13.0"); assert_eq!(Headers::default_http(&uri), headers); } diff --git a/src/stream.rs b/src/stream.rs index e9840c6..4db1246 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -1,6 +1,10 @@ //! TCP stream - -use crate::{error::Error, tls, tls::Conn, uri::Uri, CR_LF, LF}; +use crate::{ + error::{Error, ParseErr}, + tls::{self, Conn}, + uri::Uri, + CR_LF, LF, +}; use std::{ io::{self, BufRead, Read, Write}, net::{TcpStream, ToSocketAddrs}, @@ -20,18 +24,12 @@ pub enum Stream { } impl Stream { - /// Opens a TCP connection to a remote host with a connection timeout (if specified). - #[deprecated( - since = "0.12.0", - note = "Stream::new(uri, connect_timeout) was replaced with Stream::connect(uri, connect_timeout)" - )] - pub fn new(uri: &Uri, connect_timeout: Option) -> Result { - Stream::connect(uri, connect_timeout) - } - /// Opens a TCP connection to a remote host with a connection timeout (if specified). pub fn connect(uri: &Uri, connect_timeout: Option) -> Result { - let host = uri.host().unwrap_or(""); + let host = match uri.host() { + Some(h) => h, + None => return Err(Error::Parse(ParseErr::UriErr)), + }; let port = uri.corr_port(); let stream = match connect_timeout { @@ -55,7 +53,10 @@ impl Stream { match stream { Stream::Http(http_stream) => { if uri.scheme() == "https" { - let host = uri.host().unwrap_or(""); + let host = match uri.host() { + Some(h) => h, + None => return Err(Error::Parse(ParseErr::UriErr)), + }; let mut cnf = tls::Config::default(); let cnf = match root_cert_file_pem { diff --git a/src/tls.rs b/src/tls.rs index edb5732..2357c8c 100644 --- a/src/tls.rs +++ b/src/tls.rs @@ -1,5 +1,4 @@ //! secure connection over TLS - use crate::error::Error as HttpError; use std::{ fs::File, diff --git a/src/uri.rs b/src/uri.rs index c4a65ce..6f55b6b 100644 --- a/src/uri.rs +++ b/src/uri.rs @@ -249,7 +249,7 @@ impl<'a> Uri<'a> { } /// Creates a new `Uri` from current uri and relative uri. - /// Transforms the relative uri into an absolute uri. + /// Writes the new uri (raw string) into `relative_uri`. pub fn from_relative(&'a self, relative_uri: &'a mut String) -> Result, Error> { let inner_uri = self.inner; let mut resource = self.resource().to_string(); From 047845fd0fe144f83a29e3c517763a7fce42c6f2 Mon Sep 17 00:00:00 2001 From: jayjamesjay Date: Sat, 31 Aug 2024 16:39:40 +0200 Subject: [PATCH 46/47] impl Authentication --- Cargo.lock | 2 + Cargo.toml | 33 +++------ src/request.rs | 173 ++++++++++++++++++++++++++++++++++++++++++++++++ src/response.rs | 18 +++++ 4 files changed, 201 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 16e0ae8..d30b8c6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -214,6 +214,7 @@ dependencies = [ name = "http_req" version = "0.13.0" dependencies = [ + "base64", "native-tls", "rustls", "rustls-pemfile", @@ -221,6 +222,7 @@ dependencies = [ "unicase", "webpki", "webpki-roots", + "zeroize", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index de4d345..c1c4d78 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,14 @@ edition = "2021" [dependencies] unicase = "^2.7" +base64 = "^0.22.1" +zeroize = { version = "^1.8.1", features = ["zeroize_derive"] } +native-tls = { version = "^0.2", optional = true } +rustls = { version = "^0.23", optional = true } +rustls-pemfile = { version = "^2.1", optional = true } +rustls-pki-types = { version = "^1.7", features = ["alloc"], optional = true } +webpki = { version = "^0.22", optional = true } +webpki-roots = { version = "^0.26", optional = true } [features] default = ["native-tls"] @@ -22,28 +30,3 @@ rust-tls = [ "webpki-roots", "rustls-pemfile", ] - -[dependencies.native-tls] -version = "^0.2" -optional = true - -[dependencies.rustls] -version = "^0.23" -optional = true - -[dependencies.rustls-pemfile] -version = "^2.1" -optional = true - -[dependencies.webpki] -version = "^0.22" -optional = true - -[dependencies.webpki-roots] -version = "^0.26" -optional = true - -[dependencies.rustls-pki-types] -version = "^1.7" -features = ["alloc"] -optional = true diff --git a/src/request.rs b/src/request.rs index 4db9ba2..82be086 100644 --- a/src/request.rs +++ b/src/request.rs @@ -6,6 +6,7 @@ use crate::{ stream::{Stream, ThreadReceive, ThreadSend}, uri::Uri, }; +use base64::engine::{general_purpose::URL_SAFE, Engine}; use std::{ convert::TryFrom, fmt, @@ -15,6 +16,7 @@ use std::{ thread, time::{Duration, Instant}, }; +use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing}; const CR_LF: &str = "\r\n"; const DEFAULT_REDIRECT_LIMIT: usize = 5; @@ -85,6 +87,79 @@ impl fmt::Display for HttpVersion { } } +/// Authentication details: +/// - Basic: username and password +/// - Bearer: token +#[derive(Debug, PartialEq, Zeroize, ZeroizeOnDrop)] +pub struct Authentication(AuthenticationType); + +impl Authentication { + /// Creates a new `Authentication` of type `Basic`. + pub fn basic(username: &T, password: &U) -> Authentication + where + T: ToString + ?Sized, + U: ToString + ?Sized, + { + Authentication(AuthenticationType::Basic { + username: username.to_string(), + password: password.to_string(), + }) + } + + /// Creates a new `Authentication` of type `Bearer` + pub fn bearer(token: &T) -> Authentication + where + T: ToString + ?Sized, + { + Authentication(AuthenticationType::Bearer(token.to_string())) + } + + /// Generates a HTTP Authorization header. Returns `key` & `value` pair. + /// - Basic: uses base64 encoding on provided credentials + /// - Bearer: uses token as is + pub fn header(&self) -> (String, String) { + let key = "Authorization".to_string(); + let val = String::with_capacity(200) + self.0.scheme() + " " + &self.0.credentials(); + + (key, val) + } +} + +/// Authentication types +#[derive(Debug, PartialEq, Zeroize, ZeroizeOnDrop)] +enum AuthenticationType { + Basic { username: String, password: String }, + Bearer(String), +} + +impl AuthenticationType { + /// Returns scheme + const fn scheme(&self) -> &str { + use AuthenticationType::*; + + match self { + Basic { + username: _, + password: _, + } => "Basic", + Bearer(_) => "Bearer", + } + } + + /// Returns encoded credentials + fn credentials(&self) -> Zeroizing { + use AuthenticationType::*; + + match self { + Basic { username, password } => { + let credentials = Zeroizing::new(username.to_string() + ":" + password); + Zeroizing::new(URL_SAFE.encode(credentials.as_bytes())) + } + Bearer(token) => Zeroizing::new(token.to_string()), + } + } +} + /// Allows to control redirects #[derive(Debug, PartialEq, Clone, Copy)] pub enum RedirectPolicy { @@ -258,6 +333,29 @@ impl<'a> RequestMessage<'a> { self } + /// Adds an authorization header to existing headers + /// + /// # Examples + /// ``` + /// use std::convert::TryFrom; + /// use http_req::{request::{RequestMessage, Authentication}, response::Headers, uri::Uri}; + /// + /// let addr = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); + /// + /// let request_msg = RequestMessage::new(&addr) + /// .authentication(Authentication::bearer("secret456token123")); + /// ``` + pub fn authentication(&mut self, auth: T) -> &mut Self + where + Authentication: From, + { + let auth = Authentication::from(auth); + let (key, val) = auth.header(); + + self.headers.insert_raw(key, val); + self + } + /// Sets the body for request /// /// # Examples @@ -456,6 +554,26 @@ impl<'a> Request<'a> { self } + /// Adds an authorization header to existing headers. + /// + /// # Examples + /// ``` + /// use std::convert::TryFrom; + /// use http_req::{request::{RequestMessage, Authentication}, response::Headers, uri::Uri}; + /// + /// let addr = Uri::try_from("https://www.rust-lang.org/learn").unwrap(); + /// + /// let request_msg = RequestMessage::new(&addr) + /// .authentication(Authentication::bearer("secret456token123")); + /// ``` + pub fn authentication(&mut self, auth: T) -> &mut Self + where + Authentication: From, + { + self.messsage.authentication(auth); + self + } + /// Sets the body for request. /// /// # Examples @@ -784,6 +902,43 @@ mod tests { assert_eq!(&format!("{}", METHOD), "HEAD"); } + #[test] + fn authentication_basic() { + let auth = Authentication::basic("user", "password123"); + assert_eq!( + auth, + Authentication(AuthenticationType::Basic { + username: "user".to_string(), + password: "password123".to_string() + }) + ); + } + + #[test] + fn authentication_baerer() { + let auth = Authentication::bearer("456secret123token"); + assert_eq!( + auth, + Authentication(AuthenticationType::Bearer("456secret123token".to_string())) + ); + } + + #[test] + fn authentication_header() { + { + let auth = Authentication::basic("user", "password123"); + let (key, val) = auth.header(); + assert_eq!(key, "Authorization".to_string()); + assert_eq!(val, "Basic dXNlcjpwYXNzd29yZDEyMw==".to_string()); + } + { + let auth = Authentication::bearer("456secret123token"); + let (key, val) = auth.header(); + assert_eq!(key, "Authorization".to_string()); + assert_eq!(val, "Bearer 456secret123token".to_string()); + } + } + #[test] fn request_m_new() { RequestMessage::new(&Uri::try_from(URI).unwrap()); @@ -831,6 +986,24 @@ mod tests { assert_eq!(req.headers, expect_headers); } + #[test] + fn request_m_authentication() { + let uri = Uri::try_from(URI).unwrap(); + let mut req = RequestMessage::new(&uri); + let token = "456secret123token"; + let k = "Authorization"; + let v = "Bearer ".to_string() + token; + + let mut expect_headers = Headers::new(); + expect_headers.insert("Host", "doc.rust-lang.org"); + expect_headers.insert("User-Agent", "http_req/0.13.0"); + expect_headers.insert(k, &v); + + let req = req.authentication(Authentication::bearer(token)); + + assert_eq!(req.headers, expect_headers); + } + #[test] fn request_m_body() { let uri = Uri::try_from(URI).unwrap(); diff --git a/src/response.rs b/src/response.rs index 81b9e32..7a8e355 100644 --- a/src/response.rs +++ b/src/response.rs @@ -355,6 +355,24 @@ impl Headers { self.0.insert(Ascii::new(key.to_string()), val.to_string()) } + /// Inserts key-value pair into the headers and takes ownership over them. + /// + /// If the headers did not have this key present, None is returned. + /// + /// If the headers did have this key present, the value is updated, and the old value is returned. + /// The key is not updated, though; this matters for types that can be == without being identical. + /// + /// # Examples + /// ``` + /// use http_req::response::Headers; + /// + /// let mut headers = Headers::new(); + /// headers.insert_raw("Accept-Language".to_string(), "en-US".to_string()); + /// ``` + pub fn insert_raw(&mut self, key: String, val: String) -> Option { + self.0.insert(Ascii::new(key), val) + } + /// Creates default headers for a HTTP request /// /// # Examples From fca1cc2697687649df492522a4d510830db8c018 Mon Sep 17 00:00:00 2001 From: jayjamesjay Date: Sun, 13 Oct 2024 18:07:27 +0200 Subject: [PATCH 47/47] update docs --- Cargo.lock | 95 +++++++++++++++++++------------------- README.md | 4 +- examples/authentication.rs | 24 ++++++++++ src/request.rs | 62 +++++++++++++++++++++++-- 4 files changed, 132 insertions(+), 53 deletions(-) create mode 100644 examples/authentication.rs diff --git a/Cargo.lock b/Cargo.lock index d30b8c6..4b677d2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,9 +13,9 @@ dependencies = [ [[package]] name = "aws-lc-rs" -version = "1.8.1" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae74d9bd0a7530e8afd1770739ad34b36838829d6ad61818f9230f683f5ad77" +checksum = "cdd82dba44d209fddb11c190e0a94b78651f95299598e472215667417a03ff1d" dependencies = [ "aws-lc-sys", "mirai-annotations", @@ -25,9 +25,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.20.1" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f0e249228c6ad2d240c2dc94b714d711629d52bad946075d8e9b2f5391f0703" +checksum = "df7a4168111d7eb622a31b214057b8509c0a7e1794f44c546d742330dc793972" dependencies = [ "bindgen", "cc", @@ -46,9 +46,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bindgen" -version = "0.69.4" +version = "0.69.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" +checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" dependencies = [ "bitflags", "cexpr", @@ -75,9 +75,9 @@ checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "cc" -version = "1.1.14" +version = "1.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d2eb3cd3d1bf4529e31c215ee6f93ec5a3d536d9f578f93d9d33ee19562932" +checksum = "b16803a61b81d9eabb7eae2588776c4c1e584b738ede45fdbb4c972cec1e9945" dependencies = [ "jobserver", "libc", @@ -257,9 +257,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.158" +version = "0.2.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" [[package]] name = "libloading" @@ -330,9 +330,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "openssl" @@ -386,15 +386,15 @@ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "pkg-config" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "prettyplease" -version = "0.2.20" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" +checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba" dependencies = [ "proc-macro2", "syn", @@ -402,9 +402,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a" dependencies = [ "unicode-ident", ] @@ -420,9 +420,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.6" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" dependencies = [ "aho-corasick", "memchr", @@ -432,9 +432,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" dependencies = [ "aho-corasick", "memchr", @@ -443,9 +443,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "ring" @@ -470,9 +470,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustix" -version = "0.38.34" +version = "0.38.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" dependencies = [ "bitflags", "errno", @@ -483,9 +483,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.12" +version = "0.23.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044" +checksum = "415d9944693cb90382053259f89fbb077ea730ad7273047ec63b19bc9b160ba8" dependencies = [ "aws-lc-rs", "log", @@ -498,25 +498,24 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "2.1.3" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" dependencies = [ - "base64", "rustls-pki-types", ] [[package]] name = "rustls-pki-types" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" +checksum = "0e696e35370c65c9c541198af4543ccd580cf17fc25d8e05c5a242b202488c55" [[package]] name = "rustls-webpki" -version = "0.102.6" +version = "0.102.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e6b52d4fda176fd835fdc55a835d4a89b8499cad995885a21149d5ad62f852e" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" dependencies = [ "aws-lc-rs", "ring", @@ -526,11 +525,11 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.23" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +checksum = "01227be5826fa0690321a2ba6c5cd57a19cf3f6a09e76973b58e61de6ab9d1c1" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -548,9 +547,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.11.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" +checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" dependencies = [ "core-foundation-sys", "libc", @@ -576,9 +575,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.76" +version = "2.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578e081a14e0cefc3279b0472138c513f37b41a08d5a3cca9b6e4e8ceb6cd525" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" dependencies = [ "proc-macro2", "quote", @@ -587,9 +586,9 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.12.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" dependencies = [ "cfg-if", "fastrand", @@ -609,9 +608,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "untrusted" @@ -649,9 +648,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.26.3" +version = "0.26.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd7c23921eeb1713a4e851530e9b9756e4fb0e89978582942612524cf09f01cd" +checksum = "841c67bff177718f1d4dfefde8d8f0e78f9b6589319ba88312f567fc5841a958" dependencies = [ "rustls-pki-types", ] diff --git a/README.md b/README.md index 01212d9..74c45e2 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # http_req [![Rust](https://github.com/jayjamesjay/http_req/actions/workflows/rust.yml/badge.svg)](https://github.com/jayjamesjay/http_req/actions/workflows/rust.yml) -[![Crates.io](https://img.shields.io/badge/crates.io-v0.12.0-orange.svg?longCache=true)](https://crates.io/crates/http_req) -[![Docs.rs](https://docs.rs/http_req/badge.svg)](https://docs.rs/http_req/0.12.0/http_req/) +[![Crates.io](https://img.shields.io/badge/crates.io-v0.13.0-orange.svg?longCache=true)](https://crates.io/crates/http_req) +[![Docs.rs](https://docs.rs/http_req/badge.svg)](https://docs.rs/http_req/0.13.0/http_req/) Simple and lightweight HTTP client with built-in HTTPS support. diff --git a/examples/authentication.rs b/examples/authentication.rs new file mode 100644 index 0000000..5bdffe5 --- /dev/null +++ b/examples/authentication.rs @@ -0,0 +1,24 @@ +use http_req::{ + request::{Authentication, Request}, + uri::Uri, +}; + +fn main() { + // Container for body of a response. + let mut body = Vec::new(); + // URL of the website. + let uri = Uri::try_from("http://httpbin.org/basic-auth/foo/bar").unwrap(); + // Authentication details: username and password. + let auth = Authentication::basic("foo", "bar"); + + // Sends a HTTP GET request and processes the response. Saves body of the response to `body` variable. + let res = Request::new(&uri) + .authentication(auth) + .send(&mut body) + .unwrap(); + + //Prints details about the response. + println!("Status: {} {}", res.status_code(), res.reason()); + println!("Headers: {}", res.headers()); + println!("{}", String::from_utf8_lossy(&body)); +} diff --git a/src/request.rs b/src/request.rs index 82be086..1fef638 100644 --- a/src/request.rs +++ b/src/request.rs @@ -38,6 +38,15 @@ pub enum Method { } impl Method { + /// Returns a string representation of a HTTP request method. + /// + /// # Examples + /// ``` + /// use http_req::request::Method; + /// + /// let method = Method::GET; + /// assert_eq!(method.as_str(), "GET"); + /// ``` pub const fn as_str(&self) -> &str { use self::Method::*; @@ -70,6 +79,15 @@ pub enum HttpVersion { } impl HttpVersion { + /// Returns a string representation of a HTTP version. + /// + /// # Examples + /// ``` + /// use http_req::request::HttpVersion; + /// + /// let version = HttpVersion::Http10; + /// assert_eq!(version.as_str(), "HTTP/1.0"); + /// ``` pub const fn as_str(&self) -> &str { use self::HttpVersion::*; @@ -95,6 +113,13 @@ pub struct Authentication(AuthenticationType); impl Authentication { /// Creates a new `Authentication` of type `Basic`. + /// + /// # Examples + /// ``` + /// use http_req::request::Authentication; + /// + /// let auth = Authentication::basic("foo", "bar"); + /// ``` pub fn basic(username: &T, password: &U) -> Authentication where T: ToString + ?Sized, @@ -107,6 +132,13 @@ impl Authentication { } /// Creates a new `Authentication` of type `Bearer` + /// + /// # Examples + /// ``` + /// use http_req::request::Authentication; + /// + /// let auth = Authentication::bearer("secret_token"); + /// ``` pub fn bearer(token: &T) -> Authentication where T: ToString + ?Sized, @@ -117,6 +149,17 @@ impl Authentication { /// Generates a HTTP Authorization header. Returns `key` & `value` pair. /// - Basic: uses base64 encoding on provided credentials /// - Bearer: uses token as is + /// + /// # Examples + /// ``` + /// use http_req::request::Authentication; + /// + /// let auth = Authentication::bearer("secretToken"); + /// let (key, val) = auth.header(); + /// + /// assert_eq!(key, "Authorization"); + /// assert_eq!(val, "Bearer secretToken"); + /// ``` pub fn header(&self) -> (String, String) { let key = "Authorization".to_string(); let val = String::with_capacity(200) + self.0.scheme() + " " + &self.0.credentials(); @@ -173,9 +216,22 @@ impl RedirectPolicy where F: Fn(&str) -> bool, { - /// Checks the policy againt specified conditions. - /// `uri` is passed into the function in case of case of `Custom` policy. - /// Returns `true` if redirect should be followed. + /// Checks the policy againt specified conditions: + /// - Limit - checks if limit is greater than 0 + /// - Custom - runs functions `F` passing `uri` as parameter and returns its output + /// + /// # Examples + /// ``` + /// use http_req::request::RedirectPolicy; + /// + /// let uri: &str = "https://www.rust-lang.org/learn"; + /// + /// let mut policy_1: RedirectPolicy bool> = RedirectPolicy::Limit(5); + /// assert_eq!(policy_1.follow(&uri), true); + /// + /// let mut policy_2: RedirectPolicy bool> = RedirectPolicy::Custom(|uri| false); + /// assert_eq!(policy_2.follow(&uri), false); + /// ``` pub fn follow(&mut self, uri: &str) -> bool { use self::RedirectPolicy::*;