1
0
mirror of https://github.com/JKorf/CryptoExchange.Net synced 2025-12-16 02:41:03 +00:00
This commit is contained in:
Jkorf 2025-12-15 10:26:49 +01:00
parent 23075453b1
commit a4db0d3637
12 changed files with 36 additions and 415 deletions

View File

@ -1,50 +0,0 @@
using CryptoExchange.Net.Objects;
using CryptoExchange.Net.Objects.Errors;
using CryptoExchange.Net.Objects.Sockets;
using CryptoExchange.Net.Sockets;
using CryptoExchange.Net.Sockets.Default;
using System;
using System.Text.Json.Serialization;
namespace CryptoExchange.Net.UnitTests.TestImplementations.Sockets
{
internal class SubResponse
{
[JsonPropertyName("action")]
public string Action { get; set; } = null!;
[JsonPropertyName("channel")]
public string Channel { get; set; } = null!;
[JsonPropertyName("status")]
public string Status { get; set; } = null!;
}
internal class UnsubResponse
{
[JsonPropertyName("action")]
public string Action { get; set; } = null!;
[JsonPropertyName("status")]
public string Status { get; set; } = null!;
}
internal class TestChannelQuery : Query<SubResponse>
{
public TestChannelQuery(string channel, string request, bool authenticated, int weight = 1) : base(request, authenticated, weight)
{
MessageMatcher = MessageMatcher.Create<SubResponse>(request + "-" + channel, HandleMessage);
}
public CallResult<SubResponse> HandleMessage(SocketConnection connection, DateTime time, string? originalData, SubResponse message)
{
if (!message.Status.Equals("confirmed", StringComparison.OrdinalIgnoreCase))
{
return new CallResult<SubResponse>(new ServerError(ErrorInfo.Unknown with { Message = message.Status }));
}
return new CallResult<SubResponse>(message, originalData, null);
}
}
}

View File

@ -1,12 +0,0 @@
using CryptoExchange.Net.Sockets;
namespace CryptoExchange.Net.UnitTests.TestImplementations.Sockets
{
internal class TestQuery : Query<object>
{
public TestQuery(string identifier, object request, bool authenticated, int weight = 1) : base(request, authenticated, weight)
{
MessageMatcher = MessageMatcher.Create<object>(identifier);
}
}
}

View File

@ -1,30 +0,0 @@
using CryptoExchange.Net.Objects;
using CryptoExchange.Net.Objects.Sockets;
using CryptoExchange.Net.Sockets;
using CryptoExchange.Net.Sockets.Default;
using Microsoft.Extensions.Logging;
using System;
namespace CryptoExchange.Net.UnitTests.TestImplementations.Sockets
{
internal class TestSubscription<T> : Subscription
{
private readonly Action<DataEvent<T>> _handler;
public TestSubscription(ILogger logger, Action<DataEvent<T>> handler) : base(logger, false)
{
_handler = handler;
MessageMatcher = MessageMatcher.Create<T>("update-topic", DoHandleMessage);
}
public CallResult DoHandleMessage(SocketConnection connection, DateTime receiveTime, string? originalData, T message)
{
_handler.Invoke(new DataEvent<T>("Test", message, receiveTime, originalData));
return new CallResult(null);
}
protected override Query GetSubQuery(SocketConnection connection) => new TestQuery("sub", new object(), false, 1);
protected override Query GetUnsubQuery(SocketConnection connection) => new TestQuery("unsub", new object(), false, 1);
}
}

View File

@ -1,32 +0,0 @@
using CryptoExchange.Net.Objects;
using CryptoExchange.Net.Objects.Sockets;
using CryptoExchange.Net.Sockets;
using CryptoExchange.Net.Sockets.Default;
using Microsoft.Extensions.Logging;
using Moq;
using System;
namespace CryptoExchange.Net.UnitTests.TestImplementations.Sockets
{
internal class TestSubscriptionWithResponseCheck<T> : Subscription
{
private readonly Action<DataEvent<T>> _handler;
private readonly string _channel;
public TestSubscriptionWithResponseCheck(string channel, Action<DataEvent<T>> handler) : base(Mock.Of<ILogger>(), false)
{
MessageMatcher = MessageMatcher.Create<T>(channel, DoHandleMessage);
_handler = handler;
_channel = channel;
}
public CallResult DoHandleMessage(SocketConnection connection, DateTime receiveTime, string? originalData, T message)
{
_handler.Invoke(new DataEvent<T>("Test", message, receiveTime, originalData));
return new CallResult(null);
}
protected override Query GetSubQuery(SocketConnection connection) => new TestChannelQuery(_channel, "subscribe", false, 1);
protected override Query GetUnsubQuery(SocketConnection connection) => new TestChannelQuery(_channel, "unsubscribe", false, 1);
}
}

View File

@ -86,4 +86,14 @@ namespace CryptoExchange.Net.UnitTests
public string GetKey() => _credentials.Key;
public string GetSecret() => _credentials.Secret;
}
public class TestEnvironment : TradeEnvironment
{
public string TestAddress { get; }
public TestEnvironment(string name, string url) : base(name)
{
TestAddress = url;
}
}
}

View File

@ -1,132 +0,0 @@
//using System;
//using System.IO;
//using System.Net.WebSockets;
//using System.Security.Authentication;
//using System.Text;
//using System.Threading.Tasks;
//using CryptoExchange.Net.Interfaces;
//using CryptoExchange.Net.Objects;
//namespace CryptoExchange.Net.UnitTests.TestImplementations
//{
// public class TestSocket: IWebsocket
// {
// public bool CanConnect { get; set; }
// public bool Connected { get; set; }
// public event Func<Task> OnClose;
//#pragma warning disable 0067
// public event Func<Task> OnReconnected;
// public event Func<Task> OnReconnecting;
// public event Func<int, Task> OnRequestRateLimited;
//#pragma warning restore 0067
// public event Func<int, Task> OnRequestSent;
// public event Func<WebSocketMessageType, ReadOnlyMemory<byte>, Task> OnStreamMessage;
// public event Func<Exception, Task> OnError;
// public event Func<Task> OnOpen;
// public Func<Task<Uri>> GetReconnectionUrl { get; set; }
// public int Id { get; }
// public bool ShouldReconnect { get; set; }
// public TimeSpan Timeout { get; set; }
// public Func<string, string> DataInterpreterString { get; set; }
// public Func<byte[], string> DataInterpreterBytes { get; set; }
// public DateTime? DisconnectTime { get; set; }
// public string Url { get; }
// public bool IsClosed => !Connected;
// public bool IsOpen => Connected;
// public bool PingConnection { get; set; }
// public TimeSpan PingInterval { get; set; }
// public SslProtocols SSLProtocols { get; set; }
// public Encoding Encoding { get; set; }
// public int ConnectCalls { get; private set; }
// public bool Reconnecting { get; set; }
// public string Origin { get; set; }
// public int? RatelimitPerSecond { get; set; }
// public double IncomingKbps => throw new NotImplementedException();
// public Uri Uri => new Uri("");
// public TimeSpan KeepAliveInterval { get; set; }
// public static int lastId = 0;
// public static object lastIdLock = new object();
// public TestSocket()
// {
// lock (lastIdLock)
// {
// Id = lastId + 1;
// lastId++;
// }
// }
// public Task<CallResult> ConnectAsync()
// {
// Connected = CanConnect;
// ConnectCalls++;
// if (CanConnect)
// InvokeOpen();
// return Task.FromResult(CanConnect ? new CallResult(null) : new CallResult(new CantConnectError()));
// }
// public bool Send(int requestId, string data, int weight)
// {
// if(!Connected)
// throw new Exception("Socket not connected");
// OnRequestSent?.Invoke(requestId);
// return true;
// }
// public void Reset()
// {
// }
// public Task CloseAsync()
// {
// Connected = false;
// DisconnectTime = DateTime.UtcNow;
// OnClose?.Invoke();
// return Task.FromResult(0);
// }
// public void SetProxy(string host, int port)
// {
// throw new NotImplementedException();
// }
// public void Dispose()
// {
// }
// public void InvokeClose()
// {
// Connected = false;
// DisconnectTime = DateTime.UtcNow;
// Reconnecting = true;
// OnClose?.Invoke();
// }
// public void InvokeOpen()
// {
// OnOpen?.Invoke();
// }
// public void InvokeMessage(string data)
// {
// OnStreamMessage?.Invoke(WebSocketMessageType.Text, new ReadOnlyMemory<byte>(Encoding.UTF8.GetBytes(data))).Wait();
// }
// public void SetProxy(ApiProxy proxy)
// {
// throw new NotImplementedException();
// }
// public void InvokeError(Exception error)
// {
// OnError?.Invoke(error);
// }
// public Task ReconnectAsync() => Task.CompletedTask;
// }
//}

View File

@ -1,148 +0,0 @@
//using System;
//using System.Collections.Generic;
//using System.Threading;
//using System.Threading.Tasks;
//using CryptoExchange.Net.Authentication;
//using CryptoExchange.Net.Clients;
//using CryptoExchange.Net.Converters.MessageParsing;
//using CryptoExchange.Net.Interfaces;
//using CryptoExchange.Net.Objects;
//using CryptoExchange.Net.Objects.Options;
//using CryptoExchange.Net.Objects.Sockets;
//using CryptoExchange.Net.Sockets;
//using CryptoExchange.Net.UnitTests.TestImplementations.Sockets;
//using Microsoft.Extensions.Logging;
//using Moq;
//using CryptoExchange.Net.Testing.Implementations;
//using CryptoExchange.Net.SharedApis;
//using Microsoft.Extensions.Options;
//using CryptoExchange.Net.Converters.SystemTextJson;
//using System.Net.WebSockets;
//using System.Text.Json;
//using CryptoExchange.Net.Converters.MessageParsing.DynamicConverters;
//namespace CryptoExchange.Net.UnitTests.TestImplementations
//{
// internal class TestSocketClient: BaseSocketClient
// {
// public TestSubSocketClient SubClient { get; }
// /// <summary>
// /// Create a new instance of TestSocketClient
// /// </summary>
// /// <param name="optionsFunc">Configure the options to use for this client</param>
// public TestSocketClient(Action<TestSocketOptions> optionsDelegate = null)
// : this(Options.Create(ApplyOptionsDelegate(optionsDelegate)), null)
// {
// }
// public TestSocketClient(IOptions<TestSocketOptions> options, ILoggerFactory loggerFactory = null) : base(loggerFactory, "Test")
// {
// Initialize(options.Value);
// SubClient = AddApiClient(new TestSubSocketClient(options.Value, options.Value.SubOptions));
// SubClient.SocketFactory = new Mock<IWebsocketFactory>().Object;
// Mock.Get(SubClient.SocketFactory).Setup(f => f.CreateWebsocket(It.IsAny<ILogger>(), It.IsAny<WebSocketParameters>())).Returns(new TestSocket("https://test.com"));
// }
// public TestSocket CreateSocket()
// {
// Mock.Get(SubClient.SocketFactory).Setup(f => f.CreateWebsocket(It.IsAny<ILogger>(), It.IsAny<WebSocketParameters>())).Returns(new TestSocket("https://test.com"));
// return (TestSocket)SubClient.CreateSocketInternal("https://localhost:123/");
// }
// }
using CryptoExchange.Net.Objects;
public class TestEnvironment : TradeEnvironment
{
public string TestAddress { get; }
public TestEnvironment(string name, string url) : base(name)
{
TestAddress = url;
}
}
// public class TestSocketOptions: SocketExchangeOptions<TestEnvironment>
// {
// public static TestSocketOptions Default = new TestSocketOptions
// {
// Environment = new TestEnvironment("Live", "https://test.test")
// };
// /// <summary>
// /// ctor
// /// </summary>
// public TestSocketOptions()
// {
// Default?.Set(this);
// }
// public SocketApiOptions SubOptions { get; set; } = new SocketApiOptions();
// internal TestSocketOptions Set(TestSocketOptions targetOptions)
// {
// targetOptions = base.Set<TestSocketOptions>(targetOptions);
// targetOptions.SubOptions = SubOptions.Set(targetOptions.SubOptions);
// return targetOptions;
// }
// }
// public class TestSubSocketClient : SocketApiClient
// {
// private MessagePath _channelPath = MessagePath.Get().Property("channel");
// private MessagePath _actionPath = MessagePath.Get().Property("action");
// private MessagePath _topicPath = MessagePath.Get().Property("topic");
// public Subscription TestSubscription { get; private set; } = null;
// public override JsonSerializerOptions JsonSerializerOptions => new JsonSerializerOptions();
// public TestSubSocketClient(TestSocketOptions options, SocketApiOptions apiOptions) : base(new TraceLogger(), options.Environment.TestAddress, options, apiOptions)
// {
// }
// protected internal override IByteMessageAccessor CreateAccessor(WebSocketMessageType type) => new SystemTextJsonByteMessageAccessor(new System.Text.Json.JsonSerializerOptions());
// protected internal override IMessageSerializer CreateSerializer() => new SystemTextJsonMessageSerializer(new System.Text.Json.JsonSerializerOptions());
// /// <inheritdoc />
// public override string FormatSymbol(string baseAsset, string quoteAsset, TradingMode futuresType, DateTime? deliverDate = null) => $"{baseAsset.ToUpperInvariant()}{quoteAsset.ToUpperInvariant()}";
// internal IWebsocket CreateSocketInternal(string address)
// {
// return SocketFactory.CreateWebsocket(_logger, );
// }
// protected override AuthenticationProvider CreateAuthenticationProvider(ApiCredentials credentials)
// => new TestAuthProvider(credentials);
// public CallResult ConnectSocketSub(SocketConnection sub)
// {
// return ConnectSocketAsync(sub, default).Result;
// }
// public override string GetListenerIdentifier(IMessageAccessor message)
// {
// if (!message.IsValid)
// {
// return "topic";
// }
// var id = message.GetValue<string>(_channelPath);
// id ??= message.GetValue<string>(_topicPath);
// return message.GetValue<string>(_actionPath) + "-" + id;
// }
// public Task<CallResult<UpdateSubscription>> SubscribeToSomethingAsync(string channel, Action<DataEvent<string>> onUpdate, CancellationToken ct)
// {
// TestSubscription = new TestSubscriptionWithResponseCheck<string>(channel, onUpdate);
// return SubscribeAsync(TestSubscription, ct);
// }
// public override IMessageConverter CreateMessageConverter() => throw new NotImplementedException();
// }
//}

View File

@ -82,7 +82,7 @@ namespace CryptoExchange.Net.Authentication
/// <summary>
/// Load a key from a file
/// </summary>
public string ReadFromFile(string path)
public static string ReadFromFile(string path)
{
using var fileStream = File.OpenRead(path);
using var streamReader = new StreamReader(fileStream);

View File

@ -884,21 +884,25 @@ namespace CryptoExchange.Net.Clients
/// <returns></returns>
public virtual async Task UnsubscribeAllAsync()
{
var sum = _socketConnections.Sum(s => s.Value.UserSubscriptionCount);
var sum = _socketConnections.Sum(s => s.Value.UserSubscriptionCount) + _highPerfSocketConnections.Sum(s => s.Value.UserSubscriptionCount);
if (sum == 0)
return;
_logger.UnsubscribingAll(_socketConnections.Sum(s => s.Value.UserSubscriptionCount));
_logger.UnsubscribingAll(sum);
var tasks = new List<Task>();
var socketList = _socketConnections.Values;
foreach (var connection in socketList)
{
var socketList = _socketConnections.Values;
foreach (var connection in socketList)
{
foreach(var subscription in connection.Subscriptions.Where(x => x.UserSubscription))
tasks.Add(connection.CloseAsync(subscription));
}
foreach(var subscription in connection.Subscriptions.Where(x => x.UserSubscription))
tasks.Add(connection.CloseAsync(subscription));
}
var highPerfSocketList = _highPerfSocketConnections.Values;
foreach (var connection in highPerfSocketList)
tasks.Add(connection.CloseAsync());
await Task.WhenAll(tasks.ToArray()).ConfigureAwait(false);
}

View File

@ -21,7 +21,6 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
public class ArrayConverter<T> : JsonConverter<T> where T : new()
#endif
{
//private static readonly Lazy<List<ArrayPropertyInfo>> _typePropertyInfo = new Lazy<List<ArrayPropertyInfo>>(CacheTypeAttributes, LazyThreadSafetyMode.PublicationOnly);
private static SortedDictionary<int, List<ArrayPropertyInfo>>? _typePropertyInfo;
/// <inheritdoc />

View File

@ -56,7 +56,7 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
{
var dtValue = (DateTime)(object)value;
var dtValue = value;
if (dtValue == default)
writer.WriteStringValue(default(DateTime));
else

View File

@ -194,7 +194,10 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
foreach (var item in _mappingToEnum)
{
if (item.StringValue.Equals(value, StringComparison.Ordinal))
{
mapping = item;
break;
}
}
// If not found, try matching ignoring case
@ -203,7 +206,10 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
foreach (var item in _mappingToEnum)
{
if (item.StringValue.Equals(value, StringComparison.OrdinalIgnoreCase))
{
mapping = item;
break;
}
}
}
@ -310,7 +316,10 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
foreach(var item in _mappingToEnum!)
{
if (item.StringValue.Equals(value, StringComparison.Ordinal))
{
mapping = item;
break;
}
}
// If not found, try matching ignoring case
@ -319,7 +328,10 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
foreach (var item in _mappingToEnum)
{
if (item.StringValue.Equals(value, StringComparison.OrdinalIgnoreCase))
{
mapping = item;
break;
}
}
}