Initial project snapshot
This commit is contained in:
@@ -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
|
||||
Reference in New Issue
Block a user