cloudcmd/lib/server/main.js
2013-09-12 12:07:30 +00:00

436 lines
14 KiB
JavaScript

(function(){
'use strict';
/* Global var accessible from any loaded module */
global.cloudcmd = {};
var DIR, LIBDIR, SRVDIR, JSONDIR, HTMLDIR,
Util,
UTIL = 'util',
SLASH,
ISWIN32,
ext,
path, fs, zlib, url, stream,
OK, FILE_NOT_FOUND, MOVED_PERMANENTLY = 301,
REQUEST, RESPONSE,
Config = {
server : true,
socket : true,
port : 80
};
/* Consts */
exports.OK = OK = 200;
exports.FILE_NOT_FOUND = FILE_NOT_FOUND = 404;
exports.REQUEST = REQUEST = 'request';
exports.RESPONSE = RESPONSE = 'response';
/* Native Modules*/
exports.crypto = require('crypto'),
exports.child_process = require('child_process'),
exports.fs = fs = require('fs'),
exports.http = require('http'),
exports.https = require('https'),
exports.path = path = require('path'),
exports.url = url = require('url'),
exports.querystring = require('querystring'),
/* Constants */
/* current dir + 2 levels up */
exports.WIN32 = ISWIN32 = isWin32();
exports.SLASH = SLASH = '/',
exports.SRVDIR = SRVDIR = __dirname + SLASH,
exports.LIBDIR = LIBDIR = path.normalize(SRVDIR + '../'),
exports.DIR = DIR = path.normalize(LIBDIR + '../'),
exports.HTMLDIR = HTMLDIR = DIR + 'html' + SLASH,
exports.JSONDIR = JSONDIR = DIR + 'json' + SLASH,
/* Functions */
exports.require = mrequire,
exports.librequire = librequire,
exports.srvrequire = srvrequire,
exports.rootrequire = rootrequire,
exports.quietrequire = quietrequire,
exports.generateHeaders = generateHeaders,
exports.getQuery = getQuery,
exports.isGZIP = isGZIP,
exports.sendFile = sendFile,
exports.sendResponse = sendResponse,
exports.sendError = sendError,
exports.redirect = redirect,
exports.readFiles = readFiles,
exports.checkParams = checkParams,
exports.checkCallBackParams = checkCallBackParams,
/* compitability with old versions of node */
exports.fs.exists = exports.fs.exists || exports.path.exists,
/* Needed Modules */
/* we can not use librequare here */
exports.util = Util = require(LIBDIR + UTIL),
exports.zlib = zlib = mrequire('zlib'),
/* Main Information */
exports.modules = jsonrequire('modules');
exports.ext = ext = jsonrequire('ext');
exports.mainpackage = rootrequire('package');
/* base configuration */
exports.config = Config,
/*
* Any of loaded below modules could work with global var so
* it should be initialized first. Becouse of almost any of
* moudles do not depends on each other all needed information
* for all modules is initialized hear.
*/
global.cloudcmd.main = exports;
exports.VOLUMES = getVolumes(),
/* Additional Modules */
exports.stream = stream = srvrequire('stream'),
exports.socket = srvrequire('socket'),
exports.auth = srvrequire('auth').auth,
exports.appcache = srvrequire('appcache'),
exports.cloudfunc = librequire('cloudfunc'),
exports.dir = srvrequire('dir'),
exports.rest = srvrequire('rest').api,
exports.update = srvrequire('update'),
exports.ischanged = srvrequire('ischanged');
exports.commander = srvrequire('commander');
exports.minify = srvrequire('minify').Minify;
/* second initializing after all modules load, so global var is *
* totally filled of all information that should know all modules */
global.cloudcmd.main = exports;
/**
* function do safe require of needed module
* @param {Strin} pSrc
*/
function mrequire(pSrc){
var lModule,
lError = Util.tryCatch(function(){
lModule = require(pSrc);
});
if(lError)
Util.log('Module ' + pSrc + ' not connected');
return lModule;
}
function quietrequire(pSrc){
var lModule;
Util.tryCatch(function(){
lModule = require(pSrc);
});
return lModule;
}
function rootrequire(pSrc){ return mrequire(DIR + pSrc); }
function librequire(pSrc){ return mrequire(LIBDIR + pSrc); }
function srvrequire(pSrc){ return mrequire(SRVDIR + pSrc); }
function jsonrequire(pSrc){ return mrequire(JSONDIR + pSrc);}
/**
* function check is current platform is win32
*/
function isWin32(){ return process.platform === 'win32'; }
/**
* get volumes if win32 or get nothing if nix
*/
function getVolumes(){
var lRet = ISWIN32 ? [] : '/';
if(ISWIN32)
srvrequire('win').getVolumes(function(pVolumes){
console.log(pVolumes);
exports.VOLUMES = pVolumes;
});
return lRet;
}
/**
* Функция создаёт заголовки файлов
* в зависимости от расширения файла
* перед отправкой их клиенту
* @param pParams
* name - имя файла
* gzip - данные сжаты gzip'ом
* query
* https://developers.google.com/speed/docs/best-practices/caching?hl=ru#LeverageProxyCaching
*/
function generateHeaders(pParams){
var lRet = Util.checkObjTrue(pParams, ['name']);
if(lRet){
var p = pParams,
lExt = Util.getExtension(p.name),
lType = ext[lExt] || 'text/plain',
lContentEncoding = '';
/* if type of file any, but img - then we shoud specify charset */
if( !Util.isContainStr(lType, 'img') )
lContentEncoding = '; charset=UTF-8';
if( Util.isContainStr(p.query, 'download') )
lType = 'application/octet-stream';
lRet = {
'Access-Control-Allow-Origin' : '*',
'Content-Type' : lType + lContentEncoding,
'last-modified' : new Date().toString(),
'Vary' : 'Accept-Encoding'
};
if( !Util.strCmp(lExt, '.appcache') && p.cache)
lRet['cache-control'] = 'max-age=' + 31337 * 21;
if(p.gzip)
lRet['content-encoding'] = 'gzip';
}
return lRet;
}
/**
* send file to client thru pipe
* and gzip it if client support
*
* @param pName - имя файла
* @param pGzip - данные сжаты gzip'ом
*/
function sendFile(pParams){
var lRet = checkParams(pParams);
if (lRet) {
var p = pParams,
lGzip = isGZIP(p.request) && p.gzip;
p.response.writeHead(OK, generateHeaders({
name : p.name,
cache : p.cache,
gzip : lGzip,
query : getQuery(p.request)
}) );
stream.createPipe({
from: p.name,
write: p.response,
zip : lGzip && !p.gziped,
callback: function(pError) {
var lError = pError && pError.toString();
if (pError) {
p.response.writeHead(FILE_NOT_FOUND, 'OK');
p.response.end(lError);
}
}
});
}
return lRet;
}
/**
* Функция высылает ответ серверу
* @param pHead - заголовок
* @param Data - данные
* @param pName - имя отсылаемого файла
*/
function sendResponse(pParams, pData){
var lRet = checkParams(pParams);
if(lRet){
var p = pParams;
var lQuery = getQuery(p.request),
/* download, json */
lGzip = isGZIP(p.request),
lHead = generateHeaders({
name : p.name,
cache : p.cache,
gzip : lGzip,
query : lQuery
});
/* если браузер поддерживает gzip-сжатие - сжимаем данные*/
Util.ifExec(!lGzip,
function(pParams) {
var lRet = Util.checkObj(pParams, ['data']);
if(lRet) {
p.status = pParams.status || p.status;
p.data = pParams.data;
}
p.response.writeHead(p.status || OK, lHead);
p.response.end(p.data);
Util.log( p.name + ' sended');
},
function(pCallBack) {
zlib.gzip (p.data || pData, Util.call(gzipData, {
callback : pCallBack
}));
});
}
}
/**
* redirect to another URL
*/
function redirect(pParams){
var lRet = Util.checkObjTrue(pParams,
[RESPONSE]);
if(lRet){
var p = pParams;
p.response.writeHead(MOVED_PERMANENTLY, {'Location': p.url});
p.response.end();
}
}
/**
* send error response
*/
function sendError(pParams, pError) {
var lRet = checkParams(pParams);
if(lRet){
var p = pParams;
p.status = FILE_NOT_FOUND;
if (!p.data && pError)
p.data = pError.toString();
sendResponse(p);
}
}
/**
* Функция получает сжатые данные
* @param pHeader - заголовок файла
* @pName
*/
function gzipData(pParams){
var lRet = checkCallBackParams(pParams),
p = pParams;
if(lRet)
lRet = Util.checkObj(pParams.params, ['callback']);
if(lRet){
var lCallBack = p.params.callback,
lParams = {};
if(!p.error)
lParams.data = p.data;
else{
lParams.status = FILE_NOT_FOUND;
lParams.data = p.error.toString();
}
Util.exec(lCallBack, lParams);
}
}
function checkCallBackParams(pParams){
return Util.checkObj(pParams, ['error', 'data', 'params']);
}
function readFiles(pFiles, pCallBack){
var lDone = [],
lFiles,
lErrors,
lReadedFiles = {},
lDoneFunc = function (pParams){
var lRet = Util.checkObj(pParams, ['error', 'data', 'params']);
if(lRet){
lDone.pop();
var p = pParams,
lName = p.params;
if(p.error){
if(!lErrors) lErrors = {};
lErrors[lName] = p.error;
}
else{
Util.log(lName + ' readed');
lReadedFiles[lName] = p.data;
}
if( !lDone.length )
Util.exec(pCallBack, lErrors, lReadedFiles);
}
};
if( Util.isArray(pFiles) )
lFiles = pFiles;
else
lFiles = [pFiles];
for(var i = 0, n = lFiles.length; i < n; i++) {
var lName = lFiles.pop();
lDone.push(lName);
fs.readFile(lName, Util.call( lDoneFunc, lName ));
}
}
function checkParams(pParams, pAdditional){
var lRet = Util.checkObjTrue( pParams, ['name', REQUEST, RESPONSE] );
if (lRet && pAdditional)
lRet = Util.checkObjTrue( pParams, pAdditional);
return lRet;
}
function getQuery(pReq){
var lQuery, lParsedUrl;
if (pReq) {
lParsedUrl = url.parse(pReq.url);
lQuery = lParsedUrl.query;
}
return lQuery;
}
function isGZIP(pReq){
var lEnc, lGZIP;
if (pReq) {
lEnc = pReq.headers['accept-encoding'] || '';
lGZIP = lEnc.match(/\bgzip\b/);
}
return lGZIP;
}
})();