From d813fe71b7b6b419039049a51e97e68a314b7b25 Mon Sep 17 00:00:00 2001 From: Diogo Martins Date: Sat, 31 Jan 2026 20:40:39 +0000 Subject: [PATCH 1/3] Initial Commit --- .../GenHttp.Engine.Rocket/GenHttp.Engine.Rocket.csproj | 9 +++++++++ GenHTTP.slnx | 1 + 2 files changed, 10 insertions(+) create mode 100644 Engine/GenHttp.Engine.Rocket/GenHttp.Engine.Rocket.csproj diff --git a/Engine/GenHttp.Engine.Rocket/GenHttp.Engine.Rocket.csproj b/Engine/GenHttp.Engine.Rocket/GenHttp.Engine.Rocket.csproj new file mode 100644 index 00000000..237d6616 --- /dev/null +++ b/Engine/GenHttp.Engine.Rocket/GenHttp.Engine.Rocket.csproj @@ -0,0 +1,9 @@ + + + + net10.0 + enable + enable + + + diff --git a/GenHTTP.slnx b/GenHTTP.slnx index 36643172..98a0bd77 100644 --- a/GenHTTP.slnx +++ b/GenHTTP.slnx @@ -6,6 +6,7 @@ + From 9c2c95d8d4757d662f96a1fafc4609362daa4a89 Mon Sep 17 00:00:00 2001 From: Diogo Martins Date: Sat, 31 Jan 2026 23:16:20 +0000 Subject: [PATCH 2/3] wip - Adding infrastructure and protocol classes --- .../GenHTTP.Engine.Rocket.csproj | 19 ++ Engine/GenHTTP.Engine.Rocket/Host.cs | 13 + .../Infrastructure/RocketServer.cs | 66 +++++ .../Infrastructure/RocketServerBuilder.cs | 21 ++ .../Protocol/ClientContext.cs | 39 +++ .../Protocol/ClientHandler.cs | 44 ++++ .../GenHTTP.Engine.Rocket/Protocol/Request.cs | 226 ++++++++++++++++++ .../Protocol/RequestQuery.cs | 14 ++ Engine/GenHTTP.Engine.Rocket/Server.cs | 13 + .../GenHttp.Engine.Rocket.csproj | 9 - GenHTTP.slnx | 2 +- Playground/GenHTTP.Playground.csproj | 2 + Playground/Program.cs | 29 ++- 13 files changed, 474 insertions(+), 23 deletions(-) create mode 100644 Engine/GenHTTP.Engine.Rocket/GenHTTP.Engine.Rocket.csproj create mode 100644 Engine/GenHTTP.Engine.Rocket/Host.cs create mode 100644 Engine/GenHTTP.Engine.Rocket/Infrastructure/RocketServer.cs create mode 100644 Engine/GenHTTP.Engine.Rocket/Infrastructure/RocketServerBuilder.cs create mode 100644 Engine/GenHTTP.Engine.Rocket/Protocol/ClientContext.cs create mode 100644 Engine/GenHTTP.Engine.Rocket/Protocol/ClientHandler.cs create mode 100644 Engine/GenHTTP.Engine.Rocket/Protocol/Request.cs create mode 100644 Engine/GenHTTP.Engine.Rocket/Protocol/RequestQuery.cs create mode 100644 Engine/GenHTTP.Engine.Rocket/Server.cs delete mode 100644 Engine/GenHttp.Engine.Rocket/GenHttp.Engine.Rocket.csproj diff --git a/Engine/GenHTTP.Engine.Rocket/GenHTTP.Engine.Rocket.csproj b/Engine/GenHTTP.Engine.Rocket/GenHTTP.Engine.Rocket.csproj new file mode 100644 index 00000000..c784e95b --- /dev/null +++ b/Engine/GenHTTP.Engine.Rocket/GenHTTP.Engine.Rocket.csproj @@ -0,0 +1,19 @@ + + + + net10.0 + enable + enable + GenHTTP.Engine.Rocket + + + + + + + + + + + + diff --git a/Engine/GenHTTP.Engine.Rocket/Host.cs b/Engine/GenHTTP.Engine.Rocket/Host.cs new file mode 100644 index 00000000..90c43561 --- /dev/null +++ b/Engine/GenHTTP.Engine.Rocket/Host.cs @@ -0,0 +1,13 @@ +using GenHTTP.Api.Infrastructure; +using GenHTTP.Engine.Shared.Hosting; +using URocket.Engine.Configs; + +namespace GenHTTP.Engine.Rocket; + +public static class Host +{ + public static IServerHost Create(EngineOptions engineOptions) + { + return new ServerHost(Server.Create(engineOptions)); + } +} \ No newline at end of file diff --git a/Engine/GenHTTP.Engine.Rocket/Infrastructure/RocketServer.cs b/Engine/GenHTTP.Engine.Rocket/Infrastructure/RocketServer.cs new file mode 100644 index 00000000..70ed272d --- /dev/null +++ b/Engine/GenHTTP.Engine.Rocket/Infrastructure/RocketServer.cs @@ -0,0 +1,66 @@ +using System.Reflection; +using GenHTTP.Api.Content; +using GenHTTP.Api.Infrastructure; +using GenHTTP.Engine.Shared.Infrastructure; +using URocket.Engine.Configs; + +namespace GenHTTP.Engine.Rocket.Infrastructure; + +internal sealed class RocketServer : IServer +{ + public string Version { get; } + + public bool Running { get; } + + public bool Development => Configuration.DevelopmentMode; + + public IEndPointCollection EndPoints { get; } = null!; + + public IServerCompanion? Companion { get; } + + public IHandler Handler { get; } + + internal URocket.Engine.Engine Engine { get; } + + internal ServerConfiguration Configuration { get; } + + internal RocketServer(IServerCompanion? companion, IHandler handler, ServerConfiguration config, EngineOptions engineOptions) + { + Version = Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? "(n/a)"; + + Companion = companion; + Handler = handler; + + Configuration = config; + + Engine = new URocket.Engine.Engine(engineOptions); + + Running = Engine.ServerRunning; + } + + private static async ValueTask PrepareHandlerAsync(IHandler handler, IServerCompanion? companion) + { + try + { + await handler.PrepareAsync(); + } + catch (Exception e) + { + companion?.OnServerError(ServerErrorScope.General, null, e); + } + } + + public async ValueTask StartAsync() + { + await PrepareHandlerAsync(Handler, Companion); + + Engine.Listen(); + } + + public ValueTask DisposeAsync() + { + Engine.Stop(); + + return ValueTask.CompletedTask; + } +} \ No newline at end of file diff --git a/Engine/GenHTTP.Engine.Rocket/Infrastructure/RocketServerBuilder.cs b/Engine/GenHTTP.Engine.Rocket/Infrastructure/RocketServerBuilder.cs new file mode 100644 index 00000000..be394c44 --- /dev/null +++ b/Engine/GenHTTP.Engine.Rocket/Infrastructure/RocketServerBuilder.cs @@ -0,0 +1,21 @@ +using GenHTTP.Api.Content; +using GenHTTP.Api.Infrastructure; +using GenHTTP.Engine.Shared.Infrastructure; +using URocket.Engine.Configs; + +namespace GenHTTP.Engine.Rocket.Infrastructure; + +public class RocketServerBuilder : ServerBuilder +{ + private readonly EngineOptions _engineOptions; + + public RocketServerBuilder(EngineOptions engineOptions) + { + _engineOptions = engineOptions; + } + + protected override IServer Build(IServerCompanion? companion, ServerConfiguration config, IHandler handler) + { + return new RocketServer(companion, handler, config, _engineOptions); + } +} \ No newline at end of file diff --git a/Engine/GenHTTP.Engine.Rocket/Protocol/ClientContext.cs b/Engine/GenHTTP.Engine.Rocket/Protocol/ClientContext.cs new file mode 100644 index 00000000..7b288b21 --- /dev/null +++ b/Engine/GenHTTP.Engine.Rocket/Protocol/ClientContext.cs @@ -0,0 +1,39 @@ +using GenHTTP.Engine.Shared.Types; +using Microsoft.Extensions.ObjectPool; + +namespace GenHTTP.Engine.Rocket.Protocol; + +internal class ClientContext +{ + internal Request Request { get; } + + internal ResponseBuilder ResponseBuilder { get; } + + internal Response Response { get; } + + internal ClientContext() + { + Response = new Response(); + + ResponseBuilder = new ResponseBuilder(Response); + + Request = new Request(ResponseBuilder); + } + + internal void Reset() + { + Request.Reset(); + Response.Reset(); + } +} + +internal class ClientContextPolicy : PooledObjectPolicy +{ + public override ClientContext Create() => new(); + + public override bool Return(ClientContext obj) + { + obj.Reset(); + return true; + } +} diff --git a/Engine/GenHTTP.Engine.Rocket/Protocol/ClientHandler.cs b/Engine/GenHTTP.Engine.Rocket/Protocol/ClientHandler.cs new file mode 100644 index 00000000..954b8c41 --- /dev/null +++ b/Engine/GenHTTP.Engine.Rocket/Protocol/ClientHandler.cs @@ -0,0 +1,44 @@ +using GenHTTP.Engine.Rocket.Infrastructure; +using Microsoft.Extensions.ObjectPool; + +namespace GenHTTP.Engine.Rocket.Protocol; + +internal sealed class ClientHandler +{ + private static readonly DefaultObjectPool ContextPool = new(new ClientContextPolicy(), 1024 * 64); + + private readonly RocketServer _server; + + public ClientHandler(RocketServer server) + { + _server = server; + } + + public async Task RunAsync(CancellationToken cancellationToken = default) + { + try + { + while (_server.Engine.ServerRunning) + { + var conn = await _server.Engine.AcceptAsync(cancellationToken); + + if(conn is null) + continue; + + Console.WriteLine($"Connection: {conn.ClientFd}"); + } + } + catch (OperationCanceledException) + { + Console.WriteLine("Signaled to stop"); + } + catch (Exception e) + { + Console.WriteLine($"[Cowabunga!] {e.Message}"); + } + finally + { + _server.Engine.Stop(); + } + } +} \ No newline at end of file diff --git a/Engine/GenHTTP.Engine.Rocket/Protocol/Request.cs b/Engine/GenHTTP.Engine.Rocket/Protocol/Request.cs new file mode 100644 index 00000000..06e527be --- /dev/null +++ b/Engine/GenHTTP.Engine.Rocket/Protocol/Request.cs @@ -0,0 +1,226 @@ +using GenHTTP.Api.Infrastructure; +using GenHTTP.Api.Protocol; +using GenHTTP.Api.Routing; +using GenHTTP.Engine.Shared.Types; + +namespace GenHTTP.Engine.Rocket.Protocol; + +/// +/// Provides methods to access a recieved http request. +/// +internal sealed class Request : IRequest +{ + private readonly ResponseBuilder _responseBuilder; + + private bool _freshResponse = true; + + private IServer? _server; + private IEndPoint? _endPoint; + + private IClientConnection? _clientConnection = null!; + private IClientConnection? _localClient = null!; + + private Stream? _stream; + + private FlexibleRequestMethod? _method; + private RoutingTarget? _target; + + private readonly RequestHeaderCollection _headers = new(); + + private readonly CookieCollection _cookies = new(); + + private readonly ForwardingCollection _forwardings = new(); + + private readonly RequestProperties _properties = new(); + + private readonly RequestQuery _query = new(); + + private Stream? _content; + private FlexibleContentType? _contentType; + + #region Initialization + + internal Request(ResponseBuilder responseBuilder) + { + _responseBuilder = responseBuilder; + } + + #endregion + + #region Get-/Setters + + public IServer Server => _server ?? throw new InvalidOperationException("Request is not initialized yet"); + + public IEndPoint EndPoint => _endPoint ?? throw new InvalidOperationException("Request is not initialized yet"); + + public IClientConnection Client => _clientConnection ?? throw new InvalidOperationException("Request is not initialized yet"); + + public IClientConnection LocalClient => _localClient ?? throw new InvalidOperationException("Request is not initialized yet"); + + public HttpProtocol ProtocolType { get; internal set; } + + public FlexibleRequestMethod Method => _method ?? throw new InvalidOperationException("Request is not initialized yet"); + + public RoutingTarget Target => _target ?? throw new InvalidOperationException("Request is not initialized yet"); + + public IHeaderCollection Headers => _headers; + + public Stream? Content => _content; + + public FlexibleContentType? ContentType + { + get + { + if (_contentType is not null) + { + return _contentType; + } + + var type = this["Content-Type"]; + + if (type is not null) + { + return _contentType = new FlexibleContentType(type); + } + + return null; + } + } + + public string? Host => Client.Host; + + public string? Referer => this["Referer"]; + + public string? UserAgent => this["User-Agent"]; + + public string? this[string additionalHeader] => Headers.GetValueOrDefault(additionalHeader); + + public ICookieCollection Cookies => _cookies; + + public IForwardingCollection Forwardings => _forwardings; + + public IRequestQuery Query => _query; + + public IRequestProperties Properties => _properties; + + #endregion + + #region Functionality + + public IResponseBuilder Respond() + { + if (!_freshResponse) + { + _responseBuilder.Reset(); + } + else + { + _freshResponse = false; + } + + return _responseBuilder; + } + + [Obsolete("Obsolete")] + public UpgradeInfo Upgrade() => throw new NotImplementedException(); + + public bool ContainsMultipleHeaders(string key) => _headers.ContainsMultiple(key); + + #endregion + + #region Parsing + + internal void SetConnection(IServer server, Stream stream, IEndPoint endPoint) + { + _server = server; + _stream = stream; + _endPoint = endPoint; + + var protocol = _endPoint.Secure ? ClientProtocol.Https : ClientProtocol.Http; + + if (_forwardings.Count == 0) + { + _forwardings.TryAddLegacy(Headers); + } + + //_localClient = new ClientConnection(address, protocol, Headers["Host"], clientCertificate); + + //_clientConnection = _forwardings.DetermineClient(clientCertificate) ?? _localClient; + } + + internal void SetHeader(string key, string value) + { + if (string.Equals(key, "cookie", StringComparison.OrdinalIgnoreCase)) + { + _cookies.Add(value); + } + else if (string.Equals(key, "forwarded", StringComparison.OrdinalIgnoreCase)) + { + _forwardings.Add(value); + } + else + { + _headers.Add(key, value); + } + } + + internal void SetContent(Stream content) + { + _content = content; + } + + internal void SetProtocol(HttpProtocol protocol) + { + ProtocolType = protocol; + } + + internal void SetPath(WebPath path) + { + _target = new(path); + } + + internal void SetMethod(FlexibleRequestMethod method) + { + _method = method; + } + + internal void SetQuery(string key, string value) + { + _query[key] = value; + } + + internal void Reset() + { + _headers.Clear(); + _cookies.Clear(); + _forwardings.Clear(); + _properties.Clear(); + _query.Clear(); + + Content?.Dispose(); + + _content = null; + _contentType = null; + + _freshResponse = true; + } + + #endregion + + #region IDisposable Support + + private bool _disposed; + + public void Dispose() + { + if (!_disposed) + { + Content?.Dispose(); + + _disposed = true; + } + } + + #endregion + +} \ No newline at end of file diff --git a/Engine/GenHTTP.Engine.Rocket/Protocol/RequestQuery.cs b/Engine/GenHTTP.Engine.Rocket/Protocol/RequestQuery.cs new file mode 100644 index 00000000..95402c8d --- /dev/null +++ b/Engine/GenHTTP.Engine.Rocket/Protocol/RequestQuery.cs @@ -0,0 +1,14 @@ +using GenHTTP.Api.Protocol; + +namespace GenHTTP.Engine.Rocket.Protocol; + +public sealed class RequestQuery : Dictionary, IRequestQuery +{ + private const int DefaultSize = 12; + + public RequestQuery() : base(DefaultSize, StringComparer.OrdinalIgnoreCase) + { + + } + +} diff --git a/Engine/GenHTTP.Engine.Rocket/Server.cs b/Engine/GenHTTP.Engine.Rocket/Server.cs new file mode 100644 index 00000000..1a3c8fe4 --- /dev/null +++ b/Engine/GenHTTP.Engine.Rocket/Server.cs @@ -0,0 +1,13 @@ +using GenHTTP.Api.Infrastructure; +using GenHTTP.Engine.Rocket.Infrastructure; +using URocket.Engine.Configs; + +namespace GenHTTP.Engine.Rocket; + +public static class Server +{ + public static IServerBuilder Create(EngineOptions engineOptions) + { + return new RocketServerBuilder(engineOptions); + } +} \ No newline at end of file diff --git a/Engine/GenHttp.Engine.Rocket/GenHttp.Engine.Rocket.csproj b/Engine/GenHttp.Engine.Rocket/GenHttp.Engine.Rocket.csproj deleted file mode 100644 index 237d6616..00000000 --- a/Engine/GenHttp.Engine.Rocket/GenHttp.Engine.Rocket.csproj +++ /dev/null @@ -1,9 +0,0 @@ - - - - net10.0 - enable - enable - - - diff --git a/GenHTTP.slnx b/GenHTTP.slnx index 98a0bd77..5a4fe17a 100644 --- a/GenHTTP.slnx +++ b/GenHTTP.slnx @@ -6,7 +6,7 @@ - + diff --git a/Playground/GenHTTP.Playground.csproj b/Playground/GenHTTP.Playground.csproj index a267f8ca..5ae53489 100644 --- a/Playground/GenHTTP.Playground.csproj +++ b/Playground/GenHTTP.Playground.csproj @@ -13,6 +13,8 @@ + + diff --git a/Playground/Program.cs b/Playground/Program.cs index 6a603cc9..46d8b6b6 100644 --- a/Playground/Program.cs +++ b/Playground/Program.cs @@ -1,17 +1,20 @@ -using GenHTTP.Engine.Internal; -using GenHTTP.Modules.Archives; -using GenHTTP.Modules.DirectoryBrowsing; +using GenHTTP.Engine.Rocket; using GenHTTP.Modules.IO; +using GenHTTP.Modules.Layouting; using GenHTTP.Modules.Practices; +using URocket.Engine.Configs; -var archive = Resource.FromWeb("https://builds.dotnet.microsoft.com/dotnet/Sdk/10.0.102/dotnet-sdk-10.0.102-linux-x64.tar.gz"); +var resource = Resource.FromString("Hello World!"); +var content = Content.From(resource); +var app = Layout.Create() + .Add("route", content); -var tree = ArchiveTree.From(archive); - -var listing = Listing.From(tree); - -await Host.Create() - .Handler(listing) - .Defaults() - .Console() - .RunAsync(); +await Host.Create(new EngineOptions + { + Port = 8080, + ReactorCount = 12 + }) + .Handler(app) + .Defaults() + .Console() + .RunAsync(); From 3a2c37c77638aeafcfbca7ff0f9996afc03996e3 Mon Sep 17 00:00:00 2001 From: Diogo Martins Date: Sat, 31 Jan 2026 23:27:55 +0000 Subject: [PATCH 3/3] Bump uRocket to 10.5.1 - should support NET8 --- Engine/GenHTTP.Engine.Rocket/GenHTTP.Engine.Rocket.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Engine/GenHTTP.Engine.Rocket/GenHTTP.Engine.Rocket.csproj b/Engine/GenHTTP.Engine.Rocket/GenHTTP.Engine.Rocket.csproj index c784e95b..bee800e6 100644 --- a/Engine/GenHTTP.Engine.Rocket/GenHTTP.Engine.Rocket.csproj +++ b/Engine/GenHTTP.Engine.Rocket/GenHTTP.Engine.Rocket.csproj @@ -8,7 +8,7 @@ - +