The data contained in this repository can be downloaded to your computer using one of several clients.
Please see the documentation of your version control software client for more information.
Please select the desired protocol below to get the URL.
This URL has Read-Only access.
main_repo / src / net.cc @ 63a9cd38
History | View | Annotate | Download (10.2 KB)
1 |
#include "net.h" |
---|---|
2 |
#include "node.h" |
3 |
|
4 |
#include <oi_socket.h> |
5 |
#include <oi_buf.h> |
6 |
|
7 |
#include <assert.h> |
8 |
#include <sys/types.h> |
9 |
#include <sys/socket.h> |
10 |
#include <netdb.h> |
11 |
#include <strings.h> |
12 |
|
13 |
using namespace v8; |
14 |
|
15 |
static Persistent<String> readyState_str;
|
16 |
|
17 |
static Persistent<Integer> readyStateCONNECTING;
|
18 |
static Persistent<Integer> readyStateOPEN;
|
19 |
static Persistent<Integer> readyStateCLOSED;
|
20 |
|
21 |
enum encoding {UTF8, RAW};
|
22 |
|
23 |
class Socket { |
24 |
public:
|
25 |
Socket (Handle<Object> obj, double timeout);
|
26 |
~Socket (); |
27 |
|
28 |
int ConnectTCP (char *port, char *host); |
29 |
void Write (Handle<Value> arg);
|
30 |
void Disconnect ();
|
31 |
|
32 |
void SetEncoding (enum encoding); |
33 |
void SetTimeout (double); |
34 |
|
35 |
void OnConnect ();
|
36 |
void OnRead (const void *buf, size_t count); |
37 |
void OnDrain ();
|
38 |
void OnError (oi_error e);
|
39 |
void OnClose ();
|
40 |
|
41 |
private:
|
42 |
oi_socket socket_; |
43 |
struct addrinfo *address_;
|
44 |
Persistent<Object> js_object_; |
45 |
}; |
46 |
|
47 |
static void |
48 |
on_connect (oi_socket *socket) |
49 |
{ |
50 |
Socket *s = static_cast<Socket*> (socket->data);
|
51 |
s->OnConnect(); |
52 |
} |
53 |
|
54 |
static void |
55 |
on_read (oi_socket *socket, const void *buf, size_t count) |
56 |
{ |
57 |
Socket *s = static_cast<Socket*> (socket->data);
|
58 |
s->OnRead(buf, count); |
59 |
} |
60 |
|
61 |
static void |
62 |
on_drain (oi_socket *socket) |
63 |
{ |
64 |
Socket *s = static_cast<Socket*> (socket->data);
|
65 |
s->OnDrain(); |
66 |
} |
67 |
|
68 |
static void |
69 |
on_error (oi_socket *socket, oi_error e) |
70 |
{ |
71 |
Socket *s = static_cast<Socket*> (socket->data);
|
72 |
s->OnError(e); |
73 |
} |
74 |
|
75 |
static void |
76 |
on_timeout (oi_socket *socket) |
77 |
{ |
78 |
Socket *s = static_cast<Socket*> (socket->data);
|
79 |
s->OnTimeout(e); |
80 |
} |
81 |
|
82 |
static void |
83 |
on_close (oi_socket *socket) |
84 |
{ |
85 |
Socket *s = static_cast<Socket*> (socket->data);
|
86 |
s->OnClose(); |
87 |
} |
88 |
|
89 |
|
90 |
static Handle<Value>
|
91 |
NewSocket (const Arguments& args)
|
92 |
{ |
93 |
if (args.Length() > 1) |
94 |
return Undefined();
|
95 |
|
96 |
HandleScope scope; |
97 |
|
98 |
// Default options
|
99 |
double timeout = 60.0; // in seconds |
100 |
enum {RAW, UTF8} encoding = RAW;
|
101 |
|
102 |
// Set options from argument.
|
103 |
if (args.Length() == 1 && args[0]->IsObject()) { |
104 |
Local<Object> options = args[0]->ToObject();
|
105 |
Local<Value> timeout_value = options->Get(String::NewSymbol("timeout"));
|
106 |
Local<Value> encoding_value = options->Get(String::NewSymbol("encoding"));
|
107 |
|
108 |
if (timeout_value->IsNumber()) {
|
109 |
// timeout is specified in milliseconds like other time
|
110 |
// values in javascript
|
111 |
timeout = timeout_value->NumberValue() / 1000;
|
112 |
} |
113 |
|
114 |
if (encoding_value->IsString()) {
|
115 |
Local<String> encoding_string = encoding_value->ToString(); |
116 |
char buf[5]; // need enough room for "utf8" or "raw" |
117 |
encoding_string->WriteAscii(buf, 0, 4); |
118 |
buf[4] = '\0'; |
119 |
if(strcasecmp(buf, "utf8") == 0) encoding = UTF8; |
120 |
} |
121 |
} |
122 |
|
123 |
Socket *s = new Socket(args.This(), timeout);
|
124 |
if(s == NULL) |
125 |
return Undefined(); // XXX raise error? |
126 |
|
127 |
return args.This();
|
128 |
} |
129 |
|
130 |
static Socket*
|
131 |
Unwrapsocket (Handle<Object> obj) |
132 |
{ |
133 |
HandleScope scope; |
134 |
Handle<External> field = Handle<External>::Cast(obj->GetInternalField(0));
|
135 |
Socket* socket = static_cast<Socket*>(field->Value());
|
136 |
return socket;
|
137 |
} |
138 |
|
139 |
static Handle<Value>
|
140 |
SocketConnectTCPCallback (const Arguments& args)
|
141 |
{ |
142 |
if (args.Length() < 1) |
143 |
return Undefined();
|
144 |
|
145 |
HandleScope scope; |
146 |
Socket *socket = Unwrapsocket(args.Holder()); |
147 |
|
148 |
String::AsciiValue port(args[0]);
|
149 |
|
150 |
char *host = NULL; |
151 |
String::AsciiValue host_v(args[1]->ToString());
|
152 |
if(args[1]->IsString()) { |
153 |
host = *host_v; |
154 |
} |
155 |
|
156 |
int r = socket->ConnectTCP(*port, host);
|
157 |
// TODO raise error if r != 0
|
158 |
|
159 |
return Undefined();
|
160 |
} |
161 |
|
162 |
static Handle<Value>
|
163 |
SocketWriteCallback (const Arguments& args)
|
164 |
{ |
165 |
HandleScope scope; |
166 |
Socket *socket = Unwrapsocket(args.Holder()); |
167 |
socket->Write(args[0]);
|
168 |
return Undefined();
|
169 |
} |
170 |
|
171 |
static Handle<Value>
|
172 |
SocketCloseCallback (const Arguments& args)
|
173 |
{ |
174 |
HandleScope scope; |
175 |
Socket *socket = Unwrapsocket(args.Holder()); |
176 |
socket->Disconnect(); |
177 |
return Undefined();
|
178 |
} |
179 |
|
180 |
static void |
181 |
DestroySocket (Persistent<Value> _, void *data)
|
182 |
{ |
183 |
Socket *s = static_cast<Socket*> (data);
|
184 |
delete s;
|
185 |
} |
186 |
|
187 |
Socket::Socket(Handle<Object> js_object, double timeout)
|
188 |
{ |
189 |
oi_socket_init(&socket_, timeout); |
190 |
socket_.on_connect = on_connect; |
191 |
socket_.on_read = on_read; |
192 |
socket_.on_drain = on_drain; |
193 |
socket_.on_error = on_error; |
194 |
socket_.on_close = on_close; |
195 |
socket_.on_timeout = on_timeout; |
196 |
socket_.data = this;
|
197 |
|
198 |
HandleScope scope; |
199 |
js_object_ = Persistent<Object>::New(js_object); |
200 |
js_object_->SetInternalField (0, External::New(this)); |
201 |
js_object_.MakeWeak (this, DestroySocket);
|
202 |
} |
203 |
|
204 |
Socket::~Socket () |
205 |
{ |
206 |
Disconnect(); |
207 |
oi_socket_detach(&socket_); |
208 |
js_object_.Dispose(); |
209 |
js_object_.Clear(); // necessary?
|
210 |
} |
211 |
|
212 |
static struct addrinfo tcp_hints = |
213 |
/* ai_flags */ { AI_PASSIVE
|
214 |
/* ai_family */ , AF_UNSPEC
|
215 |
/* ai_socktype */ , SOCK_STREAM
|
216 |
/* ai_protocol */ , 0 |
217 |
/* ai_addrlen */ , 0 |
218 |
/* ai_addr */ , 0 |
219 |
/* ai_canonname */ , 0 |
220 |
/* ai_next */ , 0 |
221 |
}; |
222 |
|
223 |
int
|
224 |
Socket::ConnectTCP(char *port, char *host) |
225 |
{ |
226 |
int r;
|
227 |
|
228 |
HandleScope scope; |
229 |
|
230 |
js_object_->Set(readyState_str, readyStateCONNECTING); |
231 |
|
232 |
/* FIXME Blocking DNS resolution. */
|
233 |
printf("resolving host: %s, port: %s\n", host, port);
|
234 |
r = getaddrinfo (host, port, &tcp_hints, &address); |
235 |
if(r != 0) { |
236 |
perror("getaddrinfo");
|
237 |
return r;
|
238 |
} |
239 |
|
240 |
r = oi_socket_connect (&socket, address); |
241 |
if(r != 0) { |
242 |
perror("oi_socket_connect");
|
243 |
return r;
|
244 |
} |
245 |
oi_socket_attach (&socket, node_loop()); |
246 |
|
247 |
freeaddrinfo(address); |
248 |
address = NULL;
|
249 |
} |
250 |
|
251 |
void Socket::Write (Handle<Value> arg)
|
252 |
{ |
253 |
HandleScope scope; |
254 |
|
255 |
if (arg == Null()) {
|
256 |
|
257 |
oi_socket_write_eof(&socket); |
258 |
|
259 |
} else if (arg->IsString()) { |
260 |
Local<String> s = arg->ToString(); |
261 |
|
262 |
size_t l1 = s->Utf8Length(), l2; |
263 |
oi_buf *buf = oi_buf_new2(l1); |
264 |
l2 = s->WriteUtf8(buf->base, l1); |
265 |
assert(l1 == l2); |
266 |
|
267 |
oi_socket_write(&socket, buf); |
268 |
|
269 |
} else if (arg->IsArray()) { |
270 |
size_t length = array->Length(); |
271 |
Handle<Array> array = Handle<Array>::Cast(arg); |
272 |
oi_buf *buf = oi_buf_new2(length); |
273 |
for (int i = 0; i < length; i++) { |
274 |
Local<Value> int_value = array->Get(Integer::New(i)); |
275 |
buf[i] = int_value->Int32Value(); |
276 |
} |
277 |
|
278 |
oi_socket_write(&socket, buf); |
279 |
|
280 |
} else {
|
281 |
// raise error bad argument.
|
282 |
assert(0);
|
283 |
} |
284 |
} |
285 |
|
286 |
void
|
287 |
Socket::Disconnect() |
288 |
{ |
289 |
oi_socket_close(&socket); |
290 |
} |
291 |
|
292 |
void
|
293 |
Socket::OnConnect() |
294 |
{ |
295 |
HandleScope scope; |
296 |
|
297 |
assert(READY_STATE_CONNECTING == ReadyState()); |
298 |
js_object_->Set(readyState_str, readyStateOPEN); |
299 |
|
300 |
Handle<Value> on_connect_value = js_object_->Get( String::NewSymbol("on_connect") );
|
301 |
if (!on_connect_value->IsFunction())
|
302 |
return;
|
303 |
Handle<Function> on_connect = Handle<Function>::Cast(on_connect_value); |
304 |
|
305 |
TryCatch try_catch; |
306 |
|
307 |
Handle<Value> r = on_connect->Call(js_object_, 0, NULL); |
308 |
|
309 |
if(try_catch.HasCaught())
|
310 |
node_fatal_exception(try_catch); |
311 |
} |
312 |
|
313 |
void
|
314 |
Socket::OnRead (const void *buf, size_t count) |
315 |
{ |
316 |
HandleScope scope; |
317 |
|
318 |
assert(READY_STATE_OPEN == ReadyState()); |
319 |
|
320 |
Handle<Value> onread_value = js_object_->Get( String::NewSymbol("on_read") );
|
321 |
if (!onread_value->IsFunction()) return; |
322 |
Handle<Function> onread = Handle<Function>::Cast(onread_value); |
323 |
|
324 |
const int argc = 1; |
325 |
Handle<Value> argv[argc]; |
326 |
|
327 |
if(count) {
|
328 |
Handle<String> chunk = String::New((const char*)buf, count); // TODO binary data? |
329 |
argv[0] = chunk;
|
330 |
} else {
|
331 |
// TODO eof? delete write method?
|
332 |
argv[0] = Null();
|
333 |
} |
334 |
|
335 |
TryCatch try_catch; |
336 |
|
337 |
Handle<Value> r = onread->Call(js_object_, argc, argv); |
338 |
|
339 |
if(try_catch.HasCaught())
|
340 |
node_fatal_exception(try_catch); |
341 |
} |
342 |
|
343 |
void
|
344 |
Socket::OnClose () |
345 |
{ |
346 |
HandleScope scope; |
347 |
|
348 |
printf("onclose readyState %d\n", ReadyState());
|
349 |
|
350 |
assert(READY_STATE_OPEN == ReadyState()); |
351 |
js_object_->Set(readyState_str, readyStateCLOSED); |
352 |
|
353 |
Handle<Value> onclose_value = js_object_->Get( String::NewSymbol("on_close") );
|
354 |
if (!onclose_value->IsFunction()) return; |
355 |
Handle<Function> onclose = Handle<Function>::Cast(onclose_value); |
356 |
|
357 |
TryCatch try_catch; |
358 |
|
359 |
Handle<Value> r = onclose->Call(js_object_, 0, NULL); |
360 |
|
361 |
if(try_catch.HasCaught())
|
362 |
node_fatal_exception(try_catch); |
363 |
} |
364 |
|
365 |
void
|
366 |
NodeInit_net (Handle<Object> target) |
367 |
{ |
368 |
HandleScope scope; |
369 |
|
370 |
//
|
371 |
// Socket
|
372 |
//
|
373 |
Local<FunctionTemplate> socket_template = FunctionTemplate::New(NewSocket); |
374 |
target->Set(String::NewSymbol("Socket"), socket_template->GetFunction());
|
375 |
socket_template->InstanceTemplate()->SetInternalFieldCount(1);
|
376 |
|
377 |
// socket.connectTCP()
|
378 |
Local<FunctionTemplate> socket_connect_tcp = |
379 |
FunctionTemplate::New(SocketConnectTCPCallback); |
380 |
socket_template->InstanceTemplate()->Set(String::NewSymbol("connectTCP"),
|
381 |
socket_connect_tcp->GetFunction()); |
382 |
|
383 |
// socket.connectUNIX()
|
384 |
Local<FunctionTemplate> socket_connect_unix = |
385 |
FunctionTemplate::New(SocketConnectUNIXCallback); |
386 |
socket_template->InstanceTemplate()->Set(String::NewSymbol("connectUNIX"),
|
387 |
socket_connect_unix->GetFunction()); |
388 |
|
389 |
// socket.write()
|
390 |
Local<FunctionTemplate> socket_write = |
391 |
FunctionTemplate::New(SocketWriteCallback); |
392 |
socket_template->InstanceTemplate()->Set(String::NewSymbol("write"),
|
393 |
socket_write->GetFunction()); |
394 |
|
395 |
// socket.close()
|
396 |
Local<FunctionTemplate> socket_close = |
397 |
FunctionTemplate::New(SocketCloseCallback); |
398 |
socket_template->InstanceTemplate()->Set(String::NewSymbol("close"),
|
399 |
socket_close->GetFunction()); |
400 |
|
401 |
//
|
402 |
// Server
|
403 |
//
|
404 |
Local<FunctionTemplate> server_template = FunctionTemplate::New(NewServer); |
405 |
target->Set(String::NewSymbol("Server"), server_template->GetFunction());
|
406 |
server_template->InstanceTemplate()->SetInternalFieldCount(1);
|
407 |
|
408 |
// server.listenTCP()
|
409 |
Local<FunctionTemplate> server_listenTCP = |
410 |
FunctionTemplate::New(ServerListenTCPCallback); |
411 |
server_template->InstanceTemplate()->Set(String::NewSymbol("listenTCP"),
|
412 |
server_listenTCP->GetFunction()); |
413 |
|
414 |
// server.listenUNIX()
|
415 |
Local<FunctionTemplate> server_listenUNIX = |
416 |
FunctionTemplate::New(ServerListenUNIXCallback); |
417 |
server_template->InstanceTemplate()->Set(String::NewSymbol("listenUNIX"),
|
418 |
server_listenTCP->GetFunction()); |
419 |
|
420 |
// server.close()
|
421 |
Local<FunctionTemplate> server_close = FunctionTemplate::New(ServerCloseCallback); |
422 |
server_template->InstanceTemplate()->Set(String::NewSymbol("close"),
|
423 |
server_close->GetFunction()); |
424 |
} |
425 |
|