httpwsd.js 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833
  1. /* This file is part of AToMPM - A Tool for Multi-Paradigm Modelling
  2. * Copyright 2011 by the AToMPM team and licensed under the LGPL
  3. * See COPYING.lesser and README.md in the root of this project for full details
  4. */
  5. /*********************************** IMPORTS **********************************/
  6. var _util = require("util"),
  7. _fs = require('fs'),
  8. _http = require('http'),
  9. _path = require('path'),
  10. _url = require('url'),
  11. _utils = require('./utils'),
  12. _sio = require('socket.io'),
  13. _cp = require('child_process'),
  14. _fspp = require('./___fs++'),
  15. _duri = require('./___dataurize');
  16. /*********************************** GLOBALS **********************************/
  17. /* an array of WebWorkers
  18. ... each has its own mmmk instance */
  19. var workers = new Array();
  20. /* an array of response objects
  21. ... for workers to write on when they complete requests */
  22. var responses = new Array();
  23. /* a map of worker ids to socket.io socket session ids
  24. ... each socket is registered to exactly one worker
  25. ... several sockets may be registered to the same worker */
  26. var workerIds2socketIds = {};
  27. /************************************ UTILS ***********************************/
  28. /** Remove invalid characters from a string. **/
  29. function __clean_string(s)
  30. {
  31. if (s == undefined) {
  32. return s;
  33. }
  34. s = JSON.stringify(s);
  35. s = s.replace(/'/g, '');
  36. s = s.replace(/"/g, '');
  37. s = s.replace(/‘/g, '');
  38. s = s.replace(/’/g, '');
  39. s = s.replace(/\\/g, '\\');
  40. s = s.replace(/\//g, '\/');
  41. s = s.replace(/\\n/g, ' ');
  42. return s;
  43. }
  44. /** Syntactic sugar to build and send HTTP responses **/
  45. function __respond(response, statusCode, reason, data, headers)
  46. {
  47. response.writeHead(
  48. statusCode,
  49. __clean_string(reason),
  50. (headers || {'Content-Type': 'text/plain',
  51. 'Access-Control-Allow-Origin': '*'}));
  52. var encoding =
  53. (headers &&
  54. (headers['Content-Type'].match(/image/) ||
  55. headers['Content-Type'].match(/pdf/) ||
  56. headers['Content-Type'].match(/zip/) ) ?
  57. 'binary' :
  58. 'utf8'),
  59. content = reason || data;
  60. if( _utils.isObject(content) )
  61. response.end(_utils.jsons(content,null,'\t'), encoding);
  62. else
  63. response.end(content, encoding);
  64. }
  65. /** Syntactic sugar to build and send a socket.io message **/
  66. function __send(socket, statusCode, reason, data, headers)
  67. {
  68. socket.json.emit('message',
  69. {'statusCode':statusCode,
  70. 'reason':reason,
  71. 'headers':(headers || {'Content-Type': 'text/plain',
  72. 'Access-Control-Allow-Origin': '*'}),
  73. 'data':data});
  74. }
  75. /************************************ LOGIC ***********************************/
  76. var httpserver = _http.createServer(
  77. function(req, resp)
  78. {
  79. var url = _url.parse(req.url,true);
  80. url.pathname = decodeURI(url.pathname);
  81. /* serve client */
  82. if( req.method == 'GET' && url.pathname == '/atompm' )
  83. _fs.readFile('./client/atompm.html', 'utf8',
  84. function(err, data)
  85. {
  86. if(err)
  87. __respond(resp,500,String(err));
  88. else
  89. __respond(resp,200,'',data,{'Content-Type': 'text/html'});
  90. });
  91. else if( req.method == 'GET' && url.pathname == '/favicon.png' )
  92. _fs.readFile('./favicon.png', 'binary',
  93. function(err, data)
  94. {
  95. if(err)
  96. __respond(resp,500,String(err));
  97. else
  98. __respond(resp,200,'',data,{'Content-Type': 'image/png'});
  99. });
  100. /* provide an interface to the unfortunately unavoidable dataurize
  101. module which returns data URIs for resources at arbitrary URLs */
  102. else if( req.method == 'GET' && url.pathname == '/datauri' )
  103. {
  104. var target = _url.parse(decodeURI(url['query']['target']));
  105. _duri.dataurize(
  106. target,
  107. function(err,datauri)
  108. {
  109. if(err)
  110. __respond(resp,500,_utils.jsons(err));
  111. else
  112. __respond(resp,200,'',datauri);
  113. });
  114. }
  115. /* serve metamodels, buttons models and their icons */
  116. else if( req.method == 'GET' &&
  117. (url.pathname.match(/\.metamodel$/) ||
  118. url.pathname.match(/\.buttons.model$/) ||
  119. url.pathname.match(/\.icon\.png$/i)) )
  120. {
  121. var isIcon = url.pathname.match(/\.icon\.png$/i);
  122. _fs.readFile('./users/'+url.pathname, (isIcon ? 'binary' : 'utf8'),
  123. function(err, data)
  124. {
  125. if(err)
  126. __respond(resp,500,String(err));
  127. else
  128. {
  129. var contentType =
  130. (isIcon ?
  131. {'Content-Type': 'image/png'} :
  132. {'Content-Type': 'application/json'});
  133. __respond(resp,200,'',data,contentType);
  134. }
  135. });
  136. }
  137. /* serve ordinary files (e.g., js includes, images, css)
  138. NOTE:: distinguish between atompm images (e.g., grid background,
  139. filebrowser icons) and CS/Images */
  140. else if( req.method == 'GET' &&
  141. (url.pathname.match(/\.html$/) ||
  142. url.pathname.match(/\.css$/) ||
  143. url.pathname.match(/\.js$/) ||
  144. url.pathname.match(/\.pdf$/) ||
  145. url.pathname.match(/\.png$/i) ||
  146. url.pathname.match(/\.jpg$/i) ||
  147. url.pathname.match(/\.jpeg$/i) ||
  148. url.pathname.match(/\.gif$/i) ||
  149. url.pathname.match(/\.svg$/i)) )
  150. {
  151. var isImage = url.pathname.match(/\.png$/i) ||
  152. url.pathname.match(/\.jpg$/i) ||
  153. url.pathname.match(/\.jpeg$/i) ||
  154. url.pathname.match(/\.gif$/i) ||
  155. url.pathname.match(/\.svg$/i),
  156. isText = ! isImage && ! url.pathname.match(/\.pdf$/);
  157. if( isImage && ! url.pathname.match(/^\/client\/media\//) )
  158. url.pathname = '/users/'+url.pathname;
  159. _fs.readFile('.'+url.pathname, (isText ? 'utf8' : 'binary'),
  160. function(err, data)
  161. {
  162. if(err)
  163. __respond(resp,500,String(err));
  164. else
  165. {
  166. var contentType =
  167. (url.pathname.match(/\.html$/) ?
  168. {'Content-Type': 'text/html'} :
  169. url.pathname.match(/\.css$/) ?
  170. {'Content-Type': 'text/css'} :
  171. url.pathname.match(/\.js$/) ?
  172. {'Content-Type': 'application/javascript'} :
  173. url.pathname.match(/\.pdf$/) ?
  174. {'Content-Type': 'application/pdf'} :
  175. url.pathname.match(/\.png$/i) ?
  176. {'Content-Type': 'image/png'} :
  177. url.pathname.match(/\.jpg$/i) ||
  178. url.pathname.match(/\.jpeg$/i) ?
  179. {'Content-Type': 'image/jpeg'} :
  180. url.pathname.match(/\.gif$/i) ?
  181. {'Content-Type': 'image/gif'} :
  182. url.pathname.match(/\.svg$/i) ?
  183. {'Content-Type': 'image/svg+xml'} :
  184. undefined);
  185. __respond(resp,200,'',data,contentType);
  186. }
  187. });
  188. }
  189. /* serve encrypted user password */
  190. else if( req.method == 'GET' && url.pathname == '/passwd' )
  191. _fs.readFile('./users/'+url['query']['username']+'/passwd', 'utf8',
  192. function(err, data)
  193. {
  194. if(err)
  195. __respond(resp,500,String(err));
  196. else
  197. __respond(resp,200,'',data,{'Content-Type': 'text/html'});
  198. });
  199. /* create new user
  200. 1. make sure user doesn't already exist
  201. 2. make a new copy of ./users/(default)
  202. 3. create password file */
  203. else if( req.method == 'POST' && url.pathname == '/user' )
  204. {
  205. var userdir = './users/'+url['query']['username'];
  206. _fs.exists(userdir,
  207. function(exists)
  208. {
  209. if( exists )
  210. {
  211. __respond(resp,500,'username already exists');
  212. return;
  213. }
  214. _fspp.cp('./users/(default)/',userdir,
  215. function(err, stdout, stderr)
  216. {
  217. if( err )
  218. {
  219. __respond(resp,500,String(err));
  220. return;
  221. }
  222. _fs.writeFile(
  223. userdir+'/passwd',
  224. url['query']['password'],
  225. function(err)
  226. {
  227. if( err )
  228. __respond(resp,500,String(err));
  229. else
  230. __respond(resp,200);
  231. });
  232. });
  233. });
  234. }
  235. /* serve [a subset of] user preferences */
  236. else if( req.method == 'GET' && url.pathname.match(/prefs$/) )
  237. _fs.readFile('./users/'+url.pathname, 'utf8',
  238. function(err, data)
  239. {
  240. if(err)
  241. __respond(resp,500,String(err));
  242. else if( url['query']['subset'] == undefined )
  243. __respond(resp,200,'',data);
  244. else
  245. try
  246. {
  247. __respond(
  248. resp,
  249. 200,
  250. '',
  251. _utils.splitDict(
  252. _utils.jsonp(data),
  253. _utils.jsonp(url['query']['subset'])));
  254. }
  255. catch(err) {__respond(resp,500,String(err));}
  256. });
  257. /* update user preferences
  258. 1 retrieve all post data
  259. 2 read prefs file from disk
  260. 3 apply changes
  261. 4 write updated prefs to disk */
  262. else if( req.method == 'PUT' && url.pathname.match(/prefs$/) )
  263. {
  264. var reqData = '';
  265. req.addListener("data", function(chunk) {reqData += chunk;});
  266. req.addListener("end",
  267. function()
  268. {
  269. _fs.readFile('./users/'+url.pathname, 'utf8',
  270. function(err, prefs)
  271. {
  272. if(err)
  273. __respond(resp,500,String(err));
  274. else
  275. {
  276. try
  277. {
  278. prefs = _utils.jsonp(prefs);
  279. reqData = _utils.jsonp(reqData);
  280. }
  281. catch(err)
  282. {
  283. __respond(resp,500,String(err));
  284. return;
  285. }
  286. for( var pref in reqData )
  287. prefs[pref]['value'] = reqData[pref];
  288. _fs.writeFile(
  289. './users/'+url.pathname,
  290. _utils.jsons(prefs,null,'\t'),
  291. function(err, data)
  292. {
  293. if(err)
  294. __respond(resp,500,String(err));
  295. else
  296. __respond(resp,200);
  297. });
  298. }
  299. });
  300. });
  301. }
  302. /* delete specified file/folder */
  303. else if( req.method == 'DELETE' && url.pathname.match(/\.(file|folder)$/) )
  304. {
  305. if (url.pathname.match('_Trash_')) {
  306. __respond(resp,500,"cannot remove trash!");
  307. } else {
  308. var matches = url.pathname.match(/^\/(.*?)\/(.*\/)?(.*)\.(file|folder)$/),
  309. username = matches[1],
  310. folder = matches[2] || '',
  311. fname = matches[3],
  312. userdir = './users/'+username+'/',
  313. ondelete =
  314. function(err, stdout, stderr)
  315. {
  316. if( err )
  317. __respond(resp,500,String(err));
  318. else
  319. __respond(resp,200);
  320. },
  321. deletef =
  322. function(response)
  323. {
  324. var newname = userdir+'_Trash_/'+folder+fname;
  325. if (_fs.existsSync(newname)) {
  326. if (url.pathname.match(/\.folder$/)) {
  327. _fspp.deleteFolderRecursive(newname);
  328. } else {
  329. _fs.unlink(newname);
  330. }
  331. }
  332. _fspp.mv(userdir+folder+fname,userdir+'_Trash_/'+folder,ondelete);
  333. };
  334. _fs.exists(userdir+'_Trash_/'+folder,
  335. function(exists)
  336. {
  337. if( ! exists )
  338. _fspp.mkdirs(userdir+'_Trash_/'+folder,deletef);
  339. else {
  340. deletef();
  341. }
  342. });
  343. }
  344. }
  345. /* create folder */
  346. else if( req.method == 'POST' && url.pathname.match(/\.folder$/) )
  347. {
  348. var matches = url.pathname.match(/^\/(.*?)\/(.*)\.folder$/),
  349. username = matches[1],
  350. folder = matches[2],
  351. userdir = './users/'+username+'/',
  352. oncreate =
  353. function(err, stdout, stderr)
  354. {
  355. if( err )
  356. __respond(resp,500,String(err));
  357. else
  358. __respond(resp,200);
  359. };
  360. _fs.exists(userdir+folder,
  361. function(exists)
  362. {
  363. if( ! exists )
  364. _fspp.mkdirs(userdir+folder,oncreate);
  365. else {
  366. oncreate("folder " + folder + " already exists");
  367. }
  368. });
  369. }
  370. /* rename file/folder (or move) */
  371. else if( req.method == 'PUT' && url.pathname.match(/\.(folder|file)$/) )
  372. {
  373. req.setEncoding('utf8');
  374. var data = '';
  375. req.addListener("data", function(chunk) {data += chunk;});
  376. req.addListener("end",
  377. function() {
  378. data = _utils.jsonp(data);
  379. if (data.match(/^\//)) {
  380. // move
  381. var matches = url.pathname.match(/^\/(.*?)\/(.*\/)?(.*)\.(file|folder)$/),
  382. username = matches[1],
  383. folder = matches[2] || '',
  384. fname = matches[3],
  385. userdir = './users/'+username,
  386. onmove =
  387. function(err, stdout, stderr)
  388. {
  389. if( err )
  390. __respond(resp,500,String(err));
  391. else
  392. __respond(resp,200);
  393. };
  394. if (_fs.existsSync(userdir+data+fname)) {
  395. if (url.pathname.match(/\.folder$/)) {
  396. _fspp.deleteFolderRecursive(userdir+data+fname);
  397. } else {
  398. _fs.unlink(newname);
  399. }
  400. }
  401. _fspp.mv(userdir+"/"+folder+fname,userdir+data,onmove);
  402. } else {
  403. // rename
  404. var matches = url.pathname.match(/^\/(.*?)\/(.*\/)?(.*)\.(file|folder)$/),
  405. username = matches[1],
  406. folder = matches[2] || '',
  407. fname = matches[3],
  408. userdir = './users/'+username+'/',
  409. onrename =
  410. function(err, stdout, stderr)
  411. {
  412. if( err )
  413. __respond(resp,500,String(err));
  414. else
  415. __respond(resp,200);
  416. };
  417. _fs.rename(userdir+folder+fname,userdir+folder+data,onrename);
  418. }
  419. }
  420. );
  421. }
  422. else if (req.method == 'POST' && url.pathname.match(/\.formalism$/)) {
  423. // create new formalism
  424. var matches = url.pathname.match(/^(.*)\/(.*)\.formalism$/),
  425. username = matches[1],
  426. formalism = matches[2],
  427. userdir = './users/'+username+"/",
  428. oncreatefolder =
  429. function(err, stdout, stderr)
  430. {
  431. if( err )
  432. __respond(resp,500,String(err));
  433. else {
  434. _fs.createReadStream(userdir+"Formalisms/__Templates__/MetamodelTemplate.model").pipe(_fs.createWriteStream(userdir+"Formalisms/"+formalism+"/"+formalism+".model"));
  435. _fs.createReadStream(userdir+"Formalisms/__Templates__/ConcreteSyntaxTemplate.model").pipe(_fs.createWriteStream(userdir+"Formalisms/"+formalism+"/"+formalism+".defaultIcons.model"));
  436. _fs.createReadStream(userdir+"Formalisms/__Templates__/MetamodelTemplate.metamodel").pipe(_fs.createWriteStream(userdir+"Formalisms/"+formalism+"/"+formalism+".metamodel"));
  437. _fs.createReadStream(userdir+"Formalisms/__Templates__/ConcreteSyntaxTemplate.defaultIcons.metamodel").pipe(_fs.createWriteStream(userdir+"Formalisms/"+formalism+"/"+formalism+".defaultIcons.metamodel"));
  438. _fs.createReadStream(userdir+"Formalisms/__Templates__/T_TransformationTemplate.model").pipe(_fs.createWriteStream(userdir+"Formalisms/"+formalism+"/OperationalSemantics/T_OperationalSemantics.model"));
  439. _fs.createReadStream(userdir+"Formalisms/__Templates__/T_TransformationTemplate.model").pipe(_fs.createWriteStream(userdir+"Formalisms/"+formalism+"/TranslationalSemantics/T_TranslationalSemantics.model"));
  440. __respond(resp,200);
  441. }
  442. };
  443. _fs.mkdir(userdir+"Formalisms/"+formalism,function(err, stdout, stderr) {
  444. if( err )
  445. __respond(resp,500,String(err));
  446. else {
  447. _fs.mkdirSync(userdir+"Formalisms/"+formalism+"/OperationalSemantics");
  448. _fs.mkdir(userdir+"Formalisms/"+formalism+"/TranslationalSemantics", oncreatefolder);
  449. }
  450. });
  451. }
  452. else if (req.method == 'POST' && url.pathname.match(/\.transformation$/)) {
  453. // create new transformation
  454. var matches = url.pathname.match(/^\/(.*?)\/(.*)\.transformation$/),
  455. username = matches[1],
  456. userdir = './users/'+username+"/";
  457. _fs.createReadStream(userdir+"Formalisms/__Templates__/T_TransformationTemplate.model").pipe(_fs.createWriteStream('./users/'+url.pathname.slice(0, -(".transformation".length))));
  458. __respond(resp,200);
  459. }
  460. else if (req.method == 'POST' && url.pathname.match(/\.rule$/)) {
  461. // create new rule
  462. var matches = url.pathname.match(/^\/(.*?)\/(.*)\.rule$/),
  463. username = matches[1],
  464. userdir = './users/'+username+"/";
  465. _fs.createReadStream(userdir+"Formalisms/__Templates__/R_RuleTemplate.model").pipe(_fs.createWriteStream('./users/'+url.pathname.slice(0, -(".rule".length))));
  466. __respond(resp,200);
  467. }
  468. /* extract user-uploaded archive to specified folder
  469. 1. read in all data
  470. 2. make sure destination exists and is a directory
  471. 3. write data to temp file (upload###.zip)
  472. 4. extract temp file and remove it
  473. NOTE:: it's not clear why (despite hours of googling) but the
  474. "req.setEncoding('utf8')" statement makes the difference
  475. between retrieving correct and corrupted (when non-text
  476. files in zip) data */
  477. else if( req.method == 'PUT' && url.pathname.match(/\.file$/) )
  478. {
  479. req.setEncoding('utf8');
  480. let reqData = '',
  481. tmpzip = 'upload'+Date.now()+'.zip',
  482. destdir = './users/'+url.pathname.match(/(.*)\.file$/)[1]+'/';
  483. if( url.pathname.contains("..") || url.pathname.contains(";") ) {
  484. __respond(resp, 404,
  485. 'invalid pathname, no semicolons or .. allowed :: ' + url.pathname);
  486. return;
  487. }
  488. req.addListener("data", function(chunk) {reqData += chunk;});
  489. req.addListener("end",
  490. function()
  491. {
  492. _fs.stat(destdir,
  493. function(err,stats)
  494. {
  495. if( err )
  496. __respond(resp,404,String(err));
  497. else if( ! stats.isDirectory() )
  498. __respond(resp,404,
  499. 'destination is not a directory :: '+destdir);
  500. else
  501. _fs.writeFile(
  502. destdir+tmpzip,eval('('+reqData+')'),
  503. 'binary',
  504. function(err)
  505. {
  506. _cp.exec('cd '+destdir+'; unzip -o '+tmpzip,
  507. function(err, stdout, stderr)
  508. {
  509. if( err )
  510. __respond(resp,500,String(err));
  511. else
  512. __respond(resp,200);
  513. _fs.unlink(destdir+tmpzip, function(err){
  514. console.log("Unlinked " + destdir + tmpzip);
  515. }
  516. );
  517. });
  518. });
  519. });
  520. });
  521. }
  522. /* serve specified file/folder within a zip file */
  523. else if( req.method == 'GET' && url.pathname.match(/\.file$/) )
  524. {
  525. let matches = url.pathname.match(/^\/(.*?)\/(.*)\.file$/),
  526. username = matches[1],
  527. fname = './'+matches[2],
  528. userdir = './users/'+username+'/',
  529. tmpzip = 'download'+Date.now()+'.zip';
  530. if( username.contains("..") || username.contains(";") ) {
  531. __respond(resp, 404,
  532. 'invalid username, no colons or .. allowed :: ' + username);
  533. return;
  534. }
  535. _fs.exists(userdir+fname,
  536. function(exists)
  537. {
  538. if( ! exists )
  539. __respond(resp,404,
  540. 'requested file/folder does not exist :: '+fname);
  541. else
  542. _cp.exec('cd '+userdir+'; zip -r '+tmpzip+' "'+fname+'"',
  543. function(err, stdout, stderr)
  544. {
  545. if( err )
  546. __respond(resp,500,String(err));
  547. else
  548. _fs.readFile(userdir+tmpzip,
  549. function(err, data)
  550. {
  551. __respond(resp,200,'',data,
  552. {'Content-Type':'application/zip',
  553. 'Content-Disposition':
  554. 'attachment; filename="'+tmpzip+'"'});
  555. _fs.unlink(userdir+tmpzip, function(err){
  556. console.log("Unlinked " + userdir+tmpzip);
  557. });
  558. });
  559. });
  560. });
  561. }
  562. /* serve list of all files */
  563. else if( req.method == 'GET' &&
  564. url.pathname.match(/^\/.+\/filelist$/) )
  565. {
  566. var matches = url.pathname.match(/^\/(.+)\/filelist$/);
  567. _fspp.findfiles('./users/'+matches[1],
  568. function(err, stdout, stderr)
  569. {
  570. if( err )
  571. __respond(resp,404,String(err));
  572. else
  573. __respond(resp,200,'',stdout);
  574. });
  575. }
  576. /* spawn new worker */
  577. else if( (url.pathname == '/csworker' || url.pathname == '/asworker')
  578. && req.method == 'POST' )
  579. {
  580. /* setup and store new worker */
  581. var worker = _cp.fork(_path.join(__dirname, '__worker.js')),
  582. wid = workers.push(worker)-1;
  583. workerIds2socketIds[wid] = [];
  584. worker.on('message',
  585. function(msg)
  586. {
  587. /* push changes (if any) to registered sockets... even empty
  588. changelogs are pushed to facilitate sequence number-based
  589. ordering */
  590. if( msg['changelog'] != undefined )
  591. {
  592. var _msg = {'changelog':msg['changelog'],
  593. 'sequence#':msg['sequence#'],
  594. 'hitchhiker':msg['hitchhiker']};
  595. workerIds2socketIds[wid].forEach(
  596. function(sid)
  597. {
  598. __send(
  599. wsserver.sockets.sockets[sid],
  600. undefined,
  601. undefined,
  602. _msg);
  603. });
  604. }
  605. /* respond to a request */
  606. if( msg['respIndex'] != undefined )
  607. __respond(
  608. responses[msg['respIndex']],
  609. msg['statusCode'],
  610. msg['reason'],
  611. JSON.stringify(
  612. {'headers':
  613. (msg['headers'] ||
  614. {'Content-Type': 'text/plain',
  615. 'Access-Control-Allow-Origin': '*'}),
  616. 'data':msg['data'],
  617. 'sequence#':msg['sequence#']}),
  618. {'Content-Type': 'application/json'});
  619. });
  620. worker.send(
  621. {'workerType':url.pathname,
  622. 'workerId':wid});
  623. /* respond worker id (used to identify associated worker) */
  624. __respond(
  625. resp,
  626. 201,
  627. '',
  628. ''+wid);
  629. return;
  630. }
  631. /* check for worker id and it's validity */
  632. else if( url['query'] == undefined ||
  633. url['query']['wid'] == undefined )
  634. __respond(resp, 400, 'missing worker id');
  635. else if( workers[url['query']['wid']] == undefined )
  636. __respond(resp, 400, 'invalid worker id :: '+url['query']['wid']);
  637. /* save resp object and forward request to worker (if request is PUT or
  638. POST, recover request data first)
  639. TBI:: only registered sockets should be allowed to speak to worker
  640. ... one way of doing this is forcing request urls to contain
  641. cid=socket.id## */
  642. else if( req.method == 'PUT' || req.method == 'POST' )
  643. {
  644. var reqData = '';
  645. req.addListener("data", function(chunk) {reqData += chunk;});
  646. req.addListener("end",
  647. function()
  648. {
  649. workers[url['query']['wid']].send(
  650. {'method':req.method,
  651. 'uri':url.pathname,
  652. 'reqData':(reqData == '' ?
  653. undefined :
  654. eval('('+reqData+')')),
  655. 'uriData':url['query'],
  656. 'respIndex':responses.push(resp)-1});
  657. });
  658. }
  659. else
  660. workers[url['query']['wid']].send(
  661. {'method':req.method,
  662. 'uri':url.pathname,
  663. 'uriData':url['query'],
  664. 'respIndex':responses.push(resp)-1});
  665. });
  666. httpserver.listen(8124);
  667. var wsserver = _sio.listen(httpserver);
  668. wsserver.configure(
  669. function()
  670. {
  671. wsserver.set('log level',2);
  672. });
  673. wsserver.sockets.on('connection',
  674. function(socket)
  675. {
  676. /* unregister this socket from the specified worker ... when a worker
  677. has no more registered sockets, terminate it */
  678. function unregister(wid)
  679. {
  680. var i = workerIds2socketIds[wid].indexOf(socket.id);
  681. if( i == -1 )
  682. __send(socket,403,'already unregistered from worker');
  683. else
  684. {
  685. workerIds2socketIds[wid].splice(i,1);
  686. if( workerIds2socketIds[wid].length == 0 )
  687. {
  688. workers[wid].kill();
  689. workers[wid] = undefined;
  690. delete workerIds2socketIds[wid];
  691. }
  692. __send(socket,200);
  693. }
  694. }
  695. /* onmessage : on reception of data from client */
  696. socket.on('message',
  697. function(msg/*{method:_,url:_}*/)
  698. {
  699. var url = _url.parse(msg.url,true);
  700. /* check for worker id and it's validity */
  701. if( url['query'] == undefined ||
  702. url['query']['wid'] == undefined )
  703. return __send(socket,400,'missing worker id');
  704. var wid = url['query']['wid'];
  705. if( workers[wid] == undefined )
  706. __send(socket,400,'unknown worker id :: '+wid);
  707. /* register socket for requested worker */
  708. else if( msg.method == 'POST' &&
  709. url.pathname.match(/changeListener$/) )
  710. {
  711. if( workerIds2socketIds[wid].indexOf(socket.id) > -1 )
  712. __send(socket,403,'already registered to worker');
  713. else
  714. {
  715. workerIds2socketIds[wid].push(socket.id);
  716. __send(socket,201);
  717. }
  718. }
  719. /* unregister socket for requested worker */
  720. else if( msg.method == 'DELETE' &&
  721. url.pathname.match(/changeListener$/) )
  722. unregister(wid);
  723. /* unsupported request */
  724. else
  725. __send(socket,501);
  726. });
  727. /* ondisconnect : on disconnection of socket */
  728. socket.on('disconnect',
  729. function()
  730. {
  731. for( var wid in workerIds2socketIds )
  732. for( var i in workerIds2socketIds[wid] )
  733. if( workerIds2socketIds[wid][i] == socket.id )
  734. {
  735. unregister(wid);
  736. return;
  737. }
  738. });
  739. });