Initial project snapshot

This commit is contained in:
2026-04-28 22:29:50 +03:00
commit 8ba0561f4f
365 changed files with 91832 additions and 0 deletions
@@ -0,0 +1,31 @@
#pragma once
#include <string>
#include <string_view>
#include "rdp_worker/adapter/service_adapter_protocol.hpp"
#include "rdp_worker/runtime/models.hpp"
namespace rdp_worker::adapter {
struct AdapterEventDescriptor {
AdapterChannel channel;
std::string_view normalized_type;
bool adapter_origin;
bool reliable;
bool droppable;
};
class AdapterEventRouter {
public:
[[nodiscard]] AdapterEventDescriptor DescribeRenderNotification(const runtime::RenderNotification& notification) const;
[[nodiscard]] AdapterEventDescriptor DescribeClipboardNotification(const runtime::ClipboardNotification& notification) const;
[[nodiscard]] AdapterEventDescriptor DescribeClientEnvelope(std::string_view envelope_type,
std::string_view payload_kind,
std::string_view payload_action) const;
};
[[nodiscard]] std::string AdapterEventDescriptorLogLine(const AdapterEventDescriptor& descriptor);
} // namespace rdp_worker::adapter
@@ -0,0 +1,54 @@
#pragma once
#include <chrono>
#include <cstdint>
#include <memory>
#include <optional>
#include <string>
#include "rdp_worker/adapter/adapter_event_router.hpp"
#include "rdp_worker/common/logger.hpp"
#include "rdp_worker/freerdp/rdp_runtime.hpp"
#include "rdp_worker/runtime/models.hpp"
namespace rdp_worker::adapter {
class RdpAdapterRuntime {
public:
explicit RdpAdapterRuntime(std::shared_ptr<common::Logger> logger);
bool Start(const runtime::ConnectionSpec& spec);
void Disconnect(bool terminate);
bool IsConnected() const;
bool PumpEvents(std::chrono::milliseconds timeout);
int DesktopWidth() const;
int DesktopHeight() const;
bool SendFocusEvent(bool focused);
bool SendKeyboardInput(uint16_t scan_code, bool key_down, bool extended);
bool SendMouseMove(double normalized_x, double normalized_y);
bool SendMouseButton(const std::string& button, bool pressed, double normalized_x, double normalized_y);
bool SendMouseWheel(int wheel_delta, bool horizontal, double normalized_x, double normalized_y);
bool SetClipboardText(const std::string& text);
void MarkInputAppliedForGraphicsTrace(const std::string& correlation_id);
std::optional<runtime::RenderNotification> CaptureFullFrameNotification(
const std::string& state,
const std::string& capture_source);
std::optional<runtime::RenderNotification> PopRenderNotification();
std::optional<runtime::ClipboardNotification> PopClipboardNotification();
const std::string& RenderQualityProfile() const;
[[nodiscard]] const AdapterEventRouter& EventRouter() const;
private:
void TraceClientEnvelope(std::string_view envelope_type,
std::string_view payload_kind,
std::string_view payload_action);
void TraceAdapterEvent(const AdapterEventDescriptor& descriptor);
std::shared_ptr<common::Logger> logger_;
freerdp_runtime::RdpRuntime freerdp_;
AdapterEventRouter event_router_;
bool lifecycle_logged_{false};
};
} // namespace rdp_worker::adapter
@@ -0,0 +1,76 @@
#pragma once
#include <array>
#include <optional>
#include <string_view>
namespace rdp_worker::adapter {
enum class AdapterChannel {
kInput,
kControl,
kDisplay,
kCursor,
kClipboard,
kFileTransfer,
kAudio,
kDevice,
kTelemetry,
};
enum class ChannelDirection {
kClientToAdapter,
kAdapterToClient,
kBidirectional,
};
enum class ChannelReliability {
kReliableOrdered,
kReliableChunked,
kDroppableLatest,
kAdaptiveDroppable,
kSampledDroppable,
};
enum class ChannelPriority {
kCritical = 0,
kHigh = 10,
kMedium = 50,
kLow = 90,
};
struct ChannelSpec {
AdapterChannel channel;
std::string_view name;
ChannelDirection direction;
ChannelReliability reliability;
ChannelPriority priority;
bool stale_updates_droppable;
bool may_block_input;
};
[[nodiscard]] constexpr std::array<ChannelSpec, 9> AllChannelSpecs() {
return {{
{AdapterChannel::kInput, "input", ChannelDirection::kClientToAdapter, ChannelReliability::kReliableOrdered, ChannelPriority::kCritical, true, false},
{AdapterChannel::kControl, "control", ChannelDirection::kBidirectional, ChannelReliability::kReliableOrdered, ChannelPriority::kHigh, false, false},
{AdapterChannel::kDisplay, "display", ChannelDirection::kAdapterToClient, ChannelReliability::kDroppableLatest, ChannelPriority::kHigh, true, false},
{AdapterChannel::kCursor, "cursor", ChannelDirection::kAdapterToClient, ChannelReliability::kDroppableLatest, ChannelPriority::kHigh, true, false},
{AdapterChannel::kClipboard, "clipboard", ChannelDirection::kBidirectional, ChannelReliability::kReliableOrdered, ChannelPriority::kMedium, false, false},
{AdapterChannel::kFileTransfer, "file_transfer", ChannelDirection::kBidirectional, ChannelReliability::kReliableChunked, ChannelPriority::kMedium, false, false},
{AdapterChannel::kAudio, "audio", ChannelDirection::kAdapterToClient, ChannelReliability::kAdaptiveDroppable, ChannelPriority::kMedium, true, false},
{AdapterChannel::kDevice, "device", ChannelDirection::kBidirectional, ChannelReliability::kReliableOrdered, ChannelPriority::kMedium, false, false},
{AdapterChannel::kTelemetry, "telemetry", ChannelDirection::kAdapterToClient, ChannelReliability::kSampledDroppable, ChannelPriority::kLow, true, false},
}};
}
[[nodiscard]] std::optional<ChannelSpec> FindChannelSpec(std::string_view name);
[[nodiscard]] std::string_view ChannelName(AdapterChannel channel);
[[nodiscard]] std::string_view DirectionName(ChannelDirection direction);
[[nodiscard]] std::string_view ReliabilityName(ChannelReliability reliability);
[[nodiscard]] int PriorityValue(ChannelPriority priority);
[[nodiscard]] bool IsDroppable(AdapterChannel channel);
[[nodiscard]] bool IsReliable(AdapterChannel channel);
[[nodiscard]] bool ValidateAdapterChannelInvariants();
} // namespace rdp_worker::adapter
@@ -0,0 +1,53 @@
#pragma once
#include <map>
#include <optional>
#include <string>
#include <variant>
#include <vector>
namespace rdp_worker::common {
struct JsonValue;
using JsonObject = std::map<std::string, JsonValue>;
using JsonArray = std::vector<JsonValue>;
struct JsonValue {
using Variant = std::variant<std::nullptr_t, bool, double, std::string, JsonArray, JsonObject>;
Variant value;
JsonValue();
JsonValue(std::nullptr_t);
JsonValue(bool input);
JsonValue(double input);
JsonValue(int input);
JsonValue(const char* input);
JsonValue(std::string input);
JsonValue(JsonArray input);
JsonValue(JsonObject input);
[[nodiscard]] bool IsObject() const;
[[nodiscard]] bool IsArray() const;
[[nodiscard]] bool IsString() const;
[[nodiscard]] bool IsBool() const;
[[nodiscard]] bool IsNumber() const;
[[nodiscard]] const JsonObject& AsObject() const;
[[nodiscard]] const JsonArray& AsArray() const;
[[nodiscard]] const std::string& AsString() const;
[[nodiscard]] bool AsBool() const;
[[nodiscard]] double AsNumber() const;
};
JsonValue ParseJson(const std::string& input);
std::string SerializeJson(const JsonValue& value);
std::optional<std::string> GetString(const JsonObject& object, const std::string& key);
std::optional<bool> GetBool(const JsonObject& object, const std::string& key);
std::optional<double> GetNumber(const JsonObject& object, const std::string& key);
const JsonObject* GetObject(const JsonObject& object, const std::string& key);
const JsonArray* GetArray(const JsonObject& object, const std::string& key);
} // namespace rdp_worker::common
@@ -0,0 +1,31 @@
#pragma once
#include <mutex>
#include <string>
namespace rdp_worker::common {
enum class LogLevel {
kDebug,
kInfo,
kWarn,
kError
};
class Logger {
public:
explicit Logger(std::string service_name);
void Debug(const std::string& message);
void Info(const std::string& message);
void Warn(const std::string& message);
void Error(const std::string& message);
private:
void Write(LogLevel level, const std::string& message);
std::string service_name_;
std::mutex mutex_;
};
} // namespace rdp_worker::common
@@ -0,0 +1,14 @@
#pragma once
#include <chrono>
#include <string>
namespace rdp_worker::common {
using Clock = std::chrono::system_clock;
std::string ToRfc3339(Clock::time_point time_point);
Clock::time_point NowUtc();
Clock::time_point ParseRfc3339(const std::string& value);
} // namespace rdp_worker::common
@@ -0,0 +1,31 @@
#pragma once
#include <chrono>
#include <string>
#include <vector>
namespace rdp_worker::config {
struct Config {
std::string worker_id;
std::string redis_host;
int redis_port;
std::string redis_password;
int redis_db;
std::chrono::seconds worker_heartbeat_interval;
std::chrono::seconds lease_renew_interval;
std::chrono::seconds assignment_poll_interval;
bool insecure_skip_verify;
std::vector<std::string> capabilities;
bool data_plane_enabled;
std::string data_plane_listen_host;
int data_plane_listen_port;
std::string data_plane_public_key_pem;
std::string data_plane_public_key_file;
std::string data_plane_tls_cert_file;
std::string data_plane_tls_key_file;
};
Config LoadFromEnv();
} // namespace rdp_worker::config
@@ -0,0 +1,52 @@
#pragma once
#include <chrono>
#include <cstddef>
#include <memory>
#include <mutex>
#include <optional>
#include <string>
#include <vector>
#include "rdp_worker/common/json.hpp"
#include "rdp_worker/config/config.hpp"
#include "rdp_worker/coordination/redis_client.hpp"
#include "rdp_worker/runtime/models.hpp"
namespace rdp_worker::common {
class Logger;
}
namespace rdp_worker::coordination {
class ControlPlane {
public:
ControlPlane(config::Config config, std::shared_ptr<common::Logger> logger);
void Connect();
void RegisterWorker();
void ReleaseOwnedLeasesOnStartup();
void SendHeartbeat();
std::optional<runtime::Assignment> PollAssignment(std::chrono::seconds timeout);
std::optional<common::JsonObject> PollSessionEnvelope(const std::string& session_id, std::chrono::seconds timeout);
std::vector<common::JsonObject> DrainSessionEnvelopes(const std::string& session_id, std::size_t max_count);
int64_t SessionEnvelopeQueueLength(const std::string& session_id);
std::optional<runtime::WorkerLease> GetLeaseBySession(const std::string& session_id);
void RenewLease(const runtime::WorkerLease& lease);
void ReleaseLease(const runtime::WorkerLease& lease);
void PublishEvent(const runtime::WorkerEvent& event);
private:
runtime::Assignment ParseAssignment(const common::JsonObject& object) const;
runtime::WorkerLease ParseLease(const common::JsonObject& object) const;
std::string WorkerRegistrationPayload() const;
std::string LeasePayload(const runtime::WorkerLease& lease) const;
std::string EventPayload(const runtime::WorkerEvent& event) const;
config::Config config_;
std::shared_ptr<common::Logger> logger_;
mutable std::mutex mutex_;
std::unique_ptr<RedisClient> redis_;
};
} // namespace rdp_worker::coordination
@@ -0,0 +1,61 @@
#pragma once
#include <chrono>
#include <cstdint>
#include <optional>
#include <string>
#include <variant>
#include <vector>
namespace rdp_worker::coordination {
struct RedisReply {
using Array = std::vector<RedisReply>;
std::variant<std::nullptr_t, std::string, int64_t, Array> value;
[[nodiscard]] bool IsNull() const;
[[nodiscard]] bool IsString() const;
[[nodiscard]] bool IsInteger() const;
[[nodiscard]] bool IsArray() const;
[[nodiscard]] const std::string& AsString() const;
[[nodiscard]] int64_t AsInteger() const;
[[nodiscard]] const Array& AsArray() const;
};
class RedisClient {
public:
RedisClient(std::string host, int port, std::string password, int db);
~RedisClient();
void Connect();
void Close();
RedisReply Command(const std::vector<std::string>& parts);
std::optional<std::string> Get(const std::string& key);
void Set(const std::string& key, const std::string& value, std::chrono::seconds ttl);
void SAdd(const std::string& key, const std::string& value);
void SRem(const std::string& key, const std::string& value);
std::vector<std::string> SMembers(const std::string& key);
std::optional<std::vector<std::string>> BLPop(const std::string& key, std::chrono::seconds timeout);
std::optional<std::string> LPop(const std::string& key);
int64_t LLen(const std::string& key);
void RPush(const std::string& key, const std::string& value);
void Expire(const std::string& key, std::chrono::seconds ttl);
void Delete(const std::string& key);
private:
std::string ReadLine();
std::string ReadBytes(std::size_t count);
RedisReply ReadReply();
void WriteAll(const std::string& data);
std::string EncodeCommand(const std::vector<std::string>& parts) const;
std::string host_;
int port_;
std::string password_;
int db_;
int socket_fd_;
};
} // namespace rdp_worker::coordination
@@ -0,0 +1,67 @@
#pragma once
#include <cstdint>
#include "rdp_worker/cursor/cursor_update.hpp"
namespace rdp_worker::cursor {
class CursorAdapter {
public:
CursorUpdate MakePosition(std::uint64_t sequence,
int desktop_width,
int desktop_height,
int x,
int y,
bool visible) const;
CursorUpdate MakeSystem(std::uint64_t sequence,
int desktop_width,
int desktop_height,
int x,
int y,
std::uint32_t system_type) const;
CursorUpdate MakeColor(std::uint64_t sequence,
int desktop_width,
int desktop_height,
int x,
int y,
int width,
int height,
int cache_index,
std::uint64_t mask_bytes) const;
CursorUpdate MakeNew(std::uint64_t sequence,
int desktop_width,
int desktop_height,
int x,
int y,
int width,
int height,
int cache_index,
int xor_bpp,
std::uint64_t mask_bytes) const;
CursorUpdate MakeCached(std::uint64_t sequence,
int desktop_width,
int desktop_height,
int x,
int y,
int cache_index) const;
CursorUpdate MakeLarge(std::uint64_t sequence,
int desktop_width,
int desktop_height,
int x,
int y,
int width,
int height,
int cache_index,
int hot_spot_x,
int hot_spot_y,
int xor_bpp,
std::uint64_t mask_bytes) const;
};
} // namespace rdp_worker::cursor
@@ -0,0 +1,43 @@
#pragma once
#include <cstdint>
#include <string>
#include "rdp_worker/common/json.hpp"
namespace rdp_worker::cursor {
enum class CursorUpdateKind {
kPosition,
kSystem,
kColor,
kNew,
kCached,
kLarge,
};
struct CursorUpdate {
CursorUpdateKind kind{CursorUpdateKind::kPosition};
std::uint64_t sequence{0};
int desktop_width{0};
int desktop_height{0};
int x{0};
int y{0};
bool visible{true};
bool shape_changed{false};
int cache_index{-1};
int hot_spot_x{0};
int hot_spot_y{0};
int width{0};
int height{0};
int xor_bpp{0};
std::uint64_t mask_bytes{0};
std::uint32_t system_type{0};
};
const char* CursorUpdateKindName(CursorUpdateKind kind);
common::JsonObject CursorUpdateToPayload(const CursorUpdate& update,
const std::string& render_quality_profile);
} // namespace rdp_worker::cursor
@@ -0,0 +1,53 @@
#pragma once
#include <atomic>
#include <chrono>
#include <map>
#include <memory>
#include <mutex>
#include <optional>
#include <thread>
#include <boost/asio/io_context.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/ssl/context.hpp>
#include "rdp_worker/common/logger.hpp"
#include "rdp_worker/config/config.hpp"
#include "rdp_worker/dataplane/token_validator.hpp"
#include "rdp_worker/runtime/session_manager.hpp"
namespace rdp_worker::dataplane {
class DirectWssServer {
public:
DirectWssServer(config::Config config,
std::shared_ptr<runtime::SessionManager> session_manager,
std::shared_ptr<common::Logger> logger);
~DirectWssServer();
DirectWssServer(const DirectWssServer&) = delete;
DirectWssServer& operator=(const DirectWssServer&) = delete;
void Start();
void Stop();
private:
void Run();
void HandleConnection(boost::asio::ip::tcp::socket socket);
bool ConsumeJti(const DataPlaneTokenClaims& claims);
config::Config config_;
std::shared_ptr<runtime::SessionManager> session_manager_;
std::shared_ptr<common::Logger> logger_;
DataPlaneTokenValidator token_validator_;
boost::asio::io_context io_context_;
boost::asio::ssl::context ssl_context_;
std::optional<boost::asio::ip::tcp::acceptor> acceptor_;
std::thread thread_;
std::atomic<bool> stop_requested_{false};
std::mutex jti_mutex_;
std::map<std::string, std::chrono::system_clock::time_point> used_jti_;
};
} // namespace rdp_worker::dataplane
@@ -0,0 +1,38 @@
#pragma once
#include <cstdint>
#include <string>
#include <vector>
namespace rdp_worker::dataplane {
struct DataPlaneTokenClaims {
std::string session_id;
std::string attachment_id;
std::string user_id;
std::string organization_id;
std::string worker_id;
std::string resource_id;
std::vector<std::string> allowed_channels;
std::int64_t expires_at_unix{0};
std::string jti;
};
struct TokenValidationResult {
bool ok{false};
std::string reason;
DataPlaneTokenClaims claims;
};
class DataPlaneTokenValidator {
public:
DataPlaneTokenValidator(std::string public_key_pem, std::string expected_worker_id);
[[nodiscard]] TokenValidationResult Validate(const std::string& token) const;
private:
std::string public_key_pem_;
std::string expected_worker_id_;
};
} // namespace rdp_worker::dataplane
@@ -0,0 +1,268 @@
#pragma once
#include <atomic>
#include <chrono>
#include <cstdint>
#include <optional>
#include <queue>
#include <mutex>
#include <memory>
#include <string>
#include <vector>
#include <freerdp/client/cliprdr.h>
#include <freerdp/client/rdpgfx.h>
#include <freerdp/event.h>
#include <freerdp/freerdp.h>
#include <freerdp/gdi/gdi.h>
#include <freerdp/pointer.h>
#include <freerdp/update.h>
#include "rdp_worker/cursor/cursor_adapter.hpp"
#include "rdp_worker/common/logger.hpp"
#include "rdp_worker/runtime/models.hpp"
namespace rdp_worker::freerdp_runtime {
class RdpRuntime {
public:
explicit RdpRuntime(std::shared_ptr<common::Logger> logger);
~RdpRuntime();
bool Start(const runtime::ConnectionSpec& spec);
void Disconnect(bool terminate);
bool IsConnected() const;
bool PumpEvents(std::chrono::milliseconds timeout);
int DesktopWidth() const;
int DesktopHeight() const;
bool SendFocusEvent(bool focused);
bool SendKeyboardInput(uint16_t scan_code, bool key_down, bool extended);
bool SendMouseMove(double normalized_x, double normalized_y);
bool SendMouseButton(const std::string& button, bool pressed, double normalized_x, double normalized_y);
bool SendMouseWheel(int wheel_delta, bool horizontal, double normalized_x, double normalized_y);
bool SetClipboardText(const std::string& text);
void MarkInputAppliedForGraphicsTrace(const std::string& correlation_id);
std::optional<runtime::RenderNotification> CaptureFullFrameNotification(
const std::string& state,
const std::string& capture_source);
std::optional<runtime::RenderNotification> PopRenderNotification();
std::optional<runtime::ClipboardNotification> PopClipboardNotification();
const std::string& RenderQualityProfile() const;
void InstallRenderHooks();
void RemoveRenderHooks();
void EnqueueRenderNotification(const std::string& type, common::JsonObject payload);
void OnBeginPaint(rdpContext* context);
void OnEndPaint(rdpContext* context);
void OnDesktopResize(rdpContext* context);
void OnBitmapUpdate(rdpContext* context, const BITMAP_UPDATE* bitmap);
void OnRefreshRect(rdpContext* context, BYTE count, const RECTANGLE_16* areas);
void OnSurfaceBits(rdpContext* context, const SURFACE_BITS_COMMAND* surface_bits);
void OnSurfaceFrameMarker(rdpContext* context, const SURFACE_FRAME_MARKER* surface_frame_marker);
void OnSurfaceFrameBits(rdpContext* context, const SURFACE_BITS_COMMAND* surface_bits, bool first, bool last, UINT32 frame_id);
void OnChannelConnected(rdpContext* context, ChannelConnectedEventArgs* event_args);
void OnChannelDisconnected(rdpContext* context, ChannelDisconnectedEventArgs* event_args);
void OnPointerPosition(rdpContext* context, const POINTER_POSITION_UPDATE* pointer_position);
void OnPointerSystem(rdpContext* context, const POINTER_SYSTEM_UPDATE* pointer_system);
void OnPointerColor(rdpContext* context, const POINTER_COLOR_UPDATE* pointer_color);
void OnPointerNew(rdpContext* context, const POINTER_NEW_UPDATE* pointer_new);
void OnPointerCached(rdpContext* context, const POINTER_CACHED_UPDATE* pointer_cached);
void OnPointerLarge(rdpContext* context, const POINTER_LARGE_UPDATE* pointer_large);
UINT OnCliprdrServerCapabilities(CliprdrClientContext* context, const CLIPRDR_CAPABILITIES* capabilities);
UINT OnCliprdrMonitorReady(CliprdrClientContext* context, const CLIPRDR_MONITOR_READY* monitor_ready);
UINT OnCliprdrServerFormatList(CliprdrClientContext* context, const CLIPRDR_FORMAT_LIST* format_list);
UINT OnCliprdrServerFormatListResponse(CliprdrClientContext* context, const CLIPRDR_FORMAT_LIST_RESPONSE* response);
UINT OnCliprdrServerLockClipboardData(CliprdrClientContext* context, const CLIPRDR_LOCK_CLIPBOARD_DATA* lock_data);
UINT OnCliprdrServerUnlockClipboardData(CliprdrClientContext* context, const CLIPRDR_UNLOCK_CLIPBOARD_DATA* unlock_data);
UINT OnCliprdrServerFormatDataRequest(CliprdrClientContext* context, const CLIPRDR_FORMAT_DATA_REQUEST* request);
UINT OnCliprdrServerFormatDataResponse(CliprdrClientContext* context, const CLIPRDR_FORMAT_DATA_RESPONSE* response);
UINT OnCliprdrServerFileContentsRequest(CliprdrClientContext* context, const CLIPRDR_FILE_CONTENTS_REQUEST* request);
UINT OnCliprdrServerFileContentsResponse(CliprdrClientContext* context, const CLIPRDR_FILE_CONTENTS_RESPONSE* response);
private:
using DesktopResizeCallback = BOOL (*)(rdpContext*);
using BitmapUpdateCallback = BOOL (*)(rdpContext*, const BITMAP_UPDATE*);
using RefreshRectCallback = BOOL (*)(rdpContext*, BYTE, const RECTANGLE_16*);
using PointerPositionCallback = BOOL (*)(rdpContext*, const POINTER_POSITION_UPDATE*);
using BeginPaintCallback = BOOL (*)(rdpContext*);
using EndPaintCallback = BOOL (*)(rdpContext*);
using PointerSystemCallback = BOOL (*)(rdpContext*, const POINTER_SYSTEM_UPDATE*);
using PointerColorCallback = BOOL (*)(rdpContext*, const POINTER_COLOR_UPDATE*);
using PointerNewCallback = BOOL (*)(rdpContext*, const POINTER_NEW_UPDATE*);
using PointerCachedCallback = BOOL (*)(rdpContext*, const POINTER_CACHED_UPDATE*);
using PointerLargeCallback = BOOL (*)(rdpContext*, const POINTER_LARGE_UPDATE*);
using SurfaceBitsCallback = BOOL (*)(rdpContext*, const SURFACE_BITS_COMMAND*);
using SurfaceFrameMarkerCallback = BOOL (*)(rdpContext*, const SURFACE_FRAME_MARKER*);
using SurfaceFrameBitsCallback = BOOL (*)(rdpContext*, const SURFACE_BITS_COMMAND*, BOOL, BOOL, UINT32);
struct RenderHooks {
BeginPaintCallback begin_paint{};
EndPaintCallback end_paint{};
DesktopResizeCallback desktop_resize{};
BitmapUpdateCallback bitmap_update{};
RefreshRectCallback refresh_rect{};
SurfaceBitsCallback surface_bits{};
SurfaceFrameMarkerCallback surface_frame_marker{};
SurfaceFrameBitsCallback surface_frame_bits{};
PointerPositionCallback pointer_position{};
PointerSystemCallback pointer_system{};
PointerColorCallback pointer_color{};
PointerNewCallback pointer_new{};
PointerCachedCallback pointer_cached{};
PointerLargeCallback pointer_large{};
};
struct DirtyRegion {
int x{};
int y{};
int width{};
int height{};
int rectangles{};
};
struct CallbackPerfStats {
std::uint64_t begin_paint{};
std::uint64_t end_paint{};
std::uint64_t desktop_resize{};
std::uint64_t bitmap_update{};
std::uint64_t refresh_rect{};
std::uint64_t surface_bits{};
std::uint64_t surface_frame_marker{};
std::uint64_t surface_frame_bits{};
std::uint64_t pointer_position{};
std::uint64_t pointer_system{};
std::uint64_t pointer_color{};
std::uint64_t pointer_new{};
std::uint64_t pointer_cached{};
std::uint64_t pointer_large{};
std::uint64_t frame_capture_full{};
std::uint64_t frame_capture_region{};
std::uint64_t periodic_polls{};
std::uint64_t periodic_changes{};
std::uint64_t periodic_no_changes{};
std::uint64_t interactive_refresh_requests{};
std::uint64_t bitmap_update_deferred{};
std::uint64_t paint_flush_region{};
std::uint64_t paint_flush_full{};
std::uint64_t end_paint_change_fallback{};
std::uint64_t end_paint_noop{};
std::uint64_t rdpgfx_channel_connected{};
std::uint64_t rdpgfx_channel_disconnected{};
std::uint64_t rdpgfx_pipeline_init_success{};
std::uint64_t rdpgfx_pipeline_init_failed{};
std::uint64_t rdpgfx_fallback_to_gdi{};
std::uint64_t cursor_updates_enqueued{};
std::uint64_t event_pump_drained_checks{};
std::uint64_t event_pump_wait_timeouts{};
std::chrono::steady_clock::time_point first_callback_at{};
std::chrono::steady_clock::time_point last_callback_at{};
std::chrono::steady_clock::time_point last_summary_at{};
std::string last_callback_name;
};
bool ConfigureSettings(const runtime::ConnectionSpec& spec);
void ConfigureQualityProfile(const runtime::ConnectionSpec& spec);
bool ConfigureRestrictedDrive(const runtime::ConnectionSpec& spec);
bool LoadClipboardChannel();
bool SubscribeChannelEvents();
void UnsubscribeChannelEvents();
bool ConfigureClipboardChannel();
bool SendClientClipboardCapabilities();
bool SendClientClipboardFormatList();
bool SendClientClipboardFormatListResponse(bool ok);
bool SendClientClipboardDataRequest(UINT32 format_id);
bool SendClientClipboardDataResponse(UINT32 format_id);
void EnqueueClipboardText(std::string text, std::string origin);
std::optional<runtime::RenderNotification> CaptureFrameNotification(
const std::string& state,
const std::optional<DirtyRegion>& dirty_region = std::nullopt,
const std::string& capture_source = "explicit");
std::optional<runtime::RenderNotification> CaptureChangedFrameNotification(
const std::string& state,
const std::string& detection_source = "periodic_change_detector");
bool EnsureInputReady() const;
uint16_t ScaleCoordinate(double normalized, int size) const;
void RequestInteractiveFrameRefresh();
void TryEnqueueInteractiveFrameCapture(const char* reason);
std::optional<std::pair<std::string, std::int64_t>> RecentInputTraceDelay() const;
void AccumulatePendingPaintRegion(const std::optional<DirtyRegion>& dirty_region, int rectangle_count);
void ClearPendingPaintCycle();
bool SyncPollSnapshotFromCapture(const std::vector<std::uint8_t>& frame_bytes,
int frame_width,
int frame_height,
int frame_stride,
const std::optional<DirtyRegion>& dirty_region,
bool is_region);
void MaybeLogMouseMoveRate();
void RecordRdpCallback(const std::string& callback_name);
void RecordFrameCapture(bool region);
void RecordPeriodicPoll(bool changed);
void MaybeLogPeriodicNoChange(const std::string& detection_source,
std::chrono::steady_clock::time_point poll_started,
std::chrono::steady_clock::time_point poll_completed);
void MaybeLogCallbackSummary(const std::string& trigger);
void MaybeLogFirstInputCallback(const std::string& callback_name, std::chrono::steady_clock::time_point callback_at);
void LogRdpgfxFallbackIfNeeded(const std::string& reason);
void LogSurfaceBitsEvent(const std::string& callback_name, const SURFACE_BITS_COMMAND* surface_bits) const;
void EnqueueCursorUpdate(const cursor::CursorUpdate& update);
void MaybeLogCursorRate(const cursor::CursorUpdate& update);
void Cleanup();
std::shared_ptr<common::Logger> logger_;
freerdp* instance_;
std::atomic<bool> connected_;
int desktop_width_;
int desktop_height_;
std::string render_quality_profile_;
RenderHooks render_hooks_;
mutable std::mutex render_mutex_;
std::queue<runtime::RenderNotification> render_notifications_;
std::queue<runtime::ClipboardNotification> clipboard_notifications_;
cursor::CursorAdapter cursor_adapter_;
CliprdrClientContext* cliprdr_context_{nullptr};
RdpgfxClientContext* rdpgfx_context_{nullptr};
bool rdpgfx_enabled_{false};
bool channel_events_subscribed_{false};
bool rdpgfx_pipeline_active_{false};
bool rdpgfx_channel_seen_{false};
bool rdpgfx_fallback_logged_{false};
std::string client_clipboard_text_;
std::string last_client_clipboard_hash_;
UINT32 requested_server_clipboard_format_{0};
std::uint64_t clipboard_sequence_{0};
int cursor_x_{0};
int cursor_y_{0};
bool cursor_visible_{true};
std::uint64_t cursor_sequence_{0};
std::chrono::steady_clock::time_point last_cursor_rate_log_at_{};
std::uint64_t cursor_updates_since_log_{0};
int64_t frame_sequence_{0};
std::atomic<bool> interactive_frame_refresh_requested_{false};
std::atomic<std::uint32_t> interactive_frame_refresh_budget_{0};
std::chrono::steady_clock::time_point last_interactive_frame_capture_at_{};
std::chrono::steady_clock::time_point last_mouse_move_refresh_request_at_{};
std::chrono::steady_clock::time_point post_input_capture_until_{};
std::chrono::steady_clock::time_point next_post_input_capture_at_{};
std::chrono::steady_clock::time_point last_mouse_move_rate_log_at_{};
std::uint64_t mouse_moves_sent_since_log_{0};
std::vector<std::uint8_t> last_polled_frame_;
int last_polled_frame_width_{0};
int last_polled_frame_height_{0};
int last_polled_frame_stride_{0};
std::chrono::steady_clock::time_point last_periodic_frame_poll_at_{};
std::chrono::steady_clock::time_point last_periodic_no_change_log_at_{};
std::uint64_t periodic_no_change_suppressed_since_log_{0};
bool paint_cycle_active_{false};
bool pending_paint_has_bitmap_update_{false};
bool pending_paint_force_full_frame_{false};
std::optional<DirtyRegion> pending_paint_dirty_region_;
int pending_paint_dirty_rectangles_{0};
std::uint64_t pending_paint_bitmap_updates_{0};
mutable std::mutex input_trace_mutex_;
std::string last_input_trace_correlation_id_;
std::chrono::steady_clock::time_point last_input_trace_at_{};
bool last_input_waiting_for_first_callback_{false};
CallbackPerfStats callback_perf_;
};
} // namespace rdp_worker::freerdp_runtime
@@ -0,0 +1,41 @@
#pragma once
#include <optional>
#include "rdp_worker/graphics/render_update.hpp"
namespace rdp_worker::graphics {
struct GraphicsAdapterPolicy {
int max_region_area_percent{60};
bool allow_full_frame_fallback{true};
bool prefer_encoded_updates{true};
};
class GraphicsAdapter {
public:
explicit GraphicsAdapter(GraphicsAdapterPolicy policy = {});
[[nodiscard]] const GraphicsAdapterPolicy& Policy() const;
[[nodiscard]] RenderUpdate MakeFullBgraFrame(std::uint64_t sequence,
int width,
int height,
int stride,
std::vector<std::uint8_t> pixels,
bool baseline) const;
[[nodiscard]] std::optional<RenderUpdate> TryMakeBgraRegion(std::uint64_t sequence,
int desktop_width,
int desktop_height,
int stride,
Rect region,
std::vector<std::uint8_t> pixels) const;
private:
[[nodiscard]] bool RegionAllowed(int desktop_width, int desktop_height, const Rect& region) const;
GraphicsAdapterPolicy policy_;
};
} // namespace rdp_worker::graphics
@@ -0,0 +1,50 @@
#pragma once
#include <cstdint>
#include <string>
#include <vector>
namespace rdp_worker::graphics {
enum class RenderUpdateKind {
kFullBgraFrame,
kBgraRegion,
kSurfaceCreate,
kSurfaceDelete,
kSurfaceBits,
kEncodedFrame,
kCursorUpdate,
};
struct Rect {
int x{0};
int y{0};
int width{0};
int height{0};
};
struct RenderUpdate {
RenderUpdateKind kind{RenderUpdateKind::kFullBgraFrame};
std::uint64_t sequence{0};
int desktop_width{0};
int desktop_height{0};
int frame_width{0};
int frame_height{0};
int stride{0};
Rect region;
std::string pixel_format{"bgra32"};
std::string codec;
std::vector<std::uint8_t> payload;
bool droppable{true};
bool baseline{false};
};
const char* RenderUpdateKindName(RenderUpdateKind kind);
bool IsFullFrameUpdate(const RenderUpdate& update);
bool IsRegionUpdate(const RenderUpdate& update);
bool IsEncodedUpdate(const RenderUpdate& update);
} // namespace rdp_worker::graphics
@@ -0,0 +1,18 @@
#pragma once
#include <string>
#include "rdp_worker/dataplane/token_validator.hpp"
#include "rdp_worker/runtime/models.hpp"
namespace rdp_worker::runtime {
struct DirectBindValidationResult {
bool ok{false};
std::string reason;
};
DirectBindValidationResult ValidateDirectDataPlaneBind(const Assignment& assignment,
const dataplane::DataPlaneTokenClaims& claims);
} // namespace rdp_worker::runtime
@@ -0,0 +1,16 @@
#pragma once
#include <string>
#include "rdp_worker/runtime/models.hpp"
namespace rdp_worker::runtime {
class DirectEventSink {
public:
virtual ~DirectEventSink() = default;
virtual std::string AttachmentId() const = 0;
virtual void EnqueueEvent(const WorkerEvent& event) = 0;
};
} // namespace rdp_worker::runtime
@@ -0,0 +1,90 @@
#pragma once
#include <chrono>
#include <cstdint>
#include <optional>
#include <string>
#include <vector>
#include "rdp_worker/common/json.hpp"
namespace rdp_worker::runtime {
enum class SessionState {
kStarting,
kActive,
kDetached,
kReconnecting,
kTerminated,
kFailed
};
struct ConnectionSpec {
std::string resource_id;
std::string resource_name;
std::string host;
uint16_t port;
std::string username;
std::string password;
std::string domain;
std::string certificate_verification_mode{"strict"};
std::string render_quality_profile{"balanced"};
std::string redirected_drive_name;
std::string redirected_drive_path;
bool insecure_skip_verify{false};
};
struct SessionPolicy {
std::chrono::seconds detach_grace_period{1800};
std::string clipboard_mode{"disabled"};
std::string file_transfer_mode{"disabled"};
};
struct Assignment {
std::string session_id;
std::string worker_id;
std::string attachment_id;
std::string user_id;
std::string organization_id;
std::string device_id;
std::optional<std::string> takeover_of;
SessionState state{SessionState::kStarting};
ConnectionSpec connection;
SessionPolicy policy;
};
struct WorkerLease {
std::string lease_id;
std::string worker_id;
std::string session_id;
std::string resource_id;
std::string control_stream;
std::vector<std::string> capabilities;
std::string expires_at;
};
struct WorkerEvent {
std::string type;
std::string session_id;
std::string worker_id;
std::string reason;
int width{0};
int height{0};
common::JsonObject payload;
std::vector<std::uint8_t> raw_frame_bytes;
};
struct RenderNotification {
std::string type;
common::JsonObject payload;
std::vector<std::uint8_t> raw_frame_bytes;
};
struct ClipboardNotification {
std::string text;
std::string origin;
std::string content_hash;
std::uint64_t sequence_id{0};
};
} // namespace rdp_worker::runtime
@@ -0,0 +1,32 @@
#pragma once
#include <map>
#include <memory>
#include <mutex>
#include "rdp_worker/common/logger.hpp"
#include "rdp_worker/coordination/control_plane.hpp"
#include "rdp_worker/dataplane/token_validator.hpp"
#include "rdp_worker/runtime/models.hpp"
#include "rdp_worker/runtime/session_runtime.hpp"
namespace rdp_worker::runtime {
class SessionManager {
public:
SessionManager(std::shared_ptr<coordination::ControlPlane> control_plane,
std::shared_ptr<common::Logger> logger);
void ApplyAssignment(const Assignment& assignment);
void StopAll();
bool BindDirectDataPlaneAttachment(const dataplane::DataPlaneTokenClaims& claims, std::string& reason);
std::shared_ptr<SessionRuntime> BindDirectDataPlaneRuntime(const dataplane::DataPlaneTokenClaims& claims, std::string& reason);
private:
std::shared_ptr<coordination::ControlPlane> control_plane_;
std::shared_ptr<common::Logger> logger_;
std::mutex mutex_;
std::map<std::string, std::shared_ptr<SessionRuntime>> sessions_;
};
} // namespace rdp_worker::runtime
@@ -0,0 +1,134 @@
#pragma once
#include <atomic>
#include <cstdint>
#include <deque>
#include <filesystem>
#include <memory>
#include <mutex>
#include <optional>
#include <thread>
#include <unordered_map>
#include <vector>
#include "rdp_worker/adapter/rdp_adapter_runtime.hpp"
#include "rdp_worker/coordination/control_plane.hpp"
#include "rdp_worker/runtime/direct_event_sink.hpp"
#include "rdp_worker/runtime/models.hpp"
namespace rdp_worker::common {
class Logger;
}
namespace rdp_worker::runtime {
class SessionRuntime {
public:
SessionRuntime(Assignment assignment,
std::shared_ptr<coordination::ControlPlane> control_plane,
std::shared_ptr<common::Logger> logger);
~SessionRuntime();
void Start();
void ApplyAssignment(const Assignment& assignment);
void Stop(bool terminate, const std::string& reason);
std::string SessionId() const;
Assignment Snapshot() const;
bool EnqueueDirectEnvelope(common::JsonObject envelope);
void AddDirectEventSink(std::weak_ptr<DirectEventSink> sink);
void RequestDirectFullFrameRepair(std::string reason);
private:
void Run();
void DrainAndHandleEnvelopes(const std::string& session_id);
std::vector<common::JsonObject> DrainDirectEnvelopes(std::size_t max_count);
void HandleEnvelopeBatch(const std::vector<common::JsonObject>& envelopes);
void HandleEnvelope(const common::JsonObject& envelope);
void HandleFileUpload(const common::JsonObject& payload);
void HandleFileDownload(const common::JsonObject& payload);
void ScanOutboundDownloadDirectory(const std::string& session_id);
void PublishFileDownloadBlocked(const std::string& transfer_id, const std::string& file_id, const std::string& reason);
std::filesystem::path PrepareVisibleTransferDirectory(const std::string& session_id);
void CleanupVisibleTransferDirectory(const std::string& session_id);
void CleanupSessionTransferDirectory(const std::string& session_id);
void PublishDirectAttachBaselineIfRequested(const std::string& session_id);
void DrainAndPublishRenderNotifications(const std::string& session_id);
void PublishEvent(const std::string& type, const std::string& reason = {});
void PublishEvent(const std::string& type, const std::string& reason, common::JsonObject payload);
void DispatchDirectEvent(const WorkerEvent& event);
bool HasCurrentDirectEventSink();
struct FileUploadState {
std::string transfer_id;
std::string file_name;
std::filesystem::path temp_path;
std::filesystem::path final_path;
std::int64_t file_size{0};
std::int64_t total_chunks{0};
std::int64_t received{0};
std::int64_t next_index{0};
std::uint64_t hash{1469598103934665603ULL};
std::string expected_hash;
};
struct FileDownloadCandidate {
std::string file_id;
std::string file_name;
std::filesystem::path path;
std::filesystem::file_time_type modified_at{};
std::int64_t file_size{0};
std::uint64_t content_hash_value{1469598103934665603ULL};
std::string content_hash;
std::int64_t last_observed_size{-1};
std::filesystem::file_time_type last_observed_modified_at{};
int stable_observations{0};
bool available_published{false};
};
struct FileDownloadState {
std::string transfer_id;
std::string file_id;
std::string file_name;
std::filesystem::path path;
std::filesystem::file_time_type modified_at{};
std::int64_t file_size{0};
std::int64_t sent{0};
std::int64_t next_sequence{0};
std::string content_hash;
bool active{false};
};
mutable std::mutex mutex_;
Assignment assignment_;
std::shared_ptr<coordination::ControlPlane> control_plane_;
std::shared_ptr<common::Logger> logger_;
adapter::RdpAdapterRuntime rdp_adapter_;
std::thread thread_;
std::atomic<bool> stop_requested_;
std::atomic<bool> attached_;
std::atomic<bool> direct_attach_baseline_requested_{false};
bool focus_forward_logged_{false};
bool keyboard_forward_logged_{false};
bool mouse_forward_logged_{false};
std::string last_input_correlation_id_;
std::chrono::steady_clock::time_point last_input_applied_at_{};
std::deque<RenderNotification> pending_render_frames_;
std::optional<WorkerEvent> last_direct_render_frame_;
bool direct_full_repair_requested_{false};
std::string direct_full_repair_reason_;
std::chrono::steady_clock::time_point last_frame_published_at_{};
std::chrono::steady_clock::time_point last_region_loss_full_repair_at_{};
std::chrono::steady_clock::time_point last_render_rate_logged_at_{};
std::size_t render_frames_seen_since_log_{0};
std::size_t render_frames_published_since_log_{0};
std::size_t render_frames_dropped_since_log_{0};
std::unordered_map<std::string, FileUploadState> uploads_;
std::unordered_map<std::string, FileDownloadCandidate> download_candidates_;
std::unordered_map<std::string, FileDownloadState> downloads_;
std::chrono::steady_clock::time_point last_download_scan_at_{};
std::int64_t file_download_event_sequence_{0};
std::deque<common::JsonObject> direct_envelopes_;
std::vector<std::weak_ptr<DirectEventSink>> direct_event_sinks_;
};
} // namespace rdp_worker::runtime