Merge pull request #1100 from hyperium/un-url
feat(lib): remove extern Url type usage
This commit is contained in:
		| @@ -28,7 +28,7 @@ fn get_one_at_a_time(b: &mut test::Bencher) { | ||||
|  | ||||
|     let client = hyper::Client::new(&handle); | ||||
|  | ||||
|     let url: hyper::Url = format!("http://{}/get", addr).parse().unwrap(); | ||||
|     let url: hyper::Uri = format!("http://{}/get", addr).parse().unwrap(); | ||||
|  | ||||
|     b.bytes = 160 * 2 + PHRASE.len() as u64; | ||||
|     b.iter(move || { | ||||
| @@ -51,7 +51,7 @@ fn post_one_at_a_time(b: &mut test::Bencher) { | ||||
|  | ||||
|     let client = hyper::Client::new(&handle); | ||||
|  | ||||
|     let url: hyper::Url = format!("http://{}/get", addr).parse().unwrap(); | ||||
|     let url: hyper::Uri = format!("http://{}/get", addr).parse().unwrap(); | ||||
|  | ||||
|     let post = "foo bar baz quux"; | ||||
|     b.bytes = 180 * 2 + post.len() as u64 + PHRASE.len() as u64; | ||||
|   | ||||
| @@ -24,8 +24,8 @@ fn main() { | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     let url = hyper::Url::parse(&url).unwrap(); | ||||
|     if url.scheme() != "http" { | ||||
|     let url = url.parse::<hyper::Uri>().unwrap(); | ||||
|     if url.scheme() != Some("http") { | ||||
|         println!("This example only works with 'http' URLs."); | ||||
|         return; | ||||
|     } | ||||
|   | ||||
| @@ -7,7 +7,7 @@ use tokio_io::{AsyncRead, AsyncWrite}; | ||||
| use tokio::reactor::Handle; | ||||
| use tokio::net::{TcpStream, TcpStreamNew}; | ||||
| use tokio_service::Service; | ||||
| use Url; | ||||
| use Uri; | ||||
|  | ||||
| use super::dns; | ||||
|  | ||||
| @@ -15,25 +15,25 @@ use super::dns; | ||||
| /// | ||||
| /// This trait is not implemented directly, and only exists to make | ||||
| /// the intent clearer. A connector should implement `Service` with | ||||
| /// `Request=Url` and `Response: Io` instead. | ||||
| pub trait Connect: Service<Request=Url, Error=io::Error> + 'static { | ||||
| /// `Request=Uri` and `Response: Io` instead. | ||||
| pub trait Connect: Service<Request=Uri, Error=io::Error> + 'static { | ||||
|     /// The connected Io Stream. | ||||
|     type Output: AsyncRead + AsyncWrite + 'static; | ||||
|     /// A Future that will resolve to the connected Stream. | ||||
|     type Future: Future<Item=Self::Output, Error=io::Error> + 'static; | ||||
|     /// Connect to a remote address. | ||||
|     fn connect(&self, Url) -> <Self as Connect>::Future; | ||||
|     fn connect(&self, Uri) -> <Self as Connect>::Future; | ||||
| } | ||||
|  | ||||
| impl<T> Connect for T | ||||
| where T: Service<Request=Url, Error=io::Error> + 'static, | ||||
| where T: Service<Request=Uri, Error=io::Error> + 'static, | ||||
|       T::Response: AsyncRead + AsyncWrite, | ||||
|       T::Future: Future<Error=io::Error>, | ||||
| { | ||||
|     type Output = T::Response; | ||||
|     type Future = T::Future; | ||||
|  | ||||
|     fn connect(&self, url: Url) -> <Self as Connect>::Future { | ||||
|     fn connect(&self, url: Uri) -> <Self as Connect>::Future { | ||||
|         self.call(url) | ||||
|     } | ||||
| } | ||||
| @@ -66,21 +66,21 @@ impl fmt::Debug for HttpConnector { | ||||
| } | ||||
|  | ||||
| impl Service for HttpConnector { | ||||
|     type Request = Url; | ||||
|     type Request = Uri; | ||||
|     type Response = TcpStream; | ||||
|     type Error = io::Error; | ||||
|     type Future = HttpConnecting; | ||||
|  | ||||
|     fn call(&self, url: Url) -> Self::Future { | ||||
|     fn call(&self, url: Uri) -> Self::Future { | ||||
|         debug!("Http::connect({:?})", url); | ||||
|         let host = match url.host_str() { | ||||
|         let host = match url.host() { | ||||
|             Some(s) => s, | ||||
|             None => return HttpConnecting { | ||||
|                 state: State::Error(Some(io::Error::new(io::ErrorKind::InvalidInput, "invalid url"))), | ||||
|                 handle: self.handle.clone(), | ||||
|             }, | ||||
|         }; | ||||
|         let port = url.port_or_known_default().unwrap_or(80); | ||||
|         let port = url.port().unwrap_or(80); | ||||
|  | ||||
|         HttpConnecting { | ||||
|             state: State::Resolving(self.dns.resolve(host.into(), port)), | ||||
| @@ -185,13 +185,12 @@ impl<S: SslClient> HttpsConnector<S> { | ||||
| mod tests { | ||||
|     use std::io; | ||||
|     use tokio::reactor::Core; | ||||
|     use Url; | ||||
|     use super::{Connect, HttpConnector}; | ||||
|  | ||||
|     #[test] | ||||
|     fn test_non_http_url() { | ||||
|         let mut core = Core::new().unwrap(); | ||||
|         let url = Url::parse("file:///home/sean/foo.txt").unwrap(); | ||||
|         let url = "/foo/bar?baz".parse().unwrap(); | ||||
|         let connector = HttpConnector::new(1, &core.handle()); | ||||
|  | ||||
|         assert_eq!(core.run(connector.connect(url)).unwrap_err().kind(), io::ErrorKind::InvalidInput); | ||||
|   | ||||
| @@ -10,7 +10,7 @@ use std::marker::PhantomData; | ||||
| use std::rc::Rc; | ||||
| use std::time::Duration; | ||||
|  | ||||
| use futures::{Poll, Async, Future, Stream}; | ||||
| use futures::{future, Poll, Async, Future, Stream}; | ||||
| use futures::unsync::oneshot; | ||||
| use tokio_io::{AsyncRead, AsyncWrite}; | ||||
| use tokio::reactor::Handle; | ||||
| @@ -24,7 +24,7 @@ use header::{Headers, Host}; | ||||
| use http::{self, TokioBody}; | ||||
| use method::Method; | ||||
| use self::pool::{Pool, Pooled}; | ||||
| use Url; | ||||
| use uri::{self, Uri}; | ||||
|  | ||||
| pub use self::connect::{HttpConnector, Connect}; | ||||
| pub use self::request::Request; | ||||
| @@ -95,7 +95,7 @@ where C: Connect, | ||||
| { | ||||
|     /// Send a GET Request using this Client. | ||||
|     #[inline] | ||||
|     pub fn get(&self, url: Url) -> FutureResponse { | ||||
|     pub fn get(&self, url: Uri) -> FutureResponse { | ||||
|         self.request(Request::new(Method::Get, url)) | ||||
|     } | ||||
|  | ||||
| @@ -135,18 +135,30 @@ where C: Connect, | ||||
|     type Future = FutureResponse; | ||||
|  | ||||
|     fn call(&self, req: Self::Request) -> Self::Future { | ||||
|         let url = req.url().clone(); | ||||
|         let url = req.uri().clone(); | ||||
|         let domain = match uri::scheme_and_authority(&url) { | ||||
|             Some(uri) => uri, | ||||
|             None => { | ||||
|                 return FutureResponse(Box::new(future::err(::Error::Io( | ||||
|                     io::Error::new( | ||||
|                         io::ErrorKind::InvalidInput, | ||||
|                         "invalid URI for Client Request" | ||||
|                     ) | ||||
|                 )))); | ||||
|             } | ||||
|         }; | ||||
|         let host = Host::new(domain.host().expect("authority implies host").to_owned(), domain.port()); | ||||
|         let (mut head, body) = request::split(req); | ||||
|         let mut headers = Headers::new(); | ||||
|         headers.set(Host::new(url.host_str().unwrap().to_owned(), url.port())); | ||||
|         headers.set(host); | ||||
|         headers.extend(head.headers.iter()); | ||||
|         head.headers = headers; | ||||
|  | ||||
|         let checkout = self.pool.checkout(&url[..::url::Position::BeforePath]); | ||||
|         let checkout = self.pool.checkout(domain.as_ref()); | ||||
|         let connect = { | ||||
|             let handle = self.handle.clone(); | ||||
|             let pool = self.pool.clone(); | ||||
|             let pool_key = Rc::new(url[..::url::Position::BeforePath].to_owned()); | ||||
|             let pool_key = Rc::new(domain.to_string()); | ||||
|             self.connector.connect(url) | ||||
|                 .map(move |io| { | ||||
|                     let (tx, rx) = oneshot::channel(); | ||||
|   | ||||
| @@ -1,18 +1,15 @@ | ||||
| use std::fmt; | ||||
|  | ||||
| use Url; | ||||
|  | ||||
| use header::Headers; | ||||
| use http::{Body, RequestHead}; | ||||
| use method::Method; | ||||
| use uri::Uri; | ||||
| use uri::{self, Uri}; | ||||
| use version::HttpVersion; | ||||
| use std::str::FromStr; | ||||
|  | ||||
| /// A client request to a remote server. | ||||
| pub struct Request<B = Body> { | ||||
|     method: Method, | ||||
|     url: Url, | ||||
|     uri: Uri, | ||||
|     version: HttpVersion, | ||||
|     headers: Headers, | ||||
|     body: Option<B>, | ||||
| @@ -22,10 +19,10 @@ pub struct Request<B = Body> { | ||||
| impl<B> Request<B> { | ||||
|     /// Construct a new Request. | ||||
|     #[inline] | ||||
|     pub fn new(method: Method, url: Url) -> Request<B> { | ||||
|     pub fn new(method: Method, uri: Uri) -> Request<B> { | ||||
|         Request { | ||||
|             method: method, | ||||
|             url: url, | ||||
|             uri: uri, | ||||
|             version: HttpVersion::default(), | ||||
|             headers: Headers::new(), | ||||
|             body: None, | ||||
| @@ -33,9 +30,9 @@ impl<B> Request<B> { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// Read the Request Url. | ||||
|     /// Read the Request Uri. | ||||
|     #[inline] | ||||
|     pub fn url(&self) -> &Url { &self.url } | ||||
|     pub fn uri(&self) -> &Uri { &self.uri } | ||||
|  | ||||
|     /// Read the Request Version. | ||||
|     #[inline] | ||||
| @@ -57,9 +54,9 @@ impl<B> Request<B> { | ||||
|     #[inline] | ||||
|     pub fn headers_mut(&mut self) -> &mut Headers { &mut self.headers } | ||||
|  | ||||
|     /// Set the `Url` of this request. | ||||
|     /// Set the `Uri` of this request. | ||||
|     #[inline] | ||||
|     pub fn set_url(&mut self, url: Url) { self.url = url; } | ||||
|     pub fn set_uri(&mut self, uri: Uri) { self.uri = uri; } | ||||
|  | ||||
|     /// Set the `HttpVersion` of this request. | ||||
|     #[inline] | ||||
| @@ -81,7 +78,7 @@ impl<B> fmt::Debug for Request<B> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.debug_struct("Request") | ||||
|             .field("method", &self.method) | ||||
|             .field("url", &self.url) | ||||
|             .field("uri", &self.uri) | ||||
|             .field("version", &self.version) | ||||
|             .field("headers", &self.headers) | ||||
|             .finish() | ||||
| @@ -90,9 +87,9 @@ impl<B> fmt::Debug for Request<B> { | ||||
|  | ||||
| pub fn split<B>(req: Request<B>) -> (RequestHead, Option<B>) { | ||||
|     let uri = if req.is_proxy { | ||||
|         Uri::from(req.url) | ||||
|         req.uri | ||||
|     } else { | ||||
|         Uri::from_str(&req.url[::url::Position::BeforePath..::url::Position::AfterQuery]).expect("url is not uri") | ||||
|         uri::origin_form(&req.uri) | ||||
|     }; | ||||
|     let head = RequestHead { | ||||
|         subject: ::http::RequestLine(req.method, uri), | ||||
|   | ||||
							
								
								
									
										31
									
								
								src/error.rs
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								src/error.rs
									
									
									
									
									
								
							| @@ -6,7 +6,8 @@ use std::str::Utf8Error; | ||||
| use std::string::FromUtf8Error; | ||||
|  | ||||
| use httparse; | ||||
| use url; | ||||
|  | ||||
| pub use uri::UriError; | ||||
|  | ||||
| use self::Error::{ | ||||
|     Method, | ||||
| @@ -21,8 +22,6 @@ use self::Error::{ | ||||
|     Utf8 | ||||
| }; | ||||
|  | ||||
| pub use url::ParseError; | ||||
|  | ||||
| /// Result type often returned from methods that can have hyper `Error`s. | ||||
| pub type Result<T> = ::std::result::Result<T, Error>; | ||||
|  | ||||
| @@ -32,7 +31,7 @@ pub enum Error { | ||||
|     /// An invalid `Method`, such as `GE,T`. | ||||
|     Method, | ||||
|     /// An invalid `Uri`, such as `exam ple.domain`. | ||||
|     Uri(url::ParseError), | ||||
|     Uri(UriError), | ||||
|     /// An invalid `HttpVersion`, such as `HTP/1.1` | ||||
|     Version, | ||||
|     /// An invalid `Header`. | ||||
| @@ -102,15 +101,15 @@ impl StdError for Error { | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<IoError> for Error { | ||||
|     fn from(err: IoError) -> Error { | ||||
|         Io(err) | ||||
| impl From<UriError> for Error { | ||||
|     fn from(err: UriError) -> Error { | ||||
|         Uri(err) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<url::ParseError> for Error { | ||||
|     fn from(err: url::ParseError) -> Error { | ||||
|         Uri(err) | ||||
| impl From<IoError> for Error { | ||||
|     fn from(err: IoError) -> Error { | ||||
|         Io(err) | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -145,7 +144,6 @@ mod tests { | ||||
|     use std::error::Error as StdError; | ||||
|     use std::io; | ||||
|     use httparse; | ||||
|     use url; | ||||
|     use super::Error; | ||||
|     use super::Error::*; | ||||
|  | ||||
| @@ -185,7 +183,6 @@ mod tests { | ||||
|     fn test_from() { | ||||
|  | ||||
|         from_and_cause!(io::Error::new(io::ErrorKind::Other, "other") => Io(..)); | ||||
|         from_and_cause!(url::ParseError::EmptyHost => Uri(..)); | ||||
|  | ||||
|         from!(httparse::Error::HeaderName => Header); | ||||
|         from!(httparse::Error::HeaderName => Header); | ||||
| @@ -196,14 +193,4 @@ mod tests { | ||||
|         from!(httparse::Error::TooManyHeaders => TooLarge); | ||||
|         from!(httparse::Error::Version => Version); | ||||
|     } | ||||
|  | ||||
|     #[cfg(feature = "openssl")] | ||||
|     #[test] | ||||
|     fn test_from_ssl() { | ||||
|         use openssl::ssl::error::SslError; | ||||
|  | ||||
|         from!(SslError::StreamError( | ||||
|             io::Error::new(io::ErrorKind::Other, "ssl negotiation")) => Io(..)); | ||||
|         from_and_cause!(SslError::SslSessionClosed => Ssl(..)); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -9,10 +9,9 @@ | ||||
| use language_tags::LanguageTag; | ||||
| use std::fmt; | ||||
| use unicase::UniCase; | ||||
| use url::percent_encoding; | ||||
|  | ||||
| use header::{Header, Raw, parsing}; | ||||
| use header::parsing::{parse_extended_value, HTTP_VALUE}; | ||||
| use header::parsing::{parse_extended_value, http_percent_encode}; | ||||
| use header::shared::Charset; | ||||
|  | ||||
| /// The implied disposition of the content of the HTTP body | ||||
| @@ -182,8 +181,7 @@ impl fmt::Display for ContentDisposition { | ||||
|                             try!(write!(f, "{}", lang)); | ||||
|                         }; | ||||
|                         try!(write!(f, "'")); | ||||
|                         try!(f.write_str( | ||||
|                             &percent_encoding::percent_encode(bytes, HTTP_VALUE).to_string())) | ||||
|                         try!(http_percent_encode(f, bytes)) | ||||
|                     } | ||||
|                 }, | ||||
|                 DispositionParam::Ext(ref k, ref v) => try!(write!(f, "; {}=\"{}\"", k, v)), | ||||
|   | ||||
| @@ -9,6 +9,7 @@ use url::percent_encoding; | ||||
| use header::Raw; | ||||
| use header::shared::Charset; | ||||
|  | ||||
|  | ||||
| /// Reads a single raw string when parsing a header. | ||||
| pub fn from_one_raw_str<T: str::FromStr>(raw: &Raw) -> ::Result<T> { | ||||
|     if let Some(line) = raw.one() { | ||||
| @@ -132,6 +133,30 @@ pub fn parse_extended_value(val: &str) -> ::Result<ExtendedValue> { | ||||
|     }) | ||||
| } | ||||
|  | ||||
|  | ||||
| impl Display for ExtendedValue { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         let encoded_value = | ||||
|             percent_encoding::percent_encode(&self.value[..], self::percent_encoding_http::HTTP_VALUE); | ||||
|         if let Some(ref lang) = self.language_tag { | ||||
|             write!(f, "{}'{}'{}", self.charset, lang, encoded_value) | ||||
|         } else { | ||||
|             write!(f, "{}''{}", self.charset, encoded_value) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// Percent encode a sequence of bytes with a character set defined in | ||||
| /// https://tools.ietf.org/html/rfc5987#section-3.2 | ||||
| pub fn http_percent_encode(f: &mut fmt::Formatter, bytes: &[u8]) -> fmt::Result { | ||||
|     let encoded = percent_encoding::percent_encode(bytes, self::percent_encoding_http::HTTP_VALUE); | ||||
|     fmt::Display::fmt(&encoded, f) | ||||
| } | ||||
|  | ||||
| mod percent_encoding_http { | ||||
|     use std::fmt; | ||||
|     use url::percent_encoding; | ||||
|  | ||||
|     define_encode_set! { | ||||
|         /// This encode set is used for HTTP header values and is defined at | ||||
|         /// https://tools.ietf.org/html/rfc5987#section-3.2 | ||||
| @@ -146,17 +171,6 @@ impl fmt::Debug for HTTP_VALUE { | ||||
|             f.pad("HTTP_VALUE") | ||||
|         } | ||||
|     } | ||||
|  | ||||
| impl Display for ExtendedValue { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         let encoded_value = | ||||
|             percent_encoding::percent_encode(&self.value[..], HTTP_VALUE); | ||||
|         if let Some(ref lang) = self.language_tag { | ||||
|             write!(f, "{}'{}'{}", self.charset, lang, encoded_value) | ||||
|         } else { | ||||
|             write!(f, "{}''{}", self.charset, encoded_value) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(test)] | ||||
|   | ||||
| @@ -61,7 +61,7 @@ impl Http1Transaction for ServerTransaction { | ||||
|         let path = unsafe { ByteStr::from_utf8_unchecked(path) }; | ||||
|         let subject = RequestLine( | ||||
|             method, | ||||
|             try!(::uri::from_mem_str(path)), | ||||
|             try!(::uri::from_byte_str(path)), | ||||
|         ); | ||||
|  | ||||
|         headers.extend(HeadersAsBytesIter { | ||||
|   | ||||
| @@ -1,8 +1,9 @@ | ||||
| use std::ops::Deref; | ||||
| use std::str; | ||||
|  | ||||
| use bytes::Bytes; | ||||
|  | ||||
| #[derive(Debug, Clone, PartialEq, Eq)] | ||||
| #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||||
| pub struct ByteStr(Bytes); | ||||
|  | ||||
| impl ByteStr { | ||||
| @@ -10,7 +11,35 @@ impl ByteStr { | ||||
|         ByteStr(slice) | ||||
|     } | ||||
|  | ||||
|     pub fn from_static(s: &'static str) -> ByteStr { | ||||
|         ByteStr(Bytes::from_static(s.as_bytes())) | ||||
|     } | ||||
|  | ||||
|     pub fn slice(&self, from: usize, to: usize) -> ByteStr { | ||||
|         assert!(self.as_str().is_char_boundary(from)); | ||||
|         assert!(self.as_str().is_char_boundary(to)); | ||||
|         ByteStr(self.0.slice(from, to)) | ||||
|     } | ||||
|  | ||||
|     pub fn slice_to(&self, idx: usize) -> ByteStr { | ||||
|         assert!(self.as_str().is_char_boundary(idx)); | ||||
|         ByteStr(self.0.slice_to(idx)) | ||||
|     } | ||||
|  | ||||
|     pub fn as_str(&self) -> &str { | ||||
|         unsafe { str::from_utf8_unchecked(self.0.as_ref()) } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Deref for ByteStr { | ||||
|     type Target = str; | ||||
|     fn deref(&self) -> &str { | ||||
|         self.as_str() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a> From<&'a str> for ByteStr { | ||||
|     fn from(s: &'a str) -> ByteStr { | ||||
|         ByteStr(Bytes::from(s)) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| #![doc(html_root_url = "https://hyperium.github.io/hyper/")] | ||||
| #![deny(missing_docs)] | ||||
| //#![deny(warnings)] | ||||
| #![deny(warnings)] | ||||
| #![deny(missing_debug_implementations)] | ||||
| #![cfg_attr(all(test, feature = "nightly"), feature(test))] | ||||
|  | ||||
| @@ -35,7 +35,6 @@ extern crate test; | ||||
|  | ||||
|  | ||||
| pub use uri::Uri; | ||||
| pub use url::Url; | ||||
| pub use client::Client; | ||||
| pub use error::{Result, Error}; | ||||
| pub use header::Headers; | ||||
|   | ||||
							
								
								
									
										146
									
								
								src/uri.rs
									
									
									
									
									
								
							
							
						
						
									
										146
									
								
								src/uri.rs
									
									
									
									
									
								
							| @@ -1,13 +1,8 @@ | ||||
| use std::borrow::Cow; | ||||
| use std::error::Error as StdError; | ||||
| use std::fmt::{Display, self}; | ||||
| use std::ops::Deref; | ||||
| use std::str::{self, FromStr}; | ||||
|  | ||||
| use http::ByteStr; | ||||
| use Url; | ||||
| use url::ParseError as UrlError; | ||||
|  | ||||
| use Error; | ||||
|  | ||||
| /// The Request-URI of a Request's StartLine. | ||||
| /// | ||||
| @@ -32,9 +27,9 @@ use Error; | ||||
| ///  |                  |                       |               |              | | ||||
| /// scheme          authority                 path            query         fragment | ||||
| /// ``` | ||||
| #[derive(Clone)] | ||||
| #[derive(Clone, Hash)] | ||||
| pub struct Uri { | ||||
|     source: InternalUri, | ||||
|     source: ByteStr, | ||||
|     scheme_end: Option<usize>, | ||||
|     authority_end: Option<usize>, | ||||
|     query: Option<usize>, | ||||
| @@ -43,20 +38,23 @@ pub struct Uri { | ||||
|  | ||||
| impl Uri { | ||||
|     /// Parse a string into a `Uri`. | ||||
|     fn new(s: InternalUri) -> Result<Uri, Error> { | ||||
|     fn new(s: ByteStr) -> Result<Uri, UriError> { | ||||
|         if s.len() == 0 { | ||||
|             Err(Error::Uri(UrlError::RelativeUrlWithoutBase)) | ||||
|             Err(UriError(ErrorKind::Empty)) | ||||
|         } else if s.as_bytes() == b"*" { | ||||
|             // asterisk-form | ||||
|             Ok(Uri { | ||||
|                 source: InternalUri::Cow("*".into()), | ||||
|                 source: ByteStr::from_static("*"), | ||||
|                 scheme_end: None, | ||||
|                 authority_end: None, | ||||
|                 query: None, | ||||
|                 fragment: None, | ||||
|             }) | ||||
|         } else if s.as_bytes() == b"/" { | ||||
|             // shortcut for '/' | ||||
|             Ok(Uri::default()) | ||||
|         } else if s.as_bytes()[0] == b'/' { | ||||
|             // origin-form | ||||
|             let query = parse_query(&s); | ||||
|             let fragment = parse_fragment(&s); | ||||
|             Ok(Uri { | ||||
| @@ -67,22 +65,14 @@ impl Uri { | ||||
|                 fragment: fragment, | ||||
|             }) | ||||
|         } else if s.contains("://") { | ||||
|             // absolute-form | ||||
|             let scheme = parse_scheme(&s); | ||||
|             let auth = parse_authority(&s); | ||||
|             if let Some(end) = scheme { | ||||
|                 match &s[..end] { | ||||
|                     "ftp" | "gopher" | "http" | "https" | "ws" | "wss" => {}, | ||||
|                     "blob" | "file" => return Err(Error::Method), | ||||
|                     _ => return Err(Error::Method), | ||||
|                 } | ||||
|                 match auth { | ||||
|                     Some(a) => { | ||||
|                         if (end + 3) == a { | ||||
|                             return Err(Error::Method); | ||||
|                         } | ||||
|                     }, | ||||
|                     None => return Err(Error::Method), | ||||
|                 } | ||||
|             let scheme_end = scheme.expect("just checked for ':' above"); | ||||
|             let auth_end = auth.expect("just checked for ://"); | ||||
|             if scheme_end + 3 == auth_end { | ||||
|                 // authority was empty | ||||
|                 return Err(UriError(ErrorKind::MissingAuthority)); | ||||
|             } | ||||
|             let query = parse_query(&s); | ||||
|             let fragment = parse_fragment(&s); | ||||
| @@ -94,8 +84,10 @@ impl Uri { | ||||
|                 fragment: fragment, | ||||
|             }) | ||||
|         } else if (s.contains("/") || s.contains("?")) && !s.contains("://") { | ||||
|             return Err(Error::Method) | ||||
|             // last possibility is authority-form, above are illegal characters | ||||
|             return Err(UriError(ErrorKind::Malformed)) | ||||
|         } else { | ||||
|             // authority-form | ||||
|             let len = s.len(); | ||||
|             Ok(Uri { | ||||
|                 source: s, | ||||
| @@ -220,25 +212,12 @@ fn parse_fragment(s: &str) -> Option<usize> { | ||||
| } | ||||
|  | ||||
| impl FromStr for Uri { | ||||
|     type Err = Error; | ||||
|     type Err = UriError; | ||||
|  | ||||
|     fn from_str(s: &str) -> Result<Uri, Error> { | ||||
|     fn from_str(s: &str) -> Result<Uri, UriError> { | ||||
|         //TODO: refactor such that the to_owned() is only required at the end | ||||
|         //of successful parsing, so an Err doesn't needlessly clone the string. | ||||
|         Uri::new(InternalUri::Cow(Cow::Owned(s.to_owned()))) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<Url> for Uri { | ||||
|     fn from(url: Url) -> Uri { | ||||
|         let s = InternalUri::Cow(Cow::Owned(url.into_string())); | ||||
|         // TODO: we could build the indices with some simple subtraction, | ||||
|         // instead of re-parsing an already parsed string. | ||||
|         // For example: | ||||
|         // let scheme_end = url[..url::Position::AfterScheme].as_ptr() as usize | ||||
|         //   - url.as_str().as_ptr() as usize; | ||||
|         // etc | ||||
|         Uri::new(s).expect("Uri::From<Url> failed") | ||||
|         Uri::new(ByteStr::from(s)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -259,7 +238,7 @@ impl AsRef<str> for Uri { | ||||
| impl Default for Uri { | ||||
|     fn default() -> Uri { | ||||
|         Uri { | ||||
|             source: InternalUri::Cow("/".into()), | ||||
|             source: ByteStr::from_static("/"), | ||||
|             scheme_end: None, | ||||
|             authority_end: None, | ||||
|             query: None, | ||||
| @@ -280,33 +259,66 @@ impl Display for Uri { | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub fn from_mem_str(s: ByteStr) -> Result<Uri, Error> { | ||||
|     Uri::new(InternalUri::Shared(s)) | ||||
| pub fn from_byte_str(s: ByteStr) -> Result<Uri, UriError> { | ||||
|     Uri::new(s) | ||||
| } | ||||
|  | ||||
| #[derive(Clone)] | ||||
| enum InternalUri { | ||||
|     Cow(Cow<'static, str>), | ||||
|     Shared(ByteStr), | ||||
| } | ||||
|  | ||||
| impl InternalUri { | ||||
|     fn as_str(&self) -> &str { | ||||
|         match *self { | ||||
|             InternalUri::Cow(ref s) => s.as_ref(), | ||||
|             InternalUri::Shared(ref m) => m.as_str(), | ||||
|         } | ||||
| pub fn scheme_and_authority(uri: &Uri) -> Option<Uri> { | ||||
|     if uri.scheme_end.is_some() { | ||||
|         Some(Uri { | ||||
|             source: uri.source.slice_to(uri.authority_end.expect("scheme without authority")), | ||||
|             scheme_end: uri.scheme_end, | ||||
|             authority_end: uri.authority_end, | ||||
|             query: None, | ||||
|             fragment: None, | ||||
|         }) | ||||
|     } else { | ||||
|         None | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Deref for InternalUri { | ||||
|     type Target = str; | ||||
|  | ||||
|     fn deref(&self) -> &str { | ||||
|         self.as_str() | ||||
| pub fn origin_form(uri: &Uri) -> Uri { | ||||
|     let start = uri.authority_end.unwrap_or(uri.scheme_end.unwrap_or(0)); | ||||
|     let end = if let Some(f) = uri.fragment { | ||||
|         uri.source.len() - f - 1 | ||||
|     } else { | ||||
|         uri.source.len() | ||||
|     }; | ||||
|     Uri { | ||||
|         source: uri.source.slice(start, end), | ||||
|         scheme_end: None, | ||||
|         authority_end: None, | ||||
|         query: uri.query, | ||||
|         fragment: None, | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// An error parsing a `Uri`. | ||||
| #[derive(Clone, Debug)] | ||||
| pub struct UriError(ErrorKind); | ||||
|  | ||||
| #[derive(Clone, Debug)] | ||||
| enum ErrorKind { | ||||
|     Empty, | ||||
|     Malformed, | ||||
|     MissingAuthority, | ||||
| } | ||||
|  | ||||
| impl fmt::Display for UriError { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.pad(self.description()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl StdError for UriError { | ||||
|     fn description(&self) -> &str { | ||||
|         match self.0 { | ||||
|             ErrorKind::Empty => "empty Uri string", | ||||
|             ErrorKind::Malformed => "invalid character in Uri authority", | ||||
|             ErrorKind::MissingAuthority => "absolute Uri missing authority segment", | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| macro_rules! test_parse { | ||||
|     ( | ||||
| @@ -443,13 +455,3 @@ fn test_uri_parse_error() { | ||||
|     err("localhost/"); | ||||
|     err("localhost?key=val"); | ||||
| } | ||||
|  | ||||
| #[test] | ||||
| fn test_uri_from_url() { | ||||
|     let uri = Uri::from(Url::parse("http://test.com/nazghul?test=3").unwrap()); | ||||
|     assert_eq!(uri.path(), "/nazghul"); | ||||
|     assert_eq!(uri.authority(), Some("test.com")); | ||||
|     assert_eq!(uri.scheme(), Some("http")); | ||||
|     assert_eq!(uri.query(), Some("test=3")); | ||||
|     assert_eq!(uri.as_ref(), "http://test.com/nazghul?test=3"); | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user