Skip to content

Commit 3c74c1c

Browse files
committed
try load balacing online test downloads with retry logic
1 parent 32c8499 commit 3c74c1c

1 file changed

Lines changed: 45 additions & 5 deletions

File tree

src/PG.StarWarsGame.Infrastructure/Services/Steam/SteamWorkshopWebpageDownloader.cs

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.Globalization;
1+
using System;
2+
using System.Globalization;
23
using System.Net.Http;
34
using System.Threading.Tasks;
45
using System.Web;
@@ -9,6 +10,9 @@ namespace PG.StarWarsGame.Infrastructure.Services.Steam;
910
internal class SteamWorkshopWebpageDownloader : ISteamWorkshopWebpageDownloader
1011
{
1112
private const string SteamWorkshopsBaseUrl = "https://steamcommunity.com/sharedfiles/filedetails/?";
13+
private const int MaxRetries = 4;
14+
private static readonly TimeSpan InitialBackoff = TimeSpan.FromSeconds(1);
15+
private static readonly TimeSpan MaxBackoff = TimeSpan.FromSeconds(30);
1216

1317
public async Task<HtmlDocument> GetSteamWorkshopsPageHtmlAsync(ulong workshopId, CultureInfo? culture)
1418
{
@@ -20,9 +24,45 @@ public async Task<HtmlDocument> GetSteamWorkshopsPageHtmlAsync(ulong workshopId,
2024

2125
var address = $"{SteamWorkshopsBaseUrl}{queryString}";
2226
using var client = new HttpClient();
23-
var reply = await client.GetStringAsync(address);
24-
var htmlDocument = new HtmlDocument();
25-
htmlDocument.LoadHtml(reply);
26-
return htmlDocument;
27+
28+
var backoff = InitialBackoff;
29+
for (var attempt = 0; ; attempt++)
30+
{
31+
using var response = await client.GetAsync(address).ConfigureAwait(false);
32+
33+
if ((int)response.StatusCode != 429)
34+
{
35+
response.EnsureSuccessStatusCode();
36+
var reply = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
37+
var htmlDocument = new HtmlDocument();
38+
htmlDocument.LoadHtml(reply);
39+
return htmlDocument;
40+
}
41+
42+
if (attempt >= MaxRetries)
43+
response.EnsureSuccessStatusCode();
44+
45+
var wait = GetRetryAfter(response) ?? backoff;
46+
if (wait > MaxBackoff)
47+
wait = MaxBackoff;
48+
await Task.Delay(wait).ConfigureAwait(false);
49+
50+
backoff = TimeSpan.FromTicks(Math.Min(backoff.Ticks * 2, MaxBackoff.Ticks));
51+
}
52+
}
53+
54+
private static TimeSpan? GetRetryAfter(HttpResponseMessage response)
55+
{
56+
var header = response.Headers.RetryAfter;
57+
if (header is null)
58+
return null;
59+
if (header.Delta is { } delta && delta > TimeSpan.Zero)
60+
return delta;
61+
if (header.Date is { } when)
62+
{
63+
var delay = when - DateTimeOffset.UtcNow;
64+
return delay > TimeSpan.Zero ? delay : null;
65+
}
66+
return null;
2767
}
2868
}

0 commit comments

Comments
 (0)