Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adding initial support for websocket subprotocol negotation #8825

Merged
merged 1 commit into from Dec 18, 2015
Merged
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

@@ -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) => {
@@ -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);
@@ -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
@@ -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);
@@ -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

@@ -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

@@ -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

This file was deleted.

This file was deleted.

This file was deleted.

@@ -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

This file was deleted.

ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.