Skip to content

Commit

Permalink
Fixes dotnet#5105
Browse files Browse the repository at this point in the history
Add support for HttpRequestMessage objects containing StreamContent to
the AddStandardHedgingHandler() resilience API.

This change does not update any public API contracts. It updates
internal and private API contracts only.

This is a small commit to resolve comments made on the PR.
  • Loading branch information
Adam Hammond committed Apr 22, 2024
1 parent f51e60e commit deaf1cf
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ internal sealed class RequestMessageSnapshot : IResettable, IDisposable
private HttpContent? _content;

[System.Diagnostics.CodeAnalysis.SuppressMessage("Resilience", "EA0014:The async method doesn't support cancellation", Justification = "Past the point of no cancellation.")]
public static async Task<RequestMessageSnapshot> CreateAsync(HttpRequestMessage request)
public static async ValueTask<RequestMessageSnapshot> CreateAsync(HttpRequestMessage request)
{
_ = Throw.IfNull(request);

Expand All @@ -35,7 +35,7 @@ public static async Task<RequestMessageSnapshot> CreateAsync(HttpRequestMessage
}

[System.Diagnostics.CodeAnalysis.SuppressMessage("Resilience", "EA0014:The async method doesn't support cancellation", Justification = "Past the point of no cancellation.")]
public async Task<HttpRequestMessage> CreateRequestMessageAsync()
public async ValueTask<HttpRequestMessage> CreateRequestMessageAsync()
{
if (!IsInitialized())
{
Expand Down Expand Up @@ -101,7 +101,7 @@ bool IResettable.TryReset()
void IDisposable.Dispose() => _snapshots.Return(this);

[System.Diagnostics.CodeAnalysis.SuppressMessage("Resilience", "EA0014:The async method doesn't support cancellation", Justification = "Past the point of no cancellation.")]
private static async Task<(HttpContent? content, HttpContent? clonedContent)> CloneContentAsync(HttpContent? content)
private static async ValueTask<(HttpContent? content, HttpContent? clonedContent)> CloneContentAsync(HttpContent? content)
{
HttpContent? clonedContent = null;
if (content is not null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.IO;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using FluentAssertions;
using Microsoft.Extensions.Http.Resilience.Internal;
Expand Down Expand Up @@ -39,5 +42,38 @@ public void ExecuteAsync_RequestMessageNotFound_Throws()
strategy.Invoking(s => s.Execute(() => { })).Should().Throw<InvalidOperationException>();
}

[Fact]
public async Task ExecuteCoreAsync_IOExceptionThrownWhenCreatingSnapshot_ReturnsExceptionOutcome()
{
var strategy = Create();
var context = ResilienceContextPool.Shared.Get();
using var request = new HttpRequestMessage(HttpMethod.Post, new Uri("https://www.example.com/some-resource"));
using var stream = new StreamTestHelper("some stream content");
request.Content = new StreamContent(stream);
context.Properties.Set(ResilienceKeys.RequestMessage, request);

_ = await Assert.ThrowsAsync<IOException>(async () => await strategy.ExecuteAsync(context => default, context));
}

private static ResiliencePipeline Create() => new ResiliencePipelineBuilder().AddStrategy(_ => new RequestMessageSnapshotStrategy(), Mock.Of<ResilienceStrategyOptions>()).Build();

private class StreamTestHelper : MemoryStream
{
public StreamTestHelper(string str)
: base(Encoding.UTF8.GetBytes(str))
{
}

public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) => throw new IOException();

#if NET5_0_OR_GREATER
public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default) => throw new IOException();

public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default) => throw new IOException();
#else
public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) => throw new IOException();

public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) => throw new IOException();
#endif
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ public async Task CreateRequestMessageAsync_SnapshotIsReset_ThrowsException()
AddContentHeaders(_requestMessage!.Content);
using RequestMessageSnapshot snapshot = await RequestMessageSnapshot.CreateAsync(_requestMessage).ConfigureAwait(false);
((IResettable)snapshot).TryReset();
_ = await Assert.ThrowsAsync<InvalidOperationException>(snapshot.CreateRequestMessageAsync);
_ = await Assert.ThrowsAsync<InvalidOperationException>(async () => await snapshot.CreateRequestMessageAsync().ConfigureAwait(false));
}

public void Dispose()
Expand Down

0 comments on commit deaf1cf

Please sign in to comment.