UNPKG

6.52 kBJavaScriptView Raw
1var joinbuffers = require("joinbuffers");
2var isUtf8 = require('is-utf8');
3var iconv = require('iconv-lite');
4var config = require('./config.js');
5
6var prjInfo = config.get('project');
7
8var http = require('http');
9var https = require('https');
10var net = require('net');
11var debug = require('debug')('Clam:requestHandle');
12var _ = require('underscore');
13
14var path = require('path');
15var uri = require('url');
16var fs = require('fs');
17
18exports = module.exports = function () {
19 return function (req, res, next) {
20 var remotes = exports.remotes;
21 // no interfaces defined
22 if (!remotes || !remotes.length) {
23 next();
24 return;
25 }
26 var url = uri.parse(req.url);
27 _.some(remotes, function (options) {
28 // equals or matched the option pathname
29 if (options.pathname === url.pathname || url.pathname.match(options.pathname)) {
30 exports.proxy(req, res, next, options);
31 return true;
32 }
33 }) || next();
34 };
35};
36exports.remotes = [];
37
38// 'https://www.taobao.com:80/a/d/b?a=1&b=2'.match(/^(?:(http(?:s*)|socket):\/\/)*([^\:\/]+)*(?:\:(\d+))*(\/[^?]*)(?:\?(.*))*$/)
39// ["https://www.taobao.com:80/a/d/b?a=1&b=2", "https", "www.taobao.com", "80", "/a/d/b", "a=1&b=2"]
40// 'socket://www.taobao.com:80/a/d/b?a=1&b=2'.match(/^(?:(http(?:s*)|socket):\/\/)*([^\:\/]+)*(?:\:(\d+))*(\/[^?]*)(?:\?(.*))*$/)
41// ["socket://www.taobao.com:80/a/d/b?a=1&b=2", "socket", "www.taobao.com", "80", "/a/d/b", "a=1&b=2"]
42// 'socket://www.taobao.com:80/a/d/b?a=1&b=2'.match(/^(?:(http(?:s*)|socket):\/\/)*([^\:\/]+)*(?:\:(\d+))*(\/[^?]*)(?:\?(.*))*$/)
43exports._reFullURI = /^(?:(http(?:s*)|socket):\/\/)*([^\:\/]+)*(?:\:(\d+))*(\/[^?]*)(?:\?(.*))*$/;
44
45if (prjInfo && prjInfo.dataApi) {
46 _.each(prjInfo.dataApi, function (options) {
47 // 接口文件放到
48 var local = path.join(config.root(), '/.clam/dataApi/', options.local || '');
49 var remote = (options.remote || '').match(exports._reFullURI);
50 var type = options.type || 'auto';
51 var pathname = options.url;
52 if (!pathname || !remote) {
53 debug('Failed to build remote %s from %s !', remote, local);
54 }
55 exports.remotes.push({
56 pathname: pathname,
57 type: type,
58 local: local,
59 remote: {
60 // http(s) socket
61 agreement: remote[1] || 'http',
62 domain: remote[2] || 'localhost',
63 port: remote[3] || 80,
64 pathname: remote[4] || '',
65 querystring: remote[5] || ''
66 }
67 });
68 });
69}
70
71exports.handle = exports;
72exports.proxy = function (req, res, next, options) {
73 if (options.type === 'auto' || options.type === 'local') {
74 exports._local(req, res, next, options);
75 }
76 if (options.type === 'remote') {
77 exports._remote(req, res, next, options);
78 }
79};
80exports._local = function (req, res, next, options) {
81 fs.stat(options.local, function (err, stats) {
82 if (!err && stats.isFile()) {
83 var action = require(options.local);
84 if ('function' === typeof action.handle) {
85 action.handle(req, res, next, options);
86 return;
87 }
88 try {
89 // 反回的数据不是模块,就认为是json了。只允许两种。推荐直接返回json。
90 // 如果会nodejs。直接操作数据。方便。快捷
91 res.setHeader('Content-Type', 'application/json;text/json; charset=' + exports.charset);
92 res.write(JSON.stringify(action));
93 res.end();
94 } catch ( e ) {
95 debug('Stringify failed! Data: %o \n\r File: %s', action, options.local);
96 res.end(JSON.stringify({
97 "errno": 500,
98 "errmsg": "Stringify failed ! Please check your action file : "+options.local+"! "
99 }));
100 }
101 return;
102 }
103 if (options.type === 'auto') {
104 return exports._remote(req, res, next, options);
105 }
106 return next();
107 });
108};
109exports._remote = function (req, res, next, options) {
110 if (options.remote.agreement === 'http') {
111 return exports.httpProxy(req, res, next, options);
112 }
113 if (options.remote.agreement === 'https') {
114 return exports.httpsProxy(req, res, next, options);
115 }
116 if (options.remote.agreement === 'socket') {
117 return exports.socketProxy(req, res, next, options);
118 }
119 debug('Remote failed; Unknown agreement <%s> ', options.agreement);
120 next();
121};
122exports.charset = prjInfo ? prjInfo.charset[0] : "utf-8";
123exports.httpProxy = function (req, res, next, options) {
124 var remote = options.remote;
125 var url = uri.parse(req.url);
126 var pathname = remote.pathname || url.pathname;
127 var querystring = remote.querystring;
128 querystring += (url.query ? '&' : '') + (url.query || '');
129
130 var nsreq = http.request({
131 host: remote.domain,
132 port: remote.port,
133 path: pathname + '?' + querystring,
134 method: req.method,
135 headers: req.headers
136 }, function (nsres) {
137 res.statusCode = nsres.statusCode;
138 // 返回的数据中头信息确保为对方反馈数据
139 var headers = nsres.headers;
140 var headerItem;
141 var header;
142 for (header in headers) {
143 headerItem = headers[header];
144 res.setHeader(header, headerItem);
145 }
146 var buffs = [];
147 // 收到数据,立刻返回。
148 nsres.on('data', function (chunk) {
149 buffs.push(chunk);
150 });
151 // 结束同时关闭此次连接
152 nsres.on('end', function () {
153 buff = joinbuffers(buffs);
154 //fix 80% situation bom problem.quick and dirty
155 if(buff[0] === 239 && buff[1] === 187 && buff[2] === 191) {
156 buff = buff.slice(3, buff.length);
157 }
158 var buffChatset = isUtf8(buff) ? 'utf8' : 'gbk';
159 if (buffChatset !== exports.charset) {
160 buff = iconv.encode(iconv.decode(buff, buffChatset), exports.charset);
161 }
162 res.write(buff);
163 res.end();
164 });
165 });
166 req.on('data', function (chunk) {
167 nsreq.write(chunk);
168 });
169 req.on('end', function () {
170 nsreq.end();
171 });
172 nsreq.on('error', function (e) {
173 debug(e);
174 });
175};
176exports.httpsProxy = function (req, res, next, info) {
177 // ?
178};
179exports.socketProxy = function (req, res, next, info) {
180 // ?
181};
\No newline at end of file