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