From 2a9da69e2dd6c921dd47c1f3f3e102d29de4562e Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Tue, 10 Feb 2015 15:10:49 +0300 Subject: [PATCH 01/41] stream_wrap: introduce StreamBase base class --- node.gyp | 2 + src/pipe_wrap.cc | 23 +------ src/stream_base.cc | 93 +++++++++++++++++++++++++ src/stream_base.h | 48 +++++++++++++ src/stream_wrap.cc | 168 +++++++++++++++++++-------------------------- src/stream_wrap.h | 31 +++++---- src/tcp_wrap.cc | 22 +----- src/tty_wrap.cc | 20 +----- 8 files changed, 234 insertions(+), 173 deletions(-) create mode 100644 src/stream_base.cc create mode 100644 src/stream_base.h diff --git a/node.gyp b/node.gyp index 01a67a08c86bba..996121ee45cfe7 100644 --- a/node.gyp +++ b/node.gyp @@ -115,6 +115,7 @@ 'src/smalloc.cc', 'src/spawn_sync.cc', 'src/string_bytes.cc', + 'src/stream_base.cc', 'src/stream_wrap.cc', 'src/tcp_wrap.cc', 'src/timer_wrap.cc', @@ -151,6 +152,7 @@ 'src/req-wrap.h', 'src/req-wrap-inl.h', 'src/string_bytes.h', + 'src/stream_base.h', 'src/stream_wrap.h', 'src/tree.h', 'src/util.h', diff --git a/src/pipe_wrap.cc b/src/pipe_wrap.cc index 55d5f84ff49858..0f7c02fa75f054 100644 --- a/src/pipe_wrap.cc +++ b/src/pipe_wrap.cc @@ -75,32 +75,13 @@ void PipeWrap::Initialize(Handle target, Local t = env->NewFunctionTemplate(New); t->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "Pipe")); - t->InstanceTemplate()->SetInternalFieldCount(1); - - enum PropertyAttribute attributes = - static_cast(v8::ReadOnly | v8::DontDelete); - t->InstanceTemplate()->SetAccessor(env->fd_string(), - StreamWrap::GetFD, - nullptr, - Handle(), - v8::DEFAULT, - attributes); + t->InstanceTemplate()->SetInternalFieldCount(2); env->SetProtoMethod(t, "close", HandleWrap::Close); env->SetProtoMethod(t, "unref", HandleWrap::Unref); env->SetProtoMethod(t, "ref", HandleWrap::Ref); - env->SetProtoMethod(t, "setBlocking", StreamWrap::SetBlocking); - - env->SetProtoMethod(t, "readStart", StreamWrap::ReadStart); - env->SetProtoMethod(t, "readStop", StreamWrap::ReadStop); - env->SetProtoMethod(t, "shutdown", StreamWrap::Shutdown); - - env->SetProtoMethod(t, "writeBuffer", StreamWrap::WriteBuffer); - env->SetProtoMethod(t, "writeAsciiString", StreamWrap::WriteAsciiString); - env->SetProtoMethod(t, "writeUtf8String", StreamWrap::WriteUtf8String); - env->SetProtoMethod(t, "writeUcs2String", StreamWrap::WriteUcs2String); - env->SetProtoMethod(t, "writeBinaryString", StreamWrap::WriteBinaryString); + StreamWrap::AddMethods(env, t); env->SetProtoMethod(t, "bind", Bind); env->SetProtoMethod(t, "listen", Listen); diff --git a/src/stream_base.cc b/src/stream_base.cc new file mode 100644 index 00000000000000..072b107060fb41 --- /dev/null +++ b/src/stream_base.cc @@ -0,0 +1,93 @@ +#include "stream_base.h" + +#include "env.h" +#include "env-inl.h" +#include "util.h" +#include "util-inl.h" +#include "v8.h" + +namespace node { + +using v8::FunctionCallbackInfo; +using v8::FunctionTemplate; +using v8::Handle; +using v8::HandleScope; +using v8::Local; +using v8::Object; +using v8::PropertyAttribute; +using v8::PropertyCallbackInfo; +using v8::String; +using v8::Value; + + +StreamBase::StreamBase(Environment* env, Local object) { + CHECK_EQ(false, object.IsEmpty()); + CHECK_GT(object->InternalFieldCount(), 1); + object->SetAlignedPointerInInternalField(1, this); +} + + +void StreamBase::AddMethods(Environment* env, Handle t) { + HandleScope scope(env->isolate()); + + enum PropertyAttribute attributes = + static_cast(v8::ReadOnly | v8::DontDelete); + t->InstanceTemplate()->SetAccessor(env->fd_string(), + GetFD, + nullptr, + Handle(), + v8::DEFAULT, + attributes); + + env->SetProtoMethod(t, "readStart", JSMethod<&StreamBase::ReadStart>); + env->SetProtoMethod(t, "readStop", JSMethod<&StreamBase::ReadStop>); + env->SetProtoMethod(t, "shutdown", JSMethod<&StreamBase::Shutdown>); + + env->SetProtoMethod(t, "writev", JSMethod<&StreamBase::Writev>); + env->SetProtoMethod(t, "writeBuffer", JSMethod<&StreamBase::WriteBuffer>); + env->SetProtoMethod(t, + "writeAsciiString", + JSMethod<&StreamBase::WriteAsciiString>); + env->SetProtoMethod(t, + "writeUtf8String", + JSMethod<&StreamBase::WriteUtf8String>); + env->SetProtoMethod(t, + "writeUcs2String", + JSMethod<&StreamBase::WriteUcs2String>); + env->SetProtoMethod(t, + "writeBinaryString", + JSMethod<&StreamBase::WriteBinaryString>); + env->SetProtoMethod(t, "setBlocking", JSMethod<&StreamBase::SetBlocking>); +} + + +inline StreamBase* Unwrap(Local object) { + CHECK_EQ(false, object.IsEmpty()); + CHECK_GT(object->InternalFieldCount(), 1); + void* pointer = object->GetAlignedPointerFromInternalField(1); + return static_cast(pointer); +} + + +void StreamBase::GetFD(Local, const PropertyCallbackInfo& args) { + HandleScope scope(args.GetIsolate()); + StreamBase* wrap = Unwrap(args.Holder()); + if (!wrap->IsAlive()) + return args.GetReturnValue().Set(UV_EINVAL); + + args.GetReturnValue().Set(wrap->GetFD()); +} + + +template & args)> +void StreamBase::JSMethod(const FunctionCallbackInfo& args) { + HandleScope scope(args.GetIsolate()); + StreamBase* wrap = Unwrap(args.Holder()); + if (!wrap->IsAlive()) + return args.GetReturnValue().Set(UV_EINVAL); + + args.GetReturnValue().Set((wrap->*Method)(args)); +} + + +} // namespace node diff --git a/src/stream_base.h b/src/stream_base.h new file mode 100644 index 00000000000000..5a66b4836be64d --- /dev/null +++ b/src/stream_base.h @@ -0,0 +1,48 @@ +#ifndef SRC_STREAM_BASE_H_ +#define SRC_STREAM_BASE_H_ + +#include "v8.h" + +namespace node { + +// Forward declarations +class Environment; + +class StreamBase { + public: + static void AddMethods(Environment* env, + v8::Handle target); + + virtual bool IsAlive() = 0; + virtual int GetFD() = 0; + + virtual int ReadStart(const v8::FunctionCallbackInfo& args) = 0; + virtual int ReadStop(const v8::FunctionCallbackInfo& args) = 0; + virtual int Shutdown(const v8::FunctionCallbackInfo& args) = 0; + virtual int Writev(const v8::FunctionCallbackInfo& args) = 0; + virtual int WriteBuffer(const v8::FunctionCallbackInfo& args) = 0; + virtual int WriteAsciiString(const v8::FunctionCallbackInfo& args) + = 0; + virtual int WriteUtf8String(const v8::FunctionCallbackInfo& args) + = 0; + virtual int WriteUcs2String(const v8::FunctionCallbackInfo& args) + = 0; + virtual int WriteBinaryString(const v8::FunctionCallbackInfo& args) + = 0; + virtual int SetBlocking(const v8::FunctionCallbackInfo& args) = 0; + + protected: + StreamBase(Environment* env, v8::Local object); + virtual ~StreamBase() = default; + + static void GetFD(v8::Local, + const v8::PropertyCallbackInfo&); + + template & args)> + static void JSMethod(const v8::FunctionCallbackInfo& args); +}; + +} // namespace node + +#endif // SRC_STREAM_BASE_H_ diff --git a/src/stream_wrap.cc b/src/stream_wrap.cc index a9f89e47bb9813..b869171739dbe9 100644 --- a/src/stream_wrap.cc +++ b/src/stream_wrap.cc @@ -38,8 +38,8 @@ using v8::Value; void StreamWrap::Initialize(Handle target, - Handle unused, - Handle context) { + Handle unused, + Handle context) { Environment* env = Environment::GetCurrent(context); Local sw = @@ -68,6 +68,7 @@ StreamWrap::StreamWrap(Environment* env, reinterpret_cast(stream), provider, parent), + StreamBase(env, object), stream_(stream), default_callbacks_(this), callbacks_(&default_callbacks_), @@ -75,16 +76,18 @@ StreamWrap::StreamWrap(Environment* env, } -void StreamWrap::GetFD(Local, const PropertyCallbackInfo& args) { -#if !defined(_WIN32) - HandleScope scope(args.GetIsolate()); - StreamWrap* wrap = Unwrap(args.Holder()); +int StreamWrap::GetFD() { int fd = -1; - if (wrap != nullptr && wrap->stream() != nullptr) { - fd = wrap->stream()->io_watcher.fd; - } - args.GetReturnValue().Set(fd); +#if !defined(_WIN32) + if (stream() != nullptr) + fd = stream()->io_watcher.fd; #endif + return fd; +} + + +bool StreamWrap::IsAlive() { + return HandleWrap::IsAlive(this); } @@ -96,22 +99,13 @@ void StreamWrap::UpdateWriteQueueSize() { } -void StreamWrap::ReadStart(const FunctionCallbackInfo& args) { - StreamWrap* wrap = Unwrap(args.Holder()); - if (!IsAlive(wrap)) - return args.GetReturnValue().Set(UV_EINVAL); - - int err = uv_read_start(wrap->stream(), OnAlloc, OnRead); - args.GetReturnValue().Set(err); +int StreamWrap::ReadStart(const FunctionCallbackInfo& args) { + return uv_read_start(stream(), OnAlloc, OnRead); } -void StreamWrap::ReadStop(const FunctionCallbackInfo& args) { - StreamWrap* wrap = Unwrap(args.Holder()); - if (!IsAlive(wrap)) - return args.GetReturnValue().Set(UV_EINVAL); - int err = uv_read_stop(wrap->stream()); - args.GetReturnValue().Set(err); +int StreamWrap::ReadStop(const FunctionCallbackInfo& args) { + return uv_read_stop(stream()); } @@ -194,13 +188,9 @@ size_t StreamWrap::WriteBuffer(Handle val, uv_buf_t* buf) { } -void StreamWrap::WriteBuffer(const FunctionCallbackInfo& args) { +int StreamWrap::WriteBuffer(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - StreamWrap* wrap = Unwrap(args.Holder()); - if (!IsAlive(wrap)) - return args.GetReturnValue().Set(UV_EINVAL); - CHECK(args[0]->IsObject()); CHECK(Buffer::HasInstance(args[1])); @@ -217,7 +207,7 @@ void StreamWrap::WriteBuffer(const FunctionCallbackInfo& args) { // Try writing immediately without allocation uv_buf_t* bufs = &buf; size_t count = 1; - int err = wrap->callbacks()->TryWrite(&bufs, &count); + int err = callbacks()->TryWrite(&bufs, &count); if (err != 0) goto done; if (count == 0) @@ -226,13 +216,13 @@ void StreamWrap::WriteBuffer(const FunctionCallbackInfo& args) { // Allocate, or write rest storage = new char[sizeof(WriteWrap)]; - req_wrap = new(storage) WriteWrap(env, req_wrap_obj, wrap); + req_wrap = new(storage) WriteWrap(env, req_wrap_obj, this); - err = wrap->callbacks()->DoWrite(req_wrap, - bufs, - count, - nullptr, - StreamWrap::AfterWrite); + err = callbacks()->DoWrite(req_wrap, + bufs, + count, + nullptr, + StreamWrap::AfterWrite); req_wrap->Dispatched(); req_wrap_obj->Set(env->async(), True(env->isolate())); @@ -242,26 +232,22 @@ void StreamWrap::WriteBuffer(const FunctionCallbackInfo& args) { } done: - const char* msg = wrap->callbacks()->Error(); + const char* msg = callbacks()->Error(); if (msg != nullptr) { req_wrap_obj->Set(env->error_string(), OneByteString(env->isolate(), msg)); - wrap->callbacks()->ClearError(); + callbacks()->ClearError(); } req_wrap_obj->Set(env->bytes_string(), Integer::NewFromUnsigned(env->isolate(), length)); - args.GetReturnValue().Set(err); + return err; } template -void StreamWrap::WriteStringImpl(const FunctionCallbackInfo& args) { +int StreamWrap::WriteStringImpl(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); int err; - StreamWrap* wrap = Unwrap(args.Holder()); - if (!IsAlive(wrap)) - return args.GetReturnValue().Set(UV_EINVAL); - CHECK(args[0]->IsObject()); CHECK(args[1]->IsString()); @@ -277,10 +263,8 @@ void StreamWrap::WriteStringImpl(const FunctionCallbackInfo& args) { else storage_size = StringBytes::StorageSize(env->isolate(), string, encoding); - if (storage_size > INT_MAX) { - args.GetReturnValue().Set(UV_ENOBUFS); - return; - } + if (storage_size > INT_MAX) + return UV_ENOBUFS; // Try writing immediately if write size isn't too big char* storage; @@ -291,7 +275,7 @@ void StreamWrap::WriteStringImpl(const FunctionCallbackInfo& args) { uv_buf_t buf; bool try_write = storage_size + 15 <= sizeof(stack_storage) && - (!wrap->is_named_pipe_ipc() || !args[2]->IsObject()); + (!is_named_pipe_ipc() || !args[2]->IsObject()); if (try_write) { data_size = StringBytes::Write(env->isolate(), stack_storage, @@ -302,7 +286,7 @@ void StreamWrap::WriteStringImpl(const FunctionCallbackInfo& args) { uv_buf_t* bufs = &buf; size_t count = 1; - err = wrap->callbacks()->TryWrite(&bufs, &count); + err = callbacks()->TryWrite(&bufs, &count); // Failure if (err != 0) @@ -317,7 +301,7 @@ void StreamWrap::WriteStringImpl(const FunctionCallbackInfo& args) { } storage = new char[sizeof(WriteWrap) + storage_size + 15]; - req_wrap = new(storage) WriteWrap(env, req_wrap_obj, wrap); + req_wrap = new(storage) WriteWrap(env, req_wrap_obj, this); data = reinterpret_cast(ROUND_UP( reinterpret_cast(storage) + sizeof(WriteWrap), 16)); @@ -339,12 +323,12 @@ void StreamWrap::WriteStringImpl(const FunctionCallbackInfo& args) { buf = uv_buf_init(data, data_size); - if (!wrap->is_named_pipe_ipc()) { - err = wrap->callbacks()->DoWrite(req_wrap, - &buf, - 1, - nullptr, - StreamWrap::AfterWrite); + if (!is_named_pipe_ipc()) { + err = callbacks()->DoWrite(req_wrap, + &buf, + 1, + nullptr, + StreamWrap::AfterWrite); } else { uv_handle_t* send_handle = nullptr; @@ -358,7 +342,7 @@ void StreamWrap::WriteStringImpl(const FunctionCallbackInfo& args) { req_wrap->object()->Set(env->handle_string(), send_handle_obj); } - err = wrap->callbacks()->DoWrite( + err = callbacks()->DoWrite( req_wrap, &buf, 1, @@ -375,24 +359,20 @@ void StreamWrap::WriteStringImpl(const FunctionCallbackInfo& args) { } done: - const char* msg = wrap->callbacks()->Error(); + const char* msg = callbacks()->Error(); if (msg != nullptr) { req_wrap_obj->Set(env->error_string(), OneByteString(env->isolate(), msg)); - wrap->callbacks()->ClearError(); + callbacks()->ClearError(); } req_wrap_obj->Set(env->bytes_string(), Integer::NewFromUnsigned(env->isolate(), data_size)); - args.GetReturnValue().Set(err); + return err; } -void StreamWrap::Writev(const FunctionCallbackInfo& args) { +int StreamWrap::Writev(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - StreamWrap* wrap = Unwrap(args.Holder()); - if (!IsAlive(wrap)) - return args.GetReturnValue().Set(UV_EINVAL); - CHECK(args[0]->IsObject()); CHECK(args[1]->IsArray()); @@ -425,10 +405,8 @@ void StreamWrap::Writev(const FunctionCallbackInfo& args) { storage_size += chunk_size + 15; } - if (storage_size > INT_MAX) { - args.GetReturnValue().Set(UV_ENOBUFS); - return; - } + if (storage_size > INT_MAX) + return UV_ENOBUFS; if (ARRAY_SIZE(bufs_) < count) bufs = new uv_buf_t[count]; @@ -436,7 +414,7 @@ void StreamWrap::Writev(const FunctionCallbackInfo& args) { storage_size += sizeof(WriteWrap); char* storage = new char[storage_size]; WriteWrap* req_wrap = - new(storage) WriteWrap(env, req_wrap_obj, wrap); + new(storage) WriteWrap(env, req_wrap_obj, this); uint32_t bytes = 0; size_t offset = sizeof(WriteWrap); @@ -471,11 +449,11 @@ void StreamWrap::Writev(const FunctionCallbackInfo& args) { bytes += str_size; } - int err = wrap->callbacks()->DoWrite(req_wrap, - bufs, - count, - nullptr, - StreamWrap::AfterWrite); + int err = callbacks()->DoWrite(req_wrap, + bufs, + count, + nullptr, + StreamWrap::AfterWrite); // Deallocate space if (bufs != bufs_) @@ -485,10 +463,10 @@ void StreamWrap::Writev(const FunctionCallbackInfo& args) { req_wrap->object()->Set(env->async(), True(env->isolate())); req_wrap->object()->Set(env->bytes_string(), Number::New(env->isolate(), bytes)); - const char* msg = wrap->callbacks()->Error(); + const char* msg = callbacks()->Error(); if (msg != nullptr) { req_wrap_obj->Set(env->error_string(), OneByteString(env->isolate(), msg)); - wrap->callbacks()->ClearError(); + callbacks()->ClearError(); } if (err) { @@ -496,35 +474,31 @@ void StreamWrap::Writev(const FunctionCallbackInfo& args) { delete[] storage; } - args.GetReturnValue().Set(err); + return err; } -void StreamWrap::WriteAsciiString(const FunctionCallbackInfo& args) { - WriteStringImpl(args); +int StreamWrap::WriteAsciiString(const FunctionCallbackInfo& args) { + return WriteStringImpl(args); } -void StreamWrap::WriteUtf8String(const FunctionCallbackInfo& args) { - WriteStringImpl(args); +int StreamWrap::WriteUtf8String(const FunctionCallbackInfo& args) { + return WriteStringImpl(args); } -void StreamWrap::WriteUcs2String(const FunctionCallbackInfo& args) { - WriteStringImpl(args); +int StreamWrap::WriteUcs2String(const FunctionCallbackInfo& args) { + return WriteStringImpl(args); } -void StreamWrap::WriteBinaryString(const FunctionCallbackInfo& args) { - WriteStringImpl(args); +int StreamWrap::WriteBinaryString(const FunctionCallbackInfo& args) { + return WriteStringImpl(args); } -void StreamWrap::SetBlocking(const FunctionCallbackInfo& args) { - StreamWrap* wrap = Unwrap(args.Holder()); - if (!IsAlive(wrap)) - return args.GetReturnValue().Set(UV_EINVAL); +int StreamWrap::SetBlocking(const FunctionCallbackInfo& args) { CHECK_GT(args.Length(), 0); - int err = uv_stream_set_blocking(wrap->stream(), args[0]->IsTrue()); - args.GetReturnValue().Set(err); + return uv_stream_set_blocking(stream(), args[0]->IsTrue()); } void StreamWrap::AfterWrite(uv_write_t* req, int status) { @@ -564,22 +538,18 @@ void StreamWrap::AfterWrite(uv_write_t* req, int status) { } -void StreamWrap::Shutdown(const FunctionCallbackInfo& args) { +int StreamWrap::Shutdown(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - StreamWrap* wrap = Unwrap(args.Holder()); - if (!IsAlive(wrap)) - return args.GetReturnValue().Set(UV_EINVAL); - CHECK(args[0]->IsObject()); Local req_wrap_obj = args[0].As(); ShutdownWrap* req_wrap = new ShutdownWrap(env, req_wrap_obj); - int err = wrap->callbacks()->DoShutdown(req_wrap, AfterShutdown); + int err = callbacks()->DoShutdown(req_wrap, AfterShutdown); req_wrap->Dispatched(); if (err) delete req_wrap; - args.GetReturnValue().Set(err); + return err; } diff --git a/src/stream_wrap.h b/src/stream_wrap.h index 5148228112eb1e..22d2f47492c148 100644 --- a/src/stream_wrap.h +++ b/src/stream_wrap.h @@ -5,6 +5,7 @@ #include "handle_wrap.h" #include "req-wrap.h" #include "req-wrap-inl.h" +#include "stream_base.h" #include "string_bytes.h" #include "v8.h" @@ -98,7 +99,7 @@ class StreamWrapCallbacks { StreamWrap* const wrap_; }; -class StreamWrap : public HandleWrap { +class StreamWrap : public HandleWrap, public StreamBase { public: static void Initialize(v8::Handle target, v8::Handle unused, @@ -112,23 +113,23 @@ class StreamWrap : public HandleWrap { delete old; } - static void GetFD(v8::Local, - const v8::PropertyCallbackInfo&); + int GetFD(); + bool IsAlive(); // JavaScript functions - static void ReadStart(const v8::FunctionCallbackInfo& args); - static void ReadStop(const v8::FunctionCallbackInfo& args); - static void Shutdown(const v8::FunctionCallbackInfo& args); - - static void Writev(const v8::FunctionCallbackInfo& args); - static void WriteBuffer(const v8::FunctionCallbackInfo& args); - static void WriteAsciiString(const v8::FunctionCallbackInfo& args); - static void WriteUtf8String(const v8::FunctionCallbackInfo& args); - static void WriteUcs2String(const v8::FunctionCallbackInfo& args); - static void WriteBinaryString( + int ReadStart(const v8::FunctionCallbackInfo& args); + int ReadStop(const v8::FunctionCallbackInfo& args); + int Shutdown(const v8::FunctionCallbackInfo& args); + + int Writev(const v8::FunctionCallbackInfo& args); + int WriteBuffer(const v8::FunctionCallbackInfo& args); + int WriteAsciiString(const v8::FunctionCallbackInfo& args); + int WriteUtf8String(const v8::FunctionCallbackInfo& args); + int WriteUcs2String(const v8::FunctionCallbackInfo& args); + int WriteBinaryString( const v8::FunctionCallbackInfo& args); - static void SetBlocking(const v8::FunctionCallbackInfo& args); + int SetBlocking(const v8::FunctionCallbackInfo& args); inline StreamWrapCallbacks* callbacks() const { return callbacks_; @@ -187,7 +188,7 @@ class StreamWrap : public HandleWrap { uv_handle_type pending); template - static void WriteStringImpl(const v8::FunctionCallbackInfo& args); + int WriteStringImpl(const v8::FunctionCallbackInfo& args); uv_stream_t* const stream_; StreamWrapCallbacks default_callbacks_; diff --git a/src/tcp_wrap.cc b/src/tcp_wrap.cc index 3f011422ef8837..fb411b1d4e6997 100644 --- a/src/tcp_wrap.cc +++ b/src/tcp_wrap.cc @@ -70,16 +70,7 @@ void TCPWrap::Initialize(Handle target, Local t = env->NewFunctionTemplate(New); t->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "TCP")); - t->InstanceTemplate()->SetInternalFieldCount(1); - - enum PropertyAttribute attributes = - static_cast(v8::ReadOnly | v8::DontDelete); - t->InstanceTemplate()->SetAccessor(env->fd_string(), - StreamWrap::GetFD, - nullptr, - Handle(), - v8::DEFAULT, - attributes); + t->InstanceTemplate()->SetInternalFieldCount(2); // Init properties t->InstanceTemplate()->Set(String::NewFromUtf8(env->isolate(), "reading"), @@ -98,16 +89,7 @@ void TCPWrap::Initialize(Handle target, env->SetProtoMethod(t, "ref", HandleWrap::Ref); env->SetProtoMethod(t, "unref", HandleWrap::Unref); - env->SetProtoMethod(t, "readStart", StreamWrap::ReadStart); - env->SetProtoMethod(t, "readStop", StreamWrap::ReadStop); - env->SetProtoMethod(t, "shutdown", StreamWrap::Shutdown); - - env->SetProtoMethod(t, "writeBuffer", StreamWrap::WriteBuffer); - env->SetProtoMethod(t, "writeAsciiString", StreamWrap::WriteAsciiString); - env->SetProtoMethod(t, "writeUtf8String", StreamWrap::WriteUtf8String); - env->SetProtoMethod(t, "writeUcs2String", StreamWrap::WriteUcs2String); - env->SetProtoMethod(t, "writeBinaryString", StreamWrap::WriteBinaryString); - env->SetProtoMethod(t, "writev", StreamWrap::Writev); + StreamWrap::AddMethods(env, t); env->SetProtoMethod(t, "open", Open); env->SetProtoMethod(t, "bind", Bind); diff --git a/src/tty_wrap.cc b/src/tty_wrap.cc index 08c50d911f7482..b5c594ffc222a4 100644 --- a/src/tty_wrap.cc +++ b/src/tty_wrap.cc @@ -34,28 +34,12 @@ void TTYWrap::Initialize(Handle target, Local t = env->NewFunctionTemplate(New); t->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "TTY")); - t->InstanceTemplate()->SetInternalFieldCount(1); - - enum PropertyAttribute attributes = - static_cast(v8::ReadOnly | v8::DontDelete); - t->InstanceTemplate()->SetAccessor(env->fd_string(), - StreamWrap::GetFD, - nullptr, - Handle(), - v8::DEFAULT, - attributes); + t->InstanceTemplate()->SetInternalFieldCount(2); env->SetProtoMethod(t, "close", HandleWrap::Close); env->SetProtoMethod(t, "unref", HandleWrap::Unref); - env->SetProtoMethod(t, "readStart", StreamWrap::ReadStart); - env->SetProtoMethod(t, "readStop", StreamWrap::ReadStop); - - env->SetProtoMethod(t, "writeBuffer", StreamWrap::WriteBuffer); - env->SetProtoMethod(t, "writeAsciiString", StreamWrap::WriteAsciiString); - env->SetProtoMethod(t, "writeUtf8String", StreamWrap::WriteUtf8String); - env->SetProtoMethod(t, "writeUcs2String", StreamWrap::WriteUcs2String); - env->SetProtoMethod(t, "writeBinaryString", StreamWrap::WriteBinaryString); + StreamWrap::AddMethods(env, t); env->SetProtoMethod(t, "getWindowSize", TTYWrap::GetWindowSize); env->SetProtoMethod(t, "setRawMode", SetRawMode); From 2e4ceb2eb4d9d86cc2c136e4ad815ec81ef5758f Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Tue, 10 Feb 2015 15:28:23 +0300 Subject: [PATCH 02/41] stream_base: do not require extra internal field --- src/pipe_wrap.cc | 4 ++-- src/stream_base.cc | 51 +++++++++++++++++++++++----------------------- src/stream_base.h | 5 ++++- src/tcp_wrap.cc | 4 ++-- src/tty_wrap.cc | 4 ++-- 5 files changed, 35 insertions(+), 33 deletions(-) diff --git a/src/pipe_wrap.cc b/src/pipe_wrap.cc index 0f7c02fa75f054..473fdef2f72cc3 100644 --- a/src/pipe_wrap.cc +++ b/src/pipe_wrap.cc @@ -75,13 +75,13 @@ void PipeWrap::Initialize(Handle target, Local t = env->NewFunctionTemplate(New); t->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "Pipe")); - t->InstanceTemplate()->SetInternalFieldCount(2); + t->InstanceTemplate()->SetInternalFieldCount(1); env->SetProtoMethod(t, "close", HandleWrap::Close); env->SetProtoMethod(t, "unref", HandleWrap::Unref); env->SetProtoMethod(t, "ref", HandleWrap::Ref); - StreamWrap::AddMethods(env, t); + StreamWrap::AddMethods(env, t); env->SetProtoMethod(t, "bind", Bind); env->SetProtoMethod(t, "listen", Listen); diff --git a/src/stream_base.cc b/src/stream_base.cc index 072b107060fb41..09fdd1db421cae 100644 --- a/src/stream_base.cc +++ b/src/stream_base.cc @@ -2,6 +2,7 @@ #include "env.h" #include "env-inl.h" +#include "stream_wrap.h" #include "util.h" #include "util-inl.h" #include "v8.h" @@ -19,59 +20,56 @@ using v8::PropertyCallbackInfo; using v8::String; using v8::Value; +template void StreamBase::AddMethods(Environment* env, + Handle t); + StreamBase::StreamBase(Environment* env, Local object) { - CHECK_EQ(false, object.IsEmpty()); - CHECK_GT(object->InternalFieldCount(), 1); - object->SetAlignedPointerInInternalField(1, this); } +template void StreamBase::AddMethods(Environment* env, Handle t) { HandleScope scope(env->isolate()); enum PropertyAttribute attributes = static_cast(v8::ReadOnly | v8::DontDelete); t->InstanceTemplate()->SetAccessor(env->fd_string(), - GetFD, + GetFD, nullptr, Handle(), v8::DEFAULT, attributes); - env->SetProtoMethod(t, "readStart", JSMethod<&StreamBase::ReadStart>); - env->SetProtoMethod(t, "readStop", JSMethod<&StreamBase::ReadStop>); - env->SetProtoMethod(t, "shutdown", JSMethod<&StreamBase::Shutdown>); - - env->SetProtoMethod(t, "writev", JSMethod<&StreamBase::Writev>); - env->SetProtoMethod(t, "writeBuffer", JSMethod<&StreamBase::WriteBuffer>); + env->SetProtoMethod(t, "readStart", JSMethod); + env->SetProtoMethod(t, "readStop", JSMethod); + env->SetProtoMethod(t, "shutdown", JSMethod); + env->SetProtoMethod(t, "writev", JSMethod); + env->SetProtoMethod(t, + "writeBuffer", + JSMethod); env->SetProtoMethod(t, "writeAsciiString", - JSMethod<&StreamBase::WriteAsciiString>); + JSMethod); env->SetProtoMethod(t, "writeUtf8String", - JSMethod<&StreamBase::WriteUtf8String>); + JSMethod); env->SetProtoMethod(t, "writeUcs2String", - JSMethod<&StreamBase::WriteUcs2String>); + JSMethod); env->SetProtoMethod(t, "writeBinaryString", - JSMethod<&StreamBase::WriteBinaryString>); - env->SetProtoMethod(t, "setBlocking", JSMethod<&StreamBase::SetBlocking>); -} - - -inline StreamBase* Unwrap(Local object) { - CHECK_EQ(false, object.IsEmpty()); - CHECK_GT(object->InternalFieldCount(), 1); - void* pointer = object->GetAlignedPointerFromInternalField(1); - return static_cast(pointer); + JSMethod); + env->SetProtoMethod(t, + "setBlocking", + JSMethod); } +template void StreamBase::GetFD(Local, const PropertyCallbackInfo& args) { HandleScope scope(args.GetIsolate()); - StreamBase* wrap = Unwrap(args.Holder()); + Base* wrap = Unwrap(args.Holder()); if (!wrap->IsAlive()) return args.GetReturnValue().Set(UV_EINVAL); @@ -79,10 +77,11 @@ void StreamBase::GetFD(Local, const PropertyCallbackInfo& args) { } -template & args)> +template & args)> void StreamBase::JSMethod(const FunctionCallbackInfo& args) { HandleScope scope(args.GetIsolate()); - StreamBase* wrap = Unwrap(args.Holder()); + Base* wrap = Unwrap(args.Holder()); if (!wrap->IsAlive()) return args.GetReturnValue().Set(UV_EINVAL); diff --git a/src/stream_base.h b/src/stream_base.h index 5a66b4836be64d..16f0d09587aa67 100644 --- a/src/stream_base.h +++ b/src/stream_base.h @@ -10,6 +10,7 @@ class Environment; class StreamBase { public: + template static void AddMethods(Environment* env, v8::Handle target); @@ -35,10 +36,12 @@ class StreamBase { StreamBase(Environment* env, v8::Local object); virtual ~StreamBase() = default; + template static void GetFD(v8::Local, const v8::PropertyCallbackInfo&); - template & args)> static void JSMethod(const v8::FunctionCallbackInfo& args); }; diff --git a/src/tcp_wrap.cc b/src/tcp_wrap.cc index fb411b1d4e6997..441a464257c21c 100644 --- a/src/tcp_wrap.cc +++ b/src/tcp_wrap.cc @@ -70,7 +70,7 @@ void TCPWrap::Initialize(Handle target, Local t = env->NewFunctionTemplate(New); t->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "TCP")); - t->InstanceTemplate()->SetInternalFieldCount(2); + t->InstanceTemplate()->SetInternalFieldCount(1); // Init properties t->InstanceTemplate()->Set(String::NewFromUtf8(env->isolate(), "reading"), @@ -89,7 +89,7 @@ void TCPWrap::Initialize(Handle target, env->SetProtoMethod(t, "ref", HandleWrap::Ref); env->SetProtoMethod(t, "unref", HandleWrap::Unref); - StreamWrap::AddMethods(env, t); + StreamWrap::AddMethods(env, t); env->SetProtoMethod(t, "open", Open); env->SetProtoMethod(t, "bind", Bind); diff --git a/src/tty_wrap.cc b/src/tty_wrap.cc index b5c594ffc222a4..2196003040dcc5 100644 --- a/src/tty_wrap.cc +++ b/src/tty_wrap.cc @@ -34,12 +34,12 @@ void TTYWrap::Initialize(Handle target, Local t = env->NewFunctionTemplate(New); t->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "TTY")); - t->InstanceTemplate()->SetInternalFieldCount(2); + t->InstanceTemplate()->SetInternalFieldCount(1); env->SetProtoMethod(t, "close", HandleWrap::Close); env->SetProtoMethod(t, "unref", HandleWrap::Unref); - StreamWrap::AddMethods(env, t); + StreamWrap::AddMethods(env, t); env->SetProtoMethod(t, "getWindowSize", TTYWrap::GetWindowSize); env->SetProtoMethod(t, "setRawMode", SetRawMode); From 100f9bdde8b0a28eb1fc42cbebffea788703589e Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Tue, 10 Feb 2015 18:27:11 +0300 Subject: [PATCH 03/41] stream_base: specialize interface methods --- src/stream_base.cc | 61 +++++++++++++++++++++++++-- src/stream_base.h | 36 +++++++++------- src/stream_wrap.cc | 101 ++++++++++++++++----------------------------- src/stream_wrap.h | 33 +++++++-------- 4 files changed, 131 insertions(+), 100 deletions(-) diff --git a/src/stream_base.cc b/src/stream_base.cc index 09fdd1db421cae..094162614920f4 100644 --- a/src/stream_base.cc +++ b/src/stream_base.cc @@ -1,5 +1,6 @@ #include "stream_base.h" +#include "node_buffer.h" #include "env.h" #include "env-inl.h" #include "stream_wrap.h" @@ -9,6 +10,7 @@ namespace node { +using v8::Array; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; using v8::Handle; @@ -50,16 +52,16 @@ void StreamBase::AddMethods(Environment* env, Handle t) { JSMethod); env->SetProtoMethod(t, "writeAsciiString", - JSMethod); + JSMethod >); env->SetProtoMethod(t, "writeUtf8String", - JSMethod); + JSMethod >); env->SetProtoMethod(t, "writeUcs2String", - JSMethod); + JSMethod >); env->SetProtoMethod(t, "writeBinaryString", - JSMethod); + JSMethod >); env->SetProtoMethod(t, "setBlocking", JSMethod); @@ -89,4 +91,55 @@ void StreamBase::JSMethod(const FunctionCallbackInfo& args) { } +int StreamBase::ReadStart(const v8::FunctionCallbackInfo& args) { + return ReadStart(); +} + + +int StreamBase::ReadStop(const v8::FunctionCallbackInfo& args) { + return ReadStop(); +} + + +int StreamBase::Shutdown(const v8::FunctionCallbackInfo& args) { + CHECK(args[0]->IsObject()); + return Shutdown(args[0].As()); +} + + +int StreamBase::Writev(const v8::FunctionCallbackInfo& args) { + CHECK(args[0]->IsObject()); + CHECK(args[1]->IsArray()); + return Writev(args[0].As(), args[1].As()); +} + + +int StreamBase::WriteBuffer(const v8::FunctionCallbackInfo& args) { + CHECK(args[0]->IsObject()); + CHECK(Buffer::HasInstance(args[1])); + return WriteBuffer(args[0].As(), + Buffer::Data(args[1]), + Buffer::Length(args[1])); +} + + +template +int StreamBase::WriteString(const v8::FunctionCallbackInfo& args) { + CHECK(args[0]->IsObject()); + CHECK(args[1]->IsString()); + Local handle; + if (args[2]->IsObject()) + handle = args[2].As(); + return WriteString(args[0].As(), + args[1].As(), + Encoding, + handle); +} + + +int StreamBase::SetBlocking(const v8::FunctionCallbackInfo& args) { + CHECK_GT(args.Length(), 0); + return SetBlocking(args[0]->IsTrue()); +} + } // namespace node diff --git a/src/stream_base.h b/src/stream_base.h index 16f0d09587aa67..fc5abc5a49711d 100644 --- a/src/stream_base.h +++ b/src/stream_base.h @@ -1,6 +1,7 @@ #ifndef SRC_STREAM_BASE_H_ #define SRC_STREAM_BASE_H_ +#include "node.h" #include "v8.h" namespace node { @@ -17,25 +18,32 @@ class StreamBase { virtual bool IsAlive() = 0; virtual int GetFD() = 0; - virtual int ReadStart(const v8::FunctionCallbackInfo& args) = 0; - virtual int ReadStop(const v8::FunctionCallbackInfo& args) = 0; - virtual int Shutdown(const v8::FunctionCallbackInfo& args) = 0; - virtual int Writev(const v8::FunctionCallbackInfo& args) = 0; - virtual int WriteBuffer(const v8::FunctionCallbackInfo& args) = 0; - virtual int WriteAsciiString(const v8::FunctionCallbackInfo& args) - = 0; - virtual int WriteUtf8String(const v8::FunctionCallbackInfo& args) - = 0; - virtual int WriteUcs2String(const v8::FunctionCallbackInfo& args) - = 0; - virtual int WriteBinaryString(const v8::FunctionCallbackInfo& args) - = 0; - virtual int SetBlocking(const v8::FunctionCallbackInfo& args) = 0; + virtual int ReadStart() = 0; + virtual int ReadStop() = 0; + virtual int Shutdown(v8::Local req) = 0; + virtual int Writev(v8::Local req, v8::Local bufs) = 0; + virtual int WriteBuffer(v8::Local req, + const char* buf, + size_t len) = 0; + virtual int WriteString(v8::Local req, + v8::Local str, + enum encoding enc, + v8::Local handle) = 0; + virtual int SetBlocking(bool enable) = 0; protected: StreamBase(Environment* env, v8::Local object); virtual ~StreamBase() = default; + int ReadStart(const v8::FunctionCallbackInfo& args); + int ReadStop(const v8::FunctionCallbackInfo& args); + int Shutdown(const v8::FunctionCallbackInfo& args); + int Writev(const v8::FunctionCallbackInfo& args); + int WriteBuffer(const v8::FunctionCallbackInfo& args); + template + int WriteString(const v8::FunctionCallbackInfo& args); + int SetBlocking(const v8::FunctionCallbackInfo& args); + template static void GetFD(v8::Local, const v8::PropertyCallbackInfo&); diff --git a/src/stream_wrap.cc b/src/stream_wrap.cc index b869171739dbe9..22762edc48187b 100644 --- a/src/stream_wrap.cc +++ b/src/stream_wrap.cc @@ -99,12 +99,12 @@ void StreamWrap::UpdateWriteQueueSize() { } -int StreamWrap::ReadStart(const FunctionCallbackInfo& args) { +int StreamWrap::ReadStart() { return uv_read_start(stream(), OnAlloc, OnRead); } -int StreamWrap::ReadStop(const FunctionCallbackInfo& args) { +int StreamWrap::ReadStop() { return uv_read_stop(stream()); } @@ -177,32 +177,16 @@ void StreamWrap::OnRead(uv_stream_t* handle, } -size_t StreamWrap::WriteBuffer(Handle val, uv_buf_t* buf) { - CHECK(Buffer::HasInstance(val)); - - // Simple non-writev case - buf->base = Buffer::Data(val); - buf->len = Buffer::Length(val); - - return buf->len; -} - - -int StreamWrap::WriteBuffer(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - - CHECK(args[0]->IsObject()); - CHECK(Buffer::HasInstance(args[1])); - - Local req_wrap_obj = args[0].As(); - Local buf_obj = args[1].As(); - - size_t length = Buffer::Length(buf_obj); +int StreamWrap::WriteBuffer(Local req_wrap_obj, + const char* data, + size_t length) { + Environment* env = this->env(); char* storage; WriteWrap* req_wrap; uv_buf_t buf; - WriteBuffer(buf_obj, &buf); + buf.base = const_cast(data); + buf.len = length; // Try writing immediately without allocation uv_buf_t* bufs = &buf; @@ -244,16 +228,12 @@ int StreamWrap::WriteBuffer(const FunctionCallbackInfo& args) { template -int StreamWrap::WriteStringImpl(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); +int StreamWrap::WriteStringImpl(Local req_wrap_obj, + Local string, + Local send_handle_obj) { + Environment* env = this->env(); int err; - CHECK(args[0]->IsObject()); - CHECK(args[1]->IsString()); - - Local req_wrap_obj = args[0].As(); - Local string = args[1].As(); - // Compute the size of the storage that the string will be flattened into. // For UTF8 strings that are very long, go ahead and take the hit for // computing their actual size, rather than tripling the storage. @@ -275,7 +255,7 @@ int StreamWrap::WriteStringImpl(const FunctionCallbackInfo& args) { uv_buf_t buf; bool try_write = storage_size + 15 <= sizeof(stack_storage) && - (!is_named_pipe_ipc() || !args[2]->IsObject()); + (!is_named_pipe_ipc() || send_handle_obj.IsEmpty()); if (try_write) { data_size = StringBytes::Write(env->isolate(), stack_storage, @@ -332,8 +312,7 @@ int StreamWrap::WriteStringImpl(const FunctionCallbackInfo& args) { } else { uv_handle_t* send_handle = nullptr; - if (args[2]->IsObject()) { - Local send_handle_obj = args[2].As(); + if (!send_handle_obj.IsEmpty()) { HandleWrap* wrap = Unwrap(send_handle_obj); send_handle = wrap->GetHandle(); // Reference StreamWrap instance to prevent it from being garbage @@ -370,14 +349,9 @@ int StreamWrap::WriteStringImpl(const FunctionCallbackInfo& args) { } -int StreamWrap::Writev(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); +int StreamWrap::Writev(Local req_wrap_obj, Local chunks) { + Environment* env = this->env(); - CHECK(args[0]->IsObject()); - CHECK(args[1]->IsArray()); - - Local req_wrap_obj = args[0].As(); - Local chunks = args[1].As(); size_t count = chunks->Length() >> 1; uv_buf_t bufs_[16]; @@ -478,28 +452,28 @@ int StreamWrap::Writev(const FunctionCallbackInfo& args) { } -int StreamWrap::WriteAsciiString(const FunctionCallbackInfo& args) { - return WriteStringImpl(args); -} - - -int StreamWrap::WriteUtf8String(const FunctionCallbackInfo& args) { - return WriteStringImpl(args); -} - - -int StreamWrap::WriteUcs2String(const FunctionCallbackInfo& args) { - return WriteStringImpl(args); +int StreamWrap::WriteString(Local req, + Local str, + enum encoding enc, + Local handle) { + switch (enc) { + case ASCII: + return WriteStringImpl(req, str, handle); + case UTF8: + return WriteStringImpl(req, str, handle); + case UCS2: + return WriteStringImpl(req, str, handle); + case BINARY: + return WriteStringImpl(req, str, handle); + default: + return UV_ENOSYS; + } } -int StreamWrap::WriteBinaryString(const FunctionCallbackInfo& args) { - return WriteStringImpl(args); +int StreamWrap::SetBlocking(bool enable) { + return uv_stream_set_blocking(stream(), enable); } -int StreamWrap::SetBlocking(const FunctionCallbackInfo& args) { - CHECK_GT(args.Length(), 0); - return uv_stream_set_blocking(stream(), args[0]->IsTrue()); -} void StreamWrap::AfterWrite(uv_write_t* req, int status) { WriteWrap* req_wrap = ContainerOf(&WriteWrap::req_, req); @@ -538,11 +512,8 @@ void StreamWrap::AfterWrite(uv_write_t* req, int status) { } -int StreamWrap::Shutdown(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - - CHECK(args[0]->IsObject()); - Local req_wrap_obj = args[0].As(); +int StreamWrap::Shutdown(Local req_wrap_obj) { + Environment* env = this->env(); ShutdownWrap* req_wrap = new ShutdownWrap(env, req_wrap_obj); int err = callbacks()->DoShutdown(req_wrap, AfterShutdown); diff --git a/src/stream_wrap.h b/src/stream_wrap.h index 22d2f47492c148..fd25e76cbc6947 100644 --- a/src/stream_wrap.h +++ b/src/stream_wrap.h @@ -117,19 +117,18 @@ class StreamWrap : public HandleWrap, public StreamBase { bool IsAlive(); // JavaScript functions - int ReadStart(const v8::FunctionCallbackInfo& args); - int ReadStop(const v8::FunctionCallbackInfo& args); - int Shutdown(const v8::FunctionCallbackInfo& args); - - int Writev(const v8::FunctionCallbackInfo& args); - int WriteBuffer(const v8::FunctionCallbackInfo& args); - int WriteAsciiString(const v8::FunctionCallbackInfo& args); - int WriteUtf8String(const v8::FunctionCallbackInfo& args); - int WriteUcs2String(const v8::FunctionCallbackInfo& args); - int WriteBinaryString( - const v8::FunctionCallbackInfo& args); - - int SetBlocking(const v8::FunctionCallbackInfo& args); + int ReadStart(); + int ReadStop(); + int Shutdown(v8::Local req); + int Writev(v8::Local req, v8::Local bufs); + int WriteBuffer(v8::Local req, + const char* buf, + size_t len); + int WriteString(v8::Local req, + v8::Local str, + enum encoding enc, + v8::Local handle); + int SetBlocking(bool enable); inline StreamWrapCallbacks* callbacks() const { return callbacks_; @@ -153,8 +152,6 @@ class StreamWrap : public HandleWrap, public StreamBase { } protected: - static size_t WriteBuffer(v8::Handle val, uv_buf_t* buf); - StreamWrap(Environment* env, v8::Local object, uv_stream_t* stream, @@ -177,7 +174,6 @@ class StreamWrap : public HandleWrap, public StreamBase { static void OnAlloc(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf); - static void AfterShutdown(uv_shutdown_t* req, int status); static void OnRead(uv_stream_t* handle, ssize_t nread, @@ -186,9 +182,12 @@ class StreamWrap : public HandleWrap, public StreamBase { ssize_t nread, const uv_buf_t* buf, uv_handle_type pending); + static void AfterShutdown(uv_shutdown_t* req, int status); template - int WriteStringImpl(const v8::FunctionCallbackInfo& args); + int WriteStringImpl(v8::Local req, + v8::Local str, + v8::Local handle); uv_stream_t* const stream_; StreamWrapCallbacks default_callbacks_; From 5298490bbe016b8ba0e3de90f6070b22645e53a9 Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Tue, 10 Feb 2015 18:29:23 +0300 Subject: [PATCH 04/41] stream_wrap: remove WriteStringImpl --- src/stream_wrap.cc | 26 ++++---------------------- src/stream_wrap.h | 5 ----- 2 files changed, 4 insertions(+), 27 deletions(-) diff --git a/src/stream_wrap.cc b/src/stream_wrap.cc index 22762edc48187b..745970c3aaaf7b 100644 --- a/src/stream_wrap.cc +++ b/src/stream_wrap.cc @@ -227,10 +227,10 @@ int StreamWrap::WriteBuffer(Local req_wrap_obj, } -template -int StreamWrap::WriteStringImpl(Local req_wrap_obj, - Local string, - Local send_handle_obj) { +int StreamWrap::WriteString(Local req_wrap_obj, + Local string, + enum encoding encoding, + Local send_handle_obj) { Environment* env = this->env(); int err; @@ -452,24 +452,6 @@ int StreamWrap::Writev(Local req_wrap_obj, Local chunks) { } -int StreamWrap::WriteString(Local req, - Local str, - enum encoding enc, - Local handle) { - switch (enc) { - case ASCII: - return WriteStringImpl(req, str, handle); - case UTF8: - return WriteStringImpl(req, str, handle); - case UCS2: - return WriteStringImpl(req, str, handle); - case BINARY: - return WriteStringImpl(req, str, handle); - default: - return UV_ENOSYS; - } -} - int StreamWrap::SetBlocking(bool enable) { return uv_stream_set_blocking(stream(), enable); } diff --git a/src/stream_wrap.h b/src/stream_wrap.h index fd25e76cbc6947..adb83ebbe3981b 100644 --- a/src/stream_wrap.h +++ b/src/stream_wrap.h @@ -184,11 +184,6 @@ class StreamWrap : public HandleWrap, public StreamBase { uv_handle_type pending); static void AfterShutdown(uv_shutdown_t* req, int status); - template - int WriteStringImpl(v8::Local req, - v8::Local str, - v8::Local handle); - uv_stream_t* const stream_; StreamWrapCallbacks default_callbacks_; StreamWrapCallbacks* callbacks_; // Overridable callbacks From e9def28d5bbe09b419320f9a4391b430c31c86f3 Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Tue, 10 Feb 2015 23:23:07 +0300 Subject: [PATCH 05/41] stream_base: move Shutdown to StreamBase --- src/stream_base.cc | 45 +++++++++++++++++++++++++--- src/stream_base.h | 73 +++++++++++++++++++++++++++++++++++++++++++++- src/stream_wrap.cc | 41 +++++--------------------- src/stream_wrap.h | 52 +++------------------------------ 4 files changed, 124 insertions(+), 87 deletions(-) diff --git a/src/stream_base.cc b/src/stream_base.cc index 094162614920f4..054e6b2bbfd8e3 100644 --- a/src/stream_base.cc +++ b/src/stream_base.cc @@ -11,10 +11,12 @@ namespace node { using v8::Array; +using v8::Context; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; using v8::Handle; using v8::HandleScope; +using v8::Integer; using v8::Local; using v8::Object; using v8::PropertyAttribute; @@ -26,7 +28,8 @@ template void StreamBase::AddMethods(Environment* env, Handle t); -StreamBase::StreamBase(Environment* env, Local object) { +StreamBase::StreamBase(Environment* env, Local object) + : consumed_(false) { } @@ -72,7 +75,7 @@ template void StreamBase::GetFD(Local, const PropertyCallbackInfo& args) { HandleScope scope(args.GetIsolate()); Base* wrap = Unwrap(args.Holder()); - if (!wrap->IsAlive()) + if (wrap->IsConsumed() || !wrap->IsAlive()) return args.GetReturnValue().Set(UV_EINVAL); args.GetReturnValue().Set(wrap->GetFD()); @@ -84,7 +87,7 @@ template & args) { HandleScope scope(args.GetIsolate()); Base* wrap = Unwrap(args.Holder()); - if (!wrap->IsAlive()) + if (wrap->IsConsumed() || !wrap->IsAlive()) return args.GetReturnValue().Set(UV_EINVAL); args.GetReturnValue().Set((wrap->*Method)(args)); @@ -102,8 +105,42 @@ int StreamBase::ReadStop(const v8::FunctionCallbackInfo& args) { int StreamBase::Shutdown(const v8::FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + CHECK(args[0]->IsObject()); - return Shutdown(args[0].As()); + Local req_wrap_obj = args[0].As(); + + ShutdownWrap* req_wrap = new ShutdownWrap(env, req_wrap_obj, this); + int err = DoShutdown(req_wrap, AfterShutdown); + req_wrap->Dispatched(); + if (err) + delete req_wrap; + return err; +} + + +void StreamBase::AfterShutdown(uv_shutdown_t* req, int status) { + ShutdownWrap* req_wrap = static_cast(req->data); + StreamBase* wrap = req_wrap->wrap(); + Environment* env = req_wrap->env(); + + // The wrap and request objects should still be there. + CHECK_EQ(req_wrap->persistent().IsEmpty(), false); + CHECK_EQ(wrap->GetObject().IsEmpty(), false); + + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); + + Local req_wrap_obj = req_wrap->object(); + Local argv[3] = { + Integer::New(env->isolate(), status), + wrap->GetObject(), + req_wrap_obj + }; + + req_wrap->MakeCallback(env->oncomplete_string(), ARRAY_SIZE(argv), argv); + + delete req_wrap; } diff --git a/src/stream_base.h b/src/stream_base.h index fc5abc5a49711d..3d1de1e21c06e3 100644 --- a/src/stream_base.h +++ b/src/stream_base.h @@ -1,13 +1,72 @@ #ifndef SRC_STREAM_BASE_H_ #define SRC_STREAM_BASE_H_ +#include "req_wrap.h" #include "node.h" + #include "v8.h" namespace node { // Forward declarations class Environment; +class StreamBase; + +// XXX(indutny): temporary +class StreamWrap; + +class ShutdownWrap : public ReqWrap { + public: + ShutdownWrap(Environment* env, + v8::Local req_wrap_obj, + StreamBase* wrap) + : ReqWrap(env, req_wrap_obj, AsyncWrap::PROVIDER_SHUTDOWNWRAP), + wrap_(wrap) { + Wrap(req_wrap_obj, this); + } + + static void NewShutdownWrap(const v8::FunctionCallbackInfo& args) { + CHECK(args.IsConstructCall()); + } + + inline StreamBase* wrap() const { return wrap_; } + + private: + StreamBase* const wrap_; +}; + +class WriteWrap: public ReqWrap { + public: + // TODO(trevnorris): WrapWrap inherits from ReqWrap, which I've globbed + // into the same provider. How should these be broken apart? + WriteWrap(Environment* env, v8::Local obj, StreamWrap* wrap) + : ReqWrap(env, obj, AsyncWrap::PROVIDER_WRITEWRAP), + wrap_(wrap) { + Wrap(obj, this); + } + + void* operator new(size_t size, char* storage) { return storage; } + + // This is just to keep the compiler happy. It should never be called, since + // we don't use exceptions in node. + void operator delete(void* ptr, char* storage) { UNREACHABLE(); } + + inline StreamWrap* wrap() const { + return wrap_; + } + + static void NewWriteWrap(const v8::FunctionCallbackInfo& args) { + CHECK(args.IsConstructCall()); + } + + private: + // People should not be using the non-placement new and delete operator on a + // WriteWrap. Ensure this never happens. + void* operator new(size_t size) { UNREACHABLE(); } + void operator delete(void* ptr) { UNREACHABLE(); } + + StreamWrap* const wrap_; +}; class StreamBase { public: @@ -20,7 +79,6 @@ class StreamBase { virtual int ReadStart() = 0; virtual int ReadStop() = 0; - virtual int Shutdown(v8::Local req) = 0; virtual int Writev(v8::Local req, v8::Local bufs) = 0; virtual int WriteBuffer(v8::Local req, const char* buf, @@ -31,10 +89,21 @@ class StreamBase { v8::Local handle) = 0; virtual int SetBlocking(bool enable) = 0; + // Resource interface + virtual int DoShutdown(ShutdownWrap* req_wrap, uv_shutdown_cb cb) = 0; + + inline bool IsConsumed() const { return consumed_; } + + // TODO(indutny): assert that stream is not yet consumed + inline void Consume() { consumed_ = true; } + protected: StreamBase(Environment* env, v8::Local object); virtual ~StreamBase() = default; + virtual v8::Local GetObject() = 0; + static void AfterShutdown(uv_shutdown_t* req, int status); + int ReadStart(const v8::FunctionCallbackInfo& args); int ReadStop(const v8::FunctionCallbackInfo& args); int Shutdown(const v8::FunctionCallbackInfo& args); @@ -52,6 +121,8 @@ class StreamBase { int (StreamBase::*Method)( const v8::FunctionCallbackInfo& args)> static void JSMethod(const v8::FunctionCallbackInfo& args); + + bool consumed_; }; } // namespace node diff --git a/src/stream_wrap.cc b/src/stream_wrap.cc index 745970c3aaaf7b..c5dda282499227 100644 --- a/src/stream_wrap.cc +++ b/src/stream_wrap.cc @@ -91,6 +91,11 @@ bool StreamWrap::IsAlive() { } +Local StreamWrap::GetObject() { + return object(); +} + + void StreamWrap::UpdateWriteQueueSize() { HandleScope scope(env()->isolate()); Local write_queue_size = @@ -494,40 +499,8 @@ void StreamWrap::AfterWrite(uv_write_t* req, int status) { } -int StreamWrap::Shutdown(Local req_wrap_obj) { - Environment* env = this->env(); - - ShutdownWrap* req_wrap = new ShutdownWrap(env, req_wrap_obj); - int err = callbacks()->DoShutdown(req_wrap, AfterShutdown); - req_wrap->Dispatched(); - if (err) - delete req_wrap; - return err; -} - - -void StreamWrap::AfterShutdown(uv_shutdown_t* req, int status) { - ShutdownWrap* req_wrap = static_cast(req->data); - StreamWrap* wrap = static_cast(req->handle->data); - Environment* env = wrap->env(); - - // The wrap and request objects should still be there. - CHECK_EQ(req_wrap->persistent().IsEmpty(), false); - CHECK_EQ(wrap->persistent().IsEmpty(), false); - - HandleScope handle_scope(env->isolate()); - Context::Scope context_scope(env->context()); - - Local req_wrap_obj = req_wrap->object(); - Local argv[3] = { - Integer::New(env->isolate(), status), - wrap->object(), - req_wrap_obj - }; - - req_wrap->MakeCallback(env->oncomplete_string(), ARRAY_SIZE(argv), argv); - - delete req_wrap; +int StreamWrap::DoShutdown(ShutdownWrap* req_wrap, uv_shutdown_cb cb) { + return callbacks()->DoShutdown(req_wrap, cb); } diff --git a/src/stream_wrap.h b/src/stream_wrap.h index adb83ebbe3981b..634a56033a25a5 100644 --- a/src/stream_wrap.h +++ b/src/stream_wrap.h @@ -14,51 +14,6 @@ namespace node { // Forward declaration class StreamWrap; -class ShutdownWrap : public ReqWrap { - public: - ShutdownWrap(Environment* env, v8::Local req_wrap_obj) - : ReqWrap(env, req_wrap_obj, AsyncWrap::PROVIDER_SHUTDOWNWRAP) { - Wrap(req_wrap_obj, this); - } - - static void NewShutdownWrap(const v8::FunctionCallbackInfo& args) { - CHECK(args.IsConstructCall()); - } -}; - -class WriteWrap: public ReqWrap { - public: - // TODO(trevnorris): WrapWrap inherits from ReqWrap, which I've globbed - // into the same provider. How should these be broken apart? - WriteWrap(Environment* env, v8::Local obj, StreamWrap* wrap) - : ReqWrap(env, obj, AsyncWrap::PROVIDER_WRITEWRAP), - wrap_(wrap) { - Wrap(obj, this); - } - - void* operator new(size_t size, char* storage) { return storage; } - - // This is just to keep the compiler happy. It should never be called, since - // we don't use exceptions in node. - void operator delete(void* ptr, char* storage) { UNREACHABLE(); } - - inline StreamWrap* wrap() const { - return wrap_; - } - - static void NewWriteWrap(const v8::FunctionCallbackInfo& args) { - CHECK(args.IsConstructCall()); - } - - private: - // People should not be using the non-placement new and delete operator on a - // WriteWrap. Ensure this never happens. - void* operator new(size_t size) { UNREACHABLE(); } - void operator delete(void* ptr) { UNREACHABLE(); } - - StreamWrap* const wrap_; -}; - // Overridable callbacks' types class StreamWrapCallbacks { public: @@ -119,7 +74,6 @@ class StreamWrap : public HandleWrap, public StreamBase { // JavaScript functions int ReadStart(); int ReadStop(); - int Shutdown(v8::Local req); int Writev(v8::Local req, v8::Local bufs); int WriteBuffer(v8::Local req, const char* buf, @@ -130,6 +84,9 @@ class StreamWrap : public HandleWrap, public StreamBase { v8::Local handle); int SetBlocking(bool enable); + // Resource implementation + int DoShutdown(ShutdownWrap* req_wrap, uv_shutdown_cb cb); + inline StreamWrapCallbacks* callbacks() const { return callbacks_; } @@ -165,7 +122,7 @@ class StreamWrap : public HandleWrap, public StreamBase { callbacks_ = nullptr; } - void StateChange() { } + v8::Local GetObject(); void UpdateWriteQueueSize(); private: @@ -182,7 +139,6 @@ class StreamWrap : public HandleWrap, public StreamBase { ssize_t nread, const uv_buf_t* buf, uv_handle_type pending); - static void AfterShutdown(uv_shutdown_t* req, int status); uv_stream_t* const stream_; StreamWrapCallbacks default_callbacks_; From 605775cc48dae5d15b3ce71d61ec3a25b5ceb00a Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Wed, 11 Feb 2015 00:11:42 +0300 Subject: [PATCH 06/41] stream_base: move away WriteBuffer --- src/stream_base.cc | 88 +++++++++++++++++++++++++++++++++++-- src/stream_base.h | 37 ++++++++++------ src/stream_wrap.cc | 106 ++++++++++----------------------------------- src/stream_wrap.h | 13 ++++-- 4 files changed, 142 insertions(+), 102 deletions(-) diff --git a/src/stream_base.cc b/src/stream_base.cc index 054e6b2bbfd8e3..3945f0cd6ccdc6 100644 --- a/src/stream_base.cc +++ b/src/stream_base.cc @@ -154,9 +154,54 @@ int StreamBase::Writev(const v8::FunctionCallbackInfo& args) { int StreamBase::WriteBuffer(const v8::FunctionCallbackInfo& args) { CHECK(args[0]->IsObject()); CHECK(Buffer::HasInstance(args[1])); - return WriteBuffer(args[0].As(), - Buffer::Data(args[1]), - Buffer::Length(args[1])); + Environment* env = Environment::GetCurrent(args); + + Local req_wrap_obj = args[0].As(); + const char* data = Buffer::Data(args[1]); + size_t length = Buffer::Length(args[1]); + + char* storage; + WriteWrap* req_wrap; + uv_buf_t buf; + buf.base = const_cast(data); + buf.len = length; + + // Try writing immediately without allocation + uv_buf_t* bufs = &buf; + size_t count = 1; + int err = TryWrite(&bufs, &count); + if (err != 0) + goto done; + if (count == 0) + goto done; + CHECK_EQ(count, 1); + + // Allocate, or write rest + storage = new char[sizeof(WriteWrap)]; + req_wrap = new(storage) WriteWrap(env, req_wrap_obj, this); + + err = DoWrite(req_wrap, + bufs, + count, + nullptr, + StreamBase::AfterWrite); + req_wrap->Dispatched(); + req_wrap_obj->Set(env->async(), True(env->isolate())); + + if (err) { + req_wrap->~WriteWrap(); + delete[] storage; + } + + done: + const char* msg = Error(); + if (msg != nullptr) { + req_wrap_obj->Set(env->error_string(), OneByteString(env->isolate(), msg)); + ClearError(); + } + req_wrap_obj->Set(env->bytes_string(), + Integer::NewFromUnsigned(env->isolate(), length)); + return err; } @@ -174,6 +219,43 @@ int StreamBase::WriteString(const v8::FunctionCallbackInfo& args) { } +void StreamBase::AfterWrite(uv_write_t* req, int status) { + WriteWrap* req_wrap = ContainerOf(&WriteWrap::req_, req); + StreamBase* wrap = req_wrap->wrap(); + Environment* env = req_wrap->env(); + + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); + + // The wrap and request objects should still be there. + CHECK_EQ(req_wrap->persistent().IsEmpty(), false); + CHECK_EQ(wrap->GetObject().IsEmpty(), false); + + // Unref handle property + Local req_wrap_obj = req_wrap->object(); + req_wrap_obj->Delete(env->handle_string()); + wrap->DoAfterWrite(req_wrap); + + Local argv[] = { + Integer::New(env->isolate(), status), + wrap->GetObject(), + req_wrap_obj, + Undefined(env->isolate()) + }; + + const char* msg = wrap->Error(); + if (msg != nullptr) { + argv[3] = OneByteString(env->isolate(), msg); + wrap->ClearError(); + } + + req_wrap->MakeCallback(env->oncomplete_string(), ARRAY_SIZE(argv), argv); + + req_wrap->~WriteWrap(); + delete[] reinterpret_cast(req_wrap); +} + + int StreamBase::SetBlocking(const v8::FunctionCallbackInfo& args) { CHECK_GT(args.Length(), 0); return SetBlocking(args[0]->IsTrue()); diff --git a/src/stream_base.h b/src/stream_base.h index 3d1de1e21c06e3..866ed1dff572e2 100644 --- a/src/stream_base.h +++ b/src/stream_base.h @@ -12,9 +12,6 @@ namespace node { class Environment; class StreamBase; -// XXX(indutny): temporary -class StreamWrap; - class ShutdownWrap : public ReqWrap { public: ShutdownWrap(Environment* env, @@ -39,7 +36,7 @@ class WriteWrap: public ReqWrap { public: // TODO(trevnorris): WrapWrap inherits from ReqWrap, which I've globbed // into the same provider. How should these be broken apart? - WriteWrap(Environment* env, v8::Local obj, StreamWrap* wrap) + WriteWrap(Environment* env, v8::Local obj, StreamBase* wrap) : ReqWrap(env, obj, AsyncWrap::PROVIDER_WRITEWRAP), wrap_(wrap) { Wrap(obj, this); @@ -51,7 +48,7 @@ class WriteWrap: public ReqWrap { // we don't use exceptions in node. void operator delete(void* ptr, char* storage) { UNREACHABLE(); } - inline StreamWrap* wrap() const { + inline StreamBase* wrap() const { return wrap_; } @@ -65,10 +62,26 @@ class WriteWrap: public ReqWrap { void* operator new(size_t size) { UNREACHABLE(); } void operator delete(void* ptr) { UNREACHABLE(); } - StreamWrap* const wrap_; + StreamBase* const wrap_; +}; + +class StreamResource { + public: + virtual ~StreamResource() = default; + + virtual int DoShutdown(ShutdownWrap* req_wrap, uv_shutdown_cb cb) = 0; + virtual int TryWrite(uv_buf_t** bufs, size_t* count) = 0; + virtual int DoWrite(WriteWrap* w, + uv_buf_t* bufs, + size_t count, + uv_stream_t* send_handle, + uv_write_cb cb) = 0; + virtual void DoAfterWrite(WriteWrap* w) = 0; + virtual const char* Error() const = 0; + virtual void ClearError() = 0; }; -class StreamBase { +class StreamBase : public StreamResource { public: template static void AddMethods(Environment* env, @@ -80,18 +93,12 @@ class StreamBase { virtual int ReadStart() = 0; virtual int ReadStop() = 0; virtual int Writev(v8::Local req, v8::Local bufs) = 0; - virtual int WriteBuffer(v8::Local req, - const char* buf, - size_t len) = 0; virtual int WriteString(v8::Local req, v8::Local str, enum encoding enc, v8::Local handle) = 0; virtual int SetBlocking(bool enable) = 0; - // Resource interface - virtual int DoShutdown(ShutdownWrap* req_wrap, uv_shutdown_cb cb) = 0; - inline bool IsConsumed() const { return consumed_; } // TODO(indutny): assert that stream is not yet consumed @@ -102,8 +109,12 @@ class StreamBase { virtual ~StreamBase() = default; virtual v8::Local GetObject() = 0; + + // Libuv callbacks static void AfterShutdown(uv_shutdown_t* req, int status); + static void AfterWrite(uv_write_t* req, int status); + // JS Methods int ReadStart(const v8::FunctionCallbackInfo& args); int ReadStop(const v8::FunctionCallbackInfo& args); int Shutdown(const v8::FunctionCallbackInfo& args); diff --git a/src/stream_wrap.cc b/src/stream_wrap.cc index c5dda282499227..ef77dda22b05b9 100644 --- a/src/stream_wrap.cc +++ b/src/stream_wrap.cc @@ -182,56 +182,6 @@ void StreamWrap::OnRead(uv_stream_t* handle, } -int StreamWrap::WriteBuffer(Local req_wrap_obj, - const char* data, - size_t length) { - Environment* env = this->env(); - - char* storage; - WriteWrap* req_wrap; - uv_buf_t buf; - buf.base = const_cast(data); - buf.len = length; - - // Try writing immediately without allocation - uv_buf_t* bufs = &buf; - size_t count = 1; - int err = callbacks()->TryWrite(&bufs, &count); - if (err != 0) - goto done; - if (count == 0) - goto done; - CHECK_EQ(count, 1); - - // Allocate, or write rest - storage = new char[sizeof(WriteWrap)]; - req_wrap = new(storage) WriteWrap(env, req_wrap_obj, this); - - err = callbacks()->DoWrite(req_wrap, - bufs, - count, - nullptr, - StreamWrap::AfterWrite); - req_wrap->Dispatched(); - req_wrap_obj->Set(env->async(), True(env->isolate())); - - if (err) { - req_wrap->~WriteWrap(); - delete[] storage; - } - - done: - const char* msg = callbacks()->Error(); - if (msg != nullptr) { - req_wrap_obj->Set(env->error_string(), OneByteString(env->isolate(), msg)); - callbacks()->ClearError(); - } - req_wrap_obj->Set(env->bytes_string(), - Integer::NewFromUnsigned(env->isolate(), length)); - return err; -} - - int StreamWrap::WriteString(Local req_wrap_obj, Local string, enum encoding encoding, @@ -313,7 +263,7 @@ int StreamWrap::WriteString(Local req_wrap_obj, &buf, 1, nullptr, - StreamWrap::AfterWrite); + StreamBase::AfterWrite); } else { uv_handle_t* send_handle = nullptr; @@ -331,7 +281,7 @@ int StreamWrap::WriteString(Local req_wrap_obj, &buf, 1, reinterpret_cast(send_handle), - StreamWrap::AfterWrite); + StreamBase::AfterWrite); } req_wrap->Dispatched(); @@ -432,7 +382,7 @@ int StreamWrap::Writev(Local req_wrap_obj, Local chunks) { bufs, count, nullptr, - StreamWrap::AfterWrite); + StreamBase::AfterWrite); // Deallocate space if (bufs != bufs_) @@ -462,45 +412,37 @@ int StreamWrap::SetBlocking(bool enable) { } -void StreamWrap::AfterWrite(uv_write_t* req, int status) { - WriteWrap* req_wrap = ContainerOf(&WriteWrap::req_, req); - StreamWrap* wrap = req_wrap->wrap(); - Environment* env = wrap->env(); +int StreamWrap::DoShutdown(ShutdownWrap* req_wrap, uv_shutdown_cb cb) { + return callbacks()->DoShutdown(req_wrap, cb); +} - HandleScope handle_scope(env->isolate()); - Context::Scope context_scope(env->context()); - // The wrap and request objects should still be there. - CHECK_EQ(req_wrap->persistent().IsEmpty(), false); - CHECK_EQ(wrap->persistent().IsEmpty(), false); +int StreamWrap::TryWrite(uv_buf_t** bufs, size_t* count) { + return callbacks()->TryWrite(bufs, count); +} - // Unref handle property - Local req_wrap_obj = req_wrap->object(); - req_wrap_obj->Delete(env->handle_string()); - wrap->callbacks()->AfterWrite(req_wrap); - Local argv[] = { - Integer::New(env->isolate(), status), - wrap->object(), - req_wrap_obj, - Undefined(env->isolate()) - }; +int StreamWrap::DoWrite(WriteWrap* w, + uv_buf_t* bufs, + size_t count, + uv_stream_t* send_handle, + uv_write_cb cb) { + return callbacks()->DoWrite(w, bufs, count, send_handle, cb); +} - const char* msg = wrap->callbacks()->Error(); - if (msg != nullptr) { - argv[3] = OneByteString(env->isolate(), msg); - wrap->callbacks()->ClearError(); - } - req_wrap->MakeCallback(env->oncomplete_string(), ARRAY_SIZE(argv), argv); +void StreamWrap::DoAfterWrite(WriteWrap* w) { + return callbacks()->AfterWrite(w); +} + - req_wrap->~WriteWrap(); - delete[] reinterpret_cast(req_wrap); +const char* StreamWrap::Error() const { + return callbacks()->Error(); } -int StreamWrap::DoShutdown(ShutdownWrap* req_wrap, uv_shutdown_cb cb) { - return callbacks()->DoShutdown(req_wrap, cb); +void StreamWrap::ClearError() { + return callbacks()->ClearError(); } diff --git a/src/stream_wrap.h b/src/stream_wrap.h index 634a56033a25a5..5080e876a9bb96 100644 --- a/src/stream_wrap.h +++ b/src/stream_wrap.h @@ -75,9 +75,6 @@ class StreamWrap : public HandleWrap, public StreamBase { int ReadStart(); int ReadStop(); int Writev(v8::Local req, v8::Local bufs); - int WriteBuffer(v8::Local req, - const char* buf, - size_t len); int WriteString(v8::Local req, v8::Local str, enum encoding enc, @@ -86,6 +83,15 @@ class StreamWrap : public HandleWrap, public StreamBase { // Resource implementation int DoShutdown(ShutdownWrap* req_wrap, uv_shutdown_cb cb); + int TryWrite(uv_buf_t** bufs, size_t* count); + int DoWrite(WriteWrap* w, + uv_buf_t* bufs, + size_t count, + uv_stream_t* send_handle, + uv_write_cb cb); + void DoAfterWrite(WriteWrap* w); + const char* Error() const; + void ClearError(); inline StreamWrapCallbacks* callbacks() const { return callbacks_; @@ -127,7 +133,6 @@ class StreamWrap : public HandleWrap, public StreamBase { private: // Callbacks for libuv - static void AfterWrite(uv_write_t* req, int status); static void OnAlloc(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf); From 16e33d40630c8ea2949245f5de5ea6018a3a337f Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Wed, 11 Feb 2015 00:18:56 +0300 Subject: [PATCH 07/41] stream_base: move Writev/WriteString --- src/stream_base.cc | 242 +++++++++++++++++++++++++++++++++++++++++++-- src/stream_base.h | 12 +-- src/stream_wrap.cc | 234 ++----------------------------------------- src/stream_wrap.h | 10 +- 4 files changed, 248 insertions(+), 250 deletions(-) diff --git a/src/stream_base.cc b/src/stream_base.cc index 3945f0cd6ccdc6..7969c31d6bf129 100644 --- a/src/stream_base.cc +++ b/src/stream_base.cc @@ -1,13 +1,17 @@ #include "stream_base.h" +#include "node.h" #include "node_buffer.h" #include "env.h" #include "env-inl.h" #include "stream_wrap.h" +#include "string_bytes.h" #include "util.h" #include "util-inl.h" #include "v8.h" +#include // INT_MAX + namespace node { using v8::Array; @@ -18,6 +22,7 @@ using v8::Handle; using v8::HandleScope; using v8::Integer; using v8::Local; +using v8::Number; using v8::Object; using v8::PropertyAttribute; using v8::PropertyCallbackInfo; @@ -145,12 +150,116 @@ void StreamBase::AfterShutdown(uv_shutdown_t* req, int status) { int StreamBase::Writev(const v8::FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + CHECK(args[0]->IsObject()); CHECK(args[1]->IsArray()); - return Writev(args[0].As(), args[1].As()); + + Local req_wrap_obj = args[0].As(); + Local chunks = args[1].As(); + + size_t count = chunks->Length() >> 1; + + uv_buf_t bufs_[16]; + uv_buf_t* bufs = bufs_; + + // Determine storage size first + size_t storage_size = 0; + for (size_t i = 0; i < count; i++) { + Handle chunk = chunks->Get(i * 2); + + if (Buffer::HasInstance(chunk)) + continue; + // Buffer chunk, no additional storage required + + // String chunk + Handle string = chunk->ToString(env->isolate()); + enum encoding encoding = ParseEncoding(env->isolate(), + chunks->Get(i * 2 + 1)); + size_t chunk_size; + if (encoding == UTF8 && string->Length() > 65535) + chunk_size = StringBytes::Size(env->isolate(), string, encoding); + else + chunk_size = StringBytes::StorageSize(env->isolate(), string, encoding); + + storage_size += chunk_size + 15; + } + + if (storage_size > INT_MAX) + return UV_ENOBUFS; + + if (ARRAY_SIZE(bufs_) < count) + bufs = new uv_buf_t[count]; + + storage_size += sizeof(WriteWrap); + char* storage = new char[storage_size]; + WriteWrap* req_wrap = + new(storage) WriteWrap(env, req_wrap_obj, this); + + uint32_t bytes = 0; + size_t offset = sizeof(WriteWrap); + for (size_t i = 0; i < count; i++) { + Handle chunk = chunks->Get(i * 2); + + // Write buffer + if (Buffer::HasInstance(chunk)) { + bufs[i].base = Buffer::Data(chunk); + bufs[i].len = Buffer::Length(chunk); + bytes += bufs[i].len; + continue; + } + + // Write string + offset = ROUND_UP(offset, 16); + CHECK_LT(offset, storage_size); + char* str_storage = storage + offset; + size_t str_size = storage_size - offset; + + Handle string = chunk->ToString(env->isolate()); + enum encoding encoding = ParseEncoding(env->isolate(), + chunks->Get(i * 2 + 1)); + str_size = StringBytes::Write(env->isolate(), + str_storage, + str_size, + string, + encoding); + bufs[i].base = str_storage; + bufs[i].len = str_size; + offset += str_size; + bytes += str_size; + } + + int err = DoWrite(req_wrap, + bufs, + count, + nullptr, + StreamBase::AfterWrite); + + // Deallocate space + if (bufs != bufs_) + delete[] bufs; + + req_wrap->Dispatched(); + req_wrap->object()->Set(env->async(), True(env->isolate())); + req_wrap->object()->Set(env->bytes_string(), + Number::New(env->isolate(), bytes)); + const char* msg = Error(); + if (msg != nullptr) { + req_wrap_obj->Set(env->error_string(), OneByteString(env->isolate(), msg)); + ClearError(); + } + + if (err) { + req_wrap->~WriteWrap(); + delete[] storage; + } + + return err; } + + int StreamBase::WriteBuffer(const v8::FunctionCallbackInfo& args) { CHECK(args[0]->IsObject()); CHECK(Buffer::HasInstance(args[1])); @@ -205,20 +314,137 @@ int StreamBase::WriteBuffer(const v8::FunctionCallbackInfo& args) { } -template +template int StreamBase::WriteString(const v8::FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); CHECK(args[0]->IsObject()); CHECK(args[1]->IsString()); - Local handle; + + Local req_wrap_obj = args[0].As(); + Local string = args[1].As(); + Local send_handle_obj; if (args[2]->IsObject()) - handle = args[2].As(); - return WriteString(args[0].As(), - args[1].As(), - Encoding, - handle); + send_handle_obj = args[2].As(); + + int err; + + // Compute the size of the storage that the string will be flattened into. + // For UTF8 strings that are very long, go ahead and take the hit for + // computing their actual size, rather than tripling the storage. + size_t storage_size; + if (enc == UTF8 && string->Length() > 65535) + storage_size = StringBytes::Size(env->isolate(), string, enc); + else + storage_size = StringBytes::StorageSize(env->isolate(), string, enc); + + if (storage_size > INT_MAX) + return UV_ENOBUFS; + + // Try writing immediately if write size isn't too big + char* storage; + WriteWrap* req_wrap; + char* data; + char stack_storage[16384]; // 16kb + size_t data_size; + uv_buf_t buf; + + bool try_write = storage_size + 15 <= sizeof(stack_storage) && + (!IsIPCPipe() || send_handle_obj.IsEmpty()); + if (try_write) { + data_size = StringBytes::Write(env->isolate(), + stack_storage, + storage_size, + string, + enc); + buf = uv_buf_init(stack_storage, data_size); + + uv_buf_t* bufs = &buf; + size_t count = 1; + err = TryWrite(&bufs, &count); + + // Failure + if (err != 0) + goto done; + + // Success + if (count == 0) + goto done; + + // Partial write + CHECK_EQ(count, 1); + } + + storage = new char[sizeof(WriteWrap) + storage_size + 15]; + req_wrap = new(storage) WriteWrap(env, req_wrap_obj, this); + + data = reinterpret_cast(ROUND_UP( + reinterpret_cast(storage) + sizeof(WriteWrap), 16)); + + if (try_write) { + // Copy partial data + memcpy(data, buf.base, buf.len); + data_size = buf.len; + } else { + // Write it + data_size = StringBytes::Write(env->isolate(), + data, + storage_size, + string, + enc); + } + + CHECK_LE(data_size, storage_size); + + buf = uv_buf_init(data, data_size); + + if (!IsIPCPipe()) { + err = DoWrite(req_wrap, + &buf, + 1, + nullptr, + StreamBase::AfterWrite); + } else { + uv_handle_t* send_handle = nullptr; + + if (!send_handle_obj.IsEmpty()) { + HandleWrap* wrap = Unwrap(send_handle_obj); + send_handle = wrap->GetHandle(); + // Reference StreamWrap instance to prevent it from being garbage + // collected before `AfterWrite` is called. + CHECK_EQ(false, req_wrap->persistent().IsEmpty()); + req_wrap->object()->Set(env->handle_string(), send_handle_obj); + } + + err = DoWrite( + req_wrap, + &buf, + 1, + reinterpret_cast(send_handle), + StreamBase::AfterWrite); + } + + req_wrap->Dispatched(); + req_wrap->object()->Set(env->async(), True(env->isolate())); + + if (err) { + req_wrap->~WriteWrap(); + delete[] storage; + } + + done: + const char* msg = Error(); + if (msg != nullptr) { + req_wrap_obj->Set(env->error_string(), OneByteString(env->isolate(), msg)); + ClearError(); + } + req_wrap_obj->Set(env->bytes_string(), + Integer::NewFromUnsigned(env->isolate(), data_size)); + return err; } + + void StreamBase::AfterWrite(uv_write_t* req, int status) { WriteWrap* req_wrap = ContainerOf(&WriteWrap::req_, req); StreamBase* wrap = req_wrap->wrap(); diff --git a/src/stream_base.h b/src/stream_base.h index 866ed1dff572e2..287711a6fd77d0 100644 --- a/src/stream_base.h +++ b/src/stream_base.h @@ -87,16 +87,11 @@ class StreamBase : public StreamResource { static void AddMethods(Environment* env, v8::Handle target); - virtual bool IsAlive() = 0; - virtual int GetFD() = 0; + virtual bool IsAlive() const = 0; + virtual int GetFD() const = 0; virtual int ReadStart() = 0; virtual int ReadStop() = 0; - virtual int Writev(v8::Local req, v8::Local bufs) = 0; - virtual int WriteString(v8::Local req, - v8::Local str, - enum encoding enc, - v8::Local handle) = 0; virtual int SetBlocking(bool enable) = 0; inline bool IsConsumed() const { return consumed_; } @@ -109,6 +104,7 @@ class StreamBase : public StreamResource { virtual ~StreamBase() = default; virtual v8::Local GetObject() = 0; + virtual bool IsIPCPipe() const = 0; // Libuv callbacks static void AfterShutdown(uv_shutdown_t* req, int status); @@ -120,7 +116,7 @@ class StreamBase : public StreamResource { int Shutdown(const v8::FunctionCallbackInfo& args); int Writev(const v8::FunctionCallbackInfo& args); int WriteBuffer(const v8::FunctionCallbackInfo& args); - template + template int WriteString(const v8::FunctionCallbackInfo& args); int SetBlocking(const v8::FunctionCallbackInfo& args); diff --git a/src/stream_wrap.cc b/src/stream_wrap.cc index ef77dda22b05b9..c9f386b4be627d 100644 --- a/src/stream_wrap.cc +++ b/src/stream_wrap.cc @@ -76,7 +76,7 @@ StreamWrap::StreamWrap(Environment* env, } -int StreamWrap::GetFD() { +int StreamWrap::GetFD() const { int fd = -1; #if !defined(_WIN32) if (stream() != nullptr) @@ -86,7 +86,7 @@ int StreamWrap::GetFD() { } -bool StreamWrap::IsAlive() { +bool StreamWrap::IsAlive() const { return HandleWrap::IsAlive(this); } @@ -96,6 +96,11 @@ Local StreamWrap::GetObject() { } +bool StreamWrap::IsIPCPipe() const { + return is_named_pipe_ipc(); +} + + void StreamWrap::UpdateWriteQueueSize() { HandleScope scope(env()->isolate()); Local write_queue_size = @@ -182,231 +187,6 @@ void StreamWrap::OnRead(uv_stream_t* handle, } -int StreamWrap::WriteString(Local req_wrap_obj, - Local string, - enum encoding encoding, - Local send_handle_obj) { - Environment* env = this->env(); - int err; - - // Compute the size of the storage that the string will be flattened into. - // For UTF8 strings that are very long, go ahead and take the hit for - // computing their actual size, rather than tripling the storage. - size_t storage_size; - if (encoding == UTF8 && string->Length() > 65535) - storage_size = StringBytes::Size(env->isolate(), string, encoding); - else - storage_size = StringBytes::StorageSize(env->isolate(), string, encoding); - - if (storage_size > INT_MAX) - return UV_ENOBUFS; - - // Try writing immediately if write size isn't too big - char* storage; - WriteWrap* req_wrap; - char* data; - char stack_storage[16384]; // 16kb - size_t data_size; - uv_buf_t buf; - - bool try_write = storage_size + 15 <= sizeof(stack_storage) && - (!is_named_pipe_ipc() || send_handle_obj.IsEmpty()); - if (try_write) { - data_size = StringBytes::Write(env->isolate(), - stack_storage, - storage_size, - string, - encoding); - buf = uv_buf_init(stack_storage, data_size); - - uv_buf_t* bufs = &buf; - size_t count = 1; - err = callbacks()->TryWrite(&bufs, &count); - - // Failure - if (err != 0) - goto done; - - // Success - if (count == 0) - goto done; - - // Partial write - CHECK_EQ(count, 1); - } - - storage = new char[sizeof(WriteWrap) + storage_size + 15]; - req_wrap = new(storage) WriteWrap(env, req_wrap_obj, this); - - data = reinterpret_cast(ROUND_UP( - reinterpret_cast(storage) + sizeof(WriteWrap), 16)); - - if (try_write) { - // Copy partial data - memcpy(data, buf.base, buf.len); - data_size = buf.len; - } else { - // Write it - data_size = StringBytes::Write(env->isolate(), - data, - storage_size, - string, - encoding); - } - - CHECK_LE(data_size, storage_size); - - buf = uv_buf_init(data, data_size); - - if (!is_named_pipe_ipc()) { - err = callbacks()->DoWrite(req_wrap, - &buf, - 1, - nullptr, - StreamBase::AfterWrite); - } else { - uv_handle_t* send_handle = nullptr; - - if (!send_handle_obj.IsEmpty()) { - HandleWrap* wrap = Unwrap(send_handle_obj); - send_handle = wrap->GetHandle(); - // Reference StreamWrap instance to prevent it from being garbage - // collected before `AfterWrite` is called. - CHECK_EQ(false, req_wrap->persistent().IsEmpty()); - req_wrap->object()->Set(env->handle_string(), send_handle_obj); - } - - err = callbacks()->DoWrite( - req_wrap, - &buf, - 1, - reinterpret_cast(send_handle), - StreamBase::AfterWrite); - } - - req_wrap->Dispatched(); - req_wrap->object()->Set(env->async(), True(env->isolate())); - - if (err) { - req_wrap->~WriteWrap(); - delete[] storage; - } - - done: - const char* msg = callbacks()->Error(); - if (msg != nullptr) { - req_wrap_obj->Set(env->error_string(), OneByteString(env->isolate(), msg)); - callbacks()->ClearError(); - } - req_wrap_obj->Set(env->bytes_string(), - Integer::NewFromUnsigned(env->isolate(), data_size)); - return err; -} - - -int StreamWrap::Writev(Local req_wrap_obj, Local chunks) { - Environment* env = this->env(); - - size_t count = chunks->Length() >> 1; - - uv_buf_t bufs_[16]; - uv_buf_t* bufs = bufs_; - - // Determine storage size first - size_t storage_size = 0; - for (size_t i = 0; i < count; i++) { - Handle chunk = chunks->Get(i * 2); - - if (Buffer::HasInstance(chunk)) - continue; - // Buffer chunk, no additional storage required - - // String chunk - Handle string = chunk->ToString(env->isolate()); - enum encoding encoding = ParseEncoding(env->isolate(), - chunks->Get(i * 2 + 1)); - size_t chunk_size; - if (encoding == UTF8 && string->Length() > 65535) - chunk_size = StringBytes::Size(env->isolate(), string, encoding); - else - chunk_size = StringBytes::StorageSize(env->isolate(), string, encoding); - - storage_size += chunk_size + 15; - } - - if (storage_size > INT_MAX) - return UV_ENOBUFS; - - if (ARRAY_SIZE(bufs_) < count) - bufs = new uv_buf_t[count]; - - storage_size += sizeof(WriteWrap); - char* storage = new char[storage_size]; - WriteWrap* req_wrap = - new(storage) WriteWrap(env, req_wrap_obj, this); - - uint32_t bytes = 0; - size_t offset = sizeof(WriteWrap); - for (size_t i = 0; i < count; i++) { - Handle chunk = chunks->Get(i * 2); - - // Write buffer - if (Buffer::HasInstance(chunk)) { - bufs[i].base = Buffer::Data(chunk); - bufs[i].len = Buffer::Length(chunk); - bytes += bufs[i].len; - continue; - } - - // Write string - offset = ROUND_UP(offset, 16); - CHECK_LT(offset, storage_size); - char* str_storage = storage + offset; - size_t str_size = storage_size - offset; - - Handle string = chunk->ToString(env->isolate()); - enum encoding encoding = ParseEncoding(env->isolate(), - chunks->Get(i * 2 + 1)); - str_size = StringBytes::Write(env->isolate(), - str_storage, - str_size, - string, - encoding); - bufs[i].base = str_storage; - bufs[i].len = str_size; - offset += str_size; - bytes += str_size; - } - - int err = callbacks()->DoWrite(req_wrap, - bufs, - count, - nullptr, - StreamBase::AfterWrite); - - // Deallocate space - if (bufs != bufs_) - delete[] bufs; - - req_wrap->Dispatched(); - req_wrap->object()->Set(env->async(), True(env->isolate())); - req_wrap->object()->Set(env->bytes_string(), - Number::New(env->isolate(), bytes)); - const char* msg = callbacks()->Error(); - if (msg != nullptr) { - req_wrap_obj->Set(env->error_string(), OneByteString(env->isolate(), msg)); - callbacks()->ClearError(); - } - - if (err) { - req_wrap->~WriteWrap(); - delete[] storage; - } - - return err; -} - - int StreamWrap::SetBlocking(bool enable) { return uv_stream_set_blocking(stream(), enable); } diff --git a/src/stream_wrap.h b/src/stream_wrap.h index 5080e876a9bb96..a7cebecd2a039a 100644 --- a/src/stream_wrap.h +++ b/src/stream_wrap.h @@ -68,17 +68,12 @@ class StreamWrap : public HandleWrap, public StreamBase { delete old; } - int GetFD(); - bool IsAlive(); + int GetFD() const; + bool IsAlive() const; // JavaScript functions int ReadStart(); int ReadStop(); - int Writev(v8::Local req, v8::Local bufs); - int WriteString(v8::Local req, - v8::Local str, - enum encoding enc, - v8::Local handle); int SetBlocking(bool enable); // Resource implementation @@ -129,6 +124,7 @@ class StreamWrap : public HandleWrap, public StreamBase { } v8::Local GetObject(); + bool IsIPCPipe() const; void UpdateWriteQueueSize(); private: From ddd5d2867debf2911aab833c2428193017514184 Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Wed, 11 Feb 2015 16:09:39 +0300 Subject: [PATCH 08/41] stream_base: DoAlloc --- src/stream_base.cc | 6 ++---- src/stream_base.h | 3 ++- src/stream_wrap.cc | 9 +++++++-- src/stream_wrap.h | 3 ++- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/stream_base.cc b/src/stream_base.cc index 7969c31d6bf129..77611761fb822a 100644 --- a/src/stream_base.cc +++ b/src/stream_base.cc @@ -278,7 +278,7 @@ int StreamBase::WriteBuffer(const v8::FunctionCallbackInfo& args) { // Try writing immediately without allocation uv_buf_t* bufs = &buf; size_t count = 1; - int err = TryWrite(&bufs, &count); + int err = DoTryWrite(&bufs, &count); if (err != 0) goto done; if (count == 0) @@ -360,7 +360,7 @@ int StreamBase::WriteString(const v8::FunctionCallbackInfo& args) { uv_buf_t* bufs = &buf; size_t count = 1; - err = TryWrite(&bufs, &count); + err = DoTryWrite(&bufs, &count); // Failure if (err != 0) @@ -443,8 +443,6 @@ int StreamBase::WriteString(const v8::FunctionCallbackInfo& args) { } - - void StreamBase::AfterWrite(uv_write_t* req, int status) { WriteWrap* req_wrap = ContainerOf(&WriteWrap::req_, req); StreamBase* wrap = req_wrap->wrap(); diff --git a/src/stream_base.h b/src/stream_base.h index 287711a6fd77d0..1034d55a0b10b5 100644 --- a/src/stream_base.h +++ b/src/stream_base.h @@ -70,13 +70,14 @@ class StreamResource { virtual ~StreamResource() = default; virtual int DoShutdown(ShutdownWrap* req_wrap, uv_shutdown_cb cb) = 0; - virtual int TryWrite(uv_buf_t** bufs, size_t* count) = 0; + virtual int DoTryWrite(uv_buf_t** bufs, size_t* count) = 0; virtual int DoWrite(WriteWrap* w, uv_buf_t* bufs, size_t count, uv_stream_t* send_handle, uv_write_cb cb) = 0; virtual void DoAfterWrite(WriteWrap* w) = 0; + virtual void DoAlloc(size_t size, uv_buf_t* buf) = 0; virtual const char* Error() const = 0; virtual void ClearError() = 0; }; diff --git a/src/stream_wrap.cc b/src/stream_wrap.cc index c9f386b4be627d..1d445ed59f8993 100644 --- a/src/stream_wrap.cc +++ b/src/stream_wrap.cc @@ -124,7 +124,12 @@ void StreamWrap::OnAlloc(uv_handle_t* handle, uv_buf_t* buf) { StreamWrap* wrap = static_cast(handle->data); CHECK_EQ(wrap->stream(), reinterpret_cast(handle)); - wrap->callbacks()->DoAlloc(handle, suggested_size, buf); + return wrap->DoAlloc(suggested_size, buf); +} + + +void StreamWrap::DoAlloc(size_t size, uv_buf_t* buf) { + callbacks()->DoAlloc(reinterpret_cast(stream()), size, buf); } @@ -197,7 +202,7 @@ int StreamWrap::DoShutdown(ShutdownWrap* req_wrap, uv_shutdown_cb cb) { } -int StreamWrap::TryWrite(uv_buf_t** bufs, size_t* count) { +int StreamWrap::DoTryWrite(uv_buf_t** bufs, size_t* count) { return callbacks()->TryWrite(bufs, count); } diff --git a/src/stream_wrap.h b/src/stream_wrap.h index a7cebecd2a039a..91cd3d2b07fbe1 100644 --- a/src/stream_wrap.h +++ b/src/stream_wrap.h @@ -78,13 +78,14 @@ class StreamWrap : public HandleWrap, public StreamBase { // Resource implementation int DoShutdown(ShutdownWrap* req_wrap, uv_shutdown_cb cb); - int TryWrite(uv_buf_t** bufs, size_t* count); + int DoTryWrite(uv_buf_t** bufs, size_t* count); int DoWrite(WriteWrap* w, uv_buf_t* bufs, size_t count, uv_stream_t* send_handle, uv_write_cb cb); void DoAfterWrite(WriteWrap* w); + void DoAlloc(size_t size, uv_buf_t* buf); const char* Error() const; void ClearError(); From 48c1ff64a39f1edf76573e2879fc2a26fb9e751c Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Wed, 11 Feb 2015 16:13:44 +0300 Subject: [PATCH 09/41] stream_base: DoRead --- src/stream_base.h | 3 +++ src/stream_wrap.cc | 9 ++++++++- src/stream_wrap.h | 1 + 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/stream_base.h b/src/stream_base.h index 1034d55a0b10b5..7edb6e928c5071 100644 --- a/src/stream_base.h +++ b/src/stream_base.h @@ -78,6 +78,9 @@ class StreamResource { uv_write_cb cb) = 0; virtual void DoAfterWrite(WriteWrap* w) = 0; virtual void DoAlloc(size_t size, uv_buf_t* buf) = 0; + virtual void DoRead(size_t nread, + const uv_buf_t* buf, + uv_handle_type pending) = 0; virtual const char* Error() const = 0; virtual void ClearError() = 0; }; diff --git a/src/stream_wrap.cc b/src/stream_wrap.cc index 1d445ed59f8993..f0fbfd6a2da0cb 100644 --- a/src/stream_wrap.cc +++ b/src/stream_wrap.cc @@ -133,6 +133,13 @@ void StreamWrap::DoAlloc(size_t size, uv_buf_t* buf) { } +void StreamWrap::DoRead(size_t nread, + const uv_buf_t* buf, + uv_handle_type pending) { + callbacks()->DoRead(stream(), nread, buf, pending); +} + + template static Local AcceptHandle(Environment* env, uv_stream_t* pipe, @@ -173,7 +180,7 @@ void StreamWrap::OnReadCommon(uv_stream_t* handle, } } - wrap->callbacks()->DoRead(handle, nread, buf, pending); + wrap->DoRead(nread, buf, pending); } diff --git a/src/stream_wrap.h b/src/stream_wrap.h index 91cd3d2b07fbe1..ab68c3a2799324 100644 --- a/src/stream_wrap.h +++ b/src/stream_wrap.h @@ -86,6 +86,7 @@ class StreamWrap : public HandleWrap, public StreamBase { uv_write_cb cb); void DoAfterWrite(WriteWrap* w); void DoAlloc(size_t size, uv_buf_t* buf); + void DoRead(size_t nread, const uv_buf_t* buf, uv_handle_type pending); const char* Error() const; void ClearError(); From 924b327fbb924129569c4f453688947a5ccb21bc Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Wed, 11 Feb 2015 16:56:31 +0300 Subject: [PATCH 10/41] stream_base: move to callbacks --- src/stream_base.cc | 2 +- src/stream_base.h | 55 +++++++++++++++++++++++++++++++++++++++++----- src/stream_wrap.cc | 30 ++++++++++++++++--------- src/stream_wrap.h | 11 +++++++--- 4 files changed, 79 insertions(+), 19 deletions(-) diff --git a/src/stream_base.cc b/src/stream_base.cc index 77611761fb822a..ec02cd0578f298 100644 --- a/src/stream_base.cc +++ b/src/stream_base.cc @@ -458,7 +458,7 @@ void StreamBase::AfterWrite(uv_write_t* req, int status) { // Unref handle property Local req_wrap_obj = req_wrap->object(); req_wrap_obj->Delete(env->handle_string()); - wrap->DoAfterWrite(req_wrap); + wrap->OnAfterWrite(req_wrap); Local argv[] = { Integer::New(env->isolate(), status), diff --git a/src/stream_base.h b/src/stream_base.h index 7edb6e928c5071..d3e45bccdc653e 100644 --- a/src/stream_base.h +++ b/src/stream_base.h @@ -67,6 +67,18 @@ class WriteWrap: public ReqWrap { class StreamResource { public: + typedef void (*AfterWriteCb)(WriteWrap* w, void* ctx); + typedef void (*AllocCb)(size_t size, uv_buf_t* buf, void* ctx); + typedef void (*ReadCb)(size_t nread, + const uv_buf_t* buf, + uv_handle_type pending, + void* ctx); + + StreamResource() : after_write_cb_(nullptr), + alloc_cb_(nullptr), + read_cb_(nullptr) { + } + virtual ~StreamResource() = default; virtual int DoShutdown(ShutdownWrap* req_wrap, uv_shutdown_cb cb) = 0; @@ -76,13 +88,46 @@ class StreamResource { size_t count, uv_stream_t* send_handle, uv_write_cb cb) = 0; - virtual void DoAfterWrite(WriteWrap* w) = 0; - virtual void DoAlloc(size_t size, uv_buf_t* buf) = 0; - virtual void DoRead(size_t nread, - const uv_buf_t* buf, - uv_handle_type pending) = 0; virtual const char* Error() const = 0; virtual void ClearError() = 0; + + // Events + inline void OnAfterWrite(WriteWrap* w) { + after_write_cb_(w, after_write_ctx_); + } + + inline void OnAlloc(size_t size, uv_buf_t* buf) { + alloc_cb_(size, buf, alloc_ctx_); + } + + inline void OnRead(size_t nread, + const uv_buf_t* buf, + uv_handle_type pending) { + read_cb_(nread, buf, pending, read_ctx_); + } + + inline void set_after_write_cb(AfterWriteCb cb, void* ctx) { + after_write_ctx_ = ctx; + after_write_cb_ = cb; + } + + inline void set_alloc_cb(AllocCb cb, void* ctx) { + alloc_cb_ = cb; + alloc_ctx_ = ctx; + } + + inline void set_read_cb(ReadCb cb, void* ctx) { + read_cb_ = cb; + read_ctx_ = ctx; + } + + private: + AfterWriteCb after_write_cb_; + void* after_write_ctx_; + AllocCb alloc_cb_; + void* alloc_ctx_; + ReadCb read_cb_; + void* read_ctx_; }; class StreamBase : public StreamResource { diff --git a/src/stream_wrap.cc b/src/stream_wrap.cc index f0fbfd6a2da0cb..8574c73fc2ccfe 100644 --- a/src/stream_wrap.cc +++ b/src/stream_wrap.cc @@ -73,6 +73,9 @@ StreamWrap::StreamWrap(Environment* env, default_callbacks_(this), callbacks_(&default_callbacks_), callbacks_gc_(false) { + set_after_write_cb(OnAfterWriteImpl, this); + set_alloc_cb(OnAllocImpl, this); + set_read_cb(OnReadImpl, this); } @@ -124,19 +127,25 @@ void StreamWrap::OnAlloc(uv_handle_t* handle, uv_buf_t* buf) { StreamWrap* wrap = static_cast(handle->data); CHECK_EQ(wrap->stream(), reinterpret_cast(handle)); - return wrap->DoAlloc(suggested_size, buf); + + return static_cast(wrap)->OnAlloc(suggested_size, buf); } -void StreamWrap::DoAlloc(size_t size, uv_buf_t* buf) { - callbacks()->DoAlloc(reinterpret_cast(stream()), size, buf); +void StreamWrap::OnAllocImpl(size_t size, uv_buf_t* buf, void* ctx) { + StreamWrap* wrap = static_cast(ctx); + wrap->callbacks()->DoAlloc(reinterpret_cast(wrap->stream()), + size, + buf); } -void StreamWrap::DoRead(size_t nread, - const uv_buf_t* buf, - uv_handle_type pending) { - callbacks()->DoRead(stream(), nread, buf, pending); +void StreamWrap::OnReadImpl(size_t nread, + const uv_buf_t* buf, + uv_handle_type pending, + void* ctx) { + StreamWrap* wrap = static_cast(ctx); + wrap->callbacks()->DoRead(wrap->stream(), nread, buf, pending); } @@ -180,7 +189,7 @@ void StreamWrap::OnReadCommon(uv_stream_t* handle, } } - wrap->DoRead(nread, buf, pending); + static_cast(wrap)->OnRead(nread, buf, pending); } @@ -223,8 +232,9 @@ int StreamWrap::DoWrite(WriteWrap* w, } -void StreamWrap::DoAfterWrite(WriteWrap* w) { - return callbacks()->AfterWrite(w); +void StreamWrap::OnAfterWriteImpl(WriteWrap* w, void* ctx) { + StreamWrap* wrap = static_cast(ctx); + wrap->callbacks()->AfterWrite(w); } diff --git a/src/stream_wrap.h b/src/stream_wrap.h index ab68c3a2799324..dc5ac40a178258 100644 --- a/src/stream_wrap.h +++ b/src/stream_wrap.h @@ -84,9 +84,6 @@ class StreamWrap : public HandleWrap, public StreamBase { size_t count, uv_stream_t* send_handle, uv_write_cb cb); - void DoAfterWrite(WriteWrap* w); - void DoAlloc(size_t size, uv_buf_t* buf); - void DoRead(size_t nread, const uv_buf_t* buf, uv_handle_type pending); const char* Error() const; void ClearError(); @@ -143,6 +140,14 @@ class StreamWrap : public HandleWrap, public StreamBase { const uv_buf_t* buf, uv_handle_type pending); + // Resource interface implementation + static void OnAfterWriteImpl(WriteWrap* w, void* ctx); + static void OnAllocImpl(size_t size, uv_buf_t* buf, void* ctx); + static void OnReadImpl(size_t nread, + const uv_buf_t* buf, + uv_handle_type pending, + void* ctx); + uv_stream_t* const stream_; StreamWrapCallbacks default_callbacks_; StreamWrapCallbacks* callbacks_; // Overridable callbacks From 96cfde629f1455844ed242b5e335fb657471464d Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Wed, 11 Feb 2015 23:13:28 +0300 Subject: [PATCH 11/41] tls_wrap: rewrite (wip) --- src/node_crypto.cc | 20 ++-- src/stream_base.cc | 3 +- src/stream_base.h | 4 +- src/stream_wrap.cc | 217 ++++++++++++++++-------------------------- src/stream_wrap.h | 83 +++------------- src/tls_wrap.cc | 232 ++++++++++++++++++++++++++------------------- src/tls_wrap.h | 56 +++++++---- 7 files changed, 276 insertions(+), 339 deletions(-) diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 230231080b4d87..5b934e55f174a8 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -3,7 +3,7 @@ #include "node_crypto.h" #include "node_crypto_bio.h" #include "node_crypto_groups.h" -#include "tls_wrap.h" // TLSCallbacks +#include "tls_wrap.h" // TLSWrap #include "async-wrap.h" #include "async-wrap-inl.h" @@ -98,28 +98,28 @@ const char* const root_certs[] = { X509_STORE* root_cert_store; // Just to generate static methods -template class SSLWrap; -template void SSLWrap::AddMethods(Environment* env, +template class SSLWrap; +template void SSLWrap::AddMethods(Environment* env, Handle t); -template void SSLWrap::InitNPN(SecureContext* sc); -template SSL_SESSION* SSLWrap::GetSessionCallback( +template void SSLWrap::InitNPN(SecureContext* sc); +template SSL_SESSION* SSLWrap::GetSessionCallback( SSL* s, unsigned char* key, int len, int* copy); -template int SSLWrap::NewSessionCallback(SSL* s, +template int SSLWrap::NewSessionCallback(SSL* s, SSL_SESSION* sess); -template void SSLWrap::OnClientHello( +template void SSLWrap::OnClientHello( void* arg, const ClientHelloParser::ClientHello& hello); #ifdef OPENSSL_NPN_NEGOTIATED -template int SSLWrap::AdvertiseNextProtoCallback( +template int SSLWrap::AdvertiseNextProtoCallback( SSL* s, const unsigned char** data, unsigned int* len, void* arg); -template int SSLWrap::SelectNextProtoCallback( +template int SSLWrap::SelectNextProtoCallback( SSL* s, unsigned char** out, unsigned char* outlen, @@ -127,7 +127,7 @@ template int SSLWrap::SelectNextProtoCallback( unsigned int inlen, void* arg); #endif -template int SSLWrap::TLSExtStatusCallback(SSL* s, void* arg); +template int SSLWrap::TLSExtStatusCallback(SSL* s, void* arg); static void crypto_threadid_cb(CRYPTO_THREADID* tid) { diff --git a/src/stream_base.cc b/src/stream_base.cc index ec02cd0578f298..13f0b8bb83060e 100644 --- a/src/stream_base.cc +++ b/src/stream_base.cc @@ -33,8 +33,7 @@ template void StreamBase::AddMethods(Environment* env, Handle t); -StreamBase::StreamBase(Environment* env, Local object) - : consumed_(false) { +StreamBase::StreamBase() : consumed_(false) { } diff --git a/src/stream_base.h b/src/stream_base.h index d3e45bccdc653e..fe314c96cd5c23 100644 --- a/src/stream_base.h +++ b/src/stream_base.h @@ -69,7 +69,7 @@ class StreamResource { public: typedef void (*AfterWriteCb)(WriteWrap* w, void* ctx); typedef void (*AllocCb)(size_t size, uv_buf_t* buf, void* ctx); - typedef void (*ReadCb)(size_t nread, + typedef void (*ReadCb)(ssize_t nread, const uv_buf_t* buf, uv_handle_type pending, void* ctx); @@ -149,7 +149,7 @@ class StreamBase : public StreamResource { inline void Consume() { consumed_ = true; } protected: - StreamBase(Environment* env, v8::Local object); + StreamBase(); virtual ~StreamBase() = default; virtual v8::Local GetObject() = 0; diff --git a/src/stream_wrap.cc b/src/stream_wrap.cc index 8574c73fc2ccfe..1bb3c5e3e8b5de 100644 --- a/src/stream_wrap.cc +++ b/src/stream_wrap.cc @@ -68,11 +68,7 @@ StreamWrap::StreamWrap(Environment* env, reinterpret_cast(stream), provider, parent), - StreamBase(env, object), - stream_(stream), - default_callbacks_(this), - callbacks_(&default_callbacks_), - callbacks_gc_(false) { + stream_(stream) { set_after_write_cb(OnAfterWriteImpl, this); set_alloc_cb(OnAllocImpl, this); set_read_cb(OnReadImpl, this); @@ -133,26 +129,19 @@ void StreamWrap::OnAlloc(uv_handle_t* handle, void StreamWrap::OnAllocImpl(size_t size, uv_buf_t* buf, void* ctx) { - StreamWrap* wrap = static_cast(ctx); - wrap->callbacks()->DoAlloc(reinterpret_cast(wrap->stream()), - size, - buf); -} - + buf->base = static_cast(malloc(size)); + buf->len = size; -void StreamWrap::OnReadImpl(size_t nread, - const uv_buf_t* buf, - uv_handle_type pending, - void* ctx) { - StreamWrap* wrap = static_cast(ctx); - wrap->callbacks()->DoRead(wrap->stream(), nread, buf, pending); + if (buf->base == nullptr && size > 0) { + FatalError( + "node::StreamWrap::DoAlloc(size_t, uv_buf_t*, void*)", + "Out Of Memory"); + } } template -static Local AcceptHandle(Environment* env, - uv_stream_t* pipe, - AsyncWrap* parent) { +static Local AcceptHandle(Environment* env, StreamWrap* parent) { EscapableHandleScope scope(env->isolate()); Local wrap_obj; UVType* handle; @@ -164,13 +153,64 @@ static Local AcceptHandle(Environment* env, WrapType* wrap = Unwrap(wrap_obj); handle = wrap->UVHandle(); - if (uv_accept(pipe, reinterpret_cast(handle))) + if (uv_accept(parent->stream(), reinterpret_cast(handle))) abort(); return scope.Escape(wrap_obj); } +void StreamWrap::OnReadImpl(ssize_t nread, + const uv_buf_t* buf, + uv_handle_type pending, + void* ctx) { + StreamWrap* wrap = static_cast(ctx); + Environment* env = wrap->env(); + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); + + Local argv[] = { + Integer::New(env->isolate(), nread), + Undefined(env->isolate()), + Undefined(env->isolate()) + }; + + if (nread < 0) { + if (buf->base != nullptr) + free(buf->base); + wrap->MakeCallback(env->onread_string(), ARRAY_SIZE(argv), argv); + return; + } + + if (nread == 0) { + if (buf->base != nullptr) + free(buf->base); + return; + } + + char* base = static_cast(realloc(buf->base, nread)); + CHECK_LE(static_cast(nread), buf->len); + argv[1] = Buffer::Use(env, base, nread); + + Local pending_obj; + if (pending == UV_TCP) { + pending_obj = AcceptHandle(env, wrap); + } else if (pending == UV_NAMED_PIPE) { + pending_obj = AcceptHandle(env, wrap); + } else if (pending == UV_UDP) { + pending_obj = AcceptHandle(env, wrap); + } else { + CHECK_EQ(pending, UV_UNKNOWN_HANDLE); + } + + if (!pending_obj.IsEmpty()) { + argv[2] = pending_obj; + } + + wrap->MakeCallback(env->onread_string(), ARRAY_SIZE(argv), argv); +} + + void StreamWrap::OnReadCommon(uv_stream_t* handle, ssize_t nread, const uv_buf_t* buf, @@ -214,46 +254,7 @@ int StreamWrap::SetBlocking(bool enable) { int StreamWrap::DoShutdown(ShutdownWrap* req_wrap, uv_shutdown_cb cb) { - return callbacks()->DoShutdown(req_wrap, cb); -} - - -int StreamWrap::DoTryWrite(uv_buf_t** bufs, size_t* count) { - return callbacks()->TryWrite(bufs, count); -} - - -int StreamWrap::DoWrite(WriteWrap* w, - uv_buf_t* bufs, - size_t count, - uv_stream_t* send_handle, - uv_write_cb cb) { - return callbacks()->DoWrite(w, bufs, count, send_handle, cb); -} - - -void StreamWrap::OnAfterWriteImpl(WriteWrap* w, void* ctx) { - StreamWrap* wrap = static_cast(ctx); - wrap->callbacks()->AfterWrite(w); -} - - -const char* StreamWrap::Error() const { - return callbacks()->Error(); -} - - -void StreamWrap::ClearError() { - return callbacks()->ClearError(); -} - - -const char* StreamWrapCallbacks::Error() const { - return nullptr; -} - - -void StreamWrapCallbacks::ClearError() { + return uv_shutdown(&req_wrap->req_, stream(), cb); } @@ -261,13 +262,13 @@ void StreamWrapCallbacks::ClearError() { // values, shifting their base and decrementing their length. This is // required in order to skip the data that was successfully written via // uv_try_write(). -int StreamWrapCallbacks::TryWrite(uv_buf_t** bufs, size_t* count) { +int StreamWrap::DoTryWrite(uv_buf_t** bufs, size_t* count) { int err; size_t written; uv_buf_t* vbufs = *bufs; size_t vcount = *count; - err = uv_try_write(wrap()->stream(), vbufs, vcount); + err = uv_try_write(stream(), vbufs, vcount); if (err == UV_ENOSYS || err == UV_EAGAIN) return 0; if (err < 0) @@ -297,106 +298,50 @@ int StreamWrapCallbacks::TryWrite(uv_buf_t** bufs, size_t* count) { } -int StreamWrapCallbacks::DoWrite(WriteWrap* w, - uv_buf_t* bufs, - size_t count, - uv_stream_t* send_handle, - uv_write_cb cb) { +int StreamWrap::DoWrite(WriteWrap* w, + uv_buf_t* bufs, + size_t count, + uv_stream_t* send_handle, + uv_write_cb cb) { int r; if (send_handle == nullptr) { - r = uv_write(&w->req_, wrap()->stream(), bufs, count, cb); + r = uv_write(&w->req_, stream(), bufs, count, cb); } else { - r = uv_write2(&w->req_, wrap()->stream(), bufs, count, send_handle, cb); + r = uv_write2(&w->req_, stream(), bufs, count, send_handle, cb); } if (!r) { size_t bytes = 0; for (size_t i = 0; i < count; i++) bytes += bufs[i].len; - if (wrap()->stream()->type == UV_TCP) { + if (stream()->type == UV_TCP) { NODE_COUNT_NET_BYTES_SENT(bytes); - } else if (wrap()->stream()->type == UV_NAMED_PIPE) { + } else if (stream()->type == UV_NAMED_PIPE) { NODE_COUNT_PIPE_BYTES_SENT(bytes); } } - wrap()->UpdateWriteQueueSize(); + UpdateWriteQueueSize(); return r; } -void StreamWrapCallbacks::AfterWrite(WriteWrap* w) { - wrap()->UpdateWriteQueueSize(); -} -void StreamWrapCallbacks::DoAlloc(uv_handle_t* handle, - size_t suggested_size, - uv_buf_t* buf) { - buf->base = static_cast(malloc(suggested_size)); - buf->len = suggested_size; - - if (buf->base == nullptr && suggested_size > 0) { - FatalError( - "node::StreamWrapCallbacks::DoAlloc(uv_handle_t*, size_t, uv_buf_t*)", - "Out Of Memory"); - } +void StreamWrap::OnAfterWriteImpl(WriteWrap* w, void* ctx) { + StreamWrap* wrap = static_cast(ctx); + wrap->UpdateWriteQueueSize(); } -void StreamWrapCallbacks::DoRead(uv_stream_t* handle, - ssize_t nread, - const uv_buf_t* buf, - uv_handle_type pending) { - Environment* env = wrap()->env(); - HandleScope handle_scope(env->isolate()); - Context::Scope context_scope(env->context()); - - Local argv[] = { - Integer::New(env->isolate(), nread), - Undefined(env->isolate()), - Undefined(env->isolate()) - }; - - if (nread < 0) { - if (buf->base != nullptr) - free(buf->base); - wrap()->MakeCallback(env->onread_string(), ARRAY_SIZE(argv), argv); - return; - } - - if (nread == 0) { - if (buf->base != nullptr) - free(buf->base); - return; - } - - char* base = static_cast(realloc(buf->base, nread)); - CHECK_LE(static_cast(nread), buf->len); - argv[1] = Buffer::Use(env, base, nread); - - Local pending_obj; - if (pending == UV_TCP) { - pending_obj = AcceptHandle(env, handle, wrap()); - } else if (pending == UV_NAMED_PIPE) { - pending_obj = AcceptHandle(env, handle, wrap()); - } else if (pending == UV_UDP) { - pending_obj = AcceptHandle(env, handle, wrap()); - } else { - CHECK_EQ(pending, UV_UNKNOWN_HANDLE); - } - - if (!pending_obj.IsEmpty()) { - argv[2] = pending_obj; - } - - wrap()->MakeCallback(env->onread_string(), ARRAY_SIZE(argv), argv); +const char* StreamWrap::Error() const { + return nullptr; } -int StreamWrapCallbacks::DoShutdown(ShutdownWrap* req_wrap, uv_shutdown_cb cb) { - return uv_shutdown(&req_wrap->req_, wrap()->stream(), cb); +void StreamWrap::ClearError() { + // No-op } } // namespace node diff --git a/src/stream_wrap.h b/src/stream_wrap.h index dc5ac40a178258..56c9cc0139cd20 100644 --- a/src/stream_wrap.h +++ b/src/stream_wrap.h @@ -14,82 +14,30 @@ namespace node { // Forward declaration class StreamWrap; -// Overridable callbacks' types -class StreamWrapCallbacks { - public: - explicit StreamWrapCallbacks(StreamWrap* wrap) : wrap_(wrap) { - } - - explicit StreamWrapCallbacks(StreamWrapCallbacks* old) : wrap_(old->wrap()) { - } - - virtual ~StreamWrapCallbacks() = default; - - virtual const char* Error() const; - virtual void ClearError(); - - virtual int TryWrite(uv_buf_t** bufs, size_t* count); - - virtual int DoWrite(WriteWrap* w, - uv_buf_t* bufs, - size_t count, - uv_stream_t* send_handle, - uv_write_cb cb); - virtual void AfterWrite(WriteWrap* w); - virtual void DoAlloc(uv_handle_t* handle, - size_t suggested_size, - uv_buf_t* buf); - virtual void DoRead(uv_stream_t* handle, - ssize_t nread, - const uv_buf_t* buf, - uv_handle_type pending); - virtual int DoShutdown(ShutdownWrap* req_wrap, uv_shutdown_cb cb); - - protected: - inline StreamWrap* wrap() const { - return wrap_; - } - - private: - StreamWrap* const wrap_; -}; - class StreamWrap : public HandleWrap, public StreamBase { public: static void Initialize(v8::Handle target, v8::Handle unused, v8::Handle context); - void OverrideCallbacks(StreamWrapCallbacks* callbacks, bool gc) { - StreamWrapCallbacks* old = callbacks_; - callbacks_ = callbacks; - callbacks_gc_ = gc; - if (old != &default_callbacks_) - delete old; - } - - int GetFD() const; - bool IsAlive() const; + int GetFD() const override; + bool IsAlive() const override; // JavaScript functions - int ReadStart(); - int ReadStop(); - int SetBlocking(bool enable); + int ReadStart() override; + int ReadStop() override; + int SetBlocking(bool enable) override; // Resource implementation - int DoShutdown(ShutdownWrap* req_wrap, uv_shutdown_cb cb); - int DoTryWrite(uv_buf_t** bufs, size_t* count); + int DoShutdown(ShutdownWrap* req_wrap, uv_shutdown_cb cb) override; + int DoTryWrite(uv_buf_t** bufs, size_t* count) override; int DoWrite(WriteWrap* w, uv_buf_t* bufs, size_t count, uv_stream_t* send_handle, - uv_write_cb cb); - const char* Error() const; - void ClearError(); - - inline StreamWrapCallbacks* callbacks() const { - return callbacks_; - } + uv_write_cb cb) override; + const char* Error() const override; + void ClearError() override; inline uv_stream_t* stream() const { return stream_; @@ -116,10 +64,6 @@ class StreamWrap : public HandleWrap, public StreamBase { AsyncWrap* parent = nullptr); ~StreamWrap() { - if (!callbacks_gc_ && callbacks_ != &default_callbacks_) { - delete callbacks_; - } - callbacks_ = nullptr; } v8::Local GetObject(); @@ -143,17 +87,12 @@ class StreamWrap : public HandleWrap, public StreamBase { // Resource interface implementation static void OnAfterWriteImpl(WriteWrap* w, void* ctx); static void OnAllocImpl(size_t size, uv_buf_t* buf, void* ctx); - static void OnReadImpl(size_t nread, + static void OnReadImpl(ssize_t nread, const uv_buf_t* buf, uv_handle_type pending, void* ctx); uv_stream_t* const stream_; - StreamWrapCallbacks default_callbacks_; - StreamWrapCallbacks* callbacks_; // Overridable callbacks - bool callbacks_gc_; - - friend class StreamWrapCallbacks; }; diff --git a/src/tls_wrap.cc b/src/tls_wrap.cc index 9aafe3925dc8e2..5d7193b26b2b2f 100644 --- a/src/tls_wrap.cc +++ b/src/tls_wrap.cc @@ -33,17 +33,18 @@ using v8::String; using v8::Value; -TLSCallbacks::TLSCallbacks(Environment* env, - Kind kind, - Handle sc, - StreamWrapCallbacks* old) - : SSLWrap(env, Unwrap(sc), kind), - StreamWrapCallbacks(old), +TLSWrap::TLSWrap(Environment* env, + Kind kind, + Handle stream, + Handle sc) + : SSLWrap(env, Unwrap(sc), kind), AsyncWrap(env, env->tls_wrap_constructor_function()->NewInstance(), AsyncWrap::PROVIDER_TLSWRAP), sc_(Unwrap(sc)), sc_handle_(env->isolate(), sc), + stream_(Unwrap(stream)), + stream_handle_(env->isolate(), stream), enc_in_(nullptr), enc_out_(nullptr), clear_in_(nullptr), @@ -58,14 +59,19 @@ TLSCallbacks::TLSCallbacks(Environment* env, MakeWeak(this); // We've our own session callbacks - SSL_CTX_sess_set_get_cb(sc_->ctx_, SSLWrap::GetSessionCallback); - SSL_CTX_sess_set_new_cb(sc_->ctx_, SSLWrap::NewSessionCallback); + SSL_CTX_sess_set_get_cb(sc_->ctx_, SSLWrap::GetSessionCallback); + SSL_CTX_sess_set_new_cb(sc_->ctx_, SSLWrap::NewSessionCallback); + + stream_->set_after_write_cb(OnAfterWriteImpl, this); + stream_->set_alloc_cb(OnAllocImpl, this); + stream_->set_read_cb(OnReadImpl, this); + stream_->Consume(); InitSSL(); } -TLSCallbacks::~TLSCallbacks() { +TLSWrap::~TLSWrap() { enc_in_ = nullptr; enc_out_ = nullptr; delete clear_in_; @@ -73,6 +79,7 @@ TLSCallbacks::~TLSCallbacks() { sc_ = nullptr; sc_handle_.Reset(); + stream_handle_.Reset(); persistent().Reset(); #ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB @@ -111,12 +118,12 @@ bool TLSCallbacks::InvokeQueued(int status) { } -void TLSCallbacks::NewSessionDoneCb() { +void TLSWrap::NewSessionDoneCb() { Cycle(); } -void TLSCallbacks::InitSSL() { +void TLSWrap::InitSSL() { // Initialize SSL enc_in_ = NodeBIO::New(); enc_out_ = NodeBIO::New(); @@ -158,7 +165,7 @@ void TLSCallbacks::InitSSL() { } -void TLSCallbacks::Wrap(const FunctionCallbackInfo& args) { +void TLSWrap::Wrap(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); if (args.Length() < 1 || !args[0]->IsObject()) { @@ -174,40 +181,32 @@ void TLSCallbacks::Wrap(const FunctionCallbackInfo& args) { Local stream = args[0].As(); Local sc = args[1].As(); - Kind kind = args[2]->IsTrue() ? SSLWrap::kServer : - SSLWrap::kClient; - - TLSCallbacks* callbacks = nullptr; - WITH_GENERIC_STREAM(env, stream, { - callbacks = new TLSCallbacks(env, kind, sc, wrap->callbacks()); - wrap->OverrideCallbacks(callbacks, true); - }); + Kind kind = args[2]->IsTrue() ? SSLWrap::kServer : + SSLWrap::kClient; - if (callbacks == nullptr) { - return args.GetReturnValue().SetNull(); - } + TLSWrap* wrap = new TLSWrap(env, kind, stream, sc); - args.GetReturnValue().Set(callbacks->persistent()); + args.GetReturnValue().Set(wrap->persistent()); } -void TLSCallbacks::Receive(const FunctionCallbackInfo& args) { - TLSCallbacks* wrap = Unwrap(args.Holder()); +void TLSWrap::Receive(const FunctionCallbackInfo& args) { + TLSWrap* wrap = Unwrap(args.Holder()); CHECK(Buffer::HasInstance(args[0])); char* data = Buffer::Data(args[0]); size_t len = Buffer::Length(args[0]); uv_buf_t buf; - uv_stream_t* stream = wrap->wrap()->stream(); // Copy given buffer entirely or partiall if handle becomes closed - while (len > 0 && !uv_is_closing(reinterpret_cast(stream))) { - wrap->DoAlloc(reinterpret_cast(stream), len, &buf); + // XXX(indunty): figure out uv_is_closing + while (len > 0 /* && !uv_is_closing(reinterpret_cast(stream))*/) { + wrap->OnAlloc(len, &buf); size_t copy = buf.len > len ? len : buf.len; memcpy(buf.base, data, copy); buf.len = copy; - wrap->DoRead(stream, buf.len, &buf, UV_UNKNOWN_HANDLE); + wrap->OnRead(buf.len, &buf, UV_UNKNOWN_HANDLE); data += copy; len -= copy; @@ -215,10 +214,10 @@ void TLSCallbacks::Receive(const FunctionCallbackInfo& args) { } -void TLSCallbacks::Start(const FunctionCallbackInfo& args) { +void TLSWrap::Start(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - TLSCallbacks* wrap = Unwrap(args.Holder()); + TLSWrap* wrap = Unwrap(args.Holder()); if (wrap->started_) return env->ThrowError("Already started."); @@ -231,14 +230,14 @@ void TLSCallbacks::Start(const FunctionCallbackInfo& args) { } -void TLSCallbacks::SSLInfoCallback(const SSL* ssl_, int where, int ret) { +void TLSWrap::SSLInfoCallback(const SSL* ssl_, int where, int ret) { if (!(where & (SSL_CB_HANDSHAKE_START | SSL_CB_HANDSHAKE_DONE))) return; // Be compatible with older versions of OpenSSL. SSL_get_app_data() wants // a non-const SSL* in OpenSSL <= 0.9.7e. SSL* ssl = const_cast(ssl_); - TLSCallbacks* c = static_cast(SSL_get_app_data(ssl)); + TLSWrap* c = static_cast(SSL_get_app_data(ssl)); Environment* env = c->env(); HandleScope handle_scope(env->isolate()); Context::Scope context_scope(env->context()); @@ -261,7 +260,7 @@ void TLSCallbacks::SSLInfoCallback(const SSL* ssl_, int where, int ret) { } -void TLSCallbacks::EncOut() { +void TLSWrap::EncOut() { // Ignore cycling data if ClientHello wasn't yet parsed if (!hello_parser_.IsEnded()) return; @@ -291,47 +290,45 @@ void TLSCallbacks::EncOut() { write_size_ = NodeBIO::FromBIO(enc_out_)->PeekMultiple(data, size, &count); CHECK(write_size_ != 0 && count != 0); - write_req_.data = this; + Local req_wrap_obj = Object::New(env()->isolate()); + char* storage = new char[sizeof(WriteWrap)]; + WriteWrap* write_req = new(storage) WriteWrap(env(), req_wrap_obj, this); + uv_buf_t buf[ARRAY_SIZE(data)]; for (size_t i = 0; i < count; i++) buf[i] = uv_buf_init(data[i], size[i]); - int r = uv_write(&write_req_, wrap()->stream(), buf, count, EncOutCb); + int r = stream_->DoWrite(write_req, buf, count, nullptr, EncOutCb); // Ignore errors, this should be already handled in js - if (!r) { - if (wrap()->is_tcp()) { - NODE_COUNT_NET_BYTES_SENT(write_size_); - } else if (wrap()->is_named_pipe()) { - NODE_COUNT_PIPE_BYTES_SENT(write_size_); - } - } + if (!r) + NODE_COUNT_NET_BYTES_SENT(write_size_); } -void TLSCallbacks::EncOutCb(uv_write_t* req, int status) { - TLSCallbacks* callbacks = static_cast(req->data); +void TLSWrap::EncOutCb(uv_write_t* req, int status) { + TLSWrap* wrap = static_cast(req->data); // Handle error if (status) { // Ignore errors after shutdown - if (callbacks->shutdown_) + if (wrap->shutdown_) return; // Notify about error - callbacks->InvokeQueued(status); + wrap->InvokeQueued(status); return; } // Commit - NodeBIO::FromBIO(callbacks->enc_out_)->Read(nullptr, callbacks->write_size_); + NodeBIO::FromBIO(wrap->enc_out_)->Read(nullptr, wrap->write_size_); // Try writing more data - callbacks->write_size_ = 0; - callbacks->EncOut(); + wrap->write_size_ = 0; + wrap->EncOut(); } -Local TLSCallbacks::GetSSLError(int status, int* err, const char** msg) { +Local TLSWrap::GetSSLError(int status, int* err, const char** msg) { EscapableHandleScope scope(env()->isolate()); *err = SSL_get_error(ssl_, status); @@ -373,7 +370,7 @@ Local TLSCallbacks::GetSSLError(int status, int* err, const char** msg) { } -void TLSCallbacks::ClearOut() { +void TLSWrap::ClearOut() { // Ignore cycling data if ClientHello wasn't yet parsed if (!hello_parser_.IsEnded()) return; @@ -396,7 +393,7 @@ void TLSCallbacks::ClearOut() { Integer::New(env()->isolate(), read), Buffer::New(env(), out, read) }; - wrap()->MakeCallback(env()->onread_string(), ARRAY_SIZE(argv), argv); + MakeCallback(env()->onread_string(), ARRAY_SIZE(argv), argv); } } while (read > 0); @@ -404,7 +401,7 @@ void TLSCallbacks::ClearOut() { if (!eof_ && flags & SSL_RECEIVED_SHUTDOWN) { eof_ = true; Local arg = Integer::New(env()->isolate(), UV_EOF); - wrap()->MakeCallback(env()->onread_string(), 1, &arg); + MakeCallback(env()->onread_string(), 1, &arg); } if (read == -1) { @@ -427,7 +424,7 @@ void TLSCallbacks::ClearOut() { } -bool TLSCallbacks::ClearIn() { +bool TLSWrap::ClearIn() { // Ignore cycling data if ClientHello wasn't yet parsed if (!hello_parser_.IsEnded()) return false; @@ -466,28 +463,28 @@ bool TLSCallbacks::ClearIn() { } -const char* TLSCallbacks::Error() const { +const char* TLSWrap::Error() const { return error_; } -void TLSCallbacks::ClearError() { +void TLSWrap::ClearError() { delete[] error_; error_ = nullptr; } -int TLSCallbacks::TryWrite(uv_buf_t** bufs, size_t* count) { +int TLSWrap::DoTryWrite(uv_buf_t** bufs, size_t* count) { // TODO(indutny): Support it return 0; } -int TLSCallbacks::DoWrite(WriteWrap* w, - uv_buf_t* bufs, - size_t count, - uv_stream_t* send_handle, - uv_write_cb cb) { +int TLSWrap::DoWrite(WriteWrap* w, + uv_buf_t* bufs, + size_t count, + uv_stream_t* send_handle, + uv_write_cb cb) { CHECK_EQ(send_handle, nullptr); bool empty = true; @@ -504,7 +501,7 @@ int TLSCallbacks::DoWrite(WriteWrap* w, // However if there any data that should be written to socket, // callback should not be invoked immediately if (BIO_pending(enc_out_) == 0) - return uv_write(&w->req_, wrap()->stream(), bufs, count, cb); + return stream_->DoWrite(w, bufs, count, send_handle, cb); } // Queue callback to execute it on next tick @@ -552,24 +549,32 @@ int TLSCallbacks::DoWrite(WriteWrap* w, } -void TLSCallbacks::AfterWrite(WriteWrap* w) { +void TLSWrap::OnAfterWriteImpl(WriteWrap* w, void* ctx) { // Intentionally empty } -void TLSCallbacks::DoAlloc(uv_handle_t* handle, - size_t suggested_size, - uv_buf_t* buf) { +void TLSWrap::OnAllocImpl(size_t suggested_size, uv_buf_t* buf, void* ctx) { + TLSWrap* wrap = static_cast(ctx); + size_t size = 0; - buf->base = NodeBIO::FromBIO(enc_in_)->PeekWritable(&size); + buf->base = NodeBIO::FromBIO(wrap->enc_in_)->PeekWritable(&size); buf->len = size; } -void TLSCallbacks::DoRead(uv_stream_t* handle, - ssize_t nread, - const uv_buf_t* buf, - uv_handle_type pending) { +void TLSWrap::OnReadImpl(ssize_t nread, + const uv_buf_t* buf, + uv_handle_type pending, + void* ctx) { + TLSWrap* wrap = static_cast(ctx); + wrap->DoRead(nread, buf, pending); +} + + +void TLSWrap::DoRead(ssize_t nread, + const uv_buf_t* buf, + uv_handle_type pending) { if (nread < 0) { // Error should be emitted only after all data was read ClearOut(); @@ -584,7 +589,7 @@ void TLSCallbacks::DoRead(uv_stream_t* handle, HandleScope handle_scope(env()->isolate()); Context::Scope context_scope(env()->context()); Local arg = Integer::New(env()->isolate(), nread); - wrap()->MakeCallback(env()->onread_string(), 1, &arg); + MakeCallback(env()->onread_string(), 1, &arg); return; } @@ -608,19 +613,53 @@ void TLSCallbacks::DoRead(uv_stream_t* handle, } -int TLSCallbacks::DoShutdown(ShutdownWrap* req_wrap, uv_shutdown_cb cb) { +int TLSWrap::DoShutdown(ShutdownWrap* req_wrap, uv_shutdown_cb cb) { if (SSL_shutdown(ssl_) == 0) SSL_shutdown(ssl_); shutdown_ = true; EncOut(); - return StreamWrapCallbacks::DoShutdown(req_wrap, cb); + return stream_->DoShutdown(req_wrap, cb); +} + + +Local TLSWrap::GetObject() { + return object(); +} + + +bool TLSWrap::IsIPCPipe() const { + return false; +} + + +int TLSWrap::GetFD() const { + return stream_->GetFD(); +} + + +bool TLSWrap::IsAlive() const { + return stream_->IsAlive(); +} + + +int TLSWrap::ReadStart() { + return stream_->ReadStart(); +} + + +int TLSWrap::ReadStop() { + return stream_->ReadStop(); +} + +int TLSWrap::SetBlocking(bool enable) { + return stream_->SetBlocking(enable); } -void TLSCallbacks::SetVerifyMode(const FunctionCallbackInfo& args) { +void TLSWrap::SetVerifyMode(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - TLSCallbacks* wrap = Unwrap(args.Holder()); + TLSWrap* wrap = Unwrap(args.Holder()); if (args.Length() < 2 || !args[0]->IsBoolean() || !args[1]->IsBoolean()) return env->ThrowTypeError("Bad arguments, expected two booleans"); @@ -647,34 +686,34 @@ void TLSCallbacks::SetVerifyMode(const FunctionCallbackInfo& args) { } -void TLSCallbacks::EnableSessionCallbacks( +void TLSWrap::EnableSessionCallbacks( const FunctionCallbackInfo& args) { - TLSCallbacks* wrap = Unwrap(args.Holder()); + TLSWrap* wrap = Unwrap(args.Holder()); wrap->enable_session_callbacks(); EnableHelloParser(args); } -void TLSCallbacks::EnableHelloParser(const FunctionCallbackInfo& args) { - TLSCallbacks* wrap = Unwrap(args.Holder()); +void TLSWrap::EnableHelloParser(const FunctionCallbackInfo& args) { + TLSWrap* wrap = Unwrap(args.Holder()); NodeBIO::FromBIO(wrap->enc_in_)->set_initial(kMaxHelloLength); - wrap->hello_parser_.Start(SSLWrap::OnClientHello, + wrap->hello_parser_.Start(SSLWrap::OnClientHello, OnClientHelloParseEnd, wrap); } -void TLSCallbacks::OnClientHelloParseEnd(void* arg) { - TLSCallbacks* c = static_cast(arg); +void TLSWrap::OnClientHelloParseEnd(void* arg) { + TLSWrap* c = static_cast(arg); c->Cycle(); } #ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB -void TLSCallbacks::GetServername(const FunctionCallbackInfo& args) { +void TLSWrap::GetServername(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - TLSCallbacks* wrap = Unwrap(args.Holder()); + TLSWrap* wrap = Unwrap(args.Holder()); const char* servername = SSL_get_servername(wrap->ssl_, TLSEXT_NAMETYPE_host_name); @@ -686,10 +725,10 @@ void TLSCallbacks::GetServername(const FunctionCallbackInfo& args) { } -void TLSCallbacks::SetServername(const FunctionCallbackInfo& args) { +void TLSWrap::SetServername(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - TLSCallbacks* wrap = Unwrap(args.Holder()); + TLSWrap* wrap = Unwrap(args.Holder()); if (args.Length() < 1 || !args[0]->IsString()) return env->ThrowTypeError("First argument should be a string"); @@ -707,8 +746,8 @@ void TLSCallbacks::SetServername(const FunctionCallbackInfo& args) { } -int TLSCallbacks::SelectSNIContextCallback(SSL* s, int* ad, void* arg) { - TLSCallbacks* p = static_cast(SSL_get_app_data(s)); +int TLSWrap::SelectSNIContextCallback(SSL* s, int* ad, void* arg) { + TLSWrap* p = static_cast(SSL_get_app_data(s)); Environment* env = p->env(); const char* servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name); @@ -744,12 +783,12 @@ int TLSCallbacks::SelectSNIContextCallback(SSL* s, int* ad, void* arg) { #endif // SSL_CTRL_SET_TLSEXT_SERVERNAME_CB -void TLSCallbacks::Initialize(Handle target, +void TLSWrap::Initialize(Handle target, Handle unused, Handle context) { Environment* env = Environment::GetCurrent(context); - env->SetMethod(target, "wrap", TLSCallbacks::Wrap); + env->SetMethod(target, "wrap", TLSWrap::Wrap); Local t = FunctionTemplate::New(env->isolate()); t->InstanceTemplate()->SetInternalFieldCount(1); @@ -761,7 +800,8 @@ void TLSCallbacks::Initialize(Handle target, env->SetProtoMethod(t, "enableSessionCallbacks", EnableSessionCallbacks); env->SetProtoMethod(t, "enableHelloParser", EnableHelloParser); - SSLWrap::AddMethods(env, t); + StreamWrap::AddMethods(env, t); + SSLWrap::AddMethods(env, t); #ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB env->SetProtoMethod(t, "getServername", GetServername); @@ -773,4 +813,4 @@ void TLSCallbacks::Initialize(Handle target, } // namespace node -NODE_MODULE_CONTEXT_AWARE_BUILTIN(tls_wrap, node::TLSCallbacks::Initialize) +NODE_MODULE_CONTEXT_AWARE_BUILTIN(tls_wrap, node::TLSWrap::Initialize) diff --git a/src/tls_wrap.h b/src/tls_wrap.h index 3815878d586c15..1beb09249df4b8 100644 --- a/src/tls_wrap.h +++ b/src/tls_wrap.h @@ -21,33 +21,33 @@ namespace crypto { class SecureContext; } -class TLSCallbacks : public crypto::SSLWrap, - public StreamWrapCallbacks, - public AsyncWrap { +class TLSWrap : public crypto::SSLWrap, + public StreamBase, + public AsyncWrap { public: - ~TLSCallbacks() override; + ~TLSWrap() override; static void Initialize(v8::Handle target, v8::Handle unused, v8::Handle context); - const char* Error() const override; - void ClearError() override; - int TryWrite(uv_buf_t** bufs, size_t* count) override; + int GetFD() const override; + bool IsAlive() const override; + + // JavaScript functions + int ReadStart() override; + int ReadStop() override; + + int SetBlocking(bool enable) override; + int DoShutdown(ShutdownWrap* req_wrap, uv_shutdown_cb cb) override; + int DoTryWrite(uv_buf_t** bufs, size_t* count) override; int DoWrite(WriteWrap* w, uv_buf_t* bufs, size_t count, uv_stream_t* send_handle, uv_write_cb cb) override; - void AfterWrite(WriteWrap* w) override; - void DoAlloc(uv_handle_t* handle, - size_t suggested_size, - uv_buf_t* buf) override; - void DoRead(uv_stream_t* handle, - ssize_t nread, - const uv_buf_t* buf, - uv_handle_type pending) override; - int DoShutdown(ShutdownWrap* req_wrap, uv_shutdown_cb cb) override; + const char* Error() const override; + void ClearError() override; void NewSessionDoneCb(); @@ -78,10 +78,10 @@ class TLSCallbacks : public crypto::SSLWrap, ListNode member_; }; - TLSCallbacks(Environment* env, - Kind kind, - v8::Handle sc, - StreamWrapCallbacks* old); + TLSWrap(Environment* env, + Kind kind, + v8::Handle stream, + v8::Handle sc); static void SSLInfoCallback(const SSL* ssl_, int where, int ret); void InitSSL(); @@ -104,6 +104,19 @@ class TLSCallbacks : public crypto::SSLWrap, } } + v8::Local GetObject(); + bool IsIPCPipe() const; + + // Resource implementation + static void OnAfterWriteImpl(WriteWrap* w, void* ctx); + static void OnAllocImpl(size_t size, uv_buf_t* buf, void* ctx); + static void OnReadImpl(ssize_t nread, + const uv_buf_t* buf, + uv_handle_type pending, + void* ctx); + + void DoRead(ssize_t nread, const uv_buf_t* buf, uv_handle_type pending); + // If |msg| is not nullptr, caller is responsible for calling `delete[] *msg`. v8::Local GetSSLError(int status, int* err, const char** msg); @@ -125,10 +138,11 @@ class TLSCallbacks : public crypto::SSLWrap, crypto::SecureContext* sc_; v8::Persistent sc_handle_; + StreamBase* stream_; + v8::Persistent stream_handle_; BIO* enc_in_; BIO* enc_out_; NodeBIO* clear_in_; - uv_write_t write_req_; size_t write_size_; size_t write_queue_size_; typedef ListHead WriteItemList; From fd7243f084469f328b59823e8afbce827025b0c2 Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Thu, 12 Feb 2015 01:04:39 +0300 Subject: [PATCH 12/41] tls_wrap: minor fixes --- lib/_tls_legacy.js | 4 +-- src/node_crypto.cc | 2 +- src/stream_base.h | 2 +- src/stream_wrap.h | 4 +-- src/tls_wrap.cc | 68 +++++++++++++++++++++++----------------------- src/tls_wrap.h | 4 +-- 6 files changed, 42 insertions(+), 42 deletions(-) diff --git a/lib/_tls_legacy.js b/lib/_tls_legacy.js index 4148085503fc64..e899002bac8325 100644 --- a/lib/_tls_legacy.js +++ b/lib/_tls_legacy.js @@ -92,11 +92,11 @@ function onCryptoStreamFinish() { // Generate close notify // NOTE: first call checks if client has sent us shutdown, // second call enqueues shutdown into the BIO. - if (this.pair.ssl.shutdown() !== 1) { + if (this.pair.ssl.sslShutdown() !== 1) { if (this.pair.ssl && this.pair.ssl.error) return this.pair.error(); - this.pair.ssl.shutdown(); + this.pair.ssl.sslShutdown(); } if (this.pair.ssl && this.pair.ssl.error) diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 5b934e55f174a8..b4a8d0ef6b8a5e 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -973,7 +973,7 @@ void SSLWrap::AddMethods(Environment* env, Handle t) { env->SetProtoMethod(t, "getCurrentCipher", GetCurrentCipher); env->SetProtoMethod(t, "endParser", EndParser); env->SetProtoMethod(t, "renegotiate", Renegotiate); - env->SetProtoMethod(t, "shutdown", Shutdown); + env->SetProtoMethod(t, "sslShutdown", Shutdown); env->SetProtoMethod(t, "getTLSTicket", GetTLSTicket); env->SetProtoMethod(t, "newSessionDone", NewSessionDone); env->SetProtoMethod(t, "setOCSPResponse", SetOCSPResponse); diff --git a/src/stream_base.h b/src/stream_base.h index fe314c96cd5c23..d550aa50b47fad 100644 --- a/src/stream_base.h +++ b/src/stream_base.h @@ -137,6 +137,7 @@ class StreamBase : public StreamResource { v8::Handle target); virtual bool IsAlive() const = 0; + virtual bool IsIPCPipe() const = 0; virtual int GetFD() const = 0; virtual int ReadStart() = 0; @@ -153,7 +154,6 @@ class StreamBase : public StreamResource { virtual ~StreamBase() = default; virtual v8::Local GetObject() = 0; - virtual bool IsIPCPipe() const = 0; // Libuv callbacks static void AfterShutdown(uv_shutdown_t* req, int status); diff --git a/src/stream_wrap.h b/src/stream_wrap.h index 56c9cc0139cd20..4510b93c800e2c 100644 --- a/src/stream_wrap.h +++ b/src/stream_wrap.h @@ -22,6 +22,7 @@ class StreamWrap : public HandleWrap, public StreamBase { int GetFD() const override; bool IsAlive() const override; + bool IsIPCPipe() const override; // JavaScript functions int ReadStart() override; @@ -66,8 +67,7 @@ class StreamWrap : public HandleWrap, public StreamBase { ~StreamWrap() { } - v8::Local GetObject(); - bool IsIPCPipe() const; + v8::Local GetObject() override; void UpdateWriteQueueSize(); private: diff --git a/src/tls_wrap.cc b/src/tls_wrap.cc index 5d7193b26b2b2f..215eaa286db2f2 100644 --- a/src/tls_wrap.cc +++ b/src/tls_wrap.cc @@ -463,6 +463,40 @@ bool TLSWrap::ClearIn() { } +Local TLSWrap::GetObject() { + return object(); +} + + +bool TLSWrap::IsIPCPipe() const { + return stream_->IsIPCPipe(); +} + + +int TLSWrap::GetFD() const { + return stream_->GetFD(); +} + + +bool TLSWrap::IsAlive() const { + return stream_->IsAlive(); +} + + +int TLSWrap::ReadStart() { + return stream_->ReadStart(); +} + + +int TLSWrap::ReadStop() { + return stream_->ReadStop(); +} + +int TLSWrap::SetBlocking(bool enable) { + return stream_->SetBlocking(enable); +} + + const char* TLSWrap::Error() const { return error_; } @@ -622,40 +656,6 @@ int TLSWrap::DoShutdown(ShutdownWrap* req_wrap, uv_shutdown_cb cb) { } -Local TLSWrap::GetObject() { - return object(); -} - - -bool TLSWrap::IsIPCPipe() const { - return false; -} - - -int TLSWrap::GetFD() const { - return stream_->GetFD(); -} - - -bool TLSWrap::IsAlive() const { - return stream_->IsAlive(); -} - - -int TLSWrap::ReadStart() { - return stream_->ReadStart(); -} - - -int TLSWrap::ReadStop() { - return stream_->ReadStop(); -} - -int TLSWrap::SetBlocking(bool enable) { - return stream_->SetBlocking(enable); -} - - void TLSWrap::SetVerifyMode(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); diff --git a/src/tls_wrap.h b/src/tls_wrap.h index 1beb09249df4b8..b21b9aef719410 100644 --- a/src/tls_wrap.h +++ b/src/tls_wrap.h @@ -104,8 +104,8 @@ class TLSWrap : public crypto::SSLWrap, } } - v8::Local GetObject(); - bool IsIPCPipe() const; + v8::Local GetObject() override; + bool IsIPCPipe() const override; // Resource implementation static void OnAfterWriteImpl(WriteWrap* w, void* ctx); From c7c9247563a32fc6fcfbf420b6c74c0341d8845e Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Thu, 12 Feb 2015 13:49:11 +0300 Subject: [PATCH 13/41] stream_base: better Consume() API --- src/stream_base.cc | 21 +++++++++++++++------ src/stream_base.h | 6 +++--- src/tcp_wrap.cc | 2 ++ src/tls_wrap.cc | 22 +++++++++++++++------- src/tls_wrap.h | 3 ++- 5 files changed, 37 insertions(+), 17 deletions(-) diff --git a/src/stream_base.cc b/src/stream_base.cc index 13f0b8bb83060e..39caa8d27c776f 100644 --- a/src/stream_base.cc +++ b/src/stream_base.cc @@ -33,7 +33,7 @@ template void StreamBase::AddMethods(Environment* env, Handle t); -StreamBase::StreamBase() : consumed_(false) { +StreamBase::StreamBase() : consumed_by_(nullptr) { } @@ -76,10 +76,15 @@ void StreamBase::AddMethods(Environment* env, Handle t) { template -void StreamBase::GetFD(Local, const PropertyCallbackInfo& args) { +void StreamBase::GetFD(Local key, + const PropertyCallbackInfo& args) { HandleScope scope(args.GetIsolate()); - Base* wrap = Unwrap(args.Holder()); - if (wrap->IsConsumed() || !wrap->IsAlive()) + StreamBase* wrap = Unwrap(args.Holder()); + + while (wrap->ConsumedBy() != nullptr) + wrap = wrap->ConsumedBy(); + + if (!wrap->IsAlive()) return args.GetReturnValue().Set(UV_EINVAL); args.GetReturnValue().Set(wrap->GetFD()); @@ -90,8 +95,12 @@ template & args)> void StreamBase::JSMethod(const FunctionCallbackInfo& args) { HandleScope scope(args.GetIsolate()); - Base* wrap = Unwrap(args.Holder()); - if (wrap->IsConsumed() || !wrap->IsAlive()) + StreamBase* wrap = Unwrap(args.Holder()); + + while (wrap->ConsumedBy() != nullptr) + wrap = wrap->ConsumedBy(); + + if (!wrap->IsAlive()) return args.GetReturnValue().Set(UV_EINVAL); args.GetReturnValue().Set((wrap->*Method)(args)); diff --git a/src/stream_base.h b/src/stream_base.h index d550aa50b47fad..987063215591a0 100644 --- a/src/stream_base.h +++ b/src/stream_base.h @@ -144,10 +144,10 @@ class StreamBase : public StreamResource { virtual int ReadStop() = 0; virtual int SetBlocking(bool enable) = 0; - inline bool IsConsumed() const { return consumed_; } + inline StreamBase* ConsumedBy() const { return consumed_by_; } // TODO(indutny): assert that stream is not yet consumed - inline void Consume() { consumed_ = true; } + inline void Consume(StreamBase* child) { consumed_by_ = child; } protected: StreamBase(); @@ -178,7 +178,7 @@ class StreamBase : public StreamResource { const v8::FunctionCallbackInfo& args)> static void JSMethod(const v8::FunctionCallbackInfo& args); - bool consumed_; + StreamBase* consumed_by_; }; } // namespace node diff --git a/src/tcp_wrap.cc b/src/tcp_wrap.cc index 441a464257c21c..d840c1ab3a283b 100644 --- a/src/tcp_wrap.cc +++ b/src/tcp_wrap.cc @@ -153,6 +153,7 @@ TCPWrap::TCPWrap(Environment* env, Handle object, AsyncWrap* parent) CHECK_EQ(r, 0); // How do we proxy this error up to javascript? // Suggestion: uv_tcp_init() returns void. UpdateWriteQueueSize(); + fprintf(stderr, "TCPWrap() tcp %p env %p\n", this, env); } @@ -361,6 +362,7 @@ void TCPWrap::Connect(const FunctionCallbackInfo& args) { if (err == 0) { TCPConnectWrap* req_wrap = new TCPConnectWrap(env, req_wrap_obj); + fprintf(stderr, "tcp_connect() tcp %p connect %p env %p\n", wrap, req_wrap, env); err = uv_tcp_connect(&req_wrap->req_, &wrap->handle_, reinterpret_cast(&addr), diff --git a/src/tls_wrap.cc b/src/tls_wrap.cc index 215eaa286db2f2..125602bc134ba7 100644 --- a/src/tls_wrap.cc +++ b/src/tls_wrap.cc @@ -35,7 +35,8 @@ using v8::Value; TLSWrap::TLSWrap(Environment* env, Kind kind, - Handle stream, + StreamBase* stream, + Handle stream_obj, Handle sc) : SSLWrap(env, Unwrap(sc), kind), AsyncWrap(env, @@ -43,8 +44,8 @@ TLSWrap::TLSWrap(Environment* env, AsyncWrap::PROVIDER_TLSWRAP), sc_(Unwrap(sc)), sc_handle_(env->isolate(), sc), - stream_(Unwrap(stream)), - stream_handle_(env->isolate(), stream), + stream_(stream), + stream_handle_(env->isolate(), stream_obj), enc_in_(nullptr), enc_out_(nullptr), clear_in_(nullptr), @@ -65,8 +66,9 @@ TLSWrap::TLSWrap(Environment* env, stream_->set_after_write_cb(OnAfterWriteImpl, this); stream_->set_alloc_cb(OnAllocImpl, this); stream_->set_read_cb(OnReadImpl, this); - stream_->Consume(); + stream_->Consume(this); + fprintf(stderr, "TLSWrap() %p\n", this); InitSSL(); } @@ -179,14 +181,20 @@ void TLSWrap::Wrap(const FunctionCallbackInfo& args) { if (args.Length() < 3 || !args[2]->IsBoolean()) return env->ThrowTypeError("Third argument should be boolean"); - Local stream = args[0].As(); + Local stream_obj = args[0].As(); Local sc = args[1].As(); Kind kind = args[2]->IsTrue() ? SSLWrap::kServer : SSLWrap::kClient; - TLSWrap* wrap = new TLSWrap(env, kind, stream, sc); + StreamBase* stream = nullptr; + WITH_GENERIC_STREAM(env, stream_obj, { + stream = wrap; + }); + CHECK_NE(stream, nullptr); - args.GetReturnValue().Set(wrap->persistent()); + TLSWrap* res = new TLSWrap(env, kind, stream, stream_obj, sc); + + args.GetReturnValue().Set(res->persistent()); } diff --git a/src/tls_wrap.h b/src/tls_wrap.h index b21b9aef719410..5bc4519c928831 100644 --- a/src/tls_wrap.h +++ b/src/tls_wrap.h @@ -80,7 +80,8 @@ class TLSWrap : public crypto::SSLWrap, TLSWrap(Environment* env, Kind kind, - v8::Handle stream, + StreamBase* steram, + v8::Handle stream_obj, v8::Handle sc); static void SSLInfoCallback(const SSL* ssl_, int where, int ret); From 8801cc354397053037f71bcdd28bb4612f373e2c Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Thu, 12 Feb 2015 14:24:30 +0300 Subject: [PATCH 14/41] stream_base: save progress --- src/env.h | 1 + src/stream_base.cc | 41 +++++++++++++++-------------------------- src/stream_base.h | 41 ++++++++++++++++++++++++++++++++--------- src/stream_wrap.cc | 22 ++++++++++++++++------ src/stream_wrap.h | 7 ++++--- src/tls_wrap.cc | 28 ++++++++++++++++------------ src/tls_wrap.h | 11 ++++------- 7 files changed, 88 insertions(+), 63 deletions(-) diff --git a/src/env.h b/src/env.h index ccacbb09f52c2f..9b0ec4376ba7b1 100644 --- a/src/env.h +++ b/src/env.h @@ -236,6 +236,7 @@ namespace node { V(tls_wrap_constructor_function, v8::Function) \ V(tty_constructor_template, v8::FunctionTemplate) \ V(udp_constructor_function, v8::Function) \ + V(write_wrap_constructor_function, v8::Function) \ class Environment; diff --git a/src/stream_base.cc b/src/stream_base.cc index 39caa8d27c776f..cf8e3b527c4774 100644 --- a/src/stream_base.cc +++ b/src/stream_base.cc @@ -123,8 +123,12 @@ int StreamBase::Shutdown(const v8::FunctionCallbackInfo& args) { CHECK(args[0]->IsObject()); Local req_wrap_obj = args[0].As(); - ShutdownWrap* req_wrap = new ShutdownWrap(env, req_wrap_obj, this); - int err = DoShutdown(req_wrap, AfterShutdown); + ShutdownWrap* req_wrap = new ShutdownWrap(env, + req_wrap_obj, + this, + AfterShutdown); + + int err = DoShutdown(req_wrap); req_wrap->Dispatched(); if (err) delete req_wrap; @@ -132,8 +136,7 @@ int StreamBase::Shutdown(const v8::FunctionCallbackInfo& args) { } -void StreamBase::AfterShutdown(uv_shutdown_t* req, int status) { - ShutdownWrap* req_wrap = static_cast(req->data); +void StreamBase::AfterShutdown(ShutdownWrap* req_wrap, int status) { StreamBase* wrap = req_wrap->wrap(); Environment* env = req_wrap->env(); @@ -202,7 +205,7 @@ int StreamBase::Writev(const v8::FunctionCallbackInfo& args) { storage_size += sizeof(WriteWrap); char* storage = new char[storage_size]; WriteWrap* req_wrap = - new(storage) WriteWrap(env, req_wrap_obj, this); + new(storage) WriteWrap(env, req_wrap_obj, this, AfterWrite); uint32_t bytes = 0; size_t offset = sizeof(WriteWrap); @@ -237,11 +240,7 @@ int StreamBase::Writev(const v8::FunctionCallbackInfo& args) { bytes += str_size; } - int err = DoWrite(req_wrap, - bufs, - count, - nullptr, - StreamBase::AfterWrite); + int err = DoWrite(req_wrap, bufs, count, nullptr); // Deallocate space if (bufs != bufs_) @@ -295,13 +294,9 @@ int StreamBase::WriteBuffer(const v8::FunctionCallbackInfo& args) { // Allocate, or write rest storage = new char[sizeof(WriteWrap)]; - req_wrap = new(storage) WriteWrap(env, req_wrap_obj, this); + req_wrap = new(storage) WriteWrap(env, req_wrap_obj, this, AfterWrite); - err = DoWrite(req_wrap, - bufs, - count, - nullptr, - StreamBase::AfterWrite); + err = DoWrite(req_wrap, bufs, count, nullptr); req_wrap->Dispatched(); req_wrap_obj->Set(env->async(), True(env->isolate())); @@ -383,7 +378,7 @@ int StreamBase::WriteString(const v8::FunctionCallbackInfo& args) { } storage = new char[sizeof(WriteWrap) + storage_size + 15]; - req_wrap = new(storage) WriteWrap(env, req_wrap_obj, this); + req_wrap = new(storage) WriteWrap(env, req_wrap_obj, this, AfterWrite); data = reinterpret_cast(ROUND_UP( reinterpret_cast(storage) + sizeof(WriteWrap), 16)); @@ -406,11 +401,7 @@ int StreamBase::WriteString(const v8::FunctionCallbackInfo& args) { buf = uv_buf_init(data, data_size); if (!IsIPCPipe()) { - err = DoWrite(req_wrap, - &buf, - 1, - nullptr, - StreamBase::AfterWrite); + err = DoWrite(req_wrap, &buf, 1, nullptr); } else { uv_handle_t* send_handle = nullptr; @@ -427,8 +418,7 @@ int StreamBase::WriteString(const v8::FunctionCallbackInfo& args) { req_wrap, &buf, 1, - reinterpret_cast(send_handle), - StreamBase::AfterWrite); + reinterpret_cast(send_handle)); } req_wrap->Dispatched(); @@ -451,8 +441,7 @@ int StreamBase::WriteString(const v8::FunctionCallbackInfo& args) { } -void StreamBase::AfterWrite(uv_write_t* req, int status) { - WriteWrap* req_wrap = ContainerOf(&WriteWrap::req_, req); +void StreamBase::AfterWrite(WriteWrap* req_wrap, int status) { StreamBase* wrap = req_wrap->wrap(); Environment* env = req_wrap->env(); diff --git a/src/stream_base.h b/src/stream_base.h index 987063215591a0..41bf5e3abb2fd4 100644 --- a/src/stream_base.h +++ b/src/stream_base.h @@ -12,12 +12,31 @@ namespace node { class Environment; class StreamBase; -class ShutdownWrap : public ReqWrap { +template +class StreamReq { + public: + typedef void (*DoneCb)(Req* req, int status); + + StreamReq(DoneCb cb) : cb_(cb) { + } + + inline void Done(int status) { + cb_(static_cast(this), status); + } + + private: + DoneCb cb_; +}; + +class ShutdownWrap : public ReqWrap, + public StreamReq { public: ShutdownWrap(Environment* env, v8::Local req_wrap_obj, - StreamBase* wrap) + StreamBase* wrap, + DoneCb cb) : ReqWrap(env, req_wrap_obj, AsyncWrap::PROVIDER_SHUTDOWNWRAP), + StreamReq(cb), wrap_(wrap) { Wrap(req_wrap_obj, this); } @@ -32,12 +51,17 @@ class ShutdownWrap : public ReqWrap { StreamBase* const wrap_; }; -class WriteWrap: public ReqWrap { +class WriteWrap: public ReqWrap, + public StreamReq { public: // TODO(trevnorris): WrapWrap inherits from ReqWrap, which I've globbed // into the same provider. How should these be broken apart? - WriteWrap(Environment* env, v8::Local obj, StreamBase* wrap) + WriteWrap(Environment* env, + v8::Local obj, + StreamBase* wrap, + DoneCb cb) : ReqWrap(env, obj, AsyncWrap::PROVIDER_WRITEWRAP), + StreamReq(cb), wrap_(wrap) { Wrap(obj, this); } @@ -81,13 +105,12 @@ class StreamResource { virtual ~StreamResource() = default; - virtual int DoShutdown(ShutdownWrap* req_wrap, uv_shutdown_cb cb) = 0; + virtual int DoShutdown(ShutdownWrap* req_wrap) = 0; virtual int DoTryWrite(uv_buf_t** bufs, size_t* count) = 0; virtual int DoWrite(WriteWrap* w, uv_buf_t* bufs, size_t count, - uv_stream_t* send_handle, - uv_write_cb cb) = 0; + uv_stream_t* send_handle) = 0; virtual const char* Error() const = 0; virtual void ClearError() = 0; @@ -156,8 +179,8 @@ class StreamBase : public StreamResource { virtual v8::Local GetObject() = 0; // Libuv callbacks - static void AfterShutdown(uv_shutdown_t* req, int status); - static void AfterWrite(uv_write_t* req, int status); + static void AfterShutdown(ShutdownWrap* req, int status); + static void AfterWrite(WriteWrap* req, int status); // JS Methods int ReadStart(const v8::FunctionCallbackInfo& args); diff --git a/src/stream_wrap.cc b/src/stream_wrap.cc index 1bb3c5e3e8b5de..3cc56109ff4177 100644 --- a/src/stream_wrap.cc +++ b/src/stream_wrap.cc @@ -55,6 +55,7 @@ void StreamWrap::Initialize(Handle target, ww->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "WriteWrap")); target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "WriteWrap"), ww->GetFunction()); + env->set_write_wrap_constructor_function(ww->GetFunction()); } @@ -253,8 +254,14 @@ int StreamWrap::SetBlocking(bool enable) { } -int StreamWrap::DoShutdown(ShutdownWrap* req_wrap, uv_shutdown_cb cb) { - return uv_shutdown(&req_wrap->req_, stream(), cb); +int StreamWrap::DoShutdown(ShutdownWrap* req_wrap) { + return uv_shutdown(&req_wrap->req_, stream(), AfterShutdown); +} + + +void StreamWrap::AfterShutdown(uv_shutdown_t* req, int status) { + ShutdownWrap* req_wrap = ContainerOf(&ShutdownWrap::req_, req); + req_wrap->Done(status); } @@ -301,13 +308,12 @@ int StreamWrap::DoTryWrite(uv_buf_t** bufs, size_t* count) { int StreamWrap::DoWrite(WriteWrap* w, uv_buf_t* bufs, size_t count, - uv_stream_t* send_handle, - uv_write_cb cb) { + uv_stream_t* send_handle) { int r; if (send_handle == nullptr) { - r = uv_write(&w->req_, stream(), bufs, count, cb); + r = uv_write(&w->req_, stream(), bufs, count, AfterWrite); } else { - r = uv_write2(&w->req_, stream(), bufs, count, send_handle, cb); + r = uv_write2(&w->req_, stream(), bufs, count, send_handle, AfterWrite); } if (!r) { @@ -327,6 +333,10 @@ int StreamWrap::DoWrite(WriteWrap* w, } +void StreamWrap::AfterWrite(uv_write_t* req, int status) { + WriteWrap* req_wrap = ContainerOf(&WriteWrap::req_, req); + req_wrap->Done(status); +} void StreamWrap::OnAfterWriteImpl(WriteWrap* w, void* ctx) { diff --git a/src/stream_wrap.h b/src/stream_wrap.h index 4510b93c800e2c..eba13b6cd0da08 100644 --- a/src/stream_wrap.h +++ b/src/stream_wrap.h @@ -30,13 +30,12 @@ class StreamWrap : public HandleWrap, public StreamBase { int SetBlocking(bool enable) override; // Resource implementation - int DoShutdown(ShutdownWrap* req_wrap, uv_shutdown_cb cb) override; + int DoShutdown(ShutdownWrap* req_wrap) override; int DoTryWrite(uv_buf_t** bufs, size_t* count) override; int DoWrite(WriteWrap* w, uv_buf_t* bufs, size_t count, - uv_stream_t* send_handle, - uv_write_cb cb) override; + uv_stream_t* send_handle) override; const char* Error() const override; void ClearError() override; @@ -83,6 +82,8 @@ class StreamWrap : public HandleWrap, public StreamBase { ssize_t nread, const uv_buf_t* buf, uv_handle_type pending); + static void AfterWrite(uv_write_t* req, int status); + static void AfterShutdown(uv_shutdown_t* req, int status); // Resource interface implementation static void OnAfterWriteImpl(WriteWrap* w, void* ctx); diff --git a/src/tls_wrap.cc b/src/tls_wrap.cc index 125602bc134ba7..ae98a958485a4d 100644 --- a/src/tls_wrap.cc +++ b/src/tls_wrap.cc @@ -112,7 +112,7 @@ bool TLSCallbacks::InvokeQueued(int status) { WriteItemList queue; pending_write_items_.MoveBack(&queue); while (WriteItem* wi = queue.PopFront()) { - wi->cb_(&wi->w_->req_, status); + wi->w_->Done(&wi->w_->req_, status); delete wi; } @@ -298,14 +298,19 @@ void TLSWrap::EncOut() { write_size_ = NodeBIO::FromBIO(enc_out_)->PeekMultiple(data, size, &count); CHECK(write_size_ != 0 && count != 0); - Local req_wrap_obj = Object::New(env()->isolate()); + // XXX(indutny): How to do it in a better way? + Local req_wrap_obj = + env()->write_wrap_constructor_function()->NewInstance(); char* storage = new char[sizeof(WriteWrap)]; - WriteWrap* write_req = new(storage) WriteWrap(env(), req_wrap_obj, this); + WriteWrap* write_req = new(storage) WriteWrap(env(), + req_wrap_obj, + this, + EncOutCb); uv_buf_t buf[ARRAY_SIZE(data)]; for (size_t i = 0; i < count; i++) buf[i] = uv_buf_init(data[i], size[i]); - int r = stream_->DoWrite(write_req, buf, count, nullptr, EncOutCb); + int r = stream_->DoWrite(write_req, buf, count, nullptr); // Ignore errors, this should be already handled in js if (!r) @@ -313,8 +318,8 @@ void TLSWrap::EncOut() { } -void TLSWrap::EncOutCb(uv_write_t* req, int status) { - TLSWrap* wrap = static_cast(req->data); +void TLSWrap::EncOutCb(WriteWrap* req_wrap, int status) { + StreamBase* wrap = req_wrap->wrap(); // Handle error if (status) { @@ -525,8 +530,7 @@ int TLSWrap::DoTryWrite(uv_buf_t** bufs, size_t* count) { int TLSWrap::DoWrite(WriteWrap* w, uv_buf_t* bufs, size_t count, - uv_stream_t* send_handle, - uv_write_cb cb) { + uv_stream_t* send_handle) { CHECK_EQ(send_handle, nullptr); bool empty = true; @@ -543,11 +547,11 @@ int TLSWrap::DoWrite(WriteWrap* w, // However if there any data that should be written to socket, // callback should not be invoked immediately if (BIO_pending(enc_out_) == 0) - return stream_->DoWrite(w, bufs, count, send_handle, cb); + return stream_->DoWrite(w, bufs, count, send_handle); } // Queue callback to execute it on next tick - write_item_queue_.PushBack(new WriteItem(w, cb)); + write_item_queue_.PushBack(new WriteItem(w)); // Write queued data if (empty) { @@ -655,12 +659,12 @@ void TLSWrap::DoRead(ssize_t nread, } -int TLSWrap::DoShutdown(ShutdownWrap* req_wrap, uv_shutdown_cb cb) { +int TLSWrap::DoShutdown(ShutdownWrap* req_wrap) { if (SSL_shutdown(ssl_) == 0) SSL_shutdown(ssl_); shutdown_ = true; EncOut(); - return stream_->DoShutdown(req_wrap, cb); + return stream_->DoShutdown(req_wrap); } diff --git a/src/tls_wrap.h b/src/tls_wrap.h index 5bc4519c928831..9c523ff560cb1b 100644 --- a/src/tls_wrap.h +++ b/src/tls_wrap.h @@ -39,13 +39,12 @@ class TLSWrap : public crypto::SSLWrap, int ReadStop() override; int SetBlocking(bool enable) override; - int DoShutdown(ShutdownWrap* req_wrap, uv_shutdown_cb cb) override; + int DoShutdown(ShutdownWrap* req_wrap) override; int DoTryWrite(uv_buf_t** bufs, size_t* count) override; int DoWrite(WriteWrap* w, uv_buf_t* bufs, size_t count, - uv_stream_t* send_handle, - uv_write_cb cb) override; + uv_stream_t* send_handle) override; const char* Error() const override; void ClearError() override; @@ -66,15 +65,13 @@ class TLSWrap : public crypto::SSLWrap, // Write callback queue's item class WriteItem { public: - WriteItem(WriteWrap* w, uv_write_cb cb) : w_(w), cb_(cb) { + WriteItem(WriteWrap* w) : w_(w) { } ~WriteItem() { w_ = nullptr; - cb_ = nullptr; } WriteWrap* w_; - uv_write_cb cb_; ListNode member_; }; @@ -87,7 +84,7 @@ class TLSWrap : public crypto::SSLWrap, static void SSLInfoCallback(const SSL* ssl_, int where, int ret); void InitSSL(); void EncOut(); - static void EncOutCb(uv_write_t* req, int status); + static void EncOutCb(WriteWrap* req_wrap, int status); bool ClearIn(); void ClearOut(); void MakePending(); From 1fe7d75d467c99a6d4928faa65df8fd4d9f50254 Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Thu, 12 Feb 2015 14:28:46 +0300 Subject: [PATCH 15/41] src: fix build --- src/stream_base.h | 4 ++++ src/stream_wrap.cc | 5 +++++ src/stream_wrap.h | 1 + src/tcp_wrap.cc | 2 -- src/tls_wrap.cc | 8 ++++++-- src/tls_wrap.h | 1 + 6 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/stream_base.h b/src/stream_base.h index 41bf5e3abb2fd4..03ccb54e7df75a 100644 --- a/src/stream_base.h +++ b/src/stream_base.h @@ -159,6 +159,7 @@ class StreamBase : public StreamResource { static void AddMethods(Environment* env, v8::Handle target); + virtual void* Cast() = 0; virtual bool IsAlive() const = 0; virtual bool IsIPCPipe() const = 0; virtual int GetFD() const = 0; @@ -172,6 +173,9 @@ class StreamBase : public StreamResource { // TODO(indutny): assert that stream is not yet consumed inline void Consume(StreamBase* child) { consumed_by_ = child; } + template + inline Outer* Cast() { return static_cast(Cast()); } + protected: StreamBase(); virtual ~StreamBase() = default; diff --git a/src/stream_wrap.cc b/src/stream_wrap.cc index 3cc56109ff4177..66067f197322a2 100644 --- a/src/stream_wrap.cc +++ b/src/stream_wrap.cc @@ -91,6 +91,11 @@ bool StreamWrap::IsAlive() const { } +void* StreamWrap::Cast() { + return reinterpret_cast(this); +} + + Local StreamWrap::GetObject() { return object(); } diff --git a/src/stream_wrap.h b/src/stream_wrap.h index eba13b6cd0da08..9be29e94670bd9 100644 --- a/src/stream_wrap.h +++ b/src/stream_wrap.h @@ -21,6 +21,7 @@ class StreamWrap : public HandleWrap, public StreamBase { v8::Handle context); int GetFD() const override; + void* Cast() override; bool IsAlive() const override; bool IsIPCPipe() const override; diff --git a/src/tcp_wrap.cc b/src/tcp_wrap.cc index d840c1ab3a283b..441a464257c21c 100644 --- a/src/tcp_wrap.cc +++ b/src/tcp_wrap.cc @@ -153,7 +153,6 @@ TCPWrap::TCPWrap(Environment* env, Handle object, AsyncWrap* parent) CHECK_EQ(r, 0); // How do we proxy this error up to javascript? // Suggestion: uv_tcp_init() returns void. UpdateWriteQueueSize(); - fprintf(stderr, "TCPWrap() tcp %p env %p\n", this, env); } @@ -362,7 +361,6 @@ void TCPWrap::Connect(const FunctionCallbackInfo& args) { if (err == 0) { TCPConnectWrap* req_wrap = new TCPConnectWrap(env, req_wrap_obj); - fprintf(stderr, "tcp_connect() tcp %p connect %p env %p\n", wrap, req_wrap, env); err = uv_tcp_connect(&req_wrap->req_, &wrap->handle_, reinterpret_cast(&addr), diff --git a/src/tls_wrap.cc b/src/tls_wrap.cc index ae98a958485a4d..fed453de612410 100644 --- a/src/tls_wrap.cc +++ b/src/tls_wrap.cc @@ -68,7 +68,6 @@ TLSWrap::TLSWrap(Environment* env, stream_->set_read_cb(OnReadImpl, this); stream_->Consume(this); - fprintf(stderr, "TLSWrap() %p\n", this); InitSSL(); } @@ -319,7 +318,7 @@ void TLSWrap::EncOut() { void TLSWrap::EncOutCb(WriteWrap* req_wrap, int status) { - StreamBase* wrap = req_wrap->wrap(); + TLSWrap* wrap = req_wrap->wrap()->Cast(); // Handle error if (status) { @@ -476,6 +475,11 @@ bool TLSWrap::ClearIn() { } +void* TLSWrap::Cast() { + return reinterpret_cast(this); +} + + Local TLSWrap::GetObject() { return object(); } diff --git a/src/tls_wrap.h b/src/tls_wrap.h index 9c523ff560cb1b..4d0730ce69fd80 100644 --- a/src/tls_wrap.h +++ b/src/tls_wrap.h @@ -31,6 +31,7 @@ class TLSWrap : public crypto::SSLWrap, v8::Handle unused, v8::Handle context); + void* Cast() override; int GetFD() const override; bool IsAlive() const override; From 11d887788d20dfcc485bcc5b8b3b316e42dcd1d2 Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Thu, 12 Feb 2015 14:42:10 +0300 Subject: [PATCH 16/41] src: save progress --- src/stream_base.cc | 6 ++++-- src/stream_base.h | 9 ++++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/stream_base.cc b/src/stream_base.cc index cf8e3b527c4774..2a94949e621423 100644 --- a/src/stream_base.cc +++ b/src/stream_base.cc @@ -154,7 +154,8 @@ void StreamBase::AfterShutdown(ShutdownWrap* req_wrap, int status) { req_wrap_obj }; - req_wrap->MakeCallback(env->oncomplete_string(), ARRAY_SIZE(argv), argv); + if (req_wrap->object()->Has(env->oncomplete_string())) + req_wrap->MakeCallback(env->oncomplete_string(), ARRAY_SIZE(argv), argv); delete req_wrap; } @@ -470,7 +471,8 @@ void StreamBase::AfterWrite(WriteWrap* req_wrap, int status) { wrap->ClearError(); } - req_wrap->MakeCallback(env->oncomplete_string(), ARRAY_SIZE(argv), argv); + if (req_wrap->object()->Has(env->oncomplete_string())) + req_wrap->MakeCallback(env->oncomplete_string(), ARRAY_SIZE(argv), argv); req_wrap->~WriteWrap(); delete[] reinterpret_cast(req_wrap); diff --git a/src/stream_base.h b/src/stream_base.h index 03ccb54e7df75a..c0d0ce31006297 100644 --- a/src/stream_base.h +++ b/src/stream_base.h @@ -116,17 +116,20 @@ class StreamResource { // Events inline void OnAfterWrite(WriteWrap* w) { - after_write_cb_(w, after_write_ctx_); + if (after_write_cb_ != nullptr) + after_write_cb_(w, after_write_ctx_); } inline void OnAlloc(size_t size, uv_buf_t* buf) { - alloc_cb_(size, buf, alloc_ctx_); + if (alloc_cb_ != nullptr) + alloc_cb_(size, buf, alloc_ctx_); } inline void OnRead(size_t nread, const uv_buf_t* buf, uv_handle_type pending) { - read_cb_(nread, buf, pending, read_ctx_); + if (read_cb_ != nullptr) + read_cb_(nread, buf, pending, read_ctx_); } inline void set_after_write_cb(AfterWriteCb cb, void* ctx) { From 445b0fc57de993b8fc977cb35e44c8cb0dea54cd Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Thu, 12 Feb 2015 15:02:32 +0300 Subject: [PATCH 17/41] src: fix build --- src/stream_base.h | 5 +++-- src/stream_wrap.h | 2 -- src/tls_wrap.cc | 6 +++--- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/stream_base.h b/src/stream_base.h index c0d0ce31006297..035879cc59a2cb 100644 --- a/src/stream_base.h +++ b/src/stream_base.h @@ -1,7 +1,9 @@ #ifndef SRC_STREAM_BASE_H_ #define SRC_STREAM_BASE_H_ -#include "req_wrap.h" +#include "env.h" +#include "req-wrap.h" +#include "req-wrap-inl.h" #include "node.h" #include "v8.h" @@ -9,7 +11,6 @@ namespace node { // Forward declarations -class Environment; class StreamBase; template diff --git a/src/stream_wrap.h b/src/stream_wrap.h index 9be29e94670bd9..cdecde5b1cd9d8 100644 --- a/src/stream_wrap.h +++ b/src/stream_wrap.h @@ -3,8 +3,6 @@ #include "env.h" #include "handle_wrap.h" -#include "req-wrap.h" -#include "req-wrap-inl.h" #include "stream_base.h" #include "string_bytes.h" #include "v8.h" diff --git a/src/tls_wrap.cc b/src/tls_wrap.cc index fed453de612410..495f62d2de9eae 100644 --- a/src/tls_wrap.cc +++ b/src/tls_wrap.cc @@ -98,12 +98,12 @@ TLSWrap::~TLSWrap() { } -void TLSCallbacks::MakePending() { +void TLSWrap::MakePending() { write_item_queue_.MoveBack(&pending_write_items_); } -bool TLSCallbacks::InvokeQueued(int status) { +bool TLSWrap::InvokeQueued(int status) { if (pending_write_items_.IsEmpty()) return false; @@ -111,7 +111,7 @@ bool TLSCallbacks::InvokeQueued(int status) { WriteItemList queue; pending_write_items_.MoveBack(&queue); while (WriteItem* wi = queue.PopFront()) { - wi->w_->Done(&wi->w_->req_, status); + wi->w_->Done(status); delete wi; } From b827305940dc95c552a6bfbc9ce6d4f7e7c1038a Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Thu, 12 Feb 2015 16:38:00 +0300 Subject: [PATCH 18/41] stream_base: OnData! --- src/stream_base.cc | 28 ++++++++++++++++++++++++---- src/stream_base.h | 13 ++++++++++++- src/stream_wrap.cc | 22 +++++++++------------- src/stream_wrap.h | 1 + src/tls_wrap.cc | 13 ++++--------- 5 files changed, 50 insertions(+), 27 deletions(-) diff --git a/src/stream_base.cc b/src/stream_base.cc index 2a94949e621423..b4976ed0762919 100644 --- a/src/stream_base.cc +++ b/src/stream_base.cc @@ -33,10 +33,6 @@ template void StreamBase::AddMethods(Environment* env, Handle t); -StreamBase::StreamBase() : consumed_by_(nullptr) { -} - - template void StreamBase::AddMethods(Environment* env, Handle t) { HandleScope scope(env->isolate()); @@ -484,4 +480,28 @@ int StreamBase::SetBlocking(const v8::FunctionCallbackInfo& args) { return SetBlocking(args[0]->IsTrue()); } + +void StreamBase::OnData(ssize_t nread, char* data, Local handle) { + Environment* env = env_; + + Local argv[] = { + Integer::New(env->isolate(), nread), + Undefined(env->isolate()), + handle + }; + + if (data != nullptr) + argv[1] = Buffer::Use(env, data, nread); + + if (argv[2].IsEmpty()) + argv[2] = Undefined(env->isolate()); + + GetAsyncWrap()->MakeCallback(env->onread_string(), ARRAY_SIZE(argv), argv); +} + + +AsyncWrap* StreamBase::GetAsyncWrap() { + return nullptr; +} + } // namespace node diff --git a/src/stream_base.h b/src/stream_base.h index 035879cc59a2cb..ee91837fa49957 100644 --- a/src/stream_base.h +++ b/src/stream_base.h @@ -2,6 +2,7 @@ #define SRC_STREAM_BASE_H_ #include "env.h" +#include "async-wrap.h" #include "req-wrap.h" #include "req-wrap-inl.h" #include "node.h" @@ -180,12 +181,20 @@ class StreamBase : public StreamResource { template inline Outer* Cast() { return static_cast(Cast()); } + // TODO(indutny): should be a part of Resource + void OnData(ssize_t nread, char* data, v8::Local handle); + protected: - StreamBase(); + StreamBase(Environment* env) : env_(env), consumed_by_(nullptr) { + } + virtual ~StreamBase() = default; virtual v8::Local GetObject() = 0; + // Optional + virtual AsyncWrap* GetAsyncWrap(); + // Libuv callbacks static void AfterShutdown(ShutdownWrap* req, int status); static void AfterWrite(WriteWrap* req, int status); @@ -209,6 +218,8 @@ class StreamBase : public StreamResource { const v8::FunctionCallbackInfo& args)> static void JSMethod(const v8::FunctionCallbackInfo& args); + private: + Environment* env_; StreamBase* consumed_by_; }; diff --git a/src/stream_wrap.cc b/src/stream_wrap.cc index 66067f197322a2..cc30f2fd716952 100644 --- a/src/stream_wrap.cc +++ b/src/stream_wrap.cc @@ -69,6 +69,7 @@ StreamWrap::StreamWrap(Environment* env, reinterpret_cast(stream), provider, parent), + StreamBase(env), stream_(stream) { set_after_write_cb(OnAfterWriteImpl, this); set_alloc_cb(OnAllocImpl, this); @@ -101,6 +102,11 @@ Local StreamWrap::GetObject() { } +AsyncWrap* StreamWrap::GetAsyncWrap() { + return this; +} + + bool StreamWrap::IsIPCPipe() const { return is_named_pipe_ipc(); } @@ -175,16 +181,12 @@ void StreamWrap::OnReadImpl(ssize_t nread, HandleScope handle_scope(env->isolate()); Context::Scope context_scope(env->context()); - Local argv[] = { - Integer::New(env->isolate(), nread), - Undefined(env->isolate()), - Undefined(env->isolate()) - }; + Local pending_obj; if (nread < 0) { if (buf->base != nullptr) free(buf->base); - wrap->MakeCallback(env->onread_string(), ARRAY_SIZE(argv), argv); + wrap->OnData(nread, nullptr, pending_obj); return; } @@ -196,9 +198,7 @@ void StreamWrap::OnReadImpl(ssize_t nread, char* base = static_cast(realloc(buf->base, nread)); CHECK_LE(static_cast(nread), buf->len); - argv[1] = Buffer::Use(env, base, nread); - Local pending_obj; if (pending == UV_TCP) { pending_obj = AcceptHandle(env, wrap); } else if (pending == UV_NAMED_PIPE) { @@ -209,11 +209,7 @@ void StreamWrap::OnReadImpl(ssize_t nread, CHECK_EQ(pending, UV_UNKNOWN_HANDLE); } - if (!pending_obj.IsEmpty()) { - argv[2] = pending_obj; - } - - wrap->MakeCallback(env->onread_string(), ARRAY_SIZE(argv), argv); + wrap->OnData(nread, base, pending_obj); } diff --git a/src/stream_wrap.h b/src/stream_wrap.h index cdecde5b1cd9d8..50e6ce39d810bf 100644 --- a/src/stream_wrap.h +++ b/src/stream_wrap.h @@ -66,6 +66,7 @@ class StreamWrap : public HandleWrap, public StreamBase { } v8::Local GetObject() override; + AsyncWrap* GetAsyncWrap() override; void UpdateWriteQueueSize(); private: diff --git a/src/tls_wrap.cc b/src/tls_wrap.cc index 495f62d2de9eae..9cea4851478384 100644 --- a/src/tls_wrap.cc +++ b/src/tls_wrap.cc @@ -39,6 +39,7 @@ TLSWrap::TLSWrap(Environment* env, Handle stream_obj, Handle sc) : SSLWrap(env, Unwrap(sc), kind), + StreamBase(env), AsyncWrap(env, env->tls_wrap_constructor_function()->NewInstance(), AsyncWrap::PROVIDER_TLSWRAP), @@ -401,19 +402,14 @@ void TLSWrap::ClearOut() { do { read = SSL_read(ssl_, out, sizeof(out)); if (read > 0) { - Local argv[] = { - Integer::New(env()->isolate(), read), - Buffer::New(env(), out, read) - }; - MakeCallback(env()->onread_string(), ARRAY_SIZE(argv), argv); + stream_->OnData(read, out, Local()); } } while (read > 0); int flags = SSL_get_shutdown(ssl_); if (!eof_ && flags & SSL_RECEIVED_SHUTDOWN) { eof_ = true; - Local arg = Integer::New(env()->isolate(), UV_EOF); - MakeCallback(env()->onread_string(), 1, &arg); + stream_->OnData(UV_EOF, nullptr, Local()); } if (read == -1) { @@ -638,8 +634,7 @@ void TLSWrap::DoRead(ssize_t nread, HandleScope handle_scope(env()->isolate()); Context::Scope context_scope(env()->context()); - Local arg = Integer::New(env()->isolate(), nread); - MakeCallback(env()->onread_string(), 1, &arg); + stream_->OnData(nread, nullptr, Local()); return; } From ab64ce6d4e4e552df73d4b5733b4ebcdfd958c0b Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Thu, 12 Feb 2015 17:19:45 +0300 Subject: [PATCH 19/41] tls_wrap: add proper methods --- src/stream_base.cc | 5 +++-- src/stream_wrap.cc | 2 +- src/tls_wrap.cc | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/stream_base.cc b/src/stream_base.cc index b4976ed0762919..14eb2805026f66 100644 --- a/src/stream_base.cc +++ b/src/stream_base.cc @@ -6,6 +6,7 @@ #include "env-inl.h" #include "stream_wrap.h" #include "string_bytes.h" +#include "tls_wrap.h" #include "util.h" #include "util-inl.h" #include "v8.h" @@ -31,6 +32,8 @@ using v8::Value; template void StreamBase::AddMethods(Environment* env, Handle t); +template void StreamBase::AddMethods(Environment* env, + Handle t); template @@ -74,7 +77,6 @@ void StreamBase::AddMethods(Environment* env, Handle t) { template void StreamBase::GetFD(Local key, const PropertyCallbackInfo& args) { - HandleScope scope(args.GetIsolate()); StreamBase* wrap = Unwrap(args.Holder()); while (wrap->ConsumedBy() != nullptr) @@ -90,7 +92,6 @@ void StreamBase::GetFD(Local key, template & args)> void StreamBase::JSMethod(const FunctionCallbackInfo& args) { - HandleScope scope(args.GetIsolate()); StreamBase* wrap = Unwrap(args.Holder()); while (wrap->ConsumedBy() != nullptr) diff --git a/src/stream_wrap.cc b/src/stream_wrap.cc index cc30f2fd716952..5327bf8bd44dc5 100644 --- a/src/stream_wrap.cc +++ b/src/stream_wrap.cc @@ -103,7 +103,7 @@ Local StreamWrap::GetObject() { AsyncWrap* StreamWrap::GetAsyncWrap() { - return this; + return static_cast(this); } diff --git a/src/tls_wrap.cc b/src/tls_wrap.cc index 9cea4851478384..7103539118c671 100644 --- a/src/tls_wrap.cc +++ b/src/tls_wrap.cc @@ -811,7 +811,7 @@ void TLSWrap::Initialize(Handle target, env->SetProtoMethod(t, "enableSessionCallbacks", EnableSessionCallbacks); env->SetProtoMethod(t, "enableHelloParser", EnableHelloParser); - StreamWrap::AddMethods(env, t); + StreamWrap::AddMethods(env, t); SSLWrap::AddMethods(env, t); #ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB From 24a9ae6e9b8cbee6efcffe57784640a6de9ca183 Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Thu, 12 Feb 2015 17:35:35 +0300 Subject: [PATCH 20/41] stream_base: use GetParent() --- src/stream_base.cc | 8 ++++---- src/stream_base.h | 19 ++++++++++++++++--- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/stream_base.cc b/src/stream_base.cc index 14eb2805026f66..9c115c18bbda28 100644 --- a/src/stream_base.cc +++ b/src/stream_base.cc @@ -122,7 +122,7 @@ int StreamBase::Shutdown(const v8::FunctionCallbackInfo& args) { ShutdownWrap* req_wrap = new ShutdownWrap(env, req_wrap_obj, - this, + GetParent(), AfterShutdown); int err = DoShutdown(req_wrap); @@ -203,7 +203,7 @@ int StreamBase::Writev(const v8::FunctionCallbackInfo& args) { storage_size += sizeof(WriteWrap); char* storage = new char[storage_size]; WriteWrap* req_wrap = - new(storage) WriteWrap(env, req_wrap_obj, this, AfterWrite); + new(storage) WriteWrap(env, req_wrap_obj, GetParent(), AfterWrite); uint32_t bytes = 0; size_t offset = sizeof(WriteWrap); @@ -292,7 +292,7 @@ int StreamBase::WriteBuffer(const v8::FunctionCallbackInfo& args) { // Allocate, or write rest storage = new char[sizeof(WriteWrap)]; - req_wrap = new(storage) WriteWrap(env, req_wrap_obj, this, AfterWrite); + req_wrap = new(storage) WriteWrap(env, req_wrap_obj, GetParent(), AfterWrite); err = DoWrite(req_wrap, bufs, count, nullptr); req_wrap->Dispatched(); @@ -376,7 +376,7 @@ int StreamBase::WriteString(const v8::FunctionCallbackInfo& args) { } storage = new char[sizeof(WriteWrap) + storage_size + 15]; - req_wrap = new(storage) WriteWrap(env, req_wrap_obj, this, AfterWrite); + req_wrap = new(storage) WriteWrap(env, req_wrap_obj, GetParent(), AfterWrite); data = reinterpret_cast(ROUND_UP( reinterpret_cast(storage) + sizeof(WriteWrap), 16)); diff --git a/src/stream_base.h b/src/stream_base.h index ee91837fa49957..9df26d9e3a27ff 100644 --- a/src/stream_base.h +++ b/src/stream_base.h @@ -173,10 +173,19 @@ class StreamBase : public StreamResource { virtual int ReadStop() = 0; virtual int SetBlocking(bool enable) = 0; - inline StreamBase* ConsumedBy() const { return consumed_by_; } + inline StreamBase* ConsumedBy() { return consumed_by_; } + inline StreamBase* GetParent() { + StreamBase* c = this; + while (c->parent_ != nullptr) + c = c->parent_; + return c; + } // TODO(indutny): assert that stream is not yet consumed - inline void Consume(StreamBase* child) { consumed_by_ = child; } + inline void Consume(StreamBase* child) { + consumed_by_ = child; + child->parent_ = this; + } template inline Outer* Cast() { return static_cast(Cast()); } @@ -185,7 +194,9 @@ class StreamBase : public StreamResource { void OnData(ssize_t nread, char* data, v8::Local handle); protected: - StreamBase(Environment* env) : env_(env), consumed_by_(nullptr) { + StreamBase(Environment* env) : parent_(nullptr), + env_(env), + consumed_by_(nullptr) { } virtual ~StreamBase() = default; @@ -218,6 +229,8 @@ class StreamBase : public StreamResource { const v8::FunctionCallbackInfo& args)> static void JSMethod(const v8::FunctionCallbackInfo& args); + StreamBase* parent_; + private: Environment* env_; StreamBase* consumed_by_; From 372b9865db0f5d7fefe236065406cf24b70dcc46 Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Thu, 12 Feb 2015 17:59:27 +0300 Subject: [PATCH 21/41] stream_base: moar fixes --- src/stream_base.cc | 10 ++++++---- src/stream_base.h | 4 +++- src/stream_wrap.cc | 4 ++-- src/tls_wrap.cc | 6 +++--- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/stream_base.cc b/src/stream_base.cc index 9c115c18bbda28..afba99e0da3e8b 100644 --- a/src/stream_base.cc +++ b/src/stream_base.cc @@ -482,17 +482,19 @@ int StreamBase::SetBlocking(const v8::FunctionCallbackInfo& args) { } -void StreamBase::OnData(ssize_t nread, char* data, Local handle) { +void StreamBase::OnData(ssize_t nread, + Local buf, + Local handle) { Environment* env = env_; Local argv[] = { Integer::New(env->isolate(), nread), - Undefined(env->isolate()), + buf, handle }; - if (data != nullptr) - argv[1] = Buffer::Use(env, data, nread); + if (argv[1].IsEmpty()) + argv[1] = Undefined(env->isolate()); if (argv[2].IsEmpty()) argv[2] = Undefined(env->isolate()); diff --git a/src/stream_base.h b/src/stream_base.h index 9df26d9e3a27ff..b2455c27ac89ca 100644 --- a/src/stream_base.h +++ b/src/stream_base.h @@ -191,7 +191,9 @@ class StreamBase : public StreamResource { inline Outer* Cast() { return static_cast(Cast()); } // TODO(indutny): should be a part of Resource - void OnData(ssize_t nread, char* data, v8::Local handle); + void OnData(ssize_t nread, + v8::Local buf, + v8::Local handle); protected: StreamBase(Environment* env) : parent_(nullptr), diff --git a/src/stream_wrap.cc b/src/stream_wrap.cc index 5327bf8bd44dc5..a183d44d088c0e 100644 --- a/src/stream_wrap.cc +++ b/src/stream_wrap.cc @@ -186,7 +186,7 @@ void StreamWrap::OnReadImpl(ssize_t nread, if (nread < 0) { if (buf->base != nullptr) free(buf->base); - wrap->OnData(nread, nullptr, pending_obj); + wrap->OnData(nread, Local(), pending_obj); return; } @@ -209,7 +209,7 @@ void StreamWrap::OnReadImpl(ssize_t nread, CHECK_EQ(pending, UV_UNKNOWN_HANDLE); } - wrap->OnData(nread, base, pending_obj); + wrap->OnData(nread, Buffer::Use(env, base, nread), pending_obj); } diff --git a/src/tls_wrap.cc b/src/tls_wrap.cc index 7103539118c671..33482f8a3bab89 100644 --- a/src/tls_wrap.cc +++ b/src/tls_wrap.cc @@ -402,14 +402,14 @@ void TLSWrap::ClearOut() { do { read = SSL_read(ssl_, out, sizeof(out)); if (read > 0) { - stream_->OnData(read, out, Local()); + stream_->OnData(read, Buffer::New(env(), out, read), Local()); } } while (read > 0); int flags = SSL_get_shutdown(ssl_); if (!eof_ && flags & SSL_RECEIVED_SHUTDOWN) { eof_ = true; - stream_->OnData(UV_EOF, nullptr, Local()); + stream_->OnData(UV_EOF, Local(), Local()); } if (read == -1) { @@ -634,7 +634,7 @@ void TLSWrap::DoRead(ssize_t nread, HandleScope handle_scope(env()->isolate()); Context::Scope context_scope(env()->context()); - stream_->OnData(nread, nullptr, Local()); + stream_->OnData(nread, Local(), Local()); return; } From 22eafaf8c61c34c4604468931e6db66bc2993bfb Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Thu, 12 Feb 2015 18:07:57 +0300 Subject: [PATCH 22/41] stream_base: proper interfaces --- lib/net.js | 5 ++++- src/stream_base.cc | 14 ++++---------- src/stream_base.h | 17 ++--------------- 3 files changed, 10 insertions(+), 26 deletions(-) diff --git a/lib/net.js b/lib/net.js index a6c95c6857b9b2..afb1c503835e32 100644 --- a/lib/net.js +++ b/lib/net.js @@ -961,7 +961,10 @@ function afterConnect(status, handle, req, readable, writable) { return; } - assert(handle === self._handle, 'handle != self._handle'); + // Update handle if it was wrapped + handle = self._handle; + // TODO(indutny): assert that the handle is actually an ancestor of old one + // assert(handle === self._handle, 'handle != self._handle'); debug('afterConnect'); diff --git a/src/stream_base.cc b/src/stream_base.cc index afba99e0da3e8b..a179a189b61818 100644 --- a/src/stream_base.cc +++ b/src/stream_base.cc @@ -79,9 +79,6 @@ void StreamBase::GetFD(Local key, const PropertyCallbackInfo& args) { StreamBase* wrap = Unwrap(args.Holder()); - while (wrap->ConsumedBy() != nullptr) - wrap = wrap->ConsumedBy(); - if (!wrap->IsAlive()) return args.GetReturnValue().Set(UV_EINVAL); @@ -94,9 +91,6 @@ template & args) { StreamBase* wrap = Unwrap(args.Holder()); - while (wrap->ConsumedBy() != nullptr) - wrap = wrap->ConsumedBy(); - if (!wrap->IsAlive()) return args.GetReturnValue().Set(UV_EINVAL); @@ -122,7 +116,7 @@ int StreamBase::Shutdown(const v8::FunctionCallbackInfo& args) { ShutdownWrap* req_wrap = new ShutdownWrap(env, req_wrap_obj, - GetParent(), + this, AfterShutdown); int err = DoShutdown(req_wrap); @@ -203,7 +197,7 @@ int StreamBase::Writev(const v8::FunctionCallbackInfo& args) { storage_size += sizeof(WriteWrap); char* storage = new char[storage_size]; WriteWrap* req_wrap = - new(storage) WriteWrap(env, req_wrap_obj, GetParent(), AfterWrite); + new(storage) WriteWrap(env, req_wrap_obj, this, AfterWrite); uint32_t bytes = 0; size_t offset = sizeof(WriteWrap); @@ -292,7 +286,7 @@ int StreamBase::WriteBuffer(const v8::FunctionCallbackInfo& args) { // Allocate, or write rest storage = new char[sizeof(WriteWrap)]; - req_wrap = new(storage) WriteWrap(env, req_wrap_obj, GetParent(), AfterWrite); + req_wrap = new(storage) WriteWrap(env, req_wrap_obj, this, AfterWrite); err = DoWrite(req_wrap, bufs, count, nullptr); req_wrap->Dispatched(); @@ -376,7 +370,7 @@ int StreamBase::WriteString(const v8::FunctionCallbackInfo& args) { } storage = new char[sizeof(WriteWrap) + storage_size + 15]; - req_wrap = new(storage) WriteWrap(env, req_wrap_obj, GetParent(), AfterWrite); + req_wrap = new(storage) WriteWrap(env, req_wrap_obj, this, AfterWrite); data = reinterpret_cast(ROUND_UP( reinterpret_cast(storage) + sizeof(WriteWrap), 16)); diff --git a/src/stream_base.h b/src/stream_base.h index b2455c27ac89ca..4190804216622f 100644 --- a/src/stream_base.h +++ b/src/stream_base.h @@ -174,18 +174,9 @@ class StreamBase : public StreamResource { virtual int SetBlocking(bool enable) = 0; inline StreamBase* ConsumedBy() { return consumed_by_; } - inline StreamBase* GetParent() { - StreamBase* c = this; - while (c->parent_ != nullptr) - c = c->parent_; - return c; - } // TODO(indutny): assert that stream is not yet consumed - inline void Consume(StreamBase* child) { - consumed_by_ = child; - child->parent_ = this; - } + inline void Consume(StreamBase* child) { consumed_by_ = child; } template inline Outer* Cast() { return static_cast(Cast()); } @@ -196,9 +187,7 @@ class StreamBase : public StreamResource { v8::Local handle); protected: - StreamBase(Environment* env) : parent_(nullptr), - env_(env), - consumed_by_(nullptr) { + StreamBase(Environment* env) : env_(env), consumed_by_(nullptr) { } virtual ~StreamBase() = default; @@ -231,8 +220,6 @@ class StreamBase : public StreamResource { const v8::FunctionCallbackInfo& args)> static void JSMethod(const v8::FunctionCallbackInfo& args); - StreamBase* parent_; - private: Environment* env_; StreamBase* consumed_by_; From 025ed30c5e77fceb8148f0afcad92669bbf3daac Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Fri, 13 Feb 2015 09:40:35 +0100 Subject: [PATCH 23/41] tls_wrap: invoke OnData on self --- src/stream_base.h | 12 ++++++------ src/tls_wrap.cc | 16 ++++++++++------ src/tls_wrap.h | 1 + 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/stream_base.h b/src/stream_base.h index 4190804216622f..fa6cb9cd9a4ff8 100644 --- a/src/stream_base.h +++ b/src/stream_base.h @@ -173,10 +173,10 @@ class StreamBase : public StreamResource { virtual int ReadStop() = 0; virtual int SetBlocking(bool enable) = 0; - inline StreamBase* ConsumedBy() { return consumed_by_; } - - // TODO(indutny): assert that stream is not yet consumed - inline void Consume(StreamBase* child) { consumed_by_ = child; } + inline void Consume() { + CHECK_EQ(consumed_, false); + consumed_ = true; + } template inline Outer* Cast() { return static_cast(Cast()); } @@ -187,7 +187,7 @@ class StreamBase : public StreamResource { v8::Local handle); protected: - StreamBase(Environment* env) : env_(env), consumed_by_(nullptr) { + StreamBase(Environment* env) : env_(env), consumed_(false) { } virtual ~StreamBase() = default; @@ -222,7 +222,7 @@ class StreamBase : public StreamResource { private: Environment* env_; - StreamBase* consumed_by_; + bool consumed_; }; } // namespace node diff --git a/src/tls_wrap.cc b/src/tls_wrap.cc index 33482f8a3bab89..e84dbbd4c32997 100644 --- a/src/tls_wrap.cc +++ b/src/tls_wrap.cc @@ -64,10 +64,10 @@ TLSWrap::TLSWrap(Environment* env, SSL_CTX_sess_set_get_cb(sc_->ctx_, SSLWrap::GetSessionCallback); SSL_CTX_sess_set_new_cb(sc_->ctx_, SSLWrap::NewSessionCallback); + stream_->Consume(); stream_->set_after_write_cb(OnAfterWriteImpl, this); stream_->set_alloc_cb(OnAllocImpl, this); stream_->set_read_cb(OnReadImpl, this); - stream_->Consume(this); InitSSL(); } @@ -401,15 +401,14 @@ void TLSWrap::ClearOut() { int read; do { read = SSL_read(ssl_, out, sizeof(out)); - if (read > 0) { - stream_->OnData(read, Buffer::New(env(), out, read), Local()); - } + if (read > 0) + OnData(read, Buffer::New(env(), out, read), Local()); } while (read > 0); int flags = SSL_get_shutdown(ssl_); if (!eof_ && flags & SSL_RECEIVED_SHUTDOWN) { eof_ = true; - stream_->OnData(UV_EOF, Local(), Local()); + OnData(UV_EOF, Local(), Local()); } if (read == -1) { @@ -481,6 +480,11 @@ Local TLSWrap::GetObject() { } +AsyncWrap* TLSWrap::GetAsyncWrap() { + return static_cast(this); +} + + bool TLSWrap::IsIPCPipe() const { return stream_->IsIPCPipe(); } @@ -634,7 +638,7 @@ void TLSWrap::DoRead(ssize_t nread, HandleScope handle_scope(env()->isolate()); Context::Scope context_scope(env()->context()); - stream_->OnData(nread, Local(), Local()); + OnData(nread, Local(), Local()); return; } diff --git a/src/tls_wrap.h b/src/tls_wrap.h index 4d0730ce69fd80..80fc2dbd070d18 100644 --- a/src/tls_wrap.h +++ b/src/tls_wrap.h @@ -104,6 +104,7 @@ class TLSWrap : public crypto::SSLWrap, } v8::Local GetObject() override; + AsyncWrap* GetAsyncWrap() override; bool IsIPCPipe() const override; // Resource implementation From 312bd561cb5933997bc8afcd76bc2cf4fc92b130 Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Fri, 13 Feb 2015 10:19:30 +0100 Subject: [PATCH 24/41] tls_wrap.js: use new wrapping --- lib/_tls_wrap.js | 256 +++++++++++++++++++++-------------------------- src/tls_wrap.cc | 1 + 2 files changed, 115 insertions(+), 142 deletions(-) diff --git a/lib/_tls_wrap.js b/lib/_tls_wrap.js index fb63667581c54f..03533c2ffba25b 100644 --- a/lib/_tls_wrap.js +++ b/lib/_tls_wrap.js @@ -11,14 +11,17 @@ const debug = util.debuglog('tls'); const Timer = process.binding('timer_wrap').Timer; const tls_wrap = process.binding('tls_wrap'); -// Lazy load -var tls_legacy; +// constructor for lazy loading +function createTCP() { + var TCP = process.binding('tcp_wrap').TCP; + return new TCP(); +} function onhandshakestart() { debug('onhandshakestart'); var self = this; - var ssl = self.ssl; + var ssl = self._handle; var now = Timer.now(); assert(now >= ssl.lastHandshakeTime); @@ -63,7 +66,7 @@ function loadSession(self, hello, cb) { // NOTE: That we have disabled OpenSSL's internal session storage in // `node_crypto.cc` and hence its safe to rely on getting servername only // from clienthello or this place. - var ret = self.ssl.loadSession(session); + var ret = self._handle.loadSession(session); cb(null, ret); } @@ -92,9 +95,9 @@ function loadSNI(self, servername, cb) { // TODO(indutny): eventually disallow raw `SecureContext` if (context) - self.ssl.sni_context = context.context || context; + self._handle.sni_context = context.context || context; - cb(null, self.ssl.sni_context); + cb(null, self._handle.sni_context); }); } @@ -127,7 +130,7 @@ function requestOCSP(self, hello, ctx, cb) { return cb(err); if (response) - self.ssl.setOCSPResponse(response); + self._handle.setOCSPResponse(response); cb(null); } } @@ -161,7 +164,7 @@ function onclienthello(hello) { if (err) return self.destroy(err); - self.ssl.endParser(); + self._handle.endParser(); }); }); }); @@ -184,7 +187,7 @@ function onnewsession(key, session) { return; once = true; - self.ssl.newSessionDone(); + self._handle.newSessionDone(); self._newSessionPending = false; if (self._securePending) @@ -207,26 +210,12 @@ function TLSSocket(socket, options) { // Disallow wrapping TLSSocket in TLSSocket assert(!(socket instanceof TLSSocket)); - net.Socket.call(this, { - handle: socket && socket._handle, - allowHalfOpen: socket && socket.allowHalfOpen, - readable: false, - writable: false - }); - if (socket) { - this._parent = socket; - - // To prevent assertion in afterConnect() - this._connecting = socket._connecting; - } - this._tlsOptions = options; this._secureEstablished = false; this._securePending = false; this._newSessionPending = false; this._controlReleased = false; this._SNICallback = null; - this.ssl = null; this.servername = null; this.npnProtocol = null; this.authorized = false; @@ -236,15 +225,16 @@ function TLSSocket(socket, options) { // distinguishable from regular ones. this.encrypted = true; + net.Socket.call(this, { + handle: this._wrapHandle(socket ? socket._handle : createTCP()), + allowHalfOpen: socket && socket.allowHalfOpen, + readable: false, + writable: false + }); + this.on('error', this._tlsError); - if (!this._handle) { - this.once('connect', function() { - this._init(null); - }); - } else { - this._init(socket); - } + this._init(socket); // Make sure to setup all required properties like: `_connecting` before // starting the flow of the data @@ -255,23 +245,47 @@ function TLSSocket(socket, options) { util.inherits(TLSSocket, net.Socket); exports.TLSSocket = TLSSocket; -TLSSocket.prototype._init = function(socket) { - assert(this._handle); +var tcpMethods = [ + 'close', 'ref', 'unref', 'open', 'bind', 'listen', 'connect', 'bind6', + 'connect6', 'getsockname', 'getpeername', 'setNoDelay', 'setKeepAlive', + 'setSimultaneousAccepts' +]; - // lib/net.js expect this value to be non-zero if write hasn't been flushed - // immediately - // TODO(indutny): rewise this solution, it might be 1 before handshake and - // represent real writeQueueSize during regular writes. - this._handle.writeQueueSize = 1; +TLSSocket.prototype._wrapHandle = function(handle) { + var res; - var self = this; var options = this._tlsOptions; // Wrap socket's handle var context = options.secureContext || options.credentials || tls.createSecureContext(); - this.ssl = tls_wrap.wrap(this._handle, context.context, options.isServer); + res = tls_wrap.wrap(handle, context.context, options.isServer); + res._parent = handle; + res._reading = handle._reading; + + // Proxy HandleWrap and TCPWrap methods + // TODO(indutny): Do we want to support Pipes and TTYs? + tcpMethods.forEach(function(name) { + res[name] = function methodProxy() { + return handle[name].apply(handle, arguments); + }; + }); + + return res; +}; + +TLSSocket.prototype._init = function(socket) { + var self = this; + var options = this._tlsOptions; + var ssl = this._handle; + + // lib/net.js expect this value to be non-zero if write hasn't been flushed + // immediately + // TODO(indutny): rewise this solution, it might be 1 before handshake and + // represent real writeQueueSize during regular writes. + ssl.writeQueueSize = 1; + this.server = options.server || null; // For clients, we will always have either a given ca list or be using @@ -282,32 +296,32 @@ TLSSocket.prototype._init = function(socket) { this._requestCert = requestCert; this._rejectUnauthorized = rejectUnauthorized; if (requestCert || rejectUnauthorized) - this.ssl.setVerifyMode(requestCert, rejectUnauthorized); + ssl.setVerifyMode(requestCert, rejectUnauthorized); if (options.isServer) { - this.ssl.onhandshakestart = onhandshakestart.bind(this); - this.ssl.onhandshakedone = onhandshakedone.bind(this); - this.ssl.onclienthello = onclienthello.bind(this); - this.ssl.onnewsession = onnewsession.bind(this); - this.ssl.lastHandshakeTime = 0; - this.ssl.handshakes = 0; + ssl.onhandshakestart = onhandshakestart.bind(this); + ssl.onhandshakedone = onhandshakedone.bind(this); + ssl.onclienthello = onclienthello.bind(this); + ssl.onnewsession = onnewsession.bind(this); + ssl.lastHandshakeTime = 0; + ssl.handshakes = 0; if (this.server && (listenerCount(this.server, 'resumeSession') > 0 || listenerCount(this.server, 'newSession') > 0 || listenerCount(this.server, 'OCSPRequest') > 0)) { - this.ssl.enableSessionCallbacks(); + ssl.enableSessionCallbacks(); } } else { - this.ssl.onhandshakestart = function() {}; - this.ssl.onhandshakedone = this._finishInit.bind(this); - this.ssl.onocspresponse = onocspresponse.bind(this); + ssl.onhandshakestart = function() {}; + ssl.onhandshakedone = this._finishInit.bind(this); + ssl.onocspresponse = onocspresponse.bind(this); if (options.session) - this.ssl.setSession(options.session); + ssl.setSession(options.session); } - this.ssl.onerror = function(err) { + ssl.onerror = function(err) { if (self._writableState.errorEmitted) return; self._writableState.errorEmitted = true; @@ -337,11 +351,11 @@ TLSSocket.prototype._init = function(socket) { options.server._contexts.length)) { assert(typeof options.SNICallback === 'function'); this._SNICallback = options.SNICallback; - this.ssl.enableHelloParser(); + ssl.enableHelloParser(); } if (process.features.tls_npn && options.NPNProtocols) - this.ssl.setNPNProtocols(options.NPNProtocols); + ssl.setNPNProtocols(options.NPNProtocols); if (options.handshakeTimeout > 0) this.setTimeout(options.handshakeTimeout, this._handleTimeout); @@ -350,7 +364,18 @@ TLSSocket.prototype._init = function(socket) { if (socket && socket._readableState.length) { var buf; while ((buf = socket.read()) !== null) - this.ssl.receive(buf); + ssl.receive(buf); + } + + if (socket) { + this._parent = socket; + + // To prevent assertion in afterConnect() and properly kick off readStart + this._connecting = socket._connecting; + socket.once('connect', function() { + self._connecting = false; + self.emit('connect'); + }); } }; @@ -365,11 +390,11 @@ TLSSocket.prototype.renegotiate = function(options, callback) { if (requestCert !== this._requestCert || rejectUnauthorized !== this._rejectUnauthorized) { - this.ssl.setVerifyMode(requestCert, rejectUnauthorized); + this._handle.setVerifyMode(requestCert, rejectUnauthorized); this._requestCert = requestCert; this._rejectUnauthorized = rejectUnauthorized; } - if (!this.ssl.renegotiate()) { + if (!this._handle.renegotiate()) { if (callback) { process.nextTick(function() { callback(new Error('Failed to renegotiate')); @@ -391,11 +416,11 @@ TLSSocket.prototype.renegotiate = function(options, callback) { }; TLSSocket.prototype.setMaxSendFragment = function setMaxSendFragment(size) { - return this.ssl.setMaxSendFragment(size) == 1; + return this._handle.setMaxSendFragment(size) == 1; }; TLSSocket.prototype.getTLSTicket = function getTLSTicket() { - return this.ssl.getTLSTicket(); + return this._handle.getTLSTicket(); }; TLSSocket.prototype._handleTimeout = function() { @@ -424,11 +449,11 @@ TLSSocket.prototype._finishInit = function() { } if (process.features.tls_npn) { - this.npnProtocol = this.ssl.getNegotiatedProtocol(); + this.npnProtocol = this._handle.getNegotiatedProtocol(); } if (process.features.tls_sni && this._tlsOptions.isServer) { - this.servername = this.ssl.getServername(); + this.servername = this._handle.getServername(); } debug('secure established'); @@ -440,48 +465,48 @@ TLSSocket.prototype._finishInit = function() { TLSSocket.prototype._start = function() { if (this._tlsOptions.requestOCSP) - this.ssl.requestOCSP(); - this.ssl.start(); + this._handle.requestOCSP(); + this._handle.start(); }; TLSSocket.prototype.setServername = function(name) { - this.ssl.setServername(name); + this._handle.setServername(name); }; TLSSocket.prototype.setSession = function(session) { if (typeof session === 'string') session = new Buffer(session, 'binary'); - this.ssl.setSession(session); + this._handle.setSession(session); }; TLSSocket.prototype.getPeerCertificate = function(detailed) { - if (this.ssl) { + if (this._handle) { return common.translatePeerCertificate( - this.ssl.getPeerCertificate(detailed)); + this._handle.getPeerCertificate(detailed)); } return null; }; TLSSocket.prototype.getSession = function() { - if (this.ssl) { - return this.ssl.getSession(); + if (this._handle) { + return this._handle.getSession(); } return null; }; TLSSocket.prototype.isSessionReused = function() { - if (this.ssl) { - return this.ssl.isSessionReused(); + if (this._handle) { + return this._handle.isSessionReused(); } return null; }; TLSSocket.prototype.getCipher = function(err) { - if (this.ssl) { - return this.ssl.getCurrentCipher(); + if (this._handle) { + return this._handle.getCurrentCipher(); } else { return null; } @@ -620,7 +645,7 @@ function Server(/* [options], listener */) { socket.on('secure', function() { if (socket._requestCert) { - var verifyError = socket.ssl.verifyError(); + var verifyError = socket._handle.verifyError(); if (verifyError) { socket.authorizationError = verifyError.code; @@ -775,28 +800,6 @@ function normalizeConnectArgs(listArgs) { return (cb) ? [options, cb] : [options]; } -function legacyConnect(hostname, options, NPN, context) { - assert(options.socket); - if (!tls_legacy) - tls_legacy = require('_tls_legacy'); - - var pair = tls_legacy.createSecurePair(context, - false, - true, - !!options.rejectUnauthorized, - { - NPNProtocols: NPN.NPNProtocols, - servername: hostname - }); - tls_legacy.pipe(pair, options.socket); - pair.cleartext._controlReleased = true; - pair.on('error', function(err) { - pair.cleartext.emit('error', err); - }); - - return pair; -} - exports.connect = function(/* [port, host], options, cb */) { var args = normalizeConnectArgs(arguments); var options = args[0]; @@ -819,51 +822,23 @@ exports.connect = function(/* [port, host], options, cb */) { context = tls.createSecureContext(options); tls.convertNPNProtocols(options.NPNProtocols, NPN); - // Wrapping TLS socket inside another TLS socket was requested - - // create legacy secure pair var socket; - var legacy; var result; - if (options.socket instanceof TLSSocket) { - debug('legacy connect'); - legacy = true; - socket = legacyConnect(hostname, options, NPN, context); - result = socket.cleartext; - } else { - legacy = false; - socket = new TLSSocket(options.socket, { - secureContext: context, - isServer: false, - requestCert: true, - rejectUnauthorized: options.rejectUnauthorized, - session: options.session, - NPNProtocols: NPN.NPNProtocols, - requestOCSP: options.requestOCSP - }); - result = socket; - } - - if (socket._handle && !socket._connecting) { - onHandle(); - } else { - // Not even started connecting yet (or probably resolving dns address), - // catch socket errors and assign handle. - if (!legacy && options.socket) { - options.socket.once('connect', function() { - assert(options.socket._handle); - socket._handle = options.socket._handle; - socket._handle.owner = socket; - socket.emit('connect'); - }); - } - socket.once('connect', onHandle); - } + socket = new TLSSocket(options.socket, { + secureContext: context, + isServer: false, + requestCert: true, + rejectUnauthorized: options.rejectUnauthorized, + session: options.session, + NPNProtocols: NPN.NPNProtocols, + requestOCSP: options.requestOCSP + }); + result = socket; if (cb) result.once('secureConnect', cb); if (!options.socket) { - assert(!legacy); var connect_opt; if (options.path && !options.port) { connect_opt = { path: options.path }; @@ -880,20 +855,17 @@ exports.connect = function(/* [port, host], options, cb */) { return result; function onHandle() { - if (!legacy) - socket._releaseControl(); + socket._releaseControl(); if (options.session) socket.setSession(options.session); - if (!legacy) { - if (options.servername) - socket.setServername(options.servername); + if (options.servername) + socket.setServername(options.servername); - socket._start(); - } + socket._start(); socket.on('secure', function() { - var verifyError = socket.ssl.verifyError(); + var verifyError = socket._handle.verifyError(); // Verify that server's identity matches it's certificate's names if (!verifyError) { diff --git a/src/tls_wrap.cc b/src/tls_wrap.cc index e84dbbd4c32997..d9b578ebd04716 100644 --- a/src/tls_wrap.cc +++ b/src/tls_wrap.cc @@ -509,6 +509,7 @@ int TLSWrap::ReadStop() { return stream_->ReadStop(); } + int TLSWrap::SetBlocking(bool enable) { return stream_->SetBlocking(enable); } From 15033d045cf04655e4f99c71c0e8c7569a173c22 Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Fri, 13 Feb 2015 11:10:30 +0100 Subject: [PATCH 25/41] tls_wrap.js: fix tls.connect --- lib/_tls_wrap.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/_tls_wrap.js b/lib/_tls_wrap.js index 03533c2ffba25b..67ea6649390cae 100644 --- a/lib/_tls_wrap.js +++ b/lib/_tls_wrap.js @@ -226,7 +226,7 @@ function TLSSocket(socket, options) { this.encrypted = true; net.Socket.call(this, { - handle: this._wrapHandle(socket ? socket._handle : createTCP()), + handle: this._wrapHandle(socket && socket._handle), allowHalfOpen: socket && socket.allowHalfOpen, readable: false, writable: false @@ -255,6 +255,10 @@ TLSSocket.prototype._wrapHandle = function(handle) { var res; var options = this._tlsOptions; + if (!handle) { + handle = createTCP(); + handle.owner = this; + } // Wrap socket's handle var context = options.secureContext || @@ -377,6 +381,10 @@ TLSSocket.prototype._init = function(socket) { self.emit('connect'); }); } + + // Assume `tls.connect()` + if (!socket) + this._connecting = true; }; TLSSocket.prototype.renegotiate = function(options, callback) { From bd78b94b4c55518398f3f89bb99a6e990dd031c1 Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Fri, 13 Feb 2015 11:40:07 +0100 Subject: [PATCH 26/41] javascript test fixes --- lib/_tls_legacy.js | 4 +- lib/_tls_wrap.js | 103 ++++++++++-------- src/node_crypto.cc | 2 +- .../test-tls-client-default-ciphers.js | 14 ++- test/parallel/test-tls-close-notify.js | 4 +- 5 files changed, 73 insertions(+), 54 deletions(-) diff --git a/lib/_tls_legacy.js b/lib/_tls_legacy.js index e899002bac8325..fc0d115aee2e45 100644 --- a/lib/_tls_legacy.js +++ b/lib/_tls_legacy.js @@ -92,11 +92,11 @@ function onCryptoStreamFinish() { // Generate close notify // NOTE: first call checks if client has sent us shutdown, // second call enqueues shutdown into the BIO. - if (this.pair.ssl.sslShutdown() !== 1) { + if (this.pair.ssl.shutdownSSL() !== 1) { if (this.pair.ssl && this.pair.ssl.error) return this.pair.error(); - this.pair.ssl.sslShutdown(); + this.pair.ssl.shutdownSSL(); } if (this.pair.ssl && this.pair.ssl.error) diff --git a/lib/_tls_wrap.js b/lib/_tls_wrap.js index 67ea6649390cae..52da752a7b62cf 100644 --- a/lib/_tls_wrap.js +++ b/lib/_tls_wrap.js @@ -17,6 +17,12 @@ function createTCP() { return new TCP(); } +// constructor for lazy loading +function createPipe() { + var Pipe = process.binding('pipe_wrap').Pipe; + return new Pipe(); +} + function onhandshakestart() { debug('onhandshakestart'); @@ -245,10 +251,13 @@ function TLSSocket(socket, options) { util.inherits(TLSSocket, net.Socket); exports.TLSSocket = TLSSocket; -var tcpMethods = [ +var proxiedMethods = [ 'close', 'ref', 'unref', 'open', 'bind', 'listen', 'connect', 'bind6', 'connect6', 'getsockname', 'getpeername', 'setNoDelay', 'setKeepAlive', - 'setSimultaneousAccepts' + 'setSimultaneousAccepts', + + // PipeWrap + 'setPendingInstances' ]; TLSSocket.prototype._wrapHandle = function(handle) { @@ -256,7 +265,7 @@ TLSSocket.prototype._wrapHandle = function(handle) { var options = this._tlsOptions; if (!handle) { - handle = createTCP(); + handle = options.pipe ? createPipe() : createTCP(); handle.owner = this; } @@ -268,9 +277,8 @@ TLSSocket.prototype._wrapHandle = function(handle) { res._parent = handle; res._reading = handle._reading; - // Proxy HandleWrap and TCPWrap methods - // TODO(indutny): Do we want to support Pipes and TTYs? - tcpMethods.forEach(function(name) { + // Proxy HandleWrap, PipeWrap and TCPWrap methods + proxiedMethods.forEach(function(name) { res[name] = function methodProxy() { return handle[name].apply(handle, arguments); }; @@ -833,6 +841,7 @@ exports.connect = function(/* [port, host], options, cb */) { var socket; var result; socket = new TLSSocket(options.socket, { + pipe: options.path && !options.port, secureContext: context, isServer: false, requestCert: true, @@ -857,60 +866,62 @@ exports.connect = function(/* [port, host], options, cb */) { localAddress: options.localAddress }; } - socket.connect(connect_opt); + socket.connect(connect_opt, function() { + socket._start(); + }); } - return result; + socket._releaseControl(); - function onHandle() { - socket._releaseControl(); + if (options.session) + socket.setSession(options.session); - if (options.session) - socket.setSession(options.session); - - if (options.servername) - socket.setServername(options.servername); + if (options.servername) + socket.setServername(options.servername); + if (options.socket) socket._start(); - socket.on('secure', function() { - var verifyError = socket._handle.verifyError(); - // Verify that server's identity matches it's certificate's names - if (!verifyError) { - var cert = result.getPeerCertificate(); - verifyError = options.checkServerIdentity(hostname, cert); - } + socket.on('secure', function() { + var verifyError = socket._handle.verifyError(); - if (verifyError) { - result.authorized = false; - result.authorizationError = verifyError.code || verifyError.message; + // Verify that server's identity matches it's certificate's names + if (!verifyError) { + var cert = result.getPeerCertificate(); + verifyError = options.checkServerIdentity(hostname, cert); + } - if (options.rejectUnauthorized) { - result.emit('error', verifyError); - result.destroy(); - return; - } else { - result.emit('secureConnect'); - } + if (verifyError) { + result.authorized = false; + result.authorizationError = verifyError.code || verifyError.message; + + if (options.rejectUnauthorized) { + result.emit('error', verifyError); + result.destroy(); + return; } else { - result.authorized = true; result.emit('secureConnect'); } + } else { + result.authorized = true; + result.emit('secureConnect'); + } - // Uncork incoming data - result.removeListener('end', onHangUp); - }); + // Uncork incoming data + result.removeListener('end', onHangUp); + }); - function onHangUp() { - // NOTE: This logic is shared with _http_client.js - if (!socket._hadError) { - socket._hadError = true; - var error = new Error('socket hang up'); - error.code = 'ECONNRESET'; - socket.destroy(); - socket.emit('error', error); - } + function onHangUp() { + // NOTE: This logic is shared with _http_client.js + if (!socket._hadError) { + socket._hadError = true; + var error = new Error('socket hang up'); + error.code = 'ECONNRESET'; + socket.destroy(); + socket.emit('error', error); } - result.once('end', onHangUp); } + result.once('end', onHangUp); + + return result; }; diff --git a/src/node_crypto.cc b/src/node_crypto.cc index b4a8d0ef6b8a5e..85fe5df1f412d3 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -973,7 +973,7 @@ void SSLWrap::AddMethods(Environment* env, Handle t) { env->SetProtoMethod(t, "getCurrentCipher", GetCurrentCipher); env->SetProtoMethod(t, "endParser", EndParser); env->SetProtoMethod(t, "renegotiate", Renegotiate); - env->SetProtoMethod(t, "sslShutdown", Shutdown); + env->SetProtoMethod(t, "shutdownSSL", Shutdown); env->SetProtoMethod(t, "getTLSTicket", GetTLSTicket); env->SetProtoMethod(t, "newSessionDone", NewSessionDone); env->SetProtoMethod(t, "setOCSPResponse", SetOCSPResponse); diff --git a/test/parallel/test-tls-client-default-ciphers.js b/test/parallel/test-tls-client-default-ciphers.js index 1eb74e6981fd69..dfae4a7bb9a9e5 100644 --- a/test/parallel/test-tls-client-default-ciphers.js +++ b/test/parallel/test-tls-client-default-ciphers.js @@ -2,13 +2,21 @@ var assert = require('assert'); var common = require('../common'); var tls = require('tls'); +function Done() {} + function test1() { var ciphers = ''; + tls.createSecureContext = function(options) { - ciphers = options.ciphers + ciphers = options.ciphers; + throw new Done(); + } + + try { + var s = tls.connect(common.PORT); + } catch (e) { + assert(e instanceof Done); } - var s = tls.connect(common.PORT); - s.destroy(); assert.equal(ciphers, tls.DEFAULT_CIPHERS); } test1(); diff --git a/test/parallel/test-tls-close-notify.js b/test/parallel/test-tls-close-notify.js index 54f7314e2f7da0..c5decad5e51fb8 100644 --- a/test/parallel/test-tls-close-notify.js +++ b/test/parallel/test-tls-close-notify.js @@ -17,8 +17,8 @@ var server = tls.createServer({ cert: fs.readFileSync(common.fixturesDir + '/keys/agent1-cert.pem') }, function(c) { // Send close-notify without shutting down TCP socket - if (c.ssl.shutdown() !== 1) - c.ssl.shutdown(); + if (c._handle.shutdownSSL() !== 1) + c._handle.shutdownSSL(); }).listen(common.PORT, function() { var c = tls.connect(common.PORT, { rejectUnauthorized: false From 7b7bec6edd7b68c1efdfb1ffb7931dafb0a9cb33 Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Fri, 13 Feb 2015 11:54:24 +0100 Subject: [PATCH 27/41] tls_wrap: fix Receive --- src/tls_wrap.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tls_wrap.cc b/src/tls_wrap.cc index d9b578ebd04716..8c65a287387614 100644 --- a/src/tls_wrap.cc +++ b/src/tls_wrap.cc @@ -210,11 +210,11 @@ void TLSWrap::Receive(const FunctionCallbackInfo& args) { // Copy given buffer entirely or partiall if handle becomes closed // XXX(indunty): figure out uv_is_closing while (len > 0 /* && !uv_is_closing(reinterpret_cast(stream))*/) { - wrap->OnAlloc(len, &buf); + wrap->stream_->OnAlloc(len, &buf); size_t copy = buf.len > len ? len : buf.len; memcpy(buf.base, data, copy); buf.len = copy; - wrap->OnRead(buf.len, &buf, UV_UNKNOWN_HANDLE); + wrap->stream_->OnRead(buf.len, &buf, UV_UNKNOWN_HANDLE); data += copy; len -= copy; From 0c9e1a2dbc32f50b47f67e1b10ffabf8513dac27 Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Fri, 13 Feb 2015 12:09:09 +0100 Subject: [PATCH 28/41] tls_wrap: allow wrapping TLSWrap --- lib/_tls_wrap.js | 3 --- src/env.h | 1 + src/node_wrap.h | 19 ++++++++++++++++--- src/tls_wrap.cc | 1 + 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/lib/_tls_wrap.js b/lib/_tls_wrap.js index 52da752a7b62cf..aa320d06bf5e78 100644 --- a/lib/_tls_wrap.js +++ b/lib/_tls_wrap.js @@ -213,9 +213,6 @@ function onocspresponse(resp) { */ function TLSSocket(socket, options) { - // Disallow wrapping TLSSocket in TLSSocket - assert(!(socket instanceof TLSSocket)); - this._tlsOptions = options; this._secureEstablished = false; this._securePending = false; diff --git a/src/env.h b/src/env.h index 9b0ec4376ba7b1..c9b4cc0736301c 100644 --- a/src/env.h +++ b/src/env.h @@ -234,6 +234,7 @@ namespace node { V(tcp_constructor_template, v8::FunctionTemplate) \ V(tick_callback_function, v8::Function) \ V(tls_wrap_constructor_function, v8::Function) \ + V(tls_wrap_constructor_template, v8::FunctionTemplate) \ V(tty_constructor_template, v8::FunctionTemplate) \ V(udp_constructor_function, v8::Function) \ V(write_wrap_constructor_function, v8::Function) \ diff --git a/src/node_wrap.h b/src/node_wrap.h index 80d679606e9169..ddd7bd16e0d8c5 100644 --- a/src/node_wrap.h +++ b/src/node_wrap.h @@ -14,7 +14,7 @@ namespace node { -#define WITH_GENERIC_STREAM(env, obj, BODY) \ +#define WITH_GENERIC_UV_STREAM(env, obj, BODY, ELSE) \ do { \ if (env->tcp_constructor_template().IsEmpty() == false && \ env->tcp_constructor_template()->HasInstance(obj)) { \ @@ -28,16 +28,29 @@ namespace node { env->pipe_constructor_template()->HasInstance(obj)) { \ PipeWrap* const wrap = Unwrap(obj); \ BODY \ + } else { \ + ELSE \ } \ } while (0) +#define WITH_GENERIC_STREAM(env, obj, BODY) \ + do { \ + WITH_GENERIC_UV_STREAM(env, obj, BODY, { \ + if (env->tls_wrap_constructor_template().IsEmpty() == false && \ + env->tls_wrap_constructor_template()->HasInstance(obj)) { \ + TLSWrap* const wrap = Unwrap(obj); \ + BODY \ + } \ + }); \ + } while (0) + inline uv_stream_t* HandleToStream(Environment* env, v8::Local obj) { v8::HandleScope scope(env->isolate()); - WITH_GENERIC_STREAM(env, obj, { + WITH_GENERIC_UV_STREAM(env, obj, { return reinterpret_cast(wrap->UVHandle()); - }); + }, {}); return nullptr; } diff --git a/src/tls_wrap.cc b/src/tls_wrap.cc index 8c65a287387614..bcf4fe7241c565 100644 --- a/src/tls_wrap.cc +++ b/src/tls_wrap.cc @@ -824,6 +824,7 @@ void TLSWrap::Initialize(Handle target, env->SetProtoMethod(t, "setServername", SetServername); #endif // SSL_CRT_SET_TLSEXT_SERVERNAME_CB + env->set_tls_wrap_constructor_template(t); env->set_tls_wrap_constructor_function(t->GetFunction()); } From cac9cd72137970609c876d9a65b6dee452a05b13 Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Fri, 13 Feb 2015 23:11:57 +0100 Subject: [PATCH 29/41] tls_wrap: fix inception test --- src/tls_wrap.cc | 34 ++++++++++++++++++++++++++++++---- src/tls_wrap.h | 6 ++++++ 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/tls_wrap.cc b/src/tls_wrap.cc index bcf4fe7241c565..886894e795ce35 100644 --- a/src/tls_wrap.cc +++ b/src/tls_wrap.cc @@ -69,6 +69,9 @@ TLSWrap::TLSWrap(Environment* env, stream_->set_alloc_cb(OnAllocImpl, this); stream_->set_read_cb(OnReadImpl, this); + set_alloc_cb(OnAllocSelf, this); + set_read_cb(OnReadSelf, this); + InitSSL(); } @@ -401,14 +404,18 @@ void TLSWrap::ClearOut() { int read; do { read = SSL_read(ssl_, out, sizeof(out)); - if (read > 0) - OnData(read, Buffer::New(env(), out, read), Local()); + if (read > 0) { + uv_buf_t buf; + OnAlloc(read, &buf); + memcpy(buf.base, out, read); + OnRead(read, &buf, UV_UNKNOWN_HANDLE); + } } while (read > 0); int flags = SSL_get_shutdown(ssl_); if (!eof_ && flags & SSL_RECEIVED_SHUTDOWN) { eof_ = true; - OnData(UV_EOF, Local(), Local()); + OnRead(UV_EOF, nullptr, UV_UNKNOWN_HANDLE); } if (read == -1) { @@ -623,6 +630,25 @@ void TLSWrap::OnReadImpl(ssize_t nread, } +void TLSWrap::OnAllocSelf(size_t suggested_size, uv_buf_t* buf, void* ctx) { + buf->base = static_cast(malloc(suggested_size)); + CHECK_NE(buf->base, nullptr); + buf->len = suggested_size; +} + + +void TLSWrap::OnReadSelf(ssize_t nread, + const uv_buf_t* buf, + uv_handle_type pending, + void* ctx) { + TLSWrap* wrap = static_cast(ctx); + Local buf_obj; + if (buf != nullptr) + buf_obj = Buffer::Use(wrap->env(), buf->base, buf->len); + wrap->OnData(nread, buf_obj, Local()); +} + + void TLSWrap::DoRead(ssize_t nread, const uv_buf_t* buf, uv_handle_type pending) { @@ -639,7 +665,7 @@ void TLSWrap::DoRead(ssize_t nread, HandleScope handle_scope(env()->isolate()); Context::Scope context_scope(env()->context()); - OnData(nread, Local(), Local()); + OnRead(nread, nullptr, UV_UNKNOWN_HANDLE); return; } diff --git a/src/tls_wrap.h b/src/tls_wrap.h index 80fc2dbd070d18..23899201f4f61c 100644 --- a/src/tls_wrap.h +++ b/src/tls_wrap.h @@ -114,6 +114,12 @@ class TLSWrap : public crypto::SSLWrap, const uv_buf_t* buf, uv_handle_type pending, void* ctx); + static void OnAfterWriteSelf(WriteWrap* w, void* ctx); + static void OnAllocSelf(size_t size, uv_buf_t* buf, void* ctx); + static void OnReadSelf(ssize_t nread, + const uv_buf_t* buf, + uv_handle_type pending, + void* ctx); void DoRead(ssize_t nread, const uv_buf_t* buf, uv_handle_type pending); From eadd63e9df1f6bf3ab210a02fe62b5dffc6eb124 Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Fri, 13 Feb 2015 23:19:00 +0100 Subject: [PATCH 30/41] test: fix multi-key test, _handle disappears --- test/parallel/test-tls-multi-key.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/parallel/test-tls-multi-key.js b/test/parallel/test-tls-multi-key.js index cdf85008745e2e..657d9084d44d78 100644 --- a/test/parallel/test-tls-multi-key.js +++ b/test/parallel/test-tls-multi-key.js @@ -28,15 +28,14 @@ var server = tls.createServer(options, function(conn) { ciphers: 'ECDHE-ECDSA-AES256-GCM-SHA384', rejectUnauthorized: false }, function() { + ciphers.push(ecdsa.getCipher()); var rsa = tls.connect(common.PORT, { ciphers: 'ECDHE-RSA-AES256-GCM-SHA384', rejectUnauthorized: false }, function() { + ciphers.push(rsa.getCipher()); ecdsa.destroy(); rsa.destroy(); - - ciphers.push(ecdsa.getCipher()); - ciphers.push(rsa.getCipher()); server.close(); }); }); From 62c7c74f9a83e27c2fe2275b59535467d757a004 Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Fri, 13 Feb 2015 23:51:50 +0100 Subject: [PATCH 31/41] stream_base: last fixes, hopefuly --- lib/_tls_wrap.js | 7 +++++++ src/stream_base.h | 5 +++-- src/stream_wrap.cc | 5 +++++ src/stream_wrap.h | 1 + src/tls_wrap.cc | 8 ++++++-- src/tls_wrap.h | 3 ++- 6 files changed, 24 insertions(+), 5 deletions(-) diff --git a/lib/_tls_wrap.js b/lib/_tls_wrap.js index aa320d06bf5e78..a7cf228cf1cea5 100644 --- a/lib/_tls_wrap.js +++ b/lib/_tls_wrap.js @@ -477,6 +477,13 @@ TLSSocket.prototype._finishInit = function() { }; TLSSocket.prototype._start = function() { + if (this._connecting) { + this.once('connect', function() { + this._start(); + }); + return; + } + if (this._tlsOptions.requestOCSP) this._handle.requestOCSP(); this._handle.start(); diff --git a/src/stream_base.h b/src/stream_base.h index fa6cb9cd9a4ff8..4921c020858575 100644 --- a/src/stream_base.h +++ b/src/stream_base.h @@ -19,7 +19,7 @@ class StreamReq { public: typedef void (*DoneCb)(Req* req, int status); - StreamReq(DoneCb cb) : cb_(cb) { + explicit StreamReq(DoneCb cb) : cb_(cb) { } inline void Done(int status) { @@ -166,6 +166,7 @@ class StreamBase : public StreamResource { virtual void* Cast() = 0; virtual bool IsAlive() const = 0; + virtual bool IsClosing() const = 0; virtual bool IsIPCPipe() const = 0; virtual int GetFD() const = 0; @@ -187,7 +188,7 @@ class StreamBase : public StreamResource { v8::Local handle); protected: - StreamBase(Environment* env) : env_(env), consumed_(false) { + explicit StreamBase(Environment* env) : env_(env), consumed_(false) { } virtual ~StreamBase() = default; diff --git a/src/stream_wrap.cc b/src/stream_wrap.cc index a183d44d088c0e..dc8bb30968f9c8 100644 --- a/src/stream_wrap.cc +++ b/src/stream_wrap.cc @@ -92,6 +92,11 @@ bool StreamWrap::IsAlive() const { } +bool StreamWrap::IsClosing() const { + return uv_is_closing(reinterpret_cast(stream())); +} + + void* StreamWrap::Cast() { return reinterpret_cast(this); } diff --git a/src/stream_wrap.h b/src/stream_wrap.h index 50e6ce39d810bf..af1794d03a4846 100644 --- a/src/stream_wrap.h +++ b/src/stream_wrap.h @@ -21,6 +21,7 @@ class StreamWrap : public HandleWrap, public StreamBase { int GetFD() const override; void* Cast() override; bool IsAlive() const override; + bool IsClosing() const override; bool IsIPCPipe() const override; // JavaScript functions diff --git a/src/tls_wrap.cc b/src/tls_wrap.cc index 886894e795ce35..20a1047c8d9cc2 100644 --- a/src/tls_wrap.cc +++ b/src/tls_wrap.cc @@ -211,8 +211,7 @@ void TLSWrap::Receive(const FunctionCallbackInfo& args) { uv_buf_t buf; // Copy given buffer entirely or partiall if handle becomes closed - // XXX(indunty): figure out uv_is_closing - while (len > 0 /* && !uv_is_closing(reinterpret_cast(stream))*/) { + while (len > 0 && !wrap->IsClosing()) { wrap->stream_->OnAlloc(len, &buf); size_t copy = buf.len > len ? len : buf.len; memcpy(buf.base, data, copy); @@ -507,6 +506,11 @@ bool TLSWrap::IsAlive() const { } +bool TLSWrap::IsClosing() const { + return stream_->IsClosing(); +} + + int TLSWrap::ReadStart() { return stream_->ReadStart(); } diff --git a/src/tls_wrap.h b/src/tls_wrap.h index 23899201f4f61c..217398b8f33aec 100644 --- a/src/tls_wrap.h +++ b/src/tls_wrap.h @@ -34,6 +34,7 @@ class TLSWrap : public crypto::SSLWrap, void* Cast() override; int GetFD() const override; bool IsAlive() const override; + bool IsClosing() const override; // JavaScript functions int ReadStart() override; @@ -66,7 +67,7 @@ class TLSWrap : public crypto::SSLWrap, // Write callback queue's item class WriteItem { public: - WriteItem(WriteWrap* w) : w_(w) { + explicit WriteItem(WriteWrap* w) : w_(w) { } ~WriteItem() { w_ = nullptr; From a2f0e57b8a44964ab77eb28f7fdf918121b396e6 Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Sat, 14 Feb 2015 00:07:30 +0100 Subject: [PATCH 32/41] tls_wrap: return of this.ssl --- lib/_tls_wrap.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/_tls_wrap.js b/lib/_tls_wrap.js index a7cf228cf1cea5..2365dba1e7f9eb 100644 --- a/lib/_tls_wrap.js +++ b/lib/_tls_wrap.js @@ -235,6 +235,9 @@ function TLSSocket(socket, options) { writable: false }); + // Proxy for API compatibility + this.ssl = this._handle; + this.on('error', this._tlsError); this._init(socket); From bcbad15d01b775141edc33ee4d088a1d5348b093 Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Sat, 14 Feb 2015 00:29:15 +0100 Subject: [PATCH 33/41] tls_wrap.js: result=>socket --- lib/_tls_wrap.js | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/lib/_tls_wrap.js b/lib/_tls_wrap.js index 2365dba1e7f9eb..9847a3f8c4705b 100644 --- a/lib/_tls_wrap.js +++ b/lib/_tls_wrap.js @@ -845,9 +845,7 @@ exports.connect = function(/* [port, host], options, cb */) { context = tls.createSecureContext(options); tls.convertNPNProtocols(options.NPNProtocols, NPN); - var socket; - var result; - socket = new TLSSocket(options.socket, { + var socket = new TLSSocket(options.socket, { pipe: options.path && !options.port, secureContext: context, isServer: false, @@ -857,10 +855,9 @@ exports.connect = function(/* [port, host], options, cb */) { NPNProtocols: NPN.NPNProtocols, requestOCSP: options.requestOCSP }); - result = socket; if (cb) - result.once('secureConnect', cb); + socket.once('secureConnect', cb); if (!options.socket) { var connect_opt; @@ -894,28 +891,28 @@ exports.connect = function(/* [port, host], options, cb */) { // Verify that server's identity matches it's certificate's names if (!verifyError) { - var cert = result.getPeerCertificate(); + var cert = socket.getPeerCertificate(); verifyError = options.checkServerIdentity(hostname, cert); } if (verifyError) { - result.authorized = false; - result.authorizationError = verifyError.code || verifyError.message; + socket.authorized = false; + socket.authorizationError = verifyError.code || verifyError.message; if (options.rejectUnauthorized) { - result.emit('error', verifyError); - result.destroy(); + socket.emit('error', verifyError); + socket.destroy(); return; } else { - result.emit('secureConnect'); + socket.emit('secureConnect'); } } else { - result.authorized = true; - result.emit('secureConnect'); + socket.authorized = true; + socket.emit('secureConnect'); } // Uncork incoming data - result.removeListener('end', onHangUp); + socket.removeListener('end', onHangUp); }); function onHangUp() { @@ -928,7 +925,7 @@ exports.connect = function(/* [port, host], options, cb */) { socket.emit('error', error); } } - result.once('end', onHangUp); + socket.once('end', onHangUp); - return result; + return socket; }; From f8d5779adb2ddc2eac954d82390ccb17131ccd77 Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Sun, 15 Feb 2015 18:37:27 +0100 Subject: [PATCH 34/41] stream_base: remove GetObject() --- src/stream_base.cc | 8 ++++---- src/stream_base.h | 5 +---- src/stream_wrap.cc | 5 ----- src/stream_wrap.h | 1 - src/tls_wrap.cc | 5 ----- src/tls_wrap.h | 1 - 6 files changed, 5 insertions(+), 20 deletions(-) diff --git a/src/stream_base.cc b/src/stream_base.cc index a179a189b61818..bd0f6ac5ab5885 100644 --- a/src/stream_base.cc +++ b/src/stream_base.cc @@ -133,7 +133,7 @@ void StreamBase::AfterShutdown(ShutdownWrap* req_wrap, int status) { // The wrap and request objects should still be there. CHECK_EQ(req_wrap->persistent().IsEmpty(), false); - CHECK_EQ(wrap->GetObject().IsEmpty(), false); + CHECK_EQ(wrap->GetAsyncWrap()->persistent().IsEmpty(), false); HandleScope handle_scope(env->isolate()); Context::Scope context_scope(env->context()); @@ -141,7 +141,7 @@ void StreamBase::AfterShutdown(ShutdownWrap* req_wrap, int status) { Local req_wrap_obj = req_wrap->object(); Local argv[3] = { Integer::New(env->isolate(), status), - wrap->GetObject(), + wrap->GetAsyncWrap()->object(), req_wrap_obj }; @@ -442,7 +442,7 @@ void StreamBase::AfterWrite(WriteWrap* req_wrap, int status) { // The wrap and request objects should still be there. CHECK_EQ(req_wrap->persistent().IsEmpty(), false); - CHECK_EQ(wrap->GetObject().IsEmpty(), false); + CHECK_EQ(wrap->GetAsyncWrap()->persistent().IsEmpty(), false); // Unref handle property Local req_wrap_obj = req_wrap->object(); @@ -451,7 +451,7 @@ void StreamBase::AfterWrite(WriteWrap* req_wrap, int status) { Local argv[] = { Integer::New(env->isolate(), status), - wrap->GetObject(), + wrap->GetAsyncWrap()->object(), req_wrap_obj, Undefined(env->isolate()) }; diff --git a/src/stream_base.h b/src/stream_base.h index 4921c020858575..fdd2d403981d21 100644 --- a/src/stream_base.h +++ b/src/stream_base.h @@ -193,10 +193,7 @@ class StreamBase : public StreamResource { virtual ~StreamBase() = default; - virtual v8::Local GetObject() = 0; - - // Optional - virtual AsyncWrap* GetAsyncWrap(); + virtual AsyncWrap* GetAsyncWrap() = 0; // Libuv callbacks static void AfterShutdown(ShutdownWrap* req, int status); diff --git a/src/stream_wrap.cc b/src/stream_wrap.cc index dc8bb30968f9c8..15a20f4d5d8ce5 100644 --- a/src/stream_wrap.cc +++ b/src/stream_wrap.cc @@ -102,11 +102,6 @@ void* StreamWrap::Cast() { } -Local StreamWrap::GetObject() { - return object(); -} - - AsyncWrap* StreamWrap::GetAsyncWrap() { return static_cast(this); } diff --git a/src/stream_wrap.h b/src/stream_wrap.h index af1794d03a4846..a45ef275676ebb 100644 --- a/src/stream_wrap.h +++ b/src/stream_wrap.h @@ -66,7 +66,6 @@ class StreamWrap : public HandleWrap, public StreamBase { ~StreamWrap() { } - v8::Local GetObject() override; AsyncWrap* GetAsyncWrap() override; void UpdateWriteQueueSize(); diff --git a/src/tls_wrap.cc b/src/tls_wrap.cc index 20a1047c8d9cc2..5d9a5d96c18f95 100644 --- a/src/tls_wrap.cc +++ b/src/tls_wrap.cc @@ -481,11 +481,6 @@ void* TLSWrap::Cast() { } -Local TLSWrap::GetObject() { - return object(); -} - - AsyncWrap* TLSWrap::GetAsyncWrap() { return static_cast(this); } diff --git a/src/tls_wrap.h b/src/tls_wrap.h index 217398b8f33aec..f6161922929782 100644 --- a/src/tls_wrap.h +++ b/src/tls_wrap.h @@ -104,7 +104,6 @@ class TLSWrap : public crypto::SSLWrap, } } - v8::Local GetObject() override; AsyncWrap* GetAsyncWrap() override; bool IsIPCPipe() const override; From 659d0cb7dcb53f179bee248bba8ce0b761a45d4e Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Sun, 15 Feb 2015 18:43:11 +0100 Subject: [PATCH 35/41] stream_base: fix lint errors --- src/stream_base.cc | 2 +- src/stream_base.h | 2 +- src/stream_wrap.h | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/stream_base.cc b/src/stream_base.cc index bd0f6ac5ab5885..04d4780b0182a0 100644 --- a/src/stream_base.cc +++ b/src/stream_base.cc @@ -1,10 +1,10 @@ #include "stream_base.h" +#include "stream_wrap.h" #include "node.h" #include "node_buffer.h" #include "env.h" #include "env-inl.h" -#include "stream_wrap.h" #include "string_bytes.h" #include "tls_wrap.h" #include "util.h" diff --git a/src/stream_base.h b/src/stream_base.h index fdd2d403981d21..60598b63002fb4 100644 --- a/src/stream_base.h +++ b/src/stream_base.h @@ -214,7 +214,7 @@ class StreamBase : public StreamResource { const v8::PropertyCallbackInfo&); template & args)> static void JSMethod(const v8::FunctionCallbackInfo& args); diff --git a/src/stream_wrap.h b/src/stream_wrap.h index a45ef275676ebb..bd229ef03cf889 100644 --- a/src/stream_wrap.h +++ b/src/stream_wrap.h @@ -1,9 +1,10 @@ #ifndef SRC_STREAM_WRAP_H_ #define SRC_STREAM_WRAP_H_ +#include "stream_base.h" + #include "env.h" #include "handle_wrap.h" -#include "stream_base.h" #include "string_bytes.h" #include "v8.h" From 59cd1f2bb46425ee4fd019234d4f1c713171640e Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Mon, 16 Feb 2015 12:10:19 +0100 Subject: [PATCH 36/41] tls_wrap: support reading in parts --- src/tls_wrap.cc | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/tls_wrap.cc b/src/tls_wrap.cc index 5d9a5d96c18f95..ee32e88a62f72e 100644 --- a/src/tls_wrap.cc +++ b/src/tls_wrap.cc @@ -401,15 +401,25 @@ void TLSWrap::ClearOut() { char out[kClearOutChunkSize]; int read; - do { + for (;;) { read = SSL_read(ssl_, out, sizeof(out)); - if (read > 0) { + + if (read <= 0) + break; + + while (read > 0) { + int avail = read; + uv_buf_t buf; - OnAlloc(read, &buf); - memcpy(buf.base, out, read); - OnRead(read, &buf, UV_UNKNOWN_HANDLE); + OnAlloc(avail, &buf); + if (static_cast(buf.len) < avail) + avail = buf.len; + memcpy(buf.base, out, avail); + OnRead(avail, &buf, UV_UNKNOWN_HANDLE); + + read -= avail; } - } while (read > 0); + } int flags = SSL_get_shutdown(ssl_); if (!eof_ && flags & SSL_RECEIVED_SHUTDOWN) { From ce0e7a45fb337ac8f2b471f8df29750c89aba807 Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Mon, 16 Feb 2015 12:12:43 +0100 Subject: [PATCH 37/41] stream_base: OnData => EmitData --- src/stream_base.cc | 6 +++--- src/stream_base.h | 7 +++---- src/stream_wrap.cc | 4 ++-- src/tls_wrap.cc | 3 +-- 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/stream_base.cc b/src/stream_base.cc index 04d4780b0182a0..8a1d7a789dbcbc 100644 --- a/src/stream_base.cc +++ b/src/stream_base.cc @@ -476,9 +476,9 @@ int StreamBase::SetBlocking(const v8::FunctionCallbackInfo& args) { } -void StreamBase::OnData(ssize_t nread, - Local buf, - Local handle) { +void StreamBase::EmitData(ssize_t nread, + Local buf, + Local handle) { Environment* env = env_; Local argv[] = { diff --git a/src/stream_base.h b/src/stream_base.h index 60598b63002fb4..4ccae42f7220a3 100644 --- a/src/stream_base.h +++ b/src/stream_base.h @@ -182,10 +182,9 @@ class StreamBase : public StreamResource { template inline Outer* Cast() { return static_cast(Cast()); } - // TODO(indutny): should be a part of Resource - void OnData(ssize_t nread, - v8::Local buf, - v8::Local handle); + void EmitData(ssize_t nread, + v8::Local buf, + v8::Local handle); protected: explicit StreamBase(Environment* env) : env_(env), consumed_(false) { diff --git a/src/stream_wrap.cc b/src/stream_wrap.cc index 15a20f4d5d8ce5..014484f475bf36 100644 --- a/src/stream_wrap.cc +++ b/src/stream_wrap.cc @@ -186,7 +186,7 @@ void StreamWrap::OnReadImpl(ssize_t nread, if (nread < 0) { if (buf->base != nullptr) free(buf->base); - wrap->OnData(nread, Local(), pending_obj); + wrap->EmitData(nread, Local(), pending_obj); return; } @@ -209,7 +209,7 @@ void StreamWrap::OnReadImpl(ssize_t nread, CHECK_EQ(pending, UV_UNKNOWN_HANDLE); } - wrap->OnData(nread, Buffer::Use(env, base, nread), pending_obj); + wrap->EmitData(nread, Buffer::Use(env, base, nread), pending_obj); } diff --git a/src/tls_wrap.cc b/src/tls_wrap.cc index ee32e88a62f72e..96106eb3a7a1b0 100644 --- a/src/tls_wrap.cc +++ b/src/tls_wrap.cc @@ -300,7 +300,6 @@ void TLSWrap::EncOut() { write_size_ = NodeBIO::FromBIO(enc_out_)->PeekMultiple(data, size, &count); CHECK(write_size_ != 0 && count != 0); - // XXX(indutny): How to do it in a better way? Local req_wrap_obj = env()->write_wrap_constructor_function()->NewInstance(); char* storage = new char[sizeof(WriteWrap)]; @@ -654,7 +653,7 @@ void TLSWrap::OnReadSelf(ssize_t nread, Local buf_obj; if (buf != nullptr) buf_obj = Buffer::Use(wrap->env(), buf->base, buf->len); - wrap->OnData(nread, buf_obj, Local()); + wrap->EmitData(nread, buf_obj, Local()); } From 259c236d4bbc1664a48f7975f3c966cdfd2bd09c Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Mon, 16 Feb 2015 12:14:24 +0100 Subject: [PATCH 38/41] net: remove commented code --- lib/net.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/net.js b/lib/net.js index afb1c503835e32..2ca4b743459705 100644 --- a/lib/net.js +++ b/lib/net.js @@ -962,9 +962,8 @@ function afterConnect(status, handle, req, readable, writable) { } // Update handle if it was wrapped - handle = self._handle; // TODO(indutny): assert that the handle is actually an ancestor of old one - // assert(handle === self._handle, 'handle != self._handle'); + handle = self._handle; debug('afterConnect'); From 9f409e5a27f2c7c49a23d9dc4f8f14f9b35b60d4 Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Mon, 16 Feb 2015 17:29:06 +0100 Subject: [PATCH 39/41] stream_base: move SetBlocking to StreamWrap --- lib/_tls_wrap.js | 2 +- src/pipe_wrap.cc | 2 +- src/stream_base.cc | 21 ++++++--------------- src/stream_base.h | 2 -- src/stream_wrap.cc | 18 ++++++++++++++++-- src/stream_wrap.h | 6 +++++- src/tcp_wrap.cc | 2 +- src/tls_wrap.cc | 7 +------ src/tls_wrap.h | 1 - src/tty_wrap.cc | 2 +- 10 files changed, 32 insertions(+), 31 deletions(-) diff --git a/lib/_tls_wrap.js b/lib/_tls_wrap.js index 9847a3f8c4705b..10221b99c30847 100644 --- a/lib/_tls_wrap.js +++ b/lib/_tls_wrap.js @@ -254,7 +254,7 @@ exports.TLSSocket = TLSSocket; var proxiedMethods = [ 'close', 'ref', 'unref', 'open', 'bind', 'listen', 'connect', 'bind6', 'connect6', 'getsockname', 'getpeername', 'setNoDelay', 'setKeepAlive', - 'setSimultaneousAccepts', + 'setSimultaneousAccepts', 'setBlocking', // PipeWrap 'setPendingInstances' diff --git a/src/pipe_wrap.cc b/src/pipe_wrap.cc index 473fdef2f72cc3..08fed68741f614 100644 --- a/src/pipe_wrap.cc +++ b/src/pipe_wrap.cc @@ -81,7 +81,7 @@ void PipeWrap::Initialize(Handle target, env->SetProtoMethod(t, "unref", HandleWrap::Unref); env->SetProtoMethod(t, "ref", HandleWrap::Ref); - StreamWrap::AddMethods(env, t); + StreamWrap::AddMethods(env, t); env->SetProtoMethod(t, "bind", Bind); env->SetProtoMethod(t, "listen", Listen); diff --git a/src/stream_base.cc b/src/stream_base.cc index 8a1d7a789dbcbc..0a1324bb5872c5 100644 --- a/src/stream_base.cc +++ b/src/stream_base.cc @@ -68,9 +68,6 @@ void StreamBase::AddMethods(Environment* env, Handle t) { env->SetProtoMethod(t, "writeBinaryString", JSMethod >); - env->SetProtoMethod(t, - "setBlocking", - JSMethod); } @@ -98,17 +95,17 @@ void StreamBase::JSMethod(const FunctionCallbackInfo& args) { } -int StreamBase::ReadStart(const v8::FunctionCallbackInfo& args) { +int StreamBase::ReadStart(const FunctionCallbackInfo& args) { return ReadStart(); } -int StreamBase::ReadStop(const v8::FunctionCallbackInfo& args) { +int StreamBase::ReadStop(const FunctionCallbackInfo& args) { return ReadStop(); } -int StreamBase::Shutdown(const v8::FunctionCallbackInfo& args) { +int StreamBase::Shutdown(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); CHECK(args[0]->IsObject()); @@ -152,7 +149,7 @@ void StreamBase::AfterShutdown(ShutdownWrap* req_wrap, int status) { } -int StreamBase::Writev(const v8::FunctionCallbackInfo& args) { +int StreamBase::Writev(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); CHECK(args[0]->IsObject()); @@ -259,7 +256,7 @@ int StreamBase::Writev(const v8::FunctionCallbackInfo& args) { -int StreamBase::WriteBuffer(const v8::FunctionCallbackInfo& args) { +int StreamBase::WriteBuffer(const FunctionCallbackInfo& args) { CHECK(args[0]->IsObject()); CHECK(Buffer::HasInstance(args[1])); Environment* env = Environment::GetCurrent(args); @@ -310,7 +307,7 @@ int StreamBase::WriteBuffer(const v8::FunctionCallbackInfo& args) { template -int StreamBase::WriteString(const v8::FunctionCallbackInfo& args) { +int StreamBase::WriteString(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); CHECK(args[0]->IsObject()); CHECK(args[1]->IsString()); @@ -470,12 +467,6 @@ void StreamBase::AfterWrite(WriteWrap* req_wrap, int status) { } -int StreamBase::SetBlocking(const v8::FunctionCallbackInfo& args) { - CHECK_GT(args.Length(), 0); - return SetBlocking(args[0]->IsTrue()); -} - - void StreamBase::EmitData(ssize_t nread, Local buf, Local handle) { diff --git a/src/stream_base.h b/src/stream_base.h index 4ccae42f7220a3..1be62eb9414020 100644 --- a/src/stream_base.h +++ b/src/stream_base.h @@ -172,7 +172,6 @@ class StreamBase : public StreamResource { virtual int ReadStart() = 0; virtual int ReadStop() = 0; - virtual int SetBlocking(bool enable) = 0; inline void Consume() { CHECK_EQ(consumed_, false); @@ -206,7 +205,6 @@ class StreamBase : public StreamResource { int WriteBuffer(const v8::FunctionCallbackInfo& args); template int WriteString(const v8::FunctionCallbackInfo& args); - int SetBlocking(const v8::FunctionCallbackInfo& args); template static void GetFD(v8::Local, diff --git a/src/stream_wrap.cc b/src/stream_wrap.cc index 014484f475bf36..3b50f638eb0fc7 100644 --- a/src/stream_wrap.cc +++ b/src/stream_wrap.cc @@ -77,6 +77,13 @@ StreamWrap::StreamWrap(Environment* env, } +void StreamWrap::AddMethods(Environment* env, + v8::Handle target) { + env->SetProtoMethod(target, "setBlocking", SetBlocking); + StreamBase::AddMethods(env, target); +} + + int StreamWrap::GetFD() const { int fd = -1; #if !defined(_WIN32) @@ -250,8 +257,15 @@ void StreamWrap::OnRead(uv_stream_t* handle, } -int StreamWrap::SetBlocking(bool enable) { - return uv_stream_set_blocking(stream(), enable); +void StreamWrap::SetBlocking(const FunctionCallbackInfo& args) { + StreamWrap* wrap = Unwrap(args.Holder()); + + CHECK_GT(args.Length(), 0); + if (!wrap->IsAlive()) + return args.GetReturnValue().Set(UV_EINVAL); + + bool enable = args[0]->IsTrue(); + args.GetReturnValue().Set(uv_stream_set_blocking(wrap->stream(), enable)); } diff --git a/src/stream_wrap.h b/src/stream_wrap.h index bd229ef03cf889..ca673b4ef11879 100644 --- a/src/stream_wrap.h +++ b/src/stream_wrap.h @@ -28,7 +28,6 @@ class StreamWrap : public HandleWrap, public StreamBase { // JavaScript functions int ReadStart() override; int ReadStop() override; - int SetBlocking(bool enable) override; // Resource implementation int DoShutdown(ShutdownWrap* req_wrap) override; @@ -70,7 +69,12 @@ class StreamWrap : public HandleWrap, public StreamBase { AsyncWrap* GetAsyncWrap() override; void UpdateWriteQueueSize(); + static void AddMethods(Environment* env, + v8::Handle target); + private: + static void SetBlocking(const v8::FunctionCallbackInfo& args); + // Callbacks for libuv static void OnAlloc(uv_handle_t* handle, size_t suggested_size, diff --git a/src/tcp_wrap.cc b/src/tcp_wrap.cc index 441a464257c21c..a823e758ee100d 100644 --- a/src/tcp_wrap.cc +++ b/src/tcp_wrap.cc @@ -89,7 +89,7 @@ void TCPWrap::Initialize(Handle target, env->SetProtoMethod(t, "ref", HandleWrap::Ref); env->SetProtoMethod(t, "unref", HandleWrap::Unref); - StreamWrap::AddMethods(env, t); + StreamWrap::AddMethods(env, t); env->SetProtoMethod(t, "open", Open); env->SetProtoMethod(t, "bind", Bind); diff --git a/src/tls_wrap.cc b/src/tls_wrap.cc index 96106eb3a7a1b0..ab8db6951bdc30 100644 --- a/src/tls_wrap.cc +++ b/src/tls_wrap.cc @@ -525,11 +525,6 @@ int TLSWrap::ReadStop() { } -int TLSWrap::SetBlocking(bool enable) { - return stream_->SetBlocking(enable); -} - - const char* TLSWrap::Error() const { return error_; } @@ -850,7 +845,7 @@ void TLSWrap::Initialize(Handle target, env->SetProtoMethod(t, "enableSessionCallbacks", EnableSessionCallbacks); env->SetProtoMethod(t, "enableHelloParser", EnableHelloParser); - StreamWrap::AddMethods(env, t); + StreamBase::AddMethods(env, t); SSLWrap::AddMethods(env, t); #ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB diff --git a/src/tls_wrap.h b/src/tls_wrap.h index f6161922929782..42452055ced275 100644 --- a/src/tls_wrap.h +++ b/src/tls_wrap.h @@ -40,7 +40,6 @@ class TLSWrap : public crypto::SSLWrap, int ReadStart() override; int ReadStop() override; - int SetBlocking(bool enable) override; int DoShutdown(ShutdownWrap* req_wrap) override; int DoTryWrite(uv_buf_t** bufs, size_t* count) override; int DoWrite(WriteWrap* w, diff --git a/src/tty_wrap.cc b/src/tty_wrap.cc index 2196003040dcc5..186f2f0100162e 100644 --- a/src/tty_wrap.cc +++ b/src/tty_wrap.cc @@ -39,7 +39,7 @@ void TTYWrap::Initialize(Handle target, env->SetProtoMethod(t, "close", HandleWrap::Close); env->SetProtoMethod(t, "unref", HandleWrap::Unref); - StreamWrap::AddMethods(env, t); + StreamWrap::AddMethods(env, t); env->SetProtoMethod(t, "getWindowSize", TTYWrap::GetWindowSize); env->SetProtoMethod(t, "setRawMode", SetRawMode); From 223133307c458c6217fb74059324315f349bc107 Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Fri, 20 Feb 2015 20:13:07 +0100 Subject: [PATCH 40/41] node_crypto: fix --- src/node_crypto.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 85fe5df1f412d3..912320771e3f65 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -100,7 +100,7 @@ X509_STORE* root_cert_store; // Just to generate static methods template class SSLWrap; template void SSLWrap::AddMethods(Environment* env, - Handle t); + Handle t); template void SSLWrap::InitNPN(SecureContext* sc); template SSL_SESSION* SSLWrap::GetSessionCallback( SSL* s, @@ -108,7 +108,7 @@ template SSL_SESSION* SSLWrap::GetSessionCallback( int len, int* copy); template int SSLWrap::NewSessionCallback(SSL* s, - SSL_SESSION* sess); + SSL_SESSION* sess); template void SSLWrap::OnClientHello( void* arg, const ClientHelloParser::ClientHello& hello); From 07025b7847764be832a19bf6fb74074e2e54b1e5 Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Fri, 20 Feb 2015 23:10:03 +0100 Subject: [PATCH 41/41] stream_base: remove TODO --- src/stream_base.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/stream_base.h b/src/stream_base.h index 1be62eb9414020..d6b3a555b0596b 100644 --- a/src/stream_base.h +++ b/src/stream_base.h @@ -56,8 +56,6 @@ class ShutdownWrap : public ReqWrap, class WriteWrap: public ReqWrap, public StreamReq { public: - // TODO(trevnorris): WrapWrap inherits from ReqWrap, which I've globbed - // into the same provider. How should these be broken apart? WriteWrap(Environment* env, v8::Local obj, StreamBase* wrap,