This repository was archived by the owner on Nov 17, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 69
Start to flesh out Polly design #58
Merged
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
125 changes: 125 additions & 0 deletions
125
src/Microsoft.Extensions.Http.Polly/DependencyInjection/PollyHttpClientBuilderExtensions.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,125 @@ | ||
| // Copyright (c) .NET Foundation. All rights reserved. | ||
| // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
|
||
| using System; | ||
| using System.Net; | ||
| using System.Net.Http; | ||
| using Microsoft.Extensions.Http; | ||
| using Polly; | ||
|
|
||
| namespace Microsoft.Extensions.DependencyInjection | ||
| { | ||
| /// <summary> | ||
| /// Extensions methods for configuring <see cref="PolicyHttpMessageHandler"/> message handlers as part of | ||
| /// and <see cref="HttpClient"/> message handler pipeline. | ||
| /// </summary> | ||
| public static class PollyHttpClientBuilderExtensions | ||
| { | ||
| /// <summary> | ||
| /// Adds a <see cref="PolicyHttpMessageHandler"/> which will surround request execution with the provided | ||
| /// <see cref="IAsyncPolicy"/>. | ||
| /// </summary> | ||
| /// <param name="builder">The <see cref="IHttpClientBuilder"/>.</param> | ||
| /// <param name="policy">The <see cref="IAsyncPolicy"/>.</param> | ||
| /// <returns>An <see cref="IHttpClientBuilder"/> that can be used to configure the client.</returns> | ||
| /// <remarks> | ||
| /// <para> | ||
| /// See the remarks on <see cref="PolicyHttpMessageHandler"/> for guidance on configuring policies. | ||
| /// </para> | ||
| /// </remarks> | ||
| public static IHttpClientBuilder AddPolicyHandler(this IHttpClientBuilder builder, IAsyncPolicy policy) | ||
| { | ||
| if (builder == null) | ||
| { | ||
| throw new ArgumentNullException(nameof(builder)); | ||
| } | ||
|
|
||
| if (policy == null) | ||
| { | ||
| throw new ArgumentNullException(nameof(policy)); | ||
| } | ||
|
|
||
| // Important - cache policy instances so that they are singletons per handler. | ||
| var innerPolicy = policy.WrapAsync(Policy.NoOpAsync<HttpResponseMessage>()); | ||
|
|
||
| builder.AddHttpMessageHandler(() => new PolicyHttpMessageHandler(innerPolicy)); | ||
| return builder; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Adds a <see cref="PolicyHttpMessageHandler"/> which will surround request execution with the provided | ||
| /// <see cref="IAsyncPolicy{HttpResponseMessage}"/>. | ||
| /// </summary> | ||
| /// <param name="builder">The <see cref="IHttpClientBuilder"/>.</param> | ||
| /// <param name="policy">The <see cref="IAsyncPolicy{HttpResponseMessage}"/>.</param> | ||
| /// <returns>An <see cref="IHttpClientBuilder"/> that can be used to configure the client.</returns> | ||
| /// <remarks> | ||
| /// <para> | ||
| /// See the remarks on <see cref="PolicyHttpMessageHandler"/> for guidance on configuring policies. | ||
| /// </para> | ||
| /// </remarks> | ||
| public static IHttpClientBuilder AddPolicyHandler(this IHttpClientBuilder builder, IAsyncPolicy<HttpResponseMessage> policy) | ||
| { | ||
| if (builder == null) | ||
| { | ||
| throw new ArgumentNullException(nameof(builder)); | ||
| } | ||
|
|
||
| if (policy == null) | ||
| { | ||
| throw new ArgumentNullException(nameof(policy)); | ||
| } | ||
|
|
||
| builder.AddHttpMessageHandler(() => new PolicyHttpMessageHandler(policy)); | ||
| return builder; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Adds a <see cref="PolicyHttpMessageHandler"/> which will surround request execution with a <see cref="Policy"/> | ||
| /// created by executing the provided configuration delegate. The policy builder will be preconfigured to trigger | ||
| /// application of the policy for requests that fail with a connection or server error (5XX status code). | ||
| /// </summary> | ||
| /// <param name="builder">The <see cref="IHttpClientBuilder"/>.</param> | ||
| /// <param name="configurePolicy">A delegate used to create a <see cref="IAsyncPolicy{HttpResponseMessage}"/>.</param> | ||
| /// <returns>An <see cref="IHttpClientBuilder"/> that can be used to configure the client.</returns> | ||
| /// <remarks> | ||
| /// <para> | ||
| /// See the remarks on <see cref="PolicyHttpMessageHandler"/> for guidance on configuring policies. | ||
| /// </para> | ||
| /// <para> | ||
| /// The <see cref="PolicyBuilder{HttpResponseMessage}"/> provided to <paramref name="configurePolicy"/> has been | ||
| /// preconfigured to handle connection errors (as <see cref="HttpRequestException"/>) or server errors (as a 5XX HTTP | ||
| /// status code). The configuration is similar to the following code sample: | ||
| /// <code> | ||
| /// Policy.HandleAsync<HttpRequestException>().OrResult<HttpResponseMessage>(response => | ||
| /// { | ||
| /// return response.StatusCode >= HttpStatusCode.InternalServerError; | ||
| /// } | ||
| /// </code> | ||
| /// </para> | ||
| /// <para> | ||
| /// The policy created by <paramref name="configurePolicy"/> will be cached indefinitely per named client. Policies | ||
| /// are generally designed to act as singletons, and can be shared when appropriate. To share a policy across multiple | ||
| /// named clients, first create the policy and the pass it to multiple calls to | ||
| /// <see cref="AddPolicyHandler(IHttpClientBuilder, IAsyncPolicy)"/> or | ||
| /// <see cref="AddPolicyHandler(IHttpClientBuilder, IAsyncPolicy{HttpResponseMessage})"/> as desired. | ||
| /// </para> | ||
| /// </remarks> | ||
| public static IHttpClientBuilder AddServerErrorPolicyHandler( | ||
| this IHttpClientBuilder builder, | ||
| Func<PolicyBuilder<HttpResponseMessage>, IAsyncPolicy<HttpResponseMessage>> configurePolicy) | ||
| { | ||
|
|
||
| var policyBuilder = Policy.Handle<HttpRequestException>().OrResult<HttpResponseMessage>(response => | ||
| { | ||
| return response.StatusCode >= HttpStatusCode.InternalServerError; | ||
| }); | ||
|
|
||
| // Important - cache policy instances so that they are singletons per handler. | ||
| var policy = configurePolicy(policyBuilder); | ||
|
|
||
| builder.AddHttpMessageHandler(() => new PolicyHttpMessageHandler(policy)); | ||
| return builder; | ||
| } | ||
| } | ||
| } | ||
25 changes: 25 additions & 0 deletions
25
src/Microsoft.Extensions.Http.Polly/Microsoft.Extensions.Http.Polly.csproj
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk"> | ||
|
|
||
| <PropertyGroup> | ||
| <Description> | ||
| The HttpClient factory is a pattern for configuring and retrieving named HttpClients in a composable way. This package integrates IHttpClientFactory with the Polly library, to add transient-fault-handling and resiliency through fluent policies such as Retry, Circuit Breaker, Timeout, Bulkhead Isolation, and Fallback. | ||
| </Description> | ||
| <TargetFramework>netstandard2.0</TargetFramework> | ||
| <NoWarn>$(NoWarn);CS1591</NoWarn> | ||
| <GenerateDocumentationFile>true</GenerateDocumentationFile> | ||
| <PackageTags>aspnetcore;httpclient</PackageTags> | ||
|
|
||
| <!-- Don't use Microsoft.AspNetCore.Http.Polly as a namespace, that introduces ambiguities with 'Polly' --> | ||
| <RootNamespace>Microsoft.AspNetCore.Http</RootNamespace> | ||
| </PropertyGroup> | ||
|
|
||
| <ItemGroup> | ||
| <ProjectReference Include="..\Microsoft.Extensions.Http\Microsoft.Extensions.Http.csproj" /> | ||
| </ItemGroup> | ||
|
|
||
| <ItemGroup> | ||
| <PackageReference Include="Polly-Signed" Version="$(PollySignedPackageVersion)" /> | ||
| <PackageReference Include="Microsoft.Extensions.ValueStopwatch.Sources" Version="$(MicrosoftExtensionsValueStopwatchSourcesPackageVersion)" PrivateAssets="All" /> | ||
| </ItemGroup> | ||
|
|
||
| </Project> |
83 changes: 83 additions & 0 deletions
83
src/Microsoft.Extensions.Http.Polly/PolicyHttpMessageHandler.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
| // Copyright (c) .NET Foundation. All rights reserved. | ||
| // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
|
||
| using System; | ||
| using System.Net.Http; | ||
| using System.Threading; | ||
| using System.Threading.Tasks; | ||
| using Microsoft.Extensions.DependencyInjection; | ||
| using Polly; | ||
|
|
||
| namespace Microsoft.Extensions.Http | ||
| { | ||
| /// <summary> | ||
| /// A <see cref="DelegatingHandler"/> implementation that executes request processing surrounded by a <see cref="Policy"/>. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// <para> | ||
| /// This message handler implementation supports the use of policies provided by the Polly library for | ||
| /// transient-fault-handling and resiliency. | ||
| /// </para> | ||
| /// <para> | ||
| /// The documentation provided here is focused guidance for using Polly together with the <see cref="IHttpClientFactory"/>. | ||
| /// See the Polly project and its documentation (https://github.com/app-vnext/Polly) for authoritative information on Polly. | ||
| /// </para> | ||
| /// <para> | ||
| /// The extension methods on <see cref="PollyHttpClientBuilderExtensions"/> are designed as a convenient and correct | ||
| /// to creating a <see cref="PolicyHttpMessageHandler"/>. | ||
| /// </para> | ||
| /// <para> | ||
| /// The <see cref="PollyHttpClientBuilderExtensions.AddPolicyHandler(IHttpClientBuilder, IAsyncPolicy)"/> and | ||
| /// <see cref="PollyHttpClientBuilderExtensions.AddPolicyHandler(IHttpClientBuilder, IAsyncPolicy{HttpResponseMessage})"/> | ||
| /// methods support the creation of a <see cref="PolicyHttpMessageHandler"/> for any kind of policy. This includes | ||
| /// non-reactive policies such as Timeout, or Cache which don't require the underlying request to fail first. | ||
| /// </para> | ||
| /// <para> | ||
| /// The <see cref="PollyHttpClientBuilderExtensions.AddServerErrorPolicyHandler(IHttpClientBuilder, Func{PolicyBuilder{HttpResponseMessage}, IAsyncPolicy{HttpResponseMessage}})"/> | ||
| /// method is an opinionated convenience method that supports the application of a policy for requests that fail due | ||
| /// to a connection failure or server error (5XX HTTP status code). This kind of method supports only reactive policies | ||
| /// such as Retry, Circuit-Breaker or Fallback. This method is only provided for convenience, we recommend creating | ||
| /// your own policies as needed if this does not meet your requirements. | ||
| /// </para> | ||
| /// <para> | ||
| /// Take care when using policies such as Retry or Timeout together as HttpClient provides its own timeout via | ||
| /// <see cref="HttpClient.Timeout"/>. | ||
| /// </para> | ||
| /// <para> | ||
| /// All policies provided by Polly are designed to efficient when used in a long-lived way. Certain policies such as the | ||
| /// Bulkhead and Circuit-Breaker share state and may not have the desired effect when not shared properly. Take care to | ||
| /// ensure the correct lifetimes when using policies and message handlers together in custom scenarios. The extension | ||
| /// methods provided by <see cref="PollyHttpClientBuilderExtensions"/> are designed to assign a long lifetime to policies | ||
| /// and ensure that they can be used when the handler rotation feature is active. | ||
| /// </para> | ||
| /// </remarks> | ||
| public class PolicyHttpMessageHandler : DelegatingHandler | ||
| { | ||
| private readonly IAsyncPolicy<HttpResponseMessage> _policy; | ||
|
|
||
| /// <summary> | ||
| /// Creates a new <see cref="PolicyHttpMessageHandler"/>. | ||
| /// </summary> | ||
| /// <param name="policy">The policy.</param> | ||
| public PolicyHttpMessageHandler(IAsyncPolicy<HttpResponseMessage> policy) | ||
| { | ||
| if (policy == null) | ||
| { | ||
| throw new ArgumentNullException(nameof(policy)); | ||
| } | ||
|
|
||
| _policy = policy; | ||
| } | ||
|
|
||
| /// <inheritdoc /> | ||
| protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) | ||
| { | ||
| if (request == null) | ||
| { | ||
| throw new ArgumentNullException(nameof(request)); | ||
| } | ||
|
|
||
| return _policy.ExecuteAsync((ct) => base.SendAsync(request, ct), cancellationToken); | ||
| } | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@reisenberger - what do you think of this approach instead of creating two separate code paths?
Having two separate code paths just seems a bit like clutter - and then there's naming to think about 😲..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like how this simplifies the separate codepaths; that concerned me too.