// Copyright 2015-2017 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // Parity is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with Parity. If not, see . //! HTTP/HTTPS URL type. Based on URL type from Iron library. use url_lib::{self}; pub use url_lib::Host; /// HTTP/HTTPS URL type for Iron. #[derive(PartialEq, Eq, Clone, Debug)] pub struct Url { /// Raw url of url pub raw: url_lib::Url, /// The host field of the URL, probably a domain. pub host: Host, /// The connection port. pub port: u16, /// The URL path, the resource to be accessed. /// /// A *non-empty* vector encoding the parts of the URL path. /// Empty entries of `""` correspond to trailing slashes. pub path: Vec, /// The URL query. pub query: Option, /// The URL username field, from the userinfo section of the URL. /// /// `None` if the `@` character was not part of the input OR /// if a blank username was provided. /// Otherwise, a non-empty string. pub username: Option, /// The URL password field, from the userinfo section of the URL. /// /// `None` if the `@` character was not part of the input OR /// if a blank password was provided. /// Otherwise, a non-empty string. pub password: Option, } impl Url { /// Create a URL from a string. /// /// The input must be a valid URL with a special scheme for this to succeed. /// /// HTTP and HTTPS are special schemes. /// /// See: http://url.spec.whatwg.org/#special-scheme pub fn parse(input: &str) -> Result { // Parse the string using rust-url, then convert. match url_lib::Url::parse(input) { Ok(raw_url) => Url::from_generic_url(raw_url), Err(e) => Err(format!("{}", e)) } } /// Create a `Url` from a `rust-url` `Url`. pub fn from_generic_url(raw_url: url_lib::Url) -> Result { // Map empty usernames to None. let username = match raw_url.username() { "" => None, username => Some(username.to_owned()) }; // Map empty passwords to None. let password = match raw_url.password() { Some(password) if !password.is_empty() => Some(password.to_owned()), _ => None, }; let port = raw_url.port_or_known_default().ok_or_else(|| format!("Unknown port for scheme: `{}`", raw_url.scheme()))?; let host = raw_url.host().ok_or_else(|| "Valid host, because only data:, mailto: protocols does not have host.".to_owned())?.to_owned(); let path = raw_url.path_segments().ok_or_else(|| "Valid path segments. In HTTP we won't get cannot-be-a-base URLs".to_owned())? .map(|part| part.to_owned()).collect(); let query = raw_url.query().map(|x| x.to_owned()); Ok(Url { port: port, host: host, path: path, query: query, raw: raw_url, username: username, password: password, }) } } #[cfg(test)] mod test { use super::Url; #[test] fn test_default_port() { assert_eq!(Url::parse("http://example.com/wow").unwrap().port, 80u16); assert_eq!(Url::parse("https://example.com/wow").unwrap().port, 443u16); } #[test] fn test_explicit_port() { assert_eq!(Url::parse("http://localhost:3097").unwrap().port, 3097u16); } #[test] fn test_empty_username() { assert!(Url::parse("http://@example.com").unwrap().username.is_none()); assert!(Url::parse("http://:password@example.com").unwrap().username.is_none()); } #[test] fn test_not_empty_username() { let user = Url::parse("http://john:pass@example.com").unwrap().username; assert_eq!(user.unwrap(), "john"); let user = Url::parse("http://john:@example.com").unwrap().username; assert_eq!(user.unwrap(), "john"); } #[test] fn test_empty_password() { assert!(Url::parse("http://michael@example.com").unwrap().password.is_none()); assert!(Url::parse("http://:@example.com").unwrap().password.is_none()); } #[test] fn test_not_empty_password() { let pass = Url::parse("http://michael:pass@example.com").unwrap().password; assert_eq!(pass.unwrap(), "pass"); let pass = Url::parse("http://:pass@example.com").unwrap().password; assert_eq!(pass.unwrap(), "pass"); } }