Feature/dont validate cached content headers (#406)

* #372 use period timespan to decide when client can make requests again

* #400 dont validate cached body headers
This commit is contained in:
Tom Pallister
2018-06-15 20:30:25 +01:00
committed by GitHub
parent 9979f8a4b8
commit 8e1a5ce827
4 changed files with 505 additions and 435 deletions

View File

@ -1,120 +1,120 @@
using System;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Ocelot.Logging;
using Ocelot.Middleware;
using System.IO;
using Ocelot.Middleware.Multiplexer;
namespace Ocelot.Cache.Middleware
{
public class OutputCacheMiddleware : OcelotMiddleware
{
private readonly OcelotRequestDelegate _next;
private readonly IOcelotCache<CachedResponse> _outputCache;
private readonly IRegionCreator _regionCreator;
public OutputCacheMiddleware(OcelotRequestDelegate next,
IOcelotLoggerFactory loggerFactory,
IOcelotCache<CachedResponse> outputCache,
IRegionCreator regionCreator)
:base(loggerFactory.CreateLogger<OutputCacheMiddleware>())
{
_next = next;
_outputCache = outputCache;
_regionCreator = regionCreator;
}
public async Task Invoke(DownstreamContext context)
{
if (!context.DownstreamReRoute.IsCached)
{
await _next.Invoke(context);
return;
}
var downstreamUrlKey = $"{context.DownstreamRequest.Method}-{context.DownstreamRequest.OriginalString}";
Logger.LogDebug($"Started checking cache for {downstreamUrlKey}");
var cached = _outputCache.Get(downstreamUrlKey, context.DownstreamReRoute.CacheOptions.Region);
if (cached != null)
{
Logger.LogDebug($"cache entry exists for {downstreamUrlKey}");
var response = CreateHttpResponseMessage(cached);
SetHttpResponseMessageThisRequest(context, response);
Logger.LogDebug($"finished returned cached response for {downstreamUrlKey}");
return;
}
Logger.LogDebug($"no resonse cached for {downstreamUrlKey}");
await _next.Invoke(context);
if (context.IsError)
{
Logger.LogDebug($"there was a pipeline error for {downstreamUrlKey}");
return;
}
cached = await CreateCachedResponse(context.DownstreamResponse);
_outputCache.Add(downstreamUrlKey, cached, TimeSpan.FromSeconds(context.DownstreamReRoute.CacheOptions.TtlSeconds), context.DownstreamReRoute.CacheOptions.Region);
Logger.LogDebug($"finished response added to cache for {downstreamUrlKey}");
}
private void SetHttpResponseMessageThisRequest(DownstreamContext context, DownstreamResponse response)
{
context.DownstreamResponse = response;
}
internal DownstreamResponse CreateHttpResponseMessage(CachedResponse cached)
{
if (cached == null)
{
return null;
}
var content = new MemoryStream(Convert.FromBase64String(cached.Body));
var streamContent = new StreamContent(content);
foreach (var header in cached.ContentHeaders)
{
streamContent.Headers.Add(header.Key, header.Value);
}
return new DownstreamResponse(streamContent, cached.StatusCode, cached.Headers.ToList());
}
internal async Task<CachedResponse> CreateCachedResponse(DownstreamResponse response)
{
if (response == null)
{
return null;
}
var statusCode = response.StatusCode;
var headers = response.Headers.ToDictionary(v => v.Key, v => v.Values);
string body = null;
if (response.Content != null)
{
var content = await response.Content.ReadAsByteArrayAsync();
body = Convert.ToBase64String(content);
}
var contentHeaders = response?.Content?.Headers.ToDictionary(v => v.Key, v => v.Value);
var cached = new CachedResponse(statusCode, headers, body, contentHeaders);
return cached;
}
}
}
using System;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Ocelot.Logging;
using Ocelot.Middleware;
using System.IO;
using Ocelot.Middleware.Multiplexer;
namespace Ocelot.Cache.Middleware
{
public class OutputCacheMiddleware : OcelotMiddleware
{
private readonly OcelotRequestDelegate _next;
private readonly IOcelotCache<CachedResponse> _outputCache;
private readonly IRegionCreator _regionCreator;
public OutputCacheMiddleware(OcelotRequestDelegate next,
IOcelotLoggerFactory loggerFactory,
IOcelotCache<CachedResponse> outputCache,
IRegionCreator regionCreator)
:base(loggerFactory.CreateLogger<OutputCacheMiddleware>())
{
_next = next;
_outputCache = outputCache;
_regionCreator = regionCreator;
}
public async Task Invoke(DownstreamContext context)
{
if (!context.DownstreamReRoute.IsCached)
{
await _next.Invoke(context);
return;
}
var downstreamUrlKey = $"{context.DownstreamRequest.Method}-{context.DownstreamRequest.OriginalString}";
Logger.LogDebug($"Started checking cache for {downstreamUrlKey}");
var cached = _outputCache.Get(downstreamUrlKey, context.DownstreamReRoute.CacheOptions.Region);
if (cached != null)
{
Logger.LogDebug($"cache entry exists for {downstreamUrlKey}");
var response = CreateHttpResponseMessage(cached);
SetHttpResponseMessageThisRequest(context, response);
Logger.LogDebug($"finished returned cached response for {downstreamUrlKey}");
return;
}
Logger.LogDebug($"no resonse cached for {downstreamUrlKey}");
await _next.Invoke(context);
if (context.IsError)
{
Logger.LogDebug($"there was a pipeline error for {downstreamUrlKey}");
return;
}
cached = await CreateCachedResponse(context.DownstreamResponse);
_outputCache.Add(downstreamUrlKey, cached, TimeSpan.FromSeconds(context.DownstreamReRoute.CacheOptions.TtlSeconds), context.DownstreamReRoute.CacheOptions.Region);
Logger.LogDebug($"finished response added to cache for {downstreamUrlKey}");
}
private void SetHttpResponseMessageThisRequest(DownstreamContext context, DownstreamResponse response)
{
context.DownstreamResponse = response;
}
internal DownstreamResponse CreateHttpResponseMessage(CachedResponse cached)
{
if (cached == null)
{
return null;
}
var content = new MemoryStream(Convert.FromBase64String(cached.Body));
var streamContent = new StreamContent(content);
foreach (var header in cached.ContentHeaders)
{
streamContent.Headers.TryAddWithoutValidation(header.Key, header.Value);
}
return new DownstreamResponse(streamContent, cached.StatusCode, cached.Headers.ToList());
}
internal async Task<CachedResponse> CreateCachedResponse(DownstreamResponse response)
{
if (response == null)
{
return null;
}
var statusCode = response.StatusCode;
var headers = response.Headers.ToDictionary(v => v.Key, v => v.Values);
string body = null;
if (response.Content != null)
{
var content = await response.Content.ReadAsByteArrayAsync();
body = Convert.ToBase64String(content);
}
var contentHeaders = response?.Content?.Headers.ToDictionary(v => v.Key, v => v.Value);
var cached = new CachedResponse(statusCode, headers, body, contentHeaders);
return cached;
}
}
}