// Copyright (c) Microsoft. All rights reserved.
//
// Licensed under the MIT license.
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;
using NuGet.Common;
using NuGet.Protocol.Plugins;
using NuGetCredentialProvider.Util;
using ILogger = NuGetCredentialProvider.Logging.ILogger;
namespace NuGetCredentialProvider.RequestHandlers
{
///
/// A base class for implementations of .
///
/// The type of the request message.
/// The type of the response message.
internal abstract class RequestHandlerBase : IRequestHandler
where TResponse : class
{
///
/// Initializes a new instance of the class.
///
/// A to use for logging.
protected RequestHandlerBase(ILogger logger)
{
Logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
///
/// Gets a to use.
///
public virtual CancellationToken CancellationToken { get; private set; } = CancellationToken.None;
///
/// Gets the current .
///
public IConnection Connection { get; private set; }
///
/// Gets the current to use for logging.
///
public ILogger Logger { get; }
///
public async Task HandleResponseAsync(IConnection connection, Message message, IResponseHandler responseHandler, CancellationToken cancellationToken)
{
Stopwatch timer = new Stopwatch();
timer.Start();
Connection = connection;
TRequest request = MessageUtilities.DeserializePayload(message);
try {
TResponse response = null;
Logger.Verbose(string.Format(Resources.HandlingRequest, message.Type, message.Method, timer.ElapsedMilliseconds, message.Payload.ToString(Formatting.None)));
try {
using (GetProgressReporter(connection, message, cancellationToken))
{
response = await HandleRequestAsync(request).ConfigureAwait(continueOnCapturedContext: false);
}
}
catch (Exception ex) when (cancellationToken.IsCancellationRequested)
{
// NuGet will handle canceling event but verbose logs in this case might be interesting.
Logger.Verbose(string.Format(Resources.RequestHandlerCancelingExceptionMessage, ex.InnerException, ex.Message));
return;
}
Logger.Verbose(string.Format(Resources.SendingResponse, message.Type, message.Method, timer.ElapsedMilliseconds));
// If we did not send a cancel message, we must submit the response even if cancellationToken is canceled.
await responseHandler.SendResponseAsync(message, response, CancellationToken.None).ConfigureAwait(continueOnCapturedContext: false);
Logger.Verbose(string.Format(Resources.TimeElapsedAfterSendingResponse, message.Type, message.Method, timer.ElapsedMilliseconds));
}
catch (Exception ex)
{
// don't report cancellations to the console during shutdown, they're most likely not interesting.
bool cancelingDuringShutdown = ex is OperationCanceledException && Program.IsShuttingDown;
if (cancelingDuringShutdown)
{
Logger.Log(LogLevel.Verbose, allowOnConsole: false, Resources.WhileShuttingDown);
}
Logger.Log(LogLevel.Verbose, allowOnConsole: !cancelingDuringShutdown, string.Format(Resources.ResponseHandlerException, message.Method, message.RequestId));
Logger.Log(LogLevel.Verbose, allowOnConsole: !cancelingDuringShutdown, ex.ToString());
throw;
}
timer.Stop();
}
public abstract Task HandleRequestAsync(TRequest request);
protected virtual AutomaticProgressReporter GetProgressReporter(IConnection connection, Message message, CancellationToken cancellationToken)
{
return null;
}
}
}