Skip to content

Commit

Permalink
Implement test client as using C# Pipeline.
Browse files Browse the repository at this point in the history
  • Loading branch information
HarryCordewener committed Jan 4, 2024
1 parent 29cd134 commit f4c06f0
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 122 deletions.
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ Initiate a logger. A Serilog logger is required by this library at this time.
Log.Logger = log;
```

Create functions that implement your desired behavior on getting as signal.
Create functions that implement your desired behavior on getting a signal.
```csharp
private async Task WriteToOutputStreamAsync(byte[] arg, StreamWriter writer)
{
Expand Down Expand Up @@ -92,13 +92,13 @@ Initialize the Interpreter.
```csharp
var telnet = new TelnetInterpreter(TelnetInterpreter.TelnetMode.Client, _Logger.ForContext<TelnetInterpreter>())
{
CallbackOnSubmitAsync = WriteBackAsync,
CallbackNegotiationAsync = (x) => WriteToOutputStreamAsync(x, output),
SignalOnGMCPAsync = SignalGMCPAsync,
SignalOnMSSPAsync = SignalMSSPAsync,
SignalOnNAWSAsync = SignalNAWSAsync,
SignalOnPromptingAsync = SignalPromptAsync,
CharsetOrder = new[] { Encoding.GetEncoding("utf-8"), Encoding.GetEncoding("iso-8859-1") }
CallbackOnSubmitAsync = WriteBackAsync,
CallbackNegotiationAsync = (x) => WriteToOutputStreamAsync(x, output),
SignalOnGMCPAsync = SignalGMCPAsync,
SignalOnMSSPAsync = SignalMSSPAsync,
SignalOnNAWSAsync = SignalNAWSAsync,
SignalOnPromptingAsync = SignalPromptAsync,
CharsetOrder = new[] { Encoding.GetEncoding("utf-8"), Encoding.GetEncoding("iso-8859-1") }
}.BuildAsync();
```

Expand Down
108 changes: 0 additions & 108 deletions TelnetNegotiationCore.TestClient/MockClient.cs

This file was deleted.

102 changes: 102 additions & 0 deletions TelnetNegotiationCore.TestClient/MockPipelineClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
using Serilog;
using System.Net.Sockets;
using TelnetNegotiationCore.Interpreters;
using System.IO.Pipelines;
using Pipelines.Sockets.Unofficial;
using System.Buffers;
using System.Text;
using TelnetNegotiationCore.Models;
using System.Net;
using System.Collections.Immutable;

namespace TelnetNegotiationCore.TestClient
{
public class MockPipelineClient
{
readonly ILogger _Logger;

public MockPipelineClient()
{
_Logger = Log.Logger.ForContext<MockPipelineClient>();
}

private async Task WriteToOutputStreamAsync(byte[] arg, PipeWriter writer)
{
try
{
await writer.WriteAsync(new ReadOnlyMemory<byte>(arg), CancellationToken.None);
}
catch (ObjectDisposedException ode)
{
_Logger.Information("Stream has been closed", ode);
}
}

public static Task WriteBackAsync(byte[] writeback, Encoding encoding) =>
Task.Run(() => Console.WriteLine(encoding.GetString(writeback)));

public Task SignalGMCPAsync((string module, string writeback) val, Encoding encoding) =>
Task.Run(() => _Logger.Debug("GMCP Signal: {Module}: {WriteBack}", val.module, val.writeback));

public Task SignalMSSPAsync(MSSPConfig val) =>
Task.Run(() => _Logger.Debug("New MSSP: {@MSSP}", val));

public Task SignalPromptAsync() =>
Task.Run(() => _Logger.Debug("Prompt"));

public Task SignalNAWSAsync(int height, int width) =>
Task.Run(() => _Logger.Debug("Client Height and Width updated: {Height}x{Width}", height, width));

public async Task StartAsync(string address, int port)
{
var client = new TcpClient(address, port);
var stream = client.GetStream();
var pipe = StreamConnection.GetDuplex(stream, new PipeOptions());

var telnet = await new TelnetInterpreter(TelnetInterpreter.TelnetMode.Client, _Logger.ForContext<TelnetInterpreter>())
{
CallbackOnSubmitAsync = WriteBackAsync,
CallbackNegotiationAsync = (x) => WriteToOutputStreamAsync(x, pipe.Output),
SignalOnGMCPAsync = SignalGMCPAsync,
SignalOnMSSPAsync = SignalMSSPAsync,
SignalOnNAWSAsync = SignalNAWSAsync,
SignalOnPromptingAsync = SignalPromptAsync,
CharsetOrder = new[] { Encoding.GetEncoding("utf-8"), Encoding.GetEncoding("iso-8859-1") }
}.BuildAsync();

var backgroundTask = Task.Run(() => ReadFromPipeline(telnet, pipe.Input));

while (true)
{
var read = Console.ReadLine() + Environment.NewLine;

if (telnet != null)
{
await telnet.SendPromptAsync(telnet?.CurrentEncoding.GetBytes(read));
}
}
}

/// <summary>
/// Read data coming from the server and interpret it.
/// </summary>
/// <param name="reader">The Pipeline Reader</param>
/// <returns>An awaitable result</returns>
static async ValueTask ReadFromPipeline(TelnetInterpreter telnet, PipeReader reader)
{
while (true)
{
// await some data being available
ReadResult read = await reader.ReadAtLeastAsync(1);

foreach(var segment in read.Buffer)
{
await telnet.InterpretByteArrayAsync(segment.Span.ToImmutableArray());
}

// tell the pipe that we used everything
reader.AdvanceTo(read.Buffer.End);
}
}
}
}
7 changes: 3 additions & 4 deletions TelnetNegotiationCore.TestClient/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,14 @@ static async Task Main()
{
var log = new LoggerConfiguration()
.Enrich.FromLogContext()
.WriteTo.File(new CompactJsonFormatter(), "logresult.log")
.WriteTo.File(new CompactJsonFormatter(), "LogResult.log")
.WriteTo.Console()
.MinimumLevel.Debug()
.CreateLogger();

Log.Logger = log;
var client = new MockClient("127.0.0.1", 4202, log.ForContext<MockClient>());

await client.StartAsync();
var client = new MockPipelineClient();
await client.StartAsync("127.0.0.1", 4201);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
Expand All @@ -16,7 +16,9 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Pipelines.Sockets.Unofficial" Version="2.2.8" />
<PackageReference Include="Serilog.Formatting.Compact" Version="1.1.0" />
<PackageReference Include="System.IO.Pipelines" Version="8.0.0" />
</ItemGroup>

<ItemGroup>
Expand Down
2 changes: 2 additions & 0 deletions TelnetNegotiationCore.TestServer/MockServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ public void HandleDevice(object obj)

Clients.TryAdd(port, telnet);



_Logger.Information("Connection: {ConnectionState}", "Connected");

for (int currentByte = 0; currentByte != -1; currentByte = input.BaseStream.ReadByte())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
Expand Down
16 changes: 16 additions & 0 deletions TelnetNegotiationCore/Interpreters/TelnetStandardInterpreter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using TelnetNegotiationCore.Models;
using MoreLinq;
using OneOf;
using System.Collections.Immutable;

namespace TelnetNegotiationCore.Interpreters
{
Expand Down Expand Up @@ -255,5 +256,20 @@ public async Task InterpretAsync(byte bt)
await TelnetStateMachine.FireAsync(ParameterizedTrigger(Trigger.ReadNextCharacter), bt);
}
}


/// <summary>
/// Interprets the next byte in an asynchronous way.
/// TODO: Cache the value of IsDefined, or get a way to compile this down to a faster call that doesn't require reflection each time.
/// </summary>
/// <param name="bt">An integer representation of a byte.</param>
/// <returns>Awaitable Task</returns>
public async Task InterpretByteArrayAsync(ImmutableArray<byte> byteArray)
{
foreach (var b in byteArray)
{
await InterpretAsync(b);
}
}
}
}

0 comments on commit f4c06f0

Please sign in to comment.