private async Task <IEnumerable <PackageInfo> > FindPackagesByIdAsyncCore(
            string id,
            SourceCacheContext cacheContext,
            ILogger logger,
            CancellationToken cancellationToken)
        {
            for (var retry = 0; retry < 3; ++retry)
            {
                var uri = _baseUri + "FindPackagesById()?id='" + id + "'";
                var httpSourceCacheContext = HttpSourceCacheContext.Create(cacheContext, retry);

                try
                {
                    var results = new List <PackageInfo>();
                    var uris    = new HashSet <string>(StringComparer.OrdinalIgnoreCase);

                    uris.Add(uri);
                    var page   = 1;
                    var paging = true;
                    while (paging)
                    {
                        // TODO: Pages for a package ID are cached separately.
                        // So we will get inaccurate data when a page shrinks.
                        // However, (1) In most cases the pages grow rather than shrink;
                        // (2) cache for pages is valid for only 30 min.
                        // So we decide to leave current logic and observe.
                        paging = await _httpSource.GetAsync(
                            new HttpSourceCachedRequest(
                                uri,
                                $"list_{id.ToLowerInvariant()}_page{page}",
                                httpSourceCacheContext)
                        {
                            AcceptHeaderValues =
                            {
                                new MediaTypeWithQualityHeaderValue("application/atom+xml"),
                                new MediaTypeWithQualityHeaderValue("application/xml")
                            },
                            EnsureValidContents = stream => HttpStreamValidation.ValidateXml(uri, stream),
                            MaxTries            = 1
                        },
                            async httpSourceResult =>
                        {
                            if (httpSourceResult.Status == HttpSourceResultStatus.NoContent)
                            {
                                // Team city returns 204 when no versions of the package exist
                                // This should result in an empty list and we should not try to
                                // read the stream as xml.
                                return(false);
                            }

                            var doc = await V2FeedParser.LoadXmlAsync(httpSourceResult.Stream);

                            var result = doc.Root
                                         .Elements(_xnameEntry)
                                         .Select(x => BuildModel(id, x))
                                         .Where(x => x != null);

                            results.AddRange(result);

                            // Find the next url for continuation
                            var nextUri = V2FeedParser.GetNextUrl(doc);

                            // Stop if there's nothing else to GET
                            if (string.IsNullOrEmpty(nextUri))
                            {
                                return(false);
                            }

                            // check for any duplicate url and error out
                            if (!uris.Add(nextUri))
                            {
                                throw new FatalProtocolException(string.Format(
                                                                     CultureInfo.CurrentCulture,
                                                                     Strings.Protocol_duplicateUri,
                                                                     nextUri));
                            }

                            uri = nextUri;
                            page++;

                            return(true);
                        },
                            logger,
                            cancellationToken);
                    }

                    return(results);
                }
                catch (Exception ex) when(retry < 2)
                {
                    var message = string.Format(CultureInfo.CurrentCulture, Strings.Log_RetryingFindPackagesById, nameof(FindPackagesByIdAsyncCore), uri)
                                  + Environment.NewLine
                                  + ExceptionUtilities.DisplayMessage(ex);

                    logger.LogMinimal(message);
                }
                catch (Exception ex) when(retry == 2)
                {
                    var message = string.Format(
                        CultureInfo.CurrentCulture,
                        Strings.Log_FailedToRetrievePackage,
                        id,
                        uri);

                    throw new FatalProtocolException(message, ex);
                }
            }

            return(null);
        }
예제 #2
0
        internal async Task <XDocument> LoadXmlAsync(
            string uri,
            string cacheKey,
            bool ignoreNotFounds,
            SourceCacheContext sourceCacheContext,
            ILogger log,
            CancellationToken token)
        {
            if (cacheKey != null && sourceCacheContext != null)
            {
                var httpSourceCacheContext = HttpSourceCacheContext.Create(sourceCacheContext, 0);

                try
                {
                    return(await _httpSource.GetAsync(
                               new HttpSourceCachedRequest(
                                   uri,
                                   cacheKey,
                                   httpSourceCacheContext)
                    {
                        AcceptHeaderValues =
                        {
                            new MediaTypeWithQualityHeaderValue("application/atom+xml"),
                            new MediaTypeWithQualityHeaderValue("application/xml")
                        },
                        EnsureValidContents = stream => HttpStreamValidation.ValidateXml(uri, stream),
                        MaxTries = 1,
                        IgnoreNotFounds = ignoreNotFounds
                    },
                               async response =>
                    {
                        if (ignoreNotFounds && response.Status == HttpSourceResultStatus.NotFound)
                        {
                            // Treat "404 Not Found" as an empty response.
                            return null;
                        }
                        else if (response.Status == HttpSourceResultStatus.NoContent)
                        {
                            // Always treat "204 No Content" as exactly that.
                            return null;
                        }
                        else
                        {
                            return await LoadXmlAsync(response.Stream, token);
                        }
                    },
                               log,
                               token));
                }
                catch (Exception ex)
                {
                    var message = string.Format(
                        CultureInfo.CurrentCulture,
                        Strings.Log_FailedToFetchV2FeedHttp,
                        uri,
                        ex.Message);

                    throw new FatalProtocolException(message, ex);
                }
            }
            else
            {
                // return results without httpCache
                return(await _httpSource.ProcessResponseAsync(
                           new HttpSourceRequest(
                               () =>
                {
                    var request = HttpRequestMessageFactory.Create(HttpMethod.Get, uri, log);
                    request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/atom+xml"));
                    request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));
                    return request;
                })
                {
                    IsRetry = true
                },
                           async response =>
                {
                    if (response.StatusCode == HttpStatusCode.OK)
                    {
                        var networkStream = await response.Content.ReadAsStreamAsync();
                        return await LoadXmlAsync(networkStream, token);
                    }
                    else if (ignoreNotFounds && response.StatusCode == HttpStatusCode.NotFound)
                    {
                        // Treat "404 Not Found" as an empty response.
                        return null;
                    }
                    else if (response.StatusCode == HttpStatusCode.NoContent)
                    {
                        // Always treat "204 No Content" as exactly that.
                        return null;
                    }
                    else
                    {
                        throw new FatalProtocolException(string.Format(
                                                             CultureInfo.CurrentCulture,
                                                             Strings.Log_FailedToFetchV2Feed,
                                                             uri,
                                                             (int)response.StatusCode,
                                                             response.ReasonPhrase));
                    }
                },
                           sourceCacheContext,
                           log,
                           token));
            }
        }