using System.IO.Abstractions; using System.Net; using System.Reflection; using Microsoft.AspNetCore.Authorization; using Microsoft.Extensions.DependencyInjection; using Polly; using Polly.Timeout; using RateLimitHeaders.Polly; using RdtClient.Service.BackgroundServices; using RdtClient.Service.Helpers; using RdtClient.Service.Middleware; using RdtClient.Service.Services; using RdtClient.Service.Services.DebridClients; using RdtClient.Service.Wrappers; namespace RdtClient.Service; public static class DiConfig { public const String RD_CLIENT = "RdClient"; public const String TORBOX_CLIENT = "TorBoxClient"; public const String TORBOX_CLIENT_SLOW = "TorBoxClientSlow"; public static readonly String UserAgent = $"rdt-client {Assembly.GetEntryAssembly()?.GetName().Version}"; public static void RegisterRdtServices(this IServiceCollection services) { services.AddMemoryCache(); services.AddSingleton(); services.AddScoped(); services.AddSingleton(); services.AddSingleton(TorrentRunner.SharedState); services.AddSingleton(); services.AddSingleton(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddSingleton(); services.AddSingleton(serviceProvider => serviceProvider.GetRequiredService()); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddScoped(); services.AddScoped(); services.AddHostedService(); services.AddHostedService(); services.AddHostedService(); services.AddHostedService(); services.AddHostedService(); services.AddHostedService(); services.AddHostedService(); } public static void RegisterHttpClients(this IServiceCollection services) { services.AddHttpClient(); services.ConfigureHttpClientDefaults(builder => { builder.ConfigureHttpClient(httpClient => { httpClient.DefaultRequestHeaders.Add("User-Agent", UserAgent); }); }); services.AddTransient(); services.AddHttpClient(RD_CLIENT) .AddHttpMessageHandler() .AddResilienceHandler("rd_client_handler", ConfigureResiliencePipeline); // This likely works for most providers, but should be verified and then the providers changed // to this HTTP client for added resilience. services.AddHttpClient(TORBOX_CLIENT) .AddResilienceHandler("torbox_client_handler", ConfigureResiliencePipeline); services.AddHttpClient(TORBOX_CLIENT_SLOW) .AddHttpMessageHandler() .AddResilienceHandler("torbox_client_handler_slow", ConfigureResiliencePipeline); } private static void ConfigureResiliencePipeline(ResiliencePipelineBuilder builder) { builder.AddRateLimitHeaders(options => { options.EnableProactiveThrottling = true; }); builder.AddRetry(new() { ShouldHandle = args => args.Outcome switch { { Exception: HttpRequestException } => PredicateResult.True(), { Result.StatusCode: HttpStatusCode.RequestTimeout } => PredicateResult.True(), { Result.StatusCode: HttpStatusCode.TooManyRequests } => PredicateResult.True(), _ => PredicateResult.False() }, MaxRetryAttempts = 2, BackoffType = DelayBackoffType.Exponential, Delay = TimeSpan.FromSeconds(2), UseJitter = true, DelayGenerator = args => { if (args.Outcome.Result is { StatusCode: HttpStatusCode.TooManyRequests } response) { var delay = RateLimitHandler.GetRetryAfterDelay(response); var timeout = TimeSpan.FromSeconds(Settings.Get.Provider.Timeout); if (delay >= timeout) { return new((TimeSpan?)null); } return new(delay); } return new((TimeSpan?)null); } }); builder.AddTimeout(new TimeoutStrategyOptions { TimeoutGenerator = _ => new(TimeSpan.FromSeconds(Settings.Get.Provider.Timeout)) }); } }