Files
UmlautAdaptarr/UmlautAdaptarr/Services/ProxyRequestService.cs

102 lines
4.2 KiB
C#
Raw Normal View History

2024-02-12 01:57:41 +01:00
using Microsoft.Extensions.Caching.Memory;
using System.Collections.Concurrent;
using Microsoft.Extensions.Options;
using UmlautAdaptarr.Options;
2024-02-12 01:57:41 +01:00
using UmlautAdaptarr.Utilities;
namespace UmlautAdaptarr.Services
2024-02-06 23:28:29 +01:00
{
public class ProxyRequestService
2024-02-06 23:28:29 +01:00
{
2024-02-12 01:57:41 +01:00
private readonly HttpClient _httpClient;
private readonly string _userAgent;
private readonly ILogger<ProxyRequestService> _logger;
2024-02-12 01:57:41 +01:00
private readonly IMemoryCache _cache;
private readonly GlobalOptions _options;
2024-02-12 01:57:41 +01:00
private static readonly ConcurrentDictionary<string, DateTimeOffset> _lastRequestTimes = new();
private static readonly TimeSpan MINIMUM_DELAY_FOR_SAME_HOST = new(0, 0, 0, 1);
2024-02-12 01:57:41 +01:00
public ProxyRequestService(IHttpClientFactory clientFactory, ILogger<ProxyRequestService> logger, IMemoryCache cache, IOptions<GlobalOptions> options)
2024-02-12 01:57:41 +01:00
{
_options = options.Value;
2024-02-12 01:57:41 +01:00
_httpClient = clientFactory.CreateClient("HttpClient") ?? throw new ArgumentNullException(nameof(clientFactory));
_userAgent = _options.UserAgent ?? throw new ArgumentException("UserAgent must be set in appsettings.json");
2024-02-12 01:57:41 +01:00
_logger = logger;
_cache = cache;
}
2024-02-06 23:28:29 +01:00
private static async Task EnsureMinimumDelayAsync(string targetUri)
2024-02-06 23:28:29 +01:00
{
2024-02-12 01:57:41 +01:00
var host = new Uri(targetUri).Host;
if (_lastRequestTimes.TryGetValue(host, out var lastRequestTime))
{
var timeSinceLastRequest = DateTimeOffset.Now - lastRequestTime;
if (timeSinceLastRequest < MINIMUM_DELAY_FOR_SAME_HOST)
2024-02-12 01:57:41 +01:00
{
await Task.Delay(MINIMUM_DELAY_FOR_SAME_HOST - timeSinceLastRequest);
2024-02-12 01:57:41 +01:00
}
}
_lastRequestTimes[host] = DateTimeOffset.Now;
}
public async Task<HttpResponseMessage> ProxyRequestAsync(HttpContext context, string targetUri)
{
if (!HttpMethods.IsGet(context.Request.Method))
{
throw new ArgumentException("Only GET requests are supported", context.Request.Method);
}
2024-02-06 23:28:29 +01:00
2024-02-12 01:57:41 +01:00
// Check cache
if (_cache.TryGetValue(targetUri, out HttpResponseMessage cachedResponse))
2024-02-06 23:28:29 +01:00
{
2024-02-12 01:57:41 +01:00
_logger.LogInformation($"Returning cached response for {UrlUtilities.RedactApiKey(targetUri)}");
return cachedResponse!;
2024-02-06 23:28:29 +01:00
}
await EnsureMinimumDelayAsync(targetUri);
2024-02-12 01:57:41 +01:00
var requestMessage = new HttpRequestMessage
{
RequestUri = new Uri(targetUri),
Method = HttpMethod.Get,
};
// Copy request headers
2024-02-06 23:28:29 +01:00
foreach (var header in context.Request.Headers)
{
if (header.Key == "User-Agent" && _userAgent.Length != 0)
{
requestMessage.Headers.TryAddWithoutValidation(header.Key, $"{header.Value} + {_userAgent}");
}
else if (!header.Key.Equals("Host", StringComparison.OrdinalIgnoreCase))
{
requestMessage.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray());
}
}
try
{
_logger.LogInformation($"ProxyRequestService GET {UrlUtilities.RedactApiKey(targetUri)}");
2024-02-12 01:57:41 +01:00
var responseMessage = await _httpClient.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead, context.RequestAborted);
2024-02-07 04:50:55 +01:00
2024-02-12 01:57:41 +01:00
if (responseMessage.IsSuccessStatusCode)
{
_cache.Set(targetUri, responseMessage, TimeSpan.FromMinutes(_options.IndexerRequestsCacheDurationInMinutes));
2024-02-12 01:57:41 +01:00
}
2024-02-06 23:28:29 +01:00
return responseMessage;
}
catch (Exception ex)
{
2024-02-12 01:57:41 +01:00
_logger.LogError(ex, $"Error proxying request: {UrlUtilities.RedactApiKey(targetUri)}. Error: {ex.Message}");
2024-02-06 23:28:29 +01:00
2024-02-12 01:57:41 +01:00
var errorResponse = new HttpResponseMessage(System.Net.HttpStatusCode.InternalServerError)
{
Content = new StringContent($"An error occurred while processing your request: {ex.Message}")
};
return errorResponse;
}
2024-02-06 23:28:29 +01:00
}
}
}