blob: 23b468b0611b3a20d64710d6ba91025f17772146 [file] [log] [blame]
Simon Hunta25cdcd2015-01-22 13:11:42 -08001#!/usr/bin/env node
Simon Hunta25cdcd2015-01-22 13:11:42 -08002
Simon Hunt711ee242015-01-22 15:08:02 -08003// === Mock Web Socket Server - for testing the topology view
4
5var fs = require('fs'),
6 readline = require('readline'),
7 http = require('http'),
8 WebSocketServer = require('websocket').server,
Simon Hunt74928302015-01-30 12:42:06 -08009 port = 8123,
Bri Prebilic Cole236ef682015-07-28 16:10:18 -070010 scenarioRoot = 'ev/',
11 verbose = false, // show received messages from client
12 extraVerbose = false; // show ALL received messages from client
Simon Hunt711ee242015-01-22 15:08:02 -080013
14var lastcmd, // last command executed
15 lastargs, // arguments to last command
16 connection, // ws connection
17 origin, // origin of connection
Simon Hunt626d2102015-01-29 11:54:50 -080018 scid, // scenario ID
19 scdata, // scenario data
Simon Hunt711ee242015-01-22 15:08:02 -080020 scdone, // shows when scenario is over
Simon Hunt74928302015-01-30 12:42:06 -080021 eventsById, // map of event file names
22 maxEvno, // highest loaded event number
Simon Huntd450da92015-02-23 13:25:52 -080023 autoLast, // last event number for auto-advance
Simon Hunt711ee242015-01-22 15:08:02 -080024 evno, // next event number
25 evdata; // event data
26
27
Bri Prebilic Cole236ef682015-07-28 16:10:18 -070028process.argv.forEach(function (val) {
29 switch (val) {
30 case '-v': verbose = true; break;
31 case '-v!': extraVerbose = true; break;
32 }
33});
34
Simon Hunt74928302015-01-30 12:42:06 -080035var scFiles = fs.readdirSync(scenarioRoot);
Bri Prebilic Cole236ef682015-07-28 16:10:18 -070036console.log();
Simon Hunt74928302015-01-30 12:42:06 -080037console.log('Mock Server v1.0');
Bri Prebilic Cole236ef682015-07-28 16:10:18 -070038if (verbose || extraVerbose) {
39 console.log('Verbose=' + verbose, 'ExtraVerbose=' + extraVerbose);
40}
Simon Hunt74928302015-01-30 12:42:06 -080041console.log('================');
Simon Hunt5724fb42015-02-05 16:59:40 -080042listScenarios();
Simon Hunt711ee242015-01-22 15:08:02 -080043
44var rl = readline.createInterface(process.stdin, process.stdout);
45rl.setPrompt('ws> ');
Simon Hunta25cdcd2015-01-22 13:11:42 -080046
47
48var server = http.createServer(function(request, response) {
49 console.log((new Date()) + ' Received request for ' + request.url);
50 response.writeHead(404);
51 response.end();
52});
53
54server.listen(port, function() {
55 console.log((new Date()) + ' Server is listening on port ' + port);
56});
57
58server.on('listening', function () {
Simon Huntb0ec1e52015-01-28 18:13:49 -080059 console.log('OK, server is running');
Simon Hunt626d2102015-01-29 11:54:50 -080060 console.log('(? for help)');
Simon Hunta25cdcd2015-01-22 13:11:42 -080061});
62
63var wsServer = new WebSocketServer({
64 httpServer: server,
65 // You should not use autoAcceptConnections for production
66 // applications, as it defeats all standard cross-origin protection
67 // facilities built into the protocol and the browser. You should
68 // *always* verify the connection's origin and decide whether or not
69 // to accept it.
70 autoAcceptConnections: false
71});
72
73function originIsAllowed(origin) {
74 // put logic here to detect whether the specified origin is allowed.
75 return true;
76}
77
Bri Prebilic Cole236ef682015-07-28 16:10:18 -070078// displays the message if our arguments say we should
79function displayMsg(msg) {
80 var ev = JSON.parse(msg);
81 switch (ev.event) {
82 case 'topoHeartbeat': return extraVerbose;
83 default: return true;
84 }
85}
86
Simon Hunta25cdcd2015-01-22 13:11:42 -080087wsServer.on('request', function(request) {
88 console.log(); // newline after prompt
89 console.log("Origin: ", request.origin);
90
91 if (!originIsAllowed(request.origin)) {
92 // Make sure we only accept requests from an allowed origin
93 request.reject();
94 console.log((new Date()) + ' Connection from origin ' + request.origin + ' rejected.');
95 return;
96 }
97
Simon Hunt711ee242015-01-22 15:08:02 -080098 origin = request.origin;
99 connection = request.accept(null, origin);
Simon Hunta25cdcd2015-01-22 13:11:42 -0800100
101
102 console.log((new Date()) + ' Connection accepted.');
103 rl.prompt();
104
Simon Hunta25cdcd2015-01-22 13:11:42 -0800105 connection.on('message', function(message) {
Bri Prebilic Cole236ef682015-07-28 16:10:18 -0700106 if (verbose || extraVerbose) {
107 if (message.type === 'utf8') {
108 if (displayMsg(message.utf8Data)) {
109 console.log(); // newline after prompt
110 console.log('Received Message: ' + message.utf8Data);
111 }
112 //connection.sendUTF(message.utf8Data);
113 rl.prompt();
114 }
115 else if (message.type === 'binary') {
116 console.log('Received Binary Message of ' + message.binaryData.length + ' bytes');
117 //connection.sendBytes(message.binaryData);
118 }
Simon Hunta25cdcd2015-01-22 13:11:42 -0800119 }
120 });
121 connection.on('close', function(reasonCode, description) {
122 console.log((new Date()) + ' Peer ' + connection.remoteAddress + ' disconnected.');
Simon Hunt711ee242015-01-22 15:08:02 -0800123 connection = null;
124 origin = null;
Simon Hunta25cdcd2015-01-22 13:11:42 -0800125 });
126});
127
128
Simon Hunt711ee242015-01-22 15:08:02 -0800129setTimeout(doCli, 10); // allow async processes to write to stdout first
Simon Hunta25cdcd2015-01-22 13:11:42 -0800130
131function doCli() {
132 rl.prompt();
133 rl.on('line', function (line) {
134 var words = line.trim().split(' '),
Simon Hunt354c8c92015-01-22 15:32:51 -0800135 cmd = words.shift(),
136 str = words.join(' ');
137
138 if (!cmd) {
139 // repeat last command
140 cmd = lastcmd;
141 str = lastargs;
142 }
Simon Hunta25cdcd2015-01-22 13:11:42 -0800143
144 switch(cmd) {
Simon Hunt5724fb42015-02-05 16:59:40 -0800145 case 'l': listScenarios(); break;
Simon Hunt711ee242015-01-22 15:08:02 -0800146 case 'c': connStatus(); break;
147 case 'm': customMessage(str); break;
148 case 's': setScenario(str); break;
Simon Huntd450da92015-02-23 13:25:52 -0800149 case 'a': autoAdvance(); break;
Simon Hunt711ee242015-01-22 15:08:02 -0800150 case 'n': nextEvent(); break;
Simon Huntb0ec1e52015-01-28 18:13:49 -0800151 case 'r': restartScenario(); break;
Simon Hunt711ee242015-01-22 15:08:02 -0800152 case 'q': quit(); break;
153 case '?': showHelp(); break;
154 default: console.log('Say what?! (? for help)'); break;
155 }
156 lastcmd = cmd;
157 lastargs = str;
158 rl.prompt();
Simon Hunta25cdcd2015-01-22 13:11:42 -0800159
Simon Hunt711ee242015-01-22 15:08:02 -0800160 }).on('close', function () {
161 quit();
162 });
163}
Simon Hunta25cdcd2015-01-22 13:11:42 -0800164
Simon Hunt711ee242015-01-22 15:08:02 -0800165var helptext = '\n' +
Simon Hunt5724fb42015-02-05 16:59:40 -0800166 'l - list scenarios\n' +
Simon Hunt711ee242015-01-22 15:08:02 -0800167 'c - show connection status\n' +
168 'm {text} - send custom message to client\n' +
Simon Huntb0ec1e52015-01-28 18:13:49 -0800169 's {id} - load scenario {id}\n' +
Simon Hunt5724fb42015-02-05 16:59:40 -0800170 's - show scenario status\n' +
Simon Huntd450da92015-02-23 13:25:52 -0800171 'a - auto-send events\n' +
Simon Hunt711ee242015-01-22 15:08:02 -0800172 'n - send next event\n' +
Simon Huntb0ec1e52015-01-28 18:13:49 -0800173 'r - restart the scenario\n' +
Simon Hunt711ee242015-01-22 15:08:02 -0800174 'q - exit the server\n' +
175 '? - display this help text\n';
Simon Hunta25cdcd2015-01-22 13:11:42 -0800176
Simon Hunt711ee242015-01-22 15:08:02 -0800177function showHelp() {
178 console.log(helptext);
179}
180
Simon Hunt5724fb42015-02-05 16:59:40 -0800181function listScenarios() {
182 console.log('Scenarios ...');
183 console.log(scFiles.join(', '));
184 console.log();
185}
186
Simon Hunt711ee242015-01-22 15:08:02 -0800187function connStatus() {
188 if (connection) {
189 console.log('Connection from ' + origin + ' established.');
190 } else {
191 console.log('No connection.');
192 }
193}
194
195function quit() {
Simon Huntb0ec1e52015-01-28 18:13:49 -0800196 console.log('Quitting...');
Simon Hunt711ee242015-01-22 15:08:02 -0800197 process.exit(0);
198}
199
200function customMessage(m) {
201 if (connection) {
Simon Huntb0ec1e52015-01-28 18:13:49 -0800202 console.log('Sending message: ' + m);
Simon Hunt711ee242015-01-22 15:08:02 -0800203 connection.sendUTF(m);
204 } else {
205 console.warn('No current connection.');
206 }
207}
208
209function showScenarioStatus() {
210 var msg;
Simon Hunt626d2102015-01-29 11:54:50 -0800211 if (!scid) {
Simon Huntb0ec1e52015-01-28 18:13:49 -0800212 console.log('No scenario loaded.');
Simon Hunt711ee242015-01-22 15:08:02 -0800213 } else {
Simon Hunt626d2102015-01-29 11:54:50 -0800214 msg = 'Scenario: "' + scid + '", ' +
Simon Hunt711ee242015-01-22 15:08:02 -0800215 (scdone ? 'DONE' : 'next event: ' + evno);
216 console.log(msg);
217 }
218}
219
220function scenarioPath(evno) {
Simon Hunt74928302015-01-30 12:42:06 -0800221 var file = evno ? ('/' + eventsById[evno].fname) : '/scenario.json';
222 return scenarioRoot + scid + file;
Simon Hunt626d2102015-01-29 11:54:50 -0800223}
224
225
226function initScenario(verb) {
227 console.log(); // get past prompt
228 console.log(verb + ' scenario "' + scid + '"');
229 console.log(scdata.title);
230 scdata.description.forEach(function (d) {
231 console.log(' ' + d);
232 });
Simon Huntd450da92015-02-23 13:25:52 -0800233 autoLast = (scdata.params && scdata.params.lastAuto) || 0;
234 if (autoLast) {
235 console.log('[auto-advance: ' + autoLast + ']');
236 }
Simon Hunt626d2102015-01-29 11:54:50 -0800237 evno = 1;
238 scdone = false;
Simon Hunt74928302015-01-30 12:42:06 -0800239 readEventFilenames();
240}
241
242function readEventFilenames() {
243 var files = fs.readdirSync(scenarioRoot + scid),
244 eventCount = 0,
245 match, id, tag;
246
247 maxEvno = 0;
248
249 eventsById = {};
250 files.forEach(function (f) {
251 match = /^ev_(\d+)_(.*)\.json$/.exec(f);
252 if (match) {
253 eventCount++;
254 id = match[1];
255 tag = match[2];
256 eventsById[id] = {
257 fname: f,
258 num: id,
259 tag: tag
260 };
261 if (Number(id) > Number(maxEvno)) {
262 maxEvno = id;
263 }
264 }
265
266 });
267 console.log('[' + eventCount + ' events loaded, (max=' + maxEvno + ')]');
Simon Hunt711ee242015-01-22 15:08:02 -0800268}
269
270function setScenario(id) {
271 if (!id) {
272 return showScenarioStatus();
273 }
274
275 evdata = null;
Simon Hunt626d2102015-01-29 11:54:50 -0800276 scid = id;
Simon Hunt711ee242015-01-22 15:08:02 -0800277 fs.readFile(scenarioPath(), 'utf8', function (err, data) {
278 if (err) {
279 console.warn('No scenario named "' + id + '"', err);
Simon Hunt626d2102015-01-29 11:54:50 -0800280 scid = null;
Simon Hunt711ee242015-01-22 15:08:02 -0800281 } else {
Simon Hunt626d2102015-01-29 11:54:50 -0800282 scdata = JSON.parse(data);
283 initScenario('Loading');
Simon Hunta25cdcd2015-01-22 13:11:42 -0800284 }
285 rl.prompt();
Simon Hunta25cdcd2015-01-22 13:11:42 -0800286 });
Simon Hunt711ee242015-01-22 15:08:02 -0800287}
Simon Hunta25cdcd2015-01-22 13:11:42 -0800288
Simon Huntb0ec1e52015-01-28 18:13:49 -0800289function restartScenario() {
Simon Hunt626d2102015-01-29 11:54:50 -0800290 if (!scid) {
Simon Huntb0ec1e52015-01-28 18:13:49 -0800291 console.log('No scenario loaded.');
292 } else {
Simon Hunt626d2102015-01-29 11:54:50 -0800293 initScenario('Restarting');
Simon Huntb0ec1e52015-01-28 18:13:49 -0800294 }
295 rl.prompt();
296}
297
Simon Huntd450da92015-02-23 13:25:52 -0800298function eventAvailable() {
Simon Hunt626d2102015-01-29 11:54:50 -0800299 if (!scid) {
Simon Huntb0ec1e52015-01-28 18:13:49 -0800300 console.log('No scenario loaded.');
Simon Hunt711ee242015-01-22 15:08:02 -0800301 rl.prompt();
Simon Huntd450da92015-02-23 13:25:52 -0800302 return false;
303 }
304
305 if (!connection) {
306 console.log('No current connection.');
Simon Hunt711ee242015-01-22 15:08:02 -0800307 rl.prompt();
Simon Huntd450da92015-02-23 13:25:52 -0800308 return false;
309 }
310
311 if (Number(evno) > Number(maxEvno)) {
312 scdone = true;
313 console.log('Scenario DONE.');
314 return false;
315 }
316 return true;
317}
318
319function autoAdvance() {
320 if (evno > autoLast) {
321 console.log('[auto done]');
322 return;
323 }
324
325 // need to recurse with a callback, since each event send relies
326 // on an async load of event data...
327 function callback() {
328 if (eventAvailable() && evno <= autoLast) {
329 _nextEvent(callback);
Simon Hunt74928302015-01-30 12:42:06 -0800330 }
Simon Hunt711ee242015-01-22 15:08:02 -0800331 }
Simon Huntd450da92015-02-23 13:25:52 -0800332
333 callback();
334}
335
336function nextEvent() {
337 if (eventAvailable()) {
338 _nextEvent();
339 }
340}
341
342function _nextEvent(callback) {
343 var path = scenarioPath(evno);
344
345 fs.readFile(path, 'utf8', function (err, data) {
346 if (err) {
347 console.error('Oops error: ' + err);
348 } else {
349 evdata = JSON.parse(data);
350 console.log(); // get past prompt
351 console.log('Sending event #' + evno + ' [' + evdata.event +
352 '] from ' + eventsById[evno].fname);
353 connection.sendUTF(data);
354 evno++;
355 if (callback) {
356 callback();
357 }
358 }
359 rl.prompt();
360 });
Simon Hunta25cdcd2015-01-22 13:11:42 -0800361}