| File: PackagePreFetcherResult.cs | Web Access |
| Project: src\nuget-client\src\NuGet.Core\NuGet.PackageManagement\NuGet.PackageManagement.csproj (NuGet.PackageManagement) |
// 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. #nullable disable using System; using System.IO; using System.Runtime.ExceptionServices; using System.Threading.Tasks; using NuGet.Common; using NuGet.Packaging; using NuGet.Packaging.Core; using NuGet.Protocol.Core.Types; namespace NuGet.PackageManagement { public class PackagePreFetcherResult : IDisposable { private readonly Task<DownloadResourceResult> _downloadTask; private readonly string _nupkgPath; private DownloadResourceResult _result; private ExceptionDispatchInfo _exception; private readonly DateTimeOffset _downloadStartTime; private DateTimeOffset _packageFetchTime; private DateTimeOffset _taskReturnTime; private bool _disposed = false; /// <summary> /// True if the result came from the packages folder. /// </summary> /// <remarks>Not thread safe.</remarks> public bool InPackagesFolder { get; } /// <summary> /// Package identity. /// </summary> public PackageIdentity Package { get; } /// <summary> /// PackageSource for the download. This is null if the packages folder was used. /// </summary> public Configuration.PackageSource Source { get; } /// <summary> /// True if the download is complete. /// </summary> public bool IsComplete { get; private set; } public const string PackagePreFetcherInformation = nameof(PackagePreFetcherInformation); /// <summary> /// Create a PreFetcher result for a downloaded package. /// </summary> public PackagePreFetcherResult( Task<DownloadResourceResult> downloadTask, PackageIdentity package, Configuration.PackageSource source) { if (source == null) { throw new ArgumentNullException(nameof(source)); } if (downloadTask == null) { throw new ArgumentNullException(nameof(downloadTask)); } if (package == null) { throw new ArgumentNullException(nameof(package)); } _downloadTask = downloadTask; Package = package; InPackagesFolder = false; Source = source; _downloadStartTime = DateTimeOffset.Now; } /// <summary> /// Create a PreFetcher result for a package in the packages folder. /// </summary> public PackagePreFetcherResult( string nupkgPath, PackageIdentity package) { if (nupkgPath == null) { throw new ArgumentNullException(nameof(nupkgPath)); } if (package == null) { throw new ArgumentNullException(nameof(package)); } InPackagesFolder = true; IsComplete = true; Package = package; _nupkgPath = nupkgPath; } /// <summary> /// A safe wait for the download task. Exceptions are caught and stored. /// </summary> public async Task EnsureResultAsync() { // This is a noop if this has been called before, or if the result is in the packages folder. if (!InPackagesFolder && _result == null) { _packageFetchTime = DateTimeOffset.Now; try { _result = await _downloadTask; } catch (Exception ex) { _exception = ExceptionDispatchInfo.Capture(ex); } _taskReturnTime = DateTimeOffset.Now; IsComplete = true; } } /// <summary> /// Ensure and retrieve the download result. /// </summary> public async Task<DownloadResourceResult> GetResultAsync() { DownloadResourceResult result = null; if (InPackagesFolder) { // Results from the packages folder are created on demand result = GetPackagesFolderResult(_nupkgPath); } else { // Wait for the download to finish await EnsureResultAsync(); if (_exception != null) { // Rethrow the exception if the download failed _exception.Throw(); } // Use the downloadTask result result = _result; } return result; } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (_disposed) { return; } if (disposing) { // The task should be awaited before calling dispose if (_result != null) { _result.Dispose(); } } _disposed = true; } public void EmitTelemetryEvent(Guid parentId) { var telemetryEvent = new TelemetryEvent(PackagePreFetcherInformation); telemetryEvent["DownloadStartTime"] = _downloadStartTime; telemetryEvent["PackageFetchTime"] = _packageFetchTime; telemetryEvent["TaskReturnTime"] = _taskReturnTime; telemetryEvent.AddPiiData("PackageId", Package.ToString()); if (parentId != Guid.Empty) { telemetryEvent["ParentId"] = parentId.ToString(); } TelemetryActivity.EmitTelemetryEvent(telemetryEvent); } private DownloadResourceResult GetPackagesFolderResult(string nupkgPath) { // Create a download result for the package that already exists return new DownloadResourceResult( File.OpenRead(nupkgPath), new PackageArchiveReader(nupkgPath), Source?.Source) { SignatureVerified = true }; } } }