1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-28 01:57:35 +00:00

Services: Add a WebSocket service

The WebSocket service isolates communication with a WebSocket to its
own isolated process. Similar to other isolating services, it has its
own user and group.
This commit is contained in:
DexesTTP 2021-04-24 01:46:49 +02:00 committed by Linus Groh
parent c11ca9df33
commit 62ed26164b
16 changed files with 582 additions and 1 deletions

View file

@ -0,0 +1,12 @@
compile_ipc(WebSocketServer.ipc WebSocketServerEndpoint.h)
compile_ipc(WebSocketClient.ipc WebSocketClientEndpoint.h)
set(SOURCES
ClientConnection.cpp
main.cpp
WebSocketClientEndpoint.h
WebSocketServerEndpoint.h
)
serenity_bin(WebSocket)
target_link_libraries(WebSocket LibCore LibIPC LibWebSocket)

View file

@ -0,0 +1,144 @@
/*
* Copyright (c) 2021, Dex <dexes.ttp@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Badge.h>
#include <LibWebSocket/ConnectionInfo.h>
#include <LibWebSocket/Message.h>
#include <WebSocket/ClientConnection.h>
#include <WebSocket/WebSocketClientEndpoint.h>
namespace WebSocket {
static HashMap<int, RefPtr<ClientConnection>> s_connections;
ClientConnection::ClientConnection(NonnullRefPtr<Core::LocalSocket> socket, int client_id)
: IPC::ClientConnection<WebSocketClientEndpoint, WebSocketServerEndpoint>(*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);
}
OwnPtr<Messages::WebSocketServer::GreetResponse> ClientConnection::handle(const Messages::WebSocketServer::Greet&)
{
return make<Messages::WebSocketServer::GreetResponse>();
}
OwnPtr<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 make<Messages::WebSocketServer::ConnectResponse>(-1);
}
ConnectionInfo connection_info(url);
connection_info.set_origin(message.origin());
connection_info.set_protocols(message.protocols());
connection_info.set_extensions(message.extensions());
Vector<ConnectionInfo::Header> 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<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, String reason, bool was_clean) {
did_close(id, code, move(reason), was_clean);
};
connection->start();
m_connections.set(id, move(connection));
return make<Messages::WebSocketServer::ConnectResponse>(id);
}
OwnPtr<Messages::WebSocketServer::ReadyStateResponse> ClientConnection::handle(const Messages::WebSocketServer::ReadyState& message)
{
RefPtr<WebSocket> connection = m_connections.get(message.connection_id()).value_or({});
if (connection) {
return make<Messages::WebSocketServer::ReadyStateResponse>((u32)connection->ready_state());
}
return make<Messages::WebSocketServer::ReadyStateResponse>((u32)ReadyState::Closed);
}
void ClientConnection::handle(const Messages::WebSocketServer::Send& message)
{
RefPtr<WebSocket> 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<WebSocket> connection = m_connections.get(message.connection_id()).value_or({});
if (connection && connection->ready_state() == ReadyState::Open)
connection->close(message.code(), message.reason());
}
OwnPtr<Messages::WebSocketServer::SetCertificateResponse> ClientConnection::handle(const Messages::WebSocketServer::SetCertificate& message)
{
RefPtr<WebSocket> 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 make<Messages::WebSocketServer::SetCertificateResponse>(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));
}
}

View file

@ -0,0 +1,46 @@
/*
* Copyright (c) 2021, Dex <dexes.ttp@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/HashMap.h>
#include <LibIPC/ClientConnection.h>
#include <LibWebSocket/WebSocket.h>
#include <WebSocket/WebSocketClientEndpoint.h>
#include <WebSocket/WebSocketServerEndpoint.h>
namespace WebSocket {
class ClientConnection final
: public IPC::ClientConnection<WebSocketClientEndpoint, WebSocketServerEndpoint>
, public WebSocketServerEndpoint {
C_OBJECT(ClientConnection);
public:
explicit ClientConnection(NonnullRefPtr<Core::LocalSocket>, int client_id);
~ClientConnection() override;
virtual void die() override;
private:
virtual OwnPtr<Messages::WebSocketServer::GreetResponse> handle(const Messages::WebSocketServer::Greet&) override;
virtual OwnPtr<Messages::WebSocketServer::ConnectResponse> handle(const Messages::WebSocketServer::Connect&) override;
virtual OwnPtr<Messages::WebSocketServer::ReadyStateResponse> handle(const Messages::WebSocketServer::ReadyState&) override;
virtual void handle(const Messages::WebSocketServer::Send&) override;
virtual void handle(const Messages::WebSocketServer::Close&) override;
virtual OwnPtr<Messages::WebSocketServer::SetCertificateResponse> handle(const Messages::WebSocketServer::SetCertificate&) override;
void did_connect(i32);
void did_receive_message(i32, Message);
void did_error(i32, i32 message);
void did_close(i32, u16 code, String reason, bool was_clean);
void did_request_certificates(i32);
i32 m_connection_ids { 0 };
HashMap<i32, RefPtr<WebSocket>> m_connections;
};
}

View file

@ -0,0 +1,11 @@
endpoint WebSocketClient
{
// Connection API
Connected(i32 connection_id) =|
Received(i32 connection_id, bool is_text, ByteBuffer data) =|
Errored(i32 connection_id, i32 message) =|
Closed(i32 connection_id, u16 code, String reason, bool clean) =|
// Certificate requests
CertificateRequested(i32 connection_id) =|
}

View file

@ -0,0 +1,13 @@
endpoint WebSocketServer
{
// Basic protocol
Greet() => ()
// Connection API
Connect(URL url, String origin, Vector<String> protocols, Vector<String> extensions, IPC::Dictionary additional_request_headers) => (i32 connection_id)
ReadyState(i32 connection_id) => (u32 ready_state)
Send(i32 connection_id, bool is_text, ByteBuffer data) =|
Close(i32 connection_id, u16 code, String reason) =|
SetCertificate(i32 connection_id, String certificate, String key) => (bool success)
}

View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 2021, Dex <dexes.ttp@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibCore/EventLoop.h>
#include <LibCore/LocalServer.h>
#include <LibIPC/ClientConnection.h>
#include <LibTLS/Certificate.h>
#include <WebSocket/ClientConnection.h>
int main(int, char**)
{
if (pledge("stdio inet accept unix rpath cpath fattr sendfd recvfd", nullptr) < 0) {
perror("pledge");
return 1;
}
// Ensure the certificates are read out here.
[[maybe_unused]] auto& certs = DefaultRootCACertificates::the();
Core::EventLoop event_loop;
// FIXME: Establish a connection to LookupServer and then drop "unix"?
if (pledge("stdio inet accept unix sendfd recvfd", nullptr) < 0) {
perror("pledge");
return 1;
}
if (unveil("/tmp/portal/lookup", "rw") < 0) {
perror("unveil");
return 1;
}
if (unveil(nullptr, nullptr) < 0) {
perror("unveil");
return 1;
}
auto socket = Core::LocalSocket::take_over_accepted_socket_from_system_server();
VERIFY(socket);
IPC::new_client_connection<WebSocket::ClientConnection>(socket.release_nonnull(), 1);
return event_loop.exec();
}