This commit is contained in:
JustArchi 2018-04-13 09:17:27 +02:00
parent f3e12b714f
commit 66c80e618f
9 changed files with 138 additions and 54 deletions

View file

@ -26,6 +26,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ConfigureAwaitChecker.Analyzer" Version="1.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.0" />
<PackageReference Include="MSTest.TestAdapter" Version="1.3.0-beta2" />
<PackageReference Include="MSTest.TestFramework" Version="1.3.0-beta2" />

View file

@ -155,7 +155,9 @@ namespace ArchiSteamFarm {
Client.Send(request);
try {
#pragma warning disable ConfigureAwaitChecker // CAC001
return await new AsyncJob<RedeemGuestPassResponseCallback>(Client, request.SourceJobID);
#pragma warning restore ConfigureAwaitChecker // CAC001
} catch (Exception e) {
ArchiLogger.LogGenericException(e);
return null;
@ -180,7 +182,9 @@ namespace ArchiSteamFarm {
Client.Send(request);
try {
#pragma warning disable ConfigureAwaitChecker // CAC001
return await new AsyncJob<PurchaseResponseCallback>(Client, request.SourceJobID);
#pragma warning restore ConfigureAwaitChecker // CAC001
} catch (Exception e) {
ArchiLogger.LogGenericException(e);
return null;

View file

@ -35,6 +35,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ConfigureAwaitChecker.Analyzer" Version="1.0.1" />
<PackageReference Include="HtmlAgilityPack" Version="1.7.4" />
<PackageReference Include="Humanizer" Version="2.2.0" />
<PackageReference Include="ILLink.Tasks" Version="0.1.5-preview-1461378" />

View file

@ -53,6 +53,12 @@ namespace ArchiSteamFarm {
private static readonly SemaphoreSlim InventorySemaphore = new SemaphoreSlim(1, 1);
private static readonly Dictionary<string, SemaphoreSlim> WebLimitingSemaphores = new Dictionary<string, SemaphoreSlim>(3) {
{ SteamCommunityURL, new SemaphoreSlim(1, 1) },
{ SteamStoreURL, new SemaphoreSlim(1, 1) },
{ WebAPI.DefaultBaseAddress.Host, new SemaphoreSlim(1, 1) }
};
private readonly SemaphoreSlim ApiKeySemaphore = new SemaphoreSlim(1, 1);
private readonly Bot Bot;
private readonly SemaphoreSlim PublicInventorySemaphore = new SemaphoreSlim(1, 1);
@ -148,11 +154,16 @@ namespace ArchiSteamFarm {
iEconService.Timeout = WebBrowser.Timeout;
try {
response = await iEconService.DeclineTradeOffer(
tradeofferid: tradeID.ToString(),
method: WebRequestMethods.Http.Post,
secure: true
);
response = await WebLimitRequest(WebAPI.DefaultBaseAddress.Host,
#pragma warning disable ConfigureAwaitChecker // CAC001
// ReSharper disable once AccessToDisposedClosure
async () => await iEconService.DeclineTradeOffer(
tradeofferid: tradeID.ToString(),
method: WebRequestMethods.Http.Post,
secure: true
)
#pragma warning restore ConfigureAwaitChecker // CAC001
).ConfigureAwait(false);
} catch (TaskCanceledException e) {
Bot.ArchiLogger.LogGenericDebuggingException(e);
} catch (Exception e) {
@ -191,13 +202,18 @@ namespace ArchiSteamFarm {
iEconService.Timeout = WebBrowser.Timeout;
try {
response = await iEconService.GetTradeOffers(
active_only: 1,
get_descriptions: 1,
get_received_offers: 1,
secure: true,
time_historical_cutoff: uint.MaxValue
);
response = await WebLimitRequest(WebAPI.DefaultBaseAddress.Host,
#pragma warning disable ConfigureAwaitChecker // CAC001
// ReSharper disable once AccessToDisposedClosure
async () => await iEconService.GetTradeOffers(
active_only: 1,
get_descriptions: 1,
get_received_offers: 1,
secure: true,
time_historical_cutoff: uint.MaxValue
)
#pragma warning restore ConfigureAwaitChecker // CAC001
).ConfigureAwait(false);
} catch (TaskCanceledException e) {
Bot.ArchiLogger.LogGenericDebuggingException(e);
} catch (Exception e) {
@ -537,11 +553,16 @@ namespace ArchiSteamFarm {
iPlayerService.Timeout = WebBrowser.Timeout;
try {
response = await iPlayerService.GetOwnedGames(
steamid: steamID,
include_appinfo: 1,
secure: true
);
response = await WebLimitRequest(WebAPI.DefaultBaseAddress.Host,
#pragma warning disable ConfigureAwaitChecker // CAC001
// ReSharper disable once AccessToDisposedClosure
async () => await iPlayerService.GetOwnedGames(
steamid: steamID,
include_appinfo: 1,
secure: true
)
#pragma warning restore ConfigureAwaitChecker // CAC001
).ConfigureAwait(false);
} catch (TaskCanceledException e) {
Bot.ArchiLogger.LogGenericDebuggingException(e);
} catch (Exception e) {
@ -576,10 +597,15 @@ namespace ArchiSteamFarm {
iTwoFactorService.Timeout = WebBrowser.Timeout;
try {
response = await iTwoFactorService.QueryTime(
method: WebRequestMethods.Http.Post,
secure: true
);
response = await WebLimitRequest(WebAPI.DefaultBaseAddress.Host,
#pragma warning disable ConfigureAwaitChecker // CAC001
// ReSharper disable once AccessToDisposedClosure
async () => await iTwoFactorService.QueryTime(
method: WebRequestMethods.Http.Post,
secure: true
)
#pragma warning restore ConfigureAwaitChecker // CAC001
).ConfigureAwait(false);
} catch (TaskCanceledException e) {
Bot.ArchiLogger.LogGenericDebuggingException(e);
} catch (Exception e) {
@ -671,11 +697,16 @@ namespace ArchiSteamFarm {
iEconService.Timeout = WebBrowser.Timeout;
try {
response = await iEconService.GetTradeHoldDurations(
secure: true,
steamid_target: steamID,
trade_offer_access_token: tradeToken ?? "" // TODO: Change me once https://github.com/SteamRE/SteamKit/pull/522 is merged
);
response = await WebLimitRequest(WebAPI.DefaultBaseAddress.Host,
#pragma warning disable ConfigureAwaitChecker // CAC001
// ReSharper disable once AccessToDisposedClosure
async () => await iEconService.GetTradeHoldDurations(
secure: true,
steamid_target: steamID,
trade_offer_access_token: tradeToken ?? "" // TODO: Change me once https://github.com/SteamRE/SteamKit/pull/522 is merged
)
#pragma warning restore ConfigureAwaitChecker // CAC001
).ConfigureAwait(false);
} catch (TaskCanceledException e) {
Bot.ArchiLogger.LogGenericDebuggingException(e);
} catch (Exception e) {
@ -850,18 +881,23 @@ namespace ArchiSteamFarm {
// Do the magic
Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.LoggingIn, ISteamUserAuth));
KeyValue authResult = null;
KeyValue response = null;
using (dynamic iSteamUserAuth = WebAPI.GetAsyncInterface(ISteamUserAuth)) {
iSteamUserAuth.Timeout = WebBrowser.Timeout;
try {
authResult = await iSteamUserAuth.AuthenticateUser(
steamid: steamID,
sessionkey: Encoding.ASCII.GetString(WebUtility.UrlEncodeToBytes(cryptedSessionKey, 0, cryptedSessionKey.Length)),
encrypted_loginkey: Encoding.ASCII.GetString(WebUtility.UrlEncodeToBytes(cryptedLoginKey, 0, cryptedLoginKey.Length)),
method: WebRequestMethods.Http.Post,
secure: true
);
response = await WebLimitRequest(WebAPI.DefaultBaseAddress.Host,
#pragma warning disable ConfigureAwaitChecker // CAC001
// ReSharper disable once AccessToDisposedClosure
async () => await iSteamUserAuth.AuthenticateUser(
steamid: steamID,
sessionkey: Encoding.ASCII.GetString(WebUtility.UrlEncodeToBytes(cryptedSessionKey, 0, cryptedSessionKey.Length)),
encrypted_loginkey: Encoding.ASCII.GetString(WebUtility.UrlEncodeToBytes(cryptedLoginKey, 0, cryptedLoginKey.Length)),
method: WebRequestMethods.Http.Post,
secure: true
)
#pragma warning restore ConfigureAwaitChecker // CAC001
).ConfigureAwait(false);
} catch (TaskCanceledException e) {
Bot.ArchiLogger.LogGenericDebuggingException(e);
} catch (Exception e) {
@ -869,17 +905,17 @@ namespace ArchiSteamFarm {
}
}
if (authResult == null) {
if (response == null) {
return false;
}
string steamLogin = authResult["token"].Value;
string steamLogin = response["token"].Value;
if (string.IsNullOrEmpty(steamLogin)) {
Bot.ArchiLogger.LogNullError(nameof(steamLogin));
return false;
}
string steamLoginSecure = authResult["tokensecure"].Value;
string steamLoginSecure = response["tokensecure"].Value;
if (string.IsNullOrEmpty(steamLoginSecure)) {
Bot.ArchiLogger.LogNullError(nameof(steamLoginSecure));
return false;
@ -1411,7 +1447,7 @@ namespace ArchiSteamFarm {
Dictionary<string, string> data = new Dictionary<string, string>(1) { { "pin", parentalPin } };
WebBrowser.BasicResponse response = await WebBrowser.UrlPost(serviceURL + request, data, serviceURL).ConfigureAwait(false);
WebBrowser.BasicResponse response = await WebLimitRequest(serviceURL, async () => await WebBrowser.UrlPost(serviceURL + request, data, serviceURL).ConfigureAwait(false)).ConfigureAwait(false);
return (response != null) && !IsSessionExpiredUri(response.FinalUri);
}
@ -1441,7 +1477,7 @@ namespace ArchiSteamFarm {
return null;
}
WebBrowser.HtmlDocumentResponse response = await WebBrowser.UrlGetToHtmlDocument(host + request).ConfigureAwait(false);
WebBrowser.HtmlDocumentResponse response = await WebLimitRequest(host, async () => await WebBrowser.UrlGetToHtmlDocument(host + request).ConfigureAwait(false)).ConfigureAwait(false);
if (response == null) {
return null;
}
@ -1485,7 +1521,7 @@ namespace ArchiSteamFarm {
return default;
}
WebBrowser.ObjectResponse<T> response = await WebBrowser.UrlGetToJsonObject<T>(host + request).ConfigureAwait(false);
WebBrowser.ObjectResponse<T> response = await WebLimitRequest(host, async () => await WebBrowser.UrlGetToJsonObject<T>(host + request).ConfigureAwait(false)).ConfigureAwait(false);
if (response == null) {
return default;
}
@ -1529,7 +1565,7 @@ namespace ArchiSteamFarm {
return null;
}
WebBrowser.XmlDocumentResponse response = await WebBrowser.UrlGetToXmlDocument(host + request).ConfigureAwait(false);
WebBrowser.XmlDocumentResponse response = await WebLimitRequest(host, async () => await WebBrowser.UrlGetToXmlDocument(host + request).ConfigureAwait(false)).ConfigureAwait(false);
if (response == null) {
return null;
}
@ -1573,7 +1609,7 @@ namespace ArchiSteamFarm {
return false;
}
WebBrowser.BasicResponse response = await WebBrowser.UrlHead(host + request).ConfigureAwait(false);
WebBrowser.BasicResponse response = await WebLimitRequest(host, async () => await WebBrowser.UrlHead(host + request).ConfigureAwait(false)).ConfigureAwait(false);
if (response == null) {
return false;
}
@ -1646,7 +1682,7 @@ namespace ArchiSteamFarm {
}
}
WebBrowser.HtmlDocumentResponse response = await WebBrowser.UrlPostToHtmlDocument(host + request, data, referer).ConfigureAwait(false);
WebBrowser.HtmlDocumentResponse response = await WebLimitRequest(host, async () => await WebBrowser.UrlPostToHtmlDocument(host + request, data, referer).ConfigureAwait(false)).ConfigureAwait(false);
if (response == null) {
return null;
}
@ -1719,7 +1755,7 @@ namespace ArchiSteamFarm {
}
}
WebBrowser.ObjectResponse<T> response = await WebBrowser.UrlPostToJsonObject<T>(host + request, data, referer).ConfigureAwait(false);
WebBrowser.ObjectResponse<T> response = await WebLimitRequest(host, async () => await WebBrowser.UrlPostToJsonObject<T>(host + request, data, referer).ConfigureAwait(false)).ConfigureAwait(false);
if (response == null) {
return default;
}
@ -1795,7 +1831,7 @@ namespace ArchiSteamFarm {
}
}
WebBrowser.ObjectResponse<T> response = await WebBrowser.UrlPostToJsonObject<T>(host + request, data, referer).ConfigureAwait(false);
WebBrowser.ObjectResponse<T> response = await WebLimitRequest(host, async () => await WebBrowser.UrlPostToJsonObject<T>(host + request, data, referer).ConfigureAwait(false)).ConfigureAwait(false);
if (response == null) {
return default;
}
@ -1868,7 +1904,7 @@ namespace ArchiSteamFarm {
}
}
WebBrowser.BasicResponse response = await WebBrowser.UrlPost(host + request, data, referer).ConfigureAwait(false);
WebBrowser.BasicResponse response = await WebLimitRequest(host, async () => await WebBrowser.UrlPost(host + request, data, referer).ConfigureAwait(false)).ConfigureAwait(false);
if (response == null) {
return false;
}
@ -1886,6 +1922,33 @@ namespace ArchiSteamFarm {
return await UrlPostWithSession(host, request, data, referer, session, --maxTries).ConfigureAwait(false);
}
private static async Task<T> WebLimitRequest<T>(string service, Func<Task<T>> function) {
if (string.IsNullOrEmpty(service) || (function == null)) {
ASF.ArchiLogger.LogNullError(nameof(service) + " || " + nameof(function));
return default;
}
if (Program.GlobalConfig.WebLimiterDelay == 0) {
return await function().ConfigureAwait(false);
}
if (!WebLimitingSemaphores.TryGetValue(service, out SemaphoreSlim semaphore)) {
ASF.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(service), service));
return default;
}
await semaphore.WaitAsync().ConfigureAwait(false);
Task<T> task = function();
Utilities.InBackground(async () => {
await Task.WhenAll(task, Task.Delay(Program.GlobalConfig.WebLimiterDelay)).ConfigureAwait(false);
semaphore.Release();
});
return await task.ConfigureAwait(false);
}
private enum ESession : byte {
None,
Lowercase,

View file

@ -405,7 +405,9 @@ namespace ArchiSteamFarm {
await PICSSemaphore.WaitAsync().ConfigureAwait(false);
try {
#pragma warning disable ConfigureAwaitChecker // CAC001
productInfoResultSet = await SteamApps.PICSGetProductInfo(appID, null, false);
#pragma warning restore ConfigureAwaitChecker // CAC001
} catch (Exception e) {
ArchiLogger.LogGenericWarningException(e);
} finally {
@ -575,7 +577,9 @@ namespace ArchiSteamFarm {
await PICSSemaphore.WaitAsync().ConfigureAwait(false);
try {
#pragma warning disable ConfigureAwaitChecker // CAC001
productInfoResultSet = await SteamApps.PICSGetProductInfo(Enumerable.Empty<uint>(), packageIDs);
#pragma warning restore ConfigureAwaitChecker // CAC001
} catch (Exception e) {
ArchiLogger.LogGenericWarningException(e);
} finally {
@ -841,7 +845,9 @@ namespace ArchiSteamFarm {
SteamUser.WebAPIUserNonceCallback callback;
try {
#pragma warning disable ConfigureAwaitChecker // CAC001
callback = await SteamUser.RequestWebAPIUserNonce();
#pragma warning restore ConfigureAwaitChecker // CAC001
} catch (Exception e) {
ArchiLogger.LogGenericWarningException(e);
await Connect(true).ConfigureAwait(false);
@ -1352,7 +1358,9 @@ namespace ArchiSteamFarm {
try {
if (DateTime.UtcNow.Subtract(ArchiHandler.LastPacketReceived).TotalSeconds > MinHeartBeatTTL) {
#pragma warning disable ConfigureAwaitChecker // CAC001
await SteamFriends.RequestProfileInfo(SteamClient.SteamID);
#pragma warning restore ConfigureAwaitChecker // CAC001
}
HeartBeatFailures = 0;
@ -2573,7 +2581,9 @@ namespace ArchiSteamFarm {
SteamApps.FreeLicenseCallback callback;
try {
#pragma warning disable ConfigureAwaitChecker // CAC001
callback = await SteamApps.RequestFreeLicense(gameID);
#pragma warning restore ConfigureAwaitChecker // CAC001
} catch (Exception e) {
ArchiLogger.LogGenericWarningException(e);
response.Append(FormatBotResponse(string.Format(Strings.BotAddLicense, gameID, EResult.Timeout)));

View file

@ -108,6 +108,9 @@ namespace ArchiSteamFarm {
[JsonProperty(Required = Required.DisallowNull)]
internal readonly byte UpdatePeriod = 24;
[JsonProperty(Required = Required.DisallowNull)]
internal readonly ushort WebLimiterDelay = 200;
[JsonProperty(Required = Required.DisallowNull)]
internal ulong SteamOwnerID { get; private set; }

View file

@ -598,7 +598,7 @@ namespace ArchiSteamFarm {
WebSocketReceiveResult result = await webSocketContext.WebSocket.ReceiveAsync(new byte[0], CancellationToken.None).ConfigureAwait(false);
if (result.MessageType != WebSocketMessageType.Close) {
await webSocketContext.WebSocket.CloseAsync(WebSocketCloseStatus.InvalidMessageType, "You're not supposed to be sending any message but Close!", CancellationToken.None);
await webSocketContext.WebSocket.CloseAsync(WebSocketCloseStatus.InvalidMessageType, "You're not supposed to be sending any message but Close!", CancellationToken.None).ConfigureAwait(false);
break;
}

View file

@ -381,10 +381,6 @@ namespace ArchiSteamFarm {
return response;
}
if (Debugging.IsUserDebugging) {
ArchiLogger.LogGenericDebug(string.Format(Strings.Content, await response.Content.ReadAsStringAsync().ConfigureAwait(false)));
}
// WARNING: We still have undisposed response by now, make sure to dispose it ASAP if we're not returning it!
if ((response.StatusCode >= HttpStatusCode.Ambiguous) && (response.StatusCode < HttpStatusCode.BadRequest) && (maxRedirections > 0)) {
Uri redirectUri = response.Headers.Location;
@ -410,8 +406,13 @@ namespace ArchiSteamFarm {
return await InternalRequest(redirectUri, httpMethod, data, referer, httpCompletionOption, --maxRedirections).ConfigureAwait(false);
}
response.Dispose();
return null;
using (response) {
if (Debugging.IsUserDebugging) {
ArchiLogger.LogGenericDebug(string.Format(Strings.Content, await response.Content.ReadAsStringAsync().ConfigureAwait(false)));
}
return null;
}
}
private async Task<StringResponse> UrlGetToString(string request, string referer = null, byte maxTries = MaxTries) {

View file

@ -24,5 +24,6 @@
"SteamOwnerID": 0,
"SteamProtocols": 3,
"UpdateChannel": 1,
"UpdatePeriod": 24
"UpdatePeriod": 24,
"WebLimiterDelay": 200
}