1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-16 10:34:58 +00:00
serenity/Userland/Services/WebSocket/ConnectionFromClient.cpp
Guilherme Gonçalves 230c0b34d4 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.
2023-02-02 14:41:34 +01:00

144 lines
4.6 KiB
C++

/*
* Copyright (c) 2021, Dex♪ <dexes.ttp@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWebSocket/ConnectionInfo.h>
#include <LibWebSocket/Message.h>
#include <WebSocket/ConnectionFromClient.h>
#include <WebSocket/WebSocketClientEndpoint.h>
namespace WebSocket {
static HashMap<int, RefPtr<ConnectionFromClient>> s_connections;
ConnectionFromClient::ConnectionFromClient(NonnullOwnPtr<Core::Stream::LocalSocket> socket)
: IPC::ConnectionFromClient<WebSocketClientEndpoint, WebSocketServerEndpoint>(*this, move(socket), 1)
{
s_connections.set(1, *this);
}
void ConnectionFromClient::die()
{
s_connections.remove(client_id());
if (s_connections.is_empty())
Core::EventLoop::current().quit(0);
}
Messages::WebSocketServer::ConnectResponse ConnectionFromClient::connect(URL const& url, DeprecatedString const& origin,
Vector<DeprecatedString> const& protocols, Vector<DeprecatedString> const& extensions, IPC::Dictionary const& additional_request_headers)
{
if (!url.is_valid()) {
dbgln("WebSocket::Connect: Invalid URL requested: '{}'", url);
return -1;
}
ConnectionInfo connection_info(url);
connection_info.set_origin(origin);
connection_info.set_protocols(protocols);
connection_info.set_extensions(extensions);
Vector<ConnectionInfo::Header> headers;
for (auto const& header : additional_request_headers.entries()) {
headers.append({ header.key, header.value });
}
connection_info.set_headers(headers);
VERIFY(m_connection_ids < NumericLimits<i32>::max());
auto id = ++m_connection_ids;
auto connection = WebSocket::create(move(connection_info));
connection->on_open = [this, id]() {
did_connect(id);
};
connection->on_message = [this, id](auto message) {
did_receive_message(id, move(message));
};
connection->on_error = [this, id](auto message) {
did_error(id, (i32)message);
};
connection->on_close = [this, id](u16 code, DeprecatedString reason, bool was_clean) {
did_close(id, code, move(reason), was_clean);
};
connection->start();
m_connections.set(id, move(connection));
return id;
}
Messages::WebSocketServer::ReadyStateResponse ConnectionFromClient::ready_state(i32 connection_id)
{
RefPtr<WebSocket> connection = m_connections.get(connection_id).value_or({});
if (connection) {
return (u32)connection->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({});
if (connection && connection->ready_state() == ReadyState::Open) {
Message websocket_message(data, is_text);
connection->send(websocket_message);
}
}
void ConnectionFromClient::close(i32 connection_id, u16 code, DeprecatedString const& reason)
{
RefPtr<WebSocket> connection = m_connections.get(connection_id).value_or({});
if (connection && connection->ready_state() == ReadyState::Open)
connection->close(code, reason);
}
Messages::WebSocketServer::SetCertificateResponse ConnectionFromClient::set_certificate(i32 connection_id,
[[maybe_unused]] DeprecatedString const& certificate, [[maybe_unused]] DeprecatedString const& key)
{
RefPtr<WebSocket> connection = m_connections.get(connection_id).value_or({});
bool success = false;
if (connection) {
// NO OP here
// connection->set_certificate(certificate, key);
success = true;
}
return success;
}
void ConnectionFromClient::did_connect(i32 connection_id)
{
async_connected(connection_id);
}
void ConnectionFromClient::did_receive_message(i32 connection_id, Message message)
{
async_received(connection_id, message.is_text(), message.data());
}
void ConnectionFromClient::did_error(i32 connection_id, i32 message)
{
async_errored(connection_id, message);
}
void ConnectionFromClient::did_close(i32 connection_id, u16 code, DeprecatedString reason, bool was_clean)
{
async_closed(connection_id, code, reason, was_clean);
deferred_invoke([this, connection_id] {
m_connections.remove(connection_id);
});
}
void ConnectionFromClient::did_request_certificates(i32 connection_id)
{
async_certificate_requested(connection_id);
}
}