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