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 / node_tcp.cc @ 1a126ed1
History | View | Annotate | Download (7.58 KB)
1 |
#include "node_tcp.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 |
|
12 |
using namespace v8; |
13 |
|
14 |
static Persistent<String> readyState_str;
|
15 |
|
16 |
static Persistent<Integer> readyState_CONNECTING;
|
17 |
static Persistent<Integer> readyState_OPEN;
|
18 |
static Persistent<Integer> readyState_CLOSED;
|
19 |
|
20 |
enum readyState { READY_STATE_CONNECTING = 0 |
21 |
, READY_STATE_OPEN = 1
|
22 |
, READY_STATE_CLOSED = 2
|
23 |
} ; |
24 |
|
25 |
class TCPClient { |
26 |
public:
|
27 |
TCPClient(Handle<Object> obj); |
28 |
~TCPClient(); |
29 |
|
30 |
int Connect(char *host, char *port); |
31 |
void Write (Handle<Value> arg);
|
32 |
void Disconnect();
|
33 |
|
34 |
void OnOpen();
|
35 |
void OnRead(const void *buf, size_t count); |
36 |
void OnClose();
|
37 |
|
38 |
private:
|
39 |
int ReadyState();
|
40 |
oi_socket socket; |
41 |
struct addrinfo *address;
|
42 |
Persistent<Object> js_client; |
43 |
}; |
44 |
|
45 |
|
46 |
static void |
47 |
on_connect (oi_socket *socket) |
48 |
{ |
49 |
TCPClient *client = static_cast<TCPClient*> (socket->data);
|
50 |
client->OnOpen(); |
51 |
} |
52 |
|
53 |
static void |
54 |
on_close (oi_socket *socket) |
55 |
{ |
56 |
TCPClient *client = static_cast<TCPClient*> (socket->data);
|
57 |
client->OnClose(); |
58 |
} |
59 |
|
60 |
static void |
61 |
on_read (oi_socket *socket, const void *buf, size_t count) |
62 |
{ |
63 |
TCPClient *client = static_cast<TCPClient*> (socket->data);
|
64 |
client->OnRead(buf, count); |
65 |
} |
66 |
|
67 |
static struct addrinfo tcp_hints = |
68 |
/* ai_flags */ { AI_PASSIVE
|
69 |
/* ai_family */ , AF_UNSPEC
|
70 |
/* ai_socktype */ , SOCK_STREAM
|
71 |
/* ai_protocol */ , 0 |
72 |
/* ai_addrlen */ , 0 |
73 |
/* ai_addr */ , 0 |
74 |
/* ai_canonname */ , 0 |
75 |
/* ai_next */ , 0 |
76 |
}; |
77 |
|
78 |
static Handle<Value> newTCPClient
|
79 |
( const Arguments& args
|
80 |
) |
81 |
{ |
82 |
if (args.Length() < 1) |
83 |
return Undefined();
|
84 |
|
85 |
HandleScope scope; |
86 |
|
87 |
char *host = NULL; |
88 |
String::AsciiValue host_v(args[0]->ToString());
|
89 |
if(args[0]->IsString()) { |
90 |
host = *host_v; |
91 |
} |
92 |
String::AsciiValue port(args[1]);
|
93 |
|
94 |
TCPClient *client = new TCPClient(args.This());
|
95 |
if(client == NULL) |
96 |
return Undefined(); // XXX raise error? |
97 |
|
98 |
int r = client->Connect(host, *port);
|
99 |
if (r != 0) |
100 |
return Undefined(); // XXX raise error? |
101 |
|
102 |
|
103 |
return args.This();
|
104 |
} |
105 |
|
106 |
static TCPClient*
|
107 |
UnwrapClient (Handle<Object> obj) |
108 |
{ |
109 |
HandleScope scope; |
110 |
Handle<External> field = Handle<External>::Cast(obj->GetInternalField(0));
|
111 |
TCPClient* client = static_cast<TCPClient*>(field->Value());
|
112 |
return client;
|
113 |
} |
114 |
|
115 |
static Handle<Value>
|
116 |
WriteCallback (const Arguments& args)
|
117 |
{ |
118 |
HandleScope scope; |
119 |
TCPClient *client = UnwrapClient(args.Holder()); |
120 |
client->Write(args[0]);
|
121 |
return Undefined();
|
122 |
} |
123 |
|
124 |
static Handle<Value>
|
125 |
DisconnectCallback (const Arguments& args)
|
126 |
{ |
127 |
HandleScope scope; |
128 |
TCPClient *client = UnwrapClient(args.Holder()); |
129 |
client->Disconnect(); |
130 |
return Undefined();
|
131 |
} |
132 |
|
133 |
static void |
134 |
client_destroy (Persistent<Value> _, void *data)
|
135 |
{ |
136 |
TCPClient *client = static_cast<TCPClient *> (data);
|
137 |
delete client;
|
138 |
} |
139 |
|
140 |
TCPClient::TCPClient(Handle<Object> _js_client) |
141 |
{ |
142 |
oi_socket_init(&socket, 300.0); // TODO adjustable timeout |
143 |
socket.on_connect = on_connect; |
144 |
socket.on_read = on_read; |
145 |
socket.on_drain = NULL;
|
146 |
socket.on_error = NULL;
|
147 |
socket.on_close = on_close; |
148 |
socket.on_timeout = on_close; |
149 |
socket.data = this;
|
150 |
|
151 |
HandleScope scope; |
152 |
js_client = Persistent<Object>::New(_js_client); |
153 |
js_client->SetInternalField (0, External::New(this)); |
154 |
js_client.MakeWeak (this, client_destroy);
|
155 |
} |
156 |
|
157 |
TCPClient::~TCPClient () |
158 |
{ |
159 |
Disconnect(); |
160 |
oi_socket_detach (&socket); |
161 |
js_client.Dispose(); |
162 |
js_client.Clear(); // necessary?
|
163 |
} |
164 |
|
165 |
int
|
166 |
TCPClient::Connect(char *host, char *port) |
167 |
{ |
168 |
int r;
|
169 |
|
170 |
HandleScope scope; |
171 |
|
172 |
js_client->Set(readyState_str, readyState_CONNECTING); |
173 |
|
174 |
/* FIXME Blocking DNS resolution. Use oi_async. */
|
175 |
printf("resolving host: %s, port: %s\n", host, port);
|
176 |
r = getaddrinfo (host, port, &tcp_hints, &address); |
177 |
if(r != 0) { |
178 |
perror("getaddrinfo");
|
179 |
return r;
|
180 |
} |
181 |
|
182 |
r = oi_socket_connect (&socket, address); |
183 |
if(r != 0) { |
184 |
perror("oi_socket_connect");
|
185 |
return r;
|
186 |
} |
187 |
oi_socket_attach (&socket, node_loop()); |
188 |
|
189 |
freeaddrinfo(address); |
190 |
address = NULL;
|
191 |
} |
192 |
|
193 |
void TCPClient::Write (Handle<Value> arg)
|
194 |
{ |
195 |
HandleScope scope; |
196 |
|
197 |
//
|
198 |
// TODO if ReadyState() is not READY_STATE_OPEN then raise INVALID_STATE_ERR
|
199 |
//
|
200 |
|
201 |
if(arg == Null()) {
|
202 |
|
203 |
oi_socket_write_eof(&socket); |
204 |
|
205 |
} else {
|
206 |
Local<String> s = arg->ToString(); |
207 |
|
208 |
size_t l1 = s->Utf8Length(), l2; |
209 |
oi_buf *buf = oi_buf_new2(l1); |
210 |
l2 = s->WriteUtf8(buf->base, l1); |
211 |
assert(l1 == l2); |
212 |
|
213 |
oi_socket_write(&socket, buf); |
214 |
} |
215 |
} |
216 |
|
217 |
int
|
218 |
TCPClient::ReadyState() |
219 |
{ |
220 |
return js_client->Get(readyState_str)->IntegerValue();
|
221 |
} |
222 |
|
223 |
void
|
224 |
TCPClient::Disconnect() |
225 |
{ |
226 |
oi_socket_close(&socket); |
227 |
} |
228 |
|
229 |
void
|
230 |
TCPClient::OnOpen() |
231 |
{ |
232 |
HandleScope scope; |
233 |
|
234 |
assert(READY_STATE_CONNECTING == ReadyState()); |
235 |
js_client->Set(readyState_str, readyState_OPEN); |
236 |
|
237 |
Handle<Value> onopen_value = js_client->Get( String::NewSymbol("onopen") );
|
238 |
if (!onopen_value->IsFunction())
|
239 |
return;
|
240 |
Handle<Function> onopen = Handle<Function>::Cast(onopen_value); |
241 |
|
242 |
TryCatch try_catch; |
243 |
|
244 |
Handle<Value> r = onopen->Call(js_client, 0, NULL); |
245 |
|
246 |
if(try_catch.HasCaught())
|
247 |
node_fatal_exception(try_catch); |
248 |
} |
249 |
|
250 |
void
|
251 |
TCPClient::OnRead(const void *buf, size_t count) |
252 |
{ |
253 |
HandleScope scope; |
254 |
|
255 |
assert(READY_STATE_OPEN == ReadyState()); |
256 |
|
257 |
Handle<Value> onread_value = js_client->Get( String::NewSymbol("onread") );
|
258 |
if (!onread_value->IsFunction()) return; |
259 |
Handle<Function> onread = Handle<Function>::Cast(onread_value); |
260 |
|
261 |
const int argc = 1; |
262 |
Handle<Value> argv[argc]; |
263 |
|
264 |
if(count) {
|
265 |
Handle<String> chunk = String::New((const char*)buf, count); // TODO binary data? |
266 |
argv[0] = chunk;
|
267 |
} else {
|
268 |
// TODO eof? delete write method?
|
269 |
argv[0] = Null();
|
270 |
} |
271 |
|
272 |
TryCatch try_catch; |
273 |
|
274 |
Handle<Value> r = onread->Call(js_client, argc, argv); |
275 |
|
276 |
if(try_catch.HasCaught())
|
277 |
node_fatal_exception(try_catch); |
278 |
} |
279 |
|
280 |
void
|
281 |
TCPClient::OnClose() |
282 |
{ |
283 |
HandleScope scope; |
284 |
|
285 |
printf("onclose readyState %d\n", ReadyState());
|
286 |
|
287 |
assert(READY_STATE_OPEN == ReadyState()); |
288 |
js_client->Set(readyState_str, readyState_CLOSED); |
289 |
|
290 |
Handle<Value> onclose_value = js_client->Get( String::NewSymbol("onclose") );
|
291 |
if (!onclose_value->IsFunction()) return; |
292 |
Handle<Function> onclose = Handle<Function>::Cast(onclose_value); |
293 |
|
294 |
TryCatch try_catch; |
295 |
|
296 |
Handle<Value> r = onclose->Call(js_client, 0, NULL); |
297 |
|
298 |
if(try_catch.HasCaught())
|
299 |
node_fatal_exception(try_catch); |
300 |
} |
301 |
|
302 |
void
|
303 |
Init_tcp (Handle<Object> target) |
304 |
{ |
305 |
HandleScope scope; |
306 |
readyState_str = Persistent<String>::New(String::NewSymbol("readyState"));
|
307 |
|
308 |
Local<FunctionTemplate> client_t = FunctionTemplate::New(newTCPClient); |
309 |
|
310 |
client_t->InstanceTemplate()->SetInternalFieldCount(1);
|
311 |
|
312 |
/* readyState constants */
|
313 |
|
314 |
readyState_CONNECTING = Persistent<Integer>::New(Integer::New(READY_STATE_CONNECTING)); |
315 |
client_t->Set ("CONNECTING", readyState_CONNECTING);
|
316 |
|
317 |
readyState_OPEN = Persistent<Integer>::New(Integer::New(READY_STATE_OPEN)); |
318 |
client_t->Set ("OPEN", readyState_OPEN);
|
319 |
|
320 |
readyState_CLOSED = Persistent<Integer>::New(Integer::New(READY_STATE_CLOSED)); |
321 |
client_t->Set ("CLOSED", readyState_CLOSED);
|
322 |
|
323 |
/* write callback */
|
324 |
|
325 |
Local<FunctionTemplate> write_t = FunctionTemplate::New(WriteCallback); |
326 |
|
327 |
client_t->InstanceTemplate()->Set ( String::NewSymbol("write")
|
328 |
, write_t->GetFunction() |
329 |
); |
330 |
|
331 |
/* disconnect callback */
|
332 |
|
333 |
Local<FunctionTemplate> disconnect_t = FunctionTemplate::New(DisconnectCallback); |
334 |
|
335 |
client_t->InstanceTemplate()->Set ( String::NewSymbol("disconnect")
|
336 |
, disconnect_t->GetFunction() |
337 |
); |
338 |
|
339 |
target->Set(String::NewSymbol("TCPClient"), client_t->GetFunction());
|
340 |
} |
341 |
|