Potential fix for #2438
- Save StringPtr if the header hasn't been completely received yet after one packet. - Add one to num_fields and num_values. They were actually one less than the number of fields and values. - Remove always_inline makes debugging difficult, and has negligible performance benefits.
This commit is contained in:
parent
2cde498319
commit
f3da6c6c04
@ -103,27 +103,12 @@ static char* current_buffer_data;
|
|||||||
static size_t current_buffer_len;
|
static size_t current_buffer_len;
|
||||||
|
|
||||||
|
|
||||||
// gcc 3.x knows the always_inline attribute but fails at build time with a
|
|
||||||
// "sorry, unimplemented: inlining failed" error when compiling at -O0
|
|
||||||
#if defined(__GNUC__)
|
|
||||||
# if __GNUC__ >= 4
|
|
||||||
# define always_inline __attribute__((always_inline))
|
|
||||||
# else
|
|
||||||
# define always_inline inline
|
|
||||||
# endif
|
|
||||||
#elif defined(_MSC_VER)
|
|
||||||
# define always_inline __forceinline
|
|
||||||
#else
|
|
||||||
# define always_inline
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#define HTTP_CB(name) \
|
#define HTTP_CB(name) \
|
||||||
static int name(http_parser* p_) { \
|
static int name(http_parser* p_) { \
|
||||||
Parser* self = container_of(p_, Parser, parser_); \
|
Parser* self = container_of(p_, Parser, parser_); \
|
||||||
return self->name##_(); \
|
return self->name##_(); \
|
||||||
} \
|
} \
|
||||||
int always_inline name##_()
|
int name##_()
|
||||||
|
|
||||||
|
|
||||||
#define HTTP_DATA_CB(name) \
|
#define HTTP_DATA_CB(name) \
|
||||||
@ -131,7 +116,7 @@ static size_t current_buffer_len;
|
|||||||
Parser* self = container_of(p_, Parser, parser_); \
|
Parser* self = container_of(p_, Parser, parser_); \
|
||||||
return self->name##_(at, length); \
|
return self->name##_(at, length); \
|
||||||
} \
|
} \
|
||||||
int always_inline name##_(const char* at, size_t length)
|
int name##_(const char* at, size_t length)
|
||||||
|
|
||||||
|
|
||||||
static inline Persistent<String>
|
static inline Persistent<String>
|
||||||
@ -179,6 +164,19 @@ struct StringPtr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// If str_ does not point to a heap string yet, this function makes it do
|
||||||
|
// so. This is called at the end of each http_parser_execute() so as not
|
||||||
|
// to leak references. See issue #2438 and test-http-parser-bad-ref.js.
|
||||||
|
void Save() {
|
||||||
|
if (!on_heap_ && size_ > 0) {
|
||||||
|
char* s = new char[size_];
|
||||||
|
memcpy(s, str_, size_);
|
||||||
|
str_ = s;
|
||||||
|
on_heap_ = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void Reset() {
|
void Reset() {
|
||||||
if (on_heap_) {
|
if (on_heap_) {
|
||||||
delete[] str_;
|
delete[] str_;
|
||||||
@ -237,7 +235,7 @@ public:
|
|||||||
|
|
||||||
|
|
||||||
HTTP_CB(on_message_begin) {
|
HTTP_CB(on_message_begin) {
|
||||||
num_fields_ = num_values_ = -1;
|
num_fields_ = num_values_ = 0;
|
||||||
url_.Reset();
|
url_.Reset();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -252,18 +250,20 @@ public:
|
|||||||
HTTP_DATA_CB(on_header_field) {
|
HTTP_DATA_CB(on_header_field) {
|
||||||
if (num_fields_ == num_values_) {
|
if (num_fields_ == num_values_) {
|
||||||
// start of new field name
|
// start of new field name
|
||||||
if (++num_fields_ == ARRAY_SIZE(fields_)) {
|
num_fields_++;
|
||||||
|
if (num_fields_ == ARRAY_SIZE(fields_)) {
|
||||||
|
// ran out of space - flush to javascript land
|
||||||
Flush();
|
Flush();
|
||||||
num_fields_ = 0;
|
num_fields_ = 1;
|
||||||
num_values_ = -1;
|
num_values_ = 0;
|
||||||
}
|
}
|
||||||
fields_[num_fields_].Reset();
|
fields_[num_fields_ - 1].Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(num_fields_ < (int)ARRAY_SIZE(fields_));
|
assert(num_fields_ < (int)ARRAY_SIZE(fields_));
|
||||||
assert(num_fields_ == num_values_ + 1);
|
assert(num_fields_ == num_values_ + 1);
|
||||||
|
|
||||||
fields_[num_fields_].Update(at, length);
|
fields_[num_fields_ - 1].Update(at, length);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -272,13 +272,14 @@ public:
|
|||||||
HTTP_DATA_CB(on_header_value) {
|
HTTP_DATA_CB(on_header_value) {
|
||||||
if (num_values_ != num_fields_) {
|
if (num_values_ != num_fields_) {
|
||||||
// start of new header value
|
// start of new header value
|
||||||
values_[++num_values_].Reset();
|
num_values_++;
|
||||||
|
values_[num_values_ - 1].Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(num_values_ < (int)ARRAY_SIZE(values_));
|
assert(num_values_ < (int)ARRAY_SIZE(values_));
|
||||||
assert(num_values_ == num_fields_);
|
assert(num_values_ == num_fields_);
|
||||||
|
|
||||||
values_[num_values_].Update(at, length);
|
values_[num_values_ - 1].Update(at, length);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -302,7 +303,7 @@ public:
|
|||||||
if (parser_.type == HTTP_REQUEST)
|
if (parser_.type == HTTP_REQUEST)
|
||||||
message_info->Set(url_sym, url_.ToString());
|
message_info->Set(url_sym, url_.ToString());
|
||||||
}
|
}
|
||||||
num_fields_ = num_values_ = -1;
|
num_fields_ = num_values_ = 0;
|
||||||
|
|
||||||
// METHOD
|
// METHOD
|
||||||
if (parser_.type == HTTP_REQUEST) {
|
if (parser_.type == HTTP_REQUEST) {
|
||||||
@ -364,7 +365,7 @@ public:
|
|||||||
HTTP_CB(on_message_complete) {
|
HTTP_CB(on_message_complete) {
|
||||||
HandleScope scope;
|
HandleScope scope;
|
||||||
|
|
||||||
if (num_fields_ != -1)
|
if (num_fields_)
|
||||||
Flush(); // Flush trailing HTTP headers.
|
Flush(); // Flush trailing HTTP headers.
|
||||||
|
|
||||||
Local<Value> cb = handle_->Get(on_message_complete_sym);
|
Local<Value> cb = handle_->Get(on_message_complete_sym);
|
||||||
@ -401,6 +402,19 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Save() {
|
||||||
|
url_.Save();
|
||||||
|
|
||||||
|
for (int i = 0; i < num_fields_; i++) {
|
||||||
|
fields_[i].Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < num_values_; i++) {
|
||||||
|
values_[i].Save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// var bytesParsed = parser->execute(buffer, off, len);
|
// var bytesParsed = parser->execute(buffer, off, len);
|
||||||
static Handle<Value> Execute(const Arguments& args) {
|
static Handle<Value> Execute(const Arguments& args) {
|
||||||
HandleScope scope;
|
HandleScope scope;
|
||||||
@ -447,6 +461,8 @@ public:
|
|||||||
size_t nparsed =
|
size_t nparsed =
|
||||||
http_parser_execute(&parser->parser_, &settings, buffer_data + off, len);
|
http_parser_execute(&parser->parser_, &settings, buffer_data + off, len);
|
||||||
|
|
||||||
|
parser->Save();
|
||||||
|
|
||||||
// Unassign the 'buffer_' variable
|
// Unassign the 'buffer_' variable
|
||||||
assert(current_buffer);
|
assert(current_buffer);
|
||||||
current_buffer = NULL;
|
current_buffer = NULL;
|
||||||
@ -515,9 +531,9 @@ private:
|
|||||||
Local<Array> CreateHeaders() {
|
Local<Array> CreateHeaders() {
|
||||||
// num_values_ is either -1 or the entry # of the last header
|
// num_values_ is either -1 or the entry # of the last header
|
||||||
// so num_values_ == 0 means there's a single header
|
// so num_values_ == 0 means there's a single header
|
||||||
Local<Array> headers = Array::New(2 * (num_values_ + 1));
|
Local<Array> headers = Array::New(2 * num_values_);
|
||||||
|
|
||||||
for (int i = 0; i < num_values_ + 1; ++i) {
|
for (int i = 0; i < num_values_; ++i) {
|
||||||
headers->Set(2 * i, fields_[i].ToString());
|
headers->Set(2 * i, fields_[i].ToString());
|
||||||
headers->Set(2 * i + 1, values_[i].ToString());
|
headers->Set(2 * i + 1, values_[i].ToString());
|
||||||
}
|
}
|
||||||
@ -553,8 +569,8 @@ private:
|
|||||||
void Init(enum http_parser_type type) {
|
void Init(enum http_parser_type type) {
|
||||||
http_parser_init(&parser_, type);
|
http_parser_init(&parser_, type);
|
||||||
url_.Reset();
|
url_.Reset();
|
||||||
num_fields_ = -1;
|
num_fields_ = 0;
|
||||||
num_values_ = -1;
|
num_values_ = 0;
|
||||||
have_flushed_ = false;
|
have_flushed_ = false;
|
||||||
got_exception_ = false;
|
got_exception_ = false;
|
||||||
}
|
}
|
||||||
|
@ -381,7 +381,7 @@ function expectBody(expected) {
|
|||||||
//
|
//
|
||||||
(function() {
|
(function() {
|
||||||
var request = Buffer(
|
var request = Buffer(
|
||||||
'POST /it HTTP/1.1' + CRLF +
|
'POST /helpme HTTP/1.1' + CRLF +
|
||||||
'Content-Type: text/plain' + CRLF +
|
'Content-Type: text/plain' + CRLF +
|
||||||
'Transfer-Encoding: chunked' + CRLF +
|
'Transfer-Encoding: chunked' + CRLF +
|
||||||
CRLF +
|
CRLF +
|
||||||
@ -403,7 +403,7 @@ function expectBody(expected) {
|
|||||||
|
|
||||||
parser.onHeadersComplete = mustCall(function(info) {
|
parser.onHeadersComplete = mustCall(function(info) {
|
||||||
assert.equal(info.method, 'POST');
|
assert.equal(info.method, 'POST');
|
||||||
assert.equal(info.url || parser.url, '/it');
|
assert.equal(info.url || parser.url, '/helpme');
|
||||||
assert.equal(info.versionMajor, 1);
|
assert.equal(info.versionMajor, 1);
|
||||||
assert.equal(info.versionMinor, 1);
|
assert.equal(info.versionMinor, 1);
|
||||||
});
|
});
|
||||||
@ -424,7 +424,9 @@ function expectBody(expected) {
|
|||||||
|
|
||||||
for (var i = 1; i < request.length - 1; ++i) {
|
for (var i = 1; i < request.length - 1; ++i) {
|
||||||
var a = request.slice(0, i);
|
var a = request.slice(0, i);
|
||||||
|
console.error("request.slice(0, " + i + ") = ", JSON.stringify(a.toString()));
|
||||||
var b = request.slice(i);
|
var b = request.slice(i);
|
||||||
|
console.error("request.slice(" + i + ") = ", JSON.stringify(b.toString()));
|
||||||
test(a, b);
|
test(a, b);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user