diff --git a/CryptoExchange.Net/LibraryHelpers.cs b/CryptoExchange.Net/LibraryHelpers.cs index 1113620..a223b39 100644 --- a/CryptoExchange.Net/LibraryHelpers.cs +++ b/CryptoExchange.Net/LibraryHelpers.cs @@ -1,4 +1,5 @@ using CryptoExchange.Net.Objects; +using CryptoExchange.Net.Objects.Options; using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; @@ -105,31 +106,36 @@ namespace CryptoExchange.Net /// /// Create a new HttpMessageHandler instance /// - public static HttpMessageHandler CreateHttpClientMessageHandler(ApiProxy? proxy, TimeSpan? keepAliveInterval) + public static HttpMessageHandler CreateHttpClientMessageHandler(RestExchangeOptions options) { #if NET5_0_OR_GREATER var socketHandler = new SocketsHttpHandler(); try { - if (keepAliveInterval != null && keepAliveInterval != TimeSpan.Zero) + if (options.HttpKeepAliveInterval != null && options.HttpKeepAliveInterval != TimeSpan.Zero) { socketHandler.KeepAlivePingPolicy = HttpKeepAlivePingPolicy.Always; - socketHandler.KeepAlivePingDelay = keepAliveInterval.Value; + socketHandler.KeepAlivePingDelay = options.HttpKeepAliveInterval.Value; socketHandler.KeepAlivePingTimeout = TimeSpan.FromSeconds(10); } socketHandler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; socketHandler.DefaultProxyCredentials = CredentialCache.DefaultCredentials; + + socketHandler.EnableMultipleHttp2Connections = options.HttpEnableMultipleHttp2Connections; + socketHandler.PooledConnectionLifetime = options.HttpPooledConnectionLifetime; + socketHandler.PooledConnectionIdleTimeout = options.HttpPooledConnectionIdleTimeout; + socketHandler.MaxConnectionsPerServer = options.HttpMaxConnectionsPerServer; } catch (PlatformNotSupportedException) { } catch (NotImplementedException) { } // Mono runtime throws NotImplementedException - if (proxy != null) + if (options.Proxy != null) { socketHandler.Proxy = new WebProxy { - Address = new Uri($"{proxy.Host}:{proxy.Port}"), - Credentials = proxy.Password == null ? null : new NetworkCredential(proxy.Login, proxy.Password) + Address = new Uri($"{options.Proxy.Host}:{options.Proxy.Port}"), + Credentials = options.Proxy.Password == null ? null : new NetworkCredential(options.Proxy.Login, options.Proxy.Password) }; } return socketHandler; @@ -143,12 +149,12 @@ namespace CryptoExchange.Net catch (PlatformNotSupportedException) { } catch (NotImplementedException) { } // Mono runtime throws NotImplementedException - if (proxy != null) + if (options.Proxy != null) { httpHandler.Proxy = new WebProxy { - Address = new Uri($"{proxy.Host}:{proxy.Port}"), - Credentials = proxy.Password == null ? null : new NetworkCredential(proxy.Login, proxy.Password) + Address = new Uri($"{options.Proxy.Host}:{options.Proxy.Port}"), + Credentials = options.Proxy.Password == null ? null : new NetworkCredential(options.Proxy.Login, options.Proxy.Password) }; } return httpHandler; diff --git a/CryptoExchange.Net/Objects/Options/RestExchangeOptions.cs b/CryptoExchange.Net/Objects/Options/RestExchangeOptions.cs index db4cd6e..d8694d6 100644 --- a/CryptoExchange.Net/Objects/Options/RestExchangeOptions.cs +++ b/CryptoExchange.Net/Objects/Options/RestExchangeOptions.cs @@ -32,10 +32,29 @@ namespace CryptoExchange.Net.Objects.Options #else = new Version(1, 1); #endif + /// - /// Http client keep alive interval for keeping connections open + /// Http client keep alive interval for keeping connections open. Only applied when using dotnet8.0 or higher and dependency injection /// public TimeSpan? HttpKeepAliveInterval { get; set; } = TimeSpan.FromSeconds(15); +#if NET5_0_OR_GREATER + /// + /// Enable multiple simultaneous HTTP 2 connections. Only applied when using dependency injection + /// + public bool HttpEnableMultipleHttp2Connections { get; set; } = false; + /// + /// Lifetime of pooled HTTP connections; the time before a connection is recreated. Only applied when using dependency injection + /// + public TimeSpan HttpPooledConnectionLifetime { get; set; } = TimeSpan.FromMinutes(15); + /// + /// Idle timeout of pooled HTTP connections; the time before an open connection is closed when there are no requests. Only applied when using dependency injection + /// + public TimeSpan HttpPooledConnectionIdleTimeout { get; set; } = TimeSpan.FromMinutes(2); + /// + /// Max number of connections per server. Only applied when using dependency injection + /// + public int HttpMaxConnectionsPerServer { get; set; } = int.MaxValue; +#endif /// /// Set the values of this options on the target options @@ -54,6 +73,12 @@ namespace CryptoExchange.Net.Objects.Options item.CachingMaxAge = CachingMaxAge; item.HttpVersion = HttpVersion; item.HttpKeepAliveInterval = HttpKeepAliveInterval; +#if NET5_0_OR_GREATER + item.HttpMaxConnectionsPerServer = HttpMaxConnectionsPerServer; + item.HttpPooledConnectionLifetime = HttpPooledConnectionLifetime; + item.HttpPooledConnectionIdleTimeout = HttpPooledConnectionIdleTimeout; + item.HttpEnableMultipleHttp2Connections = HttpEnableMultipleHttp2Connections; +#endif return item; } } diff --git a/CryptoExchange.Net/Requests/RequestFactory.cs b/CryptoExchange.Net/Requests/RequestFactory.cs index b70064e..f7ff553 100644 --- a/CryptoExchange.Net/Requests/RequestFactory.cs +++ b/CryptoExchange.Net/Requests/RequestFactory.cs @@ -12,14 +12,16 @@ namespace CryptoExchange.Net.Requests public class RequestFactory : IRequestFactory { private HttpClient? _httpClient; + private RestExchangeOptions? _options; /// public void Configure(RestExchangeOptions options, HttpClient? client = null) { if (client == null) - client = CreateClient(options.Proxy, options.RequestTimeout, options.HttpKeepAliveInterval); + client = CreateClient(options); _httpClient = client; + _options = options; } /// @@ -39,15 +41,20 @@ namespace CryptoExchange.Net.Requests /// public void UpdateSettings(ApiProxy? proxy, TimeSpan requestTimeout, TimeSpan? httpKeepAliveInterval) { - _httpClient = CreateClient(proxy, requestTimeout, httpKeepAliveInterval); + var newOptions = new RestExchangeOptions(); + _options!.Set(newOptions); + newOptions.Proxy = proxy; + newOptions.RequestTimeout = requestTimeout; + newOptions.HttpKeepAliveInterval = httpKeepAliveInterval; + _httpClient = CreateClient(newOptions); } - private static HttpClient CreateClient(ApiProxy? proxy, TimeSpan requestTimeout, TimeSpan? httpKeepAliveInterval) + private static HttpClient CreateClient(RestExchangeOptions options) { - var handler = LibraryHelpers.CreateHttpClientMessageHandler(proxy, httpKeepAliveInterval); + var handler = LibraryHelpers.CreateHttpClientMessageHandler(options); var client = new HttpClient(handler) { - Timeout = requestTimeout + Timeout = options.RequestTimeout }; return client; }