From 69b5734374349c3b079c5060cd23fe23103e6186 Mon Sep 17 00:00:00 2001 From: Alexander Shorin Date: Sun, 2 Sep 2018 07:50:00 +0300 Subject: [PATCH] Make METHRE RFC-7230 compliant Definition of method is: ``` method = token tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA token = 1*tchar ``` So we had two issues: 1. Not all the characters were allowed. 2. Actually, we did allowed too much characters since `$-_` parsed as: "all characters in range between code 36 ($) and code 95 (_)" instead of "characters with codes 36, 45 and 95". So we did match methods like `[GET]` which are malformed according the spec. --- CHANGES/3235.bugfix | 1 + aiohttp/http_parser.py | 10 +++++++++- tests/test_http_parser.py | 2 +- tests/test_web_protocol.py | 2 +- 4 files changed, 12 insertions(+), 3 deletions(-) create mode 100644 CHANGES/3235.bugfix diff --git a/CHANGES/3235.bugfix b/CHANGES/3235.bugfix new file mode 100644 index 00000000000..5ec04391a59 --- /dev/null +++ b/CHANGES/3235.bugfix @@ -0,0 +1 @@ +Make method match regexp RFC-7230 compliant diff --git a/aiohttp/http_parser.py b/aiohttp/http_parser.py index ab6d5c4f544..b219a669b7a 100644 --- a/aiohttp/http_parser.py +++ b/aiohttp/http_parser.py @@ -29,7 +29,15 @@ 'RawRequestMessage', 'RawResponseMessage') ASCIISET = set(string.printable) -METHRE = re.compile('[A-Z0-9$-_.]+') + +# See https://tools.ietf.org/html/rfc7230#section-3.1.1 +# and https://tools.ietf.org/html/rfc7230#appendix-B +# +# method = token +# tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." / +# "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA +# token = 1*tchar +METHRE = re.compile("[!#$%&'*+\-.^_`|~0-9A-Za-z]+") VERSRE = re.compile(r'HTTP/(\d+).(\d+)') HDRRE = re.compile(rb'[\x00-\x1F\x7F()<>@,;:\[\]={} \t\\\\\"]') diff --git a/tests/test_http_parser.py b/tests/test_http_parser.py index df4350d9cb3..ebd97c0efb3 100644 --- a/tests/test_http_parser.py +++ b/tests/test_http_parser.py @@ -540,7 +540,7 @@ def test_http_request_parser_two_slashes(parser): def test_http_request_parser_bad_method(parser): with pytest.raises(http_exceptions.BadStatusLine): - parser.feed_data(b'!12%()+=~$ /get HTTP/1.1\r\n\r\n') + parser.feed_data(b'=":(e),[T];?" /get HTTP/1.1\r\n\r\n') def test_http_request_parser_bad_version(parser): diff --git a/tests/test_web_protocol.py b/tests/test_web_protocol.py index bd721c529f4..61ca0b41b16 100644 --- a/tests/test_web_protocol.py +++ b/tests/test_web_protocol.py @@ -239,7 +239,7 @@ async def test_simple(srv, loop, buf): async def test_bad_method(srv, loop, buf): srv.data_received( - b'!@#$ / HTTP/1.0\r\n' + b':BAD; / HTTP/1.0\r\n' b'Host: example.com\r\n\r\n') await asyncio.sleep(0, loop=loop)