# pyllhttp
Python wrapper for llhttp
======
A simple Python wrapper around [llhttp](https://github.com/nodejs/llhttp),
the HTTP parser for [Node.js](https://nodejs.org/).
## Install
[llhttp](https://pypi.org/project/llhttp/) via PyPI, or `pip install llhttp`
## Usage
```python
import llhttp
from pprint import pprint
pprint({"version": llhttp.version})
class request_parser(llhttp.Request):
headers = {}
url = b''
current_header_field = None
current_header_value = None
def on_message_begin(self):
print(f"MESSAGE BEGIN")
def on_url(self, url):
self.url += url
self.pause()
def on_url_complete(self):
print(f"URL {self.url}")
def on_header_field(self, field):
assert self.current_header_value is None
if self.current_header_field is None:
self.current_header_field = bytearray(field)
else:
self.current_header_field += field
def on_header_field_complete(self):
self.current_header_field = self.current_header_field.decode('iso-8859-1').lower()
assert self.current_header_field not in self.headers
def on_header_value(self, value):
assert self.current_header_field is not None
if self.current_header_value is None:
self.current_header_value = bytearray(value)
else:
self.current_header_value += value
def on_header_value_complete(self):
assert self.current_header_field is not None
self.current_header_value = bytes(self.current_header_value)
print(f"HEADER {self.current_header_field}: {self.current_header_value}")
self.headers[self.current_header_field] = self.current_header_value
self.current_header_field = None
self.current_header_value = None
def on_headers_complete(self):
assert self.current_header_field is None
assert self.current_header_value is None
def on_message_complete(self):
print("MESSAGE COMPLETE")
parser = request_parser()
assert parser.lenient_headers is not True
parser.lenient_headers = True
parser.reset()
assert parser.lenient_headers is True
buffer = b"GET /test HTTP/1.1\r\nlOl:wut\r\nOH: hai\r\n\r\n"
while buffer:
consumed = parser.execute(buffer[:2])
buffer = buffer[consumed:]
if parser.is_paused:
print("UNPAUSING")
parser.unpause()
parser.finish()
pprint({
"method": parser.method,
"url": parser.url,
"version": f"{parser.major}.{parser.minor}",
"headers": parser.headers,
})
```
```
{'version': '9.3.0'}
MESSAGE BEGIN
UNPAUSING
UNPAUSING
UNPAUSING
URL b'/test'
HEADER lol: b'wut'
HEADER oh: b'hai'
MESSAGE COMPLETE
{'headers': {'lol': b'wut', 'oh': b'hai'},
'method': 'GET',
'url': b'/test',
'version': '1.1'}
```
## Extra
This project is a toy, started to reacquaint myself with Python
[c-api](https://docs.python.org/3/c-api/) modules. If you find it useful,
please let me know.
The version number tracks the version of the incorporated llhttp.
License: [MIT](https://opensource.org/licenses/MIT)
Raw data
{
"_id": null,
"home_page": "http://github.com/pallas/pyllhttp",
"name": "llhttp",
"maintainer": null,
"docs_url": null,
"requires_python": null,
"maintainer_email": null,
"keywords": "www http parser",
"author": "Derrick Lyndon Pallas",
"author_email": "derrick@pallas.us",
"download_url": "https://files.pythonhosted.org/packages/8c/65/d119afeaa3ec3c1ac75cede9b2e6530a29fe5d56277e754d7489fc17a7e1/llhttp-9.3.0.0.tar.gz",
"platform": null,
"description": "# pyllhttp\nPython wrapper for llhttp\n======\n\nA simple Python wrapper around [llhttp](https://github.com/nodejs/llhttp),\nthe HTTP parser for [Node.js](https://nodejs.org/).\n\n## Install\n\n[llhttp](https://pypi.org/project/llhttp/) via PyPI, or `pip install llhttp`\n\n## Usage\n\n```python\nimport llhttp\nfrom pprint import pprint\n\npprint({\"version\": llhttp.version})\n\nclass request_parser(llhttp.Request):\n headers = {}\n\n url = b''\n current_header_field = None\n current_header_value = None\n\n def on_message_begin(self):\n print(f\"MESSAGE BEGIN\")\n\n def on_url(self, url):\n self.url += url\n self.pause()\n\n def on_url_complete(self):\n print(f\"URL {self.url}\")\n\n def on_header_field(self, field):\n assert self.current_header_value is None\n if self.current_header_field is None:\n self.current_header_field = bytearray(field)\n else:\n self.current_header_field += field\n\n def on_header_field_complete(self):\n self.current_header_field = self.current_header_field.decode('iso-8859-1').lower()\n assert self.current_header_field not in self.headers\n\n def on_header_value(self, value):\n assert self.current_header_field is not None\n if self.current_header_value is None:\n self.current_header_value = bytearray(value)\n else:\n self.current_header_value += value\n\n def on_header_value_complete(self):\n assert self.current_header_field is not None\n self.current_header_value = bytes(self.current_header_value)\n print(f\"HEADER {self.current_header_field}: {self.current_header_value}\")\n self.headers[self.current_header_field] = self.current_header_value\n self.current_header_field = None\n self.current_header_value = None\n\n def on_headers_complete(self):\n assert self.current_header_field is None\n assert self.current_header_value is None\n\n def on_message_complete(self):\n print(\"MESSAGE COMPLETE\")\n\nparser = request_parser()\n\nassert parser.lenient_headers is not True\nparser.lenient_headers = True\nparser.reset()\nassert parser.lenient_headers is True\n\nbuffer = b\"GET /test HTTP/1.1\\r\\nlOl:wut\\r\\nOH: hai\\r\\n\\r\\n\"\nwhile buffer:\n consumed = parser.execute(buffer[:2])\n buffer = buffer[consumed:]\n if parser.is_paused:\n print(\"UNPAUSING\")\n parser.unpause()\n\nparser.finish()\npprint({\n \"method\": parser.method,\n \"url\": parser.url,\n \"version\": f\"{parser.major}.{parser.minor}\",\n \"headers\": parser.headers,\n})\n```\n\n```\n{'version': '9.3.0'}\nMESSAGE BEGIN\nUNPAUSING\nUNPAUSING\nUNPAUSING\nURL b'/test'\nHEADER lol: b'wut'\nHEADER oh: b'hai'\nMESSAGE COMPLETE\n{'headers': {'lol': b'wut', 'oh': b'hai'},\n 'method': 'GET',\n 'url': b'/test',\n 'version': '1.1'}\n```\n\n## Extra\n\nThis project is a toy, started to reacquaint myself with Python\n[c-api](https://docs.python.org/3/c-api/) modules. If you find it useful,\nplease let me know.\n\nThe version number tracks the version of the incorporated llhttp.\n\nLicense: [MIT](https://opensource.org/licenses/MIT)\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "llhttp in python",
"version": "9.3.0.0",
"project_urls": {
"Homepage": "http://github.com/pallas/pyllhttp"
},
"split_keywords": [
"www",
"http",
"parser"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "8c65d119afeaa3ec3c1ac75cede9b2e6530a29fe5d56277e754d7489fc17a7e1",
"md5": "26c30d4a133bdb746c0faccf2d863d3b",
"sha256": "0d989363e7fb6f270b9d49956e7a3945c26778f8c70a166815264309ac6a8295"
},
"downloads": -1,
"filename": "llhttp-9.3.0.0.tar.gz",
"has_sig": false,
"md5_digest": "26c30d4a133bdb746c0faccf2d863d3b",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 37824,
"upload_time": "2025-09-01T00:12:55",
"upload_time_iso_8601": "2025-09-01T00:12:55.149521Z",
"url": "https://files.pythonhosted.org/packages/8c/65/d119afeaa3ec3c1ac75cede9b2e6530a29fe5d56277e754d7489fc17a7e1/llhttp-9.3.0.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-09-01 00:12:55",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "pallas",
"github_project": "pyllhttp",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "llhttp"
}