#include #include #include #include #include #define UNWRAP \ assert(!args.Holder().IsEmpty()); \ assert(args.Holder()->InternalFieldCount() > 0); \ StdIOWrap* wrap = \ static_cast(args.Holder()->GetPointerFromInternalField(0)); \ if (!wrap) { \ SetErrno(UV_EBADF); \ return scope.Close(Integer::New(-1)); \ } namespace node { using v8::Object; using v8::Handle; using v8::Local; using v8::Persistent; using v8::Value; using v8::HandleScope; using v8::FunctionTemplate; using v8::String; using v8::Function; using v8::TryCatch; using v8::Context; using v8::Arguments; using v8::Integer; using v8::Undefined; extern Persistent tcpConstructor; extern Persistent pipeConstructor; static Persistent constructor; class StdIOWrap : StreamWrap { public: static void Initialize(Handle target) { StreamWrap::Initialize(target); HandleScope scope; Local t = FunctionTemplate::New(New); t->SetClassName(String::NewSymbol("StdIO")); t->InstanceTemplate()->SetInternalFieldCount(1); NODE_SET_PROTOTYPE_METHOD(t, "readStart", StreamWrap::ReadStart); NODE_SET_PROTOTYPE_METHOD(t, "readStop", StreamWrap::ReadStop); NODE_SET_PROTOTYPE_METHOD(t, "write", StreamWrap::Write); NODE_SET_PROTOTYPE_METHOD(t, "listen", Listen); constructor = Persistent::New(t->GetFunction()); target->Set(String::NewSymbol("StdIO"), constructor); } private: static Handle New(const Arguments& args) { // This constructor should not be exposed to public javascript. // Therefore we assert that we are not trying to call this as a // normal function. assert(args.IsConstructCall()); uv_std_type stdHandleType = (uv_std_type)args[0]->Int32Value(); assert(stdHandleType == UV_STDIN || stdHandleType == UV_STDOUT || stdHandleType == UV_STDERR); uv_stream_t* stdHandle = uv_std_handle(stdHandleType); if (stdHandle) { HandleScope scope; StdIOWrap* wrap = new StdIOWrap(args.This()); assert(wrap); wrap->handle_ = stdHandle; wrap->SetHandle((uv_handle_t*)stdHandle); wrap->UpdateWriteQueueSize(); return scope.Close(args.This()); } else { return Undefined(); } } StdIOWrap(Handle object) : StreamWrap(object, NULL) { } static Handle Listen(const Arguments& args) { HandleScope scope; UNWRAP int backlog = args[0]->Int32Value(); int r = uv_listen(wrap->handle_, SOMAXCONN, OnConnection); // Error starting the pipe. if (r) SetErrno(uv_last_error().code); return scope.Close(Integer::New(r)); } // TODO maybe share with TCPWrap? static void OnConnection(uv_stream_t* handle, int status) { HandleScope scope; Local client_obj; StdIOWrap* wrap = static_cast(handle->data); assert(wrap->handle_ == handle); // We should not be getting this callback if someone as already called // uv_close() on the handle. assert(wrap->object_.IsEmpty() == false); if (status != 0) { // TODO Handle server error (set errno and call onconnection with NULL) assert(0); return; } // Instanciate the client javascript object and handle. switch (handle->type) { case UV_TCP: client_obj = tcpConstructor->NewInstance(); break; case UV_NAMED_PIPE: client_obj = pipeConstructor->NewInstance(); break; default: assert(0); return; } // Unwrap the client javascript object. assert(client_obj->InternalFieldCount() > 0); StreamWrap* client_wrap = static_cast(client_obj->GetPointerFromInternalField(0)); int r = uv_accept(handle, client_wrap->GetStream()); // uv_accept should always work. assert(r == 0); // Successful accept. Call the onconnection callback in JavaScript land. Local argv[1] = { client_obj }; MakeCallback(wrap->object_, "onconnection", 1, argv); } uv_stream_t* handle_; }; } // namespace node NODE_MODULE(node_stdio_wrap, node::StdIOWrap::Initialize);