Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#1172 Default CacheKeyGenerator #1849

Merged
merged 16 commits into from
Dec 14, 2023
Merged
5 changes: 4 additions & 1 deletion docs/features/caching.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,14 @@ Finally, in order to use caching on a route in your Route configuration add this

.. code-block:: json

"FileCacheOptions": { "TtlSeconds": 15, "Region": "europe-central" }
"FileCacheOptions": { "TtlSeconds": 15, "Region": "europe-central", "Header": "Authorization" }

In this example **TtlSeconds** is set to 15 which means the cache will expire after 15 seconds.
The **Region** represents a region of caching.

Additionally, if a header name is defined in the **Header** property, that header value is looked up by the key (header name) in the ``HttpRequest`` headers,
and if the header is found, its value will be included in caching key. This causes the cache to become invalid due to the header value changing.

If you look at the example `here <https://github.com/ThreeMammals/Ocelot/blob/main/test/Ocelot.ManualTest/Program.cs>`_ you can see how the cache manager is setup and then passed into the Ocelot ``AddCacheManager`` configuration method.
You can use any settings supported by the **CacheManager** package and just pass them in.

Expand Down
2 changes: 1 addition & 1 deletion src/Ocelot.Cache.CacheManager/OcelotBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public static IOcelotBuilder AddCacheManager(this IOcelotBuilder builder, Action
builder.Services.AddSingleton<IOcelotCache<FileConfiguration>>(fileConfigCacheManager);

builder.Services.RemoveAll(typeof(ICacheKeyGenerator));
builder.Services.AddSingleton<ICacheKeyGenerator, CacheKeyGenerator>();
builder.Services.AddSingleton<ICacheKeyGenerator, DefaultCacheKeyGenerator>();

return builder;
}
Expand Down
20 changes: 0 additions & 20 deletions src/Ocelot/Cache/CacheKeyGenerator.cs

This file was deleted.

48 changes: 48 additions & 0 deletions src/Ocelot/Cache/DefaultCacheKeyGenerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using Ocelot.Configuration;
using Ocelot.Request.Middleware;

namespace Ocelot.Cache
{
public class DefaultCacheKeyGenerator : ICacheKeyGenerator
{
private const char Delimiter = '-';

public async ValueTask<string> GenerateRequestCacheKey(DownstreamRequest downstreamRequest, DownstreamRoute downstreamRoute)
{
var builder = new StringBuilder()
.Append(downstreamRequest.Method)
.Append(Delimiter)
.Append(downstreamRequest.OriginalString);

var options = downstreamRoute?.CacheOptions;
var cacheOptionsHeader = options?.Header ?? string.Empty;
if (!string.IsNullOrEmpty(cacheOptionsHeader))
{
var header = downstreamRequest.Headers
.FirstOrDefault(r => r.Key.Equals(cacheOptionsHeader, StringComparison.OrdinalIgnoreCase))
.Value?.FirstOrDefault();

if (!string.IsNullOrEmpty(header))
{
builder.Append(Delimiter)
.Append(header);
}
}

if (!downstreamRequest.HasContent || !options.EnableRequestBodyHashing)
raman-m marked this conversation as resolved.
Show resolved Hide resolved
{
return MD5Helper.GenerateMd5(builder.ToString());
}

var requestContentString = await ReadContentAsync(downstreamRequest);
builder.Append(Delimiter)
.Append(requestContentString);

return MD5Helper.GenerateMd5(builder.ToString());
}

private static Task<string> ReadContentAsync(DownstreamRequest downstream) => downstream.HasContent
? downstream?.Request?.Content?.ReadAsStringAsync() ?? Task.FromResult(string.Empty)
: Task.FromResult(string.Empty);
}
}
5 changes: 3 additions & 2 deletions src/Ocelot/Cache/ICacheKeyGenerator.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
using Ocelot.Request.Middleware;
using Ocelot.Configuration;
using Ocelot.Request.Middleware;

namespace Ocelot.Cache
{
public interface ICacheKeyGenerator
{
string GenerateRequestCacheKey(DownstreamRequest downstreamRequest);
ValueTask<string> GenerateRequestCacheKey(DownstreamRequest downstreamRequest, DownstreamRoute downstreamRoute);
}
}
2 changes: 1 addition & 1 deletion src/Ocelot/Cache/Middleware/OutputCacheMiddleware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public async Task Invoke(HttpContext httpContext)

var downstreamRequest = httpContext.Items.DownstreamRequest();
var downstreamUrlKey = $"{downstreamRequest.Method}-{downstreamRequest.OriginalString}";
var downStreamRequestCacheKey = _cacheGenerator.GenerateRequestCacheKey(downstreamRequest);
var downStreamRequestCacheKey = await _cacheGenerator.GenerateRequestCacheKey(downstreamRequest, downstreamRoute);

Logger.LogDebug(() => $"Started checking cache for the '{downstreamUrlKey}' key.");

Expand Down
19 changes: 16 additions & 3 deletions src/Ocelot/Configuration/CacheOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,27 @@
{
public class CacheOptions
{
public CacheOptions(int ttlSeconds, string region)
public CacheOptions(int ttlSeconds, string region, string header)
{
TtlSeconds = ttlSeconds;
Region = region;
Region = region;
Header = header;
}

public CacheOptions(int ttlSeconds, string region, string header, bool enableContentHashing)
{
TtlSeconds = ttlSeconds;
Region = region;
Header = header;
EnableRequestBodyHashing = enableContentHashing;
}

public int TtlSeconds { get; }

public string Region { get; }
public string Region { get; }

public string Header { get; }

public bool EnableRequestBodyHashing { get; }
}
}
2 changes: 1 addition & 1 deletion src/Ocelot/Configuration/Creator/RoutesCreator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ private DownstreamRoute SetUpDownstreamRoute(FileRoute fileRoute, FileGlobalConf
.WithClaimsToDownstreamPath(claimsToDownstreamPath)
.WithRequestIdKey(requestIdKey)
.WithIsCached(fileRouteOptions.IsCached)
.WithCacheOptions(new CacheOptions(fileRoute.FileCacheOptions.TtlSeconds, region))
.WithCacheOptions(new CacheOptions(fileRoute.FileCacheOptions.TtlSeconds, region, fileRoute.FileCacheOptions.Header))
.WithDownstreamScheme(fileRoute.DownstreamScheme)
.WithLoadBalancerOptions(lbOptions)
.WithDownstreamAddresses(downstreamAddresses)
Expand Down
5 changes: 3 additions & 2 deletions src/Ocelot/Configuration/File/FileCacheOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ public FileCacheOptions(FileCacheOptions from)
Region = from.Region;
TtlSeconds = from.TtlSeconds;
}

public string Region { get; set; }

public int TtlSeconds { get; set; }
public string Region { get; set; }
public string Header { get; set; }
}
}
2 changes: 1 addition & 1 deletion src/Ocelot/DependencyInjection/OcelotBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ public OcelotBuilder(IServiceCollection services, IConfiguration configurationRo
Services.TryAddSingleton<IHttpHandlerOptionsCreator, HttpHandlerOptionsCreator>();
Services.TryAddSingleton<IDownstreamAddressesCreator, DownstreamAddressesCreator>();
Services.TryAddSingleton<IDelegatingHandlerHandlerFactory, DelegatingHandlerHandlerFactory>();
Services.TryAddSingleton<ICacheKeyGenerator, CacheKeyGenerator>();
Services.TryAddSingleton<ICacheKeyGenerator, DefaultCacheKeyGenerator>();
Services.TryAddSingleton<IOcelotConfigurationChangeTokenSource, OcelotConfigurationChangeTokenSource>();
Services.TryAddSingleton<IOptionsMonitor<IInternalConfiguration>, OcelotConfigurationMonitor>();

Expand Down
13 changes: 8 additions & 5 deletions src/Ocelot/Request/Middleware/DownstreamRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ public class DownstreamRequest
{
private readonly HttpRequestMessage _request;

public DownstreamRequest() { }

public DownstreamRequest(HttpRequestMessage request)
{
_request = request;
Expand All @@ -17,14 +19,13 @@ public DownstreamRequest(HttpRequestMessage request)
Headers = _request.Headers;
AbsolutePath = _request.RequestUri.AbsolutePath;
Query = _request.RequestUri.Query;
Content = _request.Content;
}

public HttpRequestHeaders Headers { get; }
public virtual HttpHeaders Headers { get; }
raman-m marked this conversation as resolved.
Show resolved Hide resolved

public string Method { get; }
public virtual string Method { get; }
raman-m marked this conversation as resolved.
Show resolved Hide resolved

public string OriginalString { get; }
public virtual string OriginalString { get; }
raman-m marked this conversation as resolved.
Show resolved Hide resolved

public string Scheme { get; set; }

Expand All @@ -36,7 +37,9 @@ public DownstreamRequest(HttpRequestMessage request)

public string Query { get; set; }

public HttpContent Content { get; set; }
public virtual bool HasContent { get => _request?.Content != null; }
raman-m marked this conversation as resolved.
Show resolved Hide resolved

public virtual HttpRequestMessage Request { get => _request; }
raman-m marked this conversation as resolved.
Show resolved Hide resolved

public HttpRequestMessage ToHttpRequestMessage()
{
Expand Down
4 changes: 2 additions & 2 deletions src/Ocelot/RequestId/Middleware/RequestIdMiddleware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,14 @@ private void SetOcelotRequestId(HttpContext httpContext)
}
}

private static bool ShouldAddRequestId(RequestId requestId, HttpRequestHeaders headers)
private static bool ShouldAddRequestId(RequestId requestId, HttpHeaders headers)
{
return !string.IsNullOrEmpty(requestId?.RequestIdKey)
&& !string.IsNullOrEmpty(requestId.RequestIdValue)
&& !RequestIdInHeaders(requestId, headers);
}

private static bool RequestIdInHeaders(RequestId requestId, HttpRequestHeaders headers)
private static bool RequestIdInHeaders(RequestId requestId, HttpHeaders headers)
{
return headers.TryGetValues(requestId.RequestIdKey, out var value);
}
Expand Down
32 changes: 0 additions & 32 deletions test/Ocelot.UnitTests/Cache/CacheKeyGeneratorTests.cs

This file was deleted.

Loading