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