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:
parent
2f64cd9f05
commit
949780a9ad
@ -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]
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 />
|
||||||
|
@ -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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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
|
||||||
|
@ -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>
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
{
|
{
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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];
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
{
|
{
|
||||||
|
@ -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],
|
||||||
|
Loading…
x
Reference in New Issue
Block a user