diff --git a/Userland/Services/SpiceAgent/ChunkHeader.h b/Userland/Services/SpiceAgent/ChunkHeader.h new file mode 100644 index 0000000000..bcf9c21e67 --- /dev/null +++ b/Userland/Services/SpiceAgent/ChunkHeader.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2023, Caoimhe Byrne + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include + +namespace SpiceAgent { + +class [[gnu::packed]] ChunkHeader { +public: + // Indicates where the message has come from + enum class Port : u32 { + Client = 1, + + // There are currently no messages which are meant for the server, so all messages sent by the agent (us) with this port are discarded. + Server + }; + + ChunkHeader(Port port, u32 size) + : m_port(port) + , m_size(size) + { + } + + Port port() const { return m_port; } + u32 size() const { return m_size; } + +private: + Port m_port { Port::Client }; + u32 m_size { 0 }; +}; + +} + +template<> +struct AK::Traits : public AK::GenericTraits { + static constexpr bool is_trivially_serializable() { return true; } +}; diff --git a/Userland/Services/SpiceAgent/SpiceAgent.cpp b/Userland/Services/SpiceAgent/SpiceAgent.cpp index d1991353dc..33e0bf663f 100644 --- a/Userland/Services/SpiceAgent/SpiceAgent.cpp +++ b/Userland/Services/SpiceAgent/SpiceAgent.cpp @@ -120,15 +120,19 @@ ErrorOr SpiceAgent::did_receive_clipboard_message(ClipboardMessage& messag ErrorOr SpiceAgent::read_message_buffer() { - auto port = TRY(m_spice_device->read_value()); - if (port != Port::Client) { - return Error::from_string_literal("Attempted to read message bytes from a port that wasn't meant for the client!"); - } - - auto size = TRY(m_spice_device->read_value()); - auto buffer = TRY(ByteBuffer::create_uninitialized(size)); + auto header = TRY(m_spice_device->read_value()); + auto buffer = TRY(ByteBuffer::create_uninitialized(header.size())); TRY(m_spice_device->read_until_filled(buffer)); + // If the header's size is bigger than or equal to 2048, we may have more data incoming. + while (header.size() >= message_buffer_threshold) { + header = TRY(m_spice_device->read_value()); + + auto new_buffer = TRY(ByteBuffer::create_uninitialized(header.size())); + TRY(m_spice_device->read_until_filled(new_buffer)); + TRY(buffer.try_append(new_buffer)); + } + return buffer; } }; diff --git a/Userland/Services/SpiceAgent/SpiceAgent.h b/Userland/Services/SpiceAgent/SpiceAgent.h index 8ecf4fec3b..d877f55d4a 100644 --- a/Userland/Services/SpiceAgent/SpiceAgent.h +++ b/Userland/Services/SpiceAgent/SpiceAgent.h @@ -7,6 +7,7 @@ #pragma once +#include "ChunkHeader.h" #include "Message.h" #include "MessageHeader.h" #include @@ -16,16 +17,12 @@ namespace SpiceAgent { +// The maximum amount of data that can be contained within a message's buffer. +// If the buffer's length is equal to this, then the next data recieved will be more data from the same buffer. +constexpr u32 message_buffer_threshold = 2048; + class SpiceAgent { public: - // Indicates where the message has come from. - enum class Port : u32 { - Client = 1, - - // There are currently no messages which are meant for the server, so all messages sent by the agent (us) with this port are discarded. - Server - }; - static ErrorOr> create(StringView device_path); SpiceAgent(NonnullOwnPtr spice_device, Vector const& capabilities); @@ -39,20 +36,20 @@ public: TRY(message.write_to_stream(message_stream)); // Create a header to be sent. - auto header_stream = AK::AllocatingMemoryStream(); - auto header = MessageHeader(message.type(), message_stream.used_buffer_size()); - TRY(header_stream.write_value(header)); + auto message_header_stream = AK::AllocatingMemoryStream(); + auto message_header = MessageHeader(message.type(), message_stream.used_buffer_size()); + TRY(message_header_stream.write_value(message_header)); + + // The length given in the chunk header is the length of the message header, and the message combined + auto length = message_header_stream.used_buffer_size() + message_stream.used_buffer_size(); // Currently, there are no messages from the agent which are meant for the server. // So, all messages sent by the agent with a port of Port::Server get dropped silently. - TRY(m_spice_device->write_value(Port::Client)); - - // The length of the subsequent data. - auto length = header_stream.used_buffer_size() + message_stream.used_buffer_size(); - TRY(m_spice_device->write_value(length)); + auto chunk_header = ChunkHeader(ChunkHeader::Port::Client, length); + TRY(m_spice_device->write_value(chunk_header)); // The message's header. - TRY(m_spice_device->write_until_depleted(TRY(header_stream.read_until_eof()))); + TRY(m_spice_device->write_until_depleted(TRY(message_header_stream.read_until_eof()))); // The message content. TRY(m_spice_device->write_until_depleted(TRY(message_stream.read_until_eof())));