rrestjs
Version:
HIgh performance node.js ROA & RESTFUL web framework.
602 lines (527 loc) • 13.4 kB
JavaScript
/*
*RestUtils.js 补充了一些特有的方法,沿用expressjs的utils文件
*
*/
/*!
* Connect - utils
* Copyright(c) 2010 Sencha Inc.
* Copyright(c) 2011 TJ Holowaychuk
* MIT Licensed
*/
/**
* Module dependencies.
*/
var http = require('http'),
crypto = require('crypto'),
Path = require('path'),
isWindows = process.platform === 'win32',//是否是windows
fs = require('fs');
/**
* Extract the mime type from the given request's
* _Content-Type_ header.
*
* @param {IncomingMessage} req
* @return {String}
* @api private
*/
exports.mime = function(req) {
var str = req.headers['content-type'] || '';
return str.split(';')[0];
};
/**
* Generate an `Error` from the given status `code`.
*
* @param {Number} code
* @return {Error}
* @api private
*/
exports.error = function(code){
var err = new Error(http.STATUS_CODES[code]);
err.status = code;
return err;
};
/**
* Flatten the given `arr`.
*
* @param {Array} arr
* @return {Array}
* @api private
*/
exports.flatten = function(arr, ret){
var ret = ret || []
, len = arr.length;
for (var i = 0; i < len; ++i) {
if (Array.isArray(arr[i])) {
exports.flatten(arr[i], ret);
} else {
ret.push(arr[i]);
}
}
return ret;
};
/**
* Return md5 hash of the given string and optional encoding,
* defaulting to hex.
*
* utils.md5('wahoo');
* // => "e493298061761236c96b02ea6aa8a2ad"
*
* @param {String} str
* @param {String} encoding
* @return {String}
* @api public
*/
exports.md5 = function(str, encoding){
return crypto
.createHash('md5')
.update(str)
.digest(encoding || 'hex');
};
/**
* Merge object b with object a.
*
* var a = { foo: 'bar' }
* , b = { bar: 'baz' };
*
* utils.merge(a, b);
* // => { foo: 'bar', bar: 'baz' }
*
* @param {Object} a
* @param {Object} b
* @return {Object}
* @api private
*/
exports.merge = function(a, b){
if (a && b) {
for (var key in b) {
a[key] = b[key];
}
}
return a;
};
/*
object copy
*/
exports.copy = function(a){
return JSON.parse(JSON.stringify(a));
}
exports.merge2 = function(a, b, req, res){
if (a && b) {
for (var key in b) {
if(!a.hasOwnProperty(key)){
if('function' === typeof b[key]) a[key] = b[key](req, res);
else a[key] = b[key];
}
}
}
return a;
};
/**
* Escape the given string of `html`.
*
* @param {String} html
* @return {String}
* @api private
*/
exports.escape = function(html){
return String(html)
.replace(/&(?!\w+;)/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"');
};
/**
* Return a unique identifier with the given `len`.
*
* utils.uid(10);
* // => "FDaS435D2z"
*
* @param {Number} len
* @return {String}
* @api private
*/
exports.uid = function(len) {
var buf = []
, chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
, charlen = chars.length;
for (var i = 0; i < len; ++i) {
buf.push(chars[Math.random() * charlen | 0]);
}
return buf.join('');
};
/**
* Sign the given `val` with `secret`.
*
* @param {String} val
* @param {String} secret
* @return {String}
* @api private
*/
exports.sign = function(val, secret){
return val + '.' + crypto
.createHmac('sha1', secret)
.update(val)
.digest('base64')
.replace(/=+$/, '');
};
/**
* Unsign and decode the given `val` with `secret`,
* returning `false` if the signature is invalid.
*
* @param {String} val
* @param {String} secret
* @return {String|Boolean}
* @api private
*/
exports.unsign = function(val, secret){
var parts = val.split('.')
, str = parts[0]
return exports.sign(str, secret) == val
? str
: false;
};
/**
* Parse signed cookies, returning an object
* containing the decoded key/value pairs,
* while removing the signed key from `obj`.
*
* @param {Object} obj
* @return {Object}
* @api private
*/
exports.parseSignedCookies = function(obj, secret){
var ret = {};
Object.keys(obj).forEach(function(key){
var val = obj[key]
, signed = exports.unsign(val, secret);
if (signed) {
ret[key] = signed;
delete obj[key];
}
});
return ret;
};
/**
* Parse JSON cookies.
*
* @param {Object} obj
* @return {Object}
* @api private
*/
exports.parseJSONCookies = function(obj){
Object.keys(obj).forEach(function(key){
var val = obj[key];
if (0 == val.indexOf('j:')) {
try {
obj[key] = JSON.parse(val.slice(2));
} catch (err) {
// nothing
}
}
});
return obj;
};
/**
* Parse the given cookie string into an object.
*
* @param {String} str
* @return {Object}
* @api private
*/
exports.parseCookie = function(str){
var obj = {}
, pairs = str.split(/[;,] */);
for (var i = 0, len = pairs.length; i < len; ++i) {
var pair = pairs[i]
, eqlIndex = pair.indexOf('=')
, key = pair.substr(0, eqlIndex).trim()
, val = pair.substr(++eqlIndex, pair.length).trim();
// quoted values
if ('"' == val[0]) val = val.slice(1, -1);
// only assign once
if (undefined == obj[key]) {
val = val.replace(/\+/g, ' ');
try {
obj[key] = decodeURIComponent(val);
} catch (err) {
if (err instanceof URIError) {
obj[key] = val;
} else {
throw err;
}
}
}
}
return obj;
};
/**
* Serialize the given object into a cookie string.
*
* utils.serializeCookie('name', 'tj', { httpOnly: true })
* // => "name=tj; httpOnly"
*
* @param {String} name
* @param {String} val
* @param {Object} obj
* @return {String}
* @api private
*/
exports.serializeCookie = function(name, val, obj){
var pairs = [name + '=' + encodeURIComponent(val)]
, obj = obj || {};
if (obj.domain) pairs.push('domain=' + obj.domain);
if (obj.path) pairs.push('path=' + obj.path);
if (obj.expires) pairs.push('expires=' + obj.expires.toUTCString());
if (obj.httpOnly !== false ) pairs.push('httpOnly');
if (obj.secure) pairs.push('secure');
return pairs.join('; ');
};
/**
* Pause `data` and `end` events on the given `obj`.
* Middleware performing async tasks _should_ utilize
* this utility (or similar), to re-emit data once
* the async operation has completed, otherwise these
* events may be lost.
*
* var pause = utils.pause(req);
* fs.readFile(path, function(){
* next();
* pause.resume();
* });
*
* @param {Object} obj
* @return {Object}
* @api private
*/
exports.pause = function(obj){
var onData
, onEnd
, events = [];
// buffer data
obj.on('data', onData = function(data, encoding){
events.push(['data', data, encoding]);
});
// buffer end
obj.on('end', onEnd = function(data, encoding){
events.push(['end', data, encoding]);
});
return {
end: function(){
obj.removeListener('data', onData);
obj.removeListener('end', onEnd);
},
resume: function(){
this.end();
for (var i = 0, len = events.length; i < len; ++i) {
obj.emit.apply(obj, events[i]);
}
}
};
};
/**
* Check `req` and `res` to see if it has been modified.
*
* @param {IncomingMessage} req
* @param {ServerResponse} res
* @return {Boolean}
* @api private
*/
exports.modified = function(req, res) {
var headers = res.headers || {}
, modifiedSince = req.headers['if-modified-since']
, lastModified = headers['last-modified']
, noneMatch = req.headers['if-none-match']
, etag = headers['etag'];
if (noneMatch) noneMatch = noneMatch.split(/ *, */);
// check If-None-Match
if (noneMatch && etag && ~noneMatch.indexOf(etag)) {
return false;
}
// check If-Modified-Since
if (modifiedSince && lastModified) {
modifiedSince = new Date(modifiedSince);
lastModified = new Date(lastModified);
// Ignore invalid dates
if (!isNaN(modifiedSince.getTime())) {
if (lastModified <= modifiedSince) return false;
}
}
return true;
};
/**
* Strip `Content-*` headers from `res`.
*
* @param {ServerResponse} res
* @api private
*/
exports.removeContentHeaders = function(res){
Object.keys(res._headers).forEach(function(field){
if (0 == field.indexOf('content')) {
res.removeHeader(field);
}
});
};
/**
* Check if `req` is a conditional GET request.
*
* @param {IncomingMessage} req
* @return {Boolean}
* @api private
*/
exports.conditionalGET = function(req) {
return req.headers['if-modified-since']
|| req.headers['if-none-match'];
};
/**
* Respond with 401 "Unauthorized".
*
* @param {ServerResponse} res
* @param {String} realm
* @api private
*/
exports.unauthorized = function(res, realm) {
res.statusCode = 401;
res.setHeader('WWW-Authenticate', 'Basic realm="' + realm + '"');
res.end('Unauthorized');
};
/**
* Respond with 304 "Not Modified".
*
* @param {ServerResponse} res
* @param {Object} headers
* @api private
*/
exports.notModified = function(res) {
exports.removeContentHeaders(res);
res.statusCode = 304;
res.end();
};
/**
* Return an ETag in the form of `"<size>-<mtime>"`
* from the given `stat`.
*
* @param {Object} stat
* @return {String}
* @api private
*/
exports.etag = function(stat) {
return '"' + stat.size + '-' + Number(stat.mtime) + '"';
};
/**
* Parse "Range" header `str` relative to the given file `size`.
*
* @param {Number} size
* @param {String} str
* @return {Array}
* @api private
*/
exports.parseRange = function(size, str){
var valid = true;
var arr = str.substr(6).split(',').map(function(range){
var range = range.split('-')
, start = parseInt(range[0], 10)
, end = parseInt(range[1], 10);
// -500
if (isNaN(start)) {
start = size - end;
end = size - 1;
// 500-
} else if (isNaN(end)) {
end = size - 1;
}
// Invalid
if (isNaN(start) || isNaN(end) || start > end) valid = false;
return { start: start, end: end };
});
return valid ? arr : undefined;
};
/**
* Parse the given Cache-Control `str`.
*
* @param {String} str
* @return {Object}
* @api private
*/
exports.parseCacheControl = function(str){
var directives = str.split(',')
, obj = {};
for(var i = 0, len = directives.length; i < len; i++) {
var parts = directives[i].split('=')
, key = parts.shift().trim()
, val = parseInt(parts.shift(), 10);
obj[key] = isNaN(val) ? true : val;
}
return obj;
};
/**
* Convert array-like object to an `Array`.
*
* node-bench measured "16.5 times faster than Array.prototype.slice.call()"
*
* @param {Object} obj
* @return {Array}
* @api private
*/
var toArray = exports.toArray = function(obj){
var len = obj.length
, arr = new Array(len);
for (var i = 0; i < len; ++i) {
arr[i] = obj[i];
}
return arr;
};
/*
get process dir
*/
var headReq = exports.headReq = function(req, data){//如果是head请求,则值响应http头
if(req.method === 'HEAD'){
return '';
}
else return data;
}
var ProcessPath = exports.toArray = Path.dirname(process.argv[1]);
exports.errorRes = function(res, msg, scode){
var msg = msg || 'Error';
res.statusCode = scode || 500;
res.end(resMsg(scode, msg, res._restReq));
}
exports.errorRes404 = function(res, msg){
var msg = msg || 'Error';
res.statusCode = 404;
res.end(resMsg(404, msg, res._restReq));
}
exports.forbidden = function(res, msg){ //禁止访问函数
var msg = msg || 'Forbidden';
res.statusCode = 403;
res.end(resMsg(403, msg, res._restReq));
};
exports.fdate = function(format){//格式化时间
var format = typeof format === 'undefined'?false:format.toLowerCase(),
now = new Date(),
time = now.getFullYear()+"-"+(now.getMonth()+1)+"-"+now.getDate();
if(format === 'y-m-d h:m:s'){
time += ' '+now.getHours()+":"+now.getMinutes()+":"+now.getSeconds();
}
return time;
};
var rrestVersion = '';
var resMsg = exports.resMsg = function(){
var version = fs.readFileSync(__dirname+'/../package.json', 'utf-8');
if(version){
version = rrestVersion = JSON.parse(version).version;
}
return function(code, msg, req){
var code = code || 500;
var str = '<html><head><title>'+code+' '+msg+'</title></head><body bgcolor="white"><center><h1>'+code+' '+msg+'</h1></center><hr><center><p>rrestjs / '+rrestVersion+' <br/> high performace node.js roa & restful framework</p></center></body></html>'
str = headReq(req, str);
return str;
}
}();
exports.getVersion = function(){
return _restConfig._version = rrestVersion;
}
exports.normalize_query = function(p){
var nor_path = Path.normalize(p);
if(isWindows) return nor_path.replace(/\\/g, '/');
return nor_path;
}