diff --git a/SteamKit2/SteamKit2/Steam/CDNClient.cs b/SteamKit2/SteamKit2/Steam/CDNClient.cs index 935534391..fd2c3d56c 100644 --- a/SteamKit2/SteamKit2/Steam/CDNClient.cs +++ b/SteamKit2/SteamKit2/Steam/CDNClient.cs @@ -231,7 +231,7 @@ public CDNClient( SteamClient steamClient, byte[] appTicket = null ) } this.steamClient = steamClient; - this.httpClient = new HttpClient(); + this.httpClient = steamClient.Configuration.HttpClientFactory(); this.depotIds = new ConcurrentDictionary(); this.appTicket = appTicket; diff --git a/SteamKit2/SteamKit2/Steam/SteamClient/Configuration/ISteamConfigurationBuilder.cs b/SteamKit2/SteamKit2/Steam/SteamClient/Configuration/ISteamConfigurationBuilder.cs index 5320eda60..720009203 100644 --- a/SteamKit2/SteamKit2/Steam/SteamClient/Configuration/ISteamConfigurationBuilder.cs +++ b/SteamKit2/SteamKit2/Steam/SteamClient/Configuration/ISteamConfigurationBuilder.cs @@ -5,6 +5,7 @@ using System; +using System.Net.Http; using SteamKit2.Discovery; namespace SteamKit2 @@ -44,6 +45,13 @@ public interface ISteamConfigurationBuilder /// A builder with modified configuration. ISteamConfigurationBuilder WithDirectoryFetch(bool allowDirectoryFetch); + /// + /// Configures this with custom HTTP behaviour. + /// + /// A function to create and configure a new HttpClient. + /// A builder with modified configuration. + ISteamConfigurationBuilder WithHttpClientFactory(HttpClientFactory factoryFunction); + /// /// Configures how this will be used to connect to Steam. /// diff --git a/SteamKit2/SteamKit2/Steam/SteamClient/Configuration/SteamConfiguration.cs b/SteamKit2/SteamKit2/Steam/SteamClient/Configuration/SteamConfiguration.cs index efb3ab104..c76b71a5d 100644 --- a/SteamKit2/SteamKit2/Steam/SteamClient/Configuration/SteamConfiguration.cs +++ b/SteamKit2/SteamKit2/Steam/SteamClient/Configuration/SteamConfiguration.cs @@ -5,10 +5,18 @@ using System; +using System.Net.Http; using SteamKit2.Discovery; namespace SteamKit2 { + /// + /// Factory function to create a user-configured HttpClient. + /// The HttpClient will be disposed of after use. + /// + /// A new to be used to send HTTP requests. + public delegate HttpClient HttpClientFactory(); + /// /// Configuration object to use. /// This object should not be mutated after it is passed to one or more objects. @@ -66,6 +74,12 @@ internal static SteamConfiguration CreateDefault() /// when calling SteamFriends.RequestFriendInfo without specifying flags. /// public EClientPersonaStateFlag DefaultPersonaStateFlags => state.DefaultPersonaStateFlags; + + /// + /// Factory function to create a user-configured HttpClient. + /// + public HttpClientFactory HttpClientFactory => state.HttpClientFactory; + /// /// The supported protocol types to use when attempting to connect to Steam. /// diff --git a/SteamKit2/SteamKit2/Steam/SteamClient/Configuration/SteamConfigurationBuilder.cs b/SteamKit2/SteamKit2/Steam/SteamClient/Configuration/SteamConfigurationBuilder.cs index bec9d24f2..82b0ef9b2 100644 --- a/SteamKit2/SteamKit2/Steam/SteamClient/Configuration/SteamConfigurationBuilder.cs +++ b/SteamKit2/SteamKit2/Steam/SteamClient/Configuration/SteamConfigurationBuilder.cs @@ -5,6 +5,7 @@ using System; +using System.Net.Http; using SteamKit2.Discovery; namespace SteamKit2 @@ -29,6 +30,8 @@ public static SteamConfigurationState CreateDefaultState() EClientPersonaStateFlag.SourceID | EClientPersonaStateFlag.GameExtraInfo | EClientPersonaStateFlag.LastSeen, + HttpClientFactory = DefaultHttpClientFactory, + ProtocolTypes = ProtocolTypes.Tcp, ServerListProvider = new NullServerListProvider(), @@ -68,6 +71,12 @@ public ISteamConfigurationBuilder WithDirectoryFetch(bool allowDirectoryFetch) return this; } + public ISteamConfigurationBuilder WithHttpClientFactory(HttpClientFactory factoryFunction) + { + state.HttpClientFactory = factoryFunction; + return this; + } + public ISteamConfigurationBuilder WithProtocolTypes(ProtocolTypes protocolTypes) { state.ProtocolTypes = protocolTypes; @@ -97,5 +106,7 @@ public ISteamConfigurationBuilder WithWebAPIKey(string webApiKey) state.WebAPIKey = webApiKey ?? throw new ArgumentNullException(nameof(webApiKey)); return this; } + + static HttpClient DefaultHttpClientFactory() => new HttpClient(); } } diff --git a/SteamKit2/SteamKit2/Steam/SteamClient/Configuration/SteamConfigurationState.cs b/SteamKit2/SteamKit2/Steam/SteamClient/Configuration/SteamConfigurationState.cs index c07cafc5f..1237adebc 100644 --- a/SteamKit2/SteamKit2/Steam/SteamClient/Configuration/SteamConfigurationState.cs +++ b/SteamKit2/SteamKit2/Steam/SteamClient/Configuration/SteamConfigurationState.cs @@ -15,6 +15,7 @@ struct SteamConfigurationState public uint CellID; public TimeSpan ConnectionTimeout; public EClientPersonaStateFlag DefaultPersonaStateFlags; + public HttpClientFactory HttpClientFactory; public ProtocolTypes ProtocolTypes; public IServerListProvider ServerListProvider; public EUniverse Universe; diff --git a/SteamKit2/SteamKit2/Steam/WebAPI/SteamConfigurationWebAPIExtensions.cs b/SteamKit2/SteamKit2/Steam/WebAPI/SteamConfigurationWebAPIExtensions.cs index d8f561874..dd7849e5f 100644 --- a/SteamKit2/SteamKit2/Steam/WebAPI/SteamConfigurationWebAPIExtensions.cs +++ b/SteamKit2/SteamKit2/Steam/WebAPI/SteamConfigurationWebAPIExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.Net.Http; namespace SteamKit2 { @@ -20,7 +21,7 @@ public static WebAPI.Interface GetWebAPIInterface(this SteamConfiguration config throw new ArgumentNullException(nameof(config)); } - return new WebAPI.Interface(config.WebAPIBaseAddress, iface, config.WebAPIKey); + return new WebAPI.Interface(config.GetHttpClientForWebAPI(), iface, config.WebAPIKey); } /// @@ -36,7 +37,17 @@ public static WebAPI.AsyncInterface GetAsyncWebAPIInterface(this SteamConfigurat throw new ArgumentNullException(nameof(config)); } - return new WebAPI.AsyncInterface(config.WebAPIBaseAddress, iface, config.WebAPIKey); + return new WebAPI.AsyncInterface(config.GetHttpClientForWebAPI(), iface, config.WebAPIKey); + } + + internal static HttpClient GetHttpClientForWebAPI(this SteamConfiguration config) + { + var client = config.HttpClientFactory(); + + client.BaseAddress = config.WebAPIBaseAddress; + client.Timeout = WebAPI.DefaultTimeout; + + return client; } } } diff --git a/SteamKit2/SteamKit2/Steam/WebAPI/WebAPI.cs b/SteamKit2/SteamKit2/Steam/WebAPI/WebAPI.cs index 8e3eb213e..cdce8043c 100644 --- a/SteamKit2/SteamKit2/Steam/WebAPI/WebAPI.cs +++ b/SteamKit2/SteamKit2/Steam/WebAPI/WebAPI.cs @@ -26,6 +26,8 @@ public sealed class WebAPI /// public static Uri DefaultBaseAddress { get; } = new Uri("https://api.steampowered.com/", UriKind.Absolute); + internal static TimeSpan DefaultTimeout { get; } = TimeSpan.FromSeconds(100); + /// /// Represents a single interface that exists within the Web API. /// This is a dynamic object that allows function calls to interfaces with minimal code. @@ -48,9 +50,9 @@ public TimeSpan Timeout } - internal Interface( Uri baseAddress, string iface, string apiKey ) + internal Interface( HttpClient httpClient, string iface, string apiKey ) { - asyncInterface = new AsyncInterface( baseAddress, iface, apiKey ); + asyncInterface = new AsyncInterface( httpClient, iface, apiKey ); } @@ -187,14 +189,9 @@ public TimeSpan Timeout RegexOptions.Compiled | RegexOptions.IgnoreCase ); - internal AsyncInterface( Uri baseAddress, string iface, string apiKey ) + internal AsyncInterface( HttpClient httpClient, string iface, string apiKey ) { - httpClient = new HttpClient - { - BaseAddress = baseAddress, - Timeout = TimeSpan.FromSeconds(100) - }; - + this.httpClient = httpClient; this.iface = iface; this.apiKey = apiKey; } @@ -445,7 +442,7 @@ public static Interface GetInterface( Uri baseAddress, string iface, string apiK throw new ArgumentNullException( nameof(iface) ); } - return new Interface( baseAddress, iface, apiKey ); + return new Interface( CreateDefaultHttpClient( baseAddress ), iface, apiKey ); } /// @@ -461,7 +458,7 @@ public static Interface GetInterface( string iface, string apiKey = "" ) throw new ArgumentNullException( nameof(iface) ); } - return new Interface( DefaultBaseAddress, iface, apiKey ); + return new Interface( CreateDefaultHttpClient( DefaultBaseAddress ), iface, apiKey ); } /// @@ -477,7 +474,7 @@ public static AsyncInterface GetAsyncInterface( string iface, string apiKey = "" throw new ArgumentNullException( nameof(iface) ); } - return new AsyncInterface( DefaultBaseAddress, iface, apiKey ); + return new AsyncInterface( CreateDefaultHttpClient( DefaultBaseAddress ), iface, apiKey ); } /// @@ -499,7 +496,18 @@ public static AsyncInterface GetAsyncInterface( Uri baseAddress, string iface, s throw new ArgumentNullException( nameof(iface) ); } - return new AsyncInterface( baseAddress, iface, apiKey ); + return new AsyncInterface( CreateDefaultHttpClient( baseAddress ), iface, apiKey ); + } + + static HttpClient CreateDefaultHttpClient( Uri baseAddress ) + { + var client = new HttpClient + { + BaseAddress = baseAddress, + Timeout = DefaultTimeout + }; + + return client; } } diff --git a/SteamKit2/Tests/SteamConfigurationFacts.cs b/SteamKit2/Tests/SteamConfigurationFacts.cs index 72984635d..de5377aa4 100644 --- a/SteamKit2/Tests/SteamConfigurationFacts.cs +++ b/SteamKit2/Tests/SteamConfigurationFacts.cs @@ -1,5 +1,9 @@ using System; using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Threading; using System.Threading.Tasks; using SteamKit2; using SteamKit2.Discovery; @@ -44,6 +48,16 @@ public void DefaultPersonaStateFlags() Assert.Equal(expected, configuration.DefaultPersonaStateFlags); } + [Fact] + public void DefaultHttpClientFactory() + { + using (var client = configuration.HttpClientFactory()) + { + Assert.NotNull(client); + Assert.IsType(client); + } + } + [Fact] public void ServerListProviderIsNothingFancy() { @@ -90,6 +104,7 @@ public SteamConfigurationConfiguredObjectFacts() .WithCellID(123) .WithConnectionTimeout(TimeSpan.FromMinutes(1)) .WithDefaultPersonaStateFlags(EClientPersonaStateFlag.SourceID) + .WithHttpClientFactory(() => { var c = new HttpClient(); c.DefaultRequestHeaders.Add("X-SteamKit-Tests", "true"); return c; }) .WithProtocolTypes(ProtocolTypes.WebSocket | ProtocolTypes.Udp) .WithServerListProvider(new CustomServerListProvider()) .WithUniverse(EUniverse.Internal) @@ -117,6 +132,15 @@ public void ConnectionTimeoutIsConfigured() Assert.Equal(TimeSpan.FromMinutes(1), configuration.ConnectionTimeout); } + [Fact] + public void HttpClientFactoryIsConfigured() + { + using (var client = configuration.HttpClientFactory()) + { + Assert.Equal("true", client.DefaultRequestHeaders.GetValues("X-SteamKit-Tests").FirstOrDefault()); + } + } + [Fact] public void PersonaStateFlagsIsConfigured() {