From 91dcbd44c10af893374cfb36dc7a009caa4818d0 Mon Sep 17 00:00:00 2001 From: Doug Bunting Date: Tue, 2 Apr 2019 09:24:27 -0700 Subject: [PATCH] Remove build-time service reference download feature (#8981) - #7500 - skip dotnet-watch `RenameCompiledFile()` test - #8987 --- .../src/DownloadFile.cs | 229 ------------------ .../src/GetFileReferenceMetadata.cs | 4 - .../src/GetUriReferenceMetadata.cs | 138 ----------- .../src/Properties/Resources.Designer.cs | 18 +- .../src/Resources.resx | 62 +++-- ...oft.Extensions.ApiDescription.Design.props | 20 +- ...t.Extensions.ApiDescription.Design.targets | 44 +--- .../dotnet-watch/test/GlobbingAppTests.cs | 2 +- 8 files changed, 36 insertions(+), 481 deletions(-) delete mode 100644 src/Mvc/Extensions.ApiDescription.Design/src/DownloadFile.cs delete mode 100644 src/Mvc/Extensions.ApiDescription.Design/src/GetUriReferenceMetadata.cs diff --git a/src/Mvc/Extensions.ApiDescription.Design/src/DownloadFile.cs b/src/Mvc/Extensions.ApiDescription.Design/src/DownloadFile.cs deleted file mode 100644 index 293300d6a8..0000000000 --- a/src/Mvc/Extensions.ApiDescription.Design/src/DownloadFile.cs +++ /dev/null @@ -1,229 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.IO; -using System.Net.Http; -using System.Net.Sockets; -using System.Reflection; -using System.Security.Cryptography; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Build.Framework; -using Microsoft.Build.Utilities; -using Task = System.Threading.Tasks.Task; -using Utilities = Microsoft.Build.Utilities; - -namespace Microsoft.Extensions.ApiDescription.Tasks -{ - /// - /// Downloads a file. - /// - public class DownloadFile : Utilities.Task, ICancelableTask - { - private readonly CancellationTokenSource _cts = new CancellationTokenSource(); - - /// - /// The URI to download. - /// - [Required] - public string Uri { get; set; } - - /// - /// Destination for the downloaded file. If the file already exists, it is not re-downloaded unless - /// is true. - /// - [Required] - public string DestinationPath { get; set; } - - /// - /// Should be overwritten. When true, the file is downloaded and its hash - /// compared to the existing file. If those hashes do not match (or does not - /// exist), is overwritten. - /// - public bool Overwrite { get; set; } - - /// - /// The maximum amount of time in seconds to allow for downloading the file. Defaults to 2 minutes. - /// - public int TimeoutSeconds { get; set; } = 60 * 2; - - /// - public void Cancel() => _cts.Cancel(); - - /// - public override bool Execute() => ExecuteAsync().Result; - - public async Task ExecuteAsync() - { - if (string.IsNullOrEmpty(Uri)) - { - Log.LogError("Uri parameter must not be null or empty."); - return false; - } - - if (string.IsNullOrEmpty(Uri)) - { - Log.LogError("DestinationPath parameter must not be null or empty."); - return false; - } - - var builder = new UriBuilder(Uri); - if (!string.Equals(System.Uri.UriSchemeHttp, builder.Scheme, StringComparison.OrdinalIgnoreCase) && - !string.Equals(System.Uri.UriSchemeHttps, builder.Scheme, StringComparison.OrdinalIgnoreCase)) - { - Log.LogError($"{nameof(Uri)} parameter does not have scheme {System.Uri.UriSchemeHttp} or " + - $"{System.Uri.UriSchemeHttps}."); - return false; - } - - await DownloadFileAsync(Uri, DestinationPath, Overwrite, _cts.Token, TimeoutSeconds, Log); - - return !Log.HasLoggedErrors; - } - - private static async Task DownloadFileAsync( - string uri, - string destinationPath, - bool overwrite, - CancellationToken cancellationToken, - int timeoutSeconds, - TaskLoggingHelper log) - { - var destinationExists = File.Exists(destinationPath); - if (destinationExists && !overwrite) - { - log.LogMessage($"Not downloading '{uri}' to overwrite existing file '{destinationPath}'."); - return; - } - - log.LogMessage(MessageImportance.High, $"Downloading '{uri}' to '{destinationPath}'."); - - using (var httpClient = new HttpClient()) - { - await DownloadAsync(uri, destinationPath, httpClient, cancellationToken, log, timeoutSeconds); - } - } - - public static async Task DownloadAsync( - string uri, - string destinationPath, - HttpClient httpClient, - CancellationToken cancellationToken, - TaskLoggingHelper log, - int timeoutSeconds) - { - // Timeout if the response has not begun within 1 minute - httpClient.Timeout = TimeSpan.FromMinutes(1); - - var destinationExists = File.Exists(destinationPath); - var reachedCopy = false; - try - { - using (var response = await httpClient.GetAsync(uri, cancellationToken)) - { - response.EnsureSuccessStatusCode(); - cancellationToken.ThrowIfCancellationRequested(); - - using (var responseStreamTask = response.Content.ReadAsStreamAsync()) - { - var finished = await Task.WhenAny( - responseStreamTask, - Task.Delay(TimeSpan.FromSeconds(timeoutSeconds))); - - if (!ReferenceEquals(responseStreamTask, finished)) - { - throw new TimeoutException($"Download failed to complete in {timeoutSeconds} seconds."); - } - - using (var responseStream = await responseStreamTask) - { - if (destinationExists) - { - // Check hashes before using the downloaded information. - var downloadHash = GetHash(responseStream); - responseStream.Position = 0L; - - byte[] destinationHash; - using (var destinationStream = File.OpenRead(destinationPath)) - { - destinationHash = GetHash(destinationStream); - } - - var sameHashes = downloadHash.Length == destinationHash.Length; - for (var i = 0; sameHashes && i < downloadHash.Length; i++) - { - sameHashes = downloadHash[i] == destinationHash[i]; - } - - if (sameHashes) - { - log.LogMessage($"Not overwriting existing and matching file '{destinationPath}'."); - return; - } - } - else - { - // May need to create directory to hold the file. - var destinationDirectory = Path.GetDirectoryName(destinationPath); - if (!string.IsNullOrEmpty(destinationDirectory)) - { - Directory.CreateDirectory(destinationDirectory); - } - } - - // Create or overwrite the destination file. - reachedCopy = true; - using (var outStream = File.Create(destinationPath)) - { - await responseStream.CopyToAsync(outStream); - - await outStream.FlushAsync(); - } - } - } - } - } - catch (HttpRequestException ex) when (destinationExists) - { - if (ex.InnerException is SocketException socketException) - { - log.LogWarning($"Unable to download {uri}, socket error code '{socketException.SocketErrorCode}'."); - } - else - { - log.LogWarning($"Unable to download {uri}: {ex.Message}"); - } - } - catch (Exception ex) - { - log.LogError($"Downloading '{uri}' failed."); - log.LogErrorFromException(ex, showStackTrace: true); - if (reachedCopy) - { - File.Delete(destinationPath); - } - } - } - - private static byte[] GetHash(Stream stream) - { - SHA256 algorithm; - try - { - algorithm = SHA256.Create(); - } - catch (TargetInvocationException) - { - // SHA256.Create is documented to throw this exception on FIPS-compliant machines. See - // https://msdn.microsoft.com/en-us/library/z08hz7ad Fall back to a FIPS-compliant SHA256 algorithm. - algorithm = new SHA256CryptoServiceProvider(); - } - - using (algorithm) - { - return algorithm.ComputeHash(stream); - } - } - } -} diff --git a/src/Mvc/Extensions.ApiDescription.Design/src/GetFileReferenceMetadata.cs b/src/Mvc/Extensions.ApiDescription.Design/src/GetFileReferenceMetadata.cs index 5547232bc0..7266f816ed 100644 --- a/src/Mvc/Extensions.ApiDescription.Design/src/GetFileReferenceMetadata.cs +++ b/src/Mvc/Extensions.ApiDescription.Design/src/GetFileReferenceMetadata.cs @@ -66,10 +66,6 @@ namespace Microsoft.Extensions.ApiDescription.Tasks { type = "ServiceProjectReference"; } - else if (!string.IsNullOrEmpty(item.GetMetadata("SourceUri"))) - { - type = "ServiceUriReference"; - } else { type = "ServiceFileReference"; diff --git a/src/Mvc/Extensions.ApiDescription.Design/src/GetUriReferenceMetadata.cs b/src/Mvc/Extensions.ApiDescription.Design/src/GetUriReferenceMetadata.cs deleted file mode 100644 index 42ac9033c9..0000000000 --- a/src/Mvc/Extensions.ApiDescription.Design/src/GetUriReferenceMetadata.cs +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; -using Microsoft.Build.Framework; -using Microsoft.Build.Utilities; - -namespace Microsoft.Extensions.ApiDescription.Tasks -{ - /// - /// Adds or corrects DocumentPath metadata in ServiceUriReference items. - /// - public class GetUriReferenceMetadata : Task - { - private static readonly char[] InvalidFilenameCharacters = Path.GetInvalidFileNameChars(); - private static readonly string[] InvalidFilenameStrings = new[] { ".." }; - - /// - /// Default directory for DocumentPath metadata values. - /// - public string DocumentDirectory { get; set; } - - /// - /// The ServiceUriReference items to update. - /// - [Required] - public ITaskItem[] Inputs { get; set; } - - /// - /// The updated ServiceUriReference items. Will include DocumentPath metadata with full paths. - /// - [Output] - public ITaskItem[] Outputs{ get; set; } - - /// - public override bool Execute() - { - var outputs = new List(Inputs.Length); - var destinations = new HashSet(); - foreach (var item in Inputs) - { - var newItem = new TaskItem(item); - outputs.Add(newItem); - - var uri = item.ItemSpec; - var builder = new UriBuilder(uri); - if (!builder.Uri.IsAbsoluteUri) - { - Log.LogError($"{nameof(Inputs)} item '{uri}' is not an absolute URI."); - return false; - } - - if (!string.Equals(Uri.UriSchemeHttp, builder.Scheme, StringComparison.OrdinalIgnoreCase) && - !string.Equals(Uri.UriSchemeHttps, builder.Scheme, StringComparison.OrdinalIgnoreCase)) - { - Log.LogError($"{nameof(Inputs)} item '{uri}' does not have scheme {Uri.UriSchemeHttp} or " + - $"{Uri.UriSchemeHttps}."); - return false; - } - - // If not specified, base filename on the URI. - var documentPath = item.GetMetadata("DocumentPath"); - if (string.IsNullOrEmpty(documentPath)) - { - // Default to a fairly long but identifiable and fairly unique filename. - var documentPathBuilder = new StringBuilder(builder.Host); - if (!string.IsNullOrEmpty(builder.Path) && - !string.Equals("/", builder.Path, StringComparison.Ordinal)) - { - documentPathBuilder.Append(builder.Path); - } - - if (!string.IsNullOrEmpty(builder.Query) && - !string.Equals("?", builder.Query, StringComparison.Ordinal)) - { - documentPathBuilder.Append(builder.Query); - } - - // Sanitize the string because it likely contains illegal filename characters such as '/' and '?'. - // (Do not treat slashes as folder separators.) - documentPath = documentPathBuilder.ToString(); - documentPath = string.Join("_", documentPath.Split(InvalidFilenameCharacters)); - while (documentPath.Contains(InvalidFilenameStrings[0])) - { - documentPath = string.Join( - ".", - documentPath.Split(InvalidFilenameStrings, StringSplitOptions.None)); - } - - // URI might end with ".json". Don't duplicate that or a final period. - if (!documentPath.EndsWith(".json", StringComparison.OrdinalIgnoreCase)) - { - if (documentPath.EndsWith(".", StringComparison.Ordinal)) - { - documentPath += "json"; - } - else - { - documentPath += ".json"; - } - } - } - - documentPath = GetFullPath(documentPath); - if (!destinations.Add(documentPath)) - { - // This case may occur when user is experimenting e.g. with multiple code generators or options. - // May also occur when user accidentally duplicates DocumentPath metadata. - Log.LogError(Resources.FormatDuplicateUriDocumentPaths(documentPath)); - } - - MetadataSerializer.SetMetadata(newItem, "DocumentPath", documentPath); - } - - Outputs = outputs.ToArray(); - - return !Log.HasLoggedErrors; - } - - private string GetFullPath(string path) - { - if (!Path.IsPathRooted(path)) - { - if (!string.IsNullOrEmpty(DocumentDirectory)) - { - path = Path.Combine(DocumentDirectory, path); - } - - path = Path.GetFullPath(path); - } - - return path; - } - } -} diff --git a/src/Mvc/Extensions.ApiDescription.Design/src/Properties/Resources.Designer.cs b/src/Mvc/Extensions.ApiDescription.Design/src/Properties/Resources.Designer.cs index b72e347e21..68b8789cd9 100644 --- a/src/Mvc/Extensions.ApiDescription.Design/src/Properties/Resources.Designer.cs +++ b/src/Mvc/Extensions.ApiDescription.Design/src/Properties/Resources.Designer.cs @@ -11,7 +11,7 @@ namespace Microsoft.Extensions.ApiDescription.Tasks = new ResourceManager("Microsoft.Extensions.ApiDescription.Tasks.Resources", typeof(Resources).GetTypeInfo().Assembly); /// - /// Multiple items have OutputPath='{0}'. All ServiceFileReference, ServiceProjectReference and ServiceUriReference items must have unique OutputPath metadata. + /// Multiple items have OutputPath='{0}'. All ServiceFileReference and ServiceProjectReference items must have unique OutputPath metadata. /// internal static string DuplicateFileOutputPaths { @@ -19,7 +19,7 @@ namespace Microsoft.Extensions.ApiDescription.Tasks } /// - /// Multiple items have OutputPath='{0}'. All ServiceFileReference, ServiceProjectReference and ServiceUriReference items must have unique OutputPath metadata. + /// Multiple items have OutputPath='{0}'. All ServiceFileReference and ServiceProjectReference items must have unique OutputPath metadata. /// internal static string FormatDuplicateFileOutputPaths(object p0) => string.Format(CultureInfo.CurrentCulture, GetString("DuplicateFileOutputPaths"), p0); @@ -38,20 +38,6 @@ namespace Microsoft.Extensions.ApiDescription.Tasks internal static string FormatDuplicateProjectDocumentPaths(object p0) => string.Format(CultureInfo.CurrentCulture, GetString("DuplicateProjectDocumentPaths"), p0); - /// - /// Mutliple ServiceUriReference items have DocumentPath='{0}'. ServiceUriReference items must have unique DocumentPath metadata. - /// - internal static string DuplicateUriDocumentPaths - { - get => GetString("DuplicateUriDocumentPaths"); - } - - /// - /// Mutliple ServiceUriReference items have DocumentPath='{0}'. ServiceUriReference items must have unique DocumentPath metadata. - /// - internal static string FormatDuplicateUriDocumentPaths(object p0) - => string.Format(CultureInfo.CurrentCulture, GetString("DuplicateUriDocumentPaths"), p0); - /// /// Invalid {0} metadata value for {1} item '{2}'. {0} metadata must not be set to the empty string. /// diff --git a/src/Mvc/Extensions.ApiDescription.Design/src/Resources.resx b/src/Mvc/Extensions.ApiDescription.Design/src/Resources.resx index 68fa954f09..261308c789 100644 --- a/src/Mvc/Extensions.ApiDescription.Design/src/Resources.resx +++ b/src/Mvc/Extensions.ApiDescription.Design/src/Resources.resx @@ -1,17 +1,17 @@  - @@ -118,16 +118,12 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Multiple items have OutputPath='{0}'. All ServiceFileReference, ServiceProjectReference and ServiceUriReference items must have unique OutputPath metadata. - ServiceProjectReference and ServiceUriReference items become ServiceFileReference items and all ServiceFileReference items must have unique OutputPath metadata. + Multiple items have OutputPath='{0}'. All ServiceFileReference and ServiceProjectReference items must have unique OutputPath metadata. + ServiceProjectReference items become ServiceFileReference items and all ServiceFileReference items must have unique OutputPath metadata. Mutliple ServiceProjectReference items have DocumentPath='{0}'. ServiceProjectReference items must have unique DocumentPath metadata. - - Mutliple ServiceUriReference items have DocumentPath='{0}'. ServiceUriReference items must have unique DocumentPath metadata. - Ignore corner case of ServiceProjectReference and ServiceUriReference items having the same DocumentPath. - Invalid {0} metadata value for {1} item '{2}'. {0} metadata must not be set to the empty string. diff --git a/src/Mvc/Extensions.ApiDescription.Design/src/build/Microsoft.Extensions.ApiDescription.Design.props b/src/Mvc/Extensions.ApiDescription.Design/src/build/Microsoft.Extensions.ApiDescription.Design.props index a0c5c19fd7..b07edc31d7 100644 --- a/src/Mvc/Extensions.ApiDescription.Design/src/build/Microsoft.Extensions.ApiDescription.Design.props +++ b/src/Mvc/Extensions.ApiDescription.Design/src/build/Microsoft.Extensions.ApiDescription.Design.props @@ -11,9 +11,6 @@ - - @@ -107,14 +99,6 @@ - - - - - diff --git a/src/Mvc/Extensions.ApiDescription.Design/src/build/Microsoft.Extensions.ApiDescription.Design.targets b/src/Mvc/Extensions.ApiDescription.Design/src/build/Microsoft.Extensions.ApiDescription.Design.targets index 5bc81fe530..06ee41e1d2 100644 --- a/src/Mvc/Extensions.ApiDescription.Design/src/build/Microsoft.Extensions.ApiDescription.Design.targets +++ b/src/Mvc/Extensions.ApiDescription.Design/src/build/Microsoft.Extensions.ApiDescription.Design.targets @@ -10,13 +10,8 @@ _GenerateServiceProjectReferenceDocuments; _CreateFileItemsForServiceProjectReferences - - _GetMetadataForServiceUriReferences; - _GenerateServiceUriReferenceDocuments - GenerateServiceProjectReferenceDocuments; - GenerateServiceUriReferenceDocuments; _GetMetadataForServiceFileReferences; _GenerateServiceFileReferenceCodes; _CreateCompileItemsForServiceFileReferences @@ -210,41 +205,6 @@ IgnoreExitCode="$([System.IO.File]::Exists('%(DocumentPath)'))" /> - - - - - <_Temporary Remove="@(_Temporary)" /> - - - - - - - - - - <_Temporary Remove="@(_Temporary)" /> - - - - - - - - - - - %(ServiceUriReference.Identity) - - - - - - @@ -285,8 +245,8 @@ <_Files Remove="@(_Files)" /> diff --git a/src/Tools/dotnet-watch/test/GlobbingAppTests.cs b/src/Tools/dotnet-watch/test/GlobbingAppTests.cs index e68e30120b..c964bfe8c0 100644 --- a/src/Tools/dotnet-watch/test/GlobbingAppTests.cs +++ b/src/Tools/dotnet-watch/test/GlobbingAppTests.cs @@ -74,7 +74,7 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests Assert.Equal(1, types); } - [Fact] + [Fact(Skip = "https://github.com/aspnet/AspNetCore/issues/8987")] public async Task RenameCompiledFile() { await _app.StartWatcherAsync();