(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, pipe, CloudFunc, diffPatch, querystring, OK, RANGE, FILE_NOT_FOUND, MOVED_PERMANENTLY, REQUEST, RESPONSE, Config = { server : true, socket : true, port : 80 }; /* Consts */ exports.OK = OK = 200; RANGE = 206; MOVED_PERMANENTLY = 301; 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 = 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.getPathName = getPathName, exports.isGZIP = isGZIP, exports.mainSetHeader = mainSetHeader, exports.sendFile = sendFile, exports.sendResponse = sendResponse, exports.sendError = sendError, exports.redirect = redirect, 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; /* Additional Modules */ exports.cloudfunc = CloudFunc = librequire('cloudfunc'), exports.pipe = pipe = srvrequire('pipe'), exports.socket = srvrequire('socket'), exports.console = srvrequire('console'), exports.terminal = !ISWIN32 ? srvrequire('terminal') : { init: function() {} }, exports.express = srvrequire('express'), exports.auth = srvrequire('auth').auth, exports.appcache = srvrequire('appcache'), diffPatch = librequire('diff/diff-match-patch').diff_match_patch, exports.diff = new (librequire('diff').DiffProto)(diffPatch), exports.rest = srvrequire('rest').api, exports.update = srvrequire('update'), exports.ischanged = srvrequire('ischanged'); exports.files = srvrequire('files'); 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} src */ function mrequire(src) { var module, msg, error = Util.exec.try(function() { module = require(src); }); if (error) if (error.code === 'MODULE_NOT_FOUND') msg = CloudFunc.formatMsg('require', src, 'no'); else Util.log(error); Util.log(msg); return module; } function quietrequire(src) { var module; Util.exec.try(function() { module = require(src); }); return module; } function rootrequire(src) { return mrequire(DIR + src); } function librequire(src) { return mrequire(LIBDIR + src); } function srvrequire(src) { return mrequire(SRVDIR + src); } function jsonrequire(src) { return mrequire(JSONDIR + src);} /** * function check is current platform is win32 */ function isWin32() { return process.platform === 'win32'; } /** * Функция создаёт заголовки файлов * в зависимости от расширения файла * перед отправкой их клиенту * @param pParams * name - имя файла * gzip - данные сжаты gzip'ом * query * https://developers.google.com/speed/docs/best-practices/caching?hl=ru#LeverageProxyCaching */ function generateHeaders(params) { var header, p, extension, type, encoding, isContain, cmp, maxAge = 31337 * 21; if (params.name) { p = params, extension = Util.getExt(p.name), type = ext[extension] || 'text/plain', encoding = ''; /* if type of file any, but img - then we shoud specify charset */ isContain = Util.isContainStr(type, ['img', 'image', 'audio']); if (!isContain) encoding = '; charset=UTF-8'; isContain = Util.isContainStr(p.query, 'download'); if (isContain) type = 'application/octet-stream'; header = { 'Access-Control-Allow-Origin' : '*', 'Content-Type' : type + encoding, 'Vary' : 'Accept-Encoding', 'Accept-Ranges' : 'bytes' }; if (p.time) Util.copyObj(header, { 'Last-Modified' : p.time }); if (p.range) Util.copyObj(header, { 'Content-Range' : 'bytes ' + p.range.start + '-' + p.range.end + '/' + p.range.sizeTotal, 'Content-Length': p.range.size }); cmp = Util.strCmp(ext, '.appcache'); if (!p.cache || cmp) maxAge = 0; header['Cache-Control'] = 'max-age=' + maxAge; if (p.gzip) header['Content-Encoding'] = 'gzip'; } return header; } function mainSetHeader(pParams) { var p, header, lGzip, lRet = checkParams(pParams); if (lRet) { p = pParams; lGzip = p.isGzip || isGZIP(p.request) && p.gzip; header = generateHeaders({ name : p.name, time : p.time, range : p.range, length : p.length, cache : p.cache, gzip : lGzip, query : getQuery(p.request) }); setHeader(header, p.response); p.response.statusCode = p.status || OK; } } /** * send file to client thru pipe * and gzip it if client support * */ function sendFile(params) { var p = params, ret = checkParams(params); if (ret) fs.lstat(p.name, function(error, stat) { var time, length, range, isGzip, options = {}; if (error) { sendError(params, error); } else { isGzip = isGZIP(p.request) && p.gzip; time = stat.mtime, length = stat.size, range = getRange(p.request, length); if (range) Util.copyObj(p, { range : range, status : RANGE }); Util.copyObj(p, { time : time }); mainSetHeader(params); options = { gzip : isGzip, range : range }; pipe.create(p.name, p.response, options, function(error) { if (error) sendError(params, error); }); } }); } /** * Функция высылает ответ серверу * @param pHead - заголовок * @param Data - данные * @param pName - имя отсылаемого файла */ function sendResponse(params, data, notLog) { var p, isGzip, head, ret = checkParams(params); if (ret) { p = params; data = p.data || data; isGzip = isGZIP(p.request); head = generateHeaders({ name : p.name, cache : p.cache, gzip : isGzip, query : p.query }); setHeader(head, p.response); if (!notLog) Util.log(data); /* если браузер поддерживает gzip-сжатие - сжимаем данные*/ Util.exec.if(!isGzip, function() { p.status = params.status || p.status; if (!p.data) p.data = data; p.response.statusCode = p.status || OK; p.response.end(p.data); }, function(callback) { zlib.gzip (data, function(error, data) { if (!error) p.data = data; else { p.status = FILE_NOT_FOUND; p.data = error.toString(); } Util.exec(callback, p); }); }); } } /** * redirect to another URL */ function redirect(pParams) { var p, header, lRet = Util.checkObjTrue(pParams, [RESPONSE]); if (lRet) { p = pParams; header = { 'Location': p.url }; setHeader(header, p.response); p.response.statusCode = MOVED_PERMANENTLY; p.response.end(); } } /** * send error response */ function sendError(params, error) { var p, ret = checkParams(params); if (ret) { p = params; p.status = FILE_NOT_FOUND; if (!p.data && error) p.data = error.toString(); sendResponse(p); } } function checkCallBackParams(pParams) { return Util.checkObj(pParams, ['error', 'data', 'params']); } function checkParams(params, additional) { var ret = Util.checkObjTrue(params, ['name', REQUEST, RESPONSE]); if (ret && additional) ret = Util.checkObjTrue(params, additional); return ret; } function getQuery(req) { var query, parsed; if (req) { parsed = url.parse(req.url); query = parsed.query; } return query; } function getPathName(req) { var pathname, parsed; if (req) { parsed = url.parse(req.url); pathname = parsed.pathname; /* supporting of Russian language in directory names */ pathname = querystring.unescape(pathname); } return pathname; } function isGZIP(pReq) { var lEnc, lGZIP; if (pReq) { lEnc = pReq.headers['accept-encoding'] || ''; lGZIP = lEnc.match(/\bgzip\b/); } return lGZIP; } function setHeader(header, response) { var name; if (!response.headersSent && Util.isObject(header)) for (name in header) response.setHeader(name, header[name]); } function getRange(req, sizeTotal) { var range, start, end, size, parts, rangeStr = req.headers.range; if (rangeStr) { parts = rangeStr.replace(/bytes=/, '').split('-'); start = parts[0]; end = parts[1] || sizeTotal - 1; size = (end - start) + 1; range = { start : start - 0, end : end - 0, size : size, sizeTotal : sizeTotal }; } return range; } })();