private async Task <IEnumerable <PackageInfo> > FindPackagesByIdAsyncCore(string id, CancellationToken cancellationToken) { for (var retry = 0; retry != 3; ++retry) { var uri = _baseUri + "FindPackagesById()?id='" + id + "'"; try { var results = new List <PackageInfo>(); var page = 1; while (true) { // 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. using (var data = await _httpSource.GetAsync( uri, new[] { new MediaTypeWithQualityHeaderValue("application/atom+xml"), new MediaTypeWithQualityHeaderValue("application/xml") }, $"list_{id}_page{page}", CreateCacheContext(retry), Logger, ignoreNotFounds: false, ensureValidContents: stream => HttpStreamValidation.ValidateXml(uri, stream), cancellationToken: cancellationToken)) { if (data.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. break; } var doc = V2FeedParser.LoadXml(data.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)) { break; } uri = nextUri; page++; } } return(results); } catch (Exception ex) when(retry < 2) { string 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) { // Fail silently by returning empty result list var message = string.Format(CultureInfo.CurrentCulture, Strings.Log_FailedToRetrievePackage, uri); Logger.LogError(message + Environment.NewLine + ex.Message); throw new FatalProtocolException(message, ex); } } return(null); }