Last active
May 27, 2021 01:48
-
-
Save HurricanKai/1709f09e3a63c8ba0abf2c48d6a0bf32 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<Project Sdk="Microsoft.NET.Sdk.Web"> | |
<PropertyGroup> | |
<OutputType>Exe</OutputType> | |
<TargetFramework>netcoreapp3.1</TargetFramework> | |
<LangVersion>8</LangVersion> | |
</PropertyGroup> | |
<ItemGroup> | |
<PackageReference Include="System.IO.Pipelines" Version="4.7.1" /> | |
</ItemGroup> | |
</Project> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Buffers; | |
using System.IO.Pipelines; | |
using System.Text.Unicode; | |
using System.Threading.Tasks; | |
using Microsoft.AspNetCore.Builder; | |
using Microsoft.AspNetCore.Connections; | |
using Microsoft.AspNetCore.Hosting; | |
using Microsoft.AspNetCore.Server.Kestrel.Core; | |
using Microsoft.Extensions.DependencyInjection; | |
using Microsoft.Extensions.Hosting; | |
using Microsoft.Extensions.Logging; | |
// note that the SDK of this project is the web SDK, as specified in .csproj | |
// therefore lots of things that ar normally NuGet are pre-installed | |
namespace Bedrock | |
{ | |
class Program | |
{ | |
static Task Main(string[] args) => | |
Host.CreateDefaultBuilder(args) | |
.ConfigureLogging((builder => builder.SetMinimumLevel(LogLevel.Debug))) | |
.ConfigureWebHostDefaults(builder => | |
{ | |
builder.UseStartup<Startup>() | |
.UseKestrel(options => | |
{ | |
options.ListenAnyIP(10000, listenOptions => | |
{ | |
listenOptions.Protocols = HttpProtocols.None; | |
#if DEBUG | |
listenOptions.UseConnectionLogging(); | |
#endif | |
listenOptions.UseConnectionHandler<SimpleConnectionHandler>(); | |
}); | |
}); | |
}) | |
.UseConsoleLifetime() | |
.RunConsoleAsync(); | |
} | |
public class Startup | |
{ | |
public void Configure(IApplicationBuilder app) | |
{ | |
} | |
public void ConfigureServices(IServiceCollection services) | |
{ | |
services.AddSingleton<ISomeService, ServiceImpl>(); | |
} | |
} | |
public interface ISomeService | |
{ | |
string GetResponse(string input); | |
} | |
public class ServiceImpl : ISomeService | |
{ | |
public string GetResponse(string input) => input + "!"; | |
} | |
public class SimpleConnectionHandler : ConnectionHandler | |
{ | |
private readonly ISomeService _dep; | |
private readonly ILogger _logger; | |
public SimpleConnectionHandler(ISomeService dep, ILogger<SimpleConnectionHandler> logger) | |
{ | |
_dep = dep; | |
_logger = logger; | |
} | |
public override async Task OnConnectedAsync(ConnectionContext connection) | |
{ | |
var reader = connection.Transport.Input; | |
var writer = connection.Transport.Output; | |
await Process(reader, writer); | |
} | |
// SEE https://devblogs.microsoft.com/dotnet/system-io-pipelines-high-performance-io-in-net/ | |
private async Task Process(PipeReader reader, PipeWriter writer) | |
{ | |
while (true) | |
{ | |
ReadResult result = await reader.ReadAsync(); | |
ReadOnlySequence<byte> buffer = result.Buffer; | |
buffer = ProcessBuffer(writer, buffer); | |
// Tell the PipeReader how much of the buffer we have consumed | |
reader.AdvanceTo(buffer.Start, buffer.End); | |
// Stop reading if there's no more data coming | |
if (result.IsCompleted) | |
{ | |
break; | |
} | |
} | |
} | |
private ReadOnlySequence<byte> ProcessBuffer(PipeWriter writer, ReadOnlySequence<byte> buffer) | |
{ | |
SequencePosition? position = null; | |
do | |
{ | |
// Look for a EOL in the buffer | |
position = buffer.PositionOf((byte) '\0'); | |
if (position != null) | |
{ | |
// Process the line | |
var response = ProcessLine(buffer.Slice(0, position.Value)); | |
// Skip the line + the \0 character (basically position) | |
buffer = buffer.Slice(buffer.GetPosition(1, position.Value)); | |
// Use writer.GetSpan & writer.Advance to write the response. | |
} | |
} while (position != null); | |
return buffer; | |
} | |
private ReadOnlySpan<byte> ProcessLine(ReadOnlySequence<byte> slice) | |
{ | |
var arr = slice.Slice(0, slice.Length - 1).ToArray(); | |
var dest = new char[arr.Length]; | |
Utf8.ToUtf16(arr, dest, out _, out _); | |
var str = new string(dest); | |
_logger.LogInformation($"Received: {str}"); | |
var response = _dep.GetResponse(str); | |
var dest2 = new byte[response.Length]; | |
Utf8.FromUtf16(response.AsSpan(), dest2, out _, out _); | |
return dest2; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment