re-introduce HttpClientFactory, keep FlurlClientFactory.Get separate. (the former makes sense at FlurlClient settings level; latter does not)
This commit is contained in:
parent
a2656d4056
commit
ad8c051543
@ -23,7 +23,7 @@ namespace Flurl.Test.Http
|
||||
|
||||
[Test]
|
||||
public void can_provide_custom_client_factory() {
|
||||
GetSettings().FlurlClientFactory = new SomeCustomFlurlClientFactory();
|
||||
GetSettings().HttpClientFactory = new SomeCustomHttpClientFactory();
|
||||
var client = new FlurlClient();
|
||||
Assert.IsInstanceOf<SomeCustomHttpClient>(client.HttpClient);
|
||||
Assert.IsInstanceOf<SomeCustomMessageHandler>(client.HttpMessageHandler);
|
||||
@ -115,7 +115,7 @@ namespace Flurl.Test.Http
|
||||
}
|
||||
}
|
||||
|
||||
private class SomeCustomFlurlClientFactory : IFlurlClientFactory
|
||||
private class SomeCustomHttpClientFactory : IHttpClientFactory
|
||||
{
|
||||
public HttpClient CreateHttpClient(HttpMessageHandler handler) {
|
||||
return new SomeCustomHttpClient();
|
||||
@ -124,10 +124,6 @@ namespace Flurl.Test.Http
|
||||
public HttpMessageHandler CreateMessageHandler() {
|
||||
return new SomeCustomMessageHandler();
|
||||
}
|
||||
|
||||
public IFlurlClient GetClient(Url url) {
|
||||
return new FlurlClient();
|
||||
}
|
||||
}
|
||||
|
||||
private class SomeCustomHttpClient : HttpClient { }
|
||||
|
@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Net.Http;
|
||||
|
||||
namespace Flurl.Http.Configuration
|
||||
{
|
||||
@ -12,31 +11,13 @@ namespace Flurl.Http.Configuration
|
||||
{
|
||||
private static readonly ConcurrentDictionary<string, IFlurlClient> _clients = new ConcurrentDictionary<string, IFlurlClient>();
|
||||
|
||||
/// <summary>
|
||||
/// Override in custom factory to customize the creation of HttpClient used in all Flurl HTTP calls.
|
||||
/// In order not to lose Flurl.Http functionality, it is recommended to call base.CreateClient and
|
||||
/// customize the result.
|
||||
/// </summary>
|
||||
public virtual HttpClient CreateHttpClient(HttpMessageHandler handler) {
|
||||
return new HttpClient(new FlurlMessageHandler(handler));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Override in custom factory to customize the creation of HttpClientHandler used in all Flurl HTTP calls.
|
||||
/// In order not to lose Flurl.Http functionality, it is recommended to call base.CreateMessageHandler and
|
||||
/// customize the result.
|
||||
/// </summary>
|
||||
public virtual HttpMessageHandler CreateMessageHandler() {
|
||||
return new HttpClientHandler();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uses a caching strategy of one FlurlClient per host. This maximizes reuse of underlying
|
||||
/// HttpClient/Handler while allowing things like cookies to be host-specific.
|
||||
/// </summary>
|
||||
/// <param name="url">The URL.</param>
|
||||
/// <returns>The FlurlClient instance.</returns>
|
||||
public virtual IFlurlClient GetClient(Url url) {
|
||||
public virtual IFlurlClient Get(Url url) {
|
||||
var key = new Uri(url).Host;
|
||||
return _clients.GetOrAdd(key, _ => new FlurlClient());
|
||||
}
|
||||
|
30
src/Flurl.Http/Configuration/DefaultHttpClientFactory.cs
Normal file
30
src/Flurl.Http/Configuration/DefaultHttpClientFactory.cs
Normal file
@ -0,0 +1,30 @@
|
||||
using System.Net.Http;
|
||||
|
||||
namespace Flurl.Http.Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// Default implementation of IHttpClientFactory used by FlurlHttp. The created HttpClient includes hooks
|
||||
/// that enable FlurlHttp's testing features and respect its configuration settings. Therefore, custom factories
|
||||
/// should inherit from this class, rather than implementing IHttpClientFactory directly.
|
||||
/// </summary>
|
||||
public class DefaultHttpClientFactory : IHttpClientFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Override in custom factory to customize the creation of HttpClient used in all Flurl HTTP calls.
|
||||
/// In order not to lose Flurl.Http functionality, it is recommended to call base.CreateClient and
|
||||
/// customize the result.
|
||||
/// </summary>
|
||||
public virtual HttpClient CreateHttpClient(HttpMessageHandler handler) {
|
||||
return new HttpClient(new FlurlMessageHandler(handler));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Override in custom factory to customize the creation of HttpClientHandler used in all Flurl HTTP calls.
|
||||
/// In order not to lose Flurl.Http functionality, it is recommended to call base.CreateMessageHandler and
|
||||
/// customize the result.
|
||||
/// </summary>
|
||||
public virtual HttpMessageHandler CreateMessageHandler() {
|
||||
return new HttpClientHandler();
|
||||
}
|
||||
}
|
||||
}
|
@ -21,7 +21,7 @@ namespace Flurl.Http.Configuration
|
||||
private IDictionary<string, object> _vals = new Dictionary<string, object>();
|
||||
|
||||
private static FlurlHttpSettings _baseDefaults = new FlurlHttpSettings(null) {
|
||||
FlurlClientFactory = new DefaultFlurlClientFactory(),
|
||||
HttpClientFactory = new DefaultHttpClientFactory(),
|
||||
CookiesEnabled = false,
|
||||
JsonSerializer = new NewtonsoftJsonSerializer(null),
|
||||
UrlEncodedSerializer = new DefaultUrlEncodedSerializer()
|
||||
@ -43,13 +43,12 @@ namespace Flurl.Http.Configuration
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a factory used to create HttpClient object used in Flurl HTTP calls. Default value
|
||||
/// is an instance of DefaultFlurlClientFactory. Custom factory implementations should generally
|
||||
/// inherit from DefaultFlurlClientFactory, call base.CreateClient, and manipulate the returned HttpClient,
|
||||
/// otherwise functionality such as callbacks and most testing features will be lost.
|
||||
/// is an instance of DefaultHttpClientFactory. Custom factory implementations should generally
|
||||
/// inherit from DefaultHttpClientFactory, the base implementations, and only customize as needed.
|
||||
/// </summary>
|
||||
public IFlurlClientFactory FlurlClientFactory {
|
||||
get => Get(() => FlurlClientFactory);
|
||||
set => Set(() => FlurlClientFactory, value);
|
||||
public IHttpClientFactory HttpClientFactory {
|
||||
get => Get(() => HttpClientFactory);
|
||||
set => Set(() => HttpClientFactory, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -174,4 +173,16 @@ namespace Flurl.Http.Configuration
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Global default settings for Flurl.Http
|
||||
/// </summary>
|
||||
public class GlobalFlurlHttpSettings : FlurlHttpSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the factory that defines creating, caching, and reusing FlurlClient instances and,
|
||||
/// by proxy, HttpClient instances.
|
||||
/// </summary>
|
||||
public IFlurlClientFactory FlurlClientFactory { get; set; } = new DefaultFlurlClientFactory();
|
||||
}
|
||||
}
|
||||
|
@ -1,33 +1,16 @@
|
||||
using System.Net.Http;
|
||||
|
||||
namespace Flurl.Http.Configuration
|
||||
namespace Flurl.Http.Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface defining creation of HttpClient and HttpMessageHandler used in all Flurl HTTP calls.
|
||||
/// Implementation can be added via FlurlHttp.Configure. However, in order not to lose much of
|
||||
/// Flurl.Http's functionality, it's almost always best to inherit DefaultFlurlClientFactory and
|
||||
/// extend the base implementations, rather than implementing this interface directly.
|
||||
/// Interface for defining a strategy for creating, caching, and reusing IFlurlClient instances and,
|
||||
/// by proxy, their underlying HttpClient instances.
|
||||
/// </summary>
|
||||
public interface IFlurlClientFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates the client.
|
||||
/// </summary>
|
||||
/// <param name="handler">The message handler being used in this call</param>
|
||||
/// <returns></returns>
|
||||
HttpClient CreateHttpClient(HttpMessageHandler handler);
|
||||
|
||||
/// <summary>
|
||||
/// Creates the message handler.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
HttpMessageHandler CreateMessageHandler();
|
||||
|
||||
/// <summary>
|
||||
/// Strategy to create an HttpClient or reuse an exisitng one, based on URL being called.
|
||||
/// Strategy to create a FlurlClient or reuse an exisitng one, based on URL being called.
|
||||
/// </summary>
|
||||
/// <param name="url">The URL being called.</param>
|
||||
/// <returns></returns>
|
||||
IFlurlClient GetClient(Url url);
|
||||
IFlurlClient Get(Url url);
|
||||
}
|
||||
}
|
28
src/Flurl.Http/Configuration/IHttpClientFactory.cs
Normal file
28
src/Flurl.Http/Configuration/IHttpClientFactory.cs
Normal file
@ -0,0 +1,28 @@
|
||||
using System.Net.Http;
|
||||
|
||||
namespace Flurl.Http.Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface defining creation of HttpClient and HttpMessageHandler used in all Flurl HTTP calls.
|
||||
/// Implementation can be added via FlurlHttp.Configure. However, in order not to lose much of
|
||||
/// Flurl.Http's functionality, it's almost always best to inherit DefaultHttpClientFactory and
|
||||
/// extend the base implementations, rather than implementing this interface directly.
|
||||
/// </summary>
|
||||
public interface IHttpClientFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines how HttpClient should be instantiated and configured by default. Do NOT attempt
|
||||
/// to cache/reuse HttpClient instances here - that should be done at the FlurlClient level
|
||||
/// via a custom FlurlClientFactory that gets registered globally.
|
||||
/// </summary>
|
||||
/// <param name="handler">The HttpMessageHandler used to construct the HttpClient.</param>
|
||||
/// <returns></returns>
|
||||
HttpClient CreateHttpClient(HttpMessageHandler handler);
|
||||
|
||||
/// <summary>
|
||||
/// Defines how the
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
HttpMessageHandler CreateMessageHandler();
|
||||
}
|
||||
}
|
@ -40,9 +40,9 @@ namespace Flurl.Http
|
||||
/// </summary>
|
||||
/// <param name="settings">The FlurlHttpSettings associated with this instance.</param>
|
||||
public FlurlClient(FlurlHttpSettings settings = null) {
|
||||
Settings = settings ?? new FlurlHttpSettings().Merge(HttpTest.Current?.Settings ?? FlurlHttp.GlobalSettings);
|
||||
HttpMessageHandler = Settings.FlurlClientFactory.CreateMessageHandler();
|
||||
HttpClient = Settings.FlurlClientFactory.CreateHttpClient(HttpMessageHandler);
|
||||
Settings = settings ?? new FlurlHttpSettings(FlurlHttp.GlobalSettings);
|
||||
HttpMessageHandler = Settings.HttpClientFactory.CreateMessageHandler();
|
||||
HttpClient = Settings.HttpClientFactory.CreateHttpClient(HttpMessageHandler);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -2,6 +2,7 @@
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Flurl.Http.Configuration;
|
||||
using Flurl.Http.Testing;
|
||||
|
||||
namespace Flurl.Http
|
||||
{
|
||||
@ -13,13 +14,13 @@ namespace Flurl.Http
|
||||
private static readonly object _configLock = new object();
|
||||
private static readonly Task _completedTask = Task.FromResult(0);
|
||||
|
||||
private static Lazy<FlurlHttpSettings> _settings =
|
||||
new Lazy<FlurlHttpSettings>(() => new FlurlHttpSettings());
|
||||
private static Lazy<GlobalFlurlHttpSettings> _settings =
|
||||
new Lazy<GlobalFlurlHttpSettings>(() => new GlobalFlurlHttpSettings());
|
||||
|
||||
/// <summary>
|
||||
/// Globally configured Flurl.Http settings. Should normally be written to by calling FlurlHttp.Configure once application at startup.
|
||||
/// </summary>
|
||||
public static FlurlHttpSettings GlobalSettings => _settings.Value;
|
||||
public static GlobalFlurlHttpSettings GlobalSettings => HttpTest.Current?.Settings ?? _settings.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Provides thread-safe access to Flurl.Http's global configuration settings. Should only be called once at application startup.
|
||||
|
@ -51,7 +51,7 @@ namespace Flurl.Http
|
||||
/// <param name="url">The URL to call with this FlurlRequest instance.</param>
|
||||
/// <param name="settings">The FlurlHttpSettings object used by this request.</param>
|
||||
public FlurlRequest(Url url, FlurlHttpSettings settings = null) {
|
||||
Settings = settings ?? new FlurlHttpSettings().Merge(HttpTest.Current?.Settings ?? FlurlHttp.GlobalSettings);
|
||||
Settings = settings ?? new FlurlHttpSettings().Merge(FlurlHttp.GlobalSettings);
|
||||
Url = url;
|
||||
}
|
||||
|
||||
@ -71,7 +71,13 @@ namespace Flurl.Http
|
||||
/// Gets or sets the IFlurlClient to use when sending the request.
|
||||
/// </summary>
|
||||
public IFlurlClient Client {
|
||||
get => _client = _client ?? Settings.FlurlClientFactory.GetClient(Url);
|
||||
get {
|
||||
if (_client == null) {
|
||||
_client = FlurlHttp.GlobalSettings.FlurlClientFactory.Get(Url);
|
||||
Settings.Merge(_client.Settings);
|
||||
}
|
||||
return _client;
|
||||
}
|
||||
set {
|
||||
_client = value;
|
||||
Settings.Merge(_client.Settings);
|
||||
@ -103,7 +109,6 @@ namespace Flurl.Http
|
||||
/// <param name="completionOption">The HttpCompletionOption used in the request. Optional.</param>
|
||||
/// <returns>A Task whose result is the received HttpResponseMessage.</returns>
|
||||
public async Task<HttpResponseMessage> SendAsync(HttpMethod verb, HttpContent content = null, CancellationToken? cancellationToken = null, HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead) {
|
||||
Settings.Merge(Client.Settings);
|
||||
if (Settings.Timeout.HasValue)
|
||||
Client.HttpClient.Timeout = Settings.Timeout.Value;
|
||||
var request = new HttpRequestMessage(verb, Url) { Content = content };
|
||||
|
@ -20,9 +20,10 @@ namespace Flurl.Http.Testing
|
||||
/// </summary>
|
||||
/// <exception cref="Exception">A delegate callback throws an exception.</exception>
|
||||
public HttpTest() {
|
||||
Settings = new FlurlHttpSettings {
|
||||
FlurlClientFactory = new TestFlurlClientFactory()
|
||||
}.Merge(FlurlHttp.GlobalSettings);
|
||||
Settings = new GlobalFlurlHttpSettings {
|
||||
HttpClientFactory = new TestHttpClientFactory(),
|
||||
FlurlClientFactory = new TestFlurlClientFactory()
|
||||
};
|
||||
ResponseQueue = new Queue<HttpResponseMessage>();
|
||||
CallLog = new List<HttpCall>();
|
||||
SetCurrentTest(this);
|
||||
@ -31,7 +32,7 @@ namespace Flurl.Http.Testing
|
||||
/// <summary>
|
||||
/// Gets or sets the FlurlHttpSettings object used by this test.
|
||||
/// </summary>
|
||||
public FlurlHttpSettings Settings { get; set; }
|
||||
public GlobalFlurlHttpSettings Settings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current HttpTest from the logical (async) call context
|
||||
|
@ -1,17 +1,14 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Net.Http;
|
||||
using Flurl.Http.Configuration;
|
||||
|
||||
namespace Flurl.Http.Testing
|
||||
{
|
||||
/// <summary>
|
||||
/// Fake http client factory.
|
||||
/// IHttpClientFactory implementation used to fake and record calls in tests.
|
||||
/// </summary>
|
||||
public class TestFlurlClientFactory : DefaultFlurlClientFactory
|
||||
public class TestHttpClientFactory : DefaultHttpClientFactory
|
||||
{
|
||||
private readonly Lazy<FlurlClient> _client = new Lazy<FlurlClient>(() => new FlurlClient());
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of FakeHttpMessageHander, which prevents actual HTTP calls from being made.
|
||||
/// </summary>
|
||||
@ -19,13 +16,21 @@ namespace Flurl.Http.Testing
|
||||
public override HttpMessageHandler CreateMessageHandler() {
|
||||
return new FakeHttpMessageHandler();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// IFlurlClientFactory implementation used to fake and record calls in tests.
|
||||
/// </summary>
|
||||
public class TestFlurlClientFactory : DefaultFlurlClientFactory
|
||||
{
|
||||
private readonly Lazy<FlurlClient> _client = new Lazy<FlurlClient>(() => new FlurlClient());
|
||||
|
||||
/// <summary>
|
||||
/// Returns the FlurlClient sigleton used for testing
|
||||
/// </summary>
|
||||
/// <param name="url">The URL.</param>
|
||||
/// <returns>The FlurlClient instance.</returns>
|
||||
public override IFlurlClient GetClient(Url url) {
|
||||
public override IFlurlClient Get(Url url) {
|
||||
return _client.Value;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user