From fecd5e12b127c88ed0db7f7a64e41d86f95b321f Mon Sep 17 00:00:00 2001 From: PawelKarczewski Date: Sun, 29 Oct 2023 22:29:02 +0100 Subject: [PATCH 1/3] Added Domain Sockets and Named Pipes Support to ProcessHostService --- .../Configuration/Configuration.cs | 4 +++ .../Commands/HostCommand.cs | 25 ++++++++++++++--- .../Services/HostingService.cs | 27 +++++++++++++++++-- 3 files changed, 50 insertions(+), 6 deletions(-) diff --git a/src/fiskaltrust.Launcher.Common/Configuration/Configuration.cs b/src/fiskaltrust.Launcher.Common/Configuration/Configuration.cs index 946f6228..6e998eb7 100644 --- a/src/fiskaltrust.Launcher.Common/Configuration/Configuration.cs +++ b/src/fiskaltrust.Launcher.Common/Configuration/Configuration.cs @@ -327,6 +327,10 @@ public void Decrypt(IDataProtector dataProtector) return null; } + public bool UseDomainSockets { get; init; } + public string? DomainSocketPath { get; init; } + public bool UseNamedPipes { get; init; } + public string? NamedPipeName { get; init; } } public record LauncherConfigurationInCashBoxConfiguration diff --git a/src/fiskaltrust.Launcher/Commands/HostCommand.cs b/src/fiskaltrust.Launcher/Commands/HostCommand.cs index 90168609..a9a2f5f9 100644 --- a/src/fiskaltrust.Launcher/Commands/HostCommand.cs +++ b/src/fiskaltrust.Launcher/Commands/HostCommand.cs @@ -32,6 +32,10 @@ public HostCommand() : base("host") AddOption(new Option("--debugging")); AddOption(new Option("--launcher-configuration")); AddOption(new Option("--no-process-host-service", getDefaultValue: () => false)); + AddOption(new Option("--use-domain-sockets")); + AddOption(new Option("--domain-socket-path")); + AddOption(new Option("--use-named-pipes")); + AddOption(new Option("--named-pipe-name")); } } @@ -41,14 +45,19 @@ public class HostCommandHandler : ICommandHandler public string PlebianConfiguration { get; set; } = null!; public bool NoProcessHostService { get; set; } public bool Debugging { get; set; } + + public bool UseDomainSockets { get; } + public string? DomainSocketPath { get; } private readonly CancellationToken _cancellationToken; private readonly LauncherExecutablePath _launcherExecutablePath; - public HostCommandHandler(IHostApplicationLifetime lifetime, LauncherExecutablePath launcherExecutablePath) + public HostCommandHandler(IHostApplicationLifetime lifetime, LauncherExecutablePath launcherExecutablePath, bool useDomainSockets, string? domainSocketPath) { _cancellationToken = lifetime.ApplicationStopping; _launcherExecutablePath = launcherExecutablePath; + UseDomainSockets = useDomainSockets; + DomainSocketPath = domainSocketPath; } public async Task InvokeAsync(InvocationContext context) @@ -61,9 +70,17 @@ public async Task InvokeAsync(InvocationContext context) } } - var launcherConfiguration = Common.Configuration.LauncherConfiguration.Deserialize(System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(LauncherConfiguration))); - - var plebianConfiguration = Configuration.PlebianConfiguration.Deserialize(System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(PlebianConfiguration))); + var launcherConfigurationBase64Decoded = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(LauncherConfiguration)); + var launcherConfiguration = Common.Configuration.LauncherConfiguration.Deserialize(launcherConfigurationBase64Decoded); + + launcherConfiguration = launcherConfiguration with + { + UseDomainSockets = UseDomainSockets, + DomainSocketPath = UseDomainSockets ? DomainSocketPath ?? throw new InvalidOperationException("Domain socket path must be provided when using domain sockets.") : null + }; + + var plebianConfigurationBase64Decoded = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(PlebianConfiguration)); + var plebianConfiguration = Configuration.PlebianConfiguration.Deserialize(plebianConfigurationBase64Decoded); var cashboxConfiguration = CashBoxConfigurationExt.Deserialize(await File.ReadAllTextAsync(launcherConfiguration.CashboxConfigurationFile!)); diff --git a/src/fiskaltrust.Launcher/Services/HostingService.cs b/src/fiskaltrust.Launcher/Services/HostingService.cs index 7ebdb262..883a3d91 100644 --- a/src/fiskaltrust.Launcher/Services/HostingService.cs +++ b/src/fiskaltrust.Launcher/Services/HostingService.cs @@ -54,11 +54,13 @@ public async Task HostService(Uri uri, HostingType hostingTyp { var builder = WebApplication.CreateBuilder(); + // Configure Serilog for logging builder.Host.UseSerilog((_, __, loggerConfiguration) => loggerConfiguration .AddLoggingConfiguration(_launcherConfiguration, aspLogging: true) .WriteTo.GrpcSink(_packageConfiguration, _processHostService)); + // Add HTTP logging if the log level is set to Debug or lower if (_launcherConfiguration.LogLevel <= LogLevel.Debug) { builder.Services.AddHttpLogging(options => @@ -71,9 +73,10 @@ public async Task HostService(Uri uri, HostingType hostingTyp HttpLoggingFields.ResponseStatusCode | HttpLoggingFields.ResponseBody); } - WebApplication app; + WebApplication app; + // Check if UseHttpSysBinding is enabled and log warnings if necessary if (_launcherConfiguration.UseHttpSysBinding!.Value) { const string message = $"The configuration parameter {{parametername}} will be ignored because {nameof(_launcherConfiguration.UseHttpSysBinding)} is enabled."; @@ -98,6 +101,7 @@ public async Task HostService(Uri uri, HostingType hostingTyp } } + // Create the appropriate host based on the hosting type switch (hostingType) { case HostingType.REST: @@ -117,6 +121,7 @@ public async Task HostService(Uri uri, HostingType hostingTyp throw new NotImplementedException(); } + // Use HTTP logging if the log level is set to Debug or lower if (_launcherConfiguration.LogLevel <= LogLevel.Debug) { app.UseHttpLogging(); @@ -130,6 +135,7 @@ public async Task HostService(Uri uri, HostingType hostingTyp private WebApplication CreateRestHost(WebApplicationBuilder builder, Uri uri, T instance, Action addEndpoints) { + // Configure JSON options builder.Services.Configure(options => { options.SerializerOptions.NumberHandling = System.Text.Json.Serialization.JsonNumberHandling.AllowReadingFromString; @@ -140,6 +146,7 @@ private WebApplication CreateRestHost(WebApplicationBuilder builder, Uri uri, options.SerializerOptions.DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull; }); + // Configure Kestrel server binding builder.WebHost.ConfigureBinding(uri, listenOptions => ConfigureTls(listenOptions), isHttps: !string.IsNullOrEmpty(_launcherConfiguration.TlsCertificatePath) || !string.IsNullOrEmpty(_launcherConfiguration.TlsCertificateBase64), allowSynchronousIO: true, useHttpSys: _launcherConfiguration.UseHttpSysBinding!.Value); var app = builder.Build(); @@ -239,7 +246,23 @@ private WebApplication CreateGrpcHost(WebApplicationBuilder builder, Uri uri, _logger.LogWarning($"{nameof(_launcherConfiguration.UseHttpSysBinding)} is not supported for grpc."); } - builder.WebHost.BindKestrel(uri, listenOptions => ConfigureTls(listenOptions), false, HttpProtocols.Http2); + // Support for domain sockets/namespots + if (_launcherConfiguration.UseDomainSockets) + { + builder.WebHost.UseKestrel(options => + { + options.ListenUnixSocket(_launcherConfiguration.DomainSocketPath!, listenOptions => + { + listenOptions.Protocols = HttpProtocols.Http2; + ConfigureTls(listenOptions); + }); + }); + } + else + { + builder.WebHost.BindKestrel(uri, listenOptions => ConfigureTls(listenOptions), false, HttpProtocols.Http2); + } + builder.Services.AddCodeFirstGrpc(options => options.EnableDetailedErrors = true); builder.Services.AddSingleton(instance); From 75796964f2c4e39b8c3cce59ebe5b3f3aad405cc Mon Sep 17 00:00:00 2001 From: PawelKarczewski Date: Sun, 29 Oct 2023 23:13:28 +0100 Subject: [PATCH 2/3] Rebased with master and resolved conflicts --- .../Configuration/Configuration.cs | 4 +++ .../Commands/HostCommand.cs | 36 ++++++++++++------- .../Services/HostingService.cs | 27 ++++++++++++-- 3 files changed, 53 insertions(+), 14 deletions(-) diff --git a/src/fiskaltrust.Launcher.Common/Configuration/Configuration.cs b/src/fiskaltrust.Launcher.Common/Configuration/Configuration.cs index 946f6228..6e998eb7 100644 --- a/src/fiskaltrust.Launcher.Common/Configuration/Configuration.cs +++ b/src/fiskaltrust.Launcher.Common/Configuration/Configuration.cs @@ -327,6 +327,10 @@ public void Decrypt(IDataProtector dataProtector) return null; } + public bool UseDomainSockets { get; init; } + public string? DomainSocketPath { get; init; } + public bool UseNamedPipes { get; init; } + public string? NamedPipeName { get; init; } } public record LauncherConfigurationInCashBoxConfiguration diff --git a/src/fiskaltrust.Launcher/Commands/HostCommand.cs b/src/fiskaltrust.Launcher/Commands/HostCommand.cs index bb6bec54..5720c8f7 100644 --- a/src/fiskaltrust.Launcher/Commands/HostCommand.cs +++ b/src/fiskaltrust.Launcher/Commands/HostCommand.cs @@ -32,6 +32,10 @@ public HostCommand() : base("host") AddOption(new Option("--debugging")); AddOption(new Option("--launcher-configuration")); AddOption(new Option("--no-process-host-service", getDefaultValue: () => false)); + AddOption(new Option("--use-domain-sockets")); + AddOption(new Option("--domain-socket-path")); + AddOption(new Option("--use-named-pipes")); + AddOption(new Option("--named-pipe-name")); } } @@ -41,14 +45,19 @@ public class HostCommandHandler : ICommandHandler public string PlebeianConfiguration { get; set; } = null!; public bool NoProcessHostService { get; set; } public bool Debugging { get; set; } + + public bool UseDomainSockets { get; } + public string? DomainSocketPath { get; } private readonly CancellationToken _cancellationToken; private readonly LauncherExecutablePath _launcherExecutablePath; - public HostCommandHandler(IHostApplicationLifetime lifetime, LauncherExecutablePath launcherExecutablePath) + public HostCommandHandler(IHostApplicationLifetime lifetime, LauncherExecutablePath launcherExecutablePath, bool useDomainSockets, string? domainSocketPath) { _cancellationToken = lifetime.ApplicationStopping; _launcherExecutablePath = launcherExecutablePath; + UseDomainSockets = useDomainSockets; + DomainSocketPath = domainSocketPath; } public async Task InvokeAsync(InvocationContext context) @@ -61,9 +70,17 @@ public async Task InvokeAsync(InvocationContext context) } } - var launcherConfiguration = Common.Configuration.LauncherConfiguration.Deserialize(System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(LauncherConfiguration))); - - var plebeianConfiguration = Configuration.PlebeianConfiguration.Deserialize(System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(PlebeianConfiguration))); + var launcherConfigurationBase64Decoded = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(LauncherConfiguration)); + var launcherConfiguration = Common.Configuration.LauncherConfiguration.Deserialize(launcherConfigurationBase64Decoded); + + launcherConfiguration = launcherConfiguration with + { + UseDomainSockets = UseDomainSockets, + DomainSocketPath = UseDomainSockets ? DomainSocketPath ?? throw new InvalidOperationException("Domain socket path must be provided when using domain sockets.") : null + }; + + var plebeianConfigurationBase64Decoded = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(PlebeianConfiguration)); + var plebeianConfiguration = Configuration.PlebeianConfiguration.Deserialize(plebeianConfigurationBase64Decoded); var cashboxConfiguration = CashBoxConfigurationExt.Deserialize(await File.ReadAllTextAsync(launcherConfiguration.CashboxConfigurationFile!)); @@ -96,11 +113,7 @@ public async Task InvokeAsync(InvocationContext context) .UseSerilog() .ConfigureServices(services => { - services.Configure(opts => - { - opts.ShutdownTimeout = TimeSpan.FromSeconds(30); - opts.BackgroundServiceExceptionBehavior = BackgroundServiceExceptionBehavior.StopHost; - }); + services.Configure(opts => opts.ShutdownTimeout = TimeSpan.FromSeconds(30)); services.AddSingleton(_ => launcherConfiguration); services.AddSingleton(_ => packageConfiguration); services.AddSingleton(_ => plebeianConfiguration); @@ -155,7 +168,7 @@ public async Task InvokeAsync(InvocationContext context) { Log.Error(e, "Could not load {Type}.", nameof(IMiddlewareBootstrapper)); throw; - } // Will also be detected and logged propperly later + } }); try @@ -165,7 +178,7 @@ public async Task InvokeAsync(InvocationContext context) } catch (Exception e) { - Log.Error(e, "An unhandled exception occured."); + Log.Error(e, "An unhandled exception occurred."); throw; } finally @@ -211,4 +224,3 @@ private static Dictionary ProcessPackageConfiguration(Dictionary } } } - diff --git a/src/fiskaltrust.Launcher/Services/HostingService.cs b/src/fiskaltrust.Launcher/Services/HostingService.cs index 7ebdb262..883a3d91 100644 --- a/src/fiskaltrust.Launcher/Services/HostingService.cs +++ b/src/fiskaltrust.Launcher/Services/HostingService.cs @@ -54,11 +54,13 @@ public async Task HostService(Uri uri, HostingType hostingTyp { var builder = WebApplication.CreateBuilder(); + // Configure Serilog for logging builder.Host.UseSerilog((_, __, loggerConfiguration) => loggerConfiguration .AddLoggingConfiguration(_launcherConfiguration, aspLogging: true) .WriteTo.GrpcSink(_packageConfiguration, _processHostService)); + // Add HTTP logging if the log level is set to Debug or lower if (_launcherConfiguration.LogLevel <= LogLevel.Debug) { builder.Services.AddHttpLogging(options => @@ -71,9 +73,10 @@ public async Task HostService(Uri uri, HostingType hostingTyp HttpLoggingFields.ResponseStatusCode | HttpLoggingFields.ResponseBody); } - WebApplication app; + WebApplication app; + // Check if UseHttpSysBinding is enabled and log warnings if necessary if (_launcherConfiguration.UseHttpSysBinding!.Value) { const string message = $"The configuration parameter {{parametername}} will be ignored because {nameof(_launcherConfiguration.UseHttpSysBinding)} is enabled."; @@ -98,6 +101,7 @@ public async Task HostService(Uri uri, HostingType hostingTyp } } + // Create the appropriate host based on the hosting type switch (hostingType) { case HostingType.REST: @@ -117,6 +121,7 @@ public async Task HostService(Uri uri, HostingType hostingTyp throw new NotImplementedException(); } + // Use HTTP logging if the log level is set to Debug or lower if (_launcherConfiguration.LogLevel <= LogLevel.Debug) { app.UseHttpLogging(); @@ -130,6 +135,7 @@ public async Task HostService(Uri uri, HostingType hostingTyp private WebApplication CreateRestHost(WebApplicationBuilder builder, Uri uri, T instance, Action addEndpoints) { + // Configure JSON options builder.Services.Configure(options => { options.SerializerOptions.NumberHandling = System.Text.Json.Serialization.JsonNumberHandling.AllowReadingFromString; @@ -140,6 +146,7 @@ private WebApplication CreateRestHost(WebApplicationBuilder builder, Uri uri, options.SerializerOptions.DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull; }); + // Configure Kestrel server binding builder.WebHost.ConfigureBinding(uri, listenOptions => ConfigureTls(listenOptions), isHttps: !string.IsNullOrEmpty(_launcherConfiguration.TlsCertificatePath) || !string.IsNullOrEmpty(_launcherConfiguration.TlsCertificateBase64), allowSynchronousIO: true, useHttpSys: _launcherConfiguration.UseHttpSysBinding!.Value); var app = builder.Build(); @@ -239,7 +246,23 @@ private WebApplication CreateGrpcHost(WebApplicationBuilder builder, Uri uri, _logger.LogWarning($"{nameof(_launcherConfiguration.UseHttpSysBinding)} is not supported for grpc."); } - builder.WebHost.BindKestrel(uri, listenOptions => ConfigureTls(listenOptions), false, HttpProtocols.Http2); + // Support for domain sockets/namespots + if (_launcherConfiguration.UseDomainSockets) + { + builder.WebHost.UseKestrel(options => + { + options.ListenUnixSocket(_launcherConfiguration.DomainSocketPath!, listenOptions => + { + listenOptions.Protocols = HttpProtocols.Http2; + ConfigureTls(listenOptions); + }); + }); + } + else + { + builder.WebHost.BindKestrel(uri, listenOptions => ConfigureTls(listenOptions), false, HttpProtocols.Http2); + } + builder.Services.AddCodeFirstGrpc(options => options.EnableDetailedErrors = true); builder.Services.AddSingleton(instance); From a1eef62c3eb5191ec43d42615fc02388569fbfde Mon Sep 17 00:00:00 2001 From: PawelKarczewski Date: Sun, 29 Oct 2023 23:20:03 +0100 Subject: [PATCH 3/3] Rebased and resolved conflicts --- src/fiskaltrust.Launcher.Common/Configuration/Configuration.cs | 2 +- src/fiskaltrust.Launcher/Commands/HostCommand.cs | 3 +-- src/fiskaltrust.Launcher/Services/HostingService.cs | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/fiskaltrust.Launcher.Common/Configuration/Configuration.cs b/src/fiskaltrust.Launcher.Common/Configuration/Configuration.cs index 6e998eb7..37b5ba0a 100644 --- a/src/fiskaltrust.Launcher.Common/Configuration/Configuration.cs +++ b/src/fiskaltrust.Launcher.Common/Configuration/Configuration.cs @@ -353,4 +353,4 @@ public record LauncherConfigurationInCashBoxConfiguration return configuration; } } -} +} \ No newline at end of file diff --git a/src/fiskaltrust.Launcher/Commands/HostCommand.cs b/src/fiskaltrust.Launcher/Commands/HostCommand.cs index a52a60da..5720c8f7 100644 --- a/src/fiskaltrust.Launcher/Commands/HostCommand.cs +++ b/src/fiskaltrust.Launcher/Commands/HostCommand.cs @@ -52,7 +52,7 @@ public class HostCommandHandler : ICommandHandler private readonly CancellationToken _cancellationToken; private readonly LauncherExecutablePath _launcherExecutablePath; - public HostCommandHandler(IHostApplicationLifetime lifetime, LauncherExecutablePath launcherExecutablePath) + public HostCommandHandler(IHostApplicationLifetime lifetime, LauncherExecutablePath launcherExecutablePath, bool useDomainSockets, string? domainSocketPath) { _cancellationToken = lifetime.ApplicationStopping; _launcherExecutablePath = launcherExecutablePath; @@ -224,4 +224,3 @@ private static Dictionary ProcessPackageConfiguration(Dictionary } } } - diff --git a/src/fiskaltrust.Launcher/Services/HostingService.cs b/src/fiskaltrust.Launcher/Services/HostingService.cs index 883a3d91..6648223d 100644 --- a/src/fiskaltrust.Launcher/Services/HostingService.cs +++ b/src/fiskaltrust.Launcher/Services/HostingService.cs @@ -362,4 +362,4 @@ private static ConfigureWebHostBuilder BindHttpSys(this ConfigureWebHostBuilder return builder; } } -} +} \ No newline at end of file