Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions src/YetAnotherHttpHandler/RequestContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,14 @@ public void Release()
_handle.Free();
_handle = default;
_fullyCompleted.Set();

// RequestContextHandle can be released after all the processes using it are complete.
if (_hasRequestContextHandleRef)
{
Debug.Assert(!_requestContextHandle.IsClosed);
_requestContextHandle.DangerousRelease();
}
_requestContextHandle.Dispose();
}
}

Expand Down Expand Up @@ -341,14 +349,6 @@ private void Dispose(bool disposing)
// However, caution is needed with the invocation order and timing of callbacks, as well as the handling of locks, since the finalizer thread may become blocked.
_fullyCompleted.Wait();
}

// RequestContextHandle can be released after all the processes using it are complete.
if (_hasRequestContextHandleRef)
{
Debug.Assert(!_requestContextHandle.IsClosed);
_requestContextHandle.DangerousRelease();
}
_requestContextHandle.Dispose();
}
}
}
3 changes: 3 additions & 0 deletions src/YetAnotherHttpHandler/ResponseContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ public void Complete()
WaitForLatestFlush();
_pipe.Writer.Complete();
_completed = true;
_tokenRegistration.Dispose();
}
}

Expand Down Expand Up @@ -150,6 +151,7 @@ public void CompleteAsFailed(string errorMessage, uint h2ErrorCode)
WaitForLatestFlush();
_pipe.Writer.Complete(ex);
_completed = true;
_tokenRegistration.Dispose();
}
}

Expand All @@ -164,6 +166,7 @@ public void Cancel()
WaitForLatestFlush();
_pipe.Writer.Complete(new OperationCanceledException(_cancellationToken));
_completed = true;
_tokenRegistration.Dispose();
}
}

Expand Down
22 changes: 22 additions & 0 deletions test/YetAnotherHttpHandler.Test/Helpers/TestWebAppServer.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System.Collections.Concurrent;
using System.Diagnostics;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.Extensions.DependencyInjection;
Expand All @@ -14,12 +16,17 @@ public class TestWebAppServer : IAsyncDisposable
private readonly Task _appTask;
private readonly IHostApplicationLifetime _appLifetime;
private readonly TaskCompletionSource _waitForAppStarted;
private readonly ConcurrentDictionary<string, bool> _activeConnectionsById = new();
private int _activeConnections;

public int Port { get; }
public bool IsSecure { get; }

public string BaseUri => $"{(IsSecure ? "https" : "http")}://localhost:{Port}";

public int ActiveConnections => _activeConnections;
public IReadOnlyList<string> ActiveConnectionIds => _activeConnectionsById.Keys.ToArray();

private TestWebAppServer(int port, TestWebAppServerListenMode listenMode, ITestOutputHelper? testOutputHelper, Func<WebApplicationBuilder, WebApplication> webAppBuilder, Action<WebApplicationBuilder>? configure)
{
Port = port;
Expand Down Expand Up @@ -51,6 +58,21 @@ TestWebAppServerListenMode.SecureHttp2Only or
{
listenOptions.UseHttps();
}

listenOptions.Use(async (ctx, next) =>
{
try
{
Interlocked.Increment(ref _activeConnections);
_activeConnectionsById.TryAdd(ctx.ConnectionId, true);
await next();
}
finally
{
Interlocked.Decrement(ref _activeConnections);
_activeConnectionsById.TryRemove(ctx.ConnectionId, out _);
}
});
});
});
if (testOutputHelper is not null)
Expand Down
17 changes: 16 additions & 1 deletion test/YetAnotherHttpHandler.Test/Http2TestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ public async Task Cancel_Post_SendingBody()
public async Task Cancel_Post_SendingBody_Duplex()
{
// Arrange
using var httpHandler = CreateHandler();
/*using*/ var httpHandler = CreateHandler();
var httpClient = new HttpClient(httpHandler);
await using var server = await LaunchServerAsync<TestServerForHttp1AndHttp2>();

Expand All @@ -320,13 +320,28 @@ public async Task Cancel_Post_SendingBody_Duplex()
Version = HttpVersion.Version20,
Content = content,
};

var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).WaitAsync(TimeoutToken);
var connectionId = response.Headers.TryGetValues("x-connection-id", out var values) ? string.Join(',', values) : string.Empty;
var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(500));
var ex = await Record.ExceptionAsync(async () => await response.Content.ReadAsByteArrayAsync(cts.Token).WaitAsync(TimeoutToken));

pipe.Writer.Complete();
httpHandler.Dispose();
httpClient.Dispose();
response = null;
httpHandler = null;
httpClient = null;

GC.Collect();
GC.WaitForPendingFinalizers();
Thread.Sleep(100);
GC.Collect();

// Assert
var operationCanceledException = Assert.IsAssignableFrom<OperationCanceledException>(ex);
Assert.Equal(cts.Token, operationCanceledException.CancellationToken);
Assert.DoesNotContain(connectionId, server.ActiveConnectionIds);
}
#endif

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ public static WebApplication BuildApplication(WebApplicationBuilder builder)

var app = builder.Build();

// ConnectionId header
app.Use((ctx, next) =>
{
ctx.Response.Headers["x-connection-id"] = ctx.Connection.Id;
return next(ctx);
});

// SessionState
app.Use((ctx, next) =>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,8 @@ public async Task InitializationFailure()
}

// NOTE: Currently, this test can only be run on Windows.
[Fact(Skip = "Due to the state remaining from Http2Test.Cancel_Post_SendingBody_Duplex, this test fails. Enable it after fixing that issue.")]
[Fact]
[OSSkipCondition(OperatingSystems.MacOSX | OperatingSystems.Linux)]
public async Task SetWorkerThreads()
{
GC.Collect();
Expand Down
Loading