diff --git a/src/Flurl.Http/Configuration/FlurlClientBuilder.cs b/src/Flurl.Http/Configuration/FlurlClientBuilder.cs
index a80a9605..7e01bb36 100644
--- a/src/Flurl.Http/Configuration/FlurlClientBuilder.cs
+++ b/src/Flurl.Http/Configuration/FlurlClientBuilder.cs
@@ -23,7 +23,11 @@ public interface IFlurlClientBuilder
///
/// Configure the inner-most HttpMessageHandler associated with this IFlurlClient.
///
+#if NETCOREAPP2_1_OR_GREATER
+ IFlurlClientBuilder ConfigureInnerHandler(Action configAction);
+#else
IFlurlClientBuilder ConfigureInnerHandler(Action configAction);
+#endif
///
/// Add a provided DelegatingHandler to the IFlurlClient.
@@ -44,9 +48,13 @@ public class FlurlClientBuilder : IFlurlClientBuilder
private readonly IFlurlClientFactory _factory;
private readonly string _baseUrl;
private readonly List> _addMiddleware = new();
- private readonly List> _configClient = new();
- private readonly List> _configHandler = new();
private readonly List> _configSettings = new();
+ private readonly List> _configClient = new();
+#if NETCOREAPP2_1_OR_GREATER
+ private readonly HandlerBuilder _handlerBuilder = new();
+#else
+ private readonly HandlerBuilder _handlerBuilder = new();
+#endif
///
/// Creates a new FlurlClientBuilder.
@@ -80,24 +88,20 @@ public IFlurlClientBuilder ConfigureHttpClient(Action configAction)
}
///
+#if NETCOREAPP2_1_OR_GREATER
+ public IFlurlClientBuilder ConfigureInnerHandler(Action configAction) {
+#else
public IFlurlClientBuilder ConfigureInnerHandler(Action configAction) {
- _configHandler.Add(configAction);
+#endif
+ _handlerBuilder.Configs.Add(configAction);
return this;
}
///
public IFlurlClient Build() {
- var innerHandler = _factory.CreateInnerHandler();
- foreach (var config in _configHandler) {
- if (innerHandler is HttpClientHandler hch)
- config(hch);
- else
- throw new Exception("ConfigureInnerHandler can only be used when IFlurlClientFactory.CreateInnerHandler returns an instance of HttpClientFactory.");
- }
+ var outerHandler = _handlerBuilder.Build(_factory);
- HttpMessageHandler outerHandler = innerHandler;
- foreach (var createMW in Enumerable.Reverse(_addMiddleware)) {
- var middleware = createMW();
+ foreach (var middleware in Enumerable.Reverse(_addMiddleware).Select(create => create())) {
middleware.InnerHandler = outerHandler;
outerHandler = middleware;
}
@@ -107,11 +111,27 @@ public IFlurlClient Build() {
config(httpCli);
var flurlCli = new FlurlClient(httpCli, _baseUrl);
- foreach (var config in _configSettings) {
+ foreach (var config in _configSettings)
config(flurlCli.Settings);
- }
return flurlCli;
}
+
+ // helper class to keep those compiler switches from getting too messy
+ private class HandlerBuilder where T : HttpMessageHandler
+ {
+ public List> Configs { get; } = new();
+
+ public HttpMessageHandler Build(IFlurlClientFactory fac) {
+ var handler = fac.CreateInnerHandler();
+ foreach (var config in Configs) {
+ if (handler is T h)
+ config(h);
+ else
+ throw new Exception($"ConfigureInnerHandler expected an instance of {typeof(T).Name} but received an instance of {handler.GetType().Name}.");
+ }
+ return handler;
+ }
+ }
}
}
diff --git a/src/Flurl.Http/Configuration/FlurlClientFactory.cs b/src/Flurl.Http/Configuration/FlurlClientFactory.cs
index bae9faf7..2c27bac5 100644
--- a/src/Flurl.Http/Configuration/FlurlClientFactory.cs
+++ b/src/Flurl.Http/Configuration/FlurlClientFactory.cs
@@ -5,24 +5,23 @@
namespace Flurl.Http.Configuration
{
///
- /// Interface for methods used to build and cache IFlurlClient instances.
+ /// Interface for helper methods used to construct IFlurlClient instances.
///
public interface IFlurlClientFactory
{
///
- /// 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.
+ /// Creates and configures a new HttpClient as needed when a new IFlurlClient instance is created.
+ /// Implementors should NOT attempt to cache or reuse HttpClient instances here - their lifetime is
+ /// bound one-to-one with an IFlurlClient, whose caching and reuse is managed by IFlurlClientCache.
///
- /// The HttpMessageHandler used to construct the HttpClient.
- ///
+ /// The HttpMessageHandler passed to the constructor of the HttpClient.
HttpClient CreateHttpClient(HttpMessageHandler handler);
///
- /// Defines how the HttpMessageHandler used by HttpClients that are created by
- /// this factory should be instantiated and configured.
+ /// Creates and configures a new HttpMessageHandler as needed when a new IFlurlClient instance is created.
+ /// The default implementation creates an instance of SocketsHttpHandler for platforms that support it,
+ /// otherwise HttpClientHandler.
///
- ///
HttpMessageHandler CreateInnerHandler();
}
@@ -42,38 +41,37 @@ public static class FlurlClientFactoryExtensions
///
public class DefaultFlurlClientFactory : IFlurlClientFactory
{
- ///
- /// Override in custom factory to customize the creation of HttpClient used in all Flurl HTTP calls
- /// (except when one is passed explicitly to the FlurlClient constructor). In order not to lose
- /// Flurl.Http functionality, it is recommended to call base.CreateClient and customize the result.
- ///
+ ///
public virtual HttpClient CreateHttpClient(HttpMessageHandler handler) {
return new HttpClient(handler);
}
///
- /// Override in custom factory to customize the creation of the top-level HttpMessageHandler used in all
- /// Flurl HTTP calls (except when an HttpClient is passed explicitly to the FlurlClient constructor).
- /// In order not to lose Flurl.Http functionality, it is recommended to call base.CreateMessageHandler, and
- /// either customize the returned HttpClientHandler, or set it as the InnerHandler of a DelegatingHandler.
+ /// Creates and configures a new HttpMessageHandler as needed when a new IFlurlClient instance is created.
///
public virtual HttpMessageHandler CreateInnerHandler() {
- var httpClientHandler = new HttpClientHandler();
+ // Flurl has its own mechanisms for managing cookies and redirects, so we need to disable them in the inner handler.
+#if NETCOREAPP2_1_OR_GREATER
+ var handler = new SocketsHttpHandler {
+ UseCookies = false,
+ AllowAutoRedirect = false,
+ AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
+ };
+#else
+ var handler = new HttpClientHandler();
- // flurl has its own mechanisms for managing cookies and redirects
+ if (handler.SupportsRedirectConfiguration)
+ handler.AllowAutoRedirect = false;
- try { httpClientHandler.UseCookies = false; }
- catch (PlatformNotSupportedException) { } // look out for WASM platforms (#543)
+ // #266
+ // deflate not working? see #474
+ if (handler.SupportsAutomaticDecompression)
+ handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
- if (httpClientHandler.SupportsRedirectConfiguration)
- httpClientHandler.AllowAutoRedirect = false;
-
- if (httpClientHandler.SupportsAutomaticDecompression) {
- // #266
- // deflate not working? see #474
- httpClientHandler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
- }
- return httpClientHandler;
+ try { handler.UseCookies = false; }
+ catch (PlatformNotSupportedException) { } // look out for WASM platforms (#543)
+#endif
+ return handler;
}
}
}
diff --git a/test/Flurl.Test/Http/FlurlClientBuilderTests.cs b/test/Flurl.Test/Http/FlurlClientBuilderTests.cs
index f30e37a6..527fdd42 100644
--- a/test/Flurl.Test/Http/FlurlClientBuilderTests.cs
+++ b/test/Flurl.Test/Http/FlurlClientBuilderTests.cs
@@ -1,8 +1,6 @@
using System;
-using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
-using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Flurl.Http;
@@ -52,6 +50,21 @@ public async Task can_add_middleware() {
Assert.AreEqual("blocked by flurl!", resp);
}
+ [Test]
+ public void inner_hanlder_is_SocketsHttpHandler_when_supported() {
+ var supported = typeof(HttpClientHandler).Assembly.DefinedTypes.Any(t => t.Name == "SocketsHttpHandler");
+#if NET
+ Assert.IsTrue(supported, "SocketsHttpHandler should be defined"); // sanity check (tests the test, basically)
+#endif
+ HttpMessageHandler handler = null;
+ new FlurlClientBuilder()
+ .ConfigureInnerHandler(h => handler = h)
+ .Build();
+
+ var expected = supported ? "SocketsHttpHandler" : "HttpClientHandler";
+ Assert.AreEqual(expected, handler?.GetType().Name);
+ }
+
class BlockingHandler : DelegatingHandler
{
private readonly string _msg;