summaryrefslogtreecommitdiffstats
path: root/third_party/rust/http/src/uri
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/rust/http/src/uri
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/http/src/uri')
-rw-r--r--third_party/rust/http/src/uri/authority.rs671
-rw-r--r--third_party/rust/http/src/uri/builder.rs197
-rw-r--r--third_party/rust/http/src/uri/mod.rs1118
-rw-r--r--third_party/rust/http/src/uri/path.rs564
-rw-r--r--third_party/rust/http/src/uri/port.rs151
-rw-r--r--third_party/rust/http/src/uri/scheme.rs363
-rw-r--r--third_party/rust/http/src/uri/tests.rs519
7 files changed, 3583 insertions, 0 deletions
diff --git a/third_party/rust/http/src/uri/authority.rs b/third_party/rust/http/src/uri/authority.rs
new file mode 100644
index 0000000000..7a43bc10ff
--- /dev/null
+++ b/third_party/rust/http/src/uri/authority.rs
@@ -0,0 +1,671 @@
+use std::convert::TryFrom;
+use std::hash::{Hash, Hasher};
+use std::str::FromStr;
+use std::{cmp, fmt, str};
+
+use bytes::Bytes;
+
+use super::{ErrorKind, InvalidUri, Port, URI_CHARS};
+use crate::byte_str::ByteStr;
+
+/// Represents the authority component of a URI.
+#[derive(Clone)]
+pub struct Authority {
+ pub(super) data: ByteStr,
+}
+
+impl Authority {
+ pub(super) fn empty() -> Self {
+ Authority {
+ data: ByteStr::new(),
+ }
+ }
+
+ // Not public while `bytes` is unstable.
+ pub(super) fn from_shared(s: Bytes) -> Result<Self, InvalidUri> {
+ // Precondition on create_authority: trivially satisfied by the
+ // identity clousre
+ create_authority(s, |s| s)
+ }
+
+ /// Attempt to convert an `Authority` from a static string.
+ ///
+ /// This function will not perform any copying, and the string will be
+ /// checked if it is empty or contains an invalid character.
+ ///
+ /// # Panics
+ ///
+ /// This function panics if the argument contains invalid characters or
+ /// is empty.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use http::uri::Authority;
+ /// let authority = Authority::from_static("example.com");
+ /// assert_eq!(authority.host(), "example.com");
+ /// ```
+ pub fn from_static(src: &'static str) -> Self {
+ Authority::from_shared(Bytes::from_static(src.as_bytes()))
+ .expect("static str is not valid authority")
+ }
+
+ /// Attempt to convert a `Bytes` buffer to a `Authority`.
+ ///
+ /// 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 Authority::from_shared(src);
+ });
+
+ Authority::try_from(src.as_ref())
+ }
+
+ // Note: this may return an *empty* Authority. You might want `parse_non_empty`.
+ // Postcondition: for all Ok() returns, s[..ret.unwrap()] is valid UTF-8 where
+ // ret is the return value.
+ pub(super) fn parse(s: &[u8]) -> Result<usize, InvalidUri> {
+ let mut colon_cnt = 0;
+ let mut start_bracket = false;
+ let mut end_bracket = false;
+ let mut has_percent = false;
+ let mut end = s.len();
+ let mut at_sign_pos = None;
+
+ // Among other things, this loop checks that every byte in s up to the
+ // first '/', '?', or '#' is a valid URI character (or in some contexts,
+ // a '%'). This means that each such byte is a valid single-byte UTF-8
+ // code point.
+ for (i, &b) in s.iter().enumerate() {
+ match URI_CHARS[b as usize] {
+ b'/' | b'?' | b'#' => {
+ end = i;
+ break;
+ }
+ b':' => {
+ colon_cnt += 1;
+ }
+ b'[' => {
+ if has_percent || start_bracket {
+ // Something other than the userinfo has a `%`, so reject it.
+ return Err(ErrorKind::InvalidAuthority.into());
+ }
+ start_bracket = true;
+ }
+ b']' => {
+ if end_bracket {
+ return Err(ErrorKind::InvalidAuthority.into());
+ }
+ end_bracket = true;
+
+ // Those were part of an IPv6 hostname, so forget them...
+ colon_cnt = 0;
+ has_percent = false;
+ }
+ b'@' => {
+ at_sign_pos = Some(i);
+
+ // Those weren't a port colon, but part of the
+ // userinfo, so it needs to be forgotten.
+ colon_cnt = 0;
+ has_percent = false;
+ }
+ 0 if b == b'%' => {
+ // Per https://tools.ietf.org/html/rfc3986#section-3.2.1 and
+ // https://url.spec.whatwg.org/#authority-state
+ // the userinfo can have a percent-encoded username and password,
+ // so record that a `%` was found. If this turns out to be
+ // part of the userinfo, this flag will be cleared.
+ // Also per https://tools.ietf.org/html/rfc6874, percent-encoding can
+ // be used to indicate a zone identifier.
+ // If the flag hasn't been cleared at the end, that means this
+ // was part of the hostname (and not part of an IPv6 address), and
+ // will fail with an error.
+ has_percent = true;
+ }
+ 0 => {
+ return Err(ErrorKind::InvalidUriChar.into());
+ }
+ _ => {}
+ }
+ }
+
+ if start_bracket ^ end_bracket {
+ return Err(ErrorKind::InvalidAuthority.into());
+ }
+
+ if colon_cnt > 1 {
+ // Things like 'localhost:8080:3030' are rejected.
+ return Err(ErrorKind::InvalidAuthority.into());
+ }
+
+ if end > 0 && at_sign_pos == Some(end - 1) {
+ // If there's nothing after an `@`, this is bonkers.
+ return Err(ErrorKind::InvalidAuthority.into());
+ }
+
+ if has_percent {
+ // Something after the userinfo has a `%`, so reject it.
+ return Err(ErrorKind::InvalidAuthority.into());
+ }
+
+ Ok(end)
+ }
+
+ // Parse bytes as an Authority, not allowing an empty string.
+ //
+ // This should be used by functions that allow a user to parse
+ // an `Authority` by itself.
+ //
+ // Postcondition: for all Ok() returns, s[..ret.unwrap()] is valid UTF-8 where
+ // ret is the return value.
+ fn parse_non_empty(s: &[u8]) -> Result<usize, InvalidUri> {
+ if s.is_empty() {
+ return Err(ErrorKind::Empty.into());
+ }
+ Authority::parse(s)
+ }
+
+ /// Get the host of this `Authority`.
+ ///
+ /// 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:password@example.com:123/path/data?key=value&key2=value2#fragid1
+ /// |---------|
+ /// |
+ /// host
+ /// ```
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use http::uri::*;
+ /// let authority: Authority = "example.org:80".parse().unwrap();
+ ///
+ /// assert_eq!(authority.host(), "example.org");
+ /// ```
+ #[inline]
+ pub fn host(&self) -> &str {
+ host(self.as_str())
+ }
+
+ /// Get the port part of this `Authority`.
+ ///
+ /// 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:password@example.com:123/path/data?key=value&key2=value2#fragid1
+ /// |-|
+ /// |
+ /// port
+ /// ```
+ ///
+ /// # Examples
+ ///
+ /// Authority with port
+ ///
+ /// ```
+ /// # use http::uri::Authority;
+ /// let authority: Authority = "example.org:80".parse().unwrap();
+ ///
+ /// let port = authority.port().unwrap();
+ /// assert_eq!(port.as_u16(), 80);
+ /// assert_eq!(port.as_str(), "80");
+ /// ```
+ ///
+ /// Authority without port
+ ///
+ /// ```
+ /// # use http::uri::Authority;
+ /// let authority: Authority = "example.org".parse().unwrap();
+ ///
+ /// assert!(authority.port().is_none());
+ /// ```
+ pub fn port(&self) -> Option<Port<&str>> {
+ let bytes = self.as_str();
+ bytes
+ .rfind(":")
+ .and_then(|i| Port::from_str(&bytes[i + 1..]).ok())
+ }
+
+ /// Get the port of this `Authority` as a `u16`.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # use http::uri::Authority;
+ /// let authority: Authority = "example.org:80".parse().unwrap();
+ ///
+ /// assert_eq!(authority.port_u16(), Some(80));
+ /// ```
+ pub fn port_u16(&self) -> Option<u16> {
+ self.port().and_then(|p| Some(p.as_u16()))
+ }
+
+ /// Return a str representation of the authority
+ #[inline]
+ pub fn as_str(&self) -> &str {
+ &self.data[..]
+ }
+}
+
+// Purposefully not public while `bytes` is unstable.
+// impl TryFrom<Bytes> for Authority
+
+impl AsRef<str> for Authority {
+ fn as_ref(&self) -> &str {
+ self.as_str()
+ }
+}
+
+impl PartialEq for Authority {
+ fn eq(&self, other: &Authority) -> bool {
+ self.data.eq_ignore_ascii_case(&other.data)
+ }
+}
+
+impl Eq for Authority {}
+
+/// Case-insensitive equality
+///
+/// # Examples
+///
+/// ```
+/// # use http::uri::Authority;
+/// let authority: Authority = "HELLO.com".parse().unwrap();
+/// assert_eq!(authority, "hello.coM");
+/// assert_eq!("hello.com", authority);
+/// ```
+impl PartialEq<str> for Authority {
+ fn eq(&self, other: &str) -> bool {
+ self.data.eq_ignore_ascii_case(other)
+ }
+}
+
+impl PartialEq<Authority> for str {
+ fn eq(&self, other: &Authority) -> bool {
+ self.eq_ignore_ascii_case(other.as_str())
+ }
+}
+
+impl<'a> PartialEq<Authority> for &'a str {
+ fn eq(&self, other: &Authority) -> bool {
+ self.eq_ignore_ascii_case(other.as_str())
+ }
+}
+
+impl<'a> PartialEq<&'a str> for Authority {
+ fn eq(&self, other: &&'a str) -> bool {
+ self.data.eq_ignore_ascii_case(other)
+ }
+}
+
+impl PartialEq<String> for Authority {
+ fn eq(&self, other: &String) -> bool {
+ self.data.eq_ignore_ascii_case(other.as_str())
+ }
+}
+
+impl PartialEq<Authority> for String {
+ fn eq(&self, other: &Authority) -> bool {
+ self.as_str().eq_ignore_ascii_case(other.as_str())
+ }
+}
+
+/// Case-insensitive ordering
+///
+/// # Examples
+///
+/// ```
+/// # use http::uri::Authority;
+/// let authority: Authority = "DEF.com".parse().unwrap();
+/// assert!(authority < "ghi.com");
+/// assert!(authority > "abc.com");
+/// ```
+impl PartialOrd for Authority {
+ fn partial_cmp(&self, other: &Authority) -> Option<cmp::Ordering> {
+ let left = self.data.as_bytes().iter().map(|b| b.to_ascii_lowercase());
+ let right = other.data.as_bytes().iter().map(|b| b.to_ascii_lowercase());
+ left.partial_cmp(right)
+ }
+}
+
+impl PartialOrd<str> for Authority {
+ fn partial_cmp(&self, other: &str) -> Option<cmp::Ordering> {
+ let left = self.data.as_bytes().iter().map(|b| b.to_ascii_lowercase());
+ let right = other.as_bytes().iter().map(|b| b.to_ascii_lowercase());
+ left.partial_cmp(right)
+ }
+}
+
+impl PartialOrd<Authority> for str {
+ fn partial_cmp(&self, other: &Authority) -> Option<cmp::Ordering> {
+ let left = self.as_bytes().iter().map(|b| b.to_ascii_lowercase());
+ let right = other.data.as_bytes().iter().map(|b| b.to_ascii_lowercase());
+ left.partial_cmp(right)
+ }
+}
+
+impl<'a> PartialOrd<Authority> for &'a str {
+ fn partial_cmp(&self, other: &Authority) -> Option<cmp::Ordering> {
+ let left = self.as_bytes().iter().map(|b| b.to_ascii_lowercase());
+ let right = other.data.as_bytes().iter().map(|b| b.to_ascii_lowercase());
+ left.partial_cmp(right)
+ }
+}
+
+impl<'a> PartialOrd<&'a str> for Authority {
+ fn partial_cmp(&self, other: &&'a str) -> Option<cmp::Ordering> {
+ let left = self.data.as_bytes().iter().map(|b| b.to_ascii_lowercase());
+ let right = other.as_bytes().iter().map(|b| b.to_ascii_lowercase());
+ left.partial_cmp(right)
+ }
+}
+
+impl PartialOrd<String> for Authority {
+ fn partial_cmp(&self, other: &String) -> Option<cmp::Ordering> {
+ let left = self.data.as_bytes().iter().map(|b| b.to_ascii_lowercase());
+ let right = other.as_bytes().iter().map(|b| b.to_ascii_lowercase());
+ left.partial_cmp(right)
+ }
+}
+
+impl PartialOrd<Authority> for String {
+ fn partial_cmp(&self, other: &Authority) -> Option<cmp::Ordering> {
+ let left = self.as_bytes().iter().map(|b| b.to_ascii_lowercase());
+ let right = other.data.as_bytes().iter().map(|b| b.to_ascii_lowercase());
+ left.partial_cmp(right)
+ }
+}
+
+/// Case-insensitive hashing
+///
+/// # Examples
+///
+/// ```
+/// # use http::uri::Authority;
+/// # use std::hash::{Hash, Hasher};
+/// # use std::collections::hash_map::DefaultHasher;
+///
+/// let a: Authority = "HELLO.com".parse().unwrap();
+/// let b: Authority = "hello.coM".parse().unwrap();
+///
+/// let mut s = DefaultHasher::new();
+/// a.hash(&mut s);
+/// let a = s.finish();
+///
+/// let mut s = DefaultHasher::new();
+/// b.hash(&mut s);
+/// let b = s.finish();
+///
+/// assert_eq!(a, b);
+/// ```
+impl Hash for Authority {
+ fn hash<H>(&self, state: &mut H)
+ where
+ H: Hasher,
+ {
+ self.data.len().hash(state);
+ for &b in self.data.as_bytes() {
+ state.write_u8(b.to_ascii_lowercase());
+ }
+ }
+}
+
+impl<'a> TryFrom<&'a [u8]> for Authority {
+ type Error = InvalidUri;
+ #[inline]
+ fn try_from(s: &'a [u8]) -> Result<Self, Self::Error> {
+ // parse first, and only turn into Bytes if valid
+
+ // Preconditon on create_authority: copy_from_slice() copies all of
+ // bytes from the [u8] parameter into a new Bytes
+ create_authority(s, |s| Bytes::copy_from_slice(s))
+ }
+}
+
+impl<'a> TryFrom<&'a str> for Authority {
+ type Error = InvalidUri;
+ #[inline]
+ fn try_from(s: &'a str) -> Result<Self, Self::Error> {
+ TryFrom::try_from(s.as_bytes())
+ }
+}
+
+impl TryFrom<Vec<u8>> for Authority {
+ type Error = InvalidUri;
+
+ #[inline]
+ fn try_from(vec: Vec<u8>) -> Result<Self, Self::Error> {
+ Authority::from_shared(vec.into())
+ }
+}
+
+impl TryFrom<String> for Authority {
+ type Error = InvalidUri;
+
+ #[inline]
+ fn try_from(t: String) -> Result<Self, Self::Error> {
+ Authority::from_shared(t.into())
+ }
+}
+
+impl FromStr for Authority {
+ type Err = InvalidUri;
+
+ fn from_str(s: &str) -> Result<Self, InvalidUri> {
+ TryFrom::try_from(s)
+ }
+}
+
+impl fmt::Debug for Authority {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str(self.as_str())
+ }
+}
+
+impl fmt::Display for Authority {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str(self.as_str())
+ }
+}
+
+fn host(auth: &str) -> &str {
+ let host_port = auth
+ .rsplitn(2, '@')
+ .next()
+ .expect("split always has at least 1 item");
+
+ if host_port.as_bytes()[0] == b'[' {
+ let i = host_port
+ .find(']')
+ .expect("parsing should validate brackets");
+ // ..= ranges aren't available in 1.20, our minimum Rust version...
+ &host_port[0..i + 1]
+ } else {
+ host_port
+ .split(':')
+ .next()
+ .expect("split always has at least 1 item")
+ }
+}
+
+// Precondition: f converts all of the bytes in the passed in B into the
+// returned Bytes.
+fn create_authority<B, F>(b: B, f: F) -> Result<Authority, InvalidUri>
+where
+ B: AsRef<[u8]>,
+ F: FnOnce(B) -> Bytes,
+{
+ let s = b.as_ref();
+ let authority_end = Authority::parse_non_empty(s)?;
+
+ if authority_end != s.len() {
+ return Err(ErrorKind::InvalidUriChar.into());
+ }
+
+ let bytes = f(b);
+
+ Ok(Authority {
+ // Safety: the postcondition on parse_non_empty() and the check against
+ // s.len() ensure that b is valid UTF-8. The precondition on f ensures
+ // that this is carried through to bytes.
+ data: unsafe { ByteStr::from_utf8_unchecked(bytes) },
+ })
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn parse_empty_string_is_error() {
+ let err = Authority::parse_non_empty(b"").unwrap_err();
+ assert_eq!(err.0, ErrorKind::Empty);
+ }
+
+ #[test]
+ fn equal_to_self_of_same_authority() {
+ let authority1: Authority = "example.com".parse().unwrap();
+ let authority2: Authority = "EXAMPLE.COM".parse().unwrap();
+ assert_eq!(authority1, authority2);
+ assert_eq!(authority2, authority1);
+ }
+
+ #[test]
+ fn not_equal_to_self_of_different_authority() {
+ let authority1: Authority = "example.com".parse().unwrap();
+ let authority2: Authority = "test.com".parse().unwrap();
+ assert_ne!(authority1, authority2);
+ assert_ne!(authority2, authority1);
+ }
+
+ #[test]
+ fn equates_with_a_str() {
+ let authority: Authority = "example.com".parse().unwrap();
+ assert_eq!(&authority, "EXAMPLE.com");
+ assert_eq!("EXAMPLE.com", &authority);
+ assert_eq!(authority, "EXAMPLE.com");
+ assert_eq!("EXAMPLE.com", authority);
+ }
+
+ #[test]
+ fn from_static_equates_with_a_str() {
+ let authority = Authority::from_static("example.com");
+ assert_eq!(authority, "example.com");
+ }
+
+ #[test]
+ fn not_equal_with_a_str_of_a_different_authority() {
+ let authority: Authority = "example.com".parse().unwrap();
+ assert_ne!(&authority, "test.com");
+ assert_ne!("test.com", &authority);
+ assert_ne!(authority, "test.com");
+ assert_ne!("test.com", authority);
+ }
+
+ #[test]
+ fn equates_with_a_string() {
+ let authority: Authority = "example.com".parse().unwrap();
+ assert_eq!(authority, "EXAMPLE.com".to_string());
+ assert_eq!("EXAMPLE.com".to_string(), authority);
+ }
+
+ #[test]
+ fn equates_with_a_string_of_a_different_authority() {
+ let authority: Authority = "example.com".parse().unwrap();
+ assert_ne!(authority, "test.com".to_string());
+ assert_ne!("test.com".to_string(), authority);
+ }
+
+ #[test]
+ fn compares_to_self() {
+ let authority1: Authority = "abc.com".parse().unwrap();
+ let authority2: Authority = "def.com".parse().unwrap();
+ assert!(authority1 < authority2);
+ assert!(authority2 > authority1);
+ }
+
+ #[test]
+ fn compares_with_a_str() {
+ let authority: Authority = "def.com".parse().unwrap();
+ // with ref
+ assert!(&authority < "ghi.com");
+ assert!("ghi.com" > &authority);
+ assert!(&authority > "abc.com");
+ assert!("abc.com" < &authority);
+
+ // no ref
+ assert!(authority < "ghi.com");
+ assert!("ghi.com" > authority);
+ assert!(authority > "abc.com");
+ assert!("abc.com" < authority);
+ }
+
+ #[test]
+ fn compares_with_a_string() {
+ let authority: Authority = "def.com".parse().unwrap();
+ assert!(authority < "ghi.com".to_string());
+ assert!("ghi.com".to_string() > authority);
+ assert!(authority > "abc.com".to_string());
+ assert!("abc.com".to_string() < authority);
+ }
+
+ #[test]
+ fn allows_percent_in_userinfo() {
+ let authority_str = "a%2f:b%2f@example.com";
+ let authority: Authority = authority_str.parse().unwrap();
+ assert_eq!(authority, authority_str);
+ }
+
+ #[test]
+ fn rejects_percent_in_hostname() {
+ let err = Authority::parse_non_empty(b"example%2f.com").unwrap_err();
+ assert_eq!(err.0, ErrorKind::InvalidAuthority);
+
+ let err = Authority::parse_non_empty(b"a%2f:b%2f@example%2f.com").unwrap_err();
+ assert_eq!(err.0, ErrorKind::InvalidAuthority);
+ }
+
+ #[test]
+ fn allows_percent_in_ipv6_address() {
+ let authority_str = "[fe80::1:2:3:4%25eth0]";
+ let result: Authority = authority_str.parse().unwrap();
+ assert_eq!(result, authority_str);
+ }
+
+ #[test]
+ fn rejects_percent_outside_ipv6_address() {
+ let err = Authority::parse_non_empty(b"1234%20[fe80::1:2:3:4]").unwrap_err();
+ assert_eq!(err.0, ErrorKind::InvalidAuthority);
+
+ let err = Authority::parse_non_empty(b"[fe80::1:2:3:4]%20").unwrap_err();
+ assert_eq!(err.0, ErrorKind::InvalidAuthority);
+ }
+
+ #[test]
+ fn rejects_invalid_utf8() {
+ let err = Authority::try_from([0xc0u8].as_ref()).unwrap_err();
+ assert_eq!(err.0, ErrorKind::InvalidUriChar);
+
+ let err = Authority::from_shared(Bytes::from_static([0xc0u8].as_ref()))
+ .unwrap_err();
+ assert_eq!(err.0, ErrorKind::InvalidUriChar);
+ }
+
+ #[test]
+ fn rejects_invalid_use_of_brackets() {
+ let err = Authority::parse_non_empty(b"[]@[").unwrap_err();
+ assert_eq!(err.0, ErrorKind::InvalidAuthority);
+ }
+}
diff --git a/third_party/rust/http/src/uri/builder.rs b/third_party/rust/http/src/uri/builder.rs
new file mode 100644
index 0000000000..825c0fafcc
--- /dev/null
+++ b/third_party/rust/http/src/uri/builder.rs
@@ -0,0 +1,197 @@
+use std::convert::{TryFrom, TryInto};
+
+use super::{Authority, Parts, PathAndQuery, Scheme};
+use crate::Uri;
+
+/// A builder for `Uri`s.
+///
+/// This type can be used to construct an instance of `Uri`
+/// through a builder pattern.
+#[derive(Debug)]
+pub struct Builder {
+ parts: Result<Parts, crate::Error>,
+}
+
+impl Builder {
+ /// Creates a new default instance of `Builder` to construct a `Uri`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use http::*;
+ ///
+ /// let uri = uri::Builder::new()
+ /// .scheme("https")
+ /// .authority("hyper.rs")
+ /// .path_and_query("/")
+ /// .build()
+ /// .unwrap();
+ /// ```
+ #[inline]
+ pub fn new() -> Builder {
+ Builder::default()
+ }
+
+ /// Set the `Scheme` for this URI.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use http::*;
+ ///
+ /// let mut builder = uri::Builder::new();
+ /// builder.scheme("https");
+ /// ```
+ pub fn scheme<T>(self, scheme: T) -> Self
+ where
+ Scheme: TryFrom<T>,
+ <Scheme as TryFrom<T>>::Error: Into<crate::Error>,
+ {
+ self.map(move |mut parts| {
+ let scheme = scheme.try_into().map_err(Into::into)?;
+ parts.scheme = Some(scheme);
+ Ok(parts)
+ })
+ }
+
+ /// Set the `Authority` for this URI.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use http::*;
+ ///
+ /// let uri = uri::Builder::new()
+ /// .authority("tokio.rs")
+ /// .build()
+ /// .unwrap();
+ /// ```
+ pub fn authority<T>(self, auth: T) -> Self
+ where
+ Authority: TryFrom<T>,
+ <Authority as TryFrom<T>>::Error: Into<crate::Error>,
+ {
+ self.map(move |mut parts| {
+ let auth = auth.try_into().map_err(Into::into)?;
+ parts.authority = Some(auth);
+ Ok(parts)
+ })
+ }
+
+ /// Set the `PathAndQuery` for this URI.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use http::*;
+ ///
+ /// let uri = uri::Builder::new()
+ /// .path_and_query("/hello?foo=bar")
+ /// .build()
+ /// .unwrap();
+ /// ```
+ pub fn path_and_query<T>(self, p_and_q: T) -> Self
+ where
+ PathAndQuery: TryFrom<T>,
+ <PathAndQuery as TryFrom<T>>::Error: Into<crate::Error>,
+ {
+ self.map(move |mut parts| {
+ let p_and_q = p_and_q.try_into().map_err(Into::into)?;
+ parts.path_and_query = Some(p_and_q);
+ Ok(parts)
+ })
+ }
+
+ /// Consumes this builder, and tries to construct a valid `Uri` from
+ /// the configured pieces.
+ ///
+ /// # Errors
+ ///
+ /// This function may return an error if any previously configured argument
+ /// failed to parse or get converted to the internal representation. For
+ /// example if an invalid `scheme` was specified via `scheme("!@#%/^")`
+ /// the error will be returned when this function is called rather than
+ /// when `scheme` was called.
+ ///
+ /// Additionally, the various forms of URI require certain combinations of
+ /// parts to be set to be valid. If the parts don't fit into any of the
+ /// valid forms of URI, a new error is returned.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use http::*;
+ ///
+ /// let uri = Uri::builder()
+ /// .build()
+ /// .unwrap();
+ /// ```
+ pub fn build(self) -> Result<Uri, crate::Error> {
+ let parts = self.parts?;
+ Uri::from_parts(parts).map_err(Into::into)
+ }
+
+ // private
+
+ fn map<F>(self, func: F) -> Self
+ where
+ F: FnOnce(Parts) -> Result<Parts, crate::Error>,
+ {
+
+ Builder {
+ parts: self.parts.and_then(func),
+ }
+ }
+}
+
+impl Default for Builder {
+ #[inline]
+ fn default() -> Builder {
+ Builder {
+ parts: Ok(Parts::default()),
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn build_from_str() {
+ let uri = Builder::new()
+ .scheme(Scheme::HTTP)
+ .authority("hyper.rs")
+ .path_and_query("/foo?a=1")
+ .build()
+ .unwrap();
+ assert_eq!(uri.scheme_str(), Some("http"));
+ assert_eq!(uri.authority().unwrap().host(), "hyper.rs");
+ assert_eq!(uri.path(), "/foo");
+ assert_eq!(uri.query(), Some("a=1"));
+ }
+
+ #[test]
+ fn build_from_string() {
+ for i in 1..10 {
+ let uri = Builder::new()
+ .path_and_query(format!("/foo?a={}", i))
+ .build()
+ .unwrap();
+ let expected_query = format!("a={}", i);
+ assert_eq!(uri.path(), "/foo");
+ assert_eq!(uri.query(), Some(expected_query.as_str()));
+ }
+ }
+
+ #[test]
+ fn build_from_string_ref() {
+ for i in 1..10 {
+ let p_a_q = format!("/foo?a={}", i);
+ let uri = Builder::new().path_and_query(&p_a_q).build().unwrap();
+ let expected_query = format!("a={}", i);
+ assert_eq!(uri.path(), "/foo");
+ assert_eq!(uri.query(), Some(expected_query.as_str()));
+ }
+ }
+}
diff --git a/third_party/rust/http/src/uri/mod.rs b/third_party/rust/http/src/uri/mod.rs
new file mode 100644
index 0000000000..30be83b570
--- /dev/null
+++ b/third_party/rust/http/src/uri/mod.rs
@@ -0,0 +1,1118 @@
+//! 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:password@example.com: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:password@example.com: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:password@example.com: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:password@example.com: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:password@example.com: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:password@example.com: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:password@example.com: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 ommitted, 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);
+ }
+ }
+}
diff --git a/third_party/rust/http/src/uri/path.rs b/third_party/rust/http/src/uri/path.rs
new file mode 100644
index 0000000000..be2cb65c1b
--- /dev/null
+++ b/third_party/rust/http/src/uri/path.rs
@@ -0,0 +1,564 @@
+use std::convert::TryFrom;
+use std::str::FromStr;
+use std::{cmp, fmt, hash, str};
+
+use bytes::Bytes;
+
+use super::{ErrorKind, InvalidUri};
+use crate::byte_str::ByteStr;
+
+/// Represents the path component of a URI
+#[derive(Clone)]
+pub struct PathAndQuery {
+ pub(super) data: ByteStr,
+ pub(super) query: u16,
+}
+
+const NONE: u16 = ::std::u16::MAX;
+
+impl PathAndQuery {
+ // Not public while `bytes` is unstable.
+ pub(super) fn from_shared(mut src: Bytes) -> Result<Self, InvalidUri> {
+ let mut query = NONE;
+ let mut fragment = None;
+
+ // block for iterator borrow
+ {
+ let mut iter = src.as_ref().iter().enumerate();
+
+ // path ...
+ for (i, &b) in &mut iter {
+ // See https://url.spec.whatwg.org/#path-state
+ match b {
+ b'?' => {
+ debug_assert_eq!(query, NONE);
+ query = i as u16;
+ break;
+ }
+ b'#' => {
+ fragment = Some(i);
+ break;
+ }
+
+ // This is the range of bytes that don't need to be
+ // percent-encoded in the path. If it should have been
+ // percent-encoded, then error.
+ 0x21 |
+ 0x24..=0x3B |
+ 0x3D |
+ 0x40..=0x5F |
+ 0x61..=0x7A |
+ 0x7C |
+ 0x7E => {},
+
+ // These are code points that are supposed to be
+ // percent-encoded in the path but there are clients
+ // out there sending them as is and httparse accepts
+ // to parse those requests, so they are allowed here
+ // for parity.
+ //
+ // For reference, those are code points that are used
+ // to send requests with JSON directly embedded in
+ // the URI path. Yes, those things happen for real.
+ b'"' |
+ b'{' | b'}' => {},
+
+ _ => return Err(ErrorKind::InvalidUriChar.into()),
+ }
+ }
+
+ // query ...
+ if query != NONE {
+ for (i, &b) in iter {
+ match b {
+ // While queries *should* be percent-encoded, most
+ // bytes are actually allowed...
+ // See https://url.spec.whatwg.org/#query-state
+ //
+ // Allowed: 0x21 / 0x24 - 0x3B / 0x3D / 0x3F - 0x7E
+ 0x21 |
+ 0x24..=0x3B |
+ 0x3D |
+ 0x3F..=0x7E => {},
+
+ b'#' => {
+ fragment = Some(i);
+ break;
+ }
+
+ _ => return Err(ErrorKind::InvalidUriChar.into()),
+ }
+ }
+ }
+ }
+
+ if let Some(i) = fragment {
+ src.truncate(i);
+ }
+
+ Ok(PathAndQuery {
+ data: unsafe { ByteStr::from_utf8_unchecked(src) },
+ query: query,
+ })
+ }
+
+ /// Convert a `PathAndQuery` 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 path and query.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use http::uri::*;
+ /// let v = PathAndQuery::from_static("/hello?world");
+ ///
+ /// assert_eq!(v.path(), "/hello");
+ /// assert_eq!(v.query(), Some("world"));
+ /// ```
+ #[inline]
+ pub fn from_static(src: &'static str) -> Self {
+ let src = Bytes::from_static(src.as_bytes());
+
+ PathAndQuery::from_shared(src).unwrap()
+ }
+
+ /// Attempt to convert a `Bytes` buffer to a `PathAndQuery`.
+ ///
+ /// 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 PathAndQuery::from_shared(src);
+ });
+
+ PathAndQuery::try_from(src.as_ref())
+ }
+
+ pub(super) fn empty() -> Self {
+ PathAndQuery {
+ data: ByteStr::new(),
+ query: NONE,
+ }
+ }
+
+ pub(super) fn slash() -> Self {
+ PathAndQuery {
+ data: ByteStr::from_static("/"),
+ query: NONE,
+ }
+ }
+
+ pub(super) fn star() -> Self {
+ PathAndQuery {
+ data: ByteStr::from_static("*"),
+ query: NONE,
+ }
+ }
+
+ /// Returns the path component
+ ///
+ /// The path component is **case sensitive**.
+ ///
+ /// ```notrust
+ /// abc://username:password@example.com:123/path/data?key=value&key2=value2#fragid1
+ /// |--------|
+ /// |
+ /// path
+ /// ```
+ ///
+ /// If the URI is `*` then the path component is equal to `*`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use http::uri::*;
+ ///
+ /// let path_and_query: PathAndQuery = "/hello/world".parse().unwrap();
+ ///
+ /// assert_eq!(path_and_query.path(), "/hello/world");
+ /// ```
+ #[inline]
+ pub fn path(&self) -> &str {
+ let ret = if self.query == NONE {
+ &self.data[..]
+ } else {
+ &self.data[..self.query as usize]
+ };
+
+ if ret.is_empty() {
+ return "/";
+ }
+
+ ret
+ }
+
+ /// Returns the query string component
+ ///
+ /// 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:password@example.com:123/path/data?key=value&key2=value2#fragid1
+ /// |-------------------|
+ /// |
+ /// query
+ /// ```
+ ///
+ /// # Examples
+ ///
+ /// With a query string component
+ ///
+ /// ```
+ /// # use http::uri::*;
+ /// let path_and_query: PathAndQuery = "/hello/world?key=value&foo=bar".parse().unwrap();
+ ///
+ /// assert_eq!(path_and_query.query(), Some("key=value&foo=bar"));
+ /// ```
+ ///
+ /// Without a query string component
+ ///
+ /// ```
+ /// # use http::uri::*;
+ /// let path_and_query: PathAndQuery = "/hello/world".parse().unwrap();
+ ///
+ /// assert!(path_and_query.query().is_none());
+ /// ```
+ #[inline]
+ pub fn query(&self) -> Option<&str> {
+ if self.query == NONE {
+ None
+ } else {
+ let i = self.query + 1;
+ Some(&self.data[i as usize..])
+ }
+ }
+
+ /// Returns the path and query as a string component.
+ ///
+ /// # Examples
+ ///
+ /// With a query string component
+ ///
+ /// ```
+ /// # use http::uri::*;
+ /// let path_and_query: PathAndQuery = "/hello/world?key=value&foo=bar".parse().unwrap();
+ ///
+ /// assert_eq!(path_and_query.as_str(), "/hello/world?key=value&foo=bar");
+ /// ```
+ ///
+ /// Without a query string component
+ ///
+ /// ```
+ /// # use http::uri::*;
+ /// let path_and_query: PathAndQuery = "/hello/world".parse().unwrap();
+ ///
+ /// assert_eq!(path_and_query.as_str(), "/hello/world");
+ /// ```
+ #[inline]
+ pub fn as_str(&self) -> &str {
+ let ret = &self.data[..];
+ if ret.is_empty() {
+ return "/";
+ }
+ ret
+ }
+}
+
+impl<'a> TryFrom<&'a [u8]> for PathAndQuery {
+ type Error = InvalidUri;
+ #[inline]
+ fn try_from(s: &'a [u8]) -> Result<Self, Self::Error> {
+ PathAndQuery::from_shared(Bytes::copy_from_slice(s))
+ }
+}
+
+impl<'a> TryFrom<&'a str> for PathAndQuery {
+ type Error = InvalidUri;
+ #[inline]
+ fn try_from(s: &'a str) -> Result<Self, Self::Error> {
+ TryFrom::try_from(s.as_bytes())
+ }
+}
+
+impl<'a> TryFrom<Vec<u8>> for PathAndQuery {
+ type Error = InvalidUri;
+ #[inline]
+ fn try_from(vec: Vec<u8>) -> Result<Self, Self::Error> {
+ PathAndQuery::from_shared(vec.into())
+ }
+}
+
+impl TryFrom<String> for PathAndQuery {
+ type Error = InvalidUri;
+ #[inline]
+ fn try_from(s: String) -> Result<Self, Self::Error> {
+ PathAndQuery::from_shared(s.into())
+ }
+}
+
+impl TryFrom<&String> for PathAndQuery {
+ type Error = InvalidUri;
+ #[inline]
+ fn try_from(s: &String) -> Result<Self, Self::Error> {
+ TryFrom::try_from(s.as_bytes())
+ }
+}
+
+impl FromStr for PathAndQuery {
+ type Err = InvalidUri;
+ #[inline]
+ fn from_str(s: &str) -> Result<Self, InvalidUri> {
+ TryFrom::try_from(s)
+ }
+}
+
+impl fmt::Debug for PathAndQuery {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt::Display::fmt(self, f)
+ }
+}
+
+impl fmt::Display for PathAndQuery {
+ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+ if !self.data.is_empty() {
+ match self.data.as_bytes()[0] {
+ b'/' | b'*' => write!(fmt, "{}", &self.data[..]),
+ _ => write!(fmt, "/{}", &self.data[..]),
+ }
+ } else {
+ write!(fmt, "/")
+ }
+ }
+}
+
+impl hash::Hash for PathAndQuery {
+ fn hash<H: hash::Hasher>(&self, state: &mut H) {
+ self.data.hash(state);
+ }
+}
+
+// ===== PartialEq / PartialOrd =====
+
+impl PartialEq for PathAndQuery {
+ #[inline]
+ fn eq(&self, other: &PathAndQuery) -> bool {
+ self.data == other.data
+ }
+}
+
+impl Eq for PathAndQuery {}
+
+impl PartialEq<str> for PathAndQuery {
+ #[inline]
+ fn eq(&self, other: &str) -> bool {
+ self.as_str() == other
+ }
+}
+
+impl<'a> PartialEq<PathAndQuery> for &'a str {
+ #[inline]
+ fn eq(&self, other: &PathAndQuery) -> bool {
+ self == &other.as_str()
+ }
+}
+
+impl<'a> PartialEq<&'a str> for PathAndQuery {
+ #[inline]
+ fn eq(&self, other: &&'a str) -> bool {
+ self.as_str() == *other
+ }
+}
+
+impl PartialEq<PathAndQuery> for str {
+ #[inline]
+ fn eq(&self, other: &PathAndQuery) -> bool {
+ self == other.as_str()
+ }
+}
+
+impl PartialEq<String> for PathAndQuery {
+ #[inline]
+ fn eq(&self, other: &String) -> bool {
+ self.as_str() == other.as_str()
+ }
+}
+
+impl PartialEq<PathAndQuery> for String {
+ #[inline]
+ fn eq(&self, other: &PathAndQuery) -> bool {
+ self.as_str() == other.as_str()
+ }
+}
+
+impl PartialOrd for PathAndQuery {
+ #[inline]
+ fn partial_cmp(&self, other: &PathAndQuery) -> Option<cmp::Ordering> {
+ self.as_str().partial_cmp(other.as_str())
+ }
+}
+
+impl PartialOrd<str> for PathAndQuery {
+ #[inline]
+ fn partial_cmp(&self, other: &str) -> Option<cmp::Ordering> {
+ self.as_str().partial_cmp(other)
+ }
+}
+
+impl PartialOrd<PathAndQuery> for str {
+ #[inline]
+ fn partial_cmp(&self, other: &PathAndQuery) -> Option<cmp::Ordering> {
+ self.partial_cmp(other.as_str())
+ }
+}
+
+impl<'a> PartialOrd<&'a str> for PathAndQuery {
+ #[inline]
+ fn partial_cmp(&self, other: &&'a str) -> Option<cmp::Ordering> {
+ self.as_str().partial_cmp(*other)
+ }
+}
+
+impl<'a> PartialOrd<PathAndQuery> for &'a str {
+ #[inline]
+ fn partial_cmp(&self, other: &PathAndQuery) -> Option<cmp::Ordering> {
+ self.partial_cmp(&other.as_str())
+ }
+}
+
+impl PartialOrd<String> for PathAndQuery {
+ #[inline]
+ fn partial_cmp(&self, other: &String) -> Option<cmp::Ordering> {
+ self.as_str().partial_cmp(other.as_str())
+ }
+}
+
+impl PartialOrd<PathAndQuery> for String {
+ #[inline]
+ fn partial_cmp(&self, other: &PathAndQuery) -> Option<cmp::Ordering> {
+ self.as_str().partial_cmp(other.as_str())
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn equal_to_self_of_same_path() {
+ let p1: PathAndQuery = "/hello/world&foo=bar".parse().unwrap();
+ let p2: PathAndQuery = "/hello/world&foo=bar".parse().unwrap();
+ assert_eq!(p1, p2);
+ assert_eq!(p2, p1);
+ }
+
+ #[test]
+ fn not_equal_to_self_of_different_path() {
+ let p1: PathAndQuery = "/hello/world&foo=bar".parse().unwrap();
+ let p2: PathAndQuery = "/world&foo=bar".parse().unwrap();
+ assert_ne!(p1, p2);
+ assert_ne!(p2, p1);
+ }
+
+ #[test]
+ fn equates_with_a_str() {
+ let path_and_query: PathAndQuery = "/hello/world&foo=bar".parse().unwrap();
+ assert_eq!(&path_and_query, "/hello/world&foo=bar");
+ assert_eq!("/hello/world&foo=bar", &path_and_query);
+ assert_eq!(path_and_query, "/hello/world&foo=bar");
+ assert_eq!("/hello/world&foo=bar", path_and_query);
+ }
+
+ #[test]
+ fn not_equal_with_a_str_of_a_different_path() {
+ let path_and_query: PathAndQuery = "/hello/world&foo=bar".parse().unwrap();
+ // as a reference
+ assert_ne!(&path_and_query, "/hello&foo=bar");
+ assert_ne!("/hello&foo=bar", &path_and_query);
+ // without reference
+ assert_ne!(path_and_query, "/hello&foo=bar");
+ assert_ne!("/hello&foo=bar", path_and_query);
+ }
+
+ #[test]
+ fn equates_with_a_string() {
+ let path_and_query: PathAndQuery = "/hello/world&foo=bar".parse().unwrap();
+ assert_eq!(path_and_query, "/hello/world&foo=bar".to_string());
+ assert_eq!("/hello/world&foo=bar".to_string(), path_and_query);
+ }
+
+ #[test]
+ fn not_equal_with_a_string_of_a_different_path() {
+ let path_and_query: PathAndQuery = "/hello/world&foo=bar".parse().unwrap();
+ assert_ne!(path_and_query, "/hello&foo=bar".to_string());
+ assert_ne!("/hello&foo=bar".to_string(), path_and_query);
+ }
+
+ #[test]
+ fn compares_to_self() {
+ let p1: PathAndQuery = "/a/world&foo=bar".parse().unwrap();
+ let p2: PathAndQuery = "/b/world&foo=bar".parse().unwrap();
+ assert!(p1 < p2);
+ assert!(p2 > p1);
+ }
+
+ #[test]
+ fn compares_with_a_str() {
+ let path_and_query: PathAndQuery = "/b/world&foo=bar".parse().unwrap();
+ // by ref
+ assert!(&path_and_query < "/c/world&foo=bar");
+ assert!("/c/world&foo=bar" > &path_and_query);
+ assert!(&path_and_query > "/a/world&foo=bar");
+ assert!("/a/world&foo=bar" < &path_and_query);
+
+ // by val
+ assert!(path_and_query < "/c/world&foo=bar");
+ assert!("/c/world&foo=bar" > path_and_query);
+ assert!(path_and_query > "/a/world&foo=bar");
+ assert!("/a/world&foo=bar" < path_and_query);
+ }
+
+ #[test]
+ fn compares_with_a_string() {
+ let path_and_query: PathAndQuery = "/b/world&foo=bar".parse().unwrap();
+ assert!(path_and_query < "/c/world&foo=bar".to_string());
+ assert!("/c/world&foo=bar".to_string() > path_and_query);
+ assert!(path_and_query > "/a/world&foo=bar".to_string());
+ assert!("/a/world&foo=bar".to_string() < path_and_query);
+ }
+
+ #[test]
+ fn ignores_valid_percent_encodings() {
+ assert_eq!("/a%20b", pq("/a%20b?r=1").path());
+ assert_eq!("qr=%31", pq("/a/b?qr=%31").query().unwrap());
+ }
+
+ #[test]
+ fn ignores_invalid_percent_encodings() {
+ assert_eq!("/a%%b", pq("/a%%b?r=1").path());
+ assert_eq!("/aaa%", pq("/aaa%").path());
+ assert_eq!("/aaa%", pq("/aaa%?r=1").path());
+ assert_eq!("/aa%2", pq("/aa%2").path());
+ assert_eq!("/aa%2", pq("/aa%2?r=1").path());
+ assert_eq!("qr=%3", pq("/a/b?qr=%3").query().unwrap());
+ }
+
+ #[test]
+ fn json_is_fine() {
+ assert_eq!(r#"/{"bread":"baguette"}"#, pq(r#"/{"bread":"baguette"}"#).path());
+ }
+
+ fn pq(s: &str) -> PathAndQuery {
+ s.parse().expect(&format!("parsing {}", s))
+ }
+}
diff --git a/third_party/rust/http/src/uri/port.rs b/third_party/rust/http/src/uri/port.rs
new file mode 100644
index 0000000000..8f5c5f3f7d
--- /dev/null
+++ b/third_party/rust/http/src/uri/port.rs
@@ -0,0 +1,151 @@
+use std::fmt;
+
+use super::{ErrorKind, InvalidUri};
+
+/// The port component of a URI.
+pub struct Port<T> {
+ port: u16,
+ repr: T,
+}
+
+impl<T> Port<T> {
+ /// Returns the port number as a `u16`.
+ ///
+ /// # Examples
+ ///
+ /// Port as `u16`.
+ ///
+ /// ```
+ /// # use http::uri::Authority;
+ /// let authority: Authority = "example.org:80".parse().unwrap();
+ ///
+ /// let port = authority.port().unwrap();
+ /// assert_eq!(port.as_u16(), 80);
+ /// ```
+ pub fn as_u16(&self) -> u16 {
+ self.port
+ }
+}
+
+impl<T> Port<T>
+where
+ T: AsRef<str>,
+{
+ /// Converts a `str` to a port number.
+ ///
+ /// The supplied `str` must be a valid u16.
+ pub(crate) fn from_str(bytes: T) -> Result<Self, InvalidUri> {
+ bytes
+ .as_ref()
+ .parse::<u16>()
+ .map(|port| Port { port, repr: bytes })
+ .map_err(|_| ErrorKind::InvalidPort.into())
+ }
+
+ /// Returns the port number as a `str`.
+ ///
+ /// # Examples
+ ///
+ /// Port as `str`.
+ ///
+ /// ```
+ /// # use http::uri::Authority;
+ /// let authority: Authority = "example.org:80".parse().unwrap();
+ ///
+ /// let port = authority.port().unwrap();
+ /// assert_eq!(port.as_str(), "80");
+ /// ```
+ pub fn as_str(&self) -> &str {
+ self.repr.as_ref()
+ }
+}
+
+impl<T> fmt::Debug for Port<T>
+where
+ T: fmt::Debug,
+{
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_tuple("Port").field(&self.port).finish()
+ }
+}
+
+impl<T> fmt::Display for Port<T> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ // Use `u16::fmt` so that it respects any formatting flags that
+ // may have been set (like padding, align, etc).
+ fmt::Display::fmt(&self.port, f)
+ }
+}
+
+impl<T> From<Port<T>> for u16 {
+ fn from(port: Port<T>) -> Self {
+ port.as_u16()
+ }
+}
+
+impl<T> AsRef<str> for Port<T>
+where
+ T: AsRef<str>,
+{
+ fn as_ref(&self) -> &str {
+ self.as_str()
+ }
+}
+
+impl<T, U> PartialEq<Port<U>> for Port<T> {
+ fn eq(&self, other: &Port<U>) -> bool {
+ self.port == other.port
+ }
+}
+
+impl<T> PartialEq<u16> for Port<T> {
+ fn eq(&self, other: &u16) -> bool {
+ self.port == *other
+ }
+}
+
+impl<T> PartialEq<Port<T>> for u16 {
+ fn eq(&self, other: &Port<T>) -> bool {
+ other.port == *self
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn partialeq_port() {
+ let port_a = Port::from_str("8080").unwrap();
+ let port_b = Port::from_str("8080").unwrap();
+ assert_eq!(port_a, port_b);
+ }
+
+ #[test]
+ fn partialeq_port_different_reprs() {
+ let port_a = Port {
+ repr: "8081",
+ port: 8081,
+ };
+ let port_b = Port {
+ repr: String::from("8081"),
+ port: 8081,
+ };
+ assert_eq!(port_a, port_b);
+ assert_eq!(port_b, port_a);
+ }
+
+ #[test]
+ fn partialeq_u16() {
+ let port = Port::from_str("8080").unwrap();
+ // test equals in both directions
+ assert_eq!(port, 8080);
+ assert_eq!(8080, port);
+ }
+
+ #[test]
+ fn u16_from_port() {
+ let port = Port::from_str("8080").unwrap();
+ assert_eq!(8080, u16::from(port));
+ }
+}
diff --git a/third_party/rust/http/src/uri/scheme.rs b/third_party/rust/http/src/uri/scheme.rs
new file mode 100644
index 0000000000..682b11eeea
--- /dev/null
+++ b/third_party/rust/http/src/uri/scheme.rs
@@ -0,0 +1,363 @@
+use std::convert::TryFrom;
+use std::fmt;
+use std::hash::{Hash, Hasher};
+use std::str::FromStr;
+
+use bytes::Bytes;
+
+use super::{ErrorKind, InvalidUri};
+use crate::byte_str::ByteStr;
+
+/// Represents the scheme component of a URI
+#[derive(Clone)]
+pub struct Scheme {
+ pub(super) inner: Scheme2,
+}
+
+#[derive(Clone, Debug)]
+pub(super) enum Scheme2<T = Box<ByteStr>> {
+ None,
+ Standard(Protocol),
+ Other(T),
+}
+
+#[derive(Copy, Clone, Debug)]
+pub(super) enum Protocol {
+ Http,
+ Https,
+}
+
+impl Scheme {
+ /// HTTP protocol scheme
+ pub const HTTP: Scheme = Scheme {
+ inner: Scheme2::Standard(Protocol::Http),
+ };
+
+ /// HTTP protocol over TLS.
+ pub const HTTPS: Scheme = Scheme {
+ inner: Scheme2::Standard(Protocol::Https),
+ };
+
+ pub(super) fn empty() -> Self {
+ Scheme {
+ inner: Scheme2::None,
+ }
+ }
+
+ /// Return a str representation of the scheme
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use http::uri::*;
+ /// let scheme: Scheme = "http".parse().unwrap();
+ /// assert_eq!(scheme.as_str(), "http");
+ /// ```
+ #[inline]
+ pub fn as_str(&self) -> &str {
+ use self::Protocol::*;
+ use self::Scheme2::*;
+
+ match self.inner {
+ Standard(Http) => "http",
+ Standard(Https) => "https",
+ Other(ref v) => &v[..],
+ None => unreachable!(),
+ }
+ }
+}
+
+impl<'a> TryFrom<&'a [u8]> for Scheme {
+ type Error = InvalidUri;
+ #[inline]
+ fn try_from(s: &'a [u8]) -> Result<Self, Self::Error> {
+ use self::Scheme2::*;
+
+ match Scheme2::parse_exact(s)? {
+ None => Err(ErrorKind::InvalidScheme.into()),
+ Standard(p) => Ok(Standard(p).into()),
+ Other(_) => {
+ let bytes = Bytes::copy_from_slice(s);
+
+ // Safety: postcondition on parse_exact() means that s and
+ // hence bytes are valid UTF-8.
+ let string = unsafe { ByteStr::from_utf8_unchecked(bytes) };
+
+ Ok(Other(Box::new(string)).into())
+ }
+ }
+ }
+}
+
+impl<'a> TryFrom<&'a str> for Scheme {
+ type Error = InvalidUri;
+ #[inline]
+ fn try_from(s: &'a str) -> Result<Self, Self::Error> {
+ TryFrom::try_from(s.as_bytes())
+ }
+}
+
+impl FromStr for Scheme {
+ type Err = InvalidUri;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ TryFrom::try_from(s)
+ }
+}
+
+impl fmt::Debug for Scheme {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt::Debug::fmt(self.as_str(), f)
+ }
+}
+
+impl fmt::Display for Scheme {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str(self.as_str())
+ }
+}
+
+impl AsRef<str> for Scheme {
+ #[inline]
+ fn as_ref(&self) -> &str {
+ self.as_str()
+ }
+}
+
+impl PartialEq for Scheme {
+ fn eq(&self, other: &Scheme) -> bool {
+ use self::Protocol::*;
+ use self::Scheme2::*;
+
+ match (&self.inner, &other.inner) {
+ (&Standard(Http), &Standard(Http)) => true,
+ (&Standard(Https), &Standard(Https)) => true,
+ (&Other(ref a), &Other(ref b)) => a.eq_ignore_ascii_case(b),
+ (&None, _) | (_, &None) => unreachable!(),
+ _ => false,
+ }
+ }
+}
+
+impl Eq for Scheme {}
+
+/// Case-insensitive equality
+///
+/// # Examples
+///
+/// ```
+/// # use http::uri::Scheme;
+/// let scheme: Scheme = "HTTP".parse().unwrap();
+/// assert_eq!(scheme, *"http");
+/// ```
+impl PartialEq<str> for Scheme {
+ fn eq(&self, other: &str) -> bool {
+ self.as_str().eq_ignore_ascii_case(other)
+ }
+}
+
+/// Case-insensitive equality
+impl PartialEq<Scheme> for str {
+ fn eq(&self, other: &Scheme) -> bool {
+ other == self
+ }
+}
+
+/// Case-insensitive hashing
+impl Hash for Scheme {
+ fn hash<H>(&self, state: &mut H)
+ where
+ H: Hasher,
+ {
+ match self.inner {
+ Scheme2::None => (),
+ Scheme2::Standard(Protocol::Http) => state.write_u8(1),
+ Scheme2::Standard(Protocol::Https) => state.write_u8(2),
+ Scheme2::Other(ref other) => {
+ other.len().hash(state);
+ for &b in other.as_bytes() {
+ state.write_u8(b.to_ascii_lowercase());
+ }
+ }
+ }
+ }
+}
+
+impl<T> Scheme2<T> {
+ pub(super) fn is_none(&self) -> bool {
+ match *self {
+ Scheme2::None => true,
+ _ => false,
+ }
+ }
+}
+
+// Require the scheme to not be too long in order to enable further
+// optimizations later.
+const MAX_SCHEME_LEN: usize = 64;
+
+// scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
+//
+// SCHEME_CHARS is a table of valid characters in the scheme part of a URI. An
+// entry in the table is 0 for invalid characters. For valid characters the
+// entry is itself (i.e. the entry for 43 is b'+' because b'+' == 43u8). 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 SCHEME_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, 0, 0, 0, 0, 0, 0, 0, // 3x
+ 0, 0, 0, b'+', 0, b'-', b'.', 0, b'0', b'1', // 4x
+ b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b':', 0, // 5x
+ 0, 0, 0, 0, 0, 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', 0, 0, 0, 0, 0, 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 Scheme2<usize> {
+ // Postcondition: On all Ok() returns, s is valid UTF-8
+ fn parse_exact(s: &[u8]) -> Result<Scheme2<()>, InvalidUri> {
+ match s {
+ b"http" => Ok(Protocol::Http.into()),
+ b"https" => Ok(Protocol::Https.into()),
+ _ => {
+ if s.len() > MAX_SCHEME_LEN {
+ return Err(ErrorKind::SchemeTooLong.into());
+ }
+
+ // check that each byte in s is a SCHEME_CHARS which implies
+ // that it is a valid single byte UTF-8 code point.
+ for &b in s {
+ match SCHEME_CHARS[b as usize] {
+ b':' => {
+ // Don't want :// here
+ return Err(ErrorKind::InvalidScheme.into());
+ }
+ 0 => {
+ return Err(ErrorKind::InvalidScheme.into());
+ }
+ _ => {}
+ }
+ }
+
+ Ok(Scheme2::Other(()))
+ }
+ }
+ }
+
+ pub(super) fn parse(s: &[u8]) -> Result<Scheme2<usize>, InvalidUri> {
+ if s.len() >= 7 {
+ // Check for HTTP
+ if s[..7].eq_ignore_ascii_case(b"http://") {
+ // Prefix will be striped
+ return Ok(Protocol::Http.into());
+ }
+ }
+
+ if s.len() >= 8 {
+ // Check for HTTPs
+ if s[..8].eq_ignore_ascii_case(b"https://") {
+ return Ok(Protocol::Https.into());
+ }
+ }
+
+ if s.len() > 3 {
+ for i in 0..s.len() {
+ let b = s[i];
+
+ match SCHEME_CHARS[b as usize] {
+ b':' => {
+ // Not enough data remaining
+ if s.len() < i + 3 {
+ break;
+ }
+
+ // Not a scheme
+ if &s[i + 1..i + 3] != b"//" {
+ break;
+ }
+
+ if i > MAX_SCHEME_LEN {
+ return Err(ErrorKind::SchemeTooLong.into());
+ }
+
+ // Return scheme
+ return Ok(Scheme2::Other(i));
+ }
+ // Invald scheme character, abort
+ 0 => break,
+ _ => {}
+ }
+ }
+ }
+
+ Ok(Scheme2::None)
+ }
+}
+
+impl Protocol {
+ pub(super) fn len(&self) -> usize {
+ match *self {
+ Protocol::Http => 4,
+ Protocol::Https => 5,
+ }
+ }
+}
+
+impl<T> From<Protocol> for Scheme2<T> {
+ fn from(src: Protocol) -> Self {
+ Scheme2::Standard(src)
+ }
+}
+
+#[doc(hidden)]
+impl From<Scheme2> for Scheme {
+ fn from(src: Scheme2) -> Self {
+ Scheme { inner: src }
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ #[test]
+ fn scheme_eq_to_str() {
+ assert_eq!(&scheme("http"), "http");
+ assert_eq!(&scheme("https"), "https");
+ assert_eq!(&scheme("ftp"), "ftp");
+ assert_eq!(&scheme("my+funky+scheme"), "my+funky+scheme");
+ }
+
+ #[test]
+ fn invalid_scheme_is_error() {
+ Scheme::try_from("my_funky_scheme").expect_err("Unexpectly valid Scheme");
+
+ // Invalid UTF-8
+ Scheme::try_from([0xC0].as_ref()).expect_err("Unexpectly valid Scheme");
+ }
+
+ fn scheme(s: &str) -> Scheme {
+ s.parse().expect(&format!("Invalid scheme: {}", s))
+ }
+}
diff --git a/third_party/rust/http/src/uri/tests.rs b/third_party/rust/http/src/uri/tests.rs
new file mode 100644
index 0000000000..719cb94ee3
--- /dev/null
+++ b/third_party/rust/http/src/uri/tests.rs
@@ -0,0 +1,519 @@
+use std::str::FromStr;
+
+use super::{ErrorKind, InvalidUri, Port, Uri, URI_CHARS};
+
+#[test]
+fn test_char_table() {
+ for (i, &v) in URI_CHARS.iter().enumerate() {
+ if v != 0 {
+ assert_eq!(i, v as usize);
+ }
+ }
+}
+
+macro_rules! part {
+ ($s:expr) => {
+ Some(&$s.parse().unwrap())
+ };
+}
+
+macro_rules! test_parse {
+ (
+ $test_name:ident,
+ $str:expr,
+ $alt:expr,
+ $($method:ident = $value:expr,)*
+ ) => (
+ #[test]
+ fn $test_name() {
+ let orig_str = $str;
+ let uri = match Uri::from_str(orig_str) {
+ Ok(uri) => uri,
+ Err(err) => {
+ panic!("parse error {:?} from {:?}", err, orig_str);
+ },
+ };
+ $(
+ assert_eq!(uri.$method(), $value, "{}: uri = {:?}", stringify!($method), uri);
+ )+
+ assert_eq!(uri, orig_str, "partial eq to original str");
+ assert_eq!(uri, uri.clone(), "clones are equal");
+
+ let new_str = uri.to_string();
+ let new_uri = Uri::from_str(&new_str).expect("to_string output parses again as a Uri");
+ assert_eq!(new_uri, orig_str, "round trip still equals original str");
+
+ const ALT: &'static [&'static str] = &$alt;
+
+ for &alt in ALT.iter() {
+ let other: Uri = alt.parse().unwrap();
+ assert_eq!(uri, *alt);
+ assert_eq!(uri, other);
+ }
+ }
+ );
+}
+
+test_parse! {
+ test_uri_parse_path_and_query,
+ "/some/path/here?and=then&hello#and-bye",
+ [],
+
+ scheme = None,
+ authority = None,
+ path = "/some/path/here",
+ query = Some("and=then&hello"),
+ host = None,
+}
+
+test_parse! {
+ test_uri_parse_absolute_form,
+ "http://127.0.0.1:61761/chunks",
+ [],
+
+ scheme = part!("http"),
+ authority = part!("127.0.0.1:61761"),
+ path = "/chunks",
+ query = None,
+ host = Some("127.0.0.1"),
+ port = Port::from_str("61761").ok(),
+}
+
+test_parse! {
+ test_uri_parse_absolute_form_without_path,
+ "https://127.0.0.1:61761",
+ ["https://127.0.0.1:61761/"],
+
+ scheme = part!("https"),
+ authority = part!("127.0.0.1:61761"),
+ path = "/",
+ query = None,
+ host = Some("127.0.0.1"),
+ port = Port::from_str("61761").ok(),
+}
+
+test_parse! {
+ test_uri_parse_asterisk_form,
+ "*",
+ [],
+
+ scheme = None,
+ authority = None,
+ path = "*",
+ query = None,
+ host = None,
+}
+
+test_parse! {
+ test_uri_parse_authority_no_port,
+ "localhost",
+ ["LOCALHOST", "LocaLHOSt"],
+
+ scheme = None,
+ authority = part!("localhost"),
+ path = "",
+ query = None,
+ port = None,
+ host = Some("localhost"),
+}
+
+test_parse! {
+ test_uri_authority_only_one_character_issue_197,
+ "S",
+ [],
+
+ scheme = None,
+ authority = part!("S"),
+ path = "",
+ query = None,
+ port = None,
+ host = Some("S"),
+}
+
+test_parse! {
+ test_uri_parse_authority_form,
+ "localhost:3000",
+ ["localhosT:3000"],
+
+ scheme = None,
+ authority = part!("localhost:3000"),
+ path = "",
+ query = None,
+ host = Some("localhost"),
+ port = Port::from_str("3000").ok(),
+}
+
+test_parse! {
+ test_uri_parse_absolute_with_default_port_http,
+ "http://127.0.0.1:80",
+ ["http://127.0.0.1:80/"],
+
+ scheme = part!("http"),
+ authority = part!("127.0.0.1:80"),
+ host = Some("127.0.0.1"),
+ path = "/",
+ query = None,
+ port = Port::from_str("80").ok(),
+}
+
+test_parse! {
+ test_uri_parse_absolute_with_default_port_https,
+ "https://127.0.0.1:443",
+ ["https://127.0.0.1:443/"],
+
+ scheme = part!("https"),
+ authority = part!("127.0.0.1:443"),
+ host = Some("127.0.0.1"),
+ path = "/",
+ query = None,
+ port = Port::from_str("443").ok(),
+}
+
+test_parse! {
+ test_uri_parse_fragment_questionmark,
+ "http://127.0.0.1/#?",
+ [],
+
+ scheme = part!("http"),
+ authority = part!("127.0.0.1"),
+ host = Some("127.0.0.1"),
+ path = "/",
+ query = None,
+ port = None,
+}
+
+test_parse! {
+ test_uri_parse_path_with_terminating_questionmark,
+ "http://127.0.0.1/path?",
+ [],
+
+ scheme = part!("http"),
+ authority = part!("127.0.0.1"),
+ path = "/path",
+ query = Some(""),
+ port = None,
+}
+
+test_parse! {
+ test_uri_parse_absolute_form_with_empty_path_and_nonempty_query,
+ "http://127.0.0.1?foo=bar",
+ [],
+
+ scheme = part!("http"),
+ authority = part!("127.0.0.1"),
+ path = "/",
+ query = Some("foo=bar"),
+ port = None,
+}
+
+test_parse! {
+ test_uri_parse_absolute_form_with_empty_path_and_fragment_with_slash,
+ "http://127.0.0.1#foo/bar",
+ [],
+
+ scheme = part!("http"),
+ authority = part!("127.0.0.1"),
+ path = "/",
+ query = None,
+ port = None,
+}
+
+test_parse! {
+ test_uri_parse_absolute_form_with_empty_path_and_fragment_with_questionmark,
+ "http://127.0.0.1#foo?bar",
+ [],
+
+ scheme = part!("http"),
+ authority = part!("127.0.0.1"),
+ path = "/",
+ query = None,
+ port = None,
+}
+
+test_parse! {
+ test_uri_parse_long_host_with_no_scheme,
+ "thequickbrownfoxjumpedoverthelazydogtofindthelargedangerousdragon.localhost",
+ [],
+
+ scheme = None,
+ authority = part!("thequickbrownfoxjumpedoverthelazydogtofindthelargedangerousdragon.localhost"),
+ path = "",
+ query = None,
+ port = None,
+}
+
+test_parse! {
+ test_uri_parse_long_host_with_port_and_no_scheme,
+ "thequickbrownfoxjumpedoverthelazydogtofindthelargedangerousdragon.localhost:1234",
+ [],
+
+ scheme = None,
+ authority = part!("thequickbrownfoxjumpedoverthelazydogtofindthelargedangerousdragon.localhost:1234"),
+ path = "",
+ query = None,
+ port = Port::from_str("1234").ok(),
+}
+
+test_parse! {
+ test_userinfo1,
+ "http://a:b@127.0.0.1:1234/",
+ [],
+
+ scheme = part!("http"),
+ authority = part!("a:b@127.0.0.1:1234"),
+ host = Some("127.0.0.1"),
+ path = "/",
+ query = None,
+ port = Port::from_str("1234").ok(),
+}
+
+test_parse! {
+ test_userinfo2,
+ "http://a:b@127.0.0.1/",
+ [],
+
+ scheme = part!("http"),
+ authority = part!("a:b@127.0.0.1"),
+ host = Some("127.0.0.1"),
+ path = "/",
+ query = None,
+ port = None,
+}
+
+test_parse! {
+ test_userinfo3,
+ "http://a@127.0.0.1/",
+ [],
+
+ scheme = part!("http"),
+ authority = part!("a@127.0.0.1"),
+ host = Some("127.0.0.1"),
+ path = "/",
+ query = None,
+ port = None,
+}
+
+test_parse! {
+ test_userinfo_with_port,
+ "user@localhost:3000",
+ [],
+
+ scheme = None,
+ authority = part!("user@localhost:3000"),
+ path = "",
+ query = None,
+ host = Some("localhost"),
+ port = Port::from_str("3000").ok(),
+}
+
+test_parse! {
+ test_userinfo_pass_with_port,
+ "user:pass@localhost:3000",
+ [],
+
+ scheme = None,
+ authority = part!("user:pass@localhost:3000"),
+ path = "",
+ query = None,
+ host = Some("localhost"),
+ port = Port::from_str("3000").ok(),
+}
+
+test_parse! {
+ test_ipv6,
+ "http://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]/",
+ [],
+
+ scheme = part!("http"),
+ authority = part!("[2001:0db8:85a3:0000:0000:8a2e:0370:7334]"),
+ host = Some("[2001:0db8:85a3:0000:0000:8a2e:0370:7334]"),
+ path = "/",
+ query = None,
+ port = None,
+}
+
+test_parse! {
+ test_ipv6_shorthand,
+ "http://[::1]/",
+ [],
+
+ scheme = part!("http"),
+ authority = part!("[::1]"),
+ host = Some("[::1]"),
+ path = "/",
+ query = None,
+ port = None,
+}
+
+test_parse! {
+ test_ipv6_shorthand2,
+ "http://[::]/",
+ [],
+
+ scheme = part!("http"),
+ authority = part!("[::]"),
+ host = Some("[::]"),
+ path = "/",
+ query = None,
+ port = None,
+}
+
+test_parse! {
+ test_ipv6_shorthand3,
+ "http://[2001:db8::2:1]/",
+ [],
+
+ scheme = part!("http"),
+ authority = part!("[2001:db8::2:1]"),
+ host = Some("[2001:db8::2:1]"),
+ path = "/",
+ query = None,
+ port = None,
+}
+
+test_parse! {
+ test_ipv6_with_port,
+ "http://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:8008/",
+ [],
+
+ scheme = part!("http"),
+ authority = part!("[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:8008"),
+ host = Some("[2001:0db8:85a3:0000:0000:8a2e:0370:7334]"),
+ path = "/",
+ query = None,
+ port = Port::from_str("8008").ok(),
+}
+
+test_parse! {
+ test_percentage_encoded_path,
+ "/echo/abcdefgh_i-j%20/abcdefg_i-j%20478",
+ [],
+
+ scheme = None,
+ authority = None,
+ host = None,
+ path = "/echo/abcdefgh_i-j%20/abcdefg_i-j%20478",
+ query = None,
+ port = None,
+}
+
+test_parse! {
+ test_path_permissive,
+ "/foo=bar|baz\\^~%",
+ [],
+
+ path = "/foo=bar|baz\\^~%",
+}
+
+test_parse! {
+ test_query_permissive,
+ "/?foo={bar|baz}\\^`",
+ [],
+
+ query = Some("foo={bar|baz}\\^`"),
+}
+
+#[test]
+fn test_uri_parse_error() {
+ fn err(s: &str) {
+ Uri::from_str(s).unwrap_err();
+ }
+
+ err("http://");
+ err("htt:p//host");
+ err("hyper.rs/");
+ err("hyper.rs?key=val");
+ err("?key=val");
+ err("localhost/");
+ err("localhost?key=val");
+ err("\0");
+ err("http://[::1");
+ err("http://::1]");
+ err("localhost:8080:3030");
+ err("@");
+ err("http://username:password@/wut");
+
+ // illegal queries
+ err("/?foo\rbar");
+ err("/?foo\nbar");
+ err("/?<");
+ err("/?>");
+}
+
+#[test]
+fn test_max_uri_len() {
+ let mut uri = vec![];
+ uri.extend(b"http://localhost/");
+ uri.extend(vec![b'a'; 70 * 1024]);
+
+ let uri = String::from_utf8(uri).unwrap();
+ let res: Result<Uri, InvalidUri> = uri.parse();
+
+ assert_eq!(res.unwrap_err().0, ErrorKind::TooLong);
+}
+
+#[test]
+fn test_overflowing_scheme() {
+ let mut uri = vec![];
+ uri.extend(vec![b'a'; 256]);
+ uri.extend(b"://localhost/");
+
+ let uri = String::from_utf8(uri).unwrap();
+ let res: Result<Uri, InvalidUri> = uri.parse();
+
+ assert_eq!(res.unwrap_err().0, ErrorKind::SchemeTooLong);
+}
+
+#[test]
+fn test_max_length_scheme() {
+ let mut uri = vec![];
+ uri.extend(vec![b'a'; 64]);
+ uri.extend(b"://localhost/");
+
+ let uri = String::from_utf8(uri).unwrap();
+ let uri: Uri = uri.parse().unwrap();
+
+ assert_eq!(uri.scheme_str().unwrap().len(), 64);
+}
+
+#[test]
+fn test_uri_to_path_and_query() {
+ let cases = vec![
+ ("/", "/"),
+ ("/foo?bar", "/foo?bar"),
+ ("/foo?bar#nope", "/foo?bar"),
+ ("http://hyper.rs", "/"),
+ ("http://hyper.rs/", "/"),
+ ("http://hyper.rs/path", "/path"),
+ ("http://hyper.rs?query", "/?query"),
+ ("*", "*"),
+ ];
+
+ for case in cases {
+ let uri = Uri::from_str(case.0).unwrap();
+ let s = uri.path_and_query().unwrap().to_string();
+
+ assert_eq!(s, case.1);
+ }
+}
+
+#[test]
+fn test_authority_uri_parts_round_trip() {
+ let s = "hyper.rs";
+ let uri = Uri::from_str(s).expect("first parse");
+ assert_eq!(uri, s);
+ assert_eq!(uri.to_string(), s);
+
+ let parts = uri.into_parts();
+ let uri2 = Uri::from_parts(parts).expect("from_parts");
+ assert_eq!(uri2, s);
+ assert_eq!(uri2.to_string(), s);
+}
+
+#[test]
+fn test_partial_eq_path_with_terminating_questionmark() {
+ let a = "/path";
+ let uri = Uri::from_str("/path?").expect("first parse");
+
+ assert_eq!(uri, a);
+}