diff --git a/CryptoExchange.Net.UnitTests/OptionsTests.cs b/CryptoExchange.Net.UnitTests/OptionsTests.cs
index 90c832d..8e60710 100644
--- a/CryptoExchange.Net.UnitTests/OptionsTests.cs
+++ b/CryptoExchange.Net.UnitTests/OptionsTests.cs
@@ -51,8 +51,8 @@ namespace CryptoExchange.Net.UnitTests
// assert
Assert.That(options.ReceiveWindow == TimeSpan.FromSeconds(10));
- Assert.That(options.ApiCredentials.Key.GetString() == "123");
- Assert.That(options.ApiCredentials.Secret.GetString() == "456");
+ Assert.That(options.ApiCredentials.Key == "123");
+ Assert.That(options.ApiCredentials.Secret == "456");
}
[Test]
@@ -64,10 +64,10 @@ namespace CryptoExchange.Net.UnitTests
options.Api2Options.ApiCredentials = new ApiCredentials("789", "101");
// assert
- Assert.That(options.Api1Options.ApiCredentials.Key.GetString() == "123");
- Assert.That(options.Api1Options.ApiCredentials.Secret.GetString() == "456");
- Assert.That(options.Api2Options.ApiCredentials.Key.GetString() == "789");
- Assert.That(options.Api2Options.ApiCredentials.Secret.GetString() == "101");
+ Assert.That(options.Api1Options.ApiCredentials.Key == "123");
+ Assert.That(options.Api1Options.ApiCredentials.Secret == "456");
+ Assert.That(options.Api2Options.ApiCredentials.Key == "789");
+ Assert.That(options.Api2Options.ApiCredentials.Secret == "101");
}
[Test]
diff --git a/CryptoExchange.Net.UnitTests/RestClientTests.cs b/CryptoExchange.Net.UnitTests/RestClientTests.cs
index 1371039..ced33e7 100644
--- a/CryptoExchange.Net.UnitTests/RestClientTests.cs
+++ b/CryptoExchange.Net.UnitTests/RestClientTests.cs
@@ -176,12 +176,12 @@ namespace CryptoExchange.Net.UnitTests
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);
}
triggered = false;
await Task.Delay((int)Math.Round(perSeconds * 1000) + 10);
- var result2 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Request, requestDefinition, "https://test.com", "123".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);
}
@@ -201,7 +201,7 @@ namespace CryptoExchange.Net.UnitTests
rateLimiter.RateLimitTriggered += (x) => { evnt = x; };
for (var i = 0; i < 2; i++)
{
- var result1 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Request, requestDefinition, "https://test.com", "123".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;
Assert.That(expected);
}
@@ -222,9 +222,9 @@ namespace CryptoExchange.Net.UnitTests
RateLimitEvent evnt = null;
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);
- 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);
}
@@ -243,12 +243,12 @@ namespace CryptoExchange.Net.UnitTests
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);
}
triggered = false;
await Task.Delay((int)Math.Round(perSeconds * 1000) + 10);
- var result2 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Request, requestDefinition, "https://test.com", "123".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);
}
@@ -266,7 +266,7 @@ namespace CryptoExchange.Net.UnitTests
rateLimiter.RateLimitTriggered += (x) => { evnt = x; };
for (var i = 0; i < 2; i++)
{
- var result1 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Request, requestDefinition, "https://test.com", "123".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;
Assert.That(expected);
}
@@ -286,7 +286,7 @@ namespace CryptoExchange.Net.UnitTests
rateLimiter.RateLimitTriggered += (x) => { evnt = x; };
for (var i = 0; i < 2; i++)
{
- var result1 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Request, requestDefinition, "https://test.com", "123".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;
Assert.That(expected);
}
@@ -309,9 +309,9 @@ namespace CryptoExchange.Net.UnitTests
RateLimitEvent evnt = null;
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);
- 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);
}
@@ -328,7 +328,7 @@ namespace CryptoExchange.Net.UnitTests
RateLimitEvent evnt = null;
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);
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);
@@ -348,9 +348,9 @@ namespace CryptoExchange.Net.UnitTests
RateLimitEvent evnt = null;
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);
- 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);
}
@@ -365,9 +365,9 @@ namespace CryptoExchange.Net.UnitTests
RateLimitEvent evnt = null;
rateLimiter.RateLimitTriggered += (x) => { evnt = x; };
- var result1 = await rateLimiter.ProcessAsync(new TraceLogger(), 1, RateLimitItemType.Connection, new RequestDefinition("1", HttpMethod.Get), host1, "123".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);
- 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);
}
}
diff --git a/CryptoExchange.Net.UnitTests/TestImplementations/TestBaseClient.cs b/CryptoExchange.Net.UnitTests/TestImplementations/TestBaseClient.cs
index 4a2e2f5..5f82106 100644
--- a/CryptoExchange.Net.UnitTests/TestImplementations/TestBaseClient.cs
+++ b/CryptoExchange.Net.UnitTests/TestImplementations/TestBaseClient.cs
@@ -72,7 +72,7 @@ namespace CryptoExchange.Net.UnitTests
{
}
- public string GetKey() => _credentials.Key.GetString();
- public string GetSecret() => _credentials.Secret.GetString();
+ public string GetKey() => _credentials.Key;
+ public string GetSecret() => _credentials.Secret;
}
}
diff --git a/CryptoExchange.Net/Authentication/ApiCredentials.cs b/CryptoExchange.Net/Authentication/ApiCredentials.cs
index b942853..eb7fb50 100644
--- a/CryptoExchange.Net/Authentication/ApiCredentials.cs
+++ b/CryptoExchange.Net/Authentication/ApiCredentials.cs
@@ -1,6 +1,5 @@
using System;
using System.IO;
-using System.Security;
using CryptoExchange.Net.Converters.SystemTextJson;
using CryptoExchange.Net.Converters.MessageParsing;
@@ -9,48 +8,23 @@ namespace CryptoExchange.Net.Authentication
///
/// Api credentials, used to sign requests accessing private endpoints
///
- public class ApiCredentials: IDisposable
+ public class ApiCredentials
{
///
/// The api key to authenticate requests
///
- public SecureString? Key { get; }
+ public string Key { get; }
///
/// The api secret to authenticate requests
///
- public SecureString? Secret { get; }
+ public string Secret { get; }
///
/// Type of the credentials
///
public ApiCredentialsType CredentialType { get; }
- ///
- /// Create Api credentials providing an api key and secret for authentication
- ///
- /// The api key used for identification
- /// The api secret used for signing
- public ApiCredentials(SecureString key, SecureString secret) : this(key, secret, ApiCredentialsType.Hmac)
- {
- }
-
- ///
- /// Create Api credentials providing an api key and secret for authentication
- ///
- /// The api key used for identification
- /// The api secret used for signing
- /// The type of credentials
- 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;
- }
-
///
/// Create Api credentials providing an api key and secret for authentication
///
@@ -72,8 +46,8 @@ namespace CryptoExchange.Net.Authentication
throw new ArgumentException("Key and secret can't be null/empty");
CredentialType = credentialsType;
- Key = key.ToSecureString();
- Secret = secret.ToSecureString();
+ Key = key;
+ Secret = secret;
}
///
@@ -82,8 +56,7 @@ namespace CryptoExchange.Net.Authentication
///
public virtual ApiCredentials Copy()
{
- // Use .GetString() to create a copy of the SecureString
- return new ApiCredentials(Key!.GetString(), Secret!.GetString(), CredentialType);
+ return new ApiCredentials(Key, Secret, CredentialType);
}
///
@@ -103,19 +76,10 @@ namespace CryptoExchange.Net.Authentication
if (key == null || secret == null)
throw new ArgumentException("apiKey or apiSecret value not found in Json credential file");
- Key = key.ToSecureString();
- Secret = secret.ToSecureString();
+ Key = key;
+ Secret = secret;
inputStream.Seek(0, SeekOrigin.Begin);
}
-
- ///
- /// Dispose
- ///
- public void Dispose()
- {
- Key?.Dispose();
- Secret?.Dispose();
- }
}
}
diff --git a/CryptoExchange.Net/Authentication/AuthenticationProvider.cs b/CryptoExchange.Net/Authentication/AuthenticationProvider.cs
index 55e9d80..a3ad2c0 100644
--- a/CryptoExchange.Net/Authentication/AuthenticationProvider.cs
+++ b/CryptoExchange.Net/Authentication/AuthenticationProvider.cs
@@ -14,7 +14,7 @@ namespace CryptoExchange.Net.Authentication
///
/// Base class for authentication providers
///
- public abstract class AuthenticationProvider : IDisposable
+ public abstract class AuthenticationProvider
{
internal IAuthTimeProvider TimeProvider { get; set; } = new AuthTimeProvider();
@@ -28,6 +28,11 @@ namespace CryptoExchange.Net.Authentication
///
protected byte[] _sBytes;
+ ///
+ /// Get the API key of the current credentials
+ ///
+ public string ApiKey => _credentials.Key;
+
///
/// ctor
///
@@ -38,7 +43,7 @@ namespace CryptoExchange.Net.Authentication
throw new ArgumentException("ApiKey/Secret needed");
_credentials = credentials;
- _sBytes = Encoding.UTF8.GetBytes(credentials.Secret.GetString());
+ _sBytes = Encoding.UTF8.GetBytes(credentials.Secret);
}
///
@@ -58,9 +63,9 @@ namespace CryptoExchange.Net.Authentication
RestApiClient apiClient,
Uri uri,
HttpMethod method,
- IDictionary uriParameters,
- IDictionary bodyParameters,
- Dictionary headers,
+ ref IDictionary? uriParameters,
+ ref IDictionary? bodyParameters,
+ ref Dictionary? headers,
bool auth,
ArrayParametersSerialization arraySerialization,
HttpMethodParameterPosition parameterPosition,
@@ -366,7 +371,7 @@ namespace CryptoExchange.Net.Authentication
{
#if NETSTANDARD2_1_OR_GREATER
// Read from pem private key
- var key = _credentials.Secret!.GetString()
+ var key = _credentials.Secret!
.Replace("\n", "")
.Replace("-----BEGIN PRIVATE KEY-----", "")
.Replace("-----END PRIVATE KEY-----", "")
@@ -381,7 +386,7 @@ namespace CryptoExchange.Net.Authentication
else if (_credentials.CredentialType == ApiCredentialsType.RsaXml)
{
// Read from xml private key format
- rsa.FromXmlString(_credentials.Secret!.GetString());
+ rsa.FromXmlString(_credentials.Secret!);
}
else
{
@@ -447,12 +452,6 @@ namespace CryptoExchange.Net.Authentication
else
return serializer.Serialize(parameters);
}
-
- ///
- public void Dispose()
- {
- _credentials?.Dispose();
- }
}
///
diff --git a/CryptoExchange.Net/Clients/BaseApiClient.cs b/CryptoExchange.Net/Clients/BaseApiClient.cs
index a377fbd..8ac98d0 100644
--- a/CryptoExchange.Net/Clients/BaseApiClient.cs
+++ b/CryptoExchange.Net/Clients/BaseApiClient.cs
@@ -65,10 +65,7 @@ namespace CryptoExchange.Net.Clients
BaseAddress = baseAddress;
if (apiCredentials != null)
- {
- AuthenticationProvider?.Dispose();
AuthenticationProvider = CreateAuthenticationProvider(apiCredentials.Copy());
- }
}
///
@@ -85,10 +82,7 @@ namespace CryptoExchange.Net.Clients
public void SetApiCredentials(T credentials) where T : ApiCredentials
{
if (credentials != null)
- {
- AuthenticationProvider?.Dispose();
AuthenticationProvider = CreateAuthenticationProvider(credentials.Copy());
- }
}
///
@@ -97,7 +91,6 @@ namespace CryptoExchange.Net.Clients
public virtual void Dispose()
{
_disposing = true;
- AuthenticationProvider?.Dispose();
}
}
}
diff --git a/CryptoExchange.Net/Clients/RestApiClient.cs b/CryptoExchange.Net/Clients/RestApiClient.cs
index 60eb317..0221bbc 100644
--- a/CryptoExchange.Net/Clients/RestApiClient.cs
+++ b/CryptoExchange.Net/Clients/RestApiClient.cs
@@ -196,19 +196,20 @@ namespace CryptoExchange.Net.Clients
Dictionary? additionalHeaders = null,
int? weight = null) where T : class
{
- var key = baseAddress + definition + uriParameters?.ToFormData();
+ string? cacheKey = null;
if (ShouldCache(definition))
{
- _logger.CheckingCache(key);
- var cachedValue = _cache.Get(key, ClientOptions.CachingMaxAge);
+ cacheKey = baseAddress + definition + uriParameters?.ToFormData();
+ _logger.CheckingCache(cacheKey);
+ var cachedValue = _cache.Get(cacheKey, ClientOptions.CachingMaxAge);
if (cachedValue != null)
{
- _logger.CacheHit(key);
+ _logger.CacheHit(cacheKey);
var original = (WebCallResult)cachedValue;
return original.Cached();
}
- _logger.CacheNotHit(key);
+ _logger.CacheNotHit(cacheKey);
}
int currentTry = 0;
@@ -242,7 +243,7 @@ namespace CryptoExchange.Net.Clients
if (result.Success &&
ShouldCache(definition))
{
- _cache.Add(key, result);
+ _cache.Add(cacheKey!, result);
}
return result;
@@ -343,15 +344,15 @@ namespace CryptoExchange.Net.Clients
ParameterCollection? bodyParameters,
Dictionary? additionalHeaders)
{
- var uriParams = uriParameters == null ? new ParameterCollection() : CreateParameterDictionary(uriParameters);
- var bodyParams = bodyParameters == null ? new ParameterCollection() : CreateParameterDictionary(bodyParameters);
+ var uriParams = uriParameters == null ? null : CreateParameterDictionary(uriParameters);
+ var bodyParams = bodyParameters == null ? null : CreateParameterDictionary(bodyParameters);
var uri = new Uri(baseAddress.AppendPath(definition.Path));
var arraySerialization = definition.ArraySerialization ?? ArraySerialization;
var bodyFormat = definition.RequestBodyFormat ?? RequestBodyFormat;
var parameterPosition = definition.ParameterPosition ?? ParameterPositions[definition.Method];
- var headers = new Dictionary();
+ Dictionary? headers = null;
if (AuthenticationProvider != null)
{
try
@@ -360,9 +361,9 @@ namespace CryptoExchange.Net.Clients
this,
uri,
definition.Method,
- uriParams,
- bodyParams,
- headers,
+ ref uriParams,
+ ref bodyParams,
+ ref headers,
definition.Authenticated,
arraySerialization,
parameterPosition,
@@ -375,14 +376,18 @@ 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
- uri = uri.SetParameters(uriParams, arraySerialization);
+ // Add the auth parameters to the uri, start with a new URI to be able to sort the parameters including the auth parameters
+ if (uriParams != null)
+ uri = uri.SetParameters(uriParams, arraySerialization);
var request = RequestFactory.Create(definition.Method, uri, requestId);
request.Accept = Constants.JsonContentHeader;
- foreach (var header in headers)
- request.AddHeader(header.Key, header.Value);
+ if (headers != null)
+ {
+ foreach (var header in headers)
+ request.AddHeader(header.Key, header.Value);
+ }
if (additionalHeaders != null)
{
@@ -403,7 +408,7 @@ namespace CryptoExchange.Net.Clients
if (parameterPosition == HttpMethodParameterPosition.InBody)
{
var contentType = bodyFormat == RequestBodyFormat.Json ? Constants.JsonContentHeader : Constants.FormContentHeader;
- if (bodyParams.Count != 0)
+ if (bodyParams != null && bodyParams.Count != 0)
WriteParamBody(request, bodyParams, contentType);
else
request.SetContent(RequestBodyEmptyContent, contentType);
@@ -817,9 +822,9 @@ namespace CryptoExchange.Net.Clients
this,
uri,
method,
- uriParameters,
- bodyParameters,
- headers,
+ ref uriParameters,
+ ref bodyParameters,
+ ref headers,
signed,
arraySerialization,
parameterPosition,
diff --git a/CryptoExchange.Net/ExchangeHelpers.cs b/CryptoExchange.Net/ExchangeHelpers.cs
index 20c759f..20049e2 100644
--- a/CryptoExchange.Net/ExchangeHelpers.cs
+++ b/CryptoExchange.Net/ExchangeHelpers.cs
@@ -1,6 +1,7 @@
using CryptoExchange.Net.Objects;
using System;
using System.Security.Cryptography;
+using System.Threading;
namespace CryptoExchange.Net
{
@@ -15,10 +16,6 @@ namespace CryptoExchange.Net
/// The last used id, use NextId() to get the next id and up this
///
private static int _lastId;
- ///
- /// Lock for id generating
- ///
- private static object _idLock = new();
///
/// 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
///
///
- public static int NextId()
- {
- lock (_idLock)
- {
- _lastId += 1;
- return _lastId;
- }
- }
+ public static int NextId() => Interlocked.Increment(ref _lastId);
///
/// Return the last unique id that was generated
///
///
- public static int LastId()
- {
- lock (_idLock)
- return _lastId;
- }
+ public static int LastId() => _lastId;
///
/// Generate a random string of specified length
diff --git a/CryptoExchange.Net/ExtensionMethods.cs b/CryptoExchange.Net/ExtensionMethods.cs
index 5fd22b1..c9e026b 100644
--- a/CryptoExchange.Net/ExtensionMethods.cs
+++ b/CryptoExchange.Net/ExtensionMethods.cs
@@ -113,92 +113,6 @@ namespace CryptoExchange.Net
return formData.ToString();
}
- ///
- /// Get the string the secure string is representing
- ///
- /// The source secure string
- ///
- 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;
- }
- }
-
- ///
- /// Are 2 secure strings equal
- ///
- /// Source secure string
- /// Compare secure string
- /// True if equal by value
- 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);
- }
- }
-
- ///
- /// Create a secure string from a string
- ///
- ///
- ///
- public static SecureString ToSecureString(this string source)
- {
- var secureString = new SecureString();
- foreach (var c in source)
- secureString.AppendChar(c);
- secureString.MakeReadOnly();
- return secureString;
- }
-
///
/// Validates an int is one of the allowed values
///
@@ -318,26 +232,6 @@ namespace CryptoExchange.Net
return url.TrimEnd('/');
}
- ///
- /// Fill parameters in a path. Parameters are specified by '{}' and should be specified in occuring sequence
- ///
- /// The total path string
- /// The values to fill
- ///
- 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;
- }
-
///
/// Create a new uri with the provided parameters as query
///
diff --git a/CryptoExchange.Net/Interfaces/IRateLimiter.cs b/CryptoExchange.Net/Interfaces/IRateLimiter.cs
index 8cfffea..eee90da 100644
--- a/CryptoExchange.Net/Interfaces/IRateLimiter.cs
+++ b/CryptoExchange.Net/Interfaces/IRateLimiter.cs
@@ -24,6 +24,6 @@ namespace CryptoExchange.Net.Interfaces
/// The weight of the request
/// Cancellation token to cancel waiting
/// The time in milliseconds spend waiting
- Task> LimitRequestAsync(ILogger log, string endpoint, HttpMethod method, bool signed, SecureString? apiKey, RateLimitingBehaviour limitBehaviour, int requestWeight, CancellationToken ct);
+ Task> LimitRequestAsync(ILogger log, string endpoint, HttpMethod method, bool signed, string? apiKey, RateLimitingBehaviour limitBehaviour, int requestWeight, CancellationToken ct);
}
}
diff --git a/CryptoExchange.Net/Objects/ApiProxy.cs b/CryptoExchange.Net/Objects/ApiProxy.cs
index 1e75156..08fc51e 100644
--- a/CryptoExchange.Net/Objects/ApiProxy.cs
+++ b/CryptoExchange.Net/Objects/ApiProxy.cs
@@ -24,14 +24,14 @@ namespace CryptoExchange.Net.Objects
///
/// The password of the proxy
///
- public SecureString? Password { get; }
+ public string? Password { get; }
///
/// Create new settings for a proxy
///
/// The proxy hostname/ip
/// The proxy port
- 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
/// The proxy port
/// The proxy login
/// The proxy password
- public ApiProxy(string host, int port, string? login, string? password) : this(host, port, login, password?.ToSecureString())
- {
- }
-
- ///
- /// Create new settings for a proxy
- ///
- /// The proxy hostname/ip
- /// The proxy port
- /// The proxy login
- /// The proxy password
- public ApiProxy(string host, int port, string? login, SecureString? password)
+ public ApiProxy(string host, int port, string? login, string? password)
{
Host = host;
Port = port;
diff --git a/CryptoExchange.Net/RateLimiting/Filters/AuthenticatedEndpointFilter.cs b/CryptoExchange.Net/RateLimiting/Filters/AuthenticatedEndpointFilter.cs
index ac03e22..22808d9 100644
--- a/CryptoExchange.Net/RateLimiting/Filters/AuthenticatedEndpointFilter.cs
+++ b/CryptoExchange.Net/RateLimiting/Filters/AuthenticatedEndpointFilter.cs
@@ -21,7 +21,7 @@ namespace CryptoExchange.Net.RateLimiting.Filters
}
///
- 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;
}
}
diff --git a/CryptoExchange.Net/RateLimiting/Filters/ExactPathFilter.cs b/CryptoExchange.Net/RateLimiting/Filters/ExactPathFilter.cs
index 2b99964..855a589 100644
--- a/CryptoExchange.Net/RateLimiting/Filters/ExactPathFilter.cs
+++ b/CryptoExchange.Net/RateLimiting/Filters/ExactPathFilter.cs
@@ -24,7 +24,7 @@ namespace CryptoExchange.Net.RateLimiting.Filters
}
///
- 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);
}
}
diff --git a/CryptoExchange.Net/RateLimiting/Filters/ExactPathsFilter.cs b/CryptoExchange.Net/RateLimiting/Filters/ExactPathsFilter.cs
index 82ab123..6d17663 100644
--- a/CryptoExchange.Net/RateLimiting/Filters/ExactPathsFilter.cs
+++ b/CryptoExchange.Net/RateLimiting/Filters/ExactPathsFilter.cs
@@ -22,7 +22,7 @@ namespace CryptoExchange.Net.RateLimiting.Filters
}
///
- 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);
}
}
diff --git a/CryptoExchange.Net/RateLimiting/Filters/HostFilter.cs b/CryptoExchange.Net/RateLimiting/Filters/HostFilter.cs
index 2101185..3079297 100644
--- a/CryptoExchange.Net/RateLimiting/Filters/HostFilter.cs
+++ b/CryptoExchange.Net/RateLimiting/Filters/HostFilter.cs
@@ -21,7 +21,7 @@ namespace CryptoExchange.Net.RateLimiting.Filters
}
///
- public bool Passes(RateLimitItemType type, RequestDefinition definition, string host, SecureString? apiKey)
+ public bool Passes(RateLimitItemType type, RequestDefinition definition, string host, string? apiKey)
=> host == _host;
}
diff --git a/CryptoExchange.Net/RateLimiting/Filters/LimitItemTypeFilter.cs b/CryptoExchange.Net/RateLimiting/Filters/LimitItemTypeFilter.cs
index f4277fd..8d9d4bf 100644
--- a/CryptoExchange.Net/RateLimiting/Filters/LimitItemTypeFilter.cs
+++ b/CryptoExchange.Net/RateLimiting/Filters/LimitItemTypeFilter.cs
@@ -21,7 +21,7 @@ namespace CryptoExchange.Net.RateLimiting.Filters
}
///
- public bool Passes(RateLimitItemType type, RequestDefinition definition, string host, SecureString? apiKey)
+ public bool Passes(RateLimitItemType type, RequestDefinition definition, string host, string? apiKey)
=> type == _type;
}
}
diff --git a/CryptoExchange.Net/RateLimiting/Filters/PathStartFilter.cs b/CryptoExchange.Net/RateLimiting/Filters/PathStartFilter.cs
index 402b2de..449ac72 100644
--- a/CryptoExchange.Net/RateLimiting/Filters/PathStartFilter.cs
+++ b/CryptoExchange.Net/RateLimiting/Filters/PathStartFilter.cs
@@ -22,7 +22,7 @@ namespace CryptoExchange.Net.RateLimiting.Filters
}
///
- 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);
}
}
diff --git a/CryptoExchange.Net/RateLimiting/Guards/RateLimitGuard.cs b/CryptoExchange.Net/RateLimiting/Guards/RateLimitGuard.cs
index 6ca5031..0f02b4e 100644
--- a/CryptoExchange.Net/RateLimiting/Guards/RateLimitGuard.cs
+++ b/CryptoExchange.Net/RateLimiting/Guards/RateLimitGuard.cs
@@ -14,26 +14,26 @@ namespace CryptoExchange.Net.RateLimiting.Guards
///
/// Apply guard per host
///
- public static Func PerHost { get; } = new Func((def, host, key) => host);
+ public static Func PerHost { get; } = new Func((def, host, key) => host);
///
/// Apply guard per endpoint
///
- public static Func PerEndpoint { get; } = new Func((def, host, key) => def.Path + def.Method);
+ public static Func PerEndpoint { get; } = new Func((def, host, key) => def.Path + def.Method);
///
/// Apply guard per API key
///
- public static Func PerApiKey { get; } = new Func((def, host, key) => key!.GetString());
+ public static Func PerApiKey { get; } = new Func((def, host, key) => key!);
///
/// Apply guard per API key per endpoint
///
- public static Func PerApiKeyPerEndpoint { get; } = new Func((def, host, key) => key!.GetString() + def.Path + def.Method);
+ public static Func PerApiKeyPerEndpoint { get; } = new Func((def, host, key) => key! + def.Path + def.Method);
private readonly IEnumerable _filters;
private readonly Dictionary _trackers;
private RateLimitWindowType _windowType;
private double? _decayRate;
private int? _connectionWeight;
- private readonly Func _keySelector;
+ private readonly Func _keySelector;
///
public string Name => "RateLimitGuard";
@@ -60,7 +60,7 @@ namespace CryptoExchange.Net.RateLimiting.Guards
/// Type of rate limit window
/// The decay per timespan if windowType is DecayWindowTracker
/// The weight of a new connection
- public RateLimitGuard(Func keySelector, IGuardFilter filter, int limit, TimeSpan timeSpan, RateLimitWindowType windowType, double? decayPerTimeSpan = null, int? connectionWeight = null)
+ public RateLimitGuard(Func keySelector, IGuardFilter filter, int limit, TimeSpan timeSpan, RateLimitWindowType windowType, double? decayPerTimeSpan = null, int? connectionWeight = null)
: this(keySelector, new[] { filter }, limit, timeSpan, windowType, decayPerTimeSpan, connectionWeight)
{
}
@@ -75,7 +75,7 @@ namespace CryptoExchange.Net.RateLimiting.Guards
/// Type of rate limit window
/// The decay per timespan if windowType is DecayWindowTracker
/// The weight of a new connection
- public RateLimitGuard(Func keySelector, IEnumerable filters, int limit, TimeSpan timeSpan, RateLimitWindowType windowType, double? decayPerTimeSpan = null, int? connectionWeight = null)
+ public RateLimitGuard(Func keySelector, IEnumerable filters, int limit, TimeSpan timeSpan, RateLimitWindowType windowType, double? decayPerTimeSpan = null, int? connectionWeight = null)
{
_filters = filters;
_trackers = new Dictionary();
@@ -88,7 +88,7 @@ namespace CryptoExchange.Net.RateLimiting.Guards
}
///
- 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)
{
@@ -114,7 +114,7 @@ namespace CryptoExchange.Net.RateLimiting.Guards
}
///
- 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)
{
diff --git a/CryptoExchange.Net/RateLimiting/Guards/RetryAfterGuard.cs b/CryptoExchange.Net/RateLimiting/Guards/RetryAfterGuard.cs
index 1aa5c04..b09763f 100644
--- a/CryptoExchange.Net/RateLimiting/Guards/RetryAfterGuard.cs
+++ b/CryptoExchange.Net/RateLimiting/Guards/RetryAfterGuard.cs
@@ -38,7 +38,7 @@ namespace CryptoExchange.Net.RateLimiting.Guards
}
///
- 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;
if (dif <= TimeSpan.Zero)
@@ -48,7 +48,7 @@ namespace CryptoExchange.Net.RateLimiting.Guards
}
///
- 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;
}
diff --git a/CryptoExchange.Net/RateLimiting/Guards/SingleLimitGuard.cs b/CryptoExchange.Net/RateLimiting/Guards/SingleLimitGuard.cs
index af11e68..5b174eb 100644
--- a/CryptoExchange.Net/RateLimiting/Guards/SingleLimitGuard.cs
+++ b/CryptoExchange.Net/RateLimiting/Guards/SingleLimitGuard.cs
@@ -15,19 +15,19 @@ namespace CryptoExchange.Net.RateLimiting.Guards
///
/// Default endpoint limit
///
- public static Func Default { get; } = new Func((def, host, key) => def.Path + def.Method);
+ public static Func Default { get; } = new Func((def, host, key) => def.Path + def.Method);
///
/// Endpoint limit per API key
///
- public static Func PerApiKey { get; } = new Func((def, host, key) => def.Path + def.Method);
+ public static Func PerApiKey { get; } = new Func((def, host, key) => def.Path + def.Method);
private readonly Dictionary _trackers;
private readonly RateLimitWindowType _windowType;
private readonly double? _decayRate;
private readonly int _limit;
private readonly TimeSpan _period;
- private readonly Func _keySelector;
+ private readonly Func _keySelector;
///
public string Name => "EndpointLimitGuard";
@@ -43,7 +43,7 @@ namespace CryptoExchange.Net.RateLimiting.Guards
TimeSpan period,
RateLimitWindowType windowType,
double? decayRate = null,
- Func? keySelector = null)
+ Func? keySelector = null)
{
_limit = limit;
_period = period;
@@ -54,7 +54,7 @@ namespace CryptoExchange.Net.RateLimiting.Guards
}
///
- 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);
if (!_trackers.TryGetValue(key, out var tracker))
@@ -71,7 +71,7 @@ namespace CryptoExchange.Net.RateLimiting.Guards
}
///
- 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 tracker = _trackers[key];
diff --git a/CryptoExchange.Net/RateLimiting/Interfaces/IGuardFilter.cs b/CryptoExchange.Net/RateLimiting/Interfaces/IGuardFilter.cs
index 9381f8b..7599db8 100644
--- a/CryptoExchange.Net/RateLimiting/Interfaces/IGuardFilter.cs
+++ b/CryptoExchange.Net/RateLimiting/Interfaces/IGuardFilter.cs
@@ -16,6 +16,6 @@ namespace CryptoExchange.Net.RateLimiting.Interfaces
/// The host address
/// The API key
/// True if passed
- bool Passes(RateLimitItemType type, RequestDefinition definition, string host, SecureString? apiKey);
+ bool Passes(RateLimitItemType type, RequestDefinition definition, string host, string? apiKey);
}
}
diff --git a/CryptoExchange.Net/RateLimiting/Interfaces/IRateLimitGate.cs b/CryptoExchange.Net/RateLimiting/Interfaces/IRateLimitGate.cs
index 38c1ff8..eb1d754 100644
--- a/CryptoExchange.Net/RateLimiting/Interfaces/IRateLimitGate.cs
+++ b/CryptoExchange.Net/RateLimiting/Interfaces/IRateLimitGate.cs
@@ -51,7 +51,7 @@ namespace CryptoExchange.Net.RateLimiting.Interfaces
/// Behaviour when rate limit is hit
/// Cancelation token
/// Error if RateLimitingBehaviour is Fail and rate limit is hit
- Task ProcessAsync(ILogger logger, int itemId, RateLimitItemType type, RequestDefinition definition, string baseAddress, SecureString? apiKey, int requestWeight, RateLimitingBehaviour behaviour, CancellationToken ct);
+ Task ProcessAsync(ILogger logger, int itemId, RateLimitItemType type, RequestDefinition definition, string baseAddress, string? apiKey, int requestWeight, RateLimitingBehaviour behaviour, CancellationToken ct);
///
/// 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
/// Behaviour when rate limit is hit
/// Cancelation token
/// Error if RateLimitingBehaviour is Fail and rate limit is hit
- Task ProcessSingleAsync(ILogger logger, int itemId, IRateLimitGuard guard, RateLimitItemType type, RequestDefinition definition, string baseAddress, SecureString? apiKey, RateLimitingBehaviour behaviour, CancellationToken ct);
+ Task ProcessSingleAsync(ILogger logger, int itemId, IRateLimitGuard guard, RateLimitItemType type, RequestDefinition definition, string baseAddress, string? apiKey, RateLimitingBehaviour behaviour, CancellationToken ct);
}
}
diff --git a/CryptoExchange.Net/RateLimiting/Interfaces/IRateLimitGuard.cs b/CryptoExchange.Net/RateLimiting/Interfaces/IRateLimitGuard.cs
index 7b303c3..7d3749d 100644
--- a/CryptoExchange.Net/RateLimiting/Interfaces/IRateLimitGuard.cs
+++ b/CryptoExchange.Net/RateLimiting/Interfaces/IRateLimitGuard.cs
@@ -28,7 +28,7 @@ namespace CryptoExchange.Net.RateLimiting.Interfaces
/// The API key
/// The request weight
///
- LimitCheck Check(RateLimitItemType type, RequestDefinition definition, string host, SecureString? apiKey, int requestWeight);
+ LimitCheck Check(RateLimitItemType type, RequestDefinition definition, string host, string? apiKey, int requestWeight);
///
/// Apply the request to this guard with the specified weight
@@ -39,6 +39,6 @@ namespace CryptoExchange.Net.RateLimiting.Interfaces
/// The API key
/// The request weight
///
- RateLimitState ApplyWeight(RateLimitItemType type, RequestDefinition definition, string host, SecureString? apiKey, int requestWeight);
+ RateLimitState ApplyWeight(RateLimitItemType type, RequestDefinition definition, string host, string? apiKey, int requestWeight);
}
}
diff --git a/CryptoExchange.Net/RateLimiting/RateLimitGate.cs b/CryptoExchange.Net/RateLimiting/RateLimitGate.cs
index afe91dd..cf12c96 100644
--- a/CryptoExchange.Net/RateLimiting/RateLimitGate.cs
+++ b/CryptoExchange.Net/RateLimiting/RateLimitGate.cs
@@ -36,7 +36,7 @@ namespace CryptoExchange.Net.RateLimiting
}
///
- public async Task ProcessAsync(ILogger logger, int itemId, RateLimitItemType type, RequestDefinition definition, string host, SecureString? apiKey, int requestWeight, RateLimitingBehaviour rateLimitingBehaviour, CancellationToken ct)
+ public async Task 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);
_waitingCount++;
@@ -58,8 +58,8 @@ namespace CryptoExchange.Net.RateLimiting
IRateLimitGuard guard,
RateLimitItemType type,
RequestDefinition definition,
- string host,
- SecureString? apiKey,
+ string host,
+ string? apiKey,
RateLimitingBehaviour rateLimitingBehaviour,
CancellationToken ct)
{
@@ -77,7 +77,7 @@ namespace CryptoExchange.Net.RateLimiting
}
}
- private async Task CheckGuardsAsync(IEnumerable guards, ILogger logger, int itemId, RateLimitItemType type, RequestDefinition definition, string host, SecureString? apiKey, int requestWeight, RateLimitingBehaviour rateLimitingBehaviour, CancellationToken ct)
+ private async Task CheckGuardsAsync(IEnumerable guards, ILogger logger, int itemId, RateLimitItemType type, RequestDefinition definition, string host, string? apiKey, int requestWeight, RateLimitingBehaviour rateLimitingBehaviour, CancellationToken ct)
{
foreach (var guard in guards)
{
diff --git a/CryptoExchange.Net/Testing/TestHelpers.cs b/CryptoExchange.Net/Testing/TestHelpers.cs
index 74df021..ba699d1 100644
--- a/CryptoExchange.Net/Testing/TestHelpers.cs
+++ b/CryptoExchange.Net/Testing/TestHelpers.cs
@@ -131,9 +131,9 @@ namespace CryptoExchange.Net.Testing
client,
new Uri(host.AppendPath(path)),
method,
- uriParams,
- bodyParams,
- headers,
+ ref uriParams,
+ ref bodyParams,
+ ref headers,
true,
client.ArraySerialization,
client.ParameterPositions[method],