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> {