mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 00:37:45 +00:00
LibIPC+AudioServer: Allow unsolicited server-to-client IPC messages
Client-side connection objects must now provide both client and server endpoint types. When a message is received from the server side, we try to decode it using both endpoint types and then send it to the right place for handling. This now makes it possible for AudioServer to send unsolicited messages to its clients. This opens up a ton of possibilities :^)
This commit is contained in:
parent
06ee24263c
commit
630d5b3ffd
13 changed files with 95 additions and 42 deletions
|
@ -3,7 +3,7 @@
|
|||
#include <SharedBuffer.h>
|
||||
|
||||
AClientConnection::AClientConnection()
|
||||
: ConnectionNG("/tmp/asportal")
|
||||
: ConnectionNG(*this, "/tmp/asportal")
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -76,3 +76,7 @@ int AClientConnection::get_playing_buffer()
|
|||
{
|
||||
return send_sync<AudioServer::GetPlayingBuffer>()->buffer_id();
|
||||
}
|
||||
|
||||
void AClientConnection::handle(const AudioClient::FinishedPlayingBuffer&)
|
||||
{
|
||||
}
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include <AudioServer/AudioClientEndpoint.h>
|
||||
#include <AudioServer/AudioServerEndpoint.h>
|
||||
#include <LibCore/CoreIPCClient.h>
|
||||
|
||||
class ABuffer;
|
||||
|
||||
class AClientConnection : public IPC::Client::ConnectionNG<AudioServerEndpoint> {
|
||||
class AClientConnection : public IPC::Client::ConnectionNG<AudioClientEndpoint, AudioServerEndpoint>
|
||||
, public AudioClientEndpoint {
|
||||
C_OBJECT(AClientConnection)
|
||||
public:
|
||||
AClientConnection();
|
||||
|
@ -26,4 +28,7 @@ public:
|
|||
|
||||
void set_paused(bool paused);
|
||||
void clear_buffer(bool paused = false);
|
||||
|
||||
private:
|
||||
virtual void handle(const AudioClient::FinishedPlayingBuffer&) override;
|
||||
};
|
||||
|
|
|
@ -246,11 +246,12 @@ namespace Client {
|
|||
int m_my_client_id { -1 };
|
||||
};
|
||||
|
||||
template<typename Endpoint>
|
||||
template<typename LocalEndpoint, typename PeerEndpoint>
|
||||
class ConnectionNG : public CObject {
|
||||
public:
|
||||
ConnectionNG(const StringView& address)
|
||||
: m_connection(CLocalSocket::construct(this))
|
||||
ConnectionNG(LocalEndpoint& local_endpoint, const StringView& address)
|
||||
: m_local_endpoint(local_endpoint)
|
||||
, m_connection(CLocalSocket::construct(this))
|
||||
, m_notifier(CNotifier::construct(m_connection->fd(), CNotifier::Read, this))
|
||||
{
|
||||
// We want to rate-limit our clients
|
||||
|
@ -312,8 +313,7 @@ namespace Client {
|
|||
}
|
||||
ASSERT(rc > 0);
|
||||
ASSERT(FD_ISSET(m_connection->fd(), &rfds));
|
||||
bool success = drain_messages_from_server();
|
||||
if (!success)
|
||||
if (!drain_messages_from_server())
|
||||
return nullptr;
|
||||
for (ssize_t i = 0; i < m_unprocessed_messages.size(); ++i) {
|
||||
if (m_unprocessed_messages[i]->id() == MessageType::static_message_id()) {
|
||||
|
@ -358,30 +358,42 @@ namespace Client {
|
|||
private:
|
||||
bool drain_messages_from_server()
|
||||
{
|
||||
Vector<u8> bytes;
|
||||
for (;;) {
|
||||
u8 buffer[4096];
|
||||
ssize_t nread = recv(m_connection->fd(), buffer, sizeof(buffer), MSG_DONTWAIT);
|
||||
if (nread < 0) {
|
||||
if (errno == EAGAIN) {
|
||||
return true;
|
||||
}
|
||||
if (errno == EAGAIN)
|
||||
break;
|
||||
perror("read");
|
||||
exit(1);
|
||||
return false;
|
||||
}
|
||||
if (nread == 0) {
|
||||
dbg() << "EOF on IPC fd";
|
||||
// FIXME: Dying is definitely not always appropriate!
|
||||
exit(1);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto message = Endpoint::decode_message(ByteBuffer::wrap(buffer, sizeof(buffer)));
|
||||
ASSERT(message);
|
||||
|
||||
m_unprocessed_messages.append(move(message));
|
||||
bytes.append(buffer, nread);
|
||||
}
|
||||
|
||||
size_t decoded_bytes = 0;
|
||||
for (size_t index = 0; index < (size_t)bytes.size(); index += decoded_bytes) {
|
||||
auto remaining_bytes = ByteBuffer::wrap(bytes.data() + index, bytes.size() - index);
|
||||
if (auto message = LocalEndpoint::decode_message(remaining_bytes, decoded_bytes)) {
|
||||
m_local_endpoint.handle(*message);
|
||||
} else if (auto message = PeerEndpoint::decode_message(remaining_bytes, decoded_bytes)) {
|
||||
m_unprocessed_messages.append(move(message));
|
||||
} else {
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
ASSERT(decoded_bytes);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
LocalEndpoint& m_local_endpoint;
|
||||
RefPtr<CLocalSocket> m_connection;
|
||||
RefPtr<CNotifier> m_notifier;
|
||||
Vector<OwnPtr<IMessage>> m_unprocessed_messages;
|
||||
|
|
|
@ -256,7 +256,7 @@ namespace Server {
|
|||
, m_client_id(client_id)
|
||||
{
|
||||
add_child(socket);
|
||||
m_socket->on_ready_to_read = [this] { drain_client(); };
|
||||
m_socket->on_ready_to_read = [this] { drain_messages_from_client(); };
|
||||
}
|
||||
|
||||
virtual ~ConnectionNG() override
|
||||
|
@ -287,15 +287,16 @@ namespace Server {
|
|||
ASSERT(nwritten == buffer.size());
|
||||
}
|
||||
|
||||
void drain_client()
|
||||
void drain_messages_from_client()
|
||||
{
|
||||
unsigned messages_received = 0;
|
||||
Vector<u8> bytes;
|
||||
for (;;) {
|
||||
u8 buffer[4096];
|
||||
ssize_t nread = recv(m_socket->fd(), buffer, sizeof(buffer), MSG_DONTWAIT);
|
||||
if (nread == 0 || (nread == -1 && errno == EAGAIN)) {
|
||||
if (!messages_received) {
|
||||
if (bytes.is_empty()) {
|
||||
CEventLoop::current().post_event(*this, make<DisconnectedEvent>(client_id()));
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -303,17 +304,21 @@ namespace Server {
|
|||
perror("recv");
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
auto message = m_endpoint.decode_message(ByteBuffer::wrap(buffer, nread));
|
||||
bytes.append(buffer, nread);
|
||||
}
|
||||
|
||||
size_t decoded_bytes = 0;
|
||||
for (size_t index = 0; index < (size_t)bytes.size(); index += decoded_bytes) {
|
||||
auto remaining_bytes = ByteBuffer::wrap(bytes.data() + index, bytes.size() - index);
|
||||
auto message = Endpoint::decode_message(remaining_bytes, decoded_bytes);
|
||||
if (!message) {
|
||||
dbg() << "drain_client: Endpoint didn't recognize message";
|
||||
dbg() << "drain_messages_from_client: Endpoint didn't recognize message";
|
||||
did_misbehave();
|
||||
return;
|
||||
}
|
||||
++messages_received;
|
||||
|
||||
auto response = m_endpoint.handle(*message);
|
||||
if (response)
|
||||
if (auto response = m_endpoint.handle(*message))
|
||||
post_message(*response);
|
||||
ASSERT(decoded_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ class IEndpoint {
|
|||
public:
|
||||
virtual ~IEndpoint();
|
||||
|
||||
virtual int magic() const = 0;
|
||||
virtual String name() const = 0;
|
||||
virtual OwnPtr<IMessage> handle(const IMessage&) = 0;
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ class IMessage {
|
|||
public:
|
||||
virtual ~IMessage();
|
||||
|
||||
virtual int endpoint_magic() const = 0;
|
||||
virtual int id() const = 0;
|
||||
virtual String name() const = 0;
|
||||
virtual ByteBuffer encode() const = 0;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue