From d70e71dd68e7b153b02938e9ef09a15484b87d2e Mon Sep 17 00:00:00 2001 From: JustArchi Date: Fri, 5 Jul 2019 11:39:19 +0200 Subject: [PATCH] Closes #1312 --- ArchiSteamFarm/ArchiWebHandler.cs | 87 ++++++++++++------------ ArchiSteamFarm/Bot.cs | 4 +- ArchiSteamFarm/Helpers/ArchiCacheable.cs | 2 +- ArchiSteamFarm/WebBrowser.cs | 11 ++- 4 files changed, 56 insertions(+), 48 deletions(-) diff --git a/ArchiSteamFarm/ArchiWebHandler.cs b/ArchiSteamFarm/ArchiWebHandler.cs index 19f205e2d..6a2150b88 100644 --- a/ArchiSteamFarm/ArchiWebHandler.cs +++ b/ArchiSteamFarm/ArchiWebHandler.cs @@ -989,6 +989,50 @@ namespace ArchiSteamFarm { return true; } + [PublicAPI] + public static async Task WebLimitRequest(string service, Func> function) { + if (string.IsNullOrEmpty(service) || (function == null)) { + ASF.ArchiLogger.LogNullError(nameof(service) + " || " + nameof(function)); + + return default; + } + + if (ASF.GlobalConfig.WebLimiterDelay == 0) { + return await function().ConfigureAwait(false); + } + + if (!WebLimitingSemaphores.TryGetValue(service, out (SemaphoreSlim RateLimitingSemaphore, SemaphoreSlim OpenConnectionsSemaphore) limiters)) { + ASF.ArchiLogger.LogGenericWarning(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(service), service)); + + if (!WebLimitingSemaphores.TryGetValue(nameof(ArchiWebHandler), out limiters)) { + ASF.ArchiLogger.LogNullError(nameof(limiters)); + + return await function().ConfigureAwait(false); + } + } + + // Sending a request opens a new connection + await limiters.OpenConnectionsSemaphore.WaitAsync().ConfigureAwait(false); + + try { + // It also increases number of requests + await limiters.RateLimitingSemaphore.WaitAsync().ConfigureAwait(false); + + // We release rate-limiter semaphore regardless of our task completion, since we use that one only to guarantee rate-limiting of their creation + Utilities.InBackground( + async () => { + await Task.Delay(ASF.GlobalConfig.WebLimiterDelay).ConfigureAwait(false); + limiters.RateLimitingSemaphore.Release(); + } + ); + + return await function().ConfigureAwait(false); + } finally { + // We release open connections semaphore only once we're indeed done sending a particular request + limiters.OpenConnectionsSemaphore.Release(); + } + } + internal async Task AcceptDigitalGiftCard(ulong giftCardID) { if (giftCardID == 0) { Bot.ArchiLogger.LogNullError(nameof(giftCardID)); @@ -2643,49 +2687,6 @@ namespace ArchiSteamFarm { return true; } - private static async Task WebLimitRequest(string service, Func> function) { - if (string.IsNullOrEmpty(service) || (function == null)) { - ASF.ArchiLogger.LogNullError(nameof(service) + " || " + nameof(function)); - - return default; - } - - if (ASF.GlobalConfig.WebLimiterDelay == 0) { - return await function().ConfigureAwait(false); - } - - if (!WebLimitingSemaphores.TryGetValue(service, out (SemaphoreSlim RateLimitingSemaphore, SemaphoreSlim OpenConnectionsSemaphore) limiters)) { - ASF.ArchiLogger.LogGenericWarning(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(service), service)); - - if (!WebLimitingSemaphores.TryGetValue(nameof(ArchiWebHandler), out limiters)) { - ASF.ArchiLogger.LogNullError(nameof(limiters)); - - return await function().ConfigureAwait(false); - } - } - - // Sending a request opens a new connection - await limiters.OpenConnectionsSemaphore.WaitAsync().ConfigureAwait(false); - - try { - // It also increases number of requests - await limiters.RateLimitingSemaphore.WaitAsync().ConfigureAwait(false); - - // We release rate-limiter semaphore regardless of our task completion, since we use that one only to guarantee rate-limiting of their creation - Utilities.InBackground( - async () => { - await Task.Delay(ASF.GlobalConfig.WebLimiterDelay).ConfigureAwait(false); - limiters.RateLimitingSemaphore.Release(); - } - ); - - return await function().ConfigureAwait(false); - } finally { - // We release open connections semaphore only once we're indeed done sending a particular request - limiters.OpenConnectionsSemaphore.Release(); - } - } - public enum ESession : byte { None, Lowercase, diff --git a/ArchiSteamFarm/Bot.cs b/ArchiSteamFarm/Bot.cs index f18c12211..35ace52d8 100755 --- a/ArchiSteamFarm/Bot.cs +++ b/ArchiSteamFarm/Bot.cs @@ -84,6 +84,9 @@ namespace ArchiSteamFarm { [PublicAPI] public readonly Commands Commands; + [PublicAPI] + public readonly SteamConfiguration SteamConfiguration; + [JsonProperty] public uint GamesToRedeemInBackgroundCount => BotDatabase?.GamesToRedeemInBackgroundCount ?? 0; @@ -101,7 +104,6 @@ namespace ArchiSteamFarm { internal readonly ConcurrentDictionary OwnedPackageIDs = new ConcurrentDictionary(); internal readonly SteamApps SteamApps; - internal readonly SteamConfiguration SteamConfiguration; internal readonly SteamFriends SteamFriends; internal bool CanReceiveSteamCards => !IsAccountLimited && !IsAccountLocked; diff --git a/ArchiSteamFarm/Helpers/ArchiCacheable.cs b/ArchiSteamFarm/Helpers/ArchiCacheable.cs index 0bcb082c4..485d4603b 100644 --- a/ArchiSteamFarm/Helpers/ArchiCacheable.cs +++ b/ArchiSteamFarm/Helpers/ArchiCacheable.cs @@ -42,7 +42,7 @@ namespace ArchiSteamFarm.Helpers { private T InitializedValue; private Timer MaintenanceTimer; - internal ArchiCacheable([NotNull] Func> resolveFunction, TimeSpan? cacheLifetime = null) { + public ArchiCacheable([NotNull] Func> resolveFunction, TimeSpan? cacheLifetime = null) { ResolveFunction = resolveFunction ?? throw new ArgumentNullException(nameof(resolveFunction)); CacheLifetime = cacheLifetime ?? Timeout.InfiniteTimeSpan; } diff --git a/ArchiSteamFarm/WebBrowser.cs b/ArchiSteamFarm/WebBrowser.cs index 49d21e52b..c6b923dd4 100644 --- a/ArchiSteamFarm/WebBrowser.cs +++ b/ArchiSteamFarm/WebBrowser.cs @@ -34,15 +34,18 @@ using Newtonsoft.Json; namespace ArchiSteamFarm { public sealed class WebBrowser : IDisposable { + [PublicAPI] + public const byte MaxTries = 5; // Defines maximum number of recommended tries for a single request + internal const byte MaxConnections = 5; // Defines maximum number of connections per ServicePoint. Be careful, as it also defines maximum number of sockets in CLOSE_WAIT state - internal const byte MaxTries = 5; // Defines maximum number of recommended tries for a single request private const byte ExtendedTimeoutMultiplier = 10; // Defines multiplier of timeout for WebBrowsers dealing with huge data (ASF update) private const byte MaxIdleTime = 15; // Defines in seconds, how long socket is allowed to stay in CLOSE_WAIT state after there are no connections to it - internal readonly CookieContainer CookieContainer = new CookieContainer(); + [PublicAPI] + public TimeSpan Timeout => HttpClient.Timeout; - internal TimeSpan Timeout => HttpClient.Timeout; + internal readonly CookieContainer CookieContainer = new CookieContainer(); private readonly ArchiLogger ArchiLogger; private readonly HttpClient HttpClient; @@ -593,9 +596,11 @@ namespace ArchiSteamFarm { case "https": break; case "steammobile": + // Those redirections are invalid, but we're aware of that and we have extra logic for them return response; default: + // We have no clue about those, but maybe HttpClient can handle them for us ASF.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(redirectUri.Scheme), redirectUri.Scheme));