Unverified Commit 459df42b authored by Tatsuhiro Tsujikawa's avatar Tatsuhiro Tsujikawa Committed by GitHub

Merge pull request #1442 from nghttp2/upgrade-llhttp

Bump llhttp to 2.0.4
parents 5e13274b a4c1fed5
......@@ -14,6 +14,8 @@ This project aims to:
* Verifiable
* Improving benchmarks where possible
More details in [Fedor Indutny's talk at JSConf EU 2019](https://youtu.be/x3k_5Mi66sY)
## How?
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 |
|:----------------|-----------:|-------------:|-----------:|--------:|
| **llhttp** _(C)_ | 8192.00 mb | 1497.88 mb/s | 3020458.87 ops/sec | 5.47 s |
| **llhttp** _(bitcode)_ | 8192.00 mb | 1131.75 mb/s | 2282171.24 ops/sec | 7.24 s |
| **llhttp** _(C)_ | 8192.00 mb | 1777.24 mb/s | 3583799.39 ops/sec | 4.61 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
......@@ -77,8 +78,6 @@ settings.on_message_complete = handle_on_message_complete;
*/
llhttp_init(&parser, HTTP_BOTH, &settings);
/* Use `llhttp_set_type(&parser, HTTP_REQUEST);` to override the mode */
/* Parse request! */
const char* request = "GET / HTTP/1.1\r\n\r\n";
int request_len = strlen(request);
......
#ifndef INCLUDE_LLHTTP_H_
#define INCLUDE_LLHTTP_H_
#define LLHTTP_VERSION_MAJOR 1
#define LLHTTP_VERSION_MINOR 1
#define LLHTTP_VERSION_PATCH 3
#define LLHTTP_VERSION_MAJOR 2
#define LLHTTP_VERSION_MINOR 0
#define LLHTTP_VERSION_PATCH 4
#ifndef INCLUDE_LLHTTP_ITSELF_H_
#define INCLUDE_LLHTTP_ITSELF_H_
......@@ -29,7 +29,7 @@ struct llhttp__internal_s {
uint8_t http_major;
uint8_t http_minor;
uint8_t header_state;
uint8_t flags;
uint16_t flags;
uint8_t upgrade;
uint16_t status_code;
uint8_t finish;
......@@ -66,14 +66,15 @@ enum llhttp_errno {
HPE_INVALID_CHUNK_SIZE = 12,
HPE_INVALID_STATUS = 13,
HPE_INVALID_EOF_STATE = 14,
HPE_CB_MESSAGE_BEGIN = 15,
HPE_CB_HEADERS_COMPLETE = 16,
HPE_CB_MESSAGE_COMPLETE = 17,
HPE_CB_CHUNK_HEADER = 18,
HPE_CB_CHUNK_COMPLETE = 19,
HPE_PAUSED = 20,
HPE_PAUSED_UPGRADE = 21,
HPE_USER = 22
HPE_INVALID_TRANSFER_ENCODING = 15,
HPE_CB_MESSAGE_BEGIN = 16,
HPE_CB_HEADERS_COMPLETE = 17,
HPE_CB_MESSAGE_COMPLETE = 18,
HPE_CB_CHUNK_HEADER = 19,
HPE_CB_CHUNK_COMPLETE = 20,
HPE_PAUSED = 21,
HPE_PAUSED_UPGRADE = 22,
HPE_USER = 23
};
typedef enum llhttp_errno llhttp_errno_t;
......@@ -85,7 +86,9 @@ enum llhttp_flags {
F_UPGRADE = 0x10,
F_CONTENT_LENGTH = 0x20,
F_SKIPBODY = 0x40,
F_TRAILING = 0x80
F_TRAILING = 0x80,
F_LENIENT = 0x100,
F_TRANSFER_ENCODING = 0x200
};
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(13, INVALID_STATUS, INVALID_STATUS) \
XX(14, INVALID_EOF_STATE, INVALID_EOF_STATE) \
XX(15, CB_MESSAGE_BEGIN, CB_MESSAGE_BEGIN) \
XX(16, CB_HEADERS_COMPLETE, CB_HEADERS_COMPLETE) \
XX(17, CB_MESSAGE_COMPLETE, CB_MESSAGE_COMPLETE) \
XX(18, CB_CHUNK_HEADER, CB_CHUNK_HEADER) \
XX(19, CB_CHUNK_COMPLETE, CB_CHUNK_COMPLETE) \
XX(20, PAUSED, PAUSED) \
XX(21, PAUSED_UPGRADE, PAUSED_UPGRADE) \
XX(22, USER, USER) \
XX(15, INVALID_TRANSFER_ENCODING, INVALID_TRANSFER_ENCODING) \
XX(16, CB_MESSAGE_BEGIN, CB_MESSAGE_BEGIN) \
XX(17, CB_HEADERS_COMPLETE, CB_HEADERS_COMPLETE) \
XX(18, CB_MESSAGE_COMPLETE, CB_MESSAGE_COMPLETE) \
XX(19, CB_CHUNK_HEADER, CB_CHUNK_HEADER) \
XX(20, CB_CHUNK_COMPLETE, CB_CHUNK_COMPLETE) \
XX(21, PAUSED, PAUSED) \
XX(22, PAUSED_UPGRADE, PAUSED_UPGRADE) \
XX(23, USER, USER) \
#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);
/* 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);
......@@ -353,6 +357,18 @@ const char* llhttp_errno_name(llhttp_errno_t err);
/* Returns textual name of HTTP 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
} /* extern "C" */
#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 */
......
......@@ -32,6 +32,7 @@ int llhttp__before_headers_complete(llhttp_t* parser, const char* p,
* 2 - chunk_size_start
* 3 - body_identity
* 4 - body_identity_eof
* 5 - invalid transfer-encoding for request
*/
int llhttp__after_headers_complete(llhttp_t* parser, const char* p,
const char* endp) {
......@@ -47,8 +48,29 @@ int llhttp__after_headers_complete(llhttp_t* parser, const char* p,
if (parser->flags & F_SKIPBODY) {
return 0;
} else if (parser->flags & F_CHUNKED) {
/* chunked encoding - ignore Content-Length header */
/* chunked encoding - ignore Content-Length header, prepare for a chunk */
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 {
if (!(parser->flags & F_CONTENT_LENGTH)) {
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;
should_keep_alive = llhttp_should_keep_alive(parser);
parser->flags = 0;
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 */
return should_keep_alive;
}
......@@ -95,6 +119,12 @@ int llhttp_message_needs_eof(const llhttp_t* parser) {
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)) {
return 0;
}
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment