From 9894dca86c5c89c4f9e999a9b771e443f6daf9dd Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Thu, 23 Mar 2017 14:17:17 +0100 Subject: [PATCH 1/4] Avoid a useless clone in WebSocket::Constructor --- components/script/dom/websocket.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/components/script/dom/websocket.rs b/components/script/dom/websocket.rs index 134cc9c3975..367d67d440d 100644 --- a/components/script/dom/websocket.rs +++ b/components/script/dom/websocket.rs @@ -274,7 +274,6 @@ impl WebSocket { *ws.sender.borrow_mut() = Some(dom_action_sender); - let moved_address = address.clone(); let task_source = global.networking_task_source(); let wrapper = global.get_runnable_wrapper(); thread::spawn(move || { @@ -282,7 +281,7 @@ impl WebSocket { match event { WebSocketNetworkEvent::ConnectionEstablished(headers, protocols) => { let open_thread = box ConnectionEstablishedTask { - address: moved_address.clone(), + address: address.clone(), headers: headers, protocols: protocols, }; @@ -290,22 +289,23 @@ impl WebSocket { }, WebSocketNetworkEvent::MessageReceived(message) => { let message_thread = box MessageReceivedTask { - address: moved_address.clone(), + address: address.clone(), message: message, }; task_source.queue_with_wrapper(message_thread, &wrapper).unwrap(); }, WebSocketNetworkEvent::Fail => { - fail_the_websocket_connection(moved_address.clone(), + fail_the_websocket_connection(address.clone(), &task_source, &wrapper); }, WebSocketNetworkEvent::Close(code, reason) => { - close_the_websocket_connection(moved_address.clone(), + close_the_websocket_connection(address.clone(), &task_source, &wrapper, code, reason); }, } } }); + // Step 7. Ok(ws) } From fb2c9e7bf51fbb739ee2246df58a464959b2f4e2 Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Thu, 23 Mar 2017 14:48:23 +0100 Subject: [PATCH 2/4] Introduce fetch::methods::should_be_blocked_due_to_bad_port --- components/net/fetch/methods.rs | 61 +++++++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 11 deletions(-) diff --git a/components/net/fetch/methods.rs b/components/net/fetch/methods.rs index ec9b4444e98..b0a06de064a 100644 --- a/components/net/fetch/methods.rs +++ b/components/net/fetch/methods.rs @@ -20,6 +20,7 @@ use net_traits::{FetchTaskTarget, NetworkError, ReferrerPolicy}; use net_traits::request::{Referrer, Request, RequestMode, ResponseTainting}; use net_traits::request::{Type, Origin, Window}; use net_traits::response::{Response, ResponseBody, ResponseType}; +use servo_url::ServoUrl; use std::borrow::Cow; use std::fmt; use std::fs::File; @@ -148,17 +149,8 @@ pub fn main_fetch(request: Rc, // Step 5 // TODO this step (CSP port/content blocking) - if let Some(port) = request.url().port() { - let is_ftp = request.url().scheme() == "ftp" && (port == 20 || port == 21); - static BAD_PORTS: [u16; 64] = [1, 7, 9, 11, 13, 15, 17, 19, 20, 21, 22, 23, 25, 37, 42, - 43, 53, 77, 79, 87, 95, 101, 102, 103, 104, 109, 110, 111, - 113, 115, 117, 119, 123, 135, 139, 143, 179, 389, 465, 512, - 513, 514, 515, 526, 530, 531, 532, 540, 556, 563, 587, 601, - 636, 993, 995, 2049, 3659, 4045, 6000, 6665, 6666, 6667, - 6668, 6669]; - if !is_ftp && BAD_PORTS.binary_search(&port).is_ok() { - response = Some(Response::network_error(NetworkError::Internal("Request attempted on bad port".into()))); - } + if should_be_blocked_due_to_bad_port(&request.url()) { + response = Some(Response::network_error(NetworkError::Internal("Request attempted on bad port".into()))); } // Step 6 @@ -623,3 +615,50 @@ fn should_block_nosniff(request: &Request, response: &Response) -> bool { _ => false }; } + +/// https://fetch.spec.whatwg.org/#block-bad-port +fn should_be_blocked_due_to_bad_port(url: &ServoUrl) -> bool { + // Step 1 is not applicable, this function just takes the URL directly. + + // Step 2. + let scheme = url.scheme(); + + // Step 3. + // If there is no explicit port, this means the default one is used for + // the given scheme, and thus this means the request should not be blocked + // due to a bad port. + let port = if let Some(port) = url.port() { port } else { return false }; + + // Step 4. + if scheme == "ftp" && (port == 20 || port == 21) { + return false; + } + + + // Step 5. + if is_network_scheme(scheme) && is_bad_port(port) { + return true; + } + + // Step 6. + false +} + +/// https://fetch.spec.whatwg.org/#network-scheme +fn is_network_scheme(scheme: &str) -> bool { + scheme == "ftp" || scheme == "http" || scheme == "https" +} + +/// https://fetch.spec.whatwg.org/#bad-port +fn is_bad_port(port: u16) -> bool { + static BAD_PORTS: [u16; 64] = [ + 1, 7, 9, 11, 13, 15, 17, 19, 20, 21, 22, 23, 25, 37, 42, + 43, 53, 77, 79, 87, 95, 101, 102, 103, 104, 109, 110, 111, + 113, 115, 117, 119, 123, 135, 139, 143, 179, 389, 465, 512, + 513, 514, 515, 526, 530, 531, 532, 540, 556, 563, 587, 601, + 636, 993, 995, 2049, 3659, 4045, 6000, 6665, 6666, 6667, + 6668, 6669 + ]; + + BAD_PORTS.binary_search(&port).is_ok() +} From bba0be13dd07116d02fd0f2e6d73d1ecb8e952bb Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Thu, 23 Mar 2017 15:37:32 +0100 Subject: [PATCH 3/4] Make ServoUrl::as_url return a &Url --- .../gfx/platform/macos/font_template.rs | 2 +- components/net/websocket_loader.rs | 2 +- components/script/dom/url.rs | 2 +- components/script/dom/urlhelper.rs | 22 +++++++++---------- components/script/dom/websocket.rs | 2 +- components/script/dom/window.rs | 4 ++-- components/url/lib.rs | 4 ++-- 7 files changed, 19 insertions(+), 19 deletions(-) diff --git a/components/gfx/platform/macos/font_template.rs b/components/gfx/platform/macos/font_template.rs index 3465f935360..3cd5fecdb01 100644 --- a/components/gfx/platform/macos/font_template.rs +++ b/components/gfx/platform/macos/font_template.rs @@ -92,7 +92,7 @@ impl FontTemplateData { .expect("No URL for Core Text font!") .get_string() .to_string()).expect("Couldn't parse Core Text font URL!") - .as_url().unwrap().to_file_path() + .as_url().to_file_path() .expect("Core Text font didn't name a path!"); let mut bytes = Vec::new(); File::open(path).expect("Couldn't open font file!").read_to_end(&mut bytes).unwrap(); diff --git a/components/net/websocket_loader.rs b/components/net/websocket_loader.rs index d0ce7d61836..186909c9314 100644 --- a/components/net/websocket_loader.rs +++ b/components/net/websocket_loader.rs @@ -70,7 +70,7 @@ pub fn init(connect: WebSocketCommunicate, connect_data: WebSocketConnectData, c // URL that we actually fetch from the network, after applying the replacements // specified in the hosts file. - let net_url_result = parse_url(replace_hosts(&connect_data.resource_url).as_url().unwrap()); + let net_url_result = parse_url(replace_hosts(&connect_data.resource_url).as_url()); let net_url = match net_url_result { Ok(net_url) => net_url, Err(e) => { diff --git a/components/script/dom/url.rs b/components/script/dom/url.rs index 6c9139e99fc..cf8a6201f8b 100644 --- a/components/script/dom/url.rs +++ b/components/script/dom/url.rs @@ -48,7 +48,7 @@ impl URL { } pub fn query_pairs(&self) -> Vec<(String, String)> { - self.url.borrow().as_url().unwrap().query_pairs().into_owned().collect() + self.url.borrow().as_url().query_pairs().into_owned().collect() } pub fn set_query_pairs(&self, pairs: &[(String, String)]) { diff --git a/components/script/dom/urlhelper.rs b/components/script/dom/urlhelper.rs index 5037d7fb733..e001faf7a81 100644 --- a/components/script/dom/urlhelper.rs +++ b/components/script/dom/urlhelper.rs @@ -12,37 +12,37 @@ pub struct UrlHelper; impl UrlHelper { pub fn Origin(url: &ServoUrl) -> USVString { - USVString(quirks::origin(url.as_url().unwrap()).to_owned()) + USVString(quirks::origin(url.as_url()).to_owned()) } pub fn Href(url: &ServoUrl) -> USVString { - USVString(quirks::href(url.as_url().unwrap()).to_owned()) + USVString(quirks::href(url.as_url()).to_owned()) } pub fn Hash(url: &ServoUrl) -> USVString { - USVString(quirks::hash(url.as_url().unwrap()).to_owned()) + USVString(quirks::hash(url.as_url()).to_owned()) } pub fn Host(url: &ServoUrl) -> USVString { - USVString(quirks::host(url.as_url().unwrap()).to_owned()) + USVString(quirks::host(url.as_url()).to_owned()) } pub fn Port(url: &ServoUrl) -> USVString { - USVString(quirks::port(url.as_url().unwrap()).to_owned()) + USVString(quirks::port(url.as_url()).to_owned()) } pub fn Search(url: &ServoUrl) -> USVString { - USVString(quirks::search(url.as_url().unwrap()).to_owned()) + USVString(quirks::search(url.as_url()).to_owned()) } pub fn Hostname(url: &ServoUrl) -> USVString { - USVString(quirks::hostname(url.as_url().unwrap()).to_owned()) + USVString(quirks::hostname(url.as_url()).to_owned()) } pub fn Password(url: &ServoUrl) -> USVString { - USVString(quirks::password(url.as_url().unwrap()).to_owned()) + USVString(quirks::password(url.as_url()).to_owned()) } pub fn Pathname(url: &ServoUrl) -> USVString { - USVString(quirks::pathname(url.as_url().unwrap()).to_owned()) + USVString(quirks::pathname(url.as_url()).to_owned()) } pub fn Protocol(url: &ServoUrl) -> USVString { - USVString(quirks::protocol(url.as_url().unwrap()).to_owned()) + USVString(quirks::protocol(url.as_url()).to_owned()) } pub fn Username(url: &ServoUrl) -> USVString { - USVString(quirks::username(url.as_url().unwrap()).to_owned()) + USVString(quirks::username(url.as_url()).to_owned()) } pub fn SetHash(url: &mut ServoUrl, value: USVString) { if let Some(ref mut url) = url.as_mut_url() { diff --git a/components/script/dom/websocket.rs b/components/script/dom/websocket.rs index 367d67d440d..296141a6c3a 100644 --- a/components/script/dom/websocket.rs +++ b/components/script/dom/websocket.rs @@ -210,7 +210,7 @@ impl WebSocket { let resource_url = try!(ServoUrl::parse(&url).map_err(|_| Error::Syntax)); // Although we do this replace and parse operation again in the resource thread, // we try here to be able to immediately throw a syntax error on failure. - let _ = try!(parse_url(&replace_hosts(&resource_url).as_url().unwrap()).map_err(|_| Error::Syntax)); + let _ = try!(parse_url(&replace_hosts(&resource_url).as_url()).map_err(|_| Error::Syntax)); // Step 2: Disallow https -> ws connections. // Step 3: Potentially block access to some ports. diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 3f89e7adf00..b390c44217a 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -1496,8 +1496,8 @@ impl Window { let referrer_policy = referrer_policy.or(doc.get_referrer_policy()); // https://html.spec.whatwg.org/multipage/#navigating-across-documents - if !force_reload && url.as_url().unwrap()[..Position::AfterQuery] == - doc.url().as_url().unwrap()[..Position::AfterQuery] { + if !force_reload && url.as_url()[..Position::AfterQuery] == + doc.url().as_url()[..Position::AfterQuery] { // Step 5 if let Some(fragment) = url.fragment() { doc.check_and_scroll_fragment(fragment); diff --git a/components/url/lib.rs b/components/url/lib.rs index 9e3948fc5ab..766c5bd5f5c 100644 --- a/components/url/lib.rs +++ b/components/url/lib.rs @@ -53,8 +53,8 @@ impl ServoUrl { Some(Arc::try_unwrap(self.0).unwrap_or_else(|s| (*s).clone())) } - pub fn as_url(&self) -> Option<&Arc> { - Some(&self.0) + pub fn as_url(&self) -> &Url { + &self.0 } pub fn parse(input: &str) -> Result { From 0bd54b904bf8b926afb60f4be0554bc37b30464b Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Thu, 23 Mar 2017 17:02:52 +0100 Subject: [PATCH 4/4] Properly follow the spec in WebSocket::Constructor --- components/net/fetch/methods.rs | 2 +- components/net/websocket_loader.rs | 110 +++++++----- components/net_traits/lib.rs | 3 +- components/script/dom/websocket.rs | 158 ++++-------------- .../Create-Secure-blocked-port.htm.ini | 6 + .../metadata/websockets/cookies/007.html.ini | 5 + 6 files changed, 112 insertions(+), 172 deletions(-) create mode 100644 tests/wpt/metadata/websockets/Create-Secure-blocked-port.htm.ini diff --git a/components/net/fetch/methods.rs b/components/net/fetch/methods.rs index b0a06de064a..90d1feed2d4 100644 --- a/components/net/fetch/methods.rs +++ b/components/net/fetch/methods.rs @@ -617,7 +617,7 @@ fn should_block_nosniff(request: &Request, response: &Response) -> bool { } /// https://fetch.spec.whatwg.org/#block-bad-port -fn should_be_blocked_due_to_bad_port(url: &ServoUrl) -> bool { +pub fn should_be_blocked_due_to_bad_port(url: &ServoUrl) -> bool { // Step 1 is not applicable, this function just takes the URL directly. // Step 2. diff --git a/components/net/websocket_loader.rs b/components/net/websocket_loader.rs index 186909c9314..6c0392ab65a 100644 --- a/components/net/websocket_loader.rs +++ b/components/net/websocket_loader.rs @@ -2,11 +2,13 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +use cookie::Cookie; use cookie_storage::CookieStorage; +use fetch::methods::should_be_blocked_due_to_bad_port; use http_loader; -use hyper::header::Host; -use net_traits::{WebSocketCommunicate, WebSocketConnectData, WebSocketDomAction, WebSocketNetworkEvent}; -use net_traits::MessageData; +use hyper::header::{Host, SetCookie}; +use net_traits::{CookieSource, MessageData, WebSocketCommunicate}; +use net_traits::{WebSocketConnectData, WebSocketDomAction, WebSocketNetworkEvent}; use net_traits::hosts::replace_hosts; use net_traits::unwrap_websocket_protocol; use servo_url::ServoUrl; @@ -23,72 +25,90 @@ use websocket::sender::Sender; use websocket::stream::WebSocketStream; use websocket::ws::receiver::Receiver as WSReceiver; use websocket::ws::sender::Sender as Sender_Object; -use websocket::ws::util::url::parse_url; -/// *Establish a WebSocket Connection* as defined in RFC 6455. -fn establish_a_websocket_connection(resource_url: &ServoUrl, net_url: (Host, String, bool), - origin: String, protocols: Vec, +// https://fetch.spec.whatwg.org/#concept-websocket-establish +fn establish_a_websocket_connection(resource_url: &ServoUrl, + origin: String, + protocols: Vec, cookie_jar: Arc>) - -> WebSocketResult<(Headers, Sender, Receiver)> { - let host = Host { - hostname: resource_url.host_str().unwrap().to_owned(), - port: resource_url.port_or_known_default(), - }; + -> WebSocketResult<(Headers, Sender, Receiver)> { + // Steps 1-2 are not really applicable here, given we don't exactly go + // through the same infrastructure as the Fetch spec. - let mut request = try!(Client::connect(net_url)); - request.headers.set(Origin(origin)); - request.headers.set(host); + if should_be_blocked_due_to_bad_port(resource_url) { + // Subset of steps 11-12, we inline the bad port check here from the + // main fetch algorithm for the same reason steps 1-2 are not + // applicable. + return Err(WebSocketError::RequestError("Request should be blocked due to bad port.")); + } + + // Steps 3-7. + let net_url = replace_hosts(resource_url); + let mut request = try!(Client::connect(net_url.as_url())); + + // Client::connect sets the Host header to the host of the URL that is + // passed to it, so we need to reset it afterwards to the correct one. + request.headers.set(Host { + hostname: resource_url.host_str().unwrap().to_owned(), + port: resource_url.port(), + }); + + // Step 8. if !protocols.is_empty() { request.headers.set(WebSocketProtocol(protocols.clone())); - }; + } + // Steps 9-10. + // TODO: support for permessage-deflate extension. + + // Subset of step 11. + // See step 2 of https://fetch.spec.whatwg.org/#concept-fetch. + request.headers.set(Origin(origin)); + + // Transitive subset of step 11. + // See step 17.1 of https://fetch.spec.whatwg.org/#concept-http-network-or-cache-fetch. http_loader::set_request_cookies(&resource_url, &mut request.headers, &cookie_jar); + // Step 11, somewhat. let response = try!(request.send()); + + // Step 12, 14. try!(response.validate()); - { - let protocol_in_use = unwrap_websocket_protocol(response.protocol()); - if let Some(protocol_name) = protocol_in_use { - if !protocols.is_empty() && !protocols.iter().any(|p| (&**p).eq_ignore_ascii_case(protocol_name)) { - return Err(WebSocketError::ProtocolError("Protocol in Use not in client-supplied protocol list")); - }; + // Step 13 and transitive subset of step 14. + // See step 6 of http://tools.ietf.org/html/rfc6455#section-4.1. + if let Some(protocol_name) = unwrap_websocket_protocol(response.protocol()) { + if !protocols.is_empty() && !protocols.iter().any(|p| (&**p).eq_ignore_ascii_case(protocol_name)) { + return Err(WebSocketError::ProtocolError("Protocol in Use not in client-supplied protocol list")); }; + }; + + // Transitive subset of step 11. + // See step 15 of https://fetch.spec.whatwg.org/#http-network-fetch. + if let Some(cookies) = response.headers.get::() { + let mut jar = cookie_jar.write().unwrap(); + for cookie in &**cookies { + if let Some(cookie) = Cookie::new_wrapped(cookie.clone(), resource_url, CookieSource::HTTP) { + jar.push(cookie, resource_url, CookieSource::HTTP); + } + } } let headers = response.headers.clone(); let (sender, receiver) = response.begin().split(); Ok((headers, sender, receiver)) - } pub fn init(connect: WebSocketCommunicate, connect_data: WebSocketConnectData, cookie_jar: Arc>) { thread::Builder::new().name(format!("WebSocket connection to {}", connect_data.resource_url)).spawn(move || { - // Step 8: Protocols. - - // Step 9. - - // URL that we actually fetch from the network, after applying the replacements - // specified in the hosts file. - let net_url_result = parse_url(replace_hosts(&connect_data.resource_url).as_url()); - let net_url = match net_url_result { - Ok(net_url) => net_url, - Err(e) => { - debug!("Failed to establish a WebSocket connection: {:?}", e); - let _ = connect.event_sender.send(WebSocketNetworkEvent::Fail); - return; - } - }; let channel = establish_a_websocket_connection(&connect_data.resource_url, - net_url, connect_data.origin, - connect_data.protocols.clone(), + connect_data.protocols, cookie_jar); - let (_, ws_sender, mut receiver) = match channel { - Ok(channel) => { - let _ = connect.event_sender.send(WebSocketNetworkEvent::ConnectionEstablished(channel.0.clone(), - connect_data.protocols)); - channel + let (ws_sender, mut receiver) = match channel { + Ok((headers, sender, receiver)) => { + let _ = connect.event_sender.send(WebSocketNetworkEvent::ConnectionEstablished(headers)); + (sender, receiver) }, Err(e) => { debug!("Failed to establish a WebSocket connection: {:?}", e); diff --git a/components/net_traits/lib.rs b/components/net_traits/lib.rs index f3dd1c726cf..36fc77edacc 100644 --- a/components/net_traits/lib.rs +++ b/components/net_traits/lib.rs @@ -350,8 +350,7 @@ pub enum WebSocketDomAction { pub enum WebSocketNetworkEvent { ConnectionEstablished(#[serde(deserialize_with = "::hyper_serde::deserialize", serialize_with = "::hyper_serde::serialize")] - header::Headers, - Vec), + header::Headers), MessageReceived(MessageData), Close(Option, String), Fail, diff --git a/components/script/dom/websocket.rs b/components/script/dom/websocket.rs index 296141a6c3a..a08684819e2 100644 --- a/components/script/dom/websocket.rs +++ b/components/script/dom/websocket.rs @@ -23,17 +23,13 @@ use dom::globalscope::GlobalScope; use dom::messageevent::MessageEvent; use dom::urlhelper::UrlHelper; use dom_struct::dom_struct; -use hyper; -use hyper_serde::Serde; use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; use js::jsapi::JSAutoCompartment; use js::jsval::UndefinedValue; use js::typedarray::{ArrayBuffer, CreateWith}; use net_traits::{WebSocketCommunicate, WebSocketConnectData, WebSocketDomAction, WebSocketNetworkEvent}; -use net_traits::CookieSource::HTTP; -use net_traits::CoreResourceMsg::{SetCookiesForUrl, WebsocketConnect}; +use net_traits::CoreResourceMsg::WebsocketConnect; use net_traits::MessageData; -use net_traits::hosts::replace_hosts; use net_traits::unwrap_websocket_protocol; use script_runtime::CommonScriptMsg; use script_runtime::ScriptThreadEventCategory::WebSocketEvent; @@ -47,7 +43,6 @@ use std::thread; use task_source::TaskSource; use task_source::networking::NetworkingTaskSource; use websocket::header::{Headers, WebSocketProtocol}; -use websocket::ws::util::url::parse_url; #[derive(JSTraceable, PartialEq, Copy, Clone, Debug, HeapSizeOf)] enum WebSocketRequestState { @@ -57,75 +52,6 @@ enum WebSocketRequestState { Closed = 3, } -// list of bad ports according to -// https://fetch.spec.whatwg.org/#port-blocking -const BLOCKED_PORTS_LIST: &'static [u16] = &[ - 1, // tcpmux - 7, // echo - 9, // discard - 11, // systat - 13, // daytime - 15, // netstat - 17, // qotd - 19, // chargen - 20, // ftp-data - 21, // ftp - 22, // ssh - 23, // telnet - 25, // smtp - 37, // time - 42, // name - 43, // nicname - 53, // domain - 77, // priv-rjs - 79, // finger - 87, // ttylink - 95, // supdup - 101, // hostriame - 102, // iso-tsap - 103, // gppitnp - 104, // acr-nema - 109, // pop2 - 110, // pop3 - 111, // sunrpc - 113, // auth - 115, // sftp - 117, // uucp-path - 119, // nntp - 123, // ntp - 135, // loc-srv / epmap - 139, // netbios - 143, // imap2 - 179, // bgp - 389, // ldap - 465, // smtp+ssl - 512, // print / exec - 513, // login - 514, // shell - 515, // printer - 526, // tempo - 530, // courier - 531, // chat - 532, // netnews - 540, // uucp - 556, // remotefs - 563, // nntp+ssl - 587, // smtp - 601, // syslog-conn - 636, // ldap+ssl - 993, // imap+ssl - 995, // pop3+ssl - 2049, // nfs - 3659, // apple-sasl - 4045, // lockd - 6000, // x11 - 6665, // irc (alternate) - 6666, // irc (alternate) - 6667, // irc (default) - 6668, // irc (alternate) - 6669, // irc (alternate) -]; - // Close codes defined in https://tools.ietf.org/html/rfc6455#section-7.4.1 // Names are from https://github.com/mozilla/gecko-dev/blob/master/netwerk/protocol/websocket/nsIWebSocketChannel.idl #[allow(dead_code)] @@ -202,34 +128,36 @@ impl WebSocket { global, WebSocketBinding::Wrap) } + /// https://html.spec.whatwg.org/multipage/#dom-websocket pub fn Constructor(global: &GlobalScope, url: DOMString, protocols: Option) -> Fallible> { - // Step 1. - let resource_url = try!(ServoUrl::parse(&url).map_err(|_| Error::Syntax)); - // Although we do this replace and parse operation again in the resource thread, - // we try here to be able to immediately throw a syntax error on failure. - let _ = try!(parse_url(&replace_hosts(&resource_url).as_url()).map_err(|_| Error::Syntax)); - // Step 2: Disallow https -> ws connections. + // Steps 1-2. + let url_record = ServoUrl::parse(&url).or(Err(Error::Syntax))?; - // Step 3: Potentially block access to some ports. - let port: u16 = resource_url.port_or_known_default().unwrap(); - - if BLOCKED_PORTS_LIST.iter().any(|&p| p == port) { - return Err(Error::Security); + // Step 3. + match url_record.scheme() { + "ws" | "wss" => {}, + _ => return Err(Error::Syntax), } // Step 4. - let protocols = match protocols { - Some(StringOrStringSequence::String(string)) => vec![String::from(string)], - Some(StringOrStringSequence::StringSequence(sequence)) => { - sequence.into_iter().map(String::from).collect() - }, - _ => Vec::new(), - }; + if url_record.fragment().is_some() { + return Err(Error::Syntax); + } // Step 5. + let protocols = protocols.map_or(vec![], |p| { + match p { + StringOrStringSequence::String(string) => vec![string.into()], + StringOrStringSequence::StringSequence(seq) => { + seq.into_iter().map(String::from).collect() + }, + } + }); + + // Step 6. for (i, protocol) in protocols.iter().enumerate() { // https://tools.ietf.org/html/rfc6455#section-4.1 // Handshake requirements, step 10 @@ -244,16 +172,12 @@ impl WebSocket { } } - // Step 6: Origin. - let origin = UrlHelper::Origin(&global.get_url()).0; - - // Step 7. - let ws = WebSocket::new(global, resource_url.clone()); + let ws = WebSocket::new(global, url_record.clone()); let address = Trusted::new(&*ws); let connect_data = WebSocketConnectData { - resource_url: resource_url.clone(), - origin: origin, + resource_url: url_record, + origin: UrlHelper::Origin(&global.get_url()).0, protocols: protocols, }; @@ -270,6 +194,7 @@ impl WebSocket { action_receiver: resource_action_receiver, }; + // Step 8. let _ = global.core_resource_thread().send(WebsocketConnect(connect, connect_data)); *ws.sender.borrow_mut() = Some(dom_action_sender); @@ -279,11 +204,10 @@ impl WebSocket { thread::spawn(move || { while let Ok(event) = dom_event_receiver.recv() { match event { - WebSocketNetworkEvent::ConnectionEstablished(headers, protocols) => { + WebSocketNetworkEvent::ConnectionEstablished(headers) => { let open_thread = box ConnectionEstablishedTask { address: address.clone(), headers: headers, - protocols: protocols, }; task_source.queue_with_wrapper(open_thread, &wrapper).unwrap(); }, @@ -466,45 +390,31 @@ impl WebSocketMethods for WebSocket { /// Task queued when *the WebSocket connection is established*. +/// https://html.spec.whatwg.org/multipage/#feedback-from-the-protocol:concept-websocket-established struct ConnectionEstablishedTask { address: Trusted, - protocols: Vec, headers: Headers, } impl Runnable for ConnectionEstablishedTask { fn name(&self) -> &'static str { "ConnectionEstablishedTask" } + /// https://html.spec.whatwg.org/multipage/#feedback-from-the-protocol:concept-websocket-established fn handler(self: Box) { let ws = self.address.root(); - // Step 1: Protocols. - if !self.protocols.is_empty() && self.headers.get::().is_none() { - let task_source = ws.global().networking_task_source(); - fail_the_websocket_connection(self.address, &task_source, &ws.global().get_runnable_wrapper()); - return; - } - - // Step 2. + // Step 1. ws.ready_state.set(WebSocketRequestState::Open); - // Step 3: Extensions. - //TODO: Set extensions to extensions in use + // Step 2: Extensions. + // TODO: Set extensions to extensions in use. - // Step 4: Protocols. - let protocol_in_use = unwrap_websocket_protocol(self.headers.get::()); - if let Some(protocol_name) = protocol_in_use { + // Step 3. + if let Some(protocol_name) = unwrap_websocket_protocol(self.headers.get::()) { *ws.protocol.borrow_mut() = protocol_name.to_owned(); }; - // Step 5: Cookies. - if let Some(cookies) = self.headers.get::() { - let cookies = cookies.iter().map(|c| Serde(c.clone())).collect(); - let _ = ws.global().core_resource_thread().send( - SetCookiesForUrl(ws.url.clone(), cookies, HTTP)); - } - - // Step 6. + // Step 4. ws.upcast().fire_event(atom!("open")); } } diff --git a/tests/wpt/metadata/websockets/Create-Secure-blocked-port.htm.ini b/tests/wpt/metadata/websockets/Create-Secure-blocked-port.htm.ini new file mode 100644 index 00000000000..97a5cb42ba0 --- /dev/null +++ b/tests/wpt/metadata/websockets/Create-Secure-blocked-port.htm.ini @@ -0,0 +1,6 @@ +[Create-Secure-blocked-port.htm] + type: testharness + [W3C WebSocket API - Create Secure WebSocket - Pass a URL with a blocked port - SECURITY_ERR should be thrown] + expected: FAIL + bug: https://github.com/w3c/web-platform-tests/pull/5212 + diff --git a/tests/wpt/metadata/websockets/cookies/007.html.ini b/tests/wpt/metadata/websockets/cookies/007.html.ini index 383e159c991..de71023f1e6 100644 --- a/tests/wpt/metadata/websockets/cookies/007.html.ini +++ b/tests/wpt/metadata/websockets/cookies/007.html.ini @@ -1,8 +1,13 @@ [007.html] type: testharness + [WebSockets: when to process set-cookie fields in ws response] + expected: FAIL + bug: https://github.com/w3c/web-platform-tests/issues/5213 + [007.html?wss] type: testharness [WebSockets: when to process set-cookie fields in ws response] expected: FAIL + bug: https://github.com/w3c/web-platform-tests/issues/5213