Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,11 @@ public void Dispose()
_socket?.Dispose();
_websocket?.Dispose();
}
public void Close()

public async Task CloseAsync()
{
_socket?.Close();
CloseWebSocket();
await CloseWebSocketAsync();
}

public EndPoint? LocalEndPoint => _socket?.LocalEndPoint;
Expand All @@ -108,13 +109,13 @@ public async Task WaitForCloseAsync(CancellationToken cancellationToken)
}
}

public void Shutdown(SocketShutdown how)
public async Task ShutdownAsync(SocketShutdown how)
{
_socket?.Shutdown(how);
CloseWebSocket();
await CloseWebSocketAsync();
}

private void CloseWebSocket()
private async Task CloseWebSocketAsync()
{
if (_websocket == null) return;

Expand All @@ -123,12 +124,11 @@ private void CloseWebSocket()

try
{
var task = _websocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "closing remoteLoop", CancellationToken.None);
// Block and wait for the task to complete synchronously
Task.WaitAll(task);
await _websocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "closing remoteLoop", CancellationToken.None);
}
catch (Exception)
{
// Ignore exceptions during WebSocket close in test cleanup.
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ private async Task ReadPrefixAsync()
// so that SocketsHttpHandler will not induce retry.
// The contents of what we send don't really matter, as long as it is interpreted by SocketsHttpHandler as an invalid response.
await _connectionStream.WriteAsync("HTTP/2.0 400 Bad Request\r\n\r\n"u8.ToArray());
_connectionSocket.Shutdown(SocketShutdown.Send);
await _connectionSocket.ShutdownAsync(SocketShutdown.Send);
// If WinHTTP doesn't support streaming a request without a length then it will fallback
// to HTTP/1.1. Throwing an exception to detect this case in WinHttpHandler tests.
throw new Exception("HTTP/1.1 request sent to HTTP/2 connection.");
Expand Down Expand Up @@ -397,9 +397,12 @@ public async Task WaitForClientDisconnectAsync(bool ignoreUnexpectedFrames = fal
_ignoreWindowUpdates = false;
}

public void ShutdownSend()
public async Task ShutdownSendAsync()
{
_connectionSocket?.Shutdown(SocketShutdown.Send);
if (_connectionSocket != null)
{
await _connectionSocket.ShutdownAsync(SocketShutdown.Send);
}
}

// This will cause a server-initiated shutdown of the connection.
Expand All @@ -408,7 +411,7 @@ public void ShutdownSend()
public async Task WaitForConnectionShutdownAsync(bool ignoreUnexpectedFrames = false)
{
// Shutdown our send side, so the client knows there won't be any more frames coming.
ShutdownSend();
await ShutdownSendAsync();

await WaitForClientDisconnectAsync(ignoreUnexpectedFrames: ignoreUnexpectedFrames);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ await LoopbackServer.CreateServerAsync(async (redirServer, redirUrl) =>
// Send Connection: close so the client will close connection after request is sent,
// meaning we can just read to the end to get the content
await connection.ReadRequestHeaderAndSendResponseAsync((HttpStatusCode)statusCode, $"Location: {redirUrl}\r\nConnection: close\r\n");
connection.Socket.Shutdown(SocketShutdown.Send);
await connection.Socket.ShutdownAsync(SocketShutdown.Send);
await connection.ReadToEndAsync();
});

Expand All @@ -124,7 +124,7 @@ await LoopbackServer.CreateServerAsync(async (redirServer, redirUrl) =>
// Send Connection: close so the client will close connection after request is sent,
// meaning we can just read to the end to get the content
receivedRequest = await connection.ReadRequestHeaderAndSendResponseAsync(additionalHeaders: "Connection: close\r\n");
connection.Socket.Shutdown(SocketShutdown.Send);
await connection.Socket.ShutdownAsync(SocketShutdown.Send);
receivedContent = await connection.ReadToEndAsync();
});

Expand Down
10 changes: 8 additions & 2 deletions src/libraries/Common/tests/System/Net/Http/LoopbackServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,10 @@ public async Task<Connection> EstablishConnectionAsync()
}
catch (Exception)
{
closableWrapper?.Close();
if (closableWrapper is not null)
{
await closableWrapper.CloseAsync().ConfigureAwait(false);
}
throw;
}
}
Expand Down Expand Up @@ -679,7 +682,10 @@ public override async ValueTask DisposeAsync()
// This seems to help avoid connection reset issues caused by buffered data
// that has not been sent/acked when the graceful shutdown timeout expires.
// This may throw if the socket was already closed, so eat any exception.
_socket?.Shutdown(SocketShutdown.Send);
if (_socket is not null)
{
await _socket.ShutdownAsync(SocketShutdown.Send).ConfigureAwait(false);
}
}
catch (Exception) { }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1077,7 +1077,7 @@ await Http2LoopbackServer.CreateClientAndServerAsync(async uri =>

// Server sends GOAWAY frame
await connection.SendGoAway(streamId, ProtocolErrors.ENHANCE_YOUR_CALM);
connection.ShutdownSend();
await connection.ShutdownSendAsync();
});
}

Expand Down Expand Up @@ -1487,7 +1487,7 @@ public async Task GoAwayFrame_AllPendingStreamsValid_RequestsSucceedAndConnectio
await connection.SendResponseDataAsync(streamId3, new byte[5], endStream: true);

// We will not send any more frames, so send EOF now, and ensure the client handles this properly.
connection.ShutdownSend();
await connection.ShutdownSendAsync();

// Receive all responses
HttpResponseMessage response1 = await sendTask1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ await LoopbackServer.CreateClientAndServerAsync(async url =>
await server.AcceptConnectionAsync(async connection =>
{
// Shut down the listen socket so no additional connections can happen
server.ListenSocket.Close();
await server.ListenSocket.CloseAsync();

// Initial response
await connection.ReadRequestHeaderAndSendResponseAsync(content: SimpleContent);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ await LoopbackServer.CreateClientAndServerAsync(
{
await server.AcceptConnectionAsync(async connection =>
{
server.ListenSocket.Close(); // Shut down the listen socket so attempts at additional connections would fail on the client
await server.ListenSocket.CloseAsync(); // Shut down the listen socket so attempts at additional connections would fail on the client

string response = LoopbackServer.GetContentModeResponse(mode, simpleContent);
await connection.ReadRequestHeaderAndSendCustomResponseAsync(response);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ await server.AcceptConnectionAsync(async connection =>
Assert.Equal(numBytes, await connection.ReadBlockAsync(postData, 0, numBytes));

await connection.WriteStringAsync(responseText).ConfigureAwait(false);
connection.Socket.Shutdown(SocketShutdown.Send);
await connection.Socket.ShutdownAsync(SocketShutdown.Send);
});

(await postAsync.ConfigureAwait(false)).Dispose();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1116,7 +1116,7 @@ await LoopbackServer.CreateClientAndServerAsync(async uri =>
await IgnoreExceptions(async () =>
{
LoopbackServer.Connection connection = await server.EstablishConnectionAsync().WaitAsync(cancelServerCts.Token);
connection.Socket.Shutdown(SocketShutdown.Send);
await connection.Socket.ShutdownAsync(SocketShutdown.Send);
});
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -592,7 +592,7 @@ await LoopbackServer.CreateClientAndServerAsync(
string response = LoopbackServer.GetContentModeResponse(mode, content);
await server.AcceptConnectionAsync(async connection =>
{
server.ListenSocket.Close(); // Shut down the listen socket so attempts at additional connections would fail on the client
await server.ListenSocket.CloseAsync(); // Shut down the listen socket so attempts at additional connections would fail on the client
await connection.ReadRequestHeaderAndSendCustomResponseAsync(response);
await connection.ReadRequestHeaderAndSendCustomResponseAsync(response);
});
Expand Down Expand Up @@ -1941,7 +1941,7 @@ await server.AcceptConnectionAsync(async (LoopbackServer.Connection connection)

string bigString = string.Concat(Enumerable.Repeat("abcdefghijklmnopqrstuvwxyz", 1000));
Task lotsOfDataSent = connection.SendResponseAsync(Encoding.ASCII.GetBytes(bigString));
connection.Socket.Shutdown(SocketShutdown.Send);
await connection.Socket.ShutdownAsync(SocketShutdown.Send);
await copyTask;
await lotsOfDataSent;
Assert.Equal("ghijklmnopqrstuvwxyz" + bigString, Encoding.ASCII.GetString(ms.ToArray()));
Expand Down Expand Up @@ -2165,7 +2165,7 @@ await Http2LoopbackServerFactory.CreateServerAsync(async (server, url) =>
await request2;

// Close underlying socket from first connection.
socket.Close();
await socket.CloseAsync();
}
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ public static async Task SendHttp11ServerResponseAndEosAsync(WebSocketRequestDat
}

// send server EOS (half-closing from server side)
requestData.Http11Connection!.Socket.Shutdown(SocketShutdown.Send);
await requestData.Http11Connection!.Socket.ShutdownAsync(SocketShutdown.Send);
}

public static async Task SendHttp2ServerResponseAndEosAsync(WebSocketRequestData requestData, bool eosInHeadersFrame, Func<WebSocketRequestData, CancellationToken, Task>? requestDataCallback, CancellationToken cancellationToken)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ private async Task RunClient_SendReceive_ConnectionClosedPrematurely_ReceiveAsyn
await pendingReceiveAsyncPosted.Task.WaitAsync(TimeSpan.FromMilliseconds(TimeOutMilliseconds));

// Close the underlying connection prematurely (without sending a WebSocket Close frame).
connection.Socket.Shutdown(SocketShutdown.Both);
connection.Socket.Close();
await connection.Socket.ShutdownAsync(SocketShutdown.Both);
await connection.Socket.CloseAsync();
});

// Initiate a connection attempt.
Expand Down
Loading