diff options
-rw-r--r-- | components/net/websocket_loader.rs | 35 | ||||
-rw-r--r-- | components/net_traits/lib.rs | 9 | ||||
-rw-r--r-- | components/script/dom/webidls/WebSocket.webidl | 2 | ||||
-rw-r--r-- | components/script/dom/websocket.rs | 40 | ||||
-rw-r--r-- | tests/wpt/metadata/html/dom/interfaces.html.ini | 3 | ||||
-rw-r--r-- | tests/wpt/metadata/websockets/Create-Secure-valid-url-array-protocols.htm.ini (renamed from tests/wpt/metadata/websockets/Create-Secure-valid-url-protocol-setCorrectly.htm.ini) | 10 | ||||
-rw-r--r-- | tests/wpt/metadata/websockets/Create-valid-url-array-protocols.htm.ini | 9 | ||||
-rw-r--r-- | tests/wpt/metadata/websockets/Create-valid-url-protocol-empty.htm.ini | 5 | ||||
-rw-r--r-- | tests/wpt/metadata/websockets/constructor/009.html.ini | 6 | ||||
-rw-r--r-- | tests/wpt/metadata/websockets/constructor/012.html.ini | 6 | ||||
-rw-r--r-- | tests/wpt/metadata/websockets/interfaces.html.ini | 6 | ||||
-rw-r--r-- | tests/wpt/metadata/websockets/interfaces/WebSocket/protocol/protocol-initial.html.ini | 5 |
12 files changed, 87 insertions, 49 deletions
diff --git a/components/net/websocket_loader.rs b/components/net/websocket_loader.rs index 2ae54dc8eb2..05665077845 100644 --- a/components/net/websocket_loader.rs +++ b/components/net/websocket_loader.rs @@ -5,16 +5,18 @@ use hyper::header::Host; use net_traits::MessageData; use net_traits::hosts::replace_hosts; +use net_traits::unwrap_websocket_protocol; use net_traits::{WebSocketCommunicate, WebSocketConnectData, WebSocketDomAction, WebSocketNetworkEvent}; +use std::ascii::AsciiExt; use std::sync::{Arc, Mutex}; use std::thread; use util::task::spawn_named; use websocket::client::receiver::Receiver; use websocket::client::request::Url; use websocket::client::sender::Sender; -use websocket::header::Origin; +use websocket::header::{Headers, Origin, WebSocketProtocol}; use websocket::message::Type; -use websocket::result::WebSocketResult; +use websocket::result::{WebSocketError, WebSocketResult}; use websocket::stream::WebSocketStream; use websocket::ws::receiver::Receiver as WSReceiver; use websocket::ws::sender::Sender as Sender_Object; @@ -23,8 +25,8 @@ use websocket::{Client, Message}; /// *Establish a WebSocket Connection* as defined in RFC 6455. fn establish_a_websocket_connection(resource_url: &Url, net_url: (Host, String, bool), - origin: String) - -> WebSocketResult<(Sender<WebSocketStream>, Receiver<WebSocketStream>)> { + origin: String, protocols: Vec<String>) + -> WebSocketResult<(Headers, Sender<WebSocketStream>, Receiver<WebSocketStream>)> { let host = Host { hostname: resource_url.serialize_host().unwrap(), @@ -34,11 +36,26 @@ fn establish_a_websocket_connection(resource_url: &Url, net_url: (Host, String, let mut request = try!(Client::connect(net_url)); request.headers.set(Origin(origin)); request.headers.set(host); + if !protocols.is_empty() { + request.headers.set(WebSocketProtocol(protocols.clone())); + }; let response = try!(request.send()); try!(response.validate()); - Ok(response.begin().split()) + { + 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")); + }; + }; + } + + let headers = response.headers.clone(); + let (sender, receiver) = response.begin().split(); + Ok((headers, sender, receiver)) + } pub fn init(connect: WebSocketCommunicate, connect_data: WebSocketConnectData) { @@ -60,10 +77,12 @@ pub fn init(connect: WebSocketCommunicate, connect_data: WebSocketConnectData) { }; let channel = establish_a_websocket_connection(&connect_data.resource_url, net_url, - connect_data.origin); - let (ws_sender, mut receiver) = match channel { + connect_data.origin, + connect_data.protocols.clone()); + let (_, ws_sender, mut receiver) = match channel { Ok(channel) => { - let _ = connect.event_sender.send(WebSocketNetworkEvent::ConnectionEstablished); + let _ = connect.event_sender.send(WebSocketNetworkEvent::ConnectionEstablished(channel.0.clone(), + connect_data.protocols)); channel }, Err(e) => { diff --git a/components/net_traits/lib.rs b/components/net_traits/lib.rs index fbf1b5e810a..64bfb89f62b 100644 --- a/components/net_traits/lib.rs +++ b/components/net_traits/lib.rs @@ -37,6 +37,7 @@ use std::rc::Rc; use std::thread; use url::Url; use util::mem::HeapSizeOf; +use websocket::header; pub mod hosts; pub mod image_cache_task; @@ -239,7 +240,7 @@ pub enum WebSocketDomAction { #[derive(Deserialize, Serialize)] pub enum WebSocketNetworkEvent { - ConnectionEstablished, + ConnectionEstablished(header::Headers, Vec<String>), MessageReceived(MessageData), Close, } @@ -254,6 +255,7 @@ pub struct WebSocketCommunicate { pub struct WebSocketConnectData { pub resource_url: Url, pub origin: String, + pub protocols: Vec<String>, } #[derive(Deserialize, Serialize)] @@ -429,6 +431,11 @@ pub fn load_whole_resource(resource_task: &ResourceTask, url: Url, pipeline_id: } } +/// Defensively unwraps the protocol string from the response object's protocol +pub fn unwrap_websocket_protocol(wsp: Option<&header::WebSocketProtocol>) -> Option<&str> { + wsp.and_then(|protocol_list| protocol_list.get(0).map(|protocol| protocol.as_ref())) +} + /// An unique identifier to keep track of each load message in the resource handler #[derive(Clone, PartialEq, Eq, Copy, Hash, Debug, Deserialize, Serialize, HeapSizeOf)] pub struct ResourceId(pub u32); diff --git a/components/script/dom/webidls/WebSocket.webidl b/components/script/dom/webidls/WebSocket.webidl index e18b2536743..8a07e0496eb 100644 --- a/components/script/dom/webidls/WebSocket.webidl +++ b/components/script/dom/webidls/WebSocket.webidl @@ -22,7 +22,7 @@ interface WebSocket : EventTarget { attribute EventHandler onerror; attribute EventHandler onclose; //readonly attribute DOMString extensions; - //readonly attribute DOMString protocol; + readonly attribute DOMString protocol; [Throws] void close([Clamp] optional unsigned short code, optional USVString reason); //messaging diff --git a/components/script/dom/websocket.rs b/components/script/dom/websocket.rs index 8d7e5ebd6c6..392b8258b5c 100644 --- a/components/script/dom/websocket.rs +++ b/components/script/dom/websocket.rs @@ -29,6 +29,7 @@ use libc::{uint32_t, uint8_t}; use net_traits::ControlMsg::WebsocketConnect; use net_traits::MessageData; use net_traits::hosts::replace_hosts; +use net_traits::unwrap_websocket_protocol; use net_traits::{WebSocketCommunicate, WebSocketConnectData, WebSocketDomAction, WebSocketNetworkEvent}; use ref_slice::ref_slice; use script_task::ScriptTaskEventCategory::WebSocketEvent; @@ -39,6 +40,7 @@ use std::ptr; use std::thread; use util::str::DOMString; use websocket::client::request::Url; +use websocket::header::{Headers, WebSocketProtocol}; use websocket::ws::util::url::parse_url; #[derive(JSTraceable, PartialEq, Copy, Clone, Debug, HeapSizeOf)] @@ -146,6 +148,7 @@ pub struct WebSocket { code: Cell<u16>, //Closing code reason: DOMRefCell<String>, //Closing reason binary_type: Cell<BinaryType>, + protocol: DOMRefCell<String>, //Subprotocol selected by server } impl WebSocket { @@ -164,6 +167,7 @@ impl WebSocket { code: Cell::new(0), reason: DOMRefCell::new("".to_owned()), binary_type: Cell::new(BinaryType::Blob), + protocol: DOMRefCell::new("".to_owned()), } } @@ -208,6 +212,8 @@ impl WebSocket { return Err(Error::Syntax); } + // TODO: also check that no separator characters are used + // https://tools.ietf.org/html/rfc6455#section-4.1 if protocol.chars().any(|c| c < '\u{0021}' || c > '\u{007E}') { return Err(Error::Syntax); } @@ -220,10 +226,12 @@ impl WebSocket { let address = Trusted::new(global.get_cx(), ws.r(), global.networking_task_source()); let origin = global.get_url().serialize(); + let protocols: Vec<String> = protocols.iter().map(|x| String::from(x.clone())).collect(); let connect_data = WebSocketConnectData { resource_url: resource_url.clone(), origin: origin, + protocols: protocols, }; // Create the interface for communication with the resource task @@ -246,13 +254,14 @@ impl WebSocket { let moved_address = address.clone(); let sender = global.networking_task_source(); - thread::spawn(move || { while let Ok(event) = dom_event_receiver.recv() { match event { - WebSocketNetworkEvent::ConnectionEstablished => { + WebSocketNetworkEvent::ConnectionEstablished(headers, protocols) => { let open_task = box ConnectionEstablishedTask { addr: moved_address.clone(), + headers: headers, + protocols: protocols, }; sender.send(CommonScriptMsg::RunnableMsg(WebSocketEvent, open_task)).unwrap(); }, @@ -358,6 +367,11 @@ impl WebSocketMethods for WebSocket { self.binary_type.set(btype) } + // https://html.spec.whatwg.org/multipage/#dom-websocket-protocol + fn Protocol(&self) -> DOMString { + DOMString::from(self.protocol.borrow().clone()) + } + // https://html.spec.whatwg.org/multipage/#dom-websocket-send fn Send(&self, data: USVString) -> Fallible<()> { @@ -448,22 +462,42 @@ impl WebSocketMethods for WebSocket { /// Task queued when *the WebSocket connection is established*. struct ConnectionEstablishedTask { addr: Trusted<WebSocket>, + protocols: Vec<String>, + headers: Headers, } impl Runnable for ConnectionEstablishedTask { fn handler(self: Box<Self>) { let ws = self.addr.root(); + let global = ws.global.root(); + // Step 1: Protocols. + if !self.protocols.is_empty() && self.headers.get::<WebSocketProtocol>().is_none() { + ws.failed.set(true); + ws.ready_state.set(WebSocketRequestState::Closing); + let task = box CloseTask { + addr: self.addr, + }; + let sender = global.r().networking_task_source(); + sender.send(CommonScriptMsg::RunnableMsg(WebSocketEvent, task)).unwrap(); + return; + } // Step 2. ws.ready_state.set(WebSocketRequestState::Open); // Step 3: Extensions. + //TODO: Set extensions to extensions in use + // Step 4: Protocols. + let protocol_in_use = unwrap_websocket_protocol(self.headers.get::<WebSocketProtocol>()); + if let Some(protocol_name) = protocol_in_use { + *ws.protocol.borrow_mut() = protocol_name.to_owned(); + }; + // Step 5: Cookies. // Step 6. - let global = ws.global.root(); let event = Event::new(global.r(), atom!("open"), EventBubbles::DoesNotBubble, EventCancelable::NotCancelable); diff --git a/tests/wpt/metadata/html/dom/interfaces.html.ini b/tests/wpt/metadata/html/dom/interfaces.html.ini index 7ca1add1408..735d9196bf5 100644 --- a/tests/wpt/metadata/html/dom/interfaces.html.ini +++ b/tests/wpt/metadata/html/dom/interfaces.html.ini @@ -7845,9 +7845,6 @@ [WebSocket interface: attribute extensions] expected: FAIL - [WebSocket interface: attribute protocol] - expected: FAIL - [CloseEvent interface: existence and properties of interface object] expected: FAIL diff --git a/tests/wpt/metadata/websockets/Create-Secure-valid-url-protocol-setCorrectly.htm.ini b/tests/wpt/metadata/websockets/Create-Secure-valid-url-array-protocols.htm.ini index a76a4bc2c74..f1617ea3c85 100644 --- a/tests/wpt/metadata/websockets/Create-Secure-valid-url-protocol-setCorrectly.htm.ini +++ b/tests/wpt/metadata/websockets/Create-Secure-valid-url-array-protocols.htm.ini @@ -1,9 +1,9 @@ -[Create-Secure-valid-url-protocol-setCorrectly.htm] +[Create-Secure-valid-url-array-protocols.htm] type: testharness expected: TIMEOUT - [W3C WebSocket API - Create Secure WebSocket - Pass a valid URL and protocol string - protocol should be set correctly - Connection should be opened] - expected: FAIL - - [W3C WebSocket API - Create Secure WebSocket - Pass a valid URL and protocol string - Connection should be closed] + [W3C WebSocket API - Create Secure WebSocket - Pass a valid URL and array of protocol strings - Connection should be opened] expected: NOTRUN + [W3C WebSocket API - Create Secure WebSocket - Pass a valid URL and array of protocol strings - Connection should be closed] + expected: FAIL + diff --git a/tests/wpt/metadata/websockets/Create-valid-url-array-protocols.htm.ini b/tests/wpt/metadata/websockets/Create-valid-url-array-protocols.htm.ini new file mode 100644 index 00000000000..dccba045ceb --- /dev/null +++ b/tests/wpt/metadata/websockets/Create-valid-url-array-protocols.htm.ini @@ -0,0 +1,9 @@ +[Create-valid-url-array-protocols.htm] + type: testharness + expected: TIMEOUT + [W3C WebSocket API - Create WebSocket - Pass a valid URL and array of protocol strings - Connection should be opened] + expected: NOTRUN + + [W3C WebSocket API - Create WebSocket - Pass a valid URL and array of protocol strings - Connection should be closed] + expected: FAIL + diff --git a/tests/wpt/metadata/websockets/Create-valid-url-protocol-empty.htm.ini b/tests/wpt/metadata/websockets/Create-valid-url-protocol-empty.htm.ini deleted file mode 100644 index e39be950b89..00000000000 --- a/tests/wpt/metadata/websockets/Create-valid-url-protocol-empty.htm.ini +++ /dev/null @@ -1,5 +0,0 @@ -[Create-valid-url-protocol-empty.htm] - type: testharness - [W3C WebSocket API - Create WebSocket - wsocket.protocol should be empty before connection is established] - expected: FAIL - diff --git a/tests/wpt/metadata/websockets/constructor/009.html.ini b/tests/wpt/metadata/websockets/constructor/009.html.ini deleted file mode 100644 index db8502371ba..00000000000 --- a/tests/wpt/metadata/websockets/constructor/009.html.ini +++ /dev/null @@ -1,6 +0,0 @@ -[009.html] - type: testharness - expected: TIMEOUT - [WebSockets: protocol] - expected: TIMEOUT - diff --git a/tests/wpt/metadata/websockets/constructor/012.html.ini b/tests/wpt/metadata/websockets/constructor/012.html.ini deleted file mode 100644 index 3c79462be91..00000000000 --- a/tests/wpt/metadata/websockets/constructor/012.html.ini +++ /dev/null @@ -1,6 +0,0 @@ -[012.html] - type: testharness - expected: TIMEOUT - [WebSockets: no protocol in response] - expected: TIMEOUT - diff --git a/tests/wpt/metadata/websockets/interfaces.html.ini b/tests/wpt/metadata/websockets/interfaces.html.ini index d8c25bdc068..d5eaa3f58dc 100644 --- a/tests/wpt/metadata/websockets/interfaces.html.ini +++ b/tests/wpt/metadata/websockets/interfaces.html.ini @@ -9,18 +9,12 @@ [WebSocket interface: attribute extensions] expected: FAIL - [WebSocket interface: attribute protocol] - expected: FAIL - [Stringification of new WebSocket("ws://foo")] expected: FAIL [WebSocket interface: new WebSocket("ws://foo") must inherit property "extensions" with the proper type (10)] expected: FAIL - [WebSocket interface: new WebSocket("ws://foo") must inherit property "protocol" with the proper type (11)] - expected: FAIL - [CloseEvent interface: existence and properties of interface object] expected: FAIL diff --git a/tests/wpt/metadata/websockets/interfaces/WebSocket/protocol/protocol-initial.html.ini b/tests/wpt/metadata/websockets/interfaces/WebSocket/protocol/protocol-initial.html.ini deleted file mode 100644 index 1c6ba352e3b..00000000000 --- a/tests/wpt/metadata/websockets/interfaces/WebSocket/protocol/protocol-initial.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[protocol-initial.html] - type: testharness - [WebSockets: getting protocol in connecting] - expected: FAIL - |