diff options
Diffstat (limited to 'src/js_udp_wrap.cc')
-rw-r--r-- | src/js_udp_wrap.cc | 218 |
1 files changed, 218 insertions, 0 deletions
diff --git a/src/js_udp_wrap.cc b/src/js_udp_wrap.cc new file mode 100644 index 0000000000..c516831411 --- /dev/null +++ b/src/js_udp_wrap.cc @@ -0,0 +1,218 @@ +#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) |