diff --git a/src/Protocols/Http.php b/src/Protocols/Http.php index 60083386..63cde7b4 100644 --- a/src/Protocols/Http.php +++ b/src/Protocols/Http.php @@ -142,7 +142,7 @@ public static function input(string $buffer, TcpConnection $connection): int // Disallow duplicate Content-Length headers (adjacent or separated) . '(?![\s\S]*\r\nContent-Length[ \t]*:[^\r\n]*\r\n(?:[\s\S]*?\r\n)?Content-Length[ \t]*:)' // Match request line: METHOD SP request-target SP HTTP-version CRLF - . '(?:(?-i:GET|POST|OPTIONS|HEAD|DELETE|PUT|PATCH) )+(?:/[^\x00-\x20\x7f]*)+(?: (?-i:HTTP)/1.[0-9])\r\n' + . '(?:(?-i:GET|POST|OPTIONS|HEAD|DELETE|PUT|PATCH) )+(?:(http|https):/)*(?:/[^\x00-\x20\x7f]*)+(?: (?-i:HTTP)/1.[0-9])\r\n' // Flag case-insensitive . '~i'; diff --git a/tests/Unit/Protocols/HttpTest.php b/tests/Unit/Protocols/HttpTest.php index 41ff2874..34659bf7 100644 --- a/tests/Unit/Protocols/HttpTest.php +++ b/tests/Unit/Protocols/HttpTest.php @@ -135,6 +135,26 @@ "GET / HTTP/1.2\r\n\r\n", 18, // strlen("GET / HTTP/1.2\r\n\r\n") ], + 'allow full url in request-target (compatibility)' => [ + "GET http://example.com/ HTTP/1.1\r\n\r\n", + strlen("GET http://example.com/ HTTP/1.1\r\n\r\n"), + ], + 'allow full url with port in request-target (compatibility)' => [ + "GET http://example.com:8080/ HTTP/1.1\r\n\r\n", + strlen("GET http://example.com:8080/ HTTP/1.1\r\n\r\n"), + ], + 'allow full url with query in request-target (compatibility)' => [ + "GET http://example.com/search?q=workerman HTTP/1.1\r\n\r\n", + strlen("GET http://example.com/search?q=workerman HTTP/1.1\r\n\r\n"), + ], + 'allow full url with uppercase scheme in request-target (compatibility)' => [ + "GET HTTP://example.com/ HTTP/1.1\r\n\r\n", + strlen("GET HTTP://example.com/ HTTP/1.1\r\n\r\n"), + ], + 'allow full url with path in request-target (compatibility)' => [ + "GET https://example.com/foo/bar HTTP/1.1\r\n\r\n", + strlen("GET https://example.com/foo/bar HTTP/1.1\r\n\r\n"), + ], ]); it('rejects invalid request-line cases in ::input', function (string $buffer) { @@ -151,9 +171,6 @@ 'leading whitespace before method is not allowed' => [ " GET / HTTP/1.1\r\n\r\n", ], - 'absolute-form request-target is not supported' => [ - "GET http://example.com/ HTTP/1.1\r\n\r\n", - ], 'asterisk-form request-target is not supported (including OPTIONS *)' => [ "OPTIONS * HTTP/1.1\r\n\r\n", ], @@ -187,6 +204,66 @@ 'space after version is not allowed' => [ "GET / http/1.1 \r\n\r\n", ], + 'missing space between method and path' => [ + "GET/ HTTP/1.1\r\n\r\n", + ], + 'missing space between path and version' => [ + "GET /HTTP/1.1\r\n\r\n", + ], + 'missing version' => [ + "GET / \r\n\r\n", + ], + 'missing path' => [ + "GET HTTP/1.1\r\n\r\n", + ], + 'missing method' => [ + " / HTTP/1.1\r\n\r\n", + ], + 'empty request-line' => [ + "\r\n\r\n", + ], + 'bad scheme' => [ + "GET ftp://example.com/ HTTP/1.1\r\nHost: example.com\r\n\r\n", + ], + 'bad authority in absolute-form request-target' => [ + "GET http://exa mple.com/ HTTP/1.1\r\n\r\n", + ], + 'bad path in absolute-form request-target' => [ + "GET http://example.com/fo o HTTP/1.1\r\n\r\n", + ], + 'bad query in absolute-form request-target' => [ + "GET http://example.com/search?q=wor k HTTP/1.1\r\n\r\n", + ], + 'bad fragment in absolute-form request-target' => [ + "GET http://example.com/#frag ment HTTP/1.1\r\n\r\n", + ], + 'bad port in absolute-form request-target' => [ + "GET http://example.com:80 80/ HTTP/1.1\r\n\r\n", + ], + // 'bad port number in absolute-form request-target' => [ + // "GET http://example.com:abc/ HTTP/1.1\r\n\r\n", + // ], + // 'bad port number in absolute-form request-target (too large)' => [ + // "GET http://example.com:99999/ HTTP/1.1\r\n\r\n", + // ], + // 'bad port number in absolute-form request-target (negative)' => [ + // "GET http://example.com:-80/ HTTP/1.1\r\n\r\n", + // ], + // 'bad port number in absolute-form request-target (non-digit characters)' => [ + // "GET http://example.com:8o80/ HTTP/1.1\r\n\r\n", + // ], + // 'bad port number in absolute-form request-target (empty)' => [ + // "GET http://example.com:/ HTTP/1.1\r\n\r\n", + // ], + // 'bad port number in absolute-form request-target (leading zero)' => [ + // "GET http://example.com:080/ HTTP/1.1\r\n\r\n", + // ], + 'bad full url in request-target (invalid characters in host)' => [ + "GET http://exa mple.com/ HTTP/1.1\r\n\r\n", + ], + 'bad full url in request-target' => [ + "GET htp://example.com/foo HTTP/1.1\r\n\r\n", + ], ]);