1#include "udp_wrap.h" 2#include "async_wrap-inl.h" 3#include "node_errors.h" 4#include "node_sockaddr-inl.h" 5 6#include <algorithm> 7 8// TODO(RaisinTen): Replace all uses with empty `v8::Maybe`s. 9#define JS_EXCEPTION_PENDING UV_EPROTO 10 11namespace node { 12 13using errors::TryCatchScope; 14using v8::Array; 15using v8::Context; 16using v8::FunctionCallbackInfo; 17using v8::FunctionTemplate; 18using v8::HandleScope; 19using v8::Int32; 20using v8::Isolate; 21using v8::Local; 22using v8::Object; 23using v8::Value; 24 25// JSUDPWrap is a testing utility used by test/common/udppair.js 26// to simulate UDP traffic deterministically in Node.js tests. 27class JSUDPWrap final : public UDPWrapBase, public AsyncWrap { 28 public: 29 JSUDPWrap(Environment* env, Local<Object> obj); 30 31 int RecvStart() override; 32 int RecvStop() override; 33 ssize_t Send(uv_buf_t* bufs, 34 size_t nbufs, 35 const sockaddr* addr) override; 36 SocketAddress GetPeerName() override; 37 SocketAddress GetSockName() override; 38 AsyncWrap* GetAsyncWrap() override { return this; } 39 40 static void New(const FunctionCallbackInfo<Value>& args); 41 static void EmitReceived(const FunctionCallbackInfo<Value>& args); 42 static void OnSendDone(const FunctionCallbackInfo<Value>& args); 43 static void OnAfterBind(const FunctionCallbackInfo<Value>& args); 44 45 static void Initialize(Local<Object> target, 46 Local<Value> unused, 47 Local<Context> context, 48 void* priv); 49 SET_NO_MEMORY_INFO() 50 SET_MEMORY_INFO_NAME(JSUDPWrap) 51 SET_SELF_SIZE(JSUDPWrap) 52}; 53 54JSUDPWrap::JSUDPWrap(Environment* env, Local<Object> obj) 55 : AsyncWrap(env, obj, PROVIDER_JSUDPWRAP) { 56 MakeWeak(); 57 58 obj->SetAlignedPointerInInternalField( 59 kUDPWrapBaseField, static_cast<UDPWrapBase*>(this)); 60} 61 62int JSUDPWrap::RecvStart() { 63 HandleScope scope(env()->isolate()); 64 Context::Scope context_scope(env()->context()); 65 TryCatchScope try_catch(env()); 66 Local<Value> value; 67 int32_t value_int = JS_EXCEPTION_PENDING; 68 if (!MakeCallback(env()->onreadstart_string(), 0, nullptr).ToLocal(&value) || 69 !value->Int32Value(env()->context()).To(&value_int)) { 70 if (try_catch.HasCaught() && !try_catch.HasTerminated()) 71 errors::TriggerUncaughtException(env()->isolate(), try_catch); 72 } 73 return value_int; 74} 75 76int JSUDPWrap::RecvStop() { 77 HandleScope scope(env()->isolate()); 78 Context::Scope context_scope(env()->context()); 79 TryCatchScope try_catch(env()); 80 Local<Value> value; 81 int32_t value_int = JS_EXCEPTION_PENDING; 82 if (!MakeCallback(env()->onreadstop_string(), 0, nullptr).ToLocal(&value) || 83 !value->Int32Value(env()->context()).To(&value_int)) { 84 if (try_catch.HasCaught() && !try_catch.HasTerminated()) 85 errors::TriggerUncaughtException(env()->isolate(), try_catch); 86 } 87 return value_int; 88} 89 90ssize_t JSUDPWrap::Send(uv_buf_t* bufs, 91 size_t nbufs, 92 const sockaddr* addr) { 93 HandleScope scope(env()->isolate()); 94 Context::Scope context_scope(env()->context()); 95 TryCatchScope try_catch(env()); 96 Local<Value> value; 97 int64_t value_int = JS_EXCEPTION_PENDING; 98 size_t total_len = 0; 99 100 MaybeStackBuffer<Local<Value>, 16> buffers(nbufs); 101 for (size_t i = 0; i < nbufs; i++) { 102 buffers[i] = Buffer::Copy(env(), bufs[i].base, bufs[i].len) 103 .ToLocalChecked(); 104 total_len += bufs[i].len; 105 } 106 107 Local<Object> address; 108 if (!AddressToJS(env(), addr).ToLocal(&address)) return value_int; 109 110 Local<Value> args[] = { 111 listener()->CreateSendWrap(total_len)->object(), 112 Array::New(env()->isolate(), buffers.out(), nbufs), 113 address, 114 }; 115 116 if (!MakeCallback(env()->onwrite_string(), arraysize(args), args) 117 .ToLocal(&value) || 118 !value->IntegerValue(env()->context()).To(&value_int)) { 119 if (try_catch.HasCaught() && !try_catch.HasTerminated()) 120 errors::TriggerUncaughtException(env()->isolate(), try_catch); 121 } 122 return value_int; 123} 124 125SocketAddress JSUDPWrap::GetPeerName() { 126 SocketAddress ret; 127 CHECK(SocketAddress::New(AF_INET, "127.0.0.1", 1337, &ret)); 128 return ret; 129} 130 131SocketAddress JSUDPWrap::GetSockName() { 132 SocketAddress ret; 133 CHECK(SocketAddress::New(AF_INET, "127.0.0.1", 1337, &ret)); 134 return ret; 135} 136 137void JSUDPWrap::New(const FunctionCallbackInfo<Value>& args) { 138 Environment* env = Environment::GetCurrent(args); 139 CHECK(args.IsConstructCall()); 140 new JSUDPWrap(env, args.Holder()); 141} 142 143void JSUDPWrap::EmitReceived(const FunctionCallbackInfo<Value>& args) { 144 JSUDPWrap* wrap; 145 ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); 146 Environment* env = wrap->env(); 147 148 ArrayBufferViewContents<char> buffer(args[0]); 149 const char* data = buffer.data(); 150 int len = buffer.length(); 151 152 CHECK(args[1]->IsInt32()); // family 153 CHECK(args[2]->IsString()); // address 154 CHECK(args[3]->IsInt32()); // port 155 CHECK(args[4]->IsInt32()); // flags 156 int family = args[1].As<Int32>()->Value() == 4 ? AF_INET : AF_INET6; 157 Utf8Value address(env->isolate(), args[2]); 158 int port = args[3].As<Int32>()->Value(); 159 int flags = args[3].As<Int32>()->Value(); 160 161 sockaddr_storage addr; 162 CHECK_EQ(sockaddr_for_family(family, *address, port, &addr), 0); 163 164 // Repeatedly ask the stream's owner for memory, copy the data that we 165 // just read from JS into those buffers and emit them as reads. 166 while (len != 0) { 167 uv_buf_t buf = wrap->listener()->OnAlloc(len); 168 ssize_t avail = std::min<size_t>(buf.len, len); 169 memcpy(buf.base, data, avail); 170 data += avail; 171 len -= static_cast<int>(avail); 172 wrap->listener()->OnRecv( 173 avail, buf, reinterpret_cast<sockaddr*>(&addr), flags); 174 } 175} 176 177void JSUDPWrap::OnSendDone(const FunctionCallbackInfo<Value>& args) { 178 JSUDPWrap* wrap; 179 ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); 180 181 CHECK(args[0]->IsObject()); 182 CHECK(args[1]->IsInt32()); 183 ReqWrap<uv_udp_send_t>* req_wrap; 184 ASSIGN_OR_RETURN_UNWRAP(&req_wrap, args[0].As<Object>()); 185 int status = args[1].As<Int32>()->Value(); 186 187 wrap->listener()->OnSendDone(req_wrap, status); 188} 189 190void JSUDPWrap::OnAfterBind(const FunctionCallbackInfo<Value>& args) { 191 JSUDPWrap* wrap; 192 ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); 193 194 wrap->listener()->OnAfterBind(); 195} 196 197void JSUDPWrap::Initialize(Local<Object> target, 198 Local<Value> unused, 199 Local<Context> context, 200 void* priv) { 201 Environment* env = Environment::GetCurrent(context); 202 Isolate* isolate = env->isolate(); 203 204 Local<FunctionTemplate> t = NewFunctionTemplate(isolate, New); 205 t->InstanceTemplate() 206 ->SetInternalFieldCount(UDPWrapBase::kUDPWrapBaseField + 1); 207 t->Inherit(AsyncWrap::GetConstructorTemplate(env)); 208 209 UDPWrapBase::AddMethods(env, t); 210 SetProtoMethod(isolate, t, "emitReceived", EmitReceived); 211 SetProtoMethod(isolate, t, "onSendDone", OnSendDone); 212 SetProtoMethod(isolate, t, "onAfterBind", OnAfterBind); 213 214 SetConstructorFunction(context, target, "JSUDPWrap", t); 215} 216 217 218} // namespace node 219 220NODE_BINDING_CONTEXT_AWARE_INTERNAL(js_udp_wrap, node::JSUDPWrap::Initialize) 221