protected override async Task <HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { using var act = source.StartActivity("cache"); act?.SetParentId(Tracer.CurrentSpan.Context.TraceId, Tracer.CurrentSpan.Context.SpanId); act?.SetTag("http.url", string.Concat(request.RequestUri.Scheme , "://" , request.RequestUri.Authority , request.RequestUri.PathAndQuery , request.RequestUri.Fragment) ); act?.SetTag("http.method", $"HTTP {request.Method}"); var isCachable = request.Method == HttpMethod.Get; var cacheKey = CacheKey(request); var cachedResponse = cache[cacheKey] as CachedHttpResponse; act?.SetTag("http.cache.found", isCachable && cachedResponse != null); if (isCachable && cachedResponse != null) { // Houston, we have a cache hit! // // Normally a cache would also take into account the vary-headers to make sure the request is similair. For // now we don't care about this. if (cachedResponse.Headers.TryGetValue("ETag", out IList <string> etag)) { request.Headers.IfNoneMatch.ParseAdd(etag.Single()); act?.SetTag("etag", etag.Single()); } if (cachedResponse.Headers.TryGetValue("Last-Modified", out IList <string> lastModified)) { request.Headers.IfModifiedSince = DateTimeOffset.Parse(lastModified.Single()); act?.SetTag("if-modified-since", lastModified.Single()); } } var response = await base.SendAsync(request, cancellationToken); act?.SetTag("http.status_code", response.StatusCode); if (isCachable && cachedResponse != null && response.StatusCode == HttpStatusCode.NotModified) { act?.SetTag("http.cache.hit", true); return(cachedResponse.ToHttpResponse(response)); } if (response.IsSuccessStatusCode && isCachable) { act?.SetTag("http.cache.miss", true); cachedResponse = await CachedHttpResponse.From(request, response); cache[cacheKey] = cachedResponse; // Creating the cache entry requires us to read the content stream. Since this is network stream and is // only meant to be read once, we have to reconstruct a http response from the cache entry. return(cachedResponse.ToHttpResponse(response)); } return(response); }
public async static Task <CachedHttpResponse> From(HttpRequestMessage request, HttpResponseMessage response) { var cached = new CachedHttpResponse { RequestUri = request.RequestUri , Method = request.Method.ToString() , StatusCode = response.StatusCode , ReasonPhrase = response.ReasonPhrase }; if (response.Content != null) { using var ms = new MemoryStream(); await response.Content.CopyToAsync(ms); ms.Position = 0; using var sr = new StreamContent(ms); cached.Content = await sr.ReadAsByteArrayAsync(); ms.Seek(0, SeekOrigin.Begin); } foreach (var header in response.Headers) { cached.Headers.TryAdd(header.Key, header.Value.ToList()); } foreach (var header in response.Content?.Headers) { cached.ContentHeaders.TryAdd(header.Key, header.Value.ToList()); } return(cached); }