| #include "udp_wrap.h" |
| #include "async_wrap-inl.h" |
| #include "node_errors.h" |
| #include "node_sockaddr-inl.h" |
| |
| #include <algorithm> |
| |
| namespace node { |
| |
| using errors::TryCatchScope; |
| using v8::Array; |
| using v8::Context; |
| using v8::FunctionCallbackInfo; |
| using v8::FunctionTemplate; |
| using v8::HandleScope; |
| using v8::Int32; |
| using v8::Local; |
| using v8::Object; |
| using v8::String; |
| using v8::Value; |
| |
| // JSUDPWrap is a testing utility used by test/common/udppair.js |
| // to simulate UDP traffic deterministically in Node.js tests. |
| class JSUDPWrap final : public UDPWrapBase, public AsyncWrap { |
| public: |
| JSUDPWrap(Environment* env, Local<Object> obj); |
| |
| int RecvStart() override; |
| int RecvStop() override; |
| ssize_t Send(uv_buf_t* bufs, |
| size_t nbufs, |
| const sockaddr* addr) override; |
| SocketAddress GetPeerName() override; |
| SocketAddress GetSockName() override; |
| AsyncWrap* GetAsyncWrap() override { return this; } |
| |
| static void New(const FunctionCallbackInfo<Value>& args); |
| static void EmitReceived(const FunctionCallbackInfo<Value>& args); |
| static void OnSendDone(const FunctionCallbackInfo<Value>& args); |
| static void OnAfterBind(const FunctionCallbackInfo<Value>& args); |
| |
| static void Initialize(Local<Object> target, |
| Local<Value> unused, |
| Local<Context> context, |
| void* priv); |
| SET_NO_MEMORY_INFO() |
| SET_MEMORY_INFO_NAME(JSUDPWrap) |
| SET_SELF_SIZE(JSUDPWrap) |
| }; |
| |
| JSUDPWrap::JSUDPWrap(Environment* env, Local<Object> obj) |
| : AsyncWrap(env, obj, PROVIDER_JSUDPWRAP) { |
| MakeWeak(); |
| |
| obj->SetAlignedPointerInInternalField( |
| kUDPWrapBaseField, static_cast<UDPWrapBase*>(this)); |
| } |
| |
| int JSUDPWrap::RecvStart() { |
| HandleScope scope(env()->isolate()); |
| Context::Scope context_scope(env()->context()); |
| TryCatchScope try_catch(env()); |
| Local<Value> value; |
| int32_t value_int = UV_EPROTO; |
| if (!MakeCallback(env()->onreadstart_string(), 0, nullptr).ToLocal(&value) || |
| !value->Int32Value(env()->context()).To(&value_int)) { |
| if (try_catch.HasCaught() && !try_catch.HasTerminated()) |
| errors::TriggerUncaughtException(env()->isolate(), try_catch); |
| } |
| return value_int; |
| } |
| |
| int JSUDPWrap::RecvStop() { |
| HandleScope scope(env()->isolate()); |
| Context::Scope context_scope(env()->context()); |
| TryCatchScope try_catch(env()); |
| Local<Value> value; |
| int32_t value_int = UV_EPROTO; |
| if (!MakeCallback(env()->onreadstop_string(), 0, nullptr).ToLocal(&value) || |
| !value->Int32Value(env()->context()).To(&value_int)) { |
| if (try_catch.HasCaught() && !try_catch.HasTerminated()) |
| errors::TriggerUncaughtException(env()->isolate(), try_catch); |
| } |
| return value_int; |
| } |
| |
| ssize_t JSUDPWrap::Send(uv_buf_t* bufs, |
| size_t nbufs, |
| const sockaddr* addr) { |
| HandleScope scope(env()->isolate()); |
| Context::Scope context_scope(env()->context()); |
| TryCatchScope try_catch(env()); |
| Local<Value> value; |
| int64_t value_int = UV_EPROTO; |
| size_t total_len = 0; |
| |
| MaybeStackBuffer<Local<Value>, 16> buffers(nbufs); |
| for (size_t i = 0; i < nbufs; i++) { |
| buffers[i] = Buffer::Copy(env(), bufs[i].base, bufs[i].len) |
| .ToLocalChecked(); |
| total_len += bufs[i].len; |
| } |
| |
| Local<Value> args[] = { |
| listener()->CreateSendWrap(total_len)->object(), |
| Array::New(env()->isolate(), buffers.out(), nbufs), |
| AddressToJS(env(), addr) |
| }; |
| |
| if (!MakeCallback(env()->onwrite_string(), arraysize(args), args) |
| .ToLocal(&value) || |
| !value->IntegerValue(env()->context()).To(&value_int)) { |
| if (try_catch.HasCaught() && !try_catch.HasTerminated()) |
| errors::TriggerUncaughtException(env()->isolate(), try_catch); |
| } |
| return value_int; |
| } |
| |
| SocketAddress JSUDPWrap::GetPeerName() { |
| SocketAddress ret; |
| CHECK(SocketAddress::New(AF_INET, "127.0.0.1", 1337, &ret)); |
| return ret; |
| } |
| |
| SocketAddress JSUDPWrap::GetSockName() { |
| SocketAddress ret; |
| CHECK(SocketAddress::New(AF_INET, "127.0.0.1", 1337, &ret)); |
| return ret; |
| } |
| |
| void JSUDPWrap::New(const FunctionCallbackInfo<Value>& args) { |
| Environment* env = Environment::GetCurrent(args); |
| CHECK(args.IsConstructCall()); |
| new JSUDPWrap(env, args.Holder()); |
| } |
| |
| void JSUDPWrap::EmitReceived(const FunctionCallbackInfo<Value>& args) { |
| JSUDPWrap* wrap; |
| ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); |
| Environment* env = wrap->env(); |
| |
| ArrayBufferViewContents<char> buffer(args[0]); |
| const char* data = buffer.data(); |
| int len = buffer.length(); |
| |
| CHECK(args[1]->IsInt32()); // family |
| CHECK(args[2]->IsString()); // address |
| CHECK(args[3]->IsInt32()); // port |
| CHECK(args[4]->IsInt32()); // flags |
| int family = args[1].As<Int32>()->Value() == 4 ? AF_INET : AF_INET6; |
| Utf8Value address(env->isolate(), args[2]); |
| int port = args[3].As<Int32>()->Value(); |
| int flags = args[3].As<Int32>()->Value(); |
| |
| sockaddr_storage addr; |
| CHECK_EQ(sockaddr_for_family(family, *address, port, &addr), 0); |
| |
| // Repeatedly ask the stream's owner for memory, copy the data that we |
| // just read from JS into those buffers and emit them as reads. |
| while (len != 0) { |
| uv_buf_t buf = wrap->listener()->OnAlloc(len); |
| ssize_t avail = std::min<size_t>(buf.len, len); |
| memcpy(buf.base, data, avail); |
| data += avail; |
| len -= avail; |
| wrap->listener()->OnRecv( |
| avail, buf, reinterpret_cast<sockaddr*>(&addr), flags); |
| } |
| } |
| |
| void JSUDPWrap::OnSendDone(const FunctionCallbackInfo<Value>& args) { |
| JSUDPWrap* wrap; |
| ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); |
| |
| CHECK(args[0]->IsObject()); |
| CHECK(args[1]->IsInt32()); |
| ReqWrap<uv_udp_send_t>* req_wrap; |
| ASSIGN_OR_RETURN_UNWRAP(&req_wrap, args[0].As<Object>()); |
| int status = args[1].As<Int32>()->Value(); |
| |
| wrap->listener()->OnSendDone(req_wrap, status); |
| } |
| |
| void JSUDPWrap::OnAfterBind(const FunctionCallbackInfo<Value>& args) { |
| JSUDPWrap* wrap; |
| ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); |
| |
| wrap->listener()->OnAfterBind(); |
| } |
| |
| void JSUDPWrap::Initialize(Local<Object> target, |
| Local<Value> unused, |
| Local<Context> context, |
| void* priv) { |
| Environment* env = Environment::GetCurrent(context); |
| |
| Local<FunctionTemplate> t = env->NewFunctionTemplate(New); |
| Local<String> js_udp_wrap_string = |
| FIXED_ONE_BYTE_STRING(env->isolate(), "JSUDPWrap"); |
| t->SetClassName(js_udp_wrap_string); |
| t->InstanceTemplate() |
| ->SetInternalFieldCount(UDPWrapBase::kUDPWrapBaseField + 1); |
| t->Inherit(AsyncWrap::GetConstructorTemplate(env)); |
| |
| UDPWrapBase::AddMethods(env, t); |
| env->SetProtoMethod(t, "emitReceived", EmitReceived); |
| env->SetProtoMethod(t, "onSendDone", OnSendDone); |
| env->SetProtoMethod(t, "onAfterBind", OnAfterBind); |
| |
| target->Set(env->context(), |
| js_udp_wrap_string, |
| t->GetFunction(context).ToLocalChecked()).Check(); |
| } |
| |
| |
| } // namespace node |
| |
| NODE_MODULE_CONTEXT_AWARE_INTERNAL(js_udp_wrap, node::JSUDPWrap::Initialize) |