blob: 7efb1a282a5ec9f12bf3247da51a157495f6566c [file] [log] [blame]
Alexander Afanasyev181a8b92013-02-28 13:28:53 -08001#!/usr/bin/env node
2
Wentao Shangc05dc532012-11-19 12:00:33 -08003/*
4 * @author: Wentao Shang
5 * See COPYING for copyright and distribution information.
Alexander Afanasyev03d3f742013-08-14 17:47:28 -07006 * Implement WebSocket proxy between ndnd and javascript stack.
Wentao Shangc05dc532012-11-19 12:00:33 -08007 */
8
Jeff Thompson287a3182012-11-11 18:12:20 -08009var WebSocketServer = require('ws').Server;
10var dgram = require('dgram');
11
Wentao Shangc05dc532012-11-19 12:00:33 -080012var opt = require('node-getopt').create([
Alexander Afanasyev03d3f742013-08-14 17:47:28 -070013 ['c' , 'ndnd=ARG', 'host name or ip of ndnd router'],
Wentao Shang7eb8c402012-11-19 13:30:44 -080014 ['p' , 'port=ARG', 'port number on which the proxy will listen'],
Wentao Shangc05dc532012-11-19 12:00:33 -080015 ['m' , 'maxclient=ARG', 'maximum number of concurrent client'],
16 ['L' , 'LOG=ARG', 'level of log message display'],
17 ['h' , 'help', 'display this help']
18]) // create Getopt instance
19.bindHelp() // bind option 'help' to default action
20.parseSystem(); // parse command line
Jeff Thompson287a3182012-11-11 18:12:20 -080021
Alexander Afanasyev03d3f742013-08-14 17:47:28 -070022var ndndhost = opt.options.ndnd || 'localhost';
Wentao Shang7eb8c402012-11-19 13:30:44 -080023var wsport = opt.options.port || 9696;
Jeff Thompson287a3182012-11-11 18:12:20 -080024
Wentao Shang7eb8c402012-11-19 13:30:44 -080025var wss = new WebSocketServer({port:wsport, host:'0.0.0.0'}); // Set host to '0.0.0.0' so that we can accept connections from anywhere
Alexander Afanasyev03d3f742013-08-14 17:47:28 -070026 // This host has nothing to do with ndndhost.
Wentao Shangc05dc532012-11-19 12:00:33 -080027
28var MaxNumOfClients = opt.options.maxclient || 40;
29
30var LOG = opt.options.LOG || 1;
31
32if (LOG > 0) console.log('WebSocketServer started...');
Jeff Thompson287a3182012-11-11 18:12:20 -080033
34wss.on('connection', function(ws) {
Wentao Shangc05dc532012-11-19 12:00:33 -080035 if (LOG > 0) console.log('wss.onconnection: WebSocket client connection received.');
36 if (LOG > 0) console.log('wss.onconnection: Number of clients now is ' + wss.clients.length);
Jeff Thompson287a3182012-11-11 18:12:20 -080037
38 if (wss.clients.length > MaxNumOfClients) {
Wentao Shangc05dc532012-11-19 12:00:33 -080039 if (LOG > 0) console.log('wss.onconnection: Max num of clients exceeded. Close WS connection now.');
Jeff Thompson287a3182012-11-11 18:12:20 -080040 ws.terminate();
41 return;
42 }
43
44 var udp = dgram.createSocket("udp4");
45
46 /*
47 * According to the email discussion with Michael, when we use
Alexander Afanasyev03d3f742013-08-14 17:47:28 -070048 * UDP to connect to ndnd, we MUST first send a 'heartbeat'
Jeff Thompson287a3182012-11-11 18:12:20 -080049 * UDP packet with 1-byte payload (content of this byte can
Alexander Afanasyev03d3f742013-08-14 17:47:28 -070050 * be random). The purpose of this packet is to let ndnd
51 * mark the incoming FACE as 'friendly' (with NDN_FACE_GG
Jeff Thompson287a3182012-11-11 18:12:20 -080052 * flag set). We also need to periodically send this 'heartbeat'
Alexander Afanasyev03d3f742013-08-14 17:47:28 -070053 * packet every few seconds to keep ndnd from recycling the UDP
Jeff Thompson287a3182012-11-11 18:12:20 -080054 * face. Michael recomended 8 seconds interval.
55 * --Wentao
56 */
57 // Send 'heartbeat' packet now
58 var heartbeat = new Buffer(1);
59 heartbeat[0] = 0x21;
Alexander Afanasyev03d3f742013-08-14 17:47:28 -070060 udp.send(heartbeat, 0, 1, 9695, ndndhost, null);
Jeff Thompson287a3182012-11-11 18:12:20 -080061
62 // Schedule a timer to send 'heartbeat' periodically
63 var timerID = setInterval(function() {
64 if (udp == null || udp == undefined)
65 return;
66
67 var hb = new Buffer(1);
68 hb[0] = 0x21;
Alexander Afanasyev03d3f742013-08-14 17:47:28 -070069 udp.send(hb, 0, 1, 9695, ndndhost, null);
70 if (LOG > 1) console.log('UDP heartbeat fired at ndnd.');
Jeff Thompson287a3182012-11-11 18:12:20 -080071 },
72 8000 // 8000 ms delay
73 );
74
75 ws.on('message', function(message) {
Wentao Shangc05dc532012-11-19 12:00:33 -080076 if (typeof message == 'string') {
77 if (LOG > 2) console.log("ws.onmessage: Message from clinet: " + message);
78 }
Jeff Thompson287a3182012-11-11 18:12:20 -080079 else if (typeof message == 'object') {
80 // From JS array to Buffer
81 var buffer = new Buffer(message);
82
Wentao Shangc05dc532012-11-19 12:00:33 -080083 if (LOG > 2) {
84 var logMsg = 'ws.onmessage: Byte array from client: ';
85 for (var i = 0; i < buffer.length; i++)
86 logMsg += String.fromCharCode(buffer[i]);
87 console.log(logMsg);
88 }
Jeff Thompson287a3182012-11-11 18:12:20 -080089
Alexander Afanasyev03d3f742013-08-14 17:47:28 -070090 udp.send(buffer, 0, buffer.length, 9695, ndndhost, null);
Jeff Thompson287a3182012-11-11 18:12:20 -080091 }
92 });
93
94 ws.on('close', function() {
Alexander Afanasyev03d3f742013-08-14 17:47:28 -070095 if (LOG > 0) console.log('ws.onclose: WebSocket connection closed. Close UDP connection to ndnd and stop "heartbeat" timer.');
Jeff Thompson287a3182012-11-11 18:12:20 -080096 clearInterval(timerID);
97 udp.close();
98 udp = null;
99 });
100
101 udp.on('message', function(msg, rinfo) {
Wentao Shangaa0b4122013-04-16 13:37:45 -0700102 if (msg instanceof Buffer) {
Wentao Shangc05dc532012-11-19 12:00:33 -0800103 if (LOG > 2) {
104 console.log('udp.onmessage: Byte array from server: ');
Wentao Shangaa0b4122013-04-16 13:37:45 -0700105 console.log('udp.onmessage: msg.length ' + msg.length);
Wentao Shangc05dc532012-11-19 12:00:33 -0800106 var logMsg = "";
Wentao Shangaa0b4122013-04-16 13:37:45 -0700107 for (var i = 0; i < msg.length; i++)
108 logMsg += String.fromCharCode(msg[i]);
Wentao Shangc05dc532012-11-19 12:00:33 -0800109 console.log(logMsg);
110 }
Jeff Thompson287a3182012-11-11 18:12:20 -0800111
Wentao Shangaa0b4122013-04-16 13:37:45 -0700112 ws.send(msg, {binary: true, mask: false});
Jeff Thompson287a3182012-11-11 18:12:20 -0800113 }
114 });
115
Alexander Afanasyev03d3f742013-08-14 17:47:28 -0700116 // Actually the socket close by ndnd will not cause the 'close' event to raise.
Jeff Thompson287a3182012-11-11 18:12:20 -0800117 // So this event handle is only called when the client browser shuts down the WS
118 // connection, causing ws 'close' event to raise. In that event handle, we explicitly
119 // call udp.close(). So in this function we can do nothing. Anyway, here we clear the
120 // timer and terminate ws for a second time since that will not throw exception. 'ws'
121 // will check the 'readyState' before closing, therefore avoids 'close' event loop.
122 // --Wentao
123 udp.on('close', function() {
Alexander Afanasyev03d3f742013-08-14 17:47:28 -0700124 if (LOG > 0) console.log('udp.onclose: UDP connection to ndnd terminated. Shut down WS connection to client and stop "heartbeat" timer.');
Jeff Thompson287a3182012-11-11 18:12:20 -0800125 clearInterval(timerID);
126 ws.terminate();
127 });
128
129 udp.on('error', function() {
Alexander Afanasyev03d3f742013-08-14 17:47:28 -0700130 if (LOG > 0) console.log('udp.onerror: Error on UDP connection to ndnd. Shut down WS connection to client and stop "heartbeat" timer.');
Jeff Thompson287a3182012-11-11 18:12:20 -0800131 clearInterval(timerID);
132 ws.terminate();
133 });
134});