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 |
|
20561 |
inline bool IsOWS(char c) { |
91 |
✓✓✓✓
|
20561 |
return c == ' ' || c == '\t'; |
92 |
|
|
} |
93 |
|
|
|
94 |
|
|
class BindingData : public BaseObject { |
95 |
|
|
public: |
96 |
|
897 |
BindingData(Environment* env, Local<Object> obj) |
97 |
|
897 |
: 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 |
|
344982 |
StringPtr() { |
114 |
|
344982 |
on_heap_ = false; |
115 |
|
344982 |
Reset(); |
116 |
|
344982 |
} |
117 |
|
|
|
118 |
|
|
|
119 |
|
682308 |
~StringPtr() { |
120 |
|
341154 |
Reset(); |
121 |
|
341154 |
} |
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 |
|
16006 |
void Save() { |
128 |
✓✓✓✓
|
16006 |
if (!on_heap_ && size_ > 0) { |
129 |
|
6738 |
char* s = new char[size_]; |
130 |
|
6738 |
memcpy(s, str_, size_); |
131 |
|
6738 |
str_ = s; |
132 |
|
6738 |
on_heap_ = true; |
133 |
|
|
} |
134 |
|
16006 |
} |
135 |
|
|
|
136 |
|
|
|
137 |
|
775888 |
void Reset() { |
138 |
✓✓ |
775888 |
if (on_heap_) { |
139 |
✓✗ |
6684 |
delete[] str_; |
140 |
|
6684 |
on_heap_ = false; |
141 |
|
|
} |
142 |
|
|
|
143 |
|
775888 |
str_ = nullptr; |
144 |
|
775888 |
size_ = 0; |
145 |
|
775888 |
} |
146 |
|
|
|
147 |
|
|
|
148 |
|
59017 |
void Update(const char* str, size_t size) { |
149 |
✓✓ |
59017 |
if (str_ == nullptr) { |
150 |
|
58957 |
str_ = str; |
151 |
✓✓✓✗
|
60 |
} 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 |
|
60 |
char* s = new char[size_ + size]; |
155 |
|
60 |
memcpy(s, str_, size_); |
156 |
|
60 |
memcpy(s + size_, str, size); |
157 |
|
|
|
158 |
✓✓ |
60 |
if (on_heap_) |
159 |
✓✗ |
59 |
delete[] str_; |
160 |
|
|
else |
161 |
|
1 |
on_heap_ = true; |
162 |
|
|
|
163 |
|
60 |
str_ = s; |
164 |
|
|
} |
165 |
|
59017 |
size_ += size; |
166 |
|
59017 |
} |
167 |
|
|
|
168 |
|
|
|
169 |
|
58853 |
Local<String> ToString(Environment* env) const { |
170 |
✓✓ |
58853 |
if (size_ != 0) |
171 |
|
58788 |
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 |
|
20563 |
Local<String> ToTrimmedString(Environment* env) { |
179 |
✓✓✓✓ ✓✓ |
20563 |
while (size_ > 0 && IsOWS(str_[size_ - 1])) { |
180 |
|
11 |
size_--; |
181 |
|
|
} |
182 |
|
20552 |
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 |
|
|
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 |
|
30423 |
void Push(Parser* parser) { |
210 |
|
30423 |
all_connections_.insert(parser); |
211 |
|
30423 |
} |
212 |
|
|
|
213 |
|
30405 |
void Pop(Parser* parser) { |
214 |
|
30405 |
all_connections_.erase(parser); |
215 |
|
30405 |
} |
216 |
|
|
|
217 |
|
16423 |
void PushActive(Parser* parser) { |
218 |
|
16423 |
active_connections_.insert(parser); |
219 |
|
16423 |
} |
220 |
|
|
|
221 |
|
30405 |
void PopActive(Parser* parser) { |
222 |
|
30405 |
active_connections_.erase(parser); |
223 |
|
30405 |
} |
224 |
|
|
|
225 |
|
|
SET_NO_MEMORY_INFO() |
226 |
|
|
SET_MEMORY_INFO_NAME(ConnectionsList) |
227 |
|
|
SET_SELF_SIZE(ConnectionsList) |
228 |
|
|
|
229 |
|
|
private: |
230 |
|
581 |
ConnectionsList(Environment* env, Local<Object> object) |
231 |
|
581 |
: BaseObject(env, object) { |
232 |
|
581 |
MakeWeak(); |
233 |
|
581 |
} |
234 |
|
|
|
235 |
|
|
std::set<Parser*, ParserComparator> all_connections_; |
236 |
|
|
std::set<Parser*, ParserComparator> active_connections_; |
237 |
|
|
}; |
238 |
|
|
|
239 |
|
|
class Parser : public AsyncWrap, public StreamListener { |
240 |
|
|
friend class ConnectionsList; |
241 |
|
|
friend struct ParserComparator; |
242 |
|
|
|
243 |
|
|
public: |
244 |
|
5227 |
Parser(BindingData* binding_data, Local<Object> wrap) |
245 |
|
5227 |
: AsyncWrap(binding_data->env(), wrap), |
246 |
|
|
current_buffer_len_(0), |
247 |
|
|
current_buffer_data_(nullptr), |
248 |
✓✓✓✓
|
339755 |
binding_data_(binding_data) { |
249 |
|
5227 |
} |
250 |
|
|
|
251 |
|
|
SET_NO_MEMORY_INFO() |
252 |
|
|
SET_MEMORY_INFO_NAME(Parser) |
253 |
|
|
SET_SELF_SIZE(Parser) |
254 |
|
|
|
255 |
|
17748 |
int on_message_begin() { |
256 |
|
|
// Important: Pop from the lists BEFORE resetting the last_message_start_ |
257 |
|
|
// otherwise std::set.erase will fail. |
258 |
✓✓ |
17748 |
if (connectionsList_ != nullptr) { |
259 |
|
14064 |
connectionsList_->Pop(this); |
260 |
|
14064 |
connectionsList_->PopActive(this); |
261 |
|
|
} |
262 |
|
|
|
263 |
|
17748 |
num_fields_ = num_values_ = 0; |
264 |
|
17748 |
headers_completed_ = false; |
265 |
|
17748 |
last_message_start_ = uv_hrtime(); |
266 |
|
17748 |
url_.Reset(); |
267 |
|
17748 |
status_message_.Reset(); |
268 |
|
|
|
269 |
✓✓ |
17748 |
if (connectionsList_ != nullptr) { |
270 |
|
14064 |
connectionsList_->Push(this); |
271 |
|
14064 |
connectionsList_->PushActive(this); |
272 |
|
|
} |
273 |
|
|
|
274 |
|
35496 |
Local<Value> cb = object()->Get(env()->context(), kOnMessageBegin) |
275 |
|
17748 |
.ToLocalChecked(); |
276 |
✗✓ |
17748 |
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 |
|
17748 |
return 0; |
287 |
|
|
} |
288 |
|
|
|
289 |
|
|
|
290 |
|
14228 |
int on_url(const char* at, size_t length) { |
291 |
|
14228 |
int rv = TrackHeader(length); |
292 |
✗✓ |
14228 |
if (rv != 0) { |
293 |
|
|
return rv; |
294 |
|
|
} |
295 |
|
|
|
296 |
|
14228 |
url_.Update(at, length); |
297 |
|
14228 |
return 0; |
298 |
|
|
} |
299 |
|
|
|
300 |
|
|
|
301 |
|
3510 |
int on_status(const char* at, size_t length) { |
302 |
|
3510 |
int rv = TrackHeader(length); |
303 |
✗✓ |
3510 |
if (rv != 0) { |
304 |
|
|
return rv; |
305 |
|
|
} |
306 |
|
|
|
307 |
|
3510 |
status_message_.Update(at, length); |
308 |
|
3510 |
return 0; |
309 |
|
|
} |
310 |
|
|
|
311 |
|
|
|
312 |
|
20658 |
int on_header_field(const char* at, size_t length) { |
313 |
|
20658 |
int rv = TrackHeader(length); |
314 |
✗✓ |
20658 |
if (rv != 0) { |
315 |
|
|
return rv; |
316 |
|
|
} |
317 |
|
|
|
318 |
✓✓ |
20658 |
if (num_fields_ == num_values_) { |
319 |
|
|
// start of new field name |
320 |
|
20629 |
num_fields_++; |
321 |
✓✓ |
20629 |
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 |
|
20629 |
fields_[num_fields_ - 1].Reset(); |
328 |
|
|
} |
329 |
|
|
|
330 |
✗✓ |
20658 |
CHECK_LT(num_fields_, kMaxHeaderFieldsCount); |
331 |
✗✓ |
20658 |
CHECK_EQ(num_fields_, num_values_ + 1); |
332 |
|
|
|
333 |
|
20658 |
fields_[num_fields_ - 1].Update(at, length); |
334 |
|
|
|
335 |
|
20658 |
return 0; |
336 |
|
|
} |
337 |
|
|
|
338 |
|
|
|
339 |
|
20638 |
int on_header_value(const char* at, size_t length) { |
340 |
|
20638 |
int rv = TrackHeader(length); |
341 |
✓✓ |
20638 |
if (rv != 0) { |
342 |
|
17 |
return rv; |
343 |
|
|
} |
344 |
|
|
|
345 |
✓✓ |
20621 |
if (num_values_ != num_fields_) { |
346 |
|
|
// start of new header value |
347 |
|
20598 |
num_values_++; |
348 |
|
20598 |
values_[num_values_ - 1].Reset(); |
349 |
|
|
} |
350 |
|
|
|
351 |
✗✓ |
20621 |
CHECK_LT(num_values_, arraysize(values_)); |
352 |
✗✓ |
20621 |
CHECK_EQ(num_values_, num_fields_); |
353 |
|
|
|
354 |
|
20621 |
values_[num_values_ - 1].Update(at, length); |
355 |
|
|
|
356 |
|
20621 |
return 0; |
357 |
|
|
} |
358 |
|
|
|
359 |
|
|
|
360 |
|
17688 |
int on_headers_complete() { |
361 |
|
17688 |
headers_completed_ = true; |
362 |
|
17688 |
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 |
✓✓ |
176880 |
Local<Value> argv[A_MAX]; |
381 |
|
17688 |
Local<Object> obj = object(); |
382 |
|
17688 |
Local<Value> cb = obj->Get(env()->context(), |
383 |
|
35376 |
kOnHeadersComplete).ToLocalChecked(); |
384 |
|
|
|
385 |
✗✓ |
17688 |
if (!cb->IsFunction()) |
386 |
|
|
return 0; |
387 |
|
|
|
388 |
|
17688 |
Local<Value> undefined = Undefined(env()->isolate()); |
389 |
✓✓ |
176880 |
for (size_t i = 0; i < arraysize(argv); i++) |
390 |
|
159192 |
argv[i] = undefined; |
391 |
|
|
|
392 |
✓✓ |
17688 |
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 |
|
17672 |
argv[A_HEADERS] = CreateHeaders(); |
398 |
✓✓ |
17672 |
if (parser_.type == HTTP_REQUEST) |
399 |
|
28358 |
argv[A_URL] = url_.ToString(env()); |
400 |
|
|
} |
401 |
|
|
|
402 |
|
17688 |
num_fields_ = 0; |
403 |
|
17688 |
num_values_ = 0; |
404 |
|
|
|
405 |
|
|
// METHOD |
406 |
✓✓ |
17688 |
if (parser_.type == HTTP_REQUEST) { |
407 |
|
28374 |
argv[A_METHOD] = |
408 |
|
14187 |
Uint32::NewFromUnsigned(env()->isolate(), parser_.method); |
409 |
|
|
} |
410 |
|
|
|
411 |
|
|
// STATUS |
412 |
✓✓ |
17688 |
if (parser_.type == HTTP_RESPONSE) { |
413 |
|
3501 |
argv[A_STATUS_CODE] = |
414 |
|
3501 |
Integer::New(env()->isolate(), parser_.status_code); |
415 |
|
7002 |
argv[A_STATUS_MESSAGE] = status_message_.ToString(env()); |
416 |
|
|
} |
417 |
|
|
|
418 |
|
|
// VERSION |
419 |
|
17688 |
argv[A_VERSION_MAJOR] = Integer::New(env()->isolate(), parser_.http_major); |
420 |
|
17688 |
argv[A_VERSION_MINOR] = Integer::New(env()->isolate(), parser_.http_minor); |
421 |
|
|
|
422 |
|
|
bool should_keep_alive; |
423 |
|
17688 |
should_keep_alive = llhttp_should_keep_alive(&parser_); |
424 |
|
|
|
425 |
|
17688 |
argv[A_SHOULD_KEEP_ALIVE] = |
426 |
|
|
Boolean::New(env()->isolate(), should_keep_alive); |
427 |
|
|
|
428 |
|
35376 |
argv[A_UPGRADE] = Boolean::New(env()->isolate(), parser_.upgrade); |
429 |
|
|
|
430 |
|
|
MaybeLocal<Value> head_response; |
431 |
|
|
{ |
432 |
|
|
InternalCallbackScope callback_scope( |
433 |
|
35372 |
this, InternalCallbackScope::kSkipTaskQueues); |
434 |
|
17688 |
head_response = cb.As<Function>()->Call( |
435 |
|
35376 |
env()->context(), object(), arraysize(argv), argv); |
436 |
✓✓ |
17684 |
if (head_response.IsEmpty()) callback_scope.MarkAsFailed(); |
437 |
|
|
} |
438 |
|
|
|
439 |
|
|
int64_t val; |
440 |
|
|
|
441 |
✓✓ |
53036 |
if (head_response.IsEmpty() || !head_response.ToLocalChecked() |
442 |
✓✓ |
35360 |
->IntegerValue(env()->context()) |
443 |
✗✓ |
17676 |
.To(&val)) { |
444 |
|
8 |
got_exception_ = true; |
445 |
|
8 |
return -1; |
446 |
|
|
} |
447 |
|
|
|
448 |
|
17676 |
return static_cast<int>(val); |
449 |
|
|
} |
450 |
|
|
|
451 |
|
|
|
452 |
|
11482 |
int on_body(const char* at, size_t length) { |
453 |
✗✓ |
11482 |
if (length == 0) |
454 |
|
|
return 0; |
455 |
|
|
|
456 |
|
11482 |
Environment* env = this->env(); |
457 |
|
22964 |
HandleScope handle_scope(env->isolate()); |
458 |
|
|
|
459 |
|
34446 |
Local<Value> cb = object()->Get(env->context(), kOnBody).ToLocalChecked(); |
460 |
|
|
|
461 |
✗✓ |
11482 |
if (!cb->IsFunction()) |
462 |
|
|
return 0; |
463 |
|
|
|
464 |
|
11482 |
Local<Value> buffer = Buffer::Copy(env, at, length).ToLocalChecked(); |
465 |
|
|
|
466 |
|
22964 |
MaybeLocal<Value> r = MakeCallback(cb.As<Function>(), 1, &buffer); |
467 |
|
|
|
468 |
✗✓ |
11482 |
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 |
|
11482 |
return 0; |
475 |
|
|
} |
476 |
|
|
|
477 |
|
|
|
478 |
|
16476 |
int on_message_complete() { |
479 |
|
32952 |
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 |
✓✓ |
16476 |
if (connectionsList_ != nullptr) { |
484 |
|
14000 |
connectionsList_->Pop(this); |
485 |
|
14000 |
connectionsList_->PopActive(this); |
486 |
|
|
} |
487 |
|
|
|
488 |
|
16476 |
last_message_start_ = 0; |
489 |
|
|
|
490 |
✓✓ |
16476 |
if (connectionsList_ != nullptr) { |
491 |
|
14000 |
connectionsList_->Push(this); |
492 |
|
|
} |
493 |
|
|
|
494 |
✓✓ |
16476 |
if (num_fields_) |
495 |
|
6 |
Flush(); // Flush trailing HTTP headers. |
496 |
|
|
|
497 |
|
16476 |
Local<Object> obj = object(); |
498 |
|
16476 |
Local<Value> cb = obj->Get(env()->context(), |
499 |
|
32952 |
kOnMessageComplete).ToLocalChecked(); |
500 |
|
|
|
501 |
✓✓ |
16476 |
if (!cb->IsFunction()) |
502 |
|
2 |
return 0; |
503 |
|
|
|
504 |
|
|
MaybeLocal<Value> r; |
505 |
|
|
{ |
506 |
|
|
InternalCallbackScope callback_scope( |
507 |
|
32948 |
this, InternalCallbackScope::kSkipTaskQueues); |
508 |
|
32948 |
r = cb.As<Function>()->Call(env()->context(), object(), 0, nullptr); |
509 |
✗✓ |
16474 |
if (r.IsEmpty()) callback_scope.MarkAsFailed(); |
510 |
|
|
} |
511 |
|
|
|
512 |
✗✓ |
16474 |
if (r.IsEmpty()) { |
513 |
|
|
got_exception_ = true; |
514 |
|
|
return -1; |
515 |
|
|
} |
516 |
|
|
|
517 |
|
16474 |
return 0; |
518 |
|
|
} |
519 |
|
|
|
520 |
|
|
// Reset nread for the next chunk |
521 |
|
11674 |
int on_chunk_header() { |
522 |
|
11674 |
header_nread_ = 0; |
523 |
|
11674 |
return 0; |
524 |
|
|
} |
525 |
|
|
|
526 |
|
|
|
527 |
|
|
// Reset nread for the next chunk |
528 |
|
11520 |
int on_chunk_complete() { |
529 |
|
11520 |
header_nread_ = 0; |
530 |
|
11520 |
return 0; |
531 |
|
|
} |
532 |
|
|
|
533 |
|
5227 |
static void New(const FunctionCallbackInfo<Value>& args) { |
534 |
|
5227 |
BindingData* binding_data = Environment::GetBindingData<BindingData>(args); |
535 |
|
5227 |
new Parser(binding_data, args.This()); |
536 |
|
5227 |
} |
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 |
|
6275 |
static void Free(const FunctionCallbackInfo<Value>& args) { |
548 |
|
|
Parser* parser; |
549 |
✗✓ |
6275 |
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 |
|
6275 |
parser->EmitTraceEventDestroy(); |
554 |
|
6275 |
parser->EmitDestroy(); |
555 |
|
|
} |
556 |
|
|
|
557 |
|
6278 |
static void Remove(const FunctionCallbackInfo<Value>& args) { |
558 |
|
|
Parser* parser; |
559 |
✗✓ |
6278 |
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
560 |
|
|
|
561 |
✓✓ |
6278 |
if (parser->connectionsList_ != nullptr) { |
562 |
|
2341 |
parser->connectionsList_->Pop(parser); |
563 |
|
2341 |
parser->connectionsList_->PopActive(parser); |
564 |
|
|
} |
565 |
|
|
} |
566 |
|
|
|
567 |
|
7843 |
void Save() { |
568 |
|
7843 |
url_.Save(); |
569 |
|
7843 |
status_message_.Save(); |
570 |
|
|
|
571 |
✓✓ |
8036 |
for (size_t i = 0; i < num_fields_; i++) { |
572 |
|
193 |
fields_[i].Save(); |
573 |
|
|
} |
574 |
|
|
|
575 |
✓✓ |
7970 |
for (size_t i = 0; i < num_values_; i++) { |
576 |
|
127 |
values_[i].Save(); |
577 |
|
|
} |
578 |
|
7843 |
} |
579 |
|
|
|
580 |
|
|
// var bytesParsed = parser->execute(buffer); |
581 |
|
3573 |
static void Execute(const FunctionCallbackInfo<Value>& args) { |
582 |
|
|
Parser* parser; |
583 |
✗✓ |
3573 |
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
584 |
|
|
|
585 |
|
3573 |
ArrayBufferViewContents<char> buffer(args[0]); |
586 |
|
|
|
587 |
|
3573 |
Local<Value> ret = parser->Execute(buffer.data(), buffer.length()); |
588 |
|
|
|
589 |
✓✓ |
3572 |
if (!ret.IsEmpty()) |
590 |
|
7138 |
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 |
|
66 |
args.GetReturnValue().Set(ret); |
602 |
|
|
} |
603 |
|
|
|
604 |
|
|
|
605 |
|
6480 |
static void Initialize(const FunctionCallbackInfo<Value>& args) { |
606 |
|
6480 |
Environment* env = Environment::GetCurrent(args); |
607 |
|
|
|
608 |
|
6480 |
uint64_t max_http_header_size = 0; |
609 |
|
6480 |
uint32_t lenient_flags = kLenientNone; |
610 |
✓✗ |
6480 |
ConnectionsList* connectionsList = nullptr; |
611 |
|
|
|
612 |
✗✓ |
6480 |
CHECK(args[0]->IsInt32()); |
613 |
✗✓ |
6480 |
CHECK(args[1]->IsObject()); |
614 |
|
|
|
615 |
✓✓ |
6480 |
if (args.Length() > 2) { |
616 |
✗✓ |
5310 |
CHECK(args[2]->IsNumber()); |
617 |
|
5310 |
max_http_header_size = |
618 |
|
10620 |
static_cast<uint64_t>(args[2].As<Number>()->Value()); |
619 |
|
|
} |
620 |
✓✓ |
6480 |
if (max_http_header_size == 0) { |
621 |
|
6476 |
max_http_header_size = env->options()->max_http_header_size; |
622 |
|
|
} |
623 |
|
|
|
624 |
✓✓ |
6480 |
if (args.Length() > 3) { |
625 |
✗✓ |
5310 |
CHECK(args[3]->IsInt32()); |
626 |
|
10620 |
lenient_flags = args[3].As<Int32>()->Value(); |
627 |
|
|
} |
628 |
|
|
|
629 |
✓✓✓✓ ✓✓ |
11202 |
if (args.Length() > 4 && !args[4]->IsNullOrUndefined()) { |
630 |
✗✓ |
2359 |
CHECK(args[4]->IsObject()); |
631 |
✗✓ |
2359 |
ASSIGN_OR_RETURN_UNWRAP(&connectionsList, args[4]); |
632 |
|
|
} |
633 |
|
|
|
634 |
|
|
llhttp_type_t type = |
635 |
|
12960 |
static_cast<llhttp_type_t>(args[0].As<Int32>()->Value()); |
636 |
|
|
|
637 |
✓✓✗✓
|
6480 |
CHECK(type == HTTP_REQUEST || type == HTTP_RESPONSE); |
638 |
|
|
Parser* parser; |
639 |
✗✓ |
6480 |
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
640 |
|
|
// Should always be called from the same context. |
641 |
✗✓ |
6480 |
CHECK_EQ(env, parser->env()); |
642 |
|
|
|
643 |
|
6480 |
AsyncWrap::ProviderType provider = |
644 |
✓✓ |
6480 |
(type == HTTP_REQUEST ? |
645 |
|
|
AsyncWrap::PROVIDER_HTTPINCOMINGMESSAGE |
646 |
|
|
: AsyncWrap::PROVIDER_HTTPCLIENTREQUEST); |
647 |
|
|
|
648 |
|
6480 |
parser->set_provider_type(provider); |
649 |
✓✗ |
19440 |
parser->AsyncReset(args[1].As<Object>()); |
650 |
|
6480 |
parser->Init(type, max_http_header_size, lenient_flags); |
651 |
|
|
|
652 |
✓✓ |
6480 |
if (connectionsList != nullptr) { |
653 |
|
2359 |
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 |
|
2359 |
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 |
|
2359 |
parser->connectionsList_->Push(parser); |
663 |
|
2359 |
parser->connectionsList_->PushActive(parser); |
664 |
|
|
} else { |
665 |
|
4121 |
parser->connectionsList_ = nullptr; |
666 |
|
|
} |
667 |
|
|
} |
668 |
|
|
|
669 |
|
|
template <bool should_pause> |
670 |
|
22 |
static void Pause(const FunctionCallbackInfo<Value>& args) { |
671 |
|
22 |
Environment* env = Environment::GetCurrent(args); |
672 |
|
|
Parser* parser; |
673 |
✗✓ |
22 |
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
674 |
|
|
// Should always be called from the same context. |
675 |
✗✓ |
22 |
CHECK_EQ(env, parser->env()); |
676 |
|
|
|
677 |
|
|
if constexpr (should_pause) { |
678 |
|
8 |
llhttp_pause(&parser->parser_); |
679 |
|
|
} else { |
680 |
|
14 |
llhttp_resume(&parser->parser_); |
681 |
|
|
} |
682 |
|
|
} |
683 |
|
|
|
684 |
|
|
|
685 |
|
3352 |
static void Consume(const FunctionCallbackInfo<Value>& args) { |
686 |
|
|
Parser* parser; |
687 |
✗✓ |
3352 |
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
688 |
✗✓ |
3352 |
CHECK(args[0]->IsObject()); |
689 |
|
6704 |
StreamBase* stream = StreamBase::FromObject(args[0].As<Object>()); |
690 |
✗✓ |
3352 |
CHECK_NOT_NULL(stream); |
691 |
✓✗ |
3352 |
stream->PushStreamListener(parser); |
692 |
|
|
} |
693 |
|
|
|
694 |
|
|
|
695 |
|
3339 |
static void Unconsume(const FunctionCallbackInfo<Value>& args) { |
696 |
|
|
Parser* parser; |
697 |
✗✓ |
3339 |
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
698 |
|
|
|
699 |
|
|
// Already unconsumed |
700 |
✗✓ |
3339 |
if (parser->stream_ == nullptr) |
701 |
|
|
return; |
702 |
|
|
|
703 |
✓✗ |
3339 |
parser->stream_->RemoveStreamListener(parser); |
704 |
|
|
} |
705 |
|
|
|
706 |
|
|
|
707 |
|
62 |
static void GetCurrentBuffer(const FunctionCallbackInfo<Value>& args) { |
708 |
|
|
Parser* parser; |
709 |
✗✓ |
62 |
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
710 |
|
|
|
711 |
|
62 |
Local<Object> ret = Buffer::Copy( |
712 |
|
|
parser->env(), |
713 |
|
|
parser->current_buffer_data_, |
714 |
|
124 |
parser->current_buffer_len_).ToLocalChecked(); |
715 |
|
|
|
716 |
|
124 |
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 |
|
4837 |
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 |
✗✓ |
4837 |
if (binding_data_->parser_buffer_in_use) |
747 |
|
|
return uv_buf_init(Malloc(suggested_size), suggested_size); |
748 |
|
4837 |
binding_data_->parser_buffer_in_use = true; |
749 |
|
|
|
750 |
✓✓ |
4837 |
if (binding_data_->parser_buffer.empty()) |
751 |
|
372 |
binding_data_->parser_buffer.resize(kAllocBufferSize); |
752 |
|
|
|
753 |
|
4837 |
return uv_buf_init(binding_data_->parser_buffer.data(), kAllocBufferSize); |
754 |
|
|
} |
755 |
|
|
|
756 |
|
|
|
757 |
|
4881 |
void OnStreamRead(ssize_t nread, const uv_buf_t& buf) override { |
758 |
|
4881 |
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 |
|
4875 |
auto on_scope_leave = OnScopeLeave([&]() { |
763 |
✓✓ |
4875 |
if (buf.base == binding_data_->parser_buffer.data()) |
764 |
|
4833 |
binding_data_->parser_buffer_in_use = false; |
765 |
|
|
else |
766 |
|
42 |
free(buf.base); |
767 |
|
4881 |
}); |
768 |
|
|
|
769 |
✓✓ |
4881 |
if (nread < 0) { |
770 |
|
607 |
PassReadErrorToPreviousListener(nread); |
771 |
|
607 |
return; |
772 |
|
|
} |
773 |
|
|
|
774 |
|
|
// Ignore, empty reads have special meaning in http parser |
775 |
✗✓ |
4274 |
if (nread == 0) |
776 |
|
|
return; |
777 |
|
|
|
778 |
|
4274 |
Local<Value> ret = Execute(buf.base, nread); |
779 |
|
|
|
780 |
|
|
// Exception |
781 |
✓✓ |
4271 |
if (ret.IsEmpty()) |
782 |
|
5 |
return; |
783 |
|
|
|
784 |
|
|
Local<Value> cb = |
785 |
|
12798 |
object()->Get(env()->context(), kOnExecute).ToLocalChecked(); |
786 |
|
|
|
787 |
✓✓ |
4266 |
if (!cb->IsFunction()) |
788 |
|
1003 |
return; |
789 |
|
|
|
790 |
|
|
// Hooks for GetCurrentBuffer |
791 |
|
3263 |
current_buffer_len_ = nread; |
792 |
|
3263 |
current_buffer_data_ = buf.base; |
793 |
|
|
|
794 |
|
6526 |
MakeCallback(cb.As<Function>(), 1, &ret); |
795 |
|
|
|
796 |
|
3260 |
current_buffer_len_ = 0; |
797 |
|
3260 |
current_buffer_data_ = nullptr; |
798 |
|
|
} |
799 |
|
|
|
800 |
|
|
|
801 |
|
9039 |
Local<Value> Execute(const char* data, size_t len) { |
802 |
|
9039 |
EscapableHandleScope scope(env()->isolate()); |
803 |
|
|
|
804 |
|
9039 |
current_buffer_len_ = len; |
805 |
|
9039 |
current_buffer_data_ = data; |
806 |
|
9039 |
got_exception_ = false; |
807 |
|
|
|
808 |
|
|
llhttp_errno_t err; |
809 |
|
|
|
810 |
✓✓ |
9039 |
if (data == nullptr) { |
811 |
|
1192 |
err = llhttp_finish(&parser_); |
812 |
|
|
} else { |
813 |
|
7847 |
err = llhttp_execute(&parser_, data, len); |
814 |
|
7843 |
Save(); |
815 |
|
|
} |
816 |
|
|
|
817 |
|
|
// Calculate bytes read and resume after Upgrade/CONNECT pause |
818 |
|
9035 |
size_t nread = len; |
819 |
✓✓ |
9035 |
if (err != HPE_OK) { |
820 |
|
463 |
nread = llhttp_get_error_pos(&parser_) - data; |
821 |
|
|
|
822 |
|
|
// This isn't a real pause, just a way to stop parsing early. |
823 |
✓✓ |
463 |
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 |
✗✓ |
9035 |
if (pending_pause_) { |
831 |
|
|
pending_pause_ = false; |
832 |
|
|
llhttp_pause(&parser_); |
833 |
|
|
} |
834 |
|
|
|
835 |
|
9035 |
current_buffer_len_ = 0; |
836 |
|
9035 |
current_buffer_data_ = nullptr; |
837 |
|
|
|
838 |
|
|
// If there was an exception in one of the callbacks |
839 |
✓✓ |
9035 |
if (got_exception_) |
840 |
|
8 |
return scope.Escape(Local<Value>()); |
841 |
|
|
|
842 |
|
9027 |
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 |
✓✓✓✓
|
9027 |
if (!parser_.upgrade && err != HPE_OK) { |
847 |
|
252 |
Local<Value> e = Exception::Error(env()->parse_error_string()); |
848 |
|
252 |
Local<Object> obj = e->ToObject(env()->isolate()->GetCurrentContext()) |
849 |
|
252 |
.ToLocalChecked(); |
850 |
|
252 |
obj->Set(env()->context(), |
851 |
|
|
env()->bytes_parsed_string(), |
852 |
|
504 |
nread_obj).Check(); |
853 |
|
252 |
const char* errno_reason = llhttp_get_error_reason(&parser_); |
854 |
|
|
|
855 |
|
|
Local<String> code; |
856 |
|
|
Local<String> reason; |
857 |
✓✓ |
252 |
if (err == HPE_USER) { |
858 |
|
19 |
const char* colon = strchr(errno_reason, ':'); |
859 |
✗✓ |
19 |
CHECK_NOT_NULL(colon); |
860 |
|
|
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 |
|
233 |
code = OneByteString(env()->isolate(), llhttp_errno_name(err)); |
866 |
|
233 |
reason = OneByteString(env()->isolate(), errno_reason); |
867 |
|
|
} |
868 |
|
|
|
869 |
|
756 |
obj->Set(env()->context(), env()->code_string(), code).Check(); |
870 |
|
756 |
obj->Set(env()->context(), env()->reason_string(), reason).Check(); |
871 |
|
252 |
return scope.Escape(e); |
872 |
|
|
} |
873 |
|
|
|
874 |
|
|
// No return value is needed for `Finish()` |
875 |
✓✓ |
8775 |
if (data == nullptr) { |
876 |
|
1159 |
return scope.Escape(Local<Value>()); |
877 |
|
|
} |
878 |
|
7616 |
return scope.Escape(nread_obj); |
879 |
|
|
} |
880 |
|
|
|
881 |
|
17741 |
Local<Array> CreateHeaders() { |
882 |
|
|
// There could be extra entries but the max size should be fixed |
883 |
✓✓ |
1153165 |
Local<Value> headers_v[kMaxHeaderFieldsCount * 2]; |
884 |
|
|
|
885 |
✓✓ |
38293 |
for (size_t i = 0; i < num_values_; ++i) { |
886 |
|
20552 |
headers_v[i * 2] = fields_[i].ToString(env()); |
887 |
|
41104 |
headers_v[i * 2 + 1] = values_[i].ToTrimmedString(env()); |
888 |
|
|
} |
889 |
|
|
|
890 |
|
17741 |
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 |
|
69 |
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 |
|
138 |
}; |
908 |
|
|
|
909 |
|
|
MaybeLocal<Value> r = MakeCallback(cb.As<Function>(), |
910 |
|
69 |
arraysize(argv), |
911 |
|
138 |
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 |
|
6480 |
void Init(llhttp_type_t type, uint64_t max_http_header_size, |
922 |
|
|
uint32_t lenient_flags) { |
923 |
|
6480 |
llhttp_init(&parser_, type, &settings); |
924 |
|
|
|
925 |
✓✓ |
6480 |
if (lenient_flags & kLenientHeaders) { |
926 |
|
5 |
llhttp_set_lenient_headers(&parser_, 1); |
927 |
|
|
} |
928 |
✓✓ |
6480 |
if (lenient_flags & kLenientChunkedLength) { |
929 |
|
5 |
llhttp_set_lenient_chunked_length(&parser_, 1); |
930 |
|
|
} |
931 |
✓✓ |
6480 |
if (lenient_flags & kLenientKeepAlive) { |
932 |
|
5 |
llhttp_set_lenient_keep_alive(&parser_, 1); |
933 |
|
|
} |
934 |
|
|
|
935 |
|
6480 |
header_nread_ = 0; |
936 |
|
6480 |
url_.Reset(); |
937 |
|
6480 |
status_message_.Reset(); |
938 |
|
6480 |
num_fields_ = 0; |
939 |
|
6480 |
num_values_ = 0; |
940 |
|
6480 |
have_flushed_ = false; |
941 |
|
6480 |
got_exception_ = false; |
942 |
|
6480 |
headers_completed_ = false; |
943 |
|
6480 |
max_http_header_size_ = max_http_header_size; |
944 |
|
6480 |
} |
945 |
|
|
|
946 |
|
|
|
947 |
|
59034 |
int TrackHeader(size_t len) { |
948 |
|
59034 |
header_nread_ += len; |
949 |
✓✓ |
59034 |
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 |
|
59017 |
return 0; |
954 |
|
|
} |
955 |
|
|
|
956 |
|
|
|
957 |
|
145479 |
int MaybePause() { |
958 |
✓✗ |
145479 |
if (!pending_pause_) { |
959 |
|
145479 |
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 |
|
291244 |
static int Raw(llhttp_t* p, Args ... args) { |
1002 |
|
291244 |
Parser* parser = ContainerOf(&Parser::parser_, p); |
1003 |
|
291244 |
int rv = (parser->*Member)(std::forward<Args>(args)...); |
1004 |
✓✓ |
291236 |
if (rv == 0) { |
1005 |
|
290958 |
rv = parser->MaybePause(); |
1006 |
|
|
} |
1007 |
|
291236 |
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 |
|
272171 |
bool ParserComparator::operator()(const Parser* lhs, const Parser* rhs) const { |
1018 |
✓✓✓✓
|
272171 |
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 |
|
60661 |
return lhs < rhs; |
1022 |
✓✓ |
211510 |
} else if (lhs->last_message_start_ == 0) { |
1023 |
|
20592 |
return true; |
1024 |
✓✓ |
190918 |
} else if (rhs->last_message_start_ == 0) { |
1025 |
|
6813 |
return false; |
1026 |
|
|
} |
1027 |
|
|
|
1028 |
|
184105 |
return lhs->last_message_start_ < rhs->last_message_start_; |
1029 |
|
|
} |
1030 |
|
|
|
1031 |
|
581 |
void ConnectionsList::New(const FunctionCallbackInfo<Value>& args) { |
1032 |
|
581 |
Local<Context> context = args.GetIsolate()->GetCurrentContext(); |
1033 |
|
581 |
Environment* env = Environment::GetCurrent(context); |
1034 |
|
|
|
1035 |
|
581 |
new ConnectionsList(env, args.This()); |
1036 |
|
581 |
} |
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 |
|
430 |
void ConnectionsList::Idle(const FunctionCallbackInfo<Value>& args) { |
1058 |
|
430 |
Isolate* isolate = args.GetIsolate(); |
1059 |
|
430 |
Local<Context> context = isolate->GetCurrentContext(); |
1060 |
|
|
|
1061 |
|
430 |
Local<Array> idle = Array::New(isolate); |
1062 |
|
|
ConnectionsList* list; |
1063 |
|
|
|
1064 |
✗✓ |
430 |
ASSIGN_OR_RETURN_UNWRAP(&list, args.Holder()); |
1065 |
|
|
|
1066 |
|
430 |
uint32_t i = 0; |
1067 |
✓✓ |
1761 |
for (auto parser : list->all_connections_) { |
1068 |
✓✓ |
1331 |
if (parser->last_message_start_ == 0) { |
1069 |
✗✓ |
3675 |
if (idle->Set(context, i++, parser->object()).IsNothing()) { |
1070 |
|
|
return; |
1071 |
|
|
} |
1072 |
|
|
} |
1073 |
|
|
} |
1074 |
|
|
|
1075 |
|
860 |
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 |
|
96 |
void ConnectionsList::Expired(const FunctionCallbackInfo<Value>& args) { |
1098 |
|
96 |
Isolate* isolate = args.GetIsolate(); |
1099 |
|
96 |
Local<Context> context = isolate->GetCurrentContext(); |
1100 |
|
|
|
1101 |
|
96 |
Local<Array> expired = Array::New(isolate); |
1102 |
|
|
ConnectionsList* list; |
1103 |
|
|
|
1104 |
✗✓ |
96 |
ASSIGN_OR_RETURN_UNWRAP(&list, args.Holder()); |
1105 |
✗✓ |
96 |
CHECK(args[0]->IsNumber()); |
1106 |
✗✓ |
96 |
CHECK(args[1]->IsNumber()); |
1107 |
|
|
uint64_t headers_timeout = |
1108 |
✓✗ |
192 |
static_cast<uint64_t>(args[0].As<Uint32>()->Value()) * 1000000; |
1109 |
|
|
uint64_t request_timeout = |
1110 |
|
192 |
static_cast<uint64_t>(args[1].As<Uint32>()->Value()) * 1000000; |
1111 |
|
|
|
1112 |
✓✓✗✓
|
96 |
if (headers_timeout == 0 && request_timeout == 0) { |
1113 |
|
|
return args.GetReturnValue().Set(expired); |
1114 |
✓✓✗✓
|
96 |
} else if (request_timeout > 0 && headers_timeout > request_timeout) { |
1115 |
|
|
std::swap(headers_timeout, request_timeout); |
1116 |
|
|
} |
1117 |
|
|
|
1118 |
|
96 |
const uint64_t now = uv_hrtime(); |
1119 |
|
96 |
const uint64_t headers_deadline = |
1120 |
✓✓ |
96 |
headers_timeout > 0 ? now - headers_timeout : 0; |
1121 |
|
96 |
const uint64_t request_deadline = |
1122 |
✓✓ |
96 |
request_timeout > 0 ? now - request_timeout : 0; |
1123 |
|
|
|
1124 |
|
96 |
uint32_t i = 0; |
1125 |
|
96 |
auto iter = list->active_connections_.begin(); |
1126 |
|
96 |
auto end = list->active_connections_.end(); |
1127 |
✓✓ |
205 |
while (iter != end) { |
1128 |
|
109 |
Parser* parser = *iter; |
1129 |
|
109 |
iter++; |
1130 |
|
|
|
1131 |
|
|
// Check for expiration. |
1132 |
|
109 |
if ( |
1133 |
✓✓✓✓
|
109 |
(!parser->headers_completed_ && headers_deadline > 0 && |
1134 |
✓✓✓✓
|
109 |
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 |
|
192 |
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 |
|
|
Proxy<DataCall, &Parser::on_header_field>::Raw, |
1155 |
|
|
Proxy<DataCall, &Parser::on_header_value>::Raw, |
1156 |
|
|
Proxy<Call, &Parser::on_headers_complete>::Raw, |
1157 |
|
|
Proxy<DataCall, &Parser::on_body>::Raw, |
1158 |
|
|
Proxy<Call, &Parser::on_message_complete>::Raw, |
1159 |
|
|
Proxy<Call, &Parser::on_chunk_header>::Raw, |
1160 |
|
|
Proxy<Call, &Parser::on_chunk_complete>::Raw, |
1161 |
|
|
|
1162 |
|
|
// on_url_complete |
1163 |
|
|
nullptr, |
1164 |
|
|
// on_status_complete |
1165 |
|
|
nullptr, |
1166 |
|
|
// on_header_field_complete |
1167 |
|
|
nullptr, |
1168 |
|
|
// on_header_value_complete |
1169 |
|
|
nullptr, |
1170 |
|
|
}; |
1171 |
|
|
|
1172 |
|
|
|
1173 |
|
897 |
void InitializeHttpParser(Local<Object> target, |
1174 |
|
|
Local<Value> unused, |
1175 |
|
|
Local<Context> context, |
1176 |
|
|
void* priv) { |
1177 |
|
897 |
Environment* env = Environment::GetCurrent(context); |
1178 |
|
897 |
Isolate* isolate = env->isolate(); |
1179 |
|
|
BindingData* const binding_data = |
1180 |
|
897 |
env->AddBindingData<BindingData>(context, target); |
1181 |
✗✓ |
897 |
if (binding_data == nullptr) return; |
1182 |
|
|
|
1183 |
|
897 |
Local<FunctionTemplate> t = NewFunctionTemplate(isolate, Parser::New); |
1184 |
|
1794 |
t->InstanceTemplate()->SetInternalFieldCount(Parser::kInternalFieldCount); |
1185 |
|
|
|
1186 |
|
2691 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "REQUEST"), |
1187 |
|
|
Integer::New(env->isolate(), HTTP_REQUEST)); |
1188 |
|
2691 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "RESPONSE"), |
1189 |
|
|
Integer::New(env->isolate(), HTTP_RESPONSE)); |
1190 |
|
2691 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kOnMessageBegin"), |
1191 |
|
|
Integer::NewFromUnsigned(env->isolate(), kOnMessageBegin)); |
1192 |
|
2691 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kOnHeaders"), |
1193 |
|
|
Integer::NewFromUnsigned(env->isolate(), kOnHeaders)); |
1194 |
|
2691 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kOnHeadersComplete"), |
1195 |
|
|
Integer::NewFromUnsigned(env->isolate(), kOnHeadersComplete)); |
1196 |
|
2691 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kOnBody"), |
1197 |
|
|
Integer::NewFromUnsigned(env->isolate(), kOnBody)); |
1198 |
|
2691 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kOnMessageComplete"), |
1199 |
|
|
Integer::NewFromUnsigned(env->isolate(), kOnMessageComplete)); |
1200 |
|
2691 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kOnExecute"), |
1201 |
|
|
Integer::NewFromUnsigned(env->isolate(), kOnExecute)); |
1202 |
|
2691 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kOnTimeout"), |
1203 |
|
|
Integer::NewFromUnsigned(env->isolate(), kOnTimeout)); |
1204 |
|
|
|
1205 |
|
2691 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kLenientNone"), |
1206 |
|
|
Integer::NewFromUnsigned(env->isolate(), kLenientNone)); |
1207 |
|
2691 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kLenientHeaders"), |
1208 |
|
|
Integer::NewFromUnsigned(env->isolate(), kLenientHeaders)); |
1209 |
|
2691 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kLenientChunkedLength"), |
1210 |
|
|
Integer::NewFromUnsigned(env->isolate(), kLenientChunkedLength)); |
1211 |
|
2691 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kLenientKeepAlive"), |
1212 |
|
|
Integer::NewFromUnsigned(env->isolate(), kLenientKeepAlive)); |
1213 |
|
2691 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kLenientAll"), |
1214 |
|
|
Integer::NewFromUnsigned(env->isolate(), kLenientAll)); |
1215 |
|
|
|
1216 |
|
897 |
Local<Array> methods = Array::New(env->isolate()); |
1217 |
|
|
#define V(num, name, string) \ |
1218 |
|
|
methods->Set(env->context(), \ |
1219 |
|
|
num, FIXED_ONE_BYTE_STRING(env->isolate(), #string)).Check(); |
1220 |
|
61893 |
HTTP_METHOD_MAP(V) |
1221 |
|
|
#undef V |
1222 |
|
897 |
target->Set(env->context(), |
1223 |
|
|
FIXED_ONE_BYTE_STRING(env->isolate(), "methods"), |
1224 |
|
2691 |
methods).Check(); |
1225 |
|
|
|
1226 |
|
897 |
t->Inherit(AsyncWrap::GetConstructorTemplate(env)); |
1227 |
|
897 |
SetProtoMethod(isolate, t, "close", Parser::Close); |
1228 |
|
897 |
SetProtoMethod(isolate, t, "free", Parser::Free); |
1229 |
|
897 |
SetProtoMethod(isolate, t, "remove", Parser::Remove); |
1230 |
|
897 |
SetProtoMethod(isolate, t, "execute", Parser::Execute); |
1231 |
|
897 |
SetProtoMethod(isolate, t, "finish", Parser::Finish); |
1232 |
|
897 |
SetProtoMethod(isolate, t, "initialize", Parser::Initialize); |
1233 |
|
897 |
SetProtoMethod(isolate, t, "pause", Parser::Pause<true>); |
1234 |
|
897 |
SetProtoMethod(isolate, t, "resume", Parser::Pause<false>); |
1235 |
|
897 |
SetProtoMethod(isolate, t, "consume", Parser::Consume); |
1236 |
|
897 |
SetProtoMethod(isolate, t, "unconsume", Parser::Unconsume); |
1237 |
|
897 |
SetProtoMethod(isolate, t, "getCurrentBuffer", Parser::GetCurrentBuffer); |
1238 |
|
897 |
SetProtoMethod(isolate, t, "duration", Parser::Duration); |
1239 |
|
897 |
SetProtoMethod(isolate, t, "headersCompleted", Parser::HeadersCompleted); |
1240 |
|
|
|
1241 |
|
897 |
SetConstructorFunction(context, target, "HTTPParser", t); |
1242 |
|
|
|
1243 |
|
|
Local<FunctionTemplate> c = |
1244 |
|
897 |
NewFunctionTemplate(isolate, ConnectionsList::New); |
1245 |
|
897 |
c->InstanceTemplate() |
1246 |
|
897 |
->SetInternalFieldCount(ConnectionsList::kInternalFieldCount); |
1247 |
|
897 |
SetProtoMethod(isolate, c, "all", ConnectionsList::All); |
1248 |
|
897 |
SetProtoMethod(isolate, c, "idle", ConnectionsList::Idle); |
1249 |
|
897 |
SetProtoMethod(isolate, c, "active", ConnectionsList::Active); |
1250 |
|
897 |
SetProtoMethod(isolate, c, "expired", ConnectionsList::Expired); |
1251 |
|
897 |
SetConstructorFunction(context, target, "ConnectionsList", c); |
1252 |
|
|
} |
1253 |
|
|
|
1254 |
|
|
} // anonymous namespace |
1255 |
|
|
} // namespace node |
1256 |
|
|
|
1257 |
|
5584 |
NODE_MODULE_CONTEXT_AWARE_INTERNAL(http_parser, node::InitializeHttpParser) |