diff --git a/.editorconfig b/.editorconfig
index 3856786..5b17dc0 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -1,7 +1,7 @@
root = true
[*]
-indent_style = tab
+indent_style = space
# Code files
[*.{cs,csx,vb,vbx}]
diff --git a/HTTPlease.sln b/HTTPlease.sln
index 2099d31..99d9eeb 100644
--- a/HTTPlease.sln
+++ b/HTTPlease.sln
@@ -1,6 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 15
-VisualStudioVersion = 15.0.27130.2024
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.28729.10
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{E084508E-FBEB-4A72-9C38-C34E270E8C2B}"
ProjectSection(SolutionItems) = preProject
@@ -37,6 +37,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HTTPlease.Diagnostics", "sr
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HTTPlease.Diagnostics.Tests", "test\HTTPlease.Diagnostics.Tests\HTTPlease.Diagnostics.Tests.csproj", "{7252EB2F-2373-40CD-A7B6-B24919B974AF}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HTTPlease.TestHarness", "test\HTTPlease.TestHarness\HTTPlease.TestHarness.csproj", "{A0756A83-C331-4901-B13B-31010B14784E}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -203,6 +205,18 @@ Global
{7252EB2F-2373-40CD-A7B6-B24919B974AF}.Release|x64.Build.0 = Release|Any CPU
{7252EB2F-2373-40CD-A7B6-B24919B974AF}.Release|x86.ActiveCfg = Release|Any CPU
{7252EB2F-2373-40CD-A7B6-B24919B974AF}.Release|x86.Build.0 = Release|Any CPU
+ {A0756A83-C331-4901-B13B-31010B14784E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A0756A83-C331-4901-B13B-31010B14784E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A0756A83-C331-4901-B13B-31010B14784E}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {A0756A83-C331-4901-B13B-31010B14784E}.Debug|x64.Build.0 = Debug|Any CPU
+ {A0756A83-C331-4901-B13B-31010B14784E}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {A0756A83-C331-4901-B13B-31010B14784E}.Debug|x86.Build.0 = Debug|Any CPU
+ {A0756A83-C331-4901-B13B-31010B14784E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A0756A83-C331-4901-B13B-31010B14784E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A0756A83-C331-4901-B13B-31010B14784E}.Release|x64.ActiveCfg = Release|Any CPU
+ {A0756A83-C331-4901-B13B-31010B14784E}.Release|x64.Build.0 = Release|Any CPU
+ {A0756A83-C331-4901-B13B-31010B14784E}.Release|x86.ActiveCfg = Release|Any CPU
+ {A0756A83-C331-4901-B13B-31010B14784E}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -221,6 +235,7 @@ Global
{B7DE2327-D1E3-4F30-8603-C7277A304A73} = {244D5A84-F66C-4628-A33A-5FBA3E9895B4}
{5355EEF6-2E9D-452F-A3C8-4C9F38D39EF1} = {244D5A84-F66C-4628-A33A-5FBA3E9895B4}
{7252EB2F-2373-40CD-A7B6-B24919B974AF} = {D91C25AB-6BDD-485E-8375-B638972DC8EF}
+ {A0756A83-C331-4901-B13B-31010B14784E} = {D91C25AB-6BDD-485E-8375-B638972DC8EF}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {9A554FA4-21BC-4666-8041-2FB39C103BAB}
diff --git a/docs/source/development/contributing/index.rst b/docs/source/development/contributing/index.rst
index d2d5872..e6eabf7 100644
--- a/docs/source/development/contributing/index.rst
+++ b/docs/source/development/contributing/index.rst
@@ -28,7 +28,7 @@ Some basic guidelines:
* Please don't use auto-generated comments - all this does is make it harder to know what members actually have meaningful comments.
* If you really object to adding doc comments, make a note in the pull request and we'll try to add them for you
-* Tabs, not spaces
+* Spaces, not tabs
* Please don't use the ``var`` keyword unless:
* There is a type on the right-hand side of the expression (e.g. ``new Foo()``)
diff --git a/src/HTTPlease.Core/ClientBuilder.cs b/src/HTTPlease.Core/ClientBuilder.cs
index 55cb51f..5f59b32 100644
--- a/src/HTTPlease.Core/ClientBuilder.cs
+++ b/src/HTTPlease.Core/ClientBuilder.cs
@@ -12,417 +12,417 @@ namespace HTTPlease
using Core.Utilities;
///
- /// Builds s with pipelines of HTTP message handlers.
+ /// Builds s with pipelines of HTTP message handlers.
///
///
- /// Be aware that, if you return singleton instances of message handlers from factory delegates, those handlers will be disposed if the factory encounters any exception while creating a client.
+ /// Be aware that, if you return singleton instances of message handlers from factory delegates, those handlers will be disposed if the factory encounters any exception while creating a client.
///
public sealed class ClientBuilder
- {
- ///
- /// The default factory for message-pipeline terminus handlers.
- ///
- static readonly Func DefaultMessagePipelineTerminus = existingHandler => new HttpClientHandler();
-
- ///
- /// Factory delegates used to produce the HTTP message handlers that comprise client pipelines.
- ///
- ImmutableList> _handlerFactories = ImmutableList>.Empty;
-
- ///
- /// Delegates to create or modify the that will form the message pipeline terminus.
+ {
+ ///
+ /// The default factory for message-pipeline terminus handlers.
///
- ///
- /// Each delegate is passed the result of the previous delegate (if any).
- ///
- /// Can be overridden by the value passed to CreateClient.
+ static readonly Func DefaultMessagePipelineTerminus = existingHandler => new HttpClientHandler();
+
+ ///
+ /// Factory delegates used to produce the HTTP message handlers that comprise client pipelines.
+ ///
+ ImmutableList> _handlerFactories = ImmutableList>.Empty;
+
+ ///
+ /// Delegates to create or modify the that will form the message pipeline terminus.
+ ///
+ ///
+ /// Each delegate is passed the result of the previous delegate (if any).
+ ///
+ /// Can be overridden by the value passed to CreateClient.
///
- ImmutableList> _pipelineTerminusConfigurators = ImmutableList.Create(DefaultMessagePipelineTerminus);
-
- ///
- /// Create a new HTTP client builder.
- ///
- public ClientBuilder()
- {
- }
-
- ///
- /// Create a new HTTP client builder.
- ///
- ///
- /// The HTTP client buider to copy configuration from.
- ///
- ClientBuilder(ClientBuilder copyFrom)
- {
- if (copyFrom == null)
- throw new ArgumentNullException(nameof(copyFrom));
-
- _handlerFactories = copyFrom._handlerFactories;
- _pipelineTerminusConfigurators = copyFrom._pipelineTerminusConfigurators;
- }
-
- ///
- /// Create an using the configured message-handler pipeline.
- ///
- ///
- /// An optional base URI for the .
- ///
- ///
- /// An optional that will form the message pipeline terminus.
- ///
- /// If not specified, the pre-configured message pipeline terminus is used.
- ///
- ///
- /// The new .
- ///
- public HttpClient CreateClient(Uri baseUri = null, HttpMessageHandler messagePipelineTerminus = null)
- {
- HttpMessageHandler pipelineTerminus = null;
- List pipelineHandlers = new List();
- HttpMessageHandler pipeline = null;
- HttpClient client = null;
-
- try
- {
- pipelineTerminus = messagePipelineTerminus ?? BuildMessagePipelineTerminus();
-
- foreach (Func handlerFactory in _handlerFactories)
- {
- DelegatingHandler currentHandler = null;
- try
- {
- currentHandler = handlerFactory();
- }
- catch
- {
- using (currentHandler)
- throw;
- }
- pipelineHandlers.Add(currentHandler);
- }
-
- pipeline = CreatePipeline(pipelineTerminus, pipelineHandlers);
-
- client = new HttpClient(pipeline);
- if (baseUri != null)
- client.BaseAddress = baseUri;
- }
- catch
- {
- using (pipelineTerminus)
- using (pipelineHandlers.ToAggregateDisposable())
- using (pipeline)
- using (client)
- {
- throw;
- }
- }
-
- return client;
- }
-
- ///
- /// Create a copy of the , but with the specified configuration for its message pipeline terminus.
+ ImmutableList> _pipelineTerminusConfigurators = ImmutableList.Create(DefaultMessagePipelineTerminus);
+
+ ///
+ /// Create a new HTTP client builder.
+ ///
+ public ClientBuilder()
+ {
+ }
+
+ ///
+ /// Create a new HTTP client builder.
+ ///
+ ///
+ /// The HTTP client buider to copy configuration from.
+ ///
+ ClientBuilder(ClientBuilder copyFrom)
+ {
+ if (copyFrom == null)
+ throw new ArgumentNullException(nameof(copyFrom));
+
+ _handlerFactories = copyFrom._handlerFactories;
+ _pipelineTerminusConfigurators = copyFrom._pipelineTerminusConfigurators;
+ }
+
+ ///
+ /// Create an using the configured message-handler pipeline.
+ ///
+ ///
+ /// An optional base URI for the .
+ ///
+ ///
+ /// An optional that will form the message pipeline terminus.
+ ///
+ /// If not specified, the pre-configured message pipeline terminus is used.
+ ///
+ ///
+ /// The new .
+ ///
+ public HttpClient CreateClient(Uri baseUri = null, HttpMessageHandler messagePipelineTerminus = null)
+ {
+ HttpMessageHandler pipelineTerminus = null;
+ List pipelineHandlers = new List();
+ HttpMessageHandler pipeline = null;
+ HttpClient client = null;
+
+ try
+ {
+ pipelineTerminus = messagePipelineTerminus ?? BuildMessagePipelineTerminus();
+
+ foreach (Func handlerFactory in _handlerFactories)
+ {
+ DelegatingHandler currentHandler = null;
+ try
+ {
+ currentHandler = handlerFactory();
+ }
+ catch
+ {
+ using (currentHandler)
+ throw;
+ }
+ pipelineHandlers.Add(currentHandler);
+ }
+
+ pipeline = CreatePipeline(pipelineTerminus, pipelineHandlers);
+
+ client = new HttpClient(pipeline);
+ if (baseUri != null)
+ client.BaseAddress = baseUri;
+ }
+ catch
+ {
+ using (pipelineTerminus)
+ using (pipelineHandlers.ToAggregateDisposable())
+ using (pipeline)
+ using (client)
+ {
+ throw;
+ }
+ }
+
+ return client;
+ }
+
+ ///
+ /// Create a copy of the , but with the specified configuration for its message pipeline terminus.
///
///
- /// A delegate that creates the for each that will form its message pipeline terminus.
- ///
- /// If null, the default message handler pipeline terminus will be used.
- ///
+ /// A delegate that creates the for each that will form its message pipeline terminus.
+ ///
+ /// If null, the default message handler pipeline terminus will be used.
+ ///
///
- /// The configured .
- ///
- public ClientBuilder WithMessagePipelineTerminus(Func pipelineTerminusConfigurator)
- {
- return new ClientBuilder(this)
- {
- _pipelineTerminusConfigurators = _pipelineTerminusConfigurators.Add(
- pipelineTerminusConfigurator ?? DefaultMessagePipelineTerminus
- )
- };
- }
-
- ///
- /// Create a copy of the , but with the specified message pipeline terminus.
+ /// The configured .
+ ///
+ public ClientBuilder WithMessagePipelineTerminus(Func pipelineTerminusConfigurator)
+ {
+ return new ClientBuilder(this)
+ {
+ _pipelineTerminusConfigurators = _pipelineTerminusConfigurators.Add(
+ pipelineTerminusConfigurator ?? DefaultMessagePipelineTerminus
+ )
+ };
+ }
+
+ ///
+ /// Create a copy of the , but with the specified message pipeline terminus.
///
///
- /// A delegate that creates the for each that will form its message pipeline terminus.
- ///
- /// If null, the default message handler pipeline terminus will be used.
- ///
+ /// A delegate that creates the for each that will form its message pipeline terminus.
+ ///
+ /// If null, the default message handler pipeline terminus will be used.
+ ///
+ ///
+ /// The configured .
+ ///
+ public ClientBuilder WithMessagePipelineTerminus(Func pipelineTerminusFactory)
+ {
+ Func configurator = DefaultMessagePipelineTerminus;
+ if (pipelineTerminusFactory != null)
+ configurator = _ => pipelineTerminusFactory();
+
+ return new ClientBuilder(this)
+ {
+ _pipelineTerminusConfigurators = ImmutableList.Create(configurator) // Replaces any existing configurators.
+ };
+ }
+
+ ///
+ /// Create a copy of the , adding an HTTP message-handler factory to the end of the pipeline.
+ ///
+ ///
+ /// The handler type.
+ ///
+ ///
+ /// The message-handler factory.
+ ///
+ ///
+ /// The (enables method-chaining).
+ ///
+ ///
+ /// cannot be the base class.
+ ///
+ public ClientBuilder AddHandler(Func handlerFactory)
+ where THandler : DelegatingHandler
+ {
+ if (handlerFactory == null)
+ throw new ArgumentNullException(nameof(handlerFactory));
+
+ if (typeof(THandler) == typeof(DelegatingHandler))
+ throw new InvalidOperationException("Handler type cannot be the DelegatingHandler base class.");
+
+ if (_handlerFactories.OfType>().Any())
+ {
+ throw new InvalidOperationException(
+ String.Format(
+ "The configured handler pipeline already contains a factory for message-handlers of type '{0}'.",
+ typeof(THandler).AssemblyQualifiedName
+ )
+ );
+ }
+
+ return new ClientBuilder(this)
+ {
+ _handlerFactories = _handlerFactories.Add(handlerFactory)
+ };
+ }
+
+ ///
+ /// Create a copy of the , inserting an HTTP message-handler factory to the pipeline before the factory that produces handlers of the specified type.
+ ///
+ ///
+ /// The handler type.
+ ///
+ ///
+ /// The type of handler before whose factory the new handler factory should be inserted.
+ ///
+ ///
+ /// The message-handler factory.
+ ///
+ ///
+ /// Throw an if no factory for is present?
+ ///
+ /// Default is false.
+ ///
+ ///
+ /// The (enables method-chaining).
+ ///
+ ///
+ /// and cannot be the base class.
+ ///
+ public ClientBuilder AddHandlerBefore(Func handlerFactory, bool throwIfNotPresent = false)
+ where THandler : DelegatingHandler
+ where TBeforeHandler : DelegatingHandler
+ {
+ if (handlerFactory == null)
+ throw new ArgumentNullException(nameof(handlerFactory));
+
+ if (typeof(THandler) == typeof(DelegatingHandler))
+ throw new InvalidOperationException("Handler type cannot be the DelegatingHandler base class.");
+
+ if (typeof(THandler) == typeof(DelegatingHandler))
+ throw new InvalidOperationException("Handler type cannot be the DelegatingHandler base class.");
+
+ if (_handlerFactories.OfType>().Any())
+ {
+ throw new InvalidOperationException(
+ String.Format(
+ "The configured handler pipeline already contains a factory for message-handlers of type '{0}'.",
+ typeof(THandler).AssemblyQualifiedName
+ )
+ );
+ }
+
+ Type beforeHandlerFactoryType = typeof(Func);
+ for (int handlerIndex = 0; handlerIndex < _handlerFactories.Count; handlerIndex++)
+ {
+ if (_handlerFactories[handlerIndex].GetType() == beforeHandlerFactoryType)
+ {
+ return new ClientBuilder(this)
+ {
+ _handlerFactories = _handlerFactories.Insert(handlerIndex, handlerFactory)
+ };
+ }
+ }
+
+ if (throwIfNotPresent)
+ {
+ throw new InvalidOperationException(
+ String.Format(
+ "Cannot insert factory for message-handlers of type '{0}' before the factory for message-handlers of type '{1}' (the pipeline does not contain a factory for message-handlers of this type.",
+ typeof(THandler).AssemblyQualifiedName,
+ typeof(TBeforeHandler).AssemblyQualifiedName
+ )
+ );
+ }
+
+ // TBefore is not present, so just append to the end of the pipeline.
+ return new ClientBuilder(this)
+ {
+ _handlerFactories = _handlerFactories.Add(handlerFactory)
+ };
+ }
+
+ ///
+ /// Create a copy of the , inserting an HTTP message-handler factory to the pipeline after the factory that produces handlers of the specified type.
+ ///
+ ///
+ /// The handler type.
+ ///
+ ///
+ /// The type of handler after whose factory the new handler factory should be inserted.
+ ///
+ ///
+ /// The message-handler factory.
+ ///
+ ///
+ /// Throw an if no factory for is present?
+ ///
+ /// Default is false.
+ ///
+ ///
+ /// The (enables method-chaining).
+ ///
+ ///
+ /// and cannot be the base class.
+ ///
+ public ClientBuilder AddHandlerAfter(Func handlerFactory, bool throwIfNotPresent = false)
+ where THandler : DelegatingHandler
+ where TAfterHandler : DelegatingHandler
+ {
+ if (handlerFactory == null)
+ throw new ArgumentNullException(nameof(handlerFactory));
+
+ if (typeof(THandler) == typeof(DelegatingHandler))
+ throw new InvalidOperationException("Handler type cannot be the DelegatingHandler base class.");
+
+ if (typeof(THandler) == typeof(DelegatingHandler))
+ throw new InvalidOperationException("Handler type cannot be the DelegatingHandler base class.");
+
+ if (_handlerFactories.OfType>().Any())
+ {
+ throw new InvalidOperationException(
+ String.Format(
+ "The configured handler pipeline already contains a factory for message-handlers of type '{0}'.",
+ typeof(THandler).AssemblyQualifiedName
+ )
+ );
+ }
+
+ Type afterHandlerFactoryType = typeof(Func);
+ for (int handlerIndex = 0; handlerIndex < _handlerFactories.Count; handlerIndex++)
+ {
+ if (_handlerFactories[handlerIndex].GetType() == afterHandlerFactoryType)
+ {
+ return new ClientBuilder(this)
+ {
+ _handlerFactories = _handlerFactories.Insert(handlerIndex + 1, handlerFactory)
+ };
+ }
+ }
+
+ if (throwIfNotPresent)
+ {
+ throw new InvalidOperationException(
+ String.Format(
+ "Cannot insert factory for message-handlers of type '{0}' after the factory for message-handlers of type '{1}' (the pipeline does not contain a factory for message-handlers of this type.",
+ typeof(THandler).AssemblyQualifiedName,
+ typeof(TAfterHandler).AssemblyQualifiedName
+ )
+ );
+ }
+
+ // TAfter is not present, so just append to the end of the pipeline.
+ return new ClientBuilder(this)
+ {
+ _handlerFactories = _handlerFactories.Add(handlerFactory)
+ };
+ }
+
+ ///
+ /// Enumerate the types of handlers configured in the factory's pipeline.
+ ///
+ ///
+ /// A sequence of 0 or more types.
+ ///
+ ///
+ /// This operation uses Reflection, so it can be relatively expensive; use sparingly.
+ ///
+ public IEnumerable EnumerateHandlerTypes()
+ {
+ for (int handlerIndex = 0; handlerIndex < _handlerFactories.Count; handlerIndex++)
+ {
+ Func factory = _handlerFactories[handlerIndex];
+ Type factoryDelegateType = factory.GetType();
+
+ yield return factoryDelegateType.GenericTypeArguments[0];
+ }
+ }
+
+ ///
+ /// Create an HTTP message-handler pipeline.
+ ///
+ ///
+ /// An representing the terminus of the pipeline.
+ ///
+ ///
+ /// A sequence of s representing additional steps in the pipeline.
+ ///
+ ///
+ /// An representing the head of the pipeline.
+ ///
+ static HttpMessageHandler CreatePipeline(HttpMessageHandler pipelineTerminus, IEnumerable pipelineHandlers)
+ {
+ if (pipelineTerminus == null)
+ throw new ArgumentNullException(nameof(pipelineTerminus));
+
+ if (pipelineHandlers == null)
+ throw new ArgumentNullException(nameof(pipelineHandlers));
+
+ HttpMessageHandler pipeline = pipelineTerminus;
+ foreach (DelegatingHandler pipelineHandler in pipelineHandlers.Reverse())
+ {
+ pipelineHandler.InnerHandler = pipeline;
+ pipeline = pipelineHandler;
+ }
+
+ return pipeline;
+ }
+
+ ///
+ /// Build / configure an HTTP message handler to act as the message pipeline terminus.
+ ///
///
- /// The configured .
- ///
- public ClientBuilder WithMessagePipelineTerminus(Func pipelineTerminusFactory)
- {
- Func configurator = DefaultMessagePipelineTerminus;
- if (pipelineTerminusFactory != null)
- configurator = _ => pipelineTerminusFactory();
-
- return new ClientBuilder(this)
- {
- _pipelineTerminusConfigurators = ImmutableList.Create(configurator) // Replaces any existing configurators.
- };
- }
-
- ///
- /// Create a copy of the , adding an HTTP message-handler factory to the end of the pipeline.
- ///
- ///
- /// The handler type.
- ///
- ///
- /// The message-handler factory.
- ///
- ///
- /// The (enables method-chaining).
- ///
- ///
- /// cannot be the base class.
- ///
- public ClientBuilder AddHandler(Func handlerFactory)
- where THandler : DelegatingHandler
- {
- if (handlerFactory == null)
- throw new ArgumentNullException(nameof(handlerFactory));
-
- if (typeof(THandler) == typeof(DelegatingHandler))
- throw new InvalidOperationException("Handler type cannot be the DelegatingHandler base class.");
-
- if (_handlerFactories.OfType>().Any())
- {
- throw new InvalidOperationException(
- String.Format(
- "The configured handler pipeline already contains a factory for message-handlers of type '{0}'.",
- typeof(THandler).AssemblyQualifiedName
- )
- );
- }
-
- return new ClientBuilder(this)
- {
- _handlerFactories = _handlerFactories.Add(handlerFactory)
- };
- }
-
- ///
- /// Create a copy of the , inserting an HTTP message-handler factory to the pipeline before the factory that produces handlers of the specified type.
- ///
- ///
- /// The handler type.
- ///
- ///
- /// The type of handler before whose factory the new handler factory should be inserted.
- ///
- ///
- /// The message-handler factory.
- ///
- ///
- /// Throw an if no factory for is present?
- ///
- /// Default is false.
- ///
- ///
- /// The (enables method-chaining).
- ///
- ///
- /// and cannot be the base class.
- ///
- public ClientBuilder AddHandlerBefore(Func handlerFactory, bool throwIfNotPresent = false)
- where THandler : DelegatingHandler
- where TBeforeHandler : DelegatingHandler
- {
- if (handlerFactory == null)
- throw new ArgumentNullException(nameof(handlerFactory));
-
- if (typeof(THandler) == typeof(DelegatingHandler))
- throw new InvalidOperationException("Handler type cannot be the DelegatingHandler base class.");
-
- if (typeof(THandler) == typeof(DelegatingHandler))
- throw new InvalidOperationException("Handler type cannot be the DelegatingHandler base class.");
-
- if (_handlerFactories.OfType>().Any())
- {
- throw new InvalidOperationException(
- String.Format(
- "The configured handler pipeline already contains a factory for message-handlers of type '{0}'.",
- typeof(THandler).AssemblyQualifiedName
- )
- );
- }
-
- Type beforeHandlerFactoryType = typeof(Func);
- for (int handlerIndex = 0; handlerIndex < _handlerFactories.Count; handlerIndex++)
- {
- if (_handlerFactories[handlerIndex].GetType() == beforeHandlerFactoryType)
- {
- return new ClientBuilder(this)
- {
- _handlerFactories = _handlerFactories.Insert(handlerIndex, handlerFactory)
- };
- }
- }
-
- if (throwIfNotPresent)
- {
- throw new InvalidOperationException(
- String.Format(
- "Cannot insert factory for message-handlers of type '{0}' before the factory for message-handlers of type '{1}' (the pipeline does not contain a factory for message-handlers of this type.",
- typeof(THandler).AssemblyQualifiedName,
- typeof(TBeforeHandler).AssemblyQualifiedName
- )
- );
- }
-
- // TBefore is not present, so just append to the end of the pipeline.
- return new ClientBuilder(this)
- {
- _handlerFactories = _handlerFactories.Add(handlerFactory)
- };
- }
-
- ///
- /// Create a copy of the , inserting an HTTP message-handler factory to the pipeline after the factory that produces handlers of the specified type.
- ///
- ///
- /// The handler type.
- ///
- ///
- /// The type of handler after whose factory the new handler factory should be inserted.
- ///
- ///
- /// The message-handler factory.
- ///
- ///
- /// Throw an if no factory for is present?
- ///
- /// Default is false.
- ///
- ///
- /// The (enables method-chaining).
- ///
- ///
- /// and cannot be the base class.
- ///
- public ClientBuilder AddHandlerAfter(Func handlerFactory, bool throwIfNotPresent = false)
- where THandler : DelegatingHandler
- where TAfterHandler : DelegatingHandler
- {
- if (handlerFactory == null)
- throw new ArgumentNullException(nameof(handlerFactory));
-
- if (typeof(THandler) == typeof(DelegatingHandler))
- throw new InvalidOperationException("Handler type cannot be the DelegatingHandler base class.");
-
- if (typeof(THandler) == typeof(DelegatingHandler))
- throw new InvalidOperationException("Handler type cannot be the DelegatingHandler base class.");
-
- if (_handlerFactories.OfType>().Any())
- {
- throw new InvalidOperationException(
- String.Format(
- "The configured handler pipeline already contains a factory for message-handlers of type '{0}'.",
- typeof(THandler).AssemblyQualifiedName
- )
- );
- }
-
- Type afterHandlerFactoryType = typeof(Func);
- for (int handlerIndex = 0; handlerIndex < _handlerFactories.Count; handlerIndex++)
- {
- if (_handlerFactories[handlerIndex].GetType() == afterHandlerFactoryType)
- {
- return new ClientBuilder(this)
- {
- _handlerFactories = _handlerFactories.Insert(handlerIndex + 1, handlerFactory)
- };
- }
- }
-
- if (throwIfNotPresent)
- {
- throw new InvalidOperationException(
- String.Format(
- "Cannot insert factory for message-handlers of type '{0}' after the factory for message-handlers of type '{1}' (the pipeline does not contain a factory for message-handlers of this type.",
- typeof(THandler).AssemblyQualifiedName,
- typeof(TAfterHandler).AssemblyQualifiedName
- )
- );
- }
-
- // TAfter is not present, so just append to the end of the pipeline.
- return new ClientBuilder(this)
- {
- _handlerFactories = _handlerFactories.Add(handlerFactory)
- };
- }
-
- ///
- /// Enumerate the types of handlers configured in the factory's pipeline.
- ///
- ///
- /// A sequence of 0 or more types.
- ///
- ///
- /// This operation uses Reflection, so it can be relatively expensive; use sparingly.
- ///
- public IEnumerable EnumerateHandlerTypes()
- {
- for (int handlerIndex = 0; handlerIndex < _handlerFactories.Count; handlerIndex++)
- {
- Func factory = _handlerFactories[handlerIndex];
- Type factoryDelegateType = factory.GetType();
-
- yield return factoryDelegateType.GenericTypeArguments[0];
- }
- }
-
- ///
- /// Create an HTTP message-handler pipeline.
- ///
- ///
- /// An representing the terminus of the pipeline.
- ///
- ///
- /// A sequence of s representing additional steps in the pipeline.
- ///
- ///
- /// An representing the head of the pipeline.
- ///
- static HttpMessageHandler CreatePipeline(HttpMessageHandler pipelineTerminus, IEnumerable pipelineHandlers)
- {
- if (pipelineTerminus == null)
- throw new ArgumentNullException(nameof(pipelineTerminus));
-
- if (pipelineHandlers == null)
- throw new ArgumentNullException(nameof(pipelineHandlers));
-
- HttpMessageHandler pipeline = pipelineTerminus;
- foreach (DelegatingHandler pipelineHandler in pipelineHandlers.Reverse())
- {
- pipelineHandler.InnerHandler = pipeline;
- pipeline = pipelineHandler;
- }
-
- return pipeline;
- }
-
- ///
- /// Build / configure an HTTP message handler to act as the message pipeline terminus.
- ///
- ///
- /// The configured .
- ///
- HttpMessageHandler BuildMessagePipelineTerminus()
- {
- HttpMessageHandler pipelineTerminus = null;
- foreach (Func terminusConfiguration in _pipelineTerminusConfigurators)
- pipelineTerminus = terminusConfiguration(pipelineTerminus);
-
- if (pipelineTerminus == null)
- throw new InvalidOperationException("One or more configuration delegates for the message pipeline terminus returned null.");
-
- return pipelineTerminus;
- }
- }
+ /// The configured .
+ ///
+ HttpMessageHandler BuildMessagePipelineTerminus()
+ {
+ HttpMessageHandler pipelineTerminus = null;
+ foreach (Func terminusConfiguration in _pipelineTerminusConfigurators)
+ pipelineTerminus = terminusConfiguration(pipelineTerminus);
+
+ if (pipelineTerminus == null)
+ throw new InvalidOperationException("One or more configuration delegates for the message pipeline terminus returned null.");
+
+ return pipelineTerminus;
+ }
+ }
}
diff --git a/src/HTTPlease.Core/ClientBuilderExtensions.cs b/src/HTTPlease.Core/ClientBuilderExtensions.cs
index 04f35e1..125e687 100644
--- a/src/HTTPlease.Core/ClientBuilderExtensions.cs
+++ b/src/HTTPlease.Core/ClientBuilderExtensions.cs
@@ -6,219 +6,219 @@
namespace HTTPlease
{
- ///
- /// General-purpose extensions for .
- ///
- public static class ClientBuilderExtensions
- {
- ///
- /// The CLR type.
- ///
- static readonly Type DelegatingHandlerType = typeof(DelegatingHandler);
+ ///
+ /// General-purpose extensions for .
+ ///
+ public static class ClientBuilderExtensions
+ {
+ ///
+ /// The CLR type.
+ ///
+ static readonly Type DelegatingHandlerType = typeof(DelegatingHandler);
- ///
- /// Create a new .
- ///
- ///
- /// The HTTP client builder.
- ///
- ///
- /// The base URI for the HTTP client.
- ///
- ///
- /// The client credentials used for authentication.
- ///
- ///
- /// The new .
- ///
- public static HttpClient CreateClient(this ClientBuilder clientBuilder, Uri baseUri, ICredentials credentials)
- {
- HttpClientHandler clientHandler = null;
- try
- {
- clientHandler = new HttpClientHandler
- {
- Credentials = credentials
- };
+ ///
+ /// Create a new .
+ ///
+ ///
+ /// The HTTP client builder.
+ ///
+ ///
+ /// The base URI for the HTTP client.
+ ///
+ ///
+ /// The client credentials used for authentication.
+ ///
+ ///
+ /// The new .
+ ///
+ public static HttpClient CreateClient(this ClientBuilder clientBuilder, Uri baseUri, ICredentials credentials)
+ {
+ HttpClientHandler clientHandler = null;
+ try
+ {
+ clientHandler = new HttpClientHandler
+ {
+ Credentials = credentials
+ };
- return clientBuilder.CreateClient(baseUri, clientHandler);
- }
- catch (Exception)
- {
- using (clientHandler)
- {
- throw;
- }
- }
- }
+ return clientBuilder.CreateClient(baseUri, clientHandler);
+ }
+ catch (Exception)
+ {
+ using (clientHandler)
+ {
+ throw;
+ }
+ }
+ }
- ///
- /// Create a new .
- ///
- ///
- /// The HTTP client builder.
- ///
- ///
- /// The base URI for the HTTP client.
- ///
- ///
- /// The client credentials used for authentication.
- ///
- ///
- /// The new .
- ///
- public static HttpClient CreateClient(this ClientBuilder clientBuilder, string baseUri, ICredentials credentials)
- {
- HttpClientHandler clientHandler = null;
- try
- {
- clientHandler = new HttpClientHandler
- {
- Credentials = credentials
- };
+ ///
+ /// Create a new .
+ ///
+ ///
+ /// The HTTP client builder.
+ ///
+ ///
+ /// The base URI for the HTTP client.
+ ///
+ ///
+ /// The client credentials used for authentication.
+ ///
+ ///
+ /// The new .
+ ///
+ public static HttpClient CreateClient(this ClientBuilder clientBuilder, string baseUri, ICredentials credentials)
+ {
+ HttpClientHandler clientHandler = null;
+ try
+ {
+ clientHandler = new HttpClientHandler
+ {
+ Credentials = credentials
+ };
- return clientBuilder.CreateClient(baseUri, clientHandler);
- }
- catch (Exception)
- {
- using (clientHandler)
- {
- throw;
- }
- }
- }
+ return clientBuilder.CreateClient(baseUri, clientHandler);
+ }
+ catch (Exception)
+ {
+ using (clientHandler)
+ {
+ throw;
+ }
+ }
+ }
- ///
- /// Create a new .
- ///
- ///
- /// The HTTP client builder.
- ///
- ///
- /// The base URI for the HTTP client.
- ///
- ///
- /// An optional that will form the message pipeline terminus.
- ///
- /// If not specified, a new is used.
- ///
- ///
- /// The new .
- ///
- public static HttpClient CreateClient(this ClientBuilder clientBuilder, string baseUri, HttpMessageHandler messagePipelineTerminus = null)
- {
- if (clientBuilder == null)
- throw new ArgumentNullException(nameof(clientBuilder));
+ ///
+ /// Create a new .
+ ///
+ ///
+ /// The HTTP client builder.
+ ///
+ ///
+ /// The base URI for the HTTP client.
+ ///
+ ///
+ /// An optional that will form the message pipeline terminus.
+ ///
+ /// If not specified, a new is used.
+ ///
+ ///
+ /// The new .
+ ///
+ public static HttpClient CreateClient(this ClientBuilder clientBuilder, string baseUri, HttpMessageHandler messagePipelineTerminus = null)
+ {
+ if (clientBuilder == null)
+ throw new ArgumentNullException(nameof(clientBuilder));
- if (String.IsNullOrWhiteSpace(baseUri))
- throw new ArgumentException("Argument cannot be null, empty, or composed entirely of whitespace: 'baseUri'.", nameof(baseUri));
+ if (String.IsNullOrWhiteSpace(baseUri))
+ throw new ArgumentException("Argument cannot be null, empty, or composed entirely of whitespace: 'baseUri'.", nameof(baseUri));
- return clientBuilder.CreateClient(
- new Uri(baseUri, UriKind.Absolute),
- messagePipelineTerminus
- );
- }
+ return clientBuilder.CreateClient(
+ new Uri(baseUri, UriKind.Absolute),
+ messagePipelineTerminus
+ );
+ }
- ///
- /// Register a message handler type for the pipeline used by clients created by the factory.
- ///
- ///
- /// The message handler type.
- ///
- /// Must be a sub-type of (not itself).
- ///
- ///
- /// The HTTP client builder.
- ///
- ///
- /// The client factory (enables inline use / method chaining).
- ///
- public static ClientBuilder WithMessageHandler(this ClientBuilder clientBuilder)
- where TMessageHandler : DelegatingHandler, new()
- {
- if (clientBuilder == null)
- throw new ArgumentNullException(nameof(clientBuilder));
+ ///
+ /// Register a message handler type for the pipeline used by clients created by the factory.
+ ///
+ ///
+ /// The message handler type.
+ ///
+ /// Must be a sub-type of (not itself).
+ ///
+ ///
+ /// The HTTP client builder.
+ ///
+ ///
+ /// The client factory (enables inline use / method chaining).
+ ///
+ public static ClientBuilder WithMessageHandler(this ClientBuilder clientBuilder)
+ where TMessageHandler : DelegatingHandler, new()
+ {
+ if (clientBuilder == null)
+ throw new ArgumentNullException(nameof(clientBuilder));
- if (typeof(TMessageHandler) == DelegatingHandlerType)
- throw new NotSupportedException("TMessageHandler must be a sub-type of DelegatingHandler (it cannot be DelegatingHandler).");
+ if (typeof(TMessageHandler) == DelegatingHandlerType)
+ throw new NotSupportedException("TMessageHandler must be a sub-type of DelegatingHandler (it cannot be DelegatingHandler).");
- clientBuilder.AddHandler(
- () => new TMessageHandler()
- );
+ clientBuilder.AddHandler(
+ () => new TMessageHandler()
+ );
- return clientBuilder;
- }
+ return clientBuilder;
+ }
- ///
- /// Create a copy of the , but with the specified configuration action for its (message pipeline terminus).
+ ///
+ /// Create a copy of the , but with the specified configuration action for its (message pipeline terminus).
///
- ///
- /// The HTTP client builder.
- ///
+ ///
+ /// The HTTP client builder.
+ ///
///
- /// A delegate that configures the that will form the message pipeline terminus for each produced by the builder.
- ///
+ /// A delegate that configures the that will form the message pipeline terminus for each produced by the builder.
+ ///
///
- /// The configured .
- ///
- public static ClientBuilder ConfigureHttpClientHandler(this ClientBuilder clientBuilder, Action clientHandlerConfigurator)
- {
- if (clientHandlerConfigurator == null)
- throw new ArgumentNullException(nameof(clientHandlerConfigurator));
-
- return clientBuilder.ConfigureMessagePipelineTerminus(clientHandlerConfigurator);
- }
+ /// The configured .
+ ///
+ public static ClientBuilder ConfigureHttpClientHandler(this ClientBuilder clientBuilder, Action clientHandlerConfigurator)
+ {
+ if (clientHandlerConfigurator == null)
+ throw new ArgumentNullException(nameof(clientHandlerConfigurator));
+
+ return clientBuilder.ConfigureMessagePipelineTerminus(clientHandlerConfigurator);
+ }
- ///
- /// Create a copy of the , but with the specified configuration action for its message pipeline terminus.
+ ///
+ /// Create a copy of the , but with the specified configuration action for its message pipeline terminus.
///
- ///
- /// The type of message handler to expect / configure.
- ///
- ///
- /// The HTTP client builder.
- ///
+ ///
+ /// The type of message handler to expect / configure.
+ ///
+ ///
+ /// The HTTP client builder.
+ ///
///
- /// A delegate that configures the that will form the message pipeline terminus for each produced by the builder.
- ///
+ /// A delegate that configures the that will form the message pipeline terminus for each produced by the builder.
+ ///
///
- /// The configured .
- ///
- public static ClientBuilder ConfigureMessagePipelineTerminus(this ClientBuilder clientBuilder, Action pipelineTerminusConfigurator)
- where TMessageHandler : HttpMessageHandler
- {
- if (pipelineTerminusConfigurator == null)
- throw new ArgumentNullException(nameof(pipelineTerminusConfigurator));
-
- return clientBuilder.WithMessagePipelineTerminus(existingTerminator =>
- {
- if (existingTerminator == null)
- throw new InvalidOperationException($"Cannot configure pipeline terminus (expected a handler of type '{typeof(TMessageHandler).FullName}', but the previous factory function returned null).");
+ /// The configured .
+ ///
+ public static ClientBuilder ConfigureMessagePipelineTerminus(this ClientBuilder clientBuilder, Action pipelineTerminusConfigurator)
+ where TMessageHandler : HttpMessageHandler
+ {
+ if (pipelineTerminusConfigurator == null)
+ throw new ArgumentNullException(nameof(pipelineTerminusConfigurator));
+
+ return clientBuilder.WithMessagePipelineTerminus(existingTerminator =>
+ {
+ if (existingTerminator == null)
+ throw new InvalidOperationException($"Cannot configure pipeline terminus (expected a handler of type '{typeof(TMessageHandler).FullName}', but the previous factory function returned null).");
- if (existingTerminator is TMessageHandler typedHandler)
- pipelineTerminusConfigurator(typedHandler);
- else
- throw new InvalidOperationException($"Cannot configure pipeline terminus (expected a handler of type '{typeof(TMessageHandler).FullName}', but the previous factory function returned a handler of type '{existingTerminator.GetType().FullName}').");
-
- return existingTerminator;
- });
- }
+ if (existingTerminator is TMessageHandler typedHandler)
+ pipelineTerminusConfigurator(typedHandler);
+ else
+ throw new InvalidOperationException($"Cannot configure pipeline terminus (expected a handler of type '{typeof(TMessageHandler).FullName}', but the previous factory function returned a handler of type '{existingTerminator.GetType().FullName}').");
+
+ return existingTerminator;
+ });
+ }
- ///
- /// Create a copy of the , but using the specified X.509 certificate for client authentication.
+ ///
+ /// Create a copy of the , but using the specified X.509 certificate for client authentication.
///
- ///
- /// The HTTP client builder.
- ///
+ ///
+ /// The HTTP client builder.
+ ///
///
- /// The X.509 certificate to use.
- ///
+ /// The X.509 certificate to use.
+ ///
///
- /// The configured .
- ///
- public static ClientBuilder WithClientCertificate(this ClientBuilder clientBuilder, X509Certificate2 clientCertificate)
- {
- if (clientBuilder == null)
+ /// The configured .
+ ///
+ public static ClientBuilder WithClientCertificate(this ClientBuilder clientBuilder, X509Certificate2 clientCertificate)
+ {
+ if (clientBuilder == null)
throw new ArgumentNullException(nameof(clientBuilder));
if (clientCertificate == null)
@@ -228,93 +228,93 @@ public static ClientBuilder WithClientCertificate(this ClientBuilder clientBuild
throw new InvalidOperationException($"Cannot use certificate '{clientCertificate.Subject}' as a client certificate (no private key is not available for it).");
return clientBuilder.ConfigureHttpClientHandler(clientHandler =>
- {
- clientHandler.ClientCertificates.Add(clientCertificate);
- clientHandler.ClientCertificateOptions = ClientCertificateOption.Manual;
- });
- }
+ {
+ clientHandler.ClientCertificates.Add(clientCertificate);
+ clientHandler.ClientCertificateOptions = ClientCertificateOption.Manual;
+ });
+ }
- ///
- /// Create a copy of the , but using the specified X.509 certificate for server authentication.
+ ///
+ /// Create a copy of the , but using the specified X.509 certificate for server authentication.
///
- ///
- /// The HTTP client builder.
- ///
+ ///
+ /// The HTTP client builder.
+ ///
///
- /// The X.509 certificate to expect the server to use.
- ///
- ///
- /// An optional delegate called if an unexpected error is encountered while validating the server certificate.
- ///
- /// Use this delegate to log the error.
- ///
+ /// The X.509 certificate to expect the server to use.
+ ///
+ ///
+ /// An optional delegate called if an unexpected error is encountered while validating the server certificate.
+ ///
+ /// Use this delegate to log the error.
+ ///
///
- /// The configured .
- ///
- ///
- /// Will accept the certificate or (if it's a CA certificate) any certificate issued by it.
- ///
- public static ClientBuilder WithServerCertificate(this ClientBuilder clientBuilder, X509Certificate2 expectServerCertificate, Action logError = null)
- {
- if (clientBuilder == null)
+ /// The configured .
+ ///
+ ///
+ /// Will accept the certificate or (if it's a CA certificate) any certificate issued by it.
+ ///
+ public static ClientBuilder WithServerCertificate(this ClientBuilder clientBuilder, X509Certificate2 expectServerCertificate, Action logError = null)
+ {
+ if (clientBuilder == null)
throw new ArgumentNullException(nameof(clientBuilder));
if (expectServerCertificate == null)
throw new ArgumentNullException(nameof(expectServerCertificate));
return clientBuilder.ConfigureHttpClientHandler(clientHandler =>
- {
- clientHandler.ServerCertificateCustomValidationCallback = (request, certificate, chain, sslPolicyErrors) =>
- {
- if (sslPolicyErrors != SslPolicyErrors.RemoteCertificateChainErrors)
- return false;
+ {
+ clientHandler.ServerCertificateCustomValidationCallback = (request, certificate, chain, sslPolicyErrors) =>
+ {
+ if (sslPolicyErrors != SslPolicyErrors.RemoteCertificateChainErrors)
+ return false;
- try
- {
- using (X509Chain certificateChain = new X509Chain())
- {
- certificateChain.ChainPolicy.ExtraStore.Add(expectServerCertificate);
- certificateChain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;
- certificateChain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
-
- return certificateChain.Build(certificate);
- }
- }
- catch (Exception chainException)
- {
- if (logError != null)
- logError(chainException);
-
- return false;
- }
- };
- });
- }
+ try
+ {
+ using (X509Chain certificateChain = new X509Chain())
+ {
+ certificateChain.ChainPolicy.ExtraStore.Add(expectServerCertificate);
+ certificateChain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;
+ certificateChain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
+
+ return certificateChain.Build(certificate);
+ }
+ }
+ catch (Exception chainException)
+ {
+ if (logError != null)
+ logError(chainException);
+
+ return false;
+ }
+ };
+ });
+ }
- ///
- /// Create a copy of the , but with verification of the server's SSL certificate disabled (useful when the server has a self-signed certificate).
+ ///
+ /// Create a copy of the , but with verification of the server's SSL certificate disabled (useful when the server has a self-signed certificate).
///
- ///
- /// The HTTP client builder.
- ///
- ///
- /// The configured .
- ///
- ///
- /// Will accept any certificate.
- ///
- public static ClientBuilder AcceptAnyServerCertificate(this ClientBuilder clientBuilder)
- {
- if (clientBuilder == null)
+ ///
+ /// The HTTP client builder.
+ ///
+ ///
+ /// The configured .
+ ///
+ ///
+ /// Will accept any certificate.
+ ///
+ public static ClientBuilder AcceptAnyServerCertificate(this ClientBuilder clientBuilder)
+ {
+ if (clientBuilder == null)
throw new ArgumentNullException(nameof(clientBuilder));
return clientBuilder.ConfigureHttpClientHandler(clientHandler =>
- {
- clientHandler.ServerCertificateCustomValidationCallback = (request, certificate, chain, sslPolicyErrors) =>
- {
- return true; // Verification disabled.
- };
- });
- }
- }
+ {
+ clientHandler.ServerCertificateCustomValidationCallback = (request, certificate, chain, sslPolicyErrors) =>
+ {
+ return true; // Verification disabled.
+ };
+ });
+ }
+ }
}
diff --git a/src/HTTPlease.Core/ClientExtensions.Streamed.cs b/src/HTTPlease.Core/ClientExtensions.Streamed.cs
index 70ea30d..5f0fb03 100644
--- a/src/HTTPlease.Core/ClientExtensions.Streamed.cs
+++ b/src/HTTPlease.Core/ClientExtensions.Streamed.cs
@@ -6,16 +6,16 @@
namespace HTTPlease
{
- using Core;
+ using Core;
- ///
- /// Invocation-related extension methods for s that use an .
- ///
- public static partial class ClientExtensions
+ ///
+ /// Invocation-related extension methods for s that use an .
+ ///
+ public static partial class ClientExtensions
{
- #region Invoke (streamed)
+ #region Invoke (streamed)
- ///
+ ///
/// Asynchronously execute a request as a streamed HTTP GET.
///
///
@@ -34,12 +34,12 @@ public static async Task GetStreamedAsync(this HttpClient h
{
using (HttpRequestMessage requestMessage = request.BuildRequestMessage(HttpMethod.Get, baseUri: httpClient.BaseAddress))
{
- requestMessage.MarkAsStreamed();
+ requestMessage.MarkAsStreamed();
return await httpClient.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead);
}
}
- #endregion // Invoke (streamed)
- }
+ #endregion // Invoke (streamed)
+ }
}
diff --git a/src/HTTPlease.Core/ClientExtensions.cs b/src/HTTPlease.Core/ClientExtensions.cs
index bc2e798..d82619f 100644
--- a/src/HTTPlease.Core/ClientExtensions.cs
+++ b/src/HTTPlease.Core/ClientExtensions.cs
@@ -6,263 +6,263 @@
namespace HTTPlease
{
- using Core;
+ using Core;
- ///
- /// Invocation-related extension methods for s that use an .
- ///
- public static partial class ClientExtensions
+ ///
+ /// Invocation-related extension methods for s that use an .
+ ///
+ public static partial class ClientExtensions
{
- #region Invoke
+ #region Invoke
- ///
- /// Asynchronously execute a request as an HTTP HEAD.
- ///
- ///
- /// The used to execute the request.
- ///
- ///
- /// The HTTP request.
- ///
- ///
- /// An optional cancellation token that can be used to cancel the asynchronous operation.
- ///
- ///
- /// An representing the response.
- ///
- public static async Task HeadAsync(this HttpClient httpClient, HttpRequest request, CancellationToken cancellationToken = default)
- {
- if (httpClient == null)
- throw new ArgumentNullException(nameof(httpClient));
+ ///
+ /// Asynchronously execute a request as an HTTP HEAD.
+ ///
+ ///
+ /// The used to execute the request.
+ ///
+ ///
+ /// The HTTP request.
+ ///
+ ///
+ /// An optional cancellation token that can be used to cancel the asynchronous operation.
+ ///
+ ///
+ /// An representing the response.
+ ///
+ public static async Task HeadAsync(this HttpClient httpClient, HttpRequest request, CancellationToken cancellationToken = default)
+ {
+ if (httpClient == null)
+ throw new ArgumentNullException(nameof(httpClient));
- if (request == null)
- throw new ArgumentNullException(nameof(request));
+ if (request == null)
+ throw new ArgumentNullException(nameof(request));
- return await httpClient.SendAsync(request, HttpMethod.Head, cancellationToken: cancellationToken).ConfigureAwait(false);
- }
+ return await httpClient.SendAsync(request, HttpMethod.Head, cancellationToken: cancellationToken).ConfigureAwait(false);
+ }
- ///
- /// Asynchronously execute a request as an HTTP GET.
- ///
- ///
- /// The used to execute the request.
- ///
- ///
- /// The HTTP request.
- ///
- ///
- /// An optional cancellation token that can be used to cancel the asynchronous operation.
- ///
- ///
- /// An representing the response.
- ///
- public static async Task GetAsync(this HttpClient httpClient, HttpRequest request, CancellationToken cancellationToken = default)
- {
- if (httpClient == null)
- throw new ArgumentNullException(nameof(httpClient));
+ ///
+ /// Asynchronously execute a request as an HTTP GET.
+ ///
+ ///
+ /// The used to execute the request.
+ ///
+ ///
+ /// The HTTP request.
+ ///
+ ///
+ /// An optional cancellation token that can be used to cancel the asynchronous operation.
+ ///
+ ///
+ /// An representing the response.
+ ///
+ public static async Task GetAsync(this HttpClient httpClient, HttpRequest request, CancellationToken cancellationToken = default)
+ {
+ if (httpClient == null)
+ throw new ArgumentNullException(nameof(httpClient));
- if (request == null)
- throw new ArgumentNullException(nameof(request));
+ if (request == null)
+ throw new ArgumentNullException(nameof(request));
- return await httpClient.SendAsync(request, HttpMethod.Get, cancellationToken: cancellationToken).ConfigureAwait(false);
- }
+ return await httpClient.SendAsync(request, HttpMethod.Get, cancellationToken: cancellationToken).ConfigureAwait(false);
+ }
- ///
- /// Asynchronously execute a request as an HTTP POST.
- ///
- ///
- /// The used to execute the request.
- ///
- ///
- /// The HTTP request.
- ///
- ///
- /// Optional representing the request body.
- ///
- ///
- /// An optional cancellation token that can be used to cancel the asynchronous operation.
- ///
- ///
- /// An representing the response.
- ///
- public static async Task PostAsync(this HttpClient httpClient, HttpRequest request, HttpContent postBody = null, CancellationToken cancellationToken = default)
- {
- if (httpClient == null)
- throw new ArgumentNullException(nameof(httpClient));
+ ///
+ /// Asynchronously execute a request as an HTTP POST.
+ ///
+ ///
+ /// The used to execute the request.
+ ///
+ ///
+ /// The HTTP request.
+ ///
+ ///
+ /// Optional representing the request body.
+ ///
+ ///
+ /// An optional cancellation token that can be used to cancel the asynchronous operation.
+ ///
+ ///
+ /// An representing the response.
+ ///
+ public static async Task PostAsync(this HttpClient httpClient, HttpRequest request, HttpContent postBody = null, CancellationToken cancellationToken = default)
+ {
+ if (httpClient == null)
+ throw new ArgumentNullException(nameof(httpClient));
- if (request == null)
- throw new ArgumentNullException(nameof(request));
+ if (request == null)
+ throw new ArgumentNullException(nameof(request));
- return await httpClient.SendAsync(request, HttpMethod.Post, postBody, cancellationToken).ConfigureAwait(false);
- }
+ return await httpClient.SendAsync(request, HttpMethod.Post, postBody, cancellationToken).ConfigureAwait(false);
+ }
- ///
- /// Asynchronously execute a request as an HTTP PUT.
- ///
- ///
- /// The used to execute the request.
- ///
- ///
- /// The HTTP request.
- ///
- ///
- /// representing the request body.
- ///
- ///
- /// An optional cancellation token that can be used to cancel the asynchronous operation.
- ///
- ///
- /// An representing the response.
- ///
- public static async Task PutAsync(this HttpClient httpClient, HttpRequest request, HttpContent putBody, CancellationToken cancellationToken = default)
- {
- if (httpClient == null)
- throw new ArgumentNullException(nameof(httpClient));
+ ///
+ /// Asynchronously execute a request as an HTTP PUT.
+ ///
+ ///
+ /// The used to execute the request.
+ ///
+ ///
+ /// The HTTP request.
+ ///
+ ///
+ /// representing the request body.
+ ///
+ ///
+ /// An optional cancellation token that can be used to cancel the asynchronous operation.
+ ///
+ ///
+ /// An representing the response.
+ ///
+ public static async Task PutAsync(this HttpClient httpClient, HttpRequest request, HttpContent putBody, CancellationToken cancellationToken = default)
+ {
+ if (httpClient == null)
+ throw new ArgumentNullException(nameof(httpClient));
- if (request == null)
- throw new ArgumentNullException(nameof(request));
+ if (request == null)
+ throw new ArgumentNullException(nameof(request));
- if (putBody == null)
- throw new ArgumentNullException(nameof(putBody));
+ if (putBody == null)
+ throw new ArgumentNullException(nameof(putBody));
- return await httpClient.SendAsync(request, HttpMethod.Put, putBody, cancellationToken).ConfigureAwait(false);
- }
+ return await httpClient.SendAsync(request, HttpMethod.Put, putBody, cancellationToken).ConfigureAwait(false);
+ }
- ///
- /// Asynchronously execute a request as an HTTP PATCH.
- ///
- ///
- /// The used to execute the request.
- ///
- ///
- /// The HTTP request.
- ///
- ///
- /// representing the request body.
- ///
- ///
- /// An optional cancellation token that can be used to cancel the asynchronous operation.
- ///
- ///
- /// An representing the response.
- ///
- public static async Task PatchAsync(this HttpClient httpClient, HttpRequest request, HttpContent patchBody, CancellationToken cancellationToken = default)
- {
- if (request == null)
- throw new ArgumentNullException(nameof(request));
+ ///
+ /// Asynchronously execute a request as an HTTP PATCH.
+ ///
+ ///
+ /// The used to execute the request.
+ ///
+ ///
+ /// The HTTP request.
+ ///
+ ///
+ /// representing the request body.
+ ///
+ ///
+ /// An optional cancellation token that can be used to cancel the asynchronous operation.
+ ///
+ ///
+ /// An representing the response.
+ ///
+ public static async Task PatchAsync(this HttpClient httpClient, HttpRequest request, HttpContent patchBody, CancellationToken cancellationToken = default)
+ {
+ if (request == null)
+ throw new ArgumentNullException(nameof(request));
- if (patchBody == null)
- throw new ArgumentNullException(nameof(patchBody));
+ if (patchBody == null)
+ throw new ArgumentNullException(nameof(patchBody));
- return await httpClient.SendAsync(request, OtherHttpMethods.Patch, patchBody, cancellationToken).ConfigureAwait(false);
- }
+ return await httpClient.SendAsync(request, OtherHttpMethods.Patch, patchBody, cancellationToken).ConfigureAwait(false);
+ }
- ///
- /// Asynchronously execute a request as an HTTP DELETE.
- ///
- ///
- /// The used to execute the request.
- ///
- ///
- /// The HTTP request.
- ///
- ///
- /// An optional cancellation token that can be used to cancel the asynchronous operation.
- ///
- ///
- /// An representing the response.
- ///
- public static async Task DeleteAsync(this HttpClient httpClient, HttpRequest request, CancellationToken cancellationToken = default)
- {
- if (request == null)
- throw new ArgumentNullException(nameof(request));
+ ///
+ /// Asynchronously execute a request as an HTTP DELETE.
+ ///
+ ///
+ /// The used to execute the request.
+ ///
+ ///
+ /// The HTTP request.
+ ///
+ ///
+ /// An optional cancellation token that can be used to cancel the asynchronous operation.
+ ///
+ ///
+ /// An representing the response.
+ ///
+ public static async Task DeleteAsync(this HttpClient httpClient, HttpRequest request, CancellationToken cancellationToken = default)
+ {
+ if (request == null)
+ throw new ArgumentNullException(nameof(request));
- return await httpClient.SendAsync(request, HttpMethod.Delete, cancellationToken: cancellationToken).ConfigureAwait(false);
- }
+ return await httpClient.SendAsync(request, HttpMethod.Delete, cancellationToken: cancellationToken).ConfigureAwait(false);
+ }
- ///
- /// Asynchronously execute the request using the specified HTTP method.
- ///
- ///
- /// The used to execute the request.
- ///
- ///
- /// The HTTP request.
- ///
- ///
- /// An representing the method to use.
- ///
- ///
- /// Optional representing the request body (if any).
- ///
- ///
- /// An optional cancellation token that can be used to cancel the asynchronous operation.
- ///
- ///
- /// An representing the response.
- ///
- public static async Task SendAsync(this HttpClient httpClient, HttpRequest request, HttpMethod method, HttpContent body = null, CancellationToken cancellationToken = default)
- {
- if (request == null)
- throw new ArgumentNullException(nameof(request));
+ ///
+ /// Asynchronously execute the request using the specified HTTP method.
+ ///
+ ///
+ /// The used to execute the request.
+ ///
+ ///
+ /// The HTTP request.
+ ///
+ ///
+ /// An representing the method to use.
+ ///
+ ///
+ /// Optional representing the request body (if any).
+ ///
+ ///
+ /// An optional cancellation token that can be used to cancel the asynchronous operation.
+ ///
+ ///
+ /// An representing the response.
+ ///
+ public static async Task SendAsync(this HttpClient httpClient, HttpRequest request, HttpMethod method, HttpContent body = null, CancellationToken cancellationToken = default)
+ {
+ if (request == null)
+ throw new ArgumentNullException(nameof(request));
- using (HttpRequestMessage requestMessage = request.BuildRequestMessage(method, body, httpClient.BaseAddress))
- {
- HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage, cancellationToken).ConfigureAwait(false);
- try
- {
- request.ExecuteResponseActions(responseMessage);
- }
- catch
- {
- using (responseMessage)
- {
- throw;
- }
- }
+ using (HttpRequestMessage requestMessage = request.BuildRequestMessage(method, body, httpClient.BaseAddress))
+ {
+ HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage, cancellationToken).ConfigureAwait(false);
+ try
+ {
+ request.ExecuteResponseActions(responseMessage);
+ }
+ catch
+ {
+ using (responseMessage)
+ {
+ throw;
+ }
+ }
- return responseMessage;
- }
- }
+ return responseMessage;
+ }
+ }
- #endregion // Invoke
+ #endregion // Invoke
- #region Helpers
+ #region Helpers
- ///
- /// Execute the request's configured response actions (if any) against the specified response message.
- ///
- ///
- /// The .
- ///
- ///
- /// The HTTP response message.
- ///
- static void ExecuteResponseActions(this HttpRequest request, HttpResponseMessage responseMessage)
- {
- if (request == null)
- throw new ArgumentNullException(nameof(request));
+ ///
+ /// Execute the request's configured response actions (if any) against the specified response message.
+ ///
+ ///
+ /// The .
+ ///
+ ///
+ /// The HTTP response message.
+ ///
+ static void ExecuteResponseActions(this HttpRequest request, HttpResponseMessage responseMessage)
+ {
+ if (request == null)
+ throw new ArgumentNullException(nameof(request));
- if (responseMessage == null)
- throw new ArgumentNullException(nameof(responseMessage));
+ if (responseMessage == null)
+ throw new ArgumentNullException(nameof(responseMessage));
- List responseActionExceptions = new List();
- foreach (ResponseAction