From 9bb37217287538a5fd8b47784e71635b0309b5ce Mon Sep 17 00:00:00 2001 From: maxisoft Date: Sun, 5 May 2024 11:47:14 +0200 Subject: [PATCH] Refine error handling and retry logic in RedditHelper - Simplified exception handling by removing redundant try-catch blocks. - Implemented retry logic with exponential backoff for fetching payload. - Streamlined JsonNode parsing and error handling for more robust operation. --- ASFFreeGames/Reddit/RedditHelper.cs | 65 ++++++++++++++++------------- 1 file changed, 37 insertions(+), 28 deletions(-) diff --git a/ASFFreeGames/Reddit/RedditHelper.cs b/ASFFreeGames/Reddit/RedditHelper.cs index 93edb18..be5c0b9 100644 --- a/ASFFreeGames/Reddit/RedditHelper.cs +++ b/ASFFreeGames/Reddit/RedditHelper.cs @@ -4,6 +4,7 @@ using System.IO; using System.Linq; using System.Net; +using System.Net.Http; using System.Text; using System.Text.Json; // Not using System.Text.Json for JsonDocument using System.Text.Json.Nodes; // Using System.Text.Json.Nodes for JsonNode @@ -37,14 +38,7 @@ public static async ValueTask> GetGames(Cancellatio return result; } - JsonNode jsonPayload; - - try { - jsonPayload = await GetPayload(webBrowser, cancellationToken).ConfigureAwait(false) ?? JsonNode.Parse("{}")!; - } - catch (Exception exception) when (exception is JsonException or IOException) { - return result; - } + JsonNode jsonPayload = await GetPayload(webBrowser, cancellationToken).ConfigureAwait(false) ?? JsonNode.Parse("{}")!; try { if ((jsonPayload["kind"]?.GetValue() != "Listing") || @@ -85,9 +79,14 @@ internal static RedditGameEntry[] LoadMessages(JsonNode children) { try { text = commentData["body"]?.GetValue() ?? string.Empty; - date = checked((long) (commentData["created_utc"]?.GetValue() ?? 0)); + try { + date = checked((long) (commentData["created_utc"]?.GetValue() ?? 0)); + } + catch (Exception e) when (e is FormatException or InvalidOperationException) { + date = 0; + } - if (!double.IsNormal(date)) { + if (!double.IsNormal(date) || (date <= 0)) { date = checked((long) (commentData["created"]?.GetValue() ?? 0)); } } @@ -168,36 +167,46 @@ internal static RedditGameEntry[] LoadMessages(JsonNode children) { /// /// The web browser instance to use. /// + /// /// A JSON object response or null if failed. /// Thrown when Reddit returns a server error. /// This method is based on this GitHub issue: https://github.com/maxisoft/ASFFreeGames/issues/28 - private static async ValueTask GetPayload(WebBrowser webBrowser, CancellationToken cancellationToken) { + private static async ValueTask GetPayload(WebBrowser webBrowser, CancellationToken cancellationToken, uint retry = 5) { StreamResponse? stream = null; - try { - stream = await webBrowser.UrlGetToStream(GetUrl(), rateLimitingDelay: 500, cancellationToken: cancellationToken).ConfigureAwait(false); + for (int t = 0; t < retry; t++) { + try { + stream = await webBrowser.UrlGetToStream(GetUrl(), rateLimitingDelay: 500, cancellationToken: cancellationToken).ConfigureAwait(false); - if (stream?.Content is null) { - throw new RedditServerException("Reddit server error: content is null", stream?.StatusCode ?? HttpStatusCode.InternalServerError); - } + if (stream?.Content is null) { + throw new RedditServerException("Reddit server error: content is null", stream?.StatusCode ?? HttpStatusCode.InternalServerError); + } - return await ParseJsonNode(stream, cancellationToken).ConfigureAwait(false); - } - catch (JsonException) { - if (stream is not null && stream.StatusCode.IsServerErrorCode()) { - throw new RedditServerException($"Reddit server error: {stream.StatusCode}", stream.StatusCode); + if (stream.StatusCode.IsServerErrorCode()) { + throw new RedditServerException($"Reddit server error: {stream.StatusCode}", stream.StatusCode); + } + + return await ParseJsonNode(stream, cancellationToken).ConfigureAwait(false); + } + catch (Exception e) when (e is JsonException or IOException or RedditServerException or HttpRequestException) { + // If no RedditServerException was thrown, re-throw the original Exception + if (t + 1 == retry) { + throw; + } } + finally { + if (stream is not null) { + await stream.DisposeAsync().ConfigureAwait(false); + } - // If no RedditServerException was thrown, re-throw the original JsonReaderException - throw; - } - finally { - if (stream is not null) { - await stream.DisposeAsync().ConfigureAwait(false); + stream = null; } - stream = null; + await Task.Delay((2 << t) * 100, cancellationToken).ConfigureAwait(false); + cancellationToken.ThrowIfCancellationRequested(); } + + return JsonNode.Parse("{}"); } ///