/** * 对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 } }; };