1 |
|
|
// Copyright Joyent, Inc. and other Node contributors. |
2 |
|
|
// |
3 |
|
|
// Permission is hereby granted, free of charge, to any person obtaining a |
4 |
|
|
// copy of this software and associated documentation files (the |
5 |
|
|
// "Software"), to deal in the Software without restriction, including |
6 |
|
|
// without limitation the rights to use, copy, modify, merge, publish, |
7 |
|
|
// distribute, sublicense, and/or sell copies of the Software, and to permit |
8 |
|
|
// persons to whom the Software is furnished to do so, subject to the |
9 |
|
|
// following conditions: |
10 |
|
|
// |
11 |
|
|
// The above copyright notice and this permission notice shall be included |
12 |
|
|
// in all copies or substantial portions of the Software. |
13 |
|
|
// |
14 |
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
15 |
|
|
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
16 |
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN |
17 |
|
|
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, |
18 |
|
|
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
19 |
|
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE |
20 |
|
|
// USE OR OTHER DEALINGS IN THE SOFTWARE. |
21 |
|
|
|
22 |
|
|
#include "node.h" |
23 |
|
|
#include "node_buffer.h" |
24 |
|
|
#include "util.h" |
25 |
|
|
|
26 |
|
|
#include "async_wrap-inl.h" |
27 |
|
|
#include "env-inl.h" |
28 |
|
|
#include "memory_tracker-inl.h" |
29 |
|
|
#include "stream_base-inl.h" |
30 |
|
|
#include "v8.h" |
31 |
|
|
#include "llhttp.h" |
32 |
|
|
|
33 |
|
|
#include <cstdlib> // free() |
34 |
|
|
#include <cstring> // strdup(), strchr() |
35 |
|
|
|
36 |
|
|
|
37 |
|
|
// This is a binding to llhttp (https://github.com/nodejs/llhttp) |
38 |
|
|
// The goal is to decouple sockets from parsing for more javascript-level |
39 |
|
|
// agility. A Buffer is read from a socket and passed to parser.execute(). |
40 |
|
|
// The parser then issues callbacks with slices of the data |
41 |
|
|
// parser.onMessageBegin |
42 |
|
|
// parser.onPath |
43 |
|
|
// parser.onBody |
44 |
|
|
// ... |
45 |
|
|
// No copying is performed when slicing the buffer, only small reference |
46 |
|
|
// allocations. |
47 |
|
|
|
48 |
|
|
|
49 |
|
|
namespace node { |
50 |
|
|
namespace { // NOLINT(build/namespaces) |
51 |
|
|
|
52 |
|
|
using v8::Array; |
53 |
|
|
using v8::Boolean; |
54 |
|
|
using v8::Context; |
55 |
|
|
using v8::EscapableHandleScope; |
56 |
|
|
using v8::Exception; |
57 |
|
|
using v8::Function; |
58 |
|
|
using v8::FunctionCallbackInfo; |
59 |
|
|
using v8::FunctionTemplate; |
60 |
|
|
using v8::HandleScope; |
61 |
|
|
using v8::Int32; |
62 |
|
|
using v8::Integer; |
63 |
|
|
using v8::Local; |
64 |
|
|
using v8::MaybeLocal; |
65 |
|
|
using v8::Number; |
66 |
|
|
using v8::Object; |
67 |
|
|
using v8::String; |
68 |
|
|
using v8::Uint32; |
69 |
|
|
using v8::Undefined; |
70 |
|
|
using v8::Value; |
71 |
|
|
|
72 |
|
|
const uint32_t kOnMessageBegin = 0; |
73 |
|
|
const uint32_t kOnHeaders = 1; |
74 |
|
|
const uint32_t kOnHeadersComplete = 2; |
75 |
|
|
const uint32_t kOnBody = 3; |
76 |
|
|
const uint32_t kOnMessageComplete = 4; |
77 |
|
|
const uint32_t kOnExecute = 5; |
78 |
|
|
const uint32_t kOnTimeout = 6; |
79 |
|
|
// Any more fields than this will be flushed into JS |
80 |
|
|
const size_t kMaxHeaderFieldsCount = 32; |
81 |
|
|
|
82 |
|
|
const uint32_t kLenientNone = 0; |
83 |
|
|
const uint32_t kLenientHeaders = 1 << 0; |
84 |
|
|
const uint32_t kLenientChunkedLength = 1 << 1; |
85 |
|
|
const uint32_t kLenientKeepAlive = 1 << 2; |
86 |
|
|
const uint32_t kLenientAll = kLenientHeaders | kLenientChunkedLength | |
87 |
|
|
kLenientKeepAlive; |
88 |
|
|
|
89 |
|
17553 |
inline bool IsOWS(char c) { |
90 |
✓✓✓✓
|
17553 |
return c == ' ' || c == '\t'; |
91 |
|
|
} |
92 |
|
|
|
93 |
|
|
class BindingData : public BaseObject { |
94 |
|
|
public: |
95 |
|
808 |
BindingData(Environment* env, Local<Object> obj) |
96 |
|
808 |
: BaseObject(env, obj) {} |
97 |
|
|
|
98 |
|
|
static constexpr FastStringKey type_name { "http_parser" }; |
99 |
|
|
|
100 |
|
|
std::vector<char> parser_buffer; |
101 |
|
|
bool parser_buffer_in_use = false; |
102 |
|
|
|
103 |
|
3 |
void MemoryInfo(MemoryTracker* tracker) const override { |
104 |
|
3 |
tracker->TrackField("parser_buffer", parser_buffer); |
105 |
|
3 |
} |
106 |
|
3 |
SET_SELF_SIZE(BindingData) |
107 |
|
3 |
SET_MEMORY_INFO_NAME(BindingData) |
108 |
|
|
}; |
109 |
|
|
|
110 |
|
|
// helper class for the Parser |
111 |
|
|
struct StringPtr { |
112 |
|
273834 |
StringPtr() { |
113 |
|
273834 |
on_heap_ = false; |
114 |
|
273834 |
Reset(); |
115 |
|
273834 |
} |
116 |
|
|
|
117 |
|
|
|
118 |
|
541596 |
~StringPtr() { |
119 |
|
270798 |
Reset(); |
120 |
|
270798 |
} |
121 |
|
|
|
122 |
|
|
|
123 |
|
|
// If str_ does not point to a heap string yet, this function makes it do |
124 |
|
|
// so. This is called at the end of each http_parser_execute() so as not |
125 |
|
|
// to leak references. See issue #2438 and test-http-parser-bad-ref.js. |
126 |
|
15093 |
void Save() { |
127 |
✓✓✓✓
|
15093 |
if (!on_heap_ && size_ > 0) { |
128 |
|
6256 |
char* s = new char[size_]; |
129 |
|
6256 |
memcpy(s, str_, size_); |
130 |
|
6256 |
str_ = s; |
131 |
|
6256 |
on_heap_ = true; |
132 |
|
|
} |
133 |
|
15093 |
} |
134 |
|
|
|
135 |
|
|
|
136 |
|
627049 |
void Reset() { |
137 |
✓✓ |
627049 |
if (on_heap_) { |
138 |
✓✗ |
6209 |
delete[] str_; |
139 |
|
6209 |
on_heap_ = false; |
140 |
|
|
} |
141 |
|
|
|
142 |
|
627049 |
str_ = nullptr; |
143 |
|
627049 |
size_ = 0; |
144 |
|
627049 |
} |
145 |
|
|
|
146 |
|
|
|
147 |
|
52531 |
void Update(const char* str, size_t size) { |
148 |
✓✓ |
52531 |
if (str_ == nullptr) { |
149 |
|
52464 |
str_ = str; |
150 |
✓✓✓✗
|
67 |
} else if (on_heap_ || str_ + size_ != str) { |
151 |
|
|
// Non-consecutive input, make a copy on the heap. |
152 |
|
|
// TODO(bnoordhuis) Use slab allocation, O(n) allocs is bad. |
153 |
|
67 |
char* s = new char[size_ + size]; |
154 |
|
67 |
memcpy(s, str_, size_); |
155 |
|
67 |
memcpy(s + size_, str, size); |
156 |
|
|
|
157 |
✓✓ |
67 |
if (on_heap_) |
158 |
✓✗ |
66 |
delete[] str_; |
159 |
|
|
else |
160 |
|
1 |
on_heap_ = true; |
161 |
|
|
|
162 |
|
67 |
str_ = s; |
163 |
|
|
} |
164 |
|
52531 |
size_ += size; |
165 |
|
52531 |
} |
166 |
|
|
|
167 |
|
|
|
168 |
|
52365 |
Local<String> ToString(Environment* env) const { |
169 |
✓✓ |
52365 |
if (size_ != 0) |
170 |
|
52301 |
return OneByteString(env->isolate(), str_, size_); |
171 |
|
|
else |
172 |
|
128 |
return String::Empty(env->isolate()); |
173 |
|
|
} |
174 |
|
|
|
175 |
|
|
|
176 |
|
|
// Strip trailing OWS (SPC or HTAB) from string. |
177 |
|
17555 |
Local<String> ToTrimmedString(Environment* env) { |
178 |
✓✓✓✓ ✓✓ |
17555 |
while (size_ > 0 && IsOWS(str_[size_ - 1])) { |
179 |
|
11 |
size_--; |
180 |
|
|
} |
181 |
|
17544 |
return ToString(env); |
182 |
|
|
} |
183 |
|
|
|
184 |
|
|
|
185 |
|
|
const char* str_; |
186 |
|
|
bool on_heap_; |
187 |
|
|
size_t size_; |
188 |
|
|
}; |
189 |
|
|
|
190 |
|
|
class Parser : public AsyncWrap, public StreamListener { |
191 |
|
|
public: |
192 |
|
4149 |
Parser(BindingData* binding_data, Local<Object> wrap) |
193 |
|
4149 |
: AsyncWrap(binding_data->env(), wrap), |
194 |
|
|
current_buffer_len_(0), |
195 |
|
|
current_buffer_data_(nullptr), |
196 |
✓✓✓✓
|
269685 |
binding_data_(binding_data) { |
197 |
|
4149 |
} |
198 |
|
|
|
199 |
|
|
|
200 |
|
|
void MemoryInfo(MemoryTracker* tracker) const override { |
201 |
|
|
tracker->TrackField("current_buffer", current_buffer_); |
202 |
|
|
} |
203 |
|
|
|
204 |
|
|
SET_MEMORY_INFO_NAME(Parser) |
205 |
|
|
SET_SELF_SIZE(Parser) |
206 |
|
|
|
207 |
|
17268 |
int on_message_begin() { |
208 |
|
17268 |
num_fields_ = num_values_ = 0; |
209 |
|
17268 |
url_.Reset(); |
210 |
|
17268 |
status_message_.Reset(); |
211 |
|
17268 |
header_parsing_start_time_ = uv_hrtime(); |
212 |
|
|
|
213 |
|
34536 |
Local<Value> cb = object()->Get(env()->context(), kOnMessageBegin) |
214 |
|
17268 |
.ToLocalChecked(); |
215 |
✓✓ |
17268 |
if (cb->IsFunction()) { |
216 |
|
|
InternalCallbackScope callback_scope( |
217 |
|
27774 |
this, InternalCallbackScope::kSkipTaskQueues); |
218 |
|
|
|
219 |
|
13887 |
MaybeLocal<Value> r = cb.As<Function>()->Call( |
220 |
|
27774 |
env()->context(), object(), 0, nullptr); |
221 |
|
|
|
222 |
✗✓ |
13887 |
if (r.IsEmpty()) callback_scope.MarkAsFailed(); |
223 |
|
|
} |
224 |
|
|
|
225 |
|
17268 |
return 0; |
226 |
|
|
} |
227 |
|
|
|
228 |
|
|
|
229 |
|
14051 |
int on_url(const char* at, size_t length) { |
230 |
|
14051 |
int rv = TrackHeader(length); |
231 |
✗✓ |
14051 |
if (rv != 0) { |
232 |
|
|
return rv; |
233 |
|
|
} |
234 |
|
|
|
235 |
|
14051 |
url_.Update(at, length); |
236 |
|
14051 |
return 0; |
237 |
|
|
} |
238 |
|
|
|
239 |
|
|
|
240 |
|
3209 |
int on_status(const char* at, size_t length) { |
241 |
|
3209 |
int rv = TrackHeader(length); |
242 |
✗✓ |
3209 |
if (rv != 0) { |
243 |
|
|
return rv; |
244 |
|
|
} |
245 |
|
|
|
246 |
|
3209 |
status_message_.Update(at, length); |
247 |
|
3209 |
return 0; |
248 |
|
|
} |
249 |
|
|
|
250 |
|
|
|
251 |
|
17648 |
int on_header_field(const char* at, size_t length) { |
252 |
|
17648 |
int rv = TrackHeader(length); |
253 |
✗✓ |
17648 |
if (rv != 0) { |
254 |
|
|
return rv; |
255 |
|
|
} |
256 |
|
|
|
257 |
✓✓ |
17648 |
if (num_fields_ == num_values_) { |
258 |
|
|
// start of new field name |
259 |
|
17619 |
num_fields_++; |
260 |
✓✓ |
17619 |
if (num_fields_ == kMaxHeaderFieldsCount) { |
261 |
|
|
// ran out of space - flush to javascript land |
262 |
|
47 |
Flush(); |
263 |
|
47 |
num_fields_ = 1; |
264 |
|
47 |
num_values_ = 0; |
265 |
|
|
} |
266 |
|
17619 |
fields_[num_fields_ - 1].Reset(); |
267 |
|
|
} |
268 |
|
|
|
269 |
✗✓ |
17648 |
CHECK_LT(num_fields_, kMaxHeaderFieldsCount); |
270 |
✗✓ |
17648 |
CHECK_EQ(num_fields_, num_values_ + 1); |
271 |
|
|
|
272 |
|
17648 |
fields_[num_fields_ - 1].Update(at, length); |
273 |
|
|
|
274 |
|
17648 |
return 0; |
275 |
|
|
} |
276 |
|
|
|
277 |
|
|
|
278 |
|
17640 |
int on_header_value(const char* at, size_t length) { |
279 |
|
17640 |
int rv = TrackHeader(length); |
280 |
✓✓ |
17640 |
if (rv != 0) { |
281 |
|
17 |
return rv; |
282 |
|
|
} |
283 |
|
|
|
284 |
✓✓ |
17623 |
if (num_values_ != num_fields_) { |
285 |
|
|
// start of new header value |
286 |
|
17593 |
num_values_++; |
287 |
|
17593 |
values_[num_values_ - 1].Reset(); |
288 |
|
|
} |
289 |
|
|
|
290 |
✗✓ |
17623 |
CHECK_LT(num_values_, arraysize(values_)); |
291 |
✗✓ |
17623 |
CHECK_EQ(num_values_, num_fields_); |
292 |
|
|
|
293 |
|
17623 |
values_[num_values_ - 1].Update(at, length); |
294 |
|
|
|
295 |
|
17623 |
return 0; |
296 |
|
|
} |
297 |
|
|
|
298 |
|
|
|
299 |
|
17218 |
int on_headers_complete() { |
300 |
|
17218 |
header_nread_ = 0; |
301 |
|
17218 |
header_parsing_start_time_ = 0; |
302 |
|
|
|
303 |
|
|
// Arguments for the on-headers-complete javascript callback. This |
304 |
|
|
// list needs to be kept in sync with the actual argument list for |
305 |
|
|
// `parserOnHeadersComplete` in lib/_http_common.js. |
306 |
|
|
enum on_headers_complete_arg_index { |
307 |
|
|
A_VERSION_MAJOR = 0, |
308 |
|
|
A_VERSION_MINOR, |
309 |
|
|
A_HEADERS, |
310 |
|
|
A_METHOD, |
311 |
|
|
A_URL, |
312 |
|
|
A_STATUS_CODE, |
313 |
|
|
A_STATUS_MESSAGE, |
314 |
|
|
A_UPGRADE, |
315 |
|
|
A_SHOULD_KEEP_ALIVE, |
316 |
|
|
A_MAX |
317 |
|
|
}; |
318 |
|
|
|
319 |
✓✓ |
172180 |
Local<Value> argv[A_MAX]; |
320 |
|
17218 |
Local<Object> obj = object(); |
321 |
|
17218 |
Local<Value> cb = obj->Get(env()->context(), |
322 |
|
34436 |
kOnHeadersComplete).ToLocalChecked(); |
323 |
|
|
|
324 |
✗✓ |
17218 |
if (!cb->IsFunction()) |
325 |
|
|
return 0; |
326 |
|
|
|
327 |
|
17218 |
Local<Value> undefined = Undefined(env()->isolate()); |
328 |
✓✓ |
172180 |
for (size_t i = 0; i < arraysize(argv); i++) |
329 |
|
154962 |
argv[i] = undefined; |
330 |
|
|
|
331 |
✓✓ |
17218 |
if (have_flushed_) { |
332 |
|
|
// Slow case, flush remaining headers. |
333 |
|
16 |
Flush(); |
334 |
|
|
} else { |
335 |
|
|
// Fast case, pass headers and URL to JS land. |
336 |
|
17202 |
argv[A_HEADERS] = CreateHeaders(); |
337 |
✓✓ |
17202 |
if (parser_.type == HTTP_REQUEST) |
338 |
|
28020 |
argv[A_URL] = url_.ToString(env()); |
339 |
|
|
} |
340 |
|
|
|
341 |
|
17218 |
num_fields_ = 0; |
342 |
|
17218 |
num_values_ = 0; |
343 |
|
|
|
344 |
|
|
// METHOD |
345 |
✓✓ |
17218 |
if (parser_.type == HTTP_REQUEST) { |
346 |
|
28036 |
argv[A_METHOD] = |
347 |
|
14018 |
Uint32::NewFromUnsigned(env()->isolate(), parser_.method); |
348 |
|
|
} |
349 |
|
|
|
350 |
|
|
// STATUS |
351 |
✓✓ |
17218 |
if (parser_.type == HTTP_RESPONSE) { |
352 |
|
3200 |
argv[A_STATUS_CODE] = |
353 |
|
3200 |
Integer::New(env()->isolate(), parser_.status_code); |
354 |
|
6400 |
argv[A_STATUS_MESSAGE] = status_message_.ToString(env()); |
355 |
|
|
} |
356 |
|
|
|
357 |
|
|
// VERSION |
358 |
|
17218 |
argv[A_VERSION_MAJOR] = Integer::New(env()->isolate(), parser_.http_major); |
359 |
|
17218 |
argv[A_VERSION_MINOR] = Integer::New(env()->isolate(), parser_.http_minor); |
360 |
|
|
|
361 |
|
|
bool should_keep_alive; |
362 |
|
17218 |
should_keep_alive = llhttp_should_keep_alive(&parser_); |
363 |
|
|
|
364 |
|
17218 |
argv[A_SHOULD_KEEP_ALIVE] = |
365 |
|
|
Boolean::New(env()->isolate(), should_keep_alive); |
366 |
|
|
|
367 |
|
34436 |
argv[A_UPGRADE] = Boolean::New(env()->isolate(), parser_.upgrade); |
368 |
|
|
|
369 |
|
|
MaybeLocal<Value> head_response; |
370 |
|
|
{ |
371 |
|
|
InternalCallbackScope callback_scope( |
372 |
|
34434 |
this, InternalCallbackScope::kSkipTaskQueues); |
373 |
|
17218 |
head_response = cb.As<Function>()->Call( |
374 |
|
34436 |
env()->context(), object(), arraysize(argv), argv); |
375 |
✓✓ |
17216 |
if (head_response.IsEmpty()) callback_scope.MarkAsFailed(); |
376 |
|
|
} |
377 |
|
|
|
378 |
|
|
int64_t val; |
379 |
|
|
|
380 |
✓✓ |
51632 |
if (head_response.IsEmpty() || !head_response.ToLocalChecked() |
381 |
✓✓ |
34424 |
->IntegerValue(env()->context()) |
382 |
✗✓ |
17208 |
.To(&val)) { |
383 |
|
8 |
got_exception_ = true; |
384 |
|
8 |
return -1; |
385 |
|
|
} |
386 |
|
|
|
387 |
|
17208 |
return static_cast<int>(val); |
388 |
|
|
} |
389 |
|
|
|
390 |
|
|
|
391 |
|
11196 |
int on_body(const char* at, size_t length) { |
392 |
|
11196 |
EscapableHandleScope scope(env()->isolate()); |
393 |
|
|
|
394 |
|
11196 |
Local<Object> obj = object(); |
395 |
|
22392 |
Local<Value> cb = obj->Get(env()->context(), kOnBody).ToLocalChecked(); |
396 |
|
|
|
397 |
✗✓ |
11196 |
if (!cb->IsFunction()) |
398 |
|
|
return 0; |
399 |
|
|
|
400 |
|
|
// We came from consumed stream |
401 |
✓✓ |
11196 |
if (current_buffer_.IsEmpty()) { |
402 |
|
|
// Make sure Buffer will be in parent HandleScope |
403 |
|
518 |
current_buffer_ = scope.Escape(Buffer::Copy( |
404 |
|
|
env()->isolate(), |
405 |
|
|
current_buffer_data_, |
406 |
|
1036 |
current_buffer_len_).ToLocalChecked()); |
407 |
|
|
} |
408 |
|
|
|
409 |
|
|
Local<Value> argv[3] = { |
410 |
|
|
current_buffer_, |
411 |
|
|
Integer::NewFromUnsigned( |
412 |
|
11196 |
env()->isolate(), static_cast<uint32_t>(at - current_buffer_data_)), |
413 |
|
22392 |
Integer::NewFromUnsigned(env()->isolate(), length)}; |
414 |
|
|
|
415 |
|
|
MaybeLocal<Value> r = MakeCallback(cb.As<Function>(), |
416 |
|
11196 |
arraysize(argv), |
417 |
|
22392 |
argv); |
418 |
|
|
|
419 |
✗✓ |
11196 |
if (r.IsEmpty()) { |
420 |
|
|
got_exception_ = true; |
421 |
|
|
llhttp_set_error_reason(&parser_, "HPE_JS_EXCEPTION:JS Exception"); |
422 |
|
|
return HPE_USER; |
423 |
|
|
} |
424 |
|
|
|
425 |
|
11196 |
return 0; |
426 |
|
|
} |
427 |
|
|
|
428 |
|
|
|
429 |
|
16010 |
int on_message_complete() { |
430 |
|
32020 |
HandleScope scope(env()->isolate()); |
431 |
|
|
|
432 |
✓✓ |
16010 |
if (num_fields_) |
433 |
|
4 |
Flush(); // Flush trailing HTTP headers. |
434 |
|
|
|
435 |
|
16010 |
Local<Object> obj = object(); |
436 |
|
16010 |
Local<Value> cb = obj->Get(env()->context(), |
437 |
|
32020 |
kOnMessageComplete).ToLocalChecked(); |
438 |
|
|
|
439 |
✓✓ |
16010 |
if (!cb->IsFunction()) |
440 |
|
2 |
return 0; |
441 |
|
|
|
442 |
|
|
MaybeLocal<Value> r; |
443 |
|
|
{ |
444 |
|
|
InternalCallbackScope callback_scope( |
445 |
|
32016 |
this, InternalCallbackScope::kSkipTaskQueues); |
446 |
|
32016 |
r = cb.As<Function>()->Call(env()->context(), object(), 0, nullptr); |
447 |
✗✓ |
16008 |
if (r.IsEmpty()) callback_scope.MarkAsFailed(); |
448 |
|
|
} |
449 |
|
|
|
450 |
✗✓ |
16008 |
if (r.IsEmpty()) { |
451 |
|
|
got_exception_ = true; |
452 |
|
|
return -1; |
453 |
|
|
} |
454 |
|
|
|
455 |
|
16008 |
return 0; |
456 |
|
|
} |
457 |
|
|
|
458 |
|
|
// Reset nread for the next chunk |
459 |
|
11137 |
int on_chunk_header() { |
460 |
|
11137 |
header_nread_ = 0; |
461 |
|
11137 |
return 0; |
462 |
|
|
} |
463 |
|
|
|
464 |
|
|
|
465 |
|
|
// Reset nread for the next chunk |
466 |
|
10983 |
int on_chunk_complete() { |
467 |
|
10983 |
header_nread_ = 0; |
468 |
|
10983 |
return 0; |
469 |
|
|
} |
470 |
|
|
|
471 |
|
4149 |
static void New(const FunctionCallbackInfo<Value>& args) { |
472 |
|
4149 |
BindingData* binding_data = Environment::GetBindingData<BindingData>(args); |
473 |
|
4149 |
new Parser(binding_data, args.This()); |
474 |
|
4149 |
} |
475 |
|
|
|
476 |
|
|
|
477 |
|
3 |
static void Close(const FunctionCallbackInfo<Value>& args) { |
478 |
|
|
Parser* parser; |
479 |
✗✓ |
3 |
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
480 |
|
|
|
481 |
✓✗ |
3 |
delete parser; |
482 |
|
|
} |
483 |
|
|
|
484 |
|
|
|
485 |
|
6104 |
static void Free(const FunctionCallbackInfo<Value>& args) { |
486 |
|
|
Parser* parser; |
487 |
✗✓ |
6104 |
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
488 |
|
|
|
489 |
|
|
// Since the Parser destructor isn't going to run the destroy() callbacks |
490 |
|
|
// it needs to be triggered manually. |
491 |
|
6104 |
parser->EmitTraceEventDestroy(); |
492 |
|
6104 |
parser->EmitDestroy(); |
493 |
|
|
} |
494 |
|
|
|
495 |
|
|
|
496 |
|
7361 |
void Save() { |
497 |
|
7361 |
url_.Save(); |
498 |
|
7361 |
status_message_.Save(); |
499 |
|
|
|
500 |
✓✓ |
7578 |
for (size_t i = 0; i < num_fields_; i++) { |
501 |
|
217 |
fields_[i].Save(); |
502 |
|
|
} |
503 |
|
|
|
504 |
✓✓ |
7515 |
for (size_t i = 0; i < num_values_; i++) { |
505 |
|
154 |
values_[i].Save(); |
506 |
|
|
} |
507 |
|
7361 |
} |
508 |
|
|
|
509 |
|
|
|
510 |
|
|
// var bytesParsed = parser->execute(buffer); |
511 |
|
3276 |
static void Execute(const FunctionCallbackInfo<Value>& args) { |
512 |
|
|
Parser* parser; |
513 |
✗✓ |
3276 |
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
514 |
✗✓ |
3276 |
CHECK(parser->current_buffer_.IsEmpty()); |
515 |
✗✓ |
3276 |
CHECK_EQ(parser->current_buffer_len_, 0); |
516 |
✗✓ |
3276 |
CHECK_NULL(parser->current_buffer_data_); |
517 |
|
|
|
518 |
|
3276 |
ArrayBufferViewContents<char> buffer(args[0]); |
519 |
|
|
|
520 |
|
|
// This is a hack to get the current_buffer to the callbacks with the least |
521 |
|
|
// amount of overhead. Nothing else will run while http_parser_execute() |
522 |
|
|
// runs, therefore this pointer can be set and used for the execution. |
523 |
|
3276 |
parser->current_buffer_ = args[0].As<Object>(); |
524 |
|
|
|
525 |
|
3276 |
Local<Value> ret = parser->Execute(buffer.data(), buffer.length()); |
526 |
|
|
|
527 |
✓✓ |
3275 |
if (!ret.IsEmpty()) |
528 |
|
6544 |
args.GetReturnValue().Set(ret); |
529 |
|
|
} |
530 |
|
|
|
531 |
|
|
|
532 |
|
1011 |
static void Finish(const FunctionCallbackInfo<Value>& args) { |
533 |
|
|
Parser* parser; |
534 |
✗✓ |
1011 |
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
535 |
|
|
|
536 |
✗✓ |
1011 |
CHECK(parser->current_buffer_.IsEmpty()); |
537 |
|
1011 |
Local<Value> ret = parser->Execute(nullptr, 0); |
538 |
|
|
|
539 |
✓✓ |
1011 |
if (!ret.IsEmpty()) |
540 |
|
66 |
args.GetReturnValue().Set(ret); |
541 |
|
|
} |
542 |
|
|
|
543 |
|
|
|
544 |
|
6301 |
static void Initialize(const FunctionCallbackInfo<Value>& args) { |
545 |
|
6301 |
Environment* env = Environment::GetCurrent(args); |
546 |
|
|
|
547 |
|
6301 |
uint64_t max_http_header_size = 0; |
548 |
|
6301 |
uint64_t headers_timeout = 0; |
549 |
✓✗ |
6301 |
uint32_t lenient_flags = kLenientNone; |
550 |
|
|
|
551 |
✗✓ |
6301 |
CHECK(args[0]->IsInt32()); |
552 |
✗✓ |
6301 |
CHECK(args[1]->IsObject()); |
553 |
|
|
|
554 |
✓✓ |
6301 |
if (args.Length() > 2) { |
555 |
✗✓ |
5131 |
CHECK(args[2]->IsNumber()); |
556 |
|
5131 |
max_http_header_size = |
557 |
|
10262 |
static_cast<uint64_t>(args[2].As<Number>()->Value()); |
558 |
|
|
} |
559 |
✓✓ |
6301 |
if (max_http_header_size == 0) { |
560 |
|
6297 |
max_http_header_size = env->options()->max_http_header_size; |
561 |
|
|
} |
562 |
|
|
|
563 |
✓✓ |
6301 |
if (args.Length() > 3) { |
564 |
✗✓ |
5131 |
CHECK(args[3]->IsInt32()); |
565 |
|
10262 |
lenient_flags = args[3].As<Int32>()->Value(); |
566 |
|
|
} |
567 |
|
|
|
568 |
✓✓ |
6301 |
if (args.Length() > 4) { |
569 |
✗✓ |
5131 |
CHECK(args[4]->IsInt32()); |
570 |
|
10262 |
headers_timeout = args[4].As<Int32>()->Value(); |
571 |
|
|
} |
572 |
|
|
|
573 |
|
|
llhttp_type_t type = |
574 |
|
12602 |
static_cast<llhttp_type_t>(args[0].As<Int32>()->Value()); |
575 |
|
|
|
576 |
✓✓✗✓
|
6301 |
CHECK(type == HTTP_REQUEST || type == HTTP_RESPONSE); |
577 |
|
|
Parser* parser; |
578 |
✗✓ |
6301 |
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
579 |
|
|
// Should always be called from the same context. |
580 |
✗✓ |
6301 |
CHECK_EQ(env, parser->env()); |
581 |
|
|
|
582 |
|
6301 |
AsyncWrap::ProviderType provider = |
583 |
✓✓ |
6301 |
(type == HTTP_REQUEST ? |
584 |
|
|
AsyncWrap::PROVIDER_HTTPINCOMINGMESSAGE |
585 |
|
|
: AsyncWrap::PROVIDER_HTTPCLIENTREQUEST); |
586 |
|
|
|
587 |
|
6301 |
parser->set_provider_type(provider); |
588 |
✓✗ |
18903 |
parser->AsyncReset(args[1].As<Object>()); |
589 |
|
6301 |
parser->Init(type, max_http_header_size, lenient_flags, headers_timeout); |
590 |
|
|
} |
591 |
|
|
|
592 |
|
|
template <bool should_pause> |
593 |
|
22 |
static void Pause(const FunctionCallbackInfo<Value>& args) { |
594 |
|
22 |
Environment* env = Environment::GetCurrent(args); |
595 |
|
|
Parser* parser; |
596 |
✗✓ |
30 |
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
597 |
|
|
// Should always be called from the same context. |
598 |
✗✓ |
22 |
CHECK_EQ(env, parser->env()); |
599 |
|
|
|
600 |
✓✓ |
22 |
if (parser->execute_depth_) { |
601 |
|
8 |
parser->pending_pause_ = should_pause; |
602 |
|
8 |
return; |
603 |
|
|
} |
604 |
|
|
|
605 |
|
|
if (should_pause) { |
606 |
|
8 |
llhttp_pause(&parser->parser_); |
607 |
|
|
} else { |
608 |
|
6 |
llhttp_resume(&parser->parser_); |
609 |
|
|
} |
610 |
|
|
} |
611 |
|
|
|
612 |
|
|
|
613 |
|
3326 |
static void Consume(const FunctionCallbackInfo<Value>& args) { |
614 |
|
|
Parser* parser; |
615 |
✗✓ |
3326 |
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
616 |
✗✓ |
3326 |
CHECK(args[0]->IsObject()); |
617 |
|
6652 |
StreamBase* stream = StreamBase::FromObject(args[0].As<Object>()); |
618 |
✗✓ |
3326 |
CHECK_NOT_NULL(stream); |
619 |
✓✗ |
3326 |
stream->PushStreamListener(parser); |
620 |
|
|
} |
621 |
|
|
|
622 |
|
|
|
623 |
|
3316 |
static void Unconsume(const FunctionCallbackInfo<Value>& args) { |
624 |
|
|
Parser* parser; |
625 |
✗✓ |
3316 |
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
626 |
|
|
|
627 |
|
|
// Already unconsumed |
628 |
✗✓ |
3316 |
if (parser->stream_ == nullptr) |
629 |
|
|
return; |
630 |
|
|
|
631 |
✓✗ |
3316 |
parser->stream_->RemoveStreamListener(parser); |
632 |
|
|
} |
633 |
|
|
|
634 |
|
|
|
635 |
|
38 |
static void GetCurrentBuffer(const FunctionCallbackInfo<Value>& args) { |
636 |
|
|
Parser* parser; |
637 |
✗✓ |
38 |
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
638 |
|
|
|
639 |
|
38 |
Local<Object> ret = Buffer::Copy( |
640 |
|
|
parser->env(), |
641 |
|
|
parser->current_buffer_data_, |
642 |
|
76 |
parser->current_buffer_len_).ToLocalChecked(); |
643 |
|
|
|
644 |
|
76 |
args.GetReturnValue().Set(ret); |
645 |
|
|
} |
646 |
|
|
|
647 |
|
|
protected: |
648 |
|
|
static const size_t kAllocBufferSize = 64 * 1024; |
649 |
|
|
|
650 |
|
4334 |
uv_buf_t OnStreamAlloc(size_t suggested_size) override { |
651 |
|
|
// For most types of streams, OnStreamRead will be immediately after |
652 |
|
|
// OnStreamAlloc, and will consume all data, so using a static buffer for |
653 |
|
|
// reading is more efficient. For other streams, just use Malloc() directly. |
654 |
✗✓ |
4334 |
if (binding_data_->parser_buffer_in_use) |
655 |
|
|
return uv_buf_init(Malloc(suggested_size), suggested_size); |
656 |
|
4334 |
binding_data_->parser_buffer_in_use = true; |
657 |
|
|
|
658 |
✓✓ |
4334 |
if (binding_data_->parser_buffer.empty()) |
659 |
|
347 |
binding_data_->parser_buffer.resize(kAllocBufferSize); |
660 |
|
|
|
661 |
|
4334 |
return uv_buf_init(binding_data_->parser_buffer.data(), kAllocBufferSize); |
662 |
|
|
} |
663 |
|
|
|
664 |
|
|
|
665 |
|
4371 |
void OnStreamRead(ssize_t nread, const uv_buf_t& buf) override { |
666 |
|
4371 |
HandleScope scope(env()->isolate()); |
667 |
|
|
// Once we’re done here, either indicate that the HTTP parser buffer |
668 |
|
|
// is free for re-use, or free() the data if it didn’t come from there |
669 |
|
|
// in the first place. |
670 |
|
4367 |
auto on_scope_leave = OnScopeLeave([&]() { |
671 |
✓✓ |
4367 |
if (buf.base == binding_data_->parser_buffer.data()) |
672 |
|
4332 |
binding_data_->parser_buffer_in_use = false; |
673 |
|
|
else |
674 |
|
35 |
free(buf.base); |
675 |
|
4371 |
}); |
676 |
|
|
|
677 |
✓✓ |
4371 |
if (nread < 0) { |
678 |
|
284 |
PassReadErrorToPreviousListener(nread); |
679 |
|
284 |
return; |
680 |
|
|
} |
681 |
|
|
|
682 |
|
|
// Ignore, empty reads have special meaning in http parser |
683 |
✗✓ |
4087 |
if (nread == 0) |
684 |
|
|
return; |
685 |
|
|
|
686 |
|
4087 |
current_buffer_.Clear(); |
687 |
|
4087 |
Local<Value> ret = Execute(buf.base, nread); |
688 |
|
|
|
689 |
|
|
// Exception |
690 |
✓✓ |
4086 |
if (ret.IsEmpty()) |
691 |
|
5 |
return; |
692 |
|
|
|
693 |
|
|
// check header parsing time |
694 |
✓✓✓✗
|
4081 |
if (header_parsing_start_time_ != 0 && headers_timeout_ != 0) { |
695 |
|
54 |
uint64_t now = uv_hrtime(); |
696 |
|
54 |
uint64_t parsing_time = (now - header_parsing_start_time_) / 1000000; |
697 |
|
|
|
698 |
✓✓ |
54 |
if (parsing_time > headers_timeout_) { |
699 |
|
|
Local<Value> cb = |
700 |
|
18 |
object()->Get(env()->context(), kOnTimeout).ToLocalChecked(); |
701 |
|
|
|
702 |
✗✓ |
6 |
if (!cb->IsFunction()) |
703 |
|
|
return; |
704 |
|
|
|
705 |
|
12 |
MakeCallback(cb.As<Function>(), 0, nullptr); |
706 |
|
|
|
707 |
|
6 |
return; |
708 |
|
|
} |
709 |
|
|
} |
710 |
|
|
|
711 |
|
|
Local<Value> cb = |
712 |
|
12225 |
object()->Get(env()->context(), kOnExecute).ToLocalChecked(); |
713 |
|
|
|
714 |
✓✓ |
4075 |
if (!cb->IsFunction()) |
715 |
|
1003 |
return; |
716 |
|
|
|
717 |
|
|
// Hooks for GetCurrentBuffer |
718 |
|
3072 |
current_buffer_len_ = nread; |
719 |
|
3072 |
current_buffer_data_ = buf.base; |
720 |
|
|
|
721 |
|
6144 |
MakeCallback(cb.As<Function>(), 1, &ret); |
722 |
|
|
|
723 |
|
3069 |
current_buffer_len_ = 0; |
724 |
|
3069 |
current_buffer_data_ = nullptr; |
725 |
|
|
} |
726 |
|
|
|
727 |
|
|
|
728 |
|
8374 |
Local<Value> Execute(const char* data, size_t len) { |
729 |
|
8374 |
EscapableHandleScope scope(env()->isolate()); |
730 |
|
|
|
731 |
|
8374 |
current_buffer_len_ = len; |
732 |
|
8374 |
current_buffer_data_ = data; |
733 |
|
8374 |
got_exception_ = false; |
734 |
|
|
|
735 |
|
|
llhttp_errno_t err; |
736 |
|
|
|
737 |
|
|
// Do not allow re-entering `http_parser_execute()` |
738 |
✗✓ |
8374 |
CHECK_EQ(execute_depth_, 0); |
739 |
|
|
|
740 |
|
8374 |
execute_depth_++; |
741 |
✓✓ |
8374 |
if (data == nullptr) { |
742 |
|
1011 |
err = llhttp_finish(&parser_); |
743 |
|
|
} else { |
744 |
|
7363 |
err = llhttp_execute(&parser_, data, len); |
745 |
|
7361 |
Save(); |
746 |
|
|
} |
747 |
|
8372 |
execute_depth_--; |
748 |
|
|
|
749 |
|
|
// Calculate bytes read and resume after Upgrade/CONNECT pause |
750 |
|
8372 |
size_t nread = len; |
751 |
✓✓ |
8372 |
if (err != HPE_OK) { |
752 |
|
430 |
nread = llhttp_get_error_pos(&parser_) - data; |
753 |
|
|
|
754 |
|
|
// This isn't a real pause, just a way to stop parsing early. |
755 |
✓✓ |
430 |
if (err == HPE_PAUSED_UPGRADE) { |
756 |
|
97 |
err = HPE_OK; |
757 |
|
97 |
llhttp_resume_after_upgrade(&parser_); |
758 |
|
|
} |
759 |
|
|
} |
760 |
|
|
|
761 |
|
|
// Apply pending pause |
762 |
✗✓ |
8372 |
if (pending_pause_) { |
763 |
|
|
pending_pause_ = false; |
764 |
|
|
llhttp_pause(&parser_); |
765 |
|
|
} |
766 |
|
|
|
767 |
|
|
// Unassign the 'buffer_' variable |
768 |
✓✓ |
8372 |
current_buffer_.Clear(); |
769 |
|
8372 |
current_buffer_len_ = 0; |
770 |
|
8372 |
current_buffer_data_ = nullptr; |
771 |
|
|
|
772 |
|
|
// If there was an exception in one of the callbacks |
773 |
✓✓ |
8372 |
if (got_exception_) |
774 |
|
8 |
return scope.Escape(Local<Value>()); |
775 |
|
|
|
776 |
|
8364 |
Local<Integer> nread_obj = Integer::New(env()->isolate(), nread); |
777 |
|
|
|
778 |
|
|
// If there was a parse error in one of the callbacks |
779 |
|
|
// TODO(bnoordhuis) What if there is an error on EOF? |
780 |
✓✓✓✓
|
8364 |
if (!parser_.upgrade && err != HPE_OK) { |
781 |
|
228 |
Local<Value> e = Exception::Error(env()->parse_error_string()); |
782 |
|
228 |
Local<Object> obj = e->ToObject(env()->isolate()->GetCurrentContext()) |
783 |
|
228 |
.ToLocalChecked(); |
784 |
|
228 |
obj->Set(env()->context(), |
785 |
|
|
env()->bytes_parsed_string(), |
786 |
|
456 |
nread_obj).Check(); |
787 |
|
228 |
const char* errno_reason = llhttp_get_error_reason(&parser_); |
788 |
|
|
|
789 |
|
|
Local<String> code; |
790 |
|
|
Local<String> reason; |
791 |
✓✓ |
228 |
if (err == HPE_USER) { |
792 |
|
19 |
const char* colon = strchr(errno_reason, ':'); |
793 |
✗✓ |
19 |
CHECK_NOT_NULL(colon); |
794 |
|
|
code = OneByteString(env()->isolate(), |
795 |
|
|
errno_reason, |
796 |
|
19 |
static_cast<int>(colon - errno_reason)); |
797 |
|
19 |
reason = OneByteString(env()->isolate(), colon + 1); |
798 |
|
|
} else { |
799 |
|
209 |
code = OneByteString(env()->isolate(), llhttp_errno_name(err)); |
800 |
|
209 |
reason = OneByteString(env()->isolate(), errno_reason); |
801 |
|
|
} |
802 |
|
|
|
803 |
|
684 |
obj->Set(env()->context(), env()->code_string(), code).Check(); |
804 |
|
684 |
obj->Set(env()->context(), env()->reason_string(), reason).Check(); |
805 |
|
228 |
return scope.Escape(e); |
806 |
|
|
} |
807 |
|
|
|
808 |
|
|
// No return value is needed for `Finish()` |
809 |
✓✓ |
8136 |
if (data == nullptr) { |
810 |
|
978 |
return scope.Escape(Local<Value>()); |
811 |
|
|
} |
812 |
|
7158 |
return scope.Escape(nread_obj); |
813 |
|
|
} |
814 |
|
|
|
815 |
|
17269 |
Local<Array> CreateHeaders() { |
816 |
|
|
// There could be extra entries but the max size should be fixed |
817 |
✓✓ |
1122485 |
Local<Value> headers_v[kMaxHeaderFieldsCount * 2]; |
818 |
|
|
|
819 |
✓✓ |
34813 |
for (size_t i = 0; i < num_values_; ++i) { |
820 |
|
17544 |
headers_v[i * 2] = fields_[i].ToString(env()); |
821 |
|
35088 |
headers_v[i * 2 + 1] = values_[i].ToTrimmedString(env()); |
822 |
|
|
} |
823 |
|
|
|
824 |
|
17269 |
return Array::New(env()->isolate(), headers_v, num_values_ * 2); |
825 |
|
|
} |
826 |
|
|
|
827 |
|
|
|
828 |
|
|
// spill headers and request path to JS land |
829 |
|
67 |
void Flush() { |
830 |
|
67 |
HandleScope scope(env()->isolate()); |
831 |
|
|
|
832 |
|
67 |
Local<Object> obj = object(); |
833 |
|
134 |
Local<Value> cb = obj->Get(env()->context(), kOnHeaders).ToLocalChecked(); |
834 |
|
|
|
835 |
✗✓ |
67 |
if (!cb->IsFunction()) |
836 |
|
|
return; |
837 |
|
|
|
838 |
|
|
Local<Value> argv[2] = { |
839 |
|
|
CreateHeaders(), |
840 |
|
|
url_.ToString(env()) |
841 |
|
134 |
}; |
842 |
|
|
|
843 |
|
|
MaybeLocal<Value> r = MakeCallback(cb.As<Function>(), |
844 |
|
67 |
arraysize(argv), |
845 |
|
134 |
argv); |
846 |
|
|
|
847 |
✗✓ |
67 |
if (r.IsEmpty()) |
848 |
|
|
got_exception_ = true; |
849 |
|
|
|
850 |
|
67 |
url_.Reset(); |
851 |
|
67 |
have_flushed_ = true; |
852 |
|
|
} |
853 |
|
|
|
854 |
|
|
|
855 |
|
6301 |
void Init(llhttp_type_t type, uint64_t max_http_header_size, |
856 |
|
|
uint32_t lenient_flags, uint64_t headers_timeout) { |
857 |
|
6301 |
llhttp_init(&parser_, type, &settings); |
858 |
|
|
|
859 |
✓✓ |
6301 |
if (lenient_flags & kLenientHeaders) { |
860 |
|
5 |
llhttp_set_lenient_headers(&parser_, 1); |
861 |
|
|
} |
862 |
✓✓ |
6301 |
if (lenient_flags & kLenientChunkedLength) { |
863 |
|
5 |
llhttp_set_lenient_chunked_length(&parser_, 1); |
864 |
|
|
} |
865 |
✓✓ |
6301 |
if (lenient_flags & kLenientKeepAlive) { |
866 |
|
5 |
llhttp_set_lenient_keep_alive(&parser_, 1); |
867 |
|
|
} |
868 |
|
|
|
869 |
|
6301 |
header_nread_ = 0; |
870 |
|
6301 |
url_.Reset(); |
871 |
|
6301 |
status_message_.Reset(); |
872 |
|
6301 |
num_fields_ = 0; |
873 |
|
6301 |
num_values_ = 0; |
874 |
|
6301 |
have_flushed_ = false; |
875 |
|
6301 |
got_exception_ = false; |
876 |
|
6301 |
max_http_header_size_ = max_http_header_size; |
877 |
|
6301 |
header_parsing_start_time_ = 0; |
878 |
|
6301 |
headers_timeout_ = headers_timeout; |
879 |
|
6301 |
} |
880 |
|
|
|
881 |
|
|
|
882 |
|
52548 |
int TrackHeader(size_t len) { |
883 |
|
52548 |
header_nread_ += len; |
884 |
✓✓ |
52548 |
if (header_nread_ >= max_http_header_size_) { |
885 |
|
17 |
llhttp_set_error_reason(&parser_, "HPE_HEADER_OVERFLOW:Header overflow"); |
886 |
|
17 |
return HPE_USER; |
887 |
|
|
} |
888 |
|
52531 |
return 0; |
889 |
|
|
} |
890 |
|
|
|
891 |
|
|
|
892 |
|
136227 |
int MaybePause() { |
893 |
✗✓ |
136227 |
CHECK_NE(execute_depth_, 0); |
894 |
|
|
|
895 |
✓✗ |
136227 |
if (!pending_pause_) { |
896 |
|
136227 |
return 0; |
897 |
|
|
} |
898 |
|
|
|
899 |
|
|
pending_pause_ = false; |
900 |
|
|
llhttp_set_error_reason(&parser_, "Paused in callback"); |
901 |
|
|
return HPE_PAUSED; |
902 |
|
|
} |
903 |
|
|
|
904 |
|
|
|
905 |
|
|
bool IsNotIndicativeOfMemoryLeakAtExit() const override { |
906 |
|
|
// HTTP parsers are able to emit events without any GC root referring |
907 |
|
|
// to them, because they receive events directly from the underlying |
908 |
|
|
// libuv resource. |
909 |
|
|
return true; |
910 |
|
|
} |
911 |
|
|
|
912 |
|
|
|
913 |
|
|
llhttp_t parser_; |
914 |
|
|
StringPtr fields_[kMaxHeaderFieldsCount]; // header fields |
915 |
|
|
StringPtr values_[kMaxHeaderFieldsCount]; // header values |
916 |
|
|
StringPtr url_; |
917 |
|
|
StringPtr status_message_; |
918 |
|
|
size_t num_fields_; |
919 |
|
|
size_t num_values_; |
920 |
|
|
bool have_flushed_; |
921 |
|
|
bool got_exception_; |
922 |
|
|
Local<Object> current_buffer_; |
923 |
|
|
size_t current_buffer_len_; |
924 |
|
|
const char* current_buffer_data_; |
925 |
|
|
unsigned int execute_depth_ = 0; |
926 |
|
|
bool pending_pause_ = false; |
927 |
|
|
uint64_t header_nread_ = 0; |
928 |
|
|
uint64_t max_http_header_size_; |
929 |
|
|
uint64_t headers_timeout_; |
930 |
|
|
uint64_t header_parsing_start_time_ = 0; |
931 |
|
|
|
932 |
|
|
BaseObjectPtr<BindingData> binding_data_; |
933 |
|
|
|
934 |
|
|
// These are helper functions for filling `http_parser_settings`, which turn |
935 |
|
|
// a member function of Parser into a C-style HTTP parser callback. |
936 |
|
|
template <typename Parser, Parser> struct Proxy; |
937 |
|
|
template <typename Parser, typename ...Args, int (Parser::*Member)(Args...)> |
938 |
|
|
struct Proxy<int (Parser::*)(Args...), Member> { |
939 |
|
272720 |
static int Raw(llhttp_t* p, Args ... args) { |
940 |
|
272720 |
Parser* parser = ContainerOf(&Parser::parser_, p); |
941 |
|
272720 |
int rv = (parser->*Member)(std::forward<Args>(args)...); |
942 |
✓✓ |
272716 |
if (rv == 0) { |
943 |
|
272454 |
rv = parser->MaybePause(); |
944 |
|
|
} |
945 |
|
272716 |
return rv; |
946 |
|
|
} |
947 |
|
|
}; |
948 |
|
|
|
949 |
|
|
typedef int (Parser::*Call)(); |
950 |
|
|
typedef int (Parser::*DataCall)(const char* at, size_t length); |
951 |
|
|
|
952 |
|
|
static const llhttp_settings_t settings; |
953 |
|
|
}; |
954 |
|
|
|
955 |
|
|
const llhttp_settings_t Parser::settings = { |
956 |
|
|
Proxy<Call, &Parser::on_message_begin>::Raw, |
957 |
|
|
Proxy<DataCall, &Parser::on_url>::Raw, |
958 |
|
|
Proxy<DataCall, &Parser::on_status>::Raw, |
959 |
|
|
Proxy<DataCall, &Parser::on_header_field>::Raw, |
960 |
|
|
Proxy<DataCall, &Parser::on_header_value>::Raw, |
961 |
|
|
Proxy<Call, &Parser::on_headers_complete>::Raw, |
962 |
|
|
Proxy<DataCall, &Parser::on_body>::Raw, |
963 |
|
|
Proxy<Call, &Parser::on_message_complete>::Raw, |
964 |
|
|
Proxy<Call, &Parser::on_chunk_header>::Raw, |
965 |
|
|
Proxy<Call, &Parser::on_chunk_complete>::Raw, |
966 |
|
|
|
967 |
|
|
// on_url_complete |
968 |
|
|
nullptr, |
969 |
|
|
// on_status_complete |
970 |
|
|
nullptr, |
971 |
|
|
// on_header_field_complete |
972 |
|
|
nullptr, |
973 |
|
|
// on_header_value_complete |
974 |
|
|
nullptr, |
975 |
|
|
}; |
976 |
|
|
|
977 |
|
|
|
978 |
|
808 |
void InitializeHttpParser(Local<Object> target, |
979 |
|
|
Local<Value> unused, |
980 |
|
|
Local<Context> context, |
981 |
|
|
void* priv) { |
982 |
|
808 |
Environment* env = Environment::GetCurrent(context); |
983 |
|
|
BindingData* const binding_data = |
984 |
|
808 |
env->AddBindingData<BindingData>(context, target); |
985 |
✗✓ |
808 |
if (binding_data == nullptr) return; |
986 |
|
|
|
987 |
|
808 |
Local<FunctionTemplate> t = env->NewFunctionTemplate(Parser::New); |
988 |
|
1616 |
t->InstanceTemplate()->SetInternalFieldCount(Parser::kInternalFieldCount); |
989 |
|
|
|
990 |
|
2424 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "REQUEST"), |
991 |
|
|
Integer::New(env->isolate(), HTTP_REQUEST)); |
992 |
|
2424 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "RESPONSE"), |
993 |
|
|
Integer::New(env->isolate(), HTTP_RESPONSE)); |
994 |
|
2424 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kOnMessageBegin"), |
995 |
|
|
Integer::NewFromUnsigned(env->isolate(), kOnMessageBegin)); |
996 |
|
2424 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kOnHeaders"), |
997 |
|
|
Integer::NewFromUnsigned(env->isolate(), kOnHeaders)); |
998 |
|
2424 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kOnHeadersComplete"), |
999 |
|
|
Integer::NewFromUnsigned(env->isolate(), kOnHeadersComplete)); |
1000 |
|
2424 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kOnBody"), |
1001 |
|
|
Integer::NewFromUnsigned(env->isolate(), kOnBody)); |
1002 |
|
2424 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kOnMessageComplete"), |
1003 |
|
|
Integer::NewFromUnsigned(env->isolate(), kOnMessageComplete)); |
1004 |
|
2424 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kOnExecute"), |
1005 |
|
|
Integer::NewFromUnsigned(env->isolate(), kOnExecute)); |
1006 |
|
2424 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kOnTimeout"), |
1007 |
|
|
Integer::NewFromUnsigned(env->isolate(), kOnTimeout)); |
1008 |
|
|
|
1009 |
|
2424 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kLenientNone"), |
1010 |
|
|
Integer::NewFromUnsigned(env->isolate(), kLenientNone)); |
1011 |
|
2424 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kLenientHeaders"), |
1012 |
|
|
Integer::NewFromUnsigned(env->isolate(), kLenientHeaders)); |
1013 |
|
2424 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kLenientChunkedLength"), |
1014 |
|
|
Integer::NewFromUnsigned(env->isolate(), kLenientChunkedLength)); |
1015 |
|
2424 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kLenientKeepAlive"), |
1016 |
|
|
Integer::NewFromUnsigned(env->isolate(), kLenientKeepAlive)); |
1017 |
|
2424 |
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kLenientAll"), |
1018 |
|
|
Integer::NewFromUnsigned(env->isolate(), kLenientAll)); |
1019 |
|
|
|
1020 |
|
808 |
Local<Array> methods = Array::New(env->isolate()); |
1021 |
|
|
#define V(num, name, string) \ |
1022 |
|
|
methods->Set(env->context(), \ |
1023 |
|
|
num, FIXED_ONE_BYTE_STRING(env->isolate(), #string)).Check(); |
1024 |
|
55752 |
HTTP_METHOD_MAP(V) |
1025 |
|
|
#undef V |
1026 |
|
808 |
target->Set(env->context(), |
1027 |
|
|
FIXED_ONE_BYTE_STRING(env->isolate(), "methods"), |
1028 |
|
2424 |
methods).Check(); |
1029 |
|
|
|
1030 |
|
808 |
t->Inherit(AsyncWrap::GetConstructorTemplate(env)); |
1031 |
|
808 |
env->SetProtoMethod(t, "close", Parser::Close); |
1032 |
|
808 |
env->SetProtoMethod(t, "free", Parser::Free); |
1033 |
|
808 |
env->SetProtoMethod(t, "execute", Parser::Execute); |
1034 |
|
808 |
env->SetProtoMethod(t, "finish", Parser::Finish); |
1035 |
|
808 |
env->SetProtoMethod(t, "initialize", Parser::Initialize); |
1036 |
|
808 |
env->SetProtoMethod(t, "pause", Parser::Pause<true>); |
1037 |
|
808 |
env->SetProtoMethod(t, "resume", Parser::Pause<false>); |
1038 |
|
808 |
env->SetProtoMethod(t, "consume", Parser::Consume); |
1039 |
|
808 |
env->SetProtoMethod(t, "unconsume", Parser::Unconsume); |
1040 |
|
808 |
env->SetProtoMethod(t, "getCurrentBuffer", Parser::GetCurrentBuffer); |
1041 |
|
|
|
1042 |
|
808 |
env->SetConstructorFunction(target, "HTTPParser", t); |
1043 |
|
|
} |
1044 |
|
|
|
1045 |
|
|
} // anonymous namespace |
1046 |
|
|
} // namespace node |
1047 |
|
|
|
1048 |
|
5010 |
NODE_MODULE_CONTEXT_AWARE_INTERNAL(http_parser, node::InitializeHttpParser) |