Skip to content

Commit

Permalink
Core - Replace EmbedIO with GenHTTP
Browse files Browse the repository at this point in the history
Replace EmbedIO with GenHTTP
  • Loading branch information
RobertBeekman authored Feb 15, 2025
2 parents 7176aba + fcf00af commit 17e6c65
Show file tree
Hide file tree
Showing 18 changed files with 302 additions and 457 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/master.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
dotnet-version: '9.0.x'
- name: Publish Artemis
run: dotnet publish --configuration Release -p:Version=${{ needs.version.outputs.version-number }} --runtime ${{ matrix.rid }} --output build/${{ matrix.rid }} --self-contained Artemis/src/Artemis.UI.${{ matrix.csproj }}/Artemis.UI.${{ matrix.csproj }}.csproj
- name: Publish Plugins
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/nuget.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
dotnet-version: '9.0.x'
- name: Checkout
uses: actions/checkout@v4
- name: Pack Artemis.Core
Expand Down
3 changes: 2 additions & 1 deletion src/Artemis.Core/Artemis.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@

<ItemGroup>
<PackageReference Include="DryIoc.dll" />
<PackageReference Include="EmbedIO" />
<PackageReference Include="GenHTTP.Core" />
<PackageReference Include="GenHTTP.Modules.Webservices" />
<PackageReference Include="HidSharp" />
<PackageReference Include="HPPH.SkiaSharp" />
<PackageReference Include="Humanizer.Core" />
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using System;
using EmbedIO;
using GenHTTP.Api.Protocol;

namespace Artemis.Core.Services;

Expand All @@ -8,13 +8,13 @@ namespace Artemis.Core.Services;
/// </summary>
public class EndpointRequestEventArgs : EventArgs
{
internal EndpointRequestEventArgs(IHttpContext context)
internal EndpointRequestEventArgs(IRequest request)
{
Context = context;
Request = request;
}

/// <summary>
/// Gets the HTTP context of the request
/// </summary>
public IHttpContext Context { get; }
public IRequest Request { get; }
}
39 changes: 23 additions & 16 deletions src/Artemis.Core/Services/WebServer/EndPoints/JsonPluginEndPoint.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
using System;
using System.IO;
using System.Text.Json;
using System.Threading.Tasks;
using EmbedIO;
using GenHTTP.Api.Protocol;
using GenHTTP.Modules.Basics;
using GenHTTP.Modules.Conversion.Serializers.Json;

namespace Artemis.Core.Services;

Expand All @@ -16,19 +17,19 @@ public class JsonPluginEndPoint<T> : PluginEndPoint
private readonly Action<T>? _requestHandler;
private readonly Func<T, object?>? _responseRequestHandler;

internal JsonPluginEndPoint(PluginFeature pluginFeature, string name, PluginsModule pluginsModule, Action<T> requestHandler) : base(pluginFeature, name, pluginsModule)
internal JsonPluginEndPoint(PluginFeature pluginFeature, string name, PluginsHandler pluginsHandler, Action<T> requestHandler) : base(pluginFeature, name, pluginsHandler)
{
_requestHandler = requestHandler;
ThrowOnFail = true;
Accepts = MimeType.Json;
Accepts = FlexibleContentType.Get(ContentType.ApplicationJson);
}

internal JsonPluginEndPoint(PluginFeature pluginFeature, string name, PluginsModule pluginsModule, Func<T, object?> responseRequestHandler) : base(pluginFeature, name, pluginsModule)
internal JsonPluginEndPoint(PluginFeature pluginFeature, string name, PluginsHandler pluginsHandler, Func<T, object?> responseRequestHandler) : base(pluginFeature, name, pluginsHandler)
{
_responseRequestHandler = responseRequestHandler;
ThrowOnFail = true;
Accepts = MimeType.Json;
Returns = MimeType.Json;
Accepts = FlexibleContentType.Get(ContentType.ApplicationJson);
Returns = FlexibleContentType.Get(ContentType.ApplicationJson);
}

/// <summary>
Expand All @@ -41,25 +42,25 @@ internal JsonPluginEndPoint(PluginFeature pluginFeature, string name, PluginsMod
#region Overrides of PluginEndPoint

/// <inheritdoc />
protected override async Task ProcessRequest(IHttpContext context)
protected override async Task<IResponse> ProcessRequest(IRequest request)
{
if (context.Request.HttpVerb != HttpVerbs.Post && context.Request.HttpVerb != HttpVerbs.Put)
throw HttpException.MethodNotAllowed("This end point only accepts POST and PUT calls");
if (request.Method != RequestMethod.Post && request.Method != RequestMethod.Put)
return request.Respond().Status(ResponseStatus.MethodNotAllowed).Build();

context.Response.ContentType = MimeType.Json;
if (request.Content == null)
return request.Respond().Status(ResponseStatus.BadRequest).Build();

using TextReader reader = context.OpenRequestText();
object? response = null;
try
{
T? deserialized = JsonSerializer.Deserialize<T>(await reader.ReadToEndAsync());
T? deserialized = await JsonSerializer.DeserializeAsync<T>(request.Content, WebServerService.JsonOptions);
if (deserialized == null)
throw new JsonException("Deserialization returned null");

if (_requestHandler != null)
{
_requestHandler(deserialized);
return;
return request.Respond().Status(ResponseStatus.NoContent).Build();
}

if (_responseRequestHandler != null)
Expand All @@ -73,8 +74,14 @@ protected override async Task ProcessRequest(IHttpContext context)
throw;
}

await using TextWriter writer = context.OpenResponseText();
await writer.WriteAsync(JsonSerializer.Serialize(response));
// TODO: Cache options
if (response == null)
return request.Respond().Status(ResponseStatus.NoContent).Build();
return request.Respond()
.Status(ResponseStatus.Ok)
.Content(new JsonContent(response, WebServerService.JsonOptions))
.Type(ContentType.ApplicationJson)
.Build();
}

#endregion
Expand Down
54 changes: 35 additions & 19 deletions src/Artemis.Core/Services/WebServer/EndPoints/PluginEndPoint.cs
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
using System;
using System.Net.Http;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using EmbedIO;
using GenHTTP.Api.Protocol;
using GenHTTP.Modules.Basics;
using GenHTTP.Modules.IO;
using StringContent = GenHTTP.Modules.IO.Strings.StringContent;

namespace Artemis.Core.Services;

/// <summary>
/// Represents a base type for plugin end points to be targeted by the <see cref="PluginsModule" />
/// Represents a base type for plugin end points to be targeted by the <see cref="PluginsHandler" />
/// </summary>
public abstract class PluginEndPoint
{
private readonly PluginsModule _pluginsModule;
private readonly PluginsHandler _pluginsHandler;

internal PluginEndPoint(PluginFeature pluginFeature, string name, PluginsModule pluginsModule)
internal PluginEndPoint(PluginFeature pluginFeature, string name, PluginsHandler pluginsHandler)

Check warning on line 19 in src/Artemis.Core/Services/WebServer/EndPoints/PluginEndPoint.cs

View workflow job for this annotation

GitHub Actions / Publish Nuget Packages

Non-nullable property 'Accepts' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.

Check warning on line 19 in src/Artemis.Core/Services/WebServer/EndPoints/PluginEndPoint.cs

View workflow job for this annotation

GitHub Actions / Publish Nuget Packages

Non-nullable property 'Returns' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.
{
_pluginsModule = pluginsModule;
_pluginsHandler = pluginsHandler;
PluginFeature = pluginFeature;
Name = name;

Expand All @@ -29,7 +33,7 @@ internal PluginEndPoint(PluginFeature pluginFeature, string name, PluginsModule
/// <summary>
/// Gets the full URL of the end point
/// </summary>
public string Url => $"{_pluginsModule.ServerUrl?.TrimEnd('/')}{_pluginsModule.BaseRoute}{PluginFeature.Plugin.Guid}/{Name}";
public string Url => $"{_pluginsHandler.ServerUrl}{_pluginsHandler.BaseRoute}/{PluginFeature.Plugin.Guid}/{Name}";

/// <summary>
/// Gets the plugin the end point is associated with
Expand All @@ -42,15 +46,15 @@ internal PluginEndPoint(PluginFeature pluginFeature, string name, PluginsModule
/// </summary>
public PluginInfo PluginInfo => PluginFeature.Plugin.Info;

/// <summary>
/// <summary><summary>
/// Gets the mime type of the input this end point accepts
/// </summary>
public string? Accepts { get; protected set; }
public FlexibleContentType Accepts { get; protected set; }

Check warning on line 52 in src/Artemis.Core/Services/WebServer/EndPoints/PluginEndPoint.cs

View workflow job for this annotation

GitHub Actions / Publish Nuget Packages

XML comment has badly formed XML -- 'Expected an end tag for element 'summary'.'

/// <summary>
/// Gets the mime type of the output this end point returns
/// </summary>
public string? Returns { get; protected set; }
public FlexibleContentType Returns { get; protected set; }

/// <summary>
/// Occurs whenever a request threw an unhandled exception
Expand All @@ -70,8 +74,8 @@ internal PluginEndPoint(PluginFeature pluginFeature, string name, PluginsModule
/// <summary>
/// Called whenever the end point has to process a request
/// </summary>
/// <param name="context">The HTTP context of the request</param>
protected abstract Task ProcessRequest(IHttpContext context);
/// <param name="request">The HTTP context of the request</param>
protected abstract Task<IResponse> ProcessRequest(IRequest request);

/// <summary>
/// Invokes the <see cref="RequestException" /> event
Expand All @@ -85,37 +89,49 @@ protected virtual void OnRequestException(Exception e)
/// <summary>
/// Invokes the <see cref="ProcessingRequest" /> event
/// </summary>
protected virtual void OnProcessingRequest(IHttpContext context)
protected virtual void OnProcessingRequest(IRequest request)
{
ProcessingRequest?.Invoke(this, new EndpointRequestEventArgs(context));
ProcessingRequest?.Invoke(this, new EndpointRequestEventArgs(request));
}

/// <summary>
/// Invokes the <see cref="ProcessedRequest" /> event
/// </summary>
protected virtual void OnProcessedRequest(IHttpContext context)
protected virtual void OnProcessedRequest(IRequest request)
{
ProcessedRequest?.Invoke(this, new EndpointRequestEventArgs(context));
ProcessedRequest?.Invoke(this, new EndpointRequestEventArgs(request));
}

internal async Task InternalProcessRequest(IHttpContext context)
internal async Task<IResponse> InternalProcessRequest(IRequest context)
{
try
{
OnProcessingRequest(context);
await ProcessRequest(context);

if (!Equals(context.ContentType, Accepts))
{
OnRequestException(new Exception("Unsupported media type"));
return context.Respond().Status(ResponseStatus.UnsupportedMediaType).Build();
}

IResponse response = await ProcessRequest(context);
OnProcessedRequest(context);
return response;
}
catch (Exception e)
{
OnRequestException(e);
throw;
return context.Respond()
.Status(ResponseStatus.InternalServerError)
.Content(new StringContent(e.ToString()))
.Type(ContentType.TextPlain)
.Build();
}
}

private void OnDisabled(object? sender, EventArgs e)
{
PluginFeature.Disabled -= OnDisabled;
_pluginsModule.RemovePluginEndPoint(this);
_pluginsHandler.RemovePluginEndPoint(this);
}
}
Loading

0 comments on commit 17e6c65

Please sign in to comment.