1 |
|
|
// Copyright Joyent, Inc. and other Node contributors. |
2 |
|
|
// |
3 |
|
|
// Permission is hereby granted, free of charge, to any person obtaining a |
4 |
|
|
// copy of this software and associated documentation files (the |
5 |
|
|
// "Software"), to deal in the Software without restriction, including |
6 |
|
|
// without limitation the rights to use, copy, modify, merge, publish, |
7 |
|
|
// distribute, sublicense, and/or sell copies of the Software, and to permit |
8 |
|
|
// persons to whom the Software is furnished to do so, subject to the |
9 |
|
|
// following conditions: |
10 |
|
|
// |
11 |
|
|
// The above copyright notice and this permission notice shall be included |
12 |
|
|
// in all copies or substantial portions of the Software. |
13 |
|
|
// |
14 |
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
15 |
|
|
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
16 |
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN |
17 |
|
|
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, |
18 |
|
|
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
19 |
|
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE |
20 |
|
|
// USE OR OTHER DEALINGS IN THE SOFTWARE. |
21 |
|
|
|
22 |
|
|
#include "node.h" |
23 |
|
|
#include "node_buffer.h" |
24 |
|
|
#include "util.h" |
25 |
|
|
|
26 |
|
|
#include "async_wrap-inl.h" |
27 |
|
|
#include "env-inl.h" |
28 |
|
|
#include "memory_tracker-inl.h" |
29 |
|
|
#include "stream_base-inl.h" |
30 |
|
|
#include "v8.h" |
31 |
|
|
#include "llhttp.h" |
32 |
|
|
|
33 |
|
|
#include <cstdlib> // free() |
34 |
|
|
#include <cstring> // strdup(), strchr() |
35 |
|
|
|
36 |
|
|
|
37 |
|
|
// This is a binding to llhttp (https://github.com/nodejs/llhttp) |
38 |
|
|
// The goal is to decouple sockets from parsing for more javascript-level |
39 |
|
|
// agility. A Buffer is read from a socket and passed to parser.execute(). |
40 |
|
|
// The parser then issues callbacks with slices of the data |
41 |
|
|
// parser.onMessageBegin |
42 |
|
|
// parser.onPath |
43 |
|
|
// parser.onBody |
44 |
|
|
// ... |
45 |
|
|
// No copying is performed when slicing the buffer, only small reference |
46 |
|
|
// allocations. |
47 |
|
|
|
48 |
|
|
|
49 |
|
|
namespace node { |
50 |
|
|
namespace { // NOLINT(build/namespaces) |
51 |
|
|
|
52 |
|
|
using v8::Array; |
53 |
|
|
using v8::Boolean; |
54 |
|
|
using v8::Context; |
55 |
|
|
using v8::EscapableHandleScope; |
56 |
|
|
using v8::Exception; |
57 |
|
|
using v8::Function; |
58 |
|
|
using v8::FunctionCallbackInfo; |
59 |
|
|
using v8::FunctionTemplate; |
60 |
|
|
using v8::HandleScope; |
61 |
|
|
using v8::Int32; |
62 |
|
|
using v8::Integer; |
63 |
|
|
using v8::Local; |
64 |
|
|
using v8::MaybeLocal; |
65 |
|
|
using v8::Number; |
66 |
|
|
using v8::Object; |
67 |
|
|
using v8::String; |
68 |
|
|
using v8::Uint32; |
69 |
|
|
using v8::Undefined; |
70 |
|
|
using v8::Value; |
71 |
|
|
|
72 |
|
|
const uint32_t kOnMessageBegin = 0; |
73 |
|
|
const uint32_t kOnHeaders = 1; |
74 |
|
|
const uint32_t kOnHeadersComplete = 2; |
75 |
|
|
const uint32_t kOnBody = 3; |
76 |
|
|
const uint32_t kOnMessageComplete = 4; |
77 |
|
|
const uint32_t kOnExecute = 5; |
78 |
|
|
const uint32_t kOnTimeout = 6; |
79 |
|
|
// Any more fields than this will be flushed into JS |
80 |
|
|
const size_t kMaxHeaderFieldsCount = 32; |
81 |
|
|
|
82 |
|
|
const uint32_t kLenientNone = 0; |
83 |
|
|
const uint32_t kLenientHeaders = 1 << 0; |
84 |
|
|
const uint32_t kLenientChunkedLength = 1 << 1; |
85 |
|
|
const uint32_t kLenientKeepAlive = 1 << 2; |
86 |
|
|
const uint32_t kLenientAll = kLenientHeaders | kLenientChunkedLength | |
87 |
|
|
kLenientKeepAlive; |
88 |
|
|
|
89 |
|
16794 |
inline bool IsOWS(char c) { |
90 |
✓✓✓✓
|
16794 |
return c == ' ' || c == '\t'; |
91 |
|
|
} |
92 |
|
|
|
93 |
|
|
class BindingData : public BaseObject { |
94 |
|
|
public: |
95 |
|
800 |
BindingData(Environment* env, Local<Object> obj) |
96 |
|
800 |
: BaseObject(env, obj) {} |
97 |
|
|
|
98 |
|
|
static constexpr FastStringKey type_name { "http_parser" }; |
99 |
|
|
|
100 |
|
|
std::vector<char> parser_buffer; |
101 |
|
|
bool parser_buffer_in_use = false; |
102 |
|
|
|
103 |
|
3 |
void MemoryInfo(MemoryTracker* tracker) const override { |
104 |
|
3 |
tracker->TrackField("parser_buffer", parser_buffer); |
105 |
|
3 |
} |
106 |
|
3 |
SET_SELF_SIZE(BindingData) |
107 |
|
3 |
SET_MEMORY_INFO_NAME(BindingData) |
108 |
|
|
}; |
109 |
|
|
|
110 |
|
|
// TODO(addaleax): Remove once we're on C++17. |
111 |
|
|
constexpr FastStringKey BindingData::type_name; |
112 |
|
|
|
113 |
|
|
// helper class for the Parser |
114 |
|
|
struct StringPtr { |
115 |
|
240702 |
StringPtr() { |
116 |
|
240702 |
on_heap_ = false; |
117 |
|
240702 |
Reset(); |
118 |
|
240702 |
} |
119 |
|
|
|
120 |
|
|
|
121 |
|
475332 |
~StringPtr() { |
122 |
|
237666 |
Reset(); |
123 |
|
237666 |
} |
124 |
|
|
|
125 |
|
|
|
126 |
|
|
// If str_ does not point to a heap string yet, this function makes it do |
127 |
|
|
// so. This is called at the end of each http_parser_execute() so as not |
128 |
|
|
// to leak references. See issue #2438 and test-http-parser-bad-ref.js. |
129 |
|
14646 |
void Save() { |
130 |
✓✓✓✓
|
14646 |
if (!on_heap_ && size_ > 0) { |
131 |
|
6033 |
char* s = new char[size_]; |
132 |
|
6033 |
memcpy(s, str_, size_); |
133 |
|
6033 |
str_ = s; |
134 |
|
6033 |
on_heap_ = true; |
135 |
|
|
} |
136 |
|
14646 |
} |
137 |
|
|
|
138 |
|
|
|
139 |
|
558454 |
void Reset() { |
140 |
✓✓ |
558454 |
if (on_heap_) { |
141 |
✓✗ |
5986 |
delete[] str_; |
142 |
|
5986 |
on_heap_ = false; |
143 |
|
|
} |
144 |
|
|
|
145 |
|
558454 |
str_ = nullptr; |
146 |
|
558454 |
size_ = 0; |
147 |
|
558454 |
} |
148 |
|
|
|
149 |
|
|
|
150 |
|
50789 |
void Update(const char* str, size_t size) { |
151 |
✓✓ |
50789 |
if (str_ == nullptr) { |
152 |
|
50723 |
str_ = str; |
153 |
✓✓✓✗
|
66 |
} else if (on_heap_ || str_ + size_ != str) { |
154 |
|
|
// Non-consecutive input, make a copy on the heap. |
155 |
|
|
// TODO(bnoordhuis) Use slab allocation, O(n) allocs is bad. |
156 |
|
66 |
char* s = new char[size_ + size]; |
157 |
|
66 |
memcpy(s, str_, size_); |
158 |
|
66 |
memcpy(s + size_, str, size); |
159 |
|
|
|
160 |
✓✓ |
66 |
if (on_heap_) |
161 |
✓✗ |
65 |
delete[] str_; |
162 |
|
|
else |
163 |
|
1 |
on_heap_ = true; |
164 |
|
|
|
165 |
|
66 |
str_ = s; |
166 |
|
|
} |
167 |
|
50789 |
size_ += size; |
168 |
|
50789 |
} |
169 |
|
|
|
170 |
|
|
|
171 |
|
50628 |
Local<String> ToString(Environment* env) const { |
172 |
✓✓ |
50628 |
if (size_ != 0) |
173 |
|
50564 |
return OneByteString(env->isolate(), str_, size_); |
174 |
|
|
else |
175 |
|
128 |
return String::Empty(env->isolate()); |
176 |
|
|
} |
177 |
|
|
|
178 |
|
|
|
179 |
|
|
// Strip trailing OWS (SPC or HTAB) from string. |
180 |
|
16796 |
Local<String> ToTrimmedString(Environment* env) { |
181 |
✓✓✓✓ ✓✓ |
16796 |
while (size_ > 0 && IsOWS(str_[size_ - 1])) { |
182 |
|
11 |
size_--; |
183 |
|
|
} |
184 |
|
16785 |
return ToString(env); |
185 |
|
|
} |
186 |
|
|
|
187 |
|
|
|
188 |
|
|
const char* str_; |
189 |
|
|
bool on_heap_; |
190 |
|
|
size_t size_; |
191 |
|
|
}; |
192 |
|
|
|
193 |
|
|
class Parser : public AsyncWrap, public StreamListener { |
194 |
|
|
public: |
195 |
|
3647 |
Parser(BindingData* binding_data, Local<Object> wrap) |
196 |
|
3647 |
: AsyncWrap(binding_data->env(), wrap), |
197 |
|
|
current_buffer_len_(0), |
198 |
|
|
current_buffer_data_(nullptr), |
199 |
✓✓✓✓
|
237055 |
binding_data_(binding_data) { |
200 |
|
3647 |
} |
201 |
|
|
|
202 |
|
|
|
203 |
|
|
void MemoryInfo(MemoryTracker* tracker) const override { |
204 |
|
|
tracker->TrackField("current_buffer", current_buffer_); |
205 |
|
|
} |
206 |
|
|
|
207 |
|
|
SET_MEMORY_INFO_NAME(Parser) |
208 |
|
|
SET_SELF_SIZE(Parser) |
209 |
|
|
|
210 |
|
17048 |
int on_message_begin() { |
211 |
|
17048 |
num_fields_ = num_values_ = 0; |
212 |
|
17048 |
url_.Reset(); |
213 |
|
17048 |
status_message_.Reset(); |
214 |
|
17048 |
header_parsing_start_time_ = uv_hrtime(); |
215 |
|
|
|
216 |
|
34096 |
Local<Value> cb = object()->Get(env()->context(), kOnMessageBegin) |
217 |
|
17048 |
.ToLocalChecked(); |
218 |
✓✓ |
17048 |
if (cb->IsFunction()) { |
219 |
|
|
InternalCallbackScope callback_scope( |
220 |
|
27550 |
this, InternalCallbackScope::kSkipTaskQueues); |
221 |
|
|
|
222 |
|
13775 |
MaybeLocal<Value> r = cb.As<Function>()->Call( |
223 |
|
27550 |
env()->context(), object(), 0, nullptr); |
224 |
|
|
|
225 |
✗✓ |
13775 |
if (r.IsEmpty()) callback_scope.MarkAsFailed(); |
226 |
|
|
} |
227 |
|
|
|
228 |
|
17048 |
return 0; |
229 |
|
|
} |
230 |
|
|
|
231 |
|
|
|
232 |
|
13939 |
int on_url(const char* at, size_t length) { |
233 |
|
13939 |
int rv = TrackHeader(length); |
234 |
✗✓ |
13939 |
if (rv != 0) { |
235 |
|
|
return rv; |
236 |
|
|
} |
237 |
|
|
|
238 |
|
13939 |
url_.Update(at, length); |
239 |
|
13939 |
return 0; |
240 |
|
|
} |
241 |
|
|
|
242 |
|
|
|
243 |
|
3101 |
int on_status(const char* at, size_t length) { |
244 |
|
3101 |
int rv = TrackHeader(length); |
245 |
✗✓ |
3101 |
if (rv != 0) { |
246 |
|
|
return rv; |
247 |
|
|
} |
248 |
|
|
|
249 |
|
3101 |
status_message_.Update(at, length); |
250 |
|
3101 |
return 0; |
251 |
|
|
} |
252 |
|
|
|
253 |
|
|
|
254 |
|
16887 |
int on_header_field(const char* at, size_t length) { |
255 |
|
16887 |
int rv = TrackHeader(length); |
256 |
✗✓ |
16887 |
if (rv != 0) { |
257 |
|
|
return rv; |
258 |
|
|
} |
259 |
|
|
|
260 |
✓✓ |
16887 |
if (num_fields_ == num_values_) { |
261 |
|
|
// start of new field name |
262 |
|
16858 |
num_fields_++; |
263 |
✓✓ |
16858 |
if (num_fields_ == kMaxHeaderFieldsCount) { |
264 |
|
|
// ran out of space - flush to javascript land |
265 |
|
47 |
Flush(); |
266 |
|
47 |
num_fields_ = 1; |
267 |
|
47 |
num_values_ = 0; |
268 |
|
|
} |
269 |
|
16858 |
fields_[num_fields_ - 1].Reset(); |
270 |
|
|
} |
271 |
|
|
|
272 |
✗✓ |
16887 |
CHECK_LT(num_fields_, kMaxHeaderFieldsCount); |
273 |
✗✓ |
16887 |
CHECK_EQ(num_fields_, num_values_ + 1); |
274 |
|
|
|
275 |
|
16887 |
fields_[num_fields_ - 1].Update(at, length); |
276 |
|
|
|
277 |
|
16887 |
return 0; |
278 |
|
|
} |
279 |
|
|
|
280 |
|
|
|
281 |
|
16879 |
int on_header_value(const char* at, size_t length) { |
282 |
|
16879 |
int rv = TrackHeader(length); |
283 |
✓✓ |
16879 |
if (rv != 0) { |
284 |
|
17 |
return rv; |
285 |
|
|
} |
286 |
|
|
|
287 |
✓✓ |
16862 |
if (num_values_ != num_fields_) { |
288 |
|
|
// start of new header value |
289 |
|
16833 |
num_values_++; |
290 |
|
16833 |
values_[num_values_ - 1].Reset(); |
291 |
|
|
} |
292 |
|
|
|
293 |
✗✓ |
16862 |
CHECK_LT(num_values_, arraysize(values_)); |
294 |
✗✓ |
16862 |
CHECK_EQ(num_values_, num_fields_); |
295 |
|
|
|
296 |
|
16862 |
values_[num_values_ - 1].Update(at, length); |
297 |
|
|
|
298 |
|
16862 |
return 0; |
299 |
|
|
} |
300 |
|
|
|
301 |
|
|
|
302 |
|
16999 |
int on_headers_complete() { |
303 |
|
16999 |
header_nread_ = 0; |
304 |
|
16999 |
header_parsing_start_time_ = 0; |
305 |
|
|
|
306 |
|
|
// Arguments for the on-headers-complete javascript callback. This |
307 |
|
|
// list needs to be kept in sync with the actual argument list for |
308 |
|
|
// `parserOnHeadersComplete` in lib/_http_common.js. |
309 |
|
|
enum on_headers_complete_arg_index { |
310 |
|
|
A_VERSION_MAJOR = 0, |
311 |
|
|
A_VERSION_MINOR, |
312 |
|
|
A_HEADERS, |
313 |
|
|
A_METHOD, |
314 |
|
|
A_URL, |
315 |
|
|
A_STATUS_CODE, |
316 |
|
|
A_STATUS_MESSAGE, |
317 |
|
|
A_UPGRADE, |
318 |
|
|
A_SHOULD_KEEP_ALIVE, |
319 |
|
|
A_MAX |
320 |
|
|
}; |
321 |
|
|
|
322 |
✓✓ |
169990 |
Local<Value> argv[A_MAX]; |
323 |
|
16999 |
Local<Object> obj = object(); |
324 |
|
16999 |
Local<Value> cb = obj->Get(env()->context(), |
325 |
|
33998 |
kOnHeadersComplete).ToLocalChecked(); |
326 |
|
|
|
327 |
✗✓ |
16999 |
if (!cb->IsFunction()) |
328 |
|
|
return 0; |
329 |
|
|
|
330 |
|
16999 |
Local<Value> undefined = Undefined(env()->isolate()); |
331 |
✓✓ |
169990 |
for (size_t i = 0; i < arraysize(argv); i++) |
332 |
|
152991 |
argv[i] = undefined; |
333 |
|
|
|
334 |
✓✓ |
16999 |
if (have_flushed_) { |
335 |
|
|
// Slow case, flush remaining headers. |
336 |
|
16 |
Flush(); |
337 |
|
|
} else { |
338 |
|
|
// Fast case, pass headers and URL to JS land. |
339 |
|
16983 |
argv[A_HEADERS] = CreateHeaders(); |
340 |
✓✓ |
16983 |
if (parser_.type == HTTP_REQUEST) |
341 |
|
27798 |
argv[A_URL] = url_.ToString(env()); |
342 |
|
|
} |
343 |
|
|
|
344 |
|
16999 |
num_fields_ = 0; |
345 |
|
16999 |
num_values_ = 0; |
346 |
|
|
|
347 |
|
|
// METHOD |
348 |
✓✓ |
16999 |
if (parser_.type == HTTP_REQUEST) { |
349 |
|
27814 |
argv[A_METHOD] = |
350 |
|
13907 |
Uint32::NewFromUnsigned(env()->isolate(), parser_.method); |
351 |
|
|
} |
352 |
|
|
|
353 |
|
|
// STATUS |
354 |
✓✓ |
16999 |
if (parser_.type == HTTP_RESPONSE) { |
355 |
|
3092 |
argv[A_STATUS_CODE] = |
356 |
|
3092 |
Integer::New(env()->isolate(), parser_.status_code); |
357 |
|
6184 |
argv[A_STATUS_MESSAGE] = status_message_.ToString(env()); |
358 |
|
|
} |
359 |
|
|
|
360 |
|
|
// VERSION |
361 |
|
16999 |
argv[A_VERSION_MAJOR] = Integer::New(env()->isolate(), parser_.http_major); |
362 |
|
16999 |
argv[A_VERSION_MINOR] = Integer::New(env()->isolate(), parser_.http_minor); |
363 |
|
|
|
364 |
|
|
bool should_keep_alive; |
365 |
|
16999 |
should_keep_alive = llhttp_should_keep_alive(&parser_); |
366 |
|
|
|
367 |
|
16999 |
argv[A_SHOULD_KEEP_ALIVE] = |
368 |
|
|
Boolean::New(env()->isolate(), should_keep_alive); |
369 |
|
|
|
370 |
|
33998 |
argv[A_UPGRADE] = Boolean::New(env()->isolate(), parser_.upgrade); |
371 |
|
|
|
372 |
|
|
MaybeLocal<Value> head_response; |
373 |
|
|
{ |
374 |
|
|
InternalCallbackScope callback_scope( |
375 |
|
33996 |
this, InternalCallbackScope::kSkipTaskQueues); |
376 |
|
16999 |
head_response = cb.As<Function>()->Call( |
377 |
|
33998 |
env()->context(), object(), arraysize(argv), argv); |
378 |
✓✓ |
16997 |
if (head_response.IsEmpty()) callback_scope.MarkAsFailed(); |
379 |
|
|
} |
380 |
|
|
|
381 |
|
|
int64_t val; |
382 |
|
|
|
383 |
✓✓ |
50975 |
if (head_response.IsEmpty() || !head_response.ToLocalChecked() |
384 |
✓✓ |
33986 |
->IntegerValue(env()->context()) |
385 |
✗✓ |
16989 |
.To(&val)) { |
386 |
|
8 |
got_exception_ = true; |
387 |
|
8 |
return -1; |
388 |
|
|
} |
389 |
|
|
|
390 |
|
16989 |
return static_cast<int>(val); |
391 |
|
|
} |
392 |
|
|
|
393 |
|
|
|
394 |
|
11106 |
int on_body(const char* at, size_t length) { |
395 |
|
11106 |
EscapableHandleScope scope(env()->isolate()); |
396 |
|
|
|
397 |
|
11106 |
Local<Object> obj = object(); |
398 |
|
22212 |
Local<Value> cb = obj->Get(env()->context(), kOnBody).ToLocalChecked(); |
399 |
|
|
|
400 |
✗✓ |
11106 |
if (!cb->IsFunction()) |
401 |
|
|
return 0; |
402 |
|
|
|
403 |
|
|
// We came from consumed stream |
404 |
✓✓ |
11106 |
if (current_buffer_.IsEmpty()) { |
405 |
|
|
// Make sure Buffer will be in parent HandleScope |
406 |
|
517 |
current_buffer_ = scope.Escape(Buffer::Copy( |
407 |
|
|
env()->isolate(), |
408 |
|
|
current_buffer_data_, |
409 |
|
1034 |
current_buffer_len_).ToLocalChecked()); |
410 |
|
|
} |
411 |
|
|
|
412 |
|
|
Local<Value> argv[3] = { |
413 |
|
|
current_buffer_, |
414 |
|
|
Integer::NewFromUnsigned( |
415 |
|
11106 |
env()->isolate(), static_cast<uint32_t>(at - current_buffer_data_)), |
416 |
|
22212 |
Integer::NewFromUnsigned(env()->isolate(), length)}; |
417 |
|
|
|
418 |
|
|
MaybeLocal<Value> r = MakeCallback(cb.As<Function>(), |
419 |
|
11106 |
arraysize(argv), |
420 |
|
22212 |
argv); |
421 |
|
|
|
422 |
✗✓ |
11106 |
if (r.IsEmpty()) { |
423 |
|
|
got_exception_ = true; |
424 |
|
|
llhttp_set_error_reason(&parser_, "HPE_JS_EXCEPTION:JS Exception"); |
425 |
|
|
return HPE_USER; |
426 |
|
|
} |
427 |
|
|
|
428 |
|
11106 |
return 0; |
429 |
|
|
} |
430 |
|
|
|
431 |
|
|
|
432 |
|
15792 |
int on_message_complete() { |
433 |
|
31584 |
HandleScope scope(env()->isolate()); |
434 |
|
|
|
435 |
✓✓ |
15792 |
if (num_fields_) |
436 |
|
4 |
Flush(); // Flush trailing HTTP headers. |
437 |
|
|
|
438 |
|
15792 |
Local<Object> obj = object(); |
439 |
|
15792 |
Local<Value> cb = obj->Get(env()->context(), |
440 |
|
31584 |
kOnMessageComplete).ToLocalChecked(); |
441 |
|
|
|
442 |
✓✓ |
15792 |
if (!cb->IsFunction()) |
443 |
|
2 |
return 0; |
444 |
|
|
|
445 |
|
|
MaybeLocal<Value> r; |
446 |
|
|
{ |
447 |
|
|
InternalCallbackScope callback_scope( |
448 |
|
31580 |
this, InternalCallbackScope::kSkipTaskQueues); |
449 |
|
31580 |
r = cb.As<Function>()->Call(env()->context(), object(), 0, nullptr); |
450 |
✗✓ |
15790 |
if (r.IsEmpty()) callback_scope.MarkAsFailed(); |
451 |
|
|
} |
452 |
|
|
|
453 |
✗✓ |
15790 |
if (r.IsEmpty()) { |
454 |
|
|
got_exception_ = true; |
455 |
|
|
return -1; |
456 |
|
|
} |
457 |
|
|
|
458 |
|
15790 |
return 0; |
459 |
|
|
} |
460 |
|
|
|
461 |
|
|
// Reset nread for the next chunk |
462 |
|
10941 |
int on_chunk_header() { |
463 |
|
10941 |
header_nread_ = 0; |
464 |
|
10941 |
return 0; |
465 |
|
|
} |
466 |
|
|
|
467 |
|
|
|
468 |
|
|
// Reset nread for the next chunk |
469 |
|
10787 |
int on_chunk_complete() { |
470 |
|
10787 |
header_nread_ = 0; |
471 |
|
10787 |
return 0; |
472 |
|
|
} |
473 |
|
|
|
474 |
|
3647 |
static void New(const FunctionCallbackInfo<Value>& args) { |
475 |
|
3647 |
BindingData* binding_data = Environment::GetBindingData<BindingData>(args); |
476 |
|
3647 |
new Parser(binding_data, args.This()); |
477 |
|
3647 |
} |
478 |
|
|
|
479 |
|
|
|
480 |
|
3 |
static void Close(const FunctionCallbackInfo<Value>& args) { |
481 |
|
|
Parser* parser; |
482 |
✗✓ |
3 |
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
483 |
|
|
|
484 |
✓✗ |
3 |
delete parser; |
485 |
|
|
} |
486 |
|
|
|
487 |
|
|
|
488 |
|
5919 |
static void Free(const FunctionCallbackInfo<Value>& args) { |
489 |
|
|
Parser* parser; |
490 |
✗✓ |
5919 |
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
491 |
|
|
|
492 |
|
|
// Since the Parser destructor isn't going to run the destroy() callbacks |
493 |
|
|
// it needs to be triggered manually. |
494 |
|
5919 |
parser->EmitTraceEventDestroy(); |
495 |
|
5919 |
parser->EmitDestroy(); |
496 |
|
|
} |
497 |
|
|
|
498 |
|
|
|
499 |
|
7142 |
void Save() { |
500 |
|
7142 |
url_.Save(); |
501 |
|
7142 |
status_message_.Save(); |
502 |
|
|
|
503 |
✓✓ |
7354 |
for (size_t i = 0; i < num_fields_; i++) { |
504 |
|
212 |
fields_[i].Save(); |
505 |
|
|
} |
506 |
|
|
|
507 |
✓✓ |
7292 |
for (size_t i = 0; i < num_values_; i++) { |
508 |
|
150 |
values_[i].Save(); |
509 |
|
|
} |
510 |
|
7142 |
} |
511 |
|
|
|
512 |
|
|
|
513 |
|
|
// var bytesParsed = parser->execute(buffer); |
514 |
|
3169 |
static void Execute(const FunctionCallbackInfo<Value>& args) { |
515 |
|
|
Parser* parser; |
516 |
✗✓ |
3169 |
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
517 |
✗✓ |
3169 |
CHECK(parser->current_buffer_.IsEmpty()); |
518 |
✗✓ |
3169 |
CHECK_EQ(parser->current_buffer_len_, 0); |
519 |
✗✓ |
3169 |
CHECK_NULL(parser->current_buffer_data_); |
520 |
|
|
|
521 |
|
3169 |
ArrayBufferViewContents<char> buffer(args[0]); |
522 |
|
|
|
523 |
|
|
// This is a hack to get the current_buffer to the callbacks with the least |
524 |
|
|
// amount of overhead. Nothing else will run while http_parser_execute() |
525 |
|
|
// runs, therefore this pointer can be set and used for the execution. |
526 |
|
3169 |
parser->current_buffer_ = args[0].As<Object>(); |
527 |
|
|
|
528 |
|
3169 |
Local<Value> ret = parser->Execute(buffer.data(), buffer.length()); |
529 |
|
|
|
530 |
✓✓ |
3168 |
if (!ret.IsEmpty()) |
531 |
|
6330 |
args.GetReturnValue().Set(ret); |
532 |
|
|
} |
533 |
|
|
|
534 |
|
|
|
535 |
|
990 |
static void Finish(const FunctionCallbackInfo<Value>& args) { |
536 |
|
|
Parser* parser; |
537 |
✗✓ |
990 |
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
538 |
|
|
|
539 |
✗✓ |
990 |
CHECK(parser->current_buffer_.IsEmpty()); |
540 |
|
990 |
Local<Value> ret = parser->Execute(nullptr, 0); |
541 |
|
|
|
542 |
✓✓ |
990 |
if (!ret.IsEmpty()) |
543 |
|
66 |
args.GetReturnValue().Set(ret); |
544 |
|
|
} |
545 |
|
|
|
546 |
|
|
|
547 |
|
6116 |
static void Initialize(const FunctionCallbackInfo<Value>& args) { |
548 |
|
6116 |
Environment* env = Environment::GetCurrent(args); |
549 |
|
|
|
550 |
|
6116 |
uint64_t max_http_header_size = 0; |
551 |
|
6116 |
uint64_t headers_timeout = 0; |
552 |
✓✗ |
6116 |
uint32_t lenient_flags = kLenientNone; |
553 |
|
|
|
554 |
✗✓ |
6116 |
CHECK(args[0]->IsInt32()); |
555 |
✗✓ |
6116 |
CHECK(args[1]->IsObject()); |
556 |
|
|
|
557 |
✓✓ |
6116 |
if (args.Length() > 2) { |
558 |
✗✓ |
4946 |
CHECK(args[2]->IsNumber()); |
559 |
|
4946 |
max_http_header_size = |
560 |
|
9892 |
static_cast<uint64_t>(args[2].As<Number>()->Value()); |
561 |
|
|
} |
562 |
✓✓ |
6116 |
if (max_http_header_size == 0) { |
563 |
|
6112 |
max_http_header_size = env->options()->max_http_header_size; |
564 |
|
|
} |
565 |
|
|
|
566 |
✓✓ |
6116 |
if (args.Length() > 3) { |
567 |
✗✓ |
4946 |
CHECK(args[3]->IsInt32()); |
568 |
|
9892 |
lenient_flags = args[3].As<Int32>()->Value(); |
569 |
|
|
} |
570 |
|
|
|
571 |
✓✓ |
6116 |
if (args.Length() > 4) { |
572 |
✗✓ |
4946 |
CHECK(args[4]->IsInt32()); |
573 |
|
9892 |
headers_timeout = args[4].As<Int32>()->Value(); |
574 |
|
|
} |
575 |
|
|
|
576 |
|
|
llhttp_type_t type = |
577 |
|
12232 |
static_cast<llhttp_type_t>(args[0].As<Int32>()->Value()); |
578 |
|
|
|
579 |
✓✓✗✓
|
6116 |
CHECK(type == HTTP_REQUEST || type == HTTP_RESPONSE); |
580 |
|
|
Parser* parser; |
581 |
✗✓ |
6116 |
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
582 |
|
|
// Should always be called from the same context. |
583 |
✗✓ |
6116 |
CHECK_EQ(env, parser->env()); |
584 |
|
|
|
585 |
|
6116 |
AsyncWrap::ProviderType provider = |
586 |
✓✓ |
6116 |
(type == HTTP_REQUEST ? |
587 |
|
|
AsyncWrap::PROVIDER_HTTPINCOMINGMESSAGE |
588 |
|
|
: AsyncWrap::PROVIDER_HTTPCLIENTREQUEST); |
589 |
|
|
|
590 |
|
6116 |
parser->set_provider_type(provider); |
591 |
✓✗ |
18348 |
parser->AsyncReset(args[1].As<Object>()); |
592 |
|
6116 |
parser->Init(type, max_http_header_size, lenient_flags, headers_timeout); |
593 |
|
|
} |
594 |
|
|
|
595 |
|
|
template <bool should_pause> |
596 |
|
22 |
static void Pause(const FunctionCallbackInfo<Value>& args) { |
597 |
|
22 |
Environment* env = Environment::GetCurrent(args); |
598 |
|
|
Parser* parser; |
599 |
✗✓ |
30 |
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
600 |
|
|
// Should always be called from the same context. |
601 |
✗✓ |
22 |
CHECK_EQ(env, parser->env()); |
602 |
|
|
|
603 |
✓✓ |
22 |
if (parser->execute_depth_) { |
604 |
|
8 |
parser->pending_pause_ = should_pause; |
605 |
|
8 |
return; |
606 |
|
|
} |
607 |
|
|
|
608 |
|
|
if (should_pause) { |
609 |
|
8 |
llhttp_pause(&parser->parser_); |
610 |
|
|
} else { |
611 |
|
6 |
llhttp_resume(&parser->parser_); |
612 |
|
|
} |
613 |
|
|
} |
614 |
|
|
|
615 |
|
|
|
616 |
|
3250 |
static void Consume(const FunctionCallbackInfo<Value>& args) { |
617 |
|
|
Parser* parser; |
618 |
✗✓ |
3250 |
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
619 |
✗✓ |
3250 |
CHECK(args[0]->IsObject()); |
620 |
|
6500 |
StreamBase* stream = StreamBase::FromObject(args[0].As<Object>()); |
621 |
✗✓ |
3250 |
CHECK_NOT_NULL(stream); |
622 |
✓✗ |
3250 |
stream->PushStreamListener(parser); |
623 |
|
|
} |
624 |
|
|
|
625 |
|
|
|
626 |
|
3240 |
static void Unconsume(const FunctionCallbackInfo<Value>& args) { |
627 |
|
|
Parser* parser; |
628 |
✗✓ |
3240 |
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
629 |
|
|
|
630 |
|
|
// Already unconsumed |
631 |
✗✓ |
3240 |
if (parser->stream_ == nullptr) |
632 |
|
|
return; |
633 |
|
|
|
634 |
✓✗ |
3240 |
parser->stream_->RemoveStreamListener(parser); |
635 |
|
|
} |
636 |
|
|
|
637 |
|
|
|
638 |
|
58 |
static void GetCurrentBuffer(const FunctionCallbackInfo<Value>& args) { |
639 |
|
|
Parser* parser; |
640 |
✗✓ |
58 |
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
641 |
|
|
|
642 |
|
58 |
Local<Object> ret = Buffer::Copy( |
643 |
|
|
parser->env(), |
644 |
|
|
parser->current_buffer_data_, |
645 |
|
116 |
parser->current_buffer_len_).ToLocalChecked(); |
646 |
|
|
|
647 |
|
116 |
args.GetReturnValue().Set(ret); |
648 |
|
|
} |
649 |
|
|
|
650 |
|
|
protected: |
651 |
|
|
static const size_t kAllocBufferSize = 64 * 1024; |
652 |
|
|
|
653 |
|
4212 |
uv_buf_t OnStreamAlloc(size_t suggested_size) override { |
654 |
|
|
// For most types of streams, OnStreamRead will be immediately after |
655 |
|
|
// OnStreamAlloc, and will consume all data, so using a static buffer for |
656 |
|
|
// reading is more efficient. For other streams, just use Malloc() directly. |
657 |
✗✓ |
4212 |
if (binding_data_->parser_buffer_in_use) |
658 |
|
|
return uv_buf_init(Malloc(suggested_size), suggested_size); |
659 |
|
4212 |
binding_data_->parser_buffer_in_use = true; |
660 |
|
|
|
661 |
✓✓ |
4212 |
if (binding_data_->parser_buffer.empty()) |
662 |
|
343 |
binding_data_->parser_buffer.resize(kAllocBufferSize); |
663 |
|
|
|
664 |
|
4212 |
return uv_buf_init(binding_data_->parser_buffer.data(), kAllocBufferSize); |
665 |
|
|
} |
666 |
|
|
|
667 |
|
|
|
668 |
|
4239 |
void OnStreamRead(ssize_t nread, const uv_buf_t& buf) override { |
669 |
|
4239 |
HandleScope scope(env()->isolate()); |
670 |
|
|
// Once we’re done here, either indicate that the HTTP parser buffer |
671 |
|
|
// is free for re-use, or free() the data if it didn’t come from there |
672 |
|
|
// in the first place. |
673 |
|
4235 |
auto on_scope_leave = OnScopeLeave([&]() { |
674 |
✓✓ |
4235 |
if (buf.base == binding_data_->parser_buffer.data()) |
675 |
|
4210 |
binding_data_->parser_buffer_in_use = false; |
676 |
|
|
else |
677 |
|
25 |
free(buf.base); |
678 |
|
4239 |
}); |
679 |
|
|
|
680 |
✓✓ |
4239 |
if (nread < 0) { |
681 |
|
264 |
PassReadErrorToPreviousListener(nread); |
682 |
|
264 |
return; |
683 |
|
|
} |
684 |
|
|
|
685 |
|
|
// Ignore, empty reads have special meaning in http parser |
686 |
✗✓ |
3975 |
if (nread == 0) |
687 |
|
|
return; |
688 |
|
|
|
689 |
|
3975 |
current_buffer_.Clear(); |
690 |
|
3975 |
Local<Value> ret = Execute(buf.base, nread); |
691 |
|
|
|
692 |
|
|
// Exception |
693 |
✓✓ |
3974 |
if (ret.IsEmpty()) |
694 |
|
5 |
return; |
695 |
|
|
|
696 |
|
|
// check header parsing time |
697 |
✓✓✓✗
|
3969 |
if (header_parsing_start_time_ != 0 && headers_timeout_ != 0) { |
698 |
|
52 |
uint64_t now = uv_hrtime(); |
699 |
|
52 |
uint64_t parsing_time = (now - header_parsing_start_time_) / 1000000; |
700 |
|
|
|
701 |
✓✓ |
52 |
if (parsing_time > headers_timeout_) { |
702 |
|
|
Local<Value> cb = |
703 |
|
18 |
object()->Get(env()->context(), kOnTimeout).ToLocalChecked(); |
704 |
|
|
|
705 |
✗✓ |
6 |
if (!cb->IsFunction()) |
706 |
|
|
return; |
707 |
|
|
|
708 |
|
12 |
MakeCallback(cb.As<Function>(), 0, nullptr); |
709 |
|
|
|
710 |
|
6 |
return; |
711 |
|
|
} |
712 |
|
|
} |
713 |
|
|
|
714 |
|
|
Local<Value> cb = |
715 |
|
11889 |
object()->Get(env()->context(), kOnExecute).ToLocalChecked(); |
716 |
|
|
|
717 |
✓✓ |
3963 |
if (!cb->IsFunction()) |
718 |
|
1003 |
return; |
719 |
|
|
|
720 |
|
|
// Hooks for GetCurrentBuffer |
721 |
|
2960 |
current_buffer_len_ = nread; |
722 |
|
2960 |
current_buffer_data_ = buf.base; |
723 |
|
|
|
724 |
|
5920 |
MakeCallback(cb.As<Function>(), 1, &ret); |
725 |
|
|
|
726 |
|
2957 |
current_buffer_len_ = 0; |
727 |
|
2957 |
current_buffer_data_ = nullptr; |
728 |
|
|
} |
729 |
|
|
|
730 |
|
|
|
731 |
|
8134 |
Local<Value> Execute(const char* data, size_t len) { |
732 |
|
8134 |
EscapableHandleScope scope(env()->isolate()); |
733 |
|
|
|
734 |
|
8134 |
current_buffer_len_ = len; |
735 |
|
8134 |
current_buffer_data_ = data; |
736 |
|
8134 |
got_exception_ = false; |
737 |
|
|
|
738 |
|
|
llhttp_errno_t err; |
739 |
|
|
|
740 |
|
|
// Do not allow re-entering `http_parser_execute()` |
741 |
✗✓ |
8134 |
CHECK_EQ(execute_depth_, 0); |
742 |
|
|
|
743 |
|
8134 |
execute_depth_++; |
744 |
✓✓ |
8134 |
if (data == nullptr) { |
745 |
|
990 |
err = llhttp_finish(&parser_); |
746 |
|
|
} else { |
747 |
|
7144 |
err = llhttp_execute(&parser_, data, len); |
748 |
|
7142 |
Save(); |
749 |
|
|
} |
750 |
|
8132 |
execute_depth_--; |
751 |
|
|
|
752 |
|
|
// Calculate bytes read and resume after Upgrade/CONNECT pause |
753 |
|
8132 |
size_t nread = len; |
754 |
✓✓ |
8132 |
if (err != HPE_OK) { |
755 |
|
428 |
nread = llhttp_get_error_pos(&parser_) - data; |
756 |
|
|
|
757 |
|
|
// This isn't a real pause, just a way to stop parsing early. |
758 |
✓✓ |
428 |
if (err == HPE_PAUSED_UPGRADE) { |
759 |
|
97 |
err = HPE_OK; |
760 |
|
97 |
llhttp_resume_after_upgrade(&parser_); |
761 |
|
|
} |
762 |
|
|
} |
763 |
|
|
|
764 |
|
|
// Apply pending pause |
765 |
✗✓ |
8132 |
if (pending_pause_) { |
766 |
|
|
pending_pause_ = false; |
767 |
|
|
llhttp_pause(&parser_); |
768 |
|
|
} |
769 |
|
|
|
770 |
|
|
// Unassign the 'buffer_' variable |
771 |
✓✓ |
8132 |
current_buffer_.Clear(); |
772 |
|
8132 |
current_buffer_len_ = 0; |
773 |
|
8132 |
current_buffer_data_ = nullptr; |
774 |
|
|
|
775 |
|
|
// If there was an exception in one of the callbacks |
776 |
✓✓ |
8132 |
if (got_exception_) |
777 |
|
8 |
return scope.Escape(Local<Value>()); |
778 |
|
|
|
779 |
|
8124 |
Local<Integer> nread_obj = Integer::New(env()->isolate(), nread); |
780 |
|
|
|
781 |
|
|
// If there was a parse error in one of the callbacks |
782 |
|
|
// TODO(bnoordhuis) What if there is an error on EOF? |
783 |
✓✓✓✓
|
8124 |
if (!parser_.upgrade && err != HPE_OK) { |
784 |
|
226 |
Local<Value> e = Exception::Error(env()->parse_error_string()); |
785 |
|
226 |
Local<Object> obj = e->ToObject(env()->isolate()->GetCurrentContext()) |
786 |
|
226 |
.ToLocalChecked(); |
787 |
|
226 |
obj->Set(env()->context(), |
788 |
|
|
env()->bytes_parsed_string(), |
789 |
|
452 |
nread_obj).Check(); |
790 |
|
226 |
const char* errno_reason = llhttp_get_error_reason(&parser_); |
791 |
|
|
|
792 |
|
|
Local<String> code; |
793 |
|
|
Local<String> reason; |
794 |
✓✓ |
226 |
if (err == HPE_USER) { |
795 |
|
19 |
const char* colon = strchr(errno_reason, ':'); |
796 |
✗✓ |
19 |
CHECK_NOT_NULL(colon); |
797 |
|
|
code = OneByteString(env()->isolate(), |
798 |
|
|
errno_reason, |
799 |
|
19 |
static_cast<int>(colon - errno_reason)); |
800 |
|
19 |
reason = OneByteString(env()->isolate(), colon + 1); |
801 |
|
|
} else { |
802 |
|
207 |
code = OneByteString(env()->isolate(), llhttp_errno_name(err)); |
803 |
|
207 |
reason = OneByteString(env()->isolate(), errno_reason); |
804 |
|
|
} |
805 |
|
|
|
806 |
|
678 |
obj->Set(env()->context(), env()->code_string(), code).Check(); |
807 |
|
678 |
obj->Set(env()->context(), env()->reason_string(), reason).Check(); |
808 |
|
226 |
return scope.Escape(e); |
809 |
|
|
} |
810 |
|
|
|
811 |
|
|
// No return value is needed for `Finish()` |
812 |
✓✓ |
7898 |
if (data == nullptr) { |
813 |
|
957 |
return scope.Escape(Local<Value>()); |
814 |
|
|
} |
815 |
|
6941 |
return scope.Escape(nread_obj); |
816 |
|
|
} |
817 |
|
|
|
818 |
|
17050 |
Local<Array> CreateHeaders() { |
819 |
|
|
// There could be extra entries but the max size should be fixed |
820 |
✓✓ |
1108250 |
Local<Value> headers_v[kMaxHeaderFieldsCount * 2]; |
821 |
|
|
|
822 |
✓✓ |
33835 |
for (size_t i = 0; i < num_values_; ++i) { |
823 |
|
16785 |
headers_v[i * 2] = fields_[i].ToString(env()); |
824 |
|
33570 |
headers_v[i * 2 + 1] = values_[i].ToTrimmedString(env()); |
825 |
|
|
} |
826 |
|
|
|
827 |
|
17050 |
return Array::New(env()->isolate(), headers_v, num_values_ * 2); |
828 |
|
|
} |
829 |
|
|
|
830 |
|
|
|
831 |
|
|
// spill headers and request path to JS land |
832 |
|
67 |
void Flush() { |
833 |
|
67 |
HandleScope scope(env()->isolate()); |
834 |
|
|
|
835 |
|
67 |
Local<Object> obj = object(); |
836 |
|
134 |
Local<Value> cb = obj->Get(env()->context(), kOnHeaders).ToLocalChecked(); |
837 |
|
|
|
838 |
✗✓ |
67 |
if (!cb->IsFunction()) |
839 |
|
|
return; |
840 |
|
|
|
841 |
|
|
Local<Value> argv[2] = { |
842 |
|
|
CreateHeaders(), |
843 |
|
|
url_.ToString(env()) |
844 |
|
134 |
}; |
845 |
|
|
|
846 |
|
|
MaybeLocal<Value> r = MakeCallback(cb.As<Function>(), |
847 |
|
67 |
arraysize(argv), |
848 |
|
134 |
argv); |
849 |
|
|
|
850 |
✗✓ |
67 |
if (r.IsEmpty()) |
851 |
|
|
got_exception_ = true; |
852 |
|
|
|
853 |
|
67 |
url_.Reset(); |
854 |
|
67 |
have_flushed_ = true; |
855 |
|
|
} |
856 |
|
|
|
857 |
|
|
|
858 |
|
6116 |
void Init(llhttp_type_t type, uint64_t max_http_header_size, |
859 |
|
|
uint32_t lenient_flags, uint64_t headers_timeout) { |
860 |
|
6116 |
llhttp_init(&parser_, type, &settings); |
861 |
|
|
|
862 |
✓✓ |
6116 |
if (lenient_flags & kLenientHeaders) { |
863 |
|
5 |
llhttp_set_lenient_headers(&parser_, 1); |
864 |
|
|
} |
865 |
✓✓ |
6116 |
if (lenient_flags & kLenientChunkedLength) { |
866 |
|
5 |
llhttp_set_lenient_chunked_length(&parser_, 1); |
867 |
|
|
} |
868 |
✓✓ |
6116 |
if (lenient_flags & kLenientKeepAlive) { |
869 |
|
5 |
llhttp_set_lenient_keep_alive(&parser_, 1); |
870 |
|
|
} |
871 |
|
|
|
872 |
|
6116 |
header_nread_ = 0; |
873 |
|
6116 |
url_.Reset(); |
874 |
|
6116 |
status_message_.Reset(); |
875 |
|
6116 |
num_fields_ = 0; |
876 |
|
6116 |
num_values_ = 0; |
877 |
|
6116 |
have_flushed_ = false; |
878 |
|
6116 |
got_exception_ = false; |
879 |
|
6116 |
max_http_header_size_ = max_http_header_size; |
880 |
|
6116 |
header_parsing_start_time_ = 0; |
881 |
|
6116 |
headers_timeout_ = headers_timeout; |
882 |
|
6116 |
} |
883 |
|
|
|
884 |
|
|
|
885 |
|
50806 |
int TrackHeader(size_t len) { |
886 |
|
50806 |
header_nread_ += len; |
887 |
✓✓ |
50806 |
if (header_nread_ >= max_http_header_size_) { |
888 |
|
17 |
llhttp_set_error_reason(&parser_, "HPE_HEADER_OVERFLOW:Header overflow"); |
889 |
|
17 |
return HPE_USER; |
890 |
|
|
} |
891 |
|
50789 |
return 0; |
892 |
|
|
} |
893 |
|
|
|
894 |
|
|
|
895 |
|
133346 |
int MaybePause() { |
896 |
✗✓ |
133346 |
CHECK_NE(execute_depth_, 0); |
897 |
|
|
|
898 |
✓✗ |
133346 |
if (!pending_pause_) { |
899 |
|
133346 |
return 0; |
900 |
|
|
} |
901 |
|
|
|
902 |
|
|
pending_pause_ = false; |
903 |
|
|
llhttp_set_error_reason(&parser_, "Paused in callback"); |
904 |
|
|
return HPE_PAUSED; |
905 |
|
|
} |
906 |
|
|
|
907 |
|
|
|
908 |
|
|
bool IsNotIndicativeOfMemoryLeakAtExit() const override { |
909 |
|
|
// HTTP parsers are able to emit events without any GC root referring |
910 |
|
|
// to them, because they receive events directly from the underlying |
911 |
|
|
// libuv resource. |
912 |
|
|
return true; |
913 |
|
|
} |
914 |
|
|
|
915 |
|
|
|
916 |
|
|
llhttp_t parser_; |
917 |
|
|
StringPtr fields_[kMaxHeaderFieldsCount]; // header fields |
918 |
|
|
StringPtr values_[kMaxHeaderFieldsCount]; // header values |
919 |
|
|
StringPtr url_; |
920 |
|
|
StringPtr status_message_; |
921 |
|
|
size_t num_fields_; |
922 |
|
|
size_t num_values_; |
923 |
|
|
bool have_flushed_; |
924 |
|
|
bool got_exception_; |
925 |
|
|
Local<Object> current_buffer_; |
926 |
|
|
size_t current_buffer_len_; |
927 |
|
|
const char* current_buffer_data_; |
928 |
|
|
unsigned int execute_depth_ = 0; |
929 |
|
|
bool pending_pause_ = false; |
930 |
|
|
uint64_t header_nread_ = 0; |
931 |
|
|
uint64_t max_http_header_size_; |
932 |
|
|
uint64_t headers_timeout_; |
933 |
|
|
uint64_t header_parsing_start_time_ = 0; |
934 |
|
|
|
935 |
|
|
BaseObjectPtr<BindingData> binding_data_; |
936 |
|
|
|
937 |
|
|
// These are helper functions for filling `http_parser_settings`, which turn |
938 |
|
|
// a member function of Parser into a C-style HTTP parser callback. |
939 |
|
|
template <typename Parser, Parser> struct Proxy; |
940 |
|
|
template <typename Parser, typename ...Args, int (Parser::*Member)(Args...)> |
941 |
|
|
struct Proxy<int (Parser::*)(Args...), Member> { |
942 |
|
266958 |
static int Raw(llhttp_t* p, Args ... args) { |
943 |
|
266958 |
Parser* parser = ContainerOf(&Parser::parser_, p); |
944 |
|
266958 |
int rv = (parser->*Member)(std::forward<Args>(args)...); |
945 |
✓✓ |
266954 |
if (rv == 0) { |
946 |
|
266692 |
rv = parser->MaybePause(); |
947 |
|
|
} |
948 |
|
266954 |
return rv; |
949 |
|
|
} |
950 |
|
|
}; |
951 |
|
|
|
952 |
|
|
typedef int (Parser::*Call)(); |
953 |
|
|
typedef int (Parser::*DataCall)(const char* at, size_t length); |
954 |
|
|
|
955 |
|
|
static const llhttp_settings_t settings; |
956 |
|
|
}; |
957 |
|
|
|
958 |
|
|
const llhttp_settings_t Parser::settings = { |
959 |
|
|
Proxy<Call, &Parser::on_message_begin>::Raw, |
960 |
|
|
Proxy<DataCall, &Parser::on_url>::Raw, |
961 |
|
|
Proxy<DataCall, &Parser::on_status>::Raw, |
962 |
|
|
Proxy<DataCall, &Parser::on_header_field>::Raw, |
963 |
|
|
Proxy<DataCall, &Parser::on_header_value>::Raw, |
964 |
|
|
Proxy<Call, &Parser::on_headers_complete>::Raw, |
965 |
|
|
Proxy<DataCall, &Parser::on_body>::Raw, |
966 |
|
|
Proxy<Call, &Parser::on_message_complete>::Raw, |
967 |
|
|
Proxy<Call, &Parser::on_chunk_header>::Raw, |
968 |
|
|
Proxy<Call, &Parser::on_chunk_complete>::Raw, |
969 |
|
|
|
970 |
|
|
// on_url_complete |
971 |
|
|
nullptr, |
972 |
|
|
// on_status_complete |
973 |
|
|
nullptr, |
974 |
|
|
// on_header_field_complete |
975 |
|
|
nullptr, |
976 |
|
|
// on_header_value_complete |
977 |
|
|
nullptr, |
978 |
|
|
}; |
979 |
|
|
|
980 |
|
|
|
981 |
|
800 |
void InitializeHttpParser(Local<Object> target, |
982 |
|
|
Local<Value> unused, |
983 |
|
|
Local<Context> context, |
984 |
|
|
void* priv) { |
985 |
|
800 |
Environment* env = Environment::GetCurrent(context); |
986 |
|
|
BindingData* const binding_data = |
987 |
|
800 |
env->AddBindingData<BindingData>(context, target); |
988 |
✗✓ |
800 |
if (binding_data == nullptr) return; |
989 |
|
|
|
990 |
|
800 |
Local<FunctionTemplate> t = env->NewFunctionTemplate(Parser::New); |
991 |
|
1600 |
t->InstanceTemplate()->SetInternalFieldCount(Parser::kInternalFieldCount); |
992 |
|
|
|
993 |
|
2400 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "REQUEST"), |
994 |
|
|
Integer::New(env->isolate(), HTTP_REQUEST)); |
995 |
|
2400 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "RESPONSE"), |
996 |
|
|
Integer::New(env->isolate(), HTTP_RESPONSE)); |
997 |
|
2400 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kOnMessageBegin"), |
998 |
|
|
Integer::NewFromUnsigned(env->isolate(), kOnMessageBegin)); |
999 |
|
2400 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kOnHeaders"), |
1000 |
|
|
Integer::NewFromUnsigned(env->isolate(), kOnHeaders)); |
1001 |
|
2400 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kOnHeadersComplete"), |
1002 |
|
|
Integer::NewFromUnsigned(env->isolate(), kOnHeadersComplete)); |
1003 |
|
2400 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kOnBody"), |
1004 |
|
|
Integer::NewFromUnsigned(env->isolate(), kOnBody)); |
1005 |
|
2400 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kOnMessageComplete"), |
1006 |
|
|
Integer::NewFromUnsigned(env->isolate(), kOnMessageComplete)); |
1007 |
|
2400 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kOnExecute"), |
1008 |
|
|
Integer::NewFromUnsigned(env->isolate(), kOnExecute)); |
1009 |
|
2400 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kOnTimeout"), |
1010 |
|
|
Integer::NewFromUnsigned(env->isolate(), kOnTimeout)); |
1011 |
|
|
|
1012 |
|
2400 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kLenientNone"), |
1013 |
|
|
Integer::NewFromUnsigned(env->isolate(), kLenientNone)); |
1014 |
|
2400 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kLenientHeaders"), |
1015 |
|
|
Integer::NewFromUnsigned(env->isolate(), kLenientHeaders)); |
1016 |
|
2400 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kLenientChunkedLength"), |
1017 |
|
|
Integer::NewFromUnsigned(env->isolate(), kLenientChunkedLength)); |
1018 |
|
2400 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kLenientKeepAlive"), |
1019 |
|
|
Integer::NewFromUnsigned(env->isolate(), kLenientKeepAlive)); |
1020 |
|
2400 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kLenientAll"), |
1021 |
|
|
Integer::NewFromUnsigned(env->isolate(), kLenientAll)); |
1022 |
|
|
|
1023 |
|
800 |
Local<Array> methods = Array::New(env->isolate()); |
1024 |
|
|
#define V(num, name, string) \ |
1025 |
|
|
methods->Set(env->context(), \ |
1026 |
|
|
num, FIXED_ONE_BYTE_STRING(env->isolate(), #string)).Check(); |
1027 |
|
55200 |
HTTP_METHOD_MAP(V) |
1028 |
|
|
#undef V |
1029 |
|
800 |
target->Set(env->context(), |
1030 |
|
|
FIXED_ONE_BYTE_STRING(env->isolate(), "methods"), |
1031 |
|
2400 |
methods).Check(); |
1032 |
|
|
|
1033 |
|
800 |
t->Inherit(AsyncWrap::GetConstructorTemplate(env)); |
1034 |
|
800 |
env->SetProtoMethod(t, "close", Parser::Close); |
1035 |
|
800 |
env->SetProtoMethod(t, "free", Parser::Free); |
1036 |
|
800 |
env->SetProtoMethod(t, "execute", Parser::Execute); |
1037 |
|
800 |
env->SetProtoMethod(t, "finish", Parser::Finish); |
1038 |
|
800 |
env->SetProtoMethod(t, "initialize", Parser::Initialize); |
1039 |
|
800 |
env->SetProtoMethod(t, "pause", Parser::Pause<true>); |
1040 |
|
800 |
env->SetProtoMethod(t, "resume", Parser::Pause<false>); |
1041 |
|
800 |
env->SetProtoMethod(t, "consume", Parser::Consume); |
1042 |
|
800 |
env->SetProtoMethod(t, "unconsume", Parser::Unconsume); |
1043 |
|
800 |
env->SetProtoMethod(t, "getCurrentBuffer", Parser::GetCurrentBuffer); |
1044 |
|
|
|
1045 |
|
800 |
env->SetConstructorFunction(target, "HTTPParser", t); |
1046 |
|
|
} |
1047 |
|
|
|
1048 |
|
|
} // anonymous namespace |
1049 |
|
|
} // namespace node |
1050 |
|
|
|
1051 |
|
4928 |
NODE_MODULE_CONTEXT_AWARE_INTERNAL(http_parser, node::InitializeHttpParser) |