diff --git a/Userland/Services/RequestServer/ConnectionFromClient.cpp b/Userland/Services/RequestServer/ConnectionFromClient.cpp index 0bc2c26ed0f..4ee6e093fc9 100644 --- a/Userland/Services/RequestServer/ConnectionFromClient.cpp +++ b/Userland/Services/RequestServer/ConnectionFromClient.cpp @@ -5,6 +5,7 @@ */ #include +#include #include #include #include @@ -21,20 +22,65 @@ namespace RequestServer { static HashMap> s_connections; +static IDAllocator s_client_ids; ConnectionFromClient::ConnectionFromClient(NonnullOwnPtr socket) - : IPC::ConnectionFromClient(*this, move(socket), 1) + : IPC::ConnectionFromClient(*this, move(socket), s_client_ids.allocate()) { - s_connections.set(1, *this); + s_connections.set(client_id(), *this); } void ConnectionFromClient::die() { - s_connections.remove(client_id()); + auto client_id = this->client_id(); + s_connections.remove(client_id); + s_client_ids.deallocate(client_id); + if (s_connections.is_empty()) Core::EventLoop::current().quit(0); } +Messages::RequestServer::ConnectNewClientResponse ConnectionFromClient::connect_new_client() +{ + int socket_fds[2] {}; + if (auto err = Core::System::socketpair(AF_LOCAL, SOCK_STREAM, 0, socket_fds); err.is_error()) { + dbgln("Failed to create client socketpair: {}", err.error()); + return { -1, -1 }; + } + + auto client_socket_or_error = Core::LocalSocket::adopt_fd(socket_fds[0]); + if (client_socket_or_error.is_error()) { + close(socket_fds[0]); + close(socket_fds[1]); + dbgln("Failed to adopt client socket: {}", client_socket_or_error.error()); + return { -1, -1 }; + } + auto client_socket = client_socket_or_error.release_value(); + // Note: A ref is stored in the static s_connections map + auto client = adopt_ref(*new ConnectionFromClient(move(client_socket))); + + int fd_passing_socket_fds[2] {}; + if (auto err = Core::System::socketpair(AF_LOCAL, SOCK_STREAM, 0, fd_passing_socket_fds); err.is_error()) { + close(socket_fds[1]); + dbgln("Failed to create fd-passing socketpair: {}", err.error()); + return { -1, -1 }; + } + + auto fd_passing_socket_or_error = Core::LocalSocket::adopt_fd(fd_passing_socket_fds[0]); + if (fd_passing_socket_or_error.is_error()) { + // socket_fds[0] is already owned by client + close(socket_fds[1]); + close(fd_passing_socket_fds[0]); + close(fd_passing_socket_fds[1]); + dbgln("Failed to adopt fd-passing socket: {}", fd_passing_socket_or_error.error()); + return { -1, -1 }; + } + auto fd_passing_socket = fd_passing_socket_or_error.release_value(); + client->set_fd_passing_socket(move(fd_passing_socket)); + + return { IPC::File(socket_fds[1], IPC::File::CloseAfterSending), IPC::File(fd_passing_socket_fds[1], IPC::File::CloseAfterSending) }; +} + Messages::RequestServer::IsSupportedProtocolResponse ConnectionFromClient::is_supported_protocol(ByteString const& protocol) { bool supported = Protocol::find_by_name(protocol.to_lowercase()); diff --git a/Userland/Services/RequestServer/ConnectionFromClient.h b/Userland/Services/RequestServer/ConnectionFromClient.h index c34ce44b2d0..709d5b3b6e3 100644 --- a/Userland/Services/RequestServer/ConnectionFromClient.h +++ b/Userland/Services/RequestServer/ConnectionFromClient.h @@ -32,6 +32,7 @@ public: private: explicit ConnectionFromClient(NonnullOwnPtr); + virtual Messages::RequestServer::ConnectNewClientResponse connect_new_client() override; virtual Messages::RequestServer::IsSupportedProtocolResponse is_supported_protocol(ByteString const&) override; virtual void start_request(i32 request_id, ByteString const&, URL::URL const&, HashMap const&, ByteBuffer const&, Core::ProxyData const&) override; virtual Messages::RequestServer::StopRequestResponse stop_request(i32) override; diff --git a/Userland/Services/RequestServer/RequestServer.ipc b/Userland/Services/RequestServer/RequestServer.ipc index 5ba06e6f30c..35d0fb97c4a 100644 --- a/Userland/Services/RequestServer/RequestServer.ipc +++ b/Userland/Services/RequestServer/RequestServer.ipc @@ -3,6 +3,8 @@ endpoint RequestServer { + connect_new_client() => (IPC::File client_socket, IPC::File client_fd_passing_socket) + // Test if a specific protocol is supported, e.g "http" is_supported_protocol(ByteString protocol) => (bool supported)