diff --git a/src/libraries/Common/tests/System/Net/Http/GenericLoopbackServer.cs b/src/libraries/Common/tests/System/Net/Http/GenericLoopbackServer.cs
index 9a523ca701417e..5407a6095cd0ab 100644
--- a/src/libraries/Common/tests/System/Net/Http/GenericLoopbackServer.cs
+++ b/src/libraries/Common/tests/System/Net/Http/GenericLoopbackServer.cs
@@ -86,6 +86,17 @@ public void Close()
CloseWebSocket();
}
+ public async Task WaitForCloseAsync(CancellationToken cancellationToken)
+ {
+ while (_websocket != null
+ ? _websocket.State != WebSocketState.Closed
+ : !(_socket.Poll(1, SelectMode.SelectRead) && _socket.Available == 0))
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ await Task.Delay(100);
+ }
+ }
+
public void Shutdown(SocketShutdown how)
{
_socket?.Shutdown(how);
@@ -138,6 +149,9 @@ public abstract class GenericLoopbackConnection : IDisposable
/// Waits for the client to signal cancellation.
public abstract Task WaitForCancellationAsync(bool ignoreIncomingData = true);
+ /// Waits for the client to signal cancellation.
+ public abstract Task WaitForCloseAsync(CancellationToken cancellationToken);
+
/// Helper function to make it easier to convert old test with strings.
public async Task SendResponseBodyAsync(string content, bool isFinal = true)
{
diff --git a/src/libraries/Common/tests/System/Net/Http/Http2LoopbackConnection.cs b/src/libraries/Common/tests/System/Net/Http/Http2LoopbackConnection.cs
index b3bb701e540f36..2329475728c67b 100644
--- a/src/libraries/Common/tests/System/Net/Http/Http2LoopbackConnection.cs
+++ b/src/libraries/Common/tests/System/Net/Http/Http2LoopbackConnection.cs
@@ -984,5 +984,10 @@ public override async Task WaitForCancellationAsync(bool ignoreIncomingData = tr
RstStreamFrame rstStreamFrame = Assert.IsType(frame);
Assert.Equal((int)ProtocolErrors.CANCEL, rstStreamFrame.ErrorCode);
}
+
+ public override Task WaitForCloseAsync(CancellationToken cancellationToken)
+ {
+ throw new NotImplementedException();
+ }
}
}
diff --git a/src/libraries/Common/tests/System/Net/Http/Http3LoopbackConnection.cs b/src/libraries/Common/tests/System/Net/Http/Http3LoopbackConnection.cs
index ceb36e0bcbfb12..78c49a1aaee13d 100644
--- a/src/libraries/Common/tests/System/Net/Http/Http3LoopbackConnection.cs
+++ b/src/libraries/Common/tests/System/Net/Http/Http3LoopbackConnection.cs
@@ -10,6 +10,7 @@
using System.Linq;
using System.Net.Http.Functional.Tests;
using Xunit;
+using System.Threading;
namespace System.Net.Test.Common
{
@@ -305,6 +306,11 @@ public override async Task WaitForCancellationAsync(bool ignoreIncomingData = tr
{
await GetOpenRequest().WaitForCancellationAsync(ignoreIncomingData).ConfigureAwait(false);
}
+
+ public override Task WaitForCloseAsync(CancellationToken cancellationToken)
+ {
+ throw new NotImplementedException();
+ }
}
}
diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Cancellation.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Cancellation.cs
index 7ae59fe98e54d2..a363553ba598eb 100644
--- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Cancellation.cs
+++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Cancellation.cs
@@ -28,6 +28,7 @@ public HttpClientHandler_Cancellation_Test(ITestOutputHelper output) : base(outp
[Theory]
[InlineData(false, CancellationMode.Token)]
[InlineData(true, CancellationMode.Token)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/36634", TestPlatforms.Browser)] // out of memory
public async Task PostAsync_CancelDuringRequestContentSend_TaskCanceledQuickly(bool chunkedTransfer, CancellationMode mode)
{
if (LoopbackServerFactory.Version >= HttpVersion20.Value && chunkedTransfer)
@@ -228,10 +229,21 @@ await LoopbackServerFactory.CreateServerAsync(async (server, url) =>
await connection.ReadRequestDataAsync();
await connection.SendResponseAsync(HttpStatusCode.OK, headers: headers, isFinal: false);
await clientFinished.Task;
+
+#if TARGET_BROWSER
+ // make sure that the browser closed the connection
+ await connection.WaitForCloseAsync(CancellationToken.None);
+#endif
});
var req = new HttpRequestMessage(HttpMethod.Get, url) { Version = UseVersion };
req.Headers.ConnectionClose = connectionClose;
+
+#if TARGET_BROWSER
+ var WebAssemblyEnableStreamingResponseKey = new HttpRequestOptionsKey("WebAssemblyEnableStreamingResponse");
+ req.Options.Set(WebAssemblyEnableStreamingResponseKey, true);
+#endif
+
Task getResponse = client.SendAsync(TestAsync, req, HttpCompletionOption.ResponseHeadersRead, cts.Token);
await ValidateClientCancellationAsync(async () =>
{
@@ -247,7 +259,6 @@ await ValidateClientCancellationAsync(async () =>
cts.Cancel();
await readTask;
});
-
try
{
clientFinished.SetResult(true);
@@ -256,11 +267,13 @@ await ValidateClientCancellationAsync(async () =>
});
}
}
+
[Theory]
[InlineData(CancellationMode.CancelPendingRequests, false)]
[InlineData(CancellationMode.DisposeHttpClient, false)]
[InlineData(CancellationMode.CancelPendingRequests, true)]
[InlineData(CancellationMode.DisposeHttpClient, true)]
+ [SkipOnPlatform(TestPlatforms.Browser, "Browser doesn't have blocking synchronous Stream.ReadByte and so it waits for whole body")]
public async Task GetAsync_CancelPendingRequests_DoesntCancelReadAsyncOnResponseStream(CancellationMode mode, bool copyToAsync)
{
if (IsWinHttpHandler && UseVersion >= HttpVersion20.Value)
diff --git a/src/libraries/Common/tests/System/Net/Http/LoopbackServer.cs b/src/libraries/Common/tests/System/Net/Http/LoopbackServer.cs
index 30cb5d4a3f0431..70ddfcf1f5aaf6 100644
--- a/src/libraries/Common/tests/System/Net/Http/LoopbackServer.cs
+++ b/src/libraries/Common/tests/System/Net/Http/LoopbackServer.cs
@@ -1065,6 +1065,11 @@ public override async Task WaitForCancellationAsync(bool ignoreIncomingData = tr
}
}
}
+
+ public override Task WaitForCloseAsync(CancellationToken cancellationToken)
+ {
+ return _socket.WaitForCloseAsync(cancellationToken);
+ }
}
public override async Task HandleRequestAsync(HttpStatusCode statusCode = HttpStatusCode.OK, IList headers = null, string content = "")
diff --git a/src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/Handlers/RemoteLoopHandler.cs b/src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/Handlers/RemoteLoopHandler.cs
index 4f83c67b0b30f4..557eaa494f7c78 100644
--- a/src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/Handlers/RemoteLoopHandler.cs
+++ b/src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/Handlers/RemoteLoopHandler.cs
@@ -100,6 +100,11 @@ private static async Task ProcessWebSocketRequest(HttpContext context, WebSocket
var slice = new ArraySegment(testedBuffer, 0, testedNext.Result);
await control.SendAsync(slice, WebSocketMessageType.Binary, true, cts.Token).ConfigureAwait(false);
}
+ // did we get TCP FIN?
+ if (!close && (tested.Poll(1, SelectMode.SelectRead) && tested.Available == 0))
+ {
+ close = true;
+ }
if (!close)
{
testedNext = tested.ReceiveAsync(new Memory(testedBuffer), SocketFlags.None, cts.Token).AsTask();
@@ -142,14 +147,14 @@ private static async Task ProcessWebSocketRequest(HttpContext context, WebSocket
}
catch (WebSocketException ex)
{
- logger.LogWarning("ProcessWebSocketRequest closing failed", ex);
+ logger.LogWarning("RemoteLoopHandler.ProcessWebSocketRequest closing failed", ex);
}
}
cts.Cancel();
}
catch (Exception ex)
{
- logger.LogError("ProcessWebSocketRequest failed", ex);
+ logger.LogError("RemoteLoopHandler.ProcessWebSocketRequest failed", ex);
}
finally
{
diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs
index 8d022091ea3961..b03247fbfc3d7e 100644
--- a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs
+++ b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs
@@ -148,10 +148,10 @@ protected internal override async Task SendAsync(HttpReques
{
throw new ArgumentNullException(nameof(request), SR.net_http_handler_norequest);
}
-
+ CancellationTokenRegistration? abortRegistration = null;
try
{
- var requestObject = new JSObject();
+ using var requestObject = new JSObject();
if (request.Options.TryGetValue(FetchOptions, out IDictionary? fetchOptions))
{
@@ -221,44 +221,39 @@ protected internal override async Task SendAsync(HttpReques
}
- WasmHttpReadStream? wasmHttpReadStream = null;
-
JSObject abortController = new HostObject("AbortController");
- JSObject signal = (JSObject)abortController.GetObjectProperty("signal");
+ using JSObject signal = (JSObject)abortController.GetObjectProperty("signal");
requestObject.SetObjectProperty("signal", signal);
- signal.Dispose();
- CancellationTokenSource abortCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
- CancellationTokenRegistration abortRegistration = abortCts.Token.Register((Action)(() =>
+ abortRegistration = cancellationToken.Register(() =>
{
- if (abortController.JSHandle != -1)
+ if (!abortController.IsDisposed)
{
abortController.Invoke("abort");
abortController?.Dispose();
}
- wasmHttpReadStream?.Dispose();
- abortCts.Dispose();
- }));
+ });
- var args = new System.Runtime.InteropServices.JavaScript.Array();
+ using var args = new System.Runtime.InteropServices.JavaScript.Array();
if (request.RequestUri != null)
{
args.Push(request.RequestUri.ToString());
args.Push(requestObject);
}
- requestObject.Dispose();
- var response = s_fetch?.Invoke("apply", s_window, args) as Task