Repository: aio-libs/aiohttp

Issue:
Switch http_parser to llhttp
Nodejs decide to move from their hard-coded C parser to this one.

https://github.com/nodejs/http-parser/pull/285#issuecomment-456025694

*Upd:* 
proposed parser is https://llhttp.org



Discussion:
[GitMate.io](https://gitmate.io) thinks the contributor most likely able to help you is @asvetlov.

Possibly related issues are https://github.com/aio-libs/aiohttp/issues/33 (HTTPS Support), https://github.com/aio-libs/aiohttp/issues/390 (404 on http://aiohttp.readthedocs.org/en/v0.16.3/), https://github.com/aio-libs/aiohttp/issues/320 (HTTP/2 support), https://github.com/aio-libs/aiohttp/issues/72 (HTTP POST not working), and https://github.com/aio-libs/aiohttp/issues/858 (replace http parser?).
Seems too good to be true, but must be worth trying.

Might be worth putting the link in the body of the issue so it's rendered as a link.
https://llhttp.org
It worth to try.
API seems the same but I expect complications of our build process.
Don't have any personal estimation of the issue:
1. It *would be nice to have* but not urgent, existing parser *just works*
2. I have a long list of higher priority items in my todo list
Interesting, suricata decide to write their own HTTP-parser on Rust :) https://redmine.openinfosecfoundation.org/issues/2787#change-10814

Resolving patch:
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -18,7 +18,7 @@
     NO_EXTENSIONS = True
 
 
-if IS_GIT_REPO and not (HERE / "vendor/http-parser/README.md").exists():
+if IS_GIT_REPO and not (HERE / "vendor/llhttp/README.md").exists():
     print("Install submodules when building from git clone", file=sys.stderr)
     print("Hint:", file=sys.stderr)
     print("  git submodule update --init", file=sys.stderr)
@@ -33,10 +33,13 @@
         "aiohttp._http_parser",
         [
             "aiohttp/_http_parser.c",
-            "vendor/http-parser/http_parser.c",
             "aiohttp/_find_header.c",
+            "vendor/llhttp/build/c/llhttp.c",
+            "vendor/llhttp/src/native/api.c",
+            "vendor/llhttp/src/native/http.c",
         ],
-        define_macros=[("HTTP_PARSER_STRICT", 0)],
+        define_macros=[("LLHTTP_STRICT_MODE", 0)],
+        include_dirs=["vendor/llhttp/build"],
     ),
     Extension("aiohttp._helpers", ["aiohttp/_helpers.c"]),
     Extension("aiohttp._http_writer", ["aiohttp/_http_writer.c"]),


Test patch:
diff --git a/tests/test_http_parser.py b/tests/test_http_parser.py
--- a/tests/test_http_parser.py
+++ b/tests/test_http_parser.py
@@ -13,6 +13,7 @@
 import aiohttp
 from aiohttp import http_exceptions, streams
 from aiohttp.http_parser import (
+    NO_EXTENSIONS,
     DeflateBuffer,
     HttpPayloadParser,
     HttpRequestParserPy,
@@ -85,6 +86,14 @@ def stream():
     return mock.Mock()
 
 
+@pytest.mark.skipif(NO_EXTENSIONS, reason="Extentions available but not imported")
+def test_c_parser_loaded():
+    assert "HttpRequestParserC" in dir(aiohttp.http_parser)
+    assert "HttpResponseParserC" in dir(aiohttp.http_parser)
+    assert "RawRequestMessageC" in dir(aiohttp.http_parser)
+    assert "RawResponseMessageC" in dir(aiohttp.http_parser)
+
+
 def test_parse_headers(parser: Any) -> None:
     text = b"""GET /test HTTP/1.1\r
 test: line\r