/// <summary>
        /// Asynchronously gets dependency information for a specific package.
        /// </summary>
        /// <param name="id">A package id.</param>
        /// <param name="version">A package version.</param>
        /// <param name="cacheContext">A source cache context.</param>
        /// <param name="logger">A logger.</param>
        /// <param name="cancellationToken">A cancellation token.</param>
        /// <returns>A task that represents the asynchronous operation.
        /// The task result (<see cref="Task{TResult}.Result" />) returns an
        /// <see cref="IEnumerable{NuGetVersion}" />.</returns>
        /// <exception cref="ArgumentException">Thrown if <paramref name="id" />
        /// is either <c>null</c> or an empty string.</exception>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="version" /> <c>null</c>.</exception>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="cacheContext" /> <c>null</c>.</exception>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="logger" /> <c>null</c>.</exception>
        /// <exception cref="OperationCanceledException">Thrown if <paramref name="cancellationToken" />
        /// is cancelled.</exception>
        public override async Task <FindPackageByIdDependencyInfo> GetDependencyInfoAsync(
            string id,
            NuGetVersion version,
            SourceCacheContext cacheContext,
            ILogger logger,
            CancellationToken cancellationToken)
        {
            if (string.IsNullOrEmpty(id))
            {
                throw new ArgumentException(Strings.ArgumentCannotBeNullOrEmpty, nameof(id));
            }

            if (version == null)
            {
                throw new ArgumentNullException(nameof(version));
            }

            if (cacheContext == null)
            {
                throw new ArgumentNullException(nameof(cacheContext));
            }

            if (logger == null)
            {
                throw new ArgumentNullException(nameof(logger));
            }

            var stopwatch = Stopwatch.StartNew();

            try
            {
                cancellationToken.ThrowIfCancellationRequested();

                var packageInfo = await GetPackageInfoAsync(id, version, cacheContext, logger, cancellationToken);

                if (packageInfo == null)
                {
                    logger.LogWarning($"Unable to find package {id}{version}");
                    return(null);
                }

                var reader = await _nupkgDownloader.GetNuspecReaderFromNupkgAsync(
                    packageInfo.Identity,
                    packageInfo.ContentUri,
                    cacheContext,
                    logger,
                    cancellationToken);

                return(GetDependencyInfo(reader));
            }
            finally
            {
                ProtocolDiagnostics.RaiseEvent(new ProtocolDiagnosticResourceEvent(
                                                   PackageSource.Source,
                                                   ResourceTypeName,
                                                   ThisTypeName,
                                                   nameof(GetDependencyInfoAsync),
                                                   stopwatch.Elapsed));
            }
        }
        /// <summary>
        /// Asynchronously copies a .nupkg to a target file path.
        /// </summary>
        /// <param name="destinationFilePath">The destination file path.</param>
        /// <param name="cancellationToken">A cancellation token.</param>
        /// <returns>A task that represents the asynchronous operation.
        /// The task result (<see cref="Task{TResult}.Result" />) returns a <see cref="bool" />
        /// indicating whether or not the copy was successful.</returns>
        /// <exception cref="ObjectDisposedException">Thrown if this object is disposed.</exception>
        /// <exception cref="ArgumentException">Thrown if <paramref name="destinationFilePath" />
        /// is either <c>null</c> or empty.</exception>
        /// <exception cref="OperationCanceledException">Thrown if <paramref name="cancellationToken" />
        /// is cancelled.</exception>
        public async Task <bool> CopyNupkgFileToAsync(string destinationFilePath, CancellationToken cancellationToken)
        {
            ThrowIfDisposed();

            if (string.IsNullOrEmpty(destinationFilePath))
            {
                throw new ArgumentException(
                          string.Format(
                              CultureInfo.CurrentCulture,
                              Strings.StringCannotBeNullOrEmpty,
                              nameof(destinationFilePath)),
                          nameof(destinationFilePath));
            }

            try
            {
                if (_throttle != null)
                {
                    await _throttle.WaitAsync();
                }

                cancellationToken.ThrowIfCancellationRequested();

                using (var source = File.OpenRead(_packageFilePath))
                    using (var destination = new FileStream(
                               destinationFilePath,
                               FileMode.Create,
                               FileAccess.ReadWrite,
                               FileShare.ReadWrite | FileShare.Delete,
                               bufferSize: 4096))
                    {
                        // This value comes from NuGet.Protocol.StreamExtensions.CopyToAsync(...).
                        // While 8K may or may not be the optimal buffer size for copy performance,
                        // it is better than 4K.
                        const int bufferSize = 8192;

                        await source.CopyToAsync(destination, bufferSize, cancellationToken);

                        ProtocolDiagnostics.RaiseEvent(new ProtocolDiagnosticNupkgCopiedEvent(Source, destination.Length));

                        return(true);
                    }
            }
            catch (Exception ex)
            {
                if (!await _handleExceptionAsync(ex))
                {
                    throw;
                }
            }
            finally
            {
                _throttle?.Release();
            }

            return(false);
        }
Esempio n. 3
0
        public override Task <DownloadResourceResult> GetDownloadResourceResultAsync(
            PackageIdentity identity,
            PackageDownloadContext downloadContext,
            string globalPackagesFolder,
            ILogger logger,
            CancellationToken token)
        {
            if (identity == null)
            {
                throw new ArgumentNullException(nameof(identity));
            }

            if (logger == null)
            {
                throw new ArgumentNullException(nameof(logger));
            }

            var stopwatch = Stopwatch.StartNew();

            try
            {
                // Find the package from the local folder
                LocalPackageInfo packageInfo = null;

                var sourcePackage = identity as SourcePackageDependencyInfo;

                if (sourcePackage?.DownloadUri != null)
                {
                    // Get the package directly if the full path is known
                    packageInfo = _localResource.GetPackage(sourcePackage.DownloadUri, logger, token);
                }
                else
                {
                    // Search for the local package
                    packageInfo = _localResource.GetPackage(identity, logger, token);
                }

                if (packageInfo != null)
                {
                    var stream = File.OpenRead(packageInfo.Path);
                    return(Task.FromResult(new DownloadResourceResult(stream, packageInfo.GetReader(), _localResource.Root)));
                }
                else
                {
                    return(Task.FromResult(new DownloadResourceResult(DownloadResourceResultStatus.NotFound)));
                }
            }
            finally
            {
                ProtocolDiagnostics.RaiseEvent(new ProtocolDiagnosticResourceEvent(
                                                   _source,
                                                   resourceType: nameof(DownloadResource),
                                                   type: nameof(LocalDownloadResource),
                                                   method: nameof(GetDownloadResourceResultAsync),
                                                   stopwatch.Elapsed));
            }
        }
Esempio n. 4
0
        /// <summary>
        /// Asynchronously gets dependency information for a specific package.
        /// </summary>
        /// <param name="id">A package id.</param>
        /// <param name="version">A package version.</param>
        /// <param name="cacheContext">A source cache context.</param>
        /// <param name="logger">A logger.</param>
        /// <param name="cancellationToken">A cancellation token.</param>
        /// <returns>A task that represents the asynchronous operation.
        /// The task result (<see cref="Task{TResult}.Result" />) returns an
        /// <see cref="IEnumerable{NuGetVersion}" />.</returns>
        /// <exception cref="ArgumentException">Thrown if <paramref name="id" />
        /// is either <c>null</c> or an empty string.</exception>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="version" /> <c>null</c>.</exception>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="cacheContext" /> <c>null</c>.</exception>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="logger" /> <c>null</c>.</exception>
        /// <exception cref="OperationCanceledException">Thrown if <paramref name="cancellationToken" />
        /// is cancelled.</exception>
        public override Task <FindPackageByIdDependencyInfo> GetDependencyInfoAsync(
            string id,
            NuGetVersion version,
            SourceCacheContext cacheContext,
            ILogger logger,
            CancellationToken cancellationToken)
        {
            if (string.IsNullOrEmpty(id))
            {
                throw new ArgumentException(Strings.ArgumentCannotBeNullOrEmpty, nameof(id));
            }

            if (version == null)
            {
                throw new ArgumentNullException(nameof(version));
            }

            if (cacheContext == null)
            {
                throw new ArgumentNullException(nameof(cacheContext));
            }

            if (logger == null)
            {
                throw new ArgumentNullException(nameof(logger));
            }

            var stopwatch = Stopwatch.StartNew();

            try
            {
                cancellationToken.ThrowIfCancellationRequested();

                FindPackageByIdDependencyInfo dependencyInfo = null;
                var info = GetPackageInfo(id, version, cacheContext, logger);
                if (info != null)
                {
                    dependencyInfo = GetDependencyInfo(info.Nuspec);
                }

                return(Task.FromResult(dependencyInfo));
            }
            finally
            {
                ProtocolDiagnostics.RaiseEvent(new ProtocolDiagnosticResourceEvent(
                                                   _source,
                                                   ResourceTypeName,
                                                   ThisTypeName,
                                                   nameof(GetDependencyInfoAsync),
                                                   stopwatch.Elapsed));
            }
        }
        public override async Task <DownloadResourceResult> GetDownloadResourceResultAsync(
            PackageIdentity identity,
            PackageDownloadContext downloadContext,
            string globalPackagesFolder,
            ILogger logger,
            CancellationToken token)
        {
            if (identity == null)
            {
                throw new ArgumentNullException(nameof(identity));
            }

            if (downloadContext == null)
            {
                throw new ArgumentNullException(nameof(downloadContext));
            }

            if (logger == null)
            {
                throw new ArgumentNullException(nameof(logger));
            }

            var stopwatch = Stopwatch.StartNew();

            try
            {
                var uri = await GetDownloadUrl(identity, logger, token);

                if (uri != null)
                {
                    return(await GetDownloadResultUtility.GetDownloadResultAsync(
                               _client,
                               identity,
                               uri,
                               downloadContext,
                               globalPackagesFolder,
                               logger,
                               token));
                }

                return(new DownloadResourceResult(DownloadResourceResultStatus.NotFound));
            }
            finally
            {
                ProtocolDiagnostics.RaiseEvent(new ProtocolDiagnosticResourceEvent(
                                                   _source,
                                                   resourceType: nameof(DownloadResource),
                                                   type: nameof(DownloadResourceV3),
                                                   method: nameof(GetDownloadResourceResultAsync),
                                                   duration: stopwatch.Elapsed));
            }
        }
        /// <summary>
        /// Asynchronously gets a package downloader for a package identity.
        /// </summary>
        /// <param name="packageIdentity">A package identity.</param>
        /// <param name="cacheContext">A source cache context.</param>
        /// <param name="logger">A logger.</param>
        /// <param name="cancellationToken">A cancellation token.</param>
        /// <returns>A task that represents the asynchronous operation.
        /// The task result (<see cref="Task{TResult}.Result" />) returns an <see cref="IPackageDownloader" />.</returns>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="packageIdentity" /> <c>null</c>.</exception>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="cacheContext" /> <c>null</c>.</exception>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="logger" /> <c>null</c>.</exception>
        /// <exception cref="OperationCanceledException">Thrown if <paramref name="cancellationToken" />
        /// is cancelled.</exception>
        public override async Task <IPackageDownloader> GetPackageDownloaderAsync(
            PackageIdentity packageIdentity,
            SourceCacheContext cacheContext,
            ILogger logger,
            CancellationToken cancellationToken)
        {
            if (packageIdentity == null)
            {
                throw new ArgumentNullException(nameof(packageIdentity));
            }

            if (cacheContext == null)
            {
                throw new ArgumentNullException(nameof(cacheContext));
            }

            if (logger == null)
            {
                throw new ArgumentNullException(nameof(logger));
            }

            var stopwatch = Stopwatch.StartNew();

            try
            {
                cancellationToken.ThrowIfCancellationRequested();

                var packageInfo = await GetPackageInfoAsync(
                    packageIdentity.Id,
                    packageIdentity.Version,
                    cacheContext,
                    logger,
                    cancellationToken);

                if (packageInfo == null)
                {
                    return(null);
                }

                return(new RemotePackageArchiveDownloader(SourceRepository.PackageSource.Source, this, packageInfo.Identity, cacheContext, logger));
            }
            finally
            {
                ProtocolDiagnostics.RaiseEvent(new ProtocolDiagnosticResourceEvent(
                                                   SourceRepository.PackageSource.Source,
                                                   ResourceTypeName,
                                                   ThisTypeName,
                                                   nameof(GetPackageDownloaderAsync),
                                                   stopwatch.Elapsed));
            }
        }
        /// <summary>
        /// Asynchronously gets all package versions for a package ID.
        /// </summary>
        /// <param name="id">A package ID.</param>
        /// <param name="cacheContext">A source cache context.</param>
        /// <param name="logger">A logger.</param>
        /// <param name="cancellationToken">A cancellation token.</param>
        /// <returns>A task that represents the asynchronous operation.
        /// The task result (<see cref="Task{TResult}.Result" />) returns an
        /// <see cref="IEnumerable{NuGetVersion}" />.</returns>
        /// <exception cref="ArgumentException">Thrown if <paramref name="id" />
        /// is either <c>null</c> or an empty string.</exception>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="cacheContext" /> <c>null</c>.</exception>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="logger" /> <c>null</c>.</exception>
        /// <exception cref="OperationCanceledException">Thrown if <paramref name="cancellationToken" />
        /// is cancelled.</exception>
        public override async Task <IEnumerable <NuGetVersion> > GetAllVersionsAsync(
            string id,
            SourceCacheContext cacheContext,
            ILogger logger,
            CancellationToken cancellationToken)
        {
            if (string.IsNullOrEmpty(id))
            {
                throw new ArgumentException(Strings.ArgumentCannotBeNullOrEmpty, nameof(id));
            }

            if (cacheContext == null)
            {
                throw new ArgumentNullException(nameof(cacheContext));
            }

            if (logger == null)
            {
                throw new ArgumentNullException(nameof(logger));
            }

            var stopwatch = Stopwatch.StartNew();

            try
            {
                cancellationToken.ThrowIfCancellationRequested();

                AddOrUpdateLogger(_plugin, logger);

                await _utilities.DoOncePerPluginLifetimeAsync(
                    MessageMethod.SetLogLevel.ToString(),
                    () => SetLogLevelAsync(logger, cancellationToken),
                    cancellationToken);

                var packageInfos = await EnsurePackagesAsync(id, cacheContext, logger, cancellationToken);

                return(packageInfos.Keys);
            }
            finally
            {
                ProtocolDiagnostics.RaiseEvent(new ProtocolDiagnosticResourceEvent(
                                                   _packageSource.Source,
                                                   ResourceTypeName,
                                                   ThisTypeName,
                                                   nameof(GetAllVersionsAsync),
                                                   stopwatch.Elapsed));
            }
        }
        /// <summary>
        /// Asynchronously check if exact package (id/version) exists at this source.
        /// </summary>
        /// <param name="id">A package id.</param>
        /// <param name="version">A package version.</param>
        /// <param name="cacheContext">A source cache context.</param>
        /// <param name="logger">A logger.</param>
        /// <param name="cancellationToken">A cancellation token.</param>
        /// <returns>A task that represents the asynchronous operation.
        /// The task result (<see cref="Task{TResult}.Result" />) returns an
        /// <see cref="IEnumerable{NuGetVersion}" />.</returns>
        /// <exception cref="ArgumentException">Thrown if <paramref name="id" />
        /// is either <c>null</c> or an empty string.</exception>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="version" /> <c>null</c>.</exception>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="cacheContext" /> <c>null</c>.</exception>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="logger" /> <c>null</c>.</exception>
        /// <exception cref="OperationCanceledException">Thrown if <paramref name="cancellationToken" />
        /// is cancelled.</exception>
        public override async Task <bool> DoesPackageExistAsync(
            string id,
            NuGetVersion version,
            SourceCacheContext cacheContext,
            ILogger logger,
            CancellationToken cancellationToken)
        {
            if (string.IsNullOrEmpty(id))
            {
                throw new ArgumentException(Strings.ArgumentCannotBeNullOrEmpty, nameof(id));
            }

            if (version == null)
            {
                throw new ArgumentNullException(nameof(version));
            }

            if (cacheContext == null)
            {
                throw new ArgumentNullException(nameof(cacheContext));
            }

            if (logger == null)
            {
                throw new ArgumentNullException(nameof(logger));
            }

            var stopwatch = Stopwatch.StartNew();

            try
            {
                cancellationToken.ThrowIfCancellationRequested();

                var packageInfo = await GetPackageInfoAsync(id, version, cacheContext, logger, cancellationToken);

                return(packageInfo != null);
            }
            finally
            {
                ProtocolDiagnostics.RaiseEvent(new ProtocolDiagnosticResourceEvent(
                                                   SourceRepository.PackageSource.Source,
                                                   ResourceTypeName,
                                                   ThisTypeName,
                                                   nameof(DoesPackageExistAsync),
                                                   stopwatch.Elapsed));
            }
        }
Esempio n. 9
0
        /// <summary>
        /// Asynchronously gets a package downloader for a package identity.
        /// </summary>
        /// <param name="packageIdentity">A package identity.</param>
        /// <param name="cacheContext">A source cache context.</param>
        /// <param name="logger">A logger.</param>
        /// <param name="cancellationToken">A cancellation token.</param>
        /// <returns>A task that represents the asynchronous operation.
        /// The task result (<see cref="Task{TResult}.Result" />) returns an <see cref="IPackageDownloader" />.</returns>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="packageIdentity" /> <c>null</c>.</exception>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="cacheContext" /> <c>null</c>.</exception>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="logger" /> <c>null</c>.</exception>
        /// <exception cref="OperationCanceledException">Thrown if <paramref name="cancellationToken" />
        /// is cancelled.</exception>
        public override Task <IPackageDownloader> GetPackageDownloaderAsync(
            PackageIdentity packageIdentity,
            SourceCacheContext cacheContext,
            ILogger logger,
            CancellationToken cancellationToken)
        {
            if (packageIdentity == null)
            {
                throw new ArgumentNullException(nameof(packageIdentity));
            }

            if (cacheContext == null)
            {
                throw new ArgumentNullException(nameof(cacheContext));
            }

            if (logger == null)
            {
                throw new ArgumentNullException(nameof(logger));
            }

            var stopwatch = Stopwatch.StartNew();

            try
            {
                cancellationToken.ThrowIfCancellationRequested();

                var packageInfo = GetPackageInfo(packageIdentity.Id, packageIdentity.Version, cacheContext, logger);
                IPackageDownloader packageDownloader = null;

                if (packageInfo != null)
                {
                    packageDownloader = new LocalPackageArchiveDownloader(_source, packageInfo.Path, packageInfo.Identity, logger);
                }

                return(Task.FromResult(packageDownloader));
            }
            finally
            {
                ProtocolDiagnostics.RaiseEvent(new ProtocolDiagnosticResourceEvent(
                                                   _source,
                                                   ResourceTypeName,
                                                   ThisTypeName,
                                                   nameof(GetPackageDownloaderAsync),
                                                   stopwatch.Elapsed));
            }
        }
Esempio n. 10
0
        /// <summary>
        /// Copies a .nupkg stream to the <paramref name="destination"/> stream. If the .nupkg cannot be found or if
        /// there is a network problem, no stream copy occurs.
        /// </summary>
        /// <param name="identity">The package identity.</param>
        /// <param name="url">The URL of the .nupkg.</param>
        /// <param name="destination">The destination stream. The .nupkg will be copied to this stream.</param>
        /// <param name="cacheContext">The cache context.</param>
        /// <param name="token">The cancellation token.</param>
        /// <returns>Returns true if the stream was copied, false otherwise.</returns>
        public async Task <bool> CopyNupkgToStreamAsync(
            PackageIdentity identity,
            string url,
            Stream destination,
            SourceCacheContext cacheContext,
            ILogger logger,
            CancellationToken token)
        {
            if (!destination.CanSeek)
            {
                // In order to handle retries, we need to write to a temporary file, then copy to destination in one pass.
                string tempFilePath = Path.GetTempFileName();
                using Stream tempFile = new FileStream(tempFilePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None, bufferSize: 4096, FileOptions.DeleteOnClose);
                bool result = await CopyNupkgToStreamAsync(identity, url, tempFile, cacheContext, logger, token);

                tempFile.Position = 0;
                await tempFile.CopyToAsync(destination, token);

                return(result);
            }
            else
            {
                return(await ProcessNupkgStreamAsync(
                           identity,
                           url,
                           async stream =>
                {
                    try
                    {
                        await stream.CopyToAsync(destination, token);
                        ProtocolDiagnostics.RaiseEvent(new ProtocolDiagnosticNupkgCopiedEvent(_httpSource.PackageSource, destination.Length));
                    }
                    catch when(!token.IsCancellationRequested)
                    {
                        destination.Position = 0;
                        destination.SetLength(0);
                        throw;
                    }
                },
                           cacheContext,
                           logger,
                           token));
            }
        }
        /// <summary>
        /// Asynchronously gets a package downloader for a package identity.
        /// </summary>
        /// <param name="packageIdentity">A package identity.</param>
        /// <param name="cacheContext">A source cache context.</param>
        /// <param name="logger">A logger.</param>
        /// <param name="cancellationToken">A cancellation token.</param>
        /// <returns>A task that represents the asynchronous operation.
        /// The task result (<see cref="Task{TResult}.Result" />) returns an <see cref="IPackageDownloader" />.</returns>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="packageIdentity" /> <c>null</c>.</exception>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="cacheContext" /> <c>null</c>.</exception>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="logger" /> <c>null</c>.</exception>
        /// <exception cref="OperationCanceledException">Thrown if <paramref name="cancellationToken" />
        /// is cancelled.</exception>
        public override Task <IPackageDownloader> GetPackageDownloaderAsync(
            PackageIdentity packageIdentity,
            SourceCacheContext cacheContext,
            ILogger logger,
            CancellationToken cancellationToken)
        {
            if (packageIdentity == null)
            {
                throw new ArgumentNullException(nameof(packageIdentity));
            }

            if (cacheContext == null)
            {
                throw new ArgumentNullException(nameof(cacheContext));
            }

            if (logger == null)
            {
                throw new ArgumentNullException(nameof(logger));
            }

            var stopwatch = Stopwatch.StartNew();

            try
            {
                cancellationToken.ThrowIfCancellationRequested();

                var packageReader     = new PluginPackageReader(_plugin, packageIdentity, _packageSource.Source);
                var packageDependency = new PluginPackageDownloader(_plugin, packageIdentity, packageReader, _packageSource.Source);

                return(Task.FromResult <IPackageDownloader>(packageDependency));
            }
            finally
            {
                ProtocolDiagnostics.RaiseEvent(new ProtocolDiagnosticResourceEvent(
                                                   _packageSource.Source,
                                                   ResourceTypeName,
                                                   ThisTypeName,
                                                   nameof(GetPackageDownloaderAsync),
                                                   stopwatch.Elapsed));
            }
        }
Esempio n. 12
0
 /// <summary>
 /// Copies a .nupkg stream to the <paramref name="destination"/> stream. If the .nupkg cannot be found or if
 /// there is a network problem, no stream copy occurs.
 /// </summary>
 /// <param name="identity">The package identity.</param>
 /// <param name="url">The URL of the .nupkg.</param>
 /// <param name="destination">The destination stream. The .nupkg will be copied to this stream.</param>
 /// <param name="cacheContext">The cache context.</param>
 /// <param name="token">The cancellation token.</param>
 /// <returns>Returns true if the stream was copied, false otherwise.</returns>
 public async Task <bool> CopyNupkgToStreamAsync(
     PackageIdentity identity,
     string url,
     Stream destination,
     SourceCacheContext cacheContext,
     ILogger logger,
     CancellationToken token)
 {
     return(await ProcessNupkgStreamAsync(
                identity,
                url,
                async stream =>
     {
         await stream.CopyToAsync(destination, token);
         ProtocolDiagnostics.RaiseEvent(new ProtocolDiagnosticNupkgCopiedEvent(_httpSource.PackageSource, destination.Length));
     },
                cacheContext,
                logger,
                token));
 }
Esempio n. 13
0
        /// <summary>
        /// Asynchronously gets all package versions for a package ID.
        /// </summary>
        /// <param name="id">A package ID.</param>
        /// <param name="cacheContext">A source cache context.</param>
        /// <param name="logger">A logger.</param>
        /// <param name="cancellationToken">A cancellation token.</param>
        /// <returns>A task that represents the asynchronous operation.
        /// The task result (<see cref="Task{TResult}.Result" />) returns an
        /// <see cref="IEnumerable{NuGetVersion}" />.</returns>
        /// <exception cref="ArgumentException">Thrown if <paramref name="id" />
        /// is either <c>null</c> or an empty string.</exception>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="cacheContext" /> <c>null</c>.</exception>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="logger" /> <c>null</c>.</exception>
        /// <exception cref="OperationCanceledException">Thrown if <paramref name="cancellationToken" />
        /// is cancelled.</exception>
        public override Task <IEnumerable <NuGetVersion> > GetAllVersionsAsync(
            string id,
            SourceCacheContext cacheContext,
            ILogger logger,
            CancellationToken cancellationToken)
        {
            if (string.IsNullOrEmpty(id))
            {
                throw new ArgumentException(Strings.ArgumentCannotBeNullOrEmpty, nameof(id));
            }

            if (cacheContext == null)
            {
                throw new ArgumentNullException(nameof(cacheContext));
            }

            if (logger == null)
            {
                throw new ArgumentNullException(nameof(logger));
            }

            var stopwatch = Stopwatch.StartNew();

            try
            {
                cancellationToken.ThrowIfCancellationRequested();

                var infos = GetPackageInfos(id, cacheContext, logger);

                return(Task.FromResult(infos.Select(p => p.Identity.Version)));
            }
            finally
            {
                ProtocolDiagnostics.RaiseEvent(new ProtocolDiagnosticResourceEvent(
                                                   _source,
                                                   ResourceTypeName,
                                                   ThisTypeName,
                                                   nameof(GetAllVersionsAsync),
                                                   stopwatch.Elapsed));
            }
        }
        /// <summary>
        /// Asynchronously gets all package versions for a package ID.
        /// </summary>
        /// <param name="id">A package ID.</param>
        /// <param name="cacheContext">A source cache context.</param>
        /// <param name="logger">A logger.</param>
        /// <param name="cancellationToken">A cancellation token.</param>
        /// <returns>A task that represents the asynchronous operation.
        /// The task result (<see cref="Task{TResult}.Result" />) returns an
        /// <see cref="IEnumerable{NuGetVersion}" />.</returns>
        /// <exception cref="ArgumentException">Thrown if <paramref name="id" />
        /// is either <c>null</c> or an empty string.</exception>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="cacheContext" /> <c>null</c>.</exception>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="logger" /> <c>null</c>.</exception>
        /// <exception cref="OperationCanceledException">Thrown if <paramref name="cancellationToken" />
        /// is cancelled.</exception>
        public override async Task <IEnumerable <NuGetVersion> > GetAllVersionsAsync(
            string id,
            SourceCacheContext cacheContext,
            ILogger logger,
            CancellationToken cancellationToken)
        {
            if (string.IsNullOrEmpty(id))
            {
                throw new ArgumentException(Strings.ArgumentCannotBeNullOrEmpty, nameof(id));
            }

            if (cacheContext == null)
            {
                throw new ArgumentNullException(nameof(cacheContext));
            }

            if (logger == null)
            {
                throw new ArgumentNullException(nameof(logger));
            }

            var stopwatch = Stopwatch.StartNew();

            try
            {
                cancellationToken.ThrowIfCancellationRequested();

                var result = await EnsurePackagesAsync(id, cacheContext, logger, cancellationToken);

                return(result.Select(item => item.Identity.Version));
            }
            finally
            {
                ProtocolDiagnostics.RaiseEvent(new ProtocolDiagnosticResourceEvent(
                                                   SourceRepository.PackageSource.Source,
                                                   ResourceTypeName,
                                                   ThisTypeName,
                                                   nameof(GetAllVersionsAsync),
                                                   stopwatch.Elapsed));
            }
        }
        /// <summary>
        /// Asynchronously gets dependency information for a specific package.
        /// </summary>
        /// <param name="id">A package id.</param>
        /// <param name="version">A package version.</param>
        /// <param name="cacheContext">A source cache context.</param>
        /// <param name="logger">A logger.</param>
        /// <param name="cancellationToken">A cancellation token.</param>
        /// <returns>A task that represents the asynchronous operation.
        /// The task result (<see cref="Task{TResult}.Result" />) returns an
        /// <see cref="IEnumerable{NuGetVersion}" />.</returns>
        /// <exception cref="ArgumentException">Thrown if <paramref name="id" />
        /// is either <c>null</c> or an empty string.</exception>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="version" /> <c>null</c>.</exception>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="cacheContext" /> <c>null</c>.</exception>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="logger" /> <c>null</c>.</exception>
        /// <exception cref="OperationCanceledException">Thrown if <paramref name="cancellationToken" />
        /// is cancelled.</exception>
        public override async Task <FindPackageByIdDependencyInfo> GetDependencyInfoAsync(
            string id,
            NuGetVersion version,
            SourceCacheContext cacheContext,
            ILogger logger,
            CancellationToken cancellationToken)
        {
            if (string.IsNullOrEmpty(id))
            {
                throw new ArgumentException(Strings.ArgumentCannotBeNullOrEmpty, nameof(id));
            }

            if (version == null)
            {
                throw new ArgumentNullException(nameof(version));
            }

            if (cacheContext == null)
            {
                throw new ArgumentNullException(nameof(cacheContext));
            }

            if (logger == null)
            {
                throw new ArgumentNullException(nameof(logger));
            }

            var stopwatch = Stopwatch.StartNew();

            try
            {
                cancellationToken.ThrowIfCancellationRequested();

                var packageInfos = await EnsurePackagesAsync(id, cacheContext, logger, cancellationToken);

                PackageInfo packageInfo;

                if (packageInfos.TryGetValue(version, out packageInfo))
                {
                    AddOrUpdateLogger(_plugin, logger);

                    await _utilities.DoOncePerPluginLifetimeAsync(
                        MessageMethod.SetLogLevel.ToString(),
                        () => SetLogLevelAsync(logger, cancellationToken),
                        cancellationToken);

                    var response = await _plugin.Connection.SendRequestAndReceiveResponseAsync <PrefetchPackageRequest, PrefetchPackageResponse>(
                        MessageMethod.PrefetchPackage,
                        new PrefetchPackageRequest(
                            _packageSource.Source,
                            packageInfo.Identity.Id,
                            packageInfo.Identity.Version.ToNormalizedString()),
                        cancellationToken);

                    if (response != null && response.ResponseCode == MessageResponseCode.Success)
                    {
                        using (var packageReader = new PluginPackageReader(_plugin, packageInfo.Identity, _packageSource.Source))
                        {
                            var nuspecReader = await packageReader.GetNuspecReaderAsync(cancellationToken);

                            return(GetDependencyInfo(nuspecReader));
                        }
                    }
                }

                return(null);
            }
            finally
            {
                ProtocolDiagnostics.RaiseEvent(new ProtocolDiagnosticResourceEvent(
                                                   _packageSource.Source,
                                                   ResourceTypeName,
                                                   ThisTypeName,
                                                   nameof(GetDependencyInfoAsync),
                                                   stopwatch.Elapsed));
            }
        }
Esempio n. 16
0
        public override async Task <DownloadResourceResult> GetDownloadResourceResultAsync(
            PackageIdentity identity,
            PackageDownloadContext downloadContext,
            string globalPackagesFolder,
            ILogger logger,
            CancellationToken token)
        {
            if (identity == null)
            {
                throw new ArgumentNullException(nameof(identity));
            }

            if (downloadContext == null)
            {
                throw new ArgumentNullException(nameof(downloadContext));
            }

            if (logger == null)
            {
                throw new ArgumentNullException(nameof(logger));
            }

            var stopwatch = Stopwatch.StartNew();

            try
            {
                token.ThrowIfCancellationRequested();

                var  sourcePackage = identity as SourcePackageDependencyInfo;
                bool isFromUri     = sourcePackage?.PackageHash != null &&
                                     sourcePackage?.DownloadUri != null;

                try
                {
                    if (isFromUri)
                    {
                        // If this is a SourcePackageDependencyInfo object with everything populated
                        // and it is from an online source, use the machine cache and download it using the
                        // given url.
                        return(await _feedParser.DownloadFromUrl(
                                   sourcePackage,
                                   sourcePackage.DownloadUri,
                                   downloadContext,
                                   globalPackagesFolder,
                                   logger,
                                   token));
                    }
                    else
                    {
                        using (var sourceCacheContext = new SourceCacheContext())
                        {
                            // Look up the package from the id and version and download it.
                            return(await _feedParser.DownloadFromIdentity(
                                       identity,
                                       downloadContext,
                                       globalPackagesFolder,
                                       sourceCacheContext,
                                       logger,
                                       token));
                        }
                    }
                }
                catch (OperationCanceledException)
                {
                    return(new DownloadResourceResult(DownloadResourceResultStatus.Cancelled));
                }
                catch (Exception ex) when(!(ex is FatalProtocolException))
                {
                    string message = string.Format(CultureInfo.CurrentCulture, Strings.Log_ErrorDownloading, identity, _feedParser.Source);

                    throw new FatalProtocolException(message, ex);
                }
            }
            finally
            {
                ProtocolDiagnostics.RaiseEvent(new ProtocolDiagnosticResourceEvent(
                                                   _source,
                                                   resourceType: nameof(DownloadResource),
                                                   type: nameof(DownloadResourceV2Feed),
                                                   method: nameof(GetDownloadResourceResultAsync),
                                                   stopwatch.Elapsed));
            }
        }
        /// <summary>
        /// Asynchronously copies a .nupkg to a stream.
        /// </summary>
        /// <param name="id">A package ID.</param>
        /// <param name="version">A package version.</param>
        /// <param name="destination">A destination stream.</param>
        /// <param name="cacheContext">A source cache context.</param>
        /// <param name="logger">A logger.</param>
        /// <param name="cancellationToken">A cancellation token.</param>
        /// <returns>A task that represents the asynchronous operation.
        /// The task result (<see cref="Task{TResult}.Result" />) returns an
        /// <see cref="bool" /> indicating whether or not the .nupkg file was copied.</returns>
        /// <exception cref="ArgumentException">Thrown if <paramref name="id" />
        /// is either <c>null</c> or an empty string.</exception>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="version" /> <c>null</c>.</exception>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="destination" /> <c>null</c>.</exception>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="cacheContext" /> <c>null</c>.</exception>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="logger" /> <c>null</c>.</exception>
        /// <exception cref="OperationCanceledException">Thrown if <paramref name="cancellationToken" />
        /// is cancelled.</exception>
        public override async Task <bool> CopyNupkgToStreamAsync(
            string id,
            NuGetVersion version,
            Stream destination,
            SourceCacheContext cacheContext,
            ILogger logger,
            CancellationToken cancellationToken)
        {
            if (string.IsNullOrEmpty(id))
            {
                throw new ArgumentException(Strings.ArgumentCannotBeNullOrEmpty, nameof(id));
            }

            if (version == null)
            {
                throw new ArgumentNullException(nameof(version));
            }

            if (destination == null)
            {
                throw new ArgumentNullException(nameof(destination));
            }

            if (cacheContext == null)
            {
                throw new ArgumentNullException(nameof(cacheContext));
            }

            if (logger == null)
            {
                throw new ArgumentNullException(nameof(logger));
            }

            var stopwatch = Stopwatch.StartNew();

            try
            {
                cancellationToken.ThrowIfCancellationRequested();

                var packageInfo = await GetPackageInfoAsync(id, version, cacheContext, logger, cancellationToken);

                if (packageInfo == null)
                {
                    return(false);
                }

                return(await _nupkgDownloader.CopyNupkgToStreamAsync(
                           packageInfo.Identity,
                           packageInfo.ContentUri,
                           destination,
                           cacheContext,
                           logger,
                           cancellationToken));
            }
            finally
            {
                ProtocolDiagnostics.RaiseEvent(new ProtocolDiagnosticResourceEvent(
                                                   SourceRepository.PackageSource.Source,
                                                   ResourceTypeName,
                                                   ThisTypeName,
                                                   nameof(CopyNupkgToStreamAsync),
                                                   stopwatch.Elapsed));
            }
        }
Esempio n. 18
0
        /// <summary>
        /// Make an HTTP request while retrying after failed attempts or timeouts.
        /// </summary>
        /// <remarks>
        /// This method accepts a factory to create instances of the <see cref="HttpRequestMessage"/> because
        /// requests cannot always be used. For example, suppose the request is a POST and contains content
        /// of a stream that can only be consumed once.
        /// </remarks>
        public async Task <HttpResponseMessage> SendAsync(
            HttpRetryHandlerRequest request,
            string source,
            ILogger log,
            CancellationToken cancellationToken)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }

            // If specified via environment, override the default retry delay with the values provided
            if (_enhancedHttpRetryHelper.IsEnabled)
            {
                request.RetryDelay = TimeSpan.FromMilliseconds(_enhancedHttpRetryHelper.DelayInMilliseconds);
            }

            var tries = 0;
            HttpResponseMessage response = null;
            var success = false;

            while (tries < request.MaxTries && !success)
            {
                // There are many places where another variable named "MaxTries" is set to 1,
                // so the Delay() never actually occurs.
                // When opted in to "enhanced retry", do the delay and have it increase exponentially where applicable
                // (i.e. when "tries" is allowed to be > 1)
                if (tries > 0 || (_enhancedHttpRetryHelper.IsEnabled && request.IsRetry))
                {
                    // "Enhanced" retry: In the case where this is actually a 2nd-Nth try, back off exponentially with some random.
                    // In many cases due to the external retry loop, this will be always be 1 * request.RetryDelay.TotalMilliseconds + 0-200 ms
                    if (_enhancedHttpRetryHelper.IsEnabled)
                    {
                        if (tries >= 3 || (tries == 0 && request.IsRetry))
                        {
                            log.LogVerbose("Enhanced retry: HttpRetryHandler is in a state that retry would have been abandoned or not waited if it were not enabled.");
                        }
                        await Task.Delay(TimeSpan.FromMilliseconds((Math.Pow(2, tries) * request.RetryDelay.TotalMilliseconds) + new Random().Next(200)), cancellationToken);
                    }
                    // Old behavior; always delay a constant amount
                    else
                    {
                        await Task.Delay(request.RetryDelay, cancellationToken);
                    }
                }

                tries++;
                success = true;

                using (var requestMessage = request.RequestFactory())
                {
                    var stopwatches   = new List <Stopwatch>(2);
                    var bodyStopwatch = new Stopwatch();
                    stopwatches.Add(bodyStopwatch);
                    Stopwatch headerStopwatch = null;
                    if (request.CompletionOption == HttpCompletionOption.ResponseHeadersRead)
                    {
                        headerStopwatch = new Stopwatch();
                        stopwatches.Add(headerStopwatch);
                    }
#if NET5_0
                    requestMessage.Options.Set(new HttpRequestOptionsKey <List <Stopwatch> >(StopwatchPropertyName), stopwatches);
#else
                    requestMessage.Properties[StopwatchPropertyName] = stopwatches;
#endif
                    var requestUri = requestMessage.RequestUri;

                    try
                    {
                        // The only time that we will be disposing this existing response is if we have
                        // successfully fetched an HTTP response but the response has an status code indicating
                        // failure (i.e. HTTP status code >= 500).
                        //
                        // If we don't even get an HTTP response message because an exception is thrown, then there
                        // is no response instance to dispose. Additionally, we cannot use a finally here because
                        // the caller needs the response instance returned in a non-disposed state.
                        //
                        // Also, remember that if an HTTP server continuously returns a failure status code (like
                        // 500 Internal Server Error), we will retry some number of times but eventually return the
                        // response as-is, expecting the caller to check the status code as well. This results in the
                        // success variable being set to false but the response being returned to the caller without
                        // disposing it.
                        response?.Dispose();

                        // Add common headers to the request after it is created by the factory. This includes
                        // X-NuGet-Session-Id which is added to all nuget requests.
                        foreach (var header in request.AddHeaders)
                        {
                            requestMessage.Headers.TryAddWithoutValidation(header.Key, header.Value);
                        }

                        log.LogInformation("  " + string.Format(
                                               CultureInfo.InvariantCulture,
                                               Strings.Http_RequestLog,
                                               requestMessage.Method,
                                               requestUri));

                        // Issue the request.
                        var timeoutMessage = string.Format(
                            CultureInfo.CurrentCulture,
                            Strings.Http_Timeout,
                            requestMessage.Method,
                            requestUri,
                            (int)request.RequestTimeout.TotalMilliseconds);
                        response = await TimeoutUtility.StartWithTimeout(
                            async timeoutToken =>
                        {
                            bodyStopwatch.Start();
                            headerStopwatch?.Start();
                            var responseMessage = await request.HttpClient.SendAsync(requestMessage, request.CompletionOption, timeoutToken);
                            headerStopwatch?.Stop();
                            return(responseMessage);
                        },
                            request.RequestTimeout,
                            timeoutMessage,
                            cancellationToken);

                        // Wrap the response stream so that the download can timeout.
                        if (response.Content != null)
                        {
                            var networkStream = await response.Content.ReadAsStreamAsync();

                            var timeoutStream   = new DownloadTimeoutStream(requestUri.ToString(), networkStream, request.DownloadTimeout);
                            var inProgressEvent = new ProtocolDiagnosticInProgressHttpEvent(
                                source,
                                requestUri,
                                headerStopwatch?.Elapsed,
                                (int)response.StatusCode,
                                isRetry: request.IsRetry || tries > 1,
                                isCancelled: false,
                                isLastAttempt: tries == request.MaxTries && request.IsLastAttempt);
                            var diagnosticsStream = new ProtocolDiagnosticsStream(timeoutStream, inProgressEvent, bodyStopwatch, ProtocolDiagnostics.RaiseEvent);

                            var newContent = new StreamContent(diagnosticsStream);

                            // Copy over the content headers since we are replacing the HttpContent instance associated
                            // with the response message.
                            foreach (var header in response.Content.Headers)
                            {
                                newContent.Headers.TryAddWithoutValidation(header.Key, header.Value);
                            }

                            response.Content = newContent;
                        }

                        log.LogInformation("  " + string.Format(
                                               CultureInfo.InvariantCulture,
                                               Strings.Http_ResponseLog,
                                               response.StatusCode,
                                               requestUri,
                                               bodyStopwatch.ElapsedMilliseconds));

                        if ((int)response.StatusCode >= 500)
                        {
                            success = false;
                        }
                    }
                    catch (OperationCanceledException)
                    {
                        response?.Dispose();

                        ProtocolDiagnostics.RaiseEvent(new ProtocolDiagnosticHttpEvent(
                                                           timestamp: DateTime.UtcNow,
                                                           source,
                                                           requestUri,
                                                           headerDuration: null,
                                                           eventDuration: bodyStopwatch.Elapsed,
                                                           httpStatusCode: null,
                                                           bytes: 0,
                                                           isSuccess: false,
                                                           isRetry: request.IsRetry || tries > 1,
                                                           isCancelled: true,
                                                           isLastAttempt: tries == request.MaxTries && request.IsLastAttempt));

                        throw;
                    }
                    catch (Exception e)
                    {
                        success = false;

                        response?.Dispose();

                        ProtocolDiagnostics.RaiseEvent(new ProtocolDiagnosticHttpEvent(
                                                           timestamp: DateTime.UtcNow,
                                                           source,
                                                           requestUri,
                                                           headerDuration: null,
                                                           eventDuration: bodyStopwatch.Elapsed,
                                                           httpStatusCode: null,
                                                           bytes: 0,
                                                           isSuccess: false,
                                                           isRetry: request.IsRetry || tries > 1,
                                                           isCancelled: false,
                                                           isLastAttempt: tries == request.MaxTries && request.IsLastAttempt));

                        if (tries >= request.MaxTries)
                        {
                            throw;
                        }

                        log.LogInformation(string.Format(
                                               CultureInfo.CurrentCulture,
                                               Strings.Log_RetryingHttp,
                                               requestMessage.Method,
                                               requestUri,
                                               requestMessage)
                                           + Environment.NewLine
                                           + ExceptionUtilities.DisplayMessage(e));
                    }
                }
            }

            return(response);
        }
Esempio n. 19
0
        /// <summary>
        /// Make an HTTP request while retrying after failed attempts or timeouts.
        /// </summary>
        /// <remarks>
        /// This method accepts a factory to create instances of the <see cref="HttpRequestMessage"/> because
        /// requests cannot always be used. For example, suppose the request is a POST and contains content
        /// of a stream that can only be consumed once.
        /// </remarks>
        public async Task <HttpResponseMessage> SendAsync(
            HttpRetryHandlerRequest request,
            string source,
            ILogger log,
            CancellationToken cancellationToken)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }

            var tries = 0;
            HttpResponseMessage response = null;
            var success = false;

            while (tries < request.MaxTries && !success)
            {
                if (tries > 0)
                {
                    await Task.Delay(request.RetryDelay, cancellationToken);
                }

                tries++;
                success = true;

                using (var requestMessage = request.RequestFactory())
                {
                    var stopwatches   = new List <Stopwatch>(2);
                    var bodyStopwatch = new Stopwatch();
                    stopwatches.Add(bodyStopwatch);
                    Stopwatch headerStopwatch = null;
                    if (request.CompletionOption == HttpCompletionOption.ResponseHeadersRead)
                    {
                        headerStopwatch = new Stopwatch();
                        stopwatches.Add(headerStopwatch);
                    }
                    requestMessage.Properties[StopwatchPropertyName] = stopwatches;
                    var requestUri = requestMessage.RequestUri;

                    try
                    {
                        // The only time that we will be disposing this existing response is if we have
                        // successfully fetched an HTTP response but the response has an status code indicating
                        // failure (i.e. HTTP status code >= 500).
                        //
                        // If we don't even get an HTTP response message because an exception is thrown, then there
                        // is no response instance to dispose. Additionally, we cannot use a finally here because
                        // the caller needs the response instance returned in a non-disposed state.
                        //
                        // Also, remember that if an HTTP server continuously returns a failure status code (like
                        // 500 Internal Server Error), we will retry some number of times but eventually return the
                        // response as-is, expecting the caller to check the status code as well. This results in the
                        // success variable being set to false but the response being returned to the caller without
                        // disposing it.
                        response?.Dispose();

                        // Add common headers to the request after it is created by the factory. This includes
                        // X-NuGet-Session-Id which is added to all nuget requests.
                        foreach (var header in request.AddHeaders)
                        {
                            requestMessage.Headers.TryAddWithoutValidation(header.Key, header.Value);
                        }

                        log.LogInformation("  " + string.Format(
                                               CultureInfo.InvariantCulture,
                                               Strings.Http_RequestLog,
                                               requestMessage.Method,
                                               requestUri));

                        // Issue the request.
                        var timeoutMessage = string.Format(
                            CultureInfo.CurrentCulture,
                            Strings.Http_Timeout,
                            requestMessage.Method,
                            requestUri,
                            (int)request.RequestTimeout.TotalMilliseconds);
                        response = await TimeoutUtility.StartWithTimeout(
                            async timeoutToken =>
                        {
                            bodyStopwatch.Start();
                            headerStopwatch?.Start();
                            var responseMessage = await request.HttpClient.SendAsync(requestMessage, request.CompletionOption, timeoutToken);
                            headerStopwatch?.Stop();
                            return(responseMessage);
                        },
                            request.RequestTimeout,
                            timeoutMessage,
                            cancellationToken);

                        // Wrap the response stream so that the download can timeout.
                        if (response.Content != null)
                        {
                            var networkStream = await response.Content.ReadAsStreamAsync();

                            var timeoutStream   = new DownloadTimeoutStream(requestUri.ToString(), networkStream, request.DownloadTimeout);
                            var inProgressEvent = new ProtocolDiagnosticInProgressHttpEvent(
                                source,
                                requestUri,
                                headerStopwatch?.Elapsed,
                                (int)response.StatusCode,
                                isRetry: request.IsRetry || tries > 1,
                                isCancelled: false,
                                isLastAttempt: tries == request.MaxTries && request.IsLastAttempt);
                            var diagnosticsStream = new ProtocolDiagnosticsStream(timeoutStream, inProgressEvent, bodyStopwatch, ProtocolDiagnostics.RaiseEvent);

                            var newContent = new StreamContent(diagnosticsStream);

                            // Copy over the content headers since we are replacing the HttpContent instance associated
                            // with the response message.
                            foreach (var header in response.Content.Headers)
                            {
                                newContent.Headers.TryAddWithoutValidation(header.Key, header.Value);
                            }

                            response.Content = newContent;
                        }

                        log.LogInformation("  " + string.Format(
                                               CultureInfo.InvariantCulture,
                                               Strings.Http_ResponseLog,
                                               response.StatusCode,
                                               requestUri,
                                               bodyStopwatch.ElapsedMilliseconds));

                        if ((int)response.StatusCode >= 500)
                        {
                            success = false;
                        }
                    }
                    catch (OperationCanceledException)
                    {
                        response?.Dispose();

                        ProtocolDiagnostics.RaiseEvent(new ProtocolDiagnosticHttpEvent(
                                                           timestamp: DateTime.UtcNow,
                                                           source,
                                                           requestUri,
                                                           headerDuration: null,
                                                           eventDuration: bodyStopwatch.Elapsed,
                                                           httpStatusCode: null,
                                                           bytes: 0,
                                                           isSuccess: false,
                                                           isRetry: request.IsRetry || tries > 1,
                                                           isCancelled: true,
                                                           isLastAttempt: tries == request.MaxTries && request.IsLastAttempt));

                        throw;
                    }
                    catch (Exception e)
                    {
                        success = false;

                        response?.Dispose();

                        ProtocolDiagnostics.RaiseEvent(new ProtocolDiagnosticHttpEvent(
                                                           timestamp: DateTime.UtcNow,
                                                           source,
                                                           requestUri,
                                                           headerDuration: null,
                                                           eventDuration: bodyStopwatch.Elapsed,
                                                           httpStatusCode: null,
                                                           bytes: 0,
                                                           isSuccess: false,
                                                           isRetry: request.IsRetry || tries > 1,
                                                           isCancelled: false,
                                                           isLastAttempt: tries == request.MaxTries && request.IsLastAttempt));

                        if (tries >= request.MaxTries)
                        {
                            throw;
                        }

                        log.LogInformation(string.Format(
                                               CultureInfo.CurrentCulture,
                                               Strings.Log_RetryingHttp,
                                               requestMessage.Method,
                                               requestUri,
                                               requestMessage)
                                           + Environment.NewLine
                                           + ExceptionUtilities.DisplayMessage(e));
                    }
                }
            }

            return(response);
        }
Esempio n. 20
0
        /// <summary>
        /// Asynchronously copies a .nupkg to a stream.
        /// </summary>
        /// <param name="id">A package ID.</param>
        /// <param name="version">A package version.</param>
        /// <param name="destination">A destination stream.</param>
        /// <param name="cacheContext">A source cache context.</param>
        /// <param name="logger">A logger.</param>
        /// <param name="cancellationToken">A cancellation token.</param>
        /// <returns>A task that represents the asynchronous operation.
        /// The task result (<see cref="Task{TResult}.Result" />) returns an
        /// <see cref="bool" /> indicating whether or not the .nupkg file was copied.</returns>
        /// <exception cref="ArgumentException">Thrown if <paramref name="id" />
        /// is either <c>null</c> or an empty string.</exception>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="version" /> <c>null</c>.</exception>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="destination" /> <c>null</c>.</exception>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="cacheContext" /> <c>null</c>.</exception>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="logger" /> <c>null</c>.</exception>
        /// <exception cref="OperationCanceledException">Thrown if <paramref name="cancellationToken" />
        /// is cancelled.</exception>
        public override async Task <bool> CopyNupkgToStreamAsync(
            string id,
            NuGetVersion version,
            Stream destination,
            SourceCacheContext cacheContext,
            ILogger logger,
            CancellationToken cancellationToken)
        {
            if (string.IsNullOrEmpty(id))
            {
                throw new ArgumentException(Strings.ArgumentCannotBeNullOrEmpty, nameof(id));
            }

            if (version == null)
            {
                throw new ArgumentNullException(nameof(version));
            }

            if (destination == null)
            {
                throw new ArgumentNullException(nameof(destination));
            }

            if (cacheContext == null)
            {
                throw new ArgumentNullException(nameof(cacheContext));
            }

            if (logger == null)
            {
                throw new ArgumentNullException(nameof(logger));
            }

            var stopwatch = Stopwatch.StartNew();

            try
            {
                cancellationToken.ThrowIfCancellationRequested();

                var info = GetPackageInfo(id, version, cacheContext, logger);

                if (info != null)
                {
                    using (var fileStream = File.OpenRead(info.Path))
                    {
                        await fileStream.CopyToAsync(destination, cancellationToken);

                        ProtocolDiagnostics.RaiseEvent(new ProtocolDiagnosticNupkgCopiedEvent(_source, destination.Length));
                        return(true);
                    }
                }

                return(false);
            }
            finally
            {
                ProtocolDiagnostics.RaiseEvent(new ProtocolDiagnosticResourceEvent(
                                                   _source,
                                                   ResourceTypeName,
                                                   ThisTypeName,
                                                   nameof(CopyNupkgToStreamAsync),
                                                   stopwatch.Elapsed));
            }
        }
        /// <summary>
        /// Asynchronously downloads a package.
        /// </summary>
        /// <param name="identity">The package identity.</param>
        /// <param name="downloadContext">A package download context.</param>
        /// <param name="globalPackagesFolder">The path to the global packages folder.</param>
        /// <param name="logger">A logger.</param>
        /// <param name="cancellationToken">A cancellation token.</param>
        /// <returns>A task that represents the asynchronous operation.
        /// The task result (<see cref="Task{TResult}.Result" />) returns
        /// a <see cref="DownloadResourceResult" />.</returns>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="identity" /> is <c>null</c>.</exception>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="downloadContext" />
        /// is <c>null</c>.</exception>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="logger" /> is <c>null</c>.</exception>
        /// <exception cref="OperationCanceledException">Thrown if <paramref name="cancellationToken" />
        /// is cancelled.</exception>
        public async override Task <DownloadResourceResult> GetDownloadResourceResultAsync(
            PackageIdentity identity,
            PackageDownloadContext downloadContext,
            string globalPackagesFolder,
            ILogger logger,
            CancellationToken cancellationToken)
        {
            if (identity == null)
            {
                throw new ArgumentNullException(nameof(identity));
            }

            if (downloadContext == null)
            {
                throw new ArgumentNullException(nameof(downloadContext));
            }

            if (logger == null)
            {
                throw new ArgumentNullException(nameof(logger));
            }

            var stopwatch = Stopwatch.StartNew();

            try
            {
                cancellationToken.ThrowIfCancellationRequested();

                AddOrUpdateLogger(_plugin, logger);

                await _utilities.DoOncePerPluginLifetimeAsync(
                    MessageMethod.SetLogLevel.ToString(),
                    () => SetLogLevelAsync(logger, cancellationToken),
                    cancellationToken);

                var response = await _plugin.Connection.SendRequestAndReceiveResponseAsync <PrefetchPackageRequest, PrefetchPackageResponse>(
                    MessageMethod.PrefetchPackage,
                    new PrefetchPackageRequest(_packageSource.Source, identity.Id, identity.Version.ToNormalizedString()),
                    cancellationToken);

                if (response != null)
                {
                    if (response.ResponseCode == MessageResponseCode.Success)
                    {
                        var packageReader = new PluginPackageReader(_plugin, identity, _packageSource.Source);

                        return(new DownloadResourceResult(packageReader, _packageSource.Source));
                    }

                    if (response.ResponseCode == MessageResponseCode.NotFound)
                    {
                        return(new DownloadResourceResult(DownloadResourceResultStatus.NotFound));
                    }
                }

                throw new PluginException(
                          string.Format(CultureInfo.CurrentCulture,
                                        Strings.Plugin_PackageDownloadFailed,
                                        _plugin.Name,
                                        $"{identity.Id}.{identity.Version.ToNormalizedString()}"));
            }
            finally
            {
                ProtocolDiagnostics.RaiseEvent(new ProtocolDiagnosticResourceEvent(
                                                   _packageSource.Source,
                                                   resourceType: nameof(DownloadResource),
                                                   type: nameof(DownloadResourcePlugin),
                                                   method: nameof(GetDownloadResourceResultAsync),
                                                   duration: stopwatch.Elapsed));
            }
        }