dgram: don't call into js when send cb is omitted
[platform/upstream/nodejs.git] / src / udp_wrap.cc
1 // Copyright Joyent, Inc. and other Node contributors.
2 //
3 // Permission is hereby granted, free of charge, to any person obtaining a
4 // copy of this software and associated documentation files (the
5 // "Software"), to deal in the Software without restriction, including
6 // without limitation the rights to use, copy, modify, merge, publish,
7 // distribute, sublicense, and/or sell copies of the Software, and to permit
8 // persons to whom the Software is furnished to do so, subject to the
9 // following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included
12 // in all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17 // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18 // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20 // USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22 #include "udp_wrap.h"
23 #include "node.h"
24 #include "node_buffer.h"
25 #include "handle_wrap.h"
26 #include "req_wrap.h"
27
28 #include <stdlib.h>
29
30
31 namespace node {
32
33 using v8::Function;
34 using v8::FunctionCallbackInfo;
35 using v8::FunctionTemplate;
36 using v8::Handle;
37 using v8::HandleScope;
38 using v8::Integer;
39 using v8::Local;
40 using v8::Object;
41 using v8::Persistent;
42 using v8::PropertyAttribute;
43 using v8::PropertyCallbackInfo;
44 using v8::String;
45 using v8::Uint32;
46 using v8::Undefined;
47 using v8::Value;
48
49
50 class SendWrap : public ReqWrap<uv_udp_send_t> {
51  public:
52   SendWrap(Local<Object> req_wrap_obj, bool have_callback);
53   inline bool have_callback() const;
54  private:
55   const bool have_callback_;
56 };
57
58
59 static Persistent<Function> constructor;
60 static Cached<String> buffer_sym;
61 static Cached<String> oncomplete_sym;
62 static Cached<String> onmessage_sym;
63
64
65 SendWrap::SendWrap(Local<Object> req_wrap_obj, bool have_callback)
66     : ReqWrap<uv_udp_send_t>(req_wrap_obj)
67     , have_callback_(have_callback) {
68 }
69
70
71 inline bool SendWrap::have_callback() const {
72   return have_callback_;
73 }
74
75
76 UDPWrap::UDPWrap(Handle<Object> object)
77     : HandleWrap(object, reinterpret_cast<uv_handle_t*>(&handle_)) {
78   int r = uv_udp_init(uv_default_loop(), &handle_);
79   assert(r == 0);  // can't fail anyway
80 }
81
82
83 UDPWrap::~UDPWrap() {
84 }
85
86
87 void UDPWrap::Initialize(Handle<Object> target) {
88   HandleScope scope(node_isolate);
89
90   buffer_sym = FIXED_ONE_BYTE_STRING(node_isolate, "buffer");
91   oncomplete_sym = FIXED_ONE_BYTE_STRING(node_isolate, "oncomplete");
92   onmessage_sym = FIXED_ONE_BYTE_STRING(node_isolate, "onmessage");
93
94   Local<FunctionTemplate> t = FunctionTemplate::New(New);
95   t->InstanceTemplate()->SetInternalFieldCount(1);
96   t->SetClassName(FIXED_ONE_BYTE_STRING(node_isolate, "UDP"));
97
98   enum PropertyAttribute attributes =
99       static_cast<PropertyAttribute>(v8::ReadOnly | v8::DontDelete);
100   t->InstanceTemplate()->SetAccessor(FIXED_ONE_BYTE_STRING(node_isolate, "fd"),
101                                      UDPWrap::GetFD,
102                                      NULL,
103                                      Handle<Value>(),
104                                      v8::DEFAULT,
105                                      attributes);
106
107   NODE_SET_PROTOTYPE_METHOD(t, "bind", Bind);
108   NODE_SET_PROTOTYPE_METHOD(t, "send", Send);
109   NODE_SET_PROTOTYPE_METHOD(t, "bind6", Bind6);
110   NODE_SET_PROTOTYPE_METHOD(t, "send6", Send6);
111   NODE_SET_PROTOTYPE_METHOD(t, "close", Close);
112   NODE_SET_PROTOTYPE_METHOD(t, "recvStart", RecvStart);
113   NODE_SET_PROTOTYPE_METHOD(t, "recvStop", RecvStop);
114   NODE_SET_PROTOTYPE_METHOD(t, "getsockname", GetSockName);
115   NODE_SET_PROTOTYPE_METHOD(t, "addMembership", AddMembership);
116   NODE_SET_PROTOTYPE_METHOD(t, "dropMembership", DropMembership);
117   NODE_SET_PROTOTYPE_METHOD(t, "setMulticastTTL", SetMulticastTTL);
118   NODE_SET_PROTOTYPE_METHOD(t, "setMulticastLoopback", SetMulticastLoopback);
119   NODE_SET_PROTOTYPE_METHOD(t, "setBroadcast", SetBroadcast);
120   NODE_SET_PROTOTYPE_METHOD(t, "setTTL", SetTTL);
121
122   NODE_SET_PROTOTYPE_METHOD(t, "ref", HandleWrap::Ref);
123   NODE_SET_PROTOTYPE_METHOD(t, "unref", HandleWrap::Unref);
124
125   constructor.Reset(node_isolate, t->GetFunction());
126   target->Set(FIXED_ONE_BYTE_STRING(node_isolate, "UDP"), t->GetFunction());
127 }
128
129
130 void UDPWrap::New(const FunctionCallbackInfo<Value>& args) {
131   HandleScope scope(node_isolate);
132   assert(args.IsConstructCall());
133   new UDPWrap(args.This());
134 }
135
136
137 void UDPWrap::GetFD(Local<String>, const PropertyCallbackInfo<Value>& args) {
138 #if !defined(_WIN32)
139   HandleScope scope(node_isolate);
140   UNWRAP(UDPWrap)
141   int fd = (wrap == NULL) ? -1 : wrap->handle_.io_watcher.fd;
142   args.GetReturnValue().Set(fd);
143 #endif
144 }
145
146
147 void UDPWrap::DoBind(const FunctionCallbackInfo<Value>& args, int family) {
148   HandleScope scope(node_isolate);
149   int err;
150
151   UNWRAP(UDPWrap)
152
153   // bind(ip, port, flags)
154   assert(args.Length() == 3);
155
156   String::Utf8Value address(args[0]);
157   const int port = args[1]->Uint32Value();
158   const int flags = args[2]->Uint32Value();
159
160   switch (family) {
161   case AF_INET:
162     err = uv_udp_bind(&wrap->handle_, uv_ip4_addr(*address, port), flags);
163     break;
164   case AF_INET6:
165     err = uv_udp_bind6(&wrap->handle_, uv_ip6_addr(*address, port), flags);
166     break;
167   default:
168     assert(0 && "unexpected address family");
169     abort();
170   }
171
172   args.GetReturnValue().Set(err);
173 }
174
175
176 void UDPWrap::Bind(const FunctionCallbackInfo<Value>& args) {
177   DoBind(args, AF_INET);
178 }
179
180
181 void UDPWrap::Bind6(const FunctionCallbackInfo<Value>& args) {
182   DoBind(args, AF_INET6);
183 }
184
185
186 #define X(name, fn)                                                           \
187   void UDPWrap::name(const FunctionCallbackInfo<Value>& args) {               \
188     HandleScope scope(node_isolate);                                          \
189     UNWRAP(UDPWrap)                                                           \
190     assert(args.Length() == 1);                                               \
191     int flag = args[0]->Int32Value();                                         \
192     int err = fn(&wrap->handle_, flag);                                       \
193     args.GetReturnValue().Set(err);                                           \
194   }
195
196 X(SetTTL, uv_udp_set_ttl)
197 X(SetBroadcast, uv_udp_set_broadcast)
198 X(SetMulticastTTL, uv_udp_set_multicast_ttl)
199 X(SetMulticastLoopback, uv_udp_set_multicast_loop)
200
201 #undef X
202
203
204 void UDPWrap::SetMembership(const FunctionCallbackInfo<Value>& args,
205                             uv_membership membership) {
206   HandleScope scope(node_isolate);
207   UNWRAP(UDPWrap)
208
209   assert(args.Length() == 2);
210
211   String::Utf8Value address(args[0]);
212   String::Utf8Value iface(args[1]);
213
214   const char* iface_cstr = *iface;
215   if (args[1]->IsUndefined() || args[1]->IsNull()) {
216       iface_cstr = NULL;
217   }
218
219   int err = uv_udp_set_membership(&wrap->handle_,
220                                   *address,
221                                   iface_cstr,
222                                   membership);
223   args.GetReturnValue().Set(err);
224 }
225
226
227 void UDPWrap::AddMembership(const FunctionCallbackInfo<Value>& args) {
228   SetMembership(args, UV_JOIN_GROUP);
229 }
230
231
232 void UDPWrap::DropMembership(const FunctionCallbackInfo<Value>& args) {
233   SetMembership(args, UV_LEAVE_GROUP);
234 }
235
236
237 void UDPWrap::DoSend(const FunctionCallbackInfo<Value>& args, int family) {
238   HandleScope scope(node_isolate);
239   int err;
240
241   UNWRAP(UDPWrap)
242
243   // send(req, buffer, offset, length, port, address)
244   assert(args[0]->IsObject());
245   assert(Buffer::HasInstance(args[1]));
246   assert(args[2]->IsUint32());
247   assert(args[3]->IsUint32());
248   assert(args[4]->IsUint32());
249   assert(args[5]->IsString());
250   assert(args[6]->IsBoolean());
251
252   Local<Object> req_wrap_obj = args[0].As<Object>();
253   Local<Object> buffer_obj = args[1].As<Object>();
254   size_t offset = args[2]->Uint32Value();
255   size_t length = args[3]->Uint32Value();
256   const unsigned short port = args[4]->Uint32Value();
257   String::Utf8Value address(args[5]);
258   const bool have_callback = args[6]->IsTrue();
259
260   assert(offset < Buffer::Length(buffer_obj));
261   assert(length <= Buffer::Length(buffer_obj) - offset);
262
263   SendWrap* req_wrap = new SendWrap(req_wrap_obj, have_callback);
264   req_wrap->object()->SetHiddenValue(buffer_sym, buffer_obj);
265
266   uv_buf_t buf = uv_buf_init(Buffer::Data(buffer_obj) + offset,
267                              length);
268
269   switch (family) {
270   case AF_INET:
271     err = uv_udp_send(&req_wrap->req_,
272                       &wrap->handle_,
273                       &buf,
274                       1,
275                       uv_ip4_addr(*address, port),
276                       OnSend);
277     break;
278   case AF_INET6:
279     err = uv_udp_send6(&req_wrap->req_,
280                        &wrap->handle_,
281                        &buf,
282                        1,
283                        uv_ip6_addr(*address, port),
284                        OnSend);
285     break;
286   default:
287     assert(0 && "unexpected address family");
288     abort();
289   }
290
291   req_wrap->Dispatched();
292   if (err) delete req_wrap;
293
294   args.GetReturnValue().Set(err);
295 }
296
297
298 void UDPWrap::Send(const FunctionCallbackInfo<Value>& args) {
299   DoSend(args, AF_INET);
300 }
301
302
303 void UDPWrap::Send6(const FunctionCallbackInfo<Value>& args) {
304   DoSend(args, AF_INET6);
305 }
306
307
308 void UDPWrap::RecvStart(const FunctionCallbackInfo<Value>& args) {
309   HandleScope scope(node_isolate);
310   UNWRAP(UDPWrap)
311
312   int err = uv_udp_recv_start(&wrap->handle_, OnAlloc, OnRecv);
313   // UV_EALREADY means that the socket is already bound but that's okay
314   if (err == UV_EALREADY) err = 0;
315   args.GetReturnValue().Set(err);
316 }
317
318
319 void UDPWrap::RecvStop(const FunctionCallbackInfo<Value>& args) {
320   HandleScope scope(node_isolate);
321   UNWRAP(UDPWrap)
322
323   int r = uv_udp_recv_stop(&wrap->handle_);
324   args.GetReturnValue().Set(r);
325 }
326
327
328 void UDPWrap::GetSockName(const FunctionCallbackInfo<Value>& args) {
329   HandleScope scope(node_isolate);
330   struct sockaddr_storage address;
331   UNWRAP(UDPWrap)
332
333   assert(args[0]->IsObject());
334   Local<Object> obj = args[0].As<Object>();
335
336   int addrlen = sizeof(address);
337   int err = uv_udp_getsockname(&wrap->handle_,
338                                reinterpret_cast<sockaddr*>(&address),
339                                &addrlen);
340
341   if (err == 0) {
342     const sockaddr* addr = reinterpret_cast<const sockaddr*>(&address);
343     AddressToJS(addr, obj);
344   }
345
346   args.GetReturnValue().Set(err);
347 }
348
349
350 // TODO(bnoordhuis) share with StreamWrap::AfterWrite() in stream_wrap.cc
351 void UDPWrap::OnSend(uv_udp_send_t* req, int status) {
352   SendWrap* req_wrap = static_cast<SendWrap*>(req->data);
353   if (req_wrap->have_callback()) {
354     HandleScope scope(node_isolate);
355     Local<Object> req_wrap_obj = req_wrap->object();
356     Local<Value> arg = Integer::New(status, node_isolate);
357     MakeCallback(req_wrap_obj, oncomplete_sym, 1, &arg);
358   }
359   delete req_wrap;
360 }
361
362
363 uv_buf_t UDPWrap::OnAlloc(uv_handle_t* handle, size_t suggested_size) {
364   char* data = static_cast<char*>(malloc(suggested_size));
365   if (data == NULL && suggested_size > 0) {
366     FatalError("node::UDPWrap::OnAlloc(uv_handle_t*, size_t)",
367                "Out Of Memory");
368   }
369   return uv_buf_init(data, suggested_size);
370 }
371
372
373 void UDPWrap::OnRecv(uv_udp_t* handle,
374                      ssize_t nread,
375                      uv_buf_t buf,
376                      struct sockaddr* addr,
377                      unsigned flags) {
378   if (nread == 0) {
379     if (buf.base != NULL)
380       free(buf.base);
381     return;
382   }
383
384   UDPWrap* wrap = static_cast<UDPWrap*>(handle->data);
385
386   HandleScope scope(node_isolate);
387   Local<Object> wrap_obj = wrap->object();
388   Local<Value> argv[] = {
389     Integer::New(nread, node_isolate),
390     wrap_obj,
391     Undefined(),
392     Undefined()
393   };
394
395   if (nread < 0) {
396     if (buf.base != NULL)
397       free(buf.base);
398     MakeCallback(wrap_obj, onmessage_sym, ARRAY_SIZE(argv), argv);
399     return;
400   }
401
402   buf.base = static_cast<char*>(realloc(buf.base, nread));
403
404   argv[2] = Buffer::Use(buf.base, nread);
405   argv[3] = AddressToJS(addr);
406   MakeCallback(wrap_obj, onmessage_sym, ARRAY_SIZE(argv), argv);
407 }
408
409
410 UDPWrap* UDPWrap::Unwrap(Local<Object> obj) {
411   assert(!obj.IsEmpty());
412   assert(obj->InternalFieldCount() > 0);
413   return static_cast<UDPWrap*>(obj->GetAlignedPointerFromInternalField(0));
414 }
415
416
417 Local<Object> UDPWrap::Instantiate() {
418   // If this assert fires then Initialize hasn't been called yet.
419   assert(constructor.IsEmpty() == false);
420   return NewInstance(constructor);
421 }
422
423
424 uv_udp_t* UDPWrap::UVHandle() {
425   return &handle_;
426 }
427
428
429 }  // namespace node
430
431 NODE_MODULE(node_udp_wrap, node::UDPWrap::Initialize)