diff --git a/ASFFreeGames/HttpClientSimple/SimpleHttpClientFactory.cs b/ASFFreeGames/HttpClientSimple/SimpleHttpClientFactory.cs index cf18428..c518551 100644 --- a/ASFFreeGames/HttpClientSimple/SimpleHttpClientFactory.cs +++ b/ASFFreeGames/HttpClientSimple/SimpleHttpClientFactory.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Collections.ObjectModel; using System.Net; using ArchiSteamFarm.Storage; using ASFFreeGames.Configurations; @@ -21,7 +20,7 @@ public sealed class SimpleHttpClientFactory(ASFFreeGamesOptions options) : IDisp "no-proxy" }; - private readonly Dictionary> Cache = new(); + private readonly Dictionary> Cache = new(); private enum ECacheKey { Generic, @@ -35,38 +34,45 @@ private SimpleHttpClient CreateFor(ECacheKey key, string? proxy = null) { proxy = options.Proxy; } - WebProxy? webProxy; + IWebProxy? webProxy; if (DisableProxyStrings.Contains(proxy ?? "")) { webProxy = null; } else if (!string.IsNullOrWhiteSpace(proxy)) { - webProxy = new WebProxy(proxy, BypassOnLocal: true); + SimpleWebProxy simpleProxy = new(proxy, bypassOnLocal: true); if (Uri.TryCreate(proxy, UriKind.Absolute, out Uri? uri) && !string.IsNullOrWhiteSpace(uri.UserInfo)) { string[] split = uri.UserInfo.Split(':'); if (split.Length == 2) { - webProxy.Credentials = new NetworkCredential(split[0], split[1]); + simpleProxy.Credentials = new NetworkCredential(split[0], split[1]); } } + + webProxy = simpleProxy; } else { webProxy = ArchiSteamFarm.Core.ASF.GlobalConfig?.WebProxy; } lock (Cache) { - if (Cache.TryGetValue(key, out Tuple? cached)) { - if (cached.Item1?.Address == webProxy?.Address) { + if (Cache.TryGetValue(key, out Tuple? cached)) { + // Use a test URI to get proxy addresses via IWebProxy interface + // This avoids direct WebProxy type references which cause runtime issues + Uri testUri = new("http://test.example.com", UriKind.Absolute); + Uri? cachedAddress = cached.Item1?.GetProxy(testUri); + Uri? currentAddress = webProxy?.GetProxy(testUri); + + if (cachedAddress == currentAddress) { return cached.Item2; } - else { - Cache.Remove(key); - } + + Cache.Remove(key); } #pragma warning disable CA2000 - Tuple tuple = new(webProxy, new SimpleHttpClient(webProxy)); + Tuple tuple = new(webProxy, new SimpleHttpClient(webProxy)); #pragma warning restore CA2000 Cache.Add(key, tuple); diff --git a/ASFFreeGames/HttpClientSimple/SimpleWebProxy.cs b/ASFFreeGames/HttpClientSimple/SimpleWebProxy.cs new file mode 100644 index 0000000..ccf9b9a --- /dev/null +++ b/ASFFreeGames/HttpClientSimple/SimpleWebProxy.cs @@ -0,0 +1,24 @@ +using System; +using System.Net; + +namespace Maxisoft.ASF.HttpClientSimple; + +/// +/// A simple IWebProxy implementation that avoids using System.Net.WebProxy +/// to prevent runtime version mismatch issues with WebProxy constructors. +/// +public sealed class SimpleWebProxy : IWebProxy { + public Uri ProxyAddress { get; } + public bool BypassOnLocal { get; } + public ICredentials? Credentials { get; set; } + + public SimpleWebProxy(string address, bool bypassOnLocal = true) { + ProxyAddress = new Uri(address); + BypassOnLocal = bypassOnLocal; + } + + public Uri? GetProxy(Uri destination) => ProxyAddress; + + public bool IsBypassed(Uri host) => + BypassOnLocal && (host.IsLoopback || host.Host.Equals("localhost", StringComparison.OrdinalIgnoreCase)); +} diff --git a/ASFFreeGames/Utils/LoggerFilter.cs b/ASFFreeGames/Utils/LoggerFilter.cs index f7c292b..1fe8fff 100644 --- a/ASFFreeGames/Utils/LoggerFilter.cs +++ b/ASFFreeGames/Utils/LoggerFilter.cs @@ -68,7 +68,7 @@ public IDisposable DisableLogging(Func filter, [NotNull] Bot logger.Factory.ReconfigExistingLoggers(); } - return new LoggerRemoveFilterDisposable(node); + return new LoggerRemoveFilterDisposable(filters, node); } } @@ -126,8 +126,10 @@ private static Logger GetLogger(ArchiLogger logger, string name = "ASF") { private bool RemoveFilters(BotName botName) => Filters.TryRemove(botName, out _); // A class that implements a disposable object for removing filters - private sealed class LoggerRemoveFilterDisposable(LinkedListNode> node) : IDisposable { - public void Dispose() => node.List?.Remove(node); + private sealed class LoggerRemoveFilterDisposable( + LinkedList> list, + LinkedListNode> node) : IDisposable { + public void Dispose() => list.Remove(node); } // A class that implements a custom filter that invokes a method diff --git a/Directory.Build.props b/Directory.Build.props index cda1832..d844fa1 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -25,6 +25,11 @@ NU1507 + + + false + +