diff options
author | Paul Faria <pauldfaria@gmail.com> | 2015-05-17 16:20:30 -0400 |
---|---|---|
committer | Paul Faria <pauldfaria@gmail.com> | 2015-05-19 23:54:29 -0400 |
commit | 31b709a7c23c0fc7096c72d6024169c1f166f82d (patch) | |
tree | 9ffb08af66e9c10b9c2880cd4147b200fd28ea1b /components/script/dom/websocket.rs | |
parent | c51e9f04559f04f1e820b792261e1653c6869ee5 (diff) | |
download | servo-31b709a7c23c0fc7096c72d6024169c1f166f82d.tar.gz servo-31b709a7c23c0fc7096c72d6024169c1f166f82d.zip |
Initial work on #6061.
Diffstat (limited to 'components/script/dom/websocket.rs')
-rw-r--r-- | components/script/dom/websocket.rs | 135 |
1 files changed, 127 insertions, 8 deletions
diff --git a/components/script/dom/websocket.rs b/components/script/dom/websocket.rs index 75c868b50db..b4e00457714 100644 --- a/components/script/dom/websocket.rs +++ b/components/script/dom/websocket.rs @@ -9,8 +9,7 @@ use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull; use dom::bindings::codegen::InheritTypes::EventTargetCast; use dom::bindings::codegen::InheritTypes::EventCast; use dom::bindings::error::{Error, Fallible}; -use dom::bindings::error::Error::InvalidAccess; -use dom::bindings::error::Error::Syntax; +use dom::bindings::error::Error::{InvalidAccess, Syntax}; use dom::bindings::global::{GlobalField, GlobalRef}; use dom::bindings::js::{Temporary, JSRef, Rootable}; use dom::bindings::refcounted::Trusted; @@ -33,6 +32,7 @@ use websocket::stream::WebSocketStream; use websocket::client::request::Url; use websocket::Client; +use url::{SchemeData, SchemeType, UrlParser}; #[derive(PartialEq, Copy, Clone)] #[jstraceable] @@ -63,6 +63,112 @@ pub struct WebSocket { sendCloseFrame: Cell<bool> } +fn web_socket_scheme_types(scheme: &str) -> SchemeType { + match scheme { + "ws" => SchemeType::Relative(80), + "wss" => SchemeType::Relative(443), + _ => SchemeType::NonRelative, + } +} + +fn parse_web_socket_url(url_str: &str) -> Fallible<(Url, String, u16, String, bool)> { + // https://html.spec.whatwg.org/multipage/comms.html#parse-a-websocket-url's-components + // 1. No basepath specified, so it's absolute by default + // 2. UrlParser defaults to UTF-8 encoding + // 3. Specifying only ws and wss + let parsed_url = UrlParser::new() + .scheme_type_mapper(web_socket_scheme_types) + .parse(url_str); + + if parsed_url.is_err(){ + return Err(Error::Syntax); + } + + let parsed_url = parsed_url.unwrap(); + + // 3. Didn't match ws or wss + if let SchemeData::NonRelative(_) = parsed_url.scheme_data { + return Err(Error::Syntax); + } + + // 4. If the parsed url has a non-null fragment, fail + if parsed_url.fragment != None { + return Err(Error::Syntax); + } + + // 5. Set secure false if scheme is ws otherwise if scheme is wss set true + let secure = match parsed_url.scheme.as_ref() { + "ws" => false, + "wss" => true, + _ => unreachable!() + }; + + // 6. Set host to parsed url's host + let host = parsed_url.host().unwrap().serialize(); + + // 7. If the resulting parsed URL has a port component that is not the empty + // string, then let port be that component's value; otherwise, there is no + // explicit port. + let port = match parsed_url.port() { + Some(p) => p, + + // 8. If there is no explicit port, then: if secure is false, let port + // be 80, otherwise let port be 443. + None => if secure { + 443 + } else { + 80 + }, + }; + + // 9. Let resource name be the value of the resulting parsed URL's path + // component (which might be empty). + let base_resource = parsed_url.path().unwrap().connect("/"); + let mut resource = base_resource.as_ref(); + + // 10. If resource name is the empty string, set it to a single character + // U+002F SOLIDUS (/). + if resource == "" { + resource = "/"; + } + + let mut resource = resource.to_owned(); + + // 11. If the resulting parsed URL has a non-null query component, then + // append a single U+003F QUESTION MARK character (?) to resource name, + // followed by the value of the query component. + match parsed_url.query_pairs() { + Some(pairs) => { + let mut joined_pairs = pairs.iter() + .map(|pair| { + let mut keyValue = String::new(); + keyValue.push_str(pair.0.as_ref()); + keyValue.push('='); + keyValue.push_str(pair.1.as_ref()); + keyValue + }); + + let mut base_pair = String::new(); + base_pair.push_str(joined_pairs.next().unwrap().as_ref()); + + resource.push('?'); + + let query_string = joined_pairs.fold(base_pair, |mut current, next| { + current.push('&'); + current.push_str(next.as_ref()); + current + }); + + resource.push_str(query_string.as_ref()); + }, + None => (), + } + + // 12. Return host, port, resource name, and secure. + // FIXME remove parsed_url once it's no longer used in WebSocket::new + Ok((parsed_url, host, port, resource.to_string(), secure)) +} + impl WebSocket { pub fn new_inherited(global: GlobalRef, url: DOMString) -> WebSocket { WebSocket { @@ -83,11 +189,12 @@ impl WebSocket { } - pub fn new(global: GlobalRef, url: DOMString) -> Temporary<WebSocket> { + pub fn new(global: GlobalRef, url: DOMString) -> Fallible<Temporary<WebSocket>> { /*TODO: This constructor is only a prototype, it does not accomplish the specs defined here: http://html.spec.whatwg.org - All 9 items must be satisfied. + Item 1 is already satisfied. + The remaining 8 items must be satisfied. TODO: This constructor should be responsible for spawning a thread for the receive loop after ws_root.r().Open() - See comment */ @@ -95,7 +202,18 @@ impl WebSocket { global, WebSocketBinding::Wrap).root(); let ws_root = ws_root.r(); - let parsed_url = Url::parse(&ws_root.url).unwrap(); + + let parse_url_result = parse_web_socket_url(&ws_root.url); + if let Err(e) = parse_url_result { + return Err(e); + } + + // FIXME extract the right variables once Client::connect implementation is + // fixed to follow the RFC 6455 properly + let Ok((parsed_url, _, _, _, _)) = parse_url_result; + + // TODO Client::connect does not conform to RFC 6455 + // see https://github.com/cyderize/rust-websocket/issues/38 let request = Client::connect(parsed_url).unwrap(); let response = request.send().unwrap(); response.validate().unwrap(); @@ -106,8 +224,9 @@ impl WebSocket { let failed = ws_root.failed.get(); if failed && (ready_state == WebSocketRequestState::Closed || ready_state == WebSocketRequestState::Closing) { //Do nothing else. Let the close finish. - return Temporary::from_rooted(ws_root); + return Ok(Temporary::from_rooted(ws_root)); } + let (temp_sender, temp_receiver) = response.begin().split(); let mut other_sender = ws_root.sender.borrow_mut(); let mut other_receiver = ws_root.receiver.borrow_mut(); @@ -132,11 +251,11 @@ impl WebSocket { it confirms the websocket is now closed. This requires the close event to be fired (dispatch_close fires the close event - see implementation below) */ - Temporary::from_rooted(ws_root) + Ok(Temporary::from_rooted(ws_root)) } pub fn Constructor(global: GlobalRef, url: DOMString) -> Fallible<Temporary<WebSocket>> { - Ok(WebSocket::new(global, url)) + WebSocket::new(global, url) } } |