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