Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 38 additions & 11 deletions src/uri/authority.rs
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@ use std::ascii::AsciiExt;
use std::{cmp, fmt, str};
use std::hash::{Hash, Hasher};
use std::str::FromStr;
use std::convert::TryFrom;

use bytes::Bytes;

@@ -24,8 +25,7 @@ impl Authority {

/// Attempt to convert an `Authority` from `Bytes`.
///
/// This function will be replaced by a `TryFrom` implementation once the
/// trait lands in stable.
/// This function has been replaced by `TryFrom` implementation.
///
/// # Examples
///
@@ -44,15 +44,7 @@ impl Authority {
/// # }
/// ```
pub fn from_shared(s: Bytes) -> Result<Self, InvalidUriBytes> {
let authority_end = Authority::parse_non_empty(&s[..]).map_err(InvalidUriBytes)?;

if authority_end != s.len() {
return Err(ErrorKind::InvalidUriChar.into());
}

Ok(Authority {
data: unsafe { ByteStr::from_utf8_unchecked(s) },
})
TryFrom::try_from(s)
}

/// Attempt to convert an `Authority` from a static string.
@@ -272,6 +264,41 @@ impl Authority {
}
}

impl TryFrom<Bytes> for Authority {
type Error = InvalidUriBytes;
/// Attempt to convert an `Authority` from `Bytes`.
///
/// This function has been replaced by `TryFrom` implementation.
///
/// # Examples
///
/// ```
/// # extern crate http;
/// # use http::uri::*;
/// extern crate bytes;
///
/// use bytes::Bytes;
///
/// # pub fn main() {
/// let bytes = Bytes::from("example.com");
/// let authority = Authority::from_shared(bytes).unwrap();
///
/// assert_eq!(authority.host(), "example.com");
/// # }
/// ```
fn try_from(s: Bytes) -> Result<Self, Self::Error> {
let authority_end = Authority::parse_non_empty(&s[..]).map_err(InvalidUriBytes)?;

if authority_end != s.len() {
return Err(ErrorKind::InvalidUriChar.into());
}

Ok(Authority {
data: unsafe { ByteStr::from_utf8_unchecked(s) },
})
}
}

impl AsRef<str> for Authority {
fn as_ref(&self) -> &str {
self.as_str()
129 changes: 78 additions & 51 deletions src/uri/mod.rs
Original file line number Diff line number Diff line change
@@ -34,6 +34,7 @@ use std::ascii::AsciiExt;
use std::hash::{Hash, Hasher};
use std::str::{self, FromStr};
use std::error::Error;
use std::convert::TryFrom;

use self::scheme::Scheme2;

@@ -242,8 +243,7 @@ impl Uri {

/// Attempt to convert a `Uri` from `Bytes`
///
/// This function will be replaced by a `TryFrom` implementation once the
/// trait lands in stable.
/// This function has been replaced by `TryFrom` implementation.
///
/// # Examples
///
@@ -263,55 +263,7 @@ impl Uri {
/// # }
/// ```
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::empty(),
});
}
}
}
_ => {}
}

if s[0] == b'/' && s[1] != b'/' {
return Ok(Uri {
scheme: Scheme::empty(),
authority: Authority::empty(),
path_and_query: PathAndQuery::from_shared(s)?,
});
}

parse_full(s)
TryFrom::try_from(s)
}

/// Convert a `Uri` from a static string.
@@ -707,6 +659,81 @@ impl Uri {
}
}

impl TryFrom<Bytes> for Uri {
type Error = InvalidUriBytes;

/// Attempt to convert a `Uri` from `Bytes`
///
/// # Examples
///
/// ```
/// # extern crate http;
/// # use http::uri::*;
/// extern crate bytes;
///
/// use bytes::Bytes;
///
/// # pub fn main() {
/// let bytes = Bytes::from("http://example.com/foo");
/// let uri = Uri::from_shared(bytes).unwrap();
///
/// assert_eq!(uri.host().unwrap(), "example.com");
/// assert_eq!(uri.path(), "/foo");
/// # }
/// ```
fn try_from(s: Bytes) -> Result<Uri, Self::Error> {
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'/' && s[1] != b'/' {
return Ok(Uri {
scheme: Scheme::empty(),
authority: Authority::empty(),
path_and_query: PathAndQuery::from_shared(s)?,
});
}

parse_full(s)
}
}

impl<'a> HttpTryFrom<&'a str> for Uri {
type Error = InvalidUri;

171 changes: 99 additions & 72 deletions src/uri/path.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::{cmp, fmt, str};
use std::str::FromStr;
use std::convert::TryFrom;

use bytes::Bytes;

@@ -19,8 +20,7 @@ const NONE: u16 = ::std::u16::MAX;
impl PathAndQuery {
/// Attempt to convert a `PathAndQuery` from `Bytes`.
///
/// This function will be replaced by a `TryFrom` implementation once the
/// trait lands in stable.
/// This function has been replaced by a `TryFrom` implementation.
///
/// # Examples
///
@@ -39,76 +39,8 @@ impl PathAndQuery {
/// assert_eq!(path_and_query.query(), Some("world"));
/// # }
/// ```
pub fn from_shared(mut src: Bytes) -> Result<Self, InvalidUriBytes> {
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 => {},

_ => 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,
})
pub fn from_shared(src: Bytes) -> Result<Self, InvalidUriBytes> {
TryFrom::try_from(src)
}

/// Convert a `PathAndQuery` from a static string.
@@ -276,6 +208,101 @@ impl PathAndQuery {
}
}

impl TryFrom<Bytes> for PathAndQuery {
type Error = InvalidUriBytes;

/// Attempt to convert a `PathAndQuery` from `Bytes`.
///
/// # Examples
///
/// ```
/// # extern crate http;
/// # use http::uri::*;
/// extern crate bytes;
///
/// use bytes::Bytes;
///
/// # pub fn main() {
/// let bytes = Bytes::from("/hello?world");
/// let path_and_query = PathAndQuery::from_shared(bytes).unwrap();
///
/// assert_eq!(path_and_query.path(), "/hello");
/// assert_eq!(path_and_query.query(), Some("world"));
/// # }
/// ```
fn try_from(mut src: Bytes) -> Result<Self, Self::Error> {
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 => {},

_ => 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,
})
}
}

impl HttpTryFrom<Bytes> for PathAndQuery {
type Error = InvalidUriBytes;
#[inline]
52 changes: 39 additions & 13 deletions src/uri/scheme.rs
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@ use std::ascii::AsciiExt;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::str::FromStr;
use std::convert::TryFrom;

use bytes::Bytes;

@@ -44,8 +45,7 @@ impl Scheme {

/// Attempt to convert a `Scheme` from `Bytes`
///
/// This function will be replaced by a `TryFrom` implementation once the
/// trait lands in stable.
/// This function has been replaced by a `TryFrom`
///
/// # Examples
///
@@ -64,17 +64,7 @@ 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()),
Relative => Ok(Relative.into()),
Standard(p) => Ok(Standard(p).into()),
Other(_) => {
let b = unsafe { ByteStr::from_utf8_unchecked(s) };
Ok(Other(Box::new(b)).into())
}
}
TryFrom::try_from(s)
}

pub(super) fn empty() -> Self {
@@ -113,6 +103,42 @@ impl Scheme {
}
}

impl TryFrom<Bytes> for Scheme {
type Error = InvalidUriBytes;

/// Attempt to convert a `Scheme` from `Bytes`
///
/// # Examples
///
/// ```
/// # extern crate http;
/// # use http::uri::*;
/// extern crate bytes;
///
/// use bytes::Bytes;
///
/// # pub fn main() {
/// let bytes = Bytes::from("http");
/// let scheme = Scheme::from_shared(bytes).unwrap();
///
/// assert_eq!(scheme.as_str(), "http");
/// # }
/// ```
fn try_from(s: Bytes) -> Result<Self, Self::Error> {
use self::Scheme2::*;

match Scheme2::parse_exact(&s[..]).map_err(InvalidUriBytes)? {
None => Err(ErrorKind::InvalidScheme.into()),
Relative => Ok(Relative.into()),
Standard(p) => Ok(Standard(p).into()),
Other(_) => {
let b = unsafe { ByteStr::from_utf8_unchecked(s) };
Ok(Other(Box::new(b)).into())
}
}
}
}

impl HttpTryFrom<Bytes> for Scheme {
type Error = InvalidUriBytes;
#[inline]