1
0
mirror of https://github.com/JKorf/CryptoExchange.Net synced 2025-12-14 01:33:26 +00:00
This commit is contained in:
Jkorf 2025-11-25 16:41:44 +01:00
parent 839ebc4ca9
commit fcef172069
7 changed files with 63 additions and 70 deletions

View File

@ -1,22 +1,22 @@
using CryptoExchange.Net.Converters.MessageParsing;
using CryptoExchange.Net.Converters.MessageParsing.DynamicConverters;
using CryptoExchange.Net.Objects;
using LightProto;
using System;
using System.Collections.Generic;
using System.Net.WebSockets;
using System.Text;
//using CryptoExchange.Net.Converters.MessageParsing;
//using CryptoExchange.Net.Converters.MessageParsing.DynamicConverters;
//using CryptoExchange.Net.Objects;
//using LightProto;
//using System;
//using System.Collections.Generic;
//using System.Net.WebSockets;
//using System.Text;
namespace CryptoExchange.Net.Protobuf.Converters.Protobuf
{
public abstract class DynamicProtobufConverter<T> : ISocketMessageHandler
{
public object Deserialize(ReadOnlySpan<byte> data, Type type)
{
var result = Serializer.Deserialize<T>(data);
return result;
}
//namespace CryptoExchange.Net.Protobuf.Converters.Protobuf
//{
// public abstract class DynamicProtobufConverter<T> : ISocketMessageHandler
// {
// public object Deserialize(ReadOnlySpan<byte> data, Type type)
// {
// var result = Serializer.Deserialize<T>(data);
// return result;
// }
public abstract string GetMessageIdentifier(ReadOnlySpan<byte> data, WebSocketMessageType? webSocketMessageType);
}
}
// public abstract string GetMessageIdentifier(ReadOnlySpan<byte> data, WebSocketMessageType? webSocketMessageType);
// }
//}

View File

@ -4,11 +4,6 @@
<name>CryptoExchange.Net.Protobuf</name>
</assembly>
<members>
<member name="F:CryptoExchange.Net.Protobuf.Converters.Protobuf.DynamicProtobufConverter._model">
<summary>
Runtime type model
</summary>
</member>
<member name="T:CryptoExchange.Net.Converters.Protobuf.ProtobufMessageAccessor`1">
<summary>
System.Text.Json message accessor

View File

@ -486,6 +486,10 @@ namespace CryptoExchange.Net.Clients
memoryStream.Position = 0;
originalData = await reader.ReadToEndAsync().ConfigureAwait(false);
if (_logger.IsEnabled(LogLevel.Trace))
#warning TODO extension
_logger.LogTrace("[Req {RequestId}] Received response: {Data}", request.RequestId, originalData);
// Continue processing from the memory stream since the response stream is already read and we can't seek it
responseStream.Close();
memoryStream.Position = 0;
@ -494,9 +498,12 @@ namespace CryptoExchange.Net.Clients
if (!response.IsSuccessStatusCode && !requestDefinition.TryParseOnNonSuccess)
{
// If the response status is not success it is an error by definition
Error error;
if (response.StatusCode == (HttpStatusCode)418 || response.StatusCode == (HttpStatusCode)429)
{
// Specifically handle rate limit errors
var rateError = await MessageHandler.ParseErrorRateLimitResponse(
(int)response.StatusCode,
state,
@ -512,6 +519,7 @@ namespace CryptoExchange.Net.Clients
}
else
{
// Handle a 'normal' error response. Can still be either a json error message or some random HTML or other string
error = await MessageHandler.ParseErrorResponse(
(int)response.StatusCode,
state,
@ -526,7 +534,7 @@ namespace CryptoExchange.Net.Clients
// Success status code and expected empty response, assume it's correct
return new WebCallResult<T>(response.StatusCode, response.HttpVersion, response.ResponseHeaders, sw.Elapsed, 0, originalData, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, null);
// Data response received
// Data response received, inspect the message and check if it is an error or not
var parsedError = await MessageHandler.CheckForErrorResponse(
requestDefinition,
state,
@ -547,8 +555,17 @@ namespace CryptoExchange.Net.Clients
return new WebCallResult<T>(response.StatusCode, response.HttpVersion, response.ResponseHeaders, sw.Elapsed, response.ContentLength, originalData, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, parsedError);
}
// Try deserialization into the expected type
var (deserializeResult, deserializeError) = await MessageHandler.TryDeserializeAsync<T>(responseStream, state, cancellationToken).ConfigureAwait(false);
return new WebCallResult<T>(response.StatusCode, response.HttpVersion, response.ResponseHeaders, sw.Elapsed, response.ContentLength, originalData, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, deserializeResult, deserializeError);
if (deserializeError != null)
return new WebCallResult<T>(response.StatusCode, response.HttpVersion, response.ResponseHeaders, sw.Elapsed, response.ContentLength, originalData, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, deserializeResult, deserializeError); ;
// Check the deserialized response to see if it's an error or not
var responseError = MessageHandler.CheckDeserializedResponse(response.ResponseHeaders, deserializeResult);
if (responseError != null)
return new WebCallResult<T>(response.StatusCode, response.HttpVersion, response.ResponseHeaders, sw.Elapsed, response.ContentLength, originalData, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, deserializeResult, responseError);
return new WebCallResult<T>(response.StatusCode, response.HttpVersion, response.ResponseHeaders, sw.Elapsed, response.ContentLength, originalData, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, deserializeResult, null);
}
catch (HttpRequestException requestException)
{

View File

@ -44,7 +44,10 @@ namespace CryptoExchange.Net.Converters.MessageParsing.DynamicConverters
Stream responseStream);
/// <summary>
/// Check if the response is an error response; if so return the error
/// Check if the response is an error response; if so return the error.<br />
/// Note that if the API returns a standard result wrapper, something like this:
/// <code>{ "code": 400, "msg": "error", "data": {} }</code>
/// then the `CheckDeserializedResponse` method should be used for checking the result
/// </summary>
ValueTask<Error?> CheckForErrorResponse(
RequestDefinition request,
@ -59,6 +62,11 @@ namespace CryptoExchange.Net.Converters.MessageParsing.DynamicConverters
Stream responseStream,
object? state,
CancellationToken ct);
/// <summary>
/// Check whether the resulting T object indicates an error or not
/// </summary>
Error? CheckDeserializedResponse<T>(HttpResponseHeaders responseHeaders, T result);
}
}

View File

@ -118,50 +118,19 @@ namespace CryptoExchange.Net.Converters.SystemTextJson.MessageConverters
result = await JsonSerializer.DeserializeAsync<T>(responseStream, Options)!.ConfigureAwait(false)!;
}
return (result, null);
}
catch (HttpRequestException requestException)
}
catch (JsonException ex)
{
// Request exception, can't reach server for instance
var error = new WebError(requestException.Message, requestException);
return (default, error);
var info = $"Json deserialization failed: {ex.Message}, Path: {ex.Path}, LineNumber: {ex.LineNumber}, LinePosition: {ex.BytePositionInLine}";
return (default, new DeserializeError(info, ex));
}
catch (OperationCanceledException canceledException)
catch (Exception ex)
{
if (cancellationToken != default && canceledException.CancellationToken == cancellationToken)
{
// Cancellation token canceled by caller
return (default, new CancellationRequestedError(canceledException));
}
else
{
// Request timed out
var error = new WebError($"Request timed out", exception: canceledException);
error.ErrorType = ErrorType.Timeout;
return (default, error);
}
}
catch (ArgumentException argumentException)
{
if (argumentException.Message.StartsWith("Only HTTP/"))
{
// Unsupported HTTP version error .net framework
var error = ArgumentError.Invalid(nameof(RestExchangeOptions.HttpVersion), $"Invalid HTTP version: " + argumentException.Message);
return (default, error);
}
throw;
}
catch (NotSupportedException notSupportedException)
{
if (notSupportedException.Message.StartsWith("Request version value must be one of"))
{
// Unsupported HTTP version error dotnet code
var error = ArgumentError.Invalid(nameof(RestExchangeOptions.HttpVersion), $"Invalid HTTP version: " + notSupportedException.Message);
return (default, error);
}
throw;
return (default, new DeserializeError($"Json deserialization failed: {ex.Message}", ex));
}
}
/// <inheritdoc />
public virtual Error? CheckDeserializedResponse<T>(HttpResponseHeaders responseHeaders, T result) => null;
}
}

View File

@ -80,11 +80,15 @@ namespace CryptoExchange.Net.Objects
/// <summary>
/// Get the parameter collection based on the ParameterPosition
/// </summary>
public IDictionary<string, object>? GetPositionParameters()
public IDictionary<string, object> GetPositionParameters()
{
if (ParameterPosition == HttpMethodParameterPosition.InBody)
{
BodyParameters ??= new Dictionary<string, object>();
return BodyParameters;
}
QueryParameters ??= new Dictionary<string, object>();
return QueryParameters;
}

View File

@ -565,7 +565,7 @@ namespace CryptoExchange.Net.Sockets
if (deserializationType == null)
{
// No handler found for identifier either, can't process
_logger.LogWarning("Failed to determine message type. Data: {Message}", Encoding.UTF8.GetString(data.ToArray()));
_logger.LogWarning("Failed to determine message type for identifier {Identifier}. Data: {Message}", messageIdentifier, Encoding.UTF8.GetString(data.ToArray()));
return;
}