mirror of
https://github.com/AlexMacocian/MTSC.git
synced 2026-07-15 14:59:33 +00:00
Introduce cancellation tokens for HttpRoutes (#7)
Return 404 on NotFound RouteFilters now can handle exceptions
This commit is contained in:
@@ -50,7 +50,7 @@ namespace MTSC.UnitTests
|
||||
.WithHeartbeatEnabled(true)
|
||||
.WithHeartbeatFrequency(TimeSpan.FromMilliseconds(100)))
|
||||
.AddHandler(new HttpRoutingHandler()
|
||||
.WithReturn500OnException(true)
|
||||
.WithReturn500OnUnhandledException(true)
|
||||
.AddRoute<ExceptionThrowingModule>(HttpMessage.HttpMethods.Get, "throw")
|
||||
.AddRoute<Http200Module>(HttpMessage.HttpMethods.Get, "")
|
||||
.AddRoute<TestQueryModule>(HttpMessage.HttpMethods.Get, "query")
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using MTSC.Common.Http;
|
||||
using MTSC.Common.Http.Attributes;
|
||||
using MTSC.ServerSide;
|
||||
|
||||
namespace MTSC.UnitTests.RoutingModules
|
||||
{
|
||||
@@ -9,16 +8,16 @@ namespace MTSC.UnitTests.RoutingModules
|
||||
public static bool RequestCalled { get; private set; }
|
||||
public static bool ResponseCalled { get; private set; }
|
||||
|
||||
public override RouteEnablerResponse HandleRequest(Server server, ClientData clientData, HttpRequest httpRequest)
|
||||
public override RouteEnablerResponse HandleRequest(RouteContext routeContext)
|
||||
{
|
||||
RequestCalled = true;
|
||||
return base.HandleRequest(server, clientData, httpRequest);
|
||||
return base.HandleRequest(routeContext);
|
||||
}
|
||||
|
||||
public override void HandleResponse(Server server, ClientData clientData, HttpResponse httpResponse)
|
||||
public override void HandleResponse(RouteContext routeContext)
|
||||
{
|
||||
ResponseCalled = true;
|
||||
base.HandleResponse(server, clientData, httpResponse);
|
||||
base.HandleResponse(routeContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
using MTSC.ServerSide;
|
||||
using System;
|
||||
using System;
|
||||
|
||||
namespace MTSC.Common.Http.Attributes
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
|
||||
public abstract class RouteFilterAttribute : Attribute
|
||||
{
|
||||
public virtual RouteEnablerResponse HandleRequest(Server server, ClientData clientData, HttpRequest httpRequest) => RouteEnablerResponse.Accept;
|
||||
public virtual RouteEnablerResponse HandleRequest(RouteContext routeFilterContext) => RouteEnablerResponse.Accept;
|
||||
|
||||
public virtual void HandleResponse(Server server, ClientData clientData, HttpResponse httpResponse)
|
||||
public virtual void HandleResponse(RouteContext routeFilterContext)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual RouteFilterExceptionHandlingResponse HandleException(RouteContext routeFilterContext, Exception exception)
|
||||
{
|
||||
return RouteFilterExceptionHandlingResponse.NotHandled;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
using MTSC.ServerSide;
|
||||
using System.Threading;
|
||||
|
||||
namespace MTSC.Common.Http
|
||||
{
|
||||
public sealed class RouteContext
|
||||
{
|
||||
public Server Server { get; }
|
||||
public HttpRequest HttpRequest { get; }
|
||||
public ClientData Client { get; }
|
||||
public HttpResponse HttpResponse { get; set; }
|
||||
public CancellationToken CancelRequest => this.Client.CancellationToken;
|
||||
|
||||
public RouteContext(
|
||||
Server server,
|
||||
HttpRequest httpRequest,
|
||||
ClientData clientData)
|
||||
{
|
||||
this.Server = server;
|
||||
this.HttpRequest = httpRequest;
|
||||
this.Client = clientData;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
|
||||
namespace MTSC.Common.Http
|
||||
{
|
||||
public abstract class RouteFilterExceptionHandlingResponse
|
||||
{
|
||||
internal RouteFilterExceptionHandlingResponse()
|
||||
{
|
||||
}
|
||||
|
||||
public sealed class NotHandledResponse : RouteFilterExceptionHandlingResponse
|
||||
{
|
||||
internal NotHandledResponse()
|
||||
{
|
||||
}
|
||||
}
|
||||
public sealed class HandledResponse : RouteFilterExceptionHandlingResponse
|
||||
{
|
||||
public HttpResponse HttpResponse { get; }
|
||||
|
||||
internal HandledResponse(HttpResponse httpResponse)
|
||||
{
|
||||
this.HttpResponse = httpResponse ?? throw new ArgumentNullException(nameof(httpResponse));
|
||||
}
|
||||
}
|
||||
|
||||
public static NotHandledResponse NotHandled => new();
|
||||
public static HandledResponse Handled(HttpResponse httpResponse) => new(httpResponse);
|
||||
}
|
||||
}
|
||||
@@ -36,7 +36,7 @@ namespace MTSC.Common.Http.RoutingModules
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (this.HttpRoutingHandler.Return500OnException is true)
|
||||
if (this.HttpRoutingHandler.Return500OnUnhandledException is true)
|
||||
{
|
||||
return InternalServerError500;
|
||||
}
|
||||
|
||||
+3
-3
@@ -5,13 +5,13 @@
|
||||
<TargetFrameworks>net48;netstandard2.0;netcoreapp3.1;net5.0;net6.0</TargetFrameworks>
|
||||
<ApplicationIcon />
|
||||
<StartupObject />
|
||||
<Version>5.0</Version>
|
||||
<Version>5.1</Version>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Authors>Alexandru-Victor Macocian</Authors>
|
||||
<Product>MTSC</Product>
|
||||
<Description>Modular TCP Server and Client</Description>
|
||||
<AssemblyVersion>5.0.0.0</AssemblyVersion>
|
||||
<FileVersion>5.0.0.0</FileVersion>
|
||||
<AssemblyVersion>5.1.0.0</AssemblyVersion>
|
||||
<FileVersion>5.1.0.0</FileVersion>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<PackageProjectUrl>https://github.com/AlexMacocian/MTSC</PackageProjectUrl>
|
||||
|
||||
@@ -3,6 +3,7 @@ using MTSC.ServerSide.Handlers;
|
||||
using System;
|
||||
using System.Net.Security;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
|
||||
namespace MTSC.ServerSide
|
||||
{
|
||||
@@ -13,6 +14,9 @@ namespace MTSC.ServerSide
|
||||
{
|
||||
private readonly ProducerConsumerQueue<Message> messageQueue = new();
|
||||
|
||||
private CancellationTokenSource cancellationTokenSource = new();
|
||||
|
||||
public CancellationToken CancellationToken => this.cancellationTokenSource.Token;
|
||||
public Socket Socket { get; }
|
||||
/// <summary>
|
||||
/// Latest datetime when a message has been received from the client
|
||||
@@ -115,6 +119,8 @@ namespace MTSC.ServerSide
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
this.cancellationTokenSource.Cancel();
|
||||
this.cancellationTokenSource = null;
|
||||
this.SafeNetworkStream?.Dispose();
|
||||
this.SslStream?.Dispose();
|
||||
this.Resources?.Dispose();
|
||||
|
||||
@@ -10,6 +10,7 @@ using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using static MTSC.Common.Http.HttpMessage;
|
||||
|
||||
namespace MTSC.ServerSide.Handlers
|
||||
@@ -43,7 +44,8 @@ namespace MTSC.ServerSide.Handlers
|
||||
|
||||
public TimeSpan FragmentsExpirationTime { get; set; } = TimeSpan.FromSeconds(15);
|
||||
public double MaximumRequestSize { get; set; } = double.MaxValue;
|
||||
public bool Return500OnException { get; set; } = true;
|
||||
public bool Return500OnUnhandledException { get; set; } = true;
|
||||
public bool Return404OnNotFound { get; set; } = false;
|
||||
|
||||
public HttpRoutingHandler()
|
||||
{
|
||||
@@ -53,9 +55,14 @@ namespace MTSC.ServerSide.Handlers
|
||||
}
|
||||
}
|
||||
|
||||
public HttpRoutingHandler WithReturn500OnException(bool return500OnException)
|
||||
public HttpRoutingHandler WithReturn404OnNotFound(bool return404OnNotFound)
|
||||
{
|
||||
this.Return500OnException = return500OnException;
|
||||
this.Return404OnNotFound = return404OnNotFound;
|
||||
return this;
|
||||
}
|
||||
public HttpRoutingHandler WithReturn500OnUnhandledException(bool return500OnException)
|
||||
{
|
||||
this.Return500OnUnhandledException = return500OnException;
|
||||
return this;
|
||||
}
|
||||
public HttpRoutingHandler AddHttpLogger(IHttpLogger logger)
|
||||
@@ -124,9 +131,9 @@ namespace MTSC.ServerSide.Handlers
|
||||
{
|
||||
client.ResetAffinityIfMe(this);
|
||||
client.Resources.RemoveResource<FragmentedMessage>();
|
||||
if (this.Return500OnException)
|
||||
if (this.Return500OnUnhandledException)
|
||||
{
|
||||
var response = new HttpResponse() { StatusCode = StatusCodes.InternalServerError };
|
||||
var response = InternalServerError500;
|
||||
foreach (var httpLogger in this.httpLoggers)
|
||||
{
|
||||
httpLogger.LogResponse(server, this, client, response);
|
||||
@@ -212,10 +219,11 @@ namespace MTSC.ServerSide.Handlers
|
||||
httpLogger.LogRequest(server, this, client, request);
|
||||
}
|
||||
|
||||
var routeContext = new RouteContext(server, request, client);
|
||||
foreach(var filterType in filterTypes)
|
||||
{
|
||||
var filter = server.ServiceManager.GetService(filterType) as RouteFilterAttribute;
|
||||
var filterResponse = filter.HandleRequest(server, client, request);
|
||||
var filterResponse = filter.HandleRequest(routeContext);
|
||||
if (filterResponse is RouteEnablerResponse.RouteEnablerResponseAccept)
|
||||
{
|
||||
continue;
|
||||
@@ -236,14 +244,8 @@ namespace MTSC.ServerSide.Handlers
|
||||
}
|
||||
|
||||
this.SetModuleProperties(module, request, urlValues);
|
||||
module.CallHandleRequest(request).ContinueWith((task) =>
|
||||
this.RouteHandleRequest(module, routeContext, filterTypes).ContinueWith(task =>
|
||||
{
|
||||
foreach(var filterType in filterTypes)
|
||||
{
|
||||
var filter = server.ServiceManager.GetService(filterType) as RouteFilterAttribute;
|
||||
filter.HandleResponse(server, client, task.Result);
|
||||
}
|
||||
|
||||
this.QueueResponse(client, task.Result);
|
||||
});
|
||||
|
||||
@@ -343,11 +345,46 @@ namespace MTSC.ServerSide.Handlers
|
||||
{
|
||||
client.ResetAffinityIfMe(this);
|
||||
client.Resources.RemoveResource<FragmentedMessage>();
|
||||
if (this.Return404OnNotFound)
|
||||
{
|
||||
this.QueueResponse(client, NotFound404);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<HttpResponse> RouteHandleRequest(HttpRouteBase httpRouteBase, RouteContext routeContext, List<Type> filterTypes)
|
||||
{
|
||||
try
|
||||
{
|
||||
var response = await httpRouteBase.CallHandleRequest(routeContext.HttpRequest);
|
||||
routeContext.HttpResponse = response;
|
||||
foreach (var filterType in filterTypes)
|
||||
{
|
||||
var filter = routeContext.Server.ServiceManager.GetService(filterType) as RouteFilterAttribute;
|
||||
filter.HandleResponse(routeContext);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
foreach (var filterType in filterTypes)
|
||||
{
|
||||
var filter = routeContext.Server.ServiceManager.GetService(filterType) as RouteFilterAttribute;
|
||||
if (filter.HandleException(routeContext, ex) is RouteFilterExceptionHandlingResponse.HandledResponse handledExceptionResponse)
|
||||
{
|
||||
routeContext.HttpResponse = handledExceptionResponse.HttpResponse;
|
||||
return handledExceptionResponse.HttpResponse;
|
||||
}
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private HttpRouteBase GetRoute(Slim.IServiceProvider serviceProvider, Type routeType, ClientData client, Server server)
|
||||
{
|
||||
if (!typeof(HttpRouteBase).IsAssignableFrom(routeType))
|
||||
@@ -511,5 +548,14 @@ namespace MTSC.ServerSide.Handlers
|
||||
convertedValue = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static HttpResponse NotFound404 => new()
|
||||
{
|
||||
StatusCode = StatusCodes.NotFound
|
||||
};
|
||||
private static HttpResponse InternalServerError500 => new()
|
||||
{
|
||||
StatusCode = StatusCodes.InternalServerError
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user