Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/Ocelot/Cache/DefaultCacheKeyGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ namespace Ocelot.Cache;
public class DefaultCacheKeyGenerator : ICacheKeyGenerator
{
public const char Delimiter = '-';

public async ValueTask<string> GenerateRequestCacheKey(DownstreamRequest downstreamRequest, DownstreamRoute downstreamRoute)

// TODO: Split the code into protected virtual methods for easier fine-tuning
public virtual async ValueTask<string> GenerateRequestCacheKey(DownstreamRequest downstreamRequest, DownstreamRoute downstreamRoute)
{
var builder = new StringBuilder()
.Append(downstreamRequest.Method)
Expand Down
16 changes: 11 additions & 5 deletions src/Ocelot/Cache/OutputCacheMiddleware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,17 @@ public async Task Invoke(HttpContext httpContext)
}

var downstreamResponse = httpContext.Items.DownstreamResponse();
cached = await CreateCachedResponse(downstreamResponse);

var ttl = TimeSpan.FromSeconds(options.TtlSeconds);
_outputCache.Add(downStreamRequestCacheKey, cached, options.Region, ttl);
Logger.LogDebug(() => $"Finished response added to cache for the '{downstreamUrlKey}' key.");
if (downstreamResponse.StatusCode == HttpStatusCode.OK)
{
cached = await CreateCachedResponse(downstreamResponse);
var ttl = TimeSpan.FromSeconds(options.TtlSeconds);
_outputCache.Add(downStreamRequestCacheKey, cached, options.Region, ttl);
Logger.LogDebug(() => $"Finished response added to cache for the '{downstreamUrlKey}' key.");
}
else
{
Logger.LogDebug($"HTTP request failed: could not create cache for the '{downstreamUrlKey}' key.");
}
}

private static void SetHttpResponseMessageThisRequest(HttpContext context, DownstreamResponse response)
Expand Down
6 changes: 3 additions & 3 deletions src/Ocelot/Security/IPSecurity/IPSecurityPolicy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public class IPSecurityPolicy : ISecurityPolicy
{
public Response Security(DownstreamRoute downstreamRoute, HttpContext context)
{
var clientIp = context.Connection.RemoteIpAddress;
var clientIp = context.GetClientIpAddress();
var options = downstreamRoute.SecurityOptions;
if (options == null || clientIp == null)
{
Expand All @@ -18,7 +18,7 @@ public Response Security(DownstreamRoute downstreamRoute, HttpContext context)

if (options.IPBlockedList?.Count > 0)
{
if (options.IPBlockedList.Contains(clientIp.ToString()))
if (options.IPBlockedList.Contains(clientIp))
{
var error = new UnauthenticatedError($"This request rejects access to {clientIp} IP");
return new ErrorResponse(error);
Expand All @@ -27,7 +27,7 @@ public Response Security(DownstreamRoute downstreamRoute, HttpContext context)

if (options.IPAllowedList?.Count > 0)
{
if (!options.IPAllowedList.Contains(clientIp.ToString()))
if (!options.IPAllowedList.Contains(clientIp))
{
var error = new UnauthenticatedError($"{clientIp} does not allow access, the request is invalid");
return new ErrorResponse(error);
Expand Down
66 changes: 66 additions & 0 deletions src/Ocelot/Security/SecurityPolicyExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Primitives;

namespace Ocelot.Security;

public static class SecurityPolicyExtensions
{
public static string GetClientIpAddress(this HttpContext httpContext, bool tryUseXForwardHeader = true)
{
if (httpContext == null)
{
return null;
}

string ip = null;

// X-Forwarded-For => Using the First entry in the list
if (string.IsNullOrWhiteSpace(ip) && tryUseXForwardHeader)
{
ip = httpContext.GetHeaderValue("X-Forwarded-For").SplitCsv().FirstOrDefault();
}

// RemoteIpAddress is always null in DNX RC1 Update1 (bug).
if (string.IsNullOrWhiteSpace(ip) && httpContext.Connection?.RemoteIpAddress != null)
{
ip = httpContext.Connection.RemoteIpAddress.ToString();
}

if (string.IsNullOrWhiteSpace(ip))
{
ip = httpContext.GetHeaderValue("REMOTE_ADDR");
}

if (ip == "::1")
{
ip = "127.0.0.1";
}

return ip;
}

public static string GetHeaderValue(this HttpContext httpContext, string headerName)
{
if (httpContext?.Request?.Headers?.TryGetValue(headerName, out StringValues values) ?? false)
{
return values.ToString();
}

return string.Empty;
}

public static List<string> SplitCsv(this string csvList, bool nullOrWhitespaceInputReturnsNull = false)
{
if (string.IsNullOrWhiteSpace(csvList))
{
return nullOrWhitespaceInputReturnsNull ? null : new List<string>();
}

return csvList
.TrimEnd(',')
.Split(',')
.AsEnumerable()
.Select(s => s.Trim())
.ToList();
}
}
1 change: 1 addition & 0 deletions test/Ocelot.UnitTests/Cache/OutputCacheMiddlewareTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ public async Task CreateHttpResponseMessage_CachedIsNull()
// Arrange
CachedResponse cached = null;
GivenThereIsACachedResponse(cached);
GivenResponseIsNotCached(new HttpResponseMessage(HttpStatusCode.OK));
GivenTheDownstreamRouteIs();

// Act
Expand Down
Loading