diff --git a/MTSC.UnitTests/E2ETests.cs b/MTSC.UnitTests/E2ETests.cs index 3ac47b0..b666aa3 100644 --- a/MTSC.UnitTests/E2ETests.cs +++ b/MTSC.UnitTests/E2ETests.cs @@ -9,6 +9,7 @@ using MTSC.Exceptions; using MTSC.Logging; using MTSC.ServerSide.Handlers; using MTSC.ServerSide.Schedulers; +using MTSC.UnitTests.RoutingModules; using System; using System.Diagnostics; using System.IO; @@ -42,7 +43,7 @@ namespace MTSC.UnitTests .AddHandler(new WebsocketRoutingHandler() .AddRoute("echo", new EchoWebsocketModule() .WithReceiveTemplateProvider((message) => Encoding.UTF8.GetString(message.Data)) - .WithSendTemplateProvider((s) => + .WithSendTemplateProvider((s) => { WebsocketMessage websocketMessage = new WebsocketMessage(); websocketMessage.Data = Encoding.UTF8.GetBytes(s); @@ -50,12 +51,12 @@ namespace MTSC.UnitTests return websocketMessage; }))) .AddHandler(new HttpRoutingHandler() - .AddRoute(HttpMessage.HttpMethods.Get, "", new Http200Module()) - .AddRoute(HttpMessage.HttpMethods.Get, "query", new TestQueryModule()) - .AddRoute(HttpMessage.HttpMethods.Get, "echo", new EchoModule()) - .AddRoute(HttpMessage.HttpMethods.Post, "echo", new EchoModule()) - .AddRoute(HttpMessage.HttpMethods.Get, "long-running", new LongRunningModule()) - .AddRoute(HttpMessage.HttpMethods.Post, "multipart", new MultipartModule()) + .AddRoute(HttpMessage.HttpMethods.Get, "") + .AddRoute(HttpMessage.HttpMethods.Get, "query") + .AddRoute(HttpMessage.HttpMethods.Post, "echo") + .AddRoute(HttpMessage.HttpMethods.Get, "long-running") + .AddRoute(HttpMessage.HttpMethods.Post, "multipart") + .AddRoute(HttpMessage.HttpMethods.Get, "some-module") .WithFragmentsExpirationTime(TimeSpan.FromMilliseconds(3000)) .WithMaximumSize(250000)) .AddLogger(new ConsoleLogger()) @@ -65,6 +66,15 @@ namespace MTSC.UnitTests .WithSslAuthenticationTimeout(TimeSpan.FromMilliseconds(100)); Server.RunAsync(); } + [TestMethod] + public async Task ServerParsesRequestAndResponse() + { + HttpClient httpClient = new HttpClient(); + httpClient.BaseAddress = new Uri("http://localhost:800"); + var response = await httpClient.GetAsync("some-module"); + Assert.AreEqual(response.StatusCode, HttpStatusCode.OK); + } + [TestMethod] public async Task ServerRespondsDuringLongRunningTask() { @@ -76,7 +86,7 @@ namespace MTSC.UnitTests client2.BaseAddress = new Uri("http://localhost:800"); while (!longRunningTask.IsCompleted) { - var echoResponse = await client2.GetAsync("echo"); + _ = await client2.GetAsync(""); responses++; } var result = longRunningTask.Result; @@ -103,11 +113,6 @@ namespace MTSC.UnitTests var result = httpClient.GetAsync("").GetAwaiter().GetResult(); Assert.AreEqual(result.StatusCode, HttpStatusCode.OK); } - for (int i = 0; i < 10; i++) - { - var result = httpClient.GetAsync("echo").GetAwaiter().GetResult(); - Assert.AreEqual(result.StatusCode, HttpStatusCode.OK); - } } [TestMethod] @@ -123,7 +128,7 @@ namespace MTSC.UnitTests .Connect(); HttpRequest request = new HttpRequest(); - request.Method = HttpMessage.HttpMethods.Get; + request.Method = HttpMessage.HttpMethods.Post; request.BodyString = "Brought a message to you my guy!"; request.RequestURI = "/echo"; request.Headers[HttpMessage.EntityHeaders.ContentLength] = request.BodyString.Length.ToString(); @@ -163,7 +168,7 @@ namespace MTSC.UnitTests .Connect(); HttpRequest request = new HttpRequest(); - request.Method = HttpMessage.HttpMethods.Get; + request.Method = HttpMessage.HttpMethods.Post; request.BodyString = "Brought a message to you my guy!"; request.RequestURI = "/echo"; request.Headers[HttpMessage.EntityHeaders.ContentLength] = request.BodyString.Length.ToString(); @@ -200,7 +205,7 @@ namespace MTSC.UnitTests .Connect(); HttpRequest request = new HttpRequest(); - request.Method = HttpMessage.HttpMethods.Get; + request.Method = HttpMessage.HttpMethods.Post; request.BodyString = "Brought a message to you my guy!"; request.RequestURI = "/echo"; request.Headers[HttpMessage.EntityHeaders.ContentLength] = request.BodyString.Length.ToString(); diff --git a/MTSC.UnitTests/EchoModule.cs b/MTSC.UnitTests/EchoModule.cs index 801dad0..c321674 100644 --- a/MTSC.UnitTests/EchoModule.cs +++ b/MTSC.UnitTests/EchoModule.cs @@ -7,7 +7,7 @@ namespace MTSC.UnitTests { public class EchoModule : HttpRouteBase { - public override Task HandleRequest(HttpRequest request, ClientData client, ServerSide.Server server) + public override Task HandleRequest(HttpRequest request) { return Task.FromResult(new HttpResponse { BodyString = request.BodyString, StatusCode = HttpMessage.StatusCodes.OK }); } diff --git a/MTSC.UnitTests/LongRunningModule.cs b/MTSC.UnitTests/LongRunningModule.cs index f5acdbd..1825b8b 100644 --- a/MTSC.UnitTests/LongRunningModule.cs +++ b/MTSC.UnitTests/LongRunningModule.cs @@ -7,7 +7,7 @@ namespace MTSC.UnitTests { public class LongRunningModule : HttpRouteBase { - public async override Task HandleRequest(HttpRequest request, ClientData client, Server server) + public async override Task HandleRequest(HttpRequest request) { await Task.Delay(5000); return new HttpResponse { StatusCode = HttpMessage.StatusCodes.OK }; diff --git a/MTSC.UnitTests/MultipartModule.cs b/MTSC.UnitTests/MultipartModule.cs index 6d92d3b..55c03b6 100644 --- a/MTSC.UnitTests/MultipartModule.cs +++ b/MTSC.UnitTests/MultipartModule.cs @@ -8,7 +8,7 @@ namespace MTSC.UnitTests { public class MultipartModule : HttpRouteBase { - public override Task HandleRequest(HttpRequest request, ClientData client, Server server) + public override Task HandleRequest(HttpRequest request) { if (request.Form.Count > 0) { diff --git a/MTSC.UnitTests/RoutingModules/SomeRequestConverter.cs b/MTSC.UnitTests/RoutingModules/SomeRequestConverter.cs new file mode 100644 index 0000000..c792f9d --- /dev/null +++ b/MTSC.UnitTests/RoutingModules/SomeRequestConverter.cs @@ -0,0 +1,13 @@ +using MTSC.Common.Http; +using MTSC.Common.Http.RoutingModules; + +namespace MTSC.UnitTests.RoutingModules +{ + public class SomeRequestConverter : IRequestConverter + { + public SomeRoutingRequest ConvertHttpRequest(HttpRequest httpRequest) + { + return new SomeRoutingRequest(); + } + } +} diff --git a/MTSC.UnitTests/RoutingModules/SomeResponseConverter.cs b/MTSC.UnitTests/RoutingModules/SomeResponseConverter.cs new file mode 100644 index 0000000..773443f --- /dev/null +++ b/MTSC.UnitTests/RoutingModules/SomeResponseConverter.cs @@ -0,0 +1,13 @@ +using MTSC.Common.Http; +using MTSC.Common.Http.RoutingModules; + +namespace MTSC.UnitTests.RoutingModules +{ + public class SomeResponseConverter : IResponseConverter + { + public HttpResponse ConvertResponse(SomeRoutingResponse response) + { + return new HttpResponse { StatusCode = HttpMessage.StatusCodes.OK }; + } + } +} diff --git a/MTSC.UnitTests/RoutingModules/SomeRoutingModule.cs b/MTSC.UnitTests/RoutingModules/SomeRoutingModule.cs new file mode 100644 index 0000000..6a91963 --- /dev/null +++ b/MTSC.UnitTests/RoutingModules/SomeRoutingModule.cs @@ -0,0 +1,13 @@ +using MTSC.Common.Http.RoutingModules; +using System.Threading.Tasks; + +namespace MTSC.UnitTests.RoutingModules +{ + public class SomeRoutingModule : HttpRouteBase + { + public override Task HandleRequest(SomeRoutingRequest request) + { + return Task.FromResult(new SomeRoutingResponse()); + } + } +} diff --git a/MTSC.UnitTests/RoutingModules/SomeRoutingRequest.cs b/MTSC.UnitTests/RoutingModules/SomeRoutingRequest.cs new file mode 100644 index 0000000..3e6bdcb --- /dev/null +++ b/MTSC.UnitTests/RoutingModules/SomeRoutingRequest.cs @@ -0,0 +1,9 @@ +using MTSC.Common.Http.RoutingModules; + +namespace MTSC.UnitTests.RoutingModules +{ + [RequestConvert(typeof(SomeRequestConverter))] + public class SomeRoutingRequest + { + } +} diff --git a/MTSC.UnitTests/RoutingModules/SomeRoutingResponse.cs b/MTSC.UnitTests/RoutingModules/SomeRoutingResponse.cs new file mode 100644 index 0000000..070baac --- /dev/null +++ b/MTSC.UnitTests/RoutingModules/SomeRoutingResponse.cs @@ -0,0 +1,9 @@ +using MTSC.Common.Http.RoutingModules; + +namespace MTSC.UnitTests.RoutingModules +{ + [ResponseConvert(typeof(SomeResponseConverter))] + public class SomeRoutingResponse + { + } +} diff --git a/MTSC.UnitTests/TestQueryModule.cs b/MTSC.UnitTests/TestQueryModule.cs index 5c6e18e..08931ac 100644 --- a/MTSC.UnitTests/TestQueryModule.cs +++ b/MTSC.UnitTests/TestQueryModule.cs @@ -8,7 +8,7 @@ namespace MTSC.UnitTests { class TestQueryModule : HttpRouteBase { - public override Task HandleRequest(HttpRequest request, ClientData client, ServerSide.Server server) + public override Task HandleRequest(HttpRequest request) { var query = HttpUtility.ParseQueryString(request.RequestQuery); if(query.Count == 2 && diff --git a/MTSC/Common/Http/RoutingModules/Http200Module.cs b/MTSC/Common/Http/RoutingModules/Http200Module.cs index 2af4fc7..6d56be1 100644 --- a/MTSC/Common/Http/RoutingModules/Http200Module.cs +++ b/MTSC/Common/Http/RoutingModules/Http200Module.cs @@ -5,9 +5,21 @@ namespace MTSC.Common.Http.RoutingModules { public sealed class Http200Module : HttpRouteBase { - public override Task HandleRequest(HttpRequest request, ClientData client, ServerSide.Server server) + private readonly Server server; + + public Http200Module(Server server) { - return Task.FromResult(new HttpResponse { StatusCode = HttpMessage.StatusCodes.OK }); + this.server = server; } + + public override Task HandleRequest(HttpRequest request) + { + return Task.FromResult(OK); + } + + private HttpResponse OK => new HttpResponse() + { + StatusCode = HttpMessage.StatusCodes.OK + }; } } diff --git a/MTSC/Common/Http/RoutingModules/HttpRouteBase.cs b/MTSC/Common/Http/RoutingModules/HttpRouteBase.cs index f0e9ebb..5de02db 100644 --- a/MTSC/Common/Http/RoutingModules/HttpRouteBase.cs +++ b/MTSC/Common/Http/RoutingModules/HttpRouteBase.cs @@ -1,78 +1,161 @@ using MTSC.ServerSide; +using MTSC.ServerSide.Handlers; using System; +using System.Linq; using System.Threading.Tasks; namespace MTSC.Common.Http.RoutingModules { - public abstract class HttpRouteBase + public abstract class HttpRouteBase : ISetHttpContext { - public async Task CallHandleRequest(HttpRequest request, ClientData client, ServerSide.Server server) - { - return await this.HandleRequest(request, client, server); - } + public ClientData ClientData { get; private set; } + public HttpRoutingHandler HttpRoutingHandler { get; private set; } + public Server Server { get; private set; } - public abstract Task HandleRequest(HttpRequest request, ClientData client, ServerSide.Server server); + public async Task CallHandleRequest(HttpRequest request) + { + return await this.HandleRequest(request); + } + public abstract Task HandleRequest(HttpRequest request); + + void ISetHttpContext.SetClientData(ClientData clientData) + { + this.ClientData = clientData; + } + void ISetHttpContext.SetHttpRoutingHandler(HttpRoutingHandler httpRoutingHandler) + { + this.HttpRoutingHandler = httpRoutingHandler; + } + void ISetHttpContext.SetServer(Server server) + { + this.Server = server; + } } public abstract class HttpRouteBase : HttpRouteBase { - private Func template; + private readonly static object cachedLock = new object(); + private static IRequestConverter CachedConverter { get; set; } - public HttpRouteBase(Func template) + public sealed override Task HandleRequest(HttpRequest request) { - this.template = template; + lock (cachedLock) + { + if (CachedConverter is null) + { + CachedConverter = ImplementConverter(); + } + } + + return this.HandleRequest(CachedConverter.ConvertHttpRequest(request)); } - public HttpRouteBase() - { + public abstract Task HandleRequest(T request); + private static bool MatchesRequiredType(RequestConvertAttribute attribute) + { + if (attribute.ConverterType.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IRequestConverter))) + { + return false; + } + + return true; } - public HttpRouteBase WithTemplateProvider(Func templateProvider) + private static IRequestConverter ImplementConverter() { - this.template = templateProvider; - return this; - } + var converterType = typeof(T) + .GetCustomAttributes(true) + .OfType() + .Where(MatchesRequiredType) + .Select(attribute => attribute.ConverterType) + .FirstOrDefault(); + if (converterType is null) + { + throw new InvalidOperationException($"No converter found for type {typeof(T).FullName}"); + } - public async override Task HandleRequest(HttpRequest request, ClientData client, ServerSide.Server server) - { - return await HandleRequest(template.Invoke(request), client, server); + var converter = Activator.CreateInstance(converterType) as IRequestConverter; + return converter; } - - public abstract Task HandleRequest(T request, ClientData client, ServerSide.Server server); } public abstract class HttpRouteBase : HttpRouteBase { - private Func receiveTemplate; - private Func sendTemplate; + private static readonly object reqLock = new object(), respLock = new object(); + private static IRequestConverter CachedRequestConverter { get; set; } + private static IResponseConverter CachedResponseConverter { get; set; } - public HttpRouteBase(Func receiveTemplate, Func sendTemplate) + public sealed async override Task HandleRequest(HttpRequest request) { - this.receiveTemplate = receiveTemplate; - this.sendTemplate = sendTemplate; + lock (reqLock) + { + if (CachedRequestConverter is null) + { + CachedRequestConverter = ImplementRequestConverter(); + } + } + + lock (respLock) + { + if (CachedResponseConverter is null) + { + CachedResponseConverter = ImplementResponseConverter(); + } + } + + return CachedResponseConverter.ConvertResponse(await this.HandleRequest(CachedRequestConverter.ConvertHttpRequest(request))); } - public HttpRouteBase() + public abstract Task HandleRequest(TReceive request); + + private static bool MatchesRequiredRequestType(RequestConvertAttribute attribute) { + if (attribute.ConverterType.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IRequestConverter))) + { + return false; + } + return true; } - - public HttpRouteBase WithReceiveTemplateProvider(Func templateProvider) + private static bool MatchesRequiredResponseType(ResponseConvertAttribute attribute) { - this.receiveTemplate = templateProvider; - return this; - } + if (attribute.ConverterType.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IResponseConverter))) + { + return false; + } - public HttpRouteBase WithSendTemplateProvider(Func templateProvider) + return true; + } + private static IResponseConverter ImplementResponseConverter() { - this.sendTemplate = templateProvider; - return this; - } + var converterType = typeof(TSend) + .GetCustomAttributes(true) + .OfType() + .Where(MatchesRequiredResponseType) + .Select(attribute => attribute.ConverterType) + .FirstOrDefault(); + if (converterType is null) + { + throw new InvalidOperationException($"No converter found for type {typeof(TSend).FullName}"); + } - public async override Task HandleRequest(HttpRequest request, ClientData client, Server server) + var converter = Activator.CreateInstance(converterType) as IResponseConverter; + return converter; + } + private static IRequestConverter ImplementRequestConverter() { - return sendTemplate.Invoke(await HandleRequest(receiveTemplate.Invoke(request), client, server)); - } + var converterType = typeof(TReceive) + .GetCustomAttributes(true) + .OfType() + .Where(MatchesRequiredRequestType) + .Select(attribute => attribute.ConverterType) + .FirstOrDefault(); + if (converterType is null) + { + throw new InvalidOperationException($"No converter found for type {typeof(TReceive).FullName}"); + } - public abstract Task HandleRequest(TReceive request, ClientData client, Server server); + var converter = Activator.CreateInstance(converterType) as IRequestConverter; + return converter; + } } } diff --git a/MTSC/Common/Http/RoutingModules/IRequestConverter.cs b/MTSC/Common/Http/RoutingModules/IRequestConverter.cs new file mode 100644 index 0000000..61fa505 --- /dev/null +++ b/MTSC/Common/Http/RoutingModules/IRequestConverter.cs @@ -0,0 +1,7 @@ +namespace MTSC.Common.Http.RoutingModules +{ + public interface IRequestConverter + { + T ConvertHttpRequest(HttpRequest httpRequest); + } +} diff --git a/MTSC/Common/Http/RoutingModules/IResponseConverter.cs b/MTSC/Common/Http/RoutingModules/IResponseConverter.cs new file mode 100644 index 0000000..2efb414 --- /dev/null +++ b/MTSC/Common/Http/RoutingModules/IResponseConverter.cs @@ -0,0 +1,7 @@ +namespace MTSC.Common.Http.RoutingModules +{ + public interface IResponseConverter + { + HttpResponse ConvertResponse(T response); + } +} diff --git a/MTSC/Common/Http/RoutingModules/ISetHttpContext.cs b/MTSC/Common/Http/RoutingModules/ISetHttpContext.cs new file mode 100644 index 0000000..aa55f65 --- /dev/null +++ b/MTSC/Common/Http/RoutingModules/ISetHttpContext.cs @@ -0,0 +1,12 @@ +using MTSC.ServerSide; +using MTSC.ServerSide.Handlers; + +namespace MTSC.Common.Http.RoutingModules +{ + internal interface ISetHttpContext + { + void SetClientData(ClientData clientData); + void SetServer(Server server); + void SetHttpRoutingHandler(HttpRoutingHandler httpRoutingHandler); + } +} diff --git a/MTSC/Common/Http/RoutingModules/RequestConvertAttribute.cs b/MTSC/Common/Http/RoutingModules/RequestConvertAttribute.cs new file mode 100644 index 0000000..93ccd41 --- /dev/null +++ b/MTSC/Common/Http/RoutingModules/RequestConvertAttribute.cs @@ -0,0 +1,21 @@ +using System; +using System.Linq; + +namespace MTSC.Common.Http.RoutingModules +{ + [AttributeUsage(AttributeTargets.Class)] + public class RequestConvertAttribute : Attribute + { + public Type ConverterType { get; } + + public RequestConvertAttribute(Type type) + { + if (!type.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IRequestConverter<>))) + { + throw new InvalidOperationException($"{type.FullName} is not a {typeof(IRequestConverter<>).FullName}"); + } + + this.ConverterType = type; + } + } +} diff --git a/MTSC/Common/Http/RoutingModules/ResponseConverterAttribute.cs b/MTSC/Common/Http/RoutingModules/ResponseConverterAttribute.cs new file mode 100644 index 0000000..af1b195 --- /dev/null +++ b/MTSC/Common/Http/RoutingModules/ResponseConverterAttribute.cs @@ -0,0 +1,21 @@ +using System; +using System.Linq; + +namespace MTSC.Common.Http.RoutingModules +{ + [AttributeUsage(AttributeTargets.Class)] + public class ResponseConvertAttribute : Attribute + { + public Type ConverterType { get; } + + public ResponseConvertAttribute(Type type) + { + if (!type.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IResponseConverter<>))) + { + throw new InvalidOperationException($"{type.FullName} is not a {typeof(IResponseConverter<>).FullName}"); + } + + this.ConverterType = type; + } + } +} diff --git a/MTSC/MTSC.csproj b/MTSC/MTSC.csproj index 59551f3..4e86a22 100644 --- a/MTSC/MTSC.csproj +++ b/MTSC/MTSC.csproj @@ -5,12 +5,13 @@ netcoreapp2.1;net48;netstandard2.0;netcoreapp3.1;net5.0 - 2.9 + 3.0 + latest Alexandru-Victor Macocian MTSC Modular TCP Server and Client - 0.2.9 - 0.2.9 + 0.3.0 + 0.3.0 true AnyCPU;x64 https://github.com/AlexMacocian/MTSC @@ -23,4 +24,8 @@ false + + + + diff --git a/MTSC/ServerSide/Handlers/HttpRoutingHandler.cs b/MTSC/ServerSide/Handlers/HttpRoutingHandler.cs index ff1c946..4e1766a 100644 --- a/MTSC/ServerSide/Handlers/HttpRoutingHandler.cs +++ b/MTSC/ServerSide/Handlers/HttpRoutingHandler.cs @@ -2,6 +2,7 @@ using MTSC.Common.Http.RoutingModules; using MTSC.Common.Http.Telemetry; using MTSC.Exceptions; +using Slim; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -12,17 +13,18 @@ namespace MTSC.ServerSide.Handlers { public sealed class HttpRoutingHandler : IHandler { - private static Func alwaysEnabled = (server, request, client) => RouteEnablerResponse.Accept; + private static readonly Func alwaysEnabled = (server, request, client) => RouteEnablerResponse.Accept; - private ConcurrentQueue> messageOutQueue = new ConcurrentQueue>(); - - private List httpLoggers = new List(); - - private Dictionary> messageOutQueue = new ConcurrentQueue>(); + private readonly List httpLoggers = new List(); + private readonly Dictionary)>> moduleDictionary = - new Dictionary)>>(); + private bool initialized = false; + public TimeSpan FragmentsExpirationTime { get; set; } = TimeSpan.FromSeconds(15); public double MaximumRequestSize { get; set; } = double.MaxValue; @@ -30,9 +32,11 @@ namespace MTSC.ServerSide.Handlers { foreach (HttpMethods method in (HttpMethods[])Enum.GetValues(typeof(HttpMethods))) { - moduleDictionary[method] = new Dictionary)>(); + moduleDictionary[method] = new Dictionary)>(); } + + this.serviceManager.RegisterServiceManager(); } public HttpRoutingHandler AddHttpLogger(IHttpLogger logger) @@ -40,21 +44,38 @@ namespace MTSC.ServerSide.Handlers httpLoggers.Add(logger); return this; } - public HttpRoutingHandler AddRoute( + public HttpRoutingHandler AddRoute( + HttpMethods method, + string uri) + where T : HttpRouteBase + { + this.RegisterRoute(method, uri, typeof(T), alwaysEnabled); + return this; + } + public HttpRoutingHandler AddRoute( HttpMethods method, string uri, - HttpRouteBase routeModule) + Func routeEnabler) + where T : HttpRouteBase { - moduleDictionary[method][uri] = (routeModule, alwaysEnabled); + this.RegisterRoute(method, uri, typeof(T), routeEnabler); return this; } public HttpRoutingHandler AddRoute( HttpMethods method, string uri, - HttpRouteBase routeModule, - Func routeEnabler) + Type routeType) { - moduleDictionary[method][uri] = (routeModule, routeEnabler); + this.RegisterRoute(method, uri, routeType, alwaysEnabled); + return this; + } + public HttpRoutingHandler AddRoute( + HttpMethods method, + string uri, + Func routeEnabler, + Type routeType) + { + this.RegisterRoute(method, uri, routeType, routeEnabler); return this; } public HttpRoutingHandler RemoveRoute( @@ -164,9 +185,10 @@ namespace MTSC.ServerSide.Handlers } else { - if (moduleDictionary[request.Method].ContainsKey(request.RequestURI)) + if (this.moduleDictionary[request.Method].ContainsKey(request.RequestURI)) { - (var module, var routeEnabler) = moduleDictionary[request.Method][request.RequestURI]; + (var routeType, var routeEnabler) = this.moduleDictionary[request.Method][request.RequestURI]; + var module = this.GetRoute(routeType, client, server); if (request.Complete) { var httpRequest = request.ToRequest(); @@ -194,6 +216,17 @@ namespace MTSC.ServerSide.Handlers void IHandler.Tick(Server server) { + if (this.initialized is false) + { + this.initialized = true; + this.serviceManager.RegisterSingleton(typeof(Server), typeof(Server), (sp) => server); + this.serviceManager.RegisterSingleton(typeof(HttpRoutingHandler), typeof(HttpRoutingHandler), sp => this); + foreach(var resource in server.Resources) + { + this.serviceManager.RegisterSingleton(resource.GetType(), resource.GetType(), (sp) => resource); + } + } + while (messageOutQueue.Count > 0) { if (messageOutQueue.TryDequeue(out Tuple tuple)) @@ -234,7 +267,7 @@ namespace MTSC.ServerSide.Handlers { try { - module.CallHandleRequest(request, client, server).ContinueWith((task) => + module.CallHandleRequest(request).ContinueWith((task) => { foreach (var httpLogger in this.httpLoggers) httpLogger.LogResponse(server, this, client, task.Result); QueueResponse(client, task.Result); @@ -284,5 +317,30 @@ namespace MTSC.ServerSide.Handlers public HttpRouteBase MappedModule { get; set; } public Func RouteEnabler { get; set; } } + + private HttpRouteBase GetRoute(Type routeType, ClientData client, Server server) + { + if (!typeof(HttpRouteBase).IsAssignableFrom(routeType)) + { + throw new InvalidOperationException($"Cannot create new route of type {routeType.FullName}. Not of type {typeof(HttpRouteBase).FullName}"); + } + + var module = this.serviceManager.GetService(routeType) as HttpRouteBase; + (module as ISetHttpContext).SetClientData(client); + (module as ISetHttpContext).SetServer(server); + (module as ISetHttpContext).SetHttpRoutingHandler(this); + return module; + } + + private void RegisterRoute(HttpMethods method, string uri, Type routeType, Func routeEnabler) + { + if (!typeof(HttpRouteBase).IsAssignableFrom(routeType)) + { + throw new InvalidOperationException($"{routeType.FullName} must be of type {typeof(HttpRouteBase).FullName}"); + } + + this.serviceManager.RegisterSingleton(routeType, routeType); + this.moduleDictionary[method][uri] = (routeType, routeEnabler); + } } }