mirror of
https://github.com/JKorf/CryptoExchange.Net
synced 2025-06-08 16:36:15 +00:00
Restruct
This commit is contained in:
parent
9a266e44ce
commit
69a6fabb79
@ -26,10 +26,6 @@ namespace CryptoExchange.Net
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected internal Log log;
|
protected internal Log log;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The authentication provider when api credentials have been provided
|
|
||||||
/// </summary>
|
|
||||||
protected internal AuthenticationProvider? authProvider;
|
|
||||||
/// <summary>
|
|
||||||
/// 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>
|
||||||
protected static int lastId;
|
protected static int lastId;
|
||||||
@ -57,11 +53,9 @@ namespace CryptoExchange.Net
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="exchangeName">The name of the exchange this client is for</param>
|
/// <param name="exchangeName">The name of the exchange this client is for</param>
|
||||||
/// <param name="options">The options for this client</param>
|
/// <param name="options">The options for this client</param>
|
||||||
/// <param name="authenticationProvider">The authentication provider for this client (can be null if no credentials are provided)</param>
|
protected BaseClient(string exchangeName, ClientOptions options)
|
||||||
protected BaseClient(string exchangeName, ClientOptions options, AuthenticationProvider? authenticationProvider)
|
|
||||||
{
|
{
|
||||||
log = new Log(exchangeName);
|
log = new Log(exchangeName);
|
||||||
authProvider = authenticationProvider;
|
|
||||||
log.UpdateWriters(options.LogWriters);
|
log.UpdateWriters(options.LogWriters);
|
||||||
log.Level = options.LogLevel;
|
log.Level = options.LogLevel;
|
||||||
|
|
||||||
@ -72,16 +66,6 @@ namespace CryptoExchange.Net
|
|||||||
log.Write(LogLevel.Debug, $"Client configuration: {options}, CryptoExchange.Net: v{typeof(BaseClient).Assembly.GetName().Version}, {ExchangeName}.Net: v{GetType().Assembly.GetName().Version}");
|
log.Write(LogLevel.Debug, $"Client configuration: {options}, CryptoExchange.Net: v{typeof(BaseClient).Assembly.GetName().Version}, {ExchangeName}.Net: v{GetType().Assembly.GetName().Version}");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Set the authentication provider, can be used when manually setting the API credentials
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="authenticationProvider"></param>
|
|
||||||
protected void SetAuthenticationProvider(AuthenticationProvider authenticationProvider)
|
|
||||||
{
|
|
||||||
log.Write(LogLevel.Debug, "Setting api credentials");
|
|
||||||
authProvider = authenticationProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tries to parse the json data and return a JToken, validating the input not being empty and being valid json
|
/// Tries to parse the json data and return a JToken, validating the input not being empty and being valid json
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -273,32 +257,13 @@ namespace CryptoExchange.Net
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <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>
|
|
||||||
protected static string FillPathParameter(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>
|
||||||
/// Dispose
|
/// Dispose
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual void Dispose()
|
public virtual void Dispose()
|
||||||
{
|
{
|
||||||
authProvider?.Credentials?.Dispose();
|
// TODO
|
||||||
|
//authProvider?.Credentials?.Dispose();
|
||||||
log.Write(LogLevel.Debug, "Disposing exchange client");
|
log.Write(LogLevel.Debug, "Disposing exchange client");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -85,7 +85,12 @@ namespace CryptoExchange.Net.Converters
|
|||||||
// Parse 1637745563.000 format
|
// Parse 1637745563.000 format
|
||||||
if (doubleValue < 1999999999)
|
if (doubleValue < 1999999999)
|
||||||
return ConvertFromSeconds(doubleValue);
|
return ConvertFromSeconds(doubleValue);
|
||||||
return ConvertFromMilliseconds(doubleValue);
|
if (doubleValue < 1999999999999)
|
||||||
|
return ConvertFromMilliseconds((long)doubleValue);
|
||||||
|
if (doubleValue < 1999999999999999)
|
||||||
|
return ConvertFromMicroseconds((long)doubleValue);
|
||||||
|
|
||||||
|
return ConvertFromNanoseconds((long)doubleValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(stringValue.Length == 10)
|
if(stringValue.Length == 10)
|
||||||
|
@ -72,7 +72,7 @@ namespace CryptoExchange.Net
|
|||||||
/// <param name="value"></param>
|
/// <param name="value"></param>
|
||||||
public static void AddOptionalParameter(this Dictionary<string, object> parameters, string key, object? value)
|
public static void AddOptionalParameter(this Dictionary<string, object> parameters, string key, object? value)
|
||||||
{
|
{
|
||||||
if(value != null)
|
if (value != null)
|
||||||
parameters.Add(key, value);
|
parameters.Add(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,7 +127,7 @@ namespace CryptoExchange.Net
|
|||||||
var arraysParameters = parameters.Where(p => p.Value.GetType().IsArray).ToList();
|
var arraysParameters = parameters.Where(p => p.Value.GetType().IsArray).ToList();
|
||||||
foreach (var arrayEntry in arraysParameters)
|
foreach (var arrayEntry in arraysParameters)
|
||||||
{
|
{
|
||||||
if(serializationType == ArrayParametersSerialization.Array)
|
if (serializationType == ArrayParametersSerialization.Array)
|
||||||
uriString += $"{string.Join("&", ((object[])(urlEncodeValues ? Uri.EscapeDataString(arrayEntry.Value.ToString()) : arrayEntry.Value)).Select(v => $"{arrayEntry.Key}[]={v}"))}&";
|
uriString += $"{string.Join("&", ((object[])(urlEncodeValues ? Uri.EscapeDataString(arrayEntry.Value.ToString()) : arrayEntry.Value)).Select(v => $"{arrayEntry.Key}[]={v}"))}&";
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -369,6 +369,26 @@ 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,33 +60,42 @@ namespace CryptoExchange.Net.Objects
|
|||||||
public bool ChecksumValidationEnabled { get; set; } = true;
|
public bool ChecksumValidationEnabled { get; set; } = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public class SubClientOptions
|
||||||
/// Base client options
|
|
||||||
/// </summary>
|
|
||||||
public class ClientOptions : BaseOptions
|
|
||||||
{
|
{
|
||||||
private string _baseAddress = string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The base address of the client
|
/// The base address of the sub client
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string BaseAddress
|
public string BaseAddress { get; set; }
|
||||||
{
|
|
||||||
get => _baseAddress;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_baseAddress = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The api credentials used for signing requests
|
/// The api credentials used for signing requests
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ApiCredentials? ApiCredentials { get; set; }
|
public ApiCredentials? ApiCredentials { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Copy the values of the def to the input
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="input"></param>
|
||||||
|
/// <param name="def"></param>
|
||||||
|
public new void Copy<T>(T input, T def) where T : SubClientOptions
|
||||||
|
{
|
||||||
|
input.BaseAddress = def.BaseAddress;
|
||||||
|
input.ApiCredentials = def.ApiCredentials?.Copy();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return $"{base.ToString()}, Credentials: {(ApiCredentials == null ? "-" : "Set")}, BaseAddress: {BaseAddress}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Base client options
|
||||||
|
/// </summary>
|
||||||
|
public class ClientOptions : BaseOptions
|
||||||
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Proxy to use when connecting
|
/// Proxy to use when connecting
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -102,22 +111,17 @@ namespace CryptoExchange.Net.Objects
|
|||||||
{
|
{
|
||||||
base.Copy(input, def);
|
base.Copy(input, def);
|
||||||
|
|
||||||
input.BaseAddress = def.BaseAddress;
|
|
||||||
input.ApiCredentials = def.ApiCredentials?.Copy();
|
|
||||||
input.Proxy = def.Proxy;
|
input.Proxy = def.Proxy;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return $"{base.ToString()}, Credentials: {(ApiCredentials == null ? "-" : "Set")}, BaseAddress: {BaseAddress}, Proxy: {(Proxy == null ? "-" : Proxy.Host)}";
|
return $"{base.ToString()}, Proxy: {(Proxy == null ? "-" : Proxy.Host)}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public class RestSubClientOptions: SubClientOptions
|
||||||
/// Base for rest client options
|
|
||||||
/// </summary>
|
|
||||||
public class RestClientOptions : ClientOptions
|
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// List of rate limiters to use
|
/// List of rate limiters to use
|
||||||
@ -129,6 +133,32 @@ namespace CryptoExchange.Net.Objects
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public RateLimitingBehaviour RateLimitingBehaviour { get; set; } = RateLimitingBehaviour.Wait;
|
public RateLimitingBehaviour RateLimitingBehaviour { get; set; } = RateLimitingBehaviour.Wait;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Copy the values of the def to the input
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="input"></param>
|
||||||
|
/// <param name="def"></param>
|
||||||
|
public new void Copy<T>(T input, T def) where T : RestSubClientOptions
|
||||||
|
{
|
||||||
|
base.Copy(input, def);
|
||||||
|
|
||||||
|
input.RateLimiters = def.RateLimiters.ToList();
|
||||||
|
input.RateLimitingBehaviour = def.RateLimitingBehaviour;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return $"{base.ToString()}, RateLimiters: {RateLimiters.Count}, RateLimitBehaviour: {RateLimitingBehaviour}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Base for rest client options
|
||||||
|
/// </summary>
|
||||||
|
public class RestClientOptions : ClientOptions
|
||||||
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The time the server has to respond to a request before timing out
|
/// The time the server has to respond to a request before timing out
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -150,18 +180,21 @@ namespace CryptoExchange.Net.Objects
|
|||||||
base.Copy(input, def);
|
base.Copy(input, def);
|
||||||
|
|
||||||
input.HttpClient = def.HttpClient;
|
input.HttpClient = def.HttpClient;
|
||||||
input.RateLimiters = def.RateLimiters.ToList();
|
|
||||||
input.RateLimitingBehaviour = def.RateLimitingBehaviour;
|
|
||||||
input.RequestTimeout = def.RequestTimeout;
|
input.RequestTimeout = def.RequestTimeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return $"{base.ToString()}, RateLimiters: {RateLimiters.Count}, RateLimitBehaviour: {RateLimitingBehaviour}, RequestTimeout: {RequestTimeout:c}, HttpClient: {(HttpClient == null ? "-": "set")}";
|
return $"{base.ToString()}, RequestTimeout: {RequestTimeout:c}, HttpClient: {(HttpClient == null ? "-": "set")}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class SocketSubClientOptions: SubClientOptions
|
||||||
|
{
|
||||||
|
// TODO do we need this?
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Base for socket client options
|
/// Base for socket client options
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -59,11 +59,6 @@ namespace CryptoExchange.Net
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected string requestBodyEmptyContent = "{}";
|
protected string requestBodyEmptyContent = "{}";
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// List of rate limiters
|
|
||||||
/// </summary>
|
|
||||||
protected IEnumerable<IRateLimiter> RateLimiters { get; }
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public int TotalRequestsMade { get; private set; }
|
public int TotalRequestsMade { get; private set; }
|
||||||
|
|
||||||
@ -82,8 +77,7 @@ namespace CryptoExchange.Net
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="exchangeName">The name of the exchange this client is for</param>
|
/// <param name="exchangeName">The name of the exchange this client is for</param>
|
||||||
/// <param name="exchangeOptions">The options for this client</param>
|
/// <param name="exchangeOptions">The options for this client</param>
|
||||||
/// <param name="authenticationProvider">The authentication provider for this client (can be null if no credentials are provided)</param>
|
protected RestClient(string exchangeName, RestClientOptions exchangeOptions) : base(exchangeName, exchangeOptions)
|
||||||
protected RestClient(string exchangeName, RestClientOptions exchangeOptions, AuthenticationProvider? authenticationProvider) : base(exchangeName, exchangeOptions, authenticationProvider)
|
|
||||||
{
|
{
|
||||||
if (exchangeOptions == null)
|
if (exchangeOptions == null)
|
||||||
throw new ArgumentNullException(nameof(exchangeOptions));
|
throw new ArgumentNullException(nameof(exchangeOptions));
|
||||||
@ -91,10 +85,6 @@ namespace CryptoExchange.Net
|
|||||||
ClientOptions = exchangeOptions;
|
ClientOptions = exchangeOptions;
|
||||||
RequestFactory.Configure(exchangeOptions.RequestTimeout, exchangeOptions.Proxy, exchangeOptions.HttpClient);
|
RequestFactory.Configure(exchangeOptions.RequestTimeout, exchangeOptions.Proxy, exchangeOptions.HttpClient);
|
||||||
|
|
||||||
var rateLimiters = new List<IRateLimiter>();
|
|
||||||
foreach (var rateLimiter in exchangeOptions.RateLimiters)
|
|
||||||
rateLimiters.Add(rateLimiter);
|
|
||||||
RateLimiters = rateLimiters;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -114,30 +104,32 @@ namespace CryptoExchange.Net
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[return: NotNull]
|
[return: NotNull]
|
||||||
protected virtual async Task<WebCallResult<T>> SendRequestAsync<T>(
|
protected virtual async Task<WebCallResult<T>> SendRequestAsync<T>(
|
||||||
|
RestSubClient subClient,
|
||||||
Uri uri,
|
Uri uri,
|
||||||
HttpMethod method,
|
HttpMethod method,
|
||||||
CancellationToken cancellationToken,
|
CancellationToken cancellationToken,
|
||||||
Dictionary<string, object>? parameters = null,
|
Dictionary<string, object>? parameters = null,
|
||||||
bool signed = false,
|
bool signed = false,
|
||||||
HttpMethodParameterPosition? parameterPosition = null,
|
HttpMethodParameterPosition? parameterPosition = null,
|
||||||
ArrayParametersSerialization? arraySerialization = null,
|
ArrayParametersSerialization? arraySerialization = null,
|
||||||
int requestWeight = 1,
|
int requestWeight = 1,
|
||||||
JsonSerializer? deserializer = null,
|
JsonSerializer? deserializer = null,
|
||||||
Dictionary<string, string>? additionalHeaders = null) where T : class
|
Dictionary<string, string>? additionalHeaders = null
|
||||||
|
) where T : class
|
||||||
{
|
{
|
||||||
var requestId = NextId();
|
var requestId = NextId();
|
||||||
log.Write(LogLevel.Debug, $"[{requestId}] Creating request for " + uri);
|
log.Write(LogLevel.Debug, $"[{requestId}] Creating request for " + uri);
|
||||||
if (signed && authProvider == null)
|
if (signed && subClient.AuthenticationProvider == null)
|
||||||
{
|
{
|
||||||
log.Write(LogLevel.Warning, $"[{requestId}] Request {uri.AbsolutePath} failed because no ApiCredentials were provided");
|
log.Write(LogLevel.Warning, $"[{requestId}] Request {uri.AbsolutePath} failed because no ApiCredentials were provided");
|
||||||
return new WebCallResult<T>(null, null, null, new NoApiCredentialsError());
|
return new WebCallResult<T>(null, null, null, new NoApiCredentialsError());
|
||||||
}
|
}
|
||||||
|
|
||||||
var paramsPosition = parameterPosition ?? ParameterPositions[method];
|
var paramsPosition = parameterPosition ?? ParameterPositions[method];
|
||||||
var request = ConstructRequest(uri, method, parameters, signed, paramsPosition, arraySerialization ?? this.arraySerialization, requestId, additionalHeaders);
|
var request = ConstructRequest(subClient, uri, method, parameters, signed, paramsPosition, arraySerialization ?? this.arraySerialization, requestId, additionalHeaders);
|
||||||
foreach (var limiter in RateLimiters)
|
foreach (var limiter in subClient.RateLimiters)
|
||||||
{
|
{
|
||||||
var limitResult = await limiter.LimitRequestAsync(log, uri.AbsolutePath, method, signed, ClientOptions.ApiCredentials?.Key, ClientOptions.RateLimitingBehaviour, requestWeight, cancellationToken).ConfigureAwait(false);
|
var limitResult = await limiter.LimitRequestAsync(log, uri.AbsolutePath, method, signed, subClient.Options.ApiCredentials?.Key, subClient.Options.RateLimitingBehaviour, requestWeight, cancellationToken).ConfigureAwait(false);
|
||||||
if (!limitResult.Success)
|
if (!limitResult.Success)
|
||||||
return new WebCallResult<T>(null, null, null, limitResult.Error);
|
return new WebCallResult<T>(null, null, null, limitResult.Error);
|
||||||
}
|
}
|
||||||
@ -275,6 +267,7 @@ namespace CryptoExchange.Net
|
|||||||
/// <param name="additionalHeaders">Additional headers to send with the request</param>
|
/// <param name="additionalHeaders">Additional headers to send with the request</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
protected virtual IRequest ConstructRequest(
|
protected virtual IRequest ConstructRequest(
|
||||||
|
SubClient subClient,
|
||||||
Uri uri,
|
Uri uri,
|
||||||
HttpMethod method,
|
HttpMethod method,
|
||||||
Dictionary<string, object>? parameters,
|
Dictionary<string, object>? parameters,
|
||||||
@ -287,8 +280,8 @@ namespace CryptoExchange.Net
|
|||||||
parameters ??= new Dictionary<string, object>();
|
parameters ??= new Dictionary<string, object>();
|
||||||
|
|
||||||
var uriString = uri.ToString();
|
var uriString = uri.ToString();
|
||||||
if (authProvider != null)
|
if (subClient.AuthenticationProvider != null)
|
||||||
parameters = authProvider.AddAuthenticationToParameters(uriString, method, parameters, signed, parameterPosition, arraySerialization);
|
parameters = subClient.AuthenticationProvider.AddAuthenticationToParameters(uriString, method, parameters, signed, parameterPosition, arraySerialization);
|
||||||
|
|
||||||
if (parameterPosition == HttpMethodParameterPosition.InUri && parameters?.Any() == true)
|
if (parameterPosition == HttpMethodParameterPosition.InUri && parameters?.Any() == true)
|
||||||
uriString += "?" + parameters.CreateParamString(true, arraySerialization);
|
uriString += "?" + parameters.CreateParamString(true, arraySerialization);
|
||||||
@ -298,8 +291,8 @@ namespace CryptoExchange.Net
|
|||||||
request.Accept = Constants.JsonContentHeader;
|
request.Accept = Constants.JsonContentHeader;
|
||||||
|
|
||||||
var headers = new Dictionary<string, string>();
|
var headers = new Dictionary<string, string>();
|
||||||
if (authProvider != null)
|
if (subClient.AuthenticationProvider != null)
|
||||||
headers = authProvider.AddAuthenticationToHeaders(uriString, method, parameters!, signed, parameterPosition, arraySerialization);
|
headers = subClient.AuthenticationProvider.AddAuthenticationToHeaders(uriString, method, parameters!, signed, parameterPosition, arraySerialization);
|
||||||
|
|
||||||
foreach (var header in headers)
|
foreach (var header in headers)
|
||||||
request.AddHeader(header.Key, header.Value);
|
request.AddHeader(header.Key, header.Value);
|
||||||
|
44
CryptoExchange.Net/RestSubClient.cs
Normal file
44
CryptoExchange.Net/RestSubClient.cs
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Web;
|
||||||
|
using CryptoExchange.Net.Authentication;
|
||||||
|
using CryptoExchange.Net.Interfaces;
|
||||||
|
using CryptoExchange.Net.Objects;
|
||||||
|
using CryptoExchange.Net.Requests;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
namespace CryptoExchange.Net
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Base rest client
|
||||||
|
/// </summary>
|
||||||
|
public abstract class RestSubClient: SubClient
|
||||||
|
{
|
||||||
|
internal RestSubClientOptions Options { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// List of rate limiters
|
||||||
|
/// </summary>
|
||||||
|
internal IEnumerable<IRateLimiter> RateLimiters { get; }
|
||||||
|
|
||||||
|
public RestSubClient(RestSubClientOptions options, AuthenticationProvider? authProvider): base(options,authProvider)
|
||||||
|
{
|
||||||
|
Options = options;
|
||||||
|
|
||||||
|
var rateLimiters = new List<IRateLimiter>();
|
||||||
|
foreach (var rateLimiter in options.RateLimiters)
|
||||||
|
rateLimiters.Add(rateLimiter);
|
||||||
|
RateLimiters = rateLimiters;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -104,8 +104,7 @@ namespace CryptoExchange.Net
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="exchangeName">The name of the exchange this client is for</param>
|
/// <param name="exchangeName">The name of the exchange this client is for</param>
|
||||||
/// <param name="exchangeOptions">The options for this client</param>
|
/// <param name="exchangeOptions">The options for this client</param>
|
||||||
/// <param name="authenticationProvider">The authentication provider for this client (can be null if no credentials are provided)</param>
|
protected SocketClient(string exchangeName, SocketClientOptions exchangeOptions): base(exchangeName, exchangeOptions)
|
||||||
protected SocketClient(string exchangeName, SocketClientOptions exchangeOptions, AuthenticationProvider? authenticationProvider): base(exchangeName, exchangeOptions, authenticationProvider)
|
|
||||||
{
|
{
|
||||||
if (exchangeOptions == null)
|
if (exchangeOptions == null)
|
||||||
throw new ArgumentNullException(nameof(exchangeOptions));
|
throw new ArgumentNullException(nameof(exchangeOptions));
|
||||||
@ -134,9 +133,9 @@ namespace CryptoExchange.Net
|
|||||||
/// <param name="dataHandler">The handler of update data</param>
|
/// <param name="dataHandler">The handler of update data</param>
|
||||||
/// <param name="ct">Cancellation token for closing this subscription</param>
|
/// <param name="ct">Cancellation token for closing this subscription</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
protected virtual Task<CallResult<UpdateSubscription>> SubscribeAsync<T>(object? request, string? identifier, bool authenticated, Action<DataEvent<T>> dataHandler, CancellationToken ct)
|
protected virtual Task<CallResult<UpdateSubscription>> SubscribeAsync<T>(SocketSubClient subClient, object? request, string? identifier, bool authenticated, Action<DataEvent<T>> dataHandler, CancellationToken ct)
|
||||||
{
|
{
|
||||||
return SubscribeAsync(ClientOptions.BaseAddress, request, identifier, authenticated, dataHandler, ct);
|
return SubscribeAsync(subClient, subClient.Options.BaseAddress, request, identifier, authenticated, dataHandler, ct);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -150,7 +149,7 @@ namespace CryptoExchange.Net
|
|||||||
/// <param name="dataHandler">The handler of update data</param>
|
/// <param name="dataHandler">The handler of update data</param>
|
||||||
/// <param name="ct">Cancellation token for closing this subscription</param>
|
/// <param name="ct">Cancellation token for closing this subscription</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
protected virtual async Task<CallResult<UpdateSubscription>> SubscribeAsync<T>(string url, object? request, string? identifier, bool authenticated, Action<DataEvent<T>> dataHandler, CancellationToken ct)
|
protected virtual async Task<CallResult<UpdateSubscription>> SubscribeAsync<T>(SocketSubClient subClient, string url, object? request, string? identifier, bool authenticated, Action<DataEvent<T>> dataHandler, CancellationToken ct)
|
||||||
{
|
{
|
||||||
SocketConnection socketConnection;
|
SocketConnection socketConnection;
|
||||||
SocketSubscription subscription;
|
SocketSubscription subscription;
|
||||||
@ -169,7 +168,7 @@ namespace CryptoExchange.Net
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Get a new or existing socket connection
|
// Get a new or existing socket connection
|
||||||
socketConnection = GetSocketConnection(url, authenticated);
|
socketConnection = GetSocketConnection(subClient, url, authenticated);
|
||||||
|
|
||||||
// Add a subscription on the socket connection
|
// Add a subscription on the socket connection
|
||||||
subscription = AddSubscription(request, identifier, true, socketConnection, dataHandler);
|
subscription = AddSubscription(request, identifier, true, socketConnection, dataHandler);
|
||||||
@ -254,9 +253,9 @@ namespace CryptoExchange.Net
|
|||||||
/// <param name="request">The request to send, will be serialized to json</param>
|
/// <param name="request">The request to send, will be serialized to json</param>
|
||||||
/// <param name="authenticated">If the query is to an authenticated endpoint</param>
|
/// <param name="authenticated">If the query is to an authenticated endpoint</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
protected virtual Task<CallResult<T>> QueryAsync<T>(object request, bool authenticated)
|
protected virtual Task<CallResult<T>> QueryAsync<T>(SocketSubClient subClient, object request, bool authenticated)
|
||||||
{
|
{
|
||||||
return QueryAsync<T>(ClientOptions.BaseAddress, request, authenticated);
|
return QueryAsync<T>(subClient, subClient.Options.BaseAddress, request, authenticated);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -267,14 +266,14 @@ namespace CryptoExchange.Net
|
|||||||
/// <param name="request">The request to send</param>
|
/// <param name="request">The request to send</param>
|
||||||
/// <param name="authenticated">Whether the socket should be authenticated</param>
|
/// <param name="authenticated">Whether the socket should be authenticated</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
protected virtual async Task<CallResult<T>> QueryAsync<T>(string url, object request, bool authenticated)
|
protected virtual async Task<CallResult<T>> QueryAsync<T>(SocketSubClient subClient, string url, object request, bool authenticated)
|
||||||
{
|
{
|
||||||
SocketConnection socketConnection;
|
SocketConnection socketConnection;
|
||||||
var released = false;
|
var released = false;
|
||||||
await semaphoreSlim.WaitAsync().ConfigureAwait(false);
|
await semaphoreSlim.WaitAsync().ConfigureAwait(false);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
socketConnection = GetSocketConnection(url, authenticated);
|
socketConnection = GetSocketConnection(subClient, url, authenticated);
|
||||||
if (ClientOptions.SocketSubscriptionsCombineTarget == 1)
|
if (ClientOptions.SocketSubscriptionsCombineTarget == 1)
|
||||||
{
|
{
|
||||||
// Can release early when only a single sub per connection
|
// Can release early when only a single sub per connection
|
||||||
@ -481,9 +480,10 @@ namespace CryptoExchange.Net
|
|||||||
/// <param name="address">The address the socket is for</param>
|
/// <param name="address">The address the socket is for</param>
|
||||||
/// <param name="authenticated">Whether the socket should be authenticated</param>
|
/// <param name="authenticated">Whether the socket should be authenticated</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
protected virtual SocketConnection GetSocketConnection(string address, bool authenticated)
|
protected virtual SocketConnection GetSocketConnection(SocketSubClient subClient, string address, bool authenticated)
|
||||||
{
|
{
|
||||||
var socketResult = sockets.Where(s => s.Value.Socket.Url.TrimEnd('/') == address.TrimEnd('/')
|
var socketResult = sockets.Where(s => s.Value.Socket.Url.TrimEnd('/') == address.TrimEnd('/')
|
||||||
|
&& (s.Value.SubClient.GetType() == subClient.GetType())
|
||||||
&& (s.Value.Authenticated == authenticated || !authenticated) && s.Value.Connected).OrderBy(s => s.Value.SubscriptionCount).FirstOrDefault();
|
&& (s.Value.Authenticated == authenticated || !authenticated) && s.Value.Connected).OrderBy(s => s.Value.SubscriptionCount).FirstOrDefault();
|
||||||
var result = socketResult.Equals(default(KeyValuePair<int, SocketConnection>)) ? null : socketResult.Value;
|
var result = socketResult.Equals(default(KeyValuePair<int, SocketConnection>)) ? null : socketResult.Value;
|
||||||
if (result != null)
|
if (result != null)
|
||||||
@ -497,7 +497,7 @@ namespace CryptoExchange.Net
|
|||||||
|
|
||||||
// Create new socket
|
// Create new socket
|
||||||
var socket = CreateSocket(address);
|
var socket = CreateSocket(address);
|
||||||
var socketConnection = new SocketConnection(this, socket);
|
var socketConnection = new SocketConnection(this, subClient, socket);
|
||||||
socketConnection.UnhandledMessage += HandleUnhandledMessage;
|
socketConnection.UnhandledMessage += HandleUnhandledMessage;
|
||||||
foreach (var kvp in genericHandlers)
|
foreach (var kvp in genericHandlers)
|
||||||
{
|
{
|
||||||
|
34
CryptoExchange.Net/SocketSubClient.cs
Normal file
34
CryptoExchange.Net/SocketSubClient.cs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Web;
|
||||||
|
using CryptoExchange.Net.Authentication;
|
||||||
|
using CryptoExchange.Net.Interfaces;
|
||||||
|
using CryptoExchange.Net.Objects;
|
||||||
|
using CryptoExchange.Net.Requests;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
namespace CryptoExchange.Net
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Base rest client
|
||||||
|
/// </summary>
|
||||||
|
public abstract class SocketSubClient : SubClient
|
||||||
|
{
|
||||||
|
internal SocketSubClientOptions Options { get; }
|
||||||
|
|
||||||
|
public SocketSubClient(SocketSubClientOptions options, AuthenticationProvider? authProvider): base(options,authProvider)
|
||||||
|
{
|
||||||
|
Options = options;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -224,7 +224,7 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
using CancellationTokenSource tcs = new CancellationTokenSource(TimeSpan.FromSeconds(10));
|
using CancellationTokenSource tcs = new CancellationTokenSource(TimeSpan.FromSeconds(10));
|
||||||
await _socket.ConnectAsync(new Uri(Url), default).ConfigureAwait(false);
|
await _socket.ConnectAsync(new Uri(Url), tcs.Token).ConfigureAwait(false);
|
||||||
|
|
||||||
Handle(openHandlers);
|
Handle(openHandlers);
|
||||||
}
|
}
|
||||||
|
@ -77,6 +77,8 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public IWebsocket Socket { get; set; }
|
public IWebsocket Socket { get; set; }
|
||||||
|
|
||||||
|
public SocketSubClient SubClient { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// If the socket should be reconnected upon closing
|
/// If the socket should be reconnected upon closing
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -97,6 +99,11 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public DateTime? DisconnectTime { get; set; }
|
public DateTime? DisconnectTime { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tag for identificaion
|
||||||
|
/// </summary>
|
||||||
|
public string? Tag { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// If activity is paused
|
/// If activity is paused
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -130,10 +137,11 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="client">The socket client</param>
|
/// <param name="client">The socket client</param>
|
||||||
/// <param name="socket">The socket</param>
|
/// <param name="socket">The socket</param>
|
||||||
public SocketConnection(SocketClient client, IWebsocket socket)
|
public SocketConnection(SocketClient client, SocketSubClient subClient, IWebsocket socket)
|
||||||
{
|
{
|
||||||
log = client.log;
|
log = client.log;
|
||||||
socketClient = client;
|
socketClient = client;
|
||||||
|
SubClient = subClient;
|
||||||
|
|
||||||
pendingRequests = new List<PendingRequest>();
|
pendingRequests = new List<PendingRequest>();
|
||||||
|
|
||||||
|
36
CryptoExchange.Net/SubClient.cs
Normal file
36
CryptoExchange.Net/SubClient.cs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Web;
|
||||||
|
using CryptoExchange.Net.Authentication;
|
||||||
|
using CryptoExchange.Net.Interfaces;
|
||||||
|
using CryptoExchange.Net.Objects;
|
||||||
|
using CryptoExchange.Net.Requests;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
namespace CryptoExchange.Net
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Base rest client
|
||||||
|
/// </summary>
|
||||||
|
public abstract class SubClient
|
||||||
|
{
|
||||||
|
public AuthenticationProvider? AuthenticationProvider { get; }
|
||||||
|
protected string BaseAddress { get; }
|
||||||
|
|
||||||
|
public SubClient(SubClientOptions options, AuthenticationProvider? authProvider)
|
||||||
|
{
|
||||||
|
AuthenticationProvider = authProvider;
|
||||||
|
BaseAddress = options.BaseAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user