From dbf7b8d2ce480285ec30c682eb746d912d1304a3 Mon Sep 17 00:00:00 2001 From: Giorgio Salluzzo Date: Mon, 5 Dec 2022 00:54:16 +0100 Subject: [PATCH 07/15] Switching to `httptools.parser.HttpRequestParser`. --- Pipfile | 2 +- mocket/mockhttp.py | 71 +++++++++++++++++++--------- mocket/plugins/httpretty/__init__.py | 10 ++-- tests/main/test_http.py | 2 +- 4 files changed, 56 insertions(+), 29 deletions(-) Index: mocket-3.10.9/mocket/mockhttp.py =================================================================== --- mocket-3.10.9.orig/mocket/mockhttp.py +++ mocket-3.10.9/mocket/mockhttp.py @@ -3,13 +3,10 @@ import time from http.server import BaseHTTPRequestHandler from urllib.parse import parse_qs, unquote, urlsplit -from .compat import decode_from_bytes, do_the_magic, encode_to_bytes -from .mocket import Mocket, MocketEntry +from httptools.parser import HttpRequestParser -try: - from http_parser.parser import HttpParser -except ImportError: - from http_parser.pyparser import HttpParser +from .compat import ENCODING, decode_from_bytes, do_the_magic, encode_to_bytes +from .mocket import Mocket, MocketEntry try: import magic @@ -21,31 +18,59 @@ STATUS = {k: v[0] for k, v in BaseHTTPRe CRLF = "\r\n" +class Protocol: + def __init__(self): + self.url = None + self.body = None + self.headers = {} + + def on_header(self, name: bytes, value: bytes): + self.headers[name.decode("ascii")] = value.decode("ascii") + + def on_body(self, body: bytes): + try: + self.body = body.decode(ENCODING) + except UnicodeDecodeError: + self.body = body + + def on_url(self, url: bytes): + self.url = url.decode("ascii") + + class Request: - parser = None - _body = None + _protocol = None + _parser = None def __init__(self, data): - self.parser = HttpParser() - self.parser.execute(data, len(data)) - - self.method = self.parser.get_method() - self.path = self.parser.get_path() - self.headers = self.parser.get_headers() - self.querystring = parse_qs( - unquote(self.parser.get_query_string()), keep_blank_values=True - ) - if self.querystring: - self.path += "?{}".format(self.parser.get_query_string()) + self._protocol = Protocol() + self._parser = HttpRequestParser(self._protocol) + self.add_data(data) def add_data(self, data): - self.parser.execute(data, len(data)) + self._parser.feed_data(data) + + @property + def method(self): + return self._parser.get_method().decode("ascii") + + @property + def path(self): + return self._protocol.url + + @property + def headers(self): + return self._protocol.headers + + @property + def querystring(self): + parts = self._protocol.url.split("?", 1) + if len(parts) == 2: + return parse_qs(unquote(parts[1]), keep_blank_values=True) + return {} @property def body(self): - if self._body is None: - self._body = decode_from_bytes(self.parser.recv_body()) - return self._body + return self._protocol.body def __str__(self): return "{} - {} - {}".format(self.method, self.path, self.headers) Index: mocket-3.10.9/mocket/plugins/httpretty/__init__.py =================================================================== --- mocket-3.10.9.orig/mocket/plugins/httpretty/__init__.py +++ mocket-3.10.9/mocket/plugins/httpretty/__init__.py @@ -1,6 +1,6 @@ from mocket import Mocket, mocketize from mocket.async_mocket import async_mocketize -from mocket.compat import byte_type, text_type +from mocket.compat import ENCODING, byte_type, text_type from mocket.mockhttp import Entry as MocketHttpEntry from mocket.mockhttp import Request as MocketHttpRequest from mocket.mockhttp import Response as MocketHttpResponse @@ -13,9 +13,11 @@ def httprettifier_headers(headers): class Request(MocketHttpRequest): @property def body(self): - if self._body is None: - self._body = self.parser.recv_body() - return self._body + return super().body.encode(ENCODING) + + @property + def headers(self): + return httprettifier_headers(super().headers) class Response(MocketHttpResponse): Index: mocket-3.10.9/tests/main/test_http.py =================================================================== --- mocket-3.10.9.orig/tests/main/test_http.py +++ mocket-3.10.9/tests/main/test_http.py @@ -23,7 +23,7 @@ class HttpTestCase(TestCase): def assertEqualHeaders(self, first, second, msg=None): first = {k.lower(): v for k, v in first.items()} second = {k.lower(): v for k, v in second.items()} - self.assertEqual(first, second, msg) + self.assertDictEqual(first, second, msg) @pytest.mark.skipif('os.getenv("SKIP_TRUE_HTTP", False)')