From 055b0064b69e46e64c6e4bcf2c6367d5c3f10676 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 16 Nov 2025 12:03:36 +0000 Subject: [PATCH 1/2] Initial plan From e9a9c5ea27ae4fa18fe40b5c3f1d59020d5b9cc7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 16 Nov 2025 12:13:32 +0000 Subject: [PATCH 2/2] Pass ILoggerFactory to Unobtanium ProxyServer for logging integration Co-authored-by: waldekmastykarz <11164679+waldekmastykarz@users.noreply.github.com> --- DevProxy/ApiControllers/ProxyController.cs | 6 ++- DevProxy/Commands/CertCommand.cs | 10 +++- DevProxy/Proxy/ProxyEngine.cs | 55 +++++++++++++++++----- 3 files changed, 56 insertions(+), 15 deletions(-) diff --git a/DevProxy/ApiControllers/ProxyController.cs b/DevProxy/ApiControllers/ProxyController.cs index cec82532..01aa9c5e 100644 --- a/DevProxy/ApiControllers/ProxyController.cs +++ b/DevProxy/ApiControllers/ProxyController.cs @@ -14,11 +14,12 @@ namespace DevProxy.ApiControllers; [ApiController] [Route("[controller]")] #pragma warning disable CA1515 // required for the API controller -public sealed class ProxyController(IProxyStateController proxyStateController, IProxyConfiguration proxyConfiguration) : ControllerBase +public sealed class ProxyController(IProxyStateController proxyStateController, IProxyConfiguration proxyConfiguration, ILoggerFactory loggerFactory) : ControllerBase #pragma warning restore CA1515 { private readonly IProxyStateController _proxyStateController = proxyStateController; private readonly IProxyConfiguration _proxyConfiguration = proxyConfiguration; + private readonly ILoggerFactory _loggerFactory = loggerFactory; [HttpGet] public ProxyInfo Get() => ProxyInfo.From(_proxyStateController.ProxyState, _proxyConfiguration); @@ -114,6 +115,9 @@ public IActionResult GetRootCertificate([FromQuery][Required] string format) return ValidationProblem(ModelState); } + // Ensure ProxyServer is initialized with LoggerFactory for Unobtanium logging + ProxyEngine.EnsureProxyServerInitialized(_loggerFactory); + var certificate = ProxyEngine.ProxyServer.CertificateManager.RootCertificate; if (certificate == null) { diff --git a/DevProxy/Commands/CertCommand.cs b/DevProxy/Commands/CertCommand.cs index 06c05240..7677f2b5 100644 --- a/DevProxy/Commands/CertCommand.cs +++ b/DevProxy/Commands/CertCommand.cs @@ -14,15 +14,17 @@ namespace DevProxy.Commands; sealed class CertCommand : Command { private readonly ILogger _logger; + private readonly ILoggerFactory _loggerFactory; private readonly Option _forceOption = new("--force", "-f") { Description = "Don't prompt for confirmation when removing the certificate" }; - public CertCommand(ILogger logger) : + public CertCommand(ILogger logger, ILoggerFactory loggerFactory) : base("cert", "Manage the Dev Proxy certificate") { _logger = logger; + _loggerFactory = loggerFactory; ConfigureCommand(); } @@ -49,6 +51,9 @@ private async Task EnsureCertAsync() try { + // Ensure ProxyServer is initialized with LoggerFactory for Unobtanium logging + ProxyEngine.EnsureProxyServerInitialized(_loggerFactory); + _logger.LogInformation("Ensuring certificate exists and is trusted..."); await ProxyEngine.ProxyServer.CertificateManager.EnsureRootCertificateAsync(); _logger.LogInformation("DONE"); @@ -79,6 +84,9 @@ public void RemoveCert(ParseResult parseResult) _logger.LogInformation("Uninstalling the root certificate..."); + // Ensure ProxyServer is initialized with LoggerFactory for Unobtanium logging + ProxyEngine.EnsureProxyServerInitialized(_loggerFactory); + RemoveTrustedCertificateOnMac(); ProxyEngine.ProxyServer.CertificateManager.RemoveTrustedRootCertificate(machineTrusted: false); diff --git a/DevProxy/Proxy/ProxyEngine.cs b/DevProxy/Proxy/ProxyEngine.cs index 04c1498c..ce29e653 100755 --- a/DevProxy/Proxy/ProxyEngine.cs +++ b/DevProxy/Proxy/ProxyEngine.cs @@ -30,13 +30,16 @@ sealed class ProxyEngine( IProxyConfiguration proxyConfiguration, ISet urlsToWatch, IProxyStateController proxyController, - ILogger logger) : BackgroundService, IDisposable + ILogger logger, + ILoggerFactory loggerFactory) : BackgroundService, IDisposable { private readonly IEnumerable _plugins = plugins; private readonly ILogger _logger = logger; private readonly IProxyConfiguration _config = proxyConfiguration; - internal static ProxyServer ProxyServer { get; private set; } + internal static ProxyServer ProxyServer { get; private set; } = null!; + private static bool _isProxyServerInitialized; + private static readonly object _initLock = new(); private ExplicitProxyEndPoint? _explicitEndPoint; // lists of URLs to watch, used for intercepting requests private readonly ISet _urlsToWatch = urlsToWatch; @@ -56,23 +59,49 @@ sealed class ProxyEngine( static ProxyEngine() { - ProxyServer = new(); - ProxyServer.CertificateManager.PfxFilePath = Environment.GetEnvironmentVariable("DEV_PROXY_CERT_PATH") ?? string.Empty; - ProxyServer.CertificateManager.RootCertificateName = "Dev Proxy CA"; - ProxyServer.CertificateManager.CertificateStorage = new CertificateDiskCache(); - // we need to change this to a value lower than 397 - // to avoid the ERR_CERT_VALIDITY_TOO_LONG error in Edge - ProxyServer.CertificateManager.CertificateValidDays = 365; - - using var joinableTaskContext = new JoinableTaskContext(); - var joinableTaskFactory = new JoinableTaskFactory(joinableTaskContext); - _ = joinableTaskFactory.Run(async () => await ProxyServer.CertificateManager.LoadOrCreateRootCertificateAsync()); + // ProxyServer initialization moved to EnsureProxyServerInitialized + // to enable passing ILoggerFactory for Unobtanium logging + } + + // Ensure ProxyServer is initialized with the given ILoggerFactory + // This method can be called from multiple places (ProxyEngine, CertCommand, etc.) + internal static void EnsureProxyServerInitialized(ILoggerFactory? loggerFactory = null) + { + if (_isProxyServerInitialized) + { + return; + } + + lock (_initLock) + { + if (_isProxyServerInitialized) + { + return; + } + + ProxyServer = new(loggerFactory: loggerFactory); + ProxyServer.CertificateManager.PfxFilePath = Environment.GetEnvironmentVariable("DEV_PROXY_CERT_PATH") ?? string.Empty; + ProxyServer.CertificateManager.RootCertificateName = "Dev Proxy CA"; + ProxyServer.CertificateManager.CertificateStorage = new CertificateDiskCache(); + // we need to change this to a value lower than 397 + // to avoid the ERR_CERT_VALIDITY_TOO_LONG error in Edge + ProxyServer.CertificateManager.CertificateValidDays = 365; + + using var joinableTaskContext = new JoinableTaskContext(); + var joinableTaskFactory = new JoinableTaskFactory(joinableTaskContext); + _ = joinableTaskFactory.Run(async () => await ProxyServer.CertificateManager.LoadOrCreateRootCertificateAsync()); + + _isProxyServerInitialized = true; + } } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { _cancellationToken = stoppingToken; + // Initialize ProxyServer with LoggerFactory for Unobtanium logging + EnsureProxyServerInitialized(loggerFactory); + Debug.Assert(ProxyServer is not null, "Proxy server is not initialized"); if (!_urlsToWatch.Any())