mirror of
https://github.com/JKorf/CryptoExchange.Net
synced 2025-06-11 01:46:12 +00:00
wip
This commit is contained in:
parent
cf941fe5c9
commit
5539320827
@ -8,12 +8,27 @@ using System.Text;
|
|||||||
|
|
||||||
namespace CryptoExchange.Net.Converters
|
namespace CryptoExchange.Net.Converters
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Socket message converter
|
||||||
|
/// </summary>
|
||||||
public abstract class SocketConverter
|
public abstract class SocketConverter
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Fields to use for the message subscription identifier
|
||||||
|
/// </summary>
|
||||||
public virtual string[]? SubscriptionIdFields => null;
|
public virtual string[]? SubscriptionIdFields => null;
|
||||||
|
/// <summary>
|
||||||
|
/// Fields to use for the message type identifier
|
||||||
|
/// </summary>
|
||||||
public abstract string[] TypeIdFields { get; }
|
public abstract string[] TypeIdFields { get; }
|
||||||
|
|
||||||
public abstract Type? GetDeserializationType(Dictionary<string, string> idValues, List<MessageListener> listeners);
|
/// <summary>
|
||||||
|
/// Return the type of object that the message should be parsed to based on the type id values dictionary
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="idValues"></param>
|
||||||
|
/// <param name="listeners"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public abstract Type? GetDeserializationType(Dictionary<string, string?> idValues, List<MessageListener> listeners);
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public ParsedMessage? ReadJson(Stream stream, List<MessageListener> listeners, bool outputOriginalData)
|
public ParsedMessage? ReadJson(Stream stream, List<MessageListener> listeners, bool outputOriginalData)
|
||||||
@ -37,8 +52,9 @@ namespace CryptoExchange.Net.Converters
|
|||||||
{
|
{
|
||||||
token = JToken.Load(jsonTextReader);
|
token = JToken.Load(jsonTextReader);
|
||||||
}
|
}
|
||||||
catch(Exception ex)
|
catch(Exception)
|
||||||
{
|
{
|
||||||
|
// Not a json message
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,62 +64,47 @@ namespace CryptoExchange.Net.Converters
|
|||||||
token = token.First!;
|
token = token.First!;
|
||||||
}
|
}
|
||||||
|
|
||||||
var typeIdDict = new Dictionary<string, string>();
|
var typeIdDict = new Dictionary<string, string?>();
|
||||||
foreach(var idField in TypeIdFields)
|
string idString = "";
|
||||||
|
foreach (var idField in TypeIdFields)
|
||||||
{
|
{
|
||||||
var splitTokens = idField.Split(new char[] { ':' });
|
var val = GetValueForKey(token, idField);
|
||||||
var accessToken = token;
|
idString += val;
|
||||||
foreach (var splitToken in splitTokens)
|
typeIdDict[idField] = val;
|
||||||
{
|
|
||||||
accessToken = accessToken[splitToken];
|
|
||||||
|
|
||||||
if (accessToken == null)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (accessToken.Type == JTokenType.Array)
|
|
||||||
{
|
|
||||||
// Received array, take first item as reference
|
|
||||||
accessToken = accessToken.First!;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
typeIdDict[idField] = accessToken?.ToString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
string idString = "";
|
|
||||||
if (SubscriptionIdFields != null)
|
if (SubscriptionIdFields != null)
|
||||||
{
|
{
|
||||||
|
idString = "";
|
||||||
foreach (var idField in SubscriptionIdFields)
|
foreach (var idField in SubscriptionIdFields)
|
||||||
{
|
idString += GetValueForKey(token, idField);
|
||||||
var splitTokens = idField.Split(new char[] { ':' });
|
|
||||||
var accessToken = token;
|
|
||||||
foreach (var splitToken in splitTokens)
|
|
||||||
{
|
|
||||||
accessToken = accessToken[splitToken];
|
|
||||||
if (accessToken == null)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
idString += accessToken?.ToString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
foreach (var item in typeIdDict)
|
|
||||||
idString += item.Value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result.Identifier = idString;
|
result.Identifier = idString;
|
||||||
var resultType = GetDeserializationType(typeIdDict, listeners);
|
var resultType = GetDeserializationType(typeIdDict, listeners);
|
||||||
result.Data = resultType == null ? null : token.ToObject(resultType);
|
result.Data = resultType == null ? null : token.ToObject(resultType);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public class ParsedMessage
|
private string? GetValueForKey(JToken token, string key)
|
||||||
{
|
{
|
||||||
public string Identifier { get; set; } = null!;
|
var splitTokens = key.Split(new char[] { ':' });
|
||||||
public string? OriginalData { get; set; }
|
var accessToken = token;
|
||||||
public object? Data { get; set; }
|
foreach (var splitToken in splitTokens)
|
||||||
|
{
|
||||||
|
accessToken = accessToken[splitToken];
|
||||||
|
|
||||||
|
if (accessToken == null)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (accessToken.Type == JTokenType.Array)
|
||||||
|
{
|
||||||
|
// Received array, take first item as reference
|
||||||
|
accessToken = accessToken.First!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return accessToken?.ToString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
21
CryptoExchange.Net/Objects/Sockets/ParsedMessage.cs
Normal file
21
CryptoExchange.Net/Objects/Sockets/ParsedMessage.cs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
namespace CryptoExchange.Net.Objects.Sockets
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Parsed message object
|
||||||
|
/// </summary>
|
||||||
|
public class ParsedMessage
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Identifier string
|
||||||
|
/// </summary>
|
||||||
|
public string Identifier { get; set; } = null!;
|
||||||
|
/// <summary>
|
||||||
|
/// Original data if the option is enabled
|
||||||
|
/// </summary>
|
||||||
|
public string? OriginalData { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Parsed data object
|
||||||
|
/// </summary>
|
||||||
|
public object? Data { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -322,14 +322,13 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
protected virtual async Task HandleStreamMessage(Stream stream)
|
protected virtual async Task HandleStreamMessage(Stream stream)
|
||||||
{
|
{
|
||||||
var timestamp = DateTime.UtcNow;
|
var timestamp = DateTime.UtcNow;
|
||||||
//var streamMessage = new StreamMessage(this, stream, timestamp);
|
|
||||||
TimeSpan userCodeDuration = TimeSpan.Zero;
|
TimeSpan userCodeDuration = TimeSpan.Zero;
|
||||||
|
|
||||||
List<MessageListener> listeners;
|
List<MessageListener> listeners;
|
||||||
lock (_listenerLock)
|
lock (_listenerLock)
|
||||||
listeners = _messageListeners.OrderByDescending(x => x.Priority).ToList();
|
listeners = _messageListeners.OrderByDescending(x => x.Priority).ToList();
|
||||||
|
|
||||||
var result = ApiClient.StreamConverter.ReadJson(stream, listeners.OfType<MessageListener>().ToList(), ApiClient.ApiOptions.OutputOriginalData ?? ApiClient.ClientOptions.OutputOriginalData); // TODO
|
var result = ApiClient.StreamConverter.ReadJson(stream, listeners, ApiClient.ApiOptions.OutputOriginalData ?? ApiClient.ClientOptions.OutputOriginalData); // TODO
|
||||||
if(result == null)
|
if(result == null)
|
||||||
{
|
{
|
||||||
stream.Position = 0;
|
stream.Position = 0;
|
||||||
@ -358,78 +357,42 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var pendingRequest in _pendingRequests)
|
List<PendingRequest> pendingRequests;
|
||||||
|
lock (_pendingRequests)
|
||||||
|
pendingRequests = _pendingRequests.ToList();
|
||||||
|
|
||||||
|
foreach (var pendingRequest in pendingRequests)
|
||||||
{
|
{
|
||||||
if (pendingRequest.MessageMatchesHandler(result))
|
if (pendingRequest.MessageMatchesHandler(result))
|
||||||
{
|
{
|
||||||
await pendingRequest.ProcessAsync(result).ConfigureAwait(false);
|
lock (_pendingRequests)
|
||||||
|
_pendingRequests.Remove(pendingRequest);
|
||||||
|
|
||||||
|
if (pendingRequest.Completed)
|
||||||
|
{
|
||||||
|
// Answer to a timed out request, unsub if it is a subscription request
|
||||||
|
if (pendingRequest.MessageListener != null)
|
||||||
|
{
|
||||||
|
_logger.Log(LogLevel.Warning, $"Socket {SocketId} Received subscription info after request timed out; unsubscribing. Consider increasing the RequestTimeout");
|
||||||
|
_ = UnsubscribeAsync(pendingRequest.MessageListener).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.Log(LogLevel.Trace, $"Socket {SocketId} - msg {pendingRequest.Id} - received data matched to pending request");
|
||||||
|
await pendingRequest.ProcessAsync(result).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.LogWarning("Message not matched"); // TODO
|
stream.Position = 0;
|
||||||
return;
|
var unhandledBuffer = new byte[stream.Length];
|
||||||
|
stream.Read(unhandledBuffer, 0, unhandledBuffer.Length);
|
||||||
//if (_messageIdentifierListeners.TryGetValue(result.Identifier.ToLowerInvariant(), out var idListener))
|
|
||||||
//{
|
_logger.Log(LogLevel.Warning, $"Socket {SocketId} Message not handled: " + Encoding.UTF8.GetString(unhandledBuffer));
|
||||||
// var userSw = Stopwatch.StartNew();
|
UnhandledMessage?.Invoke(result);
|
||||||
// await idListener.ProcessAsync(streamMessage).ConfigureAwait(false);
|
|
||||||
// userSw.Stop();
|
|
||||||
// userCodeDuration = userSw.Elapsed;
|
|
||||||
// handledResponse = true;
|
|
||||||
//}
|
|
||||||
//else
|
|
||||||
//{
|
|
||||||
// foreach (var listener in listeners)
|
|
||||||
// {
|
|
||||||
// if (listener.MessageMatches(streamMessage))
|
|
||||||
// {
|
|
||||||
// if (listener is PendingRequest pendingRequest)
|
|
||||||
// {
|
|
||||||
// lock (_messageListeners)
|
|
||||||
// _messageListeners.Remove(pendingRequest);
|
|
||||||
|
|
||||||
// if (pendingRequest.Completed)
|
|
||||||
// {
|
|
||||||
// // Answer to a timed out request, unsub if it is a subscription request
|
|
||||||
// if (pendingRequest.MessageListener != null)
|
|
||||||
// {
|
|
||||||
// _logger.Log(LogLevel.Warning, $"Socket {SocketId} Received subscription info after request timed out; unsubscribing. Consider increasing the RequestTimeout");
|
|
||||||
// _ = UnsubscribeAsync(pendingRequest.MessageListener).ConfigureAwait(false);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// _logger.Log(LogLevel.Trace, $"Socket {SocketId} - msg {pendingRequest.Id} - received data matched to pending request");
|
|
||||||
// await pendingRequest.ProcessAsync(streamMessage).ConfigureAwait(false);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (!ApiClient.ContinueOnQueryResponse)
|
|
||||||
// return;
|
|
||||||
|
|
||||||
// handledResponse = true;
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// else if (listener is MessageListener subscription)
|
|
||||||
// {
|
|
||||||
// currentSubscription = subscription;
|
|
||||||
// handledResponse = true;
|
|
||||||
// var userSw = Stopwatch.StartNew();
|
|
||||||
// await subscription.ProcessAsync(streamMessage).ConfigureAwait(false);
|
|
||||||
// userSw.Stop();
|
|
||||||
// userCodeDuration = userSw.Elapsed;
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
//if (!handledResponse)
|
|
||||||
//{
|
|
||||||
// if (!ApiClient.UnhandledMessageExpected)
|
|
||||||
// _logger.Log(LogLevel.Warning, $"Socket {SocketId} Message not handled: " + streamMessage.Get(ParsingUtils.GetString));
|
|
||||||
// UnhandledMessage?.Invoke(streamMessage);
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -1,12 +1,7 @@
|
|||||||
using CryptoExchange.Net.Converters;
|
using CryptoExchange.Net.Objects;
|
||||||
using CryptoExchange.Net.Interfaces;
|
|
||||||
using CryptoExchange.Net.Objects;
|
|
||||||
using CryptoExchange.Net.Objects.Sockets;
|
using CryptoExchange.Net.Objects.Sockets;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Newtonsoft.Json;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace CryptoExchange.Net.Sockets
|
namespace CryptoExchange.Net.Sockets
|
||||||
@ -16,8 +11,6 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class Subscription
|
public abstract class Subscription
|
||||||
{
|
{
|
||||||
private bool _outputOriginalData;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Logger
|
/// Logger
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -28,18 +21,19 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool Authenticated { get; }
|
public bool Authenticated { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Strings to identify this subscription with
|
||||||
|
/// </summary>
|
||||||
public abstract List<string> Identifiers { get; }
|
public abstract List<string> Identifiers { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ctor
|
/// ctor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="logger"></param>
|
/// <param name="logger"></param>
|
||||||
/// <param name="apiClient"></param>
|
|
||||||
/// <param name="authenticated"></param>
|
/// <param name="authenticated"></param>
|
||||||
public Subscription(ILogger logger, ISocketApiClient apiClient, bool authenticated)
|
public Subscription(ILogger logger, bool authenticated)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_outputOriginalData = apiClient.ApiOptions.OutputOriginalData ?? apiClient.ClientOptions.OutputOriginalData;
|
|
||||||
Authenticated = authenticated;
|
Authenticated = authenticated;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,52 +61,11 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public abstract (bool, CallResult?) MessageMatchesUnsubRequest(ParsedMessage message);
|
public abstract (bool, CallResult?) MessageMatchesUnsubRequest(ParsedMessage message);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Check if the message is an update for this subscription
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="message"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
//public abstract bool MessageMatchesEvent(ParsedMessage message);
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handle the update message
|
/// Handle the update message
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="message"></param>
|
/// <param name="message"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public abstract Task HandleEventAsync(DataEvent<ParsedMessage> message);
|
public abstract Task HandleEventAsync(DataEvent<ParsedMessage> message);
|
||||||
|
|
||||||
///// <summary>
|
|
||||||
///// Create a data event
|
|
||||||
///// </summary>
|
|
||||||
///// <typeparam name="T"></typeparam>
|
|
||||||
///// <param name="obj"></param>
|
|
||||||
///// <param name="message"></param>
|
|
||||||
///// <param name="topic"></param>
|
|
||||||
///// <param name="type"></param>
|
|
||||||
///// <returns></returns>
|
|
||||||
//protected virtual DataEvent<T> CreateDataEvent<T>(T obj, ParsedMessage message, string? topic = null, SocketUpdateType? type = null)
|
|
||||||
//{
|
|
||||||
// string? originalData = null;
|
|
||||||
// if (_outputOriginalData)
|
|
||||||
// originalData = message.Get(ParsingUtils.GetString);
|
|
||||||
|
|
||||||
// return new DataEvent<T>(obj, topic, originalData, message.Timestamp, type);
|
|
||||||
//}
|
|
||||||
|
|
||||||
///// <summary>
|
|
||||||
///// Deserialize the message to an object using Json.Net
|
|
||||||
///// </summary>
|
|
||||||
///// <typeparam name="T"></typeparam>
|
|
||||||
///// <param name="message"></param>
|
|
||||||
///// <param name="settings"></param>
|
|
||||||
///// <returns></returns>
|
|
||||||
//protected virtual Task<CallResult<T>> DeserializeAsync<T>(StreamMessage message, JsonSerializerSettings settings)
|
|
||||||
//{
|
|
||||||
// var serializer = JsonSerializer.Create(settings);
|
|
||||||
// using var sr = new StreamReader(message.Stream, Encoding.UTF8, false, (int)message.Stream.Length, true);
|
|
||||||
// using var jsonTextReader = new JsonTextReader(sr);
|
|
||||||
// var result = serializer.Deserialize<T>(jsonTextReader);
|
|
||||||
// message.Stream.Position = 0;
|
|
||||||
// return Task.FromResult(new CallResult<T>(result!));
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,9 +16,8 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
/// ctor
|
/// ctor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="logger"></param>
|
/// <param name="logger"></param>
|
||||||
/// <param name="socketApiClient"></param>
|
|
||||||
/// <param name="authenticated"></param>
|
/// <param name="authenticated"></param>
|
||||||
public SystemSubscription(ILogger logger, ISocketApiClient socketApiClient, bool authenticated = false) : base(logger, socketApiClient, authenticated)
|
public SystemSubscription(ILogger logger, bool authenticated = false) : base(logger, authenticated)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user