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