refactor(ffi): ISequence interface for sequences (#475)

It’s possible to implement interfaces even on FFI types, because
they are declared as partials.

```csharp
public partial class MySequence : ISequence {}
```
So long as the type exposes the required functions, this one-liner
is enough.
This commit is contained in:
Benoît Cortier
2024-06-12 02:12:06 +00:00
committed by GitHub
parent 7302d605c1
commit d92fa6c66e
7 changed files with 38 additions and 40 deletions
@@ -416,11 +416,7 @@ public partial class MainWindow : Window
var writeBuf = WriteBuf.New();
while (true)
{
var written = await Connection.SingleSequenceStepRead(_framed!, activationSequence, writeBuf);
if (written.GetSize().IsSome())
{
await _framed!.Write(writeBuf);
}
await Connection.SingleSequenceStep(activationSequence, writeBuf,_framed!);
if (activationSequence.GetState().GetType() != ConnectionActivationStateType.Finalized)
continue;
@@ -13,10 +13,13 @@ class Program
public static void Main(string[] args)
{
InitializeLogging();
try{
try
{
BuildAvaloniaApp()
.StartWithClassicDesktopLifetime(args);
}catch(Exception e){
}
catch (Exception e)
{
Trace.TraceError(e.Message);
Trace.TraceError(e.StackTrace);
}
@@ -46,11 +46,11 @@ public struct DiplomatWriteable : IDisposable
IntPtr flushFuncPtr = Marshal.GetFunctionPointerForDelegate(flushFunc);
IntPtr growFuncPtr = Marshal.GetFunctionPointerForDelegate(growFunc);
// flushFunc and growFunc are managed objects and might be disposed of by the garbage collector.
// To prevent this, we make the context hold the references and protect the context itself
// for automatic disposal by moving it behind a GCHandle.
DiplomatWriteableContext ctx = new DiplomatWriteableContext();
DiplomatWriteableContext ctx = new DiplomatWriteableContext();
ctx.flushFunc = flushFunc;
ctx.growFunc = growFunc;
GCHandle ctxHandle = GCHandle.Alloc(ctx);
@@ -81,7 +81,7 @@ public struct DiplomatWriteable : IDisposable
{
throw new IndexOutOfRangeException("DiplomatWriteable buffer is too big");
}
return Marshal.PtrToStringUTF8(buf, (int) len);
return Marshal.PtrToStringUTF8(buf, (int)len);
#else
byte[] utf8 = ToUtf8Bytes();
return DiplomatUtils.Utf8ToString(utf8);
@@ -0,0 +1,5 @@
namespace Devolutions.IronRdp;
public partial class ClientConnector : ISequence
{
}
@@ -29,32 +29,12 @@ public static class Connection
var cliprdr = factory.BuildCliprdr();
connector.AttachStaticCliprdr(cliprdr);
}
await ConnectBegin(framed, connector);
var (serverPublicKey, framedSsl) = await SecurityUpgrade(framed, connector);
var result = await ConnectFinalize(serverName, connector, serverPublicKey, framedSsl);
return (result, framedSsl);
}
public static async Task<Written> SingleSequenceStepRead<TStream>(Framed<TStream> frame, ConnectionActivationSequence sequence, WriteBuf buf)
where TStream: Stream
{
buf.Clear();
var pduHint = sequence.NextPduHint();
// FIXME: The NextPduHint() function signature is incorrectly generated: the return value is nullable, so this check is necessary.
if (null != pduHint)
{
var pdu = await frame.ReadByHint(pduHint);
return sequence.Step(pdu, buf);
}
return sequence.StepNoInput(buf);
}
private static async Task<(byte[], Framed<SslStream>)> SecurityUpgrade(Framed<NetworkStream> framed,
ClientConnector connector)
@@ -82,7 +62,7 @@ public static class Connection
var writeBuf = WriteBuf.New();
while (!connector.ShouldPerformSecurityUpgrade())
{
await SingleConnectStep(connector, writeBuf, framed);
await SingleSequenceStep(connector, writeBuf, framed);
}
}
@@ -98,7 +78,7 @@ public static class Connection
while (!connector.GetDynState().IsTerminal())
{
await SingleConnectStep(connector, writeBuf2, framedSsl);
await SingleSequenceStep(connector, writeBuf2, framedSsl);
}
ClientConnectorState state = connector.ConsumeAndCastToClientConnectorState();
@@ -196,23 +176,23 @@ public static class Connection
}
}
static async Task SingleConnectStep<T>(ClientConnector connector, WriteBuf buf, Framed<T> framed)
public static async Task SingleSequenceStep<S, T>(S sequence, WriteBuf buf, Framed<T> framed)
where T : Stream
where S : ISequence
{
buf.Clear();
var pduHint = connector.NextPduHint();
var pduHint = sequence.NextPduHint();
Written written;
// Don't remove, NextPduHint is generated, and it can return null
if (pduHint != null)
{
byte[] pdu = await framed.ReadByHint(pduHint);
written = connector.Step(pdu, buf);
written = sequence.Step(pdu, buf);
}
else
{
written = connector.StepNoInput(buf);
written = sequence.StepNoInput(buf);
}
if (written.GetWrittenType() == WrittenType.Nothing)
@@ -220,7 +200,7 @@ public static class Connection
return;
}
// will throw if size is not set
// Will throw an exception if the size is not set.
var size = written.GetSize().Get();
var response = new byte[size];
@@ -263,4 +243,4 @@ public static class Utils
vecU8.Fill(buffer);
return buffer;
}
}
}
@@ -0,0 +1,5 @@
namespace Devolutions.IronRdp;
public partial class ConnectionActivationSequence : ISequence
{
}
@@ -0,0 +1,9 @@
namespace Devolutions.IronRdp;
public interface ISequence
{
PduHint? NextPduHint();
Written Step(byte[] pduHint, WriteBuf buf);
Written StepNoInput(WriteBuf buf);
}