diff options
author | Ryan Dahl <ry@tinyclouds.org> | 2011-12-29 16:06:14 -0800 |
---|---|---|
committer | Ryan Dahl <ry@tinyclouds.org> | 2012-01-03 17:43:39 -0800 |
commit | f3da6c6c045fb9d629509cea53e3631342f785d3 (patch) | |
tree | 54d2df0a9fc4054ade959d9b420182cfc641fd76 | |
parent | 2cde4983199473a1bf1650ef485f94b68194aa85 (diff) | |
download | node-new-f3da6c6c045fb9d629509cea53e3631342f785d3.tar.gz |
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.
-rw-r--r-- | src/node_http_parser.cc | 78 | ||||
-rw-r--r-- | test/simple/test-http-parser.js | 6 |
2 files changed, 51 insertions, 33 deletions
diff --git a/src/node_http_parser.cc b/src/node_http_parser.cc index 38251a1e7c..8d9000d9d7 100644 --- a/src/node_http_parser.cc +++ b/src/node_http_parser.cc @@ -103,27 +103,12 @@ static char* current_buffer_data; 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) \ static int name(http_parser* p_) { \ Parser* self = container_of(p_, Parser, parser_); \ return self->name##_(); \ } \ - int always_inline name##_() + int name##_() #define HTTP_DATA_CB(name) \ @@ -131,7 +116,7 @@ static size_t current_buffer_len; Parser* self = container_of(p_, Parser, parser_); \ 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> @@ -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() { if (on_heap_) { delete[] str_; @@ -237,7 +235,7 @@ public: HTTP_CB(on_message_begin) { - num_fields_ = num_values_ = -1; + num_fields_ = num_values_ = 0; url_.Reset(); return 0; } @@ -252,18 +250,20 @@ public: HTTP_DATA_CB(on_header_field) { if (num_fields_ == num_values_) { // 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(); - num_fields_ = 0; - num_values_ = -1; + num_fields_ = 1; + num_values_ = 0; } - fields_[num_fields_].Reset(); + fields_[num_fields_ - 1].Reset(); } assert(num_fields_ < (int)ARRAY_SIZE(fields_)); assert(num_fields_ == num_values_ + 1); - fields_[num_fields_].Update(at, length); + fields_[num_fields_ - 1].Update(at, length); return 0; } @@ -272,13 +272,14 @@ public: HTTP_DATA_CB(on_header_value) { if (num_values_ != num_fields_) { // 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_ == num_fields_); - values_[num_values_].Update(at, length); + values_[num_values_ - 1].Update(at, length); return 0; } @@ -302,7 +303,7 @@ public: if (parser_.type == HTTP_REQUEST) message_info->Set(url_sym, url_.ToString()); } - num_fields_ = num_values_ = -1; + num_fields_ = num_values_ = 0; // METHOD if (parser_.type == HTTP_REQUEST) { @@ -364,7 +365,7 @@ public: HTTP_CB(on_message_complete) { HandleScope scope; - if (num_fields_ != -1) + if (num_fields_) Flush(); // Flush trailing HTTP headers. 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); static Handle<Value> Execute(const Arguments& args) { HandleScope scope; @@ -447,6 +461,8 @@ public: size_t nparsed = http_parser_execute(&parser->parser_, &settings, buffer_data + off, len); + parser->Save(); + // Unassign the 'buffer_' variable assert(current_buffer); current_buffer = NULL; @@ -515,9 +531,9 @@ private: Local<Array> CreateHeaders() { // num_values_ is either -1 or the entry # of the last 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 + 1, values_[i].ToString()); } @@ -553,8 +569,8 @@ private: void Init(enum http_parser_type type) { http_parser_init(&parser_, type); url_.Reset(); - num_fields_ = -1; - num_values_ = -1; + num_fields_ = 0; + num_values_ = 0; have_flushed_ = false; got_exception_ = false; } diff --git a/test/simple/test-http-parser.js b/test/simple/test-http-parser.js index 086d53bf44..7dc15043c4 100644 --- a/test/simple/test-http-parser.js +++ b/test/simple/test-http-parser.js @@ -381,7 +381,7 @@ function expectBody(expected) { // (function() { var request = Buffer( - 'POST /it HTTP/1.1' + CRLF + + 'POST /helpme HTTP/1.1' + CRLF + 'Content-Type: text/plain' + CRLF + 'Transfer-Encoding: chunked' + CRLF + CRLF + @@ -403,7 +403,7 @@ function expectBody(expected) { parser.onHeadersComplete = mustCall(function(info) { 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.versionMinor, 1); }); @@ -424,7 +424,9 @@ function expectBody(expected) { for (var i = 1; i < request.length - 1; ++i) { var a = request.slice(0, i); + console.error("request.slice(0, " + i + ") = ", JSON.stringify(a.toString())); var b = request.slice(i); + console.error("request.slice(" + i + ") = ", JSON.stringify(b.toString())); test(a, b); } })(); |