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
2 changes: 1 addition & 1 deletion src/AspNetCoreRateLimit/Middleware/RateLimitMiddleware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ protected RateLimitMiddleware(
public async Task Invoke(HttpContext context)
{
// check if rate limiting is enabled
if (_options == null)
if (_options == null || _options.RateLimitingEnabled == false)
{
await _next.Invoke(context);
return;
Expand Down
5 changes: 5 additions & 0 deletions src/AspNetCoreRateLimit/Models/RateLimitOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ namespace AspNetCoreRateLimit
{
public class RateLimitOptions
{
/// <summary>
/// Gets or sets whether Rate Limiting is enabled
/// </summary>
public bool RateLimitingEnabled { get; set; } = true; // default for backward compatibility

public List<RateLimitRule> GeneralRules { get; set; }

public List<string> EndpointWhitelist { get; set; }
Expand Down
207 changes: 207 additions & 0 deletions test/AspNetCoreRateLimit.Demo/appsettings.EnabledFalse.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
{
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
},

"IpRateLimiting": {
"RateLimitingEnabled": false,
"EnableEndpointRateLimiting": true,
"EnableRegexRuleMatching": true,
"StackBlockedRequests": false,
"RealIpHeader": "X-Real-IP",
"ClientIdHeader": "X-ClientId",
"HttpStatusCode": 429,
"IpWhitelist": [ "::1/10", "192.168.0.0/24" ],
"EndpointWhitelist": [ "delete:/api/values", ":/api/clients", ":/api/ClientRateLimit", ":/api/IpRateLimit", "get:/" ],
"ClientWhitelist": [ "cl-key-1", "cl-key-2" ],
"QuotaExceededResponse": {
"Content": "{{ \"message\": \"Whoa! Calm down, cowboy!\", \"details\": \"Quota exceeded. Maximum allowed: {0} per {1}. Please try again in {2} second(s).\" }}",
"ContentType": "application/json"
},
"GeneralRules": [
{
"Endpoint": ".+",
"Period": "1s",
"Limit": 2
},
{
"Endpoint": ".+",
"Period": "1m",
"Limit": 5
}
]
},

"IpRateLimitPolicies": {
"IpRules": [
{
"Ip": "84.247.85.224",
"Rules": [
{
"Endpoint": ".+",
"Period": "1s",
"Limit": 10
},
{
"Endpoint": ".+",
"Period": "1m",
"Limit": 2
},
{
"Endpoint": "post:/api/values",
"Period": "1m",
"Limit": 5
}
]
},
{
"Ip": "84.247.85.225",
"Rules": [
{
"Endpoint": ".+",
"Period": "1s",
"Limit": 10
},
{
"Endpoint": ".+",
"Period": "1m",
"Limit": 5
},
{
"Endpoint": ".+",
"Period": "1h",
"Limit": 2
}
]
},
{
"Ip": "84.247.85.226",
"Rules": [
{
"Endpoint": ".+",
"Period": "1s",
"Limit": 10
},
{
"Endpoint": ".+",
"Period": "1m",
"Limit": 5
},
{
"Endpoint": ".+",
"Period": "1d",
"Limit": 2
}
]
},
{
"Ip": "84.247.85.231",
"Rules": [
{
"Endpoint": ".+",
"Period": "1m",
"Limit": 0
}
]
},
{
"Ip": "84.247.85.232",
"Rules": [
{
"Endpoint": ".+",
"Period": "1m",
"Limit": 1,
"MonitorMode": true
}
]
}
]
},

"ClientRateLimiting": {
"RateLimitingEnabled": false,
"EnableEndpointRateLimiting": true,
"EnableRegexRuleMatching": true,
"ClientIdHeader": "X-ClientId",
"HttpStatusCode": 429,
"EndpointWhitelist": [ "((post)|(put)|(get)|(delete)):/api/values", "delete:/api/clients" ],
"ClientWhitelist": [ "cl-key-a", "cl-key-b" ],
"GeneralRules": [
{
"Endpoint": ".+",
"Period": "1s",
"Limit": 2
},
{
"Endpoint": ".+",
"Period": "1m",
"Limit": 5
},
{
"Endpoint": "post:/api/clients",
"Period": "5m",
"Limit": 3
}
]
},

"ClientRateLimitPolicies": {
"ClientRules": [
{
"ClientId": "cl-key-1",
"Rules": [
{
"Endpoint": ".+",
"Period": "1s",
"Limit": 10
},
{
"Endpoint": "get:/api/clients",
"Period": "1m",
"Limit": 2
},
{
"Endpoint": "put:/api/clients",
"Period": "5m",
"Limit": 2
}
]
},
{
"ClientId": "cl-key-2",
"Rules": [
{
"Endpoint": ".+",
"Period": "1s",
"Limit": 10
},
{
"Endpoint": "get:/api/clients",
"Period": "1m",
"Limit": 0
},
{
"Endpoint": "post:/api/clients",
"Period": "5m",
"Limit": 50
}
]
},
{
"ClientId": "cl-key-3",
"Rules": [
{
"Endpoint": "post:/api/clients",
"Period": "1s",
"Limit": 3
}
]
}
]
}
}
2 changes: 2 additions & 0 deletions test/AspNetCoreRateLimit.Demo/appsettings.Regex.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
},

"IpRateLimiting": {
"RateLimitingEnabled": true,
"EnableEndpointRateLimiting": true,
"EnableRegexRuleMatching": true,
"StackBlockedRequests": false,
Expand Down Expand Up @@ -123,6 +124,7 @@
},

"ClientRateLimiting": {
"RateLimitingEnabled": true,
"EnableEndpointRateLimiting": true,
"EnableRegexRuleMatching": true,
"ClientIdHeader": "X-ClientId",
Expand Down
2 changes: 2 additions & 0 deletions test/AspNetCoreRateLimit.Demo/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
},

"IpRateLimiting": {
"RateLimitingEnabled": true,
"EnableEndpointRateLimiting": true,
"StackBlockedRequests": false,
"RealIpHeader": "X-Real-IP",
Expand Down Expand Up @@ -130,6 +131,7 @@
},

"ClientRateLimiting": {
"RateLimitingEnabled": true,
"EnableEndpointRateLimiting": true,
"ClientIdHeader": "X-ClientId",
"HttpStatusCode": 429,
Expand Down
20 changes: 20 additions & 0 deletions test/AspNetCoreRateLimit.Tests/BaseClassFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public abstract class BaseClassFixture : IClassFixture<RateLimitWebApplicationFa
{
private readonly HttpClient _wildcardClient;
private readonly HttpClient _regexClient;
private readonly HttpClient _enabledFalseClient;

protected BaseClassFixture(RateLimitWebApplicationFactory factory)
{
Expand All @@ -29,6 +30,18 @@ protected BaseClassFixture(RateLimitWebApplicationFactory factory)
{
BaseAddress = new System.Uri("https://localhost:44304")
});

_enabledFalseClient = factory.WithWebHostBuilder(builder =>
{
builder.ConfigureAppConfiguration((context, conf) =>
{
conf.AddJsonFile("appsettings.EnabledFalse.json");
});
}).CreateClient(options: new WebApplicationFactoryClientOptions
{
BaseAddress = new System.Uri("https://localhost:44304"),

});
}

/// <summary>
Expand All @@ -43,9 +56,16 @@ protected HttpClient GetClient(ClientType clientType)
return _wildcardClient;
case ClientType.Regex:
return _regexClient;
case ClientType.EnabledFalse:
return _enabledFalseClient;
default:
throw new ArgumentOutOfRangeException(nameof(clientType), clientType, "Unexpected client type.");
}
}

protected void SetOptions(RateLimitOptions options)
{

}
}
}
25 changes: 25 additions & 0 deletions test/AspNetCoreRateLimit.Tests/ClientRateLimitTests.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using AspNetCoreRateLimit.Tests.Enums;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Xunit;

Expand Down Expand Up @@ -224,5 +225,29 @@ public async Task UpdateOptions(ClientType clientType)
// Assert
Assert.Contains(keyword, content);
}

[Theory]
[InlineData(ClientType.EnabledFalse)]
public async Task RulesDisabled(ClientType clientType)
{
// Arrange
var clientId = "cl-key-3";
int responseStatusCode = 0;

// Act
for (int i = 0; i < 4; i++)
{
var request = new HttpRequestMessage(HttpMethod.Post, apiPath);
request.Headers.Add("X-ClientId", clientId);
request.Headers.Add("X-Real-IP", ip);
request.Content = new StringContent("{}", Encoding.UTF8, "application/json");

var response = await GetClient(clientType).SendAsync(request);
responseStatusCode = (int)response.StatusCode;
}

// Assert
Assert.Equal(200, responseStatusCode);
}
}
}
3 changes: 2 additions & 1 deletion test/AspNetCoreRateLimit.Tests/Enums/ClientType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
public enum ClientType
{
Wildcard,
Regex
Regex,
EnabledFalse
}
}
24 changes: 24 additions & 0 deletions test/AspNetCoreRateLimit.Tests/IpRateLimitTests.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using AspNetCoreRateLimit.Tests.Enums;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Xunit;

Expand Down Expand Up @@ -265,5 +266,28 @@ public async Task SpecificIpRuleMonitorActive(ClientType clientType, string ip)
// Assert
Assert.Equal(200, responseStatusCode);
}

[Theory]
[InlineData(ClientType.EnabledFalse, "84.247.85.232")]
public async Task RulesDisabled(ClientType clientType, string ip)
{
// Arrange
int responseStatusCode = 0;

// Act
for (int i = 0; i < 2; i++)
{
var request = new HttpRequestMessage(HttpMethod.Get, apiValuesPath);
request.Headers.Add("X-Real-IP", ip);
request.Content = new StringContent("{}", Encoding.UTF8, "application/json");

var response = await GetClient(clientType).SendAsync(request);
responseStatusCode = (int)response.StatusCode;
}

// Assert
Assert.Equal(200, responseStatusCode);
}

}
}