Browse Source

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.
v0.7.4-release
Ryan Dahl 13 years ago
parent
commit
f3da6c6c04
  1. 78
      src/node_http_parser.cc
  2. 6
      test/simple/test-http-parser.js

78
src/node_http_parser.cc

@ -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;
} }

6
test/simple/test-http-parser.js

@ -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…
Cancel
Save