File: src\Servers\Kestrel\shared\test\HttpParsingData.cs
Web Access
Project: src\src\Servers\Kestrel\Core\test\Microsoft.AspNetCore.Server.Kestrel.Core.Tests.csproj (Microsoft.AspNetCore.Server.Kestrel.Core.Tests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
using Xunit;
 
namespace Microsoft.AspNetCore.InternalTesting;
 
public class HttpParsingData
{
    public static IEnumerable<string[]> RequestLineValidData
    {
        get
        {
            var methods = new[]
            {
                    "GET",
                    "CUSTOM",
                };
            var paths = new[]
            {
                    Tuple.Create("/", "/"),
                    Tuple.Create("/abc", "/abc"),
                    Tuple.Create("/abc/de/f", "/abc/de/f"),
                    Tuple.Create("/%20", "/ "),
                    Tuple.Create("/a%20", "/a "),
                    Tuple.Create("/%20a", "/ a"),
                    Tuple.Create("/a/b%20c", "/a/b c"),
                    Tuple.Create("/%C3%A5", "/\u00E5"),
                    Tuple.Create("/a%C3%A5a", "/a\u00E5a"),
                    Tuple.Create("/%C3%A5/bc", "/\u00E5/bc"),
                    Tuple.Create("/%25", "/%"),
                    Tuple.Create("/%25%30%30", "/%00"),
                    Tuple.Create("/%%2000", "/% 00"),
                    Tuple.Create("/%2F", "/%2F"),
                    Tuple.Create("http://host/abs/path", "/abs/path"),
                    Tuple.Create("http://host/abs/path/", "/abs/path/"),
                    Tuple.Create("http://host/a%20b%20c/", "/a b c/"),
                    Tuple.Create("https://host/abs/path", "/abs/path"),
                    Tuple.Create("https://host/abs/path/", "/abs/path/"),
                    Tuple.Create("https://host:22/abs/path", "/abs/path"),
                    Tuple.Create("https://user@host:9080/abs/path", "/abs/path"),
                    Tuple.Create("http://host/", "/"),
                    Tuple.Create("http://host", "/"),
                    Tuple.Create("https://host/", "/"),
                    Tuple.Create("https://host", "/"),
                    Tuple.Create("http://user@host/", "/"),
                    Tuple.Create("http://127.0.0.1/", "/"),
                    Tuple.Create("http://user@127.0.0.1/", "/"),
                    Tuple.Create("http://user@127.0.0.1:8080/", "/"),
                    Tuple.Create("http://127.0.0.1:8080/", "/"),
                    Tuple.Create("http://[::1]", "/"),
                    Tuple.Create("http://[::1]/path", "/path"),
                    Tuple.Create("http://[::1]:8080/", "/"),
                    Tuple.Create("http://user@[::1]:8080/", "/"),
                };
            var queryStrings = new[]
            {
                    "",
                    "?",
                    "?arg1=val1",
                    "?arg1=a%20b",
                    "?%A",
                    "?%20=space",
                    "?%C3%A5=val",
                    "?path=/home",
                    "?path=/%C3%A5/",
                    "?question=what?",
                    "?%00",
                    "?arg=%00"
                };
            var httpVersions = new[]
            {
                    "HTTP/1.0",
                    "HTTP/1.1",
                    " HTTP/1.1",
                    "   HTTP/1.1"
                };
 
            return from method in methods
                   from path in paths
                   from queryString in queryStrings
                   from httpVersion in httpVersions
                   select new[]
                   {
                           $"{method} {path.Item1}{queryString} {httpVersion}\r\n",
                           method,
                           $"{path.Item1}{queryString}",
                           $"{path.Item1}",
                           $"{path.Item2}",
                           queryString,
                           httpVersion.Trim()
                       };
        }
    }
 
    public static IEnumerable<string[]> RequestLineDotSegmentData => new[]
    {
            new[] { "GET /a/../b HTTP/1.1\r\n", "/a/../b", "/b", "" },
            new[] { "GET /%61/../%62 HTTP/1.1\r\n", "/%61/../%62", "/b", "" },
            new[] { "GET /a/%2E%2E/b HTTP/1.1\r\n", "/a/%2E%2E/b", "/b", "" },
            new[] { "GET /%61/%2E%2E/%62 HTTP/1.1\r\n", "/%61/%2E%2E/%62", "/b", "" },
            new[] { "GET /a?p=/a/../b HTTP/1.1\r\n", "/a?p=/a/../b", "/a", "?p=/a/../b" },
            new[] { "GET /a?p=/a/%2E%2E/b HTTP/1.1\r\n", "/a?p=/a/%2E%2E/b", "/a", "?p=/a/%2E%2E/b" },
            new[] { "GET http://example.com/a/../b HTTP/1.1\r\n", "http://example.com/a/../b", "/b", "" },
            new[] { "GET http://example.com/%61/../%62 HTTP/1.1\r\n", "http://example.com/%61/../%62", "/b", "" },
            new[] { "GET http://example.com/a/%2E%2E/b HTTP/1.1\r\n", "http://example.com/a/%2E%2E/b", "/b", "" },
            new[] { "GET http://example.com/%61/%2E%2E/%62 HTTP/1.1\r\n", "http://example.com/%61/%2E%2E/%62", "/b", "" },
            new[] { "GET http://example.com/a?p=/a/../b HTTP/1.1\r\n", "http://example.com/a?p=/a/../b", "/a", "?p=/a/../b" },
            new[] { "GET http://example.com/a?p=/a/%2E%2E/b HTTP/1.1\r\n", "http://example.com/a?p=/a/%2E%2E/b", "/a", "?p=/a/%2E%2E/b" },
            new[] { "GET http://example.com?p=/a/../b HTTP/1.1\r\n", "http://example.com?p=/a/../b", "/", "?p=/a/../b" },
            new[] { "GET http://example.com?p=/a/%2E%2E/b HTTP/1.1\r\n", "http://example.com?p=/a/%2E%2E/b", "/", "?p=/a/%2E%2E/b" },
 
            // Asterisk-form and authority-form should be unaffected and cause no issues
            new[] { "OPTIONS * HTTP/1.1\r\n", "*", "", "" },
            new[] { "CONNECT www.example.com HTTP/1.1\r\n", "www.example.com", "", "" },
        };
 
    public static IEnumerable<string> RequestLineIncompleteData => new[]
    {
            "G",
            "GE",
            "GET",
            "GET ",
            "GET /",
            "GET / ",
            "GET / H",
            "GET / HT",
            "GET / HTT",
            "GET / HTTP",
            "GET / HTTP/",
            "GET / HTTP/1",
            "GET / HTTP/1.",
            "GET / HTTP/1.1",
            "GET / HTTP/1.1\r",
        };
 
    public static IEnumerable<string> RequestLineInvalidData
    {
        get
        {
            return new[]
            {
                    "G\r\n",
                    "GE\r\n",
                    "GET\r\n",
                    "GET \r\n",
                    "GET /\r\n",
                    "GET / \r\n",
                    "GET/HTTP/1.1\r\n",
                    "GET /HTTP/1.1\r\n",
                    " \r\n",
                    "  \r\n",
                    "/ HTTP/1.1\r\n",
                    " / HTTP/1.1\r\n",
                    "/ \r\n",
                    "GET  \r\n",
                    "GET  HTTP/1.0\r\n",
                    "GET  HTTP/1.1\r\n",
                    "GET / \n",
                    "GET / HTTP/1.0\n",
                    "GET / HTTP/1.1\n",
                    "GET / HTTP/1.0\rA\n",
                    "GET / HTTP/1.1\ra\n",
                    "GET  / HTTP/1.1\r\n",
                    "GET   / HTTP/1.1\r\n",
                    "GET  /  HTTP/1.1\r\n",
                    "GET   /   HTTP/1.1\r\n",
                    "GET / HTTP/1.1 \r\n",
                    "GET / HTTP/1.1  \r\n",
                    "GET / H\r\n",
                    "GET / HT\r\n",
                    "GET / HTT\r\n",
                    "GET / HTTP\r\n",
                    "GET / HTTP/\r\n",
                    "GET / HTTP/1\r\n",
                    "GET / HTTP/1.\r\n",
                    "GET / HTTP/1.1a\n",
                    "GET / HTTP/1.1a\r\n",
                    "GET / HTTP/1.1ab\r\n",
                    "GET / hello\r\n",
                    "GET? / HTTP/1.1\r\n",
                    "GET ? HTTP/1.1\r\n",
                    "GET /a?b=cHTTP/1.1\r\n",
                    "GET /a%20bHTTP/1.1\r\n",
                    "GET /a%20b?c=dHTTP/1.1\r\n",
                    "GET %2F HTTP/1.1\r\n",
                    "GET %00 HTTP/1.1\r\n",
                    "GET /?d=Bad UrlToAccept HTTP/1.1\r\n",
                    "CUSTOM \r\n",
                    "CUSTOM /\r\n",
                    "CUSTOM / \r\n",
                    "CUSTOM /HTTP/1.1\r\n",
                    "CUSTOM  \r\n",
                    "CUSTOM  HTTP/1.0\r\n",
                    "CUSTOM  HTTP/1.1\r\n",
                    "CUSTOM / \n",
                    "CUSTOM / HTTP/1.0\n",
                    "CUSTOM / HTTP/1.1\n",
                    "CUSTOM / HTTP/1.0\rA\n",
                    "CUSTOM / HTTP/1.1\ra\n",
                    "CUSTOM  / HTTP/1.1\r\n",
                    "CUSTOM   / HTTP/1.1\r\n",
                    "CUSTOM  /  HTTP/1.1\r\n",
                    "CUSTOM   /   HTTP/1.1\r\n",
                    "CUSTOM / HTTP/1.1 \r\n",
                    "CUSTOM / HTTP/1.1  \r\n",
                    "CUSTOM / H\r\n",
                    "CUSTOM / HT\r\n",
                    "CUSTOM / HTT\r\n",
                    "CUSTOM / HTTP\r\n",
                    "CUSTOM / HTTP/\r\n",
                    "CUSTOM / HTTP/1\r\n",
                    "CUSTOM / HTTP/1.\r\n",
                    "CUSTOM / HTTP/1.1a\n",
                    "CUSTOM / HTTP/1.1a\r\n",
                    "CUSTOM / HTTP/1.1ab\r\n",
                    "CUSTOM / H\n",
                    "CUSTOM / HT\n",
                    "CUSTOM / HTT\n",
                    "CUSTOM / HTTP\n",
                    "CUSTOM / HTTP/\n",
                    "CUSTOM / HTTP/1\n",
                    "CUSTOM / HTTP/1.\n",
                    "CUSTOM / hello\r\n",
                    "CUSTOM / hello\n",
                    "CUSTOM ? HTTP/1.1\r\n",
                    "CUSTOM /a?b=cHTTP/1.1\r\n",
                    "CUSTOM /a%20bHTTP/1.1\r\n",
                    "CUSTOM /a%20b?c=dHTTP/1.1\r\n",
                    "CUSTOM %2F HTTP/1.1\r\n",
                    "CUSTOM %00 HTTP/1.1\r\n",
                    "CUSTOM /?d=Bad UrlToAccept HTTP/1.1\r\n",
                }.Concat(MethodWithNonTokenCharData.Select(method => $"{method} / HTTP/1.0\r\n"));
        }
    }
 
    // This list is valid in quirk mode
    public static IEnumerable<string> RequestLineInvalidDataLineFeedTerminator
    {
        get
        {
            return new[]
            {
                    "GET / HTTP/1.0\n",
                    "GET / HTTP/1.1\n",
                    "CUSTOM / HTTP/1.0\n",
                    "CUSTOM / HTTP/1.1\n",
                };
        }
    }
 
    // Bad HTTP Methods (invalid according to RFC)
    public static IEnumerable<string> MethodWithNonTokenCharData
    {
        get
        {
            return new[]
            {
                    "(",
                    ")",
                    "<",
                    ">",
                    "@",
                    ",",
                    ";",
                    ":",
                    "\\",
                    "\"",
                    "/",
                    "[",
                    "]",
                    "?",
                    "=",
                    "{",
                    "}",
                    "get@",
                    "post=",
                    "[0x00]"
                }.Concat(MethodWithNullCharData);
        }
    }
 
    public static IEnumerable<string> MethodWithNullCharData => new[]
    {
            // Bad HTTP Methods (invalid according to RFC)
            "\0",
            "\0GET",
            "G\0T",
            "GET\0",
        };
 
    public static IEnumerable<string> TargetWithEncodedNullCharData => new[]
    {
            "/%00",
            "/%00%00",
            "/%E8%00%84",
            "/%E8%85%00",
            "/%F3%00%82%86",
            "/%F3%85%00%82",
            "/%F3%85%82%00",
        };
 
    public static TheoryData<string, string> TargetInvalidData
    {
        get
        {
            var data = new TheoryData<string, string>();
 
            // Invalid absolute-form
            data.Add("GET", "http://");
            data.Add("GET", "http:/");
            data.Add("GET", "https:/");
            data.Add("GET", "http:///");
            data.Add("GET", "https://");
            data.Add("GET", "http:////");
            data.Add("GET", "http://:80");
            data.Add("GET", "http://:80/abc");
            data.Add("GET", "http://user@");
            data.Add("GET", "http://user@/abc");
            data.Add("GET", "http://abc%20xyz/abc");
            data.Add("GET", "http://%20/abc?query=%0A");
            // Valid absolute-form but with unsupported schemes
            data.Add("GET", "otherscheme://host/");
            data.Add("GET", "ws://host/");
            data.Add("GET", "wss://host/");
            // Must only have one asterisk
            data.Add("OPTIONS", "**");
            // Relative form
            data.Add("GET", "../../");
            data.Add("GET", "..\\.");
 
            return data;
        }
    }
 
    public static TheoryData<string, int> MethodNotAllowedRequestLine
    {
        get
        {
            var methods = new[]
            {
                    "GET",
                    "PUT",
                    "DELETE",
                    "POST",
                    "HEAD",
                    "TRACE",
                    "PATCH",
                    "CONNECT",
                    "OPTIONS",
                    "CUSTOM",
                };
 
            var data = new TheoryData<string, int>();
 
            foreach (var method in methods.Except(new[] { "OPTIONS" }))
            {
                data.Add($"{method} * HTTP/1.1\r\n", (int)HttpMethod.Options);
            }
 
            foreach (var method in methods.Except(new[] { "CONNECT" }))
            {
                data.Add($"{method} www.example.com:80 HTTP/1.1\r\n", (int)HttpMethod.Connect);
            }
 
            return data;
        }
    }
 
    public static IEnumerable<string> TargetWithNullCharData
    {
        get
        {
            return new[]
            {
                    "\0",
                    "/\0",
                    "/\0\0",
                    "/%C8\0",
                }.Concat(QueryStringWithNullCharData);
        }
    }
 
    public static IEnumerable<string> QueryStringWithNullCharData => new[]
    {
            "/?\0=a",
            "/?a=\0",
        };
 
    public static TheoryData<string> UnrecognizedHttpVersionData => new TheoryData<string>
        {
            "http/1.0",
            "http/1.1",
            "HTTP/1.2",
            "HTTP/3.0",
            "8charact",
        };
 
    public static IEnumerable<object[]> RequestHeaderInvalidDataLineFeedTerminator => new[]
    {
            // Missing CR
            new[] { "Header: value\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"Header: value\x0A") },
            new[] { "Header-1: value1\nHeader-2: value2\r\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"Header-1: value1\x0A") },
            new[] { "Header-1: value1\r\nHeader-2: value2\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"Header-2: value2\x0A") },
 
            // Empty header name
            new[] { ":a\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@":a\x0A") },
        };
 
    public static IEnumerable<object[]> RequestHeaderInvalidData => new[]
    {
            // Line folding
            new[] { "Header: line1\r\n line2\r\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@" line2\x0D\x0A") },
            new[] { "Header: line1\r\n\tline2\r\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"\x09line2\x0D\x0A") },
            new[] { "Header: line1\r\n  line2\r\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"  line2\x0D\x0A") },
            new[] { "Header: line1\r\n \tline2\r\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@" \x09line2\x0D\x0A") },
            new[] { "Header: line1\r\n\t line2\r\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"\x09 line2\x0D\x0A") },
            new[] { "Header: line1\r\n\t\tline2\r\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"\x09\x09line2\x0D\x0A") },
            new[] { "Header: line1\r\n \t\t line2\r\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@" \x09\x09 line2\x0D\x0A") },
            new[] { "Header: line1\r\n \t \t line2\r\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@" \x09 \x09 line2\x0D\x0A") },
            new[] { "Header-1: multi\r\n line\r\nHeader-2: value2\r\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@" line\x0D\x0A") },
            new[] { "Header-1: value1\r\nHeader-2: multi\r\n line\r\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@" line\x0D\x0A") },
            new[] { "Header-1: value1\r\n Header-2: value2\r\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@" Header-2: value2\x0D\x0A") },
            new[] { "Header-1: value1\r\n\tHeader-2: value2\r\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"\x09Header-2: value2\x0D\x0A") },
 
            // CR in value
            new[] { "Header-1: value1\r\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"Header-1: value1\x0D\x0D") },
            new[] { "Header-1: val\rue1\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"Header-1: val\x0Du") },
            new[] { "Header-1: value1\rHeader-2: value2\r\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"Header-1: value1\x0DH") },
            new[] { "Header-1: value1\r\nHeader-2: value2\r\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"Header-2: value2\x0D\x0D") },
            new[] { "Header-1: value1\r\nHeader-2: v\ralue2\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"Header-2: v\x0Da") },
            new[] { "Header-1: Value__\rVector16________Vector32\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"Header-1: Value__\x0DV") },
            new[] { "Header-1: Value___Vector16\r________Vector32\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"Header-1: Value___Vector16\x0D_") },
            new[] { "Header-1: Value___Vector16_______\rVector32\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"Header-1: Value___Vector16_______\x0DV") },
            new[] { "Header-1: Value___Vector16________Vector32\r\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"Header-1: Value___Vector16________Vector32\x0D\x0D") },
            new[] { "Header-1: Value___Vector16________Vector32_\r\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"Header-1: Value___Vector16________Vector32_\x0D\x0D") },
            new[] { "Header-1: Value___Vector16________Vector32Value___Vector16_______\rVector32\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"Header-1: Value___Vector16________Vector32Value___Vector16_______\x0DV") },
            new[] { "Header-1: Value___Vector16________Vector32Value___Vector16________Vector32\r\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"Header-1: Value___Vector16________Vector32Value___Vector16________Vector32\x0D\x0D") },
            new[] { "Header-1: Value___Vector16________Vector32Value___Vector16________Vector32_\r\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"Header-1: Value___Vector16________Vector32Value___Vector16________Vector32_\x0D\x0D") },
 
            // Missing colon
            new[] { "Header-1 value1\r\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"Header-1 value1\x0D\x0A") },
            new[] { "Header-1 value1\r\nHeader-2: value2\r\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"Header-1 value1\x0D\x0A") },
            new[] { "Header-1: value1\r\nHeader-2 value2\r\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"Header-2 value2\x0D\x0A") },
            new[] { "HeaderValue1\r\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"HeaderValue1\x0D\x0A") },
 
            // Starting with whitespace
            new[] { " Header: value\r\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@" Header: value\x0D\x0A") },
            new[] { "\tHeader: value\r\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"\x09Header: value\x0D\x0A") },
            new[] { " Header-1: value1\r\nHeader-2: value2\r\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@" Header-1: value1\x0D\x0A") },
            new[] { "\tHeader-1: value1\r\nHeader-2: value2\r\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"\x09Header-1: value1\x0D\x0A") },
 
            // Whitespace in header name
            new[] { "Header : value\r\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"Header : value\x0D\x0A") },
            new[] { "Header\t: value\r\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"Header\x09: value\x0D\x0A") },
            new[] { "Header\r: value\r\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"Header\x0D:") },
            new[] { "Header_\rVector16: value\r\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"Header_\x0DV") },
            new[] { "Header__Vector16\r: value\r\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"Header__Vector16\x0D:") },
            new[] { "Header__Vector16_\r: value\r\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"Header__Vector16_\x0D:") },
            new[] { "Header_\rVector16________Vector32: value\r\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"Header_\x0DV") },
            new[] { "Header__Vector16________Vector32\r: value\r\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"Header__Vector16________Vector32\x0D:") },
            new[] { "Header__Vector16________Vector32_\r: value\r\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"Header__Vector16________Vector32_\x0D:") },
            new[] { "Header__Vector16________Vector32Header_\rVector16________Vector32: value\r\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"Header__Vector16________Vector32Header_\x0DV") },
            new[] { "Header__Vector16________Vector32Header__Vector16________Vector32\r: value\r\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"Header__Vector16________Vector32Header__Vector16________Vector32\x0D:") },
            new[] { "Header__Vector16________Vector32Header__Vector16________Vector32_\r: value\r\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"Header__Vector16________Vector32Header__Vector16________Vector32_\x0D:") },
            new[] { "Header 1: value1\r\nHeader-2: value2\r\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"Header 1: value1\x0D\x0A") },
            new[] { "Header 1 : value1\r\nHeader-2: value2\r\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"Header 1 : value1\x0D\x0A") },
            new[] { "Header 1\t: value1\r\nHeader-2: value2\r\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"Header 1\x09: value1\x0D\x0A") },
            new[] { "Header 1\r: value1\r\nHeader-2: value2\r\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"Header 1\x0D:") },
            new[] { "Header-1: value1\r\nHeader 2: value2\r\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"Header 2: value2\x0D\x0A") },
            new[] { "Header-1: value1\r\nHeader-2 : value2\r\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"Header-2 : value2\x0D\x0A") },
            new[] { "Header-1: value1\r\nHeader-2\t: value2\r\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"Header-2\x09: value2\x0D\x0A") },
 
            // Headers not ending in CRLF line
            new[] { "Header-1: value1\r\nHeader-2: value2\r\n\r\r", CoreStrings.BadRequest_InvalidRequestHeadersNoCRLF },
            new[] { "Header-1: value1\r\nHeader-2: value2\r\n\r ", CoreStrings.BadRequest_InvalidRequestHeadersNoCRLF },
            new[] { "Header-1: value1\r\nHeader-2: value2\r\n\r \n", CoreStrings.BadRequest_InvalidRequestHeadersNoCRLF },
            new[] { "Header-1: value1\r\nHeader-2\t: value2 \n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"Header-2\x09: value2 \x0A") },
 
            // Empty header name
            new[] { ": value\r\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@": value\x0D\x0A") },
            new[] { ":a\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@":a\x0D\x0A") },
        };
 
    public static TheoryData<string, string> HostHeaderData
        => new TheoryData<string, string>
            {
                    { "OPTIONS *", "" },
                    { "GET /pub/WWW/", "" },
                    { "GET /pub/WWW/", "   " },
                    { "GET /pub/WWW/", "." },
                    { "GET /pub/WWW/", "www.example.org" },
                    { "GET http://localhost/", "localhost" },
                    { "GET http://localhost:80/", "localhost:80" },
                    { "GET https://localhost/", "localhost" },
                    { "GET https://localhost:443/", "localhost:443" },
                    { "CONNECT asp.net:80", "asp.net:80" },
                    { "CONNECT asp.net:443", "asp.net:443" },
                    { "CONNECT user-images.githubusercontent.com:443", "user-images.githubusercontent.com:443" },
            };
 
    public static TheoryData<string, string> HostHeaderInvalidData
    {
        get
        {
            // see https://tools.ietf.org/html/rfc7230#section-5.4
            var invalidHostValues = new[] {
                    "",
                    "   ",
                    "contoso.com:4000",
                    "contoso.com/",
                    "not-contoso.com",
                    "user@password:contoso.com",
                    "user@contoso.com",
                    "http://contoso.com/",
                    "http://contoso.com"
                };
 
            var data = new TheoryData<string, string>();
 
            foreach (var host in invalidHostValues)
            {
                // absolute form
                // expected: GET http://contoso.com/ => Host: contoso.com
                data.Add("GET http://contoso.com/", host);
 
                // authority-form
                // expected: CONNECT contoso.com => Host: contoso.com
                data.Add("CONNECT contoso.com", host);
            }
 
            // port mismatch when target contains port
            data.Add("GET https://contoso.com:443/", "contoso.com:5000");
            data.Add("CONNECT contoso.com:443", "contoso.com:5000");
 
            return data;
        }
    }
}