| // Copyright Joyent, Inc. and other Node contributors. |
| // |
| // Permission is hereby granted, free of charge, to any person obtaining a |
| // copy of this software and associated documentation files (the |
| // "Software"), to deal in the Software without restriction, including |
| // without limitation the rights to use, copy, modify, merge, publish, |
| // distribute, sublicense, and/or sell copies of the Software, and to permit |
| // persons to whom the Software is furnished to do so, subject to the |
| // following conditions: |
| // |
| // The above copyright notice and this permission notice shall be included |
| // in all copies or substantial portions of the Software. |
| // |
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
| // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN |
| // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, |
| // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
| // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE |
| // USE OR OTHER DEALINGS IN THE SOFTWARE. |
| |
| #include "node.h" |
| #include "node_buffer.h" |
| #include "node_internals.h" |
| |
| #include "async_wrap-inl.h" |
| #include "env-inl.h" |
| #include "http_parser.h" |
| #include "stream_base-inl.h" |
| #include "util-inl.h" |
| #include "v8.h" |
| |
| #include <stdlib.h> // free() |
| #include <string.h> // strdup() |
| |
| // This is a binding to http_parser (https://github.com/nodejs/http-parser) |
| // The goal is to decouple sockets from parsing for more javascript-level |
| // agility. A Buffer is read from a socket and passed to parser.execute(). |
| // The parser then issues callbacks with slices of the data |
| // parser.onMessageBegin |
| // parser.onPath |
| // parser.onBody |
| // ... |
| // No copying is performed when slicing the buffer, only small reference |
| // allocations. |
| |
| |
| namespace node { |
| namespace { |
| |
| using v8::Array; |
| using v8::Boolean; |
| using v8::Context; |
| using v8::EscapableHandleScope; |
| using v8::Exception; |
| using v8::Function; |
| using v8::FunctionCallbackInfo; |
| using v8::FunctionTemplate; |
| using v8::HandleScope; |
| using v8::Int32; |
| using v8::Integer; |
| using v8::Local; |
| using v8::MaybeLocal; |
| using v8::Object; |
| using v8::String; |
| using v8::Uint32; |
| using v8::Undefined; |
| using v8::Value; |
| |
| const uint32_t kOnHeaders = 0; |
| const uint32_t kOnHeadersComplete = 1; |
| const uint32_t kOnBody = 2; |
| const uint32_t kOnMessageComplete = 3; |
| const uint32_t kOnExecute = 4; |
| |
| |
| // helper class for the Parser |
| struct StringPtr { |
| StringPtr() { |
| on_heap_ = false; |
| Reset(); |
| } |
| |
| |
| ~StringPtr() { |
| Reset(); |
| } |
| |
| |
| // 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_; |
| on_heap_ = false; |
| } |
| |
| str_ = nullptr; |
| size_ = 0; |
| } |
| |
| |
| void Update(const char* str, size_t size) { |
| if (str_ == nullptr) { |
| str_ = str; |
| } else if (on_heap_ || str_ + size_ != str) { |
| // Non-consecutive input, make a copy on the heap. |
| // TODO(bnoordhuis) Use slab allocation, O(n) allocs is bad. |
| char* s = new char[size_ + size]; |
| memcpy(s, str_, size_); |
| memcpy(s + size_, str, size); |
| |
| if (on_heap_) |
| delete[] str_; |
| else |
| on_heap_ = true; |
| |
| str_ = s; |
| } |
| size_ += size; |
| } |
| |
| |
| Local<String> ToString(Environment* env) const { |
| if (str_) |
| return OneByteString(env->isolate(), str_, size_); |
| else |
| return String::Empty(env->isolate()); |
| } |
| |
| |
| const char* str_; |
| bool on_heap_; |
| size_t size_; |
| }; |
| |
| |
| class Parser : public AsyncWrap, public StreamListener { |
| public: |
| Parser(Environment* env, Local<Object> wrap, enum http_parser_type type) |
| : AsyncWrap(env, wrap, AsyncWrap::PROVIDER_HTTPPARSER), |
| current_buffer_len_(0), |
| current_buffer_data_(nullptr) { |
| Init(type); |
| } |
| |
| |
| void MemoryInfo(MemoryTracker* tracker) const override { |
| tracker->TrackField("current_buffer", current_buffer_); |
| } |
| |
| SET_MEMORY_INFO_NAME(Parser) |
| SET_SELF_SIZE(Parser) |
| |
| int on_message_begin() { |
| num_fields_ = num_values_ = 0; |
| url_.Reset(); |
| status_message_.Reset(); |
| return 0; |
| } |
| |
| |
| int on_url(const char* at, size_t length) { |
| url_.Update(at, length); |
| return 0; |
| } |
| |
| |
| int on_status(const char* at, size_t length) { |
| status_message_.Update(at, length); |
| return 0; |
| } |
| |
| |
| int on_header_field(const char* at, size_t length) { |
| if (num_fields_ == num_values_) { |
| // start of new field name |
| num_fields_++; |
| if (num_fields_ == arraysize(fields_)) { |
| // ran out of space - flush to javascript land |
| Flush(); |
| num_fields_ = 1; |
| num_values_ = 0; |
| } |
| fields_[num_fields_ - 1].Reset(); |
| } |
| |
| CHECK_LT(num_fields_, arraysize(fields_)); |
| CHECK_EQ(num_fields_, num_values_ + 1); |
| |
| fields_[num_fields_ - 1].Update(at, length); |
| |
| return 0; |
| } |
| |
| |
| int on_header_value(const char* at, size_t length) { |
| if (num_values_ != num_fields_) { |
| // start of new header value |
| num_values_++; |
| values_[num_values_ - 1].Reset(); |
| } |
| |
| CHECK_LT(num_values_, arraysize(values_)); |
| CHECK_EQ(num_values_, num_fields_); |
| |
| values_[num_values_ - 1].Update(at, length); |
| |
| return 0; |
| } |
| |
| |
| int on_headers_complete() { |
| // Arguments for the on-headers-complete javascript callback. This |
| // list needs to be kept in sync with the actual argument list for |
| // `parserOnHeadersComplete` in lib/_http_common.js. |
| enum on_headers_complete_arg_index { |
| A_VERSION_MAJOR = 0, |
| A_VERSION_MINOR, |
| A_HEADERS, |
| A_METHOD, |
| A_URL, |
| A_STATUS_CODE, |
| A_STATUS_MESSAGE, |
| A_UPGRADE, |
| A_SHOULD_KEEP_ALIVE, |
| A_MAX |
| }; |
| |
| Local<Value> argv[A_MAX]; |
| Local<Object> obj = object(); |
| Local<Value> cb = obj->Get(kOnHeadersComplete); |
| |
| if (!cb->IsFunction()) |
| return 0; |
| |
| Local<Value> undefined = Undefined(env()->isolate()); |
| for (size_t i = 0; i < arraysize(argv); i++) |
| argv[i] = undefined; |
| |
| if (have_flushed_) { |
| // Slow case, flush remaining headers. |
| Flush(); |
| } else { |
| // Fast case, pass headers and URL to JS land. |
| argv[A_HEADERS] = CreateHeaders(); |
| if (parser_.type == HTTP_REQUEST) |
| argv[A_URL] = url_.ToString(env()); |
| } |
| |
| num_fields_ = 0; |
| num_values_ = 0; |
| |
| // METHOD |
| if (parser_.type == HTTP_REQUEST) { |
| argv[A_METHOD] = |
| Uint32::NewFromUnsigned(env()->isolate(), parser_.method); |
| } |
| |
| // STATUS |
| if (parser_.type == HTTP_RESPONSE) { |
| argv[A_STATUS_CODE] = |
| Integer::New(env()->isolate(), parser_.status_code); |
| argv[A_STATUS_MESSAGE] = status_message_.ToString(env()); |
| } |
| |
| // VERSION |
| argv[A_VERSION_MAJOR] = Integer::New(env()->isolate(), parser_.http_major); |
| argv[A_VERSION_MINOR] = Integer::New(env()->isolate(), parser_.http_minor); |
| |
| argv[A_SHOULD_KEEP_ALIVE] = |
| Boolean::New(env()->isolate(), http_should_keep_alive(&parser_)); |
| |
| argv[A_UPGRADE] = Boolean::New(env()->isolate(), parser_.upgrade); |
| |
| Environment::AsyncCallbackScope callback_scope(env()); |
| |
| MaybeLocal<Value> head_response = |
| MakeCallback(cb.As<Function>(), arraysize(argv), argv); |
| |
| int64_t val; |
| |
| if (head_response.IsEmpty() || !head_response.ToLocalChecked() |
| ->IntegerValue(env()->context()) |
| .To(&val)) { |
| got_exception_ = true; |
| return -1; |
| } |
| |
| return val; |
| } |
| |
| |
| int on_body(const char* at, size_t length) { |
| EscapableHandleScope scope(env()->isolate()); |
| |
| Local<Object> obj = object(); |
| Local<Value> cb = obj->Get(kOnBody); |
| |
| if (!cb->IsFunction()) |
| return 0; |
| |
| // We came from consumed stream |
| if (current_buffer_.IsEmpty()) { |
| // Make sure Buffer will be in parent HandleScope |
| current_buffer_ = scope.Escape(Buffer::Copy( |
| env()->isolate(), |
| current_buffer_data_, |
| current_buffer_len_).ToLocalChecked()); |
| } |
| |
| Local<Value> argv[3] = { |
| current_buffer_, |
| Integer::NewFromUnsigned(env()->isolate(), at - current_buffer_data_), |
| Integer::NewFromUnsigned(env()->isolate(), length) |
| }; |
| |
| MaybeLocal<Value> r = MakeCallback(cb.As<Function>(), |
| arraysize(argv), |
| argv); |
| |
| if (r.IsEmpty()) { |
| got_exception_ = true; |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| |
| int on_message_complete() { |
| HandleScope scope(env()->isolate()); |
| |
| if (num_fields_) |
| Flush(); // Flush trailing HTTP headers. |
| |
| Local<Object> obj = object(); |
| Local<Value> cb = obj->Get(kOnMessageComplete); |
| |
| if (!cb->IsFunction()) |
| return 0; |
| |
| Environment::AsyncCallbackScope callback_scope(env()); |
| |
| MaybeLocal<Value> r = MakeCallback(cb.As<Function>(), 0, nullptr); |
| |
| if (r.IsEmpty()) { |
| got_exception_ = true; |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| |
| static void New(const FunctionCallbackInfo<Value>& args) { |
| Environment* env = Environment::GetCurrent(args); |
| CHECK(args[0]->IsInt32()); |
| http_parser_type type = |
| static_cast<http_parser_type>(args[0].As<Int32>()->Value()); |
| CHECK(type == HTTP_REQUEST || type == HTTP_RESPONSE); |
| new Parser(env, args.This(), type); |
| } |
| |
| |
| static void Close(const FunctionCallbackInfo<Value>& args) { |
| Parser* parser; |
| ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
| |
| delete parser; |
| } |
| |
| |
| static void Free(const FunctionCallbackInfo<Value>& args) { |
| Environment* env = Environment::GetCurrent(args); |
| Parser* parser; |
| ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
| |
| // Since the Parser destructor isn't going to run the destroy() callbacks |
| // it needs to be triggered manually. |
| parser->EmitTraceEventDestroy(); |
| parser->EmitDestroy(env, parser->get_async_id()); |
| } |
| |
| |
| void Save() { |
| url_.Save(); |
| status_message_.Save(); |
| |
| for (size_t i = 0; i < num_fields_; i++) { |
| fields_[i].Save(); |
| } |
| |
| for (size_t i = 0; i < num_values_; i++) { |
| values_[i].Save(); |
| } |
| } |
| |
| |
| // var bytesParsed = parser->execute(buffer); |
| static void Execute(const FunctionCallbackInfo<Value>& args) { |
| Parser* parser; |
| ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
| CHECK(parser->current_buffer_.IsEmpty()); |
| CHECK_EQ(parser->current_buffer_len_, 0); |
| CHECK_NULL(parser->current_buffer_data_); |
| CHECK_EQ(Buffer::HasInstance(args[0]), true); |
| |
| Local<Object> buffer_obj = args[0].As<Object>(); |
| char* buffer_data = Buffer::Data(buffer_obj); |
| size_t buffer_len = Buffer::Length(buffer_obj); |
| |
| // 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. |
| parser->current_buffer_ = buffer_obj; |
| |
| Local<Value> ret = parser->Execute(buffer_data, buffer_len); |
| |
| if (!ret.IsEmpty()) |
| args.GetReturnValue().Set(ret); |
| } |
| |
| |
| static void Finish(const FunctionCallbackInfo<Value>& args) { |
| Environment* env = Environment::GetCurrent(args); |
| |
| Parser* parser; |
| ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
| |
| CHECK(parser->current_buffer_.IsEmpty()); |
| parser->got_exception_ = false; |
| |
| int rv = http_parser_execute(&(parser->parser_), &settings, nullptr, 0); |
| |
| if (parser->got_exception_) |
| return; |
| |
| if (rv != 0) { |
| enum http_errno err = HTTP_PARSER_ERRNO(&parser->parser_); |
| |
| Local<Value> e = Exception::Error(env->parse_error_string()); |
| Local<Object> obj = e.As<Object>(); |
| obj->Set(env->bytes_parsed_string(), Integer::New(env->isolate(), 0)); |
| obj->Set(env->code_string(), |
| OneByteString(env->isolate(), http_errno_name(err))); |
| |
| args.GetReturnValue().Set(e); |
| } |
| } |
| |
| |
| static void Reinitialize(const FunctionCallbackInfo<Value>& args) { |
| Environment* env = Environment::GetCurrent(args); |
| |
| CHECK(args[0]->IsInt32()); |
| CHECK(args[1]->IsBoolean()); |
| bool isReused = args[1]->IsTrue(); |
| http_parser_type type = |
| static_cast<http_parser_type>(args[0].As<Int32>()->Value()); |
| |
| CHECK(type == HTTP_REQUEST || type == HTTP_RESPONSE); |
| Parser* parser; |
| ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
| // Should always be called from the same context. |
| CHECK_EQ(env, parser->env()); |
| // This parser has either just been created or it is being reused. |
| // We must only call AsyncReset for the latter case, because AsyncReset has |
| // already been called via the constructor for the former case. |
| if (isReused) { |
| parser->AsyncReset(); |
| } |
| parser->Init(type); |
| } |
| |
| |
| template <bool should_pause> |
| static void Pause(const FunctionCallbackInfo<Value>& args) { |
| Environment* env = Environment::GetCurrent(args); |
| Parser* parser; |
| ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
| // Should always be called from the same context. |
| CHECK_EQ(env, parser->env()); |
| http_parser_pause(&parser->parser_, should_pause); |
| } |
| |
| |
| static void Consume(const FunctionCallbackInfo<Value>& args) { |
| Parser* parser; |
| ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
| CHECK(args[0]->IsExternal()); |
| Local<External> stream_obj = args[0].As<External>(); |
| StreamBase* stream = static_cast<StreamBase*>(stream_obj->Value()); |
| CHECK_NOT_NULL(stream); |
| stream->PushStreamListener(parser); |
| } |
| |
| |
| static void Unconsume(const FunctionCallbackInfo<Value>& args) { |
| Parser* parser; |
| ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
| |
| // Already unconsumed |
| if (parser->stream_ == nullptr) |
| return; |
| |
| parser->stream_->RemoveStreamListener(parser); |
| } |
| |
| |
| static void GetCurrentBuffer(const FunctionCallbackInfo<Value>& args) { |
| Parser* parser; |
| ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); |
| |
| Local<Object> ret = Buffer::Copy( |
| parser->env(), |
| parser->current_buffer_data_, |
| parser->current_buffer_len_).ToLocalChecked(); |
| |
| args.GetReturnValue().Set(ret); |
| } |
| |
| protected: |
| static const size_t kAllocBufferSize = 64 * 1024; |
| |
| uv_buf_t OnStreamAlloc(size_t suggested_size) override { |
| // For most types of streams, OnStreamRead will be immediately after |
| // OnStreamAlloc, and will consume all data, so using a static buffer for |
| // reading is more efficient. For other streams, just use the default |
| // allocator, which uses Malloc(). |
| if (env()->http_parser_buffer_in_use()) |
| return StreamListener::OnStreamAlloc(suggested_size); |
| env()->set_http_parser_buffer_in_use(true); |
| |
| if (env()->http_parser_buffer() == nullptr) |
| env()->set_http_parser_buffer(new char[kAllocBufferSize]); |
| |
| return uv_buf_init(env()->http_parser_buffer(), kAllocBufferSize); |
| } |
| |
| |
| void OnStreamRead(ssize_t nread, const uv_buf_t& buf) override { |
| HandleScope scope(env()->isolate()); |
| // Once we’re done here, either indicate that the HTTP parser buffer |
| // is free for re-use, or free() the data if it didn’t come from there |
| // in the first place. |
| OnScopeLeave on_scope_leave([&]() { |
| if (buf.base == env()->http_parser_buffer()) |
| env()->set_http_parser_buffer_in_use(false); |
| else |
| free(buf.base); |
| }); |
| |
| if (nread < 0) { |
| PassReadErrorToPreviousListener(nread); |
| return; |
| } |
| |
| // Ignore, empty reads have special meaning in http parser |
| if (nread == 0) |
| return; |
| |
| current_buffer_.Clear(); |
| Local<Value> ret = Execute(buf.base, nread); |
| |
| // Exception |
| if (ret.IsEmpty()) |
| return; |
| |
| Local<Value> cb = |
| object()->Get(env()->context(), kOnExecute).ToLocalChecked(); |
| |
| if (!cb->IsFunction()) |
| return; |
| |
| // Hooks for GetCurrentBuffer |
| current_buffer_len_ = nread; |
| current_buffer_data_ = buf.base; |
| |
| MakeCallback(cb.As<Function>(), 1, &ret); |
| |
| current_buffer_len_ = 0; |
| current_buffer_data_ = nullptr; |
| } |
| |
| |
| Local<Value> Execute(char* data, size_t len) { |
| EscapableHandleScope scope(env()->isolate()); |
| |
| current_buffer_len_ = len; |
| current_buffer_data_ = data; |
| got_exception_ = false; |
| |
| size_t nparsed = |
| http_parser_execute(&parser_, &settings, data, len); |
| |
| Save(); |
| |
| // Unassign the 'buffer_' variable |
| current_buffer_.Clear(); |
| current_buffer_len_ = 0; |
| current_buffer_data_ = nullptr; |
| |
| // If there was an exception in one of the callbacks |
| if (got_exception_) |
| return scope.Escape(Local<Value>()); |
| |
| Local<Integer> nparsed_obj = Integer::New(env()->isolate(), nparsed); |
| // If there was a parse error in one of the callbacks |
| // TODO(bnoordhuis) What if there is an error on EOF? |
| if (!parser_.upgrade && nparsed != len) { |
| enum http_errno err = HTTP_PARSER_ERRNO(&parser_); |
| |
| Local<Value> e = Exception::Error(env()->parse_error_string()); |
| Local<Object> obj = e->ToObject(env()->isolate()->GetCurrentContext()) |
| .ToLocalChecked(); |
| obj->Set(env()->bytes_parsed_string(), nparsed_obj); |
| obj->Set(env()->code_string(), |
| OneByteString(env()->isolate(), http_errno_name(err))); |
| |
| return scope.Escape(e); |
| } |
| return scope.Escape(nparsed_obj); |
| } |
| |
| Local<Array> CreateHeaders() { |
| Local<Array> headers = Array::New(env()->isolate()); |
| Local<Function> fn = env()->push_values_to_array_function(); |
| Local<Value> argv[NODE_PUSH_VAL_TO_ARRAY_MAX * 2]; |
| size_t i = 0; |
| |
| do { |
| size_t j = 0; |
| while (i < num_values_ && j < arraysize(argv) / 2) { |
| argv[j * 2] = fields_[i].ToString(env()); |
| argv[j * 2 + 1] = values_[i].ToString(env()); |
| i++; |
| j++; |
| } |
| if (j > 0) { |
| fn->Call(env()->context(), headers, j * 2, argv).ToLocalChecked(); |
| } |
| } while (i < num_values_); |
| |
| return headers; |
| } |
| |
| |
| // spill headers and request path to JS land |
| void Flush() { |
| HandleScope scope(env()->isolate()); |
| |
| Local<Object> obj = object(); |
| Local<Value> cb = obj->Get(kOnHeaders); |
| |
| if (!cb->IsFunction()) |
| return; |
| |
| Local<Value> argv[2] = { |
| CreateHeaders(), |
| url_.ToString(env()) |
| }; |
| |
| MaybeLocal<Value> r = MakeCallback(cb.As<Function>(), |
| arraysize(argv), |
| argv); |
| |
| if (r.IsEmpty()) |
| got_exception_ = true; |
| |
| url_.Reset(); |
| have_flushed_ = true; |
| } |
| |
| |
| void Init(enum http_parser_type type) { |
| http_parser_init(&parser_, type); |
| url_.Reset(); |
| status_message_.Reset(); |
| num_fields_ = 0; |
| num_values_ = 0; |
| have_flushed_ = false; |
| got_exception_ = false; |
| } |
| |
| |
| http_parser parser_; |
| StringPtr fields_[32]; // header fields |
| StringPtr values_[32]; // header values |
| StringPtr url_; |
| StringPtr status_message_; |
| size_t num_fields_; |
| size_t num_values_; |
| bool have_flushed_; |
| bool got_exception_; |
| Local<Object> current_buffer_; |
| size_t current_buffer_len_; |
| char* current_buffer_data_; |
| |
| // These are helper functions for filling `http_parser_settings`, which turn |
| // a member function of Parser into a C-style HTTP parser callback. |
| template <typename Parser, Parser> struct Proxy; |
| template <typename Parser, typename ...Args, int (Parser::*Member)(Args...)> |
| struct Proxy<int (Parser::*)(Args...), Member> { |
| static int Raw(http_parser* p, Args ... args) { |
| Parser* parser = ContainerOf(&Parser::parser_, p); |
| return (parser->*Member)(std::forward<Args>(args)...); |
| } |
| }; |
| |
| typedef int (Parser::*Call)(); |
| typedef int (Parser::*DataCall)(const char* at, size_t length); |
| |
| static const struct http_parser_settings settings; |
| }; |
| |
| const struct http_parser_settings Parser::settings = { |
| Proxy<Call, &Parser::on_message_begin>::Raw, |
| Proxy<DataCall, &Parser::on_url>::Raw, |
| Proxy<DataCall, &Parser::on_status>::Raw, |
| Proxy<DataCall, &Parser::on_header_field>::Raw, |
| Proxy<DataCall, &Parser::on_header_value>::Raw, |
| Proxy<Call, &Parser::on_headers_complete>::Raw, |
| Proxy<DataCall, &Parser::on_body>::Raw, |
| Proxy<Call, &Parser::on_message_complete>::Raw, |
| nullptr, // on_chunk_header |
| nullptr // on_chunk_complete |
| }; |
| |
| |
| void Initialize(Local<Object> target, |
| Local<Value> unused, |
| Local<Context> context, |
| void* priv) { |
| Environment* env = Environment::GetCurrent(context); |
| Local<FunctionTemplate> t = env->NewFunctionTemplate(Parser::New); |
| t->InstanceTemplate()->SetInternalFieldCount(1); |
| t->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "HTTPParser")); |
| |
| t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "REQUEST"), |
| Integer::New(env->isolate(), HTTP_REQUEST)); |
| t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "RESPONSE"), |
| Integer::New(env->isolate(), HTTP_RESPONSE)); |
| t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kOnHeaders"), |
| Integer::NewFromUnsigned(env->isolate(), kOnHeaders)); |
| t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kOnHeadersComplete"), |
| Integer::NewFromUnsigned(env->isolate(), kOnHeadersComplete)); |
| t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kOnBody"), |
| Integer::NewFromUnsigned(env->isolate(), kOnBody)); |
| t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kOnMessageComplete"), |
| Integer::NewFromUnsigned(env->isolate(), kOnMessageComplete)); |
| t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kOnExecute"), |
| Integer::NewFromUnsigned(env->isolate(), kOnExecute)); |
| |
| Local<Array> methods = Array::New(env->isolate()); |
| #define V(num, name, string) \ |
| methods->Set(num, FIXED_ONE_BYTE_STRING(env->isolate(), #string)); |
| HTTP_METHOD_MAP(V) |
| #undef V |
| target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "methods"), methods); |
| |
| t->Inherit(AsyncWrap::GetConstructorTemplate(env)); |
| env->SetProtoMethod(t, "close", Parser::Close); |
| env->SetProtoMethod(t, "free", Parser::Free); |
| env->SetProtoMethod(t, "execute", Parser::Execute); |
| env->SetProtoMethod(t, "finish", Parser::Finish); |
| env->SetProtoMethod(t, "reinitialize", Parser::Reinitialize); |
| env->SetProtoMethod(t, "pause", Parser::Pause<true>); |
| env->SetProtoMethod(t, "resume", Parser::Pause<false>); |
| env->SetProtoMethod(t, "consume", Parser::Consume); |
| env->SetProtoMethod(t, "unconsume", Parser::Unconsume); |
| env->SetProtoMethod(t, "getCurrentBuffer", Parser::GetCurrentBuffer); |
| |
| target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "HTTPParser"), |
| t->GetFunction(env->context()).ToLocalChecked()); |
| } |
| |
| } // anonymous namespace |
| } // namespace node |
| |
| NODE_MODULE_CONTEXT_AWARE_INTERNAL(http_parser, node::Initialize) |