Beispiel #1
0
        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);
                        });
                    }
                });
            }
        }
Beispiel #2
0
        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);
                        });
                    });
                }
            }
        }
Beispiel #3
0
        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);
                    });
                }
            });
        }
Beispiel #4
0
        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);
                        });
                    });
                }
            }
        }
Beispiel #5
0
        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);
        }
Beispiel #6
0
        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);
        }