whistle 2.9.109 → 2.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/assets/js/log.js +8 -4
  2. package/bin/util.js +1 -1
  3. package/biz/index.js +12 -10
  4. package/biz/webui/cgi-bin/certs/active.js +6 -0
  5. package/biz/webui/cgi-bin/certs/remove.js +0 -1
  6. package/biz/webui/cgi-bin/certs/upload.js +0 -1
  7. package/biz/webui/cgi-bin/util.js +7 -20
  8. package/biz/webui/htdocs/index.html +1 -1
  9. package/biz/webui/htdocs/js/index.js +43 -43
  10. package/biz/webui/lib/index.js +10 -13
  11. package/lib/config.js +17 -36
  12. package/lib/handlers/file-proxy.js +2 -1
  13. package/lib/handlers/http-proxy.js +1 -1
  14. package/lib/https/ca.js +34 -27
  15. package/lib/https/h2.js +1 -1
  16. package/lib/https/index.js +21 -17
  17. package/lib/index.js +1 -1
  18. package/lib/init.js +13 -12
  19. package/lib/inspectors/req.js +13 -10
  20. package/lib/inspectors/res.js +375 -390
  21. package/lib/inspectors/rules.js +11 -0
  22. package/lib/plugins/compat.js +108 -0
  23. package/lib/plugins/get-plugins-sync.js +1 -4
  24. package/lib/plugins/index.js +92 -284
  25. package/lib/plugins/load-plugin.js +205 -223
  26. package/lib/plugins/module-paths.js +3 -4
  27. package/lib/plugins/proxy.js +4 -7
  28. package/lib/rules/index.js +69 -66
  29. package/lib/rules/protocols.js +6 -0
  30. package/lib/rules/rules.js +44 -32
  31. package/lib/rules/util.js +30 -0
  32. package/lib/service/composer.js +5 -5
  33. package/lib/service/util.js +7 -21
  34. package/lib/tunnel.js +15 -12
  35. package/lib/upgrade.js +9 -10
  36. package/lib/util/common.js +171 -48
  37. package/lib/util/file-mgr.js +3 -7
  38. package/lib/util/index.js +101 -143
  39. package/package.json +1 -1
  40. package/README-en_US.md +0 -80
  41. package/biz/webui/htdocs/img/assistant.svg +0 -1
@@ -1,6 +1,8 @@
1
1
  var listenerCount = require('../util/patch').listenerCount;
2
2
  var path = require('path');
3
3
  var fs = require('fs');
4
+ var net = require('net');
5
+ var qs = require('querystring');
4
6
  var express = require('express');
5
7
  var os = require('os');
6
8
  var format = require('util').format;
@@ -22,15 +24,19 @@ var rootRequire = require('../../require');
22
24
  var extractSaz = require('../service/extract-saz');
23
25
  var generateSaz = require('../service/generate-saz');
24
26
  var getSharedStorage = require('./shared-storage');
27
+ var compat = require('./compat');
25
28
 
26
29
  var getEncodeTransform = transproto.getEncodeTransform;
27
30
  var getDecodeTransform = transproto.getDecodeTransform;
28
31
  var setInternalOptions = common.setInternalOptions;
32
+ var toBuffer = common.toBuffer;
33
+ var readStream = common.readStream;
29
34
 
30
35
  var LEVELS = ['log', 'error', 'warn', 'info', 'debug', 'trace'];
31
36
  var HTTPS_RE = /^(?:https|wss):\/\//;
32
37
  var MAX_BODY_SIZE = 1024 * 256;
33
38
  var PING_INTERVAL = 22000;
39
+ var LOCALHOST = '127.0.0.1';
34
40
  var sessionStorage = new LRU({
35
41
  maxAge: 1000 * 60 * 12,
36
42
  max: 1600
@@ -42,6 +48,10 @@ var formatHeaders = hparser.formatHeaders;
42
48
  var getRawHeaderNames = hparser.getRawHeaderNames;
43
49
  var getRawHeaders = hparser.getRawHeaders;
44
50
  var STATUS_CODES = http.STATUS_CODES || {};
51
+ var clientIpKey = Symbol('clientIp');
52
+ var clientPortKey = Symbol('clientPort');
53
+ var remoteAddrKey = Symbol('remoteAddr');
54
+ var remotePortKey = Symbol('remotePort');
45
55
  var pluginName;
46
56
 
47
57
  var QUERY_RE = /\?.*$/;
@@ -59,19 +69,15 @@ var MAX_BUF_LEN = 1024 * 1024;
59
69
  var TIMEOUT = 300;
60
70
  var REQ_INTERVAL = 16;
61
71
  var pluginOpts, storage, sharedStorage;
62
- var pluginKeyMap = {};
63
72
  var MASK_OPTIONS = { mask: true };
64
73
  var BINARY_MASK_OPTIONS = { mask: true, binary: true };
65
74
  var BINARY_OPTIONS = { binary: true };
66
75
  /* eslint-disable no-undef */
67
- var REQ_ID_KEY =
68
- typeof Symbol === 'undefined' ? '$reqId_' + Date.now() : Symbol();
69
- var SESSION_KEY =
70
- typeof Symbol === 'undefined' ? '$session_' + Date.now() : Symbol();
71
- var FRAME_KEY =
72
- typeof Symbol === 'undefined' ? '$frame_' + Date.now() : Symbol();
73
- var REQ_KEY = typeof Symbol === 'undefined' ? '$req_' + Date.now() : Symbol();
74
- var CLOSED = typeof Symbol === 'undefined' ? '$colsed_' + Date.now() : Symbol();
76
+ var REQ_ID_KEY = Symbol('reqId');
77
+ var SESSION_KEY = Symbol('session');
78
+ var FRAME_KEY = Symbol('frame');
79
+ var REQ_KEY = Symbol('req');
80
+ var CLOSED = Symbol('closed');
75
81
  var NOT_NAME_RE = /[^\w.-]/;
76
82
  /* eslint-enable no-undef */
77
83
  var index = 1000;
@@ -81,19 +87,16 @@ var certsCache = new LRU({ max: 256 });
81
87
  var certsCallbacks = {};
82
88
  var ctx;
83
89
  var PLUGIN_HOOK_NAME_HEADER;
84
- var UPGRADE_HEADER;
85
- var GLOBAL_PLUGIN_VARS_HEAD;
86
- var PLUGIN_VARS_HEAD;
87
- var FROM_TUNNEL_HEADER;
88
- var REMOTE_ADDR_HEAD;
89
- var REMOTE_PORT_HEAD;
90
- var SNI_TYPE_HEADER;
90
+ var CLIENT_INFO_HEADER;
91
91
  var PROXY_ID_HEADER;
92
- var REQ_FROM_HEADER;
93
92
  var debugMode;
94
93
  var pluginInited;
95
94
  var writeDevLog;
96
95
 
96
+ var addRemoteInfo = function(req, headers) {
97
+ headers[CLIENT_INFO_HEADER] = [req[clientIpKey], req[clientPortKey], req[remoteAddrKey], req[remotePortKey]].join();
98
+ };
99
+
97
100
  process._handlePforkUncaughtException = function (msg, e) {
98
101
  msg = [
99
102
  'From: ' + pluginName + pluginVersion,
@@ -154,13 +157,6 @@ var requestData = function (options, callback) {
154
157
  });
155
158
  };
156
159
 
157
- var getValue = function (req, name) {
158
- var value = req.headers[name] || '';
159
- try {
160
- return value ? decodeURIComponent(value) : '';
161
- } catch (e) {}
162
- return String(value);
163
- };
164
160
  var setContext = function (req) {
165
161
  if (ctx) {
166
162
  req.ctx = ctx;
@@ -168,7 +164,81 @@ var setContext = function (req) {
168
164
  req.localStorage = storage;
169
165
  req.Storage = Storage;
170
166
  req.sharedStorage = sharedStorage;
171
- req.clientIp = getValue(req, pluginOpts.CLIENT_IP_HEADER) || '127.0.0.1';
167
+ // 原始请求信息 (Original request information)
168
+ var oReq = (req.originalReq = {});
169
+ var oRes = (req.originalRes = {});
170
+ var headers = req.headers;
171
+ var sessionInfo = common.getSessionInfo(headers);
172
+ if (!sessionInfo) {
173
+ var clientIp = headers[common.CLIENT_IP_HEADER];
174
+ req.clientIp = net.isIP(clientIp) ? clientIp : LOCALHOST;
175
+ req.clientPort = +headers[common.CLIENT_PORT_HEADER] || 0;
176
+ return;
177
+ }
178
+ var fullUrl = sessionInfo.fullUrl;
179
+ oReq.url = oReq.fullUrl = req.fullUrl = fullUrl;
180
+ oReq.extraUrl = sessionInfo._extraUrl;
181
+ oReq.ruleProtocol = sessionInfo._ruleProtocol;
182
+ req.isHttps = oReq.isHttps = HTTPS_RE.test(fullUrl);
183
+ req._isUpgrade = sessionInfo._isUpgrade === '1';
184
+ req.notDecompressed = oReq.notDecompressed = sessionInfo._noDecompress === '1';
185
+ req.fromTunnel = oReq.fromTunnel = sessionInfo.fromTunnel === '1';
186
+ req.fromComposer = oReq.fromComposer = sessionInfo.fromComposer === '1';
187
+ oReq.existsCustomCert = sessionInfo._existsCustomCert == '1';
188
+ oReq.isUIRequest = req.isUIRequest = sessionInfo._isUIRequest == '1';
189
+ oReq.enableCapture = sessionInfo._enableCapture == '1';
190
+ oReq.isFromPlugin = req.fromPlugin = oReq.fromPlugin = sessionInfo.isPluginReq == '1';
191
+ req[clientIpKey] = req.clientIp = oReq.clientIp = sessionInfo.clientIp || LOCALHOST;
192
+ req[clientPortKey] = req.clientPort = oReq.clientPort = +sessionInfo.clientPort || 0;
193
+ req[remoteAddrKey] = oReq.remoteAddress = sessionInfo._remoteAddr || LOCALHOST;
194
+ req[remotePortKey] = oReq.remotePort = +sessionInfo._remotePort || 0;
195
+ oReq.isHttp2 = oReq.isH2 = !!headers[common.ALPN_PROTOCOL_HEADER];
196
+ oReq.relativeUrl = sessionInfo._relativeUrl;
197
+ oReq.ruleUrl = sessionInfo._ruleUrl;
198
+ oReq.realUrl = sessionInfo._finalUrl || oReq.url;
199
+ oReq.sniValue = sessionInfo.sniRuleValue;
200
+ oReq.pipeValue = sessionInfo._pipeValue;
201
+ oReq.hostValue = sessionInfo.host;
202
+ oReq.proxyValue = sessionInfo.proxy;
203
+ oReq.pacValue = sessionInfo.pac;
204
+ oReq.globalValue = sessionInfo.globalValue;
205
+ oReq.servername = oReq.serverName = sessionInfo.serverName;
206
+ oReq.commonName = sessionInfo.commonName;
207
+ oReq.method = sessionInfo.method || 'GET';
208
+ oReq.serverIp = oRes.serverIp = sessionInfo.hostIp;
209
+ oReq.statusCode = oRes.statusCode = sessionInfo._statusCode;
210
+ oReq.ruleValue = sessionInfo._ruleValue;
211
+ oReq.pluginVars = getPluginVars(sessionInfo._pluginVarsValue);
212
+ oReq.globalPluginVars = getPluginVars(sessionInfo._globalPluginVarsValue);
213
+
214
+ var originHost = headers[common.ORIGIN_HOST_HEADER]; // for web ui;
215
+ oReq.originHost = common.isString(originHost) ? originHost : '';
216
+
217
+ var pattern = sessionInfo._rawPattern;
218
+ if (pattern && pattern[1] === ',') {
219
+ oReq.isRexExp = oReq.isRegExp = pattern[0] === '1';
220
+ oReq.pattern = pattern.substring(2);
221
+ }
222
+ var sniType = sessionInfo._sniType;
223
+ var isSNI = !sniType;
224
+ if (sniType && sniType === '1') {
225
+ isSNI = true;
226
+ req.isHttpsServer = true;
227
+ }
228
+ req.useSNI = oReq.useSNI = req.isSNI = oReq.isSNI = isSNI;
229
+ oReq.headers = headers;
230
+
231
+ var certCacheInfo = sessionInfo.hasCertCache;
232
+ oReq.certCacheName = certCacheInfo;
233
+ oReq.certCacheTime = 0;
234
+ if (certCacheInfo) {
235
+ var sepIndex = certCacheInfo.indexOf('+');
236
+ if (sepIndex !== -1) {
237
+ oReq.certCacheName = certCacheInfo.substring(0, sepIndex);
238
+ oReq.certCacheTime = parseInt(certCacheInfo.substring(sepIndex + 1)) || 0;
239
+ }
240
+ }
241
+ return compat.compatHeaders(req, sessionInfo);
172
242
  };
173
243
 
174
244
  var initState = function (req, name) {
@@ -410,11 +480,9 @@ var ADDITIONAL_FIELDS = [
410
480
  'sessionStorage'
411
481
  ];
412
482
 
413
- function getPluginVars(req, key) {
414
- var value = req.headers[key];
483
+ function getPluginVars(value) {
415
484
  value = base64ToBuffer(value);
416
485
  if (value) {
417
- delete req.headers[key];
418
486
  try {
419
487
  value = JSON.parse(value.toString());
420
488
  if (Array.isArray(value)) {
@@ -448,92 +516,17 @@ var initReq = function (req, res, isServer) {
448
516
  return getFrames(req, cb);
449
517
  };
450
518
 
451
- var reqId = getValue(req, pluginOpts.REQ_ID_HEADER);
452
- var oReq = (req.originalReq = {});
453
- var oRes = (req.originalRes = {});
454
- setContext(req);
455
- oReq.clientIp = req.clientIp;
519
+ var sessionInfo = setContext(req) || {};
520
+ var oReq = req.originalReq;
521
+ var reqId = sessionInfo.reqId;
456
522
  if (isServer) {
457
- var parseStatus = req.headers[pluginOpts.CUSTOM_PARSER_HEADER];
458
- req.customParser = oReq.customParser = notEmptyStr(parseStatus);
523
+ var parseStatus = sessionInfo.customParser;
524
+ req.customParser = oReq.customParser = !!parseStatus;
459
525
  req.customParser && addParserApi(req, res, parseStatus, reqId);
460
526
  }
461
- var conf = pluginOpts.config;
462
527
  req[REQ_ID_KEY] = oReq.id = reqId;
463
528
  addSessionStorage(req, reqId);
464
- oReq.isHttp2 = oReq.isH2 = !!req.headers[conf.ALPN_PROTOCOL_HEADER];
465
- oReq.existsCustomCert = req.headers[pluginOpts.CUSTOM_CERT_HEADER] == '1';
466
- oReq.isUIRequest = req.isUIRequest =
467
- req.headers[pluginOpts.UI_REQUEST_HEADER] == '1';
468
- oReq.enableCapture = req.headers[pluginOpts.ENABLE_CAPTURE_HEADER] == '1';
469
- oReq.isFromPlugin = req.fromPlugin = oReq.fromPlugin = req.headers[pluginOpts.PLUGIN_REQUEST_HEADER] == '1';
470
- oReq.ruleValue = getValue(req, pluginOpts.RULE_VALUE_HEADER);
471
- oReq.ruleUrl = getValue(req, pluginOpts.RULE_URL_HEADER);
472
- oReq.pipeValue = getValue(req, pluginOpts.PIPE_VALUE_HEADER);
473
- oReq.sniValue = getValue(req, pluginOpts.SNI_VALUE_HEADER);
474
- oReq.hostValue = getValue(req, pluginOpts.HOST_VALUE_HEADER);
475
- oReq.extraUrl = getValue(req, pluginOpts.EXTRA_URL_HEADER);
476
- var fullUrl = getValue(req, pluginOpts.FULL_URL_HEADER);
477
- var pattern = getValue(req, 'x-whistle-raw-pattern_');
478
- oReq.url = oReq.fullUrl = req.fullUrl = fullUrl;
479
- req.isHttps = oReq.isHttps = HTTPS_RE.test(fullUrl);
480
- oReq.remoteAddress = req.headers[REMOTE_ADDR_HEAD] || '127.0.0.1';
481
- oReq.remotePort = parseInt(req.headers[REMOTE_PORT_HEAD], 10) || 0;
482
- req.notDecompressed = oReq.notDecompressed = req.headers['x-whistle-disable-ws-decompress'] === '1';
483
- req.fromTunnel = oReq.fromTunnel = req.headers[FROM_TUNNEL_HEADER] === '1';
484
- delete req.headers[FROM_TUNNEL_HEADER];
485
- delete req.headers[REMOTE_ADDR_HEAD];
486
- delete req.headers[REMOTE_PORT_HEAD];
487
- if (pattern) {
488
- delete req.headers['x-whistle-raw-pattern_'];
489
- if (pattern[1] === ',') {
490
- oReq.isRexExp = oReq.isRegExp = pattern[0] === '1';
491
- oReq.pattern = pattern.substring(2);
492
- }
493
- }
494
- var from = req.headers[REQ_FROM_HEADER];
495
- req.fromComposer = oReq.fromComposer = from === 'W2COMPOSER';
496
- req.fromInternalPath = oReq.fromInternalPath = from === 'W2INTERNAL_PATH';
497
- oReq.originHost = req.headers['x-whistle-origin-host']; // for web ui
498
- oReq.servername = oReq.serverName = getValue(
499
- req,
500
- pluginOpts.SERVER_NAME_HEAD
501
- );
502
- var certCacheInfo = getValue(req, pluginOpts.CERT_CACHE_INFO);
503
- oReq.certCacheName = certCacheInfo;
504
- oReq.certCacheTime = 0;
505
- if (certCacheInfo) {
506
- var sepIndex = certCacheInfo.indexOf('+');
507
- if (sepIndex !== -1) {
508
- oReq.certCacheName = certCacheInfo.substring(0, sepIndex);
509
- oReq.certCacheTime = parseInt(certCacheInfo.substring(sepIndex + 1)) || 0;
510
- }
511
- }
512
- var sniType = req.headers[SNI_TYPE_HEADER];
513
- var isSNI;
514
- if (sniType) {
515
- delete req.headers[SNI_TYPE_HEADER];
516
- if (sniType === '1') {
517
- isSNI = true;
518
- req.isHttpsServer = true;
519
- }
520
- } else {
521
- isSNI = true;
522
- }
523
- req.useSNI = oReq.useSNI = req.isSNI = oReq.isSNI = isSNI;
524
- oReq.commonName = getValue(req, pluginOpts.COMMON_NAME_HEAD);
525
- oReq.realUrl = getValue(req, pluginOpts.REAL_URL_HEADER) || oReq.url;
526
- oReq.relativeUrl = getValue(req, pluginOpts.RELATIVE_URL_HEADER);
527
- oReq.method = getValue(req, pluginOpts.METHOD_HEADER) || 'GET';
528
- oReq.clientPort = getValue(req, pluginOpts.CLIENT_PORT_HEAD);
529
- oReq.globalValue = getValue(req, pluginOpts.GLOBAL_VALUE_HEAD);
530
- oReq.proxyValue = getValue(req, pluginOpts.PROXY_VALUE_HEADER);
531
- oReq.pacValue = getValue(req, pluginOpts.PAC_VALUE_HEADER);
532
- oReq.pluginVars = getPluginVars(req, PLUGIN_VARS_HEAD);
533
- oReq.globalPluginVars = getPluginVars(req, GLOBAL_PLUGIN_VARS_HEAD);
534
- oReq.serverIp = oRes.serverIp = getValue(req, pluginOpts.HOST_IP_HEADER) || '';
535
- oReq.statusCode = oRes.statusCode = getValue(req, pluginOpts.STATUS_CODE_HEADER);
536
- oReq.headers = extractHeaders(req, pluginKeyMap);
529
+ oReq.clientId = String(req.headers[common.CLIENT_ID_HEADER] || '');
537
530
  };
538
531
  var getOptions = function (opts, binary, toServer) {
539
532
  if (opts) {
@@ -821,7 +814,7 @@ var initConnectReq = function (req, res) {
821
814
  'Content-Length: ' + length,
822
815
  'Proxy-Agent: ' + pluginOpts.shortName
823
816
  ];
824
- if (err || !cb || !req.headers['x-whistle-request-tunnel-ack']) {
817
+ if (err || !cb || !req.headers[common.ACK_HEADER]) {
825
818
  resCtn.push('\r\n', body);
826
819
  res.write(resCtn.join('\r\n'));
827
820
  return cb && cb();
@@ -865,13 +858,6 @@ function getHookName(req) {
865
858
  return typeof name === 'string' ? name : null;
866
859
  }
867
860
 
868
- function isUpgrade(req) {
869
- if (req.headers[UPGRADE_HEADER]) {
870
- delete req.headers[UPGRADE_HEADER];
871
- return true;
872
- }
873
- }
874
-
875
861
  function handleError(socket, sender, receiver) {
876
862
  var emitError = function (err) {
877
863
  if (socket._emittedError) {
@@ -903,7 +889,10 @@ function getCustomBody(body, req, cb) {
903
889
  return handleCb();
904
890
  }
905
891
  var encoding = headers['content-encoding'];
906
- body = common.toBuffer(body, req) || '';
892
+ if (body && typeof body === 'object' && common.isUrlEncoded(req)) {
893
+ body = qs.stringify(body);
894
+ }
895
+ body = toBuffer(body) || '';
907
896
  if (!GZIP_RE.test(encoding)) {
908
897
  return handleCb();
909
898
  }
@@ -919,7 +908,7 @@ function wrapTunnelWriter(socket, toServer) {
919
908
  var sender = wsParser.getSender(socket, toServer);
920
909
  handleError(socket, sender);
921
910
  socket.write = function (chunk, encoding, cb) {
922
- if ((chunk = common.toBuffer(chunk))) {
911
+ if ((chunk = toBuffer(chunk))) {
923
912
  if (encoding === 'binary') {
924
913
  return write.call(this, chunk, encoding, cb);
925
914
  }
@@ -932,7 +921,7 @@ function wrapTunnelWriter(socket, toServer) {
932
921
  };
933
922
  if (toServer) {
934
923
  socket.write = function (chunk, opts, cb) {
935
- if ((chunk = common.toBuffer(chunk))) {
924
+ if ((chunk = toBuffer(chunk))) {
936
925
  if (opts === 'binary') {
937
926
  return write.call(this, chunk, opts, cb);
938
927
  }
@@ -940,12 +929,12 @@ function wrapTunnelWriter(socket, toServer) {
940
929
  }
941
930
  };
942
931
  socket.writeText = function (chunk) {
943
- if ((chunk = common.toBuffer(chunk))) {
932
+ if ((chunk = toBuffer(chunk))) {
944
933
  sender.send(chunk, getOptions(null, false, true));
945
934
  }
946
935
  };
947
936
  socket.writeBin = function (chunk) {
948
- if ((chunk = common.toBuffer(chunk))) {
937
+ if ((chunk = toBuffer(chunk))) {
949
938
  sender.send(chunk, getOptions(null, true, true));
950
939
  }
951
940
  };
@@ -957,7 +946,7 @@ function wrapTunnelWriter(socket, toServer) {
957
946
  };
958
947
  } else {
959
948
  socket.write = function (chunk, encoding, cb) {
960
- if ((chunk = common.toBuffer(chunk))) {
949
+ if ((chunk = toBuffer(chunk))) {
961
950
  if (encoding === 'binary') {
962
951
  return write.call(this, chunk, encoding, cb);
963
952
  }
@@ -1048,7 +1037,7 @@ function addFrameHandler(req, socket, maxWsPayload, fromClient, toServer) {
1048
1037
  emit.call(socket, 'data', chunk, opts);
1049
1038
  };
1050
1039
  socket.write = function (chunk, opts, cb) {
1051
- if ((chunk = common.toBuffer(chunk))) {
1040
+ if ((chunk = toBuffer(chunk))) {
1052
1041
  if (opts === 'binary') {
1053
1042
  return write.call(this, chunk, opts, cb);
1054
1043
  }
@@ -1056,12 +1045,12 @@ function addFrameHandler(req, socket, maxWsPayload, fromClient, toServer) {
1056
1045
  }
1057
1046
  };
1058
1047
  socket.writeText = function (chunk) {
1059
- if ((chunk = common.toBuffer(chunk))) {
1048
+ if ((chunk = toBuffer(chunk))) {
1060
1049
  sender.send(chunk, getOptions(null, false, toServer));
1061
1050
  }
1062
1051
  };
1063
1052
  socket.writeBin = function (chunk) {
1064
- if ((chunk = common.toBuffer(chunk))) {
1053
+ if ((chunk = toBuffer(chunk))) {
1065
1054
  sender.send(chunk, getOptions(null, true, toServer));
1066
1055
  }
1067
1056
  };
@@ -1090,16 +1079,6 @@ function formatRawHeaders(headers, req) {
1090
1079
  return formatHeaders(headers, rawNames);
1091
1080
  }
1092
1081
 
1093
- function extractHeaders(req, exlucdekyes) {
1094
- var headers = {};
1095
- Object.keys(req.headers).forEach(function (key) {
1096
- if (!exlucdekyes[key]) {
1097
- headers[key] = req.headers[key];
1098
- }
1099
- });
1100
- return headers;
1101
- }
1102
-
1103
1082
  function addErrorHandler(req, client) {
1104
1083
  var done;
1105
1084
  client.on('error', function (err) {
@@ -1137,6 +1116,7 @@ function isBodyData(result) {
1137
1116
 
1138
1117
  module.exports = async function (options, callback) {
1139
1118
  var root = options.value;
1119
+ options.CLIENT_ID_HEADER = common.CLIENT_ID_HEADER;
1140
1120
  options.extractSaz = extractSaz;
1141
1121
  options.generateSaz = generateSaz;
1142
1122
  options.zipBody = getCustomBody;
@@ -1162,7 +1142,6 @@ module.exports = async function (options, callback) {
1162
1142
  options.parseUrl = parseUrl;
1163
1143
  options.formatHeaders = formatRawHeaders;
1164
1144
  options.wsParser = wsParser;
1165
- options.RULE_PROTO_HEADER = 'x-whistle-rule-proto';
1166
1145
  pluginVersion = '@' + options.version;
1167
1146
  var wrapWsReader = function (socket, maxPayload) {
1168
1147
  wrapTunnelReader(socket, true, maxPayload);
@@ -1206,6 +1185,7 @@ module.exports = async function (options, callback) {
1206
1185
  var boundPort = config.port;
1207
1186
  var PLUGIN_HOOKS = config.PLUGIN_HOOKS;
1208
1187
  var RES_RULES_HEAD = config.RES_RULES_HEAD;
1188
+ var SHOW_LOGIN_BOX = config.SHOW_LOGIN_BOX;
1209
1189
  var setResRules = function(headers, obj) {
1210
1190
  var rules = formatRules(obj);
1211
1191
  if (!rules) {
@@ -1218,34 +1198,19 @@ module.exports = async function (options, callback) {
1218
1198
  });
1219
1199
  headers[RES_RULES_HEAD] = encodeURIComponent(obj);
1220
1200
  };
1221
- var SHOW_LOGIN_BOX = options.SHOW_LOGIN_BOX;
1222
- FROM_TUNNEL_HEADER = options.FROM_TUNNEL_HEADER;
1223
- SNI_TYPE_HEADER = config.SNI_TYPE_HEADER;
1224
- REMOTE_ADDR_HEAD = config.REMOTE_ADDR_HEAD;
1225
- REMOTE_PORT_HEAD = config.REMOTE_PORT_HEAD;
1201
+ CLIENT_INFO_HEADER = config.CLIENT_INFO_HEADER;
1226
1202
  PROXY_ID_HEADER = config.PROXY_ID_HEADER;
1227
- REQ_FROM_HEADER = config.REQ_FROM_HEADER;
1228
- options.REQ_FROM_HEADER = REQ_FROM_HEADER; // 兼容老逻辑
1229
1203
  options.getTempFilePath = function(filePath) {
1230
1204
  if (common.TEMP_PATH_RE.test(filePath)) {
1231
1205
  return path.join(config.TEMP_FILES_PATH, RegExp.$1);
1232
1206
  }
1233
1207
  };
1234
1208
 
1235
- GLOBAL_PLUGIN_VARS_HEAD = options.GLOBAL_PLUGIN_VARS_HEAD;
1236
- PLUGIN_VARS_HEAD = options.PLUGIN_VARS_HEAD;
1237
- UPGRADE_HEADER = config.UPGRADE_HEADER;
1238
- delete config.UPGRADE_HEADER;
1239
1209
  delete config.PLUGIN_HOOKS;
1240
1210
  delete config.PROXY_ID_HEADER;
1241
- delete config.REMOTE_ADDR_HEAD;
1242
- delete config.REMOTE_PORT_HEAD;
1211
+ delete config.CLIENT_INFO_HEADER;
1243
1212
  delete config.RES_RULES_HEAD;
1244
- delete config.SNI_TYPE_HEADER;
1245
- delete options.GLOBAL_PLUGIN_VARS_HEAD;
1246
- delete options.PLUGIN_VARS_HEAD;
1247
- delete options.FROM_TUNNEL_HEADER;
1248
- delete options.SHOW_LOGIN_BOX;
1213
+ delete config.SHOW_LOGIN_BOX;
1249
1214
 
1250
1215
  PLUGIN_HOOK_NAME_HEADER = config.PLUGIN_HOOK_NAME_HEADER;
1251
1216
  options.shortName = pluginName.substring(pluginName.indexOf('/') + 1);
@@ -1264,12 +1229,6 @@ module.exports = async function (options, callback) {
1264
1229
  sharedStorage = getSharedStorage(sharedFilePath);
1265
1230
  options.sharedStorage = sharedStorage;
1266
1231
  pluginOpts = options;
1267
- Object.keys(options).forEach(function (key) {
1268
- key = options[key];
1269
- if (typeof key === 'string' && !key.indexOf('x-whistle-')) {
1270
- pluginKeyMap[key] = 1;
1271
- }
1272
- });
1273
1232
  var authKey = config.authKey;
1274
1233
  delete config.authKey;
1275
1234
  var headers = {
@@ -1296,7 +1255,7 @@ module.exports = async function (options, callback) {
1296
1255
  alpnProtocol
1297
1256
  ) {
1298
1257
  var type = uri && typeof uri;
1299
- var headers, method;
1258
+ var headers, method, body;
1300
1259
  if (type !== 'string') {
1301
1260
  if (type === 'object') {
1302
1261
  if (uri.headers) {
@@ -1304,6 +1263,7 @@ module.exports = async function (options, callback) {
1304
1263
  }
1305
1264
  method = typeof uri.method === 'string' ? uri.method : null;
1306
1265
  opts = opts || uri;
1266
+ body = common.hasRequestBody(req) ? toBuffer(uri.body || uri.data) : null;
1307
1267
  uri = uri.uri || uri.url || uri.href || curUrl;
1308
1268
  } else {
1309
1269
  if (type === 'function') {
@@ -1317,7 +1277,7 @@ module.exports = async function (options, callback) {
1317
1277
  }
1318
1278
  }
1319
1279
  uri = parseUrl(uri);
1320
- headers = headers || extractHeaders(req, pluginKeyMap);
1280
+ headers = headers || req.headers;
1321
1281
  if (isWs) {
1322
1282
  headers.upgrade = headers.upgrade || 'websocket';
1323
1283
  headers.connection = 'Upgrade';
@@ -1335,14 +1295,11 @@ module.exports = async function (options, callback) {
1335
1295
  headers[PROXY_ID_HEADER] = 1;
1336
1296
  uri.host = boundIp;
1337
1297
  uri.port = boundPort;
1338
- if (req.originalReq) {
1339
- headers[REMOTE_ADDR_HEAD] = req.originalReq.remoteAddress;
1340
- headers[REMOTE_PORT_HEAD] = req.originalReq.remotePort;
1341
- }
1298
+ addRemoteInfo(req, headers);
1342
1299
  if (isHttps) {
1343
- headers[config.HTTPS_FIELD] = 1;
1300
+ headers[common.HTTPS_FIELD] = 1;
1344
1301
  if (alpnProtocol) {
1345
- headers[config.ALPN_PROTOCOL_HEADER] = alpnProtocol;
1302
+ headers[common.ALPN_PROTOCOL_HEADER] = alpnProtocol;
1346
1303
  }
1347
1304
  }
1348
1305
  isHttps = false;
@@ -1352,7 +1309,7 @@ module.exports = async function (options, callback) {
1352
1309
  if (isHttps) {
1353
1310
  if (opts.internalRequest) {
1354
1311
  isHttps = false;
1355
- headers[config.HTTPS_FIELD] = 1;
1312
+ headers[common.HTTPS_FIELD] = 1;
1356
1313
  } else {
1357
1314
  uri.protocol = 'https:';
1358
1315
  }
@@ -1361,7 +1318,7 @@ module.exports = async function (options, callback) {
1361
1318
  delete uri.hostname;
1362
1319
  uri.headers = formatRawHeaders(headers, req);
1363
1320
  uri.agent = false;
1364
- return [uri, cb, isHttps, opts];
1321
+ return [uri, cb, isHttps, opts, body];
1365
1322
  };
1366
1323
 
1367
1324
  var authPort,
@@ -1852,12 +1809,11 @@ module.exports = async function (options, callback) {
1852
1809
  customHeaders[SHOW_LOGIN_BOX] = '1';
1853
1810
  }
1854
1811
  if (result === false) {
1855
- customHeaders['x-auth-status'] = '1';
1812
+ customHeaders[common.AUTH_STATUS] = '1';
1856
1813
  if (location) {
1857
1814
  customHeaders.location = location;
1858
1815
  } else if (htmlUrl) {
1859
- customHeaders['x-auth-html-url'] =
1860
- encodeURIComponent(htmlUrl);
1816
+ customHeaders[common.AUTH_URL] = encodeURIComponent(htmlUrl);
1861
1817
  } else if (typeof htmlBody === 'string') {
1862
1818
  htmlBody = Buffer.from(htmlBody);
1863
1819
  if (htmlBody.length > MAX_BODY_SIZE) {
@@ -2052,13 +2008,11 @@ module.exports = async function (options, callback) {
2052
2008
  switch (getHookName(req)) {
2053
2009
  case PLUGIN_HOOKS.AUTH:
2054
2010
  if (authServer) {
2055
- setContext(req);
2056
2011
  authServer.emit('request', req, res);
2057
2012
  }
2058
2013
  break;
2059
2014
  case PLUGIN_HOOKS.SNI:
2060
2015
  if (sniServer) {
2061
- setContext(req);
2062
2016
  sniServer.emit('request', req, res);
2063
2017
  }
2064
2018
  break;
@@ -2071,7 +2025,7 @@ module.exports = async function (options, callback) {
2071
2025
  case PLUGIN_HOOKS.HTTP:
2072
2026
  if (httpServer) {
2073
2027
  initReq(req, res, true);
2074
- var alpnProtocol = req.headers[config.ALPN_PROTOCOL_HEADER];
2028
+ var alpnProtocol = req.headers[common.ALPN_PROTOCOL_HEADER];
2075
2029
  var curUrl = req.originalReq.realUrl;
2076
2030
  var svrRes = '';
2077
2031
  var reqRules, resRules;
@@ -2115,25 +2069,30 @@ module.exports = async function (options, callback) {
2115
2069
  uri = args[0];
2116
2070
  cb = args[1];
2117
2071
  opts = args[3];
2072
+ var reqBuff = args[4];
2118
2073
  req.setRules = req.setReqRules = res.setReqRules = noop;
2119
2074
  setReqRules(uri, getReqRules(opts, reqRules));
2120
2075
  reqRules = null;
2121
- var client = (args[2] ? httpsRequest : httpRequest)(
2122
- uri,
2123
- function (_res) {
2124
- svrRes = _res;
2125
- res.once('_wroteHead', function () {
2126
- appendTrailers(_res, res, opts && opts.trailers, req);
2127
- });
2128
- if (typeof cb === 'function') {
2129
- cb.call(this, _res);
2130
- } else {
2131
- res.writeHead();
2132
- _res.pipe(res);
2133
- }
2134
- }
2076
+ if (reqBuff && (!uri.headers || uri.headers['content-length'] != null)) {
2077
+ uri.headers = uri.headers || {};
2078
+ uri.headers['content-length'] = reqBuff.length;
2079
+ }
2080
+ var request = args[2] ? httpsRequest : httpRequest;
2081
+ var client = request(uri, function (_res) {
2082
+ svrRes = _res;
2083
+ res.once('_wroteHead', function () {
2084
+ appendTrailers(_res, res, opts && opts.trailers, req);
2085
+ });
2086
+ if (typeof cb === 'function') {
2087
+ cb.call(this, _res);
2088
+ } else {
2089
+ res.writeHead();
2090
+ _res.pipe(res);
2091
+ }
2092
+ }
2135
2093
  );
2136
2094
  addErrorHandler(req, client);
2095
+ reqBuff && client.end(reqBuff);
2137
2096
  return client;
2138
2097
  };
2139
2098
  req.passThrough = function (uri, newTrailers) {
@@ -2158,11 +2117,7 @@ module.exports = async function (options, callback) {
2158
2117
  var _request = function(reqBody, _uri) {
2159
2118
  var client = req.request(_uri || uri, function (_res) {
2160
2119
  var _response = function(resBody, trailers) {
2161
- res.writeHead(
2162
- _res.statusCode,
2163
- _res.statusMessage,
2164
- _res.headers
2165
- );
2120
+ res.writeHead(_res.statusCode, _res.statusMessage, _res.headers);
2166
2121
  appendTrailers(_res, res, trailers || newTrailers, req);
2167
2122
  if (resBody === undefined) {
2168
2123
  _res.pipe(res);
@@ -2186,7 +2141,7 @@ module.exports = async function (options, callback) {
2186
2141
  if (transformRes) {
2187
2142
  transformRes(common.wrapReadStream(_res), handleNextRes);
2188
2143
  } else if (handleRes) {
2189
- common.readStream(_res, function(rawBuffer) {
2144
+ readStream(_res, function(rawBuffer) {
2190
2145
  resBuf = rawBuffer;
2191
2146
  handleRes(rawBuffer, handleNextRes, _res);
2192
2147
  });
@@ -2215,7 +2170,7 @@ module.exports = async function (options, callback) {
2215
2170
  if (transformReq) {
2216
2171
  transformReq(common.wrapReadStream(req), handleNextReq);
2217
2172
  } else if (handleReq) {
2218
- common.readStream(req, function(rawBuffer) {
2173
+ readStream(req, function(rawBuffer) {
2219
2174
  reqBuf = rawBuffer;
2220
2175
  handleReq(rawBuffer, handleNextReq, req);
2221
2176
  });
@@ -2285,11 +2240,7 @@ module.exports = async function (options, callback) {
2285
2240
  resRules = rules;
2286
2241
  return true;
2287
2242
  };
2288
- req.writeHead = socket.writeHead = function (
2289
- code,
2290
- msg,
2291
- headers
2292
- ) {
2243
+ req.writeHead = socket.writeHead = function (code, msg, headers) {
2293
2244
  code = code > 0 ? code : svrRes.statusCode || 101;
2294
2245
  if (msg && typeof msg !== 'string') {
2295
2246
  headers = headers || msg;
@@ -2443,7 +2394,6 @@ module.exports = async function (options, callback) {
2443
2394
  }
2444
2395
 
2445
2396
  server.on('connect', function (req, socket, head) {
2446
- var isUp = isUpgrade(req);
2447
2397
  var pipeServer;
2448
2398
  switch (getHookName(req)) {
2449
2399
  case PLUGIN_HOOKS.REQ_READ:
@@ -2482,7 +2432,7 @@ module.exports = async function (options, callback) {
2482
2432
  socket.setRules = req.setResRules = socket.setResRules = noop;
2483
2433
  req.writeHead = socket.writeHead = noop;
2484
2434
  req.connect = function (uri, cb, opts) {
2485
- var headers = extractHeaders(req, pluginKeyMap);
2435
+ var headers = req.headers;
2486
2436
  var policy = headers['x-whistle-policy'];
2487
2437
  if (common.isTunnelHost(uri)) {
2488
2438
  headers.host = uri;
@@ -2518,11 +2468,7 @@ module.exports = async function (options, callback) {
2518
2468
  headers[PROXY_ID_HEADER] = req[REQ_ID_KEY];
2519
2469
  uri.host = boundIp;
2520
2470
  uri.port = boundPort;
2521
- if (req.originalReq) {
2522
- headers[REMOTE_ADDR_HEAD] =
2523
- req.originalReq.remoteAddress;
2524
- headers[REMOTE_PORT_HEAD] = req.originalReq.remotePort;
2525
- }
2471
+ addRemoteInfo(req, headers);
2526
2472
  } else {
2527
2473
  uri.host = opts.host;
2528
2474
  uri.port = opts.port > 0 ? opts.port : 80;
@@ -2542,7 +2488,7 @@ module.exports = async function (options, callback) {
2542
2488
  });
2543
2489
  return;
2544
2490
  }
2545
- if (_res.headers['x-whistle-allow-tunnel-ack']) {
2491
+ if (_res.headers[common.ALLOW_ACK]) {
2546
2492
  svrSock.write('1');
2547
2493
  }
2548
2494
  if (typeof cb === 'function') {
@@ -2567,12 +2513,12 @@ module.exports = async function (options, callback) {
2567
2513
  }
2568
2514
  break;
2569
2515
  case PLUGIN_HOOKS.TUNNEL_REQ_READ:
2570
- pipeServer = isUp ? wsReqRead : tunnelReqRead;
2516
+ initConnectReq(req, socket);
2517
+ pipeServer = req._isUpgrade ? wsReqRead : tunnelReqRead;
2571
2518
  if (pipeServer) {
2572
- initConnectReq(req, socket);
2573
2519
  req.sendEstablished(function () {
2574
2520
  socket.headers = req.headers;
2575
- if (isUp ? wsReqWrite : tunnelReqWrite) {
2521
+ if (req._isUpgrade ? wsReqWrite : tunnelReqWrite) {
2576
2522
  wrapTunnelWriter(socket);
2577
2523
  }
2578
2524
  pipeServer.emit('connect', req, socket, head);
@@ -2580,11 +2526,11 @@ module.exports = async function (options, callback) {
2580
2526
  }
2581
2527
  break;
2582
2528
  case PLUGIN_HOOKS.TUNNEL_REQ_WRITE:
2583
- pipeServer = isUp ? wsReqWrite : tunnelReqWrite;
2529
+ initConnectReq(req, socket);
2530
+ pipeServer = req._isUpgrade ? wsReqWrite : tunnelReqWrite;
2584
2531
  if (pipeServer) {
2585
- initConnectReq(req, socket);
2586
2532
  req.sendEstablished(function () {
2587
- if (isUp ? wsReqRead : tunnelReqRead) {
2533
+ if (req._isUpgrade ? wsReqRead : tunnelReqRead) {
2588
2534
  wrapTunnelReader(socket);
2589
2535
  }
2590
2536
  pipeServer.emit('connect', req, socket, head);
@@ -2592,12 +2538,12 @@ module.exports = async function (options, callback) {
2592
2538
  }
2593
2539
  break;
2594
2540
  case PLUGIN_HOOKS.TUNNEL_RES_READ:
2595
- pipeServer = isUp ? wsResRead : tunnelResRead;
2541
+ initConnectReq(req, socket);
2542
+ pipeServer = req._isUpgrade ? wsResRead : tunnelResRead;
2596
2543
  if (pipeServer) {
2597
- initConnectReq(req, socket);
2598
2544
  req.sendEstablished(function () {
2599
2545
  socket.headers = req.headers;
2600
- if (isUp ? wsResWrite : tunnelResWrite) {
2546
+ if (req._isUpgrade ? wsResWrite : tunnelResWrite) {
2601
2547
  wrapTunnelWriter(socket);
2602
2548
  }
2603
2549
  pipeServer.emit('connect', req, socket, head);
@@ -2605,11 +2551,11 @@ module.exports = async function (options, callback) {
2605
2551
  }
2606
2552
  break;
2607
2553
  case PLUGIN_HOOKS.TUNNEL_RES_WRITE:
2608
- pipeServer = isUp ? wsResWrite : tunnelResWrite;
2554
+ initConnectReq(req, socket);
2555
+ pipeServer = req._isUpgrade ? wsResWrite : tunnelResWrite;
2609
2556
  if (pipeServer) {
2610
- initConnectReq(req, socket);
2611
2557
  req.sendEstablished(function () {
2612
- if (isUp ? wsResRead : tunnelResRead) {
2558
+ if (req._isUpgrade ? wsResRead : tunnelResRead) {
2613
2559
  wrapTunnelReader(socket);
2614
2560
  }
2615
2561
  pipeServer.emit('connect', req, socket, head);
@@ -2627,6 +2573,40 @@ module.exports = async function (options, callback) {
2627
2573
  initProxy(async function(proxy) {
2628
2574
  options.connect = proxy.connect;
2629
2575
  options.request = proxy.request;
2576
+ options.streamUtils = {
2577
+ readRawBuffer: readStream,
2578
+ readBuffer: function(stream, cb) {
2579
+ readStream(stream, function() {
2580
+ stream.getBuffer(function(err, buffer) {
2581
+ if (err) {
2582
+ return stream.emit('error', err);
2583
+ }
2584
+ cb(buffer);
2585
+ });
2586
+ });
2587
+ },
2588
+ readText: function(stream, cb) {
2589
+ readStream(stream, function() {
2590
+ stream.getText(function(err, buffer) {
2591
+ if (err) {
2592
+ return stream.emit('error', err);
2593
+ }
2594
+ cb(buffer);
2595
+ });
2596
+ });
2597
+ },
2598
+ readJson: function(stream, cb) {
2599
+ readStream(stream, function() {
2600
+ stream.getJson(function(err, data) {
2601
+ if (err) {
2602
+ return stream.emit('error', err);
2603
+ }
2604
+ cb(data);
2605
+ });
2606
+ });
2607
+ }
2608
+ };
2609
+ options.readStream = readStream;
2630
2610
  options.requestInternal = function(options, cb) {
2631
2611
  if (typeof options === 'string') {
2632
2612
  options = { url: options };
@@ -2641,6 +2621,8 @@ module.exports = async function (options, callback) {
2641
2621
  port: config.port
2642
2622
  }), cb);
2643
2623
  };
2624
+ compat.compatKeys(options);
2625
+ compat.compatKeys(config);
2644
2626
  var initial = loadModule(path.join(options.value, 'initial.js')) ||
2645
2627
  loadModule(path.join(options.value, 'initialize.js'));
2646
2628
  if (initial && typeof initial !== 'function') {