|
|
@ -68,6 +68,15 @@ static Persistent<String> upgrade_sym; |
|
|
|
|
|
|
|
static struct http_parser_settings settings; |
|
|
|
|
|
|
|
|
|
|
|
// This is a hack to get the current_buffer to the callbacks with the least
|
|
|
|
// amount of overhead. Nothing else will run while http_parser_execute()
|
|
|
|
// runs, therefore this pointer can be set and used for the execution.
|
|
|
|
static Local<Value>* current_buffer; |
|
|
|
static char* current_buffer_data; |
|
|
|
static size_t current_buffer_len; |
|
|
|
|
|
|
|
|
|
|
|
// Callback prototype for http_cb
|
|
|
|
#define DEFINE_HTTP_CB(name) \ |
|
|
|
static int name(http_parser *p) { \ |
|
|
@ -88,16 +97,16 @@ static struct http_parser_settings settings; |
|
|
|
#define DEFINE_HTTP_DATA_CB(name) \ |
|
|
|
static int name(http_parser *p, const char *at, size_t length) { \ |
|
|
|
Parser *parser = static_cast<Parser*>(p->data); \ |
|
|
|
assert(parser->buffer_); \ |
|
|
|
assert(current_buffer); \ |
|
|
|
Local<Value> cb_value = parser->handle_->Get(name##_sym); \ |
|
|
|
if (!cb_value->IsFunction()) return 0; \ |
|
|
|
Local<Function> cb = Local<Function>::Cast(cb_value); \ |
|
|
|
Local<Value> argv[3] = { Local<Value>::New(parser->buffer_->handle_) \ |
|
|
|
, Integer::New(at - parser->buffer_->data()) \ |
|
|
|
Local<Value> argv[3] = { *current_buffer \ |
|
|
|
, Integer::New(at - current_buffer_data) \ |
|
|
|
, Integer::New(length) \ |
|
|
|
}; \ |
|
|
|
Local<Value> ret = cb->Call(parser->handle_, 3, argv); \ |
|
|
|
assert(parser->buffer_); \ |
|
|
|
assert(current_buffer); \ |
|
|
|
if (ret.IsEmpty()) { \ |
|
|
|
parser->got_exception_ = true; \ |
|
|
|
return -1; \ |
|
|
@ -137,12 +146,10 @@ method_to_str(unsigned short m) { |
|
|
|
class Parser : public ObjectWrap { |
|
|
|
public: |
|
|
|
Parser(enum http_parser_type type) : ObjectWrap() { |
|
|
|
buffer_ = NULL; |
|
|
|
Init(type); |
|
|
|
} |
|
|
|
|
|
|
|
~Parser() { |
|
|
|
assert(buffer_ == NULL && "Destroying a parser while it's parsing"); |
|
|
|
} |
|
|
|
|
|
|
|
DEFINE_HTTP_CB(on_message_begin) |
|
|
@ -214,7 +221,7 @@ class Parser : public ObjectWrap { |
|
|
|
} |
|
|
|
|
|
|
|
parser->Wrap(args.This()); |
|
|
|
assert(!parser->buffer_); |
|
|
|
assert(!current_buffer); |
|
|
|
|
|
|
|
return args.This(); |
|
|
|
} |
|
|
@ -225,41 +232,50 @@ class Parser : public ObjectWrap { |
|
|
|
|
|
|
|
Parser *parser = ObjectWrap::Unwrap<Parser>(args.This()); |
|
|
|
|
|
|
|
assert(!parser->buffer_); |
|
|
|
if (parser->buffer_) { |
|
|
|
assert(!current_buffer); |
|
|
|
assert(!current_buffer_data); |
|
|
|
|
|
|
|
if (current_buffer) { |
|
|
|
return ThrowException(Exception::TypeError( |
|
|
|
String::New("Already parsing a buffer"))); |
|
|
|
} |
|
|
|
|
|
|
|
if (!Buffer::HasInstance(args[0])) { |
|
|
|
Local<Value> buffer_v = args[0]; |
|
|
|
|
|
|
|
if (!Buffer::HasInstance(buffer_v)) { |
|
|
|
return ThrowException(Exception::TypeError( |
|
|
|
String::New("Argument should be a buffer"))); |
|
|
|
} |
|
|
|
|
|
|
|
Buffer * buffer = ObjectWrap::Unwrap<Buffer>(args[0]->ToObject()); |
|
|
|
Local<Object> buffer_obj = buffer_v->ToObject(); |
|
|
|
char *buffer_data = Buffer::Data(buffer_obj); |
|
|
|
size_t buffer_len = Buffer::Length(buffer_obj); |
|
|
|
|
|
|
|
size_t off = args[1]->Int32Value(); |
|
|
|
if (off >= buffer->length()) { |
|
|
|
if (off >= buffer_len) { |
|
|
|
return ThrowException(Exception::Error( |
|
|
|
String::New("Offset is out of bounds"))); |
|
|
|
} |
|
|
|
|
|
|
|
size_t len = args[2]->Int32Value(); |
|
|
|
if (off+len > buffer->length()) { |
|
|
|
if (off+len > buffer_len) { |
|
|
|
return ThrowException(Exception::Error( |
|
|
|
String::New("Length is extends beyond buffer"))); |
|
|
|
} |
|
|
|
|
|
|
|
// Assign 'buffer_' while we parse. The callbacks will access that varible.
|
|
|
|
parser->buffer_ = buffer; |
|
|
|
current_buffer = &buffer_v; |
|
|
|
current_buffer_data = buffer_data; |
|
|
|
current_buffer_len = buffer_len; |
|
|
|
parser->got_exception_ = false; |
|
|
|
|
|
|
|
size_t nparsed = |
|
|
|
http_parser_execute(&parser->parser_, &settings, buffer->data()+off, len); |
|
|
|
http_parser_execute(&parser->parser_, &settings, buffer_data + off, len); |
|
|
|
|
|
|
|
// Unassign the 'buffer_' variable
|
|
|
|
assert(parser->buffer_); |
|
|
|
parser->buffer_ = NULL; |
|
|
|
assert(current_buffer); |
|
|
|
current_buffer = NULL; |
|
|
|
current_buffer_data = NULL; |
|
|
|
|
|
|
|
// If there was an exception in one of the callbacks
|
|
|
|
if (parser->got_exception_) return Local<Value>(); |
|
|
@ -282,7 +298,7 @@ class Parser : public ObjectWrap { |
|
|
|
|
|
|
|
Parser *parser = ObjectWrap::Unwrap<Parser>(args.This()); |
|
|
|
|
|
|
|
assert(!parser->buffer_); |
|
|
|
assert(!current_buffer); |
|
|
|
parser->got_exception_ = false; |
|
|
|
|
|
|
|
http_parser_execute(&(parser->parser_), &settings, NULL, 0); |
|
|
@ -313,13 +329,10 @@ class Parser : public ObjectWrap { |
|
|
|
private: |
|
|
|
|
|
|
|
void Init (enum http_parser_type type) { |
|
|
|
assert(buffer_ == NULL); // don't call this during Execute()
|
|
|
|
http_parser_init(&parser_, type); |
|
|
|
|
|
|
|
parser_.data = this; |
|
|
|
} |
|
|
|
|
|
|
|
Buffer * buffer_; // The buffer currently being parsed.
|
|
|
|
bool got_exception_; |
|
|
|
http_parser parser_; |
|
|
|
}; |
|
|
|