thinkjs/lib/Lib/Core/Http.js
2014-07-03 16:27:56 +08:00

347 lines
10 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 对HttpRequest和HttpResponse 2个对象重新包装
* @type {Object}
*/
var querystring = require('querystring');
var url = require('url');
var cookie = thinkRequire('Cookie');
var EventEmitter = require('events').EventEmitter;
var multiparty = require('multiparty');
var localIp = '127.0.0.1';
module.exports = Class(function(){
'use strict';
return {
init: function(req, res){
this.req = req;
this.res = res;
//http对象为EventEmitter的实例
this.http = new EventEmitter();
//记录当前请求的开始时间
this.http.startTime = Date.now();
},
/**
* 执行
* @param {Function} callback [description]
* @return Promise [description]
*/
run: function(){
this._request();
this._response();
//数组的indexOf要比字符串的indexOf略快
var methods = ['POST', 'PUT', 'PATCH'];
if (methods.indexOf(this.req.method) > -1) {
return this.getPostData();
}
return getPromise(this.http);
},
/**
* 检测是否含有post数据
* @return {Boolean} [description]
*/
hasPostData: function(){
if ('transfer-encoding' in this.req.headers) {
return true;
}
var contentLength = this.req.headers['content-length'] | 0;
return contentLength > 0;
},
/**
* 含有文件的表单上传
* @return {[type]} [description]
*/
_filePost: function(){
var deferred = getDefer();
var self = this;
var form = new multiparty.Form({
maxFieldsSize: C('post_max_fields_size'),
maxFields: C('post_max_fields'),
maxFilesSize: C('post_max_file_size')
});
form.on('file', function(name, value){
self.http.file[name] = value;
});
form.on('field', function(name, value){
self.http.post[name] = value;
});
form.on('close', function(){
deferred.resolve(self.http);
});
//有错误后直接拒绝当前请求
form.on('error', function(){
self.res.statusCode = 413;
self.res.end();
});
form.parse(this.req);
return deferred.promise;
},
/**
* 普通的表单上传
* @return {[type]} [description]
*/
_commonPost: function(){
var buffers = [];
var length = 0;
var self = this;
var deferred = getDefer();
this.req.on('data', function(chunk){
buffers.push(chunk);
length += chunk.length;
});
this.req.on('end', function(){
//如果长度超过限制,直接拒绝
if (length > C('post_max_fields_size')) {
self.res.statusCode = 413;
self.res.end();
return;
}
self.http.payload = Buffer.concat(buffers).toString();
tag('form_parse', self.http).then(function(){
//默认使用querystring.parse解析
if (isEmpty(self.http.post) && self.http.payload) {
self.http.post = querystring.parse(self.http.payload) || {}
}
deferred.resolve(self.http);
})
});
return deferred.promise;
},
/**
* 获取POST过来的数据包含上传的文件
* 依赖multiparty库
* @return {[type]} [description]
*/
getPostData: function(){
//没有post数据直接返回
if (!this.hasPostData()) {
return getPromise(this.http);
}
//上传的数据中是否含有文件的检测正则
var multiReg = /^multipart\/(form-data|related);\s*boundary=(?:"([^"]+)"|([^;]+))$/i;
if (multiReg.test(this.http.contentType)) {
return this._filePost();
}else{
return this._commonPost();
}
},
/**
* HttpRequest增强
* @return {[type]} [description]
*/
_request: function(){
var req = {
//http版本号
version: this.req.httpVersion,
//请求方法
method: this.req.method,
//请求头
headers: this.req.headers,
getHeader: function(name){
return this.headers[name] || '';
},
//请求的Content-Type
contentType: (this.req.headers['content-type'] || '').split(';')[0].trim(),
//post信息
post: {},
//上传的文件信息
file: {},
//请求用户的ip
ip: function(){
var connection = this.req.connection;
var socket = this.req.socket;
var ip = (connection && connection.remoteAddress) || (socket && socket.remoteAddress);
if (ip && ip !== localIp) {
return ip;
}
return this.headers['x-forwarded-for'] || this.headers['x-real-ip'] || localIp;
},
//请求的cookie
cookie: cookie.parse(this.req.headers.cookie || '')
};
extend(this.http, req);
//解析url中的参数
var urlInfo = url.parse('//' + req.headers.host + this.req.url, true, true);
this.http.pathname = urlInfo.pathname;
//query只记录?后面的参数
this.http.query = urlInfo.query;
//get包含路由解析追加的参数
this.http.get = extend({}, urlInfo.query);
//主机名,带端口
this.http.host = urlInfo.host;
//主机名,不带端口
this.http.hostname = urlInfo.hostname;
//将原生的request对象放在http上方便后续在controller等地方使用
this.http.req = this.req;
},
/**
* HttpResponse增强
* @return {[type]} [description]
*/
_response: function(){
var res = {
/**
* 一次请求下可能会发送多个Cookie所以这里不能立即发送
* 需要临时存起来,到输出内容前统一发送
* @type {Object}
*/
_cookie: {},
/**
* 发送header
* @param {[type]} name [description]
* @param {[type]} value [description]
*/
setHeader: function(name, value){
if (this.res.headersSent) {
if (APP_DEBUG) {
console.log('headers has been sent.', name, value);
}
return;
}
this.res.setHeader(name, value);
},
/**
* 设置cookie
* @param {[type]} name [description]
* @param {[type]} value [description]
* @param {[type]} options [description]
*/
setCookie: function(name, value, options){
options = options || {};
if (typeof options === 'number') {
options = {timeout: options};
}
var timeout = options.timeout;
if (timeout === undefined) {
timeout = C('cookie_timeout');
}
delete options.timeout;
//if value is null, remove cookie
if (value === null) {
timeout = -1000;
}
var defaultOptions = {
path: C('cookie_path'),
domain: C('cookie_domain'),
expires: new Date (Date.now() + timeout * 1000)
};
if (timeout === 0) {
delete defaultOptions.expires;
}
for(var key in options){
defaultOptions[key.toLowerCase()] = options[key];
}
defaultOptions.name = name;
defaultOptions.value = encodeURIComponent(value + '');
this._cookie[name] = defaultOptions;
},
/**
* 将队列中的cookie发送出去
* @return {[type]} [description]
*/
sendCookie: function(){
var cookies = Object.values(this._cookie).map(function(item){
return cookie.stringify(item.name, item.value, item);
});
if (cookies.length) {
this.setHeader('Set-Cookie', cookies);
//发送Cookie后不清除_cookie内容websocket里需要读取
//this._cookie = {};
}
},
/**
* url跳转
* @param {[type]} url [description]
* @param {[type]} code [description]
* @return {[type]} [description]
*/
redirect: function(url, code){
this.res.statusCode = code || 302;
this.setHeader('Location', url || '/');
this.end();
},
/**
* 发送执行时间
* @param {[type]} name [description]
* @return {[type]} [description]
*/
sendTime: function(name){
var time = Date.now() - this.startTime;
this.setHeader('X-' + name, time + 'ms');
},
/**
* 输出内容
* @param {[type]} obj [description]
* @param {[type]} encoding [description]
* @return {[type]} [description]
*/
echo: function(obj, encoding){
this.sendCookie();
if (isArray(obj) || isObject(obj)) {
obj = JSON.stringify(obj);
}
if (!isString(obj) && !(obj instanceof Buffer)) {
obj += '';
}
this.res.write(obj, encoding || C('encoding'));
},
/**
* 结束URL
* @return {[type]} [description]
*/
end: function(){
this.emit('beforeEnd', this);
this.sendCookie();
this.res.end();
this.emit('afterEnd', this);
}
};
extend(this.http, res);
//将原生的response对象放在http上方便后续controller等地方使用
this.http.res = this.res;
}
};
});
/**
* 获取默认的http信息
* @param {[type]} data [description]
* @return {[type]} [description]
*/
module.exports.getDefaultHttp = function(data){
'use strict';
data = data || {};
if (isString(data)) {
if (data[0] === '{') {
data = JSON.parse(data);
}else if (/^[\w]+\=/.test(data)) {
data = querystring.parse(data);
}else{
data = {url: data};
}
}
var fn = function(){
return '';
};
var url = data.url || '';
if (url.indexOf('/') !== 0) {
url = '/' + url;
}
return {
req: {
httpVersion: '1.1',
method: data.method || 'GET',
url: url,
headers: extend({
host: data.host || localIp
}, data.headers || {}),
connection: {
remoteAddress: data.ip || localIp
}
},
res: {
end: data.end || data.close || fn,
write: data.write || data.send || fn,
setHeader: fn
}
};
};