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
+52
View File
@@ -0,0 +1,52 @@
# RAP RDP Service C# Skeleton
This directory contains an inactive C# RDP service research scaffold.
Status: inactive research scaffold. It is not wired into the runtime stack and
does not replace the current C++ worker.
## Purpose
The current C++ worker remains the active RDP runtime. The active performance
direction is C++ RDP service internals, documented in:
- `docs/architecture/RDP_SERVICE_CPP_PERFORMANCE_TARGET.md`
This C# scaffold is retained only as non-runtime research context. It must not
be used for implementation unless explicitly re-approved.
Any future alternative service would still need to keep:
- backend control-plane contracts
- worker lease/assignment model
- direct worker WSS data-plane contract
- fallback backend gateway
- session lifecycle semantics
## Rules
- No FreeRDP dependency in this service.
- No third-party RDP protocol library.
- RDP protocol details stay behind service boundaries.
- Cluster transport is not redesigned here.
- Current C++ worker remains the active and intended runtime direction.
- Do not continue this C# scaffold unless explicitly re-approved.
## Build
```powershell
dotnet build workers/rdp-service-csharp/src/Rap.Rdp.Service/Rap.Rdp.Service.csproj
```
## Current Limitations
- No RDP handshake yet.
- No NLA/CredSSP yet.
- No graphics protocol implementation yet.
- No direct WSS binding yet.
- No worker registration yet.
See:
- `docs/architecture/RDP_SERVICE_CPP_PERFORMANCE_TARGET.md`
- `docs/architecture/RDP_SERVICE_CSHARP_TARGET.md` for superseded historical context
@@ -0,0 +1,12 @@
namespace Rap.Rdp.Core;
public interface IRapDataPlaneBridge : IAsyncDisposable
{
ValueTask StartAsync(RdpSessionDescriptor session, CancellationToken cancellationToken);
ValueTask StopAsync(CancellationToken cancellationToken);
event Func<RdpInputEvent, CancellationToken, ValueTask>? InputReceived;
event Func<string, CancellationToken, ValueTask>? ClipboardTextReceived;
}
@@ -0,0 +1,43 @@
namespace Rap.Rdp.Core;
public interface IRdpGraphicsSink
{
ValueTask PublishSurfaceAsync(RdpSurfaceUpdate update, CancellationToken cancellationToken);
ValueTask PublishCursorAsync(RdpCursorUpdate update, CancellationToken cancellationToken);
}
public abstract record RdpSurfaceUpdate(
long Sequence,
string UpdateKind,
DateTimeOffset CapturedAt);
public sealed record RdpRawBitmapUpdate(
long Sequence,
DateTimeOffset CapturedAt,
int X,
int Y,
int Width,
int Height,
int DesktopWidth,
int DesktopHeight,
string PixelFormat,
ReadOnlyMemory<byte> Pixels) : RdpSurfaceUpdate(Sequence, "raw_bitmap", CapturedAt);
public sealed record RdpEncodedGraphicsUpdate(
long Sequence,
DateTimeOffset CapturedAt,
int SurfaceId,
string Codec,
int X,
int Y,
int Width,
int Height,
ReadOnlyMemory<byte> Payload) : RdpSurfaceUpdate(Sequence, "encoded_graphics", CapturedAt);
public sealed record RdpCursorUpdate(
long Sequence,
int X,
int Y,
bool Visible,
DateTimeOffset CapturedAt);
@@ -0,0 +1,39 @@
namespace Rap.Rdp.Core;
public interface IRdpProtocolEngine : IAsyncDisposable
{
ValueTask ConnectAsync(RdpSessionDescriptor session, CancellationToken cancellationToken);
ValueTask DisconnectAsync(bool terminateRemoteSession, CancellationToken cancellationToken);
ValueTask SendInputAsync(RdpInputEvent inputEvent, CancellationToken cancellationToken);
ValueTask SendClipboardTextAsync(string text, CancellationToken cancellationToken);
}
public abstract record RdpInputEvent(string CorrelationId);
public sealed record RdpKeyboardEvent(
string CorrelationId,
ushort ScanCode,
bool KeyDown,
bool Extended) : RdpInputEvent(CorrelationId);
public sealed record RdpPointerMoveEvent(
string CorrelationId,
double NormalizedX,
double NormalizedY) : RdpInputEvent(CorrelationId);
public sealed record RdpPointerButtonEvent(
string CorrelationId,
string Button,
bool Pressed,
double NormalizedX,
double NormalizedY) : RdpInputEvent(CorrelationId);
public sealed record RdpPointerWheelEvent(
string CorrelationId,
int Delta,
bool Horizontal,
double NormalizedX,
double NormalizedY) : RdpInputEvent(CorrelationId);
@@ -0,0 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
</Project>
@@ -0,0 +1,54 @@
namespace Rap.Rdp.Core;
public sealed class RdpServiceSession(
RdpSessionDescriptor descriptor,
IRdpProtocolEngine protocolEngine,
IRapDataPlaneBridge dataPlaneBridge)
: IAsyncDisposable
{
private readonly CancellationTokenSource _lifetime = new();
private int _started;
public RdpSessionDescriptor Descriptor { get; } = descriptor;
public async ValueTask StartAsync(CancellationToken cancellationToken)
{
if (Interlocked.Exchange(ref _started, 1) == 1)
{
return;
}
using var linked = CancellationTokenSource.CreateLinkedTokenSource(_lifetime.Token, cancellationToken);
dataPlaneBridge.InputReceived += OnInputReceivedAsync;
dataPlaneBridge.ClipboardTextReceived += OnClipboardTextReceivedAsync;
await protocolEngine.ConnectAsync(Descriptor, linked.Token).ConfigureAwait(false);
await dataPlaneBridge.StartAsync(Descriptor, linked.Token).ConfigureAwait(false);
}
public async ValueTask StopAsync(bool terminateRemoteSession, CancellationToken cancellationToken)
{
_lifetime.Cancel();
await dataPlaneBridge.StopAsync(cancellationToken).ConfigureAwait(false);
await protocolEngine.DisconnectAsync(terminateRemoteSession, cancellationToken).ConfigureAwait(false);
}
private ValueTask OnInputReceivedAsync(RdpInputEvent inputEvent, CancellationToken cancellationToken)
{
return protocolEngine.SendInputAsync(inputEvent, cancellationToken);
}
private ValueTask OnClipboardTextReceivedAsync(string text, CancellationToken cancellationToken)
{
return protocolEngine.SendClipboardTextAsync(text, cancellationToken);
}
public async ValueTask DisposeAsync()
{
_lifetime.Cancel();
dataPlaneBridge.InputReceived -= OnInputReceivedAsync;
dataPlaneBridge.ClipboardTextReceived -= OnClipboardTextReceivedAsync;
await dataPlaneBridge.DisposeAsync().ConfigureAwait(false);
await protocolEngine.DisposeAsync().ConfigureAwait(false);
_lifetime.Dispose();
}
}
@@ -0,0 +1,57 @@
namespace Rap.Rdp.Core;
public sealed record RdpSessionDescriptor(
string SessionId,
string AttachmentId,
string OrganizationId,
string ResourceId,
string WorkerId,
RdpTargetEndpoint Target,
RdpSecurityPolicy SecurityPolicy,
RdpRuntimePolicy RuntimePolicy);
public sealed record RdpTargetEndpoint(
string Host,
int Port,
string UserName,
string Password);
public sealed record RdpSecurityPolicy(
CertificateVerificationMode CertificateVerificationMode);
public enum CertificateVerificationMode
{
Strict,
Ignore
}
public sealed record RdpRuntimePolicy(
IReadOnlySet<RapChannel> AllowedChannels,
ClipboardMode ClipboardMode,
FileTransferMode FileTransferMode);
public enum RapChannel
{
Control,
Input,
Render,
Clipboard,
FileUpload,
Telemetry
}
public enum ClipboardMode
{
Disabled,
ClientToServer,
ServerToClient,
Bidirectional
}
public enum FileTransferMode
{
Disabled,
ClientToServer,
ServerToClient,
Bidirectional
}
@@ -0,0 +1,26 @@
using Rap.Rdp.Core;
Console.WriteLine("RAP RDP C# service skeleton");
Console.WriteLine("Status: protocol engine not implemented; current C++ worker remains active.");
Console.WriteLine($"WorkerId={Environment.GetEnvironmentVariable("RDP_SERVICE_WORKER_ID") ?? "rdp-worker-csharp-1"}");
if (args.Contains("--self-test", StringComparer.OrdinalIgnoreCase))
{
var descriptor = new RdpSessionDescriptor(
"self-test-session",
"self-test-attachment",
"self-test-org",
"self-test-resource",
"rdp-worker-csharp-1",
new RdpTargetEndpoint("127.0.0.1", 3389, "user", "password"),
new RdpSecurityPolicy(CertificateVerificationMode.Strict),
new RdpRuntimePolicy(
new HashSet<RapChannel> { RapChannel.Control, RapChannel.Input, RapChannel.Render },
ClipboardMode.Disabled,
FileTransferMode.Disabled));
Console.WriteLine($"Self-test descriptor created for session={descriptor.SessionId}");
return;
}
Console.WriteLine("Use --self-test to validate skeleton startup.");
@@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Rap.Rdp.Core\Rap.Rdp.Core.csproj" />
</ItemGroup>
</Project>