public async Task ServerDisconnectsAfterInitialRequest_SubsequentRequestUsesDifferentConnection() { using (HttpClient client = CreateHttpClient()) { await LoopbackServer.CreateServerAsync(async (listener, uri) => { // Make multiple requests iteratively. for (int i = 0; i < 2; i++) { Task <string> request = client.GetStringAsync(uri); await LoopbackServer.AcceptSocketAsync(listener, async(server, serverStream, serverReader, serverWriter) => { while (!string.IsNullOrWhiteSpace(await serverReader.ReadLineAsync())) { ; } await serverWriter.WriteAsync(LoopbackServer.DefaultHttpResponse); await request; server.Shutdown(SocketShutdown.Both); if (i == 0) { await Task.Delay(2000); // give client time to see the closing before next connect } return(null); }); } }); } }
public async Task SmallConnectionTimeout_SubsequentRequestUsesDifferentConnection(string timeoutPropertyName) { using (var handler = new SocketsHttpHandler()) { switch (timeoutPropertyName) { case "PooledConnectionLifetime": handler.PooledConnectionLifetime = TimeSpan.FromMilliseconds(1); break; case "PooledConnectionIdleTimeout": handler.PooledConnectionLifetime = TimeSpan.FromMilliseconds(1); break; default: throw new ArgumentOutOfRangeException(nameof(timeoutPropertyName)); } using (HttpClient client = new HttpClient(handler)) { await LoopbackServer.CreateServerAsync(async (listener, uri) => { // Make first request. Task <string> request1 = client.GetStringAsync(uri); await LoopbackServer.AcceptSocketAsync(listener, async(server1, serverStream1, serverReader1, serverWriter1) => { while (!string.IsNullOrWhiteSpace(await serverReader1.ReadLineAsync())) { ; } await serverWriter1.WriteAsync(LoopbackServer.DefaultHttpResponse); await request1; // Wait a small amount of time before making the second request, to give the first request time to timeout. await Task.Delay(100); // Make second request and expect it to be served from a different connection. Task <string> request2 = client.GetStringAsync(uri); await LoopbackServer.AcceptSocketAsync(listener, async(server2, serverStream2, serverReader2, serverWriter2) => { while (!string.IsNullOrWhiteSpace(await serverReader2.ReadLineAsync())) { ; } await serverWriter2.WriteAsync(LoopbackServer.DefaultHttpResponse); await request2; return(null); }); return(null); }); }); } } }
public async Task UpgradeConnection_Success() { await LoopbackServer.CreateServerAsync(async (server, url) => { using (HttpClient client = CreateHttpClient()) { // We need to use ResponseHeadersRead here, otherwise we will hang trying to buffer the response body. Task <HttpResponseMessage> getResponseTask = client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead); await LoopbackServer.AcceptSocketAsync(server, async(s, serverStream, serverReader, serverWriter) => { Task <List <string> > serverTask = LoopbackServer.ReadWriteAcceptedAsync(s, serverReader, serverWriter, $"HTTP/1.1 101 Switching Protocols\r\nDate: {DateTimeOffset.UtcNow:R}\r\n\r\n"); await TestHelper.WhenAllCompletedOrAnyFailed(getResponseTask, serverTask); using (Stream clientStream = await(await getResponseTask).Content.ReadAsStreamAsync()) { Assert.True(clientStream.CanWrite); Assert.True(clientStream.CanRead); Assert.False(clientStream.CanSeek); TextReader clientReader = new StreamReader(clientStream); TextWriter clientWriter = new StreamWriter(clientStream) { AutoFlush = true }; const string helloServer = "hello server"; const string helloClient = "hello client"; const string goodbyeServer = "goodbye server"; const string goodbyeClient = "goodbye client"; clientWriter.WriteLine(helloServer); Assert.Equal(helloServer, serverReader.ReadLine()); serverWriter.WriteLine(helloClient); Assert.Equal(helloClient, clientReader.ReadLine()); clientWriter.WriteLine(goodbyeServer); Assert.Equal(goodbyeServer, serverReader.ReadLine()); serverWriter.WriteLine(goodbyeClient); Assert.Equal(goodbyeClient, clientReader.ReadLine()); } return(null); }); } }); }
public async Task SmallConnectionTimeout_SubsequentRequestUsesDifferentConnection(string timeoutPropertyName) { using (HttpClientHandler handler = CreateHttpClientHandler()) { SetManagedHandlerProperty(handler, timeoutPropertyName, TimeSpan.FromMilliseconds(1)); using (HttpClient client = new HttpClient(handler)) { await LoopbackServer.CreateServerAsync(async (listener, uri) => { // Make first request. Task <string> request1 = client.GetStringAsync(uri); await LoopbackServer.AcceptSocketAsync(listener, async(server1, serverStream1, serverReader1, serverWriter1) => { while (!string.IsNullOrWhiteSpace(await serverReader1.ReadLineAsync())) { ; } await serverWriter1.WriteAsync(LoopbackServer.DefaultHttpResponse); await request1; // Wait a small amount of time before making the second request, to give the first request time to timeout. await Task.Delay(100); // Make second request and expect it to be served from a different connection. Task <string> request2 = client.GetStringAsync(uri); await LoopbackServer.AcceptSocketAsync(listener, async(server2, serverStream2, serverReader2, serverWriter2) => { while (!string.IsNullOrWhiteSpace(await serverReader2.ReadLineAsync())) { ; } await serverWriter2.WriteAsync(LoopbackServer.DefaultHttpResponse); await request2; return(null); }); return(null); }); }); } } }
public async Task ClientCertificates_ValidCertificate_ServerReceivesCertificateAndConnectAsyncSucceeds() { var options = new LoopbackServer.Options { UseSsl = true, WebSocketEndpoint = true }; Func <ClientWebSocket, Socket, Uri, X509Certificate2, Task> connectToServerWithClientCert = async(clientSocket, server, url, clientCert) => { // Start listening for incoming connections on the server side. Task <List <string> > acceptTask = LoopbackServer.AcceptSocketAsync(server, async(socket, stream, reader, writer) => { // Validate that the client certificate received by the server matches the one configured on // the client-side socket. SslStream sslStream = Assert.IsType <SslStream>(stream); Assert.NotNull(sslStream.RemoteCertificate); Assert.Equal(clientCert, new X509Certificate2(sslStream.RemoteCertificate)); // Complete the WebSocket upgrade over the secure channel. After this is done, the client-side // ConnectAsync should complete. Assert.True(await LoopbackServer.WebSocketHandshakeAsync(socket, reader, writer)); return(null); }, options); // Initiate a connection attempt with a client certificate configured on the socket. clientSocket.Options.ClientCertificates.Add(clientCert); var cts = new CancellationTokenSource(TimeOutMilliseconds); await clientSocket.ConnectAsync(url, cts.Token); acceptTask.Wait(cts.Token); }; await LoopbackServer.CreateServerAsync(async (server, url) => { using (X509Certificate2 clientCert = Test.Common.Configuration.Certificates.GetClientCertificate()) { using (ClientWebSocket clientSocket = new ClientWebSocket()) { await connectToServerWithClientCert(clientSocket, server, url, clientCert); } } }, options); }
public async Task ServerSendsConnectionClose_SubsequentRequestUsesDifferentConnection() { using (HttpClient client = CreateHttpClient()) { await LoopbackServer.CreateServerAsync(async (listener, uri) => { string responseBody = "HTTP/1.1 200 OK\r\n" + $"Date: {DateTimeOffset.UtcNow:R}\r\n" + "Content-Length: 0\r\n" + "Connection: close\r\n" + "\r\n"; // Make first request. Task <string> request1 = client.GetStringAsync(uri); await LoopbackServer.AcceptSocketAsync(listener, async(server1, serverStream1, serverReader1, serverWriter1) => { while (!string.IsNullOrWhiteSpace(await serverReader1.ReadLineAsync())) { ; } await serverWriter1.WriteAsync(responseBody); await request1; // Make second request and expect it to be served from a different connection. Task <string> request2 = client.GetStringAsync(uri); await LoopbackServer.AcceptSocketAsync(listener, async(server2, serverStream2, serverReader2, serverWriter2) => { while (!string.IsNullOrWhiteSpace(await serverReader2.ReadLineAsync())) { ; } await serverWriter2.WriteAsync(responseBody); await request2; return(null); }); return(null); }); }); } }
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 = ReceiveAsync(clientSocket, 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.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>(() => ReceiveAsync(clientSocket, 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>(() => ReceiveAsync(clientSocket, 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("[Non-Windows] ManagedWebSocket-based implementation."); WebSocketException pendingReceiveException = await Assert.ThrowsAsync <WebSocketException>(() => pendingReceiveAsync); Assert.Equal(WebSocketError.ConnectionClosedPrematurely, pendingReceiveException.WebSocketErrorCode); WebSocketException newReceiveException = await Assert.ThrowsAsync <WebSocketException>(() => ReceiveAsync(clientSocket, recvSegment, cts.Token)); Assert.Equal(WebSocketError.ConnectionClosedPrematurely, newReceiveException.WebSocketErrorCode); 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); }