httpwsd.js 26 KB

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