00001 #!node
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013 var cluster = require('cluster');
00014 var numCPUs = require("os").cpus().length;
00015 var fs = require('fs');
00016 var path_module = require('path');
00017 var module_holder = {};
00018 var workerData = {
00019 "serverbase": {}
00020 };
00021 var child_process = require('child_process');
00022
00023 var util = require('util');
00024 var log_file = fs.createWriteStream('/tmp/server.' + process.env["USER"] + '.log', { flags: 'a' });
00025 var log_stdout = process.stdout;
00026
00027 var getversion = function () {
00028 console.log("Getting Server Version");
00029 if (fs.existsSync("./version.txt")) {
00030 console.log("Reading Server Version from File");
00031 return "" + fs.readFileSync("./version.txt");
00032 }
00033 else {
00034 child_process.exec("git describe --tags", function (error, stdout, stderr) {
00035 version = stdout.trim() + "-Git";
00036 child_process.exec("git status --porcelain", function (error, stdout) {
00037 if (stdout.length > 0) {
00038 version += "*";
00039 }
00040 });
00041 });
00042 }
00043 }
00044 var version = getversion();
00045
00046 var config = {
00047 ignored_modules: [],
00048 baseport: 8080,
00049 portOffset: 80,
00050 hostname: "localhost",
00051 module_config: []
00052 };
00053 function loadConfig() {
00054 if (fs.existsSync("config.json")) {
00055 config = JSON.parse(fs.readFileSync("config.json"));
00056 } else {
00057 fs.writeFileSync("config.json", JSON.stringify(config));
00058 }
00059
00060 if (process.env.ARTDAQDEMO_BASE_PORT) {
00061 config.baseport = parseInt(process.env["ARTDAQDEMO_BASE_PORT"]) + config.portOffset;
00062 }
00063
00064 if (config.hostname === "localhost" && cluster.isMaster) {
00065 console.log("Listening only on localhost. To listen on a different address, set \"hostname\" in config.json.\nUse \"0.0.0.0\" to listen on all interfaces.");
00066 }
00067 }
00068
00069 loadConfig();
00070
00071 console.log = function (d) {
00072 log_file.write(util.format(d) + '\n');
00073 log_stdout.write(util.format(d) + '\n');
00074 };
00075
00076 function LoadCerts(path) {
00077 var output = [];
00078 var files = fs.readdirSync(path);
00079 for (var i = 0; i < files.length; i++) {
00080 if (files[i].search(".pem") > 0 || files[i].search(".crt") > 0) {
00081 output.push(fs.readFileSync(path + "/" + files[i]));
00082 }
00083 }
00084 return output;
00085 }
00086
00087 function GetCILogonCRL(path) {
00088
00089 var file = fs.createWriteStream(path_module.join(path, "cilogon-basic.r0"));
00090 http.get("http://crl-cilogon.ncsa-security.net/cilogon-basic.r0", function (res) { res.pipe(file); });
00091 var file2 = fs.createWriteStream(path_module.join(path, "cilogon-basic.crl"));
00092 http.get("http://crl-cilogon.ncsa-security.net/cilogon-basic.crl", function (res) { res.pipe(file2); });
00093 }
00094
00095 function LoadCRLs(path) {
00096 GetCILogonCRL(path);
00097 var output = [];
00098 var files = fs.readdirSync(path);
00099 for (var i = 0; i < files.length; i++) {
00100 if (files[i].search(".r0") > 0 || files[i].search(".crl") > 0) {
00101 output.push(fs.readFileSync(path + "/" + files[i]));
00102 }
00103 }
00104 return output;
00105 }
00106
00107
00108
00109 function LoadModules(path) {
00110 var stat = fs.lstatSync(path);
00111 if (stat.isDirectory()) {
00112
00113 var files = fs.readdirSync(path);
00114 var f, l = files.length;
00115 for (var i = 0; i < l; i++) {
00116 f = path_module.join(path, files[i]);
00117 LoadModules(f);
00118 }
00119 } else if (path.search("_module.js") > 0 && path.search("js~") < 0) {
00120 for (var im in config.ignored_modules) {
00121 if (config.ignored_modules.hasOwnProperty(im) && path.search(config.ignored_modules[im]) >= 0) {
00122 return;
00123 }
00124 }
00125 console.log("Loading Submodule " + path);
00126
00127
00128 require(path)(module_holder);
00129 console.log("Initialized Submodule " + path);
00130 }
00131 }
00132 var DIR = path_module.join(__dirname, "modules");
00133 LoadModules(DIR);
00134
00135
00136
00137 if (cluster.isMaster) {
00138
00139 function messageHandler(msg) {
00140
00141 if (!msg["name"]) {
00142
00143 console.log("Depreciated message recieved!");
00144 }
00145 if (msg["name"]) {
00146 if (msg["name"] === "request") {
00147 console.log("Request for Worker Data received");
00148 Object.keys(cluster.workers).forEach(function (id) {
00149 cluster.workers[id].send(workerData);
00150 });
00151 }
00152 else if (!msg["target"]) {
00153 console.log("Depreciated message recieved!");
00154
00155
00156
00157
00158
00159 } else {
00160 if (!msg["method"]) {
00161
00162 workerData[msg.name][msg.target] = msg.data;
00163 }
00164 else if (msg["method"] === "push") {
00165
00166 workerData[msg.name][msg.target].push(msg.data);
00167 }
00168 Object.keys(cluster.workers).forEach(function (id) {
00169 cluster.workers[id].send({ name: msg.name, target: msg.target, data: workerData[msg.name][msg.target] });
00170 });
00171 }
00172 }
00173 }
00174
00175
00176 for (var name in module_holder) {
00177 if (module_holder.hasOwnProperty(name)) {
00178 try {
00179 module_holder[name].MasterInitFunction(workerData,config.module_config[name]);
00180 } catch (err) {
00181 ;
00182 }
00183 module_holder[name].on("message", messageHandler);
00184 }
00185 }
00186
00187
00188 cluster.on('online', function (worker) {
00189 worker.send(workerData);
00190 });
00191
00192
00193 for (var i = 0; i < numCPUs; i++) {
00194
00195 var worker = cluster.fork();
00196 worker.on('message', messageHandler);
00197 }
00198
00199
00200 cluster.on("exit", function () {
00201 var newWorker = cluster.fork();
00202 newWorker.on('message', messageHandler);
00203 });
00204 } else {
00205
00206 var https = require('https');
00207 var http = require('http');
00208 var url = require('url');
00209 var qs = require('querystring');
00210
00211 function workerMessageHandler(msg) {
00212 if (!msg["name"]) {
00213
00214
00215 workerData = msg;
00216 for (var name in module_holder) {
00217 if (module_holder.hasOwnProperty(name)) {
00218 try {
00219 module_holder[name].Update(workerData[name]);
00220 } catch (err) {
00221 ;
00222 }
00223 }
00224 }
00225 } else {
00226 if (!msg["target"]) {
00227
00228 workerData[msg.name] = msg.data;
00229 } else {
00230
00231 workerData[msg.name][msg.target] = msg.data;
00232 }
00233 try {
00234 module_holder[msg.name].Update(workerData[msg.name]);
00235 } catch (err) { ; }
00236 }
00237 }
00238
00239 process.send({ name: 'request' });
00240 process.on('message', workerMessageHandler);
00241
00242 for (var name in module_holder) {
00243 if (module_holder.hasOwnProperty(name)) {
00244 module_holder[name].on("message", function (data) {
00245
00246
00247 process.send(data);
00248 });
00249 try {
00250 module_holder[name].WorkerInitFunction(workerData);
00251 } catch (err) {
00252 ;
00253 }
00254 }
00255 }
00256
00257 function serve(req, res, readOnly, username) {
00258
00259
00260 var pathname = url.parse(req.url, true).pathname;
00261 if (pathname[0] === '/') {
00262 pathname = pathname.substr(1);
00263 }
00264
00265 var moduleName = pathname.substr(0, pathname.indexOf('/'));
00266 var functionName = pathname.substr(pathname.indexOf('/') + 1);
00267 if (workerData["serverbase"][req.connection.remoteAddress]) {
00268 var clientInfo = workerData["serverbase"][req.connection.remoteAddress];
00269 if (clientInfo.lastModuleName === moduleName && clientInfo.lastFunctionName === functionName) {
00270 if (clientInfo.lastReqTime >= Date.now() - 500) {
00271 console.log("Flood control active, denying request!");
00272 res.writeHeader(200, { 'Content-Type': 'text/html' });
00273 res.end("{\"Success\": true}");
00274 return;
00275 }
00276 }
00277 }
00278 process.send({name: "serverbase", target:req.connection.remoteAddress, data: {
00279 lastModuleName: moduleName,
00280 lastFunctionName: functionName,
00281 lastReqTime: Date.now()
00282 }});
00283
00284 var dnsDone = false;
00285
00286 require('dns').reverse(req.connection.remoteAddress, function (err, domains) {
00287 dnsDone = true;
00288 if (!err) {
00289 if (functionName.search(".min.map") < 0) {
00290
00291 console.log("Received " + req.method + ", Client: " + domains[0] + " [" + req.connection.remoteAddress + "], PID: " + process.pid + " Module: " + moduleName + ", function: " + functionName);
00292
00293 }
00294 return domains[0];
00295 } else {
00296 if (functionName.search(".min.map") < 0) {
00297
00298 console.log("Received " + req.method + ", Client: " + req.connection.remoteAddress + ", PID: " + process.pid + " Module: " + moduleName + ", function: " + functionName);
00299
00300 }
00301 return "";
00302 }
00303 });
00304 if (functionName.search("GET_ServerVersion") >= 0) {
00305 res.setHeader("Content-Type", "text/plain");
00306 res.statusCode = 200;
00307 res.end(version);
00308 return;
00309 }
00310 if (moduleName === ".." || functionName.search("\\.\\.") >= 0) {
00311 console.log("Possible break-in attempt!: " + pathname);
00312 res.writeHeader(404, { 'Content-Type': 'text/html' });
00313 res.end("Error");
00314 return;
00315 }
00316 res.setHeader("Content-Type", "application/json");
00317 res.statusCode = 200;
00318
00319
00320
00321
00322
00323
00324 if (req.method === "POST") {
00325 var body = "";
00326
00327
00328 req.on('data', function (data) {
00329 body += data;
00330 });
00331
00332 req.on('end', function () {
00333
00334 var post;
00335 try {
00336 post = JSON.parse(body);
00337 } catch (e) {
00338 post = qs.parse(body);
00339 }
00340 post.who = username;
00341
00342 if (module_holder[moduleName] != null) {
00343 console.log("Module " + moduleName + ", function " + functionName + " accessType " + (readOnly ? "RO" : "RW"));
00344 var dataTemp = "";
00345 module_holder[moduleName].removeAllListeners('data').on('data', function (data) {
00346 dataTemp += data;
00347 });
00348 module_holder[moduleName].removeAllListeners('end').on('end', function (data) {
00349
00350 res.end(JSON.stringify(dataTemp + data));
00351 });
00352 module_holder[moduleName].removeAllListeners('stream').on('stream', function (str, hdrs, code) {
00353 console.log("Stream message received: " + hdrs + " CODE: " + code);
00354 res.writeHead(code, hdrs);
00355 str.pipe(res);
00356 });
00357 var data;
00358 if (readOnly) {
00359 try {
00360 data = module_holder[moduleName]["RO_" + functionName](post, workerData[moduleName]);
00361 if (data != null) {
00362
00363 res.end(JSON.stringify(data));
00364 }
00365 } catch (err) {
00366 if (err instanceof TypeError) {
00367
00368 res.end(JSON.stringify(null));
00369 }
00370 }
00371 } else {
00372 try {
00373 data = module_holder[moduleName]["RW_" + functionName](post, workerData[moduleName]);
00374 if (data != null) {
00375
00376 res.end(JSON.stringify(data));
00377 }
00378 } catch (err2) {
00379 console.log("Error caught; text: " + JSON.stringify(err2));
00380 if (err2 instanceof TypeError) {
00381
00382 data = module_holder[moduleName]["RO_" + functionName](post, workerData[moduleName]);
00383 if (data != null) {
00384
00385 res.end(JSON.stringify(data));
00386 }
00387 }
00388 }
00389 }
00390 } else {
00391 console.log("Unknown POST URL: " + pathname);
00392 res.writeHeader(404, { 'Content-Type': 'text/html' });
00393 res.end("Error");
00394 }
00395 });
00396 }
00397
00398 if (req.method === "GET" || req.method === "HEAD") {
00399
00400 if (functionName.indexOf(".") > 0) {
00401
00402 var ext = functionName.substr(functionName.lastIndexOf(".") + 1);
00403 res.setHeader("Content-Type", "text/plain");
00404
00405 switch (ext) {
00406 case "css":
00407 res.setHeader("Content-Type", "text/css");
00408 break;
00409 case "js":
00410 res.setHeader("Content-Type", "text/javascript");
00411 break;
00412 case "html":
00413 res.setHeader("Content-Type", "text/html");
00414 break;
00415 case "htm":
00416 res.setHeader("Content-Type", "text/html");
00417 break;
00418 case "root":
00419 res.setHeader("Content-Type", "application/root+root.exe");
00420 break;
00421 case "gif":
00422 res.setHeader("Content-Type", "image/gif");
00423 break;
00424 }
00425
00426 var filename = "./modules/" + moduleName + "/client/" + functionName;
00427 if (functionName.search("favicon.ico") >= 0) {
00428 filename = "./modules/base/client/images/favicon.ico";
00429 }
00430 if (fs.existsSync(filename)) {
00431 res.setHeader("Content-Length", fs.statSync(filename)["size"]);
00432 if (req.headers.range != null) {
00433 var range = req.headers.range;
00434 var offset = parseInt(range.substr(range.indexOf('=') + 1, range.indexOf('-') - (range.indexOf('=') + 1)));
00435 var endOffset = parseInt(range.substr(range.indexOf('-') + 1));
00436 console.log("Reading (" + offset + ", " + endOffset + ")");
00437
00438 res.setHeader("Content-Length", (endOffset - offset + 1).toString());
00439 var readStream = fs.createReadStream(filename, { start: parseInt(offset), end: parseInt(endOffset) });
00440 readStream.pipe(res);
00441 } else {
00442 res.end(fs.readFileSync(filename));
00443 }
00444
00445 } else {
00446 console.log("File not found: " + filename);
00447 res.setHeader("Content-Type", "text/plain");
00448 res.end("File Not Found.");
00449 }
00450 } else if (module_holder[moduleName] != null) {
00451
00452
00453 var dataTemp = "";
00454 module_holder[moduleName].removeAllListeners('data').on('data', function (data) {
00455
00456 dataTemp += data;
00457 });
00458 module_holder[moduleName].removeAllListeners('end').on('end', function (data) {
00459
00460 res.end(JSON.stringify(dataTemp + data));
00461 });
00462 module_holder[moduleName].removeAllListeners('stream').on('stream', function (str, hdrs, code) {
00463 res.writeHead(code, hdrs);
00464 str.pipe(res);
00465 });
00466 var data = module_holder[moduleName]["GET_" + functionName](workerData[moduleName]);
00467 if (data != null) {
00468
00469 res.end(JSON.stringify(data));
00470 }
00471 } else {
00472 console.log("Sending client.html");
00473
00474 res.setHeader("Content-Type", "text/html");
00475 res.end(fs.readFileSync("./client.html"), 'utf-8');
00476 console.log("Done sending client.html");
00477 }
00478 }
00479 };
00480
00481 console.log("Setting up options");
00482 var options = {
00483 key: fs.readFileSync('./certs/server.key'),
00484 cert: fs.readFileSync('./certs/server.crt'),
00485 ca: LoadCerts("./certs/certificates"),
00486 crl: LoadCRLs("./certs/certificates"),
00487 requestCert: true,
00488 rejectUnauthorized: false
00489 };
00490 var authlist = " " + fs.readFileSync("./certs/authorized_users");
00491 console.log("Done setting up options");
00492
00493
00494 var server = https.createServer(options, function (req, res) {
00495 var readOnly = true;
00496 var clientCertificate = req.connection.getPeerCertificate();
00497 var username = "HTTPS User";
00498 if (req.client.authorized) {
00499
00500
00501
00502
00503
00504 username = clientCertificate.subject.CN[0];
00505 var useremail = clientCertificate.subjectaltname.substr(6);
00506 if (authlist.search(username) > 0 || authlist.search(useremail) > 0) {
00507 readOnly = false;
00508 }
00509 }
00510
00511 console.log("User: " + username + ", readOnly: " + readOnly);
00512
00513 try {
00514 serve(req, res, readOnly, username);
00515 } catch (e) {
00516 console.trace("Unhandled error in serve: " + JSON.stringify(e));
00517 }
00518 });
00519 var insecureServer = http.createServer(function (req, res) {
00520
00521 try {
00522 serve(req, res, false, "HTTP User");
00523 } catch (e) {
00524 console.trace("Unhandled error in serve: " + JSON.stringify(e));
00525 }
00526 });
00527
00528 console.log("Listening on " + config.hostname + " ports " + config.baseport + " and " + (config.baseport + 1));
00529 server.listen(config.baseport + 1, config.hostname);
00530 insecureServer.listen(config.baseport, config.hostname);
00531 }