public async Task SendReceive_ConnectionClosedPrematurely_ReceiveAsyncFailsAndWebSocketStateUpdated() { var options = new LoopbackServer.Options { WebSocketEndpoint = true }; Func <ClientWebSocket, LoopbackServer, Uri, Task> connectToServerThatAbortsConnection = async(clientSocket, server, url) => { var pendingReceiveAsyncPosted = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); // Start listening for incoming connections on the server side. Task acceptTask = server.AcceptConnectionAsync(async connection => { // Complete the WebSocket upgrade. After this is done, the client-side ConnectAsync should complete. Assert.NotNull(await LoopbackHelper.WebSocketHandshakeAsync(connection)); // Wait for client-side ConnectAsync to complete and for a pending ReceiveAsync to be posted. 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(); }); // Initiate a connection attempt. var cts = new CancellationTokenSource(TimeOutMilliseconds); await clientSocket.ConnectAsync(url, cts.Token); // Post a pending ReceiveAsync before the TCP connection is torn down. var recvBuffer = new byte[100]; var recvSegment = new ArraySegment <byte>(recvBuffer); Task pendingReceiveAsync = ReceiveAsync(clientSocket, recvSegment, cts.Token); pendingReceiveAsyncPosted.SetResult(); // Wait for the server to close the underlying connection. await acceptTask.WaitAsync(cts.Token); WebSocketException pendingReceiveException = await Assert.ThrowsAsync <WebSocketException>(() => pendingReceiveAsync); Assert.Equal(WebSocketError.ConnectionClosedPrematurely, pendingReceiveException.WebSocketErrorCode); if (PlatformDetection.IsInAppContainer) { const uint WININET_E_CONNECTION_ABORTED = 0x80072EFE; Assert.NotNull(pendingReceiveException.InnerException); Assert.Equal(WININET_E_CONNECTION_ABORTED, (uint)pendingReceiveException.InnerException.HResult); } WebSocketException newReceiveException = await Assert.ThrowsAsync <WebSocketException>(() => ReceiveAsync(clientSocket, recvSegment, cts.Token)); Assert.Equal( ResourceHelper.GetExceptionMessage("net_WebSockets_InvalidState", "Aborted", "Open, CloseSent"), newReceiveException.Message); Assert.Equal(WebSocketState.Aborted, clientSocket.State); Assert.Null(clientSocket.CloseStatus); }; await LoopbackServer.CreateServerAsync(async (server, url) => { using (ClientWebSocket clientSocket = new ClientWebSocket()) { await connectToServerThatAbortsConnection(clientSocket, server, url); } }, options); }
public async Task SendReceive_ConnectionClosedPrematurely_ReceiveAsyncFailsAndWebSocketStateUpdated() { var options = new LoopbackServer.Options { WebSocketEndpoint = true }; Func <ClientWebSocket, Socket, Uri, Task> connectToServerThatAbortsConnection = async(clientSocket, server, url) => { AutoResetEvent pendingReceiveAsyncPosted = new AutoResetEvent(false); // Start listening for incoming connections on the server side. Task <List <string> > acceptTask = LoopbackServer.AcceptSocketAsync(server, async(socket, stream, reader, writer) => { // Complete the WebSocket upgrade. After this is done, the client-side ConnectAsync should complete. Assert.True(await LoopbackServer.WebSocketHandshakeAsync(socket, reader, writer)); // Wait for client-side ConnectAsync to complete and for a pending ReceiveAsync to be posted. pendingReceiveAsyncPosted.WaitOne(TimeOutMilliseconds); // Close the underlying connection prematurely (without sending a WebSocket Close frame). socket.Shutdown(SocketShutdown.Both); socket.Close(); return(null); }, options); // Initiate a connection attempt. var cts = new CancellationTokenSource(TimeOutMilliseconds); await clientSocket.ConnectAsync(url, cts.Token); // Post a pending ReceiveAsync before the TCP connection is torn down. var recvBuffer = new byte[100]; var recvSegment = new ArraySegment <byte>(recvBuffer); Task pendingReceiveAsync = clientSocket.ReceiveAsync(recvSegment, cts.Token); pendingReceiveAsyncPosted.Set(); // Wait for the server to close the underlying connection. acceptTask.Wait(cts.Token); // Validate I/O errors and socket state. if (!PlatformDetection.IsWindows) { _output.WriteLine("[Non-Windows] ManagedWebSocket-based implementation."); WebSocketException pendingReceiveException = await Assert.ThrowsAsync <WebSocketException>(() => pendingReceiveAsync); Assert.Equal(WebSocketError.ConnectionClosedPrematurely, pendingReceiveException.WebSocketErrorCode); WebSocketException newReceiveException = await Assert.ThrowsAsync <WebSocketException>(() => clientSocket.ReceiveAsync(recvSegment, cts.Token)); Assert.Equal(WebSocketError.ConnectionClosedPrematurely, newReceiveException.WebSocketErrorCode); Assert.Equal(WebSocketState.Open, clientSocket.State); Assert.Null(clientSocket.CloseStatus); } else if (PlatformDetection.IsFullFramework) { _output.WriteLine("[Windows] ManagedWebSocket-based implementation."); WebSocketException pendingReceiveException = await Assert.ThrowsAsync <WebSocketException>(() => pendingReceiveAsync); Assert.Equal(WebSocketError.ConnectionClosedPrematurely, pendingReceiveException.WebSocketErrorCode); WebSocketException newReceiveException = await Assert.ThrowsAsync <WebSocketException>(() => clientSocket.ReceiveAsync(recvSegment, cts.Token)); Assert.Equal(WebSocketError.Success, newReceiveException.WebSocketErrorCode); Assert.Equal( ResourceHelper.GetExceptionMessage("net_WebSockets_InvalidState", "Aborted", "Open, CloseSent"), newReceiveException.Message); Assert.Equal(WebSocketState.Aborted, clientSocket.State); Assert.Null(clientSocket.CloseStatus); } else if (PlatformDetection.IsUap) { _output.WriteLine("WinRTWebSocket-based implementation."); const uint WININET_E_CONNECTION_ABORTED = 0x80072EFE; WebSocketException pendingReceiveException = await Assert.ThrowsAsync <WebSocketException>(() => pendingReceiveAsync); Assert.Equal(WebSocketError.ConnectionClosedPrematurely, pendingReceiveException.WebSocketErrorCode); Assert.NotNull(pendingReceiveException.InnerException); Assert.Equal(WININET_E_CONNECTION_ABORTED, (uint)pendingReceiveException.InnerException.HResult); WebSocketException newReceiveException = await Assert.ThrowsAsync <WebSocketException>(() => clientSocket.ReceiveAsync(recvSegment, cts.Token)); Assert.Equal(WebSocketError.Success, newReceiveException.WebSocketErrorCode); Assert.Equal( ResourceHelper.GetExceptionMessage("net_WebSockets_InvalidState", "Aborted", "Open, CloseSent"), newReceiveException.Message); Assert.Equal(WebSocketState.Aborted, clientSocket.State); Assert.Null(clientSocket.CloseStatus); } else { _output.WriteLine("WinHttpWebSocket-based implementation."); const uint WININET_E_CONNECTION_RESET = 0x80072eff; Win32Exception pendingReceiveException = await Assert.ThrowsAnyAsync <Win32Exception>(() => pendingReceiveAsync); Assert.Equal(WININET_E_CONNECTION_RESET, (uint)pendingReceiveException.HResult); Win32Exception newReceiveException = await Assert.ThrowsAnyAsync <Win32Exception>(() => clientSocket.ReceiveAsync(recvSegment, cts.Token)); Assert.Equal(WININET_E_CONNECTION_RESET, (uint)newReceiveException.HResult); Assert.Equal(WebSocketState.Open, clientSocket.State); Assert.Null(clientSocket.CloseStatus); } }; await LoopbackServer.CreateServerAsync(async (server, url) => { using (ClientWebSocket clientSocket = new ClientWebSocket()) { await connectToServerThatAbortsConnection(clientSocket, server, url); } }, options); }