miércoles, 6 de junio de 2012

simple NodeJs Http Server

I has been a long time since last update, and i thought it would be fine to share some code i have been using for a while :D

For some blackberry projects i worked on, i have been using an http server i developed using nodeJs.
With nodeJs you program exactly how you want the server to behave.

This is the source code to be placed in file HttpServer.js
 var http = require('http');  
 var fs = require('fs');  
 var arg = process.argv.splice(2);  
 //some sanity checks  
 if (arg.length == 0 || arg[0] =="")     exitApp('empty path!, please specify rootDir via command line like "node HttpServer.js /your/root/path"');  
 if (!( requestStats = fileExists(arg[0]))) exitApp('invalid path, please check the path provided!');  
 else if (requestStats.isFile())       exitApp('path is a file!, please check the path provided!');  
 var config = {  
   serverName : "Node Http Server v1.1",  
   baseUrl : arg[0],  
   host : "0.0.0.0", // 127.0.0.1 for localhost only, 0.0.0.0 for all ips  
   port : 8123,  
   showFileStats : false,  
   showCodFilesForBlackberry : false,  
   showSpecialFiles : false, // show filenames starting with .  
   logRequests : true,  
   logUserAgent : false  
 };  
 var httpserver = http.createServer(onRequest);  
 httpserver.listen(config.port,config.host ,serverCreated_callback());  
 function serverCreated_callback(){  
      console.log(config.serverName +' running at http://' + config.host +':' + config.port + '/ base url ' + config.baseUrl);  
 }  
 function onRequest(req, res){  
      try{  
           if ( req.url == '/favicon.ico' ) return; // we filter favicon req  
           if ( req.url.indexOf("..") != -1 ) return; // we filter directory scalation  
           if ( config.logRequests ){  
                console.log(req.connection.remoteAddress + " - " + req.url);  
                if ( config.logUserAgent ){ console.log('user agent:' + req.headers['user-agent'].toLowerCase()); }  
           }  
           var filePath = config.baseUrl + req.url;  
           if ( !( requestStats = fileExists(filePath) ) ){ // files not existing 404  
                res.writeHead(404);  
                res.end();  
           }else{  
                var pureUrl = UrlToFsPath(req.url);  
                if (requestStats.isFile()){     // file (download it)            
                     res.writeHead(200,getResponseHeaders(req));  
                     res.write(fs.readFileSync(filePath));  
                     res.end();  
                }else{ // folder (list it's contents)  
                     var fileList = filterFileList(req, fs.readdirSync(config.baseUrl + pureUrl) );  
                     var htmlOutput = formatHtmlFileList(fileList,pureUrl);  
                     res.writeHead(200, { 'Content-Type': 'text/html' });  
                     res.end(htmlOutput, 'utf8');  
                }   
           }    
   }catch(error) { // just in case of fatal error  
     res.writeHead(500, { 'Content-Type': 'text/html' });  
           res.end();  
      }  
 }  
 // chechs if file exists. Returns file stats if it exists, false otherwise  
 function fileExists(path) {  
      try { return fs.statSync(path); }  
      catch(error) { return false; }  
 }  
 // adds response headers depending on what kind of filename was requested  
 function getResponseHeaders(req){  
      var requestedFilename = req.url.split("/").pop();  
      var requestedExtension = requestedFilename.split(".").pop();  
      var responseHeaders = {};  
      if (requestedExtension == "jad"){   
           responseHeaders["Content-Type"] = "text/vnd.sun.j2me.app-descriptor; charset=utf-8";   
           responseHeaders['Content-Disposition'] = 'attachment; filename=' +requestedFilename;  
      }else if (requestedExtension == "cod"){   
           responseHeaders["Content-Type"] = "application/vnd.rim.cod";   
           responseHeaders['Content-Disposition']= 'attachment; filename=' +requestedFilename;  
      }else if (requestedExtension == "html"){ responseHeaders["Content-Type"] = 'text/html'; }  
      return responseHeaders;  
 }  
 // corrects url  
 function UrlToFsPath(pureUrl){  
   if( pureUrl.substr(-1) != "/" ){ pureUrl += "/"; }  
   return unescape(pureUrl.replace(/\+/g, " "));     
 }  
 // filters files to show depending on config  
 function filterFileList(req,fileList){  
      var returnedFileList = [];  
      var isBlackberry = (req.headers['user-agent'].toLowerCase().indexOf("blackberry") != -1);  
      var hideCodFiles = isBlackberry && !config.showCodFilesForBlackberry;  
      for (var i in fileList) {  
           var filename = fileList[i];  
           var fileExtension = filename.split(".").pop();  
           if ( (!config.showSpecialFiles && filename.indexOf(".") != 0) || config.showSpecialFiles) {   
                if ((hideCodFiles && fileExtension != "cod") || !hideCodFiles){ returnedFileList.push(filename); }  
           }  
      }  
      return returnedFileList;  
 }  
 // returns file list html  
 function formatHtmlFileList(fileList,pureUrl){  
   var htmlOutput = '<h4>Folder: ' + pureUrl + "</h4><table>";  
   if ( config.showFileStats ) { htmlOutput +="<td>Create Date</td><td>Size</td>"; }  
   for (var i in fileList) {  
        var filename = fileList[i];  
        var absolutePath = pureUrl + filename;  
           var fileStats = fs.statSync(config.baseUrl + absolutePath);  
           if(fileStats.isFile()){ htmlOutput += "<tr><td><a href='" + absolutePath + "'>" + filename + "</a> </td>"; }  
           else{ htmlOutput += "<tr><td><a href='" + absolutePath + "'> " + filename + "</a> </td>"; }  
           if (config.showFileStats) { htmlOutput +="<td>" + fileStats['ctime'] + "</td><td>"+ fileStats['size'] +"</td>"; }  
   }  
   return htmlOutput +'</table>';   
 }  
 // exits the app with message in console  
 function exitApp(message){  
   console.log(message);  
   process.exit(1);  
 }  

To use this example just install nodeJs and execute this command in the console:

 node HttpServer.js /your/desired/root/path  

Then you can test it browsing to http://127.0.0.1:8123/
More simple impossible!

If you need to know more about nodejs, just take a look at their website.