diff --git a/API/Content/Concerns.cs b/API/Content/Concerns.cs index 7c9bc27f..f9bfbb18 100644 --- a/API/Content/Concerns.cs +++ b/API/Content/Concerns.cs @@ -6,36 +6,16 @@ public static class Concerns { - /// - /// Creates a handler chain to wrap the specified handler into the - /// specified concerns. - /// - /// - /// Use this utility within the handler builders to add concerns - /// to the resulting handler instance. The last concern added - /// to the list of concerns will be the root handler returned by - /// this method. - /// - /// The parent handler of the chain - /// The concerns that should be wrapped around the inner handler - /// The logic creating the actual handler to be chained - /// The outermost handler or root of the chain - public static IHandler Chain(IHandler parent, IEnumerable concerns, Func factory) + public static IHandler Chain(IEnumerable concerns, IHandler handler) { - var stack = new Stack(concerns); + var content = handler; - return Chain(parent, stack, factory); - } - - private static IHandler Chain(IHandler parent, Stack remainders, Func factory) - { - if (remainders.Count > 0) + foreach (var concern in concerns) { - var concern = remainders.Pop(); - - return concern.Build(parent, p => Chain(p, remainders, factory)); + content = concern.Build(content); } - return factory(parent); + return content; } + } diff --git a/API/Content/IConcernBuilder.cs b/API/Content/IConcernBuilder.cs index edf21a38..af8d476d 100644 --- a/API/Content/IConcernBuilder.cs +++ b/API/Content/IConcernBuilder.cs @@ -1,16 +1,11 @@ namespace GenHTTP.Api.Content; /// -/// Interface which needs to be implementd by factories providing concern instances. +/// Interface which needs to be implemented by factories providing concern instances. /// public interface IConcernBuilder { - /// - /// Builds the concern and sets the inner handler of it. - /// - /// The parent of the resulting concern handler instance - /// A method providing the content of the concern - /// The newly created concern instance - IConcern Build(IHandler parent, Func contentFactory); + IConcern Build(IHandler content); + } diff --git a/API/Content/IHandler.cs b/API/Content/IHandler.cs index 507f4c34..55ab942a 100644 --- a/API/Content/IHandler.cs +++ b/API/Content/IHandler.cs @@ -9,11 +9,6 @@ namespace GenHTTP.Api.Content; public interface IHandler { - /// - /// The parent of this handler within the routing tree. - /// - IHandler Parent { get; } - /// /// Invoked to perform computation heavy or IO bound work /// that initializes the handler before handling the @@ -38,4 +33,5 @@ public interface IHandler /// The request to be handled /// The response to be sent to the requesting client ValueTask HandleAsync(IRequest request); + } diff --git a/API/Content/IHandlerBuilder.cs b/API/Content/IHandlerBuilder.cs index daa3350c..eafa45b0 100644 --- a/API/Content/IHandlerBuilder.cs +++ b/API/Content/IHandlerBuilder.cs @@ -9,9 +9,9 @@ public interface IHandlerBuilder /// /// Creates the configured handler instance. /// - /// The parent of the handler to be created /// The newly created handler instance - IHandler Build(IHandler parent); + IHandler Build(); + } public interface IHandlerBuilder : IHandlerBuilder where TBuilder : IHandlerBuilder @@ -26,4 +26,5 @@ public interface IHandlerBuilder : IHandlerBuilder where TBuilder /// /// The concern to be added to the resulting handler TBuilder Add(IConcernBuilder concern); + } diff --git a/API/Infrastructure/IServerBuilder.cs b/API/Infrastructure/IServerBuilder.cs index 4e40d6b7..6b60a94e 100644 --- a/API/Infrastructure/IServerBuilder.cs +++ b/API/Infrastructure/IServerBuilder.cs @@ -1,6 +1,7 @@ using System.Net; using System.Security.Authentication; using System.Security.Cryptography.X509Certificates; + using GenHTTP.Api.Content; namespace GenHTTP.Api.Infrastructure; @@ -27,7 +28,18 @@ public interface IServerBuilder : IBuilder /// Note that only a single handler is supported. To build are more /// complex application, consider passing a Layout instead. /// - T Handler(IHandlerBuilder handler); + T Handler(IHandler handler); + + /// + /// Specifies the root handler that will be invoked when + /// a client request needs to be handled. + /// + /// The handler to be invoked to handle requests + /// + /// Note that only a single handler is supported. To build are more + /// complex application, consider passing a Layout instead. + /// + T Handler(IHandlerBuilder handlerBuilder) => Handler(handlerBuilder.Build()); #endregion diff --git a/API/Protocol/ContentType.cs b/API/Protocol/ContentType.cs index 8108b512..daf164d9 100644 --- a/API/Protocol/ContentType.cs +++ b/API/Protocol/ContentType.cs @@ -20,6 +20,11 @@ public enum ContentType /// TextCss, + /// + /// A human readable YAML file. + /// + TextYaml, + /// /// A JavaScript source file. /// @@ -462,6 +467,9 @@ public class FlexibleContentType { ContentType.TextEventStream, "text/event-stream" }, + { + ContentType.TextYaml, "text/yaml" + }, { ContentType.Video3Gpp, "video/3gpp" }, diff --git a/Engine/Hosting/ServerHost.cs b/Engine/Hosting/ServerHost.cs index c3de41a9..c31ea038 100644 --- a/Engine/Hosting/ServerHost.cs +++ b/Engine/Hosting/ServerHost.cs @@ -91,7 +91,7 @@ public IServerHost RequestReadTimeout(TimeSpan timeout) return this; } - public IServerHost Handler(IHandlerBuilder handler) + public IServerHost Handler(IHandler handler) { _Builder.Handler(handler); return this; diff --git a/Engine/Infrastructure/CoreRouter.cs b/Engine/Infrastructure/CoreRouter.cs index 50c294ae..9f883a44 100644 --- a/Engine/Infrastructure/CoreRouter.cs +++ b/Engine/Infrastructure/CoreRouter.cs @@ -11,25 +11,19 @@ namespace GenHTTP.Engine.Infrastructure; internal sealed class CoreRouter : IHandler { - #region Initialization + #region Get-/Setters - internal CoreRouter(IHandlerBuilder content, IEnumerable concerns) - { - Content = Concerns.Chain(this, concerns, content.Build); - } + internal IHandler Content { get; } #endregion - #region Get-/Setters + #region Initialization - public IHandler Parent + internal CoreRouter(IHandler content, IEnumerable concerns) { - get => throw new NotSupportedException("Core router has no parent"); - set => throw new NotSupportedException("Setting core router's parent is not allowed"); + Content = Concerns.Chain(concerns, content); } - internal IHandler Content { get; } - #endregion #region Functionality diff --git a/Engine/Infrastructure/ThreadedServerBuilder.cs b/Engine/Infrastructure/ThreadedServerBuilder.cs index 8cf94e5b..f65ab5f5 100644 --- a/Engine/Infrastructure/ThreadedServerBuilder.cs +++ b/Engine/Infrastructure/ThreadedServerBuilder.cs @@ -1,9 +1,12 @@ using System.Net; using System.Security.Authentication; using System.Security.Cryptography.X509Certificates; + using GenHTTP.Api.Content; using GenHTTP.Api.Infrastructure; + using GenHTTP.Engine.Infrastructure.Endpoints; + using GenHTTP.Modules.ErrorHandling; namespace GenHTTP.Engine.Infrastructure; @@ -19,7 +22,7 @@ internal sealed class ThreadedServerBuilder : IServerBuilder private bool _Development; - private IHandlerBuilder? _Handler; + private IHandler? _Handler; private ushort _Port = 8080; @@ -30,7 +33,7 @@ internal sealed class ThreadedServerBuilder : IServerBuilder #region Content - public IServerBuilder Handler(IHandlerBuilder handler) + public IServerBuilder Handler(IHandler handler) { _Handler = handler; return this; diff --git a/GenHTTP.sln b/GenHTTP.sln index ee28b0aa..10116639 100644 --- a/GenHTTP.sln +++ b/GenHTTP.sln @@ -94,6 +94,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GenHTTP.Modules.Websockets" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GenHTTP.Modules.ServerSentEvents", "Modules\ServerSentEvents\GenHTTP.Modules.ServerSentEvents.csproj", "{69F3862A-0027-4312-A890-45549AF5D2B1}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GenHTTP.Modules.Inspection", "Modules\Inspection\GenHTTP.Modules.Inspection.csproj", "{2FE9B758-187F-41B3-96BF-1C2BB006F809}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -228,6 +230,10 @@ Global {69F3862A-0027-4312-A890-45549AF5D2B1}.Debug|Any CPU.Build.0 = Debug|Any CPU {69F3862A-0027-4312-A890-45549AF5D2B1}.Release|Any CPU.ActiveCfg = Release|Any CPU {69F3862A-0027-4312-A890-45549AF5D2B1}.Release|Any CPU.Build.0 = Release|Any CPU + {2FE9B758-187F-41B3-96BF-1C2BB006F809}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2FE9B758-187F-41B3-96BF-1C2BB006F809}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2FE9B758-187F-41B3-96BF-1C2BB006F809}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2FE9B758-187F-41B3-96BF-1C2BB006F809}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -267,6 +273,7 @@ Global {A5149821-D510-4854-9DC9-D489323BC545} = {23B23225-275E-4F52-8B29-6F44C85B6ACE} {9D3D3B40-691D-4EE1-B948-82525F28FBB2} = {23B23225-275E-4F52-8B29-6F44C85B6ACE} {69F3862A-0027-4312-A890-45549AF5D2B1} = {23B23225-275E-4F52-8B29-6F44C85B6ACE} + {2FE9B758-187F-41B3-96BF-1C2BB006F809} = {23B23225-275E-4F52-8B29-6F44C85B6ACE} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {9C67B3AF-0BF6-4E21-8C39-3F74CFCF9632} diff --git a/Modules/Authentication/ApiKey/ApiKeyConcern.cs b/Modules/Authentication/ApiKey/ApiKeyConcern.cs index 1367ea53..81900609 100644 --- a/Modules/Authentication/ApiKey/ApiKeyConcern.cs +++ b/Modules/Authentication/ApiKey/ApiKeyConcern.cs @@ -1,5 +1,6 @@ using GenHTTP.Api.Content; using GenHTTP.Api.Content.Authentication; + using GenHTTP.Api.Protocol; namespace GenHTTP.Modules.Authentication.ApiKey; @@ -7,23 +8,8 @@ namespace GenHTTP.Modules.Authentication.ApiKey; public sealed class ApiKeyConcern : IConcern { - #region Initialization - - public ApiKeyConcern(IHandler parent, Func contentFactory, Func keyExtractor, Func> authenticator) - { - Parent = parent; - Content = contentFactory(this); - - KeyExtractor = keyExtractor; - Authenticator = authenticator; - } - - #endregion - #region Get-/Setters - public IHandler Parent { get; } - public IHandler Content { get; } private Func KeyExtractor { get; } @@ -32,6 +18,18 @@ public ApiKeyConcern(IHandler parent, Func contentFactory, F #endregion + #region Initialization + + public ApiKeyConcern(IHandler content, Func keyExtractor, Func> authenticator) + { + Content = content; + + KeyExtractor = keyExtractor; + Authenticator = authenticator; + } + + #endregion + #region Functionality public async ValueTask HandleAsync(IRequest request) diff --git a/Modules/Authentication/ApiKey/ApiKeyConcernBuilder.cs b/Modules/Authentication/ApiKey/ApiKeyConcernBuilder.cs index d596fa6d..fa81ae47 100644 --- a/Modules/Authentication/ApiKey/ApiKeyConcernBuilder.cs +++ b/Modules/Authentication/ApiKey/ApiKeyConcernBuilder.cs @@ -70,13 +70,13 @@ public ApiKeyConcernBuilder Keys(params string[] allowedKeys) return this; } - public IConcern Build(IHandler parent, Func contentFactory) + public IConcern Build(IHandler content) { var keyExtractor = _KeyExtractor ?? throw new BuilderMissingPropertyException("KeyExtractor"); var authenticator = _Authenticator ?? throw new BuilderMissingPropertyException("Authenticator"); - return new ApiKeyConcern(parent, contentFactory, keyExtractor, authenticator); + return new ApiKeyConcern(content, keyExtractor, authenticator); } #endregion diff --git a/Modules/Authentication/Basic/BasicAuthenticationConcern.cs b/Modules/Authentication/Basic/BasicAuthenticationConcern.cs index 8d21bc6a..89aa33ff 100644 --- a/Modules/Authentication/Basic/BasicAuthenticationConcern.cs +++ b/Modules/Authentication/Basic/BasicAuthenticationConcern.cs @@ -1,4 +1,5 @@ using System.Text; + using GenHTTP.Api.Content; using GenHTTP.Api.Content.Authentication; using GenHTTP.Api.Protocol; @@ -8,32 +9,28 @@ namespace GenHTTP.Modules.Authentication.Basic; public sealed class BasicAuthenticationConcern : IConcern { - #region Initialization - - public BasicAuthenticationConcern(IHandler parent, Func contentFactory, string realm, - Func> authenticator) - { - Parent = parent; - Content = contentFactory(this); - - Realm = realm; - Authenticator = authenticator; - } - - #endregion - #region Get-/Setters public IHandler Content { get; } - public IHandler Parent { get; } - private string Realm { get; } private Func> Authenticator { get; } #endregion + #region Initialization + + public BasicAuthenticationConcern(IHandler content, string realm, Func> authenticator) + { + Content = content; + + Realm = realm; + Authenticator = authenticator; + } + + #endregion + #region Functionality public ValueTask PrepareAsync() => Content.PrepareAsync(); diff --git a/Modules/Authentication/Basic/BasicAuthenticationConcernBuilder.cs b/Modules/Authentication/Basic/BasicAuthenticationConcernBuilder.cs index 6eae2981..4b7da07d 100644 --- a/Modules/Authentication/Basic/BasicAuthenticationConcernBuilder.cs +++ b/Modules/Authentication/Basic/BasicAuthenticationConcernBuilder.cs @@ -24,13 +24,13 @@ public BasicAuthenticationConcernBuilder Handler(Func contentFactory) + public IConcern Build(IHandler content) { var realm = _Realm ?? throw new BuilderMissingPropertyException("Realm"); var handler = _Handler ?? throw new BuilderMissingPropertyException("Handler"); - return new BasicAuthenticationConcern(parent, contentFactory, realm, handler); + return new BasicAuthenticationConcern(content, realm, handler); } #endregion diff --git a/Modules/Authentication/Basic/BasicAuthenticationKnownUsersBuilder.cs b/Modules/Authentication/Basic/BasicAuthenticationKnownUsersBuilder.cs index 70c2e423..88382569 100644 --- a/Modules/Authentication/Basic/BasicAuthenticationKnownUsersBuilder.cs +++ b/Modules/Authentication/Basic/BasicAuthenticationKnownUsersBuilder.cs @@ -24,11 +24,11 @@ public BasicAuthenticationKnownUsersBuilder Add(string user, string password) return this; } - public IConcern Build(IHandler parent, Func contentFactory) + public IConcern Build(IHandler content) { var realm = _Realm ?? throw new BuilderMissingPropertyException("Realm"); - return new BasicAuthenticationConcern(parent, contentFactory, realm, (user, password) => + return new BasicAuthenticationConcern(content, realm, (user, password) => { if (_Users.TryGetValue(user, out var expected)) { diff --git a/Modules/Authentication/Bearer/BearerAuthenticationConcern.cs b/Modules/Authentication/Bearer/BearerAuthenticationConcern.cs index 21e0e504..622d87d0 100644 --- a/Modules/Authentication/Bearer/BearerAuthenticationConcern.cs +++ b/Modules/Authentication/Bearer/BearerAuthenticationConcern.cs @@ -23,25 +23,22 @@ internal sealed class BearerAuthenticationConcern : IConcern { private ICollection? _IssuerKeys; - #region Initialization + #region Get-/Setters - internal BearerAuthenticationConcern(IHandler parent, Func contentFactory, TokenValidationOptions validationOptions) - { - Parent = parent; - Content = contentFactory(this); + public IHandler Content { get; } - ValidationOptions = validationOptions; - } + private TokenValidationOptions ValidationOptions { get; } #endregion - #region Get-/Setters - - public IHandler Content { get; } + #region Initialization - public IHandler Parent { get; } + internal BearerAuthenticationConcern(IHandler content, TokenValidationOptions validationOptions) + { + Content = content; - private TokenValidationOptions ValidationOptions { get; } + ValidationOptions = validationOptions; + } #endregion diff --git a/Modules/Authentication/Bearer/BearerAuthenticationConcernBuilder.cs b/Modules/Authentication/Bearer/BearerAuthenticationConcernBuilder.cs index 1c95f950..f97b39df 100644 --- a/Modules/Authentication/Bearer/BearerAuthenticationConcernBuilder.cs +++ b/Modules/Authentication/Bearer/BearerAuthenticationConcernBuilder.cs @@ -81,7 +81,7 @@ public BearerAuthenticationConcernBuilder AllowExpired() return this; } - public IConcern Build(IHandler parent, Func contentFactory) => new BearerAuthenticationConcern(parent, contentFactory, _Options); + public IConcern Build(IHandler content) => new BearerAuthenticationConcern(content, _Options); #endregion diff --git a/Modules/Basics/Providers/RedirectProvider.cs b/Modules/Basics/Providers/RedirectProvider.cs index 11c1b7d5..3b23a389 100644 --- a/Modules/Basics/Providers/RedirectProvider.cs +++ b/Modules/Basics/Providers/RedirectProvider.cs @@ -14,16 +14,12 @@ public sealed partial class RedirectProvider : IHandler public bool Temporary { get; } - public IHandler Parent { get; } - #endregion #region Initialization - public RedirectProvider(IHandler parent, string location, bool temporary) + public RedirectProvider(string location, bool temporary) { - Parent = parent; - Target = location; Temporary = temporary; } diff --git a/Modules/Basics/Providers/RedirectProviderBuilder.cs b/Modules/Basics/Providers/RedirectProviderBuilder.cs index 775733b7..5661f05e 100644 --- a/Modules/Basics/Providers/RedirectProviderBuilder.cs +++ b/Modules/Basics/Providers/RedirectProviderBuilder.cs @@ -30,14 +30,14 @@ public RedirectProviderBuilder Add(IConcernBuilder concern) return this; } - public IHandler Build(IHandler parent) + public IHandler Build() { if (_Location is null) { throw new BuilderMissingPropertyException("Location"); } - return Concerns.Chain(parent, _Concerns, p => new RedirectProvider(p, _Location, _Temporary)); + return Concerns.Chain(_Concerns, new RedirectProvider(_Location, _Temporary)); } #endregion diff --git a/Modules/ClientCaching/Policy/CachePolicyBuilder.cs b/Modules/ClientCaching/Policy/CachePolicyBuilder.cs index 0e91cf11..13c81735 100644 --- a/Modules/ClientCaching/Policy/CachePolicyBuilder.cs +++ b/Modules/ClientCaching/Policy/CachePolicyBuilder.cs @@ -41,11 +41,11 @@ public CachePolicyBuilder Predicate(Func predicate) return this; } - public IConcern Build(IHandler parent, Func contentFactory) + public IConcern Build(IHandler content) { var duration = _Duration ?? throw new BuilderMissingPropertyException("Duration"); - return new CachePolicyConcern(parent, contentFactory, duration, _Predicate); + return new CachePolicyConcern(content, duration, _Predicate); } #endregion diff --git a/Modules/ClientCaching/Policy/CachePolicyConcern.cs b/Modules/ClientCaching/Policy/CachePolicyConcern.cs index 622afda9..9d227388 100644 --- a/Modules/ClientCaching/Policy/CachePolicyConcern.cs +++ b/Modules/ClientCaching/Policy/CachePolicyConcern.cs @@ -1,5 +1,6 @@ using GenHTTP.Api.Content; using GenHTTP.Api.Protocol; + using GenHTTP.Modules.Basics; namespace GenHTTP.Modules.ClientCaching.Policy; @@ -7,32 +8,28 @@ namespace GenHTTP.Modules.ClientCaching.Policy; public sealed class CachePolicyConcern : IConcern { - #region Initialization - - public CachePolicyConcern(IHandler parent, Func contentFactory, - TimeSpan duration, Func? predicate) - { - Parent = parent; - Content = contentFactory(this); - - Duration = duration; - Predicate = predicate; - } - - #endregion - #region Get-/Setters public IHandler Content { get; } - public IHandler Parent { get; } - private TimeSpan Duration { get; } private Func? Predicate { get; } #endregion + #region Initialization + + public CachePolicyConcern(IHandler content, TimeSpan duration, Func? predicate) + { + Content = content; + + Duration = duration; + Predicate = predicate; + } + + #endregion + #region Functionality public async ValueTask HandleAsync(IRequest request) diff --git a/Modules/ClientCaching/Validation/CacheValidationBuilder.cs b/Modules/ClientCaching/Validation/CacheValidationBuilder.cs index 06f1bd56..d9ae16dc 100644 --- a/Modules/ClientCaching/Validation/CacheValidationBuilder.cs +++ b/Modules/ClientCaching/Validation/CacheValidationBuilder.cs @@ -5,5 +5,5 @@ namespace GenHTTP.Modules.ClientCaching.Validation; public sealed class CacheValidationBuilder : IConcernBuilder { - public IConcern Build(IHandler parent, Func contentFactory) => new CacheValidationHandler(parent, contentFactory); + public IConcern Build(IHandler content) => new CacheValidationHandler(content); } diff --git a/Modules/ClientCaching/Validation/CacheValidationHandler.cs b/Modules/ClientCaching/Validation/CacheValidationHandler.cs index b3fe8909..b38a576c 100644 --- a/Modules/ClientCaching/Validation/CacheValidationHandler.cs +++ b/Modules/ClientCaching/Validation/CacheValidationHandler.cs @@ -1,5 +1,6 @@ using GenHTTP.Api.Content; using GenHTTP.Api.Protocol; + using GenHTTP.Modules.Basics; namespace GenHTTP.Modules.ClientCaching.Validation; @@ -10,21 +11,18 @@ public sealed class CacheValidationHandler : IConcern private static readonly RequestMethod[] SupportedMethods = [RequestMethod.Get, RequestMethod.Head]; - #region Initialization + #region Get-/Setters - public CacheValidationHandler(IHandler parent, Func contentFactory) - { - Parent = parent; - Content = contentFactory(this); - } + public IHandler Content { get; } #endregion - #region Get-/Setters - - public IHandler Parent { get; } + #region Initialization - public IHandler Content { get; } + public CacheValidationHandler(IHandler content) + { + Content = content; + } #endregion diff --git a/Modules/Compression/Providers/CompressionConcern.cs b/Modules/Compression/Providers/CompressionConcern.cs index 50489b56..355c8487 100644 --- a/Modules/Compression/Providers/CompressionConcern.cs +++ b/Modules/Compression/Providers/CompressionConcern.cs @@ -12,14 +12,22 @@ public sealed class CompressionConcern : IConcern private const string Vary = "Vary"; + #region Get-/Setters + + public IHandler Content { get; } + + private IReadOnlyDictionary Algorithms { get; } + + private CompressionLevel Level { get; } + + #endregion + #region Initialization - public CompressionConcern(IHandler parent, Func contentFactory, - IReadOnlyDictionary algorithms, + public CompressionConcern(IHandler content, IReadOnlyDictionary algorithms, CompressionLevel level) { - Parent = parent; - Content = contentFactory(this); + Content = content; Algorithms = algorithms; Level = level; @@ -27,18 +35,6 @@ public CompressionConcern(IHandler parent, Func contentFacto #endregion - #region Get-/Setters - - public IHandler Content { get; } - - public IHandler Parent { get; } - - private IReadOnlyDictionary Algorithms { get; } - - private CompressionLevel Level { get; } - - #endregion - #region Functionality public async ValueTask HandleAsync(IRequest request) diff --git a/Modules/Compression/Providers/CompressionConcernBuilder.cs b/Modules/Compression/Providers/CompressionConcernBuilder.cs index dfb387f0..2265c646 100644 --- a/Modules/Compression/Providers/CompressionConcernBuilder.cs +++ b/Modules/Compression/Providers/CompressionConcernBuilder.cs @@ -27,11 +27,11 @@ public CompressionConcernBuilder Level(CompressionLevel level) return this; } - public IConcern Build(IHandler parent, Func contentFactory) + public IConcern Build(IHandler content) { var algorithms = _Algorithms.ToDictionary(a => a.Name); - return new CompressionConcern(parent, contentFactory, algorithms, _Level); + return new CompressionConcern(content, algorithms, _Level); } #endregion diff --git a/Modules/Controllers/Provider/ControllerBuilder.cs b/Modules/Controllers/Provider/ControllerBuilder.cs index e15aa912..7ede317e 100644 --- a/Modules/Controllers/Provider/ControllerBuilder.cs +++ b/Modules/Controllers/Provider/ControllerBuilder.cs @@ -59,7 +59,7 @@ public ControllerBuilder Add(IConcernBuilder concern) return this; } - public IHandler Build(IHandler parent) + public IHandler Build() { var serializers = (_Serializers ?? Serialization.Default()).Build(); @@ -71,7 +71,7 @@ public IHandler Build(IHandler parent) var extensions = new MethodRegistry(serializers, injectors, formatters); - return Concerns.Chain(parent, _Concerns, p => new ControllerHandler(p, instance, extensions)); + return Concerns.Chain(_Concerns, new ControllerHandler(instance, extensions)); } #endregion diff --git a/Modules/Controllers/Provider/ControllerHandler.cs b/Modules/Controllers/Provider/ControllerHandler.cs index dd6a9c0c..b463f2be 100644 --- a/Modules/Controllers/Provider/ControllerHandler.cs +++ b/Modules/Controllers/Provider/ControllerHandler.cs @@ -1,7 +1,9 @@ using System.Reflection; using System.Text.RegularExpressions; + using GenHTTP.Api.Content; using GenHTTP.Api.Protocol; + using GenHTTP.Modules.Reflection; using GenHTTP.Modules.Reflection.Operations; @@ -13,8 +15,6 @@ public sealed partial class ControllerHandler : IHandler, IServiceMethodProvider #region Get-/Setters - public IHandler Parent { get; } - public MethodCollection Methods { get; } private ResponseProvider ResponseProvider { get; } @@ -27,19 +27,18 @@ public sealed partial class ControllerHandler : IHandler, IServiceMethodProvider #region Initialization - public ControllerHandler(IHandler parent, object instance, MethodRegistry registry) + public ControllerHandler(object instance, MethodRegistry registry) { - Parent = parent; Registry = registry; Instance = instance; ResponseProvider = new ResponseProvider(registry); - Methods = new MethodCollection(this, AnalyzeMethods(instance.GetType(), registry)); + Methods = new MethodCollection(AnalyzeMethods(instance.GetType(), registry)); } - private IEnumerable> AnalyzeMethods(Type type, MethodRegistry registry) + private IEnumerable AnalyzeMethods(Type type, MethodRegistry registry) { foreach (var method in type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)) { @@ -49,7 +48,7 @@ private IEnumerable> AnalyzeMethods(Type type, Met var operation = CreateOperation(method, arguments); - yield return parent => new MethodHandler(parent, operation, Instance, annotation, registry); + yield return new MethodHandler(operation, Instance, annotation, registry); } } diff --git a/Modules/Conversion/GenHTTP.Modules.Conversion.csproj b/Modules/Conversion/GenHTTP.Modules.Conversion.csproj index c379fe6a..3d552497 100644 --- a/Modules/Conversion/GenHTTP.Modules.Conversion.csproj +++ b/Modules/Conversion/GenHTTP.Modules.Conversion.csproj @@ -41,11 +41,13 @@ - + + + diff --git a/Modules/Conversion/Serialization.cs b/Modules/Conversion/Serialization.cs index 58a8ee66..1aa6f320 100644 --- a/Modules/Conversion/Serialization.cs +++ b/Modules/Conversion/Serialization.cs @@ -1,8 +1,10 @@ using GenHTTP.Api.Protocol; + using GenHTTP.Modules.Conversion.Serializers; using GenHTTP.Modules.Conversion.Serializers.Forms; using GenHTTP.Modules.Conversion.Serializers.Json; using GenHTTP.Modules.Conversion.Serializers.Xml; +using GenHTTP.Modules.Conversion.Serializers.Yaml; namespace GenHTTP.Modules.Conversion; @@ -19,6 +21,7 @@ public static class Serialization /// public static SerializationBuilder Default() => new SerializationBuilder().Default(ContentType.ApplicationJson) .Add(ContentType.ApplicationJson, new JsonFormat()) + .Add(ContentType.ApplicationYaml, new YamlFormat()) .Add(ContentType.ApplicationWwwFormUrlEncoded, new FormFormat()) .Add(ContentType.TextXml, new XmlFormat()); @@ -26,4 +29,5 @@ public static class Serialization /// Returns an empty registry to be customized. /// public static SerializationBuilder Empty() => new(); + } diff --git a/Modules/Conversion/Serializers/Yaml/YamlContent.cs b/Modules/Conversion/Serializers/Yaml/YamlContent.cs new file mode 100644 index 00000000..c8774c23 --- /dev/null +++ b/Modules/Conversion/Serializers/Yaml/YamlContent.cs @@ -0,0 +1,42 @@ +using System.Text; + +using GenHTTP.Api.Protocol; + +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.NamingConventions; + +namespace GenHTTP.Modules.Conversion.Serializers.Yaml; + +public sealed class YamlContent : IResponseContent +{ + private static readonly ISerializer Serializer = new SerializerBuilder().WithNamingConvention(CamelCaseNamingConvention.Instance).Build(); + + private static readonly Encoding Encoding = Encoding.UTF8; + + #region Get-/Setters + + public ulong? Length => null; + + public object Data { get; } + + #endregion + + #region Initialization + + public YamlContent(object data) + { + Data = data; + } + + #endregion + + #region Functionality + + public ValueTask CalculateChecksumAsync() => new((ulong)Data.GetHashCode()); + + public ValueTask WriteAsync(Stream target, uint bufferSize) + => target.WriteAsync(Encoding.GetBytes(Serializer.Serialize(Data))); + + #endregion + +} diff --git a/Modules/Conversion/Serializers/Yaml/YamlFormat.cs b/Modules/Conversion/Serializers/Yaml/YamlFormat.cs new file mode 100644 index 00000000..62acf8c0 --- /dev/null +++ b/Modules/Conversion/Serializers/Yaml/YamlFormat.cs @@ -0,0 +1,45 @@ +using GenHTTP.Api.Protocol; + +using GenHTTP.Modules.Basics; + +using YamlDotNet.Serialization; + +namespace GenHTTP.Modules.Conversion.Serializers.Yaml; + +public sealed class YamlFormat : ISerializationFormat +{ + private static readonly IDeserializer Deserializer = new DeserializerBuilder().WithNamingConvention(new IgnoreCaseNamingConvention()) + .Build(); + + #region Supporting data structures + + private sealed class IgnoreCaseNamingConvention : INamingConvention + { + public string Apply(string value) => value.ToLowerInvariant(); + + public string Reverse(string value) => throw new NotImplementedException(); + } + + #endregion + + #region Functionality + + public ValueTask DeserializeAsync(Stream stream, Type type) + { + using var reader = new StreamReader(stream, leaveOpen: true); + + return new(Deserializer.Deserialize(reader, type)); + } + + public ValueTask SerializeAsync(IRequest request, object response) + { + var result = request.Respond() + .Content(new YamlContent(response)) + .Type(ContentType.ApplicationYaml); + + return new ValueTask(result); + } + + #endregion + +} diff --git a/Modules/DirectoryBrowsing/Provider/ListingProvider.cs b/Modules/DirectoryBrowsing/Provider/ListingProvider.cs index 55415c50..b8efa346 100644 --- a/Modules/DirectoryBrowsing/Provider/ListingProvider.cs +++ b/Modules/DirectoryBrowsing/Provider/ListingProvider.cs @@ -8,21 +8,18 @@ namespace GenHTTP.Modules.DirectoryBrowsing.Provider; public sealed class ListingProvider : IHandler { - #region Initialization + #region Get-/Setters - public ListingProvider(IHandler parent, IResourceContainer container) - { - Parent = parent; - Container = container; - } + public IResourceContainer Container { get; } #endregion - #region Get-/Setters - - public IHandler Parent { get; } + #region Initialization - public IResourceContainer Container { get; } + public ListingProvider(IResourceContainer container) + { + Container = container; + } #endregion diff --git a/Modules/DirectoryBrowsing/Provider/ListingRouter.cs b/Modules/DirectoryBrowsing/Provider/ListingRouter.cs index 2bad07b8..8d022566 100644 --- a/Modules/DirectoryBrowsing/Provider/ListingRouter.cs +++ b/Modules/DirectoryBrowsing/Provider/ListingRouter.cs @@ -1,6 +1,7 @@ using GenHTTP.Api.Content; using GenHTTP.Api.Content.IO; using GenHTTP.Api.Protocol; + using GenHTTP.Modules.IO; namespace GenHTTP.Modules.DirectoryBrowsing.Provider; @@ -8,21 +9,18 @@ namespace GenHTTP.Modules.DirectoryBrowsing.Provider; public sealed class ListingRouter : IHandler { - #region Initialization + #region Get-/Setters - public ListingRouter(IHandler parent, IResourceTree tree) - { - Parent = parent; - Tree = tree; - } + private IResourceTree Tree { get; } #endregion - #region Get-/Setters - - public IHandler Parent { get; } + #region Initialization - private IResourceTree Tree { get; } + public ListingRouter(IResourceTree tree) + { + Tree = tree; + } #endregion @@ -35,12 +33,12 @@ public ListingRouter(IHandler parent, IResourceTree tree) if (resource is not null) { return await Content.From(resource) - .Build(this) + .Build() .HandleAsync(request); } if (node is not null) { - return await new ListingProvider(this, node).HandleAsync(request); + return await new ListingProvider(node).HandleAsync(request); } return null; diff --git a/Modules/DirectoryBrowsing/Provider/ListingRouterBuilder.cs b/Modules/DirectoryBrowsing/Provider/ListingRouterBuilder.cs index 02fcc813..e9a3a6d4 100644 --- a/Modules/DirectoryBrowsing/Provider/ListingRouterBuilder.cs +++ b/Modules/DirectoryBrowsing/Provider/ListingRouterBuilder.cs @@ -26,11 +26,11 @@ public ListingRouterBuilder Add(IConcernBuilder concern) return this; } - public IHandler Build(IHandler parent) + public IHandler Build() { var tree = _Tree ?? throw new BuilderMissingPropertyException("tree"); - return Concerns.Chain(parent, _Concerns, p => new ListingRouter(p, tree)); + return Concerns.Chain(_Concerns, new ListingRouter( tree)); } #endregion diff --git a/Modules/ErrorHandling/Provider/ErrorSentry.cs b/Modules/ErrorHandling/Provider/ErrorSentry.cs index 4d353d7e..b6395a03 100644 --- a/Modules/ErrorHandling/Provider/ErrorSentry.cs +++ b/Modules/ErrorHandling/Provider/ErrorSentry.cs @@ -6,25 +6,22 @@ namespace GenHTTP.Modules.ErrorHandling.Provider; public sealed class ErrorSentry : IConcern where T : Exception { - #region Initialization + #region Get-/Setters - public ErrorSentry(IHandler parent, Func contentFactory, IErrorMapper errorHandler) - { - Parent = parent; - Content = contentFactory(this); + public IHandler Content { get; } - ErrorHandler = errorHandler; - } + private IErrorMapper ErrorHandler { get; } #endregion - #region Get-/Setters - - public IHandler Content { get; } + #region Initialization - public IHandler Parent { get; } + public ErrorSentry(IHandler content, IErrorMapper errorHandler) + { + Content = content; - private IErrorMapper ErrorHandler { get; } + ErrorHandler = errorHandler; + } #endregion diff --git a/Modules/ErrorHandling/Provider/ErrorSentryBuilder.cs b/Modules/ErrorHandling/Provider/ErrorSentryBuilder.cs index ae18b73a..05d226d3 100644 --- a/Modules/ErrorHandling/Provider/ErrorSentryBuilder.cs +++ b/Modules/ErrorHandling/Provider/ErrorSentryBuilder.cs @@ -22,7 +22,7 @@ public ErrorSentryBuilder(IErrorMapper handler) #region Functionality - public IConcern Build(IHandler parent, Func contentFactory) => new ErrorSentry(parent, contentFactory, Handler); + public IConcern Build(IHandler content) => new ErrorSentry(content, Handler); #endregion diff --git a/Modules/Functional/Provider/InlineBuilder.cs b/Modules/Functional/Provider/InlineBuilder.cs index cdbe1a44..11127624 100644 --- a/Modules/Functional/Provider/InlineBuilder.cs +++ b/Modules/Functional/Provider/InlineBuilder.cs @@ -193,7 +193,7 @@ public InlineBuilder Add(IConcernBuilder concern) return this; } - public IHandler Build(IHandler parent) + public IHandler Build() { var serializers = (_Serializers ?? Serialization.Default()).Build(); @@ -203,7 +203,7 @@ public IHandler Build(IHandler parent) var extensions = new MethodRegistry(serializers, injectors, formatters); - return Concerns.Chain(parent, _Concerns, p => new InlineHandler(p, _Functions, extensions)); + return Concerns.Chain(_Concerns, new InlineHandler( _Functions, extensions)); } #endregion diff --git a/Modules/Functional/Provider/InlineHandler.cs b/Modules/Functional/Provider/InlineHandler.cs index 0e3574e9..eb0d99c7 100644 --- a/Modules/Functional/Provider/InlineHandler.cs +++ b/Modules/Functional/Provider/InlineHandler.cs @@ -1,5 +1,6 @@ using GenHTTP.Api.Content; using GenHTTP.Api.Protocol; + using GenHTTP.Modules.Reflection; using GenHTTP.Modules.Reflection.Operations; @@ -10,8 +11,6 @@ public class InlineHandler : IHandler, IServiceMethodProvider #region Get-/Setters - public IHandler Parent { get; } - public MethodCollection Methods { get; } private ResponseProvider ResponseProvider { get; } @@ -20,16 +19,14 @@ public class InlineHandler : IHandler, IServiceMethodProvider #region Initialization - public InlineHandler(IHandler parent, List functions, MethodRegistry registry) + public InlineHandler(List functions, MethodRegistry registry) { - Parent = parent; - ResponseProvider = new ResponseProvider(registry); - Methods = new MethodCollection(this, AnalyzeMethods(functions, registry)); + Methods = new MethodCollection(AnalyzeMethods(functions, registry)); } - private static IEnumerable> AnalyzeMethods(List functions, MethodRegistry registry) + private static IEnumerable AnalyzeMethods(List functions, MethodRegistry registry) { foreach (var function in functions) { @@ -39,7 +36,7 @@ private static IEnumerable> AnalyzeMethods(List new MethodHandler(parent, operation, target, function.Configuration, registry); + yield return new MethodHandler(operation, target, function.Configuration, registry); } } diff --git a/Modules/IO/Providers/ContentProvider.cs b/Modules/IO/Providers/ContentProvider.cs index a69f6941..ac37e8ef 100644 --- a/Modules/IO/Providers/ContentProvider.cs +++ b/Modules/IO/Providers/ContentProvider.cs @@ -9,11 +9,20 @@ namespace GenHTTP.Modules.IO.Providers; public sealed class ContentProvider : IHandler { + #region Get-/Setters + + public IResource Resource { get; } + + private IResponseContent Content { get; } + + private FlexibleContentType ContentType { get; } + + #endregion + #region Initialization - public ContentProvider(IHandler parent, IResource resourceProvider) + public ContentProvider(IResource resourceProvider) { - Parent = parent; Resource = resourceProvider; Content = new ResourceContent(Resource); @@ -22,18 +31,6 @@ public ContentProvider(IHandler parent, IResource resourceProvider) #endregion - #region Get-/Setters - - public IHandler Parent { get; } - - public IResource Resource { get; } - - private IResponseContent Content { get; } - - private FlexibleContentType ContentType { get; } - - #endregion - #region Functionality public ValueTask HandleAsync(IRequest request) => request.Respond() diff --git a/Modules/IO/Providers/ContentProviderBuilder.cs b/Modules/IO/Providers/ContentProviderBuilder.cs index 225f147b..6c287a3d 100644 --- a/Modules/IO/Providers/ContentProviderBuilder.cs +++ b/Modules/IO/Providers/ContentProviderBuilder.cs @@ -24,11 +24,11 @@ public ContentProviderBuilder Add(IConcernBuilder concern) return this; } - public IHandler Build(IHandler parent) + public IHandler Build() { var resource = _ResourceProvider ?? throw new BuilderMissingPropertyException("resourceProvider"); - return Concerns.Chain(parent, _Concerns, p => new ContentProvider(p, resource)); + return Concerns.Chain(_Concerns, new ContentProvider( resource)); } #endregion diff --git a/Modules/IO/Providers/DownloadProvider.cs b/Modules/IO/Providers/DownloadProvider.cs index 2b07b635..ea69bcbf 100644 --- a/Modules/IO/Providers/DownloadProvider.cs +++ b/Modules/IO/Providers/DownloadProvider.cs @@ -8,30 +8,26 @@ namespace GenHTTP.Modules.IO.Providers; public sealed class DownloadProvider : IHandler { - #region Initialization - - public DownloadProvider(IHandler parent, IResource resourceProvider, string? fileName, FlexibleContentType? contentType) - { - Parent = parent; + #region Get-/Setters - Resource = resourceProvider; + public IResource Resource { get; } - FileName = fileName ?? Resource.Name; + public string? FileName { get; } - ContentType = contentType ?? Resource.ContentType ?? FlexibleContentType.Get(FileName?.GuessContentType() ?? Api.Protocol.ContentType.ApplicationForceDownload); - } + private FlexibleContentType ContentType { get; } #endregion - #region Get-/Setters - - public IHandler Parent { get; } + #region Initialization - public IResource Resource { get; } + public DownloadProvider(IResource resourceProvider, string? fileName, FlexibleContentType? contentType) + { + Resource = resourceProvider; - public string? FileName { get; } + FileName = fileName ?? Resource.Name; - private FlexibleContentType ContentType { get; } + ContentType = contentType ?? Resource.ContentType ?? FlexibleContentType.Get(FileName?.GuessContentType() ?? Api.Protocol.ContentType.ApplicationForceDownload); + } #endregion diff --git a/Modules/IO/Providers/DownloadProviderBuilder.cs b/Modules/IO/Providers/DownloadProviderBuilder.cs index c1d111da..73602a83 100644 --- a/Modules/IO/Providers/DownloadProviderBuilder.cs +++ b/Modules/IO/Providers/DownloadProviderBuilder.cs @@ -43,11 +43,11 @@ public DownloadProviderBuilder Add(IConcernBuilder concern) return this; } - public IHandler Build(IHandler parent) + public IHandler Build() { var resource = _ResourceProvider ?? throw new BuilderMissingPropertyException("resourceProvider"); - return Concerns.Chain(parent, _Concerns, p => new DownloadProvider(p, resource, _FileName, _ContentType)); + return Concerns.Chain(_Concerns, new DownloadProvider( resource, _FileName, _ContentType)); } #endregion diff --git a/Modules/IO/Providers/ResourceHandler.cs b/Modules/IO/Providers/ResourceHandler.cs index aebc0c3e..bc18d4cb 100644 --- a/Modules/IO/Providers/ResourceHandler.cs +++ b/Modules/IO/Providers/ResourceHandler.cs @@ -1,6 +1,7 @@ using GenHTTP.Api.Content; using GenHTTP.Api.Content.IO; using GenHTTP.Api.Protocol; + using GenHTTP.Modules.Basics; namespace GenHTTP.Modules.IO.Providers; @@ -8,21 +9,18 @@ namespace GenHTTP.Modules.IO.Providers; public sealed class ResourceHandler : IHandler { - #region Initialization + #region Get-/Setters - public ResourceHandler(IHandler parent, IResourceTree tree) - { - Parent = parent; - Tree = tree; - } + private IResourceTree Tree { get; } #endregion - #region Get-/Setters - - public IHandler Parent { get; } + #region Initialization - private IResourceTree Tree { get; } + public ResourceHandler(IResourceTree tree) + { + Tree = tree; + } #endregion diff --git a/Modules/IO/Providers/ResourceHandlerBuilder.cs b/Modules/IO/Providers/ResourceHandlerBuilder.cs index ef2ee3b4..1e35edac 100644 --- a/Modules/IO/Providers/ResourceHandlerBuilder.cs +++ b/Modules/IO/Providers/ResourceHandlerBuilder.cs @@ -26,11 +26,11 @@ public ResourceHandlerBuilder Add(IConcernBuilder concern) return this; } - public IHandler Build(IHandler parent) + public IHandler Build() { var tree = _Tree ?? throw new BuilderMissingPropertyException("tree"); - return Concerns.Chain(parent, _Concerns, p => new ResourceHandler(p, tree)); + return Concerns.Chain(_Concerns, new ResourceHandler( tree)); } #endregion diff --git a/Modules/IO/Ranges/RangeSupportConcern.cs b/Modules/IO/Ranges/RangeSupportConcern.cs index f2192d63..610ddc4b 100644 --- a/Modules/IO/Ranges/RangeSupportConcern.cs +++ b/Modules/IO/Ranges/RangeSupportConcern.cs @@ -12,16 +12,13 @@ public partial class RangeSupportConcern : IConcern public IHandler Content { get; } - public IHandler Parent { get; } - #endregion #region Initialization - public RangeSupportConcern(IHandler parent, Func contentFactory) + public RangeSupportConcern(IHandler content) { - Parent = parent; - Content = contentFactory(this); + Content = content; } [GeneratedRegex(@"^\s*bytes\s*=\s*([0-9]*)-([0-9]*)\s*$", RegexOptions.IgnoreCase | RegexOptions.Compiled, "en-US")] diff --git a/Modules/IO/Ranges/RangeSupportConcernBuilder.cs b/Modules/IO/Ranges/RangeSupportConcernBuilder.cs index f641826d..c8c00c10 100644 --- a/Modules/IO/Ranges/RangeSupportConcernBuilder.cs +++ b/Modules/IO/Ranges/RangeSupportConcernBuilder.cs @@ -7,7 +7,7 @@ public class RangeSupportConcernBuilder : IConcernBuilder #region Functionality - public IConcern Build(IHandler parent, Func contentFactory) => new RangeSupportConcern(parent, contentFactory); + public IConcern Build(IHandler content) => new RangeSupportConcern(content); #endregion diff --git a/Modules/Inspection/Concern/InspectionConcern.cs b/Modules/Inspection/Concern/InspectionConcern.cs new file mode 100644 index 00000000..ba0bae3c --- /dev/null +++ b/Modules/Inspection/Concern/InspectionConcern.cs @@ -0,0 +1,132 @@ +using GenHTTP.Api.Content; +using GenHTTP.Api.Protocol; + +using GenHTTP.Modules.Basics; +using GenHTTP.Modules.Conversion.Serializers; +using GenHTTP.Modules.Conversion.Serializers.Yaml; + +using Strings = GenHTTP.Modules.IO.Strings; + +namespace GenHTTP.Modules.Inspection.Concern; + +public sealed class InspectionConcern : IConcern +{ + + #region Get-/Setters + + public IHandler Content { get; } + + public SerializationRegistry Serialization { get; } + + #endregion + + #region Initialization + + public InspectionConcern(IHandler content, SerializationRegistry serialization) + { + Content = content; + Serialization = serialization; + } + + #endregion + + #region Functionality + + public ValueTask PrepareAsync() => Content.PrepareAsync(); + + public async ValueTask HandleAsync(IRequest request) + { + if (request.Query.ContainsKey("inspect")) + { + using var content = await Content.HandleAsync(request); + + var server = request.Server; + + var model = new + { + Server = new + { + Version = server.Version, + Development = server.Development, + Handler = server.Handler.ToString(), + Companion = server.Companion?.ToString(), + Endpoints = server.EndPoints.Select(e => new + { + Port = e.Port, + IPAddress = e.IPAddress.ToString(), + Secure = e.Secure, + RequestSource = e == request.EndPoint + }) + }, + Client = new + { + Protocol = request.Client.Protocol, + IPAddress = request.Client.IPAddress.ToString(), + Host = request.Client.Host + }, + LocalClient = (request.Client != request.LocalClient) ? new + { + Protocol = request.LocalClient.Protocol, + IPAddress = request.LocalClient.IPAddress.ToString(), + Host = request.LocalClient.Host + } : null, + Request = new + { + ProtocolType = request.ProtocolType, + Method = request.Method.RawMethod, + Path = request.Target.Path.ToString(), + Headers = request.Headers, + Query = request.Query, + Cookies = request.Cookies, + Content = (request.Content != null) ? new + { + Type = request.ContentType, + Body = request.Content.ToString() + } : null, + Forwardings = request.Forwardings, + Properties = request.Properties + }, + Response = (content != null) ? new { + Status = content.Status.RawStatus, + Upgraded = content.Upgraded, + Expires = content.Expires, + Modified = content.Modified, + Headers = content.Headers, + Cookies = content.Cookies, + Content = new + { + Type = content.ContentType?.RawType, + Body = content.Content?.ToString(), + Length = content.Content?.Length, + Encoding = content.ContentEncoding + } + } : null + }; + + var format = Serialization.GetSerialization(request); + + if (format == null) + { + return request.Respond() + .Status(ResponseStatus.UnsupportedMediaType) + .Content(new Strings.StringContent("Unable to find serializer for requested format")) + .Build(); + } + + var serializedModel = await format.SerializeAsync(request, model); + + if (format is YamlFormat) + { + // quirk: browsers do not display application/yaml + serializedModel.Type(ContentType.TextYaml); + } + + return serializedModel.Build(); + } + + return await Content.HandleAsync(request); + } + + #endregion + +} diff --git a/Modules/Inspection/Concern/InspectionConcernBuilder.cs b/Modules/Inspection/Concern/InspectionConcernBuilder.cs new file mode 100644 index 00000000..7887f798 --- /dev/null +++ b/Modules/Inspection/Concern/InspectionConcernBuilder.cs @@ -0,0 +1,33 @@ +using GenHTTP.Api.Content; +using GenHTTP.Api.Protocol; +using GenHTTP.Modules.Conversion.Serializers; +using GenHTTP.Modules.Conversion.Serializers.Yaml; +using Conv = GenHTTP.Modules.Conversion; + +namespace GenHTTP.Modules.Inspection.Concern; + +public sealed class InspectionConcernBuilder : IConcernBuilder +{ + private SerializationRegistry? _Serialization; + + #region Functionality + + public InspectionConcernBuilder Serialization(SerializationRegistry registry) + { + _Serialization = registry; + return this; + } + + public IConcern Build(IHandler content) + { + var serialization = _Serialization ?? Conv.Serialization.Empty() + .Default(ContentType.ApplicationYaml) + .Add(ContentType.ApplicationYaml, new YamlFormat()) + .Build(); + + return new InspectionConcern(content, serialization); + } + + #endregion + +} diff --git a/Modules/Inspection/GenHTTP.Modules.Inspection.csproj b/Modules/Inspection/GenHTTP.Modules.Inspection.csproj new file mode 100644 index 00000000..361580a0 --- /dev/null +++ b/Modules/Inspection/GenHTTP.Modules.Inspection.csproj @@ -0,0 +1,55 @@ + + + + + net8.0;net9.0 + + 13.0 + enable + true + enable + + 9.0.0.0 + 9.0.0.0 + 9.0.0 + + Andreas Nägeli + + + LICENSE + https://genhttp.org/ + + Allows to inspect any request that is sent to the server + HTTP Webserver C# Module Debugging Debug Inspection Inspector + + true + true + snupkg + + true + CS1591,CS1587,CS1572,CS1573 + + icon.png + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Modules/Inspection/Inspector.cs b/Modules/Inspection/Inspector.cs new file mode 100644 index 00000000..4af80840 --- /dev/null +++ b/Modules/Inspection/Inspector.cs @@ -0,0 +1,26 @@ +using GenHTTP.Api.Content; + +using GenHTTP.Modules.Inspection.Concern; + +namespace GenHTTP.Modules.Inspection; + +public static class Inspector +{ + + #region Builder + + public static InspectionConcernBuilder Create() => new(); + + #endregion + + #region Extensions + + public static T AddInspector(this T builder) where T : IHandlerBuilder + { + builder.Add(Create()); + return builder; + } + + #endregion + +} diff --git a/Modules/Layouting/Provider/LayoutBuilder.cs b/Modules/Layouting/Provider/LayoutBuilder.cs index a3650139..956cb4d9 100644 --- a/Modules/Layouting/Provider/LayoutBuilder.cs +++ b/Modules/Layouting/Provider/LayoutBuilder.cs @@ -6,23 +6,23 @@ public sealed class LayoutBuilder : IHandlerBuilder { private readonly List _Concerns = []; - private IHandlerBuilder? _Index; + private IHandler? _Index; - #region Initialization + #region Get-/Setters - public LayoutBuilder() - { - RoutedHandlers = new Dictionary(); - RootHandlers = new List(); - } + private Dictionary RoutedHandlers { get; } - #endregion + private List RootHandlers { get; } - #region Get-/Setters + #endregion - private Dictionary RoutedHandlers { get; } + #region Initialization - private List RootHandlers { get; } + public LayoutBuilder() + { + RoutedHandlers = []; + RootHandlers = []; + } #endregion @@ -33,19 +33,26 @@ public LayoutBuilder() /// the index of the layout. /// /// The handler used for the index of the layout - public LayoutBuilder Index(IHandlerBuilder handler) + public LayoutBuilder Index(IHandler handler) { _Index = handler; return this; } + /// + /// Sets the handler which should be invoked to provide + /// the index of the layout. + /// + /// The handler used for the index of the layout + public LayoutBuilder Index(IHandlerBuilder handler) => Index(handler.Build()); + /// /// Adds a handler that will be invoked for all URLs below /// the specified path segment. /// /// The name of the path segment to be handled /// The handler which will handle the segment - public LayoutBuilder Add(string name, IHandlerBuilder handler) + public LayoutBuilder Add(string name, IHandler handler) { if (name.Contains('/')) { @@ -56,6 +63,14 @@ public LayoutBuilder Add(string name, IHandlerBuilder handler) return this; } + /// + /// Adds a handler that will be invoked for all URLs below + /// the specified path segment. + /// + /// The name of the path segment to be handled + /// The handler which will handle the segment + public LayoutBuilder Add(string name, IHandlerBuilder handler) => Add(name, handler.Build()); + /// /// Adds a handler on root level that will be invoked if neither a /// path segment has been detected nor the index has been invoked. @@ -66,21 +81,33 @@ public LayoutBuilder Add(string name, IHandlerBuilder handler) /// Fallback handlers will be executed in the order they have been added /// to the layout. /// - public LayoutBuilder Add(IHandlerBuilder handler) + public LayoutBuilder Add(IHandler handler) { RootHandlers.Add(handler); return this; } + /// + /// Adds a handler on root level that will be invoked if neither a + /// path segment has been detected nor the index has been invoked. + /// + /// The root level handler to be added + /// + /// Can be used to provide one or multiple fallback handlers for the layout. + /// Fallback handlers will be executed in the order they have been added + /// to the layout. + /// + public LayoutBuilder Add(IHandlerBuilder handler) => Add(handler.Build()); + public LayoutBuilder Add(IConcernBuilder concern) { _Concerns.Add(concern); return this; } - public IHandler Build(IHandler parent) + public IHandler Build() { - return Concerns.Chain(parent, _Concerns, p => new LayoutRouter(p, RoutedHandlers, RootHandlers, _Index)); + return Concerns.Chain(_Concerns, new LayoutRouter(RoutedHandlers, RootHandlers, _Index)); } #endregion diff --git a/Modules/Layouting/Provider/LayoutRouter.cs b/Modules/Layouting/Provider/LayoutRouter.cs index 7fe4ac84..4386b9da 100644 --- a/Modules/Layouting/Provider/LayoutRouter.cs +++ b/Modules/Layouting/Provider/LayoutRouter.cs @@ -7,28 +7,8 @@ namespace GenHTTP.Modules.Layouting.Provider; public sealed class LayoutRouter : IHandler { - #region Initialization - - public LayoutRouter(IHandler parent, - Dictionary routedHandlers, - List rootHandlers, - IHandlerBuilder? index) - { - Parent = parent; - - RoutedHandlers = routedHandlers.ToDictionary(kv => kv.Key, kv => kv.Value.Build(this)); - - RootHandlers = rootHandlers.Select(h => h.Build(this)).ToList(); - - Index = index?.Build(this); - } - - #endregion - #region Get-/Setters - public IHandler Parent { get; } - public IReadOnlyDictionary RoutedHandlers { get; } public IReadOnlyList RootHandlers { get; } @@ -37,6 +17,17 @@ public LayoutRouter(IHandler parent, #endregion + #region Initialization + + public LayoutRouter(Dictionary routedHandlers, List rootHandlers, IHandler? index) + { + RoutedHandlers = routedHandlers; + RootHandlers = rootHandlers; + Index = index; + } + + #endregion + #region Functionality public async ValueTask HandleAsync(IRequest request) @@ -58,7 +49,7 @@ public LayoutRouter(IHandler parent, if (!request.Target.Path.TrailingSlash) { return await Redirect.To($"{request.Target.Path}/") - .Build(this) + .Build() .HandleAsync(request); } diff --git a/Modules/LoadBalancing/Provider/LoadBalancerBuilder.cs b/Modules/LoadBalancing/Provider/LoadBalancerBuilder.cs index d3e53db3..f3b0b5ca 100644 --- a/Modules/LoadBalancing/Provider/LoadBalancerBuilder.cs +++ b/Modules/LoadBalancing/Provider/LoadBalancerBuilder.cs @@ -29,9 +29,9 @@ public LoadBalancerBuilder Add(IHandlerBuilder handler, PriorityEvaluation? prio public LoadBalancerBuilder Proxy(string node, PriorityEvaluation? priority = null) => Add(ReverseProxy.Proxy.Create().Upstream(node), priority); - public IHandler Build(IHandler parent) + public IHandler Build() { - return Concerns.Chain(parent, _Concerns, p => new LoadBalancerHandler(p, _Nodes)); + return Concerns.Chain(_Concerns, new LoadBalancerHandler( _Nodes)); } #endregion diff --git a/Modules/LoadBalancing/Provider/LoadBalancerHandler.cs b/Modules/LoadBalancing/Provider/LoadBalancerHandler.cs index 78eeaabf..e8473c4f 100644 --- a/Modules/LoadBalancing/Provider/LoadBalancerHandler.cs +++ b/Modules/LoadBalancing/Provider/LoadBalancerHandler.cs @@ -7,24 +7,20 @@ namespace GenHTTP.Modules.LoadBalancing.Provider; public sealed class LoadBalancerHandler : IHandler { - #region Initialization + #region Get-/Setters - public LoadBalancerHandler(IHandler parent, List<(IHandlerBuilder, PriorityEvaluation)> nodes) - { - Parent = parent; + private readonly List<(IHandler, PriorityEvaluation)> _Nodes; - _Nodes = nodes.Select(n => (n.Item1.Build(this), n.Item2)).ToList(); - } + private static readonly Random Random = new(); #endregion - #region Get-/Setters - - public IHandler Parent { get; } - - private readonly List<(IHandler, PriorityEvaluation)> _Nodes; + #region Initialization - private static readonly Random Random = new(); + public LoadBalancerHandler(List<(IHandlerBuilder, PriorityEvaluation)> nodes) + { + _Nodes = nodes.Select(n => (n.Item1.Build(), n.Item2)).ToList(); + } #endregion diff --git a/Modules/LoadBalancing/Provider/LoadBalancerRedirectionBuilder.cs b/Modules/LoadBalancing/Provider/LoadBalancerRedirectionBuilder.cs index 68af58bd..3f95c414 100644 --- a/Modules/LoadBalancing/Provider/LoadBalancerRedirectionBuilder.cs +++ b/Modules/LoadBalancing/Provider/LoadBalancerRedirectionBuilder.cs @@ -23,11 +23,11 @@ public LoadBalancerRedirectionBuilder Root(string node) return this; } - public IHandler Build(IHandler parent) + public IHandler Build() { var root = _Root ?? throw new BuilderMissingPropertyException("root"); - return Concerns.Chain(parent, _Concerns, p => new LoadBalancerRedirectionHandler(p, root)); + return Concerns.Chain(_Concerns, new LoadBalancerRedirectionHandler( root)); } #endregion diff --git a/Modules/LoadBalancing/Provider/LoadBalancerRedirectionHandler.cs b/Modules/LoadBalancing/Provider/LoadBalancerRedirectionHandler.cs index 27a9f434..f7ee7432 100644 --- a/Modules/LoadBalancing/Provider/LoadBalancerRedirectionHandler.cs +++ b/Modules/LoadBalancing/Provider/LoadBalancerRedirectionHandler.cs @@ -1,5 +1,6 @@ using GenHTTP.Api.Content; using GenHTTP.Api.Protocol; + using GenHTTP.Modules.Basics; namespace GenHTTP.Modules.LoadBalancing.Provider; @@ -7,29 +8,25 @@ namespace GenHTTP.Modules.LoadBalancing.Provider; public sealed class LoadBalancerRedirectionHandler : IHandler { - #region Initialization - - public LoadBalancerRedirectionHandler(IHandler parent, string root) - { - Parent = parent; + #region Get-/Setters - Root = root.EndsWith('/') ? root : $"{root}/"; - } + private string Root { get; } #endregion - #region Get-/Setters - - public IHandler Parent { get; } + #region Initialization - private string Root { get; } + public LoadBalancerRedirectionHandler(string root) + { + Root = root.EndsWith('/') ? root : $"{root}/"; + } #endregion #region Functionality public ValueTask HandleAsync(IRequest request) => Redirect.To(Root + request.Target.Current, true) - .Build(this) + .Build() .HandleAsync(request); public ValueTask PrepareAsync() => ValueTask.CompletedTask; diff --git a/Modules/OpenApi/Handler/OpenApiConcern.cs b/Modules/OpenApi/Handler/OpenApiConcern.cs index 079f59cb..775c38e1 100644 --- a/Modules/OpenApi/Handler/OpenApiConcern.cs +++ b/Modules/OpenApi/Handler/OpenApiConcern.cs @@ -12,24 +12,8 @@ public sealed class OpenApiConcern : IConcern { private ReturnDocument? _Cached; - #region Initialization - - public OpenApiConcern(IHandler parent, Func contentFactory, ApiDiscoveryRegistry discovery, bool enableCaching, Action postProcessor) - { - Parent = parent; - Content = contentFactory(this); - - Discovery = discovery; - EnableCaching = enableCaching; - PostProcessor = postProcessor; - } - - #endregion - #region Get-/Setters - public IHandler Parent { get; } - public IHandler Content { get; } private ApiDiscoveryRegistry Discovery { get; } @@ -40,6 +24,19 @@ public OpenApiConcern(IHandler parent, Func contentFactory, #endregion + #region Initialization + + public OpenApiConcern(IHandler content, ApiDiscoveryRegistry discovery, bool enableCaching, Action postProcessor) + { + Content = content; + + Discovery = discovery; + EnableCaching = enableCaching; + PostProcessor = postProcessor; + } + + #endregion + #region Functionality public ValueTask PrepareAsync() => new(); diff --git a/Modules/OpenApi/Handler/OpenApiConcernBuilder.cs b/Modules/OpenApi/Handler/OpenApiConcernBuilder.cs index 644f4ba2..60bd2e78 100644 --- a/Modules/OpenApi/Handler/OpenApiConcernBuilder.cs +++ b/Modules/OpenApi/Handler/OpenApiConcernBuilder.cs @@ -72,7 +72,7 @@ public OpenApiConcernBuilder PostProcessor(Action act return this; } - public IConcern Build(IHandler parent, Func contentFactory) => new OpenApiConcern(parent, contentFactory, Discovery, _Caching, DoPostProcessing); + public IConcern Build(IHandler content) => new OpenApiConcern(content, Discovery, _Caching, DoPostProcessing); private void DoPostProcessing(IRequest request, OpenApiDocument document) { diff --git a/Modules/Reflection/MethodCollection.cs b/Modules/Reflection/MethodCollection.cs index 571b2bc9..126a5c1e 100644 --- a/Modules/Reflection/MethodCollection.cs +++ b/Modules/Reflection/MethodCollection.cs @@ -6,23 +6,18 @@ namespace GenHTTP.Modules.Reflection; public sealed class MethodCollection : IHandler { - #region Initialization - - public MethodCollection(IHandler parent, IEnumerable> methodFactories) - { - Parent = parent; + #region Get-/Setters - Methods = methodFactories.Select(factory => factory(this)) - .ToList(); - } + public List Methods { get; } #endregion - #region Get-/Setters - - public IHandler Parent { get; } + #region Initialization - public List Methods { get; } + public MethodCollection(IEnumerable methods) + { + Methods = new(methods); + } #endregion diff --git a/Modules/Reflection/MethodHandler.cs b/Modules/Reflection/MethodHandler.cs index d8030cba..08b04e76 100644 --- a/Modules/Reflection/MethodHandler.cs +++ b/Modules/Reflection/MethodHandler.cs @@ -23,8 +23,6 @@ public sealed class MethodHandler : IHandler #region Get-/Setters - public IHandler Parent { get; } - public Operation Operation { get; } public IMethodConfiguration Configuration { get; } @@ -42,15 +40,12 @@ public sealed class MethodHandler : IHandler /// /// Creates a new handler to serve a single API operation. /// - /// The parent of this handler /// The operation to be executed and provided (use to create an operation) /// The object to execute the operation on /// Additional, use-specified information about the operation /// The customized registry to be used to read and write data - public MethodHandler(IHandler parent, Operation operation, object instance, IMethodConfiguration metaData, MethodRegistry registry) + public MethodHandler(Operation operation, object instance, IMethodConfiguration metaData, MethodRegistry registry) { - Parent = parent; - Configuration = metaData; Instance = instance; diff --git a/Modules/Reflection/ResponseProvider.cs b/Modules/Reflection/ResponseProvider.cs index c39f9209..0ba7049f 100644 --- a/Modules/Reflection/ResponseProvider.cs +++ b/Modules/Reflection/ResponseProvider.cs @@ -51,7 +51,7 @@ public ResponseProvider(MethodRegistry registry) return operation.Result.Sink switch { - OperationResultSink.Dynamic => await GetDynamicResponse(request, result, handler, adjustments), + OperationResultSink.Dynamic => await GetDynamicResponse(request, result, adjustments), OperationResultSink.Stream => GetDownloadResponse(request, (Stream)result, adjustments), OperationResultSink.Formatter => GetFormattedResponse(request, result, type, adjustments), OperationResultSink.Serializer => await GetSerializedResponse(request, result, adjustments), @@ -65,7 +65,7 @@ private static IResponse GetNoContent(IRequest request, Action .Adjust(adjustments) .Build(); - private static async Task GetDynamicResponse(IRequest request, object result, IHandler handler, Action? adjustments) + private static async Task GetDynamicResponse(IRequest request, object result, Action? adjustments) { if (result is IResponseBuilder responseBuilder) { @@ -79,7 +79,7 @@ private static IResponse GetNoContent(IRequest request, Action if (result is IHandlerBuilder handlerBuilder) { - return await handlerBuilder.Build(handler) + return await handlerBuilder.Build() .HandleAsync(request); } diff --git a/Modules/ReverseProxy/Provider/ReverseProxyBuilder.cs b/Modules/ReverseProxy/Provider/ReverseProxyBuilder.cs index 83c90c9b..334d9dce 100644 --- a/Modules/ReverseProxy/Provider/ReverseProxyBuilder.cs +++ b/Modules/ReverseProxy/Provider/ReverseProxyBuilder.cs @@ -44,14 +44,14 @@ public ReverseProxyBuilder Add(IConcernBuilder concern) return this; } - public IHandler Build(IHandler parent) + public IHandler Build() { if (_Upstream is null) { throw new BuilderMissingPropertyException("Upstream"); } - return Concerns.Chain(parent, _Concerns, p => new ReverseProxyProvider(p, _Upstream, _ConnectTimeout, _ReadTimeout)); + return Concerns.Chain(_Concerns, new ReverseProxyProvider( _Upstream, _ConnectTimeout, _ReadTimeout)); } #endregion diff --git a/Modules/ReverseProxy/Provider/ReverseProxyProvider.cs b/Modules/ReverseProxy/Provider/ReverseProxyProvider.cs index 5bf61a50..5644a3a1 100644 --- a/Modules/ReverseProxy/Provider/ReverseProxyProvider.cs +++ b/Modules/ReverseProxy/Provider/ReverseProxyProvider.cs @@ -30,11 +30,18 @@ public sealed class ReverseProxyProvider : IHandler "Upgrade-Insecure-Requests" }; + #region Get-/Setters + + public string Upstream { get; } + + private HttpClient Client { get; } + + #endregion + #region Initialization - public ReverseProxyProvider(IHandler parent, string upstream, TimeSpan connectTimeout, TimeSpan readTimeout) + public ReverseProxyProvider(string upstream, TimeSpan connectTimeout, TimeSpan readTimeout) { - Parent = parent; Upstream = upstream; var handler = new SocketsHttpHandler @@ -52,16 +59,6 @@ public ReverseProxyProvider(IHandler parent, string upstream, TimeSpan connectTi #endregion - #region Get-/Setters - - public IHandler Parent { get; } - - public string Upstream { get; } - - private HttpClient Client { get; } - - #endregion - #region Functionality public async ValueTask HandleAsync(IRequest request) diff --git a/Modules/Security/Cors/CorsPolicyBuilder.cs b/Modules/Security/Cors/CorsPolicyBuilder.cs index 00b2687f..1d4f150a 100644 --- a/Modules/Security/Cors/CorsPolicyBuilder.cs +++ b/Modules/Security/Cors/CorsPolicyBuilder.cs @@ -45,7 +45,7 @@ public CorsPolicyBuilder Add(string origin, OriginPolicy? policy) public CorsPolicyBuilder Add(string origin, List? allowedMethods, List? allowedHeaders, List? exposedHeaders, bool allowCredentials, uint maxAge = 86400) => Add(origin, new OriginPolicy(allowedMethods, allowedHeaders, exposedHeaders, allowCredentials, maxAge)); - public IConcern Build(IHandler parent, Func contentFactory) => new CorsPolicyHandler(parent, contentFactory, _DefaultPolicy, _AdditionalPolicies); + public IConcern Build(IHandler content) => new CorsPolicyHandler(content, _DefaultPolicy, _AdditionalPolicies); #endregion diff --git a/Modules/Security/Cors/CorsPolicyHandler.cs b/Modules/Security/Cors/CorsPolicyHandler.cs index 41187f6a..8d83471a 100644 --- a/Modules/Security/Cors/CorsPolicyHandler.cs +++ b/Modules/Security/Cors/CorsPolicyHandler.cs @@ -8,32 +8,28 @@ public sealed class CorsPolicyHandler : IConcern { public const string ALLOW_ANY = "*"; - #region Initialization - - public CorsPolicyHandler(IHandler parent, Func contentFactory, - OriginPolicy? defaultPolicy, IDictionary additionalPolicies) - { - Parent = parent; - Content = contentFactory(this); - - DefaultPolicy = defaultPolicy; - AdditionalPolicies = additionalPolicies; - } - - #endregion - #region Get-/Setters public IHandler Content { get; } - public IHandler Parent { get; } - public OriginPolicy? DefaultPolicy { get; } public IDictionary AdditionalPolicies { get; } #endregion + #region Initialization + + public CorsPolicyHandler(IHandler content, OriginPolicy? defaultPolicy, IDictionary additionalPolicies) + { + Content = content; + + DefaultPolicy = defaultPolicy; + AdditionalPolicies = additionalPolicies; + } + + #endregion + #region Functionality public ValueTask PrepareAsync() => Content.PrepareAsync(); diff --git a/Modules/Security/Providers/SecureUpgradeConcern.cs b/Modules/Security/Providers/SecureUpgradeConcern.cs index fd890397..7e9cc5fb 100644 --- a/Modules/Security/Providers/SecureUpgradeConcern.cs +++ b/Modules/Security/Providers/SecureUpgradeConcern.cs @@ -8,25 +8,22 @@ namespace GenHTTP.Modules.Security.Providers; public sealed class SecureUpgradeConcern : IConcern { - #region Initialization + #region Get-/Setters - public SecureUpgradeConcern(IHandler parent, Func contentFactory, SecureUpgrade mode) - { - Parent = parent; - Content = contentFactory(this); + public SecureUpgrade Mode { get; } - Mode = mode; - } + public IHandler Content { get; } #endregion - #region Get-/Setters - - public SecureUpgrade Mode { get; } + #region Initialization - public IHandler Parent { get; } + public SecureUpgradeConcern(IHandler content, SecureUpgrade mode) + { + Content = content; - public IHandler Content { get; } + Mode = mode; + } #endregion @@ -46,7 +43,7 @@ public SecureUpgradeConcern(IHandler parent, Func contentFac if (Mode == SecureUpgrade.Force) { return await Redirect.To(GetRedirectLocation(request, endpoints)) - .Build(this) + .Build() .HandleAsync(request) ; } @@ -59,7 +56,7 @@ public SecureUpgradeConcern(IHandler parent, Func contentFac if (flag == "1") { var response = await Redirect.To(GetRedirectLocation(request, endpoints), true) - .Build(this) + .Build() .HandleAsync(request) ; diff --git a/Modules/Security/Providers/SecureUpgradeConcernBuilder.cs b/Modules/Security/Providers/SecureUpgradeConcernBuilder.cs index 531046b3..c93b84af 100644 --- a/Modules/Security/Providers/SecureUpgradeConcernBuilder.cs +++ b/Modules/Security/Providers/SecureUpgradeConcernBuilder.cs @@ -15,7 +15,7 @@ public SecureUpgradeConcernBuilder Mode(SecureUpgrade mode) return this; } - public IConcern Build(IHandler parent, Func contentFactory) => new SecureUpgradeConcern(parent, contentFactory, _Mode); + public IConcern Build(IHandler content) => new SecureUpgradeConcern(content, _Mode); #endregion diff --git a/Modules/Security/Providers/SnifferPreventionConcern.cs b/Modules/Security/Providers/SnifferPreventionConcern.cs index 42dc49da..cb918c2e 100644 --- a/Modules/Security/Providers/SnifferPreventionConcern.cs +++ b/Modules/Security/Providers/SnifferPreventionConcern.cs @@ -6,21 +6,18 @@ namespace GenHTTP.Modules.Security.Providers; public class SnifferPreventionConcern : IConcern { - #region Initialization + #region Get-/Setters - public SnifferPreventionConcern(IHandler parent, Func contentFactory) - { - Parent = parent; - Content = contentFactory(this); - } + public IHandler Content { get; } #endregion - #region Get-/Setters - - public IHandler Parent { get; } + #region Initialization - public IHandler Content { get; } + public SnifferPreventionConcern(IHandler content) + { + Content = content; + } #endregion diff --git a/Modules/Security/Providers/SnifferPreventionConcernBuilder.cs b/Modules/Security/Providers/SnifferPreventionConcernBuilder.cs index 709b6534..ae93acce 100644 --- a/Modules/Security/Providers/SnifferPreventionConcernBuilder.cs +++ b/Modules/Security/Providers/SnifferPreventionConcernBuilder.cs @@ -7,7 +7,7 @@ public class SnifferPreventionConcernBuilder : IConcernBuilder #region Functionality - public IConcern Build(IHandler parent, Func contentFactory) => new SnifferPreventionConcern(parent, contentFactory); + public IConcern Build(IHandler content) => new SnifferPreventionConcern(content); #endregion diff --git a/Modules/Security/Providers/StrictTransportConcern.cs b/Modules/Security/Providers/StrictTransportConcern.cs index 228e18f5..b3d2d511 100644 --- a/Modules/Security/Providers/StrictTransportConcern.cs +++ b/Modules/Security/Providers/StrictTransportConcern.cs @@ -7,23 +7,8 @@ public sealed class StrictTransportConcern : IConcern { private const string Header = "Strict-Transport-Security"; - #region Initialization - - public StrictTransportConcern(IHandler parent, Func contentFactory, StrictTransportPolicy policy) - { - Parent = parent; - Content = contentFactory(this); - - Policy = policy; - HeaderValue = GetPolicyHeader(); - } - - #endregion - #region Get-/Setters - public IHandler Parent { get; } - public IHandler Content { get; } public StrictTransportPolicy Policy { get; } @@ -32,6 +17,18 @@ public StrictTransportConcern(IHandler parent, Func contentF #endregion + #region Initialization + + public StrictTransportConcern(IHandler content, StrictTransportPolicy policy) + { + Content = content; + + Policy = policy; + HeaderValue = GetPolicyHeader(); + } + + #endregion + #region Functionality public async ValueTask HandleAsync(IRequest request) diff --git a/Modules/Security/Providers/StrictTransportConcernBuilder.cs b/Modules/Security/Providers/StrictTransportConcernBuilder.cs index 81cabd02..4987ae20 100644 --- a/Modules/Security/Providers/StrictTransportConcernBuilder.cs +++ b/Modules/Security/Providers/StrictTransportConcernBuilder.cs @@ -15,11 +15,11 @@ public StrictTransportConcernBuilder Policy(StrictTransportPolicy policy) return this; } - public IConcern Build(IHandler parent, Func contentFactory) + public IConcern Build(IHandler content) { var policy = _Policy ?? throw new BuilderMissingPropertyException("policy"); - return new StrictTransportConcern(parent, contentFactory, policy); + return new StrictTransportConcern(content, policy); } #endregion diff --git a/Modules/ServerCaching/Provider/ServerCacheHandler.cs b/Modules/ServerCaching/Provider/ServerCacheHandler.cs index 223784d2..b40bcf30 100644 --- a/Modules/ServerCaching/Provider/ServerCacheHandler.cs +++ b/Modules/ServerCaching/Provider/ServerCacheHandler.cs @@ -9,14 +9,27 @@ namespace GenHTTP.Modules.ServerCaching.Provider; public sealed class ServerCacheHandler : IConcern { + #region Get-/Setters + + public IHandler Content { get; } + + private ICache Meta { get; } + + private ICache Data { get; } + + private bool Invalidate { get; } + + private Func? Predicate { get; } + + #endregion + #region Initialization - public ServerCacheHandler(IHandler parent, Func contentFactory, + public ServerCacheHandler(IHandler content, ICache meta, ICache data, Func? predicate, bool invalidate) { - Parent = parent; - Content = contentFactory(this); + Content = content; Meta = meta; Data = data; @@ -27,22 +40,6 @@ public ServerCacheHandler(IHandler parent, Func contentFacto #endregion - #region Get-/Setters - - public IHandler Content { get; } - - public IHandler Parent { get; } - - private ICache Meta { get; } - - private ICache Data { get; } - - private bool Invalidate { get; } - - private Func? Predicate { get; } - - #endregion - #region Functionality public async ValueTask HandleAsync(IRequest request) diff --git a/Modules/ServerCaching/Provider/ServerCacheHandlerBuilder.cs b/Modules/ServerCaching/Provider/ServerCacheHandlerBuilder.cs index aba68a8e..3195961f 100644 --- a/Modules/ServerCaching/Provider/ServerCacheHandlerBuilder.cs +++ b/Modules/ServerCaching/Provider/ServerCacheHandlerBuilder.cs @@ -55,13 +55,13 @@ public ServerCacheHandlerBuilder DataStore(ICache cache) public ServerCacheHandlerBuilder DataStore(IBuilder> cache) => DataStore(cache.Build()); - public IConcern Build(IHandler parent, Func contentFactory) + public IConcern Build(IHandler content) { var meta = _Meta ?? throw new BuilderMissingPropertyException("MetaStore"); var data = _Data ?? throw new BuilderMissingPropertyException("DataStore"); - return new ServerCacheHandler(parent, contentFactory, meta, data, _Predicate, _Invalidate); + return new ServerCacheHandler(content, meta, data, _Predicate, _Invalidate); } #endregion diff --git a/Modules/ServerSentEvents/Handler/EventSourceHandler.cs b/Modules/ServerSentEvents/Handler/EventSourceHandler.cs index 71bc3249..00487262 100644 --- a/Modules/ServerSentEvents/Handler/EventSourceHandler.cs +++ b/Modules/ServerSentEvents/Handler/EventSourceHandler.cs @@ -10,8 +10,6 @@ public sealed class EventSourceHandler : IHandler #region Get-/Setters - public IHandler Parent { get; } - private Func>? Inspector { get; } private Func Generator { get; } @@ -22,10 +20,8 @@ public sealed class EventSourceHandler : IHandler #region Initialization - public EventSourceHandler(IHandler parent, Func>? inspector, Func generator, FormatterRegistry formatters) + public EventSourceHandler(Func>? inspector, Func generator, FormatterRegistry formatters) { - Parent = parent; - Inspector = inspector; Generator = generator; Formatters = formatters; diff --git a/Modules/ServerSentEvents/Handler/EventSourceHandlerBuilder.cs b/Modules/ServerSentEvents/Handler/EventSourceHandlerBuilder.cs index 2972913a..95c7f7bd 100644 --- a/Modules/ServerSentEvents/Handler/EventSourceHandlerBuilder.cs +++ b/Modules/ServerSentEvents/Handler/EventSourceHandlerBuilder.cs @@ -44,11 +44,11 @@ public EventSourceHandlerBuilder Add(IConcernBuilder concern) return this; } - public IHandler Build(IHandler parent) + public IHandler Build() { var generator = _Generator ?? throw new BuilderMissingPropertyException("Generator"); - return Concerns.Chain(parent, _Concerns, (p) => new EventSourceHandler(p, _Inspector, generator, _Formatters.Build())); + return Concerns.Chain(_Concerns, new EventSourceHandler( _Inspector, generator, _Formatters.Build())); } #endregion diff --git a/Modules/SinglePageApplications/Provider/SinglePageBuilder.cs b/Modules/SinglePageApplications/Provider/SinglePageBuilder.cs index be963287..b380bebb 100644 --- a/Modules/SinglePageApplications/Provider/SinglePageBuilder.cs +++ b/Modules/SinglePageApplications/Provider/SinglePageBuilder.cs @@ -38,11 +38,11 @@ public SinglePageBuilder ServerSideRouting() return this; } - public IHandler Build(IHandler parent) + public IHandler Build() { var tree = _Tree ?? throw new BuilderMissingPropertyException("tree"); - return Concerns.Chain(parent, _Concerns, p => new SinglePageProvider(p, tree, _ServerSideRouting)); + return Concerns.Chain(_Concerns, new SinglePageProvider( tree, _ServerSideRouting)); } #endregion diff --git a/Modules/SinglePageApplications/Provider/SinglePageProvider.cs b/Modules/SinglePageApplications/Provider/SinglePageProvider.cs index 3ec442ed..09572bed 100644 --- a/Modules/SinglePageApplications/Provider/SinglePageProvider.cs +++ b/Modules/SinglePageApplications/Provider/SinglePageProvider.cs @@ -15,30 +15,26 @@ public sealed class SinglePageProvider : IHandler private IHandler? _Index; - #region Initialization + #region Get-/Setters - public SinglePageProvider(IHandler parent, IResourceTree tree, bool serverSideRouting) - { - Parent = parent; + private IResourceTree Tree { get; } - Tree = tree; - ServerSideRouting = serverSideRouting; + private IHandler Resources { get; } - Resources = IO.Resources.From(tree) - .Build(this); - } + private bool ServerSideRouting { get; } #endregion - #region Get-/Setters - - public IHandler Parent { get; } - - private IResourceTree Tree { get; } + #region Initialization - private IHandler Resources { get; } + public SinglePageProvider(IResourceTree tree, bool serverSideRouting) + { + Tree = tree; + ServerSideRouting = serverSideRouting; - private bool ServerSideRouting { get; } + Resources = IO.Resources.From(tree) + .Build(); + } #endregion @@ -86,7 +82,7 @@ public SinglePageProvider(IHandler parent, IResourceTree tree, bool serverSideRo if ((indexFile = await Tree.TryGetResourceAsync(index)) != null) { _Index = Content.From(indexFile) - .Build(this); + .Build(); break; } diff --git a/Modules/StaticWebsites/Provider/StaticWebsiteBuilder.cs b/Modules/StaticWebsites/Provider/StaticWebsiteBuilder.cs index afdf6492..0a3faead 100644 --- a/Modules/StaticWebsites/Provider/StaticWebsiteBuilder.cs +++ b/Modules/StaticWebsites/Provider/StaticWebsiteBuilder.cs @@ -24,11 +24,11 @@ public StaticWebsiteBuilder Add(IConcernBuilder concern) return this; } - public IHandler Build(IHandler parent) + public IHandler Build() { var tree = _Tree ?? throw new BuilderMissingPropertyException("tree"); - return Concerns.Chain(parent, _Concerns, p => new StaticWebsiteHandler(p, tree)); + return Concerns.Chain(_Concerns, new StaticWebsiteHandler( tree)); } #endregion diff --git a/Modules/StaticWebsites/Provider/StaticWebsiteHandler.cs b/Modules/StaticWebsites/Provider/StaticWebsiteHandler.cs index 43feaa53..11ffe0c6 100644 --- a/Modules/StaticWebsites/Provider/StaticWebsiteHandler.cs +++ b/Modules/StaticWebsites/Provider/StaticWebsiteHandler.cs @@ -9,26 +9,23 @@ public sealed class StaticWebsiteHandler : IHandler { private static readonly string[] IndexFiles = ["index.html", "index.htm"]; - #region Initialization + #region Get-/Setters - public StaticWebsiteHandler(IHandler parent, IResourceTree tree) - { - Parent = parent; - Tree = tree; + private IResourceTree Tree { get; } - Resources = IO.Resources.From(tree) - .Build(this); - } + private IHandler Resources { get; } #endregion - #region Get-/Setters - - public IHandler Parent { get; } + #region Initialization - private IResourceTree Tree { get; } + public StaticWebsiteHandler(IResourceTree tree) + { + Tree = tree; - private IHandler Resources { get; } + Resources = IO.Resources.From(tree) + .Build(); + } #endregion @@ -49,7 +46,7 @@ public StaticWebsiteHandler(IHandler parent, IResourceTree tree) if ((file = await node.TryGetResourceAsync(indexFile)) != null) { return await Content.From(file) - .Build(this) + .Build() .HandleAsync(request); } } diff --git a/Modules/VirtualHosting/Provider/VirtualHostRouter.cs b/Modules/VirtualHosting/Provider/VirtualHostRouter.cs index 796ba972..84814fb2 100644 --- a/Modules/VirtualHosting/Provider/VirtualHostRouter.cs +++ b/Modules/VirtualHosting/Provider/VirtualHostRouter.cs @@ -7,27 +7,21 @@ namespace GenHTTP.Modules.VirtualHosting.Provider; public sealed class VirtualHostRouter : IHandler { - #region Initialization + #region Get-/Setters - public VirtualHostRouter(IHandler parent, - Dictionary hosts, - IHandlerBuilder? defaultRoute) - { - Parent = parent; + private Dictionary Hosts { get; } - Hosts = hosts.ToDictionary(kv => kv.Key, kv => kv.Value.Build(this)); - DefaultRoute = defaultRoute?.Build(this); - } + private IHandler? DefaultRoute { get; } #endregion - #region Get-/Setters - - public IHandler Parent { get; } - - private Dictionary Hosts { get; } + #region Initialization - private IHandler? DefaultRoute { get; } + public VirtualHostRouter(Dictionary hosts, IHandlerBuilder? defaultRoute) + { + Hosts = hosts.ToDictionary(kv => kv.Key, kv => kv.Value.Build()); + DefaultRoute = defaultRoute?.Build(); + } #endregion diff --git a/Modules/VirtualHosting/Provider/VirtualHostRouterBuilder.cs b/Modules/VirtualHosting/Provider/VirtualHostRouterBuilder.cs index 7accb14c..be972267 100644 --- a/Modules/VirtualHosting/Provider/VirtualHostRouterBuilder.cs +++ b/Modules/VirtualHosting/Provider/VirtualHostRouterBuilder.cs @@ -34,9 +34,9 @@ public VirtualHostRouterBuilder Default(IHandlerBuilder handler) return this; } - public IHandler Build(IHandler parent) + public IHandler Build() { - return Concerns.Chain(parent, _Concerns, p => new VirtualHostRouter(p, _Hosts, _DefaultRoute)); + return Concerns.Chain(_Concerns, new VirtualHostRouter( _Hosts, _DefaultRoute)); } #endregion diff --git a/Modules/Webservices/Provider/ServiceResourceBuilder.cs b/Modules/Webservices/Provider/ServiceResourceBuilder.cs index 5b2862b8..7c294b0c 100644 --- a/Modules/Webservices/Provider/ServiceResourceBuilder.cs +++ b/Modules/Webservices/Provider/ServiceResourceBuilder.cs @@ -55,7 +55,7 @@ public ServiceResourceBuilder Add(IConcernBuilder concern) return this; } - public IHandler Build(IHandler parent) + public IHandler Build() { var serializers = (_Serializers ?? Serialization.Default()).Build(); @@ -67,7 +67,7 @@ public IHandler Build(IHandler parent) var extensions = new MethodRegistry(serializers, injectors, formatters); - return Concerns.Chain(parent, _Concerns, p => new ServiceResourceRouter(p, instance, extensions)); + return Concerns.Chain(_Concerns, new ServiceResourceRouter( instance, extensions)); } #endregion diff --git a/Modules/Webservices/Provider/ServiceResourceRouter.cs b/Modules/Webservices/Provider/ServiceResourceRouter.cs index 9f71ff99..625e73c6 100644 --- a/Modules/Webservices/Provider/ServiceResourceRouter.cs +++ b/Modules/Webservices/Provider/ServiceResourceRouter.cs @@ -13,8 +13,6 @@ public sealed class ServiceResourceRouter : IHandler, IServiceMethodProvider public MethodCollection Methods { get; } - public IHandler Parent { get; } - public ResponseProvider ResponseProvider { get; } public object Instance { get; } @@ -23,18 +21,16 @@ public sealed class ServiceResourceRouter : IHandler, IServiceMethodProvider #region Initialization - public ServiceResourceRouter(IHandler parent, object instance, MethodRegistry registry) + public ServiceResourceRouter(object instance, MethodRegistry registry) { - Parent = parent; - Instance = instance; ResponseProvider = new ResponseProvider(registry); - Methods = new MethodCollection(this, AnalyzeMethods(instance.GetType(), registry)); + Methods = new MethodCollection(AnalyzeMethods(instance.GetType(), registry)); } - private IEnumerable> AnalyzeMethods(Type type, MethodRegistry registry) + private IEnumerable AnalyzeMethods(Type type, MethodRegistry registry) { foreach (var method in type.GetMethods(BindingFlags.Public | BindingFlags.Instance)) { @@ -44,7 +40,7 @@ private IEnumerable> AnalyzeMethods(Type type, Met { var operation = OperationBuilder.Create(attribute.Path, method, registry); - yield return parent => new MethodHandler(parent, operation, Instance, attribute, registry); + yield return new MethodHandler(operation, Instance, attribute, registry); } } } diff --git a/Modules/Websockets/Handler/WebsocketHandler.cs b/Modules/Websockets/Handler/WebsocketHandler.cs index d2f9849a..5bd8085d 100644 --- a/Modules/Websockets/Handler/WebsocketHandler.cs +++ b/Modules/Websockets/Handler/WebsocketHandler.cs @@ -10,8 +10,6 @@ public sealed class WebsocketHandler : IHandler #region Get-/Setters - public IHandler Parent { get; } - public Action? OnOpen { get; } public Action? OnClose { get; } @@ -32,7 +30,7 @@ public sealed class WebsocketHandler : IHandler #region Initialization - public WebsocketHandler(IHandler parent, List supportedProtocols, + public WebsocketHandler(List supportedProtocols, Action? onOpen, Action? onClose, Action? onMessage, @@ -41,7 +39,6 @@ public WebsocketHandler(IHandler parent, List supportedProtocols, Action? onPong, Action? onError) { - Parent = parent; SupportedProtocols = supportedProtocols; OnOpen = onOpen; diff --git a/Modules/Websockets/Handler/WebsocketHandlerBuilder.cs b/Modules/Websockets/Handler/WebsocketHandlerBuilder.cs index e247ae5a..fa5e8e73 100644 --- a/Modules/Websockets/Handler/WebsocketHandlerBuilder.cs +++ b/Modules/Websockets/Handler/WebsocketHandlerBuilder.cs @@ -104,9 +104,9 @@ public WebsocketHandlerBuilder OnError(Action h return this; } - public IHandler Build(IHandler parent) + public IHandler Build() { - return Concerns.Chain(parent, _Concerns, (p) => new WebsocketHandler(p, _SupportedProtocols, _OnOpen, _OnClose, _OnMessage, _OnBinary, _OnPing, _OnPong, _OnError)); + return Concerns.Chain(_Concerns, new WebsocketHandler(_SupportedProtocols, _OnOpen, _OnClose, _OnMessage, _OnBinary, _OnPing, _OnPong, _OnError)); } #endregion diff --git a/Playground/GenHTTP.Playground.csproj b/Playground/GenHTTP.Playground.csproj index 658dab87..50767d96 100644 --- a/Playground/GenHTTP.Playground.csproj +++ b/Playground/GenHTTP.Playground.csproj @@ -28,6 +28,7 @@ + diff --git a/Playground/Program.cs b/Playground/Program.cs index 2753d48f..c16a1245 100644 --- a/Playground/Program.cs +++ b/Playground/Program.cs @@ -1,8 +1,10 @@ using GenHTTP.Engine; + +using GenHTTP.Modules.Inspection; using GenHTTP.Modules.IO; using GenHTTP.Modules.Practices; -var app = Content.From(Resource.FromString("Hello World")); +var app = Content.From(Resource.FromString("Hello World")).AddInspector(); Host.Create() .Handler(app) diff --git a/Testing/Acceptance/Engine/BasicTests.cs b/Testing/Acceptance/Engine/BasicTests.cs index 795230db..ebf7b769 100644 --- a/Testing/Acceptance/Engine/BasicTests.cs +++ b/Testing/Acceptance/Engine/BasicTests.cs @@ -11,7 +11,7 @@ public sealed class BasicTests [TestMethod] public async Task TestBuilder() { - using var runner = new TestHost(Layout.Create()); + using var runner = new TestHost(Layout.Create().Build()); runner.Host.RequestMemoryLimit(128) .TransferBufferSize(128) diff --git a/Testing/Acceptance/Engine/CompanionTests.cs b/Testing/Acceptance/Engine/CompanionTests.cs index d9f3cdda..da1d6a71 100644 --- a/Testing/Acceptance/Engine/CompanionTests.cs +++ b/Testing/Acceptance/Engine/CompanionTests.cs @@ -1,7 +1,10 @@ using System.Net; + using GenHTTP.Api.Infrastructure; using GenHTTP.Api.Protocol; + using GenHTTP.Modules.Layouting; + using Microsoft.VisualStudio.TestTools.UnitTesting; namespace GenHTTP.Testing.Acceptance.Engine; @@ -16,7 +19,7 @@ public sealed class CompanionTests [TestMethod] public async Task TestConsole() { - using var runner = new TestHost(Layout.Create()); + using var runner = new TestHost(Layout.Create().Build()); runner.Host.Console().Start(); @@ -29,7 +32,7 @@ public async Task TestConsole() [TestMethod] public async Task TestCustom() { - using var runner = new TestHost(Layout.Create()); + using var runner = new TestHost(Layout.Create().Build()); var companion = new CustomCompanion(); diff --git a/Testing/Acceptance/Engine/CompressionTests.cs b/Testing/Acceptance/Engine/CompressionTests.cs index 3ce699ab..3fd69613 100644 --- a/Testing/Acceptance/Engine/CompressionTests.cs +++ b/Testing/Acceptance/Engine/CompressionTests.cs @@ -21,7 +21,7 @@ public sealed class CompressionTests [TestMethod] public async Task TestCompression() { - using var runner = TestHost.Run(Layout.Create()); + using var runner = TestHost.Run(Layout.Create().Build()); var request = runner.GetRequest(); request.Headers.Add("Accept-Encoding", "gzip, br, zstd"); @@ -73,7 +73,7 @@ public async Task TestCompressionDisabled() [TestMethod] public async Task TestCustomCompression() { - using var runner = new TestHost(Layout.Create()); + using var runner = new TestHost(Layout.Create().Build()); runner.Host.Compression(CompressedContent.Default().Add(new CustomAlgorithm()).Level(CompressionLevel.Optimal)).Start(); diff --git a/Testing/Acceptance/Engine/DeveloperModeTests.cs b/Testing/Acceptance/Engine/DeveloperModeTests.cs index 6831c75f..35d85b79 100644 --- a/Testing/Acceptance/Engine/DeveloperModeTests.cs +++ b/Testing/Acceptance/Engine/DeveloperModeTests.cs @@ -16,7 +16,7 @@ public sealed class DeveloperModeTests [TestMethod] public async Task TestExceptionsWithTrace() { - using var runner = new TestHost(Layout.Create()); + using var runner = new TestHost(Layout.Create().Build()); var router = Layout.Create().Index(new ThrowingProvider().Wrap()); diff --git a/Testing/Acceptance/Engine/HostTests.cs b/Testing/Acceptance/Engine/HostTests.cs index 2a1f3bc3..25198609 100644 --- a/Testing/Acceptance/Engine/HostTests.cs +++ b/Testing/Acceptance/Engine/HostTests.cs @@ -11,7 +11,7 @@ public sealed class HostTests [TestMethod] public async Task TestStart() { - using var runner = new TestHost(Layout.Create()); + using var runner = new TestHost(Layout.Create().Build()); runner.Host.Start(); @@ -23,7 +23,7 @@ public async Task TestStart() [TestMethod] public async Task TestRestart() { - using var runner = new TestHost(Layout.Create()); + using var runner = new TestHost(Layout.Create().Build()); runner.Host.Restart(); @@ -31,4 +31,5 @@ public async Task TestRestart() await response.AssertStatusAsync(HttpStatusCode.NotFound); } + } diff --git a/Testing/Acceptance/Engine/SecurityTests.cs b/Testing/Acceptance/Engine/SecurityTests.cs index 58117cd6..38474abf 100644 --- a/Testing/Acceptance/Engine/SecurityTests.cs +++ b/Testing/Acceptance/Engine/SecurityTests.cs @@ -158,7 +158,7 @@ private static async Task RunSecure(Func logic, SecureUpgr { var content = Layout.Create().Index(Content.From(Resource.FromString("Hello Alice!"))); - using var runner = new TestHost(Layout.Create(), mode is null); + using var runner = new TestHost(Layout.Create().Build(), mode is null); var port = TestHost.NextPort(); diff --git a/Testing/Acceptance/GenHTTP.Testing.Acceptance.csproj b/Testing/Acceptance/GenHTTP.Testing.Acceptance.csproj index c434f918..b212a376 100644 --- a/Testing/Acceptance/GenHTTP.Testing.Acceptance.csproj +++ b/Testing/Acceptance/GenHTTP.Testing.Acceptance.csproj @@ -63,6 +63,8 @@ + + diff --git a/Testing/Acceptance/Modules/Authentication/BasicAuthenticationTests.cs b/Testing/Acceptance/Modules/Authentication/BasicAuthenticationTests.cs index 7febd5a7..bdcd6fee 100644 --- a/Testing/Acceptance/Modules/Authentication/BasicAuthenticationTests.cs +++ b/Testing/Acceptance/Modules/Authentication/BasicAuthenticationTests.cs @@ -1,9 +1,12 @@ using System.Net; + using GenHTTP.Api.Content.Authentication; + using GenHTTP.Modules.Authentication; using GenHTTP.Modules.Authentication.Basic; using GenHTTP.Modules.Layouting; using GenHTTP.Modules.Layouting.Provider; + using Microsoft.VisualStudio.TestTools.UnitTesting; namespace GenHTTP.Testing.Acceptance.Modules.Authentication; @@ -124,5 +127,6 @@ private static async Task GetResponse(TestHost runner, stri } private static LayoutBuilder GetContent() => Layout.Create() - .Index(new UserReturningHandlerBuilder()); + .Index(new UserReturningHandler().Wrap()); + } diff --git a/Testing/Acceptance/Modules/Authentication/UserReturningHandler.cs b/Testing/Acceptance/Modules/Authentication/UserReturningHandler.cs index 1279646e..3bb4a580 100644 --- a/Testing/Acceptance/Modules/Authentication/UserReturningHandler.cs +++ b/Testing/Acceptance/Modules/Authentication/UserReturningHandler.cs @@ -6,24 +6,11 @@ namespace GenHTTP.Testing.Acceptance.Modules.Authentication; -public class UserReturningHandlerBuilder : IHandlerBuilder -{ - - public IHandler Build(IHandler parent) => new UserReturningHandler(parent); -} - public class UserReturningHandler : IHandler { - public UserReturningHandler(IHandler parent) - { - Parent = parent; - } - public ValueTask PrepareAsync() => ValueTask.CompletedTask; - public IHandler Parent { get; } - public ValueTask HandleAsync(IRequest request) { var content = request.GetUser()?.DisplayName ?? throw new ProviderException(ResponseStatus.BadRequest, "No user!"); @@ -32,4 +19,5 @@ public UserReturningHandler(IHandler parent) .Content(content) .BuildTask(); } + } diff --git a/Testing/Acceptance/Modules/CacheValidationTests.cs b/Testing/Acceptance/Modules/CacheValidationTests.cs index 81f425a2..159de15f 100644 --- a/Testing/Acceptance/Modules/CacheValidationTests.cs +++ b/Testing/Acceptance/Modules/CacheValidationTests.cs @@ -67,7 +67,7 @@ public async Task TestNoContentNoEtag() return r.Respond().Status(ResponseStatus.NoContent).Build(); }); - using var runner = TestHost.Run(noContent.Wrap()); + using var runner = TestHost.Run(noContent); using var response = await runner.GetResponseAsync(); @@ -87,4 +87,5 @@ public async Task TestOtherMethodNoETag() Assert.IsFalse(response.Headers.Contains("ETag")); } + } diff --git a/Testing/Acceptance/Modules/Controllers/ResultTypeTests.cs b/Testing/Acceptance/Modules/Controllers/ResultTypeTests.cs index 81131ff9..c7f88bdf 100644 --- a/Testing/Acceptance/Modules/Controllers/ResultTypeTests.cs +++ b/Testing/Acceptance/Modules/Controllers/ResultTypeTests.cs @@ -34,7 +34,7 @@ public sealed class TestController public IHandlerBuilder HandlerBuilder() => Content.From(Resource.FromString("HandlerBuilder")); - public IHandler Handler(IHandler parent) => Content.From(Resource.FromString("Handler")).Build(parent); + public IHandler Handler(IHandler parent) => Content.From(Resource.FromString("Handler")).Build(); public IResponseBuilder ResponseBuilder(IRequest request) => request.Respond().Content("ResponseBuilder"); diff --git a/Testing/Acceptance/Modules/ConversionTests.cs b/Testing/Acceptance/Modules/ConversionTests.cs index 5d5b0c4a..b6e0d34f 100644 --- a/Testing/Acceptance/Modules/ConversionTests.cs +++ b/Testing/Acceptance/Modules/ConversionTests.cs @@ -73,22 +73,19 @@ public ConversionHandlerBuilder(ISerializationFormat format) _Format = format; } - public IHandler Build(IHandler parent) => new ConversionHandler(_Format, parent); + public IHandler Build() => new ConversionHandler(_Format); } private class ConversionHandler : IHandler { - public ConversionHandler(ISerializationFormat format, IHandler parent) + public ConversionHandler(ISerializationFormat format) { - Parent = parent; Format = format; } public ISerializationFormat Format { get; } - public IHandler Parent { get; } - public ValueTask PrepareAsync() => ValueTask.CompletedTask; public async ValueTask HandleAsync(IRequest request) diff --git a/Testing/Acceptance/Modules/Functional/InlineTests.cs b/Testing/Acceptance/Modules/Functional/InlineTests.cs index 7146dc8e..36ace15c 100644 --- a/Testing/Acceptance/Modules/Functional/InlineTests.cs +++ b/Testing/Acceptance/Modules/Functional/InlineTests.cs @@ -190,7 +190,7 @@ public async Task TestHandler() { var target = "https://www.google.de/"; - using var host = TestHost.Run(Inline.Create().Get((IHandler parent) => Redirect.To(target).Build(parent))); + using var host = TestHost.Run(Inline.Create().Get((IHandler parent) => Redirect.To(target).Build())); using var response = await host.GetResponseAsync(); diff --git a/Testing/Acceptance/Modules/Inspection/InspectionTests.cs b/Testing/Acceptance/Modules/Inspection/InspectionTests.cs new file mode 100644 index 00000000..e470d351 --- /dev/null +++ b/Testing/Acceptance/Modules/Inspection/InspectionTests.cs @@ -0,0 +1,57 @@ +using System.Net; + +using GenHTTP.Modules.Inspection; +using GenHTTP.Modules.IO; +using GenHTTP.Modules.Layouting; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace GenHTTP.Testing.Acceptance.Modules.Inspection; + +[TestClass] +public sealed class InspectionTests +{ + + [TestMethod] + public async Task TestInspection() + { + var app = Content.From(Resource.FromString("Hello World")).AddInspector(); + + using var host = TestHost.Run(app); + + using var inspected = await host.GetResponseAsync("/one/two?inspect"); + + await inspected.AssertStatusAsync(HttpStatusCode.OK); + + AssertX.Contains("/one/two", await inspected.GetContentAsync()); + } + + [TestMethod] + public async Task TestNoInspection() + { + var app = Content.From(Resource.FromString("Hello World")).AddInspector(); + + using var host = TestHost.Run(app); + + using var notInspected = await host.GetResponseAsync("/one/two"); + + await notInspected.AssertStatusAsync(HttpStatusCode.OK); + + Assert.AreEqual("Hello World", await notInspected.GetContentAsync()); + } + + [TestMethod] + public async Task TestNotFoundInspected() + { + var app = Layout.Create().AddInspector(); + + using var host = TestHost.Run(app); + + using var inspected = await host.GetResponseAsync("/one/two?inspect"); + + await inspected.AssertStatusAsync(HttpStatusCode.OK); + + AssertX.Contains("/one/two", await inspected.GetContentAsync()); + } + +} diff --git a/Testing/Acceptance/Modules/Inspection/SerializationTests.cs b/Testing/Acceptance/Modules/Inspection/SerializationTests.cs new file mode 100644 index 00000000..c6e48a13 --- /dev/null +++ b/Testing/Acceptance/Modules/Inspection/SerializationTests.cs @@ -0,0 +1,57 @@ +using System.Net; + +using GenHTTP.Api.Protocol; + +using GenHTTP.Modules.Conversion; +using GenHTTP.Modules.Conversion.Serializers.Json; +using GenHTTP.Modules.Inspection; +using GenHTTP.Modules.Layouting; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace GenHTTP.Testing.Acceptance.Modules.Inspection; + +[TestClass] +public sealed class SerializationTests +{ + + [TestMethod] + public async Task TestCustomFormat() + { + var serialization = Serialization.Empty() + .Default(ContentType.ApplicationJson) + .Add(ContentType.ApplicationJson, new JsonFormat()) + .Build(); + + var inspection = Inspector.Create().Serialization(serialization); + + var app = Layout.Create().Add(inspection); + + using var host = TestHost.Run(app); + + using var inspected = await host.GetResponseAsync("/one/two?inspect"); + + await inspected.AssertStatusAsync(HttpStatusCode.OK); + + Assert.AreEqual("application/json", inspected.GetContentHeader("Content-Type")); + } + + [TestMethod] + public async Task TestNoFormats() + { + var serialization = Serialization.Empty() + .Default(ContentType.AudioMp4) + .Build(); + + var inspection = Inspector.Create().Serialization(serialization); + + var app = Layout.Create().Add(inspection); + + using var host = TestHost.Run(app); + + using var inspected = await host.GetResponseAsync("/one/two?inspect"); + + await inspected.AssertStatusAsync(HttpStatusCode.UnsupportedMediaType); + } + +} diff --git a/Testing/Acceptance/Modules/OpenApi/ResponseTests.cs b/Testing/Acceptance/Modules/OpenApi/ResponseTests.cs index f2b46f97..2c666331 100644 --- a/Testing/Acceptance/Modules/OpenApi/ResponseTests.cs +++ b/Testing/Acceptance/Modules/OpenApi/ResponseTests.cs @@ -58,7 +58,7 @@ public async Task TestNone() public async Task TestDynamic() { var api = Inline.Create() - .Get("h", (IHandler parent) => Redirect.To("https://google.de").Build(parent)) + .Get("h", (IHandler parent) => Redirect.To("https://google.de").Build()) .Get("hb", () => Redirect.To("https://google.de")) .Get("r", (IRequest request) => request.Respond().Build()) .Get("rb", (IRequest request) => request.Respond()); diff --git a/Testing/Acceptance/Modules/ReverseProxyTests.cs b/Testing/Acceptance/Modules/ReverseProxyTests.cs index 88012306..a25d04ac 100644 --- a/Testing/Acceptance/Modules/ReverseProxyTests.cs +++ b/Testing/Acceptance/Modules/ReverseProxyTests.cs @@ -292,9 +292,9 @@ private TestSetup(TestHost source, TestHost target) public static TestSetup Create(Func response) { // server hosting the actual web app - var testServer = new TestHost(Layout.Create(), false); + var testServer = new TestHost(Layout.Create().Build(), false); - testServer.Host.Handler(new ProxiedRouter(response).Wrap()) + testServer.Host.Handler(new ProxiedRouter(response)) .Start(); // proxying server @@ -303,7 +303,7 @@ public static TestSetup Create(Func response) .ReadTimeout(TimeSpan.FromSeconds(5)) .Upstream("http://localhost:" + testServer.Port); - var runner = new TestHost(Layout.Create()); + var runner = new TestHost(Layout.Create().Build()); runner.Host.Handler(proxy) .Start(); @@ -363,8 +363,6 @@ public ProxiedProvider(Func response) _Response = response; } - public IHandler Parent => throw new NotImplementedException(); - public ValueTask PrepareAsync() => ValueTask.CompletedTask; public ValueTask HandleAsync(IRequest request) diff --git a/Testing/Acceptance/Modules/Security/ExtensionTests.cs b/Testing/Acceptance/Modules/Security/ExtensionTests.cs index f817c328..1804344d 100644 --- a/Testing/Acceptance/Modules/Security/ExtensionTests.cs +++ b/Testing/Acceptance/Modules/Security/ExtensionTests.cs @@ -12,7 +12,7 @@ public sealed class ExtensionTests [TestMethod] public async Task ServerCanBeHardened() { - using var runner = new TestHost(Layout.Create()); + using var runner = new TestHost(Layout.Create().Build()); runner.Host.Handler(Content.From(Resource.FromString("Hello Eve!"))) .Harden() diff --git a/Testing/Acceptance/Modules/WebserviceTests.cs b/Testing/Acceptance/Modules/WebserviceTests.cs index ef87a7e3..02c7dd05 100644 --- a/Testing/Acceptance/Modules/WebserviceTests.cs +++ b/Testing/Acceptance/Modules/WebserviceTests.cs @@ -11,6 +11,8 @@ using GenHTTP.Modules.Reflection; using GenHTTP.Modules.Webservices; using Microsoft.VisualStudio.TestTools.UnitTesting; +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.NamingConventions; namespace GenHTTP.Testing.Acceptance.Modules; @@ -26,6 +28,7 @@ public sealed class TestEntity public int ID { get; set; } public double? Nullable { get; set; } + } public enum TestEnum @@ -225,6 +228,32 @@ await WithResponse("entity", HttpMethod.Post, entity, "text/xml", "text/xml", as }); } + [TestMethod] + public async Task TestEntityAsYaml() + { + const string entity = """ + id: 1 + nullable: 1234.56 + """; + + await WithResponse("entity", HttpMethod.Post, entity, "application/yaml", "application/yaml", async r => + { + await r.AssertStatusAsync(HttpStatusCode.OK); + + var deserializer = new DeserializerBuilder().WithNamingConvention(CamelCaseNamingConvention.Instance) + .Build(); + + using var reader = new StreamReader(await r.Content.ReadAsStreamAsync(), leaveOpen: true); + + var result = deserializer.Deserialize(reader, typeof(TestEntity)) as TestEntity; + + Assert.IsNotNull(result); + + Assert.AreEqual(1, result.ID); + Assert.AreEqual(1234.56, result.Nullable); + }); + } + [TestMethod] public async Task TestException() { diff --git a/Testing/Acceptance/TestOpenApiExtensions.cs b/Testing/Acceptance/TestExtensions.cs similarity index 90% rename from Testing/Acceptance/TestOpenApiExtensions.cs rename to Testing/Acceptance/TestExtensions.cs index 51f423d6..956b7e7d 100644 --- a/Testing/Acceptance/TestOpenApiExtensions.cs +++ b/Testing/Acceptance/TestExtensions.cs @@ -1,14 +1,15 @@ -using GenHTTP.Api.Content; -using GenHTTP.Testing.Acceptance.Utilities; - -namespace GenHTTP.Testing.Acceptance; - -public static class TestOpenApiExtensions -{ - - public static IHandlerBuilder Wrap(this IHandler handler) => new HandlerBuilder(handler); - - public static string? GetETag(this HttpResponseMessage response) => response.GetHeader("ETag"); - - public static DateTime WithoutMs(this DateTime date) => new(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second, date.Kind); -} +using GenHTTP.Api.Content; +using GenHTTP.Testing.Acceptance.Utilities; + +namespace GenHTTP.Testing.Acceptance; + +public static class TestExtensions +{ + + public static IHandlerBuilder Wrap(this IHandler handler) => new HandlerBuilder(handler); + + public static string? GetETag(this HttpResponseMessage response) => response.GetHeader("ETag"); + + public static DateTime WithoutMs(this DateTime date) => new(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second, date.Kind); + +} diff --git a/Testing/Acceptance/Utilities/Chain.cs b/Testing/Acceptance/Utilities/Chain.cs index 1dfd44da..cc6d544a 100644 --- a/Testing/Acceptance/Utilities/Chain.cs +++ b/Testing/Acceptance/Utilities/Chain.cs @@ -5,8 +5,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; -using NSubstitute; - namespace GenHTTP.Testing.Acceptance.Utilities; internal static class Chain @@ -16,7 +14,7 @@ public static void Works(IHandlerBuilder builder) where T : IHandlerBuilde { builder.Add(ErrorHandler.Html()); - Assert.IsTrue(builder.Build(Substitute.For()) is ErrorSentry); + Assert.IsTrue(builder.Build() is ErrorSentry); } } diff --git a/Testing/Acceptance/Utilities/FunctionalHandler.cs b/Testing/Acceptance/Utilities/FunctionalHandler.cs index 02aea573..65c43826 100644 --- a/Testing/Acceptance/Utilities/FunctionalHandler.cs +++ b/Testing/Acceptance/Utilities/FunctionalHandler.cs @@ -3,12 +3,10 @@ namespace GenHTTP.Testing.Acceptance.Utilities; -public sealed class FunctionalHandler : IHandlerWithParent +public sealed class FunctionalHandler : IHandler { private readonly Func? _ResponseProvider; - private IHandler? _Parent; - #region Initialization public FunctionalHandler(Func? responseProvider = null) @@ -18,16 +16,6 @@ public FunctionalHandler(Func? responseProvider = null) #endregion - #region Get-/Setters - - public IHandler Parent - { - get => _Parent ?? throw new InvalidOperationException(); - set => _Parent = value; - } - - #endregion - #region Functionality public ValueTask PrepareAsync() => ValueTask.CompletedTask; diff --git a/Testing/Acceptance/Utilities/HandlerBuilder.cs b/Testing/Acceptance/Utilities/HandlerBuilder.cs index f86ff403..29aaa9a1 100644 --- a/Testing/Acceptance/Utilities/HandlerBuilder.cs +++ b/Testing/Acceptance/Utilities/HandlerBuilder.cs @@ -1,31 +1,24 @@ -using GenHTTP.Api.Content; - -namespace GenHTTP.Testing.Acceptance.Utilities; - -public class HandlerBuilder : IHandlerBuilder -{ - private readonly List _Concerns = []; - - private readonly IHandler _Handler; - - public HandlerBuilder(IHandler handler) { _Handler = handler; } - - public HandlerBuilder Add(IConcernBuilder concern) - { - _Concerns.Add(concern); - return this; - } - - public IHandler Build(IHandler parent) - { - return Concerns.Chain(parent, _Concerns, p => - { - if (_Handler is IHandlerWithParent par) - { - par.Parent = p; - } - - return _Handler; - }); - } -} +using GenHTTP.Api.Content; + +namespace GenHTTP.Testing.Acceptance.Utilities; + +public class HandlerBuilder : IHandlerBuilder +{ + private readonly List _Concerns = []; + + private readonly IHandler _Handler; + + public HandlerBuilder(IHandler handler) { _Handler = handler; } + + public HandlerBuilder Add(IConcernBuilder concern) + { + _Concerns.Add(concern); + return this; + } + + public IHandler Build() + { + return Concerns.Chain(_Concerns, _Handler); + } + +} diff --git a/Testing/Acceptance/Utilities/IHandlerWithParent.cs b/Testing/Acceptance/Utilities/IHandlerWithParent.cs deleted file mode 100644 index bded6efc..00000000 --- a/Testing/Acceptance/Utilities/IHandlerWithParent.cs +++ /dev/null @@ -1,9 +0,0 @@ -using GenHTTP.Api.Content; - -namespace GenHTTP.Testing.Acceptance.Utilities; - -public interface IHandlerWithParent : IHandler -{ - - public new IHandler Parent { get; set; } -} diff --git a/Testing/Testing/TestHost.cs b/Testing/Testing/TestHost.cs index 1503e4ff..f2bd295c 100644 --- a/Testing/Testing/TestHost.cs +++ b/Testing/Testing/TestHost.cs @@ -45,15 +45,15 @@ static TestHost() /// Creates a test host that will use the given handler to provide content, /// but has yet to be started. /// - /// The handler to be tested + /// The handler to be tested /// true, if the defaults (such as compression) should be added to this handler /// true, if the server should be started in development mode - public TestHost(IHandlerBuilder handlerBuilder, bool defaults = true, bool development = true) + public TestHost(IHandler handler, bool defaults = true, bool development = true) { Port = NextPort(); Host = Engine.Host.Create() - .Handler(handlerBuilder) + .Handler(handler) .Port((ushort)Port); if (defaults) @@ -71,18 +71,27 @@ public TestHost(IHandlerBuilder handlerBuilder, bool defaults = true, bool devel /// Creates a test host that will use the given handler to provide content /// and starts it immediately. /// - /// The handler to be tested + /// The handler to be tested /// true, if the defaults (such as compression) should be added to this handler /// true, if the server should be started in development mode - public static TestHost Run(IHandlerBuilder handlerBuilder, bool defaults = true, bool development = true) + public static TestHost Run(IHandler handler, bool defaults = true, bool development = true) { - var runner = new TestHost(handlerBuilder, defaults, development); + var runner = new TestHost(handler, defaults, development); runner.Start(); return runner; } + /// + /// Creates a test host that will use the given handler to provide content + /// and starts it immediately. + /// + /// The handler to be tested + /// true, if the defaults (such as compression) should be added to this handler + /// true, if the server should be started in development mode + public static TestHost Run(IHandlerBuilder handler, bool defaults = true, bool development = true) => Run(handler.Build(), defaults, development); + /// /// Starts the server managed by this testing host. ///