SHARE
TWEET

sprites.js

a guest Nov 26th, 2012 15,822 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // includes
  2. var cluster = require('cluster');
  3. var cpuCount = require('os').cpus().length;
  4. var util = require("util");
  5. var http = require("http");
  6. var url = require('url');
  7. var fs = require('fs');
  8.  
  9. // max number of connections
  10. var MAX_CONNECTIONS = 1000000;
  11. // keep alive interval
  12. var KEEPALIVE_INTERVAL = 2*60*1000;
  13.  
  14. // configuration file
  15. var config = { messages: 0 };
  16. // number of connections
  17. var connections = 0;
  18. // list of channels
  19. var channel = [];
  20. // map of ID -> node
  21. var idmap = new Array(MAX_CONNECTIONS);
  22. // head of free id list
  23. var pHead;
  24.  
  25. // attempt to read config file
  26. fs.readFile("config.json", "utf8", function(err, data)
  27. {
  28.     if(err) { return; }
  29.  
  30.     // parse config file
  31.     config = JSON.parse(data);
  32. });
  33.  
  34. // save config file every couple of minutes
  35. setInterval(function()
  36. {
  37.     // write config file
  38.     fs.writeFile("config.json", JSON.stringify(config, null, 4), "utf8");
  39. }, 2*60*1000);
  40.  
  41. // initialize free id list
  42. for(var v=0;v<MAX_CONNECTIONS;v++)
  43. {
  44.     // allocate list entry
  45.     var pCur = new Object();
  46.     // assign id
  47.     pCur.id = v;
  48.  
  49.     // if there is no head, we're it
  50.     if(pHead === undefined) { pCur.pNext = pCur.pPrev = pHead = pCur; }
  51.  
  52.     // insert into list
  53.     pCur.pNext = pHead;
  54.     pCur.pPrev = pHead.pPrev;
  55.     pHead.pPrev.pNext = pCur;
  56.     pHead.pPrev = pCur;
  57. }
  58.  
  59. // connect left and right nodes
  60. function connectNodesLR(pL, pR)
  61. {
  62.     pL.pR = pR;
  63.     pR.pL = pL;
  64.     return;
  65. }
  66.  
  67. // connect top and bottom nodes
  68. function connectNodesTB(pT, pB)
  69. {
  70.     pT.pB = pB;
  71.     pB.pT = pT;
  72.     return;
  73. }
  74.  
  75. // insert node at the end of list
  76. function insertNode(name, pNode)
  77. {
  78.     // reference channel
  79.     var c = channel[name];
  80.  
  81.     // if channel does not exist, create it
  82.     if(c === undefined) { c = channel[name] = []; c.pHead = c.pTail = 0; }
  83.  
  84.     // debug log
  85.     //util.log("node inserted into " + name + " with id " + pHead.id + " (" + connections + " connections)");
  86.  
  87.     // add node to idmap
  88.     idmap[pHead.id] = pNode;
  89.  
  90.     // remember head item so we can delete it
  91.     var pDelete = pHead;
  92.  
  93.     // remove id from free id list
  94.     pHead.pNext.pPrev = pHead.pPrev;
  95.     pHead.pPrev.pNext = pHead.pNext;
  96.     pHead = pHead.pNext;
  97.  
  98.     // delete old head
  99.     delete pDelete;
  100.  
  101.     // special case for head
  102.     if(c.pHead == 0)
  103.     {
  104.         // initialize head and tail
  105.         c.pHead = c.pTail = pNode;
  106.  
  107.         // initialize neighbors
  108.         c.pHead.pL = c.pHead;
  109.         c.pHead.pR = c.pHead;
  110.         c.pHead.pT = c.pHead;
  111.         c.pHead.pB = c.pHead;
  112.        
  113.         return;
  114.     }
  115.  
  116.     // note: insert can occur after any node, but we'll use tail
  117.     var pPrev = c.pTail;
  118.  
  119.     // reference nodes
  120.     var pL = pPrev;
  121.     var pR = pPrev.pR;
  122.     var pB = pL.pB;
  123.     var pT = pR.pT;
  124.     var pC = pNode;
  125.  
  126.     // apply connections
  127.     connectNodesLR(pL, pC);
  128.     connectNodesLR(pC, pR);
  129.     connectNodesTB(pL, pR);
  130.     connectNodesTB(pC, pB);
  131.     connectNodesTB(pT, pC);
  132.  
  133.     // special case adjustment for N=3
  134.     if(pL.pL == pR)
  135.     {
  136.         var p0 = c.pHead;
  137.         var p1 = c.pHead.pR;
  138.         var p2 = c.pHead.pR.pR;
  139.  
  140.         connectNodesTB(p0, p2);
  141.         connectNodesTB(p1, p0);
  142.         connectNodesTB(p2, p1);
  143.     }
  144.  
  145.     // update tail, if necessary
  146.     if(pL == c.pTail) { c.pTail = pC; }
  147.  
  148.     return;    
  149. }
  150.  
  151. // delete node
  152. function deleteNode(name, id, pNode)
  153. {
  154.     // reference channel
  155.     var c = channel[name];
  156.  
  157.     // debug log
  158.     //util.log("node " + id + " deleted from " + name);
  159.  
  160.     // remove node from idmap
  161.     delete idmap[id];
  162.  
  163.     // allocate list entry
  164.     var pCur = new Object();
  165.     // assign id
  166.     pCur.id = id;
  167.  
  168.     // if there is no head, we're it
  169.     if(pHead === undefined) { pCur.pNext = pCur.pPrev = pHead = pCur; }
  170.  
  171.     // insert into list
  172.     pCur.pNext = pHead;
  173.     pCur.pPrev = pHead.pPrev;
  174.     pHead.pPrev = pCur;
  175.     pHead.pPrev.pNext = pCur;
  176.  
  177.     // reference nodes
  178.     var pL = pNode.pL;
  179.     var pR = pNode.pR;
  180.     var pT = pNode.pT;
  181.     var pB = pNode.pB;
  182.  
  183.     // apply connections
  184.     connectNodesLR(pL, pR);
  185.     connectNodesTB(pL, pB);
  186.     connectNodesTB(pT, pR);
  187.  
  188.     // update tail, if necessary
  189.     if(pNode == c.pTail) { c.pTail = pNode.pL; }
  190.     // update head, if necessary
  191.     if(pNode == c.pHead) { c.pHead = pNode.pR; }
  192.  
  193.     // if this was the last node, delete the channel
  194.     if(pNode == c.pHead) { delete channel[name]; }
  195.     // special case adjustment for N=2
  196.     else if(c.pHead == c.pTail.pL)
  197.     {
  198.         var p0 = c.pHead;
  199.         var p1 = c.pHead.pR;
  200.  
  201.         connectNodesTB(p0, p1);
  202.         connectNodesTB(p1, p0);
  203.     }
  204.  
  205.     return;
  206. }
  207.  
  208. // send message
  209. function sendMessage(query)
  210. {
  211.     // reference channel
  212.     var c = channel[query.name];
  213.  
  214.     // sanity check
  215.     if(c === undefined) { return; }
  216.  
  217.     // find node for the specified ID
  218.     var pNode = idmap[query.id];
  219.  
  220.     // sanity check
  221.     if(pNode === undefined) { return; }
  222.  
  223.     // detect appropriate neighbor
  224.     if(parseFloat(query.wx) > 0) { pNode = pNode.pR; }
  225.     else if(parseFloat(query.wx) < 0) { pNode = pNode.pL; }
  226.     if(parseFloat(query.wy) > 0) { pNode = pNode.pB; }
  227.     else if(parseFloat(query.wy) < 0) { pNode = pNode.pT; }
  228.  
  229.     // forward the message (messages are delimited by 2 endlines)
  230.     pNode.write(JSON.stringify(query) + "\n\n");
  231.  
  232.     // increment messages count
  233.     config.messages++;
  234.  
  235.     // debug log
  236.     //util.log(query.uri + " (" + query.id + ") [" + query.x + ", " + query.y + "]");
  237.  
  238.     return;
  239. }
  240.  
  241. function closeConnection(name, id, res)
  242. {
  243.     // delete node
  244.     deleteNode(name, id, res);
  245.  
  246.     // decrement connection count
  247.     connections--;
  248.    
  249.     return;
  250. }
  251.  
  252. function onClose()
  253. {
  254.     // "this" is req.connection
  255.     closeConnection(this.name, this.id, this.res);
  256.     clearInterval(this.interval);
  257.    
  258.     return;
  259. }
  260.  
  261. function onRequest(req, res)
  262. {
  263.     // parse url
  264.     var parsed = url.parse(req.url, true);
  265.  
  266.     // handle channel join
  267.     if(parsed.pathname == '/channel/join')
  268.     {
  269.         // grab free id
  270.         var id = pHead.id;
  271.  
  272.         // prepare keep alive string
  273.         var keepAlive = JSON.stringify( { 'cmd' : 'set_id', 'id' : id } ) + "\n\n";
  274.  
  275.         // prepare keep alive function
  276.         res.keepAliveFunc = function()
  277.         {
  278.             // send keep alive
  279.             res.write( keepAlive );
  280.  
  281.         };
  282.  
  283.         // cache variables
  284.         req.connection.name = parsed.query.name;
  285.         req.connection.id = id;
  286.         req.connection.res = res;
  287.         req.connection.interval = setInterval(res.keepAliveFunc, KEEPALIVE_INTERVAL);
  288.  
  289.         // increment connection count
  290.         connections++;
  291.  
  292.         // insert node
  293.         insertNode(parsed.query.name, res);
  294.  
  295.         // add connection close listener
  296.         req.connection.addListener('close', onClose);
  297.         // disable Nagle algorithm
  298.         req.connection.setNoDelay(true);
  299.         // disable connection timeout
  300.         req.connection.setTimeout(0);
  301.  
  302.         // send header
  303.         res.writeHead(200, {'Content-type':'text/plain'});
  304.         // send id
  305.         res.write( JSON.stringify( { 'cmd' : 'set_id', 'id' : id } ) + "\n\n" );
  306.         res.write( JSON.stringify( { 'cmd' : 'set_reconnect', 'timeout' : 4*60*1000 } ) + "\n\n" );
  307.     }
  308.     // handle channel send
  309.     else if(parsed.pathname == '/channel/send')
  310.     {
  311.         // send message
  312.         sendMessage(parsed.query);
  313.         // this request is done
  314.         res.end( JSON.stringify( { 'result' : '0' } ) );
  315.     }
  316.     // handle network traversal
  317.     else if(parsed.pathname == '/channel/neighbors')
  318.     {
  319.         // reference channel
  320.         var c = channel[parsed.query.name];
  321.  
  322.         // if channel does not exist, return an error
  323.         if(c === undefined)
  324.         {
  325.             // this request is done
  326.             res.end( JSON.stringify( { 'result' : '1' } ) );
  327.         }
  328.         // otherwise, check for the specified ID
  329.         else
  330.         {
  331.             // find node for the specified ID
  332.             var pNode = (parsed.query.id) ? idmap[parsed.query.id] : c.pHead;
  333.  
  334.             // result state
  335.             var result;
  336.  
  337.             // if this ID doesn't exists, return an error
  338.             if(pNode === undefined)
  339.             {
  340.                 // prepare result
  341.                 result = JSON.stringify( { 'result' : '2' } );
  342.             }
  343.             // otherwise, use the ID
  344.             else
  345.             {
  346.                 // prepare result
  347.                 result = JSON.stringify(
  348.                 {
  349.                     'result' : 0,
  350.                     'p0' : pNode.pT.pL.connection.id,
  351.                     'p1' : pNode.pT.connection.id,
  352.                     'p2' : pNode.pT.pR.connection.id,
  353.                     'p3' : pNode.pL.connection.id,
  354.                     'p4' : pNode.connection.id,
  355.                     'p5' : pNode.pR.connection.id,
  356.                     'p6' : pNode.pB.pL.connection.id,
  357.                     'p7' : pNode.pB.connection.id,
  358.                     'p8' : pNode.pB.pR.connection.id
  359.                 } );
  360.             }
  361.  
  362.             // send result in jsonp format
  363.             res.end( "result('" + result + "')" );
  364.         }
  365.     }
  366.     // handle channel count
  367.     else if(parsed.pathname == '/connections/count')
  368.     {
  369.         // send header
  370.         res.writeHead(200, {'Content-type':'text/plain'});
  371.         // send connections count
  372.         res.end( "result('" + JSON.stringify( { 'count' : connections } ) + "')" );
  373.     }
  374.     // handle channel count
  375.     else if(parsed.pathname == '/messages/count')
  376.     {
  377.         // send header
  378.         res.writeHead(200, {'Content-type':'text/plain'});
  379.         // send messages count
  380.         res.end( "result('" + JSON.stringify( { 'count' : config.messages } ) + "')" );
  381.     }
  382.     // handle garbage collection
  383.     else if(parsed.pathname == '/debug/gc')
  384.     {
  385.         // perform garbage collection
  386.         gc();
  387.  
  388.         // send header
  389.         res.writeHead(200, {'Content-type':'text/plain'});
  390.         // send result
  391.         res.end( JSON.stringify( { 'result' : '0' } ) );
  392.     }
  393.     // handle anything else
  394.     else
  395.     {
  396.         // send header
  397.         res.writeHead(200, {'Content-type':'text/plain'});
  398.         // send result
  399.         res.end( "nope" );
  400.     }
  401. };
  402.  
  403. function onWorkerRequest(req, res)
  404. {
  405.     // parse url
  406.     var parsed = url.parse(req.url, true);
  407.  
  408.     // retrieve query        
  409.     var query = { fwd : parsed.query };
  410.  
  411.     // forward query
  412.     process.send(query);
  413.  
  414.     // this request is done
  415.     res.end( JSON.stringify( { 'result' : '0' } ) );
  416. }
  417.  
  418. // master needs to listen
  419. if(cluster.isMaster)
  420. {
  421.     // listen on public port 8080
  422.     http.createServer(onRequest).listen(8080);
  423.  
  424.     // Fork worker(s) to handle send requests
  425.     for(var v=0;v<cpuCount-1;v++)
  426.     {
  427.         var worker = cluster.fork();
  428.  
  429.         // Event on worker death
  430.         cluster.on('death', function(worker) { console.log('worker ' + worker.pid + ' died'); });
  431.  
  432.         // Event on message
  433.         worker.on('message', function(msg)
  434.         {
  435.             // process forwarded query
  436.             if(msg && msg.fwd) { sendMessage(msg.fwd); }
  437.         });
  438.     }
  439. }
  440. // worker needs to fwd requests
  441. else
  442. {
  443.     // listen on public port 8081
  444.     http.createServer(onWorkerRequest).listen(8081);
  445. }
RAW Paste Data
Want to get better at JavaScript?
Learn to code JavaScript in 2017
Pastebin PRO Summer Special!
Get 40% OFF on Pastebin PRO accounts!
Top