diff options
author | Guilherme Gonçalves <ggoncalves@google.com> | 2022-12-31 10:37:10 +0000 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2023-02-02 14:41:34 +0100 |
commit | 230c0b34d4d87f4f7dc240d1e2aa236dd7b31399 (patch) | |
tree | f8b96ce4b0fe04aa8ad781730fed18bdbbf4a28a | |
parent | 9115e99e4b6e7f7f3624f4a12160d9859c30bbd3 (diff) | |
download | serenity-230c0b34d4d87f4f7dc240d1e2aa236dd7b31399.zip |
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.
19 files changed, 107 insertions, 34 deletions
diff --git a/Ladybird/WebSocketClientManagerLadybird.cpp b/Ladybird/WebSocketClientManagerLadybird.cpp index 2cbbd1413b..79edc4e2cd 100644 --- a/Ladybird/WebSocketClientManagerLadybird.cpp +++ b/Ladybird/WebSocketClientManagerLadybird.cpp @@ -19,10 +19,11 @@ NonnullRefPtr<WebSocketClientManagerLadybird> WebSocketClientManagerLadybird::cr 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); connection_info.set_origin(origin); + connection_info.set_protocols(protocols); auto impl = adopt_ref(*new WebSocketImplQt); auto web_socket = WebSocket::WebSocket::create(move(connection_info), move(impl)); diff --git a/Ladybird/WebSocketClientManagerLadybird.h b/Ladybird/WebSocketClientManagerLadybird.h index cad676bdab..a0831d28b0 100644 --- a/Ladybird/WebSocketClientManagerLadybird.h +++ b/Ladybird/WebSocketClientManagerLadybird.h @@ -19,7 +19,7 @@ public: static NonnullRefPtr<WebSocketClientManagerLadybird> create(); 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: WebSocketClientManagerLadybird(); diff --git a/Ladybird/WebSocketLadybird.cpp b/Ladybird/WebSocketLadybird.cpp index afadf2b463..e11930def6 100644 --- a/Ladybird/WebSocketLadybird.cpp +++ b/Ladybird/WebSocketLadybird.cpp @@ -74,6 +74,11 @@ Web::WebSockets::WebSocket::ReadyState WebSocketLadybird::ready_state() 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) { m_websocket->send(WebSocket::Message(binary_or_text_message, is_text)); diff --git a/Ladybird/WebSocketLadybird.h b/Ladybird/WebSocketLadybird.h index 5e7eee75f0..170d64b3e4 100644 --- a/Ladybird/WebSocketLadybird.h +++ b/Ladybird/WebSocketLadybird.h @@ -21,6 +21,7 @@ public: virtual ~WebSocketLadybird() 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(StringView message) override; virtual void close(u16 code, DeprecatedString reason) override; diff --git a/Userland/Libraries/LibProtocol/WebSocket.cpp b/Userland/Libraries/LibProtocol/WebSocket.cpp index fd85b64add..7d5b3cbe65 100644 --- a/Userland/Libraries/LibProtocol/WebSocket.cpp +++ b/Userland/Libraries/LibProtocol/WebSocket.cpp @@ -20,6 +20,11 @@ WebSocket::ReadyState WebSocket::ready_state() 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) { m_client->send({}, *this, move(binary_or_text_message), is_text); diff --git a/Userland/Libraries/LibProtocol/WebSocket.h b/Userland/Libraries/LibProtocol/WebSocket.h index d323a9edcf..eb7d859cba 100644 --- a/Userland/Libraries/LibProtocol/WebSocket.h +++ b/Userland/Libraries/LibProtocol/WebSocket.h @@ -53,6 +53,8 @@ public: ReadyState ready_state(); + DeprecatedString subprotocol_in_use(); + void send(ByteBuffer binary_or_text_message, bool is_text); void send(StringView text_message); void close(u16 code = 1005, DeprecatedString reason = {}); diff --git a/Userland/Libraries/LibProtocol/WebSocketClient.cpp b/Userland/Libraries/LibProtocol/WebSocketClient.cpp index b96d04951b..03e65268a3 100644 --- a/Userland/Libraries/LibProtocol/WebSocketClient.cpp +++ b/Userland/Libraries/LibProtocol/WebSocketClient.cpp @@ -34,6 +34,13 @@ u32 WebSocketClient::ready_state(Badge<WebSocket>, WebSocket& connection) 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) { if (!m_connections.contains(connection.id())) diff --git a/Userland/Libraries/LibProtocol/WebSocketClient.h b/Userland/Libraries/LibProtocol/WebSocketClient.h index 60281c93c0..85d026ea6e 100644 --- a/Userland/Libraries/LibProtocol/WebSocketClient.h +++ b/Userland/Libraries/LibProtocol/WebSocketClient.h @@ -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 = {}); u32 ready_state(Badge<WebSocket>, WebSocket&); + DeprecatedString subprotocol_in_use(Badge<WebSocket>, WebSocket&); void send(Badge<WebSocket>, WebSocket&, ByteBuffer, bool is_text); void close(Badge<WebSocket>, WebSocket&, u16 code, DeprecatedString reason); bool set_certificate(Badge<WebSocket>, WebSocket&, DeprecatedString, DeprecatedString); diff --git a/Userland/Libraries/LibWeb/WebSockets/WebSocket.cpp b/Userland/Libraries/LibWeb/WebSockets/WebSocket.cpp index 84fd063c28..e0fa3f6ad9 100644 --- a/Userland/Libraries/LibWeb/WebSockets/WebSocket.cpp +++ b/Userland/Libraries/LibWeb/WebSockets/WebSocket.cpp @@ -4,6 +4,7 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include <AK/QuickSort.h> #include <LibJS/Runtime/ArrayBuffer.h> #include <LibJS/Runtime/FunctionObject.h> #include <LibWeb/DOM/Document.h> @@ -45,7 +46,7 @@ WebSocketClientSocket::~WebSocketClientSocket() = default; WebSocketClientManager::WebSocketClientManager() = default; // 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()); 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"); if (!url_record.fragment().is_empty()) 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 - // 6. If any of the values in `protocols` occur more than once or otherwise fail to match the requirements, throw SyntaxError - return MUST_OR_THROW_OOM(realm.heap().allocate<WebSocket>(realm, window, url_record)); + Vector<DeprecatedString> protocols_sequence; + if (protocols.has_value()) { + // 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()) , m_window(window) { // FIXME: Integrate properly with FETCH as per https://fetch.spec.whatwg.org/#websocket-opening-handshake 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>()] { if (!weak_this) return; @@ -132,9 +154,7 @@ DeprecatedString WebSocket::protocol() const { if (!m_websocket) return DeprecatedString::empty(); - // https://websockets.spec.whatwg.org/#feedback-from-the-protocol - // FIXME: Change the protocol attribute's value to the subprotocol in use, if it is not the null value. - return DeprecatedString::empty(); + return m_websocket->subprotocol_in_use(); } // https://websockets.spec.whatwg.org/#dom-websocket-close diff --git a/Userland/Libraries/LibWeb/WebSockets/WebSocket.h b/Userland/Libraries/LibWeb/WebSockets/WebSocket.h index 96b6fb55ea..40b3d185a4 100644 --- a/Userland/Libraries/LibWeb/WebSockets/WebSocket.h +++ b/Userland/Libraries/LibWeb/WebSockets/WebSocket.h @@ -37,7 +37,7 @@ public: 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; @@ -66,7 +66,7 @@ private: void on_error(); 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 void visit_edges(Cell::Visitor&) override; @@ -99,6 +99,7 @@ public: }; 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(StringView text_message) = 0; @@ -120,7 +121,7 @@ public: static void initialize(RefPtr<WebSocketClientManager>); 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: explicit WebSocketClientManager(); diff --git a/Userland/Libraries/LibWeb/WebSockets/WebSocket.idl b/Userland/Libraries/LibWeb/WebSockets/WebSocket.idl index 7df127d950..c857e07763 100644 --- a/Userland/Libraries/LibWeb/WebSockets/WebSocket.idl +++ b/Userland/Libraries/LibWeb/WebSockets/WebSocket.idl @@ -5,8 +5,7 @@ [Exposed=(Window,Worker)] interface WebSocket : EventTarget { - // FIXME: A second "protocols" argument should be added once supported - constructor(USVString url); + constructor(USVString url, optional (DOMString or sequence<DOMString>) protocols); readonly attribute USVString url; diff --git a/Userland/Libraries/LibWebSocket/WebSocket.cpp b/Userland/Libraries/LibWebSocket/WebSocket.cpp index f3072c7aa7..35ceb54f6a 100644 --- a/Userland/Libraries/LibWebSocket/WebSocket.cpp +++ b/Userland/Libraries/LibWebSocket/WebSocket.cpp @@ -74,6 +74,11 @@ ReadyState WebSocket::ready_state() } } +DeprecatedString WebSocket::subprotocol_in_use() +{ + return m_subprotocol_in_use; +} + void WebSocket::send(Message const& message) { // 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)) { - // 6. |Sec-WebSocket-Protocol| should not contain an extension that doesn't appear in m_connection->protocols() - auto server_protocols = parts[1].split(','); - for (auto const& protocol : server_protocols) { - auto trimmed_protocol = protocol.trim_whitespace(); - bool found_protocol = false; - for (auto const& supported_protocol : m_connection.protocols()) { - if (trimmed_protocol.equals_ignoring_case(supported_protocol)) { - 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; + // 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_. + // Additionally, Section 4.2.2 says this is "Either a single value representing the subprotocol the server is ready to use or null." + auto server_protocol = parts[1].trim_whitespace(); + bool found_protocol = false; + for (auto const& supported_protocol : m_connection.protocols()) { + if (server_protocol.equals_ignoring_case(supported_protocol)) { + 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.", server_protocol); + fatal_error(WebSocket::Error::ConnectionUpgradeFailed); + return; + } + m_subprotocol_in_use = server_protocol; continue; } } diff --git a/Userland/Libraries/LibWebSocket/WebSocket.h b/Userland/Libraries/LibWebSocket/WebSocket.h index a47ee24b04..7d1ad27c26 100644 --- a/Userland/Libraries/LibWebSocket/WebSocket.h +++ b/Userland/Libraries/LibWebSocket/WebSocket.h @@ -32,6 +32,8 @@ public: ReadyState ready_state(); + DeprecatedString subprotocol_in_use(); + // Call this to start the WebSocket connection. void start(); @@ -95,6 +97,8 @@ private: InternalState m_state { InternalState::NotStarted }; + DeprecatedString m_subprotocol_in_use { DeprecatedString::empty() }; + DeprecatedString m_websocket_key; bool m_has_read_server_handshake_first_line { false }; bool m_has_read_server_handshake_upgrade { false }; diff --git a/Userland/Libraries/LibWebView/WebSocketClientAdapter.cpp b/Userland/Libraries/LibWebView/WebSocketClientAdapter.cpp index f9ae63e145..763049d9f5 100644 --- a/Userland/Libraries/LibWebView/WebSocketClientAdapter.cpp +++ b/Userland/Libraries/LibWebView/WebSocketClientAdapter.cpp @@ -87,6 +87,11 @@ Web::WebSockets::WebSocket::ReadyState WebSocketClientSocketAdapter::ready_state 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) { m_websocket->send(binary_or_text_message, is_text); @@ -115,9 +120,9 @@ WebSocketClientManagerAdapter::WebSocketClientManagerAdapter(NonnullRefPtr<Proto 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) return {}; return WebSocketClientSocketAdapter::create(underlying_websocket.release_nonnull()); diff --git a/Userland/Libraries/LibWebView/WebSocketClientAdapter.h b/Userland/Libraries/LibWebView/WebSocketClientAdapter.h index 35095331b4..2a1004d71b 100644 --- a/Userland/Libraries/LibWebView/WebSocketClientAdapter.h +++ b/Userland/Libraries/LibWebView/WebSocketClientAdapter.h @@ -26,6 +26,7 @@ public: virtual ~WebSocketClientSocketAdapter() 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(StringView text_message) override; @@ -43,7 +44,7 @@ public: 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: WebSocketClientManagerAdapter(NonnullRefPtr<Protocol::WebSocketClient>); diff --git a/Userland/Services/WebSocket/ConnectionFromClient.cpp b/Userland/Services/WebSocket/ConnectionFromClient.cpp index 032df8f0a1..1a3ed84541 100644 --- a/Userland/Services/WebSocket/ConnectionFromClient.cpp +++ b/Userland/Services/WebSocket/ConnectionFromClient.cpp @@ -75,6 +75,15 @@ Messages::WebSocketServer::ReadyStateResponse ConnectionFromClient::ready_state( 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) { RefPtr<WebSocket> connection = m_connections.get(connection_id).value_or({}); diff --git a/Userland/Services/WebSocket/ConnectionFromClient.h b/Userland/Services/WebSocket/ConnectionFromClient.h index d5bab0fb9a..aec8185b87 100644 --- a/Userland/Services/WebSocket/ConnectionFromClient.h +++ b/Userland/Services/WebSocket/ConnectionFromClient.h @@ -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::ReadyStateResponse ready_state(i32) override; + virtual Messages::WebSocketServer::SubprotocolInUseResponse subprotocol_in_use(i32) override; virtual void send(i32, bool, ByteBuffer const&) override; virtual void close(i32, u16, DeprecatedString const&) override; virtual Messages::WebSocketServer::SetCertificateResponse set_certificate(i32, DeprecatedString const&, DeprecatedString const&) override; diff --git a/Userland/Services/WebSocket/WebSocketServer.ipc b/Userland/Services/WebSocket/WebSocketServer.ipc index d0dfcf6b25..5b5bc7aa8b 100644 --- a/Userland/Services/WebSocket/WebSocketServer.ipc +++ b/Userland/Services/WebSocket/WebSocketServer.ipc @@ -5,6 +5,7 @@ endpoint WebSocketServer // Connection API 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) + subprotocol_in_use(i32 connection_id) => (DeprecatedString subprotocol_in_use) send(i32 connection_id, bool is_text, ByteBuffer data) =| close(i32 connection_id, u16 code, DeprecatedString reason) =| diff --git a/Userland/Utilities/headless-browser.cpp b/Userland/Utilities/headless-browser.cpp index 551e921d3d..2e339890e3 100644 --- a/Userland/Utilities/headless-browser.cpp +++ b/Userland/Utilities/headless-browser.cpp @@ -592,6 +592,11 @@ public: 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 { m_websocket->send(WebSocket::Message(binary_or_text_message, is_text)); @@ -661,10 +666,11 @@ public: 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); connection_info.set_origin(origin); + connection_info.set_protocols(protocols); auto connection = HeadlessWebSocket::create(WebSocket::WebSocket::create(move(connection_info))); return connection; |