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::Isolate; |
||
64 |
using v8::Local; |
||
65 |
using v8::MaybeLocal; |
||
66 |
using v8::Number; |
||
67 |
using v8::Object; |
||
68 |
using v8::String; |
||
69 |
using v8::Uint32; |
||
70 |
using v8::Undefined; |
||
71 |
using v8::Value; |
||
72 |
|||
73 |
const uint32_t kOnMessageBegin = 0; |
||
74 |
const uint32_t kOnHeaders = 1; |
||
75 |
const uint32_t kOnHeadersComplete = 2; |
||
76 |
const uint32_t kOnBody = 3; |
||
77 |
const uint32_t kOnMessageComplete = 4; |
||
78 |
const uint32_t kOnExecute = 5; |
||
79 |
const uint32_t kOnTimeout = 6; |
||
80 |
// Any more fields than this will be flushed into JS |
||
81 |
const size_t kMaxHeaderFieldsCount = 32; |
||
82 |
|||
83 |
const uint32_t kLenientNone = 0; |
||
84 |
const uint32_t kLenientHeaders = 1 << 0; |
||
85 |
const uint32_t kLenientChunkedLength = 1 << 1; |
||
86 |
const uint32_t kLenientKeepAlive = 1 << 2; |
||
87 |
const uint32_t kLenientAll = kLenientHeaders | kLenientChunkedLength | |
||
88 |
kLenientKeepAlive; |
||
89 |
|||
90 |
20600 |
inline bool IsOWS(char c) { |
|
91 |
✓✓✓✓ |
20600 |
return c == ' ' || c == '\t'; |
92 |
} |
||
93 |
|||
94 |
1658 |
class BindingData : public BaseObject { |
|
95 |
public: |
||
96 |
895 |
BindingData(Environment* env, Local<Object> obj) |
|
97 |
895 |
: BaseObject(env, obj) {} |
|
98 |
|||
99 |
static constexpr FastStringKey type_name { "http_parser" }; |
||
100 |
|||
101 |
std::vector<char> parser_buffer; |
||
102 |
bool parser_buffer_in_use = false; |
||
103 |
|||
104 |
3 |
void MemoryInfo(MemoryTracker* tracker) const override { |
|
105 |
3 |
tracker->TrackField("parser_buffer", parser_buffer); |
|
106 |
3 |
} |
|
107 |
3 |
SET_SELF_SIZE(BindingData) |
|
108 |
3 |
SET_MEMORY_INFO_NAME(BindingData) |
|
109 |
}; |
||
110 |
|||
111 |
// helper class for the Parser |
||
112 |
struct StringPtr { |
||
113 |
348414 |
StringPtr() { |
|
114 |
348414 |
on_heap_ = false; |
|
115 |
348414 |
Reset(); |
|
116 |
348414 |
} |
|
117 |
|||
118 |
|||
119 |
689436 |
~StringPtr() { |
|
120 |
344718 |
Reset(); |
|
121 |
344718 |
} |
|
122 |
|||
123 |
|||
124 |
// If str_ does not point to a heap string yet, this function makes it do |
||
125 |
// so. This is called at the end of each http_parser_execute() so as not |
||
126 |
// to leak references. See issue #2438 and test-http-parser-bad-ref.js. |
||
127 |
16159 |
void Save() { |
|
128 |
✓✓✓✓ |
16159 |
if (!on_heap_ && size_ > 0) { |
129 |
6762 |
char* s = new char[size_]; |
|
130 |
6762 |
memcpy(s, str_, size_); |
|
131 |
6762 |
str_ = s; |
|
132 |
6762 |
on_heap_ = true; |
|
133 |
} |
||
134 |
16159 |
} |
|
135 |
|||
136 |
|||
137 |
783023 |
void Reset() { |
|
138 |
✓✓ | 783023 |
if (on_heap_) { |
139 |
✓✗ | 6709 |
delete[] str_; |
140 |
6709 |
on_heap_ = false; |
|
141 |
} |
||
142 |
|||
143 |
783023 |
str_ = nullptr; |
|
144 |
783023 |
size_ = 0; |
|
145 |
783023 |
} |
|
146 |
|||
147 |
|||
148 |
59120 |
void Update(const char* str, size_t size) { |
|
149 |
✓✓ | 59120 |
if (str_ == nullptr) { |
150 |
59059 |
str_ = str; |
|
151 |
✓✓✓✗ |
61 |
} else if (on_heap_ || str_ + size_ != str) { |
152 |
// Non-consecutive input, make a copy on the heap. |
||
153 |
// TODO(bnoordhuis) Use slab allocation, O(n) allocs is bad. |
||
154 |
61 |
char* s = new char[size_ + size]; |
|
155 |
61 |
memcpy(s, str_, size_); |
|
156 |
61 |
memcpy(s + size_, str, size); |
|
157 |
|||
158 |
✓✓ | 61 |
if (on_heap_) |
159 |
✓✗ | 59 |
delete[] str_; |
160 |
else |
||
161 |
2 |
on_heap_ = true; |
|
162 |
|||
163 |
61 |
str_ = s; |
|
164 |
} |
||
165 |
59120 |
size_ += size; |
|
166 |
59120 |
} |
|
167 |
|||
168 |
|||
169 |
58944 |
Local<String> ToString(Environment* env) const { |
|
170 |
✓✓ | 58944 |
if (size_ != 0) |
171 |
58879 |
return OneByteString(env->isolate(), str_, size_); |
|
172 |
else |
||
173 |
130 |
return String::Empty(env->isolate()); |
|
174 |
} |
||
175 |
|||
176 |
|||
177 |
// Strip trailing OWS (SPC or HTAB) from string. |
||
178 |
20602 |
Local<String> ToTrimmedString(Environment* env) { |
|
179 |
✓✓✓✓ ✓✓ |
20613 |
while (size_ > 0 && IsOWS(str_[size_ - 1])) { |
180 |
11 |
size_--; |
|
181 |
} |
||
182 |
20591 |
return ToString(env); |
|
183 |
} |
||
184 |
|||
185 |
|||
186 |
const char* str_; |
||
187 |
bool on_heap_; |
||
188 |
size_t size_; |
||
189 |
}; |
||
190 |
|||
191 |
class Parser; |
||
192 |
|||
193 |
struct ParserComparator { |
||
194 |
bool operator()(const Parser* lhs, const Parser* rhs) const; |
||
195 |
}; |
||
196 |
|||
197 |
1140 |
class ConnectionsList : public BaseObject { |
|
198 |
public: |
||
199 |
static void New(const FunctionCallbackInfo<Value>& args); |
||
200 |
|||
201 |
static void All(const FunctionCallbackInfo<Value>& args); |
||
202 |
|||
203 |
static void Idle(const FunctionCallbackInfo<Value>& args); |
||
204 |
|||
205 |
static void Active(const FunctionCallbackInfo<Value>& args); |
||
206 |
|||
207 |
static void Expired(const FunctionCallbackInfo<Value>& args); |
||
208 |
|||
209 |
30441 |
void Push(Parser* parser) { |
|
210 |
30441 |
all_connections_.insert(parser); |
|
211 |
30441 |
} |
|
212 |
|||
213 |
30424 |
void Pop(Parser* parser) { |
|
214 |
30424 |
all_connections_.erase(parser); |
|
215 |
30424 |
} |
|
216 |
|||
217 |
16436 |
void PushActive(Parser* parser) { |
|
218 |
16436 |
active_connections_.insert(parser); |
|
219 |
16436 |
} |
|
220 |
|||
221 |
30424 |
void PopActive(Parser* parser) { |
|
222 |
30424 |
active_connections_.erase(parser); |
|
223 |
30424 |
} |
|
224 |
|||
225 |
SET_NO_MEMORY_INFO() |
||
226 |
SET_MEMORY_INFO_NAME(ConnectionsList) |
||
227 |
SET_SELF_SIZE(ConnectionsList) |
||
228 |
|||
229 |
private: |
||
230 |
588 |
ConnectionsList(Environment* env, Local<Object> object) |
|
231 |
588 |
: BaseObject(env, object) { |
|
232 |
588 |
MakeWeak(); |
|
233 |
588 |
} |
|
234 |
|||
235 |
std::set<Parser*, ParserComparator> all_connections_; |
||
236 |
std::set<Parser*, ParserComparator> active_connections_; |
||
237 |
}; |
||
238 |
|||
239 |
✓✗✓✓ ✓✗✓✓ |
10446 |
class Parser : public AsyncWrap, public StreamListener { |
240 |
friend class ConnectionsList; |
||
241 |
friend struct ParserComparator; |
||
242 |
|||
243 |
public: |
||
244 |
5279 |
Parser(BindingData* binding_data, Local<Object> wrap) |
|
245 |
5279 |
: AsyncWrap(binding_data->env(), wrap), |
|
246 |
current_buffer_len_(0), |
||
247 |
current_buffer_data_(nullptr), |
||
248 |
✓✓✓✓ |
5279 |
binding_data_(binding_data) { |
249 |
5279 |
} |
|
250 |
|||
251 |
SET_NO_MEMORY_INFO() |
||
252 |
SET_MEMORY_INFO_NAME(Parser) |
||
253 |
SET_SELF_SIZE(Parser) |
||
254 |
|||
255 |
17762 |
int on_message_begin() { |
|
256 |
// Important: Pop from the lists BEFORE resetting the last_message_start_ |
||
257 |
// otherwise std::set.erase will fail. |
||
258 |
✓✓ | 17762 |
if (connectionsList_ != nullptr) { |
259 |
14070 |
connectionsList_->Pop(this); |
|
260 |
14070 |
connectionsList_->PopActive(this); |
|
261 |
} |
||
262 |
|||
263 |
17762 |
num_fields_ = num_values_ = 0; |
|
264 |
17762 |
headers_completed_ = false; |
|
265 |
17762 |
last_message_start_ = uv_hrtime(); |
|
266 |
17762 |
url_.Reset(); |
|
267 |
17762 |
status_message_.Reset(); |
|
268 |
|||
269 |
✓✓ | 17762 |
if (connectionsList_ != nullptr) { |
270 |
14070 |
connectionsList_->Push(this); |
|
271 |
14070 |
connectionsList_->PushActive(this); |
|
272 |
} |
||
273 |
|||
274 |
53286 |
Local<Value> cb = object()->Get(env()->context(), kOnMessageBegin) |
|
275 |
17762 |
.ToLocalChecked(); |
|
276 |
✗✓ | 17762 |
if (cb->IsFunction()) { |
277 |
InternalCallbackScope callback_scope( |
||
278 |
this, InternalCallbackScope::kSkipTaskQueues); |
||
279 |
|||
280 |
MaybeLocal<Value> r = cb.As<Function>()->Call( |
||
281 |
env()->context(), object(), 0, nullptr); |
||
282 |
|||
283 |
if (r.IsEmpty()) callback_scope.MarkAsFailed(); |
||
284 |
} |
||
285 |
|||
286 |
17762 |
return 0; |
|
287 |
} |
||
288 |
|||
289 |
|||
290 |
14235 |
int on_url(const char* at, size_t length) { |
|
291 |
14235 |
int rv = TrackHeader(length); |
|
292 |
✗✓ | 14235 |
if (rv != 0) { |
293 |
return rv; |
||
294 |
} |
||
295 |
|||
296 |
14235 |
url_.Update(at, length); |
|
297 |
14235 |
return 0; |
|
298 |
} |
||
299 |
|||
300 |
|||
301 |
3518 |
int on_status(const char* at, size_t length) { |
|
302 |
3518 |
int rv = TrackHeader(length); |
|
303 |
✗✓ | 3518 |
if (rv != 0) { |
304 |
return rv; |
||
305 |
} |
||
306 |
|||
307 |
3518 |
status_message_.Update(at, length); |
|
308 |
3518 |
return 0; |
|
309 |
} |
||
310 |
|||
311 |
|||
312 |
20700 |
int on_header_field(const char* at, size_t length) { |
|
313 |
20700 |
int rv = TrackHeader(length); |
|
314 |
✗✓ | 20700 |
if (rv != 0) { |
315 |
return rv; |
||
316 |
} |
||
317 |
|||
318 |
✓✓ | 20700 |
if (num_fields_ == num_values_) { |
319 |
// start of new field name |
||
320 |
20671 |
num_fields_++; |
|
321 |
✓✓ | 20671 |
if (num_fields_ == kMaxHeaderFieldsCount) { |
322 |
// ran out of space - flush to javascript land |
||
323 |
47 |
Flush(); |
|
324 |
47 |
num_fields_ = 1; |
|
325 |
47 |
num_values_ = 0; |
|
326 |
} |
||
327 |
20671 |
fields_[num_fields_ - 1].Reset(); |
|
328 |
} |
||
329 |
|||
330 |
✗✓ | 20700 |
CHECK_LT(num_fields_, kMaxHeaderFieldsCount); |
331 |
✗✓ | 20700 |
CHECK_EQ(num_fields_, num_values_ + 1); |
332 |
|||
333 |
20700 |
fields_[num_fields_ - 1].Update(at, length); |
|
334 |
|||
335 |
20700 |
return 0; |
|
336 |
} |
||
337 |
|||
338 |
|||
339 |
20684 |
int on_header_value(const char* at, size_t length) { |
|
340 |
20684 |
int rv = TrackHeader(length); |
|
341 |
✓✓ | 20684 |
if (rv != 0) { |
342 |
17 |
return rv; |
|
343 |
} |
||
344 |
|||
345 |
✓✓ | 20667 |
if (num_values_ != num_fields_) { |
346 |
// start of new header value |
||
347 |
20643 |
num_values_++; |
|
348 |
20643 |
values_[num_values_ - 1].Reset(); |
|
349 |
} |
||
350 |
|||
351 |
✗✓ | 20667 |
CHECK_LT(num_values_, arraysize(values_)); |
352 |
✗✓ | 20667 |
CHECK_EQ(num_values_, num_fields_); |
353 |
|||
354 |
20667 |
values_[num_values_ - 1].Update(at, length); |
|
355 |
|||
356 |
20667 |
return 0; |
|
357 |
} |
||
358 |
|||
359 |
|||
360 |
17701 |
int on_headers_complete() { |
|
361 |
17701 |
headers_completed_ = true; |
|
362 |
17701 |
header_nread_ = 0; |
|
363 |
|||
364 |
// Arguments for the on-headers-complete javascript callback. This |
||
365 |
// list needs to be kept in sync with the actual argument list for |
||
366 |
// `parserOnHeadersComplete` in lib/_http_common.js. |
||
367 |
enum on_headers_complete_arg_index { |
||
368 |
A_VERSION_MAJOR = 0, |
||
369 |
A_VERSION_MINOR, |
||
370 |
A_HEADERS, |
||
371 |
A_METHOD, |
||
372 |
A_URL, |
||
373 |
A_STATUS_CODE, |
||
374 |
A_STATUS_MESSAGE, |
||
375 |
A_UPGRADE, |
||
376 |
A_SHOULD_KEEP_ALIVE, |
||
377 |
A_MAX |
||
378 |
}; |
||
379 |
|||
380 |
✓✓ | 177010 |
Local<Value> argv[A_MAX]; |
381 |
17701 |
Local<Object> obj = object(); |
|
382 |
35402 |
Local<Value> cb = obj->Get(env()->context(), |
|
383 |
35402 |
kOnHeadersComplete).ToLocalChecked(); |
|
384 |
|||
385 |
✗✓ | 17701 |
if (!cb->IsFunction()) |
386 |
return 0; |
||
387 |
|||
388 |
17701 |
Local<Value> undefined = Undefined(env()->isolate()); |
|
389 |
✓✓ | 177010 |
for (size_t i = 0; i < arraysize(argv); i++) |
390 |
159309 |
argv[i] = undefined; |
|
391 |
|||
392 |
✓✓ | 17701 |
if (have_flushed_) { |
393 |
// Slow case, flush remaining headers. |
||
394 |
16 |
Flush(); |
|
395 |
} else { |
||
396 |
// Fast case, pass headers and URL to JS land. |
||
397 |
35370 |
argv[A_HEADERS] = CreateHeaders(); |
|
398 |
✓✓ | 17685 |
if (parser_.type == HTTP_REQUEST) |
399 |
28368 |
argv[A_URL] = url_.ToString(env()); |
|
400 |
} |
||
401 |
|||
402 |
17701 |
num_fields_ = 0; |
|
403 |
17701 |
num_values_ = 0; |
|
404 |
|||
405 |
// METHOD |
||
406 |
✓✓ | 17701 |
if (parser_.type == HTTP_REQUEST) { |
407 |
28384 |
argv[A_METHOD] = |
|
408 |
14192 |
Uint32::NewFromUnsigned(env()->isolate(), parser_.method); |
|
409 |
} |
||
410 |
|||
411 |
// STATUS |
||
412 |
✓✓ | 17701 |
if (parser_.type == HTTP_RESPONSE) { |
413 |
7018 |
argv[A_STATUS_CODE] = |
|
414 |
3509 |
Integer::New(env()->isolate(), parser_.status_code); |
|
415 |
7018 |
argv[A_STATUS_MESSAGE] = status_message_.ToString(env()); |
|
416 |
} |
||
417 |
|||
418 |
// VERSION |
||
419 |
35402 |
argv[A_VERSION_MAJOR] = Integer::New(env()->isolate(), parser_.http_major); |
|
420 |
35402 |
argv[A_VERSION_MINOR] = Integer::New(env()->isolate(), parser_.http_minor); |
|
421 |
|||
422 |
bool should_keep_alive; |
||
423 |
17701 |
should_keep_alive = llhttp_should_keep_alive(&parser_); |
|
424 |
|||
425 |
35402 |
argv[A_SHOULD_KEEP_ALIVE] = |
|
426 |
Boolean::New(env()->isolate(), should_keep_alive); |
||
427 |
|||
428 |
35402 |
argv[A_UPGRADE] = Boolean::New(env()->isolate(), parser_.upgrade); |
|
429 |
|||
430 |
MaybeLocal<Value> head_response; |
||
431 |
{ |
||
432 |
InternalCallbackScope callback_scope( |
||
433 |
35399 |
this, InternalCallbackScope::kSkipTaskQueues); |
|
434 |
53100 |
head_response = cb.As<Function>()->Call( |
|
435 |
53100 |
env()->context(), object(), arraysize(argv), argv); |
|
436 |
✓✓ | 17698 |
if (head_response.IsEmpty()) callback_scope.MarkAsFailed(); |
437 |
} |
||
438 |
|||
439 |
int64_t val; |
||
440 |
|||
441 |
✓✓✗✓ ✓✓ |
70776 |
if (head_response.IsEmpty() || !head_response.ToLocalChecked() |
442 |
35388 |
->IntegerValue(env()->context()) |
|
443 |
17690 |
.To(&val)) { |
|
444 |
8 |
got_exception_ = true; |
|
445 |
8 |
return -1; |
|
446 |
} |
||
447 |
|||
448 |
17690 |
return static_cast<int>(val); |
|
449 |
} |
||
450 |
|||
451 |
|||
452 |
11488 |
int on_body(const char* at, size_t length) { |
|
453 |
✗✓ | 11488 |
if (length == 0) |
454 |
return 0; |
||
455 |
|||
456 |
11488 |
Environment* env = this->env(); |
|
457 |
22976 |
HandleScope handle_scope(env->isolate()); |
|
458 |
|||
459 |
34464 |
Local<Value> cb = object()->Get(env->context(), kOnBody).ToLocalChecked(); |
|
460 |
|||
461 |
✗✓ | 11488 |
if (!cb->IsFunction()) |
462 |
return 0; |
||
463 |
|||
464 |
22976 |
Local<Value> buffer = Buffer::Copy(env, at, length).ToLocalChecked(); |
|
465 |
|||
466 |
22976 |
MaybeLocal<Value> r = MakeCallback(cb.As<Function>(), 1, &buffer); |
|
467 |
|||
468 |
✗✓ | 11488 |
if (r.IsEmpty()) { |
469 |
got_exception_ = true; |
||
470 |
llhttp_set_error_reason(&parser_, "HPE_JS_EXCEPTION:JS Exception"); |
||
471 |
return HPE_USER; |
||
472 |
} |
||
473 |
|||
474 |
11488 |
return 0; |
|
475 |
} |
||
476 |
|||
477 |
|||
478 |
16488 |
int on_message_complete() { |
|
479 |
32976 |
HandleScope scope(env()->isolate()); |
|
480 |
|||
481 |
// Important: Pop from the lists BEFORE resetting the last_message_start_ |
||
482 |
// otherwise std::set.erase will fail. |
||
483 |
✓✓ | 16488 |
if (connectionsList_ != nullptr) { |
484 |
14005 |
connectionsList_->Pop(this); |
|
485 |
14005 |
connectionsList_->PopActive(this); |
|
486 |
} |
||
487 |
|||
488 |
16488 |
last_message_start_ = 0; |
|
489 |
|||
490 |
✓✓ | 16488 |
if (connectionsList_ != nullptr) { |
491 |
14005 |
connectionsList_->Push(this); |
|
492 |
} |
||
493 |
|||
494 |
✓✓ | 16488 |
if (num_fields_) |
495 |
6 |
Flush(); // Flush trailing HTTP headers. |
|
496 |
|||
497 |
16488 |
Local<Object> obj = object(); |
|
498 |
32976 |
Local<Value> cb = obj->Get(env()->context(), |
|
499 |
32976 |
kOnMessageComplete).ToLocalChecked(); |
|
500 |
|||
501 |
✓✓ | 16488 |
if (!cb->IsFunction()) |
502 |
2 |
return 0; |
|
503 |
|||
504 |
MaybeLocal<Value> r; |
||
505 |
{ |
||
506 |
InternalCallbackScope callback_scope( |
||
507 |
32972 |
this, InternalCallbackScope::kSkipTaskQueues); |
|
508 |
49458 |
r = cb.As<Function>()->Call(env()->context(), object(), 0, nullptr); |
|
509 |
✗✓ | 16486 |
if (r.IsEmpty()) callback_scope.MarkAsFailed(); |
510 |
} |
||
511 |
|||
512 |
✗✓ | 16486 |
if (r.IsEmpty()) { |
513 |
got_exception_ = true; |
||
514 |
return -1; |
||
515 |
} |
||
516 |
|||
517 |
16486 |
return 0; |
|
518 |
} |
||
519 |
|||
520 |
// Reset nread for the next chunk |
||
521 |
11677 |
int on_chunk_header() { |
|
522 |
11677 |
header_nread_ = 0; |
|
523 |
11677 |
return 0; |
|
524 |
} |
||
525 |
|||
526 |
|||
527 |
// Reset nread for the next chunk |
||
528 |
11523 |
int on_chunk_complete() { |
|
529 |
11523 |
header_nread_ = 0; |
|
530 |
11523 |
return 0; |
|
531 |
} |
||
532 |
|||
533 |
5279 |
static void New(const FunctionCallbackInfo<Value>& args) { |
|
534 |
5279 |
BindingData* binding_data = Environment::GetBindingData<BindingData>(args); |
|
535 |
5279 |
new Parser(binding_data, args.This()); |
|
536 |
5279 |
} |
|
537 |
|||
538 |
|||
539 |
4 |
static void Close(const FunctionCallbackInfo<Value>& args) { |
|
540 |
Parser* parser; |
||
541 |
✗✓ | 4 |
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
542 |
|||
543 |
✓✗ | 4 |
delete parser; |
544 |
} |
||
545 |
|||
546 |
|||
547 |
6289 |
static void Free(const FunctionCallbackInfo<Value>& args) { |
|
548 |
Parser* parser; |
||
549 |
✗✓ | 6289 |
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
550 |
|||
551 |
// Since the Parser destructor isn't going to run the destroy() callbacks |
||
552 |
// it needs to be triggered manually. |
||
553 |
6289 |
parser->EmitTraceEventDestroy(); |
|
554 |
6289 |
parser->EmitDestroy(); |
|
555 |
} |
||
556 |
|||
557 |
6292 |
static void Remove(const FunctionCallbackInfo<Value>& args) { |
|
558 |
Parser* parser; |
||
559 |
✗✓ | 6292 |
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
560 |
|||
561 |
✓✓ | 6292 |
if (parser->connectionsList_ != nullptr) { |
562 |
2349 |
parser->connectionsList_->Pop(parser); |
|
563 |
2349 |
parser->connectionsList_->PopActive(parser); |
|
564 |
} |
||
565 |
} |
||
566 |
|||
567 |
7915 |
void Save() { |
|
568 |
7915 |
url_.Save(); |
|
569 |
7915 |
status_message_.Save(); |
|
570 |
|||
571 |
✓✓ | 8111 |
for (size_t i = 0; i < num_fields_; i++) { |
572 |
196 |
fields_[i].Save(); |
|
573 |
} |
||
574 |
|||
575 |
✓✓ | 8048 |
for (size_t i = 0; i < num_values_; i++) { |
576 |
133 |
values_[i].Save(); |
|
577 |
} |
||
578 |
7915 |
} |
|
579 |
|||
580 |
// var bytesParsed = parser->execute(buffer); |
||
581 |
3595 |
static void Execute(const FunctionCallbackInfo<Value>& args) { |
|
582 |
Parser* parser; |
||
583 |
✗✓ | 3595 |
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
584 |
|||
585 |
3595 |
ArrayBufferViewContents<char> buffer(args[0]); |
|
586 |
|||
587 |
3595 |
Local<Value> ret = parser->Execute(buffer.data(), buffer.length()); |
|
588 |
|||
589 |
✓✓ | 3594 |
if (!ret.IsEmpty()) |
590 |
7182 |
args.GetReturnValue().Set(ret); |
|
591 |
} |
||
592 |
|||
593 |
|||
594 |
1192 |
static void Finish(const FunctionCallbackInfo<Value>& args) { |
|
595 |
Parser* parser; |
||
596 |
✗✓ | 1192 |
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
597 |
|||
598 |
1192 |
Local<Value> ret = parser->Execute(nullptr, 0); |
|
599 |
|||
600 |
✓✓ | 1192 |
if (!ret.IsEmpty()) |
601 |
68 |
args.GetReturnValue().Set(ret); |
|
602 |
} |
||
603 |
|||
604 |
|||
605 |
6492 |
static void Initialize(const FunctionCallbackInfo<Value>& args) { |
|
606 |
6492 |
Environment* env = Environment::GetCurrent(args); |
|
607 |
|||
608 |
6492 |
uint64_t max_http_header_size = 0; |
|
609 |
6492 |
uint32_t lenient_flags = kLenientNone; |
|
610 |
6492 |
ConnectionsList* connectionsList = nullptr; |
|
611 |
|||
612 |
✗✓ | 12984 |
CHECK(args[0]->IsInt32()); |
613 |
✗✓ | 12984 |
CHECK(args[1]->IsObject()); |
614 |
|||
615 |
✓✓ | 6492 |
if (args.Length() > 2) { |
616 |
✗✓ | 10644 |
CHECK(args[2]->IsNumber()); |
617 |
5322 |
max_http_header_size = |
|
618 |
21288 |
static_cast<uint64_t>(args[2].As<Number>()->Value()); |
|
619 |
} |
||
620 |
✓✓ | 6492 |
if (max_http_header_size == 0) { |
621 |
6488 |
max_http_header_size = env->options()->max_http_header_size; |
|
622 |
} |
||
623 |
|||
624 |
✓✓ | 6492 |
if (args.Length() > 3) { |
625 |
✗✓ | 10644 |
CHECK(args[3]->IsInt32()); |
626 |
15966 |
lenient_flags = args[3].As<Int32>()->Value(); |
|
627 |
} |
||
628 |
|||
629 |
✓✓✓✓ ✓✓ |
13596 |
if (args.Length() > 4 && !args[4]->IsNullOrUndefined()) { |
630 |
✗✓ | 4732 |
CHECK(args[4]->IsObject()); |
631 |
✗✓ | 2366 |
ASSIGN_OR_RETURN_UNWRAP(&connectionsList, args[4]); |
632 |
} |
||
633 |
|||
634 |
llhttp_type_t type = |
||
635 |
19476 |
static_cast<llhttp_type_t>(args[0].As<Int32>()->Value()); |
|
636 |
|||
637 |
✓✓✗✓ |
6492 |
CHECK(type == HTTP_REQUEST || type == HTTP_RESPONSE); |
638 |
Parser* parser; |
||
639 |
✗✓ | 6492 |
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
640 |
// Should always be called from the same context. |
||
641 |
✗✓ | 6492 |
CHECK_EQ(env, parser->env()); |
642 |
|||
643 |
6492 |
AsyncWrap::ProviderType provider = |
|
644 |
✓✓ | 6492 |
(type == HTTP_REQUEST ? |
645 |
AsyncWrap::PROVIDER_HTTPINCOMINGMESSAGE |
||
646 |
: AsyncWrap::PROVIDER_HTTPCLIENTREQUEST); |
||
647 |
|||
648 |
6492 |
parser->set_provider_type(provider); |
|
649 |
19476 |
parser->AsyncReset(args[1].As<Object>()); |
|
650 |
6492 |
parser->Init(type, max_http_header_size, lenient_flags); |
|
651 |
|||
652 |
✓✓ | 6492 |
if (connectionsList != nullptr) { |
653 |
2366 |
parser->connectionsList_ = connectionsList; |
|
654 |
|||
655 |
// This protects from a DoS attack where an attacker establishes |
||
656 |
// the connection without sending any data on applications where |
||
657 |
// server.timeout is left to the default value of zero. |
||
658 |
2366 |
parser->last_message_start_ = uv_hrtime(); |
|
659 |
|||
660 |
// Important: Push into the lists AFTER setting the last_message_start_ |
||
661 |
// otherwise std::set.erase will fail later. |
||
662 |
2366 |
parser->connectionsList_->Push(parser); |
|
663 |
2366 |
parser->connectionsList_->PushActive(parser); |
|
664 |
} else { |
||
665 |
4126 |
parser->connectionsList_ = nullptr; |
|
666 |
} |
||
667 |
} |
||
668 |
|||
669 |
template <bool should_pause> |
||
670 |
14 |
static void Pause(const FunctionCallbackInfo<Value>& args) { |
|
671 |
14 |
Environment* env = Environment::GetCurrent(args); |
|
672 |
Parser* parser; |
||
673 |
✗✓✗✓ |
14 |
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
674 |
// Should always be called from the same context. |
||
675 |
✗✓✗✓ |
14 |
CHECK_EQ(env, parser->env()); |
676 |
|||
677 |
if constexpr (should_pause) { |
||
678 |
5 |
llhttp_pause(&parser->parser_); |
|
679 |
} else { |
||
680 |
9 |
llhttp_resume(&parser->parser_); |
|
681 |
} |
||
682 |
} |
||
683 |
|||
684 |
|||
685 |
3359 |
static void Consume(const FunctionCallbackInfo<Value>& args) { |
|
686 |
Parser* parser; |
||
687 |
✗✓ | 3359 |
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
688 |
✗✓ | 6718 |
CHECK(args[0]->IsObject()); |
689 |
6718 |
StreamBase* stream = StreamBase::FromObject(args[0].As<Object>()); |
|
690 |
✗✓ | 3359 |
CHECK_NOT_NULL(stream); |
691 |
✓✗ | 3359 |
stream->PushStreamListener(parser); |
692 |
} |
||
693 |
|||
694 |
|||
695 |
3347 |
static void Unconsume(const FunctionCallbackInfo<Value>& args) { |
|
696 |
Parser* parser; |
||
697 |
✗✓ | 3347 |
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
698 |
|||
699 |
// Already unconsumed |
||
700 |
✗✓ | 3347 |
if (parser->stream_ == nullptr) |
701 |
return; |
||
702 |
|||
703 |
✓✗ | 3347 |
parser->stream_->RemoveStreamListener(parser); |
704 |
} |
||
705 |
|||
706 |
|||
707 |
44 |
static void GetCurrentBuffer(const FunctionCallbackInfo<Value>& args) { |
|
708 |
Parser* parser; |
||
709 |
✗✓ | 44 |
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
710 |
|||
711 |
88 |
Local<Object> ret = Buffer::Copy( |
|
712 |
parser->env(), |
||
713 |
parser->current_buffer_data_, |
||
714 |
88 |
parser->current_buffer_len_).ToLocalChecked(); |
|
715 |
|||
716 |
88 |
args.GetReturnValue().Set(ret); |
|
717 |
} |
||
718 |
|||
719 |
static void Duration(const FunctionCallbackInfo<Value>& args) { |
||
720 |
Parser* parser; |
||
721 |
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
||
722 |
|||
723 |
if (parser->last_message_start_ == 0) { |
||
724 |
args.GetReturnValue().Set(0); |
||
725 |
return; |
||
726 |
} |
||
727 |
|||
728 |
double duration = (uv_hrtime() - parser->last_message_start_) / 1e6; |
||
729 |
args.GetReturnValue().Set(duration); |
||
730 |
} |
||
731 |
|||
732 |
static void HeadersCompleted(const FunctionCallbackInfo<Value>& args) { |
||
733 |
Parser* parser; |
||
734 |
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
||
735 |
|||
736 |
args.GetReturnValue().Set(parser->headers_completed_); |
||
737 |
} |
||
738 |
|||
739 |
protected: |
||
740 |
static const size_t kAllocBufferSize = 64 * 1024; |
||
741 |
|||
742 |
4885 |
uv_buf_t OnStreamAlloc(size_t suggested_size) override { |
|
743 |
// For most types of streams, OnStreamRead will be immediately after |
||
744 |
// OnStreamAlloc, and will consume all data, so using a static buffer for |
||
745 |
// reading is more efficient. For other streams, just use Malloc() directly. |
||
746 |
✗✓ | 4885 |
if (binding_data_->parser_buffer_in_use) |
747 |
return uv_buf_init(Malloc(suggested_size), suggested_size); |
||
748 |
4885 |
binding_data_->parser_buffer_in_use = true; |
|
749 |
|||
750 |
✓✓ | 4885 |
if (binding_data_->parser_buffer.empty()) |
751 |
371 |
binding_data_->parser_buffer.resize(kAllocBufferSize); |
|
752 |
|||
753 |
4885 |
return uv_buf_init(binding_data_->parser_buffer.data(), kAllocBufferSize); |
|
754 |
} |
||
755 |
|||
756 |
|||
757 |
4929 |
void OnStreamRead(ssize_t nread, const uv_buf_t& buf) override { |
|
758 |
8239 |
HandleScope scope(env()->isolate()); |
|
759 |
// Once we’re done here, either indicate that the HTTP parser buffer |
||
760 |
// is free for re-use, or free() the data if it didn’t come from there |
||
761 |
// in the first place. |
||
762 |
4924 |
auto on_scope_leave = OnScopeLeave([&]() { |
|
763 |
✓✓ | 4966 |
if (buf.base == binding_data_->parser_buffer.data()) |
764 |
4882 |
binding_data_->parser_buffer_in_use = false; |
|
765 |
else |
||
766 |
42 |
free(buf.base); |
|
767 |
✓✓ | 13163 |
}); |
768 |
|||
769 |
✓✓ | 4929 |
if (nread < 0) { |
770 |
606 |
PassReadErrorToPreviousListener(nread); |
|
771 |
606 |
return; |
|
772 |
} |
||
773 |
|||
774 |
// Ignore, empty reads have special meaning in http parser |
||
775 |
✗✓ | 4323 |
if (nread == 0) |
776 |
return; |
||
777 |
|||
778 |
4323 |
Local<Value> ret = Execute(buf.base, nread); |
|
779 |
|||
780 |
// Exception |
||
781 |
✓✓ | 4321 |
if (ret.IsEmpty()) |
782 |
5 |
return; |
|
783 |
|||
784 |
Local<Value> cb = |
||
785 |
12948 |
object()->Get(env()->context(), kOnExecute).ToLocalChecked(); |
|
786 |
|||
787 |
✓✓ | 4316 |
if (!cb->IsFunction()) |
788 |
1003 |
return; |
|
789 |
|||
790 |
// Hooks for GetCurrentBuffer |
||
791 |
3313 |
current_buffer_len_ = nread; |
|
792 |
3313 |
current_buffer_data_ = buf.base; |
|
793 |
|||
794 |
6626 |
MakeCallback(cb.As<Function>(), 1, &ret); |
|
795 |
|||
796 |
3310 |
current_buffer_len_ = 0; |
|
797 |
✓✓ | 3310 |
current_buffer_data_ = nullptr; |
798 |
} |
||
799 |
|||
800 |
|||
801 |
9110 |
Local<Value> Execute(const char* data, size_t len) { |
|
802 |
9110 |
EscapableHandleScope scope(env()->isolate()); |
|
803 |
|||
804 |
9110 |
current_buffer_len_ = len; |
|
805 |
9110 |
current_buffer_data_ = data; |
|
806 |
9110 |
got_exception_ = false; |
|
807 |
|||
808 |
llhttp_errno_t err; |
||
809 |
|||
810 |
✓✓ | 9110 |
if (data == nullptr) { |
811 |
1192 |
err = llhttp_finish(&parser_); |
|
812 |
} else { |
||
813 |
7918 |
err = llhttp_execute(&parser_, data, len); |
|
814 |
7915 |
Save(); |
|
815 |
} |
||
816 |
|||
817 |
// Calculate bytes read and resume after Upgrade/CONNECT pause |
||
818 |
9107 |
size_t nread = len; |
|
819 |
✓✓ | 9107 |
if (err != HPE_OK) { |
820 |
446 |
nread = llhttp_get_error_pos(&parser_) - data; |
|
821 |
|||
822 |
// This isn't a real pause, just a way to stop parsing early. |
||
823 |
✓✓ | 446 |
if (err == HPE_PAUSED_UPGRADE) { |
824 |
102 |
err = HPE_OK; |
|
825 |
102 |
llhttp_resume_after_upgrade(&parser_); |
|
826 |
} |
||
827 |
} |
||
828 |
|||
829 |
// Apply pending pause |
||
830 |
✗✓ | 9107 |
if (pending_pause_) { |
831 |
pending_pause_ = false; |
||
832 |
llhttp_pause(&parser_); |
||
833 |
} |
||
834 |
|||
835 |
9107 |
current_buffer_len_ = 0; |
|
836 |
9107 |
current_buffer_data_ = nullptr; |
|
837 |
|||
838 |
// If there was an exception in one of the callbacks |
||
839 |
✓✓ | 9107 |
if (got_exception_) |
840 |
8 |
return scope.Escape(Local<Value>()); |
|
841 |
|||
842 |
9099 |
Local<Integer> nread_obj = Integer::New(env()->isolate(), nread); |
|
843 |
|||
844 |
// If there was a parse error in one of the callbacks |
||
845 |
// TODO(bnoordhuis) What if there is an error on EOF? |
||
846 |
✓✓✓✓ |
9099 |
if (!parser_.upgrade && err != HPE_OK) { |
847 |
235 |
Local<Value> e = Exception::Error(env()->parse_error_string()); |
|
848 |
470 |
Local<Object> obj = e->ToObject(env()->isolate()->GetCurrentContext()) |
|
849 |
235 |
.ToLocalChecked(); |
|
850 |
470 |
obj->Set(env()->context(), |
|
851 |
env()->bytes_parsed_string(), |
||
852 |
940 |
nread_obj).Check(); |
|
853 |
235 |
const char* errno_reason = llhttp_get_error_reason(&parser_); |
|
854 |
|||
855 |
Local<String> code; |
||
856 |
Local<String> reason; |
||
857 |
✓✓ | 235 |
if (err == HPE_USER) { |
858 |
19 |
const char* colon = strchr(errno_reason, ':'); |
|
859 |
✗✓ | 19 |
CHECK_NOT_NULL(colon); |
860 |
19 |
code = OneByteString(env()->isolate(), |
|
861 |
errno_reason, |
||
862 |
19 |
static_cast<int>(colon - errno_reason)); |
|
863 |
19 |
reason = OneByteString(env()->isolate(), colon + 1); |
|
864 |
} else { |
||
865 |
216 |
code = OneByteString(env()->isolate(), llhttp_errno_name(err)); |
|
866 |
216 |
reason = OneByteString(env()->isolate(), errno_reason); |
|
867 |
} |
||
868 |
|||
869 |
940 |
obj->Set(env()->context(), env()->code_string(), code).Check(); |
|
870 |
940 |
obj->Set(env()->context(), env()->reason_string(), reason).Check(); |
|
871 |
return scope.Escape(e); |
||
872 |
} |
||
873 |
|||
874 |
// No return value is needed for `Finish()` |
||
875 |
✓✓ | 8864 |
if (data == nullptr) { |
876 |
1158 |
return scope.Escape(Local<Value>()); |
|
877 |
} |
||
878 |
7706 |
return scope.Escape(nread_obj); |
|
879 |
} |
||
880 |
|||
881 |
17754 |
Local<Array> CreateHeaders() { |
|
882 |
// There could be extra entries but the max size should be fixed |
||
883 |
✓✓ | 1154010 |
Local<Value> headers_v[kMaxHeaderFieldsCount * 2]; |
884 |
|||
885 |
✓✓ | 38345 |
for (size_t i = 0; i < num_values_; ++i) { |
886 |
41182 |
headers_v[i * 2] = fields_[i].ToString(env()); |
|
887 |
41182 |
headers_v[i * 2 + 1] = values_[i].ToTrimmedString(env()); |
|
888 |
} |
||
889 |
|||
890 |
17754 |
return Array::New(env()->isolate(), headers_v, num_values_ * 2); |
|
891 |
} |
||
892 |
|||
893 |
|||
894 |
// spill headers and request path to JS land |
||
895 |
69 |
void Flush() { |
|
896 |
138 |
HandleScope scope(env()->isolate()); |
|
897 |
|||
898 |
69 |
Local<Object> obj = object(); |
|
899 |
138 |
Local<Value> cb = obj->Get(env()->context(), kOnHeaders).ToLocalChecked(); |
|
900 |
|||
901 |
✗✓ | 69 |
if (!cb->IsFunction()) |
902 |
return; |
||
903 |
|||
904 |
Local<Value> argv[2] = { |
||
905 |
CreateHeaders(), |
||
906 |
url_.ToString(env()) |
||
907 |
207 |
}; |
|
908 |
|||
909 |
MaybeLocal<Value> r = MakeCallback(cb.As<Function>(), |
||
910 |
69 |
arraysize(argv), |
|
911 |
207 |
argv); |
|
912 |
|||
913 |
✗✓ | 69 |
if (r.IsEmpty()) |
914 |
got_exception_ = true; |
||
915 |
|||
916 |
69 |
url_.Reset(); |
|
917 |
✓✗ | 69 |
have_flushed_ = true; |
918 |
} |
||
919 |
|||
920 |
|||
921 |
6492 |
void Init(llhttp_type_t type, uint64_t max_http_header_size, |
|
922 |
uint32_t lenient_flags) { |
||
923 |
6492 |
llhttp_init(&parser_, type, &settings); |
|
924 |
|||
925 |
✓✓ | 6492 |
if (lenient_flags & kLenientHeaders) { |
926 |
5 |
llhttp_set_lenient_headers(&parser_, 1); |
|
927 |
} |
||
928 |
✓✓ | 6492 |
if (lenient_flags & kLenientChunkedLength) { |
929 |
5 |
llhttp_set_lenient_chunked_length(&parser_, 1); |
|
930 |
} |
||
931 |
✓✓ | 6492 |
if (lenient_flags & kLenientKeepAlive) { |
932 |
5 |
llhttp_set_lenient_keep_alive(&parser_, 1); |
|
933 |
} |
||
934 |
|||
935 |
6492 |
header_nread_ = 0; |
|
936 |
6492 |
url_.Reset(); |
|
937 |
6492 |
status_message_.Reset(); |
|
938 |
6492 |
num_fields_ = 0; |
|
939 |
6492 |
num_values_ = 0; |
|
940 |
6492 |
have_flushed_ = false; |
|
941 |
6492 |
got_exception_ = false; |
|
942 |
6492 |
headers_completed_ = false; |
|
943 |
6492 |
max_http_header_size_ = max_http_header_size; |
|
944 |
6492 |
} |
|
945 |
|||
946 |
|||
947 |
59137 |
int TrackHeader(size_t len) { |
|
948 |
59137 |
header_nread_ += len; |
|
949 |
✓✓ | 59137 |
if (header_nread_ >= max_http_header_size_) { |
950 |
17 |
llhttp_set_error_reason(&parser_, "HPE_HEADER_OVERFLOW:Header overflow"); |
|
951 |
17 |
return HPE_USER; |
|
952 |
} |
||
953 |
59120 |
return 0; |
|
954 |
} |
||
955 |
|||
956 |
|||
957 |
145632 |
int MaybePause() { |
|
958 |
✓✗ | 145632 |
if (!pending_pause_) { |
959 |
145632 |
return 0; |
|
960 |
} |
||
961 |
|||
962 |
pending_pause_ = false; |
||
963 |
llhttp_set_error_reason(&parser_, "Paused in callback"); |
||
964 |
return HPE_PAUSED; |
||
965 |
} |
||
966 |
|||
967 |
|||
968 |
bool IsNotIndicativeOfMemoryLeakAtExit() const override { |
||
969 |
// HTTP parsers are able to emit events without any GC root referring |
||
970 |
// to them, because they receive events directly from the underlying |
||
971 |
// libuv resource. |
||
972 |
return true; |
||
973 |
} |
||
974 |
|||
975 |
|||
976 |
llhttp_t parser_; |
||
977 |
StringPtr fields_[kMaxHeaderFieldsCount]; // header fields |
||
978 |
StringPtr values_[kMaxHeaderFieldsCount]; // header values |
||
979 |
StringPtr url_; |
||
980 |
StringPtr status_message_; |
||
981 |
size_t num_fields_; |
||
982 |
size_t num_values_; |
||
983 |
bool have_flushed_; |
||
984 |
bool got_exception_; |
||
985 |
size_t current_buffer_len_; |
||
986 |
const char* current_buffer_data_; |
||
987 |
bool headers_completed_ = false; |
||
988 |
bool pending_pause_ = false; |
||
989 |
uint64_t header_nread_ = 0; |
||
990 |
uint64_t max_http_header_size_; |
||
991 |
uint64_t last_message_start_; |
||
992 |
ConnectionsList* connectionsList_; |
||
993 |
|||
994 |
BaseObjectPtr<BindingData> binding_data_; |
||
995 |
|||
996 |
// These are helper functions for filling `http_parser_settings`, which turn |
||
997 |
// a member function of Parser into a C-style HTTP parser callback. |
||
998 |
template <typename Parser, Parser> struct Proxy; |
||
999 |
template <typename Parser, typename ...Args, int (Parser::*Member)(Args...)> |
||
1000 |
struct Proxy<int (Parser::*)(Args...), Member> { |
||
1001 |
145776 |
static int Raw(llhttp_t* p, Args ... args) { |
|
1002 |
145776 |
Parser* parser = ContainerOf(&Parser::parser_, p); |
|
1003 |
145776 |
int rv = (parser->*Member)(std::forward<Args>(args)...); |
|
1004 |
✓✗✓✗ ✓✗✓✗ ✓✓✓✓ ✓✗✓✗ ✓✗✓✗ |
145773 |
if (rv == 0) { |
1005 |
145632 |
rv = parser->MaybePause(); |
|
1006 |
} |
||
1007 |
145773 |
return rv; |
|
1008 |
} |
||
1009 |
}; |
||
1010 |
|||
1011 |
typedef int (Parser::*Call)(); |
||
1012 |
typedef int (Parser::*DataCall)(const char* at, size_t length); |
||
1013 |
|||
1014 |
static const llhttp_settings_t settings; |
||
1015 |
}; |
||
1016 |
|||
1017 |
273578 |
bool ParserComparator::operator()(const Parser* lhs, const Parser* rhs) const { |
|
1018 |
✓✓✓✓ |
273578 |
if (lhs->last_message_start_ == 0 && rhs->last_message_start_ == 0) { |
1019 |
// When both parsers are idle, guarantee strict order by |
||
1020 |
// comparing pointers as ints. |
||
1021 |
60375 |
return lhs < rhs; |
|
1022 |
✓✓ | 213203 |
} else if (lhs->last_message_start_ == 0) { |
1023 |
21772 |
return true; |
|
1024 |
✓✓ | 191431 |
} else if (rhs->last_message_start_ == 0) { |
1025 |
7714 |
return false; |
|
1026 |
} |
||
1027 |
|||
1028 |
183717 |
return lhs->last_message_start_ < rhs->last_message_start_; |
|
1029 |
} |
||
1030 |
|||
1031 |
588 |
void ConnectionsList::New(const FunctionCallbackInfo<Value>& args) { |
|
1032 |
588 |
Local<Context> context = args.GetIsolate()->GetCurrentContext(); |
|
1033 |
588 |
Environment* env = Environment::GetCurrent(context); |
|
1034 |
|||
1035 |
588 |
new ConnectionsList(env, args.This()); |
|
1036 |
588 |
} |
|
1037 |
|||
1038 |
4 |
void ConnectionsList::All(const FunctionCallbackInfo<Value>& args) { |
|
1039 |
4 |
Isolate* isolate = args.GetIsolate(); |
|
1040 |
4 |
Local<Context> context = isolate->GetCurrentContext(); |
|
1041 |
|||
1042 |
4 |
Local<Array> all = Array::New(isolate); |
|
1043 |
ConnectionsList* list; |
||
1044 |
|||
1045 |
✗✓ | 4 |
ASSIGN_OR_RETURN_UNWRAP(&list, args.Holder()); |
1046 |
|||
1047 |
4 |
uint32_t i = 0; |
|
1048 |
✓✓ | 10 |
for (auto parser : list->all_connections_) { |
1049 |
✗✓ | 18 |
if (all->Set(context, i++, parser->object()).IsNothing()) { |
1050 |
return; |
||
1051 |
} |
||
1052 |
} |
||
1053 |
|||
1054 |
8 |
return args.GetReturnValue().Set(all); |
|
1055 |
} |
||
1056 |
|||
1057 |
438 |
void ConnectionsList::Idle(const FunctionCallbackInfo<Value>& args) { |
|
1058 |
438 |
Isolate* isolate = args.GetIsolate(); |
|
1059 |
438 |
Local<Context> context = isolate->GetCurrentContext(); |
|
1060 |
|||
1061 |
438 |
Local<Array> idle = Array::New(isolate); |
|
1062 |
ConnectionsList* list; |
||
1063 |
|||
1064 |
✗✓ | 438 |
ASSIGN_OR_RETURN_UNWRAP(&list, args.Holder()); |
1065 |
|||
1066 |
438 |
uint32_t i = 0; |
|
1067 |
✓✓ | 1783 |
for (auto parser : list->all_connections_) { |
1068 |
✓✓ | 1345 |
if (parser->last_message_start_ == 0) { |
1069 |
✗✓ | 3729 |
if (idle->Set(context, i++, parser->object()).IsNothing()) { |
1070 |
return; |
||
1071 |
} |
||
1072 |
} |
||
1073 |
} |
||
1074 |
|||
1075 |
876 |
return args.GetReturnValue().Set(idle); |
|
1076 |
} |
||
1077 |
|||
1078 |
void ConnectionsList::Active(const FunctionCallbackInfo<Value>& args) { |
||
1079 |
Isolate* isolate = args.GetIsolate(); |
||
1080 |
Local<Context> context = isolate->GetCurrentContext(); |
||
1081 |
|||
1082 |
Local<Array> active = Array::New(isolate); |
||
1083 |
ConnectionsList* list; |
||
1084 |
|||
1085 |
ASSIGN_OR_RETURN_UNWRAP(&list, args.Holder()); |
||
1086 |
|||
1087 |
uint32_t i = 0; |
||
1088 |
for (auto parser : list->active_connections_) { |
||
1089 |
if (active->Set(context, i++, parser->object()).IsNothing()) { |
||
1090 |
return; |
||
1091 |
} |
||
1092 |
} |
||
1093 |
|||
1094 |
return args.GetReturnValue().Set(active); |
||
1095 |
} |
||
1096 |
|||
1097 |
97 |
void ConnectionsList::Expired(const FunctionCallbackInfo<Value>& args) { |
|
1098 |
97 |
Isolate* isolate = args.GetIsolate(); |
|
1099 |
97 |
Local<Context> context = isolate->GetCurrentContext(); |
|
1100 |
|||
1101 |
97 |
Local<Array> expired = Array::New(isolate); |
|
1102 |
ConnectionsList* list; |
||
1103 |
|||
1104 |
✗✓ | 97 |
ASSIGN_OR_RETURN_UNWRAP(&list, args.Holder()); |
1105 |
✗✓ | 194 |
CHECK(args[0]->IsNumber()); |
1106 |
✗✓ | 194 |
CHECK(args[1]->IsNumber()); |
1107 |
uint64_t headers_timeout = |
||
1108 |
291 |
static_cast<uint64_t>(args[0].As<Uint32>()->Value()) * 1000000; |
|
1109 |
uint64_t request_timeout = |
||
1110 |
291 |
static_cast<uint64_t>(args[1].As<Uint32>()->Value()) * 1000000; |
|
1111 |
|||
1112 |
✓✓✗✓ |
97 |
if (headers_timeout == 0 && request_timeout == 0) { |
1113 |
return args.GetReturnValue().Set(expired); |
||
1114 |
✓✓✗✓ |
97 |
} else if (request_timeout > 0 && headers_timeout > request_timeout) { |
1115 |
std::swap(headers_timeout, request_timeout); |
||
1116 |
} |
||
1117 |
|||
1118 |
97 |
const uint64_t now = uv_hrtime(); |
|
1119 |
97 |
const uint64_t headers_deadline = |
|
1120 |
✓✓ | 97 |
headers_timeout > 0 ? now - headers_timeout : 0; |
1121 |
97 |
const uint64_t request_deadline = |
|
1122 |
✓✓ | 97 |
request_timeout > 0 ? now - request_timeout : 0; |
1123 |
|||
1124 |
97 |
uint32_t i = 0; |
|
1125 |
97 |
auto iter = list->active_connections_.begin(); |
|
1126 |
97 |
auto end = list->active_connections_.end(); |
|
1127 |
✓✓ | 315 |
while (iter != end) { |
1128 |
109 |
Parser* parser = *iter; |
|
1129 |
109 |
iter++; |
|
1130 |
|||
1131 |
// Check for expiration. |
||
1132 |
✓✓ | 109 |
if ( |
1133 |
✓✓✓✓ |
220 |
(!parser->headers_completed_ && headers_deadline > 0 && |
1134 |
✓✓ | 149 |
parser->last_message_start_ < headers_deadline) || |
1135 |
( |
||
1136 |
✓✓ | 87 |
request_deadline > 0 && |
1137 |
87 |
parser->last_message_start_ < request_deadline) |
|
1138 |
) { |
||
1139 |
✗✓ | 39 |
if (expired->Set(context, i++, parser->object()).IsNothing()) { |
1140 |
return; |
||
1141 |
} |
||
1142 |
|||
1143 |
13 |
list->active_connections_.erase(parser); |
|
1144 |
} |
||
1145 |
} |
||
1146 |
|||
1147 |
194 |
return args.GetReturnValue().Set(expired); |
|
1148 |
} |
||
1149 |
|||
1150 |
const llhttp_settings_t Parser::settings = { |
||
1151 |
Proxy<Call, &Parser::on_message_begin>::Raw, |
||
1152 |
Proxy<DataCall, &Parser::on_url>::Raw, |
||
1153 |
Proxy<DataCall, &Parser::on_status>::Raw, |
||
1154 |
|||
1155 |
// on_method |
||
1156 |
nullptr, |
||
1157 |
// on_version |
||
1158 |
nullptr, |
||
1159 |
|||
1160 |
Proxy<DataCall, &Parser::on_header_field>::Raw, |
||
1161 |
Proxy<DataCall, &Parser::on_header_value>::Raw, |
||
1162 |
|||
1163 |
// on_chunk_extension_name |
||
1164 |
nullptr, |
||
1165 |
// on_chunk_extension_value |
||
1166 |
nullptr, |
||
1167 |
|||
1168 |
Proxy<Call, &Parser::on_headers_complete>::Raw, |
||
1169 |
Proxy<DataCall, &Parser::on_body>::Raw, |
||
1170 |
Proxy<Call, &Parser::on_message_complete>::Raw, |
||
1171 |
|||
1172 |
// on_url_complete |
||
1173 |
nullptr, |
||
1174 |
// on_status_complete |
||
1175 |
nullptr, |
||
1176 |
// on_method_complete |
||
1177 |
nullptr, |
||
1178 |
// on_version_complete |
||
1179 |
nullptr, |
||
1180 |
// on_header_field_complete |
||
1181 |
nullptr, |
||
1182 |
// on_header_value_complete |
||
1183 |
nullptr, |
||
1184 |
// on_chunk_extension_name_complete |
||
1185 |
nullptr, |
||
1186 |
// on_chunk_extension_value_complete |
||
1187 |
nullptr, |
||
1188 |
|||
1189 |
Proxy<Call, &Parser::on_chunk_header>::Raw, |
||
1190 |
Proxy<Call, &Parser::on_chunk_complete>::Raw, |
||
1191 |
|||
1192 |
// on_reset, |
||
1193 |
nullptr, |
||
1194 |
}; |
||
1195 |
|||
1196 |
895 |
void InitializeHttpParser(Local<Object> target, |
|
1197 |
Local<Value> unused, |
||
1198 |
Local<Context> context, |
||
1199 |
void* priv) { |
||
1200 |
895 |
Environment* env = Environment::GetCurrent(context); |
|
1201 |
895 |
Isolate* isolate = env->isolate(); |
|
1202 |
BindingData* const binding_data = |
||
1203 |
895 |
env->AddBindingData<BindingData>(context, target); |
|
1204 |
✗✓ | 895 |
if (binding_data == nullptr) return; |
1205 |
|||
1206 |
895 |
Local<FunctionTemplate> t = NewFunctionTemplate(isolate, Parser::New); |
|
1207 |
1790 |
t->InstanceTemplate()->SetInternalFieldCount(Parser::kInternalFieldCount); |
|
1208 |
|||
1209 |
2685 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "REQUEST"), |
|
1210 |
Integer::New(env->isolate(), HTTP_REQUEST)); |
||
1211 |
2685 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "RESPONSE"), |
|
1212 |
Integer::New(env->isolate(), HTTP_RESPONSE)); |
||
1213 |
2685 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kOnMessageBegin"), |
|
1214 |
Integer::NewFromUnsigned(env->isolate(), kOnMessageBegin)); |
||
1215 |
2685 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kOnHeaders"), |
|
1216 |
Integer::NewFromUnsigned(env->isolate(), kOnHeaders)); |
||
1217 |
2685 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kOnHeadersComplete"), |
|
1218 |
Integer::NewFromUnsigned(env->isolate(), kOnHeadersComplete)); |
||
1219 |
2685 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kOnBody"), |
|
1220 |
Integer::NewFromUnsigned(env->isolate(), kOnBody)); |
||
1221 |
2685 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kOnMessageComplete"), |
|
1222 |
Integer::NewFromUnsigned(env->isolate(), kOnMessageComplete)); |
||
1223 |
2685 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kOnExecute"), |
|
1224 |
Integer::NewFromUnsigned(env->isolate(), kOnExecute)); |
||
1225 |
2685 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kOnTimeout"), |
|
1226 |
Integer::NewFromUnsigned(env->isolate(), kOnTimeout)); |
||
1227 |
|||
1228 |
2685 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kLenientNone"), |
|
1229 |
Integer::NewFromUnsigned(env->isolate(), kLenientNone)); |
||
1230 |
2685 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kLenientHeaders"), |
|
1231 |
Integer::NewFromUnsigned(env->isolate(), kLenientHeaders)); |
||
1232 |
2685 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kLenientChunkedLength"), |
|
1233 |
Integer::NewFromUnsigned(env->isolate(), kLenientChunkedLength)); |
||
1234 |
2685 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kLenientKeepAlive"), |
|
1235 |
Integer::NewFromUnsigned(env->isolate(), kLenientKeepAlive)); |
||
1236 |
2685 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kLenientAll"), |
|
1237 |
Integer::NewFromUnsigned(env->isolate(), kLenientAll)); |
||
1238 |
|||
1239 |
895 |
Local<Array> methods = Array::New(env->isolate()); |
|
1240 |
#define V(num, name, string) \ |
||
1241 |
methods->Set(env->context(), \ |
||
1242 |
num, FIXED_ONE_BYTE_STRING(env->isolate(), #string)).Check(); |
||
1243 |
91290 |
HTTP_METHOD_MAP(V) |
|
1244 |
#undef V |
||
1245 |
1790 |
target->Set(env->context(), |
|
1246 |
FIXED_ONE_BYTE_STRING(env->isolate(), "methods"), |
||
1247 |
3580 |
methods).Check(); |
|
1248 |
|||
1249 |
895 |
t->Inherit(AsyncWrap::GetConstructorTemplate(env)); |
|
1250 |
895 |
SetProtoMethod(isolate, t, "close", Parser::Close); |
|
1251 |
895 |
SetProtoMethod(isolate, t, "free", Parser::Free); |
|
1252 |
895 |
SetProtoMethod(isolate, t, "remove", Parser::Remove); |
|
1253 |
895 |
SetProtoMethod(isolate, t, "execute", Parser::Execute); |
|
1254 |
895 |
SetProtoMethod(isolate, t, "finish", Parser::Finish); |
|
1255 |
895 |
SetProtoMethod(isolate, t, "initialize", Parser::Initialize); |
|
1256 |
895 |
SetProtoMethod(isolate, t, "pause", Parser::Pause<true>); |
|
1257 |
895 |
SetProtoMethod(isolate, t, "resume", Parser::Pause<false>); |
|
1258 |
895 |
SetProtoMethod(isolate, t, "consume", Parser::Consume); |
|
1259 |
895 |
SetProtoMethod(isolate, t, "unconsume", Parser::Unconsume); |
|
1260 |
895 |
SetProtoMethod(isolate, t, "getCurrentBuffer", Parser::GetCurrentBuffer); |
|
1261 |
895 |
SetProtoMethod(isolate, t, "duration", Parser::Duration); |
|
1262 |
895 |
SetProtoMethod(isolate, t, "headersCompleted", Parser::HeadersCompleted); |
|
1263 |
|||
1264 |
895 |
SetConstructorFunction(context, target, "HTTPParser", t); |
|
1265 |
|||
1266 |
Local<FunctionTemplate> c = |
||
1267 |
895 |
NewFunctionTemplate(isolate, ConnectionsList::New); |
|
1268 |
1790 |
c->InstanceTemplate() |
|
1269 |
895 |
->SetInternalFieldCount(ConnectionsList::kInternalFieldCount); |
|
1270 |
895 |
SetProtoMethod(isolate, c, "all", ConnectionsList::All); |
|
1271 |
895 |
SetProtoMethod(isolate, c, "idle", ConnectionsList::Idle); |
|
1272 |
895 |
SetProtoMethod(isolate, c, "active", ConnectionsList::Active); |
|
1273 |
895 |
SetProtoMethod(isolate, c, "expired", ConnectionsList::Expired); |
|
1274 |
895 |
SetConstructorFunction(context, target, "ConnectionsList", c); |
|
1275 |
} |
||
1276 |
|||
1277 |
} // anonymous namespace |
||
1278 |
} // namespace node |
||
1279 |
|||
1280 |
✓✗✓✗ |
22488 |
NODE_MODULE_CONTEXT_AWARE_INTERNAL(http_parser, node::InitializeHttpParser) |
Generated by: GCOVR (Version 4.2) |