httpwsd.js 26 KB

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