├── .gitignore ├── LICENSE ├── MANIFEST.in ├── README.md ├── example.py ├── lib ├── LICENSE-MIT ├── api.c ├── http.c ├── llhttp.c └── llhttp.h ├── llhttp └── __init__.py ├── pyllhttp.c └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Derrick Lyndon Pallas 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include lib/llhttp.h 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pyllhttp 2 | Python wrapper for llhttp 3 | ====== 4 | 5 | A simple Python wrapper around [llhttp](https://github.com/nodejs/llhttp), 6 | the HTTP parser for [Node.js](https://nodejs.org/). 7 | 8 | ## Install 9 | 10 | [llhttp](https://pypi.org/project/llhttp/) via PyPI, or `pip install llhttp` 11 | 12 | ## Usage 13 | 14 | ```python 15 | import llhttp 16 | from pprint import pprint 17 | 18 | pprint({"version": llhttp.version}) 19 | 20 | class request_parser(llhttp.Request): 21 | headers = {} 22 | 23 | url = b'' 24 | current_header_field = None 25 | current_header_value = None 26 | 27 | def on_message_begin(self): 28 | print(f"MESSAGE BEGIN") 29 | 30 | def on_url(self, url): 31 | self.url += url 32 | self.pause() 33 | 34 | def on_url_complete(self): 35 | print(f"URL {self.url}") 36 | 37 | def on_header_field(self, field): 38 | assert self.current_header_value is None 39 | if self.current_header_field is None: 40 | self.current_header_field = bytearray(field) 41 | else: 42 | self.current_header_field += field 43 | 44 | def on_header_field_complete(self): 45 | self.current_header_field = self.current_header_field.decode('iso-8859-1').lower() 46 | assert self.current_header_field not in self.headers 47 | 48 | def on_header_value(self, value): 49 | assert self.current_header_field is not None 50 | if self.current_header_value is None: 51 | self.current_header_value = bytearray(value) 52 | else: 53 | self.current_header_value += value 54 | 55 | def on_header_value_complete(self): 56 | assert self.current_header_field is not None 57 | self.current_header_value = bytes(self.current_header_value) 58 | print(f"HEADER {self.current_header_field}: {self.current_header_value}") 59 | self.headers[self.current_header_field] = self.current_header_value 60 | self.current_header_field = None 61 | self.current_header_value = None 62 | 63 | def on_headers_complete(self): 64 | assert self.current_header_field is None 65 | assert self.current_header_value is None 66 | 67 | def on_message_complete(self): 68 | print("MESSAGE COMPLETE") 69 | 70 | parser = request_parser() 71 | 72 | assert parser.lenient_headers is not True 73 | parser.lenient_headers = True 74 | parser.reset() 75 | assert parser.lenient_headers is True 76 | 77 | buffer = b"GET /test HTTP/1.1\r\nlOl:wut\r\nOH: hai\r\n\r\n" 78 | while buffer: 79 | consumed = parser.execute(buffer[:2]) 80 | buffer = buffer[consumed:] 81 | if parser.is_paused: 82 | print("UNPAUSING") 83 | parser.unpause() 84 | 85 | parser.finish() 86 | pprint({ 87 | "method": parser.method, 88 | "url": parser.url, 89 | "version": f"{parser.major}.{parser.minor}", 90 | "headers": parser.headers, 91 | }) 92 | ``` 93 | 94 | ``` 95 | {'version': '3.0.0'} 96 | MESSAGE BEGIN 97 | UNPAUSING 98 | UNPAUSING 99 | UNPAUSING 100 | URL b'/test' 101 | HEADER lol: b'wut' 102 | HEADER oh: b'hai' 103 | MESSAGE COMPLETE 104 | {'headers': {'lol': b'wut', 'oh': b'hai'}, 105 | 'method': 'GET', 106 | 'url': b'/test', 107 | 'version': '1.1'} 108 | ``` 109 | 110 | ## Extra 111 | 112 | This project is a toy, started to reacquaint myself with Python 113 | [c-api](https://docs.python.org/3/c-api/) modules. If you find it useful, 114 | please let me know. 115 | 116 | The version number tracks the version of the incorporated llhttp. 117 | 118 | License: [MIT](https://opensource.org/licenses/MIT) 119 | -------------------------------------------------------------------------------- /example.py: -------------------------------------------------------------------------------- 1 | #!/bin/env python3 2 | import llhttp 3 | from pprint import pprint 4 | 5 | pprint({"version": llhttp.version}) 6 | 7 | class request_parser(llhttp.Request): 8 | headers = {} 9 | 10 | url = b'' 11 | current_header_field = None 12 | current_header_value = None 13 | 14 | def on_message_begin(self): 15 | print(f"MESSAGE BEGIN") 16 | 17 | def on_url(self, url): 18 | self.url += url 19 | self.pause() 20 | 21 | def on_url_complete(self): 22 | print(f"URL {self.url}") 23 | 24 | def on_header_field(self, field): 25 | assert self.current_header_value is None 26 | if self.current_header_field is None: 27 | self.current_header_field = bytearray(field) 28 | else: 29 | self.current_header_field += field 30 | 31 | def on_header_field_complete(self): 32 | self.current_header_field = self.current_header_field.decode('iso-8859-1').lower() 33 | assert self.current_header_field not in self.headers 34 | 35 | def on_header_value(self, value): 36 | assert self.current_header_field is not None 37 | if self.current_header_value is None: 38 | self.current_header_value = bytearray(value) 39 | else: 40 | self.current_header_value += value 41 | 42 | def on_header_value_complete(self): 43 | assert self.current_header_field is not None 44 | self.current_header_value = bytes(self.current_header_value) 45 | print(f"HEADER {self.current_header_field}: {self.current_header_value}") 46 | self.headers[self.current_header_field] = self.current_header_value 47 | self.current_header_field = None 48 | self.current_header_value = None 49 | 50 | def on_headers_complete(self): 51 | assert self.current_header_field is None 52 | assert self.current_header_value is None 53 | 54 | def on_message_complete(self): 55 | print("MESSAGE COMPLETE") 56 | 57 | parser = request_parser() 58 | 59 | assert parser.lenient_headers is not True 60 | parser.lenient_headers = True 61 | parser.reset() 62 | assert parser.lenient_headers is True 63 | 64 | buffer = b"GET /test HTTP/1.1\r\nlOl:wut\r\nOH: hai\r\n\r\n" 65 | while buffer: 66 | consumed = parser.execute(buffer[:2]) 67 | buffer = buffer[consumed:] 68 | if parser.is_paused: 69 | print("UNPAUSING") 70 | parser.unpause() 71 | 72 | parser.finish() 73 | pprint({ 74 | "method": parser.method, 75 | "url": parser.url, 76 | "version": f"{parser.major}.{parser.minor}", 77 | "headers": parser.headers, 78 | }) 79 | 80 | # 81 | -------------------------------------------------------------------------------- /lib/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | This software is licensed under the MIT License. 2 | 3 | Copyright Fedor Indutny, 2018. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a 6 | copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to permit 10 | persons to whom the Software is furnished to do so, subject to the 11 | following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included 14 | in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 17 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 19 | NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 20 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 21 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 22 | USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /lib/api.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "llhttp.h" 6 | 7 | #define CALLBACK_MAYBE(PARSER, NAME) \ 8 | do { \ 9 | const llhttp_settings_t* settings; \ 10 | settings = (const llhttp_settings_t*) (PARSER)->settings; \ 11 | if (settings == NULL || settings->NAME == NULL) { \ 12 | err = 0; \ 13 | break; \ 14 | } \ 15 | err = settings->NAME((PARSER)); \ 16 | } while (0) 17 | 18 | #define SPAN_CALLBACK_MAYBE(PARSER, NAME, START, LEN) \ 19 | do { \ 20 | const llhttp_settings_t* settings; \ 21 | settings = (const llhttp_settings_t*) (PARSER)->settings; \ 22 | if (settings == NULL || settings->NAME == NULL) { \ 23 | err = 0; \ 24 | break; \ 25 | } \ 26 | err = settings->NAME((PARSER), (START), (LEN)); \ 27 | if (err == -1) { \ 28 | err = HPE_USER; \ 29 | llhttp_set_error_reason((PARSER), "Span callback error in " #NAME); \ 30 | } \ 31 | } while (0) 32 | 33 | void llhttp_init(llhttp_t* parser, llhttp_type_t type, 34 | const llhttp_settings_t* settings) { 35 | llhttp__internal_init(parser); 36 | 37 | parser->type = type; 38 | parser->settings = (void*) settings; 39 | } 40 | 41 | 42 | #if defined(__wasm__) 43 | 44 | extern int wasm_on_message_begin(llhttp_t * p); 45 | extern int wasm_on_url(llhttp_t* p, const char* at, size_t length); 46 | extern int wasm_on_status(llhttp_t* p, const char* at, size_t length); 47 | extern int wasm_on_header_field(llhttp_t* p, const char* at, size_t length); 48 | extern int wasm_on_header_value(llhttp_t* p, const char* at, size_t length); 49 | extern int wasm_on_headers_complete(llhttp_t * p, int status_code, 50 | uint8_t upgrade, int should_keep_alive); 51 | extern int wasm_on_body(llhttp_t* p, const char* at, size_t length); 52 | extern int wasm_on_message_complete(llhttp_t * p); 53 | 54 | static int wasm_on_headers_complete_wrap(llhttp_t* p) { 55 | return wasm_on_headers_complete(p, p->status_code, p->upgrade, 56 | llhttp_should_keep_alive(p)); 57 | } 58 | 59 | const llhttp_settings_t wasm_settings = { 60 | wasm_on_message_begin, 61 | wasm_on_url, 62 | wasm_on_status, 63 | wasm_on_header_field, 64 | wasm_on_header_value, 65 | wasm_on_headers_complete_wrap, 66 | wasm_on_body, 67 | wasm_on_message_complete, 68 | NULL, 69 | NULL, 70 | }; 71 | 72 | 73 | llhttp_t* llhttp_alloc(llhttp_type_t type) { 74 | llhttp_t* parser = malloc(sizeof(llhttp_t)); 75 | llhttp_init(parser, type, &wasm_settings); 76 | return parser; 77 | } 78 | 79 | void llhttp_free(llhttp_t* parser) { 80 | free(parser); 81 | } 82 | 83 | /* Some getters required to get stuff from the parser */ 84 | 85 | uint8_t llhttp_get_type(llhttp_t* parser) { 86 | return parser->type; 87 | } 88 | 89 | uint8_t llhttp_get_http_major(llhttp_t* parser) { 90 | return parser->http_major; 91 | } 92 | 93 | uint8_t llhttp_get_http_minor(llhttp_t* parser) { 94 | return parser->http_minor; 95 | } 96 | 97 | uint8_t llhttp_get_method(llhttp_t* parser) { 98 | return parser->method; 99 | } 100 | 101 | int llhttp_get_status_code(llhttp_t* parser) { 102 | return parser->status_code; 103 | } 104 | 105 | uint8_t llhttp_get_upgrade(llhttp_t* parser) { 106 | return parser->upgrade; 107 | } 108 | 109 | #endif // defined(__wasm__) 110 | 111 | 112 | void llhttp_reset(llhttp_t* parser) { 113 | llhttp_type_t type = parser->type; 114 | const llhttp_settings_t* settings = parser->settings; 115 | void* data = parser->data; 116 | uint8_t lenient_flags = parser->lenient_flags; 117 | 118 | llhttp__internal_init(parser); 119 | 120 | parser->type = type; 121 | parser->settings = (void*) settings; 122 | parser->data = data; 123 | parser->lenient_flags = lenient_flags; 124 | } 125 | 126 | 127 | llhttp_errno_t llhttp_execute(llhttp_t* parser, const char* data, size_t len) { 128 | return llhttp__internal_execute(parser, data, data + len); 129 | } 130 | 131 | 132 | void llhttp_settings_init(llhttp_settings_t* settings) { 133 | memset(settings, 0, sizeof(*settings)); 134 | } 135 | 136 | 137 | llhttp_errno_t llhttp_finish(llhttp_t* parser) { 138 | int err; 139 | 140 | /* We're in an error state. Don't bother doing anything. */ 141 | if (parser->error != 0) { 142 | return 0; 143 | } 144 | 145 | switch (parser->finish) { 146 | case HTTP_FINISH_SAFE_WITH_CB: 147 | CALLBACK_MAYBE(parser, on_message_complete); 148 | if (err != HPE_OK) return err; 149 | 150 | /* FALLTHROUGH */ 151 | case HTTP_FINISH_SAFE: 152 | return HPE_OK; 153 | case HTTP_FINISH_UNSAFE: 154 | parser->reason = "Invalid EOF state"; 155 | return HPE_INVALID_EOF_STATE; 156 | default: 157 | abort(); 158 | } 159 | } 160 | 161 | 162 | void llhttp_pause(llhttp_t* parser) { 163 | if (parser->error != HPE_OK) { 164 | return; 165 | } 166 | 167 | parser->error = HPE_PAUSED; 168 | parser->reason = "Paused"; 169 | } 170 | 171 | 172 | void llhttp_resume(llhttp_t* parser) { 173 | if (parser->error != HPE_PAUSED) { 174 | return; 175 | } 176 | 177 | parser->error = 0; 178 | } 179 | 180 | 181 | void llhttp_resume_after_upgrade(llhttp_t* parser) { 182 | if (parser->error != HPE_PAUSED_UPGRADE) { 183 | return; 184 | } 185 | 186 | parser->error = 0; 187 | } 188 | 189 | 190 | llhttp_errno_t llhttp_get_errno(const llhttp_t* parser) { 191 | return parser->error; 192 | } 193 | 194 | 195 | const char* llhttp_get_error_reason(const llhttp_t* parser) { 196 | return parser->reason; 197 | } 198 | 199 | 200 | void llhttp_set_error_reason(llhttp_t* parser, const char* reason) { 201 | parser->reason = reason; 202 | } 203 | 204 | 205 | const char* llhttp_get_error_pos(const llhttp_t* parser) { 206 | return parser->error_pos; 207 | } 208 | 209 | 210 | const char* llhttp_errno_name(llhttp_errno_t err) { 211 | #define HTTP_ERRNO_GEN(CODE, NAME, _) case HPE_##NAME: return "HPE_" #NAME; 212 | switch (err) { 213 | HTTP_ERRNO_MAP(HTTP_ERRNO_GEN) 214 | default: abort(); 215 | } 216 | #undef HTTP_ERRNO_GEN 217 | } 218 | 219 | 220 | const char* llhttp_method_name(llhttp_method_t method) { 221 | #define HTTP_METHOD_GEN(NUM, NAME, STRING) case HTTP_##NAME: return #STRING; 222 | switch (method) { 223 | HTTP_ALL_METHOD_MAP(HTTP_METHOD_GEN) 224 | default: abort(); 225 | } 226 | #undef HTTP_METHOD_GEN 227 | } 228 | 229 | 230 | void llhttp_set_lenient_headers(llhttp_t* parser, int enabled) { 231 | if (enabled) { 232 | parser->lenient_flags |= LENIENT_HEADERS; 233 | } else { 234 | parser->lenient_flags &= ~LENIENT_HEADERS; 235 | } 236 | } 237 | 238 | 239 | void llhttp_set_lenient_chunked_length(llhttp_t* parser, int enabled) { 240 | if (enabled) { 241 | parser->lenient_flags |= LENIENT_CHUNKED_LENGTH; 242 | } else { 243 | parser->lenient_flags &= ~LENIENT_CHUNKED_LENGTH; 244 | } 245 | } 246 | 247 | 248 | void llhttp_set_lenient_keep_alive(llhttp_t* parser, int enabled) { 249 | if (enabled) { 250 | parser->lenient_flags |= LENIENT_KEEP_ALIVE; 251 | } else { 252 | parser->lenient_flags &= ~LENIENT_KEEP_ALIVE; 253 | } 254 | } 255 | 256 | void llhttp_set_lenient_transfer_encoding(llhttp_t* parser, int enabled) { 257 | if (enabled) { 258 | parser->lenient_flags |= LENIENT_TRANSFER_ENCODING; 259 | } else { 260 | parser->lenient_flags &= ~LENIENT_TRANSFER_ENCODING; 261 | } 262 | } 263 | 264 | /* Callbacks */ 265 | 266 | 267 | int llhttp__on_message_begin(llhttp_t* s, const char* p, const char* endp) { 268 | int err; 269 | CALLBACK_MAYBE(s, on_message_begin); 270 | return err; 271 | } 272 | 273 | 274 | int llhttp__on_url(llhttp_t* s, const char* p, const char* endp) { 275 | int err; 276 | SPAN_CALLBACK_MAYBE(s, on_url, p, endp - p); 277 | return err; 278 | } 279 | 280 | 281 | int llhttp__on_url_complete(llhttp_t* s, const char* p, const char* endp) { 282 | int err; 283 | CALLBACK_MAYBE(s, on_url_complete); 284 | return err; 285 | } 286 | 287 | 288 | int llhttp__on_status(llhttp_t* s, const char* p, const char* endp) { 289 | int err; 290 | SPAN_CALLBACK_MAYBE(s, on_status, p, endp - p); 291 | return err; 292 | } 293 | 294 | 295 | int llhttp__on_status_complete(llhttp_t* s, const char* p, const char* endp) { 296 | int err; 297 | CALLBACK_MAYBE(s, on_status_complete); 298 | return err; 299 | } 300 | 301 | 302 | int llhttp__on_header_field(llhttp_t* s, const char* p, const char* endp) { 303 | int err; 304 | SPAN_CALLBACK_MAYBE(s, on_header_field, p, endp - p); 305 | return err; 306 | } 307 | 308 | 309 | int llhttp__on_header_field_complete(llhttp_t* s, const char* p, const char* endp) { 310 | int err; 311 | CALLBACK_MAYBE(s, on_header_field_complete); 312 | return err; 313 | } 314 | 315 | 316 | int llhttp__on_header_value(llhttp_t* s, const char* p, const char* endp) { 317 | int err; 318 | SPAN_CALLBACK_MAYBE(s, on_header_value, p, endp - p); 319 | return err; 320 | } 321 | 322 | 323 | int llhttp__on_header_value_complete(llhttp_t* s, const char* p, const char* endp) { 324 | int err; 325 | CALLBACK_MAYBE(s, on_header_value_complete); 326 | return err; 327 | } 328 | 329 | 330 | int llhttp__on_headers_complete(llhttp_t* s, const char* p, const char* endp) { 331 | int err; 332 | CALLBACK_MAYBE(s, on_headers_complete); 333 | return err; 334 | } 335 | 336 | 337 | int llhttp__on_message_complete(llhttp_t* s, const char* p, const char* endp) { 338 | int err; 339 | CALLBACK_MAYBE(s, on_message_complete); 340 | return err; 341 | } 342 | 343 | 344 | int llhttp__on_body(llhttp_t* s, const char* p, const char* endp) { 345 | int err; 346 | SPAN_CALLBACK_MAYBE(s, on_body, p, endp - p); 347 | return err; 348 | } 349 | 350 | 351 | int llhttp__on_chunk_header(llhttp_t* s, const char* p, const char* endp) { 352 | int err; 353 | CALLBACK_MAYBE(s, on_chunk_header); 354 | return err; 355 | } 356 | 357 | 358 | int llhttp__on_chunk_complete(llhttp_t* s, const char* p, const char* endp) { 359 | int err; 360 | CALLBACK_MAYBE(s, on_chunk_complete); 361 | return err; 362 | } 363 | 364 | 365 | /* Private */ 366 | 367 | 368 | void llhttp__debug(llhttp_t* s, const char* p, const char* endp, 369 | const char* msg) { 370 | if (p == endp) { 371 | fprintf(stderr, "p=%p type=%d flags=%02x next=null debug=%s\n", s, s->type, 372 | s->flags, msg); 373 | } else { 374 | fprintf(stderr, "p=%p type=%d flags=%02x next=%02x debug=%s\n", s, 375 | s->type, s->flags, *p, msg); 376 | } 377 | } 378 | -------------------------------------------------------------------------------- /lib/http.c: -------------------------------------------------------------------------------- 1 | #include 2 | #ifndef LLHTTP__TEST 3 | # include "llhttp.h" 4 | #else 5 | # define llhttp_t llparse_t 6 | #endif /* */ 7 | 8 | int llhttp_message_needs_eof(const llhttp_t* parser); 9 | int llhttp_should_keep_alive(const llhttp_t* parser); 10 | 11 | int llhttp__before_headers_complete(llhttp_t* parser, const char* p, 12 | const char* endp) { 13 | /* Set this here so that on_headers_complete() callbacks can see it */ 14 | if ((parser->flags & F_UPGRADE) && 15 | (parser->flags & F_CONNECTION_UPGRADE)) { 16 | /* For responses, "Upgrade: foo" and "Connection: upgrade" are 17 | * mandatory only when it is a 101 Switching Protocols response, 18 | * otherwise it is purely informational, to announce support. 19 | */ 20 | parser->upgrade = 21 | (parser->type == HTTP_REQUEST || parser->status_code == 101); 22 | } else { 23 | parser->upgrade = (parser->method == HTTP_CONNECT); 24 | } 25 | return 0; 26 | } 27 | 28 | 29 | /* Return values: 30 | * 0 - No body, `restart`, message_complete 31 | * 1 - CONNECT request, `restart`, message_complete, and pause 32 | * 2 - chunk_size_start 33 | * 3 - body_identity 34 | * 4 - body_identity_eof 35 | * 5 - invalid transfer-encoding for request 36 | */ 37 | int llhttp__after_headers_complete(llhttp_t* parser, const char* p, 38 | const char* endp) { 39 | int hasBody; 40 | 41 | hasBody = parser->flags & F_CHUNKED || parser->content_length > 0; 42 | if (parser->upgrade && (parser->method == HTTP_CONNECT || 43 | (parser->flags & F_SKIPBODY) || !hasBody)) { 44 | /* Exit, the rest of the message is in a different protocol. */ 45 | return 1; 46 | } 47 | 48 | if (parser->flags & F_SKIPBODY) { 49 | return 0; 50 | } else if (parser->flags & F_CHUNKED) { 51 | /* chunked encoding - ignore Content-Length header, prepare for a chunk */ 52 | return 2; 53 | } else if (parser->flags & F_TRANSFER_ENCODING) { 54 | if (parser->type == HTTP_REQUEST && 55 | (parser->lenient_flags & LENIENT_CHUNKED_LENGTH) == 0 && 56 | (parser->lenient_flags & LENIENT_TRANSFER_ENCODING) == 0) { 57 | /* RFC 7230 3.3.3 */ 58 | 59 | /* If a Transfer-Encoding header field 60 | * is present in a request and the chunked transfer coding is not 61 | * the final encoding, the message body length cannot be determined 62 | * reliably; the server MUST respond with the 400 (Bad Request) 63 | * status code and then close the connection. 64 | */ 65 | return 5; 66 | } else { 67 | /* RFC 7230 3.3.3 */ 68 | 69 | /* If a Transfer-Encoding header field is present in a response and 70 | * the chunked transfer coding is not the final encoding, the 71 | * message body length is determined by reading the connection until 72 | * it is closed by the server. 73 | */ 74 | return 4; 75 | } 76 | } else { 77 | if (!(parser->flags & F_CONTENT_LENGTH)) { 78 | if (!llhttp_message_needs_eof(parser)) { 79 | /* Assume content-length 0 - read the next */ 80 | return 0; 81 | } else { 82 | /* Read body until EOF */ 83 | return 4; 84 | } 85 | } else if (parser->content_length == 0) { 86 | /* Content-Length header given but zero: Content-Length: 0\r\n */ 87 | return 0; 88 | } else { 89 | /* Content-Length header given and non-zero */ 90 | return 3; 91 | } 92 | } 93 | } 94 | 95 | 96 | int llhttp__after_message_complete(llhttp_t* parser, const char* p, 97 | const char* endp) { 98 | int should_keep_alive; 99 | 100 | should_keep_alive = llhttp_should_keep_alive(parser); 101 | parser->finish = HTTP_FINISH_SAFE; 102 | parser->flags = 0; 103 | 104 | /* NOTE: this is ignored in loose parsing mode */ 105 | return should_keep_alive; 106 | } 107 | 108 | 109 | int llhttp_message_needs_eof(const llhttp_t* parser) { 110 | if (parser->type == HTTP_REQUEST) { 111 | return 0; 112 | } 113 | 114 | /* See RFC 2616 section 4.4 */ 115 | if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */ 116 | parser->status_code == 204 || /* No Content */ 117 | parser->status_code == 304 || /* Not Modified */ 118 | (parser->flags & F_SKIPBODY)) { /* response to a HEAD request */ 119 | return 0; 120 | } 121 | 122 | /* RFC 7230 3.3.3, see `llhttp__after_headers_complete` */ 123 | if ((parser->flags & F_TRANSFER_ENCODING) && 124 | (parser->flags & F_CHUNKED) == 0) { 125 | return 1; 126 | } 127 | 128 | if (parser->flags & (F_CHUNKED | F_CONTENT_LENGTH)) { 129 | return 0; 130 | } 131 | 132 | return 1; 133 | } 134 | 135 | 136 | int llhttp_should_keep_alive(const llhttp_t* parser) { 137 | if (parser->http_major > 0 && parser->http_minor > 0) { 138 | /* HTTP/1.1 */ 139 | if (parser->flags & F_CONNECTION_CLOSE) { 140 | return 0; 141 | } 142 | } else { 143 | /* HTTP/1.0 or earlier */ 144 | if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) { 145 | return 0; 146 | } 147 | } 148 | 149 | return !llhttp_message_needs_eof(parser); 150 | } 151 | -------------------------------------------------------------------------------- /lib/llhttp.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_LLHTTP_H_ 2 | #define INCLUDE_LLHTTP_H_ 3 | 4 | #define LLHTTP_VERSION_MAJOR 6 5 | #define LLHTTP_VERSION_MINOR 0 6 | #define LLHTTP_VERSION_PATCH 9 7 | 8 | #ifndef LLHTTP_STRICT_MODE 9 | # define LLHTTP_STRICT_MODE 0 10 | #endif 11 | 12 | #ifndef INCLUDE_LLHTTP_ITSELF_H_ 13 | #define INCLUDE_LLHTTP_ITSELF_H_ 14 | #ifdef __cplusplus 15 | extern "C" { 16 | #endif 17 | 18 | #include 19 | 20 | typedef struct llhttp__internal_s llhttp__internal_t; 21 | struct llhttp__internal_s { 22 | int32_t _index; 23 | void* _span_pos0; 24 | void* _span_cb0; 25 | int32_t error; 26 | const char* reason; 27 | const char* error_pos; 28 | void* data; 29 | void* _current; 30 | uint64_t content_length; 31 | uint8_t type; 32 | uint8_t method; 33 | uint8_t http_major; 34 | uint8_t http_minor; 35 | uint8_t header_state; 36 | uint8_t lenient_flags; 37 | uint8_t upgrade; 38 | uint8_t finish; 39 | uint16_t flags; 40 | uint16_t status_code; 41 | void* settings; 42 | }; 43 | 44 | int llhttp__internal_init(llhttp__internal_t* s); 45 | int llhttp__internal_execute(llhttp__internal_t* s, const char* p, const char* endp); 46 | 47 | #ifdef __cplusplus 48 | } /* extern "C" */ 49 | #endif 50 | #endif /* INCLUDE_LLHTTP_ITSELF_H_ */ 51 | 52 | #ifndef LLLLHTTP_C_HEADERS_ 53 | #define LLLLHTTP_C_HEADERS_ 54 | #ifdef __cplusplus 55 | extern "C" { 56 | #endif 57 | 58 | enum llhttp_errno { 59 | HPE_OK = 0, 60 | HPE_INTERNAL = 1, 61 | HPE_STRICT = 2, 62 | HPE_CR_EXPECTED = 25, 63 | HPE_LF_EXPECTED = 3, 64 | HPE_UNEXPECTED_CONTENT_LENGTH = 4, 65 | HPE_CLOSED_CONNECTION = 5, 66 | HPE_INVALID_METHOD = 6, 67 | HPE_INVALID_URL = 7, 68 | HPE_INVALID_CONSTANT = 8, 69 | HPE_INVALID_VERSION = 9, 70 | HPE_INVALID_HEADER_TOKEN = 10, 71 | HPE_INVALID_CONTENT_LENGTH = 11, 72 | HPE_INVALID_CHUNK_SIZE = 12, 73 | HPE_INVALID_STATUS = 13, 74 | HPE_INVALID_EOF_STATE = 14, 75 | HPE_INVALID_TRANSFER_ENCODING = 15, 76 | HPE_CB_MESSAGE_BEGIN = 16, 77 | HPE_CB_HEADERS_COMPLETE = 17, 78 | HPE_CB_MESSAGE_COMPLETE = 18, 79 | HPE_CB_CHUNK_HEADER = 19, 80 | HPE_CB_CHUNK_COMPLETE = 20, 81 | HPE_PAUSED = 21, 82 | HPE_PAUSED_UPGRADE = 22, 83 | HPE_PAUSED_H2_UPGRADE = 23, 84 | HPE_USER = 24 85 | }; 86 | typedef enum llhttp_errno llhttp_errno_t; 87 | 88 | enum llhttp_flags { 89 | F_CONNECTION_KEEP_ALIVE = 0x1, 90 | F_CONNECTION_CLOSE = 0x2, 91 | F_CONNECTION_UPGRADE = 0x4, 92 | F_CHUNKED = 0x8, 93 | F_UPGRADE = 0x10, 94 | F_CONTENT_LENGTH = 0x20, 95 | F_SKIPBODY = 0x40, 96 | F_TRAILING = 0x80, 97 | F_TRANSFER_ENCODING = 0x200 98 | }; 99 | typedef enum llhttp_flags llhttp_flags_t; 100 | 101 | enum llhttp_lenient_flags { 102 | LENIENT_HEADERS = 0x1, 103 | LENIENT_CHUNKED_LENGTH = 0x2, 104 | LENIENT_KEEP_ALIVE = 0x4, 105 | LENIENT_TRANSFER_ENCODING = 0x8, 106 | LENIENT_VERSION = 0x10 107 | }; 108 | typedef enum llhttp_lenient_flags llhttp_lenient_flags_t; 109 | 110 | enum llhttp_type { 111 | HTTP_BOTH = 0, 112 | HTTP_REQUEST = 1, 113 | HTTP_RESPONSE = 2 114 | }; 115 | typedef enum llhttp_type llhttp_type_t; 116 | 117 | enum llhttp_finish { 118 | HTTP_FINISH_SAFE = 0, 119 | HTTP_FINISH_SAFE_WITH_CB = 1, 120 | HTTP_FINISH_UNSAFE = 2 121 | }; 122 | typedef enum llhttp_finish llhttp_finish_t; 123 | 124 | enum llhttp_method { 125 | HTTP_DELETE = 0, 126 | HTTP_GET = 1, 127 | HTTP_HEAD = 2, 128 | HTTP_POST = 3, 129 | HTTP_PUT = 4, 130 | HTTP_CONNECT = 5, 131 | HTTP_OPTIONS = 6, 132 | HTTP_TRACE = 7, 133 | HTTP_COPY = 8, 134 | HTTP_LOCK = 9, 135 | HTTP_MKCOL = 10, 136 | HTTP_MOVE = 11, 137 | HTTP_PROPFIND = 12, 138 | HTTP_PROPPATCH = 13, 139 | HTTP_SEARCH = 14, 140 | HTTP_UNLOCK = 15, 141 | HTTP_BIND = 16, 142 | HTTP_REBIND = 17, 143 | HTTP_UNBIND = 18, 144 | HTTP_ACL = 19, 145 | HTTP_REPORT = 20, 146 | HTTP_MKACTIVITY = 21, 147 | HTTP_CHECKOUT = 22, 148 | HTTP_MERGE = 23, 149 | HTTP_MSEARCH = 24, 150 | HTTP_NOTIFY = 25, 151 | HTTP_SUBSCRIBE = 26, 152 | HTTP_UNSUBSCRIBE = 27, 153 | HTTP_PATCH = 28, 154 | HTTP_PURGE = 29, 155 | HTTP_MKCALENDAR = 30, 156 | HTTP_LINK = 31, 157 | HTTP_UNLINK = 32, 158 | HTTP_SOURCE = 33, 159 | HTTP_PRI = 34, 160 | HTTP_DESCRIBE = 35, 161 | HTTP_ANNOUNCE = 36, 162 | HTTP_SETUP = 37, 163 | HTTP_PLAY = 38, 164 | HTTP_PAUSE = 39, 165 | HTTP_TEARDOWN = 40, 166 | HTTP_GET_PARAMETER = 41, 167 | HTTP_SET_PARAMETER = 42, 168 | HTTP_REDIRECT = 43, 169 | HTTP_RECORD = 44, 170 | HTTP_FLUSH = 45 171 | }; 172 | typedef enum llhttp_method llhttp_method_t; 173 | 174 | #define HTTP_ERRNO_MAP(XX) \ 175 | XX(0, OK, OK) \ 176 | XX(1, INTERNAL, INTERNAL) \ 177 | XX(2, STRICT, STRICT) \ 178 | XX(25, CR_EXPECTED, CR_EXPECTED) \ 179 | XX(3, LF_EXPECTED, LF_EXPECTED) \ 180 | XX(4, UNEXPECTED_CONTENT_LENGTH, UNEXPECTED_CONTENT_LENGTH) \ 181 | XX(5, CLOSED_CONNECTION, CLOSED_CONNECTION) \ 182 | XX(6, INVALID_METHOD, INVALID_METHOD) \ 183 | XX(7, INVALID_URL, INVALID_URL) \ 184 | XX(8, INVALID_CONSTANT, INVALID_CONSTANT) \ 185 | XX(9, INVALID_VERSION, INVALID_VERSION) \ 186 | XX(10, INVALID_HEADER_TOKEN, INVALID_HEADER_TOKEN) \ 187 | XX(11, INVALID_CONTENT_LENGTH, INVALID_CONTENT_LENGTH) \ 188 | XX(12, INVALID_CHUNK_SIZE, INVALID_CHUNK_SIZE) \ 189 | XX(13, INVALID_STATUS, INVALID_STATUS) \ 190 | XX(14, INVALID_EOF_STATE, INVALID_EOF_STATE) \ 191 | XX(15, INVALID_TRANSFER_ENCODING, INVALID_TRANSFER_ENCODING) \ 192 | XX(16, CB_MESSAGE_BEGIN, CB_MESSAGE_BEGIN) \ 193 | XX(17, CB_HEADERS_COMPLETE, CB_HEADERS_COMPLETE) \ 194 | XX(18, CB_MESSAGE_COMPLETE, CB_MESSAGE_COMPLETE) \ 195 | XX(19, CB_CHUNK_HEADER, CB_CHUNK_HEADER) \ 196 | XX(20, CB_CHUNK_COMPLETE, CB_CHUNK_COMPLETE) \ 197 | XX(21, PAUSED, PAUSED) \ 198 | XX(22, PAUSED_UPGRADE, PAUSED_UPGRADE) \ 199 | XX(23, PAUSED_H2_UPGRADE, PAUSED_H2_UPGRADE) \ 200 | XX(24, USER, USER) \ 201 | 202 | 203 | #define HTTP_METHOD_MAP(XX) \ 204 | XX(0, DELETE, DELETE) \ 205 | XX(1, GET, GET) \ 206 | XX(2, HEAD, HEAD) \ 207 | XX(3, POST, POST) \ 208 | XX(4, PUT, PUT) \ 209 | XX(5, CONNECT, CONNECT) \ 210 | XX(6, OPTIONS, OPTIONS) \ 211 | XX(7, TRACE, TRACE) \ 212 | XX(8, COPY, COPY) \ 213 | XX(9, LOCK, LOCK) \ 214 | XX(10, MKCOL, MKCOL) \ 215 | XX(11, MOVE, MOVE) \ 216 | XX(12, PROPFIND, PROPFIND) \ 217 | XX(13, PROPPATCH, PROPPATCH) \ 218 | XX(14, SEARCH, SEARCH) \ 219 | XX(15, UNLOCK, UNLOCK) \ 220 | XX(16, BIND, BIND) \ 221 | XX(17, REBIND, REBIND) \ 222 | XX(18, UNBIND, UNBIND) \ 223 | XX(19, ACL, ACL) \ 224 | XX(20, REPORT, REPORT) \ 225 | XX(21, MKACTIVITY, MKACTIVITY) \ 226 | XX(22, CHECKOUT, CHECKOUT) \ 227 | XX(23, MERGE, MERGE) \ 228 | XX(24, MSEARCH, M-SEARCH) \ 229 | XX(25, NOTIFY, NOTIFY) \ 230 | XX(26, SUBSCRIBE, SUBSCRIBE) \ 231 | XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \ 232 | XX(28, PATCH, PATCH) \ 233 | XX(29, PURGE, PURGE) \ 234 | XX(30, MKCALENDAR, MKCALENDAR) \ 235 | XX(31, LINK, LINK) \ 236 | XX(32, UNLINK, UNLINK) \ 237 | XX(33, SOURCE, SOURCE) \ 238 | 239 | 240 | #define RTSP_METHOD_MAP(XX) \ 241 | XX(1, GET, GET) \ 242 | XX(3, POST, POST) \ 243 | XX(6, OPTIONS, OPTIONS) \ 244 | XX(35, DESCRIBE, DESCRIBE) \ 245 | XX(36, ANNOUNCE, ANNOUNCE) \ 246 | XX(37, SETUP, SETUP) \ 247 | XX(38, PLAY, PLAY) \ 248 | XX(39, PAUSE, PAUSE) \ 249 | XX(40, TEARDOWN, TEARDOWN) \ 250 | XX(41, GET_PARAMETER, GET_PARAMETER) \ 251 | XX(42, SET_PARAMETER, SET_PARAMETER) \ 252 | XX(43, REDIRECT, REDIRECT) \ 253 | XX(44, RECORD, RECORD) \ 254 | XX(45, FLUSH, FLUSH) \ 255 | 256 | 257 | #define HTTP_ALL_METHOD_MAP(XX) \ 258 | XX(0, DELETE, DELETE) \ 259 | XX(1, GET, GET) \ 260 | XX(2, HEAD, HEAD) \ 261 | XX(3, POST, POST) \ 262 | XX(4, PUT, PUT) \ 263 | XX(5, CONNECT, CONNECT) \ 264 | XX(6, OPTIONS, OPTIONS) \ 265 | XX(7, TRACE, TRACE) \ 266 | XX(8, COPY, COPY) \ 267 | XX(9, LOCK, LOCK) \ 268 | XX(10, MKCOL, MKCOL) \ 269 | XX(11, MOVE, MOVE) \ 270 | XX(12, PROPFIND, PROPFIND) \ 271 | XX(13, PROPPATCH, PROPPATCH) \ 272 | XX(14, SEARCH, SEARCH) \ 273 | XX(15, UNLOCK, UNLOCK) \ 274 | XX(16, BIND, BIND) \ 275 | XX(17, REBIND, REBIND) \ 276 | XX(18, UNBIND, UNBIND) \ 277 | XX(19, ACL, ACL) \ 278 | XX(20, REPORT, REPORT) \ 279 | XX(21, MKACTIVITY, MKACTIVITY) \ 280 | XX(22, CHECKOUT, CHECKOUT) \ 281 | XX(23, MERGE, MERGE) \ 282 | XX(24, MSEARCH, M-SEARCH) \ 283 | XX(25, NOTIFY, NOTIFY) \ 284 | XX(26, SUBSCRIBE, SUBSCRIBE) \ 285 | XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \ 286 | XX(28, PATCH, PATCH) \ 287 | XX(29, PURGE, PURGE) \ 288 | XX(30, MKCALENDAR, MKCALENDAR) \ 289 | XX(31, LINK, LINK) \ 290 | XX(32, UNLINK, UNLINK) \ 291 | XX(33, SOURCE, SOURCE) \ 292 | XX(34, PRI, PRI) \ 293 | XX(35, DESCRIBE, DESCRIBE) \ 294 | XX(36, ANNOUNCE, ANNOUNCE) \ 295 | XX(37, SETUP, SETUP) \ 296 | XX(38, PLAY, PLAY) \ 297 | XX(39, PAUSE, PAUSE) \ 298 | XX(40, TEARDOWN, TEARDOWN) \ 299 | XX(41, GET_PARAMETER, GET_PARAMETER) \ 300 | XX(42, SET_PARAMETER, SET_PARAMETER) \ 301 | XX(43, REDIRECT, REDIRECT) \ 302 | XX(44, RECORD, RECORD) \ 303 | XX(45, FLUSH, FLUSH) \ 304 | 305 | 306 | #ifdef __cplusplus 307 | } /* extern "C" */ 308 | #endif 309 | #endif /* LLLLHTTP_C_HEADERS_ */ 310 | 311 | #ifndef INCLUDE_LLHTTP_API_H_ 312 | #define INCLUDE_LLHTTP_API_H_ 313 | #ifdef __cplusplus 314 | extern "C" { 315 | #endif 316 | #include 317 | 318 | #if defined(__wasm__) 319 | #define LLHTTP_EXPORT __attribute__((visibility("default"))) 320 | #else 321 | #define LLHTTP_EXPORT 322 | #endif 323 | 324 | typedef llhttp__internal_t llhttp_t; 325 | typedef struct llhttp_settings_s llhttp_settings_t; 326 | 327 | typedef int (*llhttp_data_cb)(llhttp_t*, const char *at, size_t length); 328 | typedef int (*llhttp_cb)(llhttp_t*); 329 | 330 | struct llhttp_settings_s { 331 | /* Possible return values 0, -1, `HPE_PAUSED` */ 332 | llhttp_cb on_message_begin; 333 | 334 | /* Possible return values 0, -1, HPE_USER */ 335 | llhttp_data_cb on_url; 336 | llhttp_data_cb on_status; 337 | llhttp_data_cb on_header_field; 338 | llhttp_data_cb on_header_value; 339 | 340 | /* Possible return values: 341 | * 0 - Proceed normally 342 | * 1 - Assume that request/response has no body, and proceed to parsing the 343 | * next message 344 | * 2 - Assume absence of body (as above) and make `llhttp_execute()` return 345 | * `HPE_PAUSED_UPGRADE` 346 | * -1 - Error 347 | * `HPE_PAUSED` 348 | */ 349 | llhttp_cb on_headers_complete; 350 | 351 | /* Possible return values 0, -1, HPE_USER */ 352 | llhttp_data_cb on_body; 353 | 354 | /* Possible return values 0, -1, `HPE_PAUSED` */ 355 | llhttp_cb on_message_complete; 356 | 357 | /* When on_chunk_header is called, the current chunk length is stored 358 | * in parser->content_length. 359 | * Possible return values 0, -1, `HPE_PAUSED` 360 | */ 361 | llhttp_cb on_chunk_header; 362 | llhttp_cb on_chunk_complete; 363 | 364 | /* Information-only callbacks, return value is ignored */ 365 | llhttp_cb on_url_complete; 366 | llhttp_cb on_status_complete; 367 | llhttp_cb on_header_field_complete; 368 | llhttp_cb on_header_value_complete; 369 | }; 370 | 371 | /* Initialize the parser with specific type and user settings. 372 | * 373 | * NOTE: lifetime of `settings` has to be at least the same as the lifetime of 374 | * the `parser` here. In practice, `settings` has to be either a static 375 | * variable or be allocated with `malloc`, `new`, etc. 376 | */ 377 | LLHTTP_EXPORT 378 | void llhttp_init(llhttp_t* parser, llhttp_type_t type, 379 | const llhttp_settings_t* settings); 380 | 381 | LLHTTP_EXPORT 382 | llhttp_t* llhttp_alloc(llhttp_type_t type); 383 | 384 | LLHTTP_EXPORT 385 | void llhttp_free(llhttp_t* parser); 386 | 387 | LLHTTP_EXPORT 388 | uint8_t llhttp_get_type(llhttp_t* parser); 389 | 390 | LLHTTP_EXPORT 391 | uint8_t llhttp_get_http_major(llhttp_t* parser); 392 | 393 | LLHTTP_EXPORT 394 | uint8_t llhttp_get_http_minor(llhttp_t* parser); 395 | 396 | LLHTTP_EXPORT 397 | uint8_t llhttp_get_method(llhttp_t* parser); 398 | 399 | LLHTTP_EXPORT 400 | int llhttp_get_status_code(llhttp_t* parser); 401 | 402 | LLHTTP_EXPORT 403 | uint8_t llhttp_get_upgrade(llhttp_t* parser); 404 | 405 | /* Reset an already initialized parser back to the start state, preserving the 406 | * existing parser type, callback settings, user data, and lenient flags. 407 | */ 408 | LLHTTP_EXPORT 409 | void llhttp_reset(llhttp_t* parser); 410 | 411 | /* Initialize the settings object */ 412 | LLHTTP_EXPORT 413 | void llhttp_settings_init(llhttp_settings_t* settings); 414 | 415 | /* Parse full or partial request/response, invoking user callbacks along the 416 | * way. 417 | * 418 | * If any of `llhttp_data_cb` returns errno not equal to `HPE_OK` - the parsing 419 | * interrupts, and such errno is returned from `llhttp_execute()`. If 420 | * `HPE_PAUSED` was used as a errno, the execution can be resumed with 421 | * `llhttp_resume()` call. 422 | * 423 | * In a special case of CONNECT/Upgrade request/response `HPE_PAUSED_UPGRADE` 424 | * is returned after fully parsing the request/response. If the user wishes to 425 | * continue parsing, they need to invoke `llhttp_resume_after_upgrade()`. 426 | * 427 | * NOTE: if this function ever returns a non-pause type error, it will continue 428 | * to return the same error upon each successive call up until `llhttp_init()` 429 | * is called. 430 | */ 431 | LLHTTP_EXPORT 432 | llhttp_errno_t llhttp_execute(llhttp_t* parser, const char* data, size_t len); 433 | 434 | /* This method should be called when the other side has no further bytes to 435 | * send (e.g. shutdown of readable side of the TCP connection.) 436 | * 437 | * Requests without `Content-Length` and other messages might require treating 438 | * all incoming bytes as the part of the body, up to the last byte of the 439 | * connection. This method will invoke `on_message_complete()` callback if the 440 | * request was terminated safely. Otherwise a error code would be returned. 441 | */ 442 | LLHTTP_EXPORT 443 | llhttp_errno_t llhttp_finish(llhttp_t* parser); 444 | 445 | /* Returns `1` if the incoming message is parsed until the last byte, and has 446 | * to be completed by calling `llhttp_finish()` on EOF 447 | */ 448 | LLHTTP_EXPORT 449 | int llhttp_message_needs_eof(const llhttp_t* parser); 450 | 451 | /* Returns `1` if there might be any other messages following the last that was 452 | * successfully parsed. 453 | */ 454 | LLHTTP_EXPORT 455 | int llhttp_should_keep_alive(const llhttp_t* parser); 456 | 457 | /* Make further calls of `llhttp_execute()` return `HPE_PAUSED` and set 458 | * appropriate error reason. 459 | * 460 | * Important: do not call this from user callbacks! User callbacks must return 461 | * `HPE_PAUSED` if pausing is required. 462 | */ 463 | LLHTTP_EXPORT 464 | void llhttp_pause(llhttp_t* parser); 465 | 466 | /* Might be called to resume the execution after the pause in user's callback. 467 | * See `llhttp_execute()` above for details. 468 | * 469 | * Call this only if `llhttp_execute()` returns `HPE_PAUSED`. 470 | */ 471 | LLHTTP_EXPORT 472 | void llhttp_resume(llhttp_t* parser); 473 | 474 | /* Might be called to resume the execution after the pause in user's callback. 475 | * See `llhttp_execute()` above for details. 476 | * 477 | * Call this only if `llhttp_execute()` returns `HPE_PAUSED_UPGRADE` 478 | */ 479 | LLHTTP_EXPORT 480 | void llhttp_resume_after_upgrade(llhttp_t* parser); 481 | 482 | /* Returns the latest return error */ 483 | LLHTTP_EXPORT 484 | llhttp_errno_t llhttp_get_errno(const llhttp_t* parser); 485 | 486 | /* Returns the verbal explanation of the latest returned error. 487 | * 488 | * Note: User callback should set error reason when returning the error. See 489 | * `llhttp_set_error_reason()` for details. 490 | */ 491 | LLHTTP_EXPORT 492 | const char* llhttp_get_error_reason(const llhttp_t* parser); 493 | 494 | /* Assign verbal description to the returned error. Must be called in user 495 | * callbacks right before returning the errno. 496 | * 497 | * Note: `HPE_USER` error code might be useful in user callbacks. 498 | */ 499 | LLHTTP_EXPORT 500 | void llhttp_set_error_reason(llhttp_t* parser, const char* reason); 501 | 502 | /* Returns the pointer to the last parsed byte before the returned error. The 503 | * pointer is relative to the `data` argument of `llhttp_execute()`. 504 | * 505 | * Note: this method might be useful for counting the number of parsed bytes. 506 | */ 507 | LLHTTP_EXPORT 508 | const char* llhttp_get_error_pos(const llhttp_t* parser); 509 | 510 | /* Returns textual name of error code */ 511 | LLHTTP_EXPORT 512 | const char* llhttp_errno_name(llhttp_errno_t err); 513 | 514 | /* Returns textual name of HTTP method */ 515 | LLHTTP_EXPORT 516 | const char* llhttp_method_name(llhttp_method_t method); 517 | 518 | 519 | /* Enables/disables lenient header value parsing (disabled by default). 520 | * 521 | * Lenient parsing disables header value token checks, extending llhttp's 522 | * protocol support to highly non-compliant clients/server. No 523 | * `HPE_INVALID_HEADER_TOKEN` will be raised for incorrect header values when 524 | * lenient parsing is "on". 525 | * 526 | * **(USE AT YOUR OWN RISK)** 527 | */ 528 | LLHTTP_EXPORT 529 | void llhttp_set_lenient_headers(llhttp_t* parser, int enabled); 530 | 531 | 532 | /* Enables/disables lenient handling of conflicting `Transfer-Encoding` and 533 | * `Content-Length` headers (disabled by default). 534 | * 535 | * Normally `llhttp` would error when `Transfer-Encoding` is present in 536 | * conjunction with `Content-Length`. This error is important to prevent HTTP 537 | * request smuggling, but may be less desirable for small number of cases 538 | * involving legacy servers. 539 | * 540 | * **(USE AT YOUR OWN RISK)** 541 | */ 542 | LLHTTP_EXPORT 543 | void llhttp_set_lenient_chunked_length(llhttp_t* parser, int enabled); 544 | 545 | 546 | /* Enables/disables lenient handling of `Connection: close` and HTTP/1.0 547 | * requests responses. 548 | * 549 | * Normally `llhttp` would error on (in strict mode) or discard (in loose mode) 550 | * the HTTP request/response after the request/response with `Connection: close` 551 | * and `Content-Length`. This is important to prevent cache poisoning attacks, 552 | * but might interact badly with outdated and insecure clients. With this flag 553 | * the extra request/response will be parsed normally. 554 | * 555 | * **(USE AT YOUR OWN RISK)** 556 | */ 557 | void llhttp_set_lenient_keep_alive(llhttp_t* parser, int enabled); 558 | 559 | /* Enables/disables lenient handling of `Transfer-Encoding` header. 560 | * 561 | * Normally `llhttp` would error when a `Transfer-Encoding` has `chunked` value 562 | * and another value after it (either in a single header or in multiple 563 | * headers whose value are internally joined using `, `). 564 | * This is mandated by the spec to reliably determine request body size and thus 565 | * avoid request smuggling. 566 | * With this flag the extra value will be parsed normally. 567 | * 568 | * **(USE AT YOUR OWN RISK)** 569 | */ 570 | void llhttp_set_lenient_transfer_encoding(llhttp_t* parser, int enabled); 571 | 572 | #ifdef __cplusplus 573 | } /* extern "C" */ 574 | #endif 575 | #endif /* INCLUDE_LLHTTP_API_H_ */ 576 | 577 | #endif /* INCLUDE_LLHTTP_H_ */ 578 | -------------------------------------------------------------------------------- /llhttp/__init__.py: -------------------------------------------------------------------------------- 1 | from __llhttp import * 2 | __all__ = ("Request", "Response") 3 | -------------------------------------------------------------------------------- /pyllhttp.c: -------------------------------------------------------------------------------- 1 | #define PY_SSIZE_T_CLEAN 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "lib/llhttp.h" 7 | 8 | #define STRING(x) #x 9 | #define XSTRING(x) STRING(x) 10 | #define LLHTTP_VERSION XSTRING(LLHTTP_VERSION_MAJOR) "." XSTRING(LLHTTP_VERSION_MINOR) "." XSTRING(LLHTTP_VERSION_PATCH) 11 | 12 | typedef struct { 13 | PyObject_HEAD 14 | llhttp_t llhttp; 15 | } parser_object; 16 | 17 | static PyObject *base_error; 18 | static PyObject *errors[] = { 19 | #define HTTP_ERRNO_GEN(CODE, NAME, _) NULL, 20 | HTTP_ERRNO_MAP(HTTP_ERRNO_GEN) 21 | #undef HTTP_ERRNO_GEN 22 | }; 23 | 24 | static PyObject *methods[] = { 25 | #define HTTP_METHOD_GEN(NUMBER, NAME, STRING) NULL, 26 | HTTP_METHOD_MAP(HTTP_METHOD_GEN) 27 | #undef HTTP_METHOD_GEN 28 | }; 29 | 30 | 31 | static int 32 | parser_callback(_Py_Identifier *type, llhttp_t *llhttp) { 33 | PyObject *result = _PyObject_CallMethodIdObjArgs(llhttp->data, type, NULL); 34 | if (result) 35 | Py_DECREF(result); 36 | 37 | if (PyErr_Occurred()) 38 | return HPE_USER; 39 | 40 | if (HPE_PAUSED == llhttp_get_errno(llhttp)) { 41 | llhttp_resume(llhttp); 42 | return HPE_PAUSED; 43 | } 44 | 45 | if (HPE_PAUSED_UPGRADE == llhttp_get_errno(llhttp)) { 46 | llhttp_resume_after_upgrade(llhttp); 47 | return HPE_PAUSED_UPGRADE; 48 | } 49 | 50 | return HPE_OK; 51 | } 52 | 53 | static int 54 | parser_data_callback(_Py_Identifier *type, llhttp_t *llhttp, const char *data, size_t length) { 55 | PyObject *payload = PyMemoryView_FromMemory((char*)data, length, PyBUF_READ); 56 | PyObject *result = _PyObject_CallMethodIdObjArgs(llhttp->data, type, payload, NULL); 57 | Py_DECREF(payload); 58 | if (result) 59 | Py_DECREF(result); 60 | 61 | if (PyErr_Occurred()) 62 | return HPE_USER; 63 | 64 | if (HPE_PAUSED == llhttp_get_errno(llhttp)) { 65 | llhttp_resume(llhttp); 66 | return HPE_PAUSED; 67 | } 68 | 69 | if (HPE_PAUSED_UPGRADE == llhttp_get_errno(llhttp)) { 70 | llhttp_resume_after_upgrade(llhttp); 71 | return HPE_PAUSED_UPGRADE; 72 | } 73 | 74 | return HPE_OK; 75 | } 76 | 77 | #define PARSER_CALLBACK(type) \ 78 | _Py_IDENTIFIER(type); \ 79 | static int parser_ ## type (llhttp_t *llhttp) \ 80 | { return parser_callback(&PyId_ ## type, llhttp); } 81 | 82 | #define PARSER_DATA_CALLBACK(type) \ 83 | _Py_IDENTIFIER(type); \ 84 | static int parser_ ## type (llhttp_t *llhttp, const char *data, size_t length) \ 85 | { return parser_data_callback(&PyId_ ## type, llhttp, data, length); } 86 | 87 | PARSER_CALLBACK(on_message_begin) 88 | PARSER_DATA_CALLBACK(on_url) 89 | PARSER_CALLBACK(on_url_complete) 90 | PARSER_DATA_CALLBACK(on_status) 91 | PARSER_CALLBACK(on_status_complete) 92 | PARSER_DATA_CALLBACK(on_header_field) 93 | PARSER_CALLBACK(on_header_field_complete) 94 | PARSER_DATA_CALLBACK(on_header_value) 95 | PARSER_CALLBACK(on_header_value_complete) 96 | PARSER_CALLBACK(on_headers_complete) 97 | PARSER_DATA_CALLBACK(on_body) 98 | PARSER_CALLBACK(on_message_complete) 99 | PARSER_CALLBACK(on_chunk_header) 100 | PARSER_CALLBACK(on_chunk_complete) 101 | 102 | llhttp_settings_t parser_settings = { 103 | .on_message_begin = parser_on_message_begin, 104 | .on_url = parser_on_url, 105 | .on_url_complete = parser_on_url_complete, 106 | .on_status = parser_on_status, 107 | .on_status_complete = parser_on_status_complete, 108 | .on_header_field = parser_on_header_field, 109 | .on_header_field_complete = parser_on_header_field_complete, 110 | .on_header_value = parser_on_header_value, 111 | .on_header_value_complete = parser_on_header_value_complete, 112 | .on_headers_complete = parser_on_headers_complete, 113 | .on_body = parser_on_body, 114 | .on_message_complete = parser_on_message_complete, 115 | .on_chunk_header = parser_on_chunk_header, 116 | .on_chunk_complete = parser_on_chunk_complete, 117 | }; 118 | 119 | static PyObject * 120 | request_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { 121 | PyObject *self = type->tp_alloc(type, 0); 122 | if (self) { 123 | llhttp_t *llhttp = &((parser_object*)self)->llhttp; 124 | llhttp_init(llhttp, HTTP_REQUEST, &parser_settings); 125 | llhttp->data = self; 126 | } 127 | return self; 128 | } 129 | 130 | static PyObject * 131 | response_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { 132 | PyObject *self = type->tp_alloc(type, 0); 133 | if (self) { 134 | llhttp_t *llhttp = &((parser_object*)self)->llhttp; 135 | llhttp_init(llhttp, HTTP_RESPONSE, &parser_settings); 136 | llhttp->data = self; 137 | } 138 | return self; 139 | } 140 | 141 | static PyObject * 142 | parser_execute(PyObject *self, PyObject *payload) { 143 | llhttp_t *llhttp = &((parser_object*)self)->llhttp; 144 | 145 | Py_buffer buffer; 146 | if (PyObject_GetBuffer(payload, &buffer, PyBUF_SIMPLE)) 147 | return NULL; 148 | 149 | if (!PyBuffer_IsContiguous(&buffer, 'C')) { 150 | PyErr_SetString(PyExc_TypeError, "buffer is not contiguous"); 151 | PyBuffer_Release(&buffer); 152 | return NULL; 153 | } 154 | 155 | llhttp_errno_t error = llhttp_execute(llhttp, buffer.buf, buffer.len); 156 | PyBuffer_Release(&buffer); 157 | 158 | if (PyErr_Occurred()) 159 | return NULL; 160 | 161 | switch (error) { 162 | case HPE_OK: 163 | return PyLong_FromUnsignedLong(buffer.len); 164 | 165 | case HPE_PAUSED: 166 | case HPE_PAUSED_UPGRADE: 167 | case HPE_PAUSED_H2_UPGRADE: 168 | return PyLong_FromUnsignedLong(llhttp->error_pos - (const char*)buffer.buf); 169 | 170 | default: 171 | PyErr_SetString(errors[error], llhttp_get_error_reason(llhttp)); 172 | return NULL; 173 | } 174 | } 175 | 176 | static PyObject * 177 | parser_pause(PyObject *self) { 178 | llhttp_t *llhttp = &((parser_object*)self)->llhttp; 179 | llhttp_pause(llhttp); 180 | Py_RETURN_NONE; 181 | } 182 | 183 | static PyObject * 184 | parser_unpause(PyObject *self) { 185 | llhttp_t *llhttp = &((parser_object*)self)->llhttp; 186 | llhttp_resume(llhttp); 187 | Py_RETURN_NONE; 188 | } 189 | 190 | static PyObject * 191 | parser_upgrade(PyObject *self) { 192 | llhttp_t *llhttp = &((parser_object*)self)->llhttp; 193 | llhttp_resume_after_upgrade(llhttp); 194 | Py_RETURN_NONE; 195 | } 196 | 197 | static PyObject * 198 | parser_finish(PyObject *self) { 199 | llhttp_t *llhttp = &((parser_object*)self)->llhttp; 200 | 201 | llhttp_errno_t error = llhttp_finish(llhttp); 202 | if (HPE_OK == error) 203 | Py_RETURN_NONE; 204 | 205 | PyErr_SetString(errors[error], llhttp_get_error_reason(llhttp)); 206 | return NULL; 207 | } 208 | 209 | static PyObject * 210 | parser_reset(PyObject *self) { 211 | llhttp_t *llhttp = &((parser_object*)self)->llhttp; 212 | llhttp_reset(llhttp); 213 | Py_RETURN_NONE; 214 | } 215 | 216 | static PyObject * parser_dummy_noargs(PyObject *self) { Py_RETURN_NONE; } 217 | static PyObject * parser_dummy_onearg(PyObject *self, PyObject *arg) { Py_RETURN_NONE; } 218 | 219 | static PyMethodDef parser_methods[] = { 220 | { "execute", (PyCFunction)parser_execute, METH_O }, 221 | { "pause", (PyCFunction)parser_pause, METH_NOARGS }, 222 | { "unpause", (PyCFunction)parser_unpause, METH_NOARGS }, 223 | { "upgrade", (PyCFunction)parser_upgrade, METH_NOARGS }, 224 | { "finish", (PyCFunction)parser_finish, METH_NOARGS }, 225 | { "reset", (PyCFunction)parser_reset, METH_NOARGS }, 226 | { "on_message_begin", (PyCFunction)parser_dummy_noargs, METH_NOARGS }, 227 | { "on_url", (PyCFunction)parser_dummy_onearg, METH_O }, 228 | { "on_url_complete", (PyCFunction)parser_dummy_noargs, METH_NOARGS }, 229 | { "on_status", (PyCFunction)parser_dummy_onearg, METH_O }, 230 | { "on_status_complete", (PyCFunction)parser_dummy_noargs, METH_NOARGS }, 231 | { "on_header_field", (PyCFunction)parser_dummy_onearg, METH_O }, 232 | { "on_header_field_complete", (PyCFunction)parser_dummy_noargs, METH_NOARGS }, 233 | { "on_header_value", (PyCFunction)parser_dummy_onearg, METH_O }, 234 | { "on_header_value_complete", (PyCFunction)parser_dummy_noargs, METH_NOARGS }, 235 | { "on_headers_complete", (PyCFunction)parser_dummy_noargs, METH_NOARGS }, 236 | { "on_body", (PyCFunction)parser_dummy_onearg, METH_O }, 237 | { "on_message_complete", (PyCFunction)parser_dummy_noargs, METH_NOARGS }, 238 | { "on_chunk_header", (PyCFunction)parser_dummy_noargs, METH_NOARGS }, 239 | { "on_chunk_complete", (PyCFunction)parser_dummy_noargs, METH_NOARGS }, 240 | { NULL } 241 | }; 242 | 243 | static PyObject * 244 | parser_method(PyObject *self, void *closure) { 245 | llhttp_t *llhttp = &((parser_object*)self)->llhttp; 246 | if (llhttp->type != HTTP_REQUEST) 247 | Py_RETURN_NONE; 248 | if (!llhttp->http_major && !llhttp->http_minor) 249 | Py_RETURN_NONE; 250 | 251 | PyObject * method = methods[llhttp->method]; 252 | Py_INCREF(method); 253 | return method; 254 | } 255 | 256 | static PyObject * 257 | parser_major(PyObject *self, void *closure) { 258 | llhttp_t *llhttp = &((parser_object*)self)->llhttp; 259 | if (!llhttp->http_major && !llhttp->http_minor) 260 | Py_RETURN_NONE; 261 | 262 | return PyLong_FromUnsignedLong(llhttp->http_major); 263 | } 264 | 265 | static PyObject * 266 | parser_minor(PyObject *self, void *closure) { 267 | llhttp_t *llhttp = &((parser_object*)self)->llhttp; 268 | if (!llhttp->http_major && !llhttp->http_minor) 269 | Py_RETURN_NONE; 270 | 271 | return PyLong_FromUnsignedLong(llhttp->http_minor); 272 | } 273 | 274 | static PyObject * 275 | parser_content_length(PyObject *self, void *closure) { 276 | llhttp_t *llhttp = &((parser_object*)self)->llhttp; 277 | if (!(llhttp->flags & F_CONTENT_LENGTH)) 278 | Py_RETURN_NONE; 279 | 280 | return PyLong_FromUnsignedLong(llhttp->content_length); 281 | } 282 | 283 | static bool 284 | get_lenient(const llhttp_t *llhttp, llhttp_lenient_flags_t flag) { 285 | return llhttp->lenient_flags & flag; 286 | } 287 | 288 | static int 289 | set_lenient(llhttp_t *llhttp, llhttp_lenient_flags_t flag, bool value) { 290 | if (value) { 291 | llhttp->lenient_flags |= flag; 292 | } else { 293 | llhttp->lenient_flags &= ~flag; 294 | } 295 | return 0; 296 | } 297 | 298 | #define LENIENT_FLAG(name) \ 299 | static PyObject * \ 300 | parser_get_lenient_ ## name(PyObject *self, void *closure) \ 301 | { return PyBool_FromLong(get_lenient(&((parser_object*)self)->llhttp, LENIENT_ ## name)); } \ 302 | \ 303 | static int \ 304 | parser_set_lenient_ ## name(PyObject *self, PyObject *value, void *closure) \ 305 | { return set_lenient(&((parser_object*)self)->llhttp, LENIENT_ ## name, PyObject_IsTrue(value)); } 306 | 307 | LENIENT_FLAG(HEADERS); 308 | LENIENT_FLAG(CHUNKED_LENGTH); 309 | LENIENT_FLAG(KEEP_ALIVE); 310 | LENIENT_FLAG(TRANSFER_ENCODING); 311 | LENIENT_FLAG(VERSION); 312 | 313 | static PyObject * 314 | parser_get_lenient_headers(PyObject *self, void *closure) { 315 | llhttp_t *llhttp = &((parser_object*)self)->llhttp; 316 | return PyBool_FromLong(llhttp->lenient_flags & LENIENT_HEADERS); 317 | } 318 | 319 | static int 320 | parser_set_lenient_headers(PyObject *self, PyObject *value, void *closure) { 321 | llhttp_t *llhttp = &((parser_object*)self)->llhttp; 322 | llhttp_set_lenient_headers(llhttp, PyObject_IsTrue(value)); 323 | return 0; 324 | } 325 | 326 | static PyObject * 327 | parser_get_lenient_chunked_length(PyObject *self, void *closure) { 328 | llhttp_t *llhttp = &((parser_object*)self)->llhttp; 329 | return PyBool_FromLong(llhttp->lenient_flags & LENIENT_CHUNKED_LENGTH); 330 | } 331 | 332 | static int 333 | parser_set_lenient_chunked_length(PyObject *self, PyObject *value, void *closure) { 334 | llhttp_t *llhttp = &((parser_object*)self)->llhttp; 335 | llhttp_set_lenient_chunked_length(llhttp, PyObject_IsTrue(value)); 336 | return 0; 337 | } 338 | 339 | static PyObject * 340 | parser_get_lenient_keep_alive(PyObject *self, void *closure) { 341 | llhttp_t *llhttp = &((parser_object*)self)->llhttp; 342 | return PyBool_FromLong(llhttp->lenient_flags & LENIENT_KEEP_ALIVE); 343 | } 344 | 345 | static int 346 | parser_set_lenient_keep_alive(PyObject *self, PyObject *value, void *closure) { 347 | llhttp_t *llhttp = &((parser_object*)self)->llhttp; 348 | llhttp_set_lenient_keep_alive(llhttp, PyObject_IsTrue(value)); 349 | return 0; 350 | } 351 | 352 | static PyObject * 353 | parser_message_needs_eof(PyObject *self, void *closure) { 354 | llhttp_t *llhttp = &((parser_object*)self)->llhttp; 355 | return PyBool_FromLong(llhttp_message_needs_eof(llhttp)); 356 | } 357 | 358 | static PyObject * 359 | parser_should_keep_alive(PyObject *self, void *closure) { 360 | llhttp_t *llhttp = &((parser_object*)self)->llhttp; 361 | return PyBool_FromLong(llhttp_should_keep_alive(llhttp)); 362 | } 363 | 364 | static PyObject * 365 | parser_is_paused(PyObject *self, void *closure) { 366 | llhttp_t *llhttp = &((parser_object*)self)->llhttp; 367 | return PyBool_FromLong(HPE_PAUSED == llhttp_get_errno(llhttp)); 368 | } 369 | 370 | static PyObject * 371 | parser_is_upgrading(PyObject *self, void *closure) { 372 | llhttp_t *llhttp = &((parser_object*)self)->llhttp; 373 | switch (llhttp_get_errno(llhttp)) { 374 | case HPE_PAUSED_UPGRADE: 375 | case HPE_PAUSED_H2_UPGRADE: 376 | Py_RETURN_TRUE; 377 | break; 378 | default: 379 | Py_RETURN_FALSE; 380 | break; 381 | } 382 | } 383 | 384 | static PyObject * 385 | parser_is_busted(PyObject *self, void *closure) { 386 | llhttp_t *llhttp = &((parser_object*)self)->llhttp; 387 | switch (llhttp_get_errno(llhttp)) { 388 | case HPE_OK: 389 | case HPE_PAUSED: 390 | case HPE_PAUSED_UPGRADE: 391 | Py_RETURN_FALSE; 392 | default: 393 | Py_RETURN_TRUE; 394 | } 395 | } 396 | 397 | static PyObject * 398 | parser_error(PyObject *self, void *closure) { 399 | llhttp_t *llhttp = &((parser_object*)self)->llhttp; 400 | if (HPE_OK == llhttp_get_errno(llhttp)) 401 | Py_RETURN_NONE; 402 | return PyUnicode_FromString(llhttp_get_error_reason(llhttp)); 403 | } 404 | 405 | static PyGetSetDef parser_getset[] = { 406 | { "method", parser_method }, 407 | { "major", parser_major }, 408 | { "minor", parser_minor }, 409 | { "content_length", parser_content_length }, 410 | { "lenient_headers", parser_get_lenient_HEADERS, parser_set_lenient_HEADERS }, 411 | { "lenient_chunked_length", parser_get_lenient_CHUNKED_LENGTH, parser_set_lenient_CHUNKED_LENGTH }, 412 | { "lenient_keep_alive", parser_get_lenient_KEEP_ALIVE, parser_set_lenient_KEEP_ALIVE }, 413 | { "lenient_transfer_encoding", parser_get_lenient_TRANSFER_ENCODING, parser_set_lenient_TRANSFER_ENCODING }, 414 | { "lenient_version", parser_get_lenient_VERSION, parser_set_lenient_VERSION }, 415 | { "message_needs_eof", parser_message_needs_eof }, 416 | { "should_keep_alive", parser_should_keep_alive }, 417 | { "is_paused", parser_is_paused }, 418 | { "is_upgrading", parser_is_upgrading }, 419 | { "is_busted", parser_is_busted }, 420 | { "error", parser_error }, 421 | { NULL } 422 | }; 423 | 424 | static void 425 | parser_dealloc(PyObject *self) { 426 | Py_TYPE(self)->tp_free((PyObject*)self); 427 | } 428 | 429 | static PyType_Slot request_slots[] = { 430 | {Py_tp_doc, "llhttp request parser"}, 431 | {Py_tp_new, request_new}, 432 | {Py_tp_dealloc, parser_dealloc}, 433 | {Py_tp_methods, parser_methods}, 434 | {Py_tp_getset, parser_getset}, 435 | {0, 0}, 436 | }; 437 | 438 | static PyType_Spec request_spec = { 439 | "llhttp.Request", 440 | sizeof(parser_object), 441 | 0, 442 | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, 443 | request_slots, 444 | }; 445 | 446 | static PyType_Slot response_slots[] = { 447 | {Py_tp_doc, "llhttp response parser"}, 448 | {Py_tp_new, response_new}, 449 | {Py_tp_dealloc, parser_dealloc}, 450 | {Py_tp_methods, parser_methods}, 451 | {Py_tp_getset, parser_getset}, 452 | {0, 0}, 453 | }; 454 | 455 | static PyType_Spec response_spec = { 456 | "llhttp.Response", 457 | sizeof(parser_object), 458 | 0, 459 | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, 460 | response_slots, 461 | }; 462 | 463 | static struct PyModuleDef llhttp_module = { 464 | PyModuleDef_HEAD_INIT, 465 | .m_name = "llhttp", 466 | .m_doc = "llhttp wrapper", 467 | .m_size = -1, 468 | }; 469 | 470 | static char * 471 | snake_to_camel(char * string) { 472 | bool upper = true; 473 | char * camel = string; 474 | for (const char * snake = string ; *snake ; ++snake) { 475 | if (isalpha(*snake)) { 476 | *camel++ = upper ? toupper(*snake) : tolower(*snake); 477 | } else if (isdigit(*snake)) { 478 | *camel++ = *snake; 479 | } 480 | upper = !isalpha(*snake); 481 | } 482 | *camel = '\0'; 483 | return string; 484 | } 485 | 486 | PyMODINIT_FUNC 487 | PyInit___llhttp(void) { 488 | PyObject *m = PyModule_Create(&llhttp_module); 489 | if (!m) 490 | return NULL; 491 | 492 | if (PyModule_AddStringConstant(m, "version", LLHTTP_VERSION)) 493 | goto fail; 494 | 495 | if ((base_error = PyErr_NewException("llhttp.Error", NULL, NULL))) { 496 | Py_INCREF(base_error); 497 | PyModule_AddObject(m, "Error", base_error); 498 | 499 | #define HTTP_ERRNO_GEN(CODE, NAME, _) \ 500 | if (CODE != HPE_OK && CODE != HPE_PAUSED && CODE != HPE_PAUSED_UPGRADE) { \ 501 | char long_name[] = "llhttp." #NAME "_Error"; \ 502 | char *short_name = snake_to_camel(long_name + strlen("llhttp.")); \ 503 | if ((errors[CODE] = PyErr_NewException(long_name, base_error, NULL))) { \ 504 | Py_INCREF(errors[CODE]); \ 505 | PyModule_AddObject(m, short_name, errors[CODE]); \ 506 | } \ 507 | } 508 | HTTP_ERRNO_MAP(HTTP_ERRNO_GEN) 509 | #undef HTTP_ERRNO_GEN 510 | 511 | } 512 | 513 | #define HTTP_METHOD_GEN(NUMBER, NAME, STRING) \ 514 | methods[HTTP_ ## NAME] = PyUnicode_FromStringAndSize(#STRING, strlen(#STRING)); 515 | HTTP_METHOD_MAP(HTTP_METHOD_GEN) 516 | #undef HTTP_METHOD_GEN 517 | 518 | PyObject *request_type = PyType_FromSpec(&request_spec); 519 | if (!request_type) 520 | goto fail; 521 | 522 | if (PyModule_AddObject(m, request_spec.name + strlen("llhttp."), request_type)) { 523 | Py_DECREF(request_type); 524 | goto fail; 525 | } 526 | 527 | PyObject *response_type = PyType_FromSpec(&response_spec); 528 | if (!response_type) 529 | goto fail; 530 | 531 | if (PyModule_AddObject(m, response_spec.name + strlen("llhttp."), response_type)) { 532 | Py_DECREF(response_type); 533 | goto fail; 534 | } 535 | 536 | return m; 537 | 538 | fail: 539 | Py_DECREF(m); 540 | return NULL; 541 | } 542 | 543 | // 544 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, Extension 2 | 3 | from os import path 4 | this_directory = path.abspath(path.dirname(__file__)) 5 | with open(path.join(this_directory, 'README.md'), encoding='utf-8') as f: 6 | long_description = f.read() 7 | 8 | setup( 9 | name = 'llhttp', 10 | version = '6.0.9.0', 11 | description = ("llhttp in python"), 12 | url = "http://github.com/pallas/pyllhttp", 13 | author = "Derrick Lyndon Pallas", 14 | author_email = "derrick@pallas.us", 15 | license = "MIT", 16 | long_description = long_description, 17 | long_description_content_type = "text/markdown", 18 | keywords = "www http parser", 19 | classifiers = [ 20 | "Development Status :: 4 - Beta", 21 | "Programming Language :: Python :: 3 :: Only", 22 | "Programming Language :: JavaScript", 23 | "Topic :: Software Development :: Libraries :: Python Modules", 24 | "Topic :: System :: Networking", 25 | "Topic :: Internet :: WWW/HTTP :: HTTP Servers", 26 | "License :: OSI Approved :: MIT License", 27 | ], 28 | packages = [ "llhttp" ], 29 | headers = [ "lib/llhttp.h" ], 30 | ext_modules = [ Extension('__llhttp', 31 | sources = """ 32 | pyllhttp.c 33 | lib/llhttp.c 34 | lib/http.c 35 | lib/api.c 36 | """.split(), 37 | language = "c", 38 | ) ], 39 | ) 40 | # --------------------------------------------------------------------------------