deps: introduce llhttp
llhttp is modern, written in human-readable TypeScript, verifiable, and is very easy to maintain. See: https://github.com/indutny/llhttp PR-URL: https://github.com/nodejs/node/pull/24059 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Refael Ackermann <refack@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Rod Vagg <rod@vagg.org> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Gus Caplan <me@gus.host> Reviewed-By: Ujjwal Sharma <usharma1998@gmail.com> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
This commit is contained in:
parent
d3f02d0da3
commit
d4654d89be
26
LICENSE
26
LICENSE
@ -606,6 +606,32 @@ The externally maintained libraries used by Node.js are:
|
||||
n° 289016). Three clause BSD license.
|
||||
"""
|
||||
|
||||
- llhttp, located at deps/llhttp, is licensed as follows:
|
||||
"""
|
||||
This software is licensed under the MIT License.
|
||||
|
||||
Copyright Fedor Indutny, 2018.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
persons to whom the Software is furnished to do so, subject to the
|
||||
following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
"""
|
||||
|
||||
- OpenSSL, located at deps/openssl, is licensed as follows:
|
||||
"""
|
||||
Copyright (c) 1998-2018 The OpenSSL Project. All rights reserved.
|
||||
|
@ -182,6 +182,11 @@ parser.add_option('--openssl-system-ca-path',
|
||||
help='Use the specified path to system CA (PEM format) in addition to '
|
||||
'the OpenSSL supplied CA store or compiled-in Mozilla CA copy.')
|
||||
|
||||
parser.add_option('--experimental-http-parser',
|
||||
action='store_true',
|
||||
dest='experimental_http_parser',
|
||||
help='use llhttp instead of http_parser')
|
||||
|
||||
shared_optgroup.add_option('--shared-http-parser',
|
||||
action='store_true',
|
||||
dest='shared_http_parser',
|
||||
@ -1106,6 +1111,9 @@ def configure_node(o):
|
||||
else:
|
||||
o['variables']['node_target_type'] = 'executable'
|
||||
|
||||
o['variables']['node_experimental_http_parser'] = \
|
||||
b(options.experimental_http_parser)
|
||||
|
||||
def configure_library(lib, output):
|
||||
shared_lib = 'shared_' + lib
|
||||
output['variables']['node_' + shared_lib] = b(getattr(options, shared_lib))
|
||||
|
22
deps/llhttp/LICENSE-MIT
vendored
Normal file
22
deps/llhttp/LICENSE-MIT
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
This software is licensed under the MIT License.
|
||||
|
||||
Copyright Fedor Indutny, 2018.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
persons to whom the Software is furnished to do so, subject to the
|
||||
following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
129
deps/llhttp/README.md
vendored
Normal file
129
deps/llhttp/README.md
vendored
Normal file
@ -0,0 +1,129 @@
|
||||
# llhttp
|
||||
[](http://travis-ci.org/indutny/llhttp)
|
||||
|
||||
Port of [http_parser][0] to [llparse][1].
|
||||
|
||||
## Why?
|
||||
|
||||
Let's face it, [http_parser][0] is practically unmaintainable. Even
|
||||
introduction of a single new method results in a significant code churn.
|
||||
|
||||
This project aims to:
|
||||
|
||||
* Make it maintainable
|
||||
* Verifiable
|
||||
* Improving benchmarks where possible
|
||||
|
||||
## How?
|
||||
|
||||
Over time, different approaches for improving [http_parser][0]'s code base
|
||||
were tried. However, all of them failed due to resulting significant performance
|
||||
degradation.
|
||||
|
||||
This project is a port of [http_parser][0] to TypeScript. [llparse][1] is used
|
||||
to generate the output C and/or bitcode artifacts, which could be compiled and
|
||||
linked with the embedder's program (like [Node.js][7]).
|
||||
|
||||
## Peformance
|
||||
|
||||
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 |
|
||||
| **http_parser** | 8192.00 mb | 694.66 mb/s | 1406180.33 req/sec | 11.79 s |
|
||||
|
||||
llhttp is faster by approximately **116%**.
|
||||
|
||||
## Maintenance
|
||||
|
||||
llhttp project has about 1400 lines of TypeScript code describing the parser
|
||||
itself and around 450 lines of C code and headers providing the helper methods.
|
||||
The whole [http_parser][0] is implemented in approximately 2500 lines of C, and
|
||||
436 lines of headers.
|
||||
|
||||
All optimizations and multi-character matching in llhttp are generated
|
||||
automatically, and thus doesn't add any extra maintenance cost. On the contrary,
|
||||
most of http_parser's code is hand-optimized and unrolled. Instead describing
|
||||
"how" it should parse the HTTP requests/responses, a maintainer should
|
||||
implement the new features in [http_parser][0] cautiously, considering
|
||||
possible performance degradation and manually optimizing the new code.
|
||||
|
||||
## Verification
|
||||
|
||||
The state machine graph is encoded explicitly in llhttp. The [llparse][1]
|
||||
automatically checks the graph for absence of loops and correct reporting of the
|
||||
input ranges (spans) like header names and values. In the future, additional
|
||||
checks could be performed to get even stricter verification of the llhttp.
|
||||
|
||||
## Usage
|
||||
|
||||
```C
|
||||
#include "llhttp.h"
|
||||
|
||||
llhttp_t parser;
|
||||
llhttp_settings_t settings;
|
||||
|
||||
/* Initialize user callbacks and settings */
|
||||
llhttp_settings_init(&settings);
|
||||
|
||||
/* Set user callback */
|
||||
settings.on_message_complete = handle_on_message_complete;
|
||||
|
||||
/* Initialize the parser in HTTP_BOTH mode, meaning that it will select between
|
||||
* HTTP_REQUEST and HTTP_RESPONSE parsing automatically while reading the first
|
||||
* input.
|
||||
*/
|
||||
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);
|
||||
|
||||
enum llhttp_errno err = llhttp_execute(&parser, request, request_len);
|
||||
if (err == HPE_OK) {
|
||||
/* Successfully parsed! */
|
||||
} else {
|
||||
fprintf(stderr, "Parse error: %s %s\n", llhttp_errno_name(err),
|
||||
parser.reason);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### LICENSE
|
||||
|
||||
This software is licensed under the MIT License.
|
||||
|
||||
Copyright Fedor Indutny, 2018.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
persons to whom the Software is furnished to do so, subject to the
|
||||
following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
[0]: https://github.com/nodejs/http-parser
|
||||
[1]: https://github.com/indutny/llparse
|
||||
[2]: https://en.wikipedia.org/wiki/Register_allocation#Spilling
|
||||
[3]: https://en.wikipedia.org/wiki/Tail_call
|
||||
[4]: https://llvm.org/docs/LangRef.html
|
||||
[5]: https://llvm.org/docs/LangRef.html#call-instruction
|
||||
[6]: https://clang.llvm.org/
|
||||
[7]: https://github.com/nodejs/node
|
46
deps/llhttp/common.gypi
vendored
Normal file
46
deps/llhttp/common.gypi
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
{
|
||||
'target_defaults': {
|
||||
'default_configuration': 'Debug',
|
||||
'configurations': {
|
||||
# TODO: hoist these out and put them somewhere common, because
|
||||
# RuntimeLibrary MUST MATCH across the entire project
|
||||
'Debug': {
|
||||
'defines': [ 'DEBUG', '_DEBUG' ],
|
||||
'cflags': [ '-Wall', '-Wextra', '-O0', '-g', '-ftrapv' ],
|
||||
'msvs_settings': {
|
||||
'VCCLCompilerTool': {
|
||||
'RuntimeLibrary': 1, # static debug
|
||||
},
|
||||
},
|
||||
},
|
||||
'Release': {
|
||||
'defines': [ 'NDEBUG' ],
|
||||
'cflags': [ '-Wall', '-Wextra', '-O3' ],
|
||||
'msvs_settings': {
|
||||
'VCCLCompilerTool': {
|
||||
'RuntimeLibrary': 0, # static release
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
'msvs_settings': {
|
||||
'VCCLCompilerTool': {
|
||||
# Compile as C++. llhttp.c is actually C99, but C++ is
|
||||
# close enough in this case.
|
||||
'CompileAs': 2,
|
||||
},
|
||||
'VCLibrarianTool': {
|
||||
},
|
||||
'VCLinkerTool': {
|
||||
'GenerateDebugInformation': 'true',
|
||||
},
|
||||
},
|
||||
'conditions': [
|
||||
['OS == "win"', {
|
||||
'defines': [
|
||||
'WIN32'
|
||||
],
|
||||
}]
|
||||
],
|
||||
},
|
||||
}
|
353
deps/llhttp/include/llhttp.h
vendored
Normal file
353
deps/llhttp/include/llhttp.h
vendored
Normal file
@ -0,0 +1,353 @@
|
||||
#ifndef INCLUDE_LLHTTP_H_
|
||||
#define INCLUDE_LLHTTP_H_
|
||||
|
||||
#define LLHTTP_VERSION_MAJOR 1
|
||||
#define LLHTTP_VERSION_MINOR 0
|
||||
#define LLHTTP_VERSION_PATCH 0
|
||||
|
||||
#ifndef INCLUDE_LLHTTP_ITSELF_H_
|
||||
#define INCLUDE_LLHTTP_ITSELF_H_
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct llhttp__internal_s llhttp__internal_t;
|
||||
struct llhttp__internal_s {
|
||||
int32_t _index;
|
||||
void* _span_pos0;
|
||||
void* _span_cb0;
|
||||
int32_t error;
|
||||
const char* reason;
|
||||
const char* error_pos;
|
||||
void* data;
|
||||
void* _current;
|
||||
uint64_t content_length;
|
||||
uint8_t type;
|
||||
uint8_t method;
|
||||
uint8_t http_major;
|
||||
uint8_t http_minor;
|
||||
uint8_t header_state;
|
||||
uint8_t flags;
|
||||
uint8_t upgrade;
|
||||
uint16_t status_code;
|
||||
uint8_t finish;
|
||||
void* settings;
|
||||
};
|
||||
|
||||
int llhttp__internal_init(llhttp__internal_t* s);
|
||||
int llhttp__internal_execute(llhttp__internal_t* s, const char* p, const char* endp);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
#endif /* INCLUDE_LLHTTP_ITSELF_H_ */
|
||||
|
||||
#ifndef LLLLHTTP_C_HEADERS_
|
||||
#define LLLLHTTP_C_HEADERS_
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum llhttp_errno {
|
||||
HPE_OK = 0,
|
||||
HPE_INTERNAL = 1,
|
||||
HPE_STRICT = 2,
|
||||
HPE_LF_EXPECTED = 3,
|
||||
HPE_UNEXPECTED_CONTENT_LENGTH = 4,
|
||||
HPE_CLOSED_CONNECTION = 5,
|
||||
HPE_INVALID_METHOD = 6,
|
||||
HPE_INVALID_URL = 7,
|
||||
HPE_INVALID_CONSTANT = 8,
|
||||
HPE_INVALID_VERSION = 9,
|
||||
HPE_INVALID_HEADER_TOKEN = 10,
|
||||
HPE_INVALID_CONTENT_LENGTH = 11,
|
||||
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
|
||||
};
|
||||
typedef enum llhttp_errno llhttp_errno_t;
|
||||
|
||||
enum llhttp_flags {
|
||||
F_CONNECTION_KEEP_ALIVE = 0x1,
|
||||
F_CONNECTION_CLOSE = 0x2,
|
||||
F_CONNECTION_UPGRADE = 0x4,
|
||||
F_CHUNKED = 0x8,
|
||||
F_UPGRADE = 0x10,
|
||||
F_CONTENT_LENGTH = 0x20,
|
||||
F_SKIPBODY = 0x40,
|
||||
F_TRAILING = 0x80
|
||||
};
|
||||
typedef enum llhttp_flags llhttp_flags_t;
|
||||
|
||||
enum llhttp_type {
|
||||
HTTP_BOTH = 0,
|
||||
HTTP_REQUEST = 1,
|
||||
HTTP_RESPONSE = 2
|
||||
};
|
||||
typedef enum llhttp_type llhttp_type_t;
|
||||
|
||||
enum llhttp_finish {
|
||||
HTTP_FINISH_SAFE = 0,
|
||||
HTTP_FINISH_SAFE_WITH_CB = 1,
|
||||
HTTP_FINISH_UNSAFE = 2
|
||||
};
|
||||
typedef enum llhttp_finish llhttp_finish_t;
|
||||
|
||||
enum llhttp_method {
|
||||
HTTP_DELETE = 0,
|
||||
HTTP_GET = 1,
|
||||
HTTP_HEAD = 2,
|
||||
HTTP_POST = 3,
|
||||
HTTP_PUT = 4,
|
||||
HTTP_CONNECT = 5,
|
||||
HTTP_OPTIONS = 6,
|
||||
HTTP_TRACE = 7,
|
||||
HTTP_COPY = 8,
|
||||
HTTP_LOCK = 9,
|
||||
HTTP_MKCOL = 10,
|
||||
HTTP_MOVE = 11,
|
||||
HTTP_PROPFIND = 12,
|
||||
HTTP_PROPPATCH = 13,
|
||||
HTTP_SEARCH = 14,
|
||||
HTTP_UNLOCK = 15,
|
||||
HTTP_BIND = 16,
|
||||
HTTP_REBIND = 17,
|
||||
HTTP_UNBIND = 18,
|
||||
HTTP_ACL = 19,
|
||||
HTTP_REPORT = 20,
|
||||
HTTP_MKACTIVITY = 21,
|
||||
HTTP_CHECKOUT = 22,
|
||||
HTTP_MERGE = 23,
|
||||
HTTP_MSEARCH = 24,
|
||||
HTTP_NOTIFY = 25,
|
||||
HTTP_SUBSCRIBE = 26,
|
||||
HTTP_UNSUBSCRIBE = 27,
|
||||
HTTP_PATCH = 28,
|
||||
HTTP_PURGE = 29,
|
||||
HTTP_MKCALENDAR = 30,
|
||||
HTTP_LINK = 31,
|
||||
HTTP_UNLINK = 32,
|
||||
HTTP_SOURCE = 33
|
||||
};
|
||||
typedef enum llhttp_method llhttp_method_t;
|
||||
|
||||
#define HTTP_ERRNO_MAP(XX) \
|
||||
XX(0, OK, OK) \
|
||||
XX(1, INTERNAL, INTERNAL) \
|
||||
XX(2, STRICT, STRICT) \
|
||||
XX(3, LF_EXPECTED, LF_EXPECTED) \
|
||||
XX(4, UNEXPECTED_CONTENT_LENGTH, UNEXPECTED_CONTENT_LENGTH) \
|
||||
XX(5, CLOSED_CONNECTION, CLOSED_CONNECTION) \
|
||||
XX(6, INVALID_METHOD, INVALID_METHOD) \
|
||||
XX(7, INVALID_URL, INVALID_URL) \
|
||||
XX(8, INVALID_CONSTANT, INVALID_CONSTANT) \
|
||||
XX(9, INVALID_VERSION, INVALID_VERSION) \
|
||||
XX(10, INVALID_HEADER_TOKEN, INVALID_HEADER_TOKEN) \
|
||||
XX(11, INVALID_CONTENT_LENGTH, INVALID_CONTENT_LENGTH) \
|
||||
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) \
|
||||
|
||||
|
||||
#define HTTP_METHOD_MAP(XX) \
|
||||
XX(0, DELETE, DELETE) \
|
||||
XX(1, GET, GET) \
|
||||
XX(2, HEAD, HEAD) \
|
||||
XX(3, POST, POST) \
|
||||
XX(4, PUT, PUT) \
|
||||
XX(5, CONNECT, CONNECT) \
|
||||
XX(6, OPTIONS, OPTIONS) \
|
||||
XX(7, TRACE, TRACE) \
|
||||
XX(8, COPY, COPY) \
|
||||
XX(9, LOCK, LOCK) \
|
||||
XX(10, MKCOL, MKCOL) \
|
||||
XX(11, MOVE, MOVE) \
|
||||
XX(12, PROPFIND, PROPFIND) \
|
||||
XX(13, PROPPATCH, PROPPATCH) \
|
||||
XX(14, SEARCH, SEARCH) \
|
||||
XX(15, UNLOCK, UNLOCK) \
|
||||
XX(16, BIND, BIND) \
|
||||
XX(17, REBIND, REBIND) \
|
||||
XX(18, UNBIND, UNBIND) \
|
||||
XX(19, ACL, ACL) \
|
||||
XX(20, REPORT, REPORT) \
|
||||
XX(21, MKACTIVITY, MKACTIVITY) \
|
||||
XX(22, CHECKOUT, CHECKOUT) \
|
||||
XX(23, MERGE, MERGE) \
|
||||
XX(24, MSEARCH, M-SEARCH) \
|
||||
XX(25, NOTIFY, NOTIFY) \
|
||||
XX(26, SUBSCRIBE, SUBSCRIBE) \
|
||||
XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \
|
||||
XX(28, PATCH, PATCH) \
|
||||
XX(29, PURGE, PURGE) \
|
||||
XX(30, MKCALENDAR, MKCALENDAR) \
|
||||
XX(31, LINK, LINK) \
|
||||
XX(32, UNLINK, UNLINK) \
|
||||
XX(33, SOURCE, SOURCE) \
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
#endif /* LLLLHTTP_C_HEADERS_ */
|
||||
|
||||
#ifndef INCLUDE_LLHTTP_API_H_
|
||||
#define INCLUDE_LLHTTP_API_H_
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef llhttp__internal_t llhttp_t;
|
||||
typedef struct llhttp_settings_s llhttp_settings_t;
|
||||
|
||||
typedef int (*llhttp_data_cb)(llhttp_t*, const char *at, size_t length);
|
||||
typedef int (*llhttp_cb)(llhttp_t*);
|
||||
|
||||
struct llhttp_settings_s {
|
||||
/* Possible return values 0, -1, `HPE_PAUSED` */
|
||||
llhttp_cb on_message_begin;
|
||||
|
||||
llhttp_data_cb on_url;
|
||||
llhttp_data_cb on_status;
|
||||
llhttp_data_cb on_header_field;
|
||||
llhttp_data_cb on_header_value;
|
||||
|
||||
/* Possible return values:
|
||||
* 0 - Proceed normally
|
||||
* 1 - Assume that request/response has no body, and proceed to parsing the
|
||||
* next message
|
||||
* 2 - Assume absence of body (as above) and make `llhttp_execute()` return
|
||||
* `HPE_PAUSED_UPGRADE`
|
||||
* -1 - Error
|
||||
* `HPE_PAUSED`
|
||||
*/
|
||||
llhttp_cb on_headers_complete;
|
||||
|
||||
llhttp_data_cb on_body;
|
||||
|
||||
/* Possible return values 0, -1, `HPE_PAUSED` */
|
||||
llhttp_cb on_message_complete;
|
||||
|
||||
/* When on_chunk_header is called, the current chunk length is stored
|
||||
* in parser->content_length.
|
||||
* Possible return values 0, -1, `HPE_PAUSED`
|
||||
*/
|
||||
llhttp_cb on_chunk_header;
|
||||
llhttp_cb on_chunk_complete;
|
||||
};
|
||||
|
||||
/* Initialize the parser with specific type and user settings */
|
||||
void llhttp_init(llhttp_t* parser, llhttp_type_t type,
|
||||
const llhttp_settings_t* settings);
|
||||
|
||||
/* Initialize the settings object */
|
||||
void llhttp_settings_init(llhttp_settings_t* settings);
|
||||
|
||||
/* Parse full or partial request/response, invoking user callbacks along the
|
||||
* way.
|
||||
*
|
||||
* If any of `llhttp_data_cb` returns errno not equal to `HPE_OK` - the parsing
|
||||
* interrupts, and such errno is returned from `llhttp_execute()`. If
|
||||
* `HPE_PAUSED` was used as a errno, the execution can be resumed with
|
||||
* `llhttp_resume()` call.
|
||||
*
|
||||
* In a special case of CONNECT/Upgrade request/response `HPE_PAUSED_UPGRADE`
|
||||
* is returned after fully parsing the request/response. If the user wishes to
|
||||
* continue parsing, they need to invoke `llhttp_resume_after_upgrade()`.
|
||||
*/
|
||||
llhttp_errno_t llhttp_execute(llhttp_t* parser, const char* data, size_t len);
|
||||
|
||||
/* This method should be called when the other side has no further bytes to
|
||||
* send (e.g. shutdown of readable side of the TCP connection.)
|
||||
*
|
||||
* Requests without `Content-Length` and other messages might require treating
|
||||
* all incoming bytes as the part of the body, up to the last byte of the
|
||||
* connection. This method will invoke `on_message_complete()` callback if the
|
||||
* request was terminated safely. Otherwise a error code would be returned.
|
||||
*/
|
||||
llhttp_errno_t llhttp_finish(llhttp_t* parser);
|
||||
|
||||
/* Returns `1` if the incoming message is parsed until the last byte, and has
|
||||
* to be completed by calling `llhttp_finish()` on EOF
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
int llhttp_should_keep_alive(const llhttp_t* parser);
|
||||
|
||||
/* Make further calls of `llhttp_execute()` return `HPE_PAUSED` and set
|
||||
* appropriate error reason.
|
||||
*
|
||||
* Important: do not call this from user callbacks! User callbacks must return
|
||||
* `HPE_PAUSED` if pausing is required.
|
||||
*/
|
||||
void llhttp_pause(llhttp_t* parser);
|
||||
|
||||
/* Might be called to resume the execution after the pause in user's callback.
|
||||
* See `llhttp_execute()` above for details.
|
||||
*
|
||||
* Call this only if `llhttp_execute()` returns `HPE_PAUSED`.
|
||||
*/
|
||||
void llhttp_resume(llhttp_t* parser);
|
||||
|
||||
/* Might be called to resume the execution after the pause in user's callback.
|
||||
* See `llhttp_execute()` above for details.
|
||||
*
|
||||
* Call this only if `llhttp_execute()` returns `HPE_PAUSED_UPGRADE`
|
||||
*/
|
||||
void llhttp_resume_after_upgrade(llhttp_t* parser);
|
||||
|
||||
/* Returns the latest return error */
|
||||
llhttp_errno_t llhttp_get_errno(const llhttp_t* parser);
|
||||
|
||||
/* Returns the verbal explanation of the latest returned error.
|
||||
*
|
||||
* Note: User callback should set error reason when returning the error. See
|
||||
* `llhttp_set_error_reason()` for details.
|
||||
*/
|
||||
const char* llhttp_get_error_reason(const llhttp_t* parser);
|
||||
|
||||
/* Assign verbal description to the returned error. Must be called in user
|
||||
* callbacks right before returning the errno.
|
||||
*
|
||||
* Note: `HPE_USER` error code might be useful in user callbacks.
|
||||
*/
|
||||
void llhttp_set_error_reason(llhttp_t* parser, const char* reason);
|
||||
|
||||
/* Returns the pointer to the last parsed byte before the returned error. The
|
||||
* pointer is relative to the `data` argument of `llhttp_execute()`.
|
||||
*
|
||||
* Note: this method might be useful for counting the number of parsed bytes.
|
||||
*/
|
||||
const char* llhttp_get_error_pos(const llhttp_t* parser);
|
||||
|
||||
/* Returns textual name of error code */
|
||||
const char* llhttp_errno_name(llhttp_errno_t err);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
#endif /* INCLUDE_LLHTTP_API_H_ */
|
||||
|
||||
#endif /* INCLUDE_LLHTTP_H_ */
|
13
deps/llhttp/llhttp.gyp
vendored
Normal file
13
deps/llhttp/llhttp.gyp
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
'targets': [
|
||||
{
|
||||
'target_name': 'llhttp',
|
||||
'type': 'static_library',
|
||||
'include_dirs': [ '.', 'include' ],
|
||||
'direct_dependent_settings': {
|
||||
'include_dirs': [ 'include' ],
|
||||
},
|
||||
'sources': [ 'src/llhttp.c', 'src/api.c', 'src/http.c' ],
|
||||
},
|
||||
]
|
||||
}
|
205
deps/llhttp/src/api.c
vendored
Normal file
205
deps/llhttp/src/api.c
vendored
Normal file
@ -0,0 +1,205 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "llhttp.h"
|
||||
|
||||
#define CALLBACK_MAYBE(PARSER, NAME, ...) \
|
||||
do { \
|
||||
llhttp_settings_t* settings; \
|
||||
settings = (llhttp_settings_t*) (PARSER)->settings; \
|
||||
if (settings == NULL || settings->NAME == NULL) { \
|
||||
err = 0; \
|
||||
break; \
|
||||
} \
|
||||
err = settings->NAME(__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
void llhttp_init(llhttp_t* parser, llhttp_type_t type,
|
||||
const llhttp_settings_t* settings) {
|
||||
llhttp__internal_init(parser);
|
||||
|
||||
parser->type = type;
|
||||
parser->settings = (void*) settings;
|
||||
}
|
||||
|
||||
|
||||
llhttp_errno_t llhttp_execute(llhttp_t* parser, const char* data, size_t len) {
|
||||
return llhttp__internal_execute(parser, data, data + len);
|
||||
}
|
||||
|
||||
|
||||
void llhttp_settings_init(llhttp_settings_t* settings) {
|
||||
memset(settings, 0, sizeof(*settings));
|
||||
}
|
||||
|
||||
|
||||
llhttp_errno_t llhttp_finish(llhttp_t* parser) {
|
||||
int err;
|
||||
|
||||
/* We're in an error state. Don't bother doing anything. */
|
||||
if (parser->error != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (parser->finish) {
|
||||
case HTTP_FINISH_SAFE_WITH_CB:
|
||||
CALLBACK_MAYBE(parser, on_message_complete, parser);
|
||||
if (err != HPE_OK) return err;
|
||||
|
||||
/* FALLTHROUGH */
|
||||
case HTTP_FINISH_SAFE:
|
||||
return HPE_OK;
|
||||
case HTTP_FINISH_UNSAFE:
|
||||
parser->reason = "Invalid EOF state";
|
||||
return HPE_INVALID_EOF_STATE;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void llhttp_pause(llhttp_t* parser) {
|
||||
if (parser->error != HPE_OK) {
|
||||
return;
|
||||
}
|
||||
|
||||
parser->error = HPE_PAUSED;
|
||||
parser->reason = "Paused";
|
||||
}
|
||||
|
||||
|
||||
void llhttp_resume(llhttp_t* parser) {
|
||||
if (parser->error != HPE_PAUSED) {
|
||||
return;
|
||||
}
|
||||
|
||||
parser->error = 0;
|
||||
}
|
||||
|
||||
|
||||
void llhttp_resume_after_upgrade(llhttp_t* parser) {
|
||||
if (parser->error != HPE_PAUSED_UPGRADE) {
|
||||
return;
|
||||
}
|
||||
|
||||
parser->error = 0;
|
||||
}
|
||||
|
||||
|
||||
llhttp_errno_t llhttp_get_errno(const llhttp_t* parser) {
|
||||
return parser->error;
|
||||
}
|
||||
|
||||
|
||||
const char* llhttp_get_error_reason(const llhttp_t* parser) {
|
||||
return parser->reason;
|
||||
}
|
||||
|
||||
|
||||
void llhttp_set_error_reason(llhttp_t* parser, const char* reason) {
|
||||
parser->reason = reason;
|
||||
}
|
||||
|
||||
|
||||
const char* llhttp_get_error_pos(const llhttp_t* parser) {
|
||||
return parser->error_pos;
|
||||
}
|
||||
|
||||
|
||||
const char* llhttp_errno_name(llhttp_errno_t err) {
|
||||
#define HTTP_ERRNO_GEN(CODE, NAME, _) case HPE_##NAME: return "HPE_" #NAME;
|
||||
switch (err) {
|
||||
HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)
|
||||
default: abort();
|
||||
}
|
||||
#undef HTTP_ERRNO_GEN
|
||||
}
|
||||
|
||||
|
||||
/* Callbacks */
|
||||
|
||||
|
||||
int llhttp__on_message_begin(llhttp_t* s, const char* p, const char* endp) {
|
||||
int err;
|
||||
CALLBACK_MAYBE(s, on_message_begin, s);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
int llhttp__on_url(llhttp_t* s, const char* p, const char* endp) {
|
||||
int err;
|
||||
CALLBACK_MAYBE(s, on_url, s, p, endp - p);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
int llhttp__on_status(llhttp_t* s, const char* p, const char* endp) {
|
||||
int err;
|
||||
CALLBACK_MAYBE(s, on_status, s, p, endp - p);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
int llhttp__on_header_field(llhttp_t* s, const char* p, const char* endp) {
|
||||
int err;
|
||||
CALLBACK_MAYBE(s, on_header_field, s, p, endp - p);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
int llhttp__on_header_value(llhttp_t* s, const char* p, const char* endp) {
|
||||
int err;
|
||||
CALLBACK_MAYBE(s, on_header_value, s, p, endp - p);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
int llhttp__on_headers_complete(llhttp_t* s, const char* p, const char* endp) {
|
||||
int err;
|
||||
CALLBACK_MAYBE(s, on_headers_complete, s);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
int llhttp__on_message_complete(llhttp_t* s, const char* p, const char* endp) {
|
||||
int err;
|
||||
CALLBACK_MAYBE(s, on_message_complete, s);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
int llhttp__on_body(llhttp_t* s, const char* p, const char* endp) {
|
||||
int err;
|
||||
CALLBACK_MAYBE(s, on_body, s, p, endp - p);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
int llhttp__on_chunk_header(llhttp_t* s, const char* p, const char* endp) {
|
||||
int err;
|
||||
CALLBACK_MAYBE(s, on_chunk_header, s);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
int llhttp__on_chunk_complete(llhttp_t* s, const char* p, const char* endp) {
|
||||
int err;
|
||||
CALLBACK_MAYBE(s, on_chunk_complete, s);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/* Private */
|
||||
|
||||
|
||||
void llhttp__debug(llhttp_t* s, const char* p, const char* endp,
|
||||
const char* msg) {
|
||||
if (p == endp) {
|
||||
fprintf(stderr, "p=%p type=%d flags=%02x next=null debug=%s\n", s, s->type,
|
||||
s->flags, msg);
|
||||
} else {
|
||||
fprintf(stderr, "p=%p type=%d flags=%02x next=%02x debug=%s\n", s,
|
||||
s->type, s->flags, *p, msg);
|
||||
}
|
||||
}
|
120
deps/llhttp/src/http.c
vendored
Normal file
120
deps/llhttp/src/http.c
vendored
Normal file
@ -0,0 +1,120 @@
|
||||
#include <stdio.h>
|
||||
#ifndef LLHTTP__TEST
|
||||
# include "llhttp.h"
|
||||
#else
|
||||
# define llhttp_t llparse_t
|
||||
#endif /* */
|
||||
|
||||
int llhttp_message_needs_eof(const llhttp_t* parser);
|
||||
int llhttp_should_keep_alive(const llhttp_t* parser);
|
||||
|
||||
int llhttp__before_headers_complete(llhttp_t* parser, const char* p,
|
||||
const char* endp) {
|
||||
/* Set this here so that on_headers_complete() callbacks can see it */
|
||||
if ((parser->flags & F_UPGRADE) &&
|
||||
(parser->flags & F_CONNECTION_UPGRADE)) {
|
||||
/* For responses, "Upgrade: foo" and "Connection: upgrade" are
|
||||
* mandatory only when it is a 101 Switching Protocols response,
|
||||
* otherwise it is purely informational, to announce support.
|
||||
*/
|
||||
parser->upgrade =
|
||||
(parser->type == HTTP_REQUEST || parser->status_code == 101);
|
||||
} else {
|
||||
parser->upgrade = (parser->method == HTTP_CONNECT);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Return values:
|
||||
* 0 - No body, `restart`, message_complete
|
||||
* 1 - CONNECT request, `restart`, message_complete, and pause
|
||||
* 2 - chunk_size_start
|
||||
* 3 - body_identity
|
||||
* 4 - body_identity_eof
|
||||
*/
|
||||
int llhttp__after_headers_complete(llhttp_t* parser, const char* p,
|
||||
const char* endp) {
|
||||
int hasBody;
|
||||
|
||||
hasBody = parser->flags & F_CHUNKED || parser->content_length > 0;
|
||||
if (parser->upgrade && (parser->method == HTTP_CONNECT ||
|
||||
(parser->flags & F_SKIPBODY) || !hasBody)) {
|
||||
/* Exit, the rest of the message is in a different protocol. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (parser->flags & F_SKIPBODY) {
|
||||
return 0;
|
||||
} else if (parser->flags & F_CHUNKED) {
|
||||
/* chunked encoding - ignore Content-Length header */
|
||||
return 2;
|
||||
} else {
|
||||
if (!(parser->flags & F_CONTENT_LENGTH)) {
|
||||
if (!llhttp_message_needs_eof(parser)) {
|
||||
/* Assume content-length 0 - read the next */
|
||||
return 0;
|
||||
} else {
|
||||
/* Read body until EOF */
|
||||
return 4;
|
||||
}
|
||||
} else if (parser->content_length == 0) {
|
||||
/* Content-Length header given but zero: Content-Length: 0\r\n */
|
||||
return 0;
|
||||
} else {
|
||||
/* Content-Length header given and non-zero */
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int llhttp__after_message_complete(llhttp_t* parser, const char* p,
|
||||
const char* endp) {
|
||||
int should_keep_alive;
|
||||
|
||||
should_keep_alive = llhttp_should_keep_alive(parser);
|
||||
parser->flags = 0;
|
||||
parser->finish = HTTP_FINISH_SAFE;
|
||||
|
||||
/* NOTE: this is ignored in loose parsing mode */
|
||||
return should_keep_alive;
|
||||
}
|
||||
|
||||
|
||||
int llhttp_message_needs_eof(const llhttp_t* parser) {
|
||||
if (parser->type == HTTP_REQUEST) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* See RFC 2616 section 4.4 */
|
||||
if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */
|
||||
parser->status_code == 204 || /* No Content */
|
||||
parser->status_code == 304 || /* Not Modified */
|
||||
(parser->flags & F_SKIPBODY)) { /* response to a HEAD request */
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (parser->flags & (F_CHUNKED | F_CONTENT_LENGTH)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int llhttp_should_keep_alive(const llhttp_t* parser) {
|
||||
if (parser->http_major > 0 && parser->http_minor > 0) {
|
||||
/* HTTP/1.1 */
|
||||
if (parser->flags & F_CONNECTION_CLOSE) {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
/* HTTP/1.0 or earlier */
|
||||
if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return !llhttp_message_needs_eof(parser);
|
||||
}
|
6044
deps/llhttp/src/llhttp.c
vendored
Normal file
6044
deps/llhttp/src/llhttp.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
node.gyp
1
node.gyp
@ -12,6 +12,7 @@
|
||||
'force_dynamic_crt%': 0,
|
||||
'node_module_version%': '',
|
||||
'node_shared_zlib%': 'false',
|
||||
'node_experimental_http_parser%': 'false',
|
||||
'node_shared_http_parser%': 'false',
|
||||
'node_shared_cares%': 'false',
|
||||
'node_shared_libuv%': 'false',
|
||||
|
11
node.gypi
11
node.gypi
@ -163,9 +163,14 @@
|
||||
],
|
||||
}],
|
||||
|
||||
[ 'node_shared_http_parser=="false"', {
|
||||
'dependencies': [ 'deps/http_parser/http_parser.gyp:http_parser' ],
|
||||
}],
|
||||
[ 'node_experimental_http_parser=="true"', {
|
||||
'defines': [ 'NODE_EXPERIMENTAL_HTTP' ],
|
||||
'dependencies': [ 'deps/llhttp/llhttp.gyp:llhttp' ],
|
||||
}, {
|
||||
'conditions': [ [ 'node_shared_http_parser=="false"', {
|
||||
'dependencies': [ 'deps/http_parser/http_parser.gyp:http_parser' ],
|
||||
} ] ],
|
||||
} ],
|
||||
|
||||
[ 'node_shared_cares=="false"', {
|
||||
'dependencies': [ 'deps/cares/cares.gyp:cares' ],
|
||||
|
@ -265,6 +265,7 @@ constexpr size_t kFsStatsBufferLength = kFsStatsFieldsNumber * 2;
|
||||
V(raw_string, "raw") \
|
||||
V(read_host_object_string, "_readHostObject") \
|
||||
V(readable_string, "readable") \
|
||||
V(reason_string, "reason") \
|
||||
V(refresh_string, "refresh") \
|
||||
V(regexp_string, "regexp") \
|
||||
V(rename_string, "rename") \
|
||||
|
24
src/http_parser_adaptor.h
Normal file
24
src/http_parser_adaptor.h
Normal file
@ -0,0 +1,24 @@
|
||||
#ifndef SRC_HTTP_PARSER_ADAPTOR_H_
|
||||
#define SRC_HTTP_PARSER_ADAPTOR_H_
|
||||
|
||||
#ifdef NODE_EXPERIMENTAL_HTTP
|
||||
# include "llhttp.h"
|
||||
|
||||
typedef llhttp_type_t parser_type_t;
|
||||
typedef llhttp_errno_t parser_errno_t;
|
||||
typedef llhttp_settings_t parser_settings_t;
|
||||
typedef llhttp_t parser_t;
|
||||
|
||||
#else /* !NODE_EXPERIMENTAL_HTTP */
|
||||
# include "http_parser.h"
|
||||
|
||||
typedef enum http_parser_type parser_type_t;
|
||||
typedef enum http_errno parser_errno_t;
|
||||
typedef http_parser_settings parser_settings_t;
|
||||
typedef http_parser parser_t;
|
||||
|
||||
#define HPE_USER HPE_UNKNOWN
|
||||
|
||||
#endif /* NODE_EXPERIMENTAL_HTTP */
|
||||
|
||||
#endif /* SRC_HTTP_PARSER_ADAPTOR_H_ */
|
@ -1,6 +1,6 @@
|
||||
#include "inspector_socket.h"
|
||||
|
||||
#include "http_parser.h"
|
||||
#include "http_parser_adaptor.h"
|
||||
#include "util-inl.h"
|
||||
|
||||
#define NODE_WANT_INTERNALS 1
|
||||
@ -433,8 +433,13 @@ class HttpHandler : public ProtocolHandler {
|
||||
explicit HttpHandler(InspectorSocket* inspector, TcpHolder::Pointer tcp)
|
||||
: ProtocolHandler(inspector, std::move(tcp)),
|
||||
parsing_value_(false) {
|
||||
#ifdef NODE_EXPERIMENTAL_HTTP
|
||||
llhttp_init(&parser_, HTTP_REQUEST, &parser_settings);
|
||||
llhttp_settings_init(&parser_settings);
|
||||
#else /* !NODE_EXPERIMENTAL_HTTP */
|
||||
http_parser_init(&parser_, HTTP_REQUEST);
|
||||
http_parser_settings_init(&parser_settings);
|
||||
#endif /* NODE_EXPERIMENTAL_HTTP */
|
||||
parser_settings.on_header_field = OnHeaderField;
|
||||
parser_settings.on_header_value = OnHeaderValue;
|
||||
parser_settings.on_message_complete = OnMessageComplete;
|
||||
@ -478,9 +483,20 @@ class HttpHandler : public ProtocolHandler {
|
||||
}
|
||||
|
||||
void OnData(std::vector<char>* data) override {
|
||||
parser_errno_t err;
|
||||
#ifdef NODE_EXPERIMENTAL_HTTP
|
||||
err = llhttp_execute(&parser_, data->data(), data->size());
|
||||
|
||||
if (err == HPE_PAUSED_UPGRADE) {
|
||||
err = HPE_OK;
|
||||
llhttp_resume_after_upgrade(&parser_);
|
||||
}
|
||||
#else /* !NODE_EXPERIMENTAL_HTTP */
|
||||
http_parser_execute(&parser_, &parser_settings, data->data(), data->size());
|
||||
err = HTTP_PARSER_ERRNO(&parser_);
|
||||
#endif /* NODE_EXPERIMENTAL_HTTP */
|
||||
data->clear();
|
||||
if (parser_.http_errno != HPE_OK) {
|
||||
if (err != HPE_OK) {
|
||||
CancelHandshake();
|
||||
}
|
||||
// Event handling may delete *this
|
||||
@ -517,14 +533,14 @@ class HttpHandler : public ProtocolHandler {
|
||||
handler->inspector()->SwitchProtocol(nullptr);
|
||||
}
|
||||
|
||||
static int OnHeaderValue(http_parser* parser, const char* at, size_t length) {
|
||||
static int OnHeaderValue(parser_t* parser, const char* at, size_t length) {
|
||||
HttpHandler* handler = From(parser);
|
||||
handler->parsing_value_ = true;
|
||||
handler->headers_[handler->current_header_].append(at, length);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int OnHeaderField(http_parser* parser, const char* at, size_t length) {
|
||||
static int OnHeaderField(parser_t* parser, const char* at, size_t length) {
|
||||
HttpHandler* handler = From(parser);
|
||||
if (handler->parsing_value_) {
|
||||
handler->parsing_value_ = false;
|
||||
@ -534,17 +550,17 @@ class HttpHandler : public ProtocolHandler {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int OnPath(http_parser* parser, const char* at, size_t length) {
|
||||
static int OnPath(parser_t* parser, const char* at, size_t length) {
|
||||
HttpHandler* handler = From(parser);
|
||||
handler->path_.append(at, length);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static HttpHandler* From(http_parser* parser) {
|
||||
static HttpHandler* From(parser_t* parser) {
|
||||
return node::ContainerOf(&HttpHandler::parser_, parser);
|
||||
}
|
||||
|
||||
static int OnMessageComplete(http_parser* parser) {
|
||||
static int OnMessageComplete(parser_t* parser) {
|
||||
// Event needs to be fired after the parser is done.
|
||||
HttpHandler* handler = From(parser);
|
||||
handler->events_.push_back(
|
||||
@ -581,8 +597,8 @@ class HttpHandler : public ProtocolHandler {
|
||||
}
|
||||
|
||||
bool parsing_value_;
|
||||
http_parser parser_;
|
||||
http_parser_settings parser_settings;
|
||||
parser_t parser_;
|
||||
parser_settings_t parser_settings;
|
||||
std::vector<HttpEvent> events_;
|
||||
std::string current_header_;
|
||||
std::map<std::string, std::string> headers_;
|
||||
|
46
src/node.cc
46
src/node.cc
@ -53,7 +53,11 @@
|
||||
#include "async_wrap-inl.h"
|
||||
#include "env-inl.h"
|
||||
#include "handle_wrap.h"
|
||||
#include "http_parser.h"
|
||||
#ifdef NODE_EXPERIMENTAL_HTTP
|
||||
# include "llhttp.h"
|
||||
#else /* !NODE_EXPERIMENTAL_HTTP */
|
||||
# include "http_parser.h"
|
||||
#endif /* NODE_EXPERIMENTAL_HTTP */
|
||||
#include "nghttp2/nghttp2ver.h"
|
||||
#include "req_wrap-inl.h"
|
||||
#include "string_bytes.h"
|
||||
@ -179,6 +183,22 @@ static node_module* modlist_internal;
|
||||
static node_module* modlist_linked;
|
||||
static node_module* modlist_addon;
|
||||
|
||||
#ifdef NODE_EXPERIMENTAL_HTTP
|
||||
static const char llhttp_version[] =
|
||||
NODE_STRINGIFY(LLHTTP_VERSION_MAJOR)
|
||||
"."
|
||||
NODE_STRINGIFY(LLHTTP_VERSION_MINOR)
|
||||
"."
|
||||
NODE_STRINGIFY(LLHTTP_VERSION_PATCH);
|
||||
#else /* !NODE_EXPERIMENTAL_HTTP */
|
||||
static const char http_parser_version[] =
|
||||
NODE_STRINGIFY(HTTP_PARSER_VERSION_MAJOR)
|
||||
"."
|
||||
NODE_STRINGIFY(HTTP_PARSER_VERSION_MINOR)
|
||||
"."
|
||||
NODE_STRINGIFY(HTTP_PARSER_VERSION_PATCH);
|
||||
#endif /* NODE_EXPERIMENTAL_HTTP */
|
||||
|
||||
// Bit flag used to track security reverts (see node_revert.h)
|
||||
unsigned int reverted = 0;
|
||||
|
||||
@ -217,17 +237,15 @@ class NodeTraceStateObserver :
|
||||
auto trace_process = tracing::TracedValue::Create();
|
||||
trace_process->BeginDictionary("versions");
|
||||
|
||||
const char http_parser_version[] =
|
||||
NODE_STRINGIFY(HTTP_PARSER_VERSION_MAJOR)
|
||||
"."
|
||||
NODE_STRINGIFY(HTTP_PARSER_VERSION_MINOR)
|
||||
"."
|
||||
NODE_STRINGIFY(HTTP_PARSER_VERSION_PATCH);
|
||||
#ifdef NODE_EXPERIMENTAL_HTTP
|
||||
trace_process->SetString("llhttp", llhttp_version);
|
||||
#else /* !NODE_EXPERIMENTAL_HTTP */
|
||||
trace_process->SetString("http_parser", http_parser_version);
|
||||
#endif /* NODE_EXPERIMENTAL_HTTP */
|
||||
|
||||
const char node_napi_version[] = NODE_STRINGIFY(NAPI_VERSION);
|
||||
const char node_modules_version[] = NODE_STRINGIFY(NODE_MODULE_VERSION);
|
||||
|
||||
trace_process->SetString("http_parser", http_parser_version);
|
||||
trace_process->SetString("node", NODE_VERSION_STRING);
|
||||
trace_process->SetString("v8", V8::GetVersion());
|
||||
trace_process->SetString("uv", uv_version_string());
|
||||
@ -1344,14 +1362,16 @@ void SetupProcessObject(Environment* env,
|
||||
Local<Object> versions = Object::New(env->isolate());
|
||||
READONLY_PROPERTY(process, "versions", versions);
|
||||
|
||||
const char http_parser_version[] = NODE_STRINGIFY(HTTP_PARSER_VERSION_MAJOR)
|
||||
"."
|
||||
NODE_STRINGIFY(HTTP_PARSER_VERSION_MINOR)
|
||||
"."
|
||||
NODE_STRINGIFY(HTTP_PARSER_VERSION_PATCH);
|
||||
#ifdef NODE_EXPERIMENTAL_HTTP
|
||||
READONLY_PROPERTY(versions,
|
||||
"llhttp",
|
||||
FIXED_ONE_BYTE_STRING(env->isolate(), llhttp_version));
|
||||
#else /* !NODE_EXPERIMENTAL_HTTP */
|
||||
READONLY_PROPERTY(versions,
|
||||
"http_parser",
|
||||
FIXED_ONE_BYTE_STRING(env->isolate(), http_parser_version));
|
||||
#endif /* NODE_EXPERIMENTAL_HTTP */
|
||||
|
||||
// +1 to get rid of the leading 'v'
|
||||
READONLY_PROPERTY(versions,
|
||||
"node",
|
||||
|
@ -25,7 +25,6 @@
|
||||
|
||||
#include "async_wrap-inl.h"
|
||||
#include "env-inl.h"
|
||||
#include "http_parser.h"
|
||||
#include "stream_base-inl.h"
|
||||
#include "util-inl.h"
|
||||
#include "v8.h"
|
||||
@ -33,6 +32,8 @@
|
||||
#include <stdlib.h> // free()
|
||||
#include <string.h> // strdup()
|
||||
|
||||
#include "http_parser_adaptor.h"
|
||||
|
||||
// This is a binding to http_parser (https://github.com/nodejs/http-parser)
|
||||
// The goal is to decouple sockets from parsing for more javascript-level
|
||||
// agility. A Buffer is read from a socket and passed to parser.execute().
|
||||
@ -148,7 +149,7 @@ struct StringPtr {
|
||||
|
||||
class Parser : public AsyncWrap, public StreamListener {
|
||||
public:
|
||||
Parser(Environment* env, Local<Object> wrap, enum http_parser_type type)
|
||||
Parser(Environment* env, Local<Object> wrap, parser_type_t type)
|
||||
: AsyncWrap(env, wrap, AsyncWrap::PROVIDER_HTTPPARSER),
|
||||
current_buffer_len_(0),
|
||||
current_buffer_data_(nullptr) {
|
||||
@ -172,18 +173,33 @@ class Parser : public AsyncWrap, public StreamListener {
|
||||
|
||||
|
||||
int on_url(const char* at, size_t length) {
|
||||
int rv = TrackHeader(length);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
url_.Update(at, length);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int on_status(const char* at, size_t length) {
|
||||
int rv = TrackHeader(length);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
status_message_.Update(at, length);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int on_header_field(const char* at, size_t length) {
|
||||
int rv = TrackHeader(length);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (num_fields_ == num_values_) {
|
||||
// start of new field name
|
||||
num_fields_++;
|
||||
@ -206,6 +222,11 @@ class Parser : public AsyncWrap, public StreamListener {
|
||||
|
||||
|
||||
int on_header_value(const char* at, size_t length) {
|
||||
int rv = TrackHeader(length);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (num_values_ != num_fields_) {
|
||||
// start of new header value
|
||||
num_values_++;
|
||||
@ -222,6 +243,10 @@ class Parser : public AsyncWrap, public StreamListener {
|
||||
|
||||
|
||||
int on_headers_complete() {
|
||||
#ifdef NODE_EXPERIMENTAL_HTTP
|
||||
header_nread_ = 0;
|
||||
#endif /* NODE_EXPERIMENTAL_HTTP */
|
||||
|
||||
// Arguments for the on-headers-complete javascript callback. This
|
||||
// list needs to be kept in sync with the actual argument list for
|
||||
// `parserOnHeadersComplete` in lib/_http_common.js.
|
||||
@ -279,8 +304,15 @@ class Parser : public AsyncWrap, public StreamListener {
|
||||
argv[A_VERSION_MAJOR] = Integer::New(env()->isolate(), parser_.http_major);
|
||||
argv[A_VERSION_MINOR] = Integer::New(env()->isolate(), parser_.http_minor);
|
||||
|
||||
bool should_keep_alive;
|
||||
#ifdef NODE_EXPERIMENTAL_HTTP
|
||||
should_keep_alive = llhttp_should_keep_alive(&parser_);
|
||||
#else /* !NODE_EXPERIMENTAL_HTTP */
|
||||
should_keep_alive = http_should_keep_alive(&parser_);
|
||||
#endif /* NODE_EXPERIMENTAL_HTTP */
|
||||
|
||||
argv[A_SHOULD_KEEP_ALIVE] =
|
||||
Boolean::New(env()->isolate(), http_should_keep_alive(&parser_));
|
||||
Boolean::New(env()->isolate(), should_keep_alive);
|
||||
|
||||
argv[A_UPGRADE] = Boolean::New(env()->isolate(), parser_.upgrade);
|
||||
|
||||
@ -332,7 +364,10 @@ class Parser : public AsyncWrap, public StreamListener {
|
||||
|
||||
if (r.IsEmpty()) {
|
||||
got_exception_ = true;
|
||||
return -1;
|
||||
#ifdef NODE_EXPERIMENTAL_HTTP
|
||||
llhttp_set_error_reason(&parser_, "JS Exception");
|
||||
#endif /* NODE_EXPERIMENTAL_HTTP */
|
||||
return HPE_USER;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -357,18 +392,33 @@ class Parser : public AsyncWrap, public StreamListener {
|
||||
|
||||
if (r.IsEmpty()) {
|
||||
got_exception_ = true;
|
||||
return -1;
|
||||
return HPE_USER;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef NODE_EXPERIMENTAL_HTTP
|
||||
// Reset nread for the next chunk
|
||||
int on_chunk_header() {
|
||||
header_nread_ = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Reset nread for the next chunk
|
||||
int on_chunk_complete() {
|
||||
header_nread_ = 0;
|
||||
return 0;
|
||||
}
|
||||
#endif /* NODE_EXPERIMENTAL_HTTP */
|
||||
|
||||
|
||||
static void New(const FunctionCallbackInfo<Value>& args) {
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
CHECK(args[0]->IsInt32());
|
||||
http_parser_type type =
|
||||
static_cast<http_parser_type>(args[0].As<Int32>()->Value());
|
||||
parser_type_t type =
|
||||
static_cast<parser_type_t>(args[0].As<Int32>()->Value());
|
||||
CHECK(type == HTTP_REQUEST || type == HTTP_RESPONSE);
|
||||
new Parser(env, args.This(), type);
|
||||
}
|
||||
@ -434,30 +484,11 @@ class Parser : public AsyncWrap, public StreamListener {
|
||||
|
||||
|
||||
static void Finish(const FunctionCallbackInfo<Value>& args) {
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
|
||||
Parser* parser;
|
||||
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder());
|
||||
|
||||
CHECK(parser->current_buffer_.IsEmpty());
|
||||
parser->got_exception_ = false;
|
||||
|
||||
int rv = http_parser_execute(&(parser->parser_), &settings, nullptr, 0);
|
||||
|
||||
if (parser->got_exception_)
|
||||
return;
|
||||
|
||||
if (rv != 0) {
|
||||
enum http_errno err = HTTP_PARSER_ERRNO(&parser->parser_);
|
||||
|
||||
Local<Value> e = Exception::Error(env->parse_error_string());
|
||||
Local<Object> obj = e.As<Object>();
|
||||
obj->Set(env->bytes_parsed_string(), Integer::New(env->isolate(), 0));
|
||||
obj->Set(env->code_string(),
|
||||
OneByteString(env->isolate(), http_errno_name(err)));
|
||||
|
||||
args.GetReturnValue().Set(e);
|
||||
}
|
||||
parser->Execute(nullptr, 0);
|
||||
}
|
||||
|
||||
|
||||
@ -467,8 +498,8 @@ class Parser : public AsyncWrap, public StreamListener {
|
||||
CHECK(args[0]->IsInt32());
|
||||
CHECK(args[1]->IsBoolean());
|
||||
bool isReused = args[1]->IsTrue();
|
||||
http_parser_type type =
|
||||
static_cast<http_parser_type>(args[0].As<Int32>()->Value());
|
||||
parser_type_t type =
|
||||
static_cast<parser_type_t>(args[0].As<Int32>()->Value());
|
||||
|
||||
CHECK(type == HTTP_REQUEST || type == HTTP_RESPONSE);
|
||||
Parser* parser;
|
||||
@ -492,7 +523,21 @@ class Parser : public AsyncWrap, public StreamListener {
|
||||
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder());
|
||||
// Should always be called from the same context.
|
||||
CHECK_EQ(env, parser->env());
|
||||
|
||||
#ifdef NODE_EXPERIMENTAL_HTTP
|
||||
if (parser->execute_depth_) {
|
||||
parser->pending_pause_ = should_pause;
|
||||
return;
|
||||
}
|
||||
|
||||
if (should_pause) {
|
||||
llhttp_pause(&parser->parser_);
|
||||
} else {
|
||||
llhttp_resume(&parser->parser_);
|
||||
}
|
||||
#else /* !NODE_EXPERIMENTAL_HTTP */
|
||||
http_parser_pause(&parser->parser_, should_pause);
|
||||
#endif /* NODE_EXPERIMENTAL_HTTP */
|
||||
}
|
||||
|
||||
|
||||
@ -602,10 +647,46 @@ class Parser : public AsyncWrap, public StreamListener {
|
||||
current_buffer_data_ = data;
|
||||
got_exception_ = false;
|
||||
|
||||
size_t nparsed =
|
||||
http_parser_execute(&parser_, &settings, data, len);
|
||||
parser_errno_t err;
|
||||
|
||||
Save();
|
||||
#ifdef NODE_EXPERIMENTAL_HTTP
|
||||
// Do not allow re-entering `http_parser_execute()`
|
||||
CHECK_EQ(execute_depth_, 0);
|
||||
|
||||
execute_depth_++;
|
||||
if (data == nullptr) {
|
||||
err = llhttp_finish(&parser_);
|
||||
} else {
|
||||
err = llhttp_execute(&parser_, data, len);
|
||||
Save();
|
||||
}
|
||||
execute_depth_--;
|
||||
|
||||
// Calculate bytes read and resume after Upgrade/CONNECT pause
|
||||
size_t nread = len;
|
||||
if (err != HPE_OK) {
|
||||
nread = llhttp_get_error_pos(&parser_) - data;
|
||||
|
||||
// This isn't a real pause, just a way to stop parsing early.
|
||||
if (err == HPE_PAUSED_UPGRADE) {
|
||||
err = HPE_OK;
|
||||
llhttp_resume_after_upgrade(&parser_);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply pending pause
|
||||
if (pending_pause_) {
|
||||
pending_pause_ = false;
|
||||
llhttp_pause(&parser_);
|
||||
}
|
||||
#else /* !NODE_EXPERIMENTAL_HTTP */
|
||||
size_t nread = http_parser_execute(&parser_, &settings, data, len);
|
||||
if (data != nullptr) {
|
||||
Save();
|
||||
}
|
||||
|
||||
err = HTTP_PARSER_ERRNO(&parser_);
|
||||
#endif /* NODE_EXPERIMENTAL_HTTP */
|
||||
|
||||
// Unassign the 'buffer_' variable
|
||||
current_buffer_.Clear();
|
||||
@ -616,22 +697,29 @@ class Parser : public AsyncWrap, public StreamListener {
|
||||
if (got_exception_)
|
||||
return scope.Escape(Local<Value>());
|
||||
|
||||
Local<Integer> nparsed_obj = Integer::New(env()->isolate(), nparsed);
|
||||
Local<Integer> nread_obj = Integer::New(env()->isolate(), nread);
|
||||
|
||||
// If there was a parse error in one of the callbacks
|
||||
// TODO(bnoordhuis) What if there is an error on EOF?
|
||||
if (!parser_.upgrade && nparsed != len) {
|
||||
enum http_errno err = HTTP_PARSER_ERRNO(&parser_);
|
||||
|
||||
if (!parser_.upgrade && err != HPE_OK) {
|
||||
Local<Value> e = Exception::Error(env()->parse_error_string());
|
||||
Local<Object> obj = e->ToObject(env()->isolate()->GetCurrentContext())
|
||||
.ToLocalChecked();
|
||||
obj->Set(env()->bytes_parsed_string(), nparsed_obj);
|
||||
obj->Set(env()->bytes_parsed_string(), nread_obj);
|
||||
#ifdef NODE_EXPERIMENTAL_HTTP
|
||||
obj->Set(env()->code_string(),
|
||||
OneByteString(env()->isolate(), llhttp_errno_name(err)));
|
||||
obj->Set(env()->reason_string(),
|
||||
OneByteString(env()->isolate(), parser_.reason));
|
||||
#else /* !NODE_EXPERIMENTAL_HTTP */
|
||||
obj->Set(env()->code_string(),
|
||||
OneByteString(env()->isolate(), http_errno_name(err)));
|
||||
#endif /* NODE_EXPERIMENTAL_HTTP */
|
||||
|
||||
return scope.Escape(e);
|
||||
}
|
||||
return scope.Escape(nparsed_obj);
|
||||
|
||||
return scope.Escape(nread_obj);
|
||||
}
|
||||
|
||||
Local<Array> CreateHeaders() {
|
||||
@ -684,8 +772,12 @@ class Parser : public AsyncWrap, public StreamListener {
|
||||
}
|
||||
|
||||
|
||||
void Init(enum http_parser_type type) {
|
||||
void Init(parser_type_t type) {
|
||||
#ifdef NODE_EXPERIMENTAL_HTTP
|
||||
llhttp_init(&parser_, type, &settings);
|
||||
#else /* !NODE_EXPERIMENTAL_HTTP */
|
||||
http_parser_init(&parser_, type);
|
||||
#endif /* NODE_EXPERIMENTAL_HTTP */
|
||||
url_.Reset();
|
||||
status_message_.Reset();
|
||||
num_fields_ = 0;
|
||||
@ -695,7 +787,35 @@ class Parser : public AsyncWrap, public StreamListener {
|
||||
}
|
||||
|
||||
|
||||
http_parser parser_;
|
||||
int TrackHeader(size_t len) {
|
||||
#ifdef NODE_EXPERIMENTAL_HTTP
|
||||
header_nread_ += len;
|
||||
if (header_nread_ >= kMaxHeaderSize) {
|
||||
llhttp_set_error_reason(&parser_, "Headers overflow");
|
||||
return HPE_USER;
|
||||
}
|
||||
#endif /* NODE_EXPERIMENTAL_HTTP */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int MaybePause() {
|
||||
#ifdef NODE_EXPERIMENTAL_HTTP
|
||||
CHECK_NE(execute_depth_, 0);
|
||||
|
||||
if (!pending_pause_) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
pending_pause_ = false;
|
||||
llhttp_set_error_reason(&parser_, "Paused in callback");
|
||||
return HPE_PAUSED;
|
||||
#else /* !NODE_EXPERIMENTAL_HTTP */
|
||||
return 0;
|
||||
#endif /* NODE_EXPERIMENTAL_HTTP */
|
||||
}
|
||||
|
||||
parser_t parser_;
|
||||
StringPtr fields_[32]; // header fields
|
||||
StringPtr values_[32]; // header values
|
||||
StringPtr url_;
|
||||
@ -707,25 +827,37 @@ class Parser : public AsyncWrap, public StreamListener {
|
||||
Local<Object> current_buffer_;
|
||||
size_t current_buffer_len_;
|
||||
char* current_buffer_data_;
|
||||
#ifdef NODE_EXPERIMENTAL_HTTP
|
||||
unsigned int execute_depth_ = 0;
|
||||
bool pending_pause_ = false;
|
||||
uint64_t header_nread_ = 0;
|
||||
#endif /* NODE_EXPERIMENTAL_HTTP */
|
||||
|
||||
// These are helper functions for filling `http_parser_settings`, which turn
|
||||
// a member function of Parser into a C-style HTTP parser callback.
|
||||
template <typename Parser, Parser> struct Proxy;
|
||||
template <typename Parser, typename ...Args, int (Parser::*Member)(Args...)>
|
||||
struct Proxy<int (Parser::*)(Args...), Member> {
|
||||
static int Raw(http_parser* p, Args ... args) {
|
||||
static int Raw(parser_t* p, Args ... args) {
|
||||
Parser* parser = ContainerOf(&Parser::parser_, p);
|
||||
return (parser->*Member)(std::forward<Args>(args)...);
|
||||
int rv = (parser->*Member)(std::forward<Args>(args)...);
|
||||
if (rv == 0) {
|
||||
rv = parser->MaybePause();
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
};
|
||||
|
||||
typedef int (Parser::*Call)();
|
||||
typedef int (Parser::*DataCall)(const char* at, size_t length);
|
||||
|
||||
static const struct http_parser_settings settings;
|
||||
static const parser_settings_t settings;
|
||||
#ifdef NODE_EXPERIMENTAL_HTTP
|
||||
static const uint64_t kMaxHeaderSize = 80 * 1024;
|
||||
#endif /* NODE_EXPERIMENTAL_HTTP */
|
||||
};
|
||||
|
||||
const struct http_parser_settings Parser::settings = {
|
||||
const parser_settings_t Parser::settings = {
|
||||
Proxy<Call, &Parser::on_message_begin>::Raw,
|
||||
Proxy<DataCall, &Parser::on_url>::Raw,
|
||||
Proxy<DataCall, &Parser::on_status>::Raw,
|
||||
@ -734,8 +866,13 @@ const struct http_parser_settings Parser::settings = {
|
||||
Proxy<Call, &Parser::on_headers_complete>::Raw,
|
||||
Proxy<DataCall, &Parser::on_body>::Raw,
|
||||
Proxy<Call, &Parser::on_message_complete>::Raw,
|
||||
nullptr, // on_chunk_header
|
||||
nullptr // on_chunk_complete
|
||||
#ifdef NODE_EXPERIMENTAL_HTTP
|
||||
Proxy<Call, &Parser::on_chunk_header>::Raw,
|
||||
Proxy<Call, &Parser::on_chunk_complete>::Raw,
|
||||
#else /* !NODE_EXPERIMENTAL_HTTP */
|
||||
nullptr,
|
||||
nullptr,
|
||||
#endif /* NODE_EXPERIMENTAL_HTTP */
|
||||
};
|
||||
|
||||
|
||||
|
@ -25,7 +25,7 @@ function flushPool() {
|
||||
function demoBug(part1, part2) {
|
||||
flushPool();
|
||||
|
||||
const parser = new HTTPParser(0);
|
||||
const parser = new HTTPParser(HTTPParser.REQUEST);
|
||||
|
||||
parser.headers = [];
|
||||
parser.url = '';
|
||||
|
@ -2,7 +2,7 @@
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
|
||||
const expected_keys = ['ares', 'http_parser', 'modules', 'node',
|
||||
const expected_keys = ['ares', 'modules', 'node',
|
||||
'uv', 'v8', 'zlib', 'nghttp2', 'napi'];
|
||||
|
||||
if (common.hasCrypto) {
|
||||
@ -16,6 +16,9 @@ if (common.hasIntl) {
|
||||
expected_keys.push('unicode');
|
||||
}
|
||||
|
||||
expected_keys.push(
|
||||
process.versions.llhttp === undefined ? 'http_parser' : 'llhttp');
|
||||
|
||||
expected_keys.sort();
|
||||
const actual_keys = Object.keys(process.versions).sort();
|
||||
|
||||
@ -24,7 +27,8 @@ assert.deepStrictEqual(actual_keys, expected_keys);
|
||||
const commonTemplate = /^\d+\.\d+\.\d+(?:-.*)?$/;
|
||||
|
||||
assert(commonTemplate.test(process.versions.ares));
|
||||
assert(commonTemplate.test(process.versions.http_parser));
|
||||
assert(commonTemplate.test(process.versions.llhttp === undefined ?
|
||||
process.versions.http_parser : process.versions.llhttp));
|
||||
assert(commonTemplate.test(process.versions.node));
|
||||
assert(commonTemplate.test(process.versions.uv));
|
||||
assert(commonTemplate.test(process.versions.zlib));
|
||||
|
@ -36,8 +36,9 @@ proc.once('exit', common.mustCall(() => {
|
||||
|
||||
assert(traces.some((trace) =>
|
||||
trace.name === 'node' &&
|
||||
trace.args.process.versions.http_parser ===
|
||||
process.versions.http_parser &&
|
||||
(trace.args.process.versions.http_parser ===
|
||||
process.versions.http_parser ||
|
||||
trace.args.process.versions.llhttp === process.versions.llhttp) &&
|
||||
trace.args.process.versions.node ===
|
||||
process.versions.node &&
|
||||
trace.args.process.versions.v8 ===
|
||||
|
@ -149,7 +149,7 @@ if (common.hasCrypto) { // eslint-disable-line node-core/crypto-check
|
||||
|
||||
{
|
||||
const { HTTPParser } = internalBinding('http_parser');
|
||||
testInitialized(new HTTPParser(0), 'HTTPParser');
|
||||
testInitialized(new HTTPParser(HTTPParser.REQUEST), 'HTTPParser');
|
||||
}
|
||||
|
||||
|
||||
|
@ -58,6 +58,7 @@ else
|
||||
fi
|
||||
|
||||
addlicense "libuv" "deps/uv" "$(cat ${rootdir}/deps/uv/LICENSE)"
|
||||
addlicense "llhttp" "deps/llhttp" "$(cat deps/llhttp/LICENSE-MIT)"
|
||||
addlicense "OpenSSL" "deps/openssl" \
|
||||
"$(sed -e '/^ \*\/$/,$d' -e '/^ [^*].*$/d' -e '/\/\*.*$/d' -e '/^$/d' -e 's/^[/ ]\* *//' ${rootdir}/deps/openssl/openssl/LICENSE)"
|
||||
addlicense "Punycode.js" "lib/punycode.js" \
|
||||
|
Loading…
x
Reference in New Issue
Block a user