| //! URI component of request and response lines |
| //! |
| //! This module primarily contains the `Uri` type which is a component of all |
| //! HTTP requests and also reexports this type at the root of the crate. A URI |
| //! is not always a "full URL" in the sense of something you'd type into a web |
| //! browser, but HTTP requests may only have paths on servers but may have full |
| //! schemes and hostnames on clients. |
| //! |
| //! # Examples |
| //! |
| //! ``` |
| //! use http::Uri; |
| //! |
| //! let uri = "/foo/bar?baz".parse::<Uri>().unwrap(); |
| //! assert_eq!(uri.path(), "/foo/bar"); |
| //! assert_eq!(uri.query(), Some("baz")); |
| //! assert_eq!(uri.host(), None); |
| //! |
| //! let uri = "https://www.rust-lang.org/install.html".parse::<Uri>().unwrap(); |
| //! assert_eq!(uri.scheme_str(), Some("https")); |
| //! assert_eq!(uri.host(), Some("www.rust-lang.org")); |
| //! assert_eq!(uri.path(), "/install.html"); |
| //! ``` |
| |
| use crate::byte_str::ByteStr; |
| use std::convert::TryFrom; |
| |
| use bytes::Bytes; |
| |
| use std::error::Error; |
| use std::hash::{Hash, Hasher}; |
| use std::str::{self, FromStr}; |
| use std::{fmt, u16, u8}; |
| |
| use self::scheme::Scheme2; |
| |
| pub use self::authority::Authority; |
| pub use self::builder::Builder; |
| pub use self::path::PathAndQuery; |
| pub use self::port::Port; |
| pub use self::scheme::Scheme; |
| |
| mod authority; |
| mod builder; |
| mod path; |
| mod port; |
| mod scheme; |
| #[cfg(test)] |
| mod tests; |
| |
| /// The URI component of a request. |
| /// |
| /// For HTTP 1, this is included as part of the request line. From Section 5.3, |
| /// Request Target: |
| /// |
| /// > Once an inbound connection is obtained, the client sends an HTTP |
| /// > request message (Section 3) with a request-target derived from the |
| /// > target URI. There are four distinct formats for the request-target, |
| /// > depending on both the method being requested and whether the request |
| /// > is to a proxy. |
| /// > |
| /// > ```notrust |
| /// > request-target = origin-form |
| /// > / absolute-form |
| /// > / authority-form |
| /// > / asterisk-form |
| /// > ``` |
| /// |
| /// The URI is structured as follows: |
| /// |
| /// ```notrust |
| /// abc://username:[email protected]:123/path/data?key=value&key2=value2#fragid1 |
| /// |-| |-------------------------------||--------| |-------------------| |-----| |
| /// | | | | | |
| /// scheme authority path query fragment |
| /// ``` |
| /// |
| /// For HTTP 2.0, the URI is encoded using pseudoheaders. |
| /// |
| /// # Examples |
| /// |
| /// ``` |
| /// use http::Uri; |
| /// |
| /// let uri = "/foo/bar?baz".parse::<Uri>().unwrap(); |
| /// assert_eq!(uri.path(), "/foo/bar"); |
| /// assert_eq!(uri.query(), Some("baz")); |
| /// assert_eq!(uri.host(), None); |
| /// |
| /// let uri = "https://www.rust-lang.org/install.html".parse::<Uri>().unwrap(); |
| /// assert_eq!(uri.scheme_str(), Some("https")); |
| /// assert_eq!(uri.host(), Some("www.rust-lang.org")); |
| /// assert_eq!(uri.path(), "/install.html"); |
| /// ``` |
| #[derive(Clone)] |
| pub struct Uri { |
| scheme: Scheme, |
| authority: Authority, |
| path_and_query: PathAndQuery, |
| } |
| |
| /// The various parts of a URI. |
| /// |
| /// This struct is used to provide to and retrieve from a URI. |
| #[derive(Debug, Default)] |
| pub struct Parts { |
| /// The scheme component of a URI |
| pub scheme: Option<Scheme>, |
| |
| /// The authority component of a URI |
| pub authority: Option<Authority>, |
| |
| /// The origin-form component of a URI |
| pub path_and_query: Option<PathAndQuery>, |
| |
| /// Allow extending in the future |
| _priv: (), |
| } |
| |
| /// An error resulting from a failed attempt to construct a URI. |
| #[derive(Debug)] |
| pub struct InvalidUri(ErrorKind); |
| |
| /// An error resulting from a failed attempt to construct a URI. |
| #[derive(Debug)] |
| pub struct InvalidUriParts(InvalidUri); |
| |
| #[derive(Debug, Eq, PartialEq)] |
| enum ErrorKind { |
| InvalidUriChar, |
| InvalidScheme, |
| InvalidAuthority, |
| InvalidPort, |
| InvalidFormat, |
| SchemeMissing, |
| AuthorityMissing, |
| PathAndQueryMissing, |
| TooLong, |
| Empty, |
| SchemeTooLong, |
| } |
| |
| // u16::MAX is reserved for None |
| const MAX_LEN: usize = (u16::MAX - 1) as usize; |
| |
| // URI_CHARS is a table of valid characters in a URI. An entry in the table is |
| // 0 for invalid characters. For valid characters the entry is itself (i.e. |
| // the entry for 33 is b'!' because b'!' == 33u8). An important characteristic |
| // of this table is that all entries above 127 are invalid. This makes all of the |
| // valid entries a valid single-byte UTF-8 code point. This means that a slice |
| // of such valid entries is valid UTF-8. |
| const URI_CHARS: [u8; 256] = [ |
| // 0 1 2 3 4 5 6 7 8 9 |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // x |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x |
| 0, 0, 0, b'!', 0, b'#', b'$', 0, b'&', b'\'', // 3x |
| b'(', b')', b'*', b'+', b',', b'-', b'.', b'/', b'0', b'1', // 4x |
| b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b':', b';', // 5x |
| 0, b'=', 0, b'?', b'@', b'A', b'B', b'C', b'D', b'E', // 6x |
| b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', // 7x |
| b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W', b'X', b'Y', // 8x |
| b'Z', b'[', 0, b']', 0, b'_', 0, b'a', b'b', b'c', // 9x |
| b'd', b'e', b'f', b'g', b'h', b'i', b'j', b'k', b'l', b'm', // 10x |
| b'n', b'o', b'p', b'q', b'r', b's', b't', b'u', b'v', b'w', // 11x |
| b'x', b'y', b'z', 0, 0, 0, b'~', 0, 0, 0, // 12x |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 13x |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 14x |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 15x |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16x |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 17x |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 18x |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 19x |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20x |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 21x |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 22x |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 23x |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 24x |
| 0, 0, 0, 0, 0, 0 // 25x |
| ]; |
| |
| impl Uri { |
| /// Creates a new builder-style object to manufacture a `Uri`. |
| /// |
| /// This method returns an instance of `Builder` which can be usd to |
| /// create a `Uri`. |
| /// |
| /// # Examples |
| /// |
| /// ``` |
| /// use http::Uri; |
| /// |
| /// let uri = Uri::builder() |
| /// .scheme("https") |
| /// .authority("hyper.rs") |
| /// .path_and_query("/") |
| /// .build() |
| /// .unwrap(); |
| /// ``` |
| pub fn builder() -> Builder { |
| Builder::new() |
| } |
| |
| /// Attempt to convert a `Parts` into a `Uri`. |
| /// |
| /// # Examples |
| /// |
| /// Relative URI |
| /// |
| /// ``` |
| /// # use http::uri::*; |
| /// let mut parts = Parts::default(); |
| /// parts.path_and_query = Some("/foo".parse().unwrap()); |
| /// |
| /// let uri = Uri::from_parts(parts).unwrap(); |
| /// |
| /// assert_eq!(uri.path(), "/foo"); |
| /// |
| /// assert!(uri.scheme().is_none()); |
| /// assert!(uri.authority().is_none()); |
| /// ``` |
| /// |
| /// Absolute URI |
| /// |
| /// ``` |
| /// # use http::uri::*; |
| /// let mut parts = Parts::default(); |
| /// parts.scheme = Some("http".parse().unwrap()); |
| /// parts.authority = Some("foo.com".parse().unwrap()); |
| /// parts.path_and_query = Some("/foo".parse().unwrap()); |
| /// |
| /// let uri = Uri::from_parts(parts).unwrap(); |
| /// |
| /// assert_eq!(uri.scheme().unwrap().as_str(), "http"); |
| /// assert_eq!(uri.authority().unwrap(), "foo.com"); |
| /// assert_eq!(uri.path(), "/foo"); |
| /// ``` |
| pub fn from_parts(src: Parts) -> Result<Uri, InvalidUriParts> { |
| if src.scheme.is_some() { |
| if src.authority.is_none() { |
| return Err(ErrorKind::AuthorityMissing.into()); |
| } |
| |
| if src.path_and_query.is_none() { |
| return Err(ErrorKind::PathAndQueryMissing.into()); |
| } |
| } else { |
| if src.authority.is_some() && src.path_and_query.is_some() { |
| return Err(ErrorKind::SchemeMissing.into()); |
| } |
| } |
| |
| let scheme = match src.scheme { |
| Some(scheme) => scheme, |
| None => Scheme { |
| inner: Scheme2::None, |
| }, |
| }; |
| |
| let authority = match src.authority { |
| Some(authority) => authority, |
| None => Authority::empty(), |
| }; |
| |
| let path_and_query = match src.path_and_query { |
| Some(path_and_query) => path_and_query, |
| None => PathAndQuery::empty(), |
| }; |
| |
| Ok(Uri { |
| scheme: scheme, |
| authority: authority, |
| path_and_query: path_and_query, |
| }) |
| } |
| |
| /// Attempt to convert a `Bytes` buffer to a `Uri`. |
| /// |
| /// This will try to prevent a copy if the type passed is the type used |
| /// internally, and will copy the data if it is not. |
| pub fn from_maybe_shared<T>(src: T) -> Result<Self, InvalidUri> |
| where |
| T: AsRef<[u8]> + 'static, |
| { |
| if_downcast_into!(T, Bytes, src, { |
| return Uri::from_shared(src); |
| }); |
| |
| Uri::try_from(src.as_ref()) |
| } |
| |
| // Not public while `bytes` is unstable. |
| fn from_shared(s: Bytes) -> Result<Uri, InvalidUri> { |
| use self::ErrorKind::*; |
| |
| if s.len() > MAX_LEN { |
| return Err(TooLong.into()); |
| } |
| |
| match s.len() { |
| 0 => { |
| return Err(Empty.into()); |
| } |
| 1 => match s[0] { |
| b'/' => { |
| return Ok(Uri { |
| scheme: Scheme::empty(), |
| authority: Authority::empty(), |
| path_and_query: PathAndQuery::slash(), |
| }); |
| } |
| b'*' => { |
| return Ok(Uri { |
| scheme: Scheme::empty(), |
| authority: Authority::empty(), |
| path_and_query: PathAndQuery::star(), |
| }); |
| } |
| _ => { |
| let authority = Authority::from_shared(s)?; |
| |
| return Ok(Uri { |
| scheme: Scheme::empty(), |
| authority: authority, |
| path_and_query: PathAndQuery::empty(), |
| }); |
| } |
| }, |
| _ => {} |
| } |
| |
| if s[0] == b'/' { |
| return Ok(Uri { |
| scheme: Scheme::empty(), |
| authority: Authority::empty(), |
| path_and_query: PathAndQuery::from_shared(s)?, |
| }); |
| } |
| |
| parse_full(s) |
| } |
| |
| /// Convert a `Uri` from a static string. |
| /// |
| /// This function will not perform any copying, however the string is |
| /// checked to ensure that it is valid. |
| /// |
| /// # Panics |
| /// |
| /// This function panics if the argument is an invalid URI. |
| /// |
| /// # Examples |
| /// |
| /// ``` |
| /// # use http::uri::Uri; |
| /// let uri = Uri::from_static("http://example.com/foo"); |
| /// |
| /// assert_eq!(uri.host().unwrap(), "example.com"); |
| /// assert_eq!(uri.path(), "/foo"); |
| /// ``` |
| pub fn from_static(src: &'static str) -> Self { |
| let s = Bytes::from_static(src.as_bytes()); |
| match Uri::from_shared(s) { |
| Ok(uri) => uri, |
| Err(e) => panic!("static str is not valid URI: {}", e), |
| } |
| } |
| |
| /// Convert a `Uri` into `Parts`. |
| /// |
| /// # Note |
| /// |
| /// This is just an inherent method providing the same functionality as |
| /// `let parts: Parts = uri.into()` |
| /// |
| /// # Examples |
| /// |
| /// ``` |
| /// # use http::uri::*; |
| /// let uri: Uri = "/foo".parse().unwrap(); |
| /// |
| /// let parts = uri.into_parts(); |
| /// |
| /// assert_eq!(parts.path_and_query.unwrap(), "/foo"); |
| /// |
| /// assert!(parts.scheme.is_none()); |
| /// assert!(parts.authority.is_none()); |
| /// ``` |
| #[inline] |
| pub fn into_parts(self) -> Parts { |
| self.into() |
| } |
| |
| /// Returns the path & query components of the Uri |
| #[inline] |
| pub fn path_and_query(&self) -> Option<&PathAndQuery> { |
| if !self.scheme.inner.is_none() || self.authority.data.is_empty() { |
| Some(&self.path_and_query) |
| } else { |
| None |
| } |
| } |
| |
| /// Get the path of this `Uri`. |
| /// |
| /// Both relative and absolute URIs contain a path component, though it |
| /// might be the empty string. The path component is **case sensitive**. |
| /// |
| /// ```notrust |
| /// abc://username:[email protected]:123/path/data?key=value&key2=value2#fragid1 |
| /// |--------| |
| /// | |
| /// path |
| /// ``` |
| /// |
| /// If the URI is `*` then the path component is equal to `*`. |
| /// |
| /// # Examples |
| /// |
| /// A relative URI |
| /// |
| /// ``` |
| /// # use http::Uri; |
| /// |
| /// let uri: Uri = "/hello/world".parse().unwrap(); |
| /// |
| /// assert_eq!(uri.path(), "/hello/world"); |
| /// ``` |
| /// |
| /// An absolute URI |
| /// |
| /// ``` |
| /// # use http::Uri; |
| /// let uri: Uri = "http://example.org/hello/world".parse().unwrap(); |
| /// |
| /// assert_eq!(uri.path(), "/hello/world"); |
| /// ``` |
| #[inline] |
| pub fn path(&self) -> &str { |
| if self.has_path() { |
| self.path_and_query.path() |
| } else { |
| "" |
| } |
| } |
| |
| /// Get the scheme of this `Uri`. |
| /// |
| /// The URI scheme refers to a specification for assigning identifiers |
| /// within that scheme. Only absolute URIs contain a scheme component, but |
| /// not all absolute URIs will contain a scheme component. Although scheme |
| /// names are case-insensitive, the canonical form is lowercase. |
| /// |
| /// ```notrust |
| /// abc://username:[email protected]:123/path/data?key=value&key2=value2#fragid1 |
| /// |-| |
| /// | |
| /// scheme |
| /// ``` |
| /// |
| /// # Examples |
| /// |
| /// Absolute URI |
| /// |
| /// ``` |
| /// use http::uri::{Scheme, Uri}; |
| /// |
| /// let uri: Uri = "http://example.org/hello/world".parse().unwrap(); |
| /// |
| /// assert_eq!(uri.scheme(), Some(&Scheme::HTTP)); |
| /// ``` |
| /// |
| /// |
| /// Relative URI |
| /// |
| /// ``` |
| /// # use http::Uri; |
| /// let uri: Uri = "/hello/world".parse().unwrap(); |
| /// |
| /// assert!(uri.scheme().is_none()); |
| /// ``` |
| #[inline] |
| pub fn scheme(&self) -> Option<&Scheme> { |
| if self.scheme.inner.is_none() { |
| None |
| } else { |
| Some(&self.scheme) |
| } |
| } |
| |
| /// Get the scheme of this `Uri` as a `&str`. |
| /// |
| /// # Example |
| /// |
| /// ``` |
| /// # use http::Uri; |
| /// let uri: Uri = "http://example.org/hello/world".parse().unwrap(); |
| /// |
| /// assert_eq!(uri.scheme_str(), Some("http")); |
| /// ``` |
| #[inline] |
| pub fn scheme_str(&self) -> Option<&str> { |
| if self.scheme.inner.is_none() { |
| None |
| } else { |
| Some(self.scheme.as_str()) |
| } |
| } |
| |
| /// Get the authority of this `Uri`. |
| /// |
| /// The authority is a hierarchical element for naming authority such that |
| /// the remainder of the URI is delegated to that authority. For HTTP, the |
| /// authority consists of the host and port. The host portion of the |
| /// authority is **case-insensitive**. |
| /// |
| /// The authority also includes a `username:password` component, however |
| /// the use of this is deprecated and should be avoided. |
| /// |
| /// ```notrust |
| /// abc://username:[email protected]:123/path/data?key=value&key2=value2#fragid1 |
| /// |-------------------------------| |
| /// | |
| /// authority |
| /// ``` |
| /// |
| /// # Examples |
| /// |
| /// Absolute URI |
| /// |
| /// ``` |
| /// # use http::Uri; |
| /// let uri: Uri = "http://example.org:80/hello/world".parse().unwrap(); |
| /// |
| /// assert_eq!(uri.authority().map(|a| a.as_str()), Some("example.org:80")); |
| /// ``` |
| /// |
| /// |
| /// Relative URI |
| /// |
| /// ``` |
| /// # use http::Uri; |
| /// let uri: Uri = "/hello/world".parse().unwrap(); |
| /// |
| /// assert!(uri.authority().is_none()); |
| /// ``` |
| #[inline] |
| pub fn authority(&self) -> Option<&Authority> { |
| if self.authority.data.is_empty() { |
| None |
| } else { |
| Some(&self.authority) |
| } |
| } |
| |
| /// Get the host of this `Uri`. |
| /// |
| /// The host subcomponent of authority is identified by an IP literal |
| /// encapsulated within square brackets, an IPv4 address in dotted- decimal |
| /// form, or a registered name. The host subcomponent is **case-insensitive**. |
| /// |
| /// ```notrust |
| /// abc://username:[email protected]:123/path/data?key=value&key2=value2#fragid1 |
| /// |---------| |
| /// | |
| /// host |
| /// ``` |
| /// |
| /// # Examples |
| /// |
| /// Absolute URI |
| /// |
| /// ``` |
| /// # use http::Uri; |
| /// let uri: Uri = "http://example.org:80/hello/world".parse().unwrap(); |
| /// |
| /// assert_eq!(uri.host(), Some("example.org")); |
| /// ``` |
| /// |
| /// |
| /// Relative URI |
| /// |
| /// ``` |
| /// # use http::Uri; |
| /// let uri: Uri = "/hello/world".parse().unwrap(); |
| /// |
| /// assert!(uri.host().is_none()); |
| /// ``` |
| #[inline] |
| pub fn host(&self) -> Option<&str> { |
| self.authority().map(|a| a.host()) |
| } |
| |
| /// Get the port part of this `Uri`. |
| /// |
| /// The port subcomponent of authority is designated by an optional port |
| /// number following the host and delimited from it by a single colon (":") |
| /// character. It can be turned into a decimal port number with the `as_u16` |
| /// method or as a `str` with the `as_str` method. |
| /// |
| /// ```notrust |
| /// abc://username:[email protected]:123/path/data?key=value&key2=value2#fragid1 |
| /// |-| |
| /// | |
| /// port |
| /// ``` |
| /// |
| /// # Examples |
| /// |
| /// Absolute URI with port |
| /// |
| /// ``` |
| /// # use http::Uri; |
| /// let uri: Uri = "http://example.org:80/hello/world".parse().unwrap(); |
| /// |
| /// let port = uri.port().unwrap(); |
| /// assert_eq!(port.as_u16(), 80); |
| /// ``` |
| /// |
| /// Absolute URI without port |
| /// |
| /// ``` |
| /// # use http::Uri; |
| /// let uri: Uri = "http://example.org/hello/world".parse().unwrap(); |
| /// |
| /// assert!(uri.port().is_none()); |
| /// ``` |
| /// |
| /// Relative URI |
| /// |
| /// ``` |
| /// # use http::Uri; |
| /// let uri: Uri = "/hello/world".parse().unwrap(); |
| /// |
| /// assert!(uri.port().is_none()); |
| /// ``` |
| pub fn port(&self) -> Option<Port<&str>> { |
| self.authority().and_then(|a| a.port()) |
| } |
| |
| /// Get the port of this `Uri` as a `u16`. |
| /// |
| /// |
| /// # Example |
| /// |
| /// ``` |
| /// # use http::{Uri, uri::Port}; |
| /// let uri: Uri = "http://example.org:80/hello/world".parse().unwrap(); |
| /// |
| /// assert_eq!(uri.port_u16(), Some(80)); |
| /// ``` |
| pub fn port_u16(&self) -> Option<u16> { |
| self.port().and_then(|p| Some(p.as_u16())) |
| } |
| |
| /// Get the query string of this `Uri`, starting after the `?`. |
| /// |
| /// The query component contains non-hierarchical data that, along with data |
| /// in the path component, serves to identify a resource within the scope of |
| /// the URI's scheme and naming authority (if any). The query component is |
| /// indicated by the first question mark ("?") character and terminated by a |
| /// number sign ("#") character or by the end of the URI. |
| /// |
| /// ```notrust |
| /// abc://username:[email protected]:123/path/data?key=value&key2=value2#fragid1 |
| /// |-------------------| |
| /// | |
| /// query |
| /// ``` |
| /// |
| /// # Examples |
| /// |
| /// Absolute URI |
| /// |
| /// ``` |
| /// # use http::Uri; |
| /// let uri: Uri = "http://example.org/hello/world?key=value".parse().unwrap(); |
| /// |
| /// assert_eq!(uri.query(), Some("key=value")); |
| /// ``` |
| /// |
| /// Relative URI with a query string component |
| /// |
| /// ``` |
| /// # use http::Uri; |
| /// let uri: Uri = "/hello/world?key=value&foo=bar".parse().unwrap(); |
| /// |
| /// assert_eq!(uri.query(), Some("key=value&foo=bar")); |
| /// ``` |
| /// |
| /// Relative URI without a query string component |
| /// |
| /// ``` |
| /// # use http::Uri; |
| /// let uri: Uri = "/hello/world".parse().unwrap(); |
| /// |
| /// assert!(uri.query().is_none()); |
| /// ``` |
| #[inline] |
| pub fn query(&self) -> Option<&str> { |
| self.path_and_query.query() |
| } |
| |
| fn has_path(&self) -> bool { |
| !self.path_and_query.data.is_empty() || !self.scheme.inner.is_none() |
| } |
| } |
| |
| impl<'a> TryFrom<&'a [u8]> for Uri { |
| type Error = InvalidUri; |
| |
| #[inline] |
| fn try_from(t: &'a [u8]) -> Result<Self, Self::Error> { |
| Uri::from_shared(Bytes::copy_from_slice(t)) |
| } |
| } |
| |
| impl<'a> TryFrom<&'a str> for Uri { |
| type Error = InvalidUri; |
| |
| #[inline] |
| fn try_from(t: &'a str) -> Result<Self, Self::Error> { |
| t.parse() |
| } |
| } |
| |
| impl<'a> TryFrom<&'a String> for Uri { |
| type Error = InvalidUri; |
| |
| #[inline] |
| fn try_from(t: &'a String) -> Result<Self, Self::Error> { |
| t.parse() |
| } |
| } |
| |
| impl TryFrom<String> for Uri { |
| type Error = InvalidUri; |
| |
| #[inline] |
| fn try_from(t: String) -> Result<Self, Self::Error> { |
| Uri::from_shared(Bytes::from(t)) |
| } |
| } |
| |
| impl<'a> TryFrom<Vec<u8>> for Uri { |
| type Error = InvalidUri; |
| |
| #[inline] |
| fn try_from(vec: Vec<u8>) -> Result<Self, Self::Error> { |
| Uri::from_shared(Bytes::from(vec)) |
| } |
| } |
| |
| impl TryFrom<Parts> for Uri { |
| type Error = InvalidUriParts; |
| |
| #[inline] |
| fn try_from(src: Parts) -> Result<Self, Self::Error> { |
| Uri::from_parts(src) |
| } |
| } |
| |
| impl<'a> TryFrom<&'a Uri> for Uri { |
| type Error = crate::Error; |
| |
| #[inline] |
| fn try_from(src: &'a Uri) -> Result<Self, Self::Error> { |
| Ok(src.clone()) |
| } |
| } |
| |
| /// Convert an `Authority` into a `Uri`. |
| impl From<Authority> for Uri { |
| fn from(authority: Authority) -> Self { |
| Self { |
| scheme: Scheme::empty(), |
| authority, |
| path_and_query: PathAndQuery::empty(), |
| } |
| } |
| } |
| |
| /// Convert a `PathAndQuery` into a `Uri`. |
| impl From<PathAndQuery> for Uri { |
| fn from(path_and_query: PathAndQuery) -> Self { |
| Self { |
| scheme: Scheme::empty(), |
| authority: Authority::empty(), |
| path_and_query, |
| } |
| } |
| } |
| |
| /// Convert a `Uri` into `Parts` |
| impl From<Uri> for Parts { |
| fn from(src: Uri) -> Self { |
| let path_and_query = if src.has_path() { |
| Some(src.path_and_query) |
| } else { |
| None |
| }; |
| |
| let scheme = match src.scheme.inner { |
| Scheme2::None => None, |
| _ => Some(src.scheme), |
| }; |
| |
| let authority = if src.authority.data.is_empty() { |
| None |
| } else { |
| Some(src.authority) |
| }; |
| |
| Parts { |
| scheme: scheme, |
| authority: authority, |
| path_and_query: path_and_query, |
| _priv: (), |
| } |
| } |
| } |
| |
| fn parse_full(mut s: Bytes) -> Result<Uri, InvalidUri> { |
| // Parse the scheme |
| let scheme = match Scheme2::parse(&s[..])? { |
| Scheme2::None => Scheme2::None, |
| Scheme2::Standard(p) => { |
| // TODO: use truncate |
| let _ = s.split_to(p.len() + 3); |
| Scheme2::Standard(p) |
| } |
| Scheme2::Other(n) => { |
| // Grab the protocol |
| let mut scheme = s.split_to(n + 3); |
| |
| // Strip ://, TODO: truncate |
| let _ = scheme.split_off(n); |
| |
| // Allocate the ByteStr |
| let val = unsafe { ByteStr::from_utf8_unchecked(scheme) }; |
| |
| Scheme2::Other(Box::new(val)) |
| } |
| }; |
| |
| // Find the end of the authority. The scheme will already have been |
| // extracted. |
| let authority_end = Authority::parse(&s[..])?; |
| |
| if scheme.is_none() { |
| if authority_end != s.len() { |
| return Err(ErrorKind::InvalidFormat.into()); |
| } |
| |
| let authority = Authority { |
| data: unsafe { ByteStr::from_utf8_unchecked(s) }, |
| }; |
| |
| return Ok(Uri { |
| scheme: scheme.into(), |
| authority: authority, |
| path_and_query: PathAndQuery::empty(), |
| }); |
| } |
| |
| // Authority is required when absolute |
| if authority_end == 0 { |
| return Err(ErrorKind::InvalidFormat.into()); |
| } |
| |
| let authority = s.split_to(authority_end); |
| let authority = Authority { |
| data: unsafe { ByteStr::from_utf8_unchecked(authority) }, |
| }; |
| |
| Ok(Uri { |
| scheme: scheme.into(), |
| authority: authority, |
| path_and_query: PathAndQuery::from_shared(s)?, |
| }) |
| } |
| |
| impl FromStr for Uri { |
| type Err = InvalidUri; |
| |
| #[inline] |
| fn from_str(s: &str) -> Result<Uri, InvalidUri> { |
| Uri::try_from(s.as_bytes()) |
| } |
| } |
| |
| impl PartialEq for Uri { |
| fn eq(&self, other: &Uri) -> bool { |
| if self.scheme() != other.scheme() { |
| return false; |
| } |
| |
| if self.authority() != other.authority() { |
| return false; |
| } |
| |
| if self.path() != other.path() { |
| return false; |
| } |
| |
| if self.query() != other.query() { |
| return false; |
| } |
| |
| true |
| } |
| } |
| |
| impl PartialEq<str> for Uri { |
| fn eq(&self, other: &str) -> bool { |
| let mut other = other.as_bytes(); |
| let mut absolute = false; |
| |
| if let Some(scheme) = self.scheme() { |
| let scheme = scheme.as_str().as_bytes(); |
| absolute = true; |
| |
| if other.len() < scheme.len() + 3 { |
| return false; |
| } |
| |
| if !scheme.eq_ignore_ascii_case(&other[..scheme.len()]) { |
| return false; |
| } |
| |
| other = &other[scheme.len()..]; |
| |
| if &other[..3] != b"://" { |
| return false; |
| } |
| |
| other = &other[3..]; |
| } |
| |
| if let Some(auth) = self.authority() { |
| let len = auth.data.len(); |
| absolute = true; |
| |
| if other.len() < len { |
| return false; |
| } |
| |
| if !auth.data.as_bytes().eq_ignore_ascii_case(&other[..len]) { |
| return false; |
| } |
| |
| other = &other[len..]; |
| } |
| |
| let path = self.path(); |
| |
| if other.len() < path.len() || path.as_bytes() != &other[..path.len()] { |
| if absolute && path == "/" { |
| // PathAndQuery can be omitted, fall through |
| } else { |
| return false; |
| } |
| } else { |
| other = &other[path.len()..]; |
| } |
| |
| if let Some(query) = self.query() { |
| if other.len() == 0 { |
| return query.len() == 0; |
| } |
| |
| if other[0] != b'?' { |
| return false; |
| } |
| |
| other = &other[1..]; |
| |
| if other.len() < query.len() { |
| return false; |
| } |
| |
| if query.as_bytes() != &other[..query.len()] { |
| return false; |
| } |
| |
| other = &other[query.len()..]; |
| } |
| |
| other.is_empty() || other[0] == b'#' |
| } |
| } |
| |
| impl PartialEq<Uri> for str { |
| fn eq(&self, uri: &Uri) -> bool { |
| uri == self |
| } |
| } |
| |
| impl<'a> PartialEq<&'a str> for Uri { |
| fn eq(&self, other: &&'a str) -> bool { |
| self == *other |
| } |
| } |
| |
| impl<'a> PartialEq<Uri> for &'a str { |
| fn eq(&self, uri: &Uri) -> bool { |
| uri == *self |
| } |
| } |
| |
| impl Eq for Uri {} |
| |
| /// Returns a `Uri` representing `/` |
| impl Default for Uri { |
| #[inline] |
| fn default() -> Uri { |
| Uri { |
| scheme: Scheme::empty(), |
| authority: Authority::empty(), |
| path_and_query: PathAndQuery::slash(), |
| } |
| } |
| } |
| |
| impl fmt::Display for Uri { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| if let Some(scheme) = self.scheme() { |
| write!(f, "{}://", scheme)?; |
| } |
| |
| if let Some(authority) = self.authority() { |
| write!(f, "{}", authority)?; |
| } |
| |
| write!(f, "{}", self.path())?; |
| |
| if let Some(query) = self.query() { |
| write!(f, "?{}", query)?; |
| } |
| |
| Ok(()) |
| } |
| } |
| |
| impl fmt::Debug for Uri { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| fmt::Display::fmt(self, f) |
| } |
| } |
| |
| impl From<ErrorKind> for InvalidUri { |
| fn from(src: ErrorKind) -> InvalidUri { |
| InvalidUri(src) |
| } |
| } |
| |
| impl From<ErrorKind> for InvalidUriParts { |
| fn from(src: ErrorKind) -> InvalidUriParts { |
| InvalidUriParts(src.into()) |
| } |
| } |
| |
| impl InvalidUri { |
| fn s(&self) -> &str { |
| match self.0 { |
| ErrorKind::InvalidUriChar => "invalid uri character", |
| ErrorKind::InvalidScheme => "invalid scheme", |
| ErrorKind::InvalidAuthority => "invalid authority", |
| ErrorKind::InvalidPort => "invalid port", |
| ErrorKind::InvalidFormat => "invalid format", |
| ErrorKind::SchemeMissing => "scheme missing", |
| ErrorKind::AuthorityMissing => "authority missing", |
| ErrorKind::PathAndQueryMissing => "path missing", |
| ErrorKind::TooLong => "uri too long", |
| ErrorKind::Empty => "empty string", |
| ErrorKind::SchemeTooLong => "scheme too long", |
| } |
| } |
| } |
| |
| impl fmt::Display for InvalidUri { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| self.s().fmt(f) |
| } |
| } |
| |
| impl Error for InvalidUri {} |
| |
| impl fmt::Display for InvalidUriParts { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| self.0.fmt(f) |
| } |
| } |
| |
| impl Error for InvalidUriParts {} |
| |
| impl Hash for Uri { |
| fn hash<H>(&self, state: &mut H) |
| where |
| H: Hasher, |
| { |
| if !self.scheme.inner.is_none() { |
| self.scheme.hash(state); |
| state.write_u8(0xff); |
| } |
| |
| if let Some(auth) = self.authority() { |
| auth.hash(state); |
| } |
| |
| Hash::hash_slice(self.path().as_bytes(), state); |
| |
| if let Some(query) = self.query() { |
| b'?'.hash(state); |
| Hash::hash_slice(query.as_bytes(), state); |
| } |
| } |
| } |