Merge pull request #1442 from nghttp2/upgrade-llhttp
Bump llhttp to 2.0.4
This commit is contained in:
commit
459df42b8b
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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
Loading…
Reference in New Issue