use HttpTryFrom;
use byte_str::ByteStr;
use bytes::Bytes;
use std::{fmt, u8, u16};
use std::ascii::AsciiExt;
use std::hash::{Hash, Hasher};
use std::str::{self, FromStr};
use std::error::Error;
#[derive(Clone)]
pub struct Uri {
scheme: Scheme,
authority: Authority,
path_and_query: PathAndQuery,
}
#[derive(Clone)]
pub struct Scheme {
inner: Scheme2,
}
#[derive(Clone, Debug)]
enum Scheme2<T = Box<ByteStr>> {
None,
Standard(Protocol),
Other(T),
}
#[derive(Copy, Clone, Debug)]
enum Protocol {
Http,
Https,
}
#[derive(Clone)]
pub struct Authority {
data: ByteStr,
}
#[derive(Clone)]
pub struct PathAndQuery {
data: ByteStr,
query: u16,
}
#[derive(Debug, Default)]
pub struct Parts {
pub scheme: Option<Scheme>,
pub authority: Option<Authority>,
pub path_and_query: Option<PathAndQuery>,
_priv: (),
}
#[derive(Debug)]
pub struct InvalidUri(ErrorKind);
#[derive(Debug)]
pub struct InvalidUriBytes(InvalidUri);
#[derive(Debug)]
pub struct InvalidUriParts(InvalidUri);
#[derive(Debug, Eq, PartialEq)]
enum ErrorKind {
InvalidUriChar,
InvalidScheme,
InvalidAuthority,
InvalidFormat,
AuthorityMissing,
PathAndQueryMissing,
TooLong,
Empty,
SchemeTooLong,
}
const MAX_LEN: usize = (u16::MAX - 1) as usize;
const MAX_SCHEME_LEN: usize = 64;
const NONE: u16 = u16::MAX;
const URI_CHARS: [u8; 256] = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, b'!', 0, b'#', b'$', 0, b'&', b'\'', b'(', b')', b'*', b'+', b',', b'-', b'.', b'/', b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b':', b';', 0, b'=', 0, b'?', b'@', b'A', b'B', b'C', b'D', b'E', b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W', b'X', b'Y', b'Z', b'[', 0, b']', 0, b'_', 0, b'a', b'b', b'c', b'd', b'e', b'f', b'g', b'h', b'i', b'j', b'k', b'l', b'm', b'n', b'o', b'p', b'q', b'r', b's', b't', b'u', b'v', b'w', b'x', b'y', b'z', 0, 0, 0, b'~', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
const SCHEME_CHARS: [u8; 256] = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, b'+', 0, b'-', b'.', 0, b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b':', 0, 0, 0, 0, 0, 0, b'A', b'B', b'C', b'D', b'E', b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W', b'X', b'Y', b'Z', 0, 0, 0, 0, 0, 0, b'a', b'b', b'c', b'd', b'e', b'f', b'g', b'h', b'i', b'j', b'k', b'l', b'm', b'n', b'o', b'p', b'q', b'r', b's', b't', b'u', b'v', b'w', b'x', b'y', b'z', 0, 0, 0, b'~', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
impl Uri {
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_none() {
return Err(ErrorKind::PathAndQueryMissing.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,
})
}
pub fn from_shared(s: Bytes) -> Result<Uri, InvalidUriBytes> {
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::slash(),
});
}
}
}
_ => {}
}
if s[0] == b'/' {
return Ok(Uri {
scheme: Scheme::empty(),
authority: Authority::empty(),
path_and_query: PathAndQuery::from_shared(s)?,
});
}
parse_full(s)
}
#[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
}
}
#[inline]
pub fn path(&self) -> &str {
if self.has_path() {
self.path_and_query.path()
} else {
""
}
}
#[inline]
pub fn scheme(&self) -> Option<&str> {
if self.scheme.inner.is_none() {
None
} else {
Some(self.scheme.as_str())
}
}
#[inline]
pub fn authority_part(&self) -> Option<&Authority> {
if self.authority.data.is_empty() {
None
} else {
Some(&self.authority)
}
}
#[deprecated(since = "0.1.1", note = "use authority_part instead")]
#[doc(hidden)]
#[inline]
pub fn authority(&self) -> Option<&str> {
if self.authority.data.is_empty() {
None
} else {
Some(self.authority.as_str())
}
}
#[inline]
pub fn host(&self) -> Option<&str> {
self.authority_part().map(|a| a.host())
}
pub fn port(&self) -> Option<u16> {
self.authority_part()
.and_then(|a| a.port())
}
#[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> HttpTryFrom<&'a str> for Uri {
type Error = InvalidUri;
#[inline]
fn try_from(t: &'a str) -> Result<Self, Self::Error> {
t.parse()
}
}
impl HttpTryFrom<Bytes> for Uri {
type Error = InvalidUriBytes;
#[inline]
fn try_from(t: Bytes) -> Result<Self, Self::Error> {
Uri::from_shared(t)
}
}
impl HttpTryFrom<Parts> for Uri {
type Error = InvalidUriParts;
#[inline]
fn try_from(src: Parts) -> Result<Self, Self::Error> {
Uri::from_parts(src)
}
}
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: (),
}
}
}
impl Scheme {
pub fn from_shared(s: Bytes) -> Result<Self, InvalidUriBytes> {
use self::Scheme2::*;
match Scheme2::parse_exact(&s[..]).map_err(InvalidUriBytes)? {
None => Err(ErrorKind::InvalidScheme.into()),
Standard(p) => Ok(Standard(p).into()),
Other(_) => {
let b = unsafe { ByteStr::from_utf8_unchecked(s) };
Ok(Other(Box::new(b)).into())
}
}
}
fn empty() -> Self {
Scheme {
inner: Scheme2::None,
}
}
#[inline]
pub fn as_str(&self) -> &str {
use self::Scheme2::*;
use self::Protocol::*;
match self.inner {
Standard(Http) => "http",
Standard(Https) => "https",
Other(ref v) => &v[..],
None => unreachable!(),
}
}
#[inline]
pub fn into_bytes(self) -> Bytes {
self.into()
}
}
impl FromStr for Scheme {
type Err = InvalidUri;
fn from_str(s: &str) -> Result<Self, Self::Err> {
use self::Scheme2::*;
match Scheme2::parse_exact(s.as_bytes())? {
None => Err(ErrorKind::InvalidScheme.into()),
Standard(p) => Ok(Standard(p).into()),
Other(_) => {
Ok(Other(Box::new(s.into())).into())
}
}
}
}
impl From<Scheme> for Bytes {
#[inline]
fn from(src: Scheme) -> Self {
use self::Scheme2::*;
use self::Protocol::*;
match src.inner {
None => Bytes::new(),
Standard(Http) => Bytes::from_static(b"http"),
Standard(Https) => Bytes::from_static(b"https"),
Other(v) => (*v).into(),
}
}
}
impl fmt::Debug for Scheme {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(self.as_str())
}
}
impl fmt::Display for Scheme {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(self.as_str())
}
}
impl<T> Scheme2<T> {
fn is_none(&self) -> bool {
match *self {
Scheme2::None => true,
_ => false,
}
}
}
impl Scheme2<usize> {
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());
}
for &b in s {
match SCHEME_CHARS[b as usize] {
b':' => {
return Err(ErrorKind::InvalidScheme.into());
}
0 => {
return Err(ErrorKind::InvalidScheme.into());
}
_ => {}
}
}
Ok(Scheme2::Other(()))
}
}
}
fn parse(s: &[u8]) -> Result<Scheme2<usize>, InvalidUri> {
if s.len() >= 7 {
if s[..7].eq_ignore_ascii_case(b"http://") {
return Ok(Protocol::Http.into());
}
}
if s.len() >= 8 {
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];
if i == MAX_SCHEME_LEN {
return Err(ErrorKind::SchemeTooLong.into());
}
match SCHEME_CHARS[b as usize] {
b':' => {
if s.len() < i + 3 {
break;
}
if &s[i+1..i+3] != b"//" {
break;
}
return Ok(Scheme2::Other(i));
}
0 => break,
_ => {}
}
}
}
Ok(Scheme2::None)
}
}
impl Protocol {
fn len(&self) -> usize {
match *self {
Protocol::Http => 4,
Protocol::Https => 5,
}
}
}
impl Authority {
fn empty() -> Self {
Authority { data: ByteStr::new() }
}
pub fn from_shared(s: Bytes) -> Result<Self, InvalidUriBytes> {
let authority_end = Authority::parse(&s[..]).map_err(InvalidUriBytes)?;
if authority_end != s.len() {
return Err(ErrorKind::InvalidUriChar.into());
}
Ok(Authority {
data: unsafe { ByteStr::from_utf8_unchecked(s) },
})
}
fn parse(s: &[u8]) -> Result<usize, InvalidUri> {
let mut start_bracket = false;
let mut end_bracket = false;
let mut end = s.len();
for (i, &b) in s.iter().enumerate() {
match URI_CHARS[b as usize] {
b'/' | b'?' | b'#' => {
end = i;
break;
}
b'[' => {
start_bracket = true;
}
b']' => {
end_bracket = true;
}
0 => {
return Err(ErrorKind::InvalidUriChar.into());
}
_ => {}
}
}
if start_bracket ^ end_bracket {
return Err(ErrorKind::InvalidAuthority.into());
}
Ok(end)
}
#[inline]
pub fn host(&self) -> &str {
host(self.as_str())
}
pub fn port(&self) -> Option<u16> {
let s = self.as_str();
s.rfind(":").and_then(|i| {
u16::from_str(&s[i+1..]).ok()
})
}
#[inline]
pub fn as_str(&self) -> &str {
&self.data[..]
}
#[inline]
pub fn into_bytes(self) -> Bytes {
self.into()
}
}
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 {}
impl PartialEq<str> for Authority {
fn eq(&self, other: &str) -> bool {
self.data.eq_ignore_ascii_case(other)
}
}
impl Hash for Authority {
fn hash<H>(&self, state: &mut H) where H: Hasher {
for &b in self.data.as_bytes() {
state.write_u8(b.to_ascii_lowercase());
}
}
}
impl FromStr for Authority {
type Err = InvalidUri;
fn from_str(s: &str) -> Result<Self, InvalidUri> {
let end = Authority::parse(s.as_bytes())?;
if end != s.len() {
return Err(ErrorKind::InvalidAuthority.into());
}
Ok(Authority { data: s.into() })
}
}
impl From<Authority> for Bytes {
#[inline]
fn from(src: Authority) -> Bytes {
src.data.into()
}
}
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())
}
}
impl PathAndQuery {
pub fn from_shared(mut src: Bytes) -> Result<Self, InvalidUriBytes> {
let mut query = NONE;
for i in 0..src.len() {
let b = src[i];
match URI_CHARS[b as usize] {
0 => {
return Err(ErrorKind::InvalidUriChar.into());
}
b'?' => {
if query == NONE {
query = i as u16;
}
}
b'#' => {
src.split_off(i);
break;
}
_ => {}
}
}
Ok(PathAndQuery {
data: unsafe { ByteStr::from_utf8_unchecked(src) },
query: query,
})
}
fn empty() -> Self {
PathAndQuery {
data: ByteStr::new(),
query: NONE,
}
}
fn slash() -> Self {
PathAndQuery {
data: ByteStr::from_static("/"),
query: NONE,
}
}
fn star() -> Self {
PathAndQuery {
data: ByteStr::from_static("*"),
query: NONE,
}
}
#[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
}
#[inline]
pub fn query(&self) -> Option<&str> {
if self.query == NONE {
None
} else {
let i = self.query + 1;
Some(&self.data[i as usize..])
}
}
#[inline]
pub fn into_bytes(self) -> Bytes {
self.into()
}
}
impl FromStr for PathAndQuery {
type Err = InvalidUri;
fn from_str(s: &str) -> Result<Self, InvalidUri> {
PathAndQuery::from_shared(s.into()).map_err(|e| e.0)
}
}
impl From<PathAndQuery> for Bytes {
fn from(src: PathAndQuery) -> Bytes {
src.data.into()
}
}
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, "/")
}
}
}
fn parse_full(mut s: Bytes) -> Result<Uri, InvalidUriBytes> {
let scheme = match Scheme2::parse(&s[..]).map_err(InvalidUriBytes)? {
Scheme2::None => Scheme2::None,
Scheme2::Standard(p) => {
let _ = s.split_to(p.len() + 3);
Scheme2::Standard(p)
}
Scheme2::Other(n) => {
let mut scheme = s.split_to(n + 3);
let _ = scheme.split_off(n);
let val = unsafe { ByteStr::from_utf8_unchecked(scheme) };
Scheme2::Other(Box::new(val))
}
};
let authority_end = Authority::parse(&s[..]).map_err(InvalidUriBytes)?;
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(),
});
}
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)?,
})
}
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");
&host_port[1..i]
} else {
host_port.split(':')
.next()
.expect("split always has at least 1 item")
}
}
impl FromStr for Uri {
type Err = InvalidUri;
#[inline]
fn from_str(s: &str) -> Result<Uri, InvalidUri> {
Uri::from_shared(s.into()).map_err(|e| e.0)
}
}
impl PartialEq for Uri {
fn eq(&self, other: &Uri) -> bool {
match (self.scheme(), other.scheme()) {
(Some(a), Some(b)) => {
if !a.eq_ignore_ascii_case(b) {
return false;
}
}
(None, None) => {}
_ => return false,
};
match (self.authority_part(), other.authority_part()) {
(Some(a), Some(b)) => {
if a != b {
return false;
}
}
(None, None) => {}
_ => 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() {
absolute = true;
if other.len() < scheme.len() + 3 {
return false;
}
if !scheme.as_bytes().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_part() {
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 == "/" {
} else {
return false;
}
} else {
other = &other[path.len()..];
}
if let Some(query) = self.query() {
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 {}
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_part() {
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 InvalidUriBytes {
fn from(src: ErrorKind) -> InvalidUriBytes {
InvalidUriBytes(src.into())
}
}
impl From<ErrorKind> for InvalidUriParts {
fn from(src: ErrorKind) -> InvalidUriParts {
InvalidUriParts(src.into())
}
}
impl fmt::Display for InvalidUri {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.description().fmt(f)
}
}
impl Error for InvalidUri {
fn description(&self) -> &str {
match self.0 {
ErrorKind::InvalidUriChar => "invalid uri character",
ErrorKind::InvalidScheme => "invalid scheme",
ErrorKind::InvalidAuthority => "invalid authority",
ErrorKind::InvalidFormat => "invalid format",
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 InvalidUriBytes {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
impl fmt::Display for InvalidUriParts {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
impl Error for InvalidUriBytes {
fn description(&self) -> &str {
self.0.description()
}
}
impl Error for InvalidUriParts {
fn description(&self) -> &str {
self.0.description()
}
}
impl<T> From<Protocol> for Scheme2<T> {
fn from(src: Protocol) -> Self {
Scheme2::Standard(src)
}
}
impl From<Scheme2> for Scheme {
fn from(src: Scheme2) -> Self {
Scheme { inner: src }
}
}
impl Hash for Uri {
fn hash<H>(&self, state: &mut H) where H: Hasher {
if let Some(scheme) = self.scheme() {
scheme.as_bytes().to_ascii_lowercase().hash(state);
"://".hash(state);
}
if let Some(auth) = self.authority_part() {
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);
}
}
}
#[test]
fn test_char_table() {
for (i, &v) in URI_CHARS.iter().enumerate() {
if v != 0 {
assert_eq!(i, v as usize);
}
}
}
macro_rules! test_parse {
(
$test_name:ident,
$str:expr,
$alt:expr,
$($method:ident = $value:expr,)*
) => (
#[test]
fn $test_name() {
let uri = Uri::from_str($str).unwrap();
$(
assert_eq!(uri.$method(), $value, stringify!($method));
)+
assert_eq!(uri, *$str);
assert_eq!(uri, uri.clone());
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_part = 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 = Some("http"),
authority_part = Some(&"127.0.0.1:61761".parse().unwrap()),
path = "/chunks",
query = None,
host = Some("127.0.0.1"),
port = Some(61761),
}
test_parse! {
test_uri_parse_absolute_form_without_path,
"https://127.0.0.1:61761",
["https://127.0.0.1:61761/"],
scheme = Some("https"),
authority_part = Some(&"127.0.0.1:61761".parse().unwrap()),
path = "/",
query = None,
port = Some(61761),
host = Some("127.0.0.1"),
}
test_parse! {
test_uri_parse_asterisk_form,
"*",
[],
scheme = None,
authority_part = None,
path = "*",
query = None,
host = None,
}
test_parse! {
test_uri_parse_authority_no_port,
"localhost",
["LOCALHOST", "LocaLHOSt"],
scheme = None,
authority_part = Some(&"localhost".parse().unwrap()),
path = "",
query = None,
port = None,
host = Some("localhost"),
}
test_parse! {
test_uri_parse_authority_form,
"localhost:3000",
["localhosT:3000"],
scheme = None,
authority_part = Some(&"localhost:3000".parse().unwrap()),
path = "",
query = None,
host = Some("localhost"),
port = Some(3000),
}
test_parse! {
test_uri_parse_absolute_with_default_port_http,
"http://127.0.0.1:80",
["http://127.0.0.1:80/"],
scheme = Some("http"),
authority_part = Some(&"127.0.0.1:80".parse().unwrap()),
host = Some("127.0.0.1"),
path = "/",
query = None,
port = Some(80),
}
test_parse! {
test_uri_parse_absolute_with_default_port_https,
"https://127.0.0.1:443",
["https://127.0.0.1:443/"],
scheme = Some("https"),
authority_part = Some(&"127.0.0.1:443".parse().unwrap()),
host = Some("127.0.0.1"),
path = "/",
query = None,
port = Some(443),
}
test_parse! {
test_uri_parse_fragment_questionmark,
"http://127.0.0.1/#?",
[],
scheme = Some("http"),
authority_part = Some(&"127.0.0.1".parse().unwrap()),
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 = Some("http"),
authority_part = Some(&"127.0.0.1".parse().unwrap()),
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 = Some("http"),
authority_part = Some(&"127.0.0.1".parse().unwrap()),
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 = Some("http"),
authority_part = Some(&"127.0.0.1".parse().unwrap()),
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 = Some("http"),
authority_part = Some(&"127.0.0.1".parse().unwrap()),
path = "/",
query = None,
port = None,
}
test_parse! {
test_userinfo1,
"http://a:b@127.0.0.1:1234/",
[],
scheme = Some("http"),
authority_part = Some(&"a:b@127.0.0.1:1234".parse().unwrap()),
host = Some("127.0.0.1"),
path = "/",
query = None,
port = Some(1234),
}
test_parse! {
test_userinfo2,
"http://a:b@127.0.0.1/",
[],
scheme = Some("http"),
authority_part = Some(&"a:b@127.0.0.1".parse().unwrap()),
host = Some("127.0.0.1"),
path = "/",
query = None,
port = None,
}
test_parse! {
test_userinfo3,
"http://a@127.0.0.1/",
[],
scheme = Some("http"),
authority_part = Some(&"a@127.0.0.1".parse().unwrap()),
host = Some("127.0.0.1"),
path = "/",
query = None,
port = None,
}
test_parse! {
test_ipv6,
"http://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]/",
[],
scheme = Some("http"),
authority_part = Some(&"[2001:0db8:85a3:0000:0000:8a2e:0370:7334]".parse().unwrap()),
host = Some("2001:0db8:85a3:0000:0000:8a2e:0370:7334"),
path = "/",
query = None,
port = None,
}
test_parse! {
test_ipv6_shorthand,
"http://[::1]/",
[],
scheme = Some("http"),
authority_part = Some(&"[::1]".parse().unwrap()),
host = Some("::1"),
path = "/",
query = None,
port = None,
}
test_parse! {
test_ipv6_shorthand2,
"http://[::]/",
[],
scheme = Some("http"),
authority_part = Some(&"[::]".parse().unwrap()),
host = Some("::"),
path = "/",
query = None,
port = None,
}
test_parse! {
test_ipv6_shorthand3,
"http://[2001:db8::2:1]/",
[],
scheme = Some("http"),
authority_part = Some(&"[2001:db8::2:1]".parse().unwrap()),
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 = Some("http"),
authority_part = Some(&"[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:8008".parse().unwrap()),
host = Some("2001:0db8:85a3:0000:0000:8a2e:0370:7334"),
path = "/",
query = None,
port = Some(8008),
}
#[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]");
}
#[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_long_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_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);
}
}