// 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; } } }