<commit_before>// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.IO;
using System.Net.Test.Common;
using System.Text;
using System.Threading.Tasks;
using Xunit;

namespace System.Net.Http.Functional.Tests
{
    public class HttpClientHandler_ResponseDrain_Test : HttpClientTestBase
    {
        public enum ContentMode
        {
            ContentLength,
            SingleChunk,
            BytePerChunk
        }

        protected static string GetResponseForContentMode(string content, ContentMode mode)
        {
            switch (mode)
            {
                case ContentMode.ContentLength:
                    return LoopbackServer.GetHttpResponse(content: content);
                case ContentMode.SingleChunk:
                    return LoopbackServer.GetSingleChunkHttpResponse(content: content);
                case ContentMode.BytePerChunk:
                    return LoopbackServer.GetBytePerChunkHttpResponse(content: content);
                default:
                    Assert.True(false);
                    return null;
            }
        }

        [OuterLoop]
        [Theory]
        [InlineData(ContentMode.ContentLength)]
        [InlineData(ContentMode.SingleChunk)]
        [InlineData(ContentMode.BytePerChunk)]
        public async Task GetAsync_DisposeBeforeReadingToEnd_DrainsRequestsAndReusesConnection(ContentMode mode)
        {
            if (IsWinHttpHandler && mode == ContentMode.BytePerChunk)
            {
                // WinHttpHandler seems to only do a limited amount of draining when chunking is used;
                // I *think* it's not draining anything beyond the current chunk being processed.
                // So, these cases won't pass.
                return;
            }

            const string simpleContent = "Hello world!";

            await LoopbackServer.CreateClientAndServerAsync(
                async url =>
                {
                    using (var 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();
                        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.<commit_msg>disable drain test variation for CurlHandler<commit_after>// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.IO;
using System.Net.Test.Common;
using System.Text;
using System.Threading.Tasks;
using Xunit;

namespace System.Net.Http.Functional.Tests
{
    public class HttpClientHandler_ResponseDrain_Test : HttpClientTestBase
    {
        public enum ContentMode
        {
            ContentLength,
            SingleChunk,
            BytePerChunk
        }

        protected static string GetResponseForContentMode(string content, ContentMode mode)
        {
            switch (mode)
            {
                case ContentMode.ContentLength:
                    return LoopbackServer.GetHttpResponse(content: content);
                case ContentMode.SingleChunk:
                    return LoopbackServer.GetSingleChunkHttpResponse(content: content);
                case ContentMode.BytePerChunk:
                    return LoopbackServer.GetBytePerChunkHttpResponse(content: content);
                default:
                    Assert.True(false);
                    return null;
            }
        }

        [OuterLoop]
        [Theory]
        [InlineData(ContentMode.ContentLength)]
        [InlineData(ContentMode.SingleChunk)]
        [InlineData(ContentMode.BytePerChunk)]
        public async Task GetAsync_DisposeBeforeReadingToEnd_DrainsRequestsAndReusesConnection(ContentMode mode)
        {
            if ((IsWinHttpHandler || IsCurlHandler) && mode == ContentMode.BytePerChunk)
            {
                // These handlers' behavior with multiple chunks is inconsistent, so disable the test.
                return;
            }

            const string simpleContent = "Hello world!";

            await LoopbackServer.CreateClientAndServerAsync(
                async url =>
                {
                    using (var 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();
                        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.
{'fraction_non_alphanumeric': 0.062075501500552834, 'fraction_numerical': 0.003474964460590744, 'mean_word_length': 1.8420107719928187, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '422311', 'n_tokens_mistral': 1456, 'n_tokens_neox': 1369, 'n_words': 426}