httpwsd.js 26 KB

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