LibWeb+LibWebSocket: DOM WebSocket subprotocol support

This adds support for WebSocket subprotocols to WebSocket DOM
objects, with some necessary plumbing to LibWebSocket and its
clients.

See the associated pull request for how this was tested.
This commit is contained in:
Guilherme Gonçalves 2022-12-31 10:37:10 +00:00 committed by Andreas Kling
parent 9115e99e4b
commit 230c0b34d4
19 changed files with 107 additions and 34 deletions

View file

@ -19,10 +19,11 @@ NonnullRefPtr<WebSocketClientManagerLadybird> WebSocketClientManagerLadybird::cr
WebSocketClientManagerLadybird::WebSocketClientManagerLadybird() = default; WebSocketClientManagerLadybird::WebSocketClientManagerLadybird() = default;
WebSocketClientManagerLadybird::~WebSocketClientManagerLadybird() = default; WebSocketClientManagerLadybird::~WebSocketClientManagerLadybird() = default;
RefPtr<Web::WebSockets::WebSocketClientSocket> WebSocketClientManagerLadybird::connect(AK::URL const& url, DeprecatedString const& origin) RefPtr<Web::WebSockets::WebSocketClientSocket> WebSocketClientManagerLadybird::connect(AK::URL const& url, DeprecatedString const& origin, Vector<DeprecatedString> const& protocols)
{ {
WebSocket::ConnectionInfo connection_info(url); WebSocket::ConnectionInfo connection_info(url);
connection_info.set_origin(origin); connection_info.set_origin(origin);
connection_info.set_protocols(protocols);
auto impl = adopt_ref(*new WebSocketImplQt); auto impl = adopt_ref(*new WebSocketImplQt);
auto web_socket = WebSocket::WebSocket::create(move(connection_info), move(impl)); auto web_socket = WebSocket::WebSocket::create(move(connection_info), move(impl));

View file

@ -19,7 +19,7 @@ public:
static NonnullRefPtr<WebSocketClientManagerLadybird> create(); static NonnullRefPtr<WebSocketClientManagerLadybird> create();
virtual ~WebSocketClientManagerLadybird() override; virtual ~WebSocketClientManagerLadybird() override;
virtual RefPtr<Web::WebSockets::WebSocketClientSocket> connect(AK::URL const&, DeprecatedString const& origin) override; virtual RefPtr<Web::WebSockets::WebSocketClientSocket> connect(AK::URL const&, DeprecatedString const& origin, Vector<DeprecatedString> const& protocols) override;
private: private:
WebSocketClientManagerLadybird(); WebSocketClientManagerLadybird();

View file

@ -74,6 +74,11 @@ Web::WebSockets::WebSocket::ReadyState WebSocketLadybird::ready_state()
VERIFY_NOT_REACHED(); VERIFY_NOT_REACHED();
} }
DeprecatedString WebSocketLadybird::subprotocol_in_use()
{
return m_websocket->subprotocol_in_use();
}
void WebSocketLadybird::send(ByteBuffer binary_or_text_message, bool is_text) void WebSocketLadybird::send(ByteBuffer binary_or_text_message, bool is_text)
{ {
m_websocket->send(WebSocket::Message(binary_or_text_message, is_text)); m_websocket->send(WebSocket::Message(binary_or_text_message, is_text));

View file

@ -21,6 +21,7 @@ public:
virtual ~WebSocketLadybird() override; virtual ~WebSocketLadybird() override;
virtual Web::WebSockets::WebSocket::ReadyState ready_state() override; virtual Web::WebSockets::WebSocket::ReadyState ready_state() override;
virtual DeprecatedString subprotocol_in_use() override;
virtual void send(ByteBuffer binary_or_text_message, bool is_text) override; virtual void send(ByteBuffer binary_or_text_message, bool is_text) override;
virtual void send(StringView message) override; virtual void send(StringView message) override;
virtual void close(u16 code, DeprecatedString reason) override; virtual void close(u16 code, DeprecatedString reason) override;

View file

@ -20,6 +20,11 @@ WebSocket::ReadyState WebSocket::ready_state()
return (WebSocket::ReadyState)m_client->ready_state({}, *this); return (WebSocket::ReadyState)m_client->ready_state({}, *this);
} }
DeprecatedString WebSocket::subprotocol_in_use()
{
return m_client->subprotocol_in_use({}, *this);
}
void WebSocket::send(ByteBuffer binary_or_text_message, bool is_text) void WebSocket::send(ByteBuffer binary_or_text_message, bool is_text)
{ {
m_client->send({}, *this, move(binary_or_text_message), is_text); m_client->send({}, *this, move(binary_or_text_message), is_text);

View file

@ -53,6 +53,8 @@ public:
ReadyState ready_state(); ReadyState ready_state();
DeprecatedString subprotocol_in_use();
void send(ByteBuffer binary_or_text_message, bool is_text); void send(ByteBuffer binary_or_text_message, bool is_text);
void send(StringView text_message); void send(StringView text_message);
void close(u16 code = 1005, DeprecatedString reason = {}); void close(u16 code = 1005, DeprecatedString reason = {});

View file

@ -34,6 +34,13 @@ u32 WebSocketClient::ready_state(Badge<WebSocket>, WebSocket& connection)
return IPCProxy::ready_state(connection.id()); return IPCProxy::ready_state(connection.id());
} }
DeprecatedString WebSocketClient::subprotocol_in_use(Badge<WebSocket>, WebSocket& connection)
{
if (!m_connections.contains(connection.id()))
return DeprecatedString::empty();
return IPCProxy::subprotocol_in_use(connection.id());
}
void WebSocketClient::send(Badge<WebSocket>, WebSocket& connection, ByteBuffer data, bool is_text) void WebSocketClient::send(Badge<WebSocket>, WebSocket& connection, ByteBuffer data, bool is_text)
{ {
if (!m_connections.contains(connection.id())) if (!m_connections.contains(connection.id()))

View file

@ -24,6 +24,7 @@ public:
RefPtr<WebSocket> connect(const URL&, DeprecatedString const& origin = {}, Vector<DeprecatedString> const& protocols = {}, Vector<DeprecatedString> const& extensions = {}, HashMap<DeprecatedString, DeprecatedString> const& request_headers = {}); RefPtr<WebSocket> connect(const URL&, DeprecatedString const& origin = {}, Vector<DeprecatedString> const& protocols = {}, Vector<DeprecatedString> const& extensions = {}, HashMap<DeprecatedString, DeprecatedString> const& request_headers = {});
u32 ready_state(Badge<WebSocket>, WebSocket&); u32 ready_state(Badge<WebSocket>, WebSocket&);
DeprecatedString subprotocol_in_use(Badge<WebSocket>, WebSocket&);
void send(Badge<WebSocket>, WebSocket&, ByteBuffer, bool is_text); void send(Badge<WebSocket>, WebSocket&, ByteBuffer, bool is_text);
void close(Badge<WebSocket>, WebSocket&, u16 code, DeprecatedString reason); void close(Badge<WebSocket>, WebSocket&, u16 code, DeprecatedString reason);
bool set_certificate(Badge<WebSocket>, WebSocket&, DeprecatedString, DeprecatedString); bool set_certificate(Badge<WebSocket>, WebSocket&, DeprecatedString, DeprecatedString);

View file

@ -4,6 +4,7 @@
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
#include <AK/QuickSort.h>
#include <LibJS/Runtime/ArrayBuffer.h> #include <LibJS/Runtime/ArrayBuffer.h>
#include <LibJS/Runtime/FunctionObject.h> #include <LibJS/Runtime/FunctionObject.h>
#include <LibWeb/DOM/Document.h> #include <LibWeb/DOM/Document.h>
@ -45,7 +46,7 @@ WebSocketClientSocket::~WebSocketClientSocket() = default;
WebSocketClientManager::WebSocketClientManager() = default; WebSocketClientManager::WebSocketClientManager() = default;
// https://websockets.spec.whatwg.org/#dom-websocket-websocket // https://websockets.spec.whatwg.org/#dom-websocket-websocket
WebIDL::ExceptionOr<JS::NonnullGCPtr<WebSocket>> WebSocket::construct_impl(JS::Realm& realm, DeprecatedString const& url) WebIDL::ExceptionOr<JS::NonnullGCPtr<WebSocket>> WebSocket::construct_impl(JS::Realm& realm, DeprecatedString const& url, Optional<Variant<DeprecatedString, Vector<DeprecatedString>>> const& protocols)
{ {
auto& window = verify_cast<HTML::Window>(realm.global_object()); auto& window = verify_cast<HTML::Window>(realm.global_object());
AK::URL url_record(url); AK::URL url_record(url);
@ -55,18 +56,39 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<WebSocket>> WebSocket::construct_impl(JS::R
return WebIDL::SyntaxError::create(realm, "Invalid protocol"); return WebIDL::SyntaxError::create(realm, "Invalid protocol");
if (!url_record.fragment().is_empty()) if (!url_record.fragment().is_empty())
return WebIDL::SyntaxError::create(realm, "Presence of URL fragment is invalid"); return WebIDL::SyntaxError::create(realm, "Presence of URL fragment is invalid");
// 5. If `protocols` is a string, set `protocols` to a sequence consisting of just that string Vector<DeprecatedString> protocols_sequence;
// 6. If any of the values in `protocols` occur more than once or otherwise fail to match the requirements, throw SyntaxError if (protocols.has_value()) {
return MUST_OR_THROW_OOM(realm.heap().allocate<WebSocket>(realm, window, url_record)); // 5. If `protocols` is a string, set `protocols` to a sequence consisting of just that string
if (protocols.value().has<DeprecatedString>())
protocols_sequence = { protocols.value().get<DeprecatedString>() };
else
protocols_sequence = protocols.value().get<Vector<DeprecatedString>>();
// 6. If any of the values in `protocols` occur more than once or otherwise fail to match the requirements, throw SyntaxError
auto sorted_protocols = protocols_sequence;
quick_sort(sorted_protocols);
for (size_t i = 0; i < sorted_protocols.size(); i++) {
// https://datatracker.ietf.org/doc/html/rfc6455
// The elements that comprise this value MUST be non-empty strings with characters in the range U+0021 to U+007E not including
// separator characters as defined in [RFC2616] and MUST all be unique strings.
auto protocol = sorted_protocols[i];
if (i < sorted_protocols.size() - 1 && protocol == sorted_protocols[i + 1])
return WebIDL::SyntaxError::create(realm, "Found a duplicate protocol name in the specified list");
for (auto character : protocol) {
if (character < '\x21' || character > '\x7E')
return WebIDL::SyntaxError::create(realm, "Found invalid character in subprotocol name");
}
}
}
return MUST_OR_THROW_OOM(realm.heap().allocate<WebSocket>(realm, window, url_record, protocols_sequence));
} }
WebSocket::WebSocket(HTML::Window& window, AK::URL& url) WebSocket::WebSocket(HTML::Window& window, AK::URL& url, Vector<DeprecatedString> const& protocols)
: EventTarget(window.realm()) : EventTarget(window.realm())
, m_window(window) , m_window(window)
{ {
// FIXME: Integrate properly with FETCH as per https://fetch.spec.whatwg.org/#websocket-opening-handshake // FIXME: Integrate properly with FETCH as per https://fetch.spec.whatwg.org/#websocket-opening-handshake
auto origin_string = m_window->associated_document().origin().serialize(); auto origin_string = m_window->associated_document().origin().serialize();
m_websocket = WebSocketClientManager::the().connect(url, origin_string); m_websocket = WebSocketClientManager::the().connect(url, origin_string, protocols);
m_websocket->on_open = [weak_this = make_weak_ptr<WebSocket>()] { m_websocket->on_open = [weak_this = make_weak_ptr<WebSocket>()] {
if (!weak_this) if (!weak_this)
return; return;
@ -132,9 +154,7 @@ DeprecatedString WebSocket::protocol() const
{ {
if (!m_websocket) if (!m_websocket)
return DeprecatedString::empty(); return DeprecatedString::empty();
// https://websockets.spec.whatwg.org/#feedback-from-the-protocol return m_websocket->subprotocol_in_use();
// FIXME: Change the protocol attribute's value to the subprotocol in use, if it is not the null value.
return DeprecatedString::empty();
} }
// https://websockets.spec.whatwg.org/#dom-websocket-close // https://websockets.spec.whatwg.org/#dom-websocket-close

View file

@ -37,7 +37,7 @@ public:
Closed = 3, Closed = 3,
}; };
static WebIDL::ExceptionOr<JS::NonnullGCPtr<WebSocket>> construct_impl(JS::Realm&, DeprecatedString const& url); static WebIDL::ExceptionOr<JS::NonnullGCPtr<WebSocket>> construct_impl(JS::Realm&, DeprecatedString const& url, Optional<Variant<DeprecatedString, Vector<DeprecatedString>>> const& protocols);
virtual ~WebSocket() override; virtual ~WebSocket() override;
@ -66,7 +66,7 @@ private:
void on_error(); void on_error();
void on_close(u16 code, DeprecatedString reason, bool was_clean); void on_close(u16 code, DeprecatedString reason, bool was_clean);
WebSocket(HTML::Window&, AK::URL&); WebSocket(HTML::Window&, AK::URL&, Vector<DeprecatedString> const& protocols);
virtual JS::ThrowCompletionOr<void> initialize(JS::Realm&) override; virtual JS::ThrowCompletionOr<void> initialize(JS::Realm&) override;
virtual void visit_edges(Cell::Visitor&) override; virtual void visit_edges(Cell::Visitor&) override;
@ -99,6 +99,7 @@ public:
}; };
virtual Web::WebSockets::WebSocket::ReadyState ready_state() = 0; virtual Web::WebSockets::WebSocket::ReadyState ready_state() = 0;
virtual DeprecatedString subprotocol_in_use() = 0;
virtual void send(ByteBuffer binary_or_text_message, bool is_text) = 0; virtual void send(ByteBuffer binary_or_text_message, bool is_text) = 0;
virtual void send(StringView text_message) = 0; virtual void send(StringView text_message) = 0;
@ -120,7 +121,7 @@ public:
static void initialize(RefPtr<WebSocketClientManager>); static void initialize(RefPtr<WebSocketClientManager>);
static WebSocketClientManager& the(); static WebSocketClientManager& the();
virtual RefPtr<WebSocketClientSocket> connect(AK::URL const&, DeprecatedString const& origin) = 0; virtual RefPtr<WebSocketClientSocket> connect(AK::URL const&, DeprecatedString const& origin, Vector<DeprecatedString> const& protocols) = 0;
protected: protected:
explicit WebSocketClientManager(); explicit WebSocketClientManager();

View file

@ -5,8 +5,7 @@
[Exposed=(Window,Worker)] [Exposed=(Window,Worker)]
interface WebSocket : EventTarget { interface WebSocket : EventTarget {
// FIXME: A second "protocols" argument should be added once supported constructor(USVString url, optional (DOMString or sequence<DOMString>) protocols);
constructor(USVString url);
readonly attribute USVString url; readonly attribute USVString url;

View file

@ -74,6 +74,11 @@ ReadyState WebSocket::ready_state()
} }
} }
DeprecatedString WebSocket::subprotocol_in_use()
{
return m_subprotocol_in_use;
}
void WebSocket::send(Message const& message) void WebSocket::send(Message const& message)
{ {
// Calling send on a socket that is not opened is not allowed // Calling send on a socket that is not opened is not allowed
@ -356,22 +361,21 @@ void WebSocket::read_server_handshake()
} }
if (header_name.equals_ignoring_case("Sec-WebSocket-Protocol"sv)) { if (header_name.equals_ignoring_case("Sec-WebSocket-Protocol"sv)) {
// 6. |Sec-WebSocket-Protocol| should not contain an extension that doesn't appear in m_connection->protocols() // 6. If the response includes a |Sec-WebSocket-Protocol| header field and this header field indicates the use of a subprotocol that was not present in the client's handshake (the server has indicated a subprotocol not requested by the client), the client MUST _Fail the WebSocket Connection_.
auto server_protocols = parts[1].split(','); // Additionally, Section 4.2.2 says this is "Either a single value representing the subprotocol the server is ready to use or null."
for (auto const& protocol : server_protocols) { auto server_protocol = parts[1].trim_whitespace();
auto trimmed_protocol = protocol.trim_whitespace(); bool found_protocol = false;
bool found_protocol = false; for (auto const& supported_protocol : m_connection.protocols()) {
for (auto const& supported_protocol : m_connection.protocols()) { if (server_protocol.equals_ignoring_case(supported_protocol)) {
if (trimmed_protocol.equals_ignoring_case(supported_protocol)) { found_protocol = true;
found_protocol = true;
}
}
if (!found_protocol) {
dbgln("WebSocket: Server HTTP Handshake Header |Sec-WebSocket-Protocol| contains '{}', which is not supported by the client. Failing connection.", trimmed_protocol);
fatal_error(WebSocket::Error::ConnectionUpgradeFailed);
return;
} }
} }
if (!found_protocol) {
dbgln("WebSocket: Server HTTP Handshake Header |Sec-WebSocket-Protocol| contains '{}', which is not supported by the client. Failing connection.", server_protocol);
fatal_error(WebSocket::Error::ConnectionUpgradeFailed);
return;
}
m_subprotocol_in_use = server_protocol;
continue; continue;
} }
} }

View file

@ -32,6 +32,8 @@ public:
ReadyState ready_state(); ReadyState ready_state();
DeprecatedString subprotocol_in_use();
// Call this to start the WebSocket connection. // Call this to start the WebSocket connection.
void start(); void start();
@ -95,6 +97,8 @@ private:
InternalState m_state { InternalState::NotStarted }; InternalState m_state { InternalState::NotStarted };
DeprecatedString m_subprotocol_in_use { DeprecatedString::empty() };
DeprecatedString m_websocket_key; DeprecatedString m_websocket_key;
bool m_has_read_server_handshake_first_line { false }; bool m_has_read_server_handshake_first_line { false };
bool m_has_read_server_handshake_upgrade { false }; bool m_has_read_server_handshake_upgrade { false };

View file

@ -87,6 +87,11 @@ Web::WebSockets::WebSocket::ReadyState WebSocketClientSocketAdapter::ready_state
VERIFY_NOT_REACHED(); VERIFY_NOT_REACHED();
} }
DeprecatedString WebSocketClientSocketAdapter::subprotocol_in_use()
{
return m_websocket->subprotocol_in_use();
}
void WebSocketClientSocketAdapter::send(ByteBuffer binary_or_text_message, bool is_text) void WebSocketClientSocketAdapter::send(ByteBuffer binary_or_text_message, bool is_text)
{ {
m_websocket->send(binary_or_text_message, is_text); m_websocket->send(binary_or_text_message, is_text);
@ -115,9 +120,9 @@ WebSocketClientManagerAdapter::WebSocketClientManagerAdapter(NonnullRefPtr<Proto
WebSocketClientManagerAdapter::~WebSocketClientManagerAdapter() = default; WebSocketClientManagerAdapter::~WebSocketClientManagerAdapter() = default;
RefPtr<Web::WebSockets::WebSocketClientSocket> WebSocketClientManagerAdapter::connect(const AK::URL& url, DeprecatedString const& origin) RefPtr<Web::WebSockets::WebSocketClientSocket> WebSocketClientManagerAdapter::connect(const AK::URL& url, DeprecatedString const& origin, Vector<DeprecatedString> const& protocols)
{ {
auto underlying_websocket = m_websocket_client->connect(url, origin); auto underlying_websocket = m_websocket_client->connect(url, origin, protocols);
if (!underlying_websocket) if (!underlying_websocket)
return {}; return {};
return WebSocketClientSocketAdapter::create(underlying_websocket.release_nonnull()); return WebSocketClientSocketAdapter::create(underlying_websocket.release_nonnull());

View file

@ -26,6 +26,7 @@ public:
virtual ~WebSocketClientSocketAdapter() override; virtual ~WebSocketClientSocketAdapter() override;
virtual Web::WebSockets::WebSocket::ReadyState ready_state() override; virtual Web::WebSockets::WebSocket::ReadyState ready_state() override;
virtual DeprecatedString subprotocol_in_use() override;
virtual void send(ByteBuffer binary_or_text_message, bool is_text) override; virtual void send(ByteBuffer binary_or_text_message, bool is_text) override;
virtual void send(StringView text_message) override; virtual void send(StringView text_message) override;
@ -43,7 +44,7 @@ public:
virtual ~WebSocketClientManagerAdapter() override; virtual ~WebSocketClientManagerAdapter() override;
virtual RefPtr<Web::WebSockets::WebSocketClientSocket> connect(const AK::URL&, DeprecatedString const& origin) override; virtual RefPtr<Web::WebSockets::WebSocketClientSocket> connect(const AK::URL&, DeprecatedString const& origin, Vector<DeprecatedString> const& protocols) override;
private: private:
WebSocketClientManagerAdapter(NonnullRefPtr<Protocol::WebSocketClient>); WebSocketClientManagerAdapter(NonnullRefPtr<Protocol::WebSocketClient>);

View file

@ -75,6 +75,15 @@ Messages::WebSocketServer::ReadyStateResponse ConnectionFromClient::ready_state(
return (u32)ReadyState::Closed; return (u32)ReadyState::Closed;
} }
Messages::WebSocketServer::SubprotocolInUseResponse ConnectionFromClient::subprotocol_in_use(i32 connection_id)
{
RefPtr<WebSocket> connection = m_connections.get(connection_id).value_or({});
if (connection) {
return connection->subprotocol_in_use();
}
return DeprecatedString::empty();
}
void ConnectionFromClient::send(i32 connection_id, bool is_text, ByteBuffer const& data) void ConnectionFromClient::send(i32 connection_id, bool is_text, ByteBuffer const& data)
{ {
RefPtr<WebSocket> connection = m_connections.get(connection_id).value_or({}); RefPtr<WebSocket> connection = m_connections.get(connection_id).value_or({});

View file

@ -28,6 +28,7 @@ private:
virtual Messages::WebSocketServer::ConnectResponse connect(URL const&, DeprecatedString const&, Vector<DeprecatedString> const&, Vector<DeprecatedString> const&, IPC::Dictionary const&) override; virtual Messages::WebSocketServer::ConnectResponse connect(URL const&, DeprecatedString const&, Vector<DeprecatedString> const&, Vector<DeprecatedString> const&, IPC::Dictionary const&) override;
virtual Messages::WebSocketServer::ReadyStateResponse ready_state(i32) override; virtual Messages::WebSocketServer::ReadyStateResponse ready_state(i32) override;
virtual Messages::WebSocketServer::SubprotocolInUseResponse subprotocol_in_use(i32) override;
virtual void send(i32, bool, ByteBuffer const&) override; virtual void send(i32, bool, ByteBuffer const&) override;
virtual void close(i32, u16, DeprecatedString const&) override; virtual void close(i32, u16, DeprecatedString const&) override;
virtual Messages::WebSocketServer::SetCertificateResponse set_certificate(i32, DeprecatedString const&, DeprecatedString const&) override; virtual Messages::WebSocketServer::SetCertificateResponse set_certificate(i32, DeprecatedString const&, DeprecatedString const&) override;

View file

@ -5,6 +5,7 @@ endpoint WebSocketServer
// Connection API // Connection API
connect(URL url, DeprecatedString origin, Vector<DeprecatedString> protocols, Vector<DeprecatedString> extensions, IPC::Dictionary additional_request_headers) => (i32 connection_id) connect(URL url, DeprecatedString origin, Vector<DeprecatedString> protocols, Vector<DeprecatedString> extensions, IPC::Dictionary additional_request_headers) => (i32 connection_id)
ready_state(i32 connection_id) => (u32 ready_state) ready_state(i32 connection_id) => (u32 ready_state)
subprotocol_in_use(i32 connection_id) => (DeprecatedString subprotocol_in_use)
send(i32 connection_id, bool is_text, ByteBuffer data) =| send(i32 connection_id, bool is_text, ByteBuffer data) =|
close(i32 connection_id, u16 code, DeprecatedString reason) =| close(i32 connection_id, u16 code, DeprecatedString reason) =|

View file

@ -592,6 +592,11 @@ public:
VERIFY_NOT_REACHED(); VERIFY_NOT_REACHED();
} }
virtual DeprecatedString subprotocol_in_use() override
{
return m_websocket->subprotocol_in_use();
}
virtual void send(ByteBuffer binary_or_text_message, bool is_text) override virtual void send(ByteBuffer binary_or_text_message, bool is_text) override
{ {
m_websocket->send(WebSocket::Message(binary_or_text_message, is_text)); m_websocket->send(WebSocket::Message(binary_or_text_message, is_text));
@ -661,10 +666,11 @@ public:
virtual ~HeadlessWebSocketClientManager() override { } virtual ~HeadlessWebSocketClientManager() override { }
virtual RefPtr<Web::WebSockets::WebSocketClientSocket> connect(AK::URL const& url, DeprecatedString const& origin) override virtual RefPtr<Web::WebSockets::WebSocketClientSocket> connect(AK::URL const& url, DeprecatedString const& origin, Vector<DeprecatedString> const& protocols) override
{ {
WebSocket::ConnectionInfo connection_info(url); WebSocket::ConnectionInfo connection_info(url);
connection_info.set_origin(origin); connection_info.set_origin(origin);
connection_info.set_protocols(protocols);
auto connection = HeadlessWebSocket::create(WebSocket::WebSocket::create(move(connection_info))); auto connection = HeadlessWebSocket::create(WebSocket::WebSocket::create(move(connection_info)));
return connection; return connection;