1
0
mirror of https://github.com/JKorf/CryptoExchange.Net synced 2025-06-08 08:26:20 +00:00

Removed SecureString usage throughout the library, removed some object allocations, removed some unused extension methods

This commit is contained in:
JKorf 2024-08-01 22:43:06 +02:00
parent 2f64cd9f05
commit 949780a9ad
25 changed files with 111 additions and 281 deletions

View File

@ -51,8 +51,8 @@ namespace CryptoExchange.Net.UnitTests
// assert // assert
Assert.That(options.ReceiveWindow == TimeSpan.FromSeconds(10)); Assert.That(options.ReceiveWindow == TimeSpan.FromSeconds(10));
Assert.That(options.ApiCredentials.Key.GetString() == "123"); Assert.That(options.ApiCredentials.Key == "123");
Assert.That(options.ApiCredentials.Secret.GetString() == "456"); Assert.That(options.ApiCredentials.Secret == "456");
} }
[Test] [Test]
@ -64,10 +64,10 @@ namespace CryptoExchange.Net.UnitTests
options.Api2Options.ApiCredentials = new ApiCredentials("789", "101"); options.Api2Options.ApiCredentials = new ApiCredentials("789", "101");
// assert // assert
Assert.That(options.Api1Options.ApiCredentials.Key.GetString() == "123"); Assert.That(options.Api1Options.ApiCredentials.Key == "123");
Assert.That(options.Api1Options.ApiCredentials.Secret.GetString() == "456"); Assert.That(options.Api1Options.ApiCredentials.Secret == "456");
Assert.That(options.Api2Options.ApiCredentials.Key.GetString() == "789"); Assert.That(options.Api2Options.ApiCredentials.Key == "789");
Assert.That(options.Api2Options.ApiCredentials.Secret.GetString() == "101"); Assert.That(options.Api2Options.ApiCredentials.Secret == "101");
} }
[Test] [Test]

View File

@ -176,12 +176,12 @@ namespace CryptoExchange.Net.UnitTests
for (var i = 0; i < requests + 1; i++) for (var i = 0; i < requests + 1; i++)
{ {
var result1 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Request, requestDefinition, "https://test.com", "123".ToSecureString(), 1, RateLimitingBehaviour.Wait, default); var result1 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Request, requestDefinition, "https://test.com", "123", 1, RateLimitingBehaviour.Wait, default);
Assert.That(i == requests? triggered : !triggered); Assert.That(i == requests? triggered : !triggered);
} }
triggered = false; triggered = false;
await Task.Delay((int)Math.Round(perSeconds * 1000) + 10); await Task.Delay((int)Math.Round(perSeconds * 1000) + 10);
var result2 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Request, requestDefinition, "https://test.com", "123".ToSecureString(), 1, RateLimitingBehaviour.Wait, default); var result2 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Request, requestDefinition, "https://test.com", "123", 1, RateLimitingBehaviour.Wait, default);
Assert.That(!triggered); Assert.That(!triggered);
} }
@ -201,7 +201,7 @@ namespace CryptoExchange.Net.UnitTests
rateLimiter.RateLimitTriggered += (x) => { evnt = x; }; rateLimiter.RateLimitTriggered += (x) => { evnt = x; };
for (var i = 0; i < 2; i++) for (var i = 0; i < 2; i++)
{ {
var result1 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Request, requestDefinition, "https://test.com", "123".ToSecureString(), 1, RateLimitingBehaviour.Wait, default); var result1 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Request, requestDefinition, "https://test.com", "123", 1, RateLimitingBehaviour.Wait, default);
bool expected = i == 1 ? (expectLimiting ? evnt.DelayTime > TimeSpan.Zero : evnt == null) : evnt == null; bool expected = i == 1 ? (expectLimiting ? evnt.DelayTime > TimeSpan.Zero : evnt == null) : evnt == null;
Assert.That(expected); Assert.That(expected);
} }
@ -222,9 +222,9 @@ namespace CryptoExchange.Net.UnitTests
RateLimitEvent evnt = null; RateLimitEvent evnt = null;
rateLimiter.RateLimitTriggered += (x) => { evnt = x; }; rateLimiter.RateLimitTriggered += (x) => { evnt = x; };
var result1 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Request, requestDefinition1, "https://test.com", "123".ToSecureString(), 1, RateLimitingBehaviour.Wait, default); var result1 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Request, requestDefinition1, "https://test.com", "123", 1, RateLimitingBehaviour.Wait, default);
Assert.That(evnt == null); Assert.That(evnt == null);
var result2 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Request, requestDefinition2, "https://test.com", "123".ToSecureString(), 1, RateLimitingBehaviour.Wait, default); var result2 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Request, requestDefinition2, "https://test.com", "123", 1, RateLimitingBehaviour.Wait, default);
Assert.That(expectLimiting ? evnt != null : evnt == null); Assert.That(expectLimiting ? evnt != null : evnt == null);
} }
@ -243,12 +243,12 @@ namespace CryptoExchange.Net.UnitTests
for (var i = 0; i < requests + 1; i++) for (var i = 0; i < requests + 1; i++)
{ {
var result1 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Request, requestDefinition, "https://test.com", "123".ToSecureString(), 1, RateLimitingBehaviour.Wait, default); var result1 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Request, requestDefinition, "https://test.com", "123", 1, RateLimitingBehaviour.Wait, default);
Assert.That(i == requests ? triggered : !triggered); Assert.That(i == requests ? triggered : !triggered);
} }
triggered = false; triggered = false;
await Task.Delay((int)Math.Round(perSeconds * 1000) + 10); await Task.Delay((int)Math.Round(perSeconds * 1000) + 10);
var result2 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Request, requestDefinition, "https://test.com", "123".ToSecureString(), 1, RateLimitingBehaviour.Wait, default); var result2 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Request, requestDefinition, "https://test.com", "123", 1, RateLimitingBehaviour.Wait, default);
Assert.That(!triggered); Assert.That(!triggered);
} }
@ -266,7 +266,7 @@ namespace CryptoExchange.Net.UnitTests
rateLimiter.RateLimitTriggered += (x) => { evnt = x; }; rateLimiter.RateLimitTriggered += (x) => { evnt = x; };
for (var i = 0; i < 2; i++) for (var i = 0; i < 2; i++)
{ {
var result1 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Request, requestDefinition, "https://test.com", "123".ToSecureString(), 1, RateLimitingBehaviour.Wait, default); var result1 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Request, requestDefinition, "https://test.com", "123", 1, RateLimitingBehaviour.Wait, default);
bool expected = i == 1 ? (expectLimited ? evnt.DelayTime > TimeSpan.Zero : evnt == null) : evnt == null; bool expected = i == 1 ? (expectLimited ? evnt.DelayTime > TimeSpan.Zero : evnt == null) : evnt == null;
Assert.That(expected); Assert.That(expected);
} }
@ -286,7 +286,7 @@ namespace CryptoExchange.Net.UnitTests
rateLimiter.RateLimitTriggered += (x) => { evnt = x; }; rateLimiter.RateLimitTriggered += (x) => { evnt = x; };
for (var i = 0; i < 2; i++) for (var i = 0; i < 2; i++)
{ {
var result1 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Request, requestDefinition, "https://test.com", "123".ToSecureString(), 1, RateLimitingBehaviour.Wait, default); var result1 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Request, requestDefinition, "https://test.com", "123", 1, RateLimitingBehaviour.Wait, default);
bool expected = i == 1 ? (expectLimited ? evnt.DelayTime > TimeSpan.Zero : evnt == null) : evnt == null; bool expected = i == 1 ? (expectLimited ? evnt.DelayTime > TimeSpan.Zero : evnt == null) : evnt == null;
Assert.That(expected); Assert.That(expected);
} }
@ -309,9 +309,9 @@ namespace CryptoExchange.Net.UnitTests
RateLimitEvent evnt = null; RateLimitEvent evnt = null;
rateLimiter.RateLimitTriggered += (x) => { evnt = x; }; rateLimiter.RateLimitTriggered += (x) => { evnt = x; };
var result1 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Request, requestDefinition1, "https://test.com", key1?.ToSecureString(), 1, RateLimitingBehaviour.Wait, default); var result1 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Request, requestDefinition1, "https://test.com", key1, 1, RateLimitingBehaviour.Wait, default);
Assert.That(evnt == null); Assert.That(evnt == null);
var result2 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Request, requestDefinition2, "https://test.com", key2?.ToSecureString(), 1, RateLimitingBehaviour.Wait, default); var result2 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Request, requestDefinition2, "https://test.com", key2, 1, RateLimitingBehaviour.Wait, default);
Assert.That(expectLimited ? evnt != null : evnt == null); Assert.That(expectLimited ? evnt != null : evnt == null);
} }
@ -328,7 +328,7 @@ namespace CryptoExchange.Net.UnitTests
RateLimitEvent evnt = null; RateLimitEvent evnt = null;
rateLimiter.RateLimitTriggered += (x) => { evnt = x; }; rateLimiter.RateLimitTriggered += (x) => { evnt = x; };
var result1 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Request, requestDefinition1, "https://test.com", "123".ToSecureString(), 1, RateLimitingBehaviour.Wait, default); var result1 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Request, requestDefinition1, "https://test.com", "123", 1, RateLimitingBehaviour.Wait, default);
Assert.That(evnt == null); Assert.That(evnt == null);
var result2 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Request, requestDefinition2, "https://test.com", null, 1, RateLimitingBehaviour.Wait, default); var result2 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Request, requestDefinition2, "https://test.com", null, 1, RateLimitingBehaviour.Wait, default);
Assert.That(expectLimited ? evnt != null : evnt == null); Assert.That(expectLimited ? evnt != null : evnt == null);
@ -348,9 +348,9 @@ namespace CryptoExchange.Net.UnitTests
RateLimitEvent evnt = null; RateLimitEvent evnt = null;
rateLimiter.RateLimitTriggered += (x) => { evnt = x; }; rateLimiter.RateLimitTriggered += (x) => { evnt = x; };
var result1 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Request, requestDefinition1, host1, "123".ToSecureString(), 1, RateLimitingBehaviour.Wait, default); var result1 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Request, requestDefinition1, host1, "123", 1, RateLimitingBehaviour.Wait, default);
Assert.That(evnt == null); Assert.That(evnt == null);
var result2 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Request, requestDefinition1, host2, "123".ToSecureString(), 1, RateLimitingBehaviour.Wait, default); var result2 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Request, requestDefinition1, host2, "123", 1, RateLimitingBehaviour.Wait, default);
Assert.That(expectLimited ? evnt != null : evnt == null); Assert.That(expectLimited ? evnt != null : evnt == null);
} }
@ -365,9 +365,9 @@ namespace CryptoExchange.Net.UnitTests
RateLimitEvent evnt = null; RateLimitEvent evnt = null;
rateLimiter.RateLimitTriggered += (x) => { evnt = x; }; rateLimiter.RateLimitTriggered += (x) => { evnt = x; };
var result1 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Connection, new RequestDefinition("1", HttpMethod.Get), host1, "123".ToSecureString(), 1, RateLimitingBehaviour.Wait, default); var result1 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Connection, new RequestDefinition("1", HttpMethod.Get), host1, "123", 1, RateLimitingBehaviour.Wait, default);
Assert.That(evnt == null); Assert.That(evnt == null);
var result2 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Connection, new RequestDefinition("1", HttpMethod.Get), host2, "123".ToSecureString(), 1, RateLimitingBehaviour.Wait, default); var result2 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Connection, new RequestDefinition("1", HttpMethod.Get), host2, "123", 1, RateLimitingBehaviour.Wait, default);
Assert.That(expectLimited ? evnt != null : evnt == null); Assert.That(expectLimited ? evnt != null : evnt == null);
} }
} }

View File

@ -72,7 +72,7 @@ namespace CryptoExchange.Net.UnitTests
{ {
} }
public string GetKey() => _credentials.Key.GetString(); public string GetKey() => _credentials.Key;
public string GetSecret() => _credentials.Secret.GetString(); public string GetSecret() => _credentials.Secret;
} }
} }

View File

@ -1,6 +1,5 @@
using System; using System;
using System.IO; using System.IO;
using System.Security;
using CryptoExchange.Net.Converters.SystemTextJson; using CryptoExchange.Net.Converters.SystemTextJson;
using CryptoExchange.Net.Converters.MessageParsing; using CryptoExchange.Net.Converters.MessageParsing;
@ -9,48 +8,23 @@ namespace CryptoExchange.Net.Authentication
/// <summary> /// <summary>
/// Api credentials, used to sign requests accessing private endpoints /// Api credentials, used to sign requests accessing private endpoints
/// </summary> /// </summary>
public class ApiCredentials: IDisposable public class ApiCredentials
{ {
/// <summary> /// <summary>
/// The api key to authenticate requests /// The api key to authenticate requests
/// </summary> /// </summary>
public SecureString? Key { get; } public string Key { get; }
/// <summary> /// <summary>
/// The api secret to authenticate requests /// The api secret to authenticate requests
/// </summary> /// </summary>
public SecureString? Secret { get; } public string Secret { get; }
/// <summary> /// <summary>
/// Type of the credentials /// Type of the credentials
/// </summary> /// </summary>
public ApiCredentialsType CredentialType { get; } public ApiCredentialsType CredentialType { get; }
/// <summary>
/// Create Api credentials providing an api key and secret for authentication
/// </summary>
/// <param name="key">The api key used for identification</param>
/// <param name="secret">The api secret used for signing</param>
public ApiCredentials(SecureString key, SecureString secret) : this(key, secret, ApiCredentialsType.Hmac)
{
}
/// <summary>
/// Create Api credentials providing an api key and secret for authentication
/// </summary>
/// <param name="key">The api key used for identification</param>
/// <param name="secret">The api secret used for signing</param>
/// <param name="credentialsType">The type of credentials</param>
public ApiCredentials(SecureString key, SecureString secret, ApiCredentialsType credentialsType)
{
if (key == null || secret == null)
throw new ArgumentException("Key and secret can't be null/empty");
CredentialType = credentialsType;
Key = key;
Secret = secret;
}
/// <summary> /// <summary>
/// Create Api credentials providing an api key and secret for authentication /// Create Api credentials providing an api key and secret for authentication
/// </summary> /// </summary>
@ -72,8 +46,8 @@ namespace CryptoExchange.Net.Authentication
throw new ArgumentException("Key and secret can't be null/empty"); throw new ArgumentException("Key and secret can't be null/empty");
CredentialType = credentialsType; CredentialType = credentialsType;
Key = key.ToSecureString(); Key = key;
Secret = secret.ToSecureString(); Secret = secret;
} }
/// <summary> /// <summary>
@ -82,8 +56,7 @@ namespace CryptoExchange.Net.Authentication
/// <returns></returns> /// <returns></returns>
public virtual ApiCredentials Copy() public virtual ApiCredentials Copy()
{ {
// Use .GetString() to create a copy of the SecureString return new ApiCredentials(Key, Secret, CredentialType);
return new ApiCredentials(Key!.GetString(), Secret!.GetString(), CredentialType);
} }
/// <summary> /// <summary>
@ -103,19 +76,10 @@ namespace CryptoExchange.Net.Authentication
if (key == null || secret == null) if (key == null || secret == null)
throw new ArgumentException("apiKey or apiSecret value not found in Json credential file"); throw new ArgumentException("apiKey or apiSecret value not found in Json credential file");
Key = key.ToSecureString(); Key = key;
Secret = secret.ToSecureString(); Secret = secret;
inputStream.Seek(0, SeekOrigin.Begin); inputStream.Seek(0, SeekOrigin.Begin);
} }
/// <summary>
/// Dispose
/// </summary>
public void Dispose()
{
Key?.Dispose();
Secret?.Dispose();
}
} }
} }

View File

@ -14,7 +14,7 @@ namespace CryptoExchange.Net.Authentication
/// <summary> /// <summary>
/// Base class for authentication providers /// Base class for authentication providers
/// </summary> /// </summary>
public abstract class AuthenticationProvider : IDisposable public abstract class AuthenticationProvider
{ {
internal IAuthTimeProvider TimeProvider { get; set; } = new AuthTimeProvider(); internal IAuthTimeProvider TimeProvider { get; set; } = new AuthTimeProvider();
@ -28,6 +28,11 @@ namespace CryptoExchange.Net.Authentication
/// </summary> /// </summary>
protected byte[] _sBytes; protected byte[] _sBytes;
/// <summary>
/// Get the API key of the current credentials
/// </summary>
public string ApiKey => _credentials.Key;
/// <summary> /// <summary>
/// ctor /// ctor
/// </summary> /// </summary>
@ -38,7 +43,7 @@ namespace CryptoExchange.Net.Authentication
throw new ArgumentException("ApiKey/Secret needed"); throw new ArgumentException("ApiKey/Secret needed");
_credentials = credentials; _credentials = credentials;
_sBytes = Encoding.UTF8.GetBytes(credentials.Secret.GetString()); _sBytes = Encoding.UTF8.GetBytes(credentials.Secret);
} }
/// <summary> /// <summary>
@ -58,9 +63,9 @@ namespace CryptoExchange.Net.Authentication
RestApiClient apiClient, RestApiClient apiClient,
Uri uri, Uri uri,
HttpMethod method, HttpMethod method,
IDictionary<string, object> uriParameters, ref IDictionary<string, object>? uriParameters,
IDictionary<string, object> bodyParameters, ref IDictionary<string, object>? bodyParameters,
Dictionary<string, string> headers, ref Dictionary<string, string>? headers,
bool auth, bool auth,
ArrayParametersSerialization arraySerialization, ArrayParametersSerialization arraySerialization,
HttpMethodParameterPosition parameterPosition, HttpMethodParameterPosition parameterPosition,
@ -366,7 +371,7 @@ namespace CryptoExchange.Net.Authentication
{ {
#if NETSTANDARD2_1_OR_GREATER #if NETSTANDARD2_1_OR_GREATER
// Read from pem private key // Read from pem private key
var key = _credentials.Secret!.GetString() var key = _credentials.Secret!
.Replace("\n", "") .Replace("\n", "")
.Replace("-----BEGIN PRIVATE KEY-----", "") .Replace("-----BEGIN PRIVATE KEY-----", "")
.Replace("-----END PRIVATE KEY-----", "") .Replace("-----END PRIVATE KEY-----", "")
@ -381,7 +386,7 @@ namespace CryptoExchange.Net.Authentication
else if (_credentials.CredentialType == ApiCredentialsType.RsaXml) else if (_credentials.CredentialType == ApiCredentialsType.RsaXml)
{ {
// Read from xml private key format // Read from xml private key format
rsa.FromXmlString(_credentials.Secret!.GetString()); rsa.FromXmlString(_credentials.Secret!);
} }
else else
{ {
@ -447,12 +452,6 @@ namespace CryptoExchange.Net.Authentication
else else
return serializer.Serialize(parameters); return serializer.Serialize(parameters);
} }
/// <inheritdoc />
public void Dispose()
{
_credentials?.Dispose();
}
} }
/// <inheritdoc /> /// <inheritdoc />

View File

@ -65,10 +65,7 @@ namespace CryptoExchange.Net.Clients
BaseAddress = baseAddress; BaseAddress = baseAddress;
if (apiCredentials != null) if (apiCredentials != null)
{
AuthenticationProvider?.Dispose();
AuthenticationProvider = CreateAuthenticationProvider(apiCredentials.Copy()); AuthenticationProvider = CreateAuthenticationProvider(apiCredentials.Copy());
}
} }
/// <summary> /// <summary>
@ -85,10 +82,7 @@ namespace CryptoExchange.Net.Clients
public void SetApiCredentials<T>(T credentials) where T : ApiCredentials public void SetApiCredentials<T>(T credentials) where T : ApiCredentials
{ {
if (credentials != null) if (credentials != null)
{
AuthenticationProvider?.Dispose();
AuthenticationProvider = CreateAuthenticationProvider(credentials.Copy()); AuthenticationProvider = CreateAuthenticationProvider(credentials.Copy());
}
} }
/// <summary> /// <summary>
@ -97,7 +91,6 @@ namespace CryptoExchange.Net.Clients
public virtual void Dispose() public virtual void Dispose()
{ {
_disposing = true; _disposing = true;
AuthenticationProvider?.Dispose();
} }
} }
} }

View File

@ -196,19 +196,20 @@ namespace CryptoExchange.Net.Clients
Dictionary<string, string>? additionalHeaders = null, Dictionary<string, string>? additionalHeaders = null,
int? weight = null) where T : class int? weight = null) where T : class
{ {
var key = baseAddress + definition + uriParameters?.ToFormData(); string? cacheKey = null;
if (ShouldCache(definition)) if (ShouldCache(definition))
{ {
_logger.CheckingCache(key); cacheKey = baseAddress + definition + uriParameters?.ToFormData();
var cachedValue = _cache.Get(key, ClientOptions.CachingMaxAge); _logger.CheckingCache(cacheKey);
var cachedValue = _cache.Get(cacheKey, ClientOptions.CachingMaxAge);
if (cachedValue != null) if (cachedValue != null)
{ {
_logger.CacheHit(key); _logger.CacheHit(cacheKey);
var original = (WebCallResult<T>)cachedValue; var original = (WebCallResult<T>)cachedValue;
return original.Cached(); return original.Cached();
} }
_logger.CacheNotHit(key); _logger.CacheNotHit(cacheKey);
} }
int currentTry = 0; int currentTry = 0;
@ -242,7 +243,7 @@ namespace CryptoExchange.Net.Clients
if (result.Success && if (result.Success &&
ShouldCache(definition)) ShouldCache(definition))
{ {
_cache.Add(key, result); _cache.Add(cacheKey!, result);
} }
return result; return result;
@ -343,15 +344,15 @@ namespace CryptoExchange.Net.Clients
ParameterCollection? bodyParameters, ParameterCollection? bodyParameters,
Dictionary<string, string>? additionalHeaders) Dictionary<string, string>? additionalHeaders)
{ {
var uriParams = uriParameters == null ? new ParameterCollection() : CreateParameterDictionary(uriParameters); var uriParams = uriParameters == null ? null : CreateParameterDictionary(uriParameters);
var bodyParams = bodyParameters == null ? new ParameterCollection() : CreateParameterDictionary(bodyParameters); var bodyParams = bodyParameters == null ? null : CreateParameterDictionary(bodyParameters);
var uri = new Uri(baseAddress.AppendPath(definition.Path)); var uri = new Uri(baseAddress.AppendPath(definition.Path));
var arraySerialization = definition.ArraySerialization ?? ArraySerialization; var arraySerialization = definition.ArraySerialization ?? ArraySerialization;
var bodyFormat = definition.RequestBodyFormat ?? RequestBodyFormat; var bodyFormat = definition.RequestBodyFormat ?? RequestBodyFormat;
var parameterPosition = definition.ParameterPosition ?? ParameterPositions[definition.Method]; var parameterPosition = definition.ParameterPosition ?? ParameterPositions[definition.Method];
var headers = new Dictionary<string, string>(); Dictionary<string, string>? headers = null;
if (AuthenticationProvider != null) if (AuthenticationProvider != null)
{ {
try try
@ -360,9 +361,9 @@ namespace CryptoExchange.Net.Clients
this, this,
uri, uri,
definition.Method, definition.Method,
uriParams, ref uriParams,
bodyParams, ref bodyParams,
headers, ref headers,
definition.Authenticated, definition.Authenticated,
arraySerialization, arraySerialization,
parameterPosition, parameterPosition,
@ -376,13 +377,17 @@ namespace CryptoExchange.Net.Clients
} }
// Add the auth parameters to the uri, start with a new URI to be able to sort the parameters including the auth parameters // Add the auth parameters to the uri, start with a new URI to be able to sort the parameters including the auth parameters
uri = uri.SetParameters(uriParams, arraySerialization); if (uriParams != null)
uri = uri.SetParameters(uriParams, arraySerialization);
var request = RequestFactory.Create(definition.Method, uri, requestId); var request = RequestFactory.Create(definition.Method, uri, requestId);
request.Accept = Constants.JsonContentHeader; request.Accept = Constants.JsonContentHeader;
foreach (var header in headers) if (headers != null)
request.AddHeader(header.Key, header.Value); {
foreach (var header in headers)
request.AddHeader(header.Key, header.Value);
}
if (additionalHeaders != null) if (additionalHeaders != null)
{ {
@ -403,7 +408,7 @@ namespace CryptoExchange.Net.Clients
if (parameterPosition == HttpMethodParameterPosition.InBody) if (parameterPosition == HttpMethodParameterPosition.InBody)
{ {
var contentType = bodyFormat == RequestBodyFormat.Json ? Constants.JsonContentHeader : Constants.FormContentHeader; var contentType = bodyFormat == RequestBodyFormat.Json ? Constants.JsonContentHeader : Constants.FormContentHeader;
if (bodyParams.Count != 0) if (bodyParams != null && bodyParams.Count != 0)
WriteParamBody(request, bodyParams, contentType); WriteParamBody(request, bodyParams, contentType);
else else
request.SetContent(RequestBodyEmptyContent, contentType); request.SetContent(RequestBodyEmptyContent, contentType);
@ -817,9 +822,9 @@ namespace CryptoExchange.Net.Clients
this, this,
uri, uri,
method, method,
uriParameters, ref uriParameters,
bodyParameters, ref bodyParameters,
headers, ref headers,
signed, signed,
arraySerialization, arraySerialization,
parameterPosition, parameterPosition,

View File

@ -1,6 +1,7 @@
using CryptoExchange.Net.Objects; using CryptoExchange.Net.Objects;
using System; using System;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Threading;
namespace CryptoExchange.Net namespace CryptoExchange.Net
{ {
@ -15,10 +16,6 @@ namespace CryptoExchange.Net
/// The last used id, use NextId() to get the next id and up this /// The last used id, use NextId() to get the next id and up this
/// </summary> /// </summary>
private static int _lastId; private static int _lastId;
/// <summary>
/// Lock for id generating
/// </summary>
private static object _idLock = new();
/// <summary> /// <summary>
/// Clamp a value between a min and max /// Clamp a value between a min and max
@ -135,24 +132,13 @@ namespace CryptoExchange.Net
/// Generate a new unique id. The id is staticly stored so it is guarenteed to be unique /// Generate a new unique id. The id is staticly stored so it is guarenteed to be unique
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public static int NextId() public static int NextId() => Interlocked.Increment(ref _lastId);
{
lock (_idLock)
{
_lastId += 1;
return _lastId;
}
}
/// <summary> /// <summary>
/// Return the last unique id that was generated /// Return the last unique id that was generated
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public static int LastId() public static int LastId() => _lastId;
{
lock (_idLock)
return _lastId;
}
/// <summary> /// <summary>
/// Generate a random string of specified length /// Generate a random string of specified length

View File

@ -113,92 +113,6 @@ namespace CryptoExchange.Net
return formData.ToString(); return formData.ToString();
} }
/// <summary>
/// Get the string the secure string is representing
/// </summary>
/// <param name="source">The source secure string</param>
/// <returns></returns>
public static string GetString(this SecureString source)
{
lock (source)
{
string result;
var length = source.Length;
var pointer = IntPtr.Zero;
var chars = new char[length];
try
{
pointer = Marshal.SecureStringToBSTR(source);
Marshal.Copy(pointer, chars, 0, length);
result = string.Join("", chars);
}
finally
{
if (pointer != IntPtr.Zero)
{
Marshal.ZeroFreeBSTR(pointer);
}
}
return result;
}
}
/// <summary>
/// Are 2 secure strings equal
/// </summary>
/// <param name="ss1">Source secure string</param>
/// <param name="ss2">Compare secure string</param>
/// <returns>True if equal by value</returns>
public static bool IsEqualTo(this SecureString ss1, SecureString ss2)
{
IntPtr bstr1 = IntPtr.Zero;
IntPtr bstr2 = IntPtr.Zero;
try
{
bstr1 = Marshal.SecureStringToBSTR(ss1);
bstr2 = Marshal.SecureStringToBSTR(ss2);
int length1 = Marshal.ReadInt32(bstr1, -4);
int length2 = Marshal.ReadInt32(bstr2, -4);
if (length1 == length2)
{
for (int x = 0; x < length1; ++x)
{
byte b1 = Marshal.ReadByte(bstr1, x);
byte b2 = Marshal.ReadByte(bstr2, x);
if (b1 != b2) return false;
}
}
else
{
return false;
}
return true;
}
finally
{
if (bstr2 != IntPtr.Zero) Marshal.ZeroFreeBSTR(bstr2);
if (bstr1 != IntPtr.Zero) Marshal.ZeroFreeBSTR(bstr1);
}
}
/// <summary>
/// Create a secure string from a string
/// </summary>
/// <param name="source"></param>
/// <returns></returns>
public static SecureString ToSecureString(this string source)
{
var secureString = new SecureString();
foreach (var c in source)
secureString.AppendChar(c);
secureString.MakeReadOnly();
return secureString;
}
/// <summary> /// <summary>
/// Validates an int is one of the allowed values /// Validates an int is one of the allowed values
/// </summary> /// </summary>
@ -318,26 +232,6 @@ namespace CryptoExchange.Net
return url.TrimEnd('/'); return url.TrimEnd('/');
} }
/// <summary>
/// Fill parameters in a path. Parameters are specified by '{}' and should be specified in occuring sequence
/// </summary>
/// <param name="path">The total path string</param>
/// <param name="values">The values to fill</param>
/// <returns></returns>
public static string FillPathParameters(this string path, params string[] values)
{
foreach (var value in values)
{
var index = path.IndexOf("{}", StringComparison.Ordinal);
if (index >= 0)
{
path = path.Remove(index, 2);
path = path.Insert(index, value);
}
}
return path;
}
/// <summary> /// <summary>
/// Create a new uri with the provided parameters as query /// Create a new uri with the provided parameters as query
/// </summary> /// </summary>

View File

@ -24,6 +24,6 @@ namespace CryptoExchange.Net.Interfaces
/// <param name="requestWeight">The weight of the request</param> /// <param name="requestWeight">The weight of the request</param>
/// <param name="ct">Cancellation token to cancel waiting</param> /// <param name="ct">Cancellation token to cancel waiting</param>
/// <returns>The time in milliseconds spend waiting</returns> /// <returns>The time in milliseconds spend waiting</returns>
Task<CallResult<int>> LimitRequestAsync(ILogger log, string endpoint, HttpMethod method, bool signed, SecureString? apiKey, RateLimitingBehaviour limitBehaviour, int requestWeight, CancellationToken ct); Task<CallResult<int>> LimitRequestAsync(ILogger log, string endpoint, HttpMethod method, bool signed, string? apiKey, RateLimitingBehaviour limitBehaviour, int requestWeight, CancellationToken ct);
} }
} }

View File

@ -24,14 +24,14 @@ namespace CryptoExchange.Net.Objects
/// <summary> /// <summary>
/// The password of the proxy /// The password of the proxy
/// </summary> /// </summary>
public SecureString? Password { get; } public string? Password { get; }
/// <summary> /// <summary>
/// Create new settings for a proxy /// Create new settings for a proxy
/// </summary> /// </summary>
/// <param name="host">The proxy hostname/ip</param> /// <param name="host">The proxy hostname/ip</param>
/// <param name="port">The proxy port</param> /// <param name="port">The proxy port</param>
public ApiProxy(string host, int port): this(host, port, null, (SecureString?)null) public ApiProxy(string host, int port): this(host, port, null, null)
{ {
} }
@ -42,18 +42,7 @@ namespace CryptoExchange.Net.Objects
/// <param name="port">The proxy port</param> /// <param name="port">The proxy port</param>
/// <param name="login">The proxy login</param> /// <param name="login">The proxy login</param>
/// <param name="password">The proxy password</param> /// <param name="password">The proxy password</param>
public ApiProxy(string host, int port, string? login, string? password) : this(host, port, login, password?.ToSecureString()) public ApiProxy(string host, int port, string? login, string? password)
{
}
/// <summary>
/// Create new settings for a proxy
/// </summary>
/// <param name="host">The proxy hostname/ip</param>
/// <param name="port">The proxy port</param>
/// <param name="login">The proxy login</param>
/// <param name="password">The proxy password</param>
public ApiProxy(string host, int port, string? login, SecureString? password)
{ {
Host = host; Host = host;
Port = port; Port = port;

View File

@ -21,7 +21,7 @@ namespace CryptoExchange.Net.RateLimiting.Filters
} }
/// <inheritdoc /> /// <inheritdoc />
public bool Passes(RateLimitItemType type, RequestDefinition definition, string host, SecureString? apiKey) public bool Passes(RateLimitItemType type, RequestDefinition definition, string host, string? apiKey)
=> definition.Authenticated == _authenticated; => definition.Authenticated == _authenticated;
} }
} }

View File

@ -24,7 +24,7 @@ namespace CryptoExchange.Net.RateLimiting.Filters
} }
/// <inheritdoc /> /// <inheritdoc />
public bool Passes(RateLimitItemType type, RequestDefinition definition, string host, SecureString? apiKey) public bool Passes(RateLimitItemType type, RequestDefinition definition, string host, string? apiKey)
=> string.Equals(definition.Path, _path, StringComparison.OrdinalIgnoreCase); => string.Equals(definition.Path, _path, StringComparison.OrdinalIgnoreCase);
} }
} }

View File

@ -22,7 +22,7 @@ namespace CryptoExchange.Net.RateLimiting.Filters
} }
/// <inheritdoc /> /// <inheritdoc />
public bool Passes(RateLimitItemType type, RequestDefinition definition, string host, SecureString? apiKey) public bool Passes(RateLimitItemType type, RequestDefinition definition, string host, string? apiKey)
=> _paths.Contains(definition.Path); => _paths.Contains(definition.Path);
} }
} }

View File

@ -21,7 +21,7 @@ namespace CryptoExchange.Net.RateLimiting.Filters
} }
/// <inheritdoc /> /// <inheritdoc />
public bool Passes(RateLimitItemType type, RequestDefinition definition, string host, SecureString? apiKey) public bool Passes(RateLimitItemType type, RequestDefinition definition, string host, string? apiKey)
=> host == _host; => host == _host;
} }

View File

@ -21,7 +21,7 @@ namespace CryptoExchange.Net.RateLimiting.Filters
} }
/// <inheritdoc /> /// <inheritdoc />
public bool Passes(RateLimitItemType type, RequestDefinition definition, string host, SecureString? apiKey) public bool Passes(RateLimitItemType type, RequestDefinition definition, string host, string? apiKey)
=> type == _type; => type == _type;
} }
} }

View File

@ -22,7 +22,7 @@ namespace CryptoExchange.Net.RateLimiting.Filters
} }
/// <inheritdoc /> /// <inheritdoc />
public bool Passes(RateLimitItemType type, RequestDefinition definition, string host, SecureString? apiKey) public bool Passes(RateLimitItemType type, RequestDefinition definition, string host, string? apiKey)
=> definition.Path.StartsWith(_path, StringComparison.OrdinalIgnoreCase); => definition.Path.StartsWith(_path, StringComparison.OrdinalIgnoreCase);
} }
} }

View File

@ -14,26 +14,26 @@ namespace CryptoExchange.Net.RateLimiting.Guards
/// <summary> /// <summary>
/// Apply guard per host /// Apply guard per host
/// </summary> /// </summary>
public static Func<RequestDefinition, string, SecureString?, string> PerHost { get; } = new Func<RequestDefinition, string, SecureString?, string>((def, host, key) => host); public static Func<RequestDefinition, string, string?, string> PerHost { get; } = new Func<RequestDefinition, string, string?, string>((def, host, key) => host);
/// <summary> /// <summary>
/// Apply guard per endpoint /// Apply guard per endpoint
/// </summary> /// </summary>
public static Func<RequestDefinition, string, SecureString?, string> PerEndpoint { get; } = new Func<RequestDefinition, string, SecureString?, string>((def, host, key) => def.Path + def.Method); public static Func<RequestDefinition, string, string?, string> PerEndpoint { get; } = new Func<RequestDefinition, string, string?, string>((def, host, key) => def.Path + def.Method);
/// <summary> /// <summary>
/// Apply guard per API key /// Apply guard per API key
/// </summary> /// </summary>
public static Func<RequestDefinition, string, SecureString?, string> PerApiKey { get; } = new Func<RequestDefinition, string, SecureString?, string>((def, host, key) => key!.GetString()); public static Func<RequestDefinition, string, string?, string> PerApiKey { get; } = new Func<RequestDefinition, string, string?, string>((def, host, key) => key!);
/// <summary> /// <summary>
/// Apply guard per API key per endpoint /// Apply guard per API key per endpoint
/// </summary> /// </summary>
public static Func<RequestDefinition, string, SecureString?, string> PerApiKeyPerEndpoint { get; } = new Func<RequestDefinition, string, SecureString?, string>((def, host, key) => key!.GetString() + def.Path + def.Method); public static Func<RequestDefinition, string, string?, string> PerApiKeyPerEndpoint { get; } = new Func<RequestDefinition, string, string?, string>((def, host, key) => key! + def.Path + def.Method);
private readonly IEnumerable<IGuardFilter> _filters; private readonly IEnumerable<IGuardFilter> _filters;
private readonly Dictionary<string, IWindowTracker> _trackers; private readonly Dictionary<string, IWindowTracker> _trackers;
private RateLimitWindowType _windowType; private RateLimitWindowType _windowType;
private double? _decayRate; private double? _decayRate;
private int? _connectionWeight; private int? _connectionWeight;
private readonly Func<RequestDefinition, string, SecureString?, string> _keySelector; private readonly Func<RequestDefinition, string, string?, string> _keySelector;
/// <inheritdoc /> /// <inheritdoc />
public string Name => "RateLimitGuard"; public string Name => "RateLimitGuard";
@ -60,7 +60,7 @@ namespace CryptoExchange.Net.RateLimiting.Guards
/// <param name="windowType">Type of rate limit window</param> /// <param name="windowType">Type of rate limit window</param>
/// <param name="decayPerTimeSpan">The decay per timespan if windowType is DecayWindowTracker</param> /// <param name="decayPerTimeSpan">The decay per timespan if windowType is DecayWindowTracker</param>
/// <param name="connectionWeight">The weight of a new connection</param> /// <param name="connectionWeight">The weight of a new connection</param>
public RateLimitGuard(Func<RequestDefinition, string, SecureString?, string> keySelector, IGuardFilter filter, int limit, TimeSpan timeSpan, RateLimitWindowType windowType, double? decayPerTimeSpan = null, int? connectionWeight = null) public RateLimitGuard(Func<RequestDefinition, string, string?, string> keySelector, IGuardFilter filter, int limit, TimeSpan timeSpan, RateLimitWindowType windowType, double? decayPerTimeSpan = null, int? connectionWeight = null)
: this(keySelector, new[] { filter }, limit, timeSpan, windowType, decayPerTimeSpan, connectionWeight) : this(keySelector, new[] { filter }, limit, timeSpan, windowType, decayPerTimeSpan, connectionWeight)
{ {
} }
@ -75,7 +75,7 @@ namespace CryptoExchange.Net.RateLimiting.Guards
/// <param name="windowType">Type of rate limit window</param> /// <param name="windowType">Type of rate limit window</param>
/// <param name="decayPerTimeSpan">The decay per timespan if windowType is DecayWindowTracker</param> /// <param name="decayPerTimeSpan">The decay per timespan if windowType is DecayWindowTracker</param>
/// <param name="connectionWeight">The weight of a new connection</param> /// <param name="connectionWeight">The weight of a new connection</param>
public RateLimitGuard(Func<RequestDefinition, string, SecureString?, string> keySelector, IEnumerable<IGuardFilter> filters, int limit, TimeSpan timeSpan, RateLimitWindowType windowType, double? decayPerTimeSpan = null, int? connectionWeight = null) public RateLimitGuard(Func<RequestDefinition, string, string?, string> keySelector, IEnumerable<IGuardFilter> filters, int limit, TimeSpan timeSpan, RateLimitWindowType windowType, double? decayPerTimeSpan = null, int? connectionWeight = null)
{ {
_filters = filters; _filters = filters;
_trackers = new Dictionary<string, IWindowTracker>(); _trackers = new Dictionary<string, IWindowTracker>();
@ -88,7 +88,7 @@ namespace CryptoExchange.Net.RateLimiting.Guards
} }
/// <inheritdoc /> /// <inheritdoc />
public LimitCheck Check(RateLimitItemType type, RequestDefinition definition, string host, SecureString? apiKey, int requestWeight) public LimitCheck Check(RateLimitItemType type, RequestDefinition definition, string host, string? apiKey, int requestWeight)
{ {
foreach(var filter in _filters) foreach(var filter in _filters)
{ {
@ -114,7 +114,7 @@ namespace CryptoExchange.Net.RateLimiting.Guards
} }
/// <inheritdoc /> /// <inheritdoc />
public RateLimitState ApplyWeight(RateLimitItemType type, RequestDefinition definition, string host, SecureString? apiKey, int requestWeight) public RateLimitState ApplyWeight(RateLimitItemType type, RequestDefinition definition, string host, string? apiKey, int requestWeight)
{ {
foreach (var filter in _filters) foreach (var filter in _filters)
{ {

View File

@ -38,7 +38,7 @@ namespace CryptoExchange.Net.RateLimiting.Guards
} }
/// <inheritdoc /> /// <inheritdoc />
public LimitCheck Check(RateLimitItemType type, RequestDefinition definition, string host, SecureString? apiKey, int requestWeight) public LimitCheck Check(RateLimitItemType type, RequestDefinition definition, string host, string? apiKey, int requestWeight)
{ {
var dif = (After + _windowBuffer) - DateTime.UtcNow; var dif = (After + _windowBuffer) - DateTime.UtcNow;
if (dif <= TimeSpan.Zero) if (dif <= TimeSpan.Zero)
@ -48,7 +48,7 @@ namespace CryptoExchange.Net.RateLimiting.Guards
} }
/// <inheritdoc /> /// <inheritdoc />
public RateLimitState ApplyWeight(RateLimitItemType type, RequestDefinition definition, string host, SecureString? apiKey, int requestWeight) public RateLimitState ApplyWeight(RateLimitItemType type, RequestDefinition definition, string host, string? apiKey, int requestWeight)
{ {
return RateLimitState.NotApplied; return RateLimitState.NotApplied;
} }

View File

@ -15,19 +15,19 @@ namespace CryptoExchange.Net.RateLimiting.Guards
/// <summary> /// <summary>
/// Default endpoint limit /// Default endpoint limit
/// </summary> /// </summary>
public static Func<RequestDefinition, string, SecureString?, string> Default { get; } = new Func<RequestDefinition, string, SecureString?, string>((def, host, key) => def.Path + def.Method); public static Func<RequestDefinition, string, string?, string> Default { get; } = new Func<RequestDefinition, string, string?, string>((def, host, key) => def.Path + def.Method);
/// <summary> /// <summary>
/// Endpoint limit per API key /// Endpoint limit per API key
/// </summary> /// </summary>
public static Func<RequestDefinition, string, SecureString?, string> PerApiKey { get; } = new Func<RequestDefinition, string, SecureString?, string>((def, host, key) => def.Path + def.Method); public static Func<RequestDefinition, string, string?, string> PerApiKey { get; } = new Func<RequestDefinition, string, string?, string>((def, host, key) => def.Path + def.Method);
private readonly Dictionary<string, IWindowTracker> _trackers; private readonly Dictionary<string, IWindowTracker> _trackers;
private readonly RateLimitWindowType _windowType; private readonly RateLimitWindowType _windowType;
private readonly double? _decayRate; private readonly double? _decayRate;
private readonly int _limit; private readonly int _limit;
private readonly TimeSpan _period; private readonly TimeSpan _period;
private readonly Func<RequestDefinition, string, SecureString?, string> _keySelector; private readonly Func<RequestDefinition, string, string?, string> _keySelector;
/// <inheritdoc /> /// <inheritdoc />
public string Name => "EndpointLimitGuard"; public string Name => "EndpointLimitGuard";
@ -43,7 +43,7 @@ namespace CryptoExchange.Net.RateLimiting.Guards
TimeSpan period, TimeSpan period,
RateLimitWindowType windowType, RateLimitWindowType windowType,
double? decayRate = null, double? decayRate = null,
Func<RequestDefinition, string, SecureString?, string>? keySelector = null) Func<RequestDefinition, string, string?, string>? keySelector = null)
{ {
_limit = limit; _limit = limit;
_period = period; _period = period;
@ -54,7 +54,7 @@ namespace CryptoExchange.Net.RateLimiting.Guards
} }
/// <inheritdoc /> /// <inheritdoc />
public LimitCheck Check(RateLimitItemType type, RequestDefinition definition, string host, SecureString? apiKey, int requestWeight) public LimitCheck Check(RateLimitItemType type, RequestDefinition definition, string host, string? apiKey, int requestWeight)
{ {
var key = _keySelector(definition, host, apiKey); var key = _keySelector(definition, host, apiKey);
if (!_trackers.TryGetValue(key, out var tracker)) if (!_trackers.TryGetValue(key, out var tracker))
@ -71,7 +71,7 @@ namespace CryptoExchange.Net.RateLimiting.Guards
} }
/// <inheritdoc /> /// <inheritdoc />
public RateLimitState ApplyWeight(RateLimitItemType type, RequestDefinition definition, string host, SecureString? apiKey, int requestWeight) public RateLimitState ApplyWeight(RateLimitItemType type, RequestDefinition definition, string host, string? apiKey, int requestWeight)
{ {
var key = _keySelector(definition, host, apiKey); var key = _keySelector(definition, host, apiKey);
var tracker = _trackers[key]; var tracker = _trackers[key];

View File

@ -16,6 +16,6 @@ namespace CryptoExchange.Net.RateLimiting.Interfaces
/// <param name="host">The host address</param> /// <param name="host">The host address</param>
/// <param name="apiKey">The API key</param> /// <param name="apiKey">The API key</param>
/// <returns>True if passed</returns> /// <returns>True if passed</returns>
bool Passes(RateLimitItemType type, RequestDefinition definition, string host, SecureString? apiKey); bool Passes(RateLimitItemType type, RequestDefinition definition, string host, string? apiKey);
} }
} }

View File

@ -51,7 +51,7 @@ namespace CryptoExchange.Net.RateLimiting.Interfaces
/// <param name="behaviour">Behaviour when rate limit is hit</param> /// <param name="behaviour">Behaviour when rate limit is hit</param>
/// <param name="ct">Cancelation token</param> /// <param name="ct">Cancelation token</param>
/// <returns>Error if RateLimitingBehaviour is Fail and rate limit is hit</returns> /// <returns>Error if RateLimitingBehaviour is Fail and rate limit is hit</returns>
Task<CallResult> ProcessAsync(ILogger logger, int itemId, RateLimitItemType type, RequestDefinition definition, string baseAddress, SecureString? apiKey, int requestWeight, RateLimitingBehaviour behaviour, CancellationToken ct); Task<CallResult> ProcessAsync(ILogger logger, int itemId, RateLimitItemType type, RequestDefinition definition, string baseAddress, string? apiKey, int requestWeight, RateLimitingBehaviour behaviour, CancellationToken ct);
/// <summary> /// <summary>
/// Enforces the rate limit as defined in the request definition. When a rate limit is hit will wait for the rate limit to pass if RateLimitingBehaviour is Wait, or return an error if it is set to Fail /// Enforces the rate limit as defined in the request definition. When a rate limit is hit will wait for the rate limit to pass if RateLimitingBehaviour is Wait, or return an error if it is set to Fail
@ -66,6 +66,6 @@ namespace CryptoExchange.Net.RateLimiting.Interfaces
/// <param name="behaviour">Behaviour when rate limit is hit</param> /// <param name="behaviour">Behaviour when rate limit is hit</param>
/// <param name="ct">Cancelation token</param> /// <param name="ct">Cancelation token</param>
/// <returns>Error if RateLimitingBehaviour is Fail and rate limit is hit</returns> /// <returns>Error if RateLimitingBehaviour is Fail and rate limit is hit</returns>
Task<CallResult> ProcessSingleAsync(ILogger logger, int itemId, IRateLimitGuard guard, RateLimitItemType type, RequestDefinition definition, string baseAddress, SecureString? apiKey, RateLimitingBehaviour behaviour, CancellationToken ct); Task<CallResult> ProcessSingleAsync(ILogger logger, int itemId, IRateLimitGuard guard, RateLimitItemType type, RequestDefinition definition, string baseAddress, string? apiKey, RateLimitingBehaviour behaviour, CancellationToken ct);
} }
} }

View File

@ -28,7 +28,7 @@ namespace CryptoExchange.Net.RateLimiting.Interfaces
/// <param name="apiKey">The API key</param> /// <param name="apiKey">The API key</param>
/// <param name="requestWeight">The request weight</param> /// <param name="requestWeight">The request weight</param>
/// <returns></returns> /// <returns></returns>
LimitCheck Check(RateLimitItemType type, RequestDefinition definition, string host, SecureString? apiKey, int requestWeight); LimitCheck Check(RateLimitItemType type, RequestDefinition definition, string host, string? apiKey, int requestWeight);
/// <summary> /// <summary>
/// Apply the request to this guard with the specified weight /// Apply the request to this guard with the specified weight
@ -39,6 +39,6 @@ namespace CryptoExchange.Net.RateLimiting.Interfaces
/// <param name="apiKey">The API key</param> /// <param name="apiKey">The API key</param>
/// <param name="requestWeight">The request weight</param> /// <param name="requestWeight">The request weight</param>
/// <returns></returns> /// <returns></returns>
RateLimitState ApplyWeight(RateLimitItemType type, RequestDefinition definition, string host, SecureString? apiKey, int requestWeight); RateLimitState ApplyWeight(RateLimitItemType type, RequestDefinition definition, string host, string? apiKey, int requestWeight);
} }
} }

View File

@ -36,7 +36,7 @@ namespace CryptoExchange.Net.RateLimiting
} }
/// <inheritdoc /> /// <inheritdoc />
public async Task<CallResult> ProcessAsync(ILogger logger, int itemId, RateLimitItemType type, RequestDefinition definition, string host, SecureString? apiKey, int requestWeight, RateLimitingBehaviour rateLimitingBehaviour, CancellationToken ct) public async Task<CallResult> ProcessAsync(ILogger logger, int itemId, RateLimitItemType type, RequestDefinition definition, string host, string? apiKey, int requestWeight, RateLimitingBehaviour rateLimitingBehaviour, CancellationToken ct)
{ {
await _semaphore.WaitAsync(ct).ConfigureAwait(false); await _semaphore.WaitAsync(ct).ConfigureAwait(false);
_waitingCount++; _waitingCount++;
@ -59,7 +59,7 @@ namespace CryptoExchange.Net.RateLimiting
RateLimitItemType type, RateLimitItemType type,
RequestDefinition definition, RequestDefinition definition,
string host, string host,
SecureString? apiKey, string? apiKey,
RateLimitingBehaviour rateLimitingBehaviour, RateLimitingBehaviour rateLimitingBehaviour,
CancellationToken ct) CancellationToken ct)
{ {
@ -77,7 +77,7 @@ namespace CryptoExchange.Net.RateLimiting
} }
} }
private async Task<CallResult> CheckGuardsAsync(IEnumerable<IRateLimitGuard> guards, ILogger logger, int itemId, RateLimitItemType type, RequestDefinition definition, string host, SecureString? apiKey, int requestWeight, RateLimitingBehaviour rateLimitingBehaviour, CancellationToken ct) private async Task<CallResult> CheckGuardsAsync(IEnumerable<IRateLimitGuard> guards, ILogger logger, int itemId, RateLimitItemType type, RequestDefinition definition, string host, string? apiKey, int requestWeight, RateLimitingBehaviour rateLimitingBehaviour, CancellationToken ct)
{ {
foreach (var guard in guards) foreach (var guard in guards)
{ {

View File

@ -131,9 +131,9 @@ namespace CryptoExchange.Net.Testing
client, client,
new Uri(host.AppendPath(path)), new Uri(host.AppendPath(path)),
method, method,
uriParams, ref uriParams,
bodyParams, ref bodyParams,
headers, ref headers,
true, true,
client.ArraySerialization, client.ArraySerialization,
client.ParameterPositions[method], client.ParameterPositions[method],