aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom/websocket.rs
diff options
context:
space:
mode:
authorPaul Faria <pauldfaria@gmail.com>2015-05-17 16:20:30 -0400
committerPaul Faria <pauldfaria@gmail.com>2015-05-19 23:54:29 -0400
commit31b709a7c23c0fc7096c72d6024169c1f166f82d (patch)
tree9ffb08af66e9c10b9c2880cd4147b200fd28ea1b /components/script/dom/websocket.rs
parentc51e9f04559f04f1e820b792261e1653c6869ee5 (diff)
downloadservo-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.rs135
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)
}
}