Skip to content

Commit c8d5e31

Browse files
addaleaxBridgeAR
authored andcommitted
http: make parser choice a runtime flag
Add a `--http-parser=llhttp` vs `--http-parser=traditional` command line switch, to make testing and comparing the new llhttp-based implementation easier. PR-URL: #24739 Refs: #24730 Reviewed-By: Joyee Cheung <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Fedor Indutny <[email protected]> Reviewed-By: Gus Caplan <[email protected]> Reviewed-By: Matheus Marchini <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Ali Ijaz Sheikh <[email protected]>
1 parent a22ac0b commit c8d5e31

29 files changed

+157
-85
lines changed

configure.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@
187187
parser.add_option('--experimental-http-parser',
188188
action='store_true',
189189
dest='experimental_http_parser',
190-
help='use llhttp instead of http_parser')
190+
help='use llhttp instead of http_parser by default')
191191

192192
shared_optgroup.add_option('--shared-http-parser',
193193
action='store_true',

doc/api/cli.md

+16
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,22 @@ added: v6.0.0
119119
Force FIPS-compliant crypto on startup. (Cannot be disabled from script code.)
120120
(Same requirements as `--enable-fips`.)
121121

122+
### `--http-parser=library`
123+
<!-- YAML
124+
added: REPLACEME
125+
-->
126+
127+
Chooses an HTTP parser library. Available values are:
128+
129+
- `llhttp` for https://llhttp.org/
130+
- `legacy` for https://github.com/nodejs/http-parser
131+
132+
The default is `legacy`, unless otherwise specified when building Node.js.
133+
134+
This flag exists to aid in experimentation with the internal implementation of
135+
the Node.js http parser.
136+
This flag is likely to become a no-op and removed at some point in the future.
137+
122138
### `--icu-data-dir=file`
123139
<!-- YAML
124140
added: v0.11.15

doc/node.1

+6
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,12 @@ Enable experimental ES module support in VM module.
9797
.It Fl -experimental-worker
9898
Enable experimental worker threads using worker_threads module.
9999
.
100+
.It Fl -http-parser Ns = Ns Ar library
101+
Chooses an HTTP parser library. Available values are
102+
.Sy llhttp
103+
or
104+
.Sy legacy .
105+
.
100106
.It Fl -force-fips
101107
Force FIPS-compliant crypto on startup
102108
(Cannot be disabled from script code).

lib/_http_client.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,14 @@
2424
const util = require('util');
2525
const net = require('net');
2626
const url = require('url');
27-
const { HTTPParser } = internalBinding('http_parser');
2827
const assert = require('assert').ok;
2928
const {
3029
_checkIsHttpToken: checkIsHttpToken,
3130
debug,
3231
freeParser,
3332
httpSocketSetup,
34-
parsers
33+
parsers,
34+
HTTPParser,
3535
} = require('_http_common');
3636
const { OutgoingMessage } = require('_http_outgoing');
3737
const Agent = require('_http_agent');

lib/_http_common.js

+7-2
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,11 @@
2121

2222
'use strict';
2323

24-
const { methods, HTTPParser } = internalBinding('http_parser');
24+
const { getOptionValue } = require('internal/options');
25+
26+
const { methods, HTTPParser } =
27+
getOptionValue('--http-parser') === 'legacy' ?
28+
internalBinding('http_parser') : internalBinding('http_parser_llhttp');
2529

2630
const { FreeList } = require('internal/freelist');
2731
const { ondrain } = require('internal/http');
@@ -243,5 +247,6 @@ module.exports = {
243247
httpSocketSetup,
244248
methods,
245249
parsers,
246-
kIncomingMessage
250+
kIncomingMessage,
251+
HTTPParser
247252
};

lib/_http_server.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323

2424
const util = require('util');
2525
const net = require('net');
26-
const { HTTPParser } = internalBinding('http_parser');
2726
const assert = require('assert').ok;
2827
const {
2928
parsers,
@@ -34,6 +33,7 @@ const {
3433
chunkExpression,
3534
httpSocketSetup,
3635
kIncomingMessage,
36+
HTTPParser,
3737
_checkInvalidHeaderChar: checkInvalidHeaderChar
3838
} = require('_http_common');
3939
const { OutgoingMessage } = require('_http_outgoing');

lib/internal/child_process.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ const handleConversion = {
127127
if (freeParser === undefined)
128128
freeParser = require('_http_common').freeParser;
129129
if (HTTPParser === undefined)
130-
HTTPParser = internalBinding('http_parser').HTTPParser;
130+
HTTPParser = require('_http_common').HTTPParser;
131131

132132
// In case of an HTTP connection socket, release the associated
133133
// resources

node.gyp

+3-1
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,8 @@
352352
'src/node_encoding.cc',
353353
'src/node_errors.cc',
354354
'src/node_file.cc',
355-
'src/node_http_parser.cc',
355+
'src/node_http_parser_llhttp.cc',
356+
'src/node_http_parser_traditional.cc',
356357
'src/node_http2.cc',
357358
'src/node_i18n.cc',
358359
'src/node_messaging.cc',
@@ -423,6 +424,7 @@
423424
'src/node_contextify.h',
424425
'src/node_errors.h',
425426
'src/node_file.h',
427+
'src/node_http_parser_impl.h',
426428
'src/node_http2.h',
427429
'src/node_http2_state.h',
428430
'src/node_i18n.h',

node.gypi

+8-6
Original file line numberDiff line numberDiff line change
@@ -164,12 +164,14 @@
164164
}],
165165

166166
[ 'node_experimental_http_parser=="true"', {
167-
'defines': [ 'NODE_EXPERIMENTAL_HTTP' ],
168-
'dependencies': [ 'deps/llhttp/llhttp.gyp:llhttp' ],
169-
}, {
170-
'conditions': [ [ 'node_shared_http_parser=="false"', {
171-
'dependencies': [ 'deps/http_parser/http_parser.gyp:http_parser' ],
172-
} ] ],
167+
'defines': [ 'NODE_EXPERIMENTAL_HTTP_DEFAULT' ],
168+
} ],
169+
170+
[ 'node_shared_http_parser=="false"', {
171+
'dependencies': [
172+
'deps/http_parser/http_parser.gyp:http_parser',
173+
'deps/llhttp/llhttp.gyp:llhttp'
174+
],
173175
} ],
174176

175177
[ 'node_shared_cares=="false"', {

src/inspector_socket.cc

+10-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
#include "inspector_socket.h"
22

3+
#ifdef NODE_EXPERIMENTAL_HTTP_DEFAULT
4+
#define NODE_EXPERIMENTAL_HTTP
5+
#endif
36
#include "http_parser_adaptor.h"
7+
48
#include "util-inl.h"
59

610
#define NODE_WANT_INTERNALS 1
@@ -433,13 +437,13 @@ class HttpHandler : public ProtocolHandler {
433437
explicit HttpHandler(InspectorSocket* inspector, TcpHolder::Pointer tcp)
434438
: ProtocolHandler(inspector, std::move(tcp)),
435439
parsing_value_(false) {
436-
#ifdef NODE_EXPERIMENTAL_HTTP
440+
#ifdef NODE_EXPERIMENTAL_HTTP_DEFAULT
437441
llhttp_init(&parser_, HTTP_REQUEST, &parser_settings);
438442
llhttp_settings_init(&parser_settings);
439-
#else /* !NODE_EXPERIMENTAL_HTTP */
443+
#else /* !NODE_EXPERIMENTAL_HTTP_DEFAULT */
440444
http_parser_init(&parser_, HTTP_REQUEST);
441445
http_parser_settings_init(&parser_settings);
442-
#endif /* NODE_EXPERIMENTAL_HTTP */
446+
#endif /* NODE_EXPERIMENTAL_HTTP_DEFAULT */
443447
parser_settings.on_header_field = OnHeaderField;
444448
parser_settings.on_header_value = OnHeaderValue;
445449
parser_settings.on_message_complete = OnMessageComplete;
@@ -484,17 +488,17 @@ class HttpHandler : public ProtocolHandler {
484488

485489
void OnData(std::vector<char>* data) override {
486490
parser_errno_t err;
487-
#ifdef NODE_EXPERIMENTAL_HTTP
491+
#ifdef NODE_EXPERIMENTAL_HTTP_DEFAULT
488492
err = llhttp_execute(&parser_, data->data(), data->size());
489493

490494
if (err == HPE_PAUSED_UPGRADE) {
491495
err = HPE_OK;
492496
llhttp_resume_after_upgrade(&parser_);
493497
}
494-
#else /* !NODE_EXPERIMENTAL_HTTP */
498+
#else /* !NODE_EXPERIMENTAL_HTTP_DEFAULT */
495499
http_parser_execute(&parser_, &parser_settings, data->data(), data->size());
496500
err = HTTP_PARSER_ERRNO(&parser_);
497-
#endif /* NODE_EXPERIMENTAL_HTTP */
501+
#endif /* NODE_EXPERIMENTAL_HTTP_DEFAULT */
498502
data->clear();
499503
if (err != HPE_OK) {
500504
CancelHandshake();

src/node_binding.cc

+1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
V(heap_utils) \
3737
V(http2) \
3838
V(http_parser) \
39+
V(http_parser_llhttp) \
3940
V(inspector) \
4041
V(js_stream) \
4142
V(messaging) \

src/node_http_parser.cc src/node_http_parser_impl.h

+12-6
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@
1919
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
2020
// USE OR OTHER DEALINGS IN THE SOFTWARE.
2121

22+
// This file is included from 2 files, node_http_parser_traditional.cc
23+
// and node_http_parser_llhttp.cc.
24+
25+
#ifndef SRC_NODE_HTTP_PARSER_IMPL_H_
26+
#define SRC_NODE_HTTP_PARSER_IMPL_H_
27+
2228
#include "node.h"
2329
#include "node_buffer.h"
2430
#include "node_internals.h"
@@ -47,7 +53,7 @@
4753

4854

4955
namespace node {
50-
namespace {
56+
namespace { // NOLINT(build/namespaces)
5157

5258
using v8::Array;
5359
using v8::Boolean;
@@ -910,10 +916,10 @@ const parser_settings_t Parser::settings = {
910916
};
911917

912918

913-
void Initialize(Local<Object> target,
914-
Local<Value> unused,
915-
Local<Context> context,
916-
void* priv) {
919+
void InitializeHttpParser(Local<Object> target,
920+
Local<Value> unused,
921+
Local<Context> context,
922+
void* priv) {
917923
Environment* env = Environment::GetCurrent(context);
918924
Local<FunctionTemplate> t = env->NewFunctionTemplate(Parser::New);
919925
t->InstanceTemplate()->SetInternalFieldCount(1);
@@ -964,4 +970,4 @@ void Initialize(Local<Object> target,
964970
} // anonymous namespace
965971
} // namespace node
966972

967-
NODE_MODULE_CONTEXT_AWARE_INTERNAL(http_parser, node::Initialize)
973+
#endif // SRC_NODE_HTTP_PARSER_IMPL_H_

src/node_http_parser_llhttp.cc

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#define NODE_EXPERIMENTAL_HTTP 1
2+
3+
#include "node_http_parser_impl.h"
4+
5+
namespace node {
6+
7+
const char* llhttp_version =
8+
NODE_STRINGIFY(LLHTTP_VERSION_MAJOR)
9+
"."
10+
NODE_STRINGIFY(LLHTTP_VERSION_MINOR)
11+
"."
12+
NODE_STRINGIFY(LLHTTP_VERSION_PATCH);
13+
14+
} // namespace node
15+
16+
NODE_MODULE_CONTEXT_AWARE_INTERNAL(http_parser_llhttp,
17+
node::InitializeHttpParser)

src/node_http_parser_traditional.cc

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#ifdef NODE_EXPERIMENTAL_HTTP
2+
#undef NODE_EXPERIMENTAL_HTTP
3+
#endif
4+
5+
#include "node_http_parser_impl.h"
6+
7+
namespace node {
8+
9+
const char* http_parser_version =
10+
NODE_STRINGIFY(HTTP_PARSER_VERSION_MAJOR)
11+
"."
12+
NODE_STRINGIFY(HTTP_PARSER_VERSION_MINOR)
13+
"."
14+
NODE_STRINGIFY(HTTP_PARSER_VERSION_PATCH);
15+
16+
} // namespace node
17+
18+
NODE_MODULE_CONTEXT_AWARE_INTERNAL(http_parser, node::InitializeHttpParser)

src/node_internals.h

+3
Original file line numberDiff line numberDiff line change
@@ -697,6 +697,9 @@ static inline const char* errno_string(int errorno) {
697697

698698
extern double prog_start_time;
699699

700+
extern const char* llhttp_version;
701+
extern const char* http_parser_version;
702+
700703
void Abort(const v8::FunctionCallbackInfo<v8::Value>& args);
701704
void Chdir(const v8::FunctionCallbackInfo<v8::Value>& args);
702705
void CPUUsage(const v8::FunctionCallbackInfo<v8::Value>& args);

src/node_metadata.cc

+2-14
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,6 @@
1111
#include "node_crypto.h"
1212
#endif
1313

14-
#ifdef NODE_EXPERIMENTAL_HTTP
15-
#include "llhttp.h"
16-
#else /* !NODE_EXPERIMENTAL_HTTP */
17-
#include "http_parser.h"
18-
#endif /* NODE_EXPERIMENTAL_HTTP */
19-
2014
namespace node {
2115

2216
namespace per_process {
@@ -32,14 +26,8 @@ Metadata::Versions::Versions() {
3226
modules = NODE_STRINGIFY(NODE_MODULE_VERSION);
3327
nghttp2 = NGHTTP2_VERSION;
3428
napi = NODE_STRINGIFY(NAPI_VERSION);
35-
36-
#ifdef NODE_EXPERIMENTAL_HTTP
37-
llhttp = NODE_STRINGIFY(LLHTTP_VERSION_MAJOR) "." NODE_STRINGIFY(
38-
LLHTTP_VERSION_MINOR) "." NODE_STRINGIFY(LLHTTP_VERSION_PATCH);
39-
#else /* !NODE_EXPERIMENTAL_HTTP */
40-
http_parser = NODE_STRINGIFY(HTTP_PARSER_VERSION_MAJOR) "." NODE_STRINGIFY(
41-
HTTP_PARSER_VERSION_MINOR) "." NODE_STRINGIFY(HTTP_PARSER_VERSION_PATCH);
42-
#endif /* NODE_EXPERIMENTAL_HTTP */
29+
llhttp = llhttp_version;
30+
http_parser = http_parser_version;
4331

4432
#if HAVE_OPENSSL
4533
openssl = crypto::GetOpenSSLVersion();

src/node_metadata.h

+3-8
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,9 @@ namespace node {
1515
V(ares) \
1616
V(modules) \
1717
V(nghttp2) \
18-
V(napi)
19-
20-
#ifdef NODE_EXPERIMENTAL_HTTP
21-
#define NODE_VERSIONS_KEY_HTTP(V) V(llhttp)
22-
#else /* !NODE_EXPERIMENTAL_HTTP */
23-
#define NODE_VERSIONS_KEY_HTTP(V) V(http_parser)
24-
#endif /* NODE_EXPERIMENTAL_HTTP */
18+
V(napi) \
19+
V(llhttp) \
20+
V(http_parser) \
2521

2622
#if HAVE_OPENSSL
2723
#define NODE_VERSIONS_KEY_CRYPTO(V) V(openssl)
@@ -31,7 +27,6 @@ namespace node {
3127

3228
#define NODE_VERSIONS_KEYS(V) \
3329
NODE_VERSIONS_KEYS_BASE(V) \
34-
NODE_VERSIONS_KEY_HTTP(V) \
3530
NODE_VERSIONS_KEY_CRYPTO(V)
3631

3732
class Metadata {

src/node_options.cc

+14
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ void EnvironmentOptions::CheckOptions(std::vector<std::string>* errors) {
3939
if (syntax_check_only && has_eval_string) {
4040
errors->push_back("either --check or --eval can be used, not both");
4141
}
42+
43+
if (http_parser != "legacy" && http_parser != "llhttp") {
44+
errors->push_back("invalid value for --http-parser");
45+
}
46+
4247
debug_options->CheckOptions(errors);
4348
}
4449

@@ -102,6 +107,15 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
102107
&EnvironmentOptions::experimental_worker,
103108
kAllowedInEnvironment);
104109
AddOption("--expose-internals", "", &EnvironmentOptions::expose_internals);
110+
AddOption("--http-parser",
111+
"Select which HTTP parser to use; either 'legacy' or 'llhttp' "
112+
#ifdef NODE_EXPERIMENTAL_HTTP_DEFAULT
113+
"(default: llhttp).",
114+
#else
115+
"(default: legacy).",
116+
#endif
117+
&EnvironmentOptions::http_parser,
118+
kAllowedInEnvironment);
105119
AddOption("--loader",
106120
"(with --experimental-modules) use the specified file as a "
107121
"custom loader",

src/node_options.h

+6
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,12 @@ class EnvironmentOptions : public Options {
7272
bool experimental_vm_modules = false;
7373
bool experimental_worker = false;
7474
bool expose_internals = false;
75+
std::string http_parser =
76+
#ifdef NODE_EXPERIMENTAL_HTTP_DEFAULT
77+
"llhttp";
78+
#else
79+
"legacy";
80+
#endif
7581
bool no_deprecation = false;
7682
bool no_force_async_hooks_checks = false;
7783
bool no_warnings = false;

0 commit comments

Comments
 (0)