mirror of
https://github.com/JKorf/CryptoExchange.Net
synced 2025-12-17 03:28:18 +00:00
Performance update: Authentication Added Ed25519 signing support for NET8.0 and newer Added static methods on ApiCredentials to create credentials of a specific type Added static ApiCredentials.ReadFromFile method to read a key from file Added required abstract SupportedCredentialTypes property on AuthenticationProvider base class General Performance Added checks before logging statements to prevent overhead of building the log string if logging is not needed Added ExchangeHelpers.ProcessQueuedAsync method to process updates async Replaced locking object types from object to Lock in NET9.0 and newer Replaced some Task response types with ValueTask to prevent allocation overhead on hot paths Updated Json ArrayConverter to reduce some allocation overhead Updated Json BoolConverter to prevent boxing Updated Json DateTimeConverter to prevent boxing Updated Json EnumConverter caching to reduce lookup overhead Updated ExtensionMethods.CreateParamString to reduce allocations Updated ExtensionMethods.AppendPath to reduce overhead REST Refactored REST message processing to separate IRestMessageHandler instance Split RestApiClient.PrepareAsync into CheckTimeSync and RateLimitAsync Updated IRequest.Accept type from string to MediaTypeWithQualityHeaderValue to prevent creation on each request Updated IRequest.GetHeaders response type from KeyValuePair<string, string[]>[] to HttpRequestHeaders to prevent additional mapping Updated IResponse.ResponseHeaders type from KeyValuePair<string, string[]>[] to HttpResponseHeaders to prevent additional mapping Updated WebCallResult RequestHeaders and ResponseHeaders types to HttpRequestHeaders and HttpResponseHeaders Removed unnecessary empty dictionary initializations for each request Removed CallResult creation in internal methods to prevent having to create multiple versions for different result types Socket Added HighPerformance websocket client implementation which significantly reduces memory overhead and improves speed but with certain limitations Added MaxIndividualSubscriptionsPerConnection setting in SocketApiClient to limit the number of individual stream subscriptions on a connection Added SocketIndividualSubscriptionCombineTarget option to set the target number of individual stream subscriptions per connection Added new websocket message handling logic which is faster and reduces memory allocation Added UseUpdatedDeserialization option to toggle between updated deserialization and old deserialization Added Exchange property to DataEvent to prevent additional mapping overhead for Shared apis Refactored message callback to be sync instead of async to prevent async overhead Refactored CryptoExchangeWebSocketClient.IncomingKbps calculation to significantly reduce overhead Moved websocket client creation from SocketApiClient to SocketConnection Removed DataEvent.As and DataEvent.ToCallResult methods in favor of single ToType method Removed DataEvent creation on lower levels to prevent having to create multiple versions for different result types Removed Subscription<TSubResponse, TUnsubResponse> as its no longer used Other Added null check to ParameterCollection for required parameters Added Net10.0 target framework Updated dependency versions Updated Shared asset aliases check to be culture invariant Updated Error string representation Updated some namespaces Updated SymbolOrderBook processing of buffered updates to prevent additional allocation Removed ExchangeEvent type which is no longer needed Removed unused usings
383 lines
20 KiB
C#
383 lines
20 KiB
C#
using CryptoExchange.Net.Objects;
|
|
using CryptoExchange.Net.UnitTests.TestImplementations;
|
|
using NUnit.Framework;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Net.Http;
|
|
using System.Threading.Tasks;
|
|
using System.Threading;
|
|
using NUnit.Framework.Legacy;
|
|
using CryptoExchange.Net.RateLimiting;
|
|
using CryptoExchange.Net.RateLimiting.Guards;
|
|
using CryptoExchange.Net.RateLimiting.Filters;
|
|
using CryptoExchange.Net.RateLimiting.Interfaces;
|
|
using System.Text.Json;
|
|
|
|
namespace CryptoExchange.Net.UnitTests
|
|
{
|
|
[TestFixture()]
|
|
public class RestClientTests
|
|
{
|
|
[TestCase]
|
|
public void RequestingData_Should_ResultInData()
|
|
{
|
|
// arrange
|
|
var client = new TestRestClient();
|
|
var expected = new TestObject() { DecimalData = 1.23M, IntData = 10, StringData = "Some data" };
|
|
client.SetResponse(JsonSerializer.Serialize(expected, new JsonSerializerOptions { TypeInfoResolver = new TestSerializerContext() }), out _);
|
|
|
|
// act
|
|
var result = client.Api1.Request<TestObject>().Result;
|
|
|
|
// assert
|
|
Assert.That(result.Success);
|
|
Assert.That(TestHelpers.AreEqual(expected, result.Data));
|
|
}
|
|
|
|
[TestCase]
|
|
public void ReceivingInvalidData_Should_ResultInError()
|
|
{
|
|
// arrange
|
|
var client = new TestRestClient();
|
|
client.SetResponse("{\"property\": 123", out _);
|
|
|
|
// act
|
|
var result = client.Api1.Request<TestObject>().Result;
|
|
|
|
// assert
|
|
ClassicAssert.IsFalse(result.Success);
|
|
Assert.That(result.Error != null);
|
|
}
|
|
|
|
[TestCase]
|
|
public async Task ReceivingErrorCode_Should_ResultInError()
|
|
{
|
|
// arrange
|
|
var client = new TestRestClient();
|
|
client.SetErrorWithoutResponse(System.Net.HttpStatusCode.BadRequest, "Invalid request");
|
|
|
|
// act
|
|
var result = await client.Api1.Request<TestObject>();
|
|
|
|
// assert
|
|
ClassicAssert.IsFalse(result.Success);
|
|
Assert.That(result.Error != null);
|
|
}
|
|
|
|
[TestCase]
|
|
public async Task ReceivingErrorAndNotParsingError_Should_ResultInFlatError()
|
|
{
|
|
// arrange
|
|
var client = new TestRestClient();
|
|
client.SetErrorWithResponse("{\"errorMessage\": \"Invalid request\", \"errorCode\": 123}", System.Net.HttpStatusCode.BadRequest);
|
|
|
|
// act
|
|
var result = await client.Api1.Request<TestObject>();
|
|
|
|
// assert
|
|
ClassicAssert.IsFalse(result.Success);
|
|
Assert.That(result.Error != null);
|
|
Assert.That(result.Error is ServerError);
|
|
}
|
|
|
|
[TestCase]
|
|
public async Task ReceivingErrorAndParsingError_Should_ResultInParsedError()
|
|
{
|
|
// arrange
|
|
var client = new ParseErrorTestRestClient();
|
|
client.SetErrorWithResponse("{\"errorMessage\": \"Invalid request\", \"errorCode\": 123}", System.Net.HttpStatusCode.BadRequest);
|
|
|
|
// act
|
|
var result = await client.Api2.Request<TestObject>();
|
|
|
|
// assert
|
|
ClassicAssert.IsFalse(result.Success);
|
|
Assert.That(result.Error != null);
|
|
Assert.That(result.Error is ServerError);
|
|
Assert.That(result.Error.ErrorCode == "123");
|
|
Assert.That(result.Error.Message == "Invalid request");
|
|
}
|
|
|
|
[TestCase]
|
|
public void SettingOptions_Should_ResultInOptionsSet()
|
|
{
|
|
// arrange
|
|
// act
|
|
var options = new TestClientOptions();
|
|
options.Api1Options.TimestampRecalculationInterval = TimeSpan.FromMinutes(10);
|
|
options.Api1Options.OutputOriginalData = true;
|
|
options.RequestTimeout = TimeSpan.FromMinutes(1);
|
|
var client = new TestBaseClient(options);
|
|
|
|
// assert
|
|
Assert.That(((TestClientOptions)client.ClientOptions).Api1Options.TimestampRecalculationInterval == TimeSpan.FromMinutes(10));
|
|
Assert.That(((TestClientOptions)client.ClientOptions).Api1Options.OutputOriginalData == true);
|
|
Assert.That(((TestClientOptions)client.ClientOptions).RequestTimeout == TimeSpan.FromMinutes(1));
|
|
}
|
|
|
|
[TestCase("GET", HttpMethodParameterPosition.InUri)] // No need to test InBody for GET since thats not valid
|
|
[TestCase("POST", HttpMethodParameterPosition.InBody)]
|
|
[TestCase("POST", HttpMethodParameterPosition.InUri)]
|
|
[TestCase("DELETE", HttpMethodParameterPosition.InBody)]
|
|
[TestCase("DELETE", HttpMethodParameterPosition.InUri)]
|
|
[TestCase("PUT", HttpMethodParameterPosition.InUri)]
|
|
[TestCase("PUT", HttpMethodParameterPosition.InBody)]
|
|
public async Task Setting_Should_ResultInOptionsSet(string method, HttpMethodParameterPosition pos)
|
|
{
|
|
// arrange
|
|
// act
|
|
var client = new TestRestClient();
|
|
|
|
client.Api1.SetParameterPosition(new HttpMethod(method), pos);
|
|
|
|
client.SetResponse("{}", out var request);
|
|
|
|
await client.Api1.RequestWithParams<TestObject>(new HttpMethod(method), new ParameterCollection
|
|
{
|
|
{ "TestParam1", "Value1" },
|
|
{ "TestParam2", 2 },
|
|
},
|
|
new Dictionary<string, string>
|
|
{
|
|
{ "TestHeader", "123" }
|
|
});
|
|
|
|
// assert
|
|
Assert.That(request.Method == new HttpMethod(method));
|
|
Assert.That((request.Content?.Contains("TestParam1") == true) == (pos == HttpMethodParameterPosition.InBody));
|
|
Assert.That((request.Uri.ToString().Contains("TestParam1")) == (pos == HttpMethodParameterPosition.InUri));
|
|
Assert.That((request.Content?.Contains("TestParam2") == true) == (pos == HttpMethodParameterPosition.InBody));
|
|
Assert.That((request.Uri.ToString().Contains("TestParam2")) == (pos == HttpMethodParameterPosition.InUri));
|
|
Assert.That(request.GetHeaders().First().Key == "TestHeader");
|
|
Assert.That(request.GetHeaders().First().Value.Contains("123"));
|
|
}
|
|
|
|
|
|
[TestCase(1, 0.1)]
|
|
[TestCase(2, 0.1)]
|
|
[TestCase(5, 1)]
|
|
[TestCase(1, 2)]
|
|
public async Task PartialEndpointRateLimiterBasics(int requests, double perSeconds)
|
|
{
|
|
var rateLimiter = new RateLimitGate("Test");
|
|
rateLimiter.AddGuard(new RateLimitGuard(RateLimitGuard.PerHost, new PathStartFilter("/sapi/"), requests, TimeSpan.FromSeconds(perSeconds), RateLimitWindowType.Fixed));
|
|
|
|
var triggered = false;
|
|
rateLimiter.RateLimitTriggered += (x) => { triggered = true; };
|
|
var requestDefinition = new RequestDefinition("/sapi/v1/system/status", HttpMethod.Get);
|
|
|
|
for (var i = 0; i < requests + 1; i++)
|
|
{
|
|
var result1 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Request, requestDefinition, "https://test.com", "123", 1, RateLimitingBehaviour.Wait, null, default);
|
|
Assert.That(i == requests? triggered : !triggered);
|
|
}
|
|
triggered = false;
|
|
await Task.Delay((int)Math.Round(perSeconds * 1000) + 10);
|
|
var result2 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Request, requestDefinition, "https://test.com", "123", 1, RateLimitingBehaviour.Wait, null, default);
|
|
Assert.That(!triggered);
|
|
}
|
|
|
|
[TestCase("/sapi/test1", true)]
|
|
[TestCase("/sapi/test2", true)]
|
|
[TestCase("/api/test1", false)]
|
|
[TestCase("sapi/test1", true)]
|
|
[TestCase("/sapi/", true)]
|
|
public async Task PartialEndpointRateLimiterEndpoints(string endpoint, bool expectLimiting)
|
|
{
|
|
var rateLimiter = new RateLimitGate("Test");
|
|
rateLimiter.AddGuard(new RateLimitGuard(RateLimitGuard.PerHost, new PathStartFilter("/sapi/"), 1, TimeSpan.FromSeconds(0.1), RateLimitWindowType.Fixed));
|
|
|
|
var requestDefinition = new RequestDefinition(endpoint, HttpMethod.Get);
|
|
|
|
RateLimitEvent evnt = null;
|
|
rateLimiter.RateLimitTriggered += (x) => { evnt = x; };
|
|
for (var i = 0; i < 2; i++)
|
|
{
|
|
var result1 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Request, requestDefinition, "https://test.com", "123", 1, RateLimitingBehaviour.Wait, null, default);
|
|
bool expected = i == 1 ? (expectLimiting ? evnt.DelayTime > TimeSpan.Zero : evnt == null) : evnt == null;
|
|
Assert.That(expected);
|
|
}
|
|
}
|
|
|
|
[TestCase("/sapi/", "/sapi/", true)]
|
|
[TestCase("/sapi/test", "/sapi/test", true)]
|
|
[TestCase("/sapi/test", "/sapi/test123", false)]
|
|
[TestCase("/sapi/test", "/sapi/", false)]
|
|
public async Task PartialEndpointRateLimiterEndpoints(string endpoint1, string endpoint2, bool expectLimiting)
|
|
{
|
|
var rateLimiter = new RateLimitGate("Test");
|
|
rateLimiter.AddGuard(new RateLimitGuard(RateLimitGuard.PerEndpoint, new PathStartFilter("/sapi/"), 1, TimeSpan.FromSeconds(0.1), RateLimitWindowType.Fixed));
|
|
|
|
var requestDefinition1 = new RequestDefinition(endpoint1, HttpMethod.Get);
|
|
var requestDefinition2 = new RequestDefinition(endpoint2, HttpMethod.Get);
|
|
|
|
RateLimitEvent evnt = null;
|
|
rateLimiter.RateLimitTriggered += (x) => { evnt = x; };
|
|
|
|
var result1 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Request, requestDefinition1, "https://test.com", "123", 1, RateLimitingBehaviour.Wait, null, default);
|
|
Assert.That(evnt == null);
|
|
var result2 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Request, requestDefinition2, "https://test.com", "123", 1, RateLimitingBehaviour.Wait, null, default);
|
|
Assert.That(expectLimiting ? evnt != null : evnt == null);
|
|
}
|
|
|
|
[TestCase(1, 0.1)]
|
|
[TestCase(2, 0.1)]
|
|
[TestCase(5, 1)]
|
|
[TestCase(1, 2)]
|
|
public async Task EndpointRateLimiterBasics(int requests, double perSeconds)
|
|
{
|
|
var rateLimiter = new RateLimitGate("Test");
|
|
rateLimiter.AddGuard(new RateLimitGuard(RateLimitGuard.PerEndpoint, new PathStartFilter("/sapi/test"), requests, TimeSpan.FromSeconds(perSeconds), RateLimitWindowType.Fixed));
|
|
|
|
bool triggered = false;
|
|
rateLimiter.RateLimitTriggered += (x) => { triggered = true; };
|
|
var requestDefinition = new RequestDefinition("/sapi/test", HttpMethod.Get);
|
|
|
|
for (var i = 0; i < requests + 1; i++)
|
|
{
|
|
var result1 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Request, requestDefinition, "https://test.com", "123", 1, RateLimitingBehaviour.Wait, null, default);
|
|
Assert.That(i == requests ? triggered : !triggered);
|
|
}
|
|
triggered = false;
|
|
await Task.Delay((int)Math.Round(perSeconds * 1000) + 10);
|
|
var result2 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Request, requestDefinition, "https://test.com", "123", 1, RateLimitingBehaviour.Wait, null, default);
|
|
Assert.That(!triggered);
|
|
}
|
|
|
|
[TestCase("/", false)]
|
|
[TestCase("/sapi/test", true)]
|
|
[TestCase("/sapi/test/123", false)]
|
|
public async Task EndpointRateLimiterEndpoints(string endpoint, bool expectLimited)
|
|
{
|
|
var rateLimiter = new RateLimitGate("Test");
|
|
rateLimiter.AddGuard(new RateLimitGuard(RateLimitGuard.PerEndpoint, new ExactPathFilter("/sapi/test"), 1, TimeSpan.FromSeconds(0.1), RateLimitWindowType.Fixed));
|
|
|
|
var requestDefinition = new RequestDefinition(endpoint, HttpMethod.Get);
|
|
|
|
RateLimitEvent evnt = null;
|
|
rateLimiter.RateLimitTriggered += (x) => { evnt = x; };
|
|
for (var i = 0; i < 2; i++)
|
|
{
|
|
var result1 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Request, requestDefinition, "https://test.com", "123", 1, RateLimitingBehaviour.Wait, null, default);
|
|
bool expected = i == 1 ? (expectLimited ? evnt.DelayTime > TimeSpan.Zero : evnt == null) : evnt == null;
|
|
Assert.That(expected);
|
|
}
|
|
}
|
|
|
|
[TestCase("/", false)]
|
|
[TestCase("/sapi/test", true)]
|
|
[TestCase("/sapi/test2", true)]
|
|
[TestCase("/sapi/test23", false)]
|
|
public async Task EndpointRateLimiterMultipleEndpoints(string endpoint, bool expectLimited)
|
|
{
|
|
var rateLimiter = new RateLimitGate("Test");
|
|
rateLimiter.AddGuard(new RateLimitGuard(RateLimitGuard.PerEndpoint, new ExactPathsFilter(new[] { "/sapi/test", "/sapi/test2" }), 1, TimeSpan.FromSeconds(0.1), RateLimitWindowType.Fixed));
|
|
var requestDefinition = new RequestDefinition(endpoint, HttpMethod.Get);
|
|
|
|
RateLimitEvent evnt = null;
|
|
rateLimiter.RateLimitTriggered += (x) => { evnt = x; };
|
|
for (var i = 0; i < 2; i++)
|
|
{
|
|
var result1 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Request, requestDefinition, "https://test.com", "123", 1, RateLimitingBehaviour.Wait, null, default);
|
|
bool expected = i == 1 ? (expectLimited ? evnt.DelayTime > TimeSpan.Zero : evnt == null) : evnt == null;
|
|
Assert.That(expected);
|
|
}
|
|
}
|
|
|
|
[TestCase("123", "123", "/sapi/test", "/sapi/test", true)]
|
|
[TestCase("123", "456", "/sapi/test", "/sapi/test", false)]
|
|
[TestCase("123", "123", "/sapi/test", "/sapi/test2", true)]
|
|
[TestCase("123", "123", "/sapi/test2", "/sapi/test", true)]
|
|
[TestCase(null, "123", "/sapi/test", "/sapi/test", false)]
|
|
[TestCase("123", null, "/sapi/test", "/sapi/test", false)]
|
|
[TestCase(null, null, "/sapi/test", "/sapi/test", false)]
|
|
public async Task ApiKeyRateLimiterBasics(string key1, string key2, string endpoint1, string endpoint2, bool expectLimited)
|
|
{
|
|
var rateLimiter = new RateLimitGate("Test");
|
|
rateLimiter.AddGuard(new RateLimitGuard(RateLimitGuard.PerApiKey, new AuthenticatedEndpointFilter(true), 1, TimeSpan.FromSeconds(0.1), RateLimitWindowType.Sliding));
|
|
var requestDefinition1 = new RequestDefinition(endpoint1, HttpMethod.Get) { Authenticated = key1 != null };
|
|
var requestDefinition2 = new RequestDefinition(endpoint2, HttpMethod.Get) { Authenticated = key2 != null };
|
|
|
|
RateLimitEvent evnt = null;
|
|
rateLimiter.RateLimitTriggered += (x) => { evnt = x; };
|
|
|
|
var result1 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Request, requestDefinition1, "https://test.com", key1, 1, RateLimitingBehaviour.Wait, null, default);
|
|
Assert.That(evnt == null);
|
|
var result2 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Request, requestDefinition2, "https://test.com", key2, 1, RateLimitingBehaviour.Wait, null, default);
|
|
Assert.That(expectLimited ? evnt != null : evnt == null);
|
|
}
|
|
|
|
[TestCase("/sapi/test", "/sapi/test", true)]
|
|
[TestCase("/sapi/test1", "/api/test2", true)]
|
|
[TestCase("/", "/sapi/test2", true)]
|
|
public async Task TotalRateLimiterBasics(string endpoint1, string endpoint2, bool expectLimited)
|
|
{
|
|
var rateLimiter = new RateLimitGate("Test");
|
|
rateLimiter.AddGuard(new RateLimitGuard(RateLimitGuard.PerHost, Array.Empty<IGuardFilter>(), 1, TimeSpan.FromSeconds(0.1), RateLimitWindowType.Fixed));
|
|
var requestDefinition1 = new RequestDefinition(endpoint1, HttpMethod.Get);
|
|
var requestDefinition2 = new RequestDefinition(endpoint2, HttpMethod.Get) { Authenticated = true };
|
|
|
|
RateLimitEvent evnt = null;
|
|
rateLimiter.RateLimitTriggered += (x) => { evnt = x; };
|
|
|
|
var result1 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Request, requestDefinition1, "https://test.com", "123", 1, RateLimitingBehaviour.Wait, null, default);
|
|
Assert.That(evnt == null);
|
|
var result2 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Request, requestDefinition2, "https://test.com", null, 1, RateLimitingBehaviour.Wait, null, default);
|
|
Assert.That(expectLimited ? evnt != null : evnt == null);
|
|
}
|
|
|
|
[TestCase("https://test.com", "/sapi/test", "https://test.com", "/sapi/test", true)]
|
|
[TestCase("https://test2.com", "/sapi/test", "https://test.com", "/sapi/test", false)]
|
|
[TestCase("https://test.com", "/sapi/test", "https://test2.com", "/sapi/test", false)]
|
|
[TestCase("https://test.com", "/sapi/test", "https://test.com", "/sapi/test2", true)]
|
|
public async Task HostRateLimiterBasics(string host1, string endpoint1, string host2, string endpoint2, bool expectLimited)
|
|
{
|
|
var rateLimiter = new RateLimitGate("Test");
|
|
rateLimiter.AddGuard(new RateLimitGuard(RateLimitGuard.PerHost, new HostFilter("https://test.com"), 1, TimeSpan.FromSeconds(0.1), RateLimitWindowType.Fixed));
|
|
var requestDefinition1 = new RequestDefinition(endpoint1, HttpMethod.Get);
|
|
var requestDefinition2 = new RequestDefinition(endpoint2, HttpMethod.Get) { Authenticated = true };
|
|
|
|
RateLimitEvent evnt = null;
|
|
rateLimiter.RateLimitTriggered += (x) => { evnt = x; };
|
|
|
|
var result1 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Request, requestDefinition1, host1, "123", 1, RateLimitingBehaviour.Wait, null, default);
|
|
Assert.That(evnt == null);
|
|
var result2 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Request, requestDefinition1, host2, "123", 1, RateLimitingBehaviour.Wait, null, default);
|
|
Assert.That(expectLimited ? evnt != null : evnt == null);
|
|
}
|
|
|
|
[TestCase("https://test.com", "https://test.com", true)]
|
|
[TestCase("https://test2.com", "https://test.com", false)]
|
|
[TestCase("https://test.com", "https://test2.com", false)]
|
|
public async Task ConnectionRateLimiterBasics(string host1, string host2, bool expectLimited)
|
|
{
|
|
var rateLimiter = new RateLimitGate("Test");
|
|
rateLimiter.AddGuard(new RateLimitGuard(RateLimitGuard.PerHost, new LimitItemTypeFilter(RateLimitItemType.Connection), 1, TimeSpan.FromSeconds(0.1), RateLimitWindowType.Fixed));
|
|
|
|
RateLimitEvent evnt = null;
|
|
rateLimiter.RateLimitTriggered += (x) => { evnt = x; };
|
|
|
|
var result1 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Connection, new RequestDefinition("1", HttpMethod.Get), host1, "123", 1, RateLimitingBehaviour.Wait, null, default);
|
|
Assert.That(evnt == null);
|
|
var result2 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Connection, new RequestDefinition("1", HttpMethod.Get), host2, "123", 1, RateLimitingBehaviour.Wait, null, default);
|
|
Assert.That(expectLimited ? evnt != null : evnt == null);
|
|
}
|
|
|
|
[Test]
|
|
public async Task ConnectionRateLimiterCancel()
|
|
{
|
|
var rateLimiter = new RateLimitGate("Test");
|
|
rateLimiter.AddGuard(new RateLimitGuard(RateLimitGuard.PerHost, new LimitItemTypeFilter(RateLimitItemType.Connection), 1, TimeSpan.FromSeconds(10), RateLimitWindowType.Fixed));
|
|
|
|
RateLimitEvent evnt = null;
|
|
rateLimiter.RateLimitTriggered += (x) => { evnt = x; };
|
|
var ct = new CancellationTokenSource(TimeSpan.FromSeconds(0.2));
|
|
|
|
var result1 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Connection, new RequestDefinition("1", HttpMethod.Get), "https://test.com", "123", 1, RateLimitingBehaviour.Wait, null, ct.Token);
|
|
var result2 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Connection, new RequestDefinition("1", HttpMethod.Get), "https://test.com", "123", 1, RateLimitingBehaviour.Wait, null, ct.Token);
|
|
Assert.That(result2.Error, Is.TypeOf<CancellationRequestedError>());
|
|
}
|
|
}
|
|
}
|