mirror of
https://github.com/coderaiser/cloudcmd.git
synced 2026-01-23 10:45:47 +00:00
321 lines
9.6 KiB
JavaScript
321 lines
9.6 KiB
JavaScript
(function() {
|
|
'use strict';
|
|
|
|
var fs = require('fs'),
|
|
zlib = require('zlib'),
|
|
url = require('url'),
|
|
querystring = require('querystring'),
|
|
|
|
DIR_JSON = __dirname + '/../../json/',
|
|
|
|
Util = require('../util'),
|
|
pipe = require('./pipe'),
|
|
ext = require(DIR_JSON + 'ext'),
|
|
|
|
OK = 200,
|
|
RANGE = 206,
|
|
MOVED_PERMANENTLY = 301,
|
|
FILE_NOT_FOUND = 404;
|
|
|
|
exports.redirect = redirect;
|
|
|
|
exports.send = send;
|
|
exports.sendError = sendError;
|
|
exports.sendFile = sendFile;
|
|
|
|
exports.isGZIP = isGZIP;
|
|
|
|
exports.getPathName = getPathName;
|
|
exports.getQuery = getQuery;
|
|
|
|
exports.setHeader = setHeader;
|
|
|
|
/* Функция высылает ответ серверу
|
|
* @param data
|
|
* @param params
|
|
* @param notLog
|
|
*/
|
|
function send(data, params, notLog) {
|
|
var p, isGzip, head,
|
|
ret = checkParams(params);
|
|
|
|
if (ret) {
|
|
p = params;
|
|
data = data;
|
|
isGzip = p.gzip && isGZIP(p.request);
|
|
|
|
head = generateHeaders({
|
|
name : p.name,
|
|
cache : p.cache,
|
|
gzip : isGzip,
|
|
query : p.query
|
|
});
|
|
|
|
fillHeader(head, p.response);
|
|
|
|
if (!notLog)
|
|
Util.log(data);
|
|
|
|
/* если браузер поддерживает gzip-сжатие - сжимаем данные*/
|
|
Util.exec.if(!isGzip,
|
|
function() {
|
|
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.message;
|
|
}
|
|
|
|
callback();
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Функция создаёт заголовки файлов
|
|
* в зависимости от расширения файла
|
|
* перед отправкой их клиенту
|
|
* @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(extension, '.appcache');
|
|
if (!p.cache || cmp)
|
|
maxAge = 0;
|
|
|
|
header['Cache-Control'] = 'max-age=' + maxAge;
|
|
|
|
if (p.gzip)
|
|
header['Content-Encoding'] = 'gzip';
|
|
}
|
|
|
|
return header;
|
|
}
|
|
|
|
function setHeader(pParams) {
|
|
var p, header, gzip,
|
|
lRet = checkParams(pParams);
|
|
|
|
if (lRet) {
|
|
p = pParams;
|
|
gzip = 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 : gzip,
|
|
query : getQuery(p.request)
|
|
});
|
|
|
|
fillHeader(header, p.response);
|
|
p.response.statusCode = p.status || OK;
|
|
}
|
|
}
|
|
|
|
function fillHeader(header, response) {
|
|
var isObject = Util.isObject(header),
|
|
isSent = response.headersSent;
|
|
|
|
if (!isSent && isObject)
|
|
Object.keys(header).forEach(function(name) {
|
|
response.setHeader(name, header[name]);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 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(error, params);
|
|
} 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
|
|
});
|
|
|
|
setHeader(params);
|
|
|
|
options = {
|
|
gzip : isGzip,
|
|
range : range
|
|
};
|
|
|
|
pipe.create(p.name, p.response, options, function(error) {
|
|
if (error)
|
|
sendError(error, params);
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* send error response
|
|
*/
|
|
function sendError(error, params) {
|
|
var data, ret = checkParams(params);
|
|
|
|
if (ret) {
|
|
params.status = FILE_NOT_FOUND;
|
|
data = error.message || '' + error;
|
|
|
|
send(data, params);
|
|
}
|
|
}
|
|
|
|
function checkParams(params) {
|
|
var ret = true;
|
|
|
|
Util.checkArgs(arguments, ['params']);
|
|
Util.checkArgs([params.name, params.request, params.response], ['name', 'requst', 'response']);
|
|
|
|
return ret;
|
|
}
|
|
|
|
function getQuery(req) {
|
|
var query, parsed;
|
|
|
|
Util.checkArgs(arguments, ['req']);
|
|
|
|
parsed = url.parse(req.url);
|
|
query = parsed.query;
|
|
|
|
return query;
|
|
}
|
|
|
|
function getPathName(req) {
|
|
var pathname, parsed;
|
|
|
|
Util.checkArgs(arguments, ['req']);
|
|
|
|
parsed = url.parse(req.url);
|
|
pathname = parsed.pathname;
|
|
/* supporting of Russian language in directory names */
|
|
pathname = querystring.unescape(pathname);
|
|
|
|
return pathname;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
function isGZIP(req) {
|
|
var enc, is;
|
|
|
|
if (req) {
|
|
enc = req.headers['accept-encoding'] || '';
|
|
is = enc.match(/\bgzip\b/);
|
|
}
|
|
|
|
return is;
|
|
}
|
|
|
|
/**
|
|
* redirect to another URL
|
|
*/
|
|
function redirect(url, response) {
|
|
var header = {
|
|
'Location': url
|
|
};
|
|
|
|
Util.checkArgs(arguments, ['url', 'response']);
|
|
|
|
fillHeader(header, response);
|
|
response.statusCode = MOVED_PERMANENTLY;
|
|
response.end();
|
|
}
|
|
|
|
})();
|