mirror of
https://github.com/JKorf/CryptoExchange.Net
synced 2025-09-05 15:11:42 +00:00
Compare commits
6 Commits
CryptoExch
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
b8c6d55156 | ||
|
d9a5481db2 | ||
|
6a8bb42c0e | ||
|
2445f001ab | ||
|
c84fa9ac32 | ||
|
d44a11c44e |
@ -6,9 +6,9 @@
|
||||
<PackageId>CryptoExchange.Net.Protobuf</PackageId>
|
||||
<Authors>JKorf</Authors>
|
||||
<Description>Protobuf support for CryptoExchange.Net</Description>
|
||||
<PackageVersion>9.6.0</PackageVersion>
|
||||
<AssemblyVersion>9.6.0</AssemblyVersion>
|
||||
<FileVersion>9.6.0</FileVersion>
|
||||
<PackageVersion>9.7.0</PackageVersion>
|
||||
<AssemblyVersion>9.7.0</AssemblyVersion>
|
||||
<FileVersion>9.7.0</FileVersion>
|
||||
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
|
||||
<PackageTags>CryptoExchange;CryptoExchange.Net</PackageTags>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
@ -41,7 +41,7 @@
|
||||
<DocumentationFile>CryptoExchange.Net.Protobuf.xml</DocumentationFile>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CryptoExchange.Net" Version="9.6.0" />
|
||||
<PackageReference Include="CryptoExchange.Net" Version="9.7.0" />
|
||||
<PackageReference Include="protobuf-net" Version="3.2.56" />
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -5,6 +5,9 @@
|
||||
Protobuf support for CryptoExchange.Net.
|
||||
|
||||
## Release notes
|
||||
* Version 9.7.0 - 01 Sep 2025
|
||||
* Updated CryptoExchange.Net version to 9.7.0, see https://github.com/JKorf/CryptoExchange.Net/releases/
|
||||
|
||||
* Version 9.6.0 - 25 Aug 2025
|
||||
* Updated CryptoExchange.Net version to 9.6.0
|
||||
|
||||
|
@ -5,6 +5,7 @@ using NUnit.Framework.Legacy;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
@ -113,6 +114,7 @@ namespace CryptoExchange.Net.UnitTests
|
||||
{
|
||||
var result = new WebCallResult<TestObjectResult>(
|
||||
System.Net.HttpStatusCode.OK,
|
||||
HttpVersion.Version11,
|
||||
new KeyValuePair<string, string[]>[0],
|
||||
TimeSpan.FromSeconds(1),
|
||||
null,
|
||||
@ -143,6 +145,7 @@ namespace CryptoExchange.Net.UnitTests
|
||||
{
|
||||
var result = new WebCallResult<TestObjectResult>(
|
||||
System.Net.HttpStatusCode.OK,
|
||||
HttpVersion.Version11,
|
||||
new KeyValuePair<string, string[]>[0],
|
||||
TimeSpan.FromSeconds(1),
|
||||
null,
|
||||
|
@ -60,8 +60,8 @@ namespace CryptoExchange.Net.UnitTests.TestImplementations
|
||||
request.Setup(c => c.GetHeaders()).Returns(() => headers.ToArray());
|
||||
|
||||
var factory = Mock.Get(Api1.RequestFactory);
|
||||
factory.Setup(c => c.Create(It.IsAny<HttpMethod>(), It.IsAny<Uri>(), It.IsAny<int>()))
|
||||
.Callback<HttpMethod, Uri, int>((method, uri, id) =>
|
||||
factory.Setup(c => c.Create(It.IsAny<Version>(), It.IsAny<HttpMethod>(), It.IsAny<Uri>(), It.IsAny<int>()))
|
||||
.Callback<Version, HttpMethod, Uri, int>((version, method, uri, id) =>
|
||||
{
|
||||
request.Setup(a => a.Uri).Returns(uri);
|
||||
request.Setup(a => a.Method).Returns(method);
|
||||
@ -69,8 +69,8 @@ namespace CryptoExchange.Net.UnitTests.TestImplementations
|
||||
.Returns(request.Object);
|
||||
|
||||
factory = Mock.Get(Api2.RequestFactory);
|
||||
factory.Setup(c => c.Create(It.IsAny<HttpMethod>(), It.IsAny<Uri>(), It.IsAny<int>()))
|
||||
.Callback<HttpMethod, Uri, int>((method, uri, id) =>
|
||||
factory.Setup(c => c.Create(It.IsAny<Version>(), It.IsAny<HttpMethod>(), It.IsAny<Uri>(), It.IsAny<int>()))
|
||||
.Callback<Version, HttpMethod, Uri, int>((version, method, uri, id) =>
|
||||
{
|
||||
request.Setup(a => a.Uri).Returns(uri);
|
||||
request.Setup(a => a.Method).Returns(method);
|
||||
@ -90,12 +90,12 @@ namespace CryptoExchange.Net.UnitTests.TestImplementations
|
||||
request.Setup(c => c.GetResponseAsync(It.IsAny<CancellationToken>())).Throws(we);
|
||||
|
||||
var factory = Mock.Get(Api1.RequestFactory);
|
||||
factory.Setup(c => c.Create(It.IsAny<HttpMethod>(), It.IsAny<Uri>(), It.IsAny<int>()))
|
||||
factory.Setup(c => c.Create(It.IsAny<Version>(), It.IsAny<HttpMethod>(), It.IsAny<Uri>(), It.IsAny<int>()))
|
||||
.Returns(request.Object);
|
||||
|
||||
|
||||
factory = Mock.Get(Api2.RequestFactory);
|
||||
factory.Setup(c => c.Create(It.IsAny<HttpMethod>(), It.IsAny<Uri>(), It.IsAny<int>()))
|
||||
factory.Setup(c => c.Create(It.IsAny<Version>(), It.IsAny<HttpMethod>(), It.IsAny<Uri>(), It.IsAny<int>()))
|
||||
.Returns(request.Object);
|
||||
}
|
||||
|
||||
@ -118,13 +118,13 @@ namespace CryptoExchange.Net.UnitTests.TestImplementations
|
||||
request.Setup(c => c.GetHeaders()).Returns(headers.ToArray());
|
||||
|
||||
var factory = Mock.Get(Api1.RequestFactory);
|
||||
factory.Setup(c => c.Create(It.IsAny<HttpMethod>(), It.IsAny<Uri>(), It.IsAny<int>()))
|
||||
.Callback<HttpMethod, Uri, int>((method, uri, id) => request.Setup(a => a.Uri).Returns(uri))
|
||||
factory.Setup(c => c.Create(It.IsAny<Version>(), It.IsAny<HttpMethod>(), It.IsAny<Uri>(), It.IsAny<int>()))
|
||||
.Callback<Version, HttpMethod, Uri, int>((version, method, uri, id) => request.Setup(a => a.Uri).Returns(uri))
|
||||
.Returns(request.Object);
|
||||
|
||||
factory = Mock.Get(Api2.RequestFactory);
|
||||
factory.Setup(c => c.Create(It.IsAny<HttpMethod>(), It.IsAny<Uri>(), It.IsAny<int>()))
|
||||
.Callback<HttpMethod, Uri, int>((method, uri, id) => request.Setup(a => a.Uri).Returns(uri))
|
||||
factory.Setup(c => c.Create(It.IsAny<Version>(), It.IsAny<HttpMethod>(), It.IsAny<Uri>(), It.IsAny<int>()))
|
||||
.Callback<Version, HttpMethod, Uri, int>((version, method, uri, id) => request.Setup(a => a.Uri).Returns(uri))
|
||||
.Returns(request.Object);
|
||||
}
|
||||
}
|
||||
|
@ -106,7 +106,7 @@ namespace CryptoExchange.Net.Clients
|
||||
options,
|
||||
apiOptions)
|
||||
{
|
||||
RequestFactory.Configure(options.Proxy, options.RequestTimeout, httpClient);
|
||||
RequestFactory.Configure(options, httpClient);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -388,7 +388,7 @@ namespace CryptoExchange.Net.Clients
|
||||
queryString = $"?{queryString}";
|
||||
|
||||
var uri = new Uri(baseAddress.AppendPath(definition.Path) + queryString);
|
||||
var request = RequestFactory.Create(definition.Method, uri, requestId);
|
||||
var request = RequestFactory.Create(ClientOptions.HttpVersion, definition.Method, uri, requestId);
|
||||
request.Accept = Constants.JsonContentHeader;
|
||||
|
||||
foreach (var header in requestConfiguration.Headers)
|
||||
@ -443,9 +443,6 @@ namespace CryptoExchange.Net.Clients
|
||||
{
|
||||
response = await request.GetResponseAsync(cancellationToken).ConfigureAwait(false);
|
||||
sw.Stop();
|
||||
var statusCode = response.StatusCode;
|
||||
var headers = response.ResponseHeaders;
|
||||
var responseLength = response.ContentLength;
|
||||
responseStream = await response.GetResponseStreamAsync().ConfigureAwait(false);
|
||||
var outputOriginalData = ApiOptions.OutputOriginalData ?? ClientOptions.OutputOriginalData;
|
||||
|
||||
@ -475,18 +472,18 @@ namespace CryptoExchange.Net.Clients
|
||||
if (error.Code == null || error.Code == 0)
|
||||
error.Code = (int)response.StatusCode;
|
||||
|
||||
return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, sw.Elapsed, responseLength, OutputOriginalData ? accessor.GetOriginalString() : null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, error!);
|
||||
return new WebCallResult<T>(response.StatusCode, response.HttpVersion, response.ResponseHeaders, sw.Elapsed, response.ContentLength, OutputOriginalData ? accessor.GetOriginalString() : null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, error!);
|
||||
}
|
||||
|
||||
var valid = await accessor.Read(responseStream, outputOriginalData).ConfigureAwait(false);
|
||||
if (typeof(T) == typeof(object))
|
||||
// Success status code and expected empty response, assume it's correct
|
||||
return new WebCallResult<T>(statusCode, headers, sw.Elapsed, 0, accessor.OriginalDataAvailable ? accessor.GetOriginalString() : "[Data only available when OutputOriginal = true in client options]", request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, null);
|
||||
return new WebCallResult<T>(response.StatusCode, response.HttpVersion, response.ResponseHeaders, sw.Elapsed, 0, accessor.OriginalDataAvailable ? accessor.GetOriginalString() : "[Data only available when OutputOriginal = true in client options]", request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, null);
|
||||
|
||||
if (!valid)
|
||||
{
|
||||
// Invalid json
|
||||
return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, sw.Elapsed, responseLength, OutputOriginalData ? accessor.GetOriginalString() : null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, valid.Error);
|
||||
return new WebCallResult<T>(response.StatusCode, response.HttpVersion, response.ResponseHeaders, sw.Elapsed, response.ContentLength, OutputOriginalData ? accessor.GetOriginalString() : null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, valid.Error);
|
||||
}
|
||||
|
||||
// Json response received
|
||||
@ -503,33 +500,55 @@ namespace CryptoExchange.Net.Clients
|
||||
}
|
||||
|
||||
// Success status code, but TryParseError determined it was an error response
|
||||
return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, sw.Elapsed, responseLength, OutputOriginalData ? accessor.GetOriginalString() : null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, parsedError);
|
||||
return new WebCallResult<T>(response.StatusCode, response.HttpVersion, response.ResponseHeaders, sw.Elapsed, response.ContentLength, OutputOriginalData ? accessor.GetOriginalString() : null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, parsedError);
|
||||
}
|
||||
|
||||
var deserializeResult = accessor.Deserialize<T>();
|
||||
return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, sw.Elapsed, responseLength, OutputOriginalData ? accessor.GetOriginalString() : null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, deserializeResult.Data, deserializeResult.Error);
|
||||
return new WebCallResult<T>(response.StatusCode, response.HttpVersion, response.ResponseHeaders, sw.Elapsed, response.ContentLength, OutputOriginalData ? accessor.GetOriginalString() : null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, deserializeResult.Data, deserializeResult.Error);
|
||||
}
|
||||
catch (HttpRequestException requestException)
|
||||
{
|
||||
// Request exception, can't reach server for instance
|
||||
var error = new WebError(requestException.Message, requestException);
|
||||
return new WebCallResult<T>(null, null, sw.Elapsed, null, null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, error);
|
||||
return new WebCallResult<T>(null, null, null, sw.Elapsed, null, null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, error);
|
||||
}
|
||||
catch (OperationCanceledException canceledException)
|
||||
{
|
||||
if (cancellationToken != default && canceledException.CancellationToken == cancellationToken)
|
||||
{
|
||||
// Cancellation token canceled by caller
|
||||
return new WebCallResult<T>(null, null, sw.Elapsed, null, null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, new CancellationRequestedError(canceledException));
|
||||
return new WebCallResult<T>(null, null, null, sw.Elapsed, null, null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, new CancellationRequestedError(canceledException));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Request timed out
|
||||
var error = new WebError($"Request timed out", exception: canceledException);
|
||||
error.ErrorType = ErrorType.Timeout;
|
||||
return new WebCallResult<T>(null, null, sw.Elapsed, null, null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, error);
|
||||
return new WebCallResult<T>(null, null, null, sw.Elapsed, null, null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, 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 {request.HttpVersion}: " + argumentException.Message);
|
||||
return new WebCallResult<T>(null, null, null, sw.Elapsed, null, null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, 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 {request.HttpVersion}: " + notSupportedException.Message);
|
||||
return new WebCallResult<T>(null, null, null, sw.Elapsed, null, null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, error);
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
accessor?.Clear();
|
||||
@ -674,21 +693,21 @@ namespace CryptoExchange.Net.Clients
|
||||
{
|
||||
base.SetOptions(options);
|
||||
|
||||
RequestFactory.UpdateSettings(options.Proxy, options.RequestTimeout ?? ClientOptions.RequestTimeout);
|
||||
RequestFactory.UpdateSettings(options.Proxy, options.RequestTimeout ?? ClientOptions.RequestTimeout, ClientOptions.HttpKeepAliveInterval);
|
||||
}
|
||||
|
||||
internal async Task<WebCallResult<bool>> SyncTimeAsync()
|
||||
{
|
||||
var timeSyncParams = GetTimeSyncInfo();
|
||||
if (timeSyncParams == null)
|
||||
return new WebCallResult<bool>(null, null, null, null, null, null, null, null, null, null, ResultDataSource.Server, true, null);
|
||||
return new WebCallResult<bool>(null, null, null, null, null, null, null, null, null, null, null, ResultDataSource.Server, true, null);
|
||||
|
||||
if (await timeSyncParams.TimeSyncState.Semaphore.WaitAsync(0).ConfigureAwait(false))
|
||||
{
|
||||
if (!timeSyncParams.SyncTime || DateTime.UtcNow - timeSyncParams.TimeSyncState.LastSyncTime < timeSyncParams.RecalculationInterval)
|
||||
{
|
||||
timeSyncParams.TimeSyncState.Semaphore.Release();
|
||||
return new WebCallResult<bool>(null, null, null, null, null, null, null, null, null, null, ResultDataSource.Server, true, null);
|
||||
return new WebCallResult<bool>(null, null, null, null, null, null, null, null, null, null, null, ResultDataSource.Server, true, null);
|
||||
}
|
||||
|
||||
var localTime = DateTime.UtcNow;
|
||||
@ -717,7 +736,7 @@ namespace CryptoExchange.Net.Clients
|
||||
timeSyncParams.TimeSyncState.Semaphore.Release();
|
||||
}
|
||||
|
||||
return new WebCallResult<bool>(null, null, null, null, null, null, null, null, null, null, ResultDataSource.Server, true, null);
|
||||
return new WebCallResult<bool>(null, null, null, null, null, null, null, null, null, null, null, ResultDataSource.Server, true, null);
|
||||
}
|
||||
|
||||
private bool ShouldCache(RequestDefinition definition)
|
||||
|
@ -6,9 +6,9 @@
|
||||
<PackageId>CryptoExchange.Net</PackageId>
|
||||
<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>
|
||||
<PackageVersion>9.6.0</PackageVersion>
|
||||
<AssemblyVersion>9.6.0</AssemblyVersion>
|
||||
<FileVersion>9.6.0</FileVersion>
|
||||
<PackageVersion>9.7.0</PackageVersion>
|
||||
<AssemblyVersion>9.7.0</AssemblyVersion>
|
||||
<FileVersion>9.7.0</FileVersion>
|
||||
<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>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
|
@ -28,6 +28,10 @@ namespace CryptoExchange.Net.Interfaces
|
||||
/// </summary>
|
||||
Uri Uri { get; }
|
||||
/// <summary>
|
||||
/// HTTP protocol version
|
||||
/// </summary>
|
||||
Version HttpVersion { get; }
|
||||
/// <summary>
|
||||
/// internal request id for tracing
|
||||
/// </summary>
|
||||
int RequestId { get; }
|
||||
|
@ -1,4 +1,5 @@
|
||||
using CryptoExchange.Net.Objects;
|
||||
using CryptoExchange.Net.Objects.Options;
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
|
||||
@ -12,25 +13,21 @@ namespace CryptoExchange.Net.Interfaces
|
||||
/// <summary>
|
||||
/// Create a request for an uri
|
||||
/// </summary>
|
||||
/// <param name="method"></param>
|
||||
/// <param name="uri"></param>
|
||||
/// <param name="requestId"></param>
|
||||
/// <returns></returns>
|
||||
IRequest Create(HttpMethod method, Uri uri, int requestId);
|
||||
IRequest Create(Version httpRequestVersion, HttpMethod method, Uri uri, int requestId);
|
||||
|
||||
/// <summary>
|
||||
/// Configure the requests created by this factory
|
||||
/// </summary>
|
||||
/// <param name="requestTimeout">Request timeout to use</param>
|
||||
/// <param name="options">Rest client options</param>
|
||||
/// <param name="httpClient">Optional shared http client instance</param>
|
||||
/// <param name="proxy">Optional proxy to use when no http client is provided</param>
|
||||
void Configure(ApiProxy? proxy, TimeSpan requestTimeout, HttpClient? httpClient = null);
|
||||
void Configure(RestExchangeOptions options, HttpClient? httpClient = null);
|
||||
|
||||
/// <summary>
|
||||
/// Update settings
|
||||
/// </summary>
|
||||
/// <param name="proxy">Proxy to use</param>
|
||||
/// <param name="requestTimeout">Request timeout to use</param>
|
||||
void UpdateSettings(ApiProxy? proxy, TimeSpan requestTimeout);
|
||||
/// <param name="httpKeepAliveInterval">Http client keep alive interval</param>
|
||||
void UpdateSettings(ApiProxy? proxy, TimeSpan requestTimeout, TimeSpan? httpKeepAliveInterval);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
@ -15,6 +16,11 @@ namespace CryptoExchange.Net.Interfaces
|
||||
/// </summary>
|
||||
HttpStatusCode StatusCode { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Http protocol version
|
||||
/// </summary>
|
||||
Version HttpVersion { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the status code indicates a success status
|
||||
/// </summary>
|
||||
|
@ -1,5 +1,8 @@
|
||||
using System;
|
||||
using CryptoExchange.Net.Objects;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
|
||||
namespace CryptoExchange.Net
|
||||
@ -43,5 +46,58 @@ namespace CryptoExchange.Net
|
||||
|
||||
return clientOrderId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new HttpMessageHandler instance
|
||||
/// </summary>
|
||||
public static HttpMessageHandler CreateHttpClientMessageHandler(ApiProxy? proxy, TimeSpan? keepAliveInterval)
|
||||
{
|
||||
#if NET5_0_OR_GREATER
|
||||
var socketHandler = new SocketsHttpHandler();
|
||||
try
|
||||
{
|
||||
if (keepAliveInterval != null && keepAliveInterval != TimeSpan.Zero)
|
||||
{
|
||||
socketHandler.KeepAlivePingPolicy = HttpKeepAlivePingPolicy.Always;
|
||||
socketHandler.KeepAlivePingDelay = keepAliveInterval.Value;
|
||||
socketHandler.KeepAlivePingTimeout = TimeSpan.FromSeconds(10);
|
||||
}
|
||||
|
||||
socketHandler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
|
||||
socketHandler.DefaultProxyCredentials = CredentialCache.DefaultCredentials;
|
||||
}
|
||||
catch (PlatformNotSupportedException) { }
|
||||
catch (NotImplementedException) { } // Mono runtime throws NotImplementedException
|
||||
|
||||
if (proxy != null)
|
||||
{
|
||||
socketHandler.Proxy = new WebProxy
|
||||
{
|
||||
Address = new Uri($"{proxy.Host}:{proxy.Port}"),
|
||||
Credentials = proxy.Password == null ? null : new NetworkCredential(proxy.Login, proxy.Password)
|
||||
};
|
||||
}
|
||||
return socketHandler;
|
||||
#else
|
||||
var httpHandler = new HttpClientHandler();
|
||||
try
|
||||
{
|
||||
httpHandler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
|
||||
httpHandler.DefaultProxyCredentials = CredentialCache.DefaultCredentials;
|
||||
}
|
||||
catch (PlatformNotSupportedException) { }
|
||||
catch (NotImplementedException) { } // Mono runtime throws NotImplementedException
|
||||
|
||||
if (proxy != null)
|
||||
{
|
||||
httpHandler.Proxy = new WebProxy
|
||||
{
|
||||
Address = new Uri($"{proxy.Host}:{proxy.Port}"),
|
||||
Credentials = proxy.Password == null ? null : new NetworkCredential(proxy.Login, proxy.Password)
|
||||
};
|
||||
}
|
||||
return httpHandler;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -205,6 +205,11 @@ namespace CryptoExchange.Net.Objects
|
||||
/// The request http method
|
||||
/// </summary>
|
||||
public HttpMethod? RequestMethod { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// HTTP protocol version
|
||||
/// </summary>
|
||||
public Version? HttpVersion { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The headers sent with the request
|
||||
@ -251,6 +256,7 @@ namespace CryptoExchange.Net.Objects
|
||||
/// </summary>
|
||||
public WebCallResult(
|
||||
HttpStatusCode? code,
|
||||
Version? httpVersion,
|
||||
KeyValuePair<string, string[]>[]? responseHeaders,
|
||||
TimeSpan? responseTime,
|
||||
string? originalData,
|
||||
@ -262,6 +268,7 @@ namespace CryptoExchange.Net.Objects
|
||||
Error? error) : base(error)
|
||||
{
|
||||
ResponseStatusCode = code;
|
||||
HttpVersion = httpVersion;
|
||||
ResponseHeaders = responseHeaders;
|
||||
ResponseTime = responseTime;
|
||||
RequestId = requestId;
|
||||
@ -286,7 +293,7 @@ namespace CryptoExchange.Net.Objects
|
||||
/// <returns></returns>
|
||||
public WebCallResult AsError(Error error)
|
||||
{
|
||||
return new WebCallResult(ResponseStatusCode, ResponseHeaders, ResponseTime, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, error);
|
||||
return new WebCallResult(ResponseStatusCode, HttpVersion, ResponseHeaders, ResponseTime, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, error);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -297,7 +304,7 @@ namespace CryptoExchange.Net.Objects
|
||||
/// <returns></returns>
|
||||
public WebCallResult<K> As<K>([AllowNull] K data)
|
||||
{
|
||||
return new WebCallResult<K>(ResponseStatusCode, ResponseHeaders, ResponseTime, 0, null, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, ResultDataSource.Server, data, Error);
|
||||
return new WebCallResult<K>(ResponseStatusCode, HttpVersion, ResponseHeaders, ResponseTime, 0, null, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, ResultDataSource.Server, data, Error);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -334,7 +341,7 @@ namespace CryptoExchange.Net.Objects
|
||||
/// <returns></returns>
|
||||
public WebCallResult<K> AsError<K>(Error error)
|
||||
{
|
||||
return new WebCallResult<K>(ResponseStatusCode, ResponseHeaders, ResponseTime, 0, null, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, ResultDataSource.Server, default, error);
|
||||
return new WebCallResult<K>(ResponseStatusCode, HttpVersion, ResponseHeaders, ResponseTime, 0, null, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, ResultDataSource.Server, default, error);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@ -355,6 +362,11 @@ namespace CryptoExchange.Net.Objects
|
||||
/// </summary>
|
||||
public HttpMethod? RequestMethod { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// HTTP protocol version
|
||||
/// </summary>
|
||||
public Version? HttpVersion { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The headers sent with the request
|
||||
/// </summary>
|
||||
@ -403,21 +415,9 @@ namespace CryptoExchange.Net.Objects
|
||||
/// <summary>
|
||||
/// Create a new result
|
||||
/// </summary>
|
||||
/// <param name="code"></param>
|
||||
/// <param name="responseHeaders"></param>
|
||||
/// <param name="responseTime"></param>
|
||||
/// <param name="responseLength"></param>
|
||||
/// <param name="originalData"></param>
|
||||
/// <param name="requestId"></param>
|
||||
/// <param name="requestUrl"></param>
|
||||
/// <param name="requestBody"></param>
|
||||
/// <param name="requestMethod"></param>
|
||||
/// <param name="requestHeaders"></param>
|
||||
/// <param name="dataSource"></param>
|
||||
/// <param name="data"></param>
|
||||
/// <param name="error"></param>
|
||||
public WebCallResult(
|
||||
HttpStatusCode? code,
|
||||
Version? httpVersion,
|
||||
KeyValuePair<string, string[]>[]? responseHeaders,
|
||||
TimeSpan? responseTime,
|
||||
long? responseLength,
|
||||
@ -431,6 +431,7 @@ namespace CryptoExchange.Net.Objects
|
||||
[AllowNull] T data,
|
||||
Error? error) : base(data, originalData, error)
|
||||
{
|
||||
HttpVersion = httpVersion;
|
||||
ResponseStatusCode = code;
|
||||
ResponseHeaders = responseHeaders;
|
||||
ResponseTime = responseTime;
|
||||
@ -450,7 +451,7 @@ namespace CryptoExchange.Net.Objects
|
||||
/// <returns></returns>
|
||||
public new WebCallResult AsDataless()
|
||||
{
|
||||
return new WebCallResult(ResponseStatusCode, ResponseHeaders, ResponseTime, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, Error);
|
||||
return new WebCallResult(ResponseStatusCode, HttpVersion, ResponseHeaders, ResponseTime, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, Error);
|
||||
}
|
||||
/// <summary>
|
||||
/// Copy as a dataless result
|
||||
@ -458,14 +459,14 @@ namespace CryptoExchange.Net.Objects
|
||||
/// <returns></returns>
|
||||
public new WebCallResult AsDatalessError(Error error)
|
||||
{
|
||||
return new WebCallResult(ResponseStatusCode, ResponseHeaders, ResponseTime, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, error);
|
||||
return new WebCallResult(ResponseStatusCode, HttpVersion, ResponseHeaders, ResponseTime, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, error);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new error result
|
||||
/// </summary>
|
||||
/// <param name="error">The error</param>
|
||||
public WebCallResult(Error? error) : this(null, null, null, null, null, null, null, null, null, null, ResultDataSource.Server, default, error) { }
|
||||
public WebCallResult(Error? error) : this(null, null, null, null, null, null, null, null, null, null, null, ResultDataSource.Server, default, error) { }
|
||||
|
||||
/// <summary>
|
||||
/// Copy the WebCallResult to a new data type
|
||||
@ -475,7 +476,7 @@ namespace CryptoExchange.Net.Objects
|
||||
/// <returns></returns>
|
||||
public new WebCallResult<K> As<K>([AllowNull] K data)
|
||||
{
|
||||
return new WebCallResult<K>(ResponseStatusCode, ResponseHeaders, ResponseTime, ResponseLength, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, DataSource, data, Error);
|
||||
return new WebCallResult<K>(ResponseStatusCode, HttpVersion, ResponseHeaders, ResponseTime, ResponseLength, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, DataSource, data, Error);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -486,7 +487,7 @@ namespace CryptoExchange.Net.Objects
|
||||
/// <returns></returns>
|
||||
public new WebCallResult<K> AsError<K>(Error error)
|
||||
{
|
||||
return new WebCallResult<K>(ResponseStatusCode, ResponseHeaders, ResponseTime, ResponseLength, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, DataSource, default, error);
|
||||
return new WebCallResult<K>(ResponseStatusCode, HttpVersion, ResponseHeaders, ResponseTime, ResponseLength, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, DataSource, default, error);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -498,7 +499,7 @@ namespace CryptoExchange.Net.Objects
|
||||
/// <returns></returns>
|
||||
public new WebCallResult<K> AsErrorWithData<K>(Error error, K data)
|
||||
{
|
||||
return new WebCallResult<K>(ResponseStatusCode, ResponseHeaders, ResponseTime, ResponseLength, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, DataSource, data, error);
|
||||
return new WebCallResult<K>(ResponseStatusCode, HttpVersion, ResponseHeaders, ResponseTime, ResponseLength, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, DataSource, data, error);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -569,7 +570,7 @@ namespace CryptoExchange.Net.Objects
|
||||
/// <returns></returns>
|
||||
internal WebCallResult<T> Cached()
|
||||
{
|
||||
return new WebCallResult<T>(ResponseStatusCode, ResponseHeaders, ResponseTime, ResponseLength, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, ResultDataSource.Cache, Data, Error);
|
||||
return new WebCallResult<T>(ResponseStatusCode, HttpVersion, ResponseHeaders, ResponseTime, ResponseLength, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, ResultDataSource.Cache, Data, Error);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
@ -1,5 +1,7 @@
|
||||
using CryptoExchange.Net.Authentication;
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
|
||||
namespace CryptoExchange.Net.Objects.Options
|
||||
{
|
||||
@ -28,6 +30,20 @@ namespace CryptoExchange.Net.Objects.Options
|
||||
/// </summary>
|
||||
public TimeSpan CachingMaxAge { get; set; } = TimeSpan.FromSeconds(5);
|
||||
|
||||
/// <summary>
|
||||
/// The HTTP protocol version to use, typically 2.0 or 1.1
|
||||
/// </summary>
|
||||
public Version HttpVersion { get; set; }
|
||||
#if NET5_0_OR_GREATER
|
||||
= new Version(2, 0);
|
||||
#else
|
||||
= new Version(1, 1);
|
||||
#endif
|
||||
/// <summary>
|
||||
/// Http client keep alive interval for keeping connections open
|
||||
/// </summary>
|
||||
public TimeSpan? HttpKeepAliveInterval { get; set; } = TimeSpan.FromSeconds(15);
|
||||
|
||||
/// <summary>
|
||||
/// Set the values of this options on the target options
|
||||
/// </summary>
|
||||
@ -43,6 +59,8 @@ namespace CryptoExchange.Net.Objects.Options
|
||||
item.RateLimitingBehaviour = RateLimitingBehaviour;
|
||||
item.CachingEnabled = CachingEnabled;
|
||||
item.CachingMaxAge = CachingMaxAge;
|
||||
item.HttpVersion = HttpVersion;
|
||||
item.HttpKeepAliveInterval = HttpKeepAliveInterval;
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
@ -50,6 +50,9 @@ namespace CryptoExchange.Net.Requests
|
||||
/// <inheritdoc />
|
||||
public Uri Uri => _request.RequestUri!;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Version HttpVersion => _request.Version!;
|
||||
|
||||
/// <inheritdoc />
|
||||
public int RequestId { get; }
|
||||
|
||||
@ -81,7 +84,9 @@ namespace CryptoExchange.Net.Requests
|
||||
/// <inheritdoc />
|
||||
public async Task<IResponse> GetResponseAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return new Response(await _httpClient.SendAsync(_request, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false));
|
||||
var response = await _httpClient.SendAsync(_request, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
return new Response(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ using System.Net;
|
||||
using System.Net.Http;
|
||||
using CryptoExchange.Net.Interfaces;
|
||||
using CryptoExchange.Net.Objects;
|
||||
using CryptoExchange.Net.Objects.Options;
|
||||
|
||||
namespace CryptoExchange.Net.Requests
|
||||
{
|
||||
@ -14,54 +15,43 @@ namespace CryptoExchange.Net.Requests
|
||||
private HttpClient? _httpClient;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Configure(ApiProxy? proxy, TimeSpan requestTimeout, HttpClient? client = null)
|
||||
public void Configure(RestExchangeOptions options, HttpClient? client = null)
|
||||
{
|
||||
if (client == null)
|
||||
client = CreateClient(proxy, requestTimeout);
|
||||
client = CreateClient(options.Proxy, options.RequestTimeout, options.HttpKeepAliveInterval);
|
||||
|
||||
_httpClient = client;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IRequest Create(HttpMethod method, Uri uri, int requestId)
|
||||
public IRequest Create(Version httpRequestVersion, HttpMethod method, Uri uri, int requestId)
|
||||
{
|
||||
if (_httpClient == null)
|
||||
throw new InvalidOperationException("Cant create request before configuring http client");
|
||||
|
||||
return new Request(new HttpRequestMessage(method, uri), _httpClient, requestId);
|
||||
var requestMessage = new HttpRequestMessage(method, uri);
|
||||
requestMessage.Version = httpRequestVersion;
|
||||
#if NET5_0_OR_GREATER
|
||||
requestMessage.VersionPolicy = HttpVersionPolicy.RequestVersionOrLower;
|
||||
#endif
|
||||
return new Request(requestMessage, _httpClient, requestId);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void UpdateSettings(ApiProxy? proxy, TimeSpan requestTimeout)
|
||||
public void UpdateSettings(ApiProxy? proxy, TimeSpan requestTimeout, TimeSpan? httpKeepAliveInterval)
|
||||
{
|
||||
_httpClient = CreateClient(proxy, requestTimeout);
|
||||
_httpClient = CreateClient(proxy, requestTimeout, httpKeepAliveInterval);
|
||||
}
|
||||
|
||||
private static HttpClient CreateClient(ApiProxy? proxy, TimeSpan requestTimeout)
|
||||
private static HttpClient CreateClient(ApiProxy? proxy, TimeSpan requestTimeout, TimeSpan? httpKeepAliveInterval)
|
||||
{
|
||||
var handler = new HttpClientHandler();
|
||||
try
|
||||
{
|
||||
handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
|
||||
handler.DefaultProxyCredentials = CredentialCache.DefaultCredentials;
|
||||
}
|
||||
catch (PlatformNotSupportedException) { }
|
||||
catch (NotImplementedException) { } // Mono runtime throws NotImplementedException
|
||||
|
||||
if (proxy != null)
|
||||
{
|
||||
handler.Proxy = new WebProxy
|
||||
{
|
||||
Address = new Uri($"{proxy.Host}:{proxy.Port}"),
|
||||
Credentials = proxy.Password == null ? null : new NetworkCredential(proxy.Login, proxy.Password)
|
||||
};
|
||||
}
|
||||
|
||||
var handler = LibraryHelpers.CreateHttpClientMessageHandler(proxy, httpKeepAliveInterval);
|
||||
var client = new HttpClient(handler)
|
||||
{
|
||||
Timeout = requestTimeout
|
||||
Timeout = requestTimeout
|
||||
};
|
||||
return client;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
@ -18,6 +19,9 @@ namespace CryptoExchange.Net.Requests
|
||||
/// <inheritdoc />
|
||||
public HttpStatusCode StatusCode => _response.StatusCode;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Version HttpVersion => _response.Version;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsSuccessStatusCode => _response.IsSuccessStatusCode;
|
||||
|
||||
|
@ -48,6 +48,7 @@ namespace CryptoExchange.Net.SharedApis
|
||||
WebCallResult<T> result,
|
||||
INextPageToken? nextPageToken = null) :
|
||||
base(result.ResponseStatusCode,
|
||||
result.HttpVersion,
|
||||
result.ResponseHeaders,
|
||||
result.ResponseTime,
|
||||
result.ResponseLength,
|
||||
@ -75,6 +76,7 @@ namespace CryptoExchange.Net.SharedApis
|
||||
WebCallResult<T> result,
|
||||
INextPageToken? nextPageToken = null) :
|
||||
base(result.ResponseStatusCode,
|
||||
result.HttpVersion,
|
||||
result.ResponseHeaders,
|
||||
result.ResponseTime,
|
||||
result.ResponseLength,
|
||||
@ -100,6 +102,7 @@ namespace CryptoExchange.Net.SharedApis
|
||||
string exchange,
|
||||
TradingMode[]? dataTradeModes,
|
||||
HttpStatusCode? code,
|
||||
Version? httpVersion,
|
||||
KeyValuePair<string, string[]>[]? responseHeaders,
|
||||
TimeSpan? responseTime,
|
||||
long? responseLength,
|
||||
@ -114,6 +117,7 @@ namespace CryptoExchange.Net.SharedApis
|
||||
Error? error,
|
||||
INextPageToken? nextPageToken = null) : base(
|
||||
code,
|
||||
httpVersion,
|
||||
responseHeaders,
|
||||
responseTime,
|
||||
responseLength,
|
||||
@ -140,7 +144,7 @@ namespace CryptoExchange.Net.SharedApis
|
||||
/// <returns></returns>
|
||||
public new ExchangeWebResult<K> As<K>([AllowNull] K data)
|
||||
{
|
||||
return new ExchangeWebResult<K>(Exchange, DataTradeMode, ResponseStatusCode, ResponseHeaders, ResponseTime, ResponseLength, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, DataSource, data, Error, NextPageToken);
|
||||
return new ExchangeWebResult<K>(Exchange, DataTradeMode, ResponseStatusCode, HttpVersion, ResponseHeaders, ResponseTime, ResponseLength, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, DataSource, data, Error, NextPageToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
@ -21,6 +21,8 @@ namespace CryptoExchange.Net.Testing.Implementations
|
||||
|
||||
public Uri Uri { get; set; }
|
||||
|
||||
public Version HttpVersion { get; set; }
|
||||
|
||||
public int RequestId { get; set; }
|
||||
|
||||
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
||||
|
@ -1,5 +1,6 @@
|
||||
using CryptoExchange.Net.Interfaces;
|
||||
using CryptoExchange.Net.Objects;
|
||||
using CryptoExchange.Net.Objects.Options;
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
|
||||
@ -14,11 +15,11 @@ namespace CryptoExchange.Net.Testing.Implementations
|
||||
_request = request;
|
||||
}
|
||||
|
||||
public void Configure(ApiProxy? proxy, TimeSpan requestTimeout, HttpClient? httpClient = null)
|
||||
{
|
||||
public void Configure(RestExchangeOptions options, HttpClient? client)
|
||||
{
|
||||
}
|
||||
|
||||
public IRequest Create(HttpMethod method, Uri uri, int requestId)
|
||||
public IRequest Create(Version httpRequestVersion, HttpMethod method, Uri uri, int requestId)
|
||||
{
|
||||
_request.Method = method;
|
||||
_request.Uri = uri;
|
||||
@ -26,6 +27,6 @@ namespace CryptoExchange.Net.Testing.Implementations
|
||||
return _request;
|
||||
}
|
||||
|
||||
public void UpdateSettings(ApiProxy? proxy, TimeSpan requestTimeout) {}
|
||||
public void UpdateSettings(ApiProxy? proxy, TimeSpan requestTimeout, TimeSpan? httpKeepAliveInterval) {}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using CryptoExchange.Net.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
@ -11,6 +12,7 @@ namespace CryptoExchange.Net.Testing.Implementations
|
||||
private readonly Stream _response;
|
||||
|
||||
public HttpStatusCode StatusCode { get; }
|
||||
public Version HttpVersion { get; }
|
||||
|
||||
public bool IsSuccessStatusCode { get; }
|
||||
|
||||
@ -21,6 +23,7 @@ namespace CryptoExchange.Net.Testing.Implementations
|
||||
public TestResponse(HttpStatusCode code, Stream response)
|
||||
{
|
||||
StatusCode = code;
|
||||
HttpVersion = new Version(2, 0);
|
||||
IsSuccessStatusCode = code == HttpStatusCode.OK;
|
||||
_response = response;
|
||||
}
|
||||
|
11
README.md
11
README.md
@ -38,6 +38,10 @@ Full list of all libraries part of the CryptoExchange.Net ecosystem. Consider us
|
||||
|
||||
Any of these can be installed independently or install [CryptoClients.Net](https://github.com/jkorf/CryptoClients.Net) which includes all exchange API's.
|
||||
|
||||
### Full demo application
|
||||
A full demo application is available using the [CryptoClients.Net](https://github.com/jkorf/CryptoClients.Net) library:
|
||||
https://github.com/JKorf/CryptoManager.Net
|
||||
|
||||
## Discord
|
||||
[](https://discord.gg/MSpeEtSY8t)
|
||||
A Discord server is available [here](https://discord.gg/MSpeEtSY8t). Feel free to join for discussion and/or questions around the CryptoExchange.Net and implementation libraries.
|
||||
@ -59,6 +63,13 @@ 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).
|
||||
|
||||
## Release notes
|
||||
* Version 9.7.0 - 01 Sep 2025
|
||||
* Added LibraryHelpers.CreateHttpClientMessageHandle to standardize HttpMessageHandler creation
|
||||
* Added REST client option for selecting HTTP protocol version
|
||||
* Added REST client option for HTTP client keep alive interval
|
||||
* Added HttpVersion to WebCallResult responses
|
||||
* Updated request logic to default to using HTTP version 2.0 for dotnet core
|
||||
|
||||
* Version 9.6.0 - 25 Aug 2025
|
||||
* Added support for parsing REST response even though status indicates error
|
||||
* Added better support for subscriptions without subscribe confirmation
|
||||
|
Loading…
x
Reference in New Issue
Block a user