From 1da0c46ab93edc8c11ba5b5dfcc25a85de79080e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ale=C5=A1=20Smodi=C5=A1?= <aless@guru.si>
Date: Mon, 12 Oct 2015 17:25:14 +0200
Subject: Add the express web framework to the node server.

---
 web/main.js      | 86 ++++++++++++++++++++++++++++++++++++--------------------
 web/package.json |  3 +-
 2 files changed, 57 insertions(+), 32 deletions(-)

(limited to 'web')

diff --git a/web/main.js b/web/main.js
index b3c096f..9ac0492 100644
--- a/web/main.js
+++ b/web/main.js
@@ -1,8 +1,14 @@
-var engine = require('engine.io'),
-    http_server = require('http').createServer(),
-    net = require('net'),
-    Promise = require('bluebird'),
-    log4js = require('log4js');
+var engine = require('engine.io'), // web sockets communication handler, sitting on the low-level HTTP handler
+    http_server = require('http').createServer(), // the low-level HTTP handler
+    net = require('net'), // TCP sockets library
+    Promise = require('bluebird'), // the promises library
+    log4js = require('log4js'), // the logger
+    express = require('express'), // library providing the Express web framework
+    http_app = express(); // web framework engine, sitting on the low-level HTTP handler
+
+// ================================================================================
+// The logger
+// ================================================================================
 
 log4js.loadAppender('file');
 log4js.addAppender(log4js.appenders.file(process.env.CODEQ_WEB_LOG || 'codeq-web.log'), 'log');
@@ -13,6 +19,7 @@ var logException = function (msg, error) {
     logger.error(msg);
 };
 
+// prevent the node from exiting because of an unhandled exception
 Promise.onPossiblyUnhandledRejection(function (error) {
     logException('Unhandled promise rejection: ' + error, error);
 });
@@ -21,8 +28,11 @@ process.on('uncaughtException', function (error) {
     logException('Uncaught exception: ' + error, error);
 });
 
-http_server.listen(8083, 'localhost');
+// ================================================================================
+// Low-level HTTP handlers, just do the minimum and forward to upper layers
+// ================================================================================
 
+// instantiate the web socket communication handler
 var server = new engine.Server({
     'pingTimeout': 60000, // in ms
     'pingInterval': 25000, // in ms
@@ -31,43 +41,59 @@ var server = new engine.Server({
     'cookie': false // no cookies
 });
 
+http_server.listen(8083, 'localhost');
+
 http_server.on('request', function (request, response) {
     var uriParts = request.url.split('/'),  // uriParts[0] will be an empty string, because uri must start with a /
-        params, session;
+        params;
 
     logger.debug('HTTP server request, URL: ' + request.url);
 
-    if ((uriParts.length <= 1) || (uriParts[1] === 'ws')) {
-        if ((uriParts.length == 3) && (uriParts[2].substring(0, 7) === 'logout?')) {
-            // special logout service
-            params = uriParts[2].substring(7).split('&')[0].split('=');
-            if ((params.length == 2) && (params[0] === 'sid')) {
-                session = sessions[params[1]];
-                if (session) {
-                    logger.debug('Logging out via AJAX, sid=' + params[1]);
-                    guiHandlers.logout(session, {'sid': params[1], 'action': 'logout'});
-                }
-            }
-            response.writeHead(200, {'Content-Type': 'text/plain'});
-            response.write('OK');
-            response.end();
+    if ((uriParts.length >= 3) && (uriParts[1] === 'ws')) {
+        // could be a web services request, check if there's a path following
+        params = uriParts[2];
+        if (!params || (params[0] === '?')) {
+            server.handleRequest(request, response); // no path follows /ws/, only query string, forward it to engine.io
         }
         else {
-            server.handleRequest(request, response);
+            http_app(request, response); // there's a path present beyond /ws/, forward it to express
         }
     }
     else {
-        response.writeHead(404, {'Content-Type': 'text/plain'});
-        response.write('Not found');
-        response.end();
+        http_app(request, response); // URI does not begin with /ws/, forward it to express
     }
 });
 
 http_server.on('upgrade', function (request, socket, head) {
-    server.handleUpgrade(request, socket, head);
+    server.handleUpgrade(request, socket, head); // initiating web socket communication
+});
+
+// ================================================================================
+// Express web framework, handle any AJAX here
+// ================================================================================
+
+http_app.get('/ws/logout', function (req, res) {
+    var sid = req.query.sid,
+        session = sessions[sid];
+    if (session) {
+        logger.debug('Logging out via AJAX, sid=' + sid);
+        guiHandlers.logout(session, {'sid': sid, 'action': 'logout'});
+    }
+    else if (sid) {
+        logger.warn('Cannot logout via AJAX: no session exists with sid=' + sid);
+    }
+    else {
+        logger.warn('No sid provided for AJAX logout');
+    }
+    res.set('Content-Type', 'text/plain');
+    res.send('OK');
 });
 
 
+// ================================================================================
+// Web services stuff - handlers for GUI requests, usually just forwarded to Python
+// ================================================================================
+
 // connected clients
 var sessions = {
     // sid: sessions description {sid: string, lastActivity: Date.now(), socket: net.Socket}
@@ -305,12 +331,10 @@ server.on('connection', function (socket) {
     });
 });
 
-//var sessionActivityTimeout = setInterval(function () {
-//    var sid;
-//}, 4200000); // once every 70 minutes
-
 
-// ========== Python server connection ==========
+// ================================================================================
+// Python server connection - sending to and receiving data from Python
+// ================================================================================
 
 var pythonClient = null, // the Socket for communication with the python server
     pythonSaturated = false, // whether the kernel-level buffer is full, in this case only push into the pythonQueue
diff --git a/web/package.json b/web/package.json
index d1eba7d..b8a4872 100644
--- a/web/package.json
+++ b/web/package.json
@@ -4,6 +4,7 @@
   "dependencies": {
     "engine.io": "1.5.x",
     "bluebird": "2.9.x",
-    "log4js": "0.6.x"
+    "log4js": "0.6.x",
+    "express": "4.13.x"
   }
 }
\ No newline at end of file
-- 
cgit v1.2.1