Skip to content

Commit

Permalink
First draft
Browse files Browse the repository at this point in the history
  • Loading branch information
Kaliumhexacyanoferrat committed Oct 18, 2024
1 parent 430641b commit 0236ae5
Show file tree
Hide file tree
Showing 17 changed files with 523 additions and 30 deletions.
2 changes: 2 additions & 0 deletions API/Infrastructure/IClientConnection.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Net;
using System.Net.Sockets;
using GenHTTP.Api.Protocol;

namespace GenHTTP.Api.Infrastructure;
Expand All @@ -25,4 +26,5 @@ public interface IClientConnection
/// to the server.
/// </summary>
string? Host { get; }

}
4 changes: 3 additions & 1 deletion API/Protocol/IRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ public interface IRequest : IDisposable
/// <returns>The newly created response</returns>
IResponseBuilder Respond();

UpgradeInfo Upgrade();

#endregion

#region General Infrastructure
Expand All @@ -51,7 +53,7 @@ public interface IRequest : IDisposable

/// <summary>
/// If the request has been forwarded by a proxy, the client property
/// will return the originating client where this property will return
/// will return the originating client while this property will return
/// the information of the proxy.
/// </summary>
IClientConnection LocalClient { get; }
Expand Down
2 changes: 2 additions & 0 deletions API/Protocol/IResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ public interface IResponse : IDisposable
/// </summary>
FlexibleResponseStatus Status { get; set; }

public bool Upgraded { get; }

#endregion

#region Headers
Expand Down
5 changes: 5 additions & 0 deletions API/Protocol/UpgradeInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
using System.Net.Sockets;

namespace GenHTTP.Api.Protocol;

public record UpgradeInfo(Socket Socket, IResponse Response);
1 change: 1 addition & 0 deletions Engine/Protocol/ClientConnection.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Net;
using System.Net.Sockets;
using GenHTTP.Api.Infrastructure;
using GenHTTP.Api.Protocol;

Expand Down
66 changes: 42 additions & 24 deletions Engine/Protocol/ClientHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ internal sealed class ClientHandler

internal ClientHandler(Socket socket, Stream stream, IServer server, IEndPoint endPoint, NetworkConfiguration config)
{
Socket = socket;

Server = server;
EndPoint = endPoint;

Expand All @@ -43,6 +45,8 @@ internal ClientHandler(Socket socket, Stream stream, IServer server, IEndPoint e

internal IServer Server { get; }

internal Socket Socket { get; }

internal IEndPoint EndPoint { get; }

internal NetworkConfiguration Configuration { get; }
Expand All @@ -61,41 +65,46 @@ internal ClientHandler(Socket socket, Stream stream, IServer server, IEndPoint e

internal async PooledValueTask Run()
{
var status = ConnectionStatus.Close;

try
{
await HandlePipe(PipeReader.Create(Stream, ReaderOptions)).ConfigureAwait(false);
status = await HandlePipe(PipeReader.Create(Stream, ReaderOptions)).ConfigureAwait(false);
}
catch (Exception e)
{
Server.Companion?.OnServerError(ServerErrorScope.ClientConnection, Connection.GetAddress(), e);
}
finally
{
try
{
await Stream.DisposeAsync();
}
catch (Exception e)
if (status != ConnectionStatus.Upgraded)
{
Server.Companion?.OnServerError(ServerErrorScope.ClientConnection, Connection.GetAddress(), e);
}
try
{
await Stream.DisposeAsync();
}
catch (Exception e)
{
Server.Companion?.OnServerError(ServerErrorScope.ClientConnection, Connection.GetAddress(), e);
}

try
{
Connection.Shutdown(SocketShutdown.Both);
await Connection.DisconnectAsync(false);
Connection.Close();
try
{
Connection.Shutdown(SocketShutdown.Both);
await Connection.DisconnectAsync(false);
Connection.Close();

Connection.Dispose();
}
catch (Exception e)
{
Server.Companion?.OnServerError(ServerErrorScope.ClientConnection, Connection.GetAddress(), e);
Connection.Dispose();
}
catch (Exception e)
{
Server.Companion?.OnServerError(ServerErrorScope.ClientConnection, Connection.GetAddress(), e);
}
}
}
}

private async PooledValueTask HandlePipe(PipeReader reader)
private async PooledValueTask<ConnectionStatus> HandlePipe(PipeReader reader)
{
try
{
Expand All @@ -109,9 +118,11 @@ private async PooledValueTask HandlePipe(PipeReader reader)
{
while (Server.Running && (request = await parser.TryParseAsync(buffer)) is not null)
{
if (!await HandleRequest(request, !buffer.ReadRequired))
var status = await HandleRequest(request, !buffer.ReadRequired);

if (status is ConnectionStatus.Close or ConnectionStatus.Upgraded)
{
break;
return status;
}
}
}
Expand All @@ -132,21 +143,28 @@ private async PooledValueTask HandlePipe(PipeReader reader)
{
await reader.CompleteAsync();
}

return ConnectionStatus.Close;
}

private async PooledValueTask<bool> HandleRequest(RequestBuilder builder, bool dataRemaining)
private async PooledValueTask<ConnectionStatus> HandleRequest(RequestBuilder builder, bool dataRemaining)
{
using var request = builder.Connection(Server, EndPoint, Connection.GetAddress()).Build();
using var request = builder.Connection(Server, Socket, EndPoint, Connection.GetAddress()).Build();

KeepAlive ??= request["Connection"]?.Equals("Keep-Alive", StringComparison.InvariantCultureIgnoreCase) ?? request.ProtocolType == HttpProtocol.Http11;

var keepAlive = KeepAlive.Value;

using var response = await Server.Handler.HandleAsync(request) ?? throw new InvalidOperationException("The root request handler did not return a response");

if (response.Upgraded)
{
return ConnectionStatus.Upgraded;
}

var success = await ResponseHandler.Handle(request, response, keepAlive, dataRemaining);

return success && keepAlive;
return (success && keepAlive) ? ConnectionStatus.KeepAlive : ConnectionStatus.Close;
}

private async PooledValueTask SendError(Exception e, ResponseStatus status)
Expand Down
8 changes: 8 additions & 0 deletions Engine/Protocol/ConnectionStatus.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace GenHTTP.Engine.Protocol;

public enum ConnectionStatus
{
Close,
KeepAlive,
Upgraded
}
10 changes: 8 additions & 2 deletions Engine/Protocol/Request.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using GenHTTP.Api.Infrastructure;
using System.Net.Sockets;
using GenHTTP.Api.Infrastructure;
using GenHTTP.Api.Protocol;
using GenHTTP.Api.Routing;

Expand All @@ -9,6 +10,7 @@ namespace GenHTTP.Engine.Protocol;
/// </summary>
internal sealed class Request : IRequest
{
private Socket _Socket;

private FlexibleContentType? _ContentType;
private ICookieCollection? _Cookies;
Expand All @@ -21,10 +23,12 @@ internal sealed class Request : IRequest

#region Initialization

internal Request(IServer server, IEndPoint endPoint, IClientConnection client, IClientConnection localClient, HttpProtocol protocol, FlexibleRequestMethod method,
internal Request(IServer server, Socket socket, IEndPoint endPoint, IClientConnection client, IClientConnection localClient, HttpProtocol protocol, FlexibleRequestMethod method,
RoutingTarget target, IHeaderCollection headers, ICookieCollection? cookies, IForwardingCollection? forwardings,
IRequestQuery? query, Stream? content)
{
_Socket = socket;

Client = client;
LocalClient = localClient;

Expand All @@ -50,6 +54,8 @@ internal Request(IServer server, IEndPoint endPoint, IClientConnection client, I

public IResponseBuilder Respond() => new ResponseBuilder().Status(ResponseStatus.Ok);

public UpgradeInfo Upgrade() => new(_Socket, new Response() { Upgraded = true });

#endregion

#region Get-/Setters
Expand Down
13 changes: 10 additions & 3 deletions Engine/Protocol/RequestBuilder.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Net;
using System.Net.Sockets;
using GenHTTP.Api.Infrastructure;
using GenHTTP.Api.Protocol;
using GenHTTP.Api.Routing;
Expand All @@ -7,7 +8,7 @@ namespace GenHTTP.Engine.Protocol;

internal sealed class RequestBuilder : IBuilder<IRequest>
{

private Socket? _Socket;
private IPAddress? _Address;

private Stream? _Content;
Expand Down Expand Up @@ -52,8 +53,9 @@ private ForwardingCollection Forwardings

#region Functionality

public RequestBuilder Connection(IServer server, IEndPoint endPoint, IPAddress? address)
public RequestBuilder Connection(IServer server, Socket socket, IEndPoint endPoint, IPAddress? address)
{
_Socket = socket;
_Server = server;
_Address = address;
_EndPoint = endPoint;
Expand Down Expand Up @@ -118,6 +120,11 @@ public IRequest Build()
throw new BuilderMissingPropertyException("Server");
}

if (_Socket == null)
{
throw new BuilderMissingPropertyException("Socket");
}

if (_EndPoint is null)
{
throw new BuilderMissingPropertyException("EndPoint");
Expand Down Expand Up @@ -159,7 +166,7 @@ public IRequest Build()

var client = DetermineClient() ?? localClient;

return new Request(_Server, _EndPoint, client, localClient, (HttpProtocol)_Protocol, _RequestMethod,
return new Request(_Server, _Socket, _EndPoint, client, localClient, (HttpProtocol)_Protocol, _RequestMethod,
_Target, Headers, _Cookies, _Forwardings, _Query, _Content);
}
catch (Exception)
Expand Down
2 changes: 2 additions & 0 deletions Engine/Protocol/Response.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ public void SetCookie(Cookie cookie)

public ulong? ContentLength { get; set; }

public bool Upgraded { get; set; }

public IResponseContent? Content { get; set; }

public ICookieCollection Cookies => WriteableCookies;
Expand Down
7 changes: 7 additions & 0 deletions GenHTTP.sln
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GenHTTP.Modules.Pages", "Mo
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GenHTTP.Modules.OpenApi", "Modules\OpenApi\GenHTTP.Modules.OpenApi.csproj", "{A5149821-D510-4854-9DC9-D489323BC545}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GenHTTP.Modules.Websockets", "Modules\Websockets\GenHTTP.Modules.Websockets.csproj", "{9D3D3B40-691D-4EE1-B948-82525F28FBB2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -216,6 +218,10 @@ Global
{A5149821-D510-4854-9DC9-D489323BC545}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A5149821-D510-4854-9DC9-D489323BC545}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A5149821-D510-4854-9DC9-D489323BC545}.Release|Any CPU.Build.0 = Release|Any CPU
{9D3D3B40-691D-4EE1-B948-82525F28FBB2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9D3D3B40-691D-4EE1-B948-82525F28FBB2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9D3D3B40-691D-4EE1-B948-82525F28FBB2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9D3D3B40-691D-4EE1-B948-82525F28FBB2}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -253,6 +259,7 @@ Global
{FC7F7D69-5ED0-4D3B-B201-EDBCEC71B8DB} = {A7930BE4-0549-4197-B139-B1A73E74B464}
{4CDA31EB-A6C2-4634-9379-9306D3996B21} = {23B23225-275E-4F52-8B29-6F44C85B6ACE}
{A5149821-D510-4854-9DC9-D489323BC545} = {23B23225-275E-4F52-8B29-6F44C85B6ACE}
{9D3D3B40-691D-4EE1-B948-82525F28FBB2} = {23B23225-275E-4F52-8B29-6F44C85B6ACE}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {9C67B3AF-0BF6-4E21-8C39-3F74CFCF9632}
Expand Down
53 changes: 53 additions & 0 deletions Modules/Websockets/GenHTTP.Modules.Websockets.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>

<TargetFrameworks>net8.0;net9.0</TargetFrameworks>

<LangVersion>13.0</LangVersion>
<Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<ImplicitUsings>enable</ImplicitUsings>

<AssemblyVersion>9.0.0.0</AssemblyVersion>
<FileVersion>9.0.0.0</FileVersion>
<Version>9.0.0</Version>

<Authors>Andreas Nägeli</Authors>
<Company/>

<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageProjectUrl>https://genhttp.org/</PackageProjectUrl>

<Description>Allows to handle websocket connection to a GenHTTP server</Description>
<PackageTags>HTTP Webserver C# Module Websockets</PackageTags>

<PublishRepositoryUrl>true</PublishRepositoryUrl>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>

<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>CS1591,CS1587,CS1572,CS1573</NoWarn>

<PackageIcon>icon.png</PackageIcon>

</PropertyGroup>

<ItemGroup>

<None Include="..\..\LICENSE" Pack="true" PackagePath="\"/>
<None Include="..\..\Resources\icon.png" Pack="true" PackagePath="\"/>

</ItemGroup>

<ItemGroup>

<ProjectReference Include="..\..\API\GenHTTP.Api.csproj"/>

<PackageReference Include="Fleck" Version="1.2.0" />

<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All"/>

</ItemGroup>

</Project>
Loading

0 comments on commit 0236ae5

Please sign in to comment.