mirror of
https://github.com/JKorf/CryptoExchange.Net
synced 2025-06-10 01:16:24 +00:00
Merge pull request #47 from ridicoulous/master
Optional shared HttpClient Request id tracing in logging
This commit is contained in:
commit
0c2b868a03
@ -36,6 +36,10 @@ namespace CryptoExchange.Net
|
|||||||
/// The auth provider
|
/// The auth provider
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected internal AuthenticationProvider? authProvider;
|
protected internal AuthenticationProvider? authProvider;
|
||||||
|
/// <summary>
|
||||||
|
/// Should check objects for missing properties based on the model and the received JSON
|
||||||
|
/// </summary>
|
||||||
|
public bool ShouldCheckObjects { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The last used id
|
/// The last used id
|
||||||
@ -73,6 +77,7 @@ namespace CryptoExchange.Net
|
|||||||
apiProxy = options.Proxy;
|
apiProxy = options.Proxy;
|
||||||
|
|
||||||
log.Write(LogVerbosity.Debug, $"Client configuration: {options}");
|
log.Write(LogVerbosity.Debug, $"Client configuration: {options}");
|
||||||
|
ShouldCheckObjects = options.ShouldCheckObjects;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -127,8 +132,9 @@ namespace CryptoExchange.Net
|
|||||||
/// <param name="data">The data to deserialize</param>
|
/// <param name="data">The data to deserialize</param>
|
||||||
/// <param name="checkObject">Whether or not the parsing should be checked for missing properties (will output data to the logging if log verbosity is Debug)</param>
|
/// <param name="checkObject">Whether or not the parsing should be checked for missing properties (will output data to the logging if log verbosity is Debug)</param>
|
||||||
/// <param name="serializer">A specific serializer to use</param>
|
/// <param name="serializer">A specific serializer to use</param>
|
||||||
|
/// <param name="requestId">Id of the request</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
protected CallResult<T> Deserialize<T>(string data, bool checkObject = true, JsonSerializer? serializer = null)
|
protected CallResult<T> Deserialize<T>(string data, bool? checkObject = null, JsonSerializer? serializer = null, int? requestId = null)
|
||||||
{
|
{
|
||||||
var tokenResult = ValidateJson(data);
|
var tokenResult = ValidateJson(data);
|
||||||
if (!tokenResult)
|
if (!tokenResult)
|
||||||
@ -137,7 +143,7 @@ namespace CryptoExchange.Net
|
|||||||
return new CallResult<T>(default, tokenResult.Error);
|
return new CallResult<T>(default, tokenResult.Error);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Deserialize<T>(tokenResult.Data, checkObject, serializer);
|
return Deserialize<T>(tokenResult.Data, checkObject, serializer, requestId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -148,30 +154,30 @@ namespace CryptoExchange.Net
|
|||||||
/// <param name="checkObject">Whether or not the parsing should be checked for missing properties (will output data to the logging if log verbosity is Debug)</param>
|
/// <param name="checkObject">Whether or not the parsing should be checked for missing properties (will output data to the logging if log verbosity is Debug)</param>
|
||||||
/// <param name="serializer">A specific serializer to use</param>
|
/// <param name="serializer">A specific serializer to use</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
protected CallResult<T> Deserialize<T>(JToken obj, bool checkObject = true, JsonSerializer? serializer = null)
|
protected CallResult<T> Deserialize<T>(JToken obj, bool? checkObject = null, JsonSerializer? serializer = null, int? requestId = null)
|
||||||
{
|
{
|
||||||
if (serializer == null)
|
if (serializer == null)
|
||||||
serializer = defaultSerializer;
|
serializer = defaultSerializer;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (checkObject && log.Level == LogVerbosity.Debug)
|
if ((checkObject ?? ShouldCheckObjects)&& log.Level == LogVerbosity.Debug)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (obj is JObject o)
|
if (obj is JObject o)
|
||||||
{
|
{
|
||||||
CheckObject(typeof(T), o);
|
CheckObject(typeof(T), o, requestId);
|
||||||
}
|
}
|
||||||
else if (obj is JArray j)
|
else if (obj is JArray j)
|
||||||
{
|
{
|
||||||
if (j.HasValues && j[0] is JObject jObject)
|
if (j.HasValues && j[0] is JObject jObject)
|
||||||
CheckObject(typeof(T).GetElementType(), jObject);
|
CheckObject(typeof(T).GetElementType(), jObject, requestId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
log.Write(LogVerbosity.Debug, "Failed to check response data: " + e.Message);
|
log.Write(LogVerbosity.Debug, $"{(requestId != null ? $"[{ requestId}] " : "")}Failed to check response data: " + e.Message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,19 +185,19 @@ namespace CryptoExchange.Net
|
|||||||
}
|
}
|
||||||
catch (JsonReaderException jre)
|
catch (JsonReaderException jre)
|
||||||
{
|
{
|
||||||
var info = $"Deserialize JsonReaderException: {jre.Message}, Path: {jre.Path}, LineNumber: {jre.LineNumber}, LinePosition: {jre.LinePosition}. Received data: {obj}";
|
var info = $"{(requestId != null ? $"[{requestId}] " : "")}Deserialize JsonReaderException: {jre.Message}, Path: {jre.Path}, LineNumber: {jre.LineNumber}, LinePosition: {jre.LinePosition}. Received data: {obj}";
|
||||||
log.Write(LogVerbosity.Error, info);
|
log.Write(LogVerbosity.Error, info);
|
||||||
return new CallResult<T>(default, new DeserializeError(info));
|
return new CallResult<T>(default, new DeserializeError(info));
|
||||||
}
|
}
|
||||||
catch (JsonSerializationException jse)
|
catch (JsonSerializationException jse)
|
||||||
{
|
{
|
||||||
var info = $"Deserialize JsonSerializationException: {jse.Message}. Received data: {obj}";
|
var info = $"{(requestId != null ? $"[{requestId}] " : "")}Deserialize JsonSerializationException: {jse.Message}. Received data: {obj}";
|
||||||
log.Write(LogVerbosity.Error, info);
|
log.Write(LogVerbosity.Error, info);
|
||||||
return new CallResult<T>(default, new DeserializeError(info));
|
return new CallResult<T>(default, new DeserializeError(info));
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
var info = $"Deserialize Unknown Exception: {ex.Message}. Received data: {obj}";
|
var info = $"{(requestId != null ? $"[{requestId}] " : "")}Deserialize Unknown Exception: {ex.Message}. Received data: {obj}";
|
||||||
log.Write(LogVerbosity.Error, info);
|
log.Write(LogVerbosity.Error, info);
|
||||||
return new CallResult<T>(default, new DeserializeError(info));
|
return new CallResult<T>(default, new DeserializeError(info));
|
||||||
}
|
}
|
||||||
@ -203,8 +209,9 @@ namespace CryptoExchange.Net
|
|||||||
/// <typeparam name="T">The type to deserialize into</typeparam>
|
/// <typeparam name="T">The type to deserialize into</typeparam>
|
||||||
/// <param name="stream">The stream to deserialize</param>
|
/// <param name="stream">The stream to deserialize</param>
|
||||||
/// <param name="serializer">A specific serializer to use</param>
|
/// <param name="serializer">A specific serializer to use</param>
|
||||||
|
/// <param name="requestId">Id of the request</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
protected async Task<CallResult<T>> Deserialize<T>(Stream stream, JsonSerializer? serializer = null)
|
protected async Task<CallResult<T>> Deserialize<T>(Stream stream, JsonSerializer? serializer = null, int? requestId = null)
|
||||||
{
|
{
|
||||||
if (serializer == null)
|
if (serializer == null)
|
||||||
serializer = defaultSerializer;
|
serializer = defaultSerializer;
|
||||||
@ -215,8 +222,8 @@ namespace CryptoExchange.Net
|
|||||||
if (log.Level == LogVerbosity.Debug)
|
if (log.Level == LogVerbosity.Debug)
|
||||||
{
|
{
|
||||||
var data = await reader.ReadToEndAsync().ConfigureAwait(false);
|
var data = await reader.ReadToEndAsync().ConfigureAwait(false);
|
||||||
log.Write(LogVerbosity.Debug, $"Data received: {data}");
|
log.Write(LogVerbosity.Debug, $"{(requestId != null ? $"[{requestId}] ": "")}Data received: {data}");
|
||||||
return Deserialize<T>(data);
|
return Deserialize<T>(data, null, serializer, requestId);
|
||||||
}
|
}
|
||||||
|
|
||||||
using var jsonReader = new JsonTextReader(reader);
|
using var jsonReader = new JsonTextReader(reader);
|
||||||
@ -227,7 +234,7 @@ namespace CryptoExchange.Net
|
|||||||
if(stream.CanSeek)
|
if(stream.CanSeek)
|
||||||
stream.Seek(0, SeekOrigin.Begin);
|
stream.Seek(0, SeekOrigin.Begin);
|
||||||
var data = await ReadStream(stream).ConfigureAwait(false);
|
var data = await ReadStream(stream).ConfigureAwait(false);
|
||||||
log.Write(LogVerbosity.Error, $"Deserialize JsonReaderException: {jre.Message}, Path: {jre.Path}, LineNumber: {jre.LineNumber}, LinePosition: {jre.LinePosition}, data: {data}");
|
log.Write(LogVerbosity.Error, $"{(requestId != null ? $"[{requestId}] " : "")}Deserialize JsonReaderException: {jre.Message}, Path: {jre.Path}, LineNumber: {jre.LineNumber}, LinePosition: {jre.LinePosition}, data: {data}");
|
||||||
return new CallResult<T>(default, new DeserializeError(data));
|
return new CallResult<T>(default, new DeserializeError(data));
|
||||||
}
|
}
|
||||||
catch (JsonSerializationException jse)
|
catch (JsonSerializationException jse)
|
||||||
@ -235,7 +242,7 @@ namespace CryptoExchange.Net
|
|||||||
if (stream.CanSeek)
|
if (stream.CanSeek)
|
||||||
stream.Seek(0, SeekOrigin.Begin);
|
stream.Seek(0, SeekOrigin.Begin);
|
||||||
var data = await ReadStream(stream).ConfigureAwait(false);
|
var data = await ReadStream(stream).ConfigureAwait(false);
|
||||||
log.Write(LogVerbosity.Error, $"Deserialize JsonSerializationException: {jse.Message}, data: {data}");
|
log.Write(LogVerbosity.Error, $"{(requestId != null ? $"[{requestId}] " : "")}Deserialize JsonSerializationException: {jse.Message}, data: {data}");
|
||||||
return new CallResult<T>(default, new DeserializeError(data));
|
return new CallResult<T>(default, new DeserializeError(data));
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@ -243,7 +250,7 @@ namespace CryptoExchange.Net
|
|||||||
if (stream.CanSeek)
|
if (stream.CanSeek)
|
||||||
stream.Seek(0, SeekOrigin.Begin);
|
stream.Seek(0, SeekOrigin.Begin);
|
||||||
var data = await ReadStream(stream).ConfigureAwait(false);
|
var data = await ReadStream(stream).ConfigureAwait(false);
|
||||||
log.Write(LogVerbosity.Error, $"Deserialize Unknown Exception: {ex.Message}, data: {data}");
|
log.Write(LogVerbosity.Error, $"{(requestId != null ? $"[{requestId}] " : "")}Deserialize Unknown Exception: {ex.Message}, data: {data}");
|
||||||
return new CallResult<T>(default, new DeserializeError(data));
|
return new CallResult<T>(default, new DeserializeError(data));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -254,8 +261,11 @@ namespace CryptoExchange.Net
|
|||||||
return await reader.ReadToEndAsync().ConfigureAwait(false);
|
return await reader.ReadToEndAsync().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CheckObject(Type type, JObject obj)
|
private void CheckObject(Type type, JObject obj, int? requestId = null)
|
||||||
{
|
{
|
||||||
|
if (type == null)
|
||||||
|
return;
|
||||||
|
|
||||||
if (type.GetCustomAttribute<JsonConverterAttribute>(true) != null)
|
if (type.GetCustomAttribute<JsonConverterAttribute>(true) != null)
|
||||||
// If type has a custom JsonConverter we assume this will handle property mapping
|
// If type has a custom JsonConverter we assume this will handle property mapping
|
||||||
return;
|
return;
|
||||||
@ -265,7 +275,7 @@ namespace CryptoExchange.Net
|
|||||||
|
|
||||||
if (!obj.HasValues && type != typeof(object))
|
if (!obj.HasValues && type != typeof(object))
|
||||||
{
|
{
|
||||||
log.Write(LogVerbosity.Warning, $"Expected `{type.Name}`, but received object was empty");
|
log.Write(LogVerbosity.Warning, $"{(requestId != null ? $"[{requestId}] " : "")}Expected `{type.Name}`, but received object was empty");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -291,7 +301,7 @@ namespace CryptoExchange.Net
|
|||||||
{
|
{
|
||||||
if (!(type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<,>)))
|
if (!(type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<,>)))
|
||||||
{
|
{
|
||||||
log.Write(LogVerbosity.Warning, $"Local object doesn't have property `{token.Key}` expected in type `{type.Name}`");
|
log.Write(LogVerbosity.Warning, $"{(requestId != null ? $"[{requestId}] " : "")}Local object doesn't have property `{token.Key}` expected in type `{type.Name}`");
|
||||||
isDif = true;
|
isDif = true;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
@ -306,9 +316,9 @@ namespace CryptoExchange.Net
|
|||||||
if (!IsSimple(propType) && propType != typeof(DateTime))
|
if (!IsSimple(propType) && propType != typeof(DateTime))
|
||||||
{
|
{
|
||||||
if (propType.IsArray && token.Value.HasValues && ((JArray)token.Value).Any() && ((JArray)token.Value)[0] is JObject)
|
if (propType.IsArray && token.Value.HasValues && ((JArray)token.Value).Any() && ((JArray)token.Value)[0] is JObject)
|
||||||
CheckObject(propType.GetElementType(), (JObject)token.Value[0]);
|
CheckObject(propType.GetElementType(), (JObject)token.Value[0], requestId);
|
||||||
else if (token.Value is JObject o)
|
else if (token.Value is JObject o)
|
||||||
CheckObject(propType, o);
|
CheckObject(propType, o, requestId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -321,11 +331,11 @@ namespace CryptoExchange.Net
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
isDif = true;
|
isDif = true;
|
||||||
log.Write(LogVerbosity.Warning, $"Local object has property `{prop}` but was not found in received object of type `{type.Name}`");
|
log.Write(LogVerbosity.Warning, $"{(requestId != null ? $"[{requestId}] " : "")}Local object has property `{prop}` but was not found in received object of type `{type.Name}`");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isDif)
|
if (isDif)
|
||||||
log.Write(LogVerbosity.Debug, "Returned data: " + obj);
|
log.Write(LogVerbosity.Debug, $"{(requestId != null ? $"[{ requestId}] " : "")}Returned data: " + obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static PropertyInfo? GetProperty(string name, IEnumerable<PropertyInfo> props)
|
private static PropertyInfo? GetProperty(string name, IEnumerable<PropertyInfo> props)
|
||||||
|
@ -224,6 +224,11 @@
|
|||||||
The auth provider
|
The auth provider
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
|
<member name="P:CryptoExchange.Net.BaseClient.ShouldCheckObjects">
|
||||||
|
<summary>
|
||||||
|
Should check objects for missing properties based on the model and the received JSON
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
<member name="F:CryptoExchange.Net.BaseClient.lastId">
|
<member name="F:CryptoExchange.Net.BaseClient.lastId">
|
||||||
<summary>
|
<summary>
|
||||||
The last used id
|
The last used id
|
||||||
@ -259,7 +264,7 @@
|
|||||||
<param name="data">The data to parse</param>
|
<param name="data">The data to parse</param>
|
||||||
<returns></returns>
|
<returns></returns>
|
||||||
</member>
|
</member>
|
||||||
<member name="M:CryptoExchange.Net.BaseClient.Deserialize``1(System.String,System.Boolean,Newtonsoft.Json.JsonSerializer)">
|
<member name="M:CryptoExchange.Net.BaseClient.Deserialize``1(System.String,System.Nullable{System.Boolean},Newtonsoft.Json.JsonSerializer,System.Nullable{System.Int32})">
|
||||||
<summary>
|
<summary>
|
||||||
Deserialize a string into an object
|
Deserialize a string into an object
|
||||||
</summary>
|
</summary>
|
||||||
@ -267,9 +272,10 @@
|
|||||||
<param name="data">The data to deserialize</param>
|
<param name="data">The data to deserialize</param>
|
||||||
<param name="checkObject">Whether or not the parsing should be checked for missing properties (will output data to the logging if log verbosity is Debug)</param>
|
<param name="checkObject">Whether or not the parsing should be checked for missing properties (will output data to the logging if log verbosity is Debug)</param>
|
||||||
<param name="serializer">A specific serializer to use</param>
|
<param name="serializer">A specific serializer to use</param>
|
||||||
|
<param name="requestId">Id of the request</param>
|
||||||
<returns></returns>
|
<returns></returns>
|
||||||
</member>
|
</member>
|
||||||
<member name="M:CryptoExchange.Net.BaseClient.Deserialize``1(Newtonsoft.Json.Linq.JToken,System.Boolean,Newtonsoft.Json.JsonSerializer)">
|
<member name="M:CryptoExchange.Net.BaseClient.Deserialize``1(Newtonsoft.Json.Linq.JToken,System.Nullable{System.Boolean},Newtonsoft.Json.JsonSerializer,System.Nullable{System.Int32})">
|
||||||
<summary>
|
<summary>
|
||||||
Deserialize a JToken into an object
|
Deserialize a JToken into an object
|
||||||
</summary>
|
</summary>
|
||||||
@ -279,13 +285,14 @@
|
|||||||
<param name="serializer">A specific serializer to use</param>
|
<param name="serializer">A specific serializer to use</param>
|
||||||
<returns></returns>
|
<returns></returns>
|
||||||
</member>
|
</member>
|
||||||
<member name="M:CryptoExchange.Net.BaseClient.Deserialize``1(System.IO.Stream,Newtonsoft.Json.JsonSerializer)">
|
<member name="M:CryptoExchange.Net.BaseClient.Deserialize``1(System.IO.Stream,Newtonsoft.Json.JsonSerializer,System.Nullable{System.Int32})">
|
||||||
<summary>
|
<summary>
|
||||||
Deserialize a stream into an object
|
Deserialize a stream into an object
|
||||||
</summary>
|
</summary>
|
||||||
<typeparam name="T">The type to deserialize into</typeparam>
|
<typeparam name="T">The type to deserialize into</typeparam>
|
||||||
<param name="stream">The stream to deserialize</param>
|
<param name="stream">The stream to deserialize</param>
|
||||||
<param name="serializer">A specific serializer to use</param>
|
<param name="serializer">A specific serializer to use</param>
|
||||||
|
<param name="requestId">Id of the request</param>
|
||||||
<returns></returns>
|
<returns></returns>
|
||||||
</member>
|
</member>
|
||||||
<member name="M:CryptoExchange.Net.BaseClient.NextId">
|
<member name="M:CryptoExchange.Net.BaseClient.NextId">
|
||||||
@ -588,6 +595,11 @@
|
|||||||
Uri
|
Uri
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
|
<member name="P:CryptoExchange.Net.Interfaces.IRequest.RequestId">
|
||||||
|
<summary>
|
||||||
|
internal request id for tracing
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
<member name="M:CryptoExchange.Net.Interfaces.IRequest.SetContent(System.Byte[])">
|
<member name="M:CryptoExchange.Net.Interfaces.IRequest.SetContent(System.Byte[])">
|
||||||
<summary>
|
<summary>
|
||||||
Set byte content
|
Set byte content
|
||||||
@ -620,20 +632,22 @@
|
|||||||
Request factory interface
|
Request factory interface
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
<member name="M:CryptoExchange.Net.Interfaces.IRequestFactory.Create(System.Net.Http.HttpMethod,System.String)">
|
<member name="M:CryptoExchange.Net.Interfaces.IRequestFactory.Create(System.Net.Http.HttpMethod,System.String,System.Int32)">
|
||||||
<summary>
|
<summary>
|
||||||
Create a request for an uri
|
Create a request for an uri
|
||||||
</summary>
|
</summary>
|
||||||
<param name="method"></param>
|
<param name="method"></param>
|
||||||
<param name="uri"></param>
|
<param name="uri"></param>
|
||||||
|
<param name="requestId"></param>
|
||||||
<returns></returns>
|
<returns></returns>
|
||||||
</member>
|
</member>
|
||||||
<member name="M:CryptoExchange.Net.Interfaces.IRequestFactory.Configure(System.TimeSpan,CryptoExchange.Net.Objects.ApiProxy)">
|
<member name="M:CryptoExchange.Net.Interfaces.IRequestFactory.Configure(System.TimeSpan,CryptoExchange.Net.Objects.ApiProxy,System.Net.Http.HttpClient)">
|
||||||
<summary>
|
<summary>
|
||||||
Configure the requests created by this factory
|
Configure the requests created by this factory
|
||||||
</summary>
|
</summary>
|
||||||
<param name="requestTimeout">Request timeout to use</param>
|
<param name="requestTimeout">Request timeout to use</param>
|
||||||
<param name="proxy">Proxy settings to use</param>
|
<param name="proxy">Proxy settings to use</param>
|
||||||
|
<param name="httpClient">Optional shared http client instance</param>
|
||||||
</member>
|
</member>
|
||||||
<member name="T:CryptoExchange.Net.Interfaces.IResponse">
|
<member name="T:CryptoExchange.Net.Interfaces.IResponse">
|
||||||
<summary>
|
<summary>
|
||||||
@ -1582,6 +1596,11 @@
|
|||||||
The api credentials
|
The api credentials
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
|
<member name="P:CryptoExchange.Net.Objects.ClientOptions.ShouldCheckObjects">
|
||||||
|
<summary>
|
||||||
|
Should check objects for missing properties based on the model and the received JSON
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
<member name="P:CryptoExchange.Net.Objects.ClientOptions.Proxy">
|
<member name="P:CryptoExchange.Net.Objects.ClientOptions.Proxy">
|
||||||
<summary>
|
<summary>
|
||||||
Proxy to use
|
Proxy to use
|
||||||
@ -1616,11 +1635,23 @@
|
|||||||
The time the server has to respond to a request before timing out
|
The time the server has to respond to a request before timing out
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
|
<member name="P:CryptoExchange.Net.Objects.RestClientOptions.HttpClient">
|
||||||
|
<summary>
|
||||||
|
Http client to use. If a HttpClient is provided in this property the RequestTimeout and Proxy options will be ignored and should be set on the provided HttpClient instance
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
<member name="M:CryptoExchange.Net.Objects.RestClientOptions.#ctor(System.String)">
|
<member name="M:CryptoExchange.Net.Objects.RestClientOptions.#ctor(System.String)">
|
||||||
<summary>
|
<summary>
|
||||||
ctor
|
ctor
|
||||||
</summary>
|
</summary>
|
||||||
<param name="baseAddress"></param>
|
<param name="baseAddress">The base address of the API</param>
|
||||||
|
</member>
|
||||||
|
<member name="M:CryptoExchange.Net.Objects.RestClientOptions.#ctor(System.Net.Http.HttpClient,System.String)">
|
||||||
|
<summary>
|
||||||
|
ctor
|
||||||
|
</summary>
|
||||||
|
<param name="baseAddress">The base address of the API</param>
|
||||||
|
<param name="httpClient">Shared http client instance</param>
|
||||||
</member>
|
</member>
|
||||||
<member name="M:CryptoExchange.Net.Objects.RestClientOptions.Copy``1">
|
<member name="M:CryptoExchange.Net.Objects.RestClientOptions.Copy``1">
|
||||||
<summary>
|
<summary>
|
||||||
@ -2024,12 +2055,13 @@
|
|||||||
Request object
|
Request object
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
<member name="M:CryptoExchange.Net.Requests.Request.#ctor(System.Net.Http.HttpRequestMessage,System.Net.Http.HttpClient)">
|
<member name="M:CryptoExchange.Net.Requests.Request.#ctor(System.Net.Http.HttpRequestMessage,System.Net.Http.HttpClient,System.Int32)">
|
||||||
<summary>
|
<summary>
|
||||||
Create request object for web request
|
Create request object for web request
|
||||||
</summary>
|
</summary>
|
||||||
<param name="request"></param>
|
<param name="request"></param>
|
||||||
<param name="client"></param>
|
<param name="client"></param>
|
||||||
|
<param name="requestId"></param>
|
||||||
</member>
|
</member>
|
||||||
<member name="P:CryptoExchange.Net.Requests.Request.Content">
|
<member name="P:CryptoExchange.Net.Requests.Request.Content">
|
||||||
<inheritdoc />
|
<inheritdoc />
|
||||||
@ -2043,6 +2075,9 @@
|
|||||||
<member name="P:CryptoExchange.Net.Requests.Request.Uri">
|
<member name="P:CryptoExchange.Net.Requests.Request.Uri">
|
||||||
<inheritdoc />
|
<inheritdoc />
|
||||||
</member>
|
</member>
|
||||||
|
<member name="P:CryptoExchange.Net.Requests.Request.RequestId">
|
||||||
|
<inheritdoc />
|
||||||
|
</member>
|
||||||
<member name="M:CryptoExchange.Net.Requests.Request.SetContent(System.String,System.String)">
|
<member name="M:CryptoExchange.Net.Requests.Request.SetContent(System.String,System.String)">
|
||||||
<inheritdoc />
|
<inheritdoc />
|
||||||
</member>
|
</member>
|
||||||
@ -2060,10 +2095,10 @@
|
|||||||
WebRequest factory
|
WebRequest factory
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
<member name="M:CryptoExchange.Net.Requests.RequestFactory.Configure(System.TimeSpan,CryptoExchange.Net.Objects.ApiProxy)">
|
<member name="M:CryptoExchange.Net.Requests.RequestFactory.Configure(System.TimeSpan,CryptoExchange.Net.Objects.ApiProxy,System.Net.Http.HttpClient)">
|
||||||
<inheritdoc />
|
<inheritdoc />
|
||||||
</member>
|
</member>
|
||||||
<member name="M:CryptoExchange.Net.Requests.RequestFactory.Create(System.Net.Http.HttpMethod,System.String)">
|
<member name="M:CryptoExchange.Net.Requests.RequestFactory.Create(System.Net.Http.HttpMethod,System.String,System.Int32)">
|
||||||
<inheritdoc />
|
<inheritdoc />
|
||||||
</member>
|
</member>
|
||||||
<member name="T:CryptoExchange.Net.Requests.Response">
|
<member name="T:CryptoExchange.Net.Requests.Response">
|
||||||
@ -2189,7 +2224,7 @@
|
|||||||
<param name="signed">Whether or not the request should be authenticated</param>
|
<param name="signed">Whether or not the request should be authenticated</param>
|
||||||
<param name="checkResult">Whether or not the resulting object should be checked for missing properties in the mapping (only outputs if log verbosity is Debug)</param>
|
<param name="checkResult">Whether or not the resulting object should be checked for missing properties in the mapping (only outputs if log verbosity is Debug)</param>
|
||||||
<param name="postPosition">Where the post parameters should be placed</param>
|
<param name="postPosition">Where the post parameters should be placed</param>
|
||||||
<param name="arraySerialization">How array paramters should be serialized</param>
|
<param name="arraySerialization">How array parameters should be serialized</param>
|
||||||
<returns></returns>
|
<returns></returns>
|
||||||
</member>
|
</member>
|
||||||
<member name="M:CryptoExchange.Net.RestClient.GetResponse``1(CryptoExchange.Net.Interfaces.IRequest,System.Threading.CancellationToken)">
|
<member name="M:CryptoExchange.Net.RestClient.GetResponse``1(CryptoExchange.Net.Interfaces.IRequest,System.Threading.CancellationToken)">
|
||||||
@ -2208,7 +2243,7 @@
|
|||||||
<param name="data">Received data</param>
|
<param name="data">Received data</param>
|
||||||
<returns>Null if not an error, Error otherwise</returns>
|
<returns>Null if not an error, Error otherwise</returns>
|
||||||
</member>
|
</member>
|
||||||
<member name="M:CryptoExchange.Net.RestClient.ConstructRequest(System.Uri,System.Net.Http.HttpMethod,System.Collections.Generic.Dictionary{System.String,System.Object},System.Boolean,CryptoExchange.Net.Objects.PostParameters,CryptoExchange.Net.Objects.ArrayParametersSerialization)">
|
<member name="M:CryptoExchange.Net.RestClient.ConstructRequest(System.Uri,System.Net.Http.HttpMethod,System.Collections.Generic.Dictionary{System.String,System.Object},System.Boolean,CryptoExchange.Net.Objects.PostParameters,CryptoExchange.Net.Objects.ArrayParametersSerialization,System.Int32)">
|
||||||
<summary>
|
<summary>
|
||||||
Creates a request object
|
Creates a request object
|
||||||
</summary>
|
</summary>
|
||||||
@ -2217,7 +2252,8 @@
|
|||||||
<param name="parameters">The parameters of the request</param>
|
<param name="parameters">The parameters of the request</param>
|
||||||
<param name="signed">Whether or not the request should be authenticated</param>
|
<param name="signed">Whether or not the request should be authenticated</param>
|
||||||
<param name="postPosition">Where the post parameters should be placed</param>
|
<param name="postPosition">Where the post parameters should be placed</param>
|
||||||
<param name="arraySerialization">How array paramters should be serialized</param>
|
<param name="arraySerialization">How array parameters should be serialized</param>
|
||||||
|
<param name="requestId">Unique id of a request</param>
|
||||||
<returns></returns>
|
<returns></returns>
|
||||||
</member>
|
</member>
|
||||||
<member name="M:CryptoExchange.Net.RestClient.WriteParamBody(CryptoExchange.Net.Interfaces.IRequest,System.Collections.Generic.Dictionary{System.String,System.Object},System.String)">
|
<member name="M:CryptoExchange.Net.RestClient.WriteParamBody(CryptoExchange.Net.Interfaces.IRequest,System.Collections.Generic.Dictionary{System.String,System.Object},System.String)">
|
||||||
|
@ -27,6 +27,10 @@ namespace CryptoExchange.Net.Interfaces
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
Uri Uri { get; }
|
Uri Uri { get; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
/// internal request id for tracing
|
||||||
|
/// </summary>
|
||||||
|
int RequestId { get; }
|
||||||
|
/// <summary>
|
||||||
/// Set byte content
|
/// Set byte content
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="data"></param>
|
/// <param name="data"></param>
|
||||||
|
@ -14,14 +14,16 @@ namespace CryptoExchange.Net.Interfaces
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="method"></param>
|
/// <param name="method"></param>
|
||||||
/// <param name="uri"></param>
|
/// <param name="uri"></param>
|
||||||
|
/// <param name="requestId"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
IRequest Create(HttpMethod method, string uri);
|
IRequest Create(HttpMethod method, string uri, int requestId);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Configure the requests created by this factory
|
/// Configure the requests created by this factory
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="requestTimeout">Request timeout to use</param>
|
/// <param name="requestTimeout">Request timeout to use</param>
|
||||||
/// <param name="proxy">Proxy settings to use</param>
|
/// <param name="proxy">Proxy settings to use</param>
|
||||||
void Configure(TimeSpan requestTimeout, ApiProxy? proxy);
|
/// <param name="httpClient">Optional shared http client instance</param>
|
||||||
|
void Configure(TimeSpan requestTimeout, ApiProxy? proxy, HttpClient? httpClient=null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Net.Http;
|
||||||
using CryptoExchange.Net.Authentication;
|
using CryptoExchange.Net.Authentication;
|
||||||
using CryptoExchange.Net.Interfaces;
|
using CryptoExchange.Net.Interfaces;
|
||||||
using CryptoExchange.Net.Logging;
|
using CryptoExchange.Net.Logging;
|
||||||
@ -85,6 +86,10 @@ namespace CryptoExchange.Net.Objects
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public ApiCredentials? ApiCredentials { get; set; }
|
public ApiCredentials? ApiCredentials { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Should check objects for missing properties based on the model and the received JSON
|
||||||
|
/// </summary>
|
||||||
|
public bool ShouldCheckObjects { get; set; } = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Proxy to use
|
/// Proxy to use
|
||||||
@ -127,14 +132,27 @@ namespace CryptoExchange.Net.Objects
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public TimeSpan RequestTimeout { get; set; } = TimeSpan.FromSeconds(30);
|
public TimeSpan RequestTimeout { get; set; } = TimeSpan.FromSeconds(30);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Http client to use. If a HttpClient is provided in this property the RequestTimeout and Proxy options will be ignored and should be set on the provided HttpClient instance
|
||||||
|
/// </summary>
|
||||||
|
public HttpClient? HttpClient { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ctor
|
/// ctor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="baseAddress"></param>
|
/// <param name="baseAddress">The base address of the API</param>
|
||||||
public RestClientOptions(string baseAddress): base(baseAddress)
|
public RestClientOptions(string baseAddress): base(baseAddress)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// ctor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="baseAddress">The base address of the API</param>
|
||||||
|
/// <param name="httpClient">Shared http client instance</param>
|
||||||
|
public RestClientOptions(HttpClient httpClient, string baseAddress) : base(baseAddress)
|
||||||
|
{
|
||||||
|
HttpClient = httpClient;
|
||||||
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a copy of the options
|
/// Create a copy of the options
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -150,7 +168,8 @@ namespace CryptoExchange.Net.Objects
|
|||||||
LogWriters = LogWriters,
|
LogWriters = LogWriters,
|
||||||
RateLimiters = RateLimiters,
|
RateLimiters = RateLimiters,
|
||||||
RateLimitingBehaviour = RateLimitingBehaviour,
|
RateLimitingBehaviour = RateLimitingBehaviour,
|
||||||
RequestTimeout = RequestTimeout
|
RequestTimeout = RequestTimeout,
|
||||||
|
HttpClient = HttpClient
|
||||||
};
|
};
|
||||||
|
|
||||||
if (ApiCredentials != null)
|
if (ApiCredentials != null)
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.IO;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@ -21,10 +22,12 @@ namespace CryptoExchange.Net.Requests
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="request"></param>
|
/// <param name="request"></param>
|
||||||
/// <param name="client"></param>
|
/// <param name="client"></param>
|
||||||
public Request(HttpRequestMessage request, HttpClient client)
|
/// <param name="requestId"></param>
|
||||||
|
public Request(HttpRequestMessage request, HttpClient client, int requestId)
|
||||||
{
|
{
|
||||||
httpClient = client;
|
httpClient = client;
|
||||||
this.request = request;
|
this.request = request;
|
||||||
|
RequestId = requestId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@ -45,6 +48,8 @@ namespace CryptoExchange.Net.Requests
|
|||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public Uri Uri => request.RequestUri;
|
public Uri Uri => request.RequestUri;
|
||||||
|
/// <inheritdoc />
|
||||||
|
public int RequestId { get; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void SetContent(string data, string contentType)
|
public void SetContent(string data, string contentType)
|
||||||
|
@ -14,7 +14,9 @@ namespace CryptoExchange.Net.Requests
|
|||||||
private HttpClient? httpClient;
|
private HttpClient? httpClient;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void Configure(TimeSpan requestTimeout, ApiProxy? proxy)
|
public void Configure(TimeSpan requestTimeout, ApiProxy? proxy, HttpClient? client = null)
|
||||||
|
{
|
||||||
|
if (client == null)
|
||||||
{
|
{
|
||||||
HttpMessageHandler handler = new HttpClientHandler()
|
HttpMessageHandler handler = new HttpClientHandler()
|
||||||
{
|
{
|
||||||
@ -25,16 +27,21 @@ namespace CryptoExchange.Net.Requests
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
httpClient = new HttpClient(handler) {Timeout = requestTimeout};
|
httpClient = new HttpClient(handler) { Timeout = requestTimeout };
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
httpClient = client;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IRequest Create(HttpMethod method, string uri)
|
public IRequest Create(HttpMethod method, string uri, int requestId)
|
||||||
{
|
{
|
||||||
if (httpClient == null)
|
if (httpClient == null)
|
||||||
throw new InvalidOperationException("Cant create request before configuring http client");
|
throw new InvalidOperationException("Cant create request before configuring http client");
|
||||||
|
|
||||||
return new Request(new HttpRequestMessage(method, uri), httpClient);
|
return new Request(new HttpRequestMessage(method, uri), httpClient, requestId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ namespace CryptoExchange.Net
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Base rest client
|
/// Base rest client
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class RestClient: BaseClient, IRestClient
|
public abstract class RestClient : BaseClient, IRestClient
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The factory for creating requests. Used for unit testing
|
/// The factory for creating requests. Used for unit testing
|
||||||
@ -77,13 +77,13 @@ namespace CryptoExchange.Net
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="exchangeOptions"></param>
|
/// <param name="exchangeOptions"></param>
|
||||||
/// <param name="authenticationProvider"></param>
|
/// <param name="authenticationProvider"></param>
|
||||||
protected RestClient(RestClientOptions exchangeOptions, AuthenticationProvider? authenticationProvider): base(exchangeOptions, authenticationProvider)
|
protected RestClient(RestClientOptions exchangeOptions, AuthenticationProvider? authenticationProvider) : base(exchangeOptions, authenticationProvider)
|
||||||
{
|
{
|
||||||
if (exchangeOptions == null)
|
if (exchangeOptions == null)
|
||||||
throw new ArgumentNullException(nameof(exchangeOptions));
|
throw new ArgumentNullException(nameof(exchangeOptions));
|
||||||
|
|
||||||
RequestTimeout = exchangeOptions.RequestTimeout;
|
RequestTimeout = exchangeOptions.RequestTimeout;
|
||||||
RequestFactory.Configure(exchangeOptions.RequestTimeout, exchangeOptions.Proxy);
|
RequestFactory.Configure(exchangeOptions.RequestTimeout, exchangeOptions.Proxy, exchangeOptions.HttpClient);
|
||||||
RateLimitBehaviour = exchangeOptions.RateLimitingBehaviour;
|
RateLimitBehaviour = exchangeOptions.RateLimitingBehaviour;
|
||||||
var rateLimiters = new List<IRateLimiter>();
|
var rateLimiters = new List<IRateLimiter>();
|
||||||
foreach (var rateLimiter in exchangeOptions.RateLimiters)
|
foreach (var rateLimiter in exchangeOptions.RateLimiters)
|
||||||
@ -134,10 +134,10 @@ namespace CryptoExchange.Net
|
|||||||
{
|
{
|
||||||
reply = await ping.SendPingAsync(uri.Host).ConfigureAwait(false);
|
reply = await ping.SendPingAsync(uri.Host).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch(PingException e)
|
catch (PingException e)
|
||||||
{
|
{
|
||||||
if (e.InnerException == null)
|
if (e.InnerException == null)
|
||||||
return new CallResult<long>(0, new CantConnectError {Message = "Ping failed: " + e.Message});
|
return new CallResult<long>(0, new CantConnectError { Message = "Ping failed: " + e.Message });
|
||||||
|
|
||||||
if (e.InnerException is SocketException exception)
|
if (e.InnerException is SocketException exception)
|
||||||
return new CallResult<long>(0, new CantConnectError { Message = "Ping failed: " + exception.SocketErrorCode });
|
return new CallResult<long>(0, new CantConnectError { Message = "Ping failed: " + exception.SocketErrorCode });
|
||||||
@ -149,7 +149,7 @@ namespace CryptoExchange.Net
|
|||||||
ping.Dispose();
|
ping.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ct.IsCancellationRequested)
|
if (ct.IsCancellationRequested)
|
||||||
return new CallResult<long>(0, new CancellationRequestedError());
|
return new CallResult<long>(0, new CancellationRequestedError());
|
||||||
|
|
||||||
return reply.Status == IPStatus.Success ? new CallResult<long>(reply.RoundtripTime, null) : new CallResult<long>(0, new CantConnectError { Message = "Ping failed: " + reply.Status });
|
return reply.Status == IPStatus.Success ? new CallResult<long>(reply.RoundtripTime, null) : new CallResult<long>(0, new CantConnectError { Message = "Ping failed: " + reply.Status });
|
||||||
@ -166,38 +166,39 @@ namespace CryptoExchange.Net
|
|||||||
/// <param name="signed">Whether or not the request should be authenticated</param>
|
/// <param name="signed">Whether or not the request should be authenticated</param>
|
||||||
/// <param name="checkResult">Whether or not the resulting object should be checked for missing properties in the mapping (only outputs if log verbosity is Debug)</param>
|
/// <param name="checkResult">Whether or not the resulting object should be checked for missing properties in the mapping (only outputs if log verbosity is Debug)</param>
|
||||||
/// <param name="postPosition">Where the post parameters should be placed</param>
|
/// <param name="postPosition">Where the post parameters should be placed</param>
|
||||||
/// <param name="arraySerialization">How array paramters should be serialized</param>
|
/// <param name="arraySerialization">How array parameters should be serialized</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[return: NotNull]
|
[return: NotNull]
|
||||||
protected virtual async Task<WebCallResult<T>> SendRequest<T>(Uri uri, HttpMethod method, CancellationToken cancellationToken,
|
protected virtual async Task<WebCallResult<T>> SendRequest<T>(Uri uri, HttpMethod method, CancellationToken cancellationToken,
|
||||||
Dictionary<string, object>? parameters = null, bool signed = false, bool checkResult = true, PostParameters? postPosition = null, ArrayParametersSerialization? arraySerialization = null) where T : class
|
Dictionary<string, object>? parameters = null, bool signed = false, bool checkResult = true, PostParameters? postPosition = null, ArrayParametersSerialization? arraySerialization = null) where T : class
|
||||||
{
|
{
|
||||||
log.Write(LogVerbosity.Debug, "Creating request for " + uri);
|
var requestId = NextId();
|
||||||
|
log.Write(LogVerbosity.Debug, $"[{requestId}] Creating request for " + uri);
|
||||||
if (signed && authProvider == null)
|
if (signed && authProvider == null)
|
||||||
{
|
{
|
||||||
log.Write(LogVerbosity.Warning, $"Request {uri.AbsolutePath} failed because no ApiCredentials were provided");
|
log.Write(LogVerbosity.Warning, $"[{requestId}] Request {uri.AbsolutePath} failed because no ApiCredentials were provided");
|
||||||
return new WebCallResult<T>(null, null, null, new NoApiCredentialsError());
|
return new WebCallResult<T>(null, null, null, new NoApiCredentialsError());
|
||||||
}
|
}
|
||||||
|
|
||||||
var request = ConstructRequest(uri, method, parameters, signed, postPosition ?? postParametersPosition, arraySerialization ?? this.arraySerialization);
|
var request = ConstructRequest(uri, method, parameters, signed, postPosition ?? postParametersPosition, arraySerialization ?? this.arraySerialization, requestId);
|
||||||
foreach (var limiter in RateLimiters)
|
foreach (var limiter in RateLimiters)
|
||||||
{
|
{
|
||||||
var limitResult = limiter.LimitRequest(this, uri.AbsolutePath, RateLimitBehaviour);
|
var limitResult = limiter.LimitRequest(this, uri.AbsolutePath, RateLimitBehaviour);
|
||||||
if (!limitResult.Success)
|
if (!limitResult.Success)
|
||||||
{
|
{
|
||||||
log.Write(LogVerbosity.Debug, $"Request {uri.AbsolutePath} failed because of rate limit");
|
log.Write(LogVerbosity.Debug, $"[{requestId}] Request {uri.AbsolutePath} failed because of rate limit");
|
||||||
return new WebCallResult<T>(null, null, null, limitResult.Error);
|
return new WebCallResult<T>(null, null, null, limitResult.Error);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (limitResult.Data > 0)
|
if (limitResult.Data > 0)
|
||||||
log.Write(LogVerbosity.Debug, $"Request {uri.AbsolutePath} was limited by {limitResult.Data}ms by {limiter.GetType().Name}");
|
log.Write(LogVerbosity.Debug, $"[{requestId}] Request {uri.AbsolutePath} was limited by {limitResult.Data}ms by {limiter.GetType().Name}");
|
||||||
}
|
}
|
||||||
|
|
||||||
string? paramString = null;
|
string? paramString = null;
|
||||||
if (method == HttpMethod.Post)
|
if (method == HttpMethod.Post)
|
||||||
paramString = " with request body " + request.Content;
|
paramString = " with request body " + request.Content;
|
||||||
|
|
||||||
log.Write(LogVerbosity.Debug, $"Sending {method}{(signed ? " signed" : "")} request to {request.Uri}{paramString ?? " "}{(apiProxy == null? "": $" via proxy {apiProxy.Host}")}");
|
log.Write(LogVerbosity.Debug, $"[{requestId}] Sending {method}{(signed ? " signed" : "")} request to {request.Uri}{paramString ?? " "}{(apiProxy == null ? "" : $" via proxy {apiProxy.Host}")}");
|
||||||
return await GetResponse<T>(request, cancellationToken).ConfigureAwait(false);
|
return await GetResponse<T>(request, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,21 +225,21 @@ namespace CryptoExchange.Net
|
|||||||
var data = await reader.ReadToEndAsync().ConfigureAwait(false);
|
var data = await reader.ReadToEndAsync().ConfigureAwait(false);
|
||||||
responseStream.Close();
|
responseStream.Close();
|
||||||
response.Close();
|
response.Close();
|
||||||
log.Write(LogVerbosity.Debug, $"Data received: {data}");
|
log.Write(LogVerbosity.Debug, $"[{request.RequestId}] Data received: {data}");
|
||||||
|
|
||||||
var parseResult = ValidateJson(data);
|
var parseResult = ValidateJson(data);
|
||||||
if (!parseResult.Success)
|
if (!parseResult.Success)
|
||||||
return WebCallResult<T>.CreateErrorResult(response.StatusCode, response.ResponseHeaders, new ServerError(data));
|
return WebCallResult<T>.CreateErrorResult(response.StatusCode, response.ResponseHeaders, new ServerError(data));
|
||||||
var error = await TryParseError(parseResult.Data);
|
var error = await TryParseError(parseResult.Data);
|
||||||
if(error != null)
|
if (error != null)
|
||||||
return WebCallResult<T>.CreateErrorResult(response.StatusCode, response.ResponseHeaders, error);
|
return WebCallResult<T>.CreateErrorResult(response.StatusCode, response.ResponseHeaders, error);
|
||||||
|
|
||||||
var deserializeResult = Deserialize<T>(parseResult.Data);
|
var deserializeResult = Deserialize<T>(parseResult.Data, null, null, request.RequestId);
|
||||||
return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, deserializeResult.Data, deserializeResult.Error);
|
return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, deserializeResult.Data, deserializeResult.Error);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var desResult = await Deserialize<T>(responseStream).ConfigureAwait(false);
|
var desResult = await Deserialize<T>(responseStream, null, request.RequestId).ConfigureAwait(false);
|
||||||
responseStream.Close();
|
responseStream.Close();
|
||||||
response.Close();
|
response.Close();
|
||||||
|
|
||||||
@ -249,31 +250,31 @@ namespace CryptoExchange.Net
|
|||||||
{
|
{
|
||||||
using var reader = new StreamReader(responseStream);
|
using var reader = new StreamReader(responseStream);
|
||||||
var data = await reader.ReadToEndAsync().ConfigureAwait(false);
|
var data = await reader.ReadToEndAsync().ConfigureAwait(false);
|
||||||
log.Write(LogVerbosity.Debug, $"Error received: {data}");
|
log.Write(LogVerbosity.Debug, $"[{request.RequestId}] Error received: {data}");
|
||||||
responseStream.Close();
|
responseStream.Close();
|
||||||
response.Close();
|
response.Close();
|
||||||
var parseResult = ValidateJson(data);
|
var parseResult = ValidateJson(data);
|
||||||
return new WebCallResult<T>(statusCode, headers, default, parseResult.Success ? ParseErrorResponse(parseResult.Data) :new ServerError(data));
|
return new WebCallResult<T>(statusCode, headers, default, parseResult.Success ? ParseErrorResponse(parseResult.Data) : new ServerError(data));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (HttpRequestException requestException)
|
catch (HttpRequestException requestException)
|
||||||
{
|
{
|
||||||
log.Write(LogVerbosity.Warning, "Request exception: " + requestException.Message);
|
log.Write(LogVerbosity.Warning, $"[{request.RequestId}] Request exception: " + requestException.Message);
|
||||||
return new WebCallResult<T>(null, null, default, new ServerError(requestException.Message));
|
return new WebCallResult<T>(null, null, default, new ServerError(requestException.Message));
|
||||||
}
|
}
|
||||||
catch (TaskCanceledException canceledException)
|
catch (TaskCanceledException canceledException)
|
||||||
{
|
{
|
||||||
if(canceledException.CancellationToken == cancellationToken)
|
if (canceledException.CancellationToken == cancellationToken)
|
||||||
{
|
{
|
||||||
// Cancellation token cancelled
|
// Cancellation token cancelled
|
||||||
log.Write(LogVerbosity.Warning, "Request cancel requested");
|
log.Write(LogVerbosity.Warning, $"[{request.RequestId}] Request cancel requested");
|
||||||
return new WebCallResult<T>(null, null, default, new CancellationRequestedError());
|
return new WebCallResult<T>(null, null, default, new CancellationRequestedError());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Request timed out
|
// Request timed out
|
||||||
log.Write(LogVerbosity.Warning, "Request timed out");
|
log.Write(LogVerbosity.Warning, $"[{request.RequestId}] Request timed out");
|
||||||
return new WebCallResult<T>(null, null, default, new WebError("Request timed out"));
|
return new WebCallResult<T>(null, null, default, new WebError($"[{request.RequestId}] Request timed out"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -297,22 +298,23 @@ namespace CryptoExchange.Net
|
|||||||
/// <param name="parameters">The parameters of the request</param>
|
/// <param name="parameters">The parameters of the request</param>
|
||||||
/// <param name="signed">Whether or not the request should be authenticated</param>
|
/// <param name="signed">Whether or not the request should be authenticated</param>
|
||||||
/// <param name="postPosition">Where the post parameters should be placed</param>
|
/// <param name="postPosition">Where the post parameters should be placed</param>
|
||||||
/// <param name="arraySerialization">How array paramters should be serialized</param>
|
/// <param name="arraySerialization">How array parameters should be serialized</param>
|
||||||
|
/// <param name="requestId">Unique id of a request</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
protected virtual IRequest ConstructRequest(Uri uri, HttpMethod method, Dictionary<string, object>? parameters, bool signed, PostParameters postPosition, ArrayParametersSerialization arraySerialization)
|
protected virtual IRequest ConstructRequest(Uri uri, HttpMethod method, Dictionary<string, object>? parameters, bool signed, PostParameters postPosition, ArrayParametersSerialization arraySerialization, int requestId)
|
||||||
{
|
{
|
||||||
if (parameters == null)
|
if (parameters == null)
|
||||||
parameters = new Dictionary<string, object>();
|
parameters = new Dictionary<string, object>();
|
||||||
|
|
||||||
var uriString = uri.ToString();
|
var uriString = uri.ToString();
|
||||||
if(authProvider != null)
|
if (authProvider != null)
|
||||||
parameters = authProvider.AddAuthenticationToParameters(uriString, method, parameters, signed, postPosition, arraySerialization);
|
parameters = authProvider.AddAuthenticationToParameters(uriString, method, parameters, signed, postPosition, arraySerialization);
|
||||||
|
|
||||||
if((method == HttpMethod.Get || method == HttpMethod.Delete || postPosition == PostParameters.InUri) && parameters?.Any() == true)
|
if ((method == HttpMethod.Get || method == HttpMethod.Delete || postPosition == PostParameters.InUri) && parameters?.Any() == true)
|
||||||
uriString += "?" + parameters.CreateParamString(true, arraySerialization);
|
uriString += "?" + parameters.CreateParamString(true, arraySerialization);
|
||||||
|
|
||||||
var contentType = requestBodyFormat == RequestBodyFormat.Json ? Constants.JsonContentHeader : Constants.FormContentHeader;
|
var contentType = requestBodyFormat == RequestBodyFormat.Json ? Constants.JsonContentHeader : Constants.FormContentHeader;
|
||||||
var request = RequestFactory.Create(method, uriString);
|
var request = RequestFactory.Create(method, uriString, requestId);
|
||||||
request.Accept = Constants.JsonContentHeader;
|
request.Accept = Constants.JsonContentHeader;
|
||||||
|
|
||||||
var headers = new Dictionary<string, string>();
|
var headers = new Dictionary<string, string>();
|
||||||
@ -324,7 +326,7 @@ namespace CryptoExchange.Net
|
|||||||
|
|
||||||
if ((method == HttpMethod.Post || method == HttpMethod.Put) && postPosition != PostParameters.InUri)
|
if ((method == HttpMethod.Post || method == HttpMethod.Put) && postPosition != PostParameters.InUri)
|
||||||
{
|
{
|
||||||
if(parameters?.Any() == true)
|
if (parameters?.Any() == true)
|
||||||
WriteParamBody(request, parameters, contentType);
|
WriteParamBody(request, parameters, contentType);
|
||||||
else
|
else
|
||||||
request.SetContent(requestBodyEmptyContent, contentType);
|
request.SetContent(requestBodyEmptyContent, contentType);
|
||||||
@ -346,7 +348,7 @@ namespace CryptoExchange.Net
|
|||||||
var stringData = JsonConvert.SerializeObject(parameters.OrderBy(p => p.Key).ToDictionary(p => p.Key, p => p.Value));
|
var stringData = JsonConvert.SerializeObject(parameters.OrderBy(p => p.Key).ToDictionary(p => p.Key, p => p.Value));
|
||||||
request.SetContent(stringData, contentType);
|
request.SetContent(stringData, contentType);
|
||||||
}
|
}
|
||||||
else if(requestBodyFormat == RequestBodyFormat.FormData)
|
else if (requestBodyFormat == RequestBodyFormat.FormData)
|
||||||
{
|
{
|
||||||
var formData = HttpUtility.ParseQueryString(string.Empty);
|
var formData = HttpUtility.ParseQueryString(string.Empty);
|
||||||
foreach (var kvp in parameters.OrderBy(p => p.Key))
|
foreach (var kvp in parameters.OrderBy(p => p.Key))
|
||||||
@ -354,7 +356,7 @@ namespace CryptoExchange.Net
|
|||||||
if (kvp.Value.GetType().IsArray)
|
if (kvp.Value.GetType().IsArray)
|
||||||
{
|
{
|
||||||
var array = (Array)kvp.Value;
|
var array = (Array)kvp.Value;
|
||||||
foreach(var value in array)
|
foreach (var value in array)
|
||||||
formData.Add(kvp.Key, value.ToString());
|
formData.Add(kvp.Key, value.ToString());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
Loading…
x
Reference in New Issue
Block a user