using System.Net; using System.Net.Http; namespace Zcbot.WindowsNode; internal enum TransferFailureKind { Authentication, Transient, Permanent, Cancelled, } internal static class TransferRetryPolicy { private static readonly TimeSpan[] DownloadBackoff = [ TimeSpan.FromMilliseconds(250), TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(3), ]; internal static TransferFailureKind Classify(HttpStatusCode statusCode) { if (statusCode is HttpStatusCode.Unauthorized or HttpStatusCode.Forbidden) { return TransferFailureKind.Authentication; } if (statusCode is HttpStatusCode.RequestTimeout or HttpStatusCode.TooManyRequests || (int)statusCode >= 500) { return TransferFailureKind.Transient; } return TransferFailureKind.Permanent; } internal static TransferFailureKind Classify(Exception exception) => exception switch { OperationCanceledException => TransferFailureKind.Cancelled, HttpRequestException or IOException => TransferFailureKind.Transient, _ => TransferFailureKind.Permanent, }; internal static TimeSpan? RetryDelay( HttpMethod method, int attempt, HttpStatusCode? statusCode = null, Exception? exception = null) { if (method != HttpMethod.Get || attempt >= DownloadBackoff.Length) { return null; } var kind = statusCode is HttpStatusCode status ? Classify(status) : exception is not null ? Classify(exception) : TransferFailureKind.Permanent; return kind == TransferFailureKind.Transient ? DownloadBackoff[attempt] : null; } }