use url::{Url, Position};
use url::Host as UrlHost;
use hyper::header::Host;
use result::{WebSocketResult, WSUrlErrorKind};
pub trait ToWebSocketUrlComponents {
fn to_components(&self) -> WebSocketResult<(Host, String, bool)>;
}
impl ToWebSocketUrlComponents for str {
fn to_components(&self) -> WebSocketResult<(Host, String, bool)> {
parse_url_str(&self)
}
}
impl ToWebSocketUrlComponents for Url {
fn to_components(&self) -> WebSocketResult<(Host, String, bool)> {
parse_url(&self)
}
}
impl ToWebSocketUrlComponents for (Host, String, bool) {
fn to_components(&self) -> WebSocketResult<(Host, String, bool)> {
let (mut host, mut resource_name, secure) = self.clone();
host.port = Some(match host.port {
Some(port) => port,
None => if secure { 443 } else { 80 },
});
if resource_name.is_empty() {
resource_name = "/".to_owned();
}
Ok((host, resource_name, secure))
}
}
impl<'a> ToWebSocketUrlComponents for (Host, &'a str, bool) {
fn to_components(&self) -> WebSocketResult<(Host, String, bool)> {
(self.0.clone(), self.1.to_owned(), self.2).to_components()
}
}
impl<'a> ToWebSocketUrlComponents for (Host, &'a str) {
fn to_components(&self) -> WebSocketResult<(Host, String, bool)> {
(self.0.clone(), self.1.to_owned(), false).to_components()
}
}
impl ToWebSocketUrlComponents for (Host, String) {
fn to_components(&self) -> WebSocketResult<(Host, String, bool)> {
(self.0.clone(), self.1.clone(), false).to_components()
}
}
impl ToWebSocketUrlComponents for (UrlHost, u16, String, bool) {
fn to_components(&self) -> WebSocketResult<(Host, String, bool)> {
(Host {
hostname: self.0.to_string(),
port: Some(self.1)
}, self.2.clone(), self.3).to_components()
}
}
impl<'a> ToWebSocketUrlComponents for (UrlHost, u16, &'a str, bool) {
fn to_components(&self) -> WebSocketResult<(Host, String, bool)> {
(Host {
hostname: self.0.to_string(),
port: Some(self.1)
}, self.2, self.3).to_components()
}
}
impl<'a, T: ToWebSocketUrlComponents> ToWebSocketUrlComponents for &'a T {
fn to_components(&self) -> WebSocketResult<(Host, String, bool)> {
(**self).to_components()
}
}
pub fn parse_url_str(url_str: &str) -> WebSocketResult<(Host, String, bool)> {
let parsed_url = try!(Url::parse(url_str));
parse_url(&parsed_url)
}
pub fn parse_url(url: &Url) -> WebSocketResult<(Host, String, bool)> {
if url.fragment().is_some() {
return Err(From::from(WSUrlErrorKind::CannotSetFragment));
}
let secure = match url.scheme() {
"ws" => false,
"wss" => true,
_ => return Err(From::from(WSUrlErrorKind::InvalidScheme)),
};
let host = url.host_str().unwrap().to_owned(); let port = url.port_or_known_default();
let resource = url[Position::BeforePath..Position::AfterQuery].to_owned();
Ok((Host { hostname: host, port: port }, resource, secure))
}
#[cfg(all(feature = "nightly", test))]
mod tests {
use super::*;
use url::Url;
use result::{WebSocketError, WSUrlErrorKind};
fn url_for_test() -> Url {
Url::parse("ws://www.example.com:8080/some/path?a=b&c=d").unwrap()
}
#[test]
fn test_parse_url_fragments_not_accepted() {
let url = &mut url_for_test();
url.set_fragment(Some("non_null_fragment"));
let result = parse_url(url);
match result {
Err(WebSocketError::WebSocketUrlError(
WSUrlErrorKind::CannotSetFragment)) => (),
Err(e) => panic!("Expected WSUrlErrorKind::CannotSetFragment but got {}", e),
Ok(_) => panic!("Expected WSUrlErrorKind::CannotSetFragment but got Ok")
}
}
#[test]
fn test_parse_url_invalid_schemes_return_error() {
let url = &mut url_for_test();
let invalid_schemes = &["http", "https", "gopher", "file", "ftp", "other"];
for scheme in invalid_schemes {
url.set_scheme(scheme).unwrap();
let result = parse_url(url);
match result {
Err(WebSocketError::WebSocketUrlError(
WSUrlErrorKind::InvalidScheme)) => (),
Err(e) => panic!("Expected WSUrlErrorKind::InvalidScheme but got {}", e),
Ok(_) => panic!("Expected WSUrlErrorKind::InvalidScheme but got Ok")
}
}
}
#[test]
fn test_parse_url_valid_schemes_return_ok() {
let url = &mut url_for_test();
let valid_schemes = &["ws", "wss"];
for scheme in valid_schemes {
url.set_scheme(scheme).unwrap();
let result = parse_url(url);
match result {
Ok(_) => (),
Err(e) => panic!("Expected Ok, but got {}", e)
}
}
}
#[test]
fn test_parse_url_ws_returns_unset_secure_flag() {
let url = &mut url_for_test();
url.set_scheme("ws").unwrap();
let result = parse_url(url);
let secure = match result {
Ok((_, _, secure)) => secure,
Err(e) => panic!(e),
};
assert!(!secure);
}
#[test]
fn test_parse_url_wss_returns_set_secure_flag() {
let url = &mut url_for_test();
url.set_scheme("wss").unwrap();
let result = parse_url(url);
let secure = match result {
Ok((_, _, secure)) => secure,
Err(e) => panic!(e),
};
assert!(secure);
}
#[test]
fn test_parse_url_generates_proper_output() {
let url = &url_for_test();
let result = parse_url(url);
let (host, resource) = match result {
Ok((host, resource, _)) => (host, resource),
Err(e) => panic!(e),
};
assert_eq!(host.hostname, "www.example.com".to_owned());
assert_eq!(resource, "/some/path?a=b&c=d".to_owned());
match host.port {
Some(port) => assert_eq!(port, 8080),
_ => panic!("Port should not be None"),
}
}
#[test]
fn test_parse_url_empty_path_should_give_slash() {
let url = &mut url_for_test();
url.set_path("/");
let result = parse_url(url);
let resource = match result {
Ok((_, resource, _)) => resource,
Err(e) => panic!(e),
};
assert_eq!(resource, "/?a=b&c=d".to_owned());
}
#[test]
fn test_parse_url_none_query_should_not_append_question_mark() {
let url = &mut url_for_test();
url.set_query(None);
let result = parse_url(url);
let resource = match result {
Ok((_, resource, _)) => resource,
Err(e) => panic!(e),
};
assert_eq!(resource, "/some/path".to_owned());
}
#[test]
fn test_parse_url_none_port_should_use_default_port() {
let url = &mut url_for_test();
url.set_port(None).unwrap();
let result = parse_url(url);
let host = match result {
Ok((host, _, _)) => host,
Err(e) => panic!(e),
};
match host.port {
Some(80) => (),
Some(p) => panic!("Expected port to be 80 but got {}", p),
None => panic!("Expected port to be 80 but got `None`"),
}
}
#[test]
fn test_parse_url_str_valid_url1() {
let url_str = "ws://www.example.com/some/path?a=b&c=d";
let result = parse_url_str(url_str);
let (host, resource, secure) = match result {
Ok((host, resource, secure)) => (host, resource, secure),
Err(e) => panic!(e),
};
match host.port {
Some(80) => (),
Some(p) => panic!("Expected port 80 but got {}", p),
None => panic!("Expected port 80 but got `None`")
}
assert_eq!(host.hostname, "www.example.com".to_owned());
assert_eq!(resource, "/some/path?a=b&c=d".to_owned());
assert!(!secure);
}
#[test]
fn test_parse_url_str_valid_url2() {
let url_str = "wss://www.example.com";
let result = parse_url_str(url_str);
let (host, resource, secure) = match result {
Ok((host, resource, secure)) => (host, resource, secure),
Err(e) => panic!(e)
};
match host.port {
Some(443) => (),
Some(p) => panic!("Expected port 443 but got {}", p),
None => panic!("Expected port 443 but got `None`")
}
assert_eq!(host.hostname, "www.example.com".to_owned());
assert_eq!(resource, "/".to_owned());
assert!(secure);
}
#[test]
fn test_parse_url_str_invalid_relative_url() {
let url_str = "/some/relative/path?a=b&c=d";
let result = parse_url_str(url_str);
match result {
Err(WebSocketError::UrlError(_)) => (),
Err(e) => panic!("Expected UrlError, but got unexpected error {}", e),
Ok(_) => panic!("Expected UrlError, but got Ok"),
}
}
#[test]
fn test_parse_url_str_invalid_url_scheme() {
let url_str = "http://www.example.com/some/path?a=b&c=d";
let result = parse_url_str(url_str);
match result {
Err(WebSocketError::WebSocketUrlError(WSUrlErrorKind::InvalidScheme)) => (),
Err(e) => panic!("Expected InvalidScheme, but got unexpected error {}", e),
Ok(_) => panic!("Expected InvalidScheme, but got Ok"),
}
}
#[test]
fn test_parse_url_str_invalid_url_fragment() {
let url_str = "http://www.example.com/some/path#some-id";
let result = parse_url_str(url_str);
match result {
Err(WebSocketError::WebSocketUrlError(WSUrlErrorKind::CannotSetFragment)) => (),
Err(e) => panic!("Expected CannotSetFragment, but got unexpected error {}", e),
Ok(_) => panic!("Expected CannotSetFragment, but got Ok"),
}
}
}