// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http2Cat;
using Microsoft.AspNetCore.Server.IntegrationTesting;
using Microsoft.AspNetCore.Server.IntegrationTesting.Common;
using Microsoft.AspNetCore.Server.IntegrationTesting.IIS;
using Microsoft.AspNetCore.Server;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2;
using Microsoft.AspNetCore.InternalTesting;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Net.Http.Headers;
using Xunit;

namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests;

/// <summary>
/// These features/tests Are only supported on newer versions of Windows and IIS. They are not supported
/// on IIS Express even on the new Windows versions because IIS Express has its own outdated copy of IIS.
/// </summary>
[Collection(IISHttpsTestSiteCollection.Name)]
public class Http2TrailerResetTests : FunctionalTestsBase
{
    private const string WindowsVersionForTrailers = "10.0.20238";

    public Http2TrailerResetTests(IISTestSiteFixture fixture)
    {
        var port = TestPortHelper.GetNextSSLPort();
        fixture.DeploymentParameters.ApplicationBaseUriHint = $"https://localhost:{port}/";
        fixture.DeploymentParameters.AddHttpsToServerConfig();
        fixture.DeploymentParameters.SetWindowsAuth(false);
        Fixture = fixture;
    }

    public IISTestSiteFixture Fixture { get; }

    [ConditionalFact]
    [MinimumOSVersion(OperatingSystems.Windows, WindowsVersionForTrailers)]
    public async Task ResponseTrailers_HTTP2_TrailersAvailable()
    {
        var response = await SendRequestAsync(Fixture.Client.BaseAddress.ToString() + "ResponseTrailers_HTTP2_TrailersAvailable");

        response.EnsureSuccessStatusCode();
        Assert.Equal(HttpVersion.Version20, response.Version);
        Assert.Empty(response.TrailingHeaders);
    }

    [ConditionalFact]
    [MinimumOSVersion(OperatingSystems.Windows, WindowsVersionForTrailers)]
    public async Task ResponseTrailers_ProhibitedTrailers_Blocked()
    {
        var response = await SendRequestAsync(Fixture.Client.BaseAddress.ToString() + "ResponseTrailers_ProhibitedTrailers_Blocked");

        response.EnsureSuccessStatusCode();
        Assert.Equal(HttpVersion.Version20, response.Version);
        Assert.Empty(response.TrailingHeaders);
    }

    [ConditionalFact]
    [MinimumOSVersion(OperatingSystems.Windows, WindowsVersionForTrailers)]
    public async Task ResponseTrailers_NoBody_TrailersSent()
    {
        var response = await SendRequestAsync(Fixture.Client.BaseAddress.ToString() + "ResponseTrailers_NoBody_TrailersSent");

        response.EnsureSuccessStatusCode();
        Assert.Equal(HttpVersion.Version20, response.Version);
        Assert.NotEmpty(response.TrailingHeaders);
        Assert.Equal("TrailerValue", response.TrailingHeaders.GetValues("TrailerName").Single());
    }

    [ConditionalFact]
    [MinimumOSVersion(OperatingSystems.Windows, WindowsVersionForTrailers)]
    public async Task ResponseTrailers_WithBody_TrailersSent()
    {
        var response = await SendRequestAsync(Fixture.Client.BaseAddress.ToString() + "ResponseTrailers_WithBody_TrailersSent");

        response.EnsureSuccessStatusCode();
        Assert.Equal(HttpVersion.Version20, response.Version);
        Assert.Equal("Hello World", await response.Content.ReadAsStringAsync());
        Assert.NotEmpty(response.TrailingHeaders);
        Assert.Equal("Trailer Value", response.TrailingHeaders.GetValues("TrailerName").Single());
    }

    [ConditionalFact]
    [MinimumOSVersion(OperatingSystems.Windows, WindowsVersionForTrailers)]
    public async Task ResponseTrailers_WithContentLengthBody_TrailersSent()
    {
        var body = "Hello World";

        var response = await SendRequestAsync(Fixture.Client.BaseAddress.ToString() + "ResponseTrailers_WithContentLengthBody_TrailersSent");

        response.EnsureSuccessStatusCode();
        Assert.Equal(HttpVersion.Version20, response.Version);
        Assert.Equal(body, await response.Content.ReadAsStringAsync());
        Assert.NotEmpty(response.TrailingHeaders);
        Assert.Equal("Trailer Value", response.TrailingHeaders.GetValues("TrailerName").Single());
    }

    [ConditionalFact]
    [MinimumOSVersion(OperatingSystems.Windows, WindowsVersionForTrailers)]
    public async Task ResponseTrailers_WithTrailersBeforeContentLengthBody_TrailersSent()
    {
        var body = "Hello World";

        var response = await SendRequestAsync(Fixture.Client.BaseAddress.ToString() + "ResponseTrailers_WithTrailersBeforeContentLengthBody_TrailersSent");

        response.EnsureSuccessStatusCode();
        Assert.Equal(HttpVersion.Version20, response.Version);
        // Avoid HttpContent's automatic content-length calculation.
        Assert.True(response.Content.Headers.TryGetValues(HeaderNames.ContentLength, out var contentLength), HeaderNames.ContentLength);
        Assert.Equal((2 * body.Length).ToString(CultureInfo.InvariantCulture), contentLength.First());
        Assert.Equal(body + body, await response.Content.ReadAsStringAsync());
        Assert.NotEmpty(response.TrailingHeaders);
        Assert.Equal("Trailer Value", response.TrailingHeaders.GetValues("TrailerName").Single());
    }

    [ConditionalFact]
    [MinimumOSVersion(OperatingSystems.Windows, WindowsVersionForTrailers)]
    public async Task ResponseTrailers_WithContentLengthBodyAndDeclared_TrailersSent()
    {
        var body = "Hello World";

        var response = await SendRequestAsync(Fixture.Client.BaseAddress.ToString() + "ResponseTrailers_WithContentLengthBodyAndDeclared_TrailersSent");

        response.EnsureSuccessStatusCode();
        Assert.Equal(HttpVersion.Version20, response.Version);
        // Avoid HttpContent's automatic content-length calculation.
        Assert.True(response.Content.Headers.TryGetValues(HeaderNames.ContentLength, out var contentLength), HeaderNames.ContentLength);
        Assert.Equal(body.Length.ToString(CultureInfo.InvariantCulture), contentLength.First());
        Assert.Equal("TrailerName", response.Headers.Trailer.Single());
        Assert.Equal(body, await response.Content.ReadAsStringAsync());
        Assert.NotEmpty(response.TrailingHeaders);
        Assert.Equal("Trailer Value", response.TrailingHeaders.GetValues("TrailerName").Single());
    }

    [ConditionalFact]
    [MinimumOSVersion(OperatingSystems.Windows, WindowsVersionForTrailers)]
    public async Task ResponseTrailers_MultipleValues_SentAsSeparateHeaders()
    {
        var response = await SendRequestAsync(Fixture.Client.BaseAddress.ToString() + "ResponseTrailers_MultipleValues_SentAsSeparateHeaders");

        response.EnsureSuccessStatusCode();
        Assert.Equal(HttpVersion.Version20, response.Version);
        Assert.NotEmpty(response.TrailingHeaders);

        Assert.Equal(new[] { "TrailerValue0", "TrailerValue1" }, response.TrailingHeaders.GetValues("TrailerName"));
    }

    [ConditionalFact]
    [MinimumOSVersion(OperatingSystems.Windows, WindowsVersionForTrailers)]
    public async Task ResponseTrailers_CompleteAsyncNoBody_TrailersSent()
    {
        // The app func for CompleteAsync will not finish until CompleteAsync_Completed is sent.
        // This verifies that the response is sent to the client with CompleteAsync
        var response = await SendRequestAsync(Fixture.Client.BaseAddress.ToString() + "ResponseTrailers_CompleteAsyncNoBody_TrailersSent");
        response.EnsureSuccessStatusCode();
        Assert.Equal(HttpVersion.Version20, response.Version);
        Assert.NotEmpty(response.TrailingHeaders);
        Assert.Equal("TrailerValue", response.TrailingHeaders.GetValues("TrailerName").Single());

        var response2 = await SendRequestAsync(Fixture.Client.BaseAddress.ToString() + "ResponseTrailers_CompleteAsyncNoBody_TrailersSent_Completed");
        Assert.True(response2.IsSuccessStatusCode);
    }

    [ConditionalFact]
    [MinimumOSVersion(OperatingSystems.Windows, WindowsVersionForTrailers)]
    public async Task ResponseTrailers_CompleteAsyncWithBody_TrailersSent()
    {
        // The app func for CompleteAsync will not finish until CompleteAsync_Completed is sent.
        // This verifies that the response is sent to the client with CompleteAsync
        var response = await SendRequestAsync(Fixture.Client.BaseAddress.ToString() + "ResponseTrailers_CompleteAsyncWithBody_TrailersSent");
        response.EnsureSuccessStatusCode();
        Assert.Equal(HttpVersion.Version20, response.Version);
        Assert.Equal("Hello World", await response.Content.ReadAsStringAsync());
        Assert.NotEmpty(response.TrailingHeaders);
        Assert.Equal("Trailer Value", response.TrailingHeaders.GetValues("TrailerName").Single());

        var response2 = await SendRequestAsync(Fixture.Client.BaseAddress.ToString() + "ResponseTrailers_CompleteAsyncWithBody_TrailersSent_Completed");
        Assert.True(response2.IsSuccessStatusCode);
    }

    [ConditionalFact]
    [RequiresNewHandler]
    [MinimumOSVersion(OperatingSystems.Windows, WindowsVersionForTrailers)]
    public async Task AppException_AfterHeaders_ResetInternalError()
    {
        await new HostBuilder()
            .UseHttp2Cat(Fixture.Client.BaseAddress.AbsoluteUri, async h2Connection =>
            {
                await h2Connection.InitializeConnectionAsync();

                h2Connection.Logger.LogInformation("Initialized http2 connection. Starting stream 1.");

                await h2Connection.StartStreamAsync(1, GetHeaders("/AppException_AfterHeaders_ResetInternalError"), endStream: true);

                await h2Connection.ReceiveHeadersAsync(1, decodedHeaders =>
                {
                    Assert.Equal("200", decodedHeaders[InternalHeaderNames.Status]);
                });

                var frame = await h2Connection.ReceiveFrameAsync();

                Http2Utilities.VerifyResetFrame(frame, expectedStreamId: 1, Http2ErrorCode.INTERNAL_ERROR);

                h2Connection.Logger.LogInformation("Connection stopped.");
            })
            .Build().RunAsync();
    }

    [ConditionalFact]
    [RequiresNewHandler]
    [MinimumOSVersion(OperatingSystems.Windows, WindowsVersionForTrailers)]
    public async Task Reset_BeforeResponse_Resets()
    {
        await new HostBuilder()
            .UseHttp2Cat(Fixture.Client.BaseAddress.AbsoluteUri, async h2Connection =>
            {
                await h2Connection.InitializeConnectionAsync();

                h2Connection.Logger.LogInformation("Initialized http2 connection. Starting stream 1.");

                await h2Connection.StartStreamAsync(1, GetHeaders("/Reset_BeforeResponse_Resets"), endStream: true);

                var resetFrame = await h2Connection.ReceiveFrameAsync();
                Http2Utilities.VerifyResetFrame(resetFrame, expectedStreamId: 1, expectedErrorCode: (Http2ErrorCode)1111);

                // Any app errors?
                var client = CreateClient();
                var response = await client.GetAsync(Fixture.Client.BaseAddress + "/Reset_BeforeResponse_Resets_Complete");
                Assert.True(response.IsSuccessStatusCode);

                h2Connection.Logger.LogInformation("Connection stopped.");
            })
            .Build().RunAsync();
    }

    [ConditionalFact]
    [RequiresNewHandler]
    [MinimumOSVersion(OperatingSystems.Windows, WindowsVersionForTrailers)]
    public async Task RequestClose_SendsGoAway()
    {
        await new HostBuilder()
          .UseHttp2Cat(Fixture.Client.BaseAddress.AbsoluteUri, async h2Connection =>
          {
              await h2Connection.InitializeConnectionAsync();

              h2Connection.Logger.LogInformation("Initialized http2 connection. Starting stream 1.");

              await h2Connection.StartStreamAsync(1, GetHeaders("/ConnectionRequestClose"), endStream: true);

              var goAwayFrame = await h2Connection.ReceiveFrameAsync();
              Http2Utilities.VerifyGoAway(goAwayFrame, int.MaxValue, Http2ErrorCode.NO_ERROR);

              await h2Connection.ReceiveHeadersAsync(1, decodedHeaders =>
              {
                  // HTTP/2 filters out the connection header
                  Assert.False(decodedHeaders.ContainsKey(HeaderNames.Connection));
                  Assert.Equal("200", decodedHeaders[InternalHeaderNames.Status]);
              });

              var dataFrame = await h2Connection.ReceiveFrameAsync();
              Http2Utilities.VerifyDataFrame(dataFrame, 1, endOfStream: true, length: 0);

              // Http.Sys doesn't send a final GoAway unless we ignore the first one and send 200 additional streams.

              h2Connection.Logger.LogInformation("Connection stopped.");
          })
          .Build().RunAsync();
    }

    [ConditionalFact]
    [RequiresNewHandler]
    [MinimumOSVersion(OperatingSystems.Windows, WindowsVersionForTrailers)]
    public async Task Reset_BeforeResponse_Zero_Resets()
    {
        await new HostBuilder()
            .UseHttp2Cat(Fixture.Client.BaseAddress.AbsoluteUri, async h2Connection =>
            {
                await h2Connection.InitializeConnectionAsync();

                h2Connection.Logger.LogInformation("Initialized http2 connection. Starting stream 1.");

                await h2Connection.StartStreamAsync(1, GetHeaders("/Reset_BeforeResponse_Zero_Resets"), endStream: true);

                var resetFrame = await h2Connection.ReceiveFrameAsync();
                Http2Utilities.VerifyResetFrame(resetFrame, expectedStreamId: 1, expectedErrorCode: (Http2ErrorCode)0);

                // Any app errors?
                var client = CreateClient();
                var response = await client.GetAsync(Fixture.Client.BaseAddress + "/Reset_BeforeResponse_Zero_Resets_Complete");
                Assert.True(response.IsSuccessStatusCode);

                h2Connection.Logger.LogInformation("Connection stopped.");
            })
            .Build().RunAsync();
    }

    [ConditionalFact]
    [RequiresNewHandler]
    [MinimumOSVersion(OperatingSystems.Windows, WindowsVersionForTrailers)]
    public async Task Reset_AfterResponseHeaders_Resets()
    {
        await new HostBuilder()
            .UseHttp2Cat(Fixture.Client.BaseAddress.AbsoluteUri, async h2Connection =>
            {
                await h2Connection.InitializeConnectionAsync();

                h2Connection.Logger.LogInformation("Initialized http2 connection. Starting stream 1.");

                await h2Connection.StartStreamAsync(1, GetHeaders("/Reset_AfterResponseHeaders_Resets"), endStream: true);

                // Any app errors?
                var client = CreateClient();
                var response = await client.GetAsync(Fixture.Client.BaseAddress + "/Reset_AfterResponseHeaders_Resets_Complete");
                Assert.True(response.IsSuccessStatusCode);

                await h2Connection.ReceiveHeadersAsync(1, decodedHeaders =>
                {
                    Assert.Equal("200", decodedHeaders[InternalHeaderNames.Status]);
                });

                var resetFrame = await h2Connection.ReceiveFrameAsync();
                Http2Utilities.VerifyResetFrame(resetFrame, expectedStreamId: 1, expectedErrorCode: (Http2ErrorCode)1111);

                h2Connection.Logger.LogInformation("Connection stopped.");
            })
            .Build().RunAsync();
    }

    [ConditionalFact]
    [RequiresNewHandler]
    [MinimumOSVersion(OperatingSystems.Windows, WindowsVersionForTrailers)]
    public async Task Reset_DuringResponseBody_Resets()
    {
        await new HostBuilder()
            .UseHttp2Cat(Fixture.Client.BaseAddress.AbsoluteUri, async h2Connection =>
            {
                await h2Connection.InitializeConnectionAsync();

                h2Connection.Logger.LogInformation("Initialized http2 connection. Starting stream 1.");

                await h2Connection.StartStreamAsync(1, GetHeaders("/Reset_DuringResponseBody_Resets"), endStream: true);

                // This is currently flaky, can either receive header or reset at this point
                var headerOrResetFrame = await h2Connection.ReceiveFrameAsync();
                Assert.True(headerOrResetFrame.Type == Http2FrameType.HEADERS || headerOrResetFrame.Type == Http2FrameType.RST_STREAM);

                if (headerOrResetFrame.Type == Http2FrameType.HEADERS)
                {
                    var dataFrame = await h2Connection.ReceiveFrameAsync();
                    Http2Utilities.VerifyDataFrame(dataFrame, 1, endOfStream: false, length: 11);

                    var resetFrame = await h2Connection.ReceiveFrameAsync();
                    Http2Utilities.VerifyResetFrame(resetFrame, expectedStreamId: 1, expectedErrorCode: (Http2ErrorCode)1111);
                }
                else
                {
                    Http2Utilities.VerifyResetFrame(headerOrResetFrame, expectedStreamId: 1, expectedErrorCode: (Http2ErrorCode)1111);
                }

                h2Connection.Logger.LogInformation("Connection stopped.");
            })
            .Build().RunAsync();
    }

    [ConditionalFact]
    [RequiresNewHandler]
    [MinimumOSVersion(OperatingSystems.Windows, WindowsVersionForTrailers)]
    public async Task Reset_BeforeRequestBody_Resets()
    {
        await new HostBuilder()
            .UseHttp2Cat(Fixture.Client.BaseAddress.AbsoluteUri, async h2Connection =>
            {
                await h2Connection.InitializeConnectionAsync();

                h2Connection.Logger.LogInformation("Initialized http2 connection. Starting stream 1.");

                await h2Connection.StartStreamAsync(1, GetPostHeaders("/Reset_BeforeRequestBody_Resets"), endStream: false);

                // Any app errors?
                //Assert.Equal(0, await appResult.Task.DefaultTimeout());

                var resetFrame = await h2Connection.ReceiveFrameAsync();
                Http2Utilities.VerifyResetFrame(resetFrame, expectedStreamId: 1, expectedErrorCode: (Http2ErrorCode)1111);

                h2Connection.Logger.LogInformation("Connection stopped.");
            })
            .Build().RunAsync();
    }

    [ConditionalFact]
    [RequiresNewHandler]
    [MinimumOSVersion(OperatingSystems.Windows, WindowsVersionForTrailers)]
    [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/61056")]
    public async Task Reset_DuringRequestBody_Resets()
    {
        await new HostBuilder()
            .UseHttp2Cat(Fixture.Client.BaseAddress.AbsoluteUri, async h2Connection =>
            {
                await h2Connection.InitializeConnectionAsync();

                h2Connection.Logger.LogInformation("Initialized http2 connection. Starting stream 1.");

                await h2Connection.StartStreamAsync(1, GetPostHeaders("/Reset_DuringRequestBody_Resets"), endStream: false);
                await h2Connection.SendDataAsync(1, new byte[10], endStream: false);

                // Any app errors?
                //Assert.Equal(0, await appResult.Task.DefaultTimeout());

                var resetFrame = await h2Connection.ReceiveFrameAsync();
                Http2Utilities.VerifyResetFrame(resetFrame, expectedStreamId: 1, expectedErrorCode: (Http2ErrorCode)1111);

                h2Connection.Logger.LogInformation("Connection stopped.");
            })
            .Build().RunAsync();
    }

    [ConditionalFact]
    [MinimumOSVersion(OperatingSystems.Windows, WindowsVersionForTrailers)]
    public async Task Reset_AfterCompleteAsync_NoReset()
    {
        await new HostBuilder()
            .UseHttp2Cat(Fixture.Client.BaseAddress.AbsoluteUri, async h2Connection =>
            {
                await h2Connection.InitializeConnectionAsync();

                h2Connection.Logger.LogInformation("Initialized http2 connection. Starting stream 1.");

                await h2Connection.StartStreamAsync(1, GetHeaders("/Reset_AfterCompleteAsync_NoReset"), endStream: true);

                await h2Connection.ReceiveHeadersAsync(1, decodedHeaders =>
                {
                    Assert.Equal("200", decodedHeaders[InternalHeaderNames.Status]);
                });

                var dataFrame = await h2Connection.ReceiveFrameAsync();
                Http2Utilities.VerifyDataFrame(dataFrame, 1, endOfStream: false, length: 11);

                dataFrame = await h2Connection.ReceiveFrameAsync();
                Http2Utilities.VerifyDataFrame(dataFrame, 1, endOfStream: true, length: 0);

                h2Connection.Logger.LogInformation("Connection stopped.");
            })
            .Build().RunAsync();
    }

    [ConditionalFact]
    [MinimumOSVersion(OperatingSystems.Windows, WindowsVersionForTrailers)]
    public async Task Reset_CompleteAsyncDuringRequestBody_Resets()
    {
        await new HostBuilder()
            .UseHttp2Cat(Fixture.Client.BaseAddress.AbsoluteUri, async h2Connection =>
            {
                await h2Connection.InitializeConnectionAsync();

                h2Connection.Logger.LogInformation("Initialized http2 connection. Starting stream 1.");

                await h2Connection.StartStreamAsync(1, GetPostHeaders("/Reset_CompleteAsyncDuringRequestBody_Resets"), endStream: false);
                await h2Connection.SendDataAsync(1, new byte[10], endStream: false);

                await h2Connection.ReceiveHeadersAsync(1, decodedHeaders =>
                {
                    Assert.Equal("200", decodedHeaders[InternalHeaderNames.Status]);
                });

                var dataFrame = await h2Connection.ReceiveFrameAsync();

                Http2Utilities.VerifyDataFrame(dataFrame, 1, endOfStream: true, length: 0);

                var resetFrame = await h2Connection.ReceiveFrameAsync();
                Http2Utilities.VerifyResetFrame(resetFrame, expectedStreamId: 1, expectedErrorCode: Http2ErrorCode.NO_ERROR);

                h2Connection.Logger.LogInformation("Connection stopped.");
            })
            .Build().RunAsync();
    }

    private static List<KeyValuePair<string, string>> GetHeaders(string path)
    {
        var headers = Headers.ToList();

        var kvp = new KeyValuePair<string, string>(InternalHeaderNames.Path, path);
        headers.Add(kvp);
        return headers;
    }

    private static List<KeyValuePair<string, string>> GetPostHeaders(string path)
    {
        var headers = PostRequestHeaders.ToList();

        var kvp = new KeyValuePair<string, string>(InternalHeaderNames.Path, path);
        headers.Add(kvp);
        return headers;
    }

    private static HttpClient CreateClient()
    {
        var handler = new HttpClientHandler();
        handler.MaxResponseHeadersLength = 128;
        handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
        var client = new HttpClient(handler) { Timeout = TimeSpan.FromSeconds(200) };
        return client;
    }

    private async Task<HttpResponseMessage> SendRequestAsync(string uri, bool http2 = true)
    {
        var handler = new HttpClientHandler();
        handler.MaxResponseHeadersLength = 128;
        handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
        using var client = new HttpClient(handler) { Timeout = TimeSpan.FromSeconds(200) };
        client.DefaultRequestVersion = http2 ? HttpVersion.Version20 : HttpVersion.Version11;
        return await client.GetAsync(uri);
    }

    private static readonly IEnumerable<KeyValuePair<string, string>> Headers = new[]
    {
            new KeyValuePair<string, string>(InternalHeaderNames.Method, "GET"),
            new KeyValuePair<string, string>(InternalHeaderNames.Scheme, "https"),
            new KeyValuePair<string, string>(InternalHeaderNames.Authority, "localhost:443"),
            new KeyValuePair<string, string>("user-agent", "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:54.0) Gecko/20100101 Firefox/54.0"),
            new KeyValuePair<string, string>("accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"),
            new KeyValuePair<string, string>("accept-language", "en-US,en;q=0.5"),
            new KeyValuePair<string, string>("accept-encoding", "gzip, deflate, br"),
            new KeyValuePair<string, string>("upgrade-insecure-requests", "1"),
        };

    private static readonly IEnumerable<KeyValuePair<string, string>> PostRequestHeaders = new[]
    {
            new KeyValuePair<string, string>(InternalHeaderNames.Method, "POST"),
            new KeyValuePair<string, string>(InternalHeaderNames.Scheme, "https"),
            new KeyValuePair<string, string>(InternalHeaderNames.Authority, "localhost:80"),
        };
}
