GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
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 |
17162 |
inline bool IsOWS(char c) { |
|
83 |
✓✓✓✓ |
17162 |
return c == ' ' || c == '\t'; |
84 |
} |
||
85 |
|||
86 |
1430 |
class BindingData : public BaseObject { |
|
87 |
public: |
||
88 |
742 |
BindingData(Environment* env, Local<Object> obj) |
|
89 |
742 |
: BaseObject(env, obj) {} |
|
90 |
|||
91 |
static constexpr FastStringKey binding_data_name { "http_parser" }; |
||
92 |
|||
93 |
std::vector<char> parser_buffer; |
||
94 |
bool parser_buffer_in_use = false; |
||
95 |
|||
96 |
3 |
void MemoryInfo(MemoryTracker* tracker) const override { |
|
97 |
3 |
tracker->TrackField("parser_buffer", parser_buffer); |
|
98 |
3 |
} |
|
99 |
3 |
SET_SELF_SIZE(BindingData) |
|
100 |
3 |
SET_MEMORY_INFO_NAME(BindingData) |
|
101 |
}; |
||
102 |
|||
103 |
// TODO(addaleax): Remove once we're on C++17. |
||
104 |
constexpr FastStringKey BindingData::binding_data_name; |
||
105 |
|||
106 |
// helper class for the Parser |
||
107 |
struct StringPtr { |
||
108 |
237006 |
StringPtr() { |
|
109 |
237006 |
on_heap_ = false; |
|
110 |
237006 |
Reset(); |
|
111 |
237006 |
} |
|
112 |
|||
113 |
|||
114 |
469788 |
~StringPtr() { |
|
115 |
234894 |
Reset(); |
|
116 |
234894 |
} |
|
117 |
|||
118 |
|||
119 |
// If str_ does not point to a heap string yet, this function makes it do |
||
120 |
// so. This is called at the end of each http_parser_execute() so as not |
||
121 |
// to leak references. See issue #2438 and test-http-parser-bad-ref.js. |
||
122 |
14455 |
void Save() { |
|
123 |
✓✓✓✓ |
14455 |
if (!on_heap_ && size_ > 0) { |
124 |
5942 |
char* s = new char[size_]; |
|
125 |
5942 |
memcpy(s, str_, size_); |
|
126 |
5942 |
str_ = s; |
|
127 |
5942 |
on_heap_ = true; |
|
128 |
} |
||
129 |
14455 |
} |
|
130 |
|||
131 |
|||
132 |
552377 |
void Reset() { |
|
133 |
✓✓ | 552377 |
if (on_heap_) { |
134 |
✓✗ | 5908 |
delete[] str_; |
135 |
5908 |
on_heap_ = false; |
|
136 |
} |
||
137 |
|||
138 |
552377 |
str_ = nullptr; |
|
139 |
552377 |
size_ = 0; |
|
140 |
552377 |
} |
|
141 |
|||
142 |
|||
143 |
51428 |
void Update(const char* str, size_t size) { |
|
144 |
✓✓ | 51428 |
if (str_ == nullptr) { |
145 |
51365 |
str_ = str; |
|
146 |
✓✓✓✗ |
63 |
} else if (on_heap_ || str_ + size_ != str) { |
147 |
// Non-consecutive input, make a copy on the heap. |
||
148 |
// TODO(bnoordhuis) Use slab allocation, O(n) allocs is bad. |
||
149 |
63 |
char* s = new char[size_ + size]; |
|
150 |
63 |
memcpy(s, str_, size_); |
|
151 |
63 |
memcpy(s + size_, str, size); |
|
152 |
|||
153 |
✓✓ | 63 |
if (on_heap_) |
154 |
✓✗ | 62 |
delete[] str_; |
155 |
else |
||
156 |
1 |
on_heap_ = true; |
|
157 |
|||
158 |
63 |
str_ = s; |
|
159 |
} |
||
160 |
51428 |
size_ += size; |
|
161 |
51428 |
} |
|
162 |
|||
163 |
|||
164 |
51280 |
Local<String> ToString(Environment* env) const { |
|
165 |
✓✓ | 51280 |
if (size_ != 0) |
166 |
51216 |
return OneByteString(env->isolate(), str_, size_); |
|
167 |
else |
||
168 |
128 |
return String::Empty(env->isolate()); |
|
169 |
} |
||
170 |
|||
171 |
|||
172 |
// Strip trailing OWS (SPC or HTAB) from string. |
||
173 |
17164 |
Local<String> ToTrimmedString(Environment* env) { |
|
174 |
✓✓✓✓ ✓✓ |
17175 |
while (size_ > 0 && IsOWS(str_[size_ - 1])) { |
175 |
11 |
size_--; |
|
176 |
} |
||
177 |
17153 |
return ToString(env); |
|
178 |
} |
||
179 |
|||
180 |
|||
181 |
const char* str_; |
||
182 |
bool on_heap_; |
||
183 |
size_t size_; |
||
184 |
}; |
||
185 |
|||
186 |
✓✗✓✓ ✓✗✓✓ |
7118 |
class Parser : public AsyncWrap, public StreamListener { |
187 |
public: |
||
188 |
3591 |
Parser(BindingData* binding_data, Local<Object> wrap) |
|
189 |
3591 |
: AsyncWrap(binding_data->env(), wrap), |
|
190 |
current_buffer_len_(0), |
||
191 |
current_buffer_data_(nullptr), |
||
192 |
✓✓✓✓ |
7182 |
binding_data_(binding_data) { |
193 |
3591 |
} |
|
194 |
|||
195 |
|||
196 |
void MemoryInfo(MemoryTracker* tracker) const override { |
||
197 |
tracker->TrackField("current_buffer", current_buffer_); |
||
198 |
} |
||
199 |
|||
200 |
SET_MEMORY_INFO_NAME(Parser) |
||
201 |
SET_SELF_SIZE(Parser) |
||
202 |
|||
203 |
16961 |
int on_message_begin() { |
|
204 |
16961 |
num_fields_ = num_values_ = 0; |
|
205 |
16961 |
url_.Reset(); |
|
206 |
16961 |
status_message_.Reset(); |
|
207 |
16961 |
header_parsing_start_time_ = uv_hrtime(); |
|
208 |
|||
209 |
50883 |
Local<Value> cb = object()->Get(env()->context(), kOnMessageBegin) |
|
210 |
16961 |
.ToLocalChecked(); |
|
211 |
✓✓ | 16961 |
if (cb->IsFunction()) { |
212 |
InternalCallbackScope callback_scope( |
||
213 |
27606 |
this, InternalCallbackScope::kSkipTaskQueues); |
|
214 |
|||
215 |
27606 |
MaybeLocal<Value> r = cb.As<Function>()->Call( |
|
216 |
55212 |
env()->context(), object(), 0, nullptr); |
|
217 |
|||
218 |
✗✓ | 13803 |
if (r.IsEmpty()) callback_scope.MarkAsFailed(); |
219 |
} |
||
220 |
|||
221 |
16961 |
return 0; |
|
222 |
} |
||
223 |
|||
224 |
|||
225 |
13892 |
int on_url(const char* at, size_t length) { |
|
226 |
13892 |
int rv = TrackHeader(length); |
|
227 |
✗✓ | 13892 |
if (rv != 0) { |
228 |
return rv; |
||
229 |
} |
||
230 |
|||
231 |
13892 |
url_.Update(at, length); |
|
232 |
13892 |
return 0; |
|
233 |
} |
||
234 |
|||
235 |
|||
236 |
3059 |
int on_status(const char* at, size_t length) { |
|
237 |
3059 |
int rv = TrackHeader(length); |
|
238 |
✗✓ | 3059 |
if (rv != 0) { |
239 |
return rv; |
||
240 |
} |
||
241 |
|||
242 |
3059 |
status_message_.Update(at, length); |
|
243 |
3059 |
return 0; |
|
244 |
} |
||
245 |
|||
246 |
|||
247 |
17251 |
int on_header_field(const char* at, size_t length) { |
|
248 |
17251 |
int rv = TrackHeader(length); |
|
249 |
✗✓ | 17251 |
if (rv != 0) { |
250 |
return rv; |
||
251 |
} |
||
252 |
|||
253 |
✓✓ | 17251 |
if (num_fields_ == num_values_) { |
254 |
// start of new field name |
||
255 |
17222 |
num_fields_++; |
|
256 |
✓✓ | 17222 |
if (num_fields_ == kMaxHeaderFieldsCount) { |
257 |
// ran out of space - flush to javascript land |
||
258 |
47 |
Flush(); |
|
259 |
47 |
num_fields_ = 1; |
|
260 |
47 |
num_values_ = 0; |
|
261 |
} |
||
262 |
17222 |
fields_[num_fields_ - 1].Reset(); |
|
263 |
} |
||
264 |
|||
265 |
✗✓ | 17251 |
CHECK_LT(num_fields_, kMaxHeaderFieldsCount); |
266 |
✗✓ | 17251 |
CHECK_EQ(num_fields_, num_values_ + 1); |
267 |
|||
268 |
17251 |
fields_[num_fields_ - 1].Update(at, length); |
|
269 |
|||
270 |
17251 |
return 0; |
|
271 |
} |
||
272 |
|||
273 |
|||
274 |
17241 |
int on_header_value(const char* at, size_t length) { |
|
275 |
17241 |
int rv = TrackHeader(length); |
|
276 |
✓✓ | 17241 |
if (rv != 0) { |
277 |
15 |
return rv; |
|
278 |
} |
||
279 |
|||
280 |
✓✓ | 17226 |
if (num_values_ != num_fields_) { |
281 |
// start of new header value |
||
282 |
17200 |
num_values_++; |
|
283 |
17200 |
values_[num_values_ - 1].Reset(); |
|
284 |
} |
||
285 |
|||
286 |
✗✓ | 17226 |
CHECK_LT(num_values_, arraysize(values_)); |
287 |
✗✓ | 17226 |
CHECK_EQ(num_values_, num_fields_); |
288 |
|||
289 |
17226 |
values_[num_values_ - 1].Update(at, length); |
|
290 |
|||
291 |
17226 |
return 0; |
|
292 |
} |
||
293 |
|||
294 |
|||
295 |
16915 |
int on_headers_complete() { |
|
296 |
16915 |
header_nread_ = 0; |
|
297 |
16915 |
header_parsing_start_time_ = 0; |
|
298 |
|||
299 |
// Arguments for the on-headers-complete javascript callback. This |
||
300 |
// list needs to be kept in sync with the actual argument list for |
||
301 |
// `parserOnHeadersComplete` in lib/_http_common.js. |
||
302 |
enum on_headers_complete_arg_index { |
||
303 |
A_VERSION_MAJOR = 0, |
||
304 |
A_VERSION_MINOR, |
||
305 |
A_HEADERS, |
||
306 |
A_METHOD, |
||
307 |
A_URL, |
||
308 |
A_STATUS_CODE, |
||
309 |
A_STATUS_MESSAGE, |
||
310 |
A_UPGRADE, |
||
311 |
A_SHOULD_KEEP_ALIVE, |
||
312 |
A_MAX |
||
313 |
}; |
||
314 |
|||
315 |
✓✓ | 169150 |
Local<Value> argv[A_MAX]; |
316 |
16915 |
Local<Object> obj = object(); |
|
317 |
33830 |
Local<Value> cb = obj->Get(env()->context(), |
|
318 |
50745 |
kOnHeadersComplete).ToLocalChecked(); |
|
319 |
|||
320 |
✗✓ | 16915 |
if (!cb->IsFunction()) |
321 |
return 0; |
||
322 |
|||
323 |
16915 |
Local<Value> undefined = Undefined(env()->isolate()); |
|
324 |
✓✓ | 169150 |
for (size_t i = 0; i < arraysize(argv); i++) |
325 |
152235 |
argv[i] = undefined; |
|
326 |
|||
327 |
✓✓ | 16915 |
if (have_flushed_) { |
328 |
// Slow case, flush remaining headers. |
||
329 |
16 |
Flush(); |
|
330 |
} else { |
||
331 |
// Fast case, pass headers and URL to JS land. |
||
332 |
33798 |
argv[A_HEADERS] = CreateHeaders(); |
|
333 |
✓✓ | 16899 |
if (parser_.type == HTTP_REQUEST) |
334 |
27710 |
argv[A_URL] = url_.ToString(env()); |
|
335 |
} |
||
336 |
|||
337 |
16915 |
num_fields_ = 0; |
|
338 |
16915 |
num_values_ = 0; |
|
339 |
|||
340 |
// METHOD |
||
341 |
✓✓ | 16915 |
if (parser_.type == HTTP_REQUEST) { |
342 |
13863 |
argv[A_METHOD] = |
|
343 |
27726 |
Uint32::NewFromUnsigned(env()->isolate(), parser_.method); |
|
344 |
} |
||
345 |
|||
346 |
// STATUS |
||
347 |
✓✓ | 16915 |
if (parser_.type == HTTP_RESPONSE) { |
348 |
3052 |
argv[A_STATUS_CODE] = |
|
349 |
6104 |
Integer::New(env()->isolate(), parser_.status_code); |
|
350 |
6104 |
argv[A_STATUS_MESSAGE] = status_message_.ToString(env()); |
|
351 |
} |
||
352 |
|||
353 |
// VERSION |
||
354 |
33830 |
argv[A_VERSION_MAJOR] = Integer::New(env()->isolate(), parser_.http_major); |
|
355 |
33830 |
argv[A_VERSION_MINOR] = Integer::New(env()->isolate(), parser_.http_minor); |
|
356 |
|||
357 |
bool should_keep_alive; |
||
358 |
16915 |
should_keep_alive = llhttp_should_keep_alive(&parser_); |
|
359 |
|||
360 |
16915 |
argv[A_SHOULD_KEEP_ALIVE] = |
|
361 |
33830 |
Boolean::New(env()->isolate(), should_keep_alive); |
|
362 |
|||
363 |
33830 |
argv[A_UPGRADE] = Boolean::New(env()->isolate(), parser_.upgrade); |
|
364 |
|||
365 |
MaybeLocal<Value> head_response; |
||
366 |
{ |
||
367 |
InternalCallbackScope callback_scope( |
||
368 |
16913 |
this, InternalCallbackScope::kSkipTaskQueues); |
|
369 |
33828 |
head_response = cb.As<Function>()->Call( |
|
370 |
67658 |
env()->context(), object(), arraysize(argv), argv); |
|
371 |
✓✓ | 16913 |
if (head_response.IsEmpty()) callback_scope.MarkAsFailed(); |
372 |
} |
||
373 |
|||
374 |
int64_t val; |
||
375 |
|||
376 |
✓✓✗✓ ✓✓ |
67636 |
if (head_response.IsEmpty() || !head_response.ToLocalChecked() |
377 |
50723 |
->IntegerValue(env()->context()) |
|
378 |
16905 |
.To(&val)) { |
|
379 |
8 |
got_exception_ = true; |
|
380 |
8 |
return -1; |
|
381 |
} |
||
382 |
|||
383 |
16905 |
return val; |
|
384 |
} |
||
385 |
|||
386 |
|||
387 |
11033 |
int on_body(const char* at, size_t length) { |
|
388 |
11033 |
EscapableHandleScope scope(env()->isolate()); |
|
389 |
|||
390 |
11033 |
Local<Object> obj = object(); |
|
391 |
33099 |
Local<Value> cb = obj->Get(env()->context(), kOnBody).ToLocalChecked(); |
|
392 |
|||
393 |
✗✓ | 11033 |
if (!cb->IsFunction()) |
394 |
return 0; |
||
395 |
|||
396 |
// We came from consumed stream |
||
397 |
✓✓ | 22066 |
if (current_buffer_.IsEmpty()) { |
398 |
// Make sure Buffer will be in parent HandleScope |
||
399 |
996 |
current_buffer_ = scope.Escape(Buffer::Copy( |
|
400 |
env()->isolate(), |
||
401 |
current_buffer_data_, |
||
402 |
996 |
current_buffer_len_).ToLocalChecked()); |
|
403 |
} |
||
404 |
|||
405 |
Local<Value> argv[3] = { |
||
406 |
current_buffer_, |
||
407 |
11033 |
Integer::NewFromUnsigned(env()->isolate(), at - current_buffer_data_), |
|
408 |
Integer::NewFromUnsigned(env()->isolate(), length) |
||
409 |
55165 |
}; |
|
410 |
|||
411 |
MaybeLocal<Value> r = MakeCallback(cb.As<Function>(), |
||
412 |
11033 |
arraysize(argv), |
|
413 |
22066 |
argv); |
|
414 |
|||
415 |
✗✓ | 11033 |
if (r.IsEmpty()) { |
416 |
got_exception_ = true; |
||
417 |
llhttp_set_error_reason(&parser_, "HPE_JS_EXCEPTION:JS Exception"); |
||
418 |
return HPE_USER; |
||
419 |
} |
||
420 |
|||
421 |
11033 |
return 0; |
|
422 |
} |
||
423 |
|||
424 |
|||
425 |
15709 |
int on_message_complete() { |
|
426 |
31418 |
HandleScope scope(env()->isolate()); |
|
427 |
|||
428 |
✓✓ | 15709 |
if (num_fields_) |
429 |
4 |
Flush(); // Flush trailing HTTP headers. |
|
430 |
|||
431 |
15709 |
Local<Object> obj = object(); |
|
432 |
31418 |
Local<Value> cb = obj->Get(env()->context(), |
|
433 |
47127 |
kOnMessageComplete).ToLocalChecked(); |
|
434 |
|||
435 |
✓✓ | 15709 |
if (!cb->IsFunction()) |
436 |
2 |
return 0; |
|
437 |
|||
438 |
MaybeLocal<Value> r; |
||
439 |
{ |
||
440 |
InternalCallbackScope callback_scope( |
||
441 |
31414 |
this, InternalCallbackScope::kSkipTaskQueues); |
|
442 |
62828 |
r = cb.As<Function>()->Call(env()->context(), object(), 0, nullptr); |
|
443 |
✗✓ | 15707 |
if (r.IsEmpty()) callback_scope.MarkAsFailed(); |
444 |
} |
||
445 |
|||
446 |
✗✓ | 15707 |
if (r.IsEmpty()) { |
447 |
got_exception_ = true; |
||
448 |
return -1; |
||
449 |
} |
||
450 |
|||
451 |
15707 |
return 0; |
|
452 |
} |
||
453 |
|||
454 |
// Reset nread for the next chunk |
||
455 |
10922 |
int on_chunk_header() { |
|
456 |
10922 |
header_nread_ = 0; |
|
457 |
10922 |
return 0; |
|
458 |
} |
||
459 |
|||
460 |
|||
461 |
// Reset nread for the next chunk |
||
462 |
10768 |
int on_chunk_complete() { |
|
463 |
10768 |
header_nread_ = 0; |
|
464 |
10768 |
return 0; |
|
465 |
} |
||
466 |
|||
467 |
3591 |
static void New(const FunctionCallbackInfo<Value>& args) { |
|
468 |
3591 |
BindingData* binding_data = Environment::GetBindingData<BindingData>(args); |
|
469 |
3591 |
new Parser(binding_data, args.This()); |
|
470 |
3591 |
} |
|
471 |
|||
472 |
|||
473 |
3 |
static void Close(const FunctionCallbackInfo<Value>& args) { |
|
474 |
Parser* parser; |
||
475 |
✗✓ | 3 |
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
476 |
|||
477 |
✓✗ | 3 |
delete parser; |
478 |
} |
||
479 |
|||
480 |
|||
481 |
5837 |
static void Free(const FunctionCallbackInfo<Value>& args) { |
|
482 |
Parser* parser; |
||
483 |
✗✓ | 5837 |
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
484 |
|||
485 |
// Since the Parser destructor isn't going to run the destroy() callbacks |
||
486 |
// it needs to be triggered manually. |
||
487 |
5837 |
parser->EmitTraceEventDestroy(); |
|
488 |
5837 |
parser->EmitDestroy(); |
|
489 |
} |
||
490 |
|||
491 |
|||
492 |
7055 |
void Save() { |
|
493 |
7055 |
url_.Save(); |
|
494 |
7055 |
status_message_.Save(); |
|
495 |
|||
496 |
✓✓ | 7257 |
for (size_t i = 0; i < num_fields_; i++) { |
497 |
202 |
fields_[i].Save(); |
|
498 |
} |
||
499 |
|||
500 |
✓✓ | 7198 |
for (size_t i = 0; i < num_values_; i++) { |
501 |
143 |
values_[i].Save(); |
|
502 |
} |
||
503 |
7055 |
} |
|
504 |
|||
505 |
|||
506 |
// var bytesParsed = parser->execute(buffer); |
||
507 |
3140 |
static void Execute(const FunctionCallbackInfo<Value>& args) { |
|
508 |
Parser* parser; |
||
509 |
✗✓ | 3140 |
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
510 |
✗✓ | 6280 |
CHECK(parser->current_buffer_.IsEmpty()); |
511 |
✗✓ | 3140 |
CHECK_EQ(parser->current_buffer_len_, 0); |
512 |
✗✓ | 3140 |
CHECK_NULL(parser->current_buffer_data_); |
513 |
|||
514 |
3140 |
ArrayBufferViewContents<char> buffer(args[0]); |
|
515 |
|||
516 |
// This is a hack to get the current_buffer to the callbacks with the least |
||
517 |
// amount of overhead. Nothing else will run while http_parser_execute() |
||
518 |
// runs, therefore this pointer can be set and used for the execution. |
||
519 |
9420 |
parser->current_buffer_ = args[0].As<Object>(); |
|
520 |
|||
521 |
3140 |
Local<Value> ret = parser->Execute(buffer.data(), buffer.length()); |
|
522 |
|||
523 |
✓✓ | 3139 |
if (!ret.IsEmpty()) |
524 |
6272 |
args.GetReturnValue().Set(ret); |
|
525 |
} |
||
526 |
|||
527 |
|||
528 |
922 |
static void Finish(const FunctionCallbackInfo<Value>& args) { |
|
529 |
Parser* parser; |
||
530 |
✗✓ | 922 |
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
531 |
|||
532 |
✗✓ | 1844 |
CHECK(parser->current_buffer_.IsEmpty()); |
533 |
922 |
Local<Value> ret = parser->Execute(nullptr, 0); |
|
534 |
|||
535 |
✓✓ | 922 |
if (!ret.IsEmpty()) |
536 |
62 |
args.GetReturnValue().Set(ret); |
|
537 |
} |
||
538 |
|||
539 |
|||
540 |
6033 |
static void Initialize(const FunctionCallbackInfo<Value>& args) { |
|
541 |
6033 |
Environment* env = Environment::GetCurrent(args); |
|
542 |
12066 |
bool lenient = args[3]->IsTrue(); |
|
543 |
|||
544 |
6033 |
uint64_t max_http_header_size = 0; |
|
545 |
6033 |
uint64_t headers_timeout = 0; |
|
546 |
|||
547 |
✗✓ | 12066 |
CHECK(args[0]->IsInt32()); |
548 |
✗✓ | 12066 |
CHECK(args[1]->IsObject()); |
549 |
|||
550 |
✓✓ | 6033 |
if (args.Length() > 2) { |
551 |
✗✓ | 9726 |
CHECK(args[2]->IsNumber()); |
552 |
14589 |
max_http_header_size = args[2].As<Number>()->Value(); |
|
553 |
} |
||
554 |
✓✓ | 6033 |
if (max_http_header_size == 0) { |
555 |
6031 |
max_http_header_size = env->options()->max_http_header_size; |
|
556 |
} |
||
557 |
|||
558 |
✓✓ | 6033 |
if (args.Length() > 4) { |
559 |
✗✓ | 9726 |
CHECK(args[4]->IsInt32()); |
560 |
14589 |
headers_timeout = args[4].As<Number>()->Value(); |
|
561 |
} |
||
562 |
|||
563 |
llhttp_type_t type = |
||
564 |
18099 |
static_cast<llhttp_type_t>(args[0].As<Int32>()->Value()); |
|
565 |
|||
566 |
✓✓✗✓ |
6033 |
CHECK(type == HTTP_REQUEST || type == HTTP_RESPONSE); |
567 |
Parser* parser; |
||
568 |
✗✓ | 6033 |
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
569 |
// Should always be called from the same context. |
||
570 |
✗✓ | 6033 |
CHECK_EQ(env, parser->env()); |
571 |
|||
572 |
AsyncWrap::ProviderType provider = |
||
573 |
✓✓ | 6033 |
(type == HTTP_REQUEST ? |
574 |
AsyncWrap::PROVIDER_HTTPINCOMINGMESSAGE |
||
575 |
6033 |
: AsyncWrap::PROVIDER_HTTPCLIENTREQUEST); |
|
576 |
|||
577 |
6033 |
parser->set_provider_type(provider); |
|
578 |
12066 |
parser->AsyncReset(args[1].As<Object>()); |
|
579 |
6033 |
parser->Init(type, max_http_header_size, lenient, headers_timeout); |
|
580 |
} |
||
581 |
|||
582 |
template <bool should_pause> |
||
583 |
11 |
static void Pause(const FunctionCallbackInfo<Value>& args) { |
|
584 |
11 |
Environment* env = Environment::GetCurrent(args); |
|
585 |
Parser* parser; |
||
586 |
✗✓✗✓ |
15 |
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
587 |
// Should always be called from the same context. |
||
588 |
✗✓✗✓ |
11 |
CHECK_EQ(env, parser->env()); |
589 |
|||
590 |
✓✓✗✓ |
11 |
if (parser->execute_depth_) { |
591 |
4 |
parser->pending_pause_ = should_pause; |
|
592 |
4 |
return; |
|
593 |
} |
||
594 |
|||
595 |
if (should_pause) { |
||
596 |
4 |
llhttp_pause(&parser->parser_); |
|
597 |
} else { |
||
598 |
3 |
llhttp_resume(&parser->parser_); |
|
599 |
} |
||
600 |
} |
||
601 |
|||
602 |
|||
603 |
3219 |
static void Consume(const FunctionCallbackInfo<Value>& args) { |
|
604 |
Parser* parser; |
||
605 |
✗✓ | 3219 |
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
606 |
✗✓ | 6438 |
CHECK(args[0]->IsObject()); |
607 |
6438 |
StreamBase* stream = StreamBase::FromObject(args[0].As<Object>()); |
|
608 |
✗✓ | 3219 |
CHECK_NOT_NULL(stream); |
609 |
✓✗ | 3219 |
stream->PushStreamListener(parser); |
610 |
} |
||
611 |
|||
612 |
|||
613 |
3209 |
static void Unconsume(const FunctionCallbackInfo<Value>& args) { |
|
614 |
Parser* parser; |
||
615 |
✗✓ | 3209 |
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
616 |
|||
617 |
// Already unconsumed |
||
618 |
✗✓ | 3209 |
if (parser->stream_ == nullptr) |
619 |
return; |
||
620 |
|||
621 |
✓✗ | 3209 |
parser->stream_->RemoveStreamListener(parser); |
622 |
} |
||
623 |
|||
624 |
|||
625 |
50 |
static void GetCurrentBuffer(const FunctionCallbackInfo<Value>& args) { |
|
626 |
Parser* parser; |
||
627 |
✗✓ | 50 |
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
628 |
|||
629 |
100 |
Local<Object> ret = Buffer::Copy( |
|
630 |
parser->env(), |
||
631 |
parser->current_buffer_data_, |
||
632 |
100 |
parser->current_buffer_len_).ToLocalChecked(); |
|
633 |
|||
634 |
100 |
args.GetReturnValue().Set(ret); |
|
635 |
} |
||
636 |
|||
637 |
protected: |
||
638 |
static const size_t kAllocBufferSize = 64 * 1024; |
||
639 |
|||
640 |
4145 |
uv_buf_t OnStreamAlloc(size_t suggested_size) override { |
|
641 |
// For most types of streams, OnStreamRead will be immediately after |
||
642 |
// OnStreamAlloc, and will consume all data, so using a static buffer for |
||
643 |
// reading is more efficient. For other streams, just use Malloc() directly. |
||
644 |
✗✓ | 4145 |
if (binding_data_->parser_buffer_in_use) |
645 |
return uv_buf_init(Malloc(suggested_size), suggested_size); |
||
646 |
4145 |
binding_data_->parser_buffer_in_use = true; |
|
647 |
|||
648 |
✓✓ | 4145 |
if (binding_data_->parser_buffer.empty()) |
649 |
325 |
binding_data_->parser_buffer.resize(kAllocBufferSize); |
|
650 |
|||
651 |
4145 |
return uv_buf_init(binding_data_->parser_buffer.data(), kAllocBufferSize); |
|
652 |
} |
||
653 |
|||
654 |
|||
655 |
4165 |
void OnStreamRead(ssize_t nread, const uv_buf_t& buf) override { |
|
656 |
7065 |
HandleScope scope(env()->isolate()); |
|
657 |
// Once we’re done here, either indicate that the HTTP parser buffer |
||
658 |
// is free for re-use, or free() the data if it didn’t come from there |
||
659 |
// in the first place. |
||
660 |
4161 |
auto on_scope_leave = OnScopeLeave([&]() { |
|
661 |
✓✓ | 4179 |
if (buf.base == binding_data_->parser_buffer.data()) |
662 |
4143 |
binding_data_->parser_buffer_in_use = false; |
|
663 |
else |
||
664 |
18 |
free(buf.base); |
|
665 |
✓✓ | 11226 |
}); |
666 |
|||
667 |
✓✓ | 4165 |
if (nread < 0) { |
668 |
248 |
PassReadErrorToPreviousListener(nread); |
|
669 |
248 |
return; |
|
670 |
} |
||
671 |
|||
672 |
// Ignore, empty reads have special meaning in http parser |
||
673 |
✗✓ | 3917 |
if (nread == 0) |
674 |
return; |
||
675 |
|||
676 |
3917 |
current_buffer_.Clear(); |
|
677 |
3917 |
Local<Value> ret = Execute(buf.base, nread); |
|
678 |
|||
679 |
// Exception |
||
680 |
✓✓ | 3916 |
if (ret.IsEmpty()) |
681 |
5 |
return; |
|
682 |
|||
683 |
// check header parsing time |
||
684 |
✓✓✓✗ |
3911 |
if (header_parsing_start_time_ != 0 && headers_timeout_ != 0) { |
685 |
42 |
uint64_t now = uv_hrtime(); |
|
686 |
42 |
uint64_t parsing_time = (now - header_parsing_start_time_) / 1e6; |
|
687 |
|||
688 |
✓✓ | 42 |
if (parsing_time > headers_timeout_) { |
689 |
Local<Value> cb = |
||
690 |
18 |
object()->Get(env()->context(), kOnTimeout).ToLocalChecked(); |
|
691 |
|||
692 |
✗✓ | 6 |
if (!cb->IsFunction()) |
693 |
return; |
||
694 |
|||
695 |
6 |
MakeCallback(cb.As<Function>(), 0, nullptr); |
|
696 |
|||
697 |
6 |
return; |
|
698 |
} |
||
699 |
} |
||
700 |
|||
701 |
Local<Value> cb = |
||
702 |
11715 |
object()->Get(env()->context(), kOnExecute).ToLocalChecked(); |
|
703 |
|||
704 |
✓✓ | 3905 |
if (!cb->IsFunction()) |
705 |
1002 |
return; |
|
706 |
|||
707 |
// Hooks for GetCurrentBuffer |
||
708 |
2903 |
current_buffer_len_ = nread; |
|
709 |
2903 |
current_buffer_data_ = buf.base; |
|
710 |
|||
711 |
2903 |
MakeCallback(cb.As<Function>(), 1, &ret); |
|
712 |
|||
713 |
2900 |
current_buffer_len_ = 0; |
|
714 |
✓✓ | 2900 |
current_buffer_data_ = nullptr; |
715 |
} |
||
716 |
|||
717 |
|||
718 |
7979 |
Local<Value> Execute(const char* data, size_t len) { |
|
719 |
7979 |
EscapableHandleScope scope(env()->isolate()); |
|
720 |
|||
721 |
7979 |
current_buffer_len_ = len; |
|
722 |
7979 |
current_buffer_data_ = data; |
|
723 |
7979 |
got_exception_ = false; |
|
724 |
|||
725 |
llhttp_errno_t err; |
||
726 |
|||
727 |
// Do not allow re-entering `http_parser_execute()` |
||
728 |
✗✓ | 7979 |
CHECK_EQ(execute_depth_, 0); |
729 |
|||
730 |
7979 |
execute_depth_++; |
|
731 |
✓✓ | 7979 |
if (data == nullptr) { |
732 |
922 |
err = llhttp_finish(&parser_); |
|
733 |
} else { |
||
734 |
7057 |
err = llhttp_execute(&parser_, data, len); |
|
735 |
7055 |
Save(); |
|
736 |
} |
||
737 |
7977 |
execute_depth_--; |
|
738 |
|||
739 |
// Calculate bytes read and resume after Upgrade/CONNECT pause |
||
740 |
7977 |
size_t nread = len; |
|
741 |
✓✓ | 7977 |
if (err != HPE_OK) { |
742 |
332 |
nread = llhttp_get_error_pos(&parser_) - data; |
|
743 |
|||
744 |
// This isn't a real pause, just a way to stop parsing early. |
||
745 |
✓✓ | 332 |
if (err == HPE_PAUSED_UPGRADE) { |
746 |
53 |
err = HPE_OK; |
|
747 |
53 |
llhttp_resume_after_upgrade(&parser_); |
|
748 |
} |
||
749 |
} |
||
750 |
|||
751 |
// Apply pending pause |
||
752 |
✗✓ | 7977 |
if (pending_pause_) { |
753 |
pending_pause_ = false; |
||
754 |
llhttp_pause(&parser_); |
||
755 |
} |
||
756 |
|||
757 |
// Unassign the 'buffer_' variable |
||
758 |
7977 |
current_buffer_.Clear(); |
|
759 |
7977 |
current_buffer_len_ = 0; |
|
760 |
7977 |
current_buffer_data_ = nullptr; |
|
761 |
|||
762 |
// If there was an exception in one of the callbacks |
||
763 |
✓✓ | 7977 |
if (got_exception_) |
764 |
8 |
return scope.Escape(Local<Value>()); |
|
765 |
|||
766 |
7969 |
Local<Integer> nread_obj = Integer::New(env()->isolate(), nread); |
|
767 |
|||
768 |
// If there was a parse error in one of the callbacks |
||
769 |
// TODO(bnoordhuis) What if there is an error on EOF? |
||
770 |
✓✓✓✓ |
7969 |
if (!parser_.upgrade && err != HPE_OK) { |
771 |
218 |
Local<Value> e = Exception::Error(env()->parse_error_string()); |
|
772 |
654 |
Local<Object> obj = e->ToObject(env()->isolate()->GetCurrentContext()) |
|
773 |
218 |
.ToLocalChecked(); |
|
774 |
436 |
obj->Set(env()->context(), |
|
775 |
env()->bytes_parsed_string(), |
||
776 |
872 |
nread_obj).Check(); |
|
777 |
218 |
const char* errno_reason = llhttp_get_error_reason(&parser_); |
|
778 |
|||
779 |
Local<String> code; |
||
780 |
Local<String> reason; |
||
781 |
✓✓ | 218 |
if (err == HPE_USER) { |
782 |
15 |
const char* colon = strchr(errno_reason, ':'); |
|
783 |
✗✓ | 15 |
CHECK_NOT_NULL(colon); |
784 |
code = OneByteString(env()->isolate(), errno_reason, |
||
785 |
15 |
colon - errno_reason); |
|
786 |
15 |
reason = OneByteString(env()->isolate(), colon + 1); |
|
787 |
} else { |
||
788 |
203 |
code = OneByteString(env()->isolate(), llhttp_errno_name(err)); |
|
789 |
203 |
reason = OneByteString(env()->isolate(), errno_reason); |
|
790 |
} |
||
791 |
|||
792 |
872 |
obj->Set(env()->context(), env()->code_string(), code).Check(); |
|
793 |
872 |
obj->Set(env()->context(), env()->reason_string(), reason).Check(); |
|
794 |
return scope.Escape(e); |
||
795 |
} |
||
796 |
|||
797 |
// No return value is needed for `Finish()` |
||
798 |
✓✓ | 7751 |
if (data == nullptr) { |
799 |
891 |
return scope.Escape(Local<Value>()); |
|
800 |
} |
||
801 |
6860 |
return scope.Escape(nread_obj); |
|
802 |
} |
||
803 |
|||
804 |
16966 |
Local<Array> CreateHeaders() { |
|
805 |
// There could be extra entries but the max size should be fixed |
||
806 |
✓✓ | 1102790 |
Local<Value> headers_v[kMaxHeaderFieldsCount * 2]; |
807 |
|||
808 |
✓✓ | 34119 |
for (size_t i = 0; i < num_values_; ++i) { |
809 |
34306 |
headers_v[i * 2] = fields_[i].ToString(env()); |
|
810 |
34306 |
headers_v[i * 2 + 1] = values_[i].ToTrimmedString(env()); |
|
811 |
} |
||
812 |
|||
813 |
16966 |
return Array::New(env()->isolate(), headers_v, num_values_ * 2); |
|
814 |
} |
||
815 |
|||
816 |
|||
817 |
// spill headers and request path to JS land |
||
818 |
67 |
void Flush() { |
|
819 |
134 |
HandleScope scope(env()->isolate()); |
|
820 |
|||
821 |
67 |
Local<Object> obj = object(); |
|
822 |
201 |
Local<Value> cb = obj->Get(env()->context(), kOnHeaders).ToLocalChecked(); |
|
823 |
|||
824 |
✗✓ | 67 |
if (!cb->IsFunction()) |
825 |
return; |
||
826 |
|||
827 |
Local<Value> argv[2] = { |
||
828 |
CreateHeaders(), |
||
829 |
url_.ToString(env()) |
||
830 |
201 |
}; |
|
831 |
|||
832 |
MaybeLocal<Value> r = MakeCallback(cb.As<Function>(), |
||
833 |
67 |
arraysize(argv), |
|
834 |
134 |
argv); |
|
835 |
|||
836 |
✗✓ | 67 |
if (r.IsEmpty()) |
837 |
got_exception_ = true; |
||
838 |
|||
839 |
67 |
url_.Reset(); |
|
840 |
✓✗ | 67 |
have_flushed_ = true; |
841 |
} |
||
842 |
|||
843 |
|||
844 |
6033 |
void Init(llhttp_type_t type, uint64_t max_http_header_size, |
|
845 |
bool lenient, uint64_t headers_timeout) { |
||
846 |
6033 |
llhttp_init(&parser_, type, &settings); |
|
847 |
6033 |
llhttp_set_lenient(&parser_, lenient); |
|
848 |
6033 |
header_nread_ = 0; |
|
849 |
6033 |
url_.Reset(); |
|
850 |
6033 |
status_message_.Reset(); |
|
851 |
6033 |
num_fields_ = 0; |
|
852 |
6033 |
num_values_ = 0; |
|
853 |
6033 |
have_flushed_ = false; |
|
854 |
6033 |
got_exception_ = false; |
|
855 |
6033 |
max_http_header_size_ = max_http_header_size; |
|
856 |
6033 |
header_parsing_start_time_ = 0; |
|
857 |
6033 |
headers_timeout_ = headers_timeout; |
|
858 |
6033 |
} |
|
859 |
|||
860 |
|||
861 |
51443 |
int TrackHeader(size_t len) { |
|
862 |
51443 |
header_nread_ += len; |
|
863 |
✓✓ | 51443 |
if (header_nread_ >= max_http_header_size_) { |
864 |
15 |
llhttp_set_error_reason(&parser_, "HPE_HEADER_OVERFLOW:Header overflow"); |
|
865 |
15 |
return HPE_USER; |
|
866 |
} |
||
867 |
51428 |
return 0; |
|
868 |
} |
||
869 |
|||
870 |
|||
871 |
133600 |
int MaybePause() { |
|
872 |
✗✓ | 133600 |
CHECK_NE(execute_depth_, 0); |
873 |
|||
874 |
✓✗ | 133600 |
if (!pending_pause_) { |
875 |
133600 |
return 0; |
|
876 |
} |
||
877 |
|||
878 |
pending_pause_ = false; |
||
879 |
llhttp_set_error_reason(&parser_, "Paused in callback"); |
||
880 |
return HPE_PAUSED; |
||
881 |
} |
||
882 |
|||
883 |
|||
884 |
bool IsNotIndicativeOfMemoryLeakAtExit() const override { |
||
885 |
// HTTP parsers are able to emit events without any GC root referring |
||
886 |
// to them, because they receive events directly from the underlying |
||
887 |
// libuv resource. |
||
888 |
return true; |
||
889 |
} |
||
890 |
|||
891 |
|||
892 |
llhttp_t parser_; |
||
893 |
StringPtr fields_[kMaxHeaderFieldsCount]; // header fields |
||
894 |
StringPtr values_[kMaxHeaderFieldsCount]; // header values |
||
895 |
StringPtr url_; |
||
896 |
StringPtr status_message_; |
||
897 |
size_t num_fields_; |
||
898 |
size_t num_values_; |
||
899 |
bool have_flushed_; |
||
900 |
bool got_exception_; |
||
901 |
Local<Object> current_buffer_; |
||
902 |
size_t current_buffer_len_; |
||
903 |
const char* current_buffer_data_; |
||
904 |
unsigned int execute_depth_ = 0; |
||
905 |
bool pending_pause_ = false; |
||
906 |
uint64_t header_nread_ = 0; |
||
907 |
uint64_t max_http_header_size_; |
||
908 |
uint64_t headers_timeout_; |
||
909 |
uint64_t header_parsing_start_time_ = 0; |
||
910 |
|||
911 |
BaseObjectPtr<BindingData> binding_data_; |
||
912 |
|||
913 |
// These are helper functions for filling `http_parser_settings`, which turn |
||
914 |
// a member function of Parser into a C-style HTTP parser callback. |
||
915 |
template <typename Parser, Parser> struct Proxy; |
||
916 |
template <typename Parser, typename ...Args, int (Parser::*Member)(Args...)> |
||
917 |
struct Proxy<int (Parser::*)(Args...), Member> { |
||
918 |
133751 |
static int Raw(llhttp_t* p, Args ... args) { |
|
919 |
133751 |
Parser* parser = ContainerOf(&Parser::parser_, p); |
|
920 |
133751 |
int rv = (parser->*Member)(std::forward<Args>(args)...); |
|
921 |
✓✗✓✗ ✓✗✓✗ ✓✓✓✓ ✓✗✓✗ ✓✗✓✗ |
133749 |
if (rv == 0) { |
922 |
133600 |
rv = parser->MaybePause(); |
|
923 |
} |
||
924 |
133749 |
return rv; |
|
925 |
} |
||
926 |
}; |
||
927 |
|||
928 |
typedef int (Parser::*Call)(); |
||
929 |
typedef int (Parser::*DataCall)(const char* at, size_t length); |
||
930 |
|||
931 |
static const llhttp_settings_t settings; |
||
932 |
}; |
||
933 |
|||
934 |
const llhttp_settings_t Parser::settings = { |
||
935 |
Proxy<Call, &Parser::on_message_begin>::Raw, |
||
936 |
Proxy<DataCall, &Parser::on_url>::Raw, |
||
937 |
Proxy<DataCall, &Parser::on_status>::Raw, |
||
938 |
Proxy<DataCall, &Parser::on_header_field>::Raw, |
||
939 |
Proxy<DataCall, &Parser::on_header_value>::Raw, |
||
940 |
Proxy<Call, &Parser::on_headers_complete>::Raw, |
||
941 |
Proxy<DataCall, &Parser::on_body>::Raw, |
||
942 |
Proxy<Call, &Parser::on_message_complete>::Raw, |
||
943 |
Proxy<Call, &Parser::on_chunk_header>::Raw, |
||
944 |
Proxy<Call, &Parser::on_chunk_complete>::Raw, |
||
945 |
}; |
||
946 |
|||
947 |
|||
948 |
742 |
void InitializeHttpParser(Local<Object> target, |
|
949 |
Local<Value> unused, |
||
950 |
Local<Context> context, |
||
951 |
void* priv) { |
||
952 |
742 |
Environment* env = Environment::GetCurrent(context); |
|
953 |
BindingData* const binding_data = |
||
954 |
742 |
env->AddBindingData<BindingData>(context, target); |
|
955 |
✗✓ | 742 |
if (binding_data == nullptr) return; |
956 |
|||
957 |
742 |
Local<FunctionTemplate> t = env->NewFunctionTemplate(Parser::New); |
|
958 |
1484 |
t->InstanceTemplate()->SetInternalFieldCount(Parser::kInternalFieldCount); |
|
959 |
1484 |
t->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "HTTPParser")); |
|
960 |
|||
961 |
2968 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "REQUEST"), |
|
962 |
742 |
Integer::New(env->isolate(), HTTP_REQUEST)); |
|
963 |
2968 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "RESPONSE"), |
|
964 |
742 |
Integer::New(env->isolate(), HTTP_RESPONSE)); |
|
965 |
2968 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kOnMessageBegin"), |
|
966 |
742 |
Integer::NewFromUnsigned(env->isolate(), kOnMessageBegin)); |
|
967 |
2968 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kOnHeaders"), |
|
968 |
742 |
Integer::NewFromUnsigned(env->isolate(), kOnHeaders)); |
|
969 |
2968 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kOnHeadersComplete"), |
|
970 |
742 |
Integer::NewFromUnsigned(env->isolate(), kOnHeadersComplete)); |
|
971 |
2968 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kOnBody"), |
|
972 |
742 |
Integer::NewFromUnsigned(env->isolate(), kOnBody)); |
|
973 |
2968 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kOnMessageComplete"), |
|
974 |
742 |
Integer::NewFromUnsigned(env->isolate(), kOnMessageComplete)); |
|
975 |
2968 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kOnExecute"), |
|
976 |
742 |
Integer::NewFromUnsigned(env->isolate(), kOnExecute)); |
|
977 |
2968 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kOnTimeout"), |
|
978 |
742 |
Integer::NewFromUnsigned(env->isolate(), kOnTimeout)); |
|
979 |
|||
980 |
742 |
Local<Array> methods = Array::New(env->isolate()); |
|
981 |
#define V(num, name, string) \ |
||
982 |
methods->Set(env->context(), \ |
||
983 |
num, FIXED_ONE_BYTE_STRING(env->isolate(), #string)).Check(); |
||
984 |
13356 |
HTTP_METHOD_MAP(V) |
|
985 |
#undef V |
||
986 |
1484 |
target->Set(env->context(), |
|
987 |
FIXED_ONE_BYTE_STRING(env->isolate(), "methods"), |
||
988 |
8904 |
methods).Check(); |
|
989 |
3710 |
||
990 |
5936 |
t->Inherit(AsyncWrap::GetConstructorTemplate(env)); |
|
991 |
5194 |
env->SetProtoMethod(t, "close", Parser::Close); |
|
992 |
5194 |
env->SetProtoMethod(t, "free", Parser::Free); |
|
993 |
5194 |
env->SetProtoMethod(t, "execute", Parser::Execute); |
|
994 |
4452 |
env->SetProtoMethod(t, "finish", Parser::Finish); |
|
995 |
5936 |
env->SetProtoMethod(t, "initialize", Parser::Initialize); |
|
996 |
5194 |
env->SetProtoMethod(t, "pause", Parser::Pause<true>); |
|
997 |
5194 |
env->SetProtoMethod(t, "resume", Parser::Pause<false>); |
|
998 |
5194 |
env->SetProtoMethod(t, "consume", Parser::Consume); |
|
999 |
4452 |
env->SetProtoMethod(t, "unconsume", Parser::Unconsume); |
|
1000 |
5936 |
env->SetProtoMethod(t, "getCurrentBuffer", Parser::GetCurrentBuffer); |
|
1001 |
4452 |
||
1002 |
5194 |
target->Set(env->context(), |
|
1003 |
5936 |
FIXED_ONE_BYTE_STRING(env->isolate(), "HTTPParser"), |
|
1004 |
8162 |
t->GetFunction(env->context()).ToLocalChecked()).Check(); |
|
1005 |
4452 |
} |
|
1006 |
4452 |
||
1007 |
3710 |
} // anonymous namespace |
|
1008 |
4452 |
} // namespace node |
|
1009 |
|||
1010 |
✓✗✓✗ |
18704 |
NODE_MODULE_CONTEXT_AWARE_INTERNAL(http_parser, node::InitializeHttpParser) |
Generated by: GCOVR (Version 3.4) |