|
| 1 | +# llhttp |
| 2 | +[](http://travis-ci.org/indutny/llhttp) |
| 3 | + |
| 4 | +Port of [http_parser][0] to [llparse][1]. |
| 5 | + |
| 6 | +## Why? |
| 7 | + |
| 8 | +Let's face it, [http_parser][0] is practically unmaintainable. Even |
| 9 | +introduction of a single new method results in a significant code churn. |
| 10 | + |
| 11 | +This project aims to: |
| 12 | + |
| 13 | +* Make it maintainable |
| 14 | +* Verifiable |
| 15 | +* Improving benchmarks where possible |
| 16 | + |
| 17 | +## How? |
| 18 | + |
| 19 | +Over time, different approaches for improving [http_parser][0]'s code base |
| 20 | +were tried. However, all of them failed due to resulting significant performance |
| 21 | +degradation. |
| 22 | + |
| 23 | +This project is a port of [http_parser][0] to TypeScript. [llparse][1] is used |
| 24 | +to generate the output C and/or bitcode artifacts, which could be compiled and |
| 25 | +linked with the embedder's program (like [Node.js][7]). |
| 26 | + |
| 27 | +## Peformance |
| 28 | + |
| 29 | +So far llhttp outperforms http_parser: |
| 30 | + |
| 31 | +| | input size | bandwidth | reqs/sec | time | |
| 32 | +|:----------------|-----------:|-------------:|-----------:|--------:| |
| 33 | +| **llhttp** _(C)_ | 8192.00 mb | 1497.88 mb/s | 3020458.87 ops/sec | 5.47 s | |
| 34 | +| **llhttp** _(bitcode)_ | 8192.00 mb | 1131.75 mb/s | 2282171.24 ops/sec | 7.24 s | |
| 35 | +| **http_parser** | 8192.00 mb | 694.66 mb/s | 1406180.33 req/sec | 11.79 s | |
| 36 | + |
| 37 | +llhttp is faster by approximately **116%**. |
| 38 | + |
| 39 | +## Maintenance |
| 40 | + |
| 41 | +llhttp project has about 1400 lines of TypeScript code describing the parser |
| 42 | +itself and around 450 lines of C code and headers providing the helper methods. |
| 43 | +The whole [http_parser][0] is implemented in approximately 2500 lines of C, and |
| 44 | +436 lines of headers. |
| 45 | + |
| 46 | +All optimizations and multi-character matching in llhttp are generated |
| 47 | +automatically, and thus doesn't add any extra maintenance cost. On the contrary, |
| 48 | +most of http_parser's code is hand-optimized and unrolled. Instead describing |
| 49 | +"how" it should parse the HTTP requests/responses, a maintainer should |
| 50 | +implement the new features in [http_parser][0] cautiously, considering |
| 51 | +possible performance degradation and manually optimizing the new code. |
| 52 | + |
| 53 | +## Verification |
| 54 | + |
| 55 | +The state machine graph is encoded explicitly in llhttp. The [llparse][1] |
| 56 | +automatically checks the graph for absence of loops and correct reporting of the |
| 57 | +input ranges (spans) like header names and values. In the future, additional |
| 58 | +checks could be performed to get even stricter verification of the llhttp. |
| 59 | + |
| 60 | +## Usage |
| 61 | + |
| 62 | +```C |
| 63 | +#include "llhttp.h" |
| 64 | + |
| 65 | +llhttp_t parser; |
| 66 | +llhttp_settings_t settings; |
| 67 | + |
| 68 | +/* Initialize user callbacks and settings */ |
| 69 | +llhttp_settings_init(&settings); |
| 70 | + |
| 71 | +/* Set user callback */ |
| 72 | +settings.on_message_complete = handle_on_message_complete; |
| 73 | + |
| 74 | +/* Initialize the parser in HTTP_BOTH mode, meaning that it will select between |
| 75 | + * HTTP_REQUEST and HTTP_RESPONSE parsing automatically while reading the first |
| 76 | + * input. |
| 77 | + */ |
| 78 | +llhttp_init(&parser, HTTP_BOTH, &settings); |
| 79 | + |
| 80 | +/* Use `llhttp_set_type(&parser, HTTP_REQUEST);` to override the mode */ |
| 81 | + |
| 82 | +/* Parse request! */ |
| 83 | +const char* request = "GET / HTTP/1.1\r\n\r\n"; |
| 84 | +int request_len = strlen(request); |
| 85 | + |
| 86 | +enum llhttp_errno err = llhttp_execute(&parser, request, request_len); |
| 87 | +if (err == HPE_OK) { |
| 88 | + /* Successfully parsed! */ |
| 89 | +} else { |
| 90 | + fprintf(stderr, "Parse error: %s %s\n", llhttp_errno_name(err), |
| 91 | + parser.reason); |
| 92 | +} |
| 93 | +``` |
| 94 | +
|
| 95 | +--- |
| 96 | +
|
| 97 | +#### LICENSE |
| 98 | +
|
| 99 | +This software is licensed under the MIT License. |
| 100 | +
|
| 101 | +Copyright Fedor Indutny, 2018. |
| 102 | +
|
| 103 | +Permission is hereby granted, free of charge, to any person obtaining a |
| 104 | +copy of this software and associated documentation files (the |
| 105 | +"Software"), to deal in the Software without restriction, including |
| 106 | +without limitation the rights to use, copy, modify, merge, publish, |
| 107 | +distribute, sublicense, and/or sell copies of the Software, and to permit |
| 108 | +persons to whom the Software is furnished to do so, subject to the |
| 109 | +following conditions: |
| 110 | +
|
| 111 | +The above copyright notice and this permission notice shall be included |
| 112 | +in all copies or substantial portions of the Software. |
| 113 | +
|
| 114 | +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
| 115 | +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| 116 | +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN |
| 117 | +NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, |
| 118 | +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
| 119 | +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE |
| 120 | +USE OR OTHER DEALINGS IN THE SOFTWARE. |
| 121 | +
|
| 122 | +[0]: https://github.com/nodejs/http-parser |
| 123 | +[1]: https://github.com/indutny/llparse |
| 124 | +[2]: https://en.wikipedia.org/wiki/Register_allocation#Spilling |
| 125 | +[3]: https://en.wikipedia.org/wiki/Tail_call |
| 126 | +[4]: https://llvm.org/docs/LangRef.html |
| 127 | +[5]: https://llvm.org/docs/LangRef.html#call-instruction |
| 128 | +[6]: https://clang.llvm.org/ |
| 129 | +[7]: https://github.com/nodejs/node |
0 commit comments