102 lines
3.6 KiB
C#
102 lines
3.6 KiB
C#
using System.IO;
|
|
using System.Net;
|
|
using System.Net.Http.Headers;
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
namespace StellaOps.Feedser.Source.Vndr.Cisco.Internal;
|
|
|
|
internal sealed class CiscoOAuthMessageHandler : DelegatingHandler
|
|
{
|
|
private readonly CiscoAccessTokenProvider _tokenProvider;
|
|
private readonly ILogger<CiscoOAuthMessageHandler> _logger;
|
|
|
|
public CiscoOAuthMessageHandler(
|
|
CiscoAccessTokenProvider tokenProvider,
|
|
ILogger<CiscoOAuthMessageHandler> logger)
|
|
{
|
|
_tokenProvider = tokenProvider ?? throw new ArgumentNullException(nameof(tokenProvider));
|
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
|
}
|
|
|
|
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(request);
|
|
|
|
HttpRequestMessage? retryTemplate = null;
|
|
try
|
|
{
|
|
retryTemplate = await CloneRequestAsync(request, cancellationToken).ConfigureAwait(false);
|
|
}
|
|
catch (IOException)
|
|
{
|
|
// Unable to buffer content; retry will fail if needed.
|
|
retryTemplate = null;
|
|
}
|
|
|
|
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", await _tokenProvider.GetTokenAsync(cancellationToken).ConfigureAwait(false));
|
|
var response = await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
|
|
if (response.StatusCode != HttpStatusCode.Unauthorized)
|
|
{
|
|
return response;
|
|
}
|
|
|
|
response.Dispose();
|
|
_logger.LogWarning("Cisco openVuln request returned 401 Unauthorized; refreshing access token.");
|
|
await _tokenProvider.RefreshAsync(cancellationToken).ConfigureAwait(false);
|
|
|
|
if (retryTemplate is null)
|
|
{
|
|
_tokenProvider.Invalidate();
|
|
throw new HttpRequestException("Cisco openVuln request returned 401 Unauthorized and could not be retried.");
|
|
}
|
|
|
|
retryTemplate.Headers.Authorization = new AuthenticationHeaderValue("Bearer", await _tokenProvider.GetTokenAsync(cancellationToken).ConfigureAwait(false));
|
|
|
|
try
|
|
{
|
|
var retryResponse = await base.SendAsync(retryTemplate, cancellationToken).ConfigureAwait(false);
|
|
if (retryResponse.StatusCode == HttpStatusCode.Unauthorized)
|
|
{
|
|
_tokenProvider.Invalidate();
|
|
}
|
|
|
|
return retryResponse;
|
|
}
|
|
finally
|
|
{
|
|
retryTemplate.Dispose();
|
|
}
|
|
}
|
|
|
|
private static async Task<HttpRequestMessage?> CloneRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
|
{
|
|
var clone = new HttpRequestMessage(request.Method, request.RequestUri)
|
|
{
|
|
Version = request.Version,
|
|
VersionPolicy = request.VersionPolicy,
|
|
};
|
|
|
|
foreach (var header in request.Headers)
|
|
{
|
|
clone.Headers.TryAddWithoutValidation(header.Key, header.Value);
|
|
}
|
|
|
|
if (request.Content is not null)
|
|
{
|
|
using var memory = new MemoryStream();
|
|
await request.Content.CopyToAsync(memory, cancellationToken).ConfigureAwait(false);
|
|
memory.Position = 0;
|
|
var buffer = memory.ToArray();
|
|
var contentClone = new ByteArrayContent(buffer);
|
|
foreach (var header in request.Content.Headers)
|
|
{
|
|
contentClone.Headers.TryAddWithoutValidation(header.Key, header.Value);
|
|
}
|
|
|
|
clone.Content = contentClone;
|
|
}
|
|
|
|
return clone;
|
|
}
|
|
}
|