/// <summary> /// Caching Get request. /// </summary> public async Task <T> GetAsync <T>( HttpSourceCachedRequest request, Func <HttpSourceResult, Task <T> > processAsync, ILogger log, CancellationToken token) { var cacheResult = HttpCacheUtility.InitializeHttpCacheResult( HttpCacheDirectory, _sourceUri, request.CacheKey, request.CacheContext); return(await ConcurrencyUtilities.ExecuteWithFileLockedAsync( cacheResult.CacheFile, action : async lockedToken => { cacheResult.Stream = TryReadCacheFile(request.Uri, cacheResult.MaxAge, cacheResult.CacheFile); if (cacheResult.Stream != null) { log.LogInformation(string.Format(CultureInfo.InvariantCulture, " " + Strings.Http_RequestLog, "CACHE", request.Uri)); // Validate the content fetched from the cache. try { request.EnsureValidContents?.Invoke(cacheResult.Stream); cacheResult.Stream.Seek(0, SeekOrigin.Begin); var httpSourceResult = new HttpSourceResult( HttpSourceResultStatus.OpenedFromDisk, cacheResult.CacheFile, cacheResult.Stream); return await processAsync(httpSourceResult); } catch (Exception e) { cacheResult.Stream.Dispose(); cacheResult.Stream = null; string message = string.Format(CultureInfo.CurrentCulture, Strings.Log_InvalidCacheEntry, request.Uri) + Environment.NewLine + ExceptionUtilities.DisplayMessage(e); log.LogWarning(message); } } Func <HttpRequestMessage> requestFactory = () => { var requestMessage = HttpRequestMessageFactory.Create(HttpMethod.Get, request.Uri, log); foreach (var acceptHeaderValue in request.AcceptHeaderValues) { requestMessage.Headers.Accept.Add(acceptHeaderValue); } return requestMessage; }; Func <Task <ThrottledResponse> > throttledResponseFactory = () => GetThrottledResponse( requestFactory, request.RequestTimeout, request.DownloadTimeout, request.MaxTries, log, lockedToken); using (var throttledResponse = await throttledResponseFactory()) { if (request.IgnoreNotFounds && throttledResponse.Response.StatusCode == HttpStatusCode.NotFound) { var httpSourceResult = new HttpSourceResult(HttpSourceResultStatus.NotFound); return await processAsync(httpSourceResult); } if (throttledResponse.Response.StatusCode == HttpStatusCode.NoContent) { // Ignore reading and caching the empty stream. var httpSourceResult = new HttpSourceResult(HttpSourceResultStatus.NoContent); return await processAsync(httpSourceResult); } throttledResponse.Response.EnsureSuccessStatusCode(); if (!request.CacheContext.DirectDownload) { await HttpCacheUtility.CreateCacheFileAsync( cacheResult, throttledResponse.Response, request.EnsureValidContents, lockedToken); using (var httpSourceResult = new HttpSourceResult( HttpSourceResultStatus.OpenedFromDisk, cacheResult.CacheFile, cacheResult.Stream)) { return await processAsync(httpSourceResult); } } else { // Note that we do not execute the content validator on the response stream when skipping // the cache. We cannot seek on the network stream and it is not valuable to download the // content twice just to validate the first time (considering that the second download could // be different from the first thus rendering the first validation meaningless). using (var stream = await throttledResponse.Response.Content.ReadAsStreamAsync()) using (var httpSourceResult = new HttpSourceResult( HttpSourceResultStatus.OpenedFromNetwork, cacheFileName: null, stream: stream)) { return await processAsync(httpSourceResult); } } } }, token : token)); }
/// <summary> /// Caching Get request. /// </summary> public async Task <HttpSourceResult> GetAsync( HttpSourceCachedRequest request, ILogger log, CancellationToken token) { var result = HttpCacheUtility.InitializeHttpCacheResult( HttpCacheDirectory, _baseUri, request.CacheKey, request.CacheContext); return(await ConcurrencyUtilities.ExecuteWithFileLockedAsync( result.CacheFile, action : async lockedToken => { result.Stream = TryReadCacheFile(request.Uri, result.MaxAge, result.CacheFile); if (result.Stream != null) { log.LogInformation(string.Format(CultureInfo.InvariantCulture, " " + Strings.Http_RequestLog, "CACHE", request.Uri)); // Validate the content fetched from the cache. try { request.EnsureValidContents?.Invoke(result.Stream); result.Stream.Seek(0, SeekOrigin.Begin); return new HttpSourceResult( HttpSourceResultStatus.OpenedFromDisk, result.CacheFile, result.Stream); } catch (Exception e) { result.Stream.Dispose(); result.Stream = null; string message = string.Format(CultureInfo.CurrentCulture, Strings.Log_InvalidCacheEntry, request.Uri) + Environment.NewLine + ExceptionUtilities.DisplayMessage(e); log.LogWarning(message); } } Func <HttpRequestMessage> requestFactory = () => { var requestMessage = HttpRequestMessageFactory.Create(HttpMethod.Get, request.Uri, log); foreach (var acceptHeaderValue in request.AcceptHeaderValues) { requestMessage.Headers.Accept.Add(acceptHeaderValue); } return requestMessage; }; Func <Task <ThrottledResponse> > throttledResponseFactory = () => GetThrottledResponse( requestFactory, request.RequestTimeout, request.DownloadTimeout, log, lockedToken); using (var throttledResponse = await throttledResponseFactory()) { if (request.IgnoreNotFounds && throttledResponse.Response.StatusCode == HttpStatusCode.NotFound) { return new HttpSourceResult(HttpSourceResultStatus.NotFound); } if (throttledResponse.Response.StatusCode == HttpStatusCode.NoContent) { // Ignore reading and caching the empty stream. return new HttpSourceResult(HttpSourceResultStatus.NoContent); } throttledResponse.Response.EnsureSuccessStatusCode(); await HttpCacheUtility.CreateCacheFileAsync( result, request.Uri, throttledResponse.Response, request.CacheContext, request.EnsureValidContents, lockedToken); return new HttpSourceResult( HttpSourceResultStatus.OpenedFromDisk, result.CacheFile, result.Stream); } }, token : token)); }