File: Tasks\ZipDeploy\ZipDeploymentStatus.cs
Web Access
Project: ..\..\..\src\WebSdk\Publish\Tasks\Microsoft.NET.Sdk.Publish.Tasks.csproj (Microsoft.NET.Sdk.Publish.Tasks)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Net;
using System.Net.Http;
using System.Text.Json;
using Microsoft.Build.Utilities;
using Microsoft.NET.Sdk.Publish.Tasks.Properties;
 
namespace Microsoft.NET.Sdk.Publish.Tasks.ZipDeploy
{
    public class ZipDeploymentStatus
    {
        private const int MaxMinutesToWait = 3;
        private const int StatusRefreshDelaySeconds = 3;
        private const int RetryCount = 3;
        private const int RetryDelaySeconds = 1;
 
        private readonly IHttpClient _client;
        private readonly string _userAgent;
        private readonly TaskLoggingHelper _log;
        private readonly bool _logMessages;
 
        public ZipDeploymentStatus(IHttpClient client, string userAgent, TaskLoggingHelper log, bool logMessages)
        {
            _client = client;
            _userAgent = userAgent;
            _log = log;
            _logMessages = logMessages;
        }
 
        public async Task<DeploymentResponse?> PollDeploymentStatusAsync(string deploymentUrl, string? userName, string? password)
        {
            var tokenSource = new CancellationTokenSource(TimeSpan.FromMinutes(MaxMinutesToWait));
 
            if (_logMessages)
            {
                _log.LogMessage(Resources.ZIPDEPLOY_DeploymentStatusPolling);
            }
 
            DeploymentResponse? deploymentResponse = null;
            DeployStatus? deployStatus = DeployStatus.Pending;
 
            while (!tokenSource.IsCancellationRequested
                && deployStatus != DeployStatus.Success
                && deployStatus != DeployStatus.Failed
                && deployStatus != DeployStatus.Unknown)
            {
                try
                {
                    deploymentResponse = await InvokeGetRequestWithRetryAsync<DeploymentResponse>(
                        deploymentUrl, userName, password, RetryCount, TimeSpan.FromSeconds(RetryDelaySeconds), tokenSource);
 
                    deployStatus = deploymentResponse?.Status is not null
                        ? deploymentResponse.Status
                        : DeployStatus.Unknown;
 
                    if (_logMessages)
                    {
                        _log.LogMessage(string.Format(Resources.ZIPDEPLOY_DeploymentStatus, Enum.GetName(typeof(DeployStatus), deployStatus)));
                    }
                }
                catch (HttpRequestException)
                {
                    return deploymentResponse;
                }
 
                await System.Threading.Tasks.Task.Delay(TimeSpan.FromSeconds(StatusRefreshDelaySeconds));
            }
 
            return deploymentResponse ?? new() { Status = DeployStatus.Unknown };
        }
 
        private async Task<T?> InvokeGetRequestWithRetryAsync<T>(string url, string? userName, string? password, int retryCount, TimeSpan retryDelay, CancellationTokenSource cts)
        {
            IHttpResponse? response = null;
            await RetryAsync(async () =>
            {
                response = await _client.GetRequestAsync(new Uri(url, UriKind.RelativeOrAbsolute), userName, password, _userAgent, cts.Token);
            }, retryCount, retryDelay);
 
            if (response?.StatusCode != HttpStatusCode.OK && response?.StatusCode != HttpStatusCode.Accepted)
            {
                return default;
            }
 
            using var stream = await response.GetResponseBodyAsync();
            if (stream is null)
            {
                return default;
            }
            var reader = new StreamReader(stream, Encoding.UTF8);
            return FromJson<T>(reader.ReadToEnd());
        }
 
        private async System.Threading.Tasks.Task RetryAsync(Func<System.Threading.Tasks.Task> func, int retryCount, TimeSpan retryDelay)
        {
            while (true)
            {
                try
                {
                    await func();
                    return;
                }
                catch (Exception)
                {
                    if (retryCount <= 0)
                    {
                        throw;
                    }
                    retryCount--;
                }
 
                await System.Threading.Tasks.Task.Delay(retryDelay);
            }
        }
 
        private static T? FromJson<T>(string jsonString)
        {
            return JsonSerializer.Deserialize<T>(jsonString,
                new JsonSerializerOptions
                {
                    AllowTrailingCommas = true,
                    ReadCommentHandling = JsonCommentHandling.Skip
                });
        }
    }
}