Merge pull request #1442 from nghttp2/upgrade-llhttp

Bump llhttp to 2.0.4
This commit is contained in:
Tatsuhiro Tsujikawa 2020-02-11 23:26:41 +09:00 committed by GitHub
commit 459df42b8b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 786 additions and 395 deletions

View File

@ -14,6 +14,8 @@ This project aims to:
* Verifiable * Verifiable
* Improving benchmarks where possible * Improving benchmarks where possible
More details in [Fedor Indutny's talk at JSConf EU 2019](https://youtu.be/x3k_5Mi66sY)
## How? ## How?
Over time, different approaches for improving [http_parser][0]'s code base Over time, different approaches for improving [http_parser][0]'s code base
@ -30,11 +32,10 @@ So far llhttp outperforms http_parser:
| | input size | bandwidth | reqs/sec | time | | | input size | bandwidth | reqs/sec | time |
|:----------------|-----------:|-------------:|-----------:|--------:| |:----------------|-----------:|-------------:|-----------:|--------:|
| **llhttp** _(C)_ | 8192.00 mb | 1497.88 mb/s | 3020458.87 ops/sec | 5.47 s | | **llhttp** _(C)_ | 8192.00 mb | 1777.24 mb/s | 3583799.39 ops/sec | 4.61 s |
| **llhttp** _(bitcode)_ | 8192.00 mb | 1131.75 mb/s | 2282171.24 ops/sec | 7.24 s |
| **http_parser** | 8192.00 mb | 694.66 mb/s | 1406180.33 req/sec | 11.79 s | | **http_parser** | 8192.00 mb | 694.66 mb/s | 1406180.33 req/sec | 11.79 s |
llhttp is faster by approximately **116%**. llhttp is faster by approximately **156%**.
## Maintenance ## Maintenance
@ -77,8 +78,6 @@ settings.on_message_complete = handle_on_message_complete;
*/ */
llhttp_init(&parser, HTTP_BOTH, &settings); llhttp_init(&parser, HTTP_BOTH, &settings);
/* Use `llhttp_set_type(&parser, HTTP_REQUEST);` to override the mode */
/* Parse request! */ /* Parse request! */
const char* request = "GET / HTTP/1.1\r\n\r\n"; const char* request = "GET / HTTP/1.1\r\n\r\n";
int request_len = strlen(request); int request_len = strlen(request);

View File

@ -1,9 +1,9 @@
#ifndef INCLUDE_LLHTTP_H_ #ifndef INCLUDE_LLHTTP_H_
#define INCLUDE_LLHTTP_H_ #define INCLUDE_LLHTTP_H_
#define LLHTTP_VERSION_MAJOR 1 #define LLHTTP_VERSION_MAJOR 2
#define LLHTTP_VERSION_MINOR 1 #define LLHTTP_VERSION_MINOR 0
#define LLHTTP_VERSION_PATCH 3 #define LLHTTP_VERSION_PATCH 4
#ifndef INCLUDE_LLHTTP_ITSELF_H_ #ifndef INCLUDE_LLHTTP_ITSELF_H_
#define INCLUDE_LLHTTP_ITSELF_H_ #define INCLUDE_LLHTTP_ITSELF_H_
@ -29,7 +29,7 @@ struct llhttp__internal_s {
uint8_t http_major; uint8_t http_major;
uint8_t http_minor; uint8_t http_minor;
uint8_t header_state; uint8_t header_state;
uint8_t flags; uint16_t flags;
uint8_t upgrade; uint8_t upgrade;
uint16_t status_code; uint16_t status_code;
uint8_t finish; uint8_t finish;
@ -66,14 +66,15 @@ enum llhttp_errno {
HPE_INVALID_CHUNK_SIZE = 12, HPE_INVALID_CHUNK_SIZE = 12,
HPE_INVALID_STATUS = 13, HPE_INVALID_STATUS = 13,
HPE_INVALID_EOF_STATE = 14, HPE_INVALID_EOF_STATE = 14,
HPE_CB_MESSAGE_BEGIN = 15, HPE_INVALID_TRANSFER_ENCODING = 15,
HPE_CB_HEADERS_COMPLETE = 16, HPE_CB_MESSAGE_BEGIN = 16,
HPE_CB_MESSAGE_COMPLETE = 17, HPE_CB_HEADERS_COMPLETE = 17,
HPE_CB_CHUNK_HEADER = 18, HPE_CB_MESSAGE_COMPLETE = 18,
HPE_CB_CHUNK_COMPLETE = 19, HPE_CB_CHUNK_HEADER = 19,
HPE_PAUSED = 20, HPE_CB_CHUNK_COMPLETE = 20,
HPE_PAUSED_UPGRADE = 21, HPE_PAUSED = 21,
HPE_USER = 22 HPE_PAUSED_UPGRADE = 22,
HPE_USER = 23
}; };
typedef enum llhttp_errno llhttp_errno_t; typedef enum llhttp_errno llhttp_errno_t;
@ -85,7 +86,9 @@ enum llhttp_flags {
F_UPGRADE = 0x10, F_UPGRADE = 0x10,
F_CONTENT_LENGTH = 0x20, F_CONTENT_LENGTH = 0x20,
F_SKIPBODY = 0x40, F_SKIPBODY = 0x40,
F_TRAILING = 0x80 F_TRAILING = 0x80,
F_LENIENT = 0x100,
F_TRANSFER_ENCODING = 0x200
}; };
typedef enum llhttp_flags llhttp_flags_t; typedef enum llhttp_flags llhttp_flags_t;
@ -157,14 +160,15 @@ typedef enum llhttp_method llhttp_method_t;
XX(12, INVALID_CHUNK_SIZE, INVALID_CHUNK_SIZE) \ XX(12, INVALID_CHUNK_SIZE, INVALID_CHUNK_SIZE) \
XX(13, INVALID_STATUS, INVALID_STATUS) \ XX(13, INVALID_STATUS, INVALID_STATUS) \
XX(14, INVALID_EOF_STATE, INVALID_EOF_STATE) \ XX(14, INVALID_EOF_STATE, INVALID_EOF_STATE) \
XX(15, CB_MESSAGE_BEGIN, CB_MESSAGE_BEGIN) \ XX(15, INVALID_TRANSFER_ENCODING, INVALID_TRANSFER_ENCODING) \
XX(16, CB_HEADERS_COMPLETE, CB_HEADERS_COMPLETE) \ XX(16, CB_MESSAGE_BEGIN, CB_MESSAGE_BEGIN) \
XX(17, CB_MESSAGE_COMPLETE, CB_MESSAGE_COMPLETE) \ XX(17, CB_HEADERS_COMPLETE, CB_HEADERS_COMPLETE) \
XX(18, CB_CHUNK_HEADER, CB_CHUNK_HEADER) \ XX(18, CB_MESSAGE_COMPLETE, CB_MESSAGE_COMPLETE) \
XX(19, CB_CHUNK_COMPLETE, CB_CHUNK_COMPLETE) \ XX(19, CB_CHUNK_HEADER, CB_CHUNK_HEADER) \
XX(20, PAUSED, PAUSED) \ XX(20, CB_CHUNK_COMPLETE, CB_CHUNK_COMPLETE) \
XX(21, PAUSED_UPGRADE, PAUSED_UPGRADE) \ XX(21, PAUSED, PAUSED) \
XX(22, USER, USER) \ XX(22, PAUSED_UPGRADE, PAUSED_UPGRADE) \
XX(23, USER, USER) \
#define HTTP_METHOD_MAP(XX) \ #define HTTP_METHOD_MAP(XX) \
@ -297,7 +301,7 @@ llhttp_errno_t llhttp_finish(llhttp_t* parser);
int llhttp_message_needs_eof(const llhttp_t* parser); int llhttp_message_needs_eof(const llhttp_t* parser);
/* Returns `1` if there might be any other messages following the last that was /* Returns `1` if there might be any other messages following the last that was
* successfuly parsed. * successfully parsed.
*/ */
int llhttp_should_keep_alive(const llhttp_t* parser); int llhttp_should_keep_alive(const llhttp_t* parser);
@ -353,6 +357,18 @@ const char* llhttp_errno_name(llhttp_errno_t err);
/* Returns textual name of HTTP method */ /* Returns textual name of HTTP method */
const char* llhttp_method_name(llhttp_method_t method); const char* llhttp_method_name(llhttp_method_t method);
/* Enables/disables lenient header value parsing (disabled by default).
*
* Lenient parsing disables header value token checks, extending llhttp's
* protocol support to highly non-compliant clients/server. No
* `HPE_INVALID_HEADER_TOKEN` will be raised for incorrect header values when
* lenient parsing is "on".
*
* **(USE AT YOUR OWN RISK)**
*/
void llhttp_set_lenient(llhttp_t* parser, int enabled);
#ifdef __cplusplus #ifdef __cplusplus
} /* extern "C" */ } /* extern "C" */
#endif #endif

View File

@ -127,6 +127,15 @@ const char* llhttp_method_name(llhttp_method_t method) {
} }
void llhttp_set_lenient(llhttp_t* parser, int enabled) {
if (enabled) {
parser->flags |= F_LENIENT;
} else {
parser->flags &= ~F_LENIENT;
}
}
/* Callbacks */ /* Callbacks */

View File

@ -32,6 +32,7 @@ int llhttp__before_headers_complete(llhttp_t* parser, const char* p,
* 2 - chunk_size_start * 2 - chunk_size_start
* 3 - body_identity * 3 - body_identity
* 4 - body_identity_eof * 4 - body_identity_eof
* 5 - invalid transfer-encoding for request
*/ */
int llhttp__after_headers_complete(llhttp_t* parser, const char* p, int llhttp__after_headers_complete(llhttp_t* parser, const char* p,
const char* endp) { const char* endp) {
@ -47,8 +48,29 @@ int llhttp__after_headers_complete(llhttp_t* parser, const char* p,
if (parser->flags & F_SKIPBODY) { if (parser->flags & F_SKIPBODY) {
return 0; return 0;
} else if (parser->flags & F_CHUNKED) { } else if (parser->flags & F_CHUNKED) {
/* chunked encoding - ignore Content-Length header */ /* chunked encoding - ignore Content-Length header, prepare for a chunk */
return 2; return 2;
} else if (parser->flags & F_TRANSFER_ENCODING) {
if (parser->type == HTTP_REQUEST && (parser->flags & F_LENIENT) == 0) {
/* RFC 7230 3.3.3 */
/* If a Transfer-Encoding header field
* is present in a request and the chunked transfer coding is not
* the final encoding, the message body length cannot be determined
* reliably; the server MUST respond with the 400 (Bad Request)
* status code and then close the connection.
*/
return 5;
} else {
/* RFC 7230 3.3.3 */
/* If a Transfer-Encoding header field is present in a response and
* the chunked transfer coding is not the final encoding, the
* message body length is determined by reading the connection until
* it is closed by the server.
*/
return 4;
}
} else { } else {
if (!(parser->flags & F_CONTENT_LENGTH)) { if (!(parser->flags & F_CONTENT_LENGTH)) {
if (!llhttp_message_needs_eof(parser)) { if (!llhttp_message_needs_eof(parser)) {
@ -74,9 +96,11 @@ int llhttp__after_message_complete(llhttp_t* parser, const char* p,
int should_keep_alive; int should_keep_alive;
should_keep_alive = llhttp_should_keep_alive(parser); should_keep_alive = llhttp_should_keep_alive(parser);
parser->flags = 0;
parser->finish = HTTP_FINISH_SAFE; parser->finish = HTTP_FINISH_SAFE;
/* Keep `F_LENIENT` flag between messages, but reset every other flag */
parser->flags &= F_LENIENT;
/* NOTE: this is ignored in loose parsing mode */ /* NOTE: this is ignored in loose parsing mode */
return should_keep_alive; return should_keep_alive;
} }
@ -95,6 +119,12 @@ int llhttp_message_needs_eof(const llhttp_t* parser) {
return 0; return 0;
} }
/* RFC 7230 3.3.3, see `llhttp__after_headers_complete` */
if ((parser->flags & F_TRANSFER_ENCODING) &&
(parser->flags & F_CHUNKED) == 0) {
return 1;
}
if (parser->flags & (F_CHUNKED | F_CONTENT_LENGTH)) { if (parser->flags & (F_CHUNKED | F_CONTENT_LENGTH)) {
return 0; return 0;
} }

File diff suppressed because it is too large Load Diff