public async Task GetAsync_DisposeBeforeReadingToEnd_DrainsRequestsAndReusesConnection(LoopbackServer.ContentMode mode)
        {
            const string simpleContent = "Hello world!";

            await LoopbackServer.CreateClientAndServerAsync(
                async url =>
            {
                using (HttpClient client = CreateHttpClient())
                {
                    HttpResponseMessage response1 = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
                    ValidateResponseHeaders(response1, simpleContent.Length, mode);

                    // Read up to exactly 1 byte before the end of the response
                    Stream responseStream = await response1.Content.ReadAsStreamAsync(TestAsync);
                    byte[] bytes          = await ReadToByteCount(responseStream, simpleContent.Length - 1);
                    Assert.Equal(simpleContent.Substring(0, simpleContent.Length - 1), Encoding.ASCII.GetString(bytes));

                    // Introduce a short delay to try to ensure that when we dispose the response,
                    // all response data is available and we can drain synchronously and reuse the connection.
                    await Task.Delay(100);

                    response1.Dispose();

                    // Issue another request.  We'll confirm that it comes on the same connection.
                    HttpResponseMessage response2 = await client.GetAsync(url);
                    ValidateResponseHeaders(response2, simpleContent.Length, mode);
                    Assert.Equal(simpleContent, await response2.Content.ReadAsStringAsync());
                }
            },
                async server =>
            {
                await server.AcceptConnectionAsync(async connection =>
                {
                    server.ListenSocket.Close();     // 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);
                    await connection.ReadRequestHeaderAndSendCustomResponseAsync(response);
                });
            });
        }
        public async Task GetAsyncWithMaxConnections_DisposeBeforeReadingToEnd_DrainsRequestsAndReusesConnection(int totalSize, int readSize, LoopbackServer.ContentMode mode)
        {
            await LoopbackServer.CreateClientAndServerAsync(
                async url =>
            {
                HttpClientHandler handler = CreateHttpClientHandler();
                SetResponseDrainTimeout(handler, Timeout.InfiniteTimeSpan);

                // Set MaxConnectionsPerServer to 1.  This will ensure we will wait for the previous request to drain (or fail to)
                handler.MaxConnectionsPerServer = 1;

                using (HttpClient client = CreateHttpClient(handler))
                {
                    HttpResponseMessage response1 = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
                    ValidateResponseHeaders(response1, totalSize, mode);

                    // Read part but not all of response
                    Stream responseStream = await response1.Content.ReadAsStreamAsync(TestAsync);
                    await ReadToByteCount(responseStream, readSize);

                    response1.Dispose();

                    // Issue another request.  We'll confirm that it comes on the same connection.
                    HttpResponseMessage response2 = await client.GetAsync(url);
                    ValidateResponseHeaders(response2, totalSize, mode);
                    Assert.Equal(totalSize, (await response2.Content.ReadAsStringAsync()).Length);
                }
            },
                async server =>
            {
                string content  = new string('a', totalSize);
                string response = LoopbackServer.GetContentModeResponse(mode, content);
                await server.AcceptConnectionAsync(async connection =>
                {
                    // Process the first request, with some introduced delays in the response to
                    // stress the draining.
                    await connection.ReadRequestHeaderAsync().ConfigureAwait(false);
                    foreach (char c in response)
                    {
                        await connection.Writer.WriteAsync(c);
                    }

                    // Process the second request.
                    await connection.ReadRequestHeaderAndSendCustomResponseAsync(response);
                });
            });
        }
        protected static void ValidateResponseHeaders(HttpResponseMessage response, int contentLength, LoopbackServer.ContentMode mode)
        {
            Assert.Equal(HttpStatusCode.OK, response.StatusCode);

            switch (mode)
            {
            case LoopbackServer.ContentMode.ContentLength:
                Assert.Equal(contentLength, response.Content.Headers.ContentLength);
                break;

            case LoopbackServer.ContentMode.SingleChunk:
            case LoopbackServer.ContentMode.BytePerChunk:
                Assert.True(response.Headers.TransferEncodingChunked);
                break;
            }
        }
        public async Task GetAsyncWithMaxConnections_DisposeBeforeReadingToEnd_KillsConnection(int totalSize, int readSize, LoopbackServer.ContentMode mode)
        {
            await LoopbackServer.CreateClientAndServerAsync(
                async url =>
            {
                HttpClientHandler handler = CreateHttpClientHandler();
                SetResponseDrainTimeout(handler, Timeout.InfiniteTimeSpan);

                // Set MaxConnectionsPerServer to 1.  This will ensure we will wait for the previous request to drain (or fail to)
                handler.MaxConnectionsPerServer = 1;

                using (HttpClient client = CreateHttpClient(handler))
                {
                    HttpResponseMessage response1 = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
                    ValidateResponseHeaders(response1, totalSize, mode);

                    // Read part but not all of response
                    Stream responseStream = await response1.Content.ReadAsStreamAsync(TestAsync);
                    await ReadToByteCount(responseStream, readSize);

                    response1.Dispose();

                    // Issue another request.  We'll confirm that it comes on a new connection.
                    HttpResponseMessage response2 = await client.GetAsync(url);
                    ValidateResponseHeaders(response2, totalSize, mode);
                    Assert.Equal(totalSize, (await response2.Content.ReadAsStringAsync()).Length);
                }
            },
                async server =>
            {
                string content = new string('a', totalSize);
                await server.AcceptConnectionAsync(async connection =>
                {
                    await connection.ReadRequestHeaderAsync();
                    try
                    {
                        await connection.Writer.WriteAsync(LoopbackServer.GetContentModeResponse(mode, content, connectionClose: false));
                    }
                    catch (Exception) { }         // Eat errors from client disconnect.

                    await server.AcceptConnectionSendCustomResponseAndCloseAsync(LoopbackServer.GetContentModeResponse(mode, content, connectionClose: true));
                });
            });
        }
        public async Task GetAsyncLargeRequestWithMaxConnections_DisposeBeforeReadingToEnd_DrainsRequestsAndReusesConnection(int totalSize, int readSize, LoopbackServer.ContentMode mode)
        {
            await GetAsyncWithMaxConnections_DisposeBeforeReadingToEnd_DrainsRequestsAndReusesConnection(totalSize, readSize, mode);

            return;
        }
        public async Task ResponseHeadersRead_SynchronizationContextNotUsedByHandler(bool responseHeadersRead, LoopbackServer.ContentMode contentMode)
        {
            if (IsWinHttpHandler && (PlatformDetection.IsWindows7 || PlatformDetection.IsWindows8x))
            {   // [ActiveIssue("https://github.com/dotnet/runtime/issues/54034")]
                return;
            }

            await Task.Run(async delegate // escape xunit's sync ctx
            {
                await LoopbackServer.CreateClientAndServerAsync(uri =>
                {
                    return(Task.Run(() => // allow client and server to run concurrently even though this is all synchronous/blocking
                    {
                        var sc = new TrackingSynchronizationContext();
                        SynchronizationContext.SetSynchronizationContext(sc);

                        using (HttpClient client = CreateHttpClient())
                        {
                            if (responseHeadersRead)
                            {
                                using (HttpResponseMessage resp = client.GetAsync(uri, HttpCompletionOption.ResponseHeadersRead).GetAwaiter().GetResult())
                                    using (Stream respStream = resp.Content.ReadAsStreamAsync().GetAwaiter().GetResult())
                                    {
                                        byte[] buffer = new byte[0x1000];
                                        while (respStream.ReadAsync(buffer, 0, buffer.Length).GetAwaiter().GetResult() > 0)
                                        {
                                            ;
                                        }
                                    }
                            }
                            else
                            {
                                client.GetStringAsync(uri).GetAwaiter().GetResult();
                            }
                        }

                        Assert.True(sc.CallStacks.Count == 0, "Sync Ctx used: " + string.Join(Environment.NewLine + Environment.NewLine, sc.CallStacks));
                    }));
                }, async server =>
                {
                    await server.AcceptConnectionAsync(async connection =>
                    {
                        await connection.ReadRequestHeaderAsync();
                        await connection.WriteStringAsync(
                            LoopbackServer.GetContentModeResponse(
                                contentMode,
                                string.Concat(Enumerable.Repeat('s', 10_000)),
                                connectionClose: true));
                    });
                }, new LoopbackServer.Options {
                    StreamWrapper = s => new DribbleStream(s)
                });
            });
Example #7
0
        public async Task GetAsyncLargeRequestWithMaxConnections_DisposeBeforeReadingToEnd_DrainsRequestsAndReusesConnection(int totalSize, int readSize, LoopbackServer.ContentMode mode)
        {
            // SocketsHttpHandler will reliably drain up to 1MB; other handlers don't.
            if (!UseSocketsHttpHandler)
            {
                return;
            }

            await GetAsyncWithMaxConnections_DisposeBeforeReadingToEnd_DrainsRequestsAndReusesConnection(totalSize, readSize, mode);

            return;
        }