1
0
mirror of https://github.com/JKorf/CryptoExchange.Net synced 2025-12-14 18:00:26 +00:00
This commit is contained in:
JKorf 2025-11-16 21:41:03 +01:00 committed by Jkorf
parent 68f772a13a
commit 9c43f58e6c
18 changed files with 683 additions and 471 deletions

View File

@ -1,234 +1,234 @@
using CryptoExchange.Net.Objects;
using CryptoExchange.Net.Objects.Sockets;
using CryptoExchange.Net.Sockets;
using CryptoExchange.Net.Testing.Implementations;
using CryptoExchange.Net.UnitTests.TestImplementations;
using CryptoExchange.Net.UnitTests.TestImplementations.Sockets;
using Microsoft.Extensions.Logging;
using Moq;
using NUnit.Framework;
using NUnit.Framework.Legacy;
using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
//using CryptoExchange.Net.Objects;
//using CryptoExchange.Net.Objects.Sockets;
//using CryptoExchange.Net.Sockets;
//using CryptoExchange.Net.Testing.Implementations;
//using CryptoExchange.Net.UnitTests.TestImplementations;
//using CryptoExchange.Net.UnitTests.TestImplementations.Sockets;
//using Microsoft.Extensions.Logging;
//using Moq;
//using NUnit.Framework;
//using NUnit.Framework.Legacy;
//using System;
//using System.Collections.Generic;
//using System.Net.Sockets;
//using System.Text.Json;
//using System.Threading;
//using System.Threading.Tasks;
namespace CryptoExchange.Net.UnitTests
{
[TestFixture]
public class SocketClientTests
{
[TestCase]
public void SettingOptions_Should_ResultInOptionsSet()
{
//arrange
//act
var client = new TestSocketClient(options =>
{
options.SubOptions.ApiCredentials = new Authentication.ApiCredentials("1", "2");
options.SubOptions.MaxSocketConnections = 1;
});
//namespace CryptoExchange.Net.UnitTests
//{
// [TestFixture]
// public class SocketClientTests
// {
// [TestCase]
// public void SettingOptions_Should_ResultInOptionsSet()
// {
// //arrange
// //act
// var client = new TestSocketClient(options =>
// {
// options.SubOptions.ApiCredentials = new Authentication.ApiCredentials("1", "2");
// options.SubOptions.MaxSocketConnections = 1;
// });
//assert
ClassicAssert.NotNull(client.SubClient.ApiOptions.ApiCredentials);
Assert.That(1 == client.SubClient.ApiOptions.MaxSocketConnections);
}
// //assert
// ClassicAssert.NotNull(client.SubClient.ApiOptions.ApiCredentials);
// Assert.That(1 == client.SubClient.ApiOptions.MaxSocketConnections);
// }
[TestCase(true)]
[TestCase(false)]
public void ConnectSocket_Should_ReturnConnectionResult(bool canConnect)
{
//arrange
var client = new TestSocketClient();
var socket = client.CreateSocket();
socket.CanConnect = canConnect;
// [TestCase(true)]
// [TestCase(false)]
// public void ConnectSocket_Should_ReturnConnectionResult(bool canConnect)
// {
// //arrange
// var client = new TestSocketClient();
// var socket = client.CreateSocket();
// socket.CanConnect = canConnect;
//act
var connectResult = client.SubClient.ConnectSocketSub(
new SocketConnection(new TraceLogger(), new TestWebsocketFactory(socket), new WebSocketParameters(new Uri("https://localhost/"), ReconnectPolicy.Disabled), client.SubClient, ""));
// //act
// var connectResult = client.SubClient.ConnectSocketSub(
// new SocketConnection(new TraceLogger(), new TestWebsocketFactory(socket), new WebSocketParameters(new Uri("https://localhost/"), ReconnectPolicy.Disabled), client.SubClient, ""));
//assert
Assert.That(connectResult.Success == canConnect);
}
// //assert
// Assert.That(connectResult.Success == canConnect);
// }
[TestCase]
public void SocketMessages_Should_BeProcessedInDataHandlers()
{
// arrange
var client = new TestSocketClient(options => {
options.ReconnectInterval = TimeSpan.Zero;
});
var socket = client.CreateSocket();
socket.CanConnect = true;
var sub = new SocketConnection(new TraceLogger(), new TestWebsocketFactory(socket), new WebSocketParameters(new Uri("https://localhost/"), ReconnectPolicy.Disabled), client.SubClient, "");
var rstEvent = new ManualResetEvent(false);
Dictionary<string, string> result = null;
// [TestCase]
// public void SocketMessages_Should_BeProcessedInDataHandlers()
// {
// // arrange
// var client = new TestSocketClient(options => {
// options.ReconnectInterval = TimeSpan.Zero;
// });
// var socket = client.CreateSocket();
// socket.CanConnect = true;
// var sub = new SocketConnection(new TraceLogger(), new TestWebsocketFactory(socket), new WebSocketParameters(new Uri("https://localhost/"), ReconnectPolicy.Disabled), client.SubClient, "");
// var rstEvent = new ManualResetEvent(false);
// Dictionary<string, string> result = null;
client.SubClient.ConnectSocketSub(sub);
// client.SubClient.ConnectSocketSub(sub);
var subObj = new TestSubscription<Dictionary<string, string>>(Mock.Of<ILogger>(), (messageEvent) =>
{
result = messageEvent.Data;
rstEvent.Set();
});
sub.AddSubscription(subObj);
// var subObj = new TestSubscription<Dictionary<string, string>>(Mock.Of<ILogger>(), (messageEvent) =>
// {
// result = messageEvent.Data;
// rstEvent.Set();
// });
// sub.AddSubscription(subObj);
// act
socket.InvokeMessage("{\"property\": \"123\", \"action\": \"update\", \"topic\": \"topic\"}");
rstEvent.WaitOne(1000);
// // act
// socket.InvokeMessage("{\"property\": \"123\", \"action\": \"update\", \"topic\": \"topic\"}");
// rstEvent.WaitOne(1000);
// assert
Assert.That(result["property"] == "123");
}
// // assert
// Assert.That(result["property"] == "123");
// }
[TestCase(false)]
[TestCase(true)]
public void SocketMessages_Should_ContainOriginalDataIfEnabled(bool enabled)
{
// arrange
var client = new TestSocketClient(options =>
{
options.ReconnectInterval = TimeSpan.Zero;
options.SubOptions.OutputOriginalData = enabled;
});
var socket = client.CreateSocket();
socket.CanConnect = true;
var sub = new SocketConnection(new TraceLogger(), new TestWebsocketFactory(socket), new WebSocketParameters(new Uri("https://localhost/"), ReconnectPolicy.Disabled), client.SubClient, "");
var rstEvent = new ManualResetEvent(false);
string original = null;
// [TestCase(false)]
// [TestCase(true)]
// public void SocketMessages_Should_ContainOriginalDataIfEnabled(bool enabled)
// {
// // arrange
// var client = new TestSocketClient(options =>
// {
// options.ReconnectInterval = TimeSpan.Zero;
// options.SubOptions.OutputOriginalData = enabled;
// });
// var socket = client.CreateSocket();
// socket.CanConnect = true;
// var sub = new SocketConnection(new TraceLogger(), new TestWebsocketFactory(socket), new WebSocketParameters(new Uri("https://localhost/"), ReconnectPolicy.Disabled), client.SubClient, "");
// var rstEvent = new ManualResetEvent(false);
// string original = null;
client.SubClient.ConnectSocketSub(sub);
var subObj = new TestSubscription<Dictionary<string, string>>(Mock.Of<ILogger>(), (messageEvent) =>
{
original = messageEvent.OriginalData;
rstEvent.Set();
});
sub.AddSubscription(subObj);
var msgToSend = JsonSerializer.Serialize(new { topic = "topic", action = "update", property = "123" });
// client.SubClient.ConnectSocketSub(sub);
// var subObj = new TestSubscription<Dictionary<string, string>>(Mock.Of<ILogger>(), (messageEvent) =>
// {
// original = messageEvent.OriginalData;
// rstEvent.Set();
// });
// sub.AddSubscription(subObj);
// var msgToSend = JsonSerializer.Serialize(new { topic = "topic", action = "update", property = "123" });
// act
socket.InvokeMessage(msgToSend);
rstEvent.WaitOne(1000);
// // act
// socket.InvokeMessage(msgToSend);
// rstEvent.WaitOne(1000);
// assert
Assert.That(original == (enabled ? msgToSend : null));
}
// // assert
// Assert.That(original == (enabled ? msgToSend : null));
// }
[TestCase()]
public void UnsubscribingStream_Should_CloseTheSocket()
{
// arrange
var client = new TestSocketClient(options =>
{
options.ReconnectInterval = TimeSpan.Zero;
});
var socket = client.CreateSocket();
socket.CanConnect = true;
var sub = new SocketConnection(new TraceLogger(), new TestWebsocketFactory(socket), new WebSocketParameters(new Uri("https://localhost/"), ReconnectPolicy.Disabled), client.SubClient, "");
client.SubClient.ConnectSocketSub(sub);
// [TestCase()]
// public void UnsubscribingStream_Should_CloseTheSocket()
// {
// // arrange
// var client = new TestSocketClient(options =>
// {
// options.ReconnectInterval = TimeSpan.Zero;
// });
// var socket = client.CreateSocket();
// socket.CanConnect = true;
// var sub = new SocketConnection(new TraceLogger(), new TestWebsocketFactory(socket), new WebSocketParameters(new Uri("https://localhost/"), ReconnectPolicy.Disabled), client.SubClient, "");
// client.SubClient.ConnectSocketSub(sub);
var subscription = new TestSubscription<Dictionary<string, string>>(Mock.Of<ILogger>(), (messageEvent) => { });
var ups = new UpdateSubscription(sub, subscription);
sub.AddSubscription(subscription);
// var subscription = new TestSubscription<Dictionary<string, string>>(Mock.Of<ILogger>(), (messageEvent) => { });
// var ups = new UpdateSubscription(sub, subscription);
// sub.AddSubscription(subscription);
// act
client.UnsubscribeAsync(ups).Wait();
// // act
// client.UnsubscribeAsync(ups).Wait();
// assert
Assert.That(socket.Connected == false);
}
// // assert
// Assert.That(socket.Connected == false);
// }
[TestCase()]
public void UnsubscribingAll_Should_CloseAllSockets()
{
// arrange
var client = new TestSocketClient(options => { options.ReconnectInterval = TimeSpan.Zero; });
var socket1 = client.CreateSocket();
var socket2 = client.CreateSocket();
socket1.CanConnect = true;
socket2.CanConnect = true;
var sub1 = new SocketConnection(new TraceLogger(), new TestWebsocketFactory(socket1), new WebSocketParameters(new Uri("https://localhost/"), ReconnectPolicy.Disabled), client.SubClient, "");
var sub2 = new SocketConnection(new TraceLogger(), new TestWebsocketFactory(socket2), new WebSocketParameters(new Uri("https://localhost/"), ReconnectPolicy.Disabled), client.SubClient, "");
client.SubClient.ConnectSocketSub(sub1);
client.SubClient.ConnectSocketSub(sub2);
var subscription1 = new TestSubscription<Dictionary<string, string>>(Mock.Of<ILogger>(), (messageEvent) => { });
var subscription2 = new TestSubscription<Dictionary<string, string>>(Mock.Of<ILogger>(), (messageEvent) => { });
// [TestCase()]
// public void UnsubscribingAll_Should_CloseAllSockets()
// {
// // arrange
// var client = new TestSocketClient(options => { options.ReconnectInterval = TimeSpan.Zero; });
// var socket1 = client.CreateSocket();
// var socket2 = client.CreateSocket();
// socket1.CanConnect = true;
// socket2.CanConnect = true;
// var sub1 = new SocketConnection(new TraceLogger(), new TestWebsocketFactory(socket1), new WebSocketParameters(new Uri("https://localhost/"), ReconnectPolicy.Disabled), client.SubClient, "");
// var sub2 = new SocketConnection(new TraceLogger(), new TestWebsocketFactory(socket2), new WebSocketParameters(new Uri("https://localhost/"), ReconnectPolicy.Disabled), client.SubClient, "");
// client.SubClient.ConnectSocketSub(sub1);
// client.SubClient.ConnectSocketSub(sub2);
// var subscription1 = new TestSubscription<Dictionary<string, string>>(Mock.Of<ILogger>(), (messageEvent) => { });
// var subscription2 = new TestSubscription<Dictionary<string, string>>(Mock.Of<ILogger>(), (messageEvent) => { });
sub1.AddSubscription(subscription1);
sub2.AddSubscription(subscription2);
var ups1 = new UpdateSubscription(sub1, subscription1);
var ups2 = new UpdateSubscription(sub2, subscription2);
// sub1.AddSubscription(subscription1);
// sub2.AddSubscription(subscription2);
// var ups1 = new UpdateSubscription(sub1, subscription1);
// var ups2 = new UpdateSubscription(sub2, subscription2);
// act
client.UnsubscribeAllAsync().Wait();
// // act
// client.UnsubscribeAllAsync().Wait();
// assert
Assert.That(socket1.Connected == false);
Assert.That(socket2.Connected == false);
}
// // assert
// Assert.That(socket1.Connected == false);
// Assert.That(socket2.Connected == false);
// }
[TestCase()]
public void FailingToConnectSocket_Should_ReturnError()
{
// arrange
var client = new TestSocketClient(options => { options.ReconnectInterval = TimeSpan.Zero; });
var socket = client.CreateSocket();
socket.CanConnect = false;
var sub1 = new SocketConnection(new TraceLogger(), new TestWebsocketFactory(socket), new WebSocketParameters(new Uri("https://localhost/"), ReconnectPolicy.Disabled), client.SubClient, "");
// [TestCase()]
// public void FailingToConnectSocket_Should_ReturnError()
// {
// // arrange
// var client = new TestSocketClient(options => { options.ReconnectInterval = TimeSpan.Zero; });
// var socket = client.CreateSocket();
// socket.CanConnect = false;
// var sub1 = new SocketConnection(new TraceLogger(), new TestWebsocketFactory(socket), new WebSocketParameters(new Uri("https://localhost/"), ReconnectPolicy.Disabled), client.SubClient, "");
// act
var connectResult = client.SubClient.ConnectSocketSub(sub1);
// // act
// var connectResult = client.SubClient.ConnectSocketSub(sub1);
// assert
ClassicAssert.IsFalse(connectResult.Success);
}
// // assert
// ClassicAssert.IsFalse(connectResult.Success);
// }
[TestCase()]
public async Task ErrorResponse_ShouldNot_ConfirmSubscription()
{
// arrange
var channel = "trade_btcusd";
var client = new TestSocketClient(opt =>
{
opt.OutputOriginalData = true;
opt.SocketSubscriptionsCombineTarget = 1;
});
var socket = client.CreateSocket();
socket.CanConnect = true;
client.SubClient.ConnectSocketSub(new SocketConnection(new TraceLogger(), new TestWebsocketFactory(socket), new WebSocketParameters(new Uri("https://localhost/"), ReconnectPolicy.Disabled), client.SubClient, ""));
// [TestCase()]
// public async Task ErrorResponse_ShouldNot_ConfirmSubscription()
// {
// // arrange
// var channel = "trade_btcusd";
// var client = new TestSocketClient(opt =>
// {
// opt.OutputOriginalData = true;
// opt.SocketSubscriptionsCombineTarget = 1;
// });
// var socket = client.CreateSocket();
// socket.CanConnect = true;
// client.SubClient.ConnectSocketSub(new SocketConnection(new TraceLogger(), new TestWebsocketFactory(socket), new WebSocketParameters(new Uri("https://localhost/"), ReconnectPolicy.Disabled), client.SubClient, ""));
// act
var sub = client.SubClient.SubscribeToSomethingAsync(channel, onUpdate => {}, ct: default);
socket.InvokeMessage(JsonSerializer.Serialize(new { channel, action = "subscribe", status = "error" }));
await sub;
// // act
// var sub = client.SubClient.SubscribeToSomethingAsync(channel, onUpdate => {}, ct: default);
// socket.InvokeMessage(JsonSerializer.Serialize(new { channel, action = "subscribe", status = "error" }));
// await sub;
// assert
ClassicAssert.IsTrue(client.SubClient.TestSubscription.Status != SubscriptionStatus.Subscribed);
}
// // assert
// ClassicAssert.IsTrue(client.SubClient.TestSubscription.Status != SubscriptionStatus.Subscribed);
// }
[TestCase()]
public async Task SuccessResponse_Should_ConfirmSubscription()
{
// arrange
var channel = "trade_btcusd";
var client = new TestSocketClient(opt =>
{
opt.OutputOriginalData = true;
opt.SocketSubscriptionsCombineTarget = 1;
});
var socket = client.CreateSocket();
socket.CanConnect = true;
client.SubClient.ConnectSocketSub(new SocketConnection(new TraceLogger(), new TestWebsocketFactory(socket), new WebSocketParameters(new Uri("https://localhost/"), ReconnectPolicy.Disabled), client.SubClient, ""));
// [TestCase()]
// public async Task SuccessResponse_Should_ConfirmSubscription()
// {
// // arrange
// var channel = "trade_btcusd";
// var client = new TestSocketClient(opt =>
// {
// opt.OutputOriginalData = true;
// opt.SocketSubscriptionsCombineTarget = 1;
// });
// var socket = client.CreateSocket();
// socket.CanConnect = true;
// client.SubClient.ConnectSocketSub(new SocketConnection(new TraceLogger(), new TestWebsocketFactory(socket), new WebSocketParameters(new Uri("https://localhost/"), ReconnectPolicy.Disabled), client.SubClient, ""));
// act
var sub = client.SubClient.SubscribeToSomethingAsync(channel, onUpdate => {}, ct: default);
socket.InvokeMessage(JsonSerializer.Serialize(new { channel, action = "subscribe", status = "confirmed" }));
await sub;
// // act
// var sub = client.SubClient.SubscribeToSomethingAsync(channel, onUpdate => {}, ct: default);
// socket.InvokeMessage(JsonSerializer.Serialize(new { channel, action = "subscribe", status = "confirmed" }));
// await sub;
// assert
Assert.That(client.SubClient.TestSubscription.Status == SubscriptionStatus.Subscribed);
}
}
}
// // assert
// Assert.That(client.SubClient.TestSubscription.Status == SubscriptionStatus.Subscribed);
// }
// }
//}

View File

@ -1,56 +1,59 @@
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 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;
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;
namespace CryptoExchange.Net.UnitTests.TestImplementations
{
internal class TestSocketClient: BaseSocketClient
{
public TestSubSocketClient SubClient { get; }
/// <summary>
/// Create a new instance of KucoinSocketClient
/// </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/");
}
}
public class TestEnvironment : TradeEnvironment
{
@ -62,82 +65,84 @@ namespace CryptoExchange.Net.UnitTests.TestImplementations
}
}
public class TestSocketOptions: SocketExchangeOptions<TestEnvironment>
{
public static TestSocketOptions Default = new TestSocketOptions
{
Environment = new TestEnvironment("Live", "https://test.test")
};
// 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);
}
// /// <summary>
// /// ctor
// /// </summary>
// public TestSocketOptions()
// {
// Default?.Set(this);
// }
public SocketApiOptions SubOptions { get; set; } = new SocketApiOptions();
// 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;
}
}
// 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 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 Subscription TestSubscription { get; private set; } = null;
public override JsonSerializerOptions JsonSerializerOptions => new JsonSerializerOptions();
// public override JsonSerializerOptions JsonSerializerOptions => new JsonSerializerOptions();
public TestSubSocketClient(TestSocketOptions options, SocketApiOptions apiOptions) : base(new TraceLogger(), options.Environment.TestAddress, options, apiOptions)
{
// 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());
// 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()}";
// /// <inheritdoc />
// public override string FormatSymbol(string baseAsset, string quoteAsset, TradingMode futuresType, DateTime? deliverDate = null) => $"{baseAsset.ToUpperInvariant()}{quoteAsset.ToUpperInvariant()}";
internal IWebsocket CreateSocketInternal(string address)
{
return CreateSocket(address);
}
// internal IWebsocket CreateSocketInternal(string address)
// {
// return SocketFactory.CreateWebsocket(_logger, );
// }
protected override AuthenticationProvider CreateAuthenticationProvider(ApiCredentials credentials)
=> new TestAuthProvider(credentials);
// protected override AuthenticationProvider CreateAuthenticationProvider(ApiCredentials credentials)
// => new TestAuthProvider(credentials);
public CallResult ConnectSocketSub(SocketConnection sub)
{
return ConnectSocketAsync(sub, default).Result;
}
// public CallResult ConnectSocketSub(SocketConnection sub)
// {
// return ConnectSocketAsync(sub, default).Result;
// }
public override string GetListenerIdentifier(IMessageAccessor message)
{
if (!message.IsValid)
{
return "topic";
}
// public override string GetListenerIdentifier(IMessageAccessor message)
// {
// if (!message.IsValid)
// {
// return "topic";
// }
var id = message.GetValue<string>(_channelPath);
id ??= message.GetValue<string>(_topicPath);
// var id = message.GetValue<string>(_channelPath);
// id ??= message.GetValue<string>(_topicPath);
return message.GetValue<string>(_actionPath) + "-" + id;
}
// 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 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

@ -1,3 +1,4 @@
using CryptoExchange.Net.Converters.MessageParsing.DynamicConverters;
using CryptoExchange.Net.Interfaces;
using CryptoExchange.Net.Logging.Extensions;
using CryptoExchange.Net.Objects;
@ -813,19 +814,20 @@ namespace CryptoExchange.Net.Clients
Proxy = ClientOptions.Proxy,
Timeout = ApiOptions.SocketNoDataTimeout ?? ClientOptions.SocketNoDataTimeout,
ReceiveBufferSize = ClientOptions.ReceiveBufferSize,
UseNewMessageDeserialization = ClientOptions.EnabledNewDeserialization
};
/// <summary>
/// Create a socket for an address
/// </summary>
/// <param name="address">The address the socket should connect to</param>
/// <returns></returns>
protected internal virtual IWebsocket CreateSocket(string address)
{
var socket = SocketFactory.CreateWebsocket(_logger, GetWebSocketParameters(address));
_logger.SocketCreatedForAddress(socket.Id, address);
return socket;
}
///// <summary>
///// Create a socket for an address
///// </summary>
///// <param name="address">The address the socket should connect to</param>
///// <returns></returns>
//protected internal virtual IWebsocket CreateSocket(string address)
//{
// var socket = SocketFactory.CreateWebsocket(_logger, GetWebSocketParameters(address));
// _logger.SocketCreatedForAddress(socket.Id, address);
// return socket;
//}
/// <summary>
/// Unsubscribe an update subscription
@ -1059,5 +1061,7 @@ namespace CryptoExchange.Net.Clients
/// <param name="data"></param>
/// <returns></returns>
public virtual ReadOnlyMemory<byte> PreprocessStreamMessage(SocketConnection connection, WebSocketMessageType type, ReadOnlyMemory<byte> data) => data;
public abstract IMessageConverter CreateMessageConverter();
}
}

View File

@ -0,0 +1,55 @@
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Net.WebSockets;
using System.Text;
using System.Text.Json;
namespace CryptoExchange.Net.Converters.MessageParsing.DynamicConverters
{
public ref struct MessageType
{
public Type Type { get; set; }
public string? Identifier { get; set; }
}
public interface IMessageConverter
{
MessageType GetMessageType(ReadOnlySpan<byte> data, WebSocketMessageType? webSocketMessageType);
object Deserialize(ReadOnlySpan<byte> data, Type type);
}
public abstract class DynamicConverter : IMessageConverter
{
public abstract JsonSerializerOptions Options { get; }
public abstract MessageType GetMessageType(ReadOnlySpan<byte> data, WebSocketMessageType? webSocketMessageType);
public virtual object Deserialize(ReadOnlySpan<byte> data, Type type)
{
return JsonSerializer.Deserialize(data, type, Options);
}
}
public abstract class StaticConverter : IMessageConverter
{
public abstract JsonSerializerOptions Options { get; }
public abstract MessageType GetMessageType(ReadOnlySpan<byte> data, WebSocketMessageType? webSocketMessageType);
public object? Deserialize(ReadOnlySpan<byte> data, Type type)
{
return JsonSerializer.Deserialize(data, type, Options);
}
}
public abstract class StaticConverter<T> : StaticConverter
{
public override MessageType GetMessageType(ReadOnlySpan<byte> data,, WebSocketMessageType? webSocketMessageType) =>
new MessageType { Type = typeof(T), Identifier = GetMessageListenId(data, webSocketMessageType) };
public abstract string GetMessageListenId(ReadOnlySpan<byte> data, WebSocketMessageType? webSocketMessageType);
}
}

View File

@ -21,15 +21,35 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
/// <inheritdoc />
public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
{
return typeToConvert == typeof(bool) ? new BoolConverterInner<bool>() : new BoolConverterInner<bool?>();
return typeToConvert == typeof(bool) ? new BoolConverterInner() : new BoolConverterInnerNullable();
}
private class BoolConverterInner<T> : JsonConverter<T>
private class BoolConverterInnerNullable : JsonConverter<bool?>
{
public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> (T)((object?)ReadBool(ref reader, typeToConvert, options) ?? default(T))!;
public override bool? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> ReadBool(ref reader, typeToConvert, options);
public bool? ReadBool(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
public override void Write(Utf8JsonWriter writer, bool? value, JsonSerializerOptions options)
{
if (value is bool boolVal)
writer.WriteBooleanValue(boolVal);
else
writer.WriteNullValue();
}
}
private class BoolConverterInner : JsonConverter<bool>
{
public override bool Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> ReadBool(ref reader, typeToConvert, options) ?? false;
public override void Write(Utf8JsonWriter writer, bool value, JsonSerializerOptions options)
{
writer.WriteBooleanValue(value);
}
}
private static bool? ReadBool(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.True)
return true;
@ -48,7 +68,7 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
if (string.IsNullOrEmpty(value))
{
if (typeToConvert == typeof(bool))
LibraryHelpers.StaticLogger?.LogWarning("Received null bool value, but property type is not a nullable bool. Resolver: {Resolver}", options.TypeInfoResolver?.GetType()?.Name);
LibraryHelpers.StaticLogger?.LogWarning("Received null or empty bool value, but property type is not a nullable bool. Resolver: {Resolver}", options.TypeInfoResolver?.GetType()?.Name);
return default;
}
@ -72,14 +92,5 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
throw new SerializationException($"Can't convert bool value {value}");
}
public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
{
if (value is bool boolVal)
writer.WriteBooleanValue(boolVal);
else
writer.WriteNullValue();
}
}
}
}

View File

@ -27,15 +27,45 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
/// <inheritdoc />
public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
{
return typeToConvert == typeof(DateTime) ? new DateTimeConverterInner<DateTime>() : new DateTimeConverterInner<DateTime?>();
return typeToConvert == typeof(DateTime) ? new DateTimeConverterInner() : new NullableDateTimeConverterInner();
}
private class DateTimeConverterInner<T> : JsonConverter<T>
private class NullableDateTimeConverterInner : JsonConverter<DateTime?>
{
public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> (T)((object?)ReadDateTime(ref reader, typeToConvert, options) ?? default(T))!;
public override DateTime? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> ReadDateTime(ref reader, typeToConvert, options);
private DateTime? ReadDateTime(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
public override void Write(Utf8JsonWriter writer, DateTime? value, JsonSerializerOptions options)
{
if (value == null)
{
writer.WriteNullValue();
return;
}
if (value.Value == default)
writer.WriteStringValue(default(DateTime));
else
writer.WriteNumberValue((long)Math.Round((value.Value - new DateTime(1970, 1, 1)).TotalMilliseconds));
}
}
private class DateTimeConverterInner : JsonConverter<DateTime>
{
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> ReadDateTime(ref reader, typeToConvert, options) ?? default;
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
{
var dtValue = (DateTime)(object)value;
if (dtValue == default)
writer.WriteStringValue(default(DateTime));
else
writer.WriteNumberValue((long)Math.Round((dtValue - new DateTime(1970, 1, 1)).TotalMilliseconds));
}
}
private static DateTime? ReadDateTime(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.Null)
{
@ -71,23 +101,6 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
}
}
public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
{
if (value == null)
{
writer.WriteNullValue();
}
else
{
var dtValue = (DateTime)(object)value;
if (dtValue == default)
writer.WriteStringValue(default(DateTime));
else
writer.WriteNumberValue((long)Math.Round((dtValue - new DateTime(1970, 1, 1)).TotalMilliseconds));
}
}
}
/// <summary>
/// Parse a double value to datetime
/// </summary>

View File

@ -20,6 +20,8 @@ namespace CryptoExchange.Net.Interfaces
/// The matcher for this listener
/// </summary>
public MessageMatcher MessageMatcher { get; }
public HashSet<Type> DeserializationTypes { get; set; }
/// <summary>
/// Handle a message
/// </summary>

View File

@ -1,4 +1,5 @@
using CryptoExchange.Net.Objects.Sockets;
using CryptoExchange.Net.Sockets;
using Microsoft.Extensions.Logging;
using System.IO.Pipelines;
@ -15,7 +16,7 @@ namespace CryptoExchange.Net.Interfaces
/// <param name="logger">The logger</param>
/// <param name="parameters">The parameters to use for the connection</param>
/// <returns></returns>
IWebsocket CreateWebsocket(ILogger logger, WebSocketParameters parameters);
IWebsocket CreateWebsocket(ILogger logger, SocketConnection connection, WebSocketParameters parameters);
/// <summary>
/// Create high performance websocket

View File

@ -61,6 +61,8 @@ namespace CryptoExchange.Net.Objects.Options
/// </remarks>
public int? ReceiveBufferSize { get; set; }
public bool EnabledNewDeserialization { get; set; }
/// <summary>
/// Create a copy of this options
/// </summary>
@ -82,6 +84,7 @@ namespace CryptoExchange.Net.Objects.Options
item.RateLimitingBehaviour = RateLimitingBehaviour;
item.RateLimiterEnabled = RateLimiterEnabled;
item.ReceiveBufferSize = ReceiveBufferSize;
item.EnabledNewDeserialization = EnabledNewDeserialization;
return item;
}
}

View File

@ -75,6 +75,8 @@ namespace CryptoExchange.Net.Objects.Sockets
/// </summary>
public int? ReceiveBufferSize { get; set; } = null;
public bool UseNewMessageDeserialization { get; set; }
/// <summary>
/// ctor
/// </summary>

View File

@ -9,6 +9,7 @@ using System;
using System.Buffers;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data.Common;
using System.IO;
using System.Linq;
using System.Net;
@ -145,21 +146,27 @@ namespace CryptoExchange.Net.Sockets
/// <inheritdoc />
public Func<Task<Uri?>>? GetReconnectionUrl { get; set; }
private SocketConnection _connection;
/// <summary>
/// ctor
/// </summary>
/// <param name="logger">The log object to use</param>
/// <param name="websocketParameters">The parameters for this socket</param>
public CryptoExchangeWebSocketClient(ILogger logger, WebSocketParameters websocketParameters)
public CryptoExchangeWebSocketClient(ILogger logger, SocketConnection connection, WebSocketParameters websocketParameters)
{
Id = NextStreamId();
_logger = logger;
_connection = connection;
Parameters = websocketParameters;
_receivedMessages = new List<ReceiveItem>();
_sendEvent = new AsyncResetEvent();
_sendBuffer = new ConcurrentQueue<SendItem>();
_ctsSource = new CancellationTokenSource();
if (websocketParameters.UseNewMessageDeserialization)
_receiveBufferSize = 1024;
else
_receiveBufferSize = websocketParameters.ReceiveBufferSize ?? _defaultReceiveBufferSize;
_closeSem = new SemaphoreSlim(1, 1);
@ -682,7 +689,10 @@ namespace CryptoExchange.Net.Sockets
{
// Received a complete message and it's not multi part
_logger.SocketReceivedSingleMessage(Id, receiveResult.Count);
if (!Parameters.UseNewMessageDeserialization)
await ProcessData(receiveResult.MessageType, new ReadOnlyMemory<byte>(buffer.Array!, buffer.Offset, receiveResult.Count)).ConfigureAwait(false);
else
ProcessDataNew(receiveResult.MessageType, new ReadOnlySpan<byte>(buffer.Array!, buffer.Offset, receiveResult.Count));
}
else
{
@ -717,7 +727,11 @@ namespace CryptoExchange.Net.Sockets
{
_logger.SocketReassembledMessage(Id, multipartStream!.Length);
// Get the underlying buffer of the memory stream holding the written data and delimit it (GetBuffer return the full array, not only the written part)
if (!Parameters.UseNewMessageDeserialization)
await ProcessData(receiveResult.MessageType, new ReadOnlyMemory<byte>(multipartStream.GetBuffer(), 0, (int)multipartStream.Length)).ConfigureAwait(false);
else
ProcessDataNew(receiveResult.MessageType, new ReadOnlySpan<byte>(multipartStream.GetBuffer(), 0, (int)multipartStream.Length));
}
else
{
@ -743,6 +757,19 @@ namespace CryptoExchange.Net.Sockets
}
}
/// <summary>
/// Process a stream message
/// </summary>
/// <param name="type"></param>
/// <param name="data"></param>
/// <returns></returns>
protected void ProcessDataNew(WebSocketMessageType type, ReadOnlySpan<byte> data)
{
LastActionTime = DateTime.UtcNow;
_connection.HandleStreamMessage2(type, data);
//await (OnStreamMessage?.Invoke(type, data) ?? Task.CompletedTask).ConfigureAwait(false);
}
/// <summary>
/// Process a stream message
/// </summary>

View File

@ -484,7 +484,21 @@ namespace CryptoExchange.Net.Sockets
#pragma warning restore IL3050 // Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.
#pragma warning restore IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code
{
var tasks = _typedSubscriptions.Select(sub =>
if (_typedSubscriptions.Count == 1)
{
// If there is only one listener we can prevent the overhead of the await which will call a `ToList`
await DelegateToSubscription(_typedSubscriptions[0], update!).ConfigureAwait(false);
continue;
}
var tasks = _typedSubscriptions.Select(sub => DelegateToSubscription(sub, update!));
await LibraryHelpers.WhenAll(tasks).ConfigureAwait(false);
}
}
catch (OperationCanceledException) { }
}
private ValueTask DelegateToSubscription(HighPerfSubscription<T> sub, T update)
{
try
{
@ -496,11 +510,6 @@ namespace CryptoExchange.Net.Sockets
_logger.UserMessageProcessingFailed(SocketId, ex.Message, ex);
return new ValueTask();
}
});
await LibraryHelpers.WhenAll(tasks).ConfigureAwait(false);
}
}
catch (OperationCanceledException) { }
}
}
}

View File

@ -90,7 +90,7 @@ namespace CryptoExchange.Net.Sockets
/// <summary>
/// Get any handler links matching with the listen id
/// </summary>
public List<MessageHandlerLink> GetHandlerLinks(string listenId) => HandlerLinks.Where(x => x.Check(listenId)).ToList();
public IEnumerable<MessageHandlerLink> GetHandlerLinks(string listenId) => HandlerLinks.Where(x => x.Check(listenId));
/// <inheritdoc />
public override string ToString() => string.Join(",", HandlerLinks.Select(x => x.ToString()));
@ -113,6 +113,7 @@ namespace CryptoExchange.Net.Sockets
/// Deserialization type
/// </summary>
public abstract Type GetDeserializationType(IMessageAccessor accessor);
public abstract Type DeserializationType { get; }
/// <summary>
/// ctor
@ -150,6 +151,8 @@ namespace CryptoExchange.Net.Sockets
{
private Func<SocketConnection, DataEvent<TServer>, CallResult> _handler;
public override Type DeserializationType => typeof(TServer);
/// <inheritdoc />
public override Type GetDeserializationType(IMessageAccessor accessor) => typeof(TServer);

View File

@ -65,10 +65,21 @@ namespace CryptoExchange.Net.Sockets
/// </summary>
public AsyncResetEvent? ContinueAwaiter { get; set; }
public HashSet<Type> DeserializationTypes { get; set; }
private MessageMatcher _matcher;
/// <summary>
/// Matcher for this query
/// Matcher for this subscription
/// </summary>
public MessageMatcher MessageMatcher { get; set; } = null!;
public MessageMatcher MessageMatcher
{
get => _matcher;
set
{
_matcher = value;
DeserializationTypes = new HashSet<Type>(MessageMatcher.HandlerLinks.Select(x => x.DeserializationType));
}
}
/// <summary>
/// The query request object

View File

@ -1,4 +1,5 @@
using CryptoExchange.Net.Clients;
using CryptoExchange.Net.Converters.MessageParsing.DynamicConverters;
using CryptoExchange.Net.Interfaces;
using CryptoExchange.Net.Logging.Extensions;
using CryptoExchange.Net.Objects;
@ -11,6 +12,7 @@ using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Net.WebSockets;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
@ -266,6 +268,8 @@ namespace CryptoExchange.Net.Sockets
private IByteMessageAccessor? _stringMessageAccessor;
private IByteMessageAccessor? _byteMessageAccessor;
private IMessageConverter? _messageConverter;
/// <summary>
/// The task that is sending periodic data on the websocket. Can be used for sending Ping messages every x seconds or similar. Not necessary.
/// </summary>
@ -296,7 +300,7 @@ namespace CryptoExchange.Net.Sockets
Tag = tag;
Properties = new Dictionary<string, object>();
_socket = socketFactory.CreateWebsocket(logger, parameters);
_socket = socketFactory.CreateWebsocket(logger, this, parameters);
_logger.SocketCreatedForAddress(_socket.Id, parameters.Uri.ToString());
_socket.OnStreamMessage += HandleStreamMessage;
@ -500,6 +504,52 @@ namespace CryptoExchange.Net.Sockets
return Task.CompletedTask;
}
/// <summary>
/// Handle a message
/// </summary>
protected internal virtual void HandleStreamMessage2(WebSocketMessageType type, ReadOnlySpan<byte> data)
{
//var sw = Stopwatch.StartNew();
var receiveTime = DateTime.UtcNow;
//// 1. Decrypt/Preprocess if necessary
//data = ApiClient.PreprocessStreamMessage(this, type, data);
_messageConverter ??= ApiClient.CreateMessageConverter();
var messageType = _messageConverter.GetMessageType(data, type);
if (messageType.Type == null)
{
// Failed to determine message type
return;
}
var result = _messageConverter.Deserialize(data, messageType.Type);
if (result == null)
{
// Deserialize error
return;
}
var targetType = messageType.Type;
List<IMessageProcessor> listeners;
lock (_listenersLock)
listeners = _listeners.Where(x => x.DeserializationTypes.Contains(targetType)).ToList();
if (listeners.Count == 0)
{
// No subscriptions found for type
return;
}
var dataEvent = new DataEvent<object>(result, null, null, null /*originalData*/, receiveTime, null);
foreach (var subscription in listeners)
{
var links = subscription.MessageMatcher.GetHandlerLinks(messageType.Identifier);
foreach(var link in links)
subscription.Handle(this, dataEvent, link);
}
}
/// <summary>
/// Handle a message
/// </summary>

View File

@ -35,6 +35,8 @@ namespace CryptoExchange.Net.Sockets
/// </summary>
public bool UserSubscription { get; set; }
public HashSet<Type> DeserializationTypes { get; set; }
private SubscriptionStatus _status;
/// <summary>
/// Current subscription status
@ -72,10 +74,20 @@ namespace CryptoExchange.Net.Sockets
/// </summary>
public bool Authenticated { get; }
private MessageMatcher _matcher;
/// <summary>
/// Matcher for this subscription
/// </summary>
public MessageMatcher MessageMatcher { get; set; } = null!;
public MessageMatcher MessageMatcher
{
get => _matcher;
set
{
_matcher = value;
DeserializationTypes = new HashSet<Type>(MessageMatcher.HandlerLinks.Select(x => x.DeserializationType));
}
}
/// <summary>
/// Cancellation token registration
@ -109,7 +121,10 @@ namespace CryptoExchange.Net.Sockets
/// <summary>
/// ctor
/// </summary>
public Subscription(ILogger logger, bool authenticated, bool userSubscription = true)
public Subscription(
ILogger logger,
bool authenticated,
bool userSubscription = true)
{
_logger = logger;
Authenticated = authenticated;

View File

@ -13,9 +13,9 @@ namespace CryptoExchange.Net.Sockets
public class WebsocketFactory : IWebsocketFactory
{
/// <inheritdoc />
public IWebsocket CreateWebsocket(ILogger logger, WebSocketParameters parameters)
public IWebsocket CreateWebsocket(ILogger logger, SocketConnection connection, WebSocketParameters parameters)
{
return new CryptoExchangeWebSocketClient(logger, parameters);
return new CryptoExchangeWebSocketClient(logger, connection, parameters);
}
/// <inheritdoc />
public IHighPerfWebsocket CreateHighPerfWebsocket(ILogger logger, WebSocketParameters parameters, PipeWriter pipeWriter)

View File

@ -1,5 +1,6 @@
using CryptoExchange.Net.Interfaces;
using CryptoExchange.Net.Objects.Sockets;
using CryptoExchange.Net.Sockets;
using Microsoft.Extensions.Logging;
using System;
using System.IO.Pipelines;
@ -16,6 +17,6 @@ namespace CryptoExchange.Net.Testing.Implementations
}
public IHighPerfWebsocket CreateHighPerfWebsocket(ILogger logger, WebSocketParameters parameters, PipeWriter pipeWriter) => throw new NotImplementedException();
public IWebsocket CreateWebsocket(ILogger logger, WebSocketParameters parameters) => _socket;
public IWebsocket CreateWebsocket(ILogger logger, SocketConnection connection, WebSocketParameters parameters) => _socket;
}
}