Skip to content

Commit ebe662a

Browse files
TomPallistertotubamowocatcherwongjlukawskaburetjph
authored
make rate limiting whitelist a function so users can override with dynamic behaviour
* Make rate-limiting client whitelist dynamic * Refactor `RateLimitOptions.ClientWhiteList` * Fix typo in variable `enbleRateLimiting` * Fix case in variable `clientIdheader` author Taiwo Otubamowo <[email protected]> * fix 1045 * #492 log 500 with error log level, acceptance test, unit test * #492 minor changes * initial commit for new feature #1077 allow to limit the number of concurrent tcp connection to a downstream service * protect code against value not in accurate range add unit test * Do not crash host on Dispose * Add test * Pin GitVersion.CommandLine package version * #683 validate if there are duplicated placeholders in UpstreamPathTemplate * Use registered scheme from Eureka (#1087) * extra test * very brief mention MaxConnectionsPerServer in docs * build develop like a PR * more docs * test Co-authored-by: Taiwo O. <[email protected]> Co-authored-by: Catcher Wong <[email protected]> Co-authored-by: jlukawska <[email protected]> Co-authored-by: buretjph <[email protected]> Co-authored-by: Jonathan Mezach <[email protected]> Co-authored-by: 彭伟 <[email protected]>
1 parent 07df311 commit ebe662a

File tree

15 files changed

+303
-126
lines changed

15 files changed

+303
-126
lines changed

Ocelot.sln

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
99
ProjectSection(SolutionItems) = preProject
1010
.dockerignore = .dockerignore
1111
.gitignore = .gitignore
12-
build-and-release-unstable.ps1 = build-and-release-unstable.ps1
13-
build-and-run-tests.ps1 = build-and-run-tests.ps1
1412
build.cake = build.cake
1513
build.ps1 = build.ps1
1614
codeanalysis.ruleset = codeanalysis.ruleset
17-
docker-compose.yaml = docker-compose.yaml
18-
Dockerfile = Dockerfile
1915
GitVersion.yml = GitVersion.yml
20-
global.json = global.json
2116
LICENSE.md = LICENSE.md
2217
README.md = README.md
23-
release.ps1 = release.ps1
2418
ReleaseNotes.md = ReleaseNotes.md
25-
run-acceptance-tests.ps1 = run-acceptance-tests.ps1
26-
run-benchmarks.ps1 = run-benchmarks.ps1
27-
run-unit-tests.ps1 = run-unit-tests.ps1
28-
version.ps1 = version.ps1
2919
EndProjectSection
3020
EndProject
3121
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{5B401523-36DA-4491-B73A-7590A26E420B}"
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"iisSettings": {
3+
"windowsAuthentication": false,
4+
"anonymousAuthentication": true,
5+
"iisExpress": {
6+
"applicationUrl": "http://localhost:55029/",
7+
"sslPort": 44390
8+
}
9+
},
10+
"profiles": {
11+
"IIS Express": {
12+
"commandName": "IISExpress",
13+
"launchBrowser": true,
14+
"environmentVariables": {
15+
"ASPNETCORE_ENVIRONMENT": "Development"
16+
}
17+
},
18+
"OcelotBasic": {
19+
"commandName": "Project",
20+
"launchBrowser": true,
21+
"environmentVariables": {
22+
"ASPNETCORE_ENVIRONMENT": "Development"
23+
},
24+
"applicationUrl": "https://localhost:5001;http://localhost:5000"
25+
}
26+
}
27+
}

src/Ocelot/Configuration/Builder/RateLimitOptionsBuilder.cs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.Collections.Generic;
1+
using System;
2+
using System.Collections.Generic;
23

34
namespace Ocelot.Configuration.Builder
45
{
@@ -7,6 +8,7 @@ public class RateLimitOptionsBuilder
78
private bool _enableRateLimiting;
89
private string _clientIdHeader;
910
private List<string> _clientWhitelist;
11+
private Func<List<string>> _getClientWhitelist;
1012
private bool _disableRateLimitHeaders;
1113
private string _quotaExceededMessage;
1214
private string _rateLimitCounterPrefix;
@@ -19,15 +21,15 @@ public RateLimitOptionsBuilder WithEnableRateLimiting(bool enableRateLimiting)
1921
return this;
2022
}
2123

22-
public RateLimitOptionsBuilder WithClientIdHeader(string clientIdheader)
24+
public RateLimitOptionsBuilder WithClientIdHeader(string clientIdHeader)
2325
{
24-
_clientIdHeader = clientIdheader;
26+
_clientIdHeader = clientIdHeader;
2527
return this;
2628
}
2729

28-
public RateLimitOptionsBuilder WithClientWhiteList(List<string> clientWhitelist)
30+
public RateLimitOptionsBuilder WithClientWhiteList(Func<List<string>> getClientWhitelist)
2931
{
30-
_clientWhitelist = clientWhitelist;
32+
_getClientWhitelist = getClientWhitelist;
3133
return this;
3234
}
3335

@@ -63,9 +65,9 @@ public RateLimitOptionsBuilder WithHttpStatusCode(int httpStatusCode)
6365

6466
public RateLimitOptions Build()
6567
{
66-
return new RateLimitOptions(_enableRateLimiting, _clientIdHeader, _clientWhitelist,
67-
_disableRateLimitHeaders, _quotaExceededMessage, _rateLimitCounterPrefix,
68+
return new RateLimitOptions(_enableRateLimiting, _clientIdHeader, _getClientWhitelist,
69+
_disableRateLimitHeaders, _quotaExceededMessage, _rateLimitCounterPrefix,
6870
_rateLimitRule, _httpStatusCode);
6971
}
7072
}
71-
}
73+
}

src/Ocelot/Configuration/Creator/RateLimitOptionsCreator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ public RateLimitOptions Create(FileRateLimitRule fileRateLimitRule, FileGlobalCo
1111
{
1212
return new RateLimitOptionsBuilder()
1313
.WithClientIdHeader(globalConfiguration.RateLimitOptions.ClientIdHeader)
14-
.WithClientWhiteList(fileRateLimitRule.ClientWhitelist)
14+
.WithClientWhiteList(() => fileRateLimitRule.ClientWhitelist)
1515
.WithDisableRateLimitHeaders(globalConfiguration.RateLimitOptions.DisableRateLimitHeaders)
1616
.WithEnableRateLimiting(fileRateLimitRule.EnableRateLimiting)
1717
.WithHttpStatusCode(globalConfiguration.RateLimitOptions.HttpStatusCode)
Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.Collections.Generic;
1+
using System;
2+
using System.Collections.Generic;
23

34
namespace Ocelot.Configuration
45
{
@@ -7,12 +8,14 @@ namespace Ocelot.Configuration
78
/// </summary>
89
public class RateLimitOptions
910
{
10-
public RateLimitOptions(bool enbleRateLimiting, string clientIdHeader, List<string> clientWhitelist, bool disableRateLimitHeaders,
11+
private readonly Func<List<string>> _getClientWhitelist;
12+
13+
public RateLimitOptions(bool enableRateLimiting, string clientIdHeader, Func<List<string>> getClientWhitelist, bool disableRateLimitHeaders,
1114
string quotaExceededMessage, string rateLimitCounterPrefix, RateLimitRule rateLimitRule, int httpStatusCode)
1215
{
13-
EnableRateLimiting = enbleRateLimiting;
16+
EnableRateLimiting = enableRateLimiting;
1417
ClientIdHeader = clientIdHeader;
15-
ClientWhitelist = clientWhitelist ?? new List<string>();
18+
_getClientWhitelist = getClientWhitelist;
1619
DisableRateLimitHeaders = disableRateLimitHeaders;
1720
QuotaExceededMessage = quotaExceededMessage;
1821
RateLimitCounterPrefix = rateLimitCounterPrefix;
@@ -22,18 +25,21 @@ public RateLimitOptions(bool enbleRateLimiting, string clientIdHeader, List<stri
2225

2326
public RateLimitRule RateLimitRule { get; private set; }
2427

25-
public List<string> ClientWhitelist { get; private set; }
28+
/// <summary>
29+
/// Gets the list of white listed clients
30+
/// </summary>
31+
public List<string> ClientWhitelist { get => _getClientWhitelist(); }
2632

2733
/// <summary>
2834
/// Gets or sets the HTTP header that holds the client identifier, by default is X-ClientId
2935
/// </summary>
30-
public string ClientIdHeader { get; private set; }
31-
36+
public string ClientIdHeader { get; private set; }
37+
3238
/// <summary>
3339
/// Gets or sets the HTTP Status code returned when rate limiting occurs, by default value is set to 429 (Too Many Requests)
3440
/// </summary>
35-
public int HttpStatusCode { get; private set; }
36-
41+
public int HttpStatusCode { get; private set; }
42+
3743
/// <summary>
3844
/// Gets or sets a value that will be used as a formatter for the QuotaExceeded response message.
3945
/// If none specified the default will be:
@@ -44,8 +50,8 @@ public RateLimitOptions(bool enbleRateLimiting, string clientIdHeader, List<stri
4450
/// <summary>
4551
/// Gets or sets the counter prefix, used to compose the rate limit counter cache key
4652
/// </summary>
47-
public string RateLimitCounterPrefix { get; private set; }
48-
53+
public string RateLimitCounterPrefix { get; private set; }
54+
4955
/// <summary>
5056
/// Enables endpoint rate limiting based URL path and HTTP verb
5157
/// </summary>
@@ -56,4 +62,4 @@ public RateLimitOptions(bool enbleRateLimiting, string clientIdHeader, List<stri
5662
/// </summary>
5763
public bool DisableRateLimitHeaders { get; private set; }
5864
}
59-
}
65+
}

src/Ocelot/Requester/HttpExeptionToErrorMapper.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
namespace Ocelot.Requester
22
{
3-
using Errors;
3+
using Ocelot.Errors;
44
using Microsoft.Extensions.DependencyInjection;
55
using System;
66
using System.Collections.Generic;
@@ -23,7 +23,7 @@ public Error Map(Exception exception)
2323
return _mappers[type](exception);
2424
}
2525

26-
if (type == typeof(OperationCanceledException))
26+
if (type == typeof(OperationCanceledException) || type.IsSubclassOf(typeof(OperationCanceledException)))
2727
{
2828
return new RequestCanceledError(exception.Message);
2929
}

src/Ocelot/Requester/Middleware/HttpRequesterMiddleware.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1+
using System.Net;
2+
using System.Net.Http;
13
using Ocelot.Logging;
24
using Ocelot.Middleware;
35
using System.Threading.Tasks;
6+
using Ocelot.Responses;
47

58
namespace Ocelot.Requester.Middleware
69
{
@@ -22,6 +25,8 @@ public async Task Invoke(DownstreamContext context)
2225
{
2326
var response = await _requester.GetResponse(context);
2427

28+
CreateLogBasedOnResponse(response);
29+
2530
if (response.IsError)
2631
{
2732
Logger.LogDebug("IHttpRequester returned an error, setting pipeline error");
@@ -36,5 +41,19 @@ public async Task Invoke(DownstreamContext context)
3641

3742
await _next.Invoke(context);
3843
}
44+
45+
private void CreateLogBasedOnResponse(Response<HttpResponseMessage> response)
46+
{
47+
if (response.Data?.StatusCode <= HttpStatusCode.BadRequest)
48+
{
49+
Logger.LogInformation(
50+
$"{(int)response.Data.StatusCode} ({response.Data.ReasonPhrase}) status code, request uri: {response.Data.RequestMessage?.RequestUri}");
51+
}
52+
else if (response.Data?.StatusCode >= HttpStatusCode.BadRequest)
53+
{
54+
Logger.LogWarning(
55+
$"{(int) response.Data.StatusCode} ({response.Data.ReasonPhrase}) status code, request uri: {response.Data.RequestMessage?.RequestUri}");
56+
}
57+
}
3958
}
4059
}
Lines changed: 74 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,75 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
2-
<PropertyGroup>
3-
<VersionPrefix>0.0.0-dev</VersionPrefix>
4-
<TargetFramework>netcoreapp3.1</TargetFramework>
5-
<AssemblyName>Ocelot.AcceptanceTests</AssemblyName>
6-
<OutputType>Exe</OutputType>
7-
<PackageId>Ocelot.AcceptanceTests</PackageId>
8-
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
9-
<RuntimeIdentifiers>osx.10.11-x64;osx.10.12-x64;win7-x64;win10-x64</RuntimeIdentifiers>
10-
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
11-
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
12-
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
13-
<CodeAnalysisRuleSet>..\..\codeanalysis.ruleset</CodeAnalysisRuleSet>
14-
</PropertyGroup>
15-
16-
<ItemGroup>
17-
<None Update="appsettings.product.json">
18-
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
19-
</None>
20-
<None Update="appsettings.json">
21-
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
22-
</None>
23-
<None Update="idsrv3test.pfx">
24-
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
25-
</None>
26-
</ItemGroup>
27-
<ItemGroup>
28-
<ProjectReference Include="..\..\src\Ocelot.Tracing.Butterfly\Ocelot.Tracing.Butterfly.csproj" />
29-
<ProjectReference Include="..\..\src\Ocelot\Ocelot.csproj" />
30-
<ProjectReference Include="..\..\src\Ocelot.Cache.CacheManager\Ocelot.Cache.CacheManager.csproj" />
31-
<ProjectReference Include="..\..\src\Ocelot.Provider.Consul\Ocelot.Provider.Consul.csproj" />
32-
<ProjectReference Include="..\..\src\Ocelot.Provider.Eureka\Ocelot.Provider.Eureka.csproj" />
33-
<ProjectReference Include="..\..\src\Ocelot.Provider.Polly\Ocelot.Provider.Polly.csproj" />
34-
<ProjectReference Include="..\Ocelot.ManualTest\Ocelot.ManualTest.csproj" />
35-
</ItemGroup>
36-
<ItemGroup>
37-
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
38-
</ItemGroup>
39-
40-
<ItemGroup>
41-
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="3.0.0" />
42-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.3.0" />
43-
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.66">
44-
<PrivateAssets>all</PrivateAssets>
45-
</PackageReference>
46-
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1">
47-
<PrivateAssets>all</PrivateAssets>
48-
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
49-
</PackageReference>
50-
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="3.0.0" />
51-
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="3.0.0" />
52-
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.0.0" />
53-
<PackageReference Include="Microsoft.Extensions.Logging" Version="3.0.0" />
54-
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="3.0.0" />
55-
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="3.0.0" />
56-
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="3.0.0" />
57-
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="3.0.0" />
58-
<PackageReference Include="Microsoft.DotNet.InternalAbstractions" Version="1.0.500-preview2-1-003177" />
59-
<PackageReference Include="Shouldly" Version="4.0.0-beta0002" />
60-
<PackageReference Include="TestStack.BDDfy" Version="4.3.2" />
61-
<PackageReference Include="xunit" Version="2.4.1" />
62-
<PackageReference Include="Butterfly.Client.AspNetCore" Version="0.0.8" />
63-
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="3.0.1" />
64-
<PackageReference Include="IdentityServer4" Version="3.0.1" />
65-
<PackageReference Include="Consul" Version="0.7.2.6" />
66-
<PackageReference Include="Rafty" Version="0.4.4" />
67-
<PackageReference Include="CacheManager.Microsoft.Extensions.Logging" Version="2.0.0-beta-1629" />
68-
<PackageReference Include="CacheManager.Serialization.Json" Version="2.0.0-beta-1629" />
69-
<PackageReference Include="Pivotal.Discovery.ClientCore" Version="2.2.0" />
70-
</ItemGroup>
71-
<ItemGroup>
72-
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.0.0" />
73-
</ItemGroup>
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<VersionPrefix>0.0.0-dev</VersionPrefix>
4+
<TargetFramework>netcoreapp3.1</TargetFramework>
5+
<AssemblyName>Ocelot.AcceptanceTests</AssemblyName>
6+
<OutputType>Exe</OutputType>
7+
<PackageId>Ocelot.AcceptanceTests</PackageId>
8+
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
9+
<RuntimeIdentifiers>osx.10.11-x64;osx.10.12-x64;win7-x64;win10-x64</RuntimeIdentifiers>
10+
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
11+
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
12+
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
13+
<CodeAnalysisRuleSet>..\..\codeanalysis.ruleset</CodeAnalysisRuleSet>
14+
</PropertyGroup>
15+
16+
<ItemGroup>
17+
<None Update="appsettings.product.json">
18+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
19+
</None>
20+
<None Update="appsettings.json">
21+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
22+
</None>
23+
<None Update="idsrv3test.pfx">
24+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
25+
</None>
26+
</ItemGroup>
27+
<ItemGroup>
28+
<ProjectReference Include="..\..\src\Ocelot.Tracing.Butterfly\Ocelot.Tracing.Butterfly.csproj" />
29+
<ProjectReference Include="..\..\src\Ocelot\Ocelot.csproj" />
30+
<ProjectReference Include="..\..\src\Ocelot.Cache.CacheManager\Ocelot.Cache.CacheManager.csproj" />
31+
<ProjectReference Include="..\..\src\Ocelot.Provider.Consul\Ocelot.Provider.Consul.csproj" />
32+
<ProjectReference Include="..\..\src\Ocelot.Provider.Eureka\Ocelot.Provider.Eureka.csproj" />
33+
<ProjectReference Include="..\..\src\Ocelot.Provider.Polly\Ocelot.Provider.Polly.csproj" />
34+
<ProjectReference Include="..\Ocelot.ManualTest\Ocelot.ManualTest.csproj" />
35+
</ItemGroup>
36+
<ItemGroup>
37+
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
38+
</ItemGroup>
39+
40+
<ItemGroup>
41+
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="3.0.0" />
42+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.3.0" />
43+
<PackageReference Include="Moq" Version="4.13.0" />
44+
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.66">
45+
<PrivateAssets>all</PrivateAssets>
46+
</PackageReference>
47+
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1">
48+
<PrivateAssets>all</PrivateAssets>
49+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
50+
</PackageReference>
51+
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="3.0.0" />
52+
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="3.0.0" />
53+
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.0.0" />
54+
<PackageReference Include="Microsoft.Extensions.Logging" Version="3.0.0" />
55+
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="3.0.0" />
56+
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="3.0.0" />
57+
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="3.0.0" />
58+
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="3.0.0" />
59+
<PackageReference Include="Microsoft.DotNet.InternalAbstractions" Version="1.0.500-preview2-1-003177" />
60+
<PackageReference Include="Shouldly" Version="4.0.0-beta0002" />
61+
<PackageReference Include="TestStack.BDDfy" Version="4.3.2" />
62+
<PackageReference Include="xunit" Version="2.4.1" />
63+
<PackageReference Include="Butterfly.Client.AspNetCore" Version="0.0.8" />
64+
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="3.0.1" />
65+
<PackageReference Include="IdentityServer4" Version="3.0.1" />
66+
<PackageReference Include="Consul" Version="0.7.2.6" />
67+
<PackageReference Include="Rafty" Version="0.4.4" />
68+
<PackageReference Include="CacheManager.Microsoft.Extensions.Logging" Version="2.0.0-beta-1629" />
69+
<PackageReference Include="CacheManager.Serialization.Json" Version="2.0.0-beta-1629" />
70+
<PackageReference Include="Pivotal.Discovery.ClientCore" Version="2.2.0" />
71+
</ItemGroup>
72+
<ItemGroup>
73+
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.0.0" />
74+
</ItemGroup>
7475
</Project>

0 commit comments

Comments
 (0)