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.

Statistics
| Branch: | Revision:

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