blob: e861e83f3ca1ef17d31e580958a2878f21890f33 [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,
10 scenarioRoot = 'ev/';
Simon Hunt711ee242015-01-22 15:08:02 -080011
12var lastcmd, // last command executed
13 lastargs, // arguments to last command
14 connection, // ws connection
15 origin, // origin of connection
Simon Hunt626d2102015-01-29 11:54:50 -080016 scid, // scenario ID
17 scdata, // scenario data
Simon Hunt711ee242015-01-22 15:08:02 -080018 scdone, // shows when scenario is over
Simon Hunt74928302015-01-30 12:42:06 -080019 eventsById, // map of event file names
20 maxEvno, // highest loaded event number
Simon Huntd450da92015-02-23 13:25:52 -080021 autoLast, // last event number for auto-advance
Simon Hunt711ee242015-01-22 15:08:02 -080022 evno, // next event number
23 evdata; // event data
24
25
Simon Hunt74928302015-01-30 12:42:06 -080026var scFiles = fs.readdirSync(scenarioRoot);
27console.log('Mock Server v1.0');
28console.log('================');
Simon Hunt5724fb42015-02-05 16:59:40 -080029listScenarios();
Simon Hunt711ee242015-01-22 15:08:02 -080030
31var rl = readline.createInterface(process.stdin, process.stdout);
32rl.setPrompt('ws> ');
Simon Hunta25cdcd2015-01-22 13:11:42 -080033
34
35var server = http.createServer(function(request, response) {
36 console.log((new Date()) + ' Received request for ' + request.url);
37 response.writeHead(404);
38 response.end();
39});
40
41server.listen(port, function() {
42 console.log((new Date()) + ' Server is listening on port ' + port);
43});
44
45server.on('listening', function () {
Simon Huntb0ec1e52015-01-28 18:13:49 -080046 console.log('OK, server is running');
Simon Hunt626d2102015-01-29 11:54:50 -080047 console.log('(? for help)');
Simon Hunta25cdcd2015-01-22 13:11:42 -080048});
49
50var wsServer = new WebSocketServer({
51 httpServer: server,
52 // You should not use autoAcceptConnections for production
53 // applications, as it defeats all standard cross-origin protection
54 // facilities built into the protocol and the browser. You should
55 // *always* verify the connection's origin and decide whether or not
56 // to accept it.
57 autoAcceptConnections: false
58});
59
60function originIsAllowed(origin) {
61 // put logic here to detect whether the specified origin is allowed.
62 return true;
63}
64
Simon Hunta25cdcd2015-01-22 13:11:42 -080065wsServer.on('request', function(request) {
66 console.log(); // newline after prompt
67 console.log("Origin: ", request.origin);
68
69 if (!originIsAllowed(request.origin)) {
70 // Make sure we only accept requests from an allowed origin
71 request.reject();
72 console.log((new Date()) + ' Connection from origin ' + request.origin + ' rejected.');
73 return;
74 }
75
Simon Hunt711ee242015-01-22 15:08:02 -080076 origin = request.origin;
77 connection = request.accept(null, origin);
Simon Hunta25cdcd2015-01-22 13:11:42 -080078
79
80 console.log((new Date()) + ' Connection accepted.');
81 rl.prompt();
82
Simon Hunta25cdcd2015-01-22 13:11:42 -080083 connection.on('message', function(message) {
84 if (message.type === 'utf8') {
85 console.log(); // newline after prompt
86 console.log('Received Message: ' + message.utf8Data);
87 //connection.sendUTF(message.utf8Data);
88 rl.prompt();
89 }
90 else if (message.type === 'binary') {
91 console.log('Received Binary Message of ' + message.binaryData.length + ' bytes');
92 //connection.sendBytes(message.binaryData);
93 }
94 });
95 connection.on('close', function(reasonCode, description) {
96 console.log((new Date()) + ' Peer ' + connection.remoteAddress + ' disconnected.');
Simon Hunt711ee242015-01-22 15:08:02 -080097 connection = null;
98 origin = null;
Simon Hunta25cdcd2015-01-22 13:11:42 -080099 });
100});
101
102
Simon Hunt711ee242015-01-22 15:08:02 -0800103setTimeout(doCli, 10); // allow async processes to write to stdout first
Simon Hunta25cdcd2015-01-22 13:11:42 -0800104
105function doCli() {
106 rl.prompt();
107 rl.on('line', function (line) {
108 var words = line.trim().split(' '),
Simon Hunt354c8c92015-01-22 15:32:51 -0800109 cmd = words.shift(),
110 str = words.join(' ');
111
112 if (!cmd) {
113 // repeat last command
114 cmd = lastcmd;
115 str = lastargs;
116 }
Simon Hunta25cdcd2015-01-22 13:11:42 -0800117
118 switch(cmd) {
Simon Hunt5724fb42015-02-05 16:59:40 -0800119 case 'l': listScenarios(); break;
Simon Hunt711ee242015-01-22 15:08:02 -0800120 case 'c': connStatus(); break;
121 case 'm': customMessage(str); break;
122 case 's': setScenario(str); break;
Simon Huntd450da92015-02-23 13:25:52 -0800123 case 'a': autoAdvance(); break;
Simon Hunt711ee242015-01-22 15:08:02 -0800124 case 'n': nextEvent(); break;
Simon Huntb0ec1e52015-01-28 18:13:49 -0800125 case 'r': restartScenario(); break;
Simon Hunt711ee242015-01-22 15:08:02 -0800126 case 'q': quit(); break;
127 case '?': showHelp(); break;
128 default: console.log('Say what?! (? for help)'); break;
129 }
130 lastcmd = cmd;
131 lastargs = str;
132 rl.prompt();
Simon Hunta25cdcd2015-01-22 13:11:42 -0800133
Simon Hunt711ee242015-01-22 15:08:02 -0800134 }).on('close', function () {
135 quit();
136 });
137}
Simon Hunta25cdcd2015-01-22 13:11:42 -0800138
Simon Hunt711ee242015-01-22 15:08:02 -0800139var helptext = '\n' +
Simon Hunt5724fb42015-02-05 16:59:40 -0800140 'l - list scenarios\n' +
Simon Hunt711ee242015-01-22 15:08:02 -0800141 'c - show connection status\n' +
142 'm {text} - send custom message to client\n' +
Simon Huntb0ec1e52015-01-28 18:13:49 -0800143 's {id} - load scenario {id}\n' +
Simon Hunt5724fb42015-02-05 16:59:40 -0800144 's - show scenario status\n' +
Simon Huntd450da92015-02-23 13:25:52 -0800145 'a - auto-send events\n' +
Simon Hunt711ee242015-01-22 15:08:02 -0800146 'n - send next event\n' +
Simon Huntb0ec1e52015-01-28 18:13:49 -0800147 'r - restart the scenario\n' +
Simon Hunt711ee242015-01-22 15:08:02 -0800148 'q - exit the server\n' +
149 '? - display this help text\n';
Simon Hunta25cdcd2015-01-22 13:11:42 -0800150
Simon Hunt711ee242015-01-22 15:08:02 -0800151function showHelp() {
152 console.log(helptext);
153}
154
Simon Hunt5724fb42015-02-05 16:59:40 -0800155function listScenarios() {
156 console.log('Scenarios ...');
157 console.log(scFiles.join(', '));
158 console.log();
159}
160
Simon Hunt711ee242015-01-22 15:08:02 -0800161function connStatus() {
162 if (connection) {
163 console.log('Connection from ' + origin + ' established.');
164 } else {
165 console.log('No connection.');
166 }
167}
168
169function quit() {
Simon Huntb0ec1e52015-01-28 18:13:49 -0800170 console.log('Quitting...');
Simon Hunt711ee242015-01-22 15:08:02 -0800171 process.exit(0);
172}
173
174function customMessage(m) {
175 if (connection) {
Simon Huntb0ec1e52015-01-28 18:13:49 -0800176 console.log('Sending message: ' + m);
Simon Hunt711ee242015-01-22 15:08:02 -0800177 connection.sendUTF(m);
178 } else {
179 console.warn('No current connection.');
180 }
181}
182
183function showScenarioStatus() {
184 var msg;
Simon Hunt626d2102015-01-29 11:54:50 -0800185 if (!scid) {
Simon Huntb0ec1e52015-01-28 18:13:49 -0800186 console.log('No scenario loaded.');
Simon Hunt711ee242015-01-22 15:08:02 -0800187 } else {
Simon Hunt626d2102015-01-29 11:54:50 -0800188 msg = 'Scenario: "' + scid + '", ' +
Simon Hunt711ee242015-01-22 15:08:02 -0800189 (scdone ? 'DONE' : 'next event: ' + evno);
190 console.log(msg);
191 }
192}
193
194function scenarioPath(evno) {
Simon Hunt74928302015-01-30 12:42:06 -0800195 var file = evno ? ('/' + eventsById[evno].fname) : '/scenario.json';
196 return scenarioRoot + scid + file;
Simon Hunt626d2102015-01-29 11:54:50 -0800197}
198
199
200function initScenario(verb) {
201 console.log(); // get past prompt
202 console.log(verb + ' scenario "' + scid + '"');
203 console.log(scdata.title);
204 scdata.description.forEach(function (d) {
205 console.log(' ' + d);
206 });
Simon Huntd450da92015-02-23 13:25:52 -0800207 autoLast = (scdata.params && scdata.params.lastAuto) || 0;
208 if (autoLast) {
209 console.log('[auto-advance: ' + autoLast + ']');
210 }
Simon Hunt626d2102015-01-29 11:54:50 -0800211 evno = 1;
212 scdone = false;
Simon Hunt74928302015-01-30 12:42:06 -0800213 readEventFilenames();
214}
215
216function readEventFilenames() {
217 var files = fs.readdirSync(scenarioRoot + scid),
218 eventCount = 0,
219 match, id, tag;
220
221 maxEvno = 0;
222
223 eventsById = {};
224 files.forEach(function (f) {
225 match = /^ev_(\d+)_(.*)\.json$/.exec(f);
226 if (match) {
227 eventCount++;
228 id = match[1];
229 tag = match[2];
230 eventsById[id] = {
231 fname: f,
232 num: id,
233 tag: tag
234 };
235 if (Number(id) > Number(maxEvno)) {
236 maxEvno = id;
237 }
238 }
239
240 });
241 console.log('[' + eventCount + ' events loaded, (max=' + maxEvno + ')]');
Simon Hunt711ee242015-01-22 15:08:02 -0800242}
243
244function setScenario(id) {
245 if (!id) {
246 return showScenarioStatus();
247 }
248
249 evdata = null;
Simon Hunt626d2102015-01-29 11:54:50 -0800250 scid = id;
Simon Hunt711ee242015-01-22 15:08:02 -0800251 fs.readFile(scenarioPath(), 'utf8', function (err, data) {
252 if (err) {
253 console.warn('No scenario named "' + id + '"', err);
Simon Hunt626d2102015-01-29 11:54:50 -0800254 scid = null;
Simon Hunt711ee242015-01-22 15:08:02 -0800255 } else {
Simon Hunt626d2102015-01-29 11:54:50 -0800256 scdata = JSON.parse(data);
257 initScenario('Loading');
Simon Hunta25cdcd2015-01-22 13:11:42 -0800258 }
259 rl.prompt();
Simon Hunta25cdcd2015-01-22 13:11:42 -0800260 });
Simon Hunt711ee242015-01-22 15:08:02 -0800261}
Simon Hunta25cdcd2015-01-22 13:11:42 -0800262
Simon Huntb0ec1e52015-01-28 18:13:49 -0800263function restartScenario() {
Simon Hunt626d2102015-01-29 11:54:50 -0800264 if (!scid) {
Simon Huntb0ec1e52015-01-28 18:13:49 -0800265 console.log('No scenario loaded.');
266 } else {
Simon Hunt626d2102015-01-29 11:54:50 -0800267 initScenario('Restarting');
Simon Huntb0ec1e52015-01-28 18:13:49 -0800268 }
269 rl.prompt();
270}
271
Simon Huntd450da92015-02-23 13:25:52 -0800272function eventAvailable() {
Simon Hunt626d2102015-01-29 11:54:50 -0800273 if (!scid) {
Simon Huntb0ec1e52015-01-28 18:13:49 -0800274 console.log('No scenario loaded.');
Simon Hunt711ee242015-01-22 15:08:02 -0800275 rl.prompt();
Simon Huntd450da92015-02-23 13:25:52 -0800276 return false;
277 }
278
279 if (!connection) {
280 console.log('No current connection.');
Simon Hunt711ee242015-01-22 15:08:02 -0800281 rl.prompt();
Simon Huntd450da92015-02-23 13:25:52 -0800282 return false;
283 }
284
285 if (Number(evno) > Number(maxEvno)) {
286 scdone = true;
287 console.log('Scenario DONE.');
288 return false;
289 }
290 return true;
291}
292
293function autoAdvance() {
294 if (evno > autoLast) {
295 console.log('[auto done]');
296 return;
297 }
298
299 // need to recurse with a callback, since each event send relies
300 // on an async load of event data...
301 function callback() {
302 if (eventAvailable() && evno <= autoLast) {
303 _nextEvent(callback);
Simon Hunt74928302015-01-30 12:42:06 -0800304 }
Simon Hunt711ee242015-01-22 15:08:02 -0800305 }
Simon Huntd450da92015-02-23 13:25:52 -0800306
307 callback();
308}
309
310function nextEvent() {
311 if (eventAvailable()) {
312 _nextEvent();
313 }
314}
315
316function _nextEvent(callback) {
317 var path = scenarioPath(evno);
318
319 fs.readFile(path, 'utf8', function (err, data) {
320 if (err) {
321 console.error('Oops error: ' + err);
322 } else {
323 evdata = JSON.parse(data);
324 console.log(); // get past prompt
325 console.log('Sending event #' + evno + ' [' + evdata.event +
326 '] from ' + eventsById[evno].fname);
327 connection.sendUTF(data);
328 evno++;
329 if (callback) {
330 callback();
331 }
332 }
333 rl.prompt();
334 });
Simon Hunta25cdcd2015-01-22 13:11:42 -0800335}