/* * Copyright (c) 2021, Dex♪ * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include namespace WebSocket { static HashMap> s_connections; ClientConnection::ClientConnection(NonnullRefPtr socket, int client_id) : IPC::ClientConnection(*this, move(socket), client_id) { s_connections.set(client_id, *this); } ClientConnection::~ClientConnection() { } void ClientConnection::die() { s_connections.remove(client_id()); if (s_connections.is_empty()) Core::EventLoop::current().quit(0); } void ClientConnection::handle(const Messages::WebSocketServer::Greet&) { } Messages::WebSocketServer::ConnectResponse ClientConnection::handle(const Messages::WebSocketServer::Connect& message) { const auto& url = message.url(); if (!url.is_valid()) { dbgln("WebSocket::Connect: Invalid URL requested: '{}'", url); return -1; } ConnectionInfo connection_info(url); connection_info.set_origin(message.origin()); connection_info.set_protocols(message.protocols()); connection_info.set_extensions(message.extensions()); Vector headers; for (const auto& header : message.additional_request_headers().entries()) { headers.append({ header.key, header.value }); } connection_info.set_headers(headers); VERIFY(m_connection_ids < NumericLimits::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, String 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 ClientConnection::handle(const Messages::WebSocketServer::ReadyState& message) { RefPtr connection = m_connections.get(message.connection_id()).value_or({}); if (connection) { return (u32)connection->ready_state(); } return (u32)ReadyState::Closed; } void ClientConnection::handle(const Messages::WebSocketServer::Send& message) { RefPtr connection = m_connections.get(message.connection_id()).value_or({}); if (connection && connection->ready_state() == ReadyState::Open) { Message websocket_message(message.data(), message.is_text()); connection->send(websocket_message); } } void ClientConnection::handle(const Messages::WebSocketServer::Close& message) { RefPtr connection = m_connections.get(message.connection_id()).value_or({}); if (connection && connection->ready_state() == ReadyState::Open) connection->close(message.code(), message.reason()); } Messages::WebSocketServer::SetCertificateResponse ClientConnection::handle(const Messages::WebSocketServer::SetCertificate& message) { RefPtr connection = m_connections.get(message.connection_id()).value_or({}); bool success = false; if (connection) { // NO OP here // connection->set_certificate(message.certificate(), message.key()); success = true; } return success; } void ClientConnection::did_connect(i32 connection_id) { post_message(Messages::WebSocketClient::Connected(connection_id)); } void ClientConnection::did_receive_message(i32 connection_id, Message message) { post_message(Messages::WebSocketClient::Received(connection_id, message.is_text(), message.data())); } void ClientConnection::did_error(i32 connection_id, i32 message) { post_message(Messages::WebSocketClient::Errored(connection_id, message)); } void ClientConnection::did_close(i32 connection_id, u16 code, String reason, bool was_clean) { post_message(Messages::WebSocketClient::Closed(connection_id, code, reason, was_clean)); deferred_invoke([this, connection_id] { m_connections.remove(connection_id); }); } void ClientConnection::did_request_certificates(i32 connection_id) { post_message(Messages::WebSocketClient::CertificateRequested(connection_id)); } }