diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Http/CliHttpClient.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Http/CliHttpClient.cs index ca1a23fcfc..bd3037d830 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Http/CliHttpClient.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Http/CliHttpClient.cs @@ -1,9 +1,15 @@ using System; +using System.Collections.Generic; using System.IO; using System.Net.Http; using System.Text; +using System.Threading; +using System.Threading.Tasks; using IdentityModel.Client; +using Polly; +using Polly.Extensions.Http; using Volo.Abp.Cli.Auth; +using Microsoft.Extensions.Logging; namespace Volo.Abp.Cli.Http { @@ -41,5 +47,55 @@ namespace Volo.Abp.Cli.Http client.SetBearerToken(accessToken); } } + + public async Task GetHttpResponseMessageWithRetryAsync + ( + string url, + CancellationToken? cancellationToken = null, + ILogger logger = null, + IEnumerable sleepDurations = null + ) + { + if (sleepDurations == null) + { + sleepDurations = new[] + { + TimeSpan.FromSeconds(2), + TimeSpan.FromSeconds(4), + TimeSpan.FromSeconds(7) + }; + } + + if (!cancellationToken.HasValue) + { + cancellationToken = CancellationToken.None; + } + + return await HttpPolicyExtensions + .HandleTransientHttpError() + .OrResult(msg => !msg.IsSuccessStatusCode) + .WaitAndRetryAsync(sleepDurations, + (responseMessage, timeSpan, retryCount, context) => + { + if (responseMessage.Exception != null) + { + string httpErrorCode = responseMessage.Result == null ? + httpErrorCode = string.Empty : + "HTTP-" + (int)responseMessage.Result.StatusCode + ", "; + + logger?.LogWarning( + $"{retryCount}. HTTP request attempt failed to {url} with an error: {httpErrorCode}{responseMessage.Exception.Message}. " + + $"Waiting {timeSpan.TotalSeconds} secs for the next try..."); + } + else if (responseMessage.Result != null) + { + logger?.LogWarning( + $"{retryCount}. HTTP request attempt failed to {url} with an error: {(int)responseMessage.Result.StatusCode}-{responseMessage.Result.ReasonPhrase}. " + + $"Waiting {timeSpan.TotalSeconds} secs for the next try..."); + } + }) + .ExecuteAsync(async () => await this.GetAsync(url, cancellationToken.Value)); + } + } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Licensing/AbpIoApiKeyService.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Licensing/AbpIoApiKeyService.cs index cddead548f..33a2e10fdf 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Licensing/AbpIoApiKeyService.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Licensing/AbpIoApiKeyService.cs @@ -13,6 +13,7 @@ using Volo.Abp.Cli.Http; using Volo.Abp.Cli.ProjectBuilding; using Volo.Abp.DependencyInjection; using Volo.Abp.Json; +using Volo.Abp.Threading; namespace Volo.Abp.Cli.Licensing { @@ -20,14 +21,21 @@ namespace Volo.Abp.Cli.Licensing { protected IJsonSerializer JsonSerializer { get; } protected IRemoteServiceExceptionHandler RemoteServiceExceptionHandler { get; } + protected ICancellationTokenProvider CancellationTokenProvider { get; } + private readonly ILogger _logger; private DeveloperApiKeyResult _apiKeyResult = null; - public AbpIoApiKeyService(IJsonSerializer jsonSerializer, IRemoteServiceExceptionHandler remoteServiceExceptionHandler, ILogger logger) + public AbpIoApiKeyService( + IJsonSerializer jsonSerializer, + ICancellationTokenProvider cancellationTokenProvider, + IRemoteServiceExceptionHandler remoteServiceExceptionHandler, + ILogger logger) { JsonSerializer = jsonSerializer; RemoteServiceExceptionHandler = remoteServiceExceptionHandler; _logger = logger; + CancellationTokenProvider = cancellationTokenProvider; } public async Task GetApiKeyOrNullAsync(bool invalidateCache = false) @@ -51,31 +59,10 @@ namespace Volo.Abp.Cli.Licensing using (var client = new CliHttpClient()) { - var response = await HttpPolicyExtensions - .HandleTransientHttpError() - .OrResult(msg => !msg.IsSuccessStatusCode) - .WaitAndRetryAsync(new[] - { - TimeSpan.FromSeconds(1), - TimeSpan.FromSeconds(3), - TimeSpan.FromSeconds(7) - }, - (responseMessage, timeSpan, retryCount, context) => - { - if (responseMessage.Exception != null) - { - _logger.LogWarning( - $"{retryCount}. request attempt failed to {url} with an error: \"{responseMessage.Exception.Message}\". " + - $"Waiting {timeSpan.TotalSeconds} secs for the next try..."); - } - else if (responseMessage.Result != null) - { - _logger.LogWarning( - $"{retryCount}. request attempt failed {url} with {(int)responseMessage.Result.StatusCode}-{responseMessage.Result.ReasonPhrase}. " + - $"Waiting {timeSpan.TotalSeconds} secs for the next try..."); - } - }) - .ExecuteAsync(async () => await client.GetAsync(url)); + var response = await client.GetHttpResponseMessageWithRetryAsync( + url: url, + cancellationToken: CancellationTokenProvider.Token, + logger: _logger); if (!response.IsSuccessStatusCode) { diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/NuGet/NuGetService.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/NuGet/NuGetService.cs index c0eb6fd209..0fd35582bf 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/NuGet/NuGetService.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/NuGet/NuGetService.cs @@ -1,16 +1,10 @@ using Newtonsoft.Json; using NuGet.Versioning; -using System; using System.Collections.Generic; -using System.IO; using System.Linq; -using System.Net; -using System.Net.Http; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; -using Polly; -using Polly.Extensions.Http; using Volo.Abp.Cli.Auth; using Volo.Abp.Cli.Http; using Volo.Abp.Cli.Licensing; @@ -29,6 +23,8 @@ namespace Volo.Abp.Cli.NuGet protected ICancellationTokenProvider CancellationTokenProvider { get; } protected IRemoteServiceExceptionHandler RemoteServiceExceptionHandler { get; } private readonly IApiKeyService _apiKeyService; + private List _proPackageList; + private DeveloperApiKeyResult _apiKeyResult; public NuGetService( IJsonSerializer jsonSerializer, @@ -45,20 +41,20 @@ namespace Volo.Abp.Cli.NuGet public async Task GetLatestVersionOrNullAsync(string packageId, bool includePreviews = false, bool includeNightly = false) { - List proPackageList = null; - if (AuthService.IsLoggedIn()) { - proPackageList = await GetProPackageListAsync(); + if (_proPackageList == null) + { + _proPackageList = await GetProPackageListAsync(); + } } string url; if (includeNightly) { - url = - $"https://www.myget.org/F/abp-nightly/api/v3/flatcontainer/{packageId.ToLowerInvariant()}/index.json"; + url = $"https://www.myget.org/F/abp-nightly/api/v3/flatcontainer/{packageId.ToLowerInvariant()}/index.json"; } - else if (proPackageList?.Contains(packageId) ?? false) + else if (_proPackageList?.Contains(packageId) ?? false) { url = await GetNuGetUrlForCommercialPackage(packageId); } @@ -67,15 +63,13 @@ namespace Volo.Abp.Cli.NuGet url = $"https://api.nuget.org/v3-flatcontainer/{packageId.ToLowerInvariant()}/index.json"; } - using (var client = new CliHttpClient(setBearerToken: false)) { - var responseMessage = await GetHttpResponseMessageWithRetryAsync(client, url); - - if (!responseMessage.IsSuccessStatusCode) - { - throw new Exception($"ERROR: Remote server returns '{responseMessage.StatusCode}'"); - } + var responseMessage = await client.GetHttpResponseMessageWithRetryAsync( + url, + cancellationToken: CancellationTokenProvider.Token, + logger: Logger + ); await RemoteServiceExceptionHandler.EnsureSuccessfulHttpResponseAsync(responseMessage); @@ -98,62 +92,41 @@ namespace Volo.Abp.Cli.NuGet private async Task GetNuGetUrlForCommercialPackage(string packageId) { - var apiKeyResult = await _apiKeyService.GetApiKeyOrNullAsync(); - return CliUrls.GetNuGetPackageInfoUrl(apiKeyResult.ApiKey, packageId); - } + if (_apiKeyResult == null) + { + _apiKeyResult = await _apiKeyService.GetApiKeyOrNullAsync(); + } - private async Task GetHttpResponseMessageWithRetryAsync(HttpClient client, string url) - { - return await HttpPolicyExtensions - .HandleTransientHttpError() - .OrResult(msg => !msg.IsSuccessStatusCode) - .WaitAndRetryAsync(new[] - { - TimeSpan.FromSeconds(2), - TimeSpan.FromSeconds(4), - TimeSpan.FromSeconds(7) - }, - (responseMessage, timeSpan, retryCount, context) => - { - if (responseMessage.Exception != null) - { - Logger.LogWarning( - $"{retryCount}. HTTP request attempt failed to {url} with an error: HTTP {(int)responseMessage.Result.StatusCode}-{responseMessage.Exception.Message}. " + - $"Waiting {timeSpan.TotalSeconds} secs for the next try..."); - } - else if (responseMessage.Result != null) - { - Logger.LogWarning( - $"{retryCount}. HTTP request attempt failed to {url} with an error: {(int)responseMessage.Result.StatusCode}-{responseMessage.Result.ReasonPhrase}. " + - $"Waiting {timeSpan.TotalSeconds} secs for the next try..."); - } - }) - .ExecuteAsync(async () => await client.GetAsync(url, CancellationTokenProvider.Token)); + return CliUrls.GetNuGetPackageInfoUrl(_apiKeyResult.ApiKey, packageId); } private async Task> GetProPackageListAsync() { using var client = new CliHttpClient(); - var responseMessage = await client.GetAsync( - $"{CliUrls.WwwAbpIo}api/app/nugetPackage/proPackageNames", - CancellationTokenProvider.Token + var url = $"{CliUrls.WwwAbpIo}api/app/nugetPackage/proPackageNames"; + + var responseMessage = await client.GetHttpResponseMessageWithRetryAsync( + url: url, + cancellationToken: CancellationTokenProvider.Token, + logger: Logger ); - if (!responseMessage.IsSuccessStatusCode) + if (responseMessage.IsSuccessStatusCode) { - var exceptionMessage = "Remote server returns '" + (int)responseMessage.StatusCode + "-" + responseMessage.ReasonPhrase + "'. "; - var remoteServiceErrorMessage = await RemoteServiceExceptionHandler.GetAbpRemoteServiceErrorAsync(responseMessage); + return JsonSerializer.Deserialize>(await responseMessage.Content.ReadAsStringAsync()); + } - if (remoteServiceErrorMessage != null) - { - exceptionMessage += remoteServiceErrorMessage; - } - Logger.LogInformation(exceptionMessage); - return null; + var exceptionMessage = "Remote server returns '" + (int)responseMessage.StatusCode + "-" + responseMessage.ReasonPhrase + "'. "; + var remoteServiceErrorMessage = await RemoteServiceExceptionHandler.GetAbpRemoteServiceErrorAsync(responseMessage); + + if (remoteServiceErrorMessage != null) + { + exceptionMessage += remoteServiceErrorMessage; } - return JsonSerializer.Deserialize>(await responseMessage.Content.ReadAsStringAsync()); + Logger.LogError(exceptionMessage); + return null; } public class NuGetVersionResultDto