1
0
mirror of https://github.com/JKorf/CryptoExchange.Net.git synced 2026-08-14 09:52:53 +00:00

Compare commits

..

8 Commits

8 changed files with 37 additions and 23 deletions
@@ -454,9 +454,6 @@ namespace CryptoExchange.Net.Clients
{ {
memoryStream.Position = 0; memoryStream.Position = 0;
originalData = await reader.ReadToEndAsync().ConfigureAwait(false); originalData = await reader.ReadToEndAsync().ConfigureAwait(false);
if (_logger.IsEnabled(LogLevel.Trace))
_logger.RestApiReceivedResponse(request.RequestId, originalData);
} }
// Continue processing from the memory stream since the response stream is already read and we can't seek it // Continue processing from the memory stream since the response stream is already read and we can't seek it
@@ -802,7 +802,6 @@ namespace CryptoExchange.Net.Clients
return new CallResult<HighPerfSocketConnection<TUpdateType>>(socketConnection); return new CallResult<HighPerfSocketConnection<TUpdateType>>(socketConnection);
} }
/// <summary> /// <summary>
/// Process an unhandled message /// Process an unhandled message
/// </summary> /// </summary>
@@ -811,6 +810,14 @@ namespace CryptoExchange.Net.Clients
{ {
} }
/// <summary>
/// Process an unhandled message
/// </summary>
/// <param name="connection">The socket connection</param>
/// <param name="typeIdentifier">The type as identified</param>
/// <param name="data">The data</param>
protected internal virtual bool HandleUnhandledMessage(SocketConnection connection, string typeIdentifier, ReadOnlySpan<byte> data) => false;
/// <summary> /// <summary>
/// Process connect rate limited /// Process connect rate limited
/// </summary> /// </summary>
+3 -3
View File
@@ -6,9 +6,9 @@
<PackageId>CryptoExchange.Net</PackageId> <PackageId>CryptoExchange.Net</PackageId>
<Authors>JKorf</Authors> <Authors>JKorf</Authors>
<Description>CryptoExchange.Net is a base library which is used to implement different cryptocurrency (exchange) API's. It provides a standardized way of implementing different API's, which results in a very similar experience for users of the API implementations.</Description> <Description>CryptoExchange.Net is a base library which is used to implement different cryptocurrency (exchange) API's. It provides a standardized way of implementing different API's, which results in a very similar experience for users of the API implementations.</Description>
<PackageVersion>10.2.0</PackageVersion> <PackageVersion>10.2.3</PackageVersion>
<AssemblyVersion>10.2.0</AssemblyVersion> <AssemblyVersion>10.2.3</AssemblyVersion>
<FileVersion>10.2.0</FileVersion> <FileVersion>10.2.3</FileVersion>
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance> <PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
<PackageTags>OKX;OKX.Net;Mexc;Mexc.Net;Kucoin;Kucoin.Net;Kraken;Kraken.Net;Huobi;Huobi.Net;CoinEx;CoinEx.Net;Bybit;Bybit.Net;Bitget;Bitget.Net;Bitfinex;Bitfinex.Net;Binance;Binance.Net;CryptoCurrency;CryptoCurrency Exchange;CryptoExchange.Net</PackageTags> <PackageTags>OKX;OKX.Net;Mexc;Mexc.Net;Kucoin;Kucoin.Net;Kraken;Kraken.Net;Huobi;Huobi.Net;CoinEx;CoinEx.Net;Bybit;Bybit.Net;Bitget;Bitget.Net;Bitfinex;Bitfinex.Net;Binance;Binance.Net;CryptoCurrency;CryptoCurrency Exchange;CryptoExchange.Net</PackageTags>
<RepositoryType>git</RepositoryType> <RepositoryType>git</RepositoryType>
+4
View File
@@ -86,8 +86,12 @@ namespace CryptoExchange.Net
} }
else if (serializationType == ArrayParametersSerialization.MultipleValues) else if (serializationType == ArrayParametersSerialization.MultipleValues)
{ {
bool firstArrayValue = true;
foreach (var entry in (object[])parameter.Value) foreach (var entry in (object[])parameter.Value)
{ {
if (!firstArrayValue)
uriString.Append('&');
firstArrayValue = false;
uriString.Append(parameter.Key); uriString.Append(parameter.Key);
uriString.Append("="); uriString.Append("=");
if (urlEncodeValues) if (urlEncodeValues)
@@ -22,7 +22,6 @@ namespace CryptoExchange.Net.Logging.Extensions
private static readonly Action<ILogger, string, Exception?> _restApiCacheHit; private static readonly Action<ILogger, string, Exception?> _restApiCacheHit;
private static readonly Action<ILogger, string, Exception?> _restApiCacheNotHit; private static readonly Action<ILogger, string, Exception?> _restApiCacheNotHit;
private static readonly Action<ILogger, int?, Exception?> _restApiCancellationRequested; private static readonly Action<ILogger, int?, Exception?> _restApiCancellationRequested;
private static readonly Action<ILogger, int?, string?, Exception?> _restApiReceivedResponse;
static RestApiClientLoggingExtensions() static RestApiClientLoggingExtensions()
{ {
@@ -91,11 +90,6 @@ namespace CryptoExchange.Net.Logging.Extensions
new EventId(4012, "RestApiCancellationRequested"), new EventId(4012, "RestApiCancellationRequested"),
"[Req {RequestId}] Request cancelled by user"); "[Req {RequestId}] Request cancelled by user");
_restApiReceivedResponse = LoggerMessage.Define<int?, string?>(
LogLevel.Trace,
new EventId(4013, "RestApiReceivedResponse"),
"[Req {RequestId}] Received response: {Data}");
} }
public static void RestApiErrorReceived(this ILogger logger, int? requestId, HttpStatusCode? responseStatusCode, long responseTime, string? error, string? originalData, Exception? exception) public static void RestApiErrorReceived(this ILogger logger, int? requestId, HttpStatusCode? responseStatusCode, long responseTime, string? error, string? originalData, Exception? exception)
@@ -161,10 +155,5 @@ namespace CryptoExchange.Net.Logging.Extensions
{ {
_restApiCancellationRequested(logger, requestId, null); _restApiCancellationRequested(logger, requestId, null);
} }
public static void RestApiReceivedResponse(this ILogger logger, int requestId, string? originalData)
{
_restApiReceivedResponse(logger, requestId, originalData, null);
}
} }
} }
@@ -570,9 +570,13 @@ namespace CryptoExchange.Net.Sockets.Default
} }
if (deserializationType == null) if (deserializationType == null)
{
if (!ApiClient.HandleUnhandledMessage(this, typeIdentifier, data))
{ {
// No handler found for identifier either, can't process // No handler found for identifier either, can't process
_logger.LogWarning("Failed to determine message type for identifier {Identifier}. Data: {Message}", typeIdentifier, Encoding.UTF8.GetString(data.ToArray())); _logger.LogWarning("Failed to determine message type for identifier {Identifier}. Data: {Message}", typeIdentifier, Encoding.UTF8.GetString(data.ToArray()));
}
return; return;
} }
@@ -1291,8 +1295,9 @@ namespace CryptoExchange.Net.Sockets.Default
public void UpdateSequenceNumber(long sequenceNumber) public void UpdateSequenceNumber(long sequenceNumber)
{ {
if (ApiClient.EnforceSequenceNumbers if (ApiClient.EnforceSequenceNumbers
&& _lastSequenceNumber != 0 && _lastSequenceNumber != 0 // Initial value is 0
&& _lastSequenceNumber + 1 != sequenceNumber) && _lastSequenceNumber != sequenceNumber // When there are multiple listeners for the same message it's possible this gets recorded multiple times, shouldn't be an issue
&& _lastSequenceNumber + 1 != sequenceNumber) // Expected value
{ {
// Not sequential // Not sequential
_logger.LogWarning("[Sckt {SocketId}] update not in sequence. Last recorded sequence number: {LastSequence}, update sequence number: {UpdateSequence}. Reconnecting", SocketId, _lastSequenceNumber, sequenceNumber); _logger.LogWarning("[Sckt {SocketId}] update not in sequence. Last recorded sequence number: {LastSequence}, update sequence number: {UpdateSequence}. Reconnecting", SocketId, _lastSequenceNumber, sequenceNumber);
+2 -1
View File
@@ -113,7 +113,8 @@ namespace CryptoExchange.Net
/// <param name="api"></param> /// <param name="api"></param>
internal static void RegisterRestApi(string api) internal static void RegisterRestApi(string api)
{ {
_lastRestDelays[api] = new RestTimeOffset(); if (!_lastRestDelays.ContainsKey(api))
_lastRestDelays.TryAdd(api, new RestTimeOffset());
} }
/// <summary> /// <summary>
+11
View File
@@ -66,6 +66,17 @@ Make a one time donation in a crypto currency of your choice. If you prefer to d
Alternatively, sponsor me on Github using [Github Sponsors](https://github.com/sponsors/JKorf). Alternatively, sponsor me on Github using [Github Sponsors](https://github.com/sponsors/JKorf).
## Release notes ## Release notes
* Version 10.2.3 - 14 Jan 2026
* Added HandleUnhandledMessage virtual method to SocketApiClient to allow some processing for messages which couldn't be mapped via the normal way
* Fixed semaphore exception when creating a new REST client while time sync is in progress on another client
* Version 10.2.2 - 13 Jan 2026
* Allow the same websocket connection sequence number to be recorded multiple times
* Version 10.2.1 - 13 Jan 2026
* Removed duplicate logging for rest responses in Trace verbosity
* Fixed parameter URL creation for array values with ArrayParametersSerialization.MultipleValues
* Version 10.2.0 - 12 Jan 2026 * Version 10.2.0 - 12 Jan 2026
* Added EnforceSequenceNumbers property on SocketApiClient to configure whether websocket message contain sequence numbers and if these should be checked to be sequential * Added EnforceSequenceNumbers property on SocketApiClient to configure whether websocket message contain sequence numbers and if these should be checked to be sequential
* Added fallback to existing websocket connection if no dedicated request connection was found * Added fallback to existing websocket connection if no dedicated request connection was found