Skip to content

Commit 4daa5a3

Browse files
Add Wired.IO as an engine
1 parent 8616601 commit 4daa5a3

18 files changed

Lines changed: 951 additions & 0 deletions

Adapters/WiredIO/Adapter.cs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
using GenHTTP.Adapters.WiredIO.Mapping;
2+
using GenHTTP.Api.Content;
3+
using GenHTTP.Api.Infrastructure;
4+
5+
using GenHTTP.Modules.ClientCaching;
6+
using GenHTTP.Modules.Compression;
7+
using GenHTTP.Modules.ErrorHandling;
8+
using GenHTTP.Modules.IO;
9+
10+
using Wired.IO.App;
11+
using Wired.IO.Http11.Context;
12+
13+
namespace GenHTTP.Adapters.WiredIO;
14+
15+
public static class Adapter
16+
{
17+
18+
/// <summary>
19+
/// Registers the given handler to respond to requests to the specified path.
20+
/// </summary>
21+
/// <param name="app">The application to add the mapping to</param>
22+
/// <param name="path">The path to register the handler for</param>
23+
/// <param name="handler">The handler to be registered</param>
24+
/// <param name="companion">An object that will be informed about handled requests and any error</param>
25+
public static void Map(this WiredApp<Http11Context> app, string path, IHandlerBuilder handler, IServerCompanion? companion = null)
26+
=> Map(app, path, handler.Build(), companion);
27+
28+
/// <summary>
29+
/// Registers the given handler to respond to requests to the specified path.
30+
/// </summary>
31+
/// <param name="app">The application to add the mapping to</param>
32+
/// <param name="path">The path to register the handler for</param>
33+
/// <param name="handler">The handler to be registered</param>
34+
/// <param name="companion">An object that will be informed about handled requests and any error</param>
35+
public static void Map(this WiredApp<Http11Context> app, string path, IHandler handler, IServerCompanion? companion = null)
36+
=> app.Map(path + "/{*any}", async context => await Bridge.MapAsync(context, handler, companion: companion, registeredPath: path));
37+
38+
/// <summary>
39+
/// Enables default features on the given handler. This should be used on the
40+
/// outer-most handler only.
41+
/// </summary>
42+
/// <param name="builder">The handler to be configured</param>
43+
/// <param name="errorHandling">If enabled, any exception will be catched and converted into an error response</param>
44+
/// <param name="compression">If enabled, responses will automatically be compressed if possible</param>
45+
/// <param name="clientCaching">If enabled, ETags are attached to any generated response and the tag is evaluated on the next request of the same resource</param>
46+
/// <param name="rangeSupport">If enabled, clients can request ranges instead of the complete response body</param>
47+
/// <typeparam name="T">The type of the handler builder which will be returned to allow the factory pattern</typeparam>
48+
/// <returns>The handler builder instance to be chained</returns>
49+
public static T Defaults<T>(this T builder, bool errorHandling = true, bool compression = true, bool clientCaching = true, bool rangeSupport = false) where T : IHandlerBuilder<T>
50+
{
51+
if (compression)
52+
{
53+
builder.Add(CompressedContent.Default());
54+
}
55+
56+
if (rangeSupport)
57+
{
58+
builder.Add(RangeSupport.Create());
59+
}
60+
61+
if (clientCaching)
62+
{
63+
builder.Add(ClientCache.Validation());
64+
}
65+
66+
if (errorHandling)
67+
{
68+
builder.Add(ErrorHandler.Default());
69+
}
70+
71+
return builder;
72+
}
73+
74+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
5+
<TargetFramework>net9.0</TargetFramework>
6+
7+
<Description>Adapter to run GenHTTP handlers within an Wired.IO app.</Description>
8+
<PackageTags>Wired.IO Adapter GenHTTP</PackageTags>
9+
10+
</PropertyGroup>
11+
12+
<ItemGroup>
13+
14+
<PackageReference Include="Wired.IO" Version="9.5.0" />
15+
16+
<ProjectReference Include="..\..\API\GenHTTP.Api.csproj"/>
17+
18+
<ProjectReference Include="..\..\Engine\Shared\GenHTTP.Engine.Shared.csproj" />
19+
20+
<ProjectReference Include="..\..\Modules\ClientCaching\GenHTTP.Modules.ClientCaching.csproj" />
21+
22+
<ProjectReference Include="..\..\Modules\Compression\GenHTTP.Modules.Compression.csproj" />
23+
24+
<ProjectReference Include="..\..\Modules\IO\GenHTTP.Modules.IO.csproj"/>
25+
26+
<ProjectReference Include="..\..\Modules\ErrorHandling\GenHTTP.Modules.ErrorHandling.csproj"/>
27+
28+
</ItemGroup>
29+
30+
</Project>

Adapters/WiredIO/Mapping/Bridge.cs

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
using GenHTTP.Adapters.WiredIO.Server;
2+
using GenHTTP.Adapters.WiredIO.Types;
3+
4+
using GenHTTP.Api.Content;
5+
using GenHTTP.Api.Infrastructure;
6+
using GenHTTP.Api.Protocol;
7+
8+
namespace GenHTTP.Adapters.WiredIO.Mapping;
9+
10+
public static class Bridge
11+
{
12+
13+
public static async ValueTask MapAsync(HttpContext context, IHandler handler, IServer? server = null, IServerCompanion? companion = null, string? registeredPath = null)
14+
{
15+
var actualServer = server ?? new ImplicitServer(handler, companion);
16+
17+
try
18+
{
19+
using var request = new Request(actualServer, context);
20+
21+
if (registeredPath != null)
22+
{
23+
AdvanceTo(request, registeredPath);
24+
}
25+
26+
using var response = await handler.HandleAsync(request);
27+
28+
if (response == null)
29+
{
30+
context.Response.StatusCode = 404;
31+
}
32+
else
33+
{
34+
await WriteAsync(response, context);
35+
36+
actualServer.Companion?.OnRequestHandled(request, response);
37+
}
38+
}
39+
catch (Exception e)
40+
{
41+
actualServer.Companion?.OnServerError(ServerErrorScope.ServerConnection, context.Connection.RemoteIpAddress, e);
42+
throw;
43+
}
44+
}
45+
46+
private static async ValueTask WriteAsync(IResponse response, HttpContext context)
47+
{
48+
var target = context.Response;
49+
50+
target.StatusCode = response.Status.RawStatus;
51+
52+
foreach (var header in response.Headers)
53+
{
54+
target.Headers.Append(header.Key, header.Value);
55+
}
56+
57+
if (response.Modified != null)
58+
{
59+
target.Headers.LastModified = response.Modified.Value.ToUniversalTime().ToString("r");
60+
}
61+
62+
if (response.Expires != null)
63+
{
64+
target.Headers.Expires = response.Expires.Value.ToUniversalTime().ToString("r");
65+
}
66+
67+
if (response.HasCookies)
68+
{
69+
foreach (var cookie in response.Cookies)
70+
{
71+
if (cookie.Value.MaxAge != null)
72+
{
73+
target.Cookies.Append(cookie.Key, cookie.Value.Value, new()
74+
{
75+
MaxAge = TimeSpan.FromSeconds(cookie.Value.MaxAge.Value)
76+
});
77+
}
78+
else
79+
{
80+
target.Cookies.Append(cookie.Key, cookie.Value.Value);
81+
}
82+
}
83+
}
84+
85+
if (response.Content != null)
86+
{
87+
target.ContentLength = (long?)response.ContentLength ?? (long?)response.Content.Length;
88+
89+
target.ContentType = response.ContentType?.Charset != null ? $"{response.ContentType?.RawType}; charset={response.ContentType?.Charset}" : response.ContentType?.RawType;
90+
91+
if (response.ContentEncoding != null)
92+
{
93+
target.Headers.ContentEncoding = response.ContentEncoding;
94+
}
95+
96+
await response.Content.WriteAsync(target.Body, 65 * 1024);
97+
}
98+
}
99+
100+
private static void AdvanceTo(Request request, string registeredPath)
101+
{
102+
var parts = registeredPath.Split('/', StringSplitOptions.RemoveEmptyEntries);
103+
104+
foreach (var _ in parts)
105+
{
106+
request.Target.Advance();
107+
}
108+
}
109+
110+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
using GenHTTP.Api.Infrastructure;
2+
3+
namespace GenHTTP.Adapters.WiredIO.Server;
4+
5+
public class EmptyEndpoints : List<IEndPoint>, IEndPointCollection;
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
using System.Runtime.InteropServices;
2+
using GenHTTP.Api.Content;
3+
using GenHTTP.Api.Infrastructure;
4+
5+
namespace GenHTTP.Adapters.WiredIO.Server;
6+
7+
public sealed class ImplicitServer : IServer
8+
{
9+
10+
#region Get-/Setters
11+
12+
public string Version => RuntimeInformation.FrameworkDescription;
13+
14+
public bool Running { get; }
15+
16+
public bool Development
17+
{
18+
get
19+
{
20+
var env = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
21+
return string.Compare(env, "Development", StringComparison.OrdinalIgnoreCase) == 0;
22+
}
23+
}
24+
25+
public IEndPointCollection EndPoints { get; }
26+
27+
public IServerCompanion? Companion { get; }
28+
29+
public IHandler Handler { get; }
30+
31+
#endregion
32+
33+
#region Initialization
34+
35+
public ImplicitServer(IHandler handler, IServerCompanion? companion)
36+
{
37+
Handler = handler;
38+
Companion = companion;
39+
40+
EndPoints = new EmptyEndpoints();
41+
42+
Running = true;
43+
}
44+
45+
#endregion
46+
47+
#region Functionality
48+
49+
public ValueTask DisposeAsync() => new();
50+
51+
public ValueTask StartAsync() => throw new InvalidOperationException("Server is managed by ASP.NET Core and cannot be started");
52+
53+
#endregion
54+
55+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using System.Net;
2+
using System.Security.Cryptography.X509Certificates;
3+
4+
using GenHTTP.Api.Infrastructure;
5+
using GenHTTP.Api.Protocol;
6+
7+
namespace GenHTTP.Adapters.WiredIO.Types;
8+
9+
public sealed class ClientConnection : IClientConnection
10+
{
11+
12+
#region Get-/Setters
13+
14+
public IPAddress IPAddress => Info.RemoteIpAddress ?? throw new InvalidOperationException("Remote client IP address is not known");
15+
16+
public ClientProtocol? Protocol { get; }
17+
18+
public string? Host => Request.Host.HasValue ? Request.Host.Value : null;
19+
20+
public X509Certificate? Certificate => Info.ClientCertificate;
21+
22+
private ConnectionInfo Info { get; }
23+
24+
private HttpRequest Request { get; }
25+
26+
#endregion
27+
28+
#region Initialization
29+
30+
public ClientConnection(ConnectionInfo info, HttpRequest request)
31+
{
32+
Info = info;
33+
Request = request;
34+
35+
Protocol = (request.IsHttps) ? ClientProtocol.Https : ClientProtocol.Http;
36+
}
37+
38+
#endregion
39+
40+
}

Adapters/WiredIO/Types/Cookies.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using GenHTTP.Api.Protocol;
2+
3+
namespace GenHTTP.Adapters.WiredIO.Types;
4+
5+
public sealed class Cookies : Dictionary<string, Cookie>, ICookieCollection
6+
{
7+
8+
public Cookies(HttpRequest request)
9+
{
10+
foreach (var cookie in request.Cookies)
11+
{
12+
Add(cookie.Key, new(cookie.Key, cookie.Value));
13+
}
14+
}
15+
16+
public void Dispose()
17+
{
18+
19+
}
20+
21+
}

0 commit comments

Comments
 (0)