whistle 2.10.5 → 2.10.7

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 (56) hide show
  1. package/assets/menu.html +2 -2
  2. package/assets/modal.html +2 -2
  3. package/assets/tab.html +2 -2
  4. package/bin/api.js +545 -0
  5. package/bin/import.js +3 -3
  6. package/bin/plugin.js +4 -8
  7. package/bin/use.js +22 -8
  8. package/bin/util.js +12 -7
  9. package/bin/whistle.js +11 -1
  10. package/biz/webui/cgi-bin/abort.js +14 -5
  11. package/biz/webui/cgi-bin/frames.js +12 -0
  12. package/biz/webui/cgi-bin/get-session.js +7 -3
  13. package/biz/webui/cgi-bin/is-enabled-https.js +6 -0
  14. package/biz/webui/cgi-bin/plugins/disable-plugin.js +16 -7
  15. package/biz/webui/cgi-bin/plugins/list.js +17 -0
  16. package/biz/webui/cgi-bin/plugins/plugin.js +21 -0
  17. package/biz/webui/cgi-bin/plugins/status.js +21 -0
  18. package/biz/webui/cgi-bin/rules/is-multi-select.js +7 -0
  19. package/biz/webui/cgi-bin/rules/move-top.js +11 -0
  20. package/biz/webui/cgi-bin/rules/select.js +1 -1
  21. package/biz/webui/cgi-bin/rules/select2.js +16 -0
  22. package/biz/webui/cgi-bin/rules/status.js +6 -0
  23. package/biz/webui/cgi-bin/rules/unselect.js +3 -2
  24. package/biz/webui/cgi-bin/rules/unselect2.js +18 -0
  25. package/biz/webui/cgi-bin/rules/value.js +19 -0
  26. package/biz/webui/cgi-bin/sessions.js +6 -0
  27. package/biz/webui/cgi-bin/util.js +45 -20
  28. package/biz/webui/htdocs/index.html +1 -1
  29. package/biz/webui/htdocs/js/index.js +51 -51
  30. package/biz/webui/lib/index.js +1 -3
  31. package/lib/https/index.js +7 -5
  32. package/lib/init.js +1 -0
  33. package/lib/inspectors/data.js +39 -34
  34. package/lib/inspectors/req.js +9 -6
  35. package/lib/inspectors/res.js +10 -14
  36. package/lib/plugins/index.js +2 -2
  37. package/lib/plugins/load-plugin.js +19 -8
  38. package/lib/rules/util.js +20 -10
  39. package/lib/service/composer.js +7 -22
  40. package/lib/service/data-center.js +0 -12
  41. package/lib/service/generate-saz.js +2 -2
  42. package/lib/service/service.js +8 -7
  43. package/lib/service/util.js +6 -16
  44. package/lib/socket-mgr.js +23 -17
  45. package/lib/tunnel.js +7 -17
  46. package/lib/upgrade.js +4 -8
  47. package/lib/util/common.js +78 -11
  48. package/lib/util/data-server.js +273 -0
  49. package/lib/util/file-mgr.js +7 -4
  50. package/lib/util/http-mgr.js +17 -9
  51. package/lib/util/index.js +67 -54
  52. package/lib/util/parse-url.js +11 -9
  53. package/lib/util/patch.js +2 -6
  54. package/lib/util/speed-transform.js +17 -6
  55. package/lib/util/whistle-transform.js +18 -7
  56. package/package.json +10 -7
@@ -36,7 +36,6 @@ var uploadJsonParser = bodyParser.json(UPLOAD_PARSE_CONF);
36
36
  var GET_METHOD_RE = /^get$/i;
37
37
  var WEINRE_RE = /^\/weinre\/.*/;
38
38
  var ALLOW_PLUGIN_PATHS = ['/cgi-bin/rules/list2', '/cgi-bin/values/list2', '/cgi-bin/get-custom-certs-info'];
39
- var SPECIAL_PATHS = ['/cgi-bin/rules/project'];
40
39
  var DONT_CHECK_PATHS = ['/cgi-bin/server-info', '/cgi-bin/plugins/is-enable', '/cgi-bin/plugins/get-plugins',
41
40
  '/preview.html', '/cgi-bin/rootca', '/cgi-bin/check-update', '/cgi-bin/log/set', '/cgi-bin/status'];
42
41
  var GUEST_PATHS = ['/cgi-bin/composer', '/cgi-bin/socket/data', '/cgi-bin/abort', '/cgi-bin/socket/abort',
@@ -196,8 +195,7 @@ function checkAuth(req, res) {
196
195
  if (verifyLogin(req, res, auth)) {
197
196
  return true;
198
197
  }
199
- if (config.specialAuth && SPECIAL_PATHS.indexOf(req.path) !== -1 &&
200
- config.specialAuth === req.headers['x-whistle-special-auth']) {
198
+ if (config.specialAuth && config.specialAuth === req.headers['x-whistle-special-auth']) {
201
199
  return true;
202
200
  }
203
201
  requireLogin(req, res);
@@ -198,7 +198,7 @@ function resolveWebsocket(socket, wss) {
198
198
  var delay = reqDelay + resDelay;
199
199
  if (delay > 0) {
200
200
  clearTimeout(timeout);
201
- setTimeout(end, delay);
201
+ timeout = setTimeout(end, delay);
202
202
  } else {
203
203
  end();
204
204
  }
@@ -304,7 +304,7 @@ function resolveWebsocket(socket, wss) {
304
304
  var reqDelay = util.getMatcherValue(_rules.reqDelay);
305
305
  if (reqDelay > 0) {
306
306
  clearTimeout(timeout);
307
- setTimeout(function () {
307
+ timeout = setTimeout(function () {
308
308
  timeout = setTimeout(function () {
309
309
  destroy(util.TIMEOUT_ERR);
310
310
  }, CONN_TIMEOUT);
@@ -779,7 +779,7 @@ function resolveWebsocket(socket, wss) {
779
779
  var curResHeaders = (reqSocket.headers = res.headers);
780
780
  socket.resHeaders = curResHeaders;
781
781
  getResRules(socket, res, function () {
782
- util.delay(util.getMatcherValue(_rules.resDelay), function () {
782
+ util.setDelayTimer(socket, util.delay(util.getMatcherValue(_rules.resDelay), function () {
783
783
  if (util.needAbortRes(socket)) {
784
784
  socket.__statusCode = res.statusCode;
785
785
  return destroy();
@@ -812,7 +812,7 @@ function resolveWebsocket(socket, wss) {
812
812
  resData.statusCode = code;
813
813
  reqEmitter.emit('response', data);
814
814
  execCallback(null, reqSocket);
815
- });
815
+ }));
816
816
  });
817
817
  },
818
818
  true
@@ -1015,8 +1015,10 @@ function toHttp1(req, res) {
1015
1015
  var handleAbort = function () {
1016
1016
  if (client) {
1017
1017
  client.abort();
1018
- res.destroy();
1019
1018
  client = null;
1019
+ if (!res.writableEnded && !res.finished) {
1020
+ res.destroy();
1021
+ }
1020
1022
  }
1021
1023
  };
1022
1024
  addReqInfo(req);
package/lib/init.js CHANGED
@@ -49,6 +49,7 @@ function addErrorEvents(req, res) {
49
49
  }
50
50
  countdown();
51
51
  req._hasError = true;
52
+ util.clearDelayTimer(req);
52
53
  clientReq = req._clientReq || clientReq;
53
54
  if (clientReq) {
54
55
  if (clientReq.destroy) {
@@ -19,7 +19,7 @@ function getZipType(options) {
19
19
  }
20
20
 
21
21
  function unzipBody(options, body, callback) {
22
- return zlib.unzip(getZipType(options), body, callback);
22
+ return zlib.unzip(getZipType(options), util.formatBuffer(body), callback);
23
23
  }
24
24
 
25
25
  function checkType(res) {
@@ -244,7 +244,8 @@ function emitDataEvents(req, res, proxy) {
244
244
  var captureReqStream;
245
245
  var maxReqSize;
246
246
  var reqInfo;
247
- var setReqBody = function (body, end) {
247
+ var setReqBody = function (end) {
248
+ reqBody = util.formatBuffer(reqBody);
248
249
  if (captureReqStream && cseSep) {
249
250
  reqBody = parseFrame(reqBody, false, end);
250
251
  } else {
@@ -257,7 +258,7 @@ function emitDataEvents(req, res, proxy) {
257
258
  maxReqSize = useBigData ? BIG_DATA_SIZE : (captureReqStream ? MAX_REQ_BODY_SIZE : MAX_SIZE);
258
259
  if (captureReqStream) {
259
260
  data.isCse = !!cseSep;
260
- reqBody && !reqData.body && setReqBody(reqBody);
261
+ reqBody && !reqData.body && setReqBody();
261
262
  }
262
263
  }
263
264
  };
@@ -277,32 +278,33 @@ function emitDataEvents(req, res, proxy) {
277
278
  var end = stream.end;
278
279
  stream.write = function (chunk) {
279
280
  if (chunk) {
281
+ reqData.size += chunk.length;
280
282
  if (reqBody || reqBody === null) {
281
283
  if (useFrames) {
282
284
  reqBody = '';
283
285
  } else {
284
- reqBody = reqBody ? Buffer.concat([reqBody, chunk]) : chunk;
286
+ reqBody = util.joinBuffer(reqBody, chunk);
285
287
  if (captureReqStream) {
286
- setReqBody(reqBody);
287
- }
288
- if (reqBody.length > maxReqSize) {
288
+ setReqBody();
289
+ if (reqBody && reqBody.length > maxReqSize) {
290
+ reqBody = false;
291
+ }
292
+ } else if (reqData.size > maxReqSize) {
289
293
  reqBody = false;
290
294
  }
291
295
  }
292
296
  }
293
- reqData.size += chunk.length;
294
297
  }
295
298
  updateEvent && proxy.emit(updateEvent, req.fullUrl, false);
296
299
  return write.apply(this, arguments);
297
300
  };
298
301
  stream.end = function (chunk) {
299
- if (chunk) {
300
- reqData.size += chunk.length;
301
- reqBody = reqBody ? Buffer.concat([reqBody, chunk]) : chunk;
302
- }
303
302
  requestTime = Date.now();
304
303
  if (useFrames) {
305
304
  reqBody = '';
305
+ } else if (chunk) {
306
+ reqData.size += chunk.length;
307
+ reqBody = util.joinBuffer(reqBody, chunk);
306
308
  }
307
309
  unzipBody(reqInfo, reqBody, function (err, body) {
308
310
  setRequestTime(requestTime);
@@ -310,7 +312,7 @@ function emitDataEvents(req, res, proxy) {
310
312
  data.endTime = endTime;
311
313
  }
312
314
  reqBody = err ? util.getErrorStack(err) : body;
313
- setReqBody(reqBody, true);
315
+ setReqBody(true);
314
316
  !data.isCse && setUnzipSize(body, reqData);
315
317
  checkBodySize(reqData, useBigData);
316
318
  });
@@ -339,6 +341,14 @@ function emitDataEvents(req, res, proxy) {
339
341
  }
340
342
  }
341
343
  var maxResSize = useBigData ? BIG_DATA_SIZE : (captureStream ? MAX_RES_BODY_SIZE : MAX_SIZE);
344
+ var setResBody = function (end) {
345
+ resBody = util.formatBuffer(resBody);
346
+ if (isSse) {
347
+ resBody = parseFrame(resBody, true, end);
348
+ } else {
349
+ resData.body = resBody;
350
+ }
351
+ };
342
352
  data.isSse = isSse;
343
353
  captureStream && updateReqInfo();
344
354
  if (!cleared && util.hasBody(info, req) && checkType(info)) {
@@ -353,46 +363,39 @@ function emitDataEvents(req, res, proxy) {
353
363
  var end = stream.end;
354
364
  stream.write = function (chunk) {
355
365
  if (chunk) {
366
+ resData.size += chunk.length;
356
367
  if (resBody || resBody === null) {
357
368
  if (useFrames) {
358
369
  resBody = '';
359
370
  } else {
360
- resBody = resBody ? Buffer.concat([resBody, chunk]) : chunk;
371
+ resBody = util.joinBuffer(resBody, chunk);
361
372
  if (captureStream) {
362
- if (isSse) {
363
- resBody = parseFrame(resBody, true);
364
- } else {
365
- resData.body = resBody;
373
+ setResBody();
374
+ if (resBody && resBody.length > maxResSize) {
375
+ resBody = false;
366
376
  }
367
- }
368
- if (resBody && resBody.length > maxResSize) {
377
+ } else if (resData.size > maxResSize) {
369
378
  resBody = false;
370
379
  }
371
380
  }
372
381
  }
373
- resData.size += chunk.length;
374
382
  }
375
383
  updateEvent && proxy.emit(updateEvent, req.fullUrl, true);
376
384
  return write.apply(this, arguments);
377
385
  };
378
386
  stream.end = function (chunk) {
379
- if (chunk) {
380
- resData.size += chunk.length;
381
- resBody = resBody ? Buffer.concat([resBody, chunk]) : chunk;
382
- }
383
387
  endTime = Date.now();
384
388
  delete data.abort;
385
389
  if (useFrames) {
386
390
  resBody = '';
391
+ } else if (chunk) {
392
+ resData.size += chunk.length;
393
+ resBody = util.joinBuffer(resBody, chunk);
387
394
  }
388
395
  resData.hasGzipError = unzipBody(info, resBody, function (err, body) {
389
396
  if (!data.resError) {
390
397
  resBody = err ? util.getErrorStack(err) : body;
391
- if (isSse) {
392
- resBody = parseFrame(resBody, true, true);
393
- } else {
394
- resData.body = resBody;
395
- }
398
+ setResBody(true);
396
399
  }
397
400
  !isSse && setUnzipSize(body, resData);
398
401
  checkBodySize(resData, useBigData);
@@ -442,13 +445,14 @@ function emitDataEvents(req, res, proxy) {
442
445
 
443
446
  function handleError(err) {
444
447
  req._hasError = true;
448
+ util.clearDelayTimer(req);
445
449
  if (!data || endTime || data.endTime || (data.responseTime && !err)) {
446
450
  return;
447
451
  }
448
452
  !endTime && setEndTime();
449
453
  delete data.abort;
450
- var msg = err && err.message;
451
- if (msg !== 'Aborted') {
454
+ // 不能乱优化,跟 var msg = err && err.message; if (msg !== 'Aborted' && msg !== 'aborted') 不一样
455
+ if (err && err.message !== 'Aborted' && err.message !== 'aborted') {
452
456
  data.resError = true;
453
457
  if (resData.body) {
454
458
  data.errMsg = util.getErrorStack(err);
@@ -531,10 +535,11 @@ function emitDataEvents(req, res, proxy) {
531
535
 
532
536
  module.exports = function (req, res, next) {
533
537
  emitDataEvents(req, res, this);
534
- util.delay(util.getMatcherValue(req.rules.reqDelay), function () {
538
+ var reqDelay = util.getMatcherValue(req.rules.reqDelay);
539
+ util.setDelayTimer(req, util.delay(reqDelay, function () {
535
540
  if (util.needAbortReq(req)) {
536
541
  return res.destroy();
537
542
  }
538
543
  next();
539
- });
544
+ }));
540
545
  };
@@ -10,7 +10,6 @@ var handleWeinre = require('./weinre');
10
10
  var handleLog = require('./log');
11
11
 
12
12
  var Transform = require('pipestream').Transform;
13
- var WhistleTransform = util.WhistleTransform;
14
13
  var ReplacePatternTransform = util.ReplacePatternTransform;
15
14
  var ReplaceStringTransform = util.ReplaceStringTransform;
16
15
  var FileWriterTransform = util.FileWriterTransform;
@@ -127,7 +126,7 @@ function handleReq(req, data, reqRules, delType) {
127
126
  }
128
127
 
129
128
  util.isWhistleTransformData(data) &&
130
- req.addZipTransform(new WhistleTransform(data));
129
+ req.addZipTransform(new util.WhistleTransform(data, req));
131
130
  var opList = util.parseHeaderReplace(req.rules.headerReplace).req;
132
131
  if (opList) {
133
132
  var host = req.headers.host;
@@ -159,6 +158,7 @@ function handleParams(req, params, urlParams, enableBigData) {
159
158
  var delProps = util.parseDelReqBody(req);
160
159
  var hasBody;
161
160
  var buffer;
161
+ var len = 0;
162
162
  if (params || delProps) {
163
163
  var maxReqSize = enableBigData || util.isEnable(req, 'reqMergeBigData') ? BIG_MAX_REQ_SIZE : MAX_REQ_SIZE;
164
164
  var transform;
@@ -171,15 +171,18 @@ function handleParams(req, params, urlParams, enableBigData) {
171
171
  transform._transform = function (chunk, _, callback) {
172
172
  if (chunk) {
173
173
  if (!interrupt) {
174
- buffer = buffer ? Buffer.concat([buffer, chunk]) : chunk;
174
+ len += chunk.length;
175
+ buffer = buffer || [];
176
+ buffer.push(chunk);
175
177
  chunk = null;
176
- if (buffer.length > maxReqSize) {
178
+ if (len > maxReqSize) {
177
179
  interrupt = true;
178
- chunk = buffer;
180
+ chunk = Buffer.concat(buffer, len);
179
181
  buffer = null;
180
182
  }
181
183
  }
182
184
  } else if (buffer && buffer.length) {
185
+ buffer = util.formatBuffer(buffer, len);
183
186
  var body;
184
187
  var isGBK = !util.isUtf8(buffer);
185
188
  if (isGBK) {
@@ -388,7 +391,7 @@ function handleParams(req, params, urlParams, enableBigData) {
388
391
  lastChunk = !chunk;
389
392
  if (chunk && preBuffer !== null) {
390
393
  preBuffer = preBuffer ? Buffer.concat([preBuffer, chunk]) : chunk;
391
- if (!preBuffer || preBuffer.length <= length) {
394
+ if (preBuffer.length <= length) {
392
395
  return callback(null, lastChunk ? preBuffer : null);
393
396
  }
394
397
  if (!util.startWithList(preBuffer, startBoundary)) {
@@ -11,10 +11,6 @@ var pluginMgr = require('../plugins');
11
11
  var hparser = require('hparser');
12
12
  var config = require('../config');
13
13
 
14
- var WhistleTransform = util.WhistleTransform;
15
- var SpeedTransform = util.SpeedTransform;
16
- var ReplacePatternTransform = util.ReplacePatternTransform;
17
- var ReplaceStringTransform = util.ReplaceStringTransform;
18
14
  var FileWriterTransform = util.FileWriterTransform;
19
15
  var formatHeaders = hparser.formatHeaders;
20
16
  var getRawHeaderNames = hparser.getRawHeaderNames;
@@ -89,10 +85,10 @@ function showDnsError(res, err) {
89
85
  function setCookies(headers, data) {
90
86
  var newCookies = data.headers['set-cookie'];
91
87
  if (!Array.isArray(newCookies)) {
92
- if (!newCookies || typeof newCookies !== 'string') {
88
+ if (!util.isString(newCookies)) {
93
89
  return;
94
90
  }
95
- newCookies = newCookies.split(',');
91
+ newCookies = [newCookies];
96
92
  }
97
93
  if (newCookies.length) {
98
94
  var cookies = headers['set-cookie'];
@@ -105,12 +101,12 @@ function setCookies(headers, data) {
105
101
  var newNameMap = {};
106
102
  newCookies.forEach(function (cookie) {
107
103
  var index = cookie.indexOf('=');
108
- var name = index == -1 ? name : cookie.substring(0, index);
104
+ var name = index == -1 ? cookie : cookie.substring(0, index);
109
105
  newNameMap[name] = 1;
110
106
  });
111
107
  cookies.forEach(function (cookie) {
112
108
  var index = cookie.indexOf('=');
113
- var name = index == -1 ? name : cookie.substring(0, index);
109
+ var name = index == -1 ? cookie : cookie.substring(0, index);
114
110
  if (!newNameMap[name]) {
115
111
  newCookies.push(cookie);
116
112
  }
@@ -137,9 +133,9 @@ function handleReplace(res, replacement) {
137
133
  util.isOriginalRegExp(pattern) &&
138
134
  (pattern = util.toOriginalRegExp(pattern))
139
135
  ) {
140
- res.addTextTransform(new ReplacePatternTransform(pattern, value, util.isSSE(res)));
136
+ res.addTextTransform(new util.ReplacePatternTransform(pattern, value, util.isSSE(res)));
141
137
  } else if (pattern) {
142
- res.addTextTransform(new ReplaceStringTransform(pattern, value, util.isSSE(res)));
138
+ res.addTextTransform(new util.ReplaceStringTransform(pattern, value, util.isSSE(res)));
143
139
  }
144
140
  });
145
141
  }
@@ -1008,7 +1004,7 @@ module.exports = function (req, res, next) {
1008
1004
  data.top = resPrepend;
1009
1005
  data.bottom = resAppend;
1010
1006
 
1011
- var speedTransform = data.speed || data.delay ? new SpeedTransform(data) : null;
1007
+ var speedTransform = data.speed || data.delay ? new util.SpeedTransform(data, req) : null;
1012
1008
  delete data.headers;
1013
1009
  delete data.speed;
1014
1010
  delete data.delay;
@@ -1113,7 +1109,7 @@ module.exports = function (req, res, next) {
1113
1109
  }
1114
1110
  if (util.isWhistleTransformData(data)) {
1115
1111
  data.noDoctype = util.isDisable(req, 'doctype');
1116
- res.addZipTransform(new WhistleTransform(data));
1112
+ res.addZipTransform(new util.WhistleTransform(data, req));
1117
1113
  }
1118
1114
  if (hasResBody) {
1119
1115
  handleReplace(res, replacement);
@@ -1166,7 +1162,7 @@ module.exports = function (req, res, next) {
1166
1162
  if (_resHeaders[config.ALPN_PROTOCOL_HEADER] === 'h2') {
1167
1163
  req.useH2 = true;
1168
1164
  }
1169
- util.delay(
1165
+ util.setDelayTimer(req, util.delay(
1170
1166
  util.getMatcherValue(resRules.resDelay),
1171
1167
  function () {
1172
1168
  if (req._hasError) {
@@ -1299,7 +1295,7 @@ module.exports = function (req, res, next) {
1299
1295
  util.emitError(res, e);
1300
1296
  }
1301
1297
  }
1302
- );
1298
+ ));
1303
1299
  },
1304
1300
  util.isEnable(req, 'forceReqWrite')
1305
1301
  );
@@ -516,8 +516,8 @@ var delayTimer;
516
516
 
517
517
  function refreshPlugins() {
518
518
  updating = true;
519
- delayTimer && clearTimeout(delayTimer);
520
- updateTimer && clearTimeout(updateTimer);
519
+ clearTimeout(delayTimer);
520
+ clearTimeout(updateTimer);
521
521
  delayTimer = null;
522
522
  updateTimer = null;
523
523
  getPlugin(function (result) {
@@ -46,7 +46,6 @@ var httpRequest = http.request;
46
46
  var httpsRequest = https.request;
47
47
  var formatHeaders = hparser.formatHeaders;
48
48
  var getRawHeaderNames = hparser.getRawHeaderNames;
49
- var getRawHeaders = hparser.getRawHeaders;
50
49
  var STATUS_CODES = http.STATUS_CODES || {};
51
50
  var clientIpKey = Symbol('clientIp');
52
51
  var clientPortKey = Symbol('clientPort');
@@ -333,7 +332,7 @@ var pushFrame = function (reqId, data, opts, isClient) {
333
332
  addFrame(opts);
334
333
  };
335
334
  var addParserApi = function (req, conn, state, reqId) {
336
- state = state.split(',').forEach(function (name) {
335
+ state.split(',').forEach(function (name) {
337
336
  initState(req, name);
338
337
  });
339
338
  req.on('clientFrame', function (data, opts) {
@@ -542,7 +541,7 @@ var getOptions = function (opts, binary, toServer) {
542
541
  var base64ToBuffer = function (base64) {
543
542
  if (base64) {
544
543
  try {
545
- return new Buffer(base64, 'base64');
544
+ return Buffer.from(base64, 'base64');
546
545
  } catch (e) {}
547
546
  }
548
547
  };
@@ -906,6 +905,9 @@ function wrapTunnelWriter(socket, toServer) {
906
905
  var write = socket.write;
907
906
  var end = socket.end;
908
907
  var sender = wsParser.getSender(socket, toServer);
908
+ var notNeedDrain = function() {
909
+ return socket.writableNeedDrain !== true;
910
+ };
909
911
  handleError(socket, sender);
910
912
  socket.write = function (chunk, encoding, cb) {
911
913
  if ((chunk = toBuffer(chunk))) {
@@ -917,6 +919,7 @@ function wrapTunnelWriter(socket, toServer) {
917
919
  } else {
918
920
  sender.send(chunk);
919
921
  }
922
+ return notNeedDrain();
920
923
  }
921
924
  };
922
925
  if (toServer) {
@@ -926,16 +929,19 @@ function wrapTunnelWriter(socket, toServer) {
926
929
  return write.call(this, chunk, opts, cb);
927
930
  }
928
931
  sender.send(chunk, getOptions(opts, false, true));
932
+ return notNeedDrain();
929
933
  }
930
934
  };
931
935
  socket.writeText = function (chunk) {
932
936
  if ((chunk = toBuffer(chunk))) {
933
937
  sender.send(chunk, getOptions(null, false, true));
938
+ return notNeedDrain();
934
939
  }
935
940
  };
936
941
  socket.writeBin = function (chunk) {
937
942
  if ((chunk = toBuffer(chunk))) {
938
943
  sender.send(chunk, getOptions(null, true, true));
944
+ return notNeedDrain();
939
945
  }
940
946
  };
941
947
  socket.closeWebSocket = function (code, data, cb) {
@@ -951,6 +957,7 @@ function wrapTunnelWriter(socket, toServer) {
951
957
  return write.call(this, chunk, encoding, cb);
952
958
  }
953
959
  sender.send(chunk);
960
+ return notNeedDrain();
954
961
  }
955
962
  };
956
963
  }
@@ -1021,6 +1028,9 @@ function addFrameHandler(req, socket, maxWsPayload, fromClient, toServer) {
1021
1028
  var end = socket.end;
1022
1029
  var lastOpts;
1023
1030
  var sender = wsParser.getSender(socket, toServer);
1031
+ var notNeedDrain = function() {
1032
+ return socket.writableNeedDrain !== true;
1033
+ };
1024
1034
 
1025
1035
  handleError(socket, sender, receiver);
1026
1036
  socket.emit = function (type, chunk) {
@@ -1042,16 +1052,19 @@ function addFrameHandler(req, socket, maxWsPayload, fromClient, toServer) {
1042
1052
  return write.call(this, chunk, opts, cb);
1043
1053
  }
1044
1054
  sender.send(chunk, getOptions(opts || lastOpts, false, toServer));
1055
+ return notNeedDrain();
1045
1056
  }
1046
1057
  };
1047
1058
  socket.writeText = function (chunk) {
1048
1059
  if ((chunk = toBuffer(chunk))) {
1049
1060
  sender.send(chunk, getOptions(null, false, toServer));
1061
+ return notNeedDrain();
1050
1062
  }
1051
1063
  };
1052
1064
  socket.writeBin = function (chunk) {
1053
1065
  if ((chunk = toBuffer(chunk))) {
1054
1066
  sender.send(chunk, getOptions(null, true, toServer));
1067
+ return notNeedDrain();
1055
1068
  }
1056
1069
  };
1057
1070
  socket.end = function (chunk, opts, cb) {
@@ -2253,11 +2266,9 @@ module.exports = async function (options, callback) {
2253
2266
  socket.setRules = req.setResRules = socket.setResRules = noop;
2254
2267
  resRules = null;
2255
2268
  }
2256
- var rawData = 'HTTP/1.1 ' + code + ' ' + (msg || svrRes.statusMessage || STATUS_CODES[code] || '');
2257
- if (headers = headers && getRawHeaders(formatRawHeaders(headers, svrRes))) {
2258
- rawData += '\r\n' + headers;
2259
- }
2260
- socket.write(rawData + '\r\n\r\n', 'binary');
2269
+ headers = headers && formatRawHeaders(headers, svrRes);
2270
+ msg = msg || svrRes.statusMessage || STATUS_CODES[code] || '';
2271
+ socket.write(common.getHttpMeta('HTTP/1.1', code, msg, headers), 'binary');
2261
2272
  };
2262
2273
  req.request = function (uri, cb, opts) {
2263
2274
  var args = normalizeArgs(uri, cb, req, curUrl, true, opts);
package/lib/rules/util.js CHANGED
@@ -166,14 +166,19 @@ function unselectRulesFile(file, force, all) {
166
166
  }
167
167
  var selectedList = getSelectedRulesList();
168
168
  var hasChanged;
169
- all = all || [file];
170
- all.forEach(function(name) {
171
- var index = selectedList.indexOf(name);
172
- if (index != -1) {
173
- selectedList.splice(index, 1);
174
- hasChanged = true;
175
- }
176
- });
169
+ if (file === false) {
170
+ hasChanged = selectedList.length;
171
+ selectedList = [];
172
+ } else {
173
+ all = all || [file];
174
+ all.forEach(function(name) {
175
+ var index = selectedList.indexOf(name);
176
+ if (index != -1) {
177
+ selectedList.splice(index, 1);
178
+ hasChanged = true;
179
+ }
180
+ });
181
+ }
177
182
  if (hasChanged) {
178
183
  rulesStorage.setProperty('selectedList', selectedList);
179
184
  parseRules();
@@ -185,7 +190,7 @@ function unselectRulesFile(file, force, all) {
185
190
  function allowMultipleChoice() {
186
191
  return (
187
192
  !config.disabledMultipleOption &&
188
- propertiesStorage.getProperty('allowMultipleChoice')
193
+ !!propertiesStorage.getProperty('allowMultipleChoice')
189
194
  );
190
195
  }
191
196
 
@@ -290,8 +295,10 @@ exports.rules = {
290
295
  }
291
296
  });
292
297
  return {
298
+ laterRulesFirst: propertiesStorage.getProperty('backRulesFirst') === true,
293
299
  pluginsDisabled: propertiesStorage.getProperty('disabledAllPlugins'),
294
300
  disabled: !config.notAllowedDisableRules && propertiesStorage.getProperty('disabledAllRules'),
301
+ multiSelect: allowMultipleChoice(),
295
302
  list: list
296
303
  };
297
304
  },
@@ -320,6 +327,9 @@ exports.rules = {
320
327
  exists: function(name) {
321
328
  return rulesStorage.existsFile(name);
322
329
  },
330
+ isSelected: function(name) {
331
+ return getSelectedRulesList().indexOf(name) !== -1;
332
+ },
323
333
  recycleBin: rulesStorage.recycleBin,
324
334
  enableBackRulesFirst: function (backRulesFirst) {
325
335
  var curFlag = propertiesStorage.getProperty('backRulesFirst') === true;
@@ -338,7 +348,7 @@ exports.rules = {
338
348
  });
339
349
  return {
340
350
  backRulesFirst: propertiesStorage.getProperty('backRulesFirst') === true,
341
- allowMultipleChoice: !!propertiesStorage.getProperty('allowMultipleChoice'),
351
+ allowMultipleChoice: allowMultipleChoice(),
342
352
  list: list
343
353
  };
344
354
  },
@@ -11,7 +11,6 @@ var Storage = require('../rules/storage');
11
11
  var dataCenter = require('./data-center');
12
12
 
13
13
  var formatHeaders = hparser.formatHeaders;
14
- var getRawHeaders = hparser.getRawHeaders;
15
14
  var getRawHeaderNames = hparser.getRawHeaderNames;
16
15
  var config = {};
17
16
  var noop = common.noop;
@@ -68,10 +67,8 @@ function handleComposerHistory(data, isConn) {
68
67
  body: body.length > MAX_BODY_LEN ? body.substring(0, MAX_BODY_LEN) : body,
69
68
  isHexText: !!data.isHexText,
70
69
  base64: base64,
71
- enableProxyRules: !!data.enableProxyRules
70
+ enableProxyRules: data.enableProxyRules
72
71
  };
73
- var dataHash;
74
- var base64Hash;
75
72
  for (var i = 0, len = composerHistory.length; i < len; i++) {
76
73
  var item = composerHistory[i];
77
74
  if (
@@ -81,16 +78,12 @@ function handleComposerHistory(data, isConn) {
81
78
  item.body === result.body &&
82
79
  item.base64 === result.base64 &&
83
80
  !item.useH2 !== result.useH2 &&
84
- !item.enableProxyRules !== result.enableProxyRules
81
+ item.enableProxyRules === result.enableProxyRules
85
82
  ) {
86
- dataHash = item.dataHash;
87
- base64Hash = item.base64Hash;
88
83
  composerHistory.splice(i, 1);
89
84
  break;
90
85
  }
91
86
  }
92
- result.dataHash = dataHash;
93
- result.base64Hash = base64Hash;
94
87
  composerHistory.unshift(result);
95
88
  var overflow = composerHistory.length - MAX_HISTORY_LEN;
96
89
  if (overflow > 0) {
@@ -183,16 +176,6 @@ function handleConnect(options, cb, count, testId) {
183
176
  }
184
177
  }
185
178
 
186
- function getReqRaw(options) {
187
- var headers = options.headers;
188
- var statusLine = options.method +' ' + (options.path || '/') +' ' + 'HTTP/1.1';
189
- var raw = statusLine;
190
- if (headers = getRawHeaders(headers)) {
191
- raw += '\r\n' + headers;
192
- }
193
- return raw + '\r\n\r\n';
194
- }
195
-
196
179
  function handleWebSocket(options, cb, count, testId) {
197
180
  count = getReqCount(count);
198
181
  if (options.protocol === 'https:' || options.protocol === 'wss:') {
@@ -213,7 +196,7 @@ function handleWebSocket(options, cb, count, testId) {
213
196
  if (err) {
214
197
  handleCallback(execCb, err, null, testId);
215
198
  } else {
216
- socket.write(getReqRaw(options));
199
+ socket.write(common.getHttpMeta(options.method, options.path || '/', 'HTTP/1.1', options.headers));
217
200
  var data = options.body;
218
201
  if ((!data || !data.length) && !cb) {
219
202
  return drain(socket);
@@ -293,7 +276,7 @@ function handleHttp(options, cb, count, reqId, testId) {
293
276
  if (buffer === null) {
294
277
  return;
295
278
  }
296
- timer && clearTimeout(timer);
279
+ clearTimeout(timer);
297
280
  var headers = svrRes.headers;
298
281
  if (typeof headers.trailer === 'string' && headers.trailer.indexOf(',') !== -1) {
299
282
  headers.trailer = headers.trailer.split(',');
@@ -397,7 +380,9 @@ exports.handleRequest = function(req, res) {
397
380
  headers[config.FROM_COM_HEADER] = '1';
398
381
  var isTest = reqBody.isTest;
399
382
  var testId;
400
- if (reqBody.enableProxyRules === false || isTest) {
383
+ var enableProxyRules = reqBody.enableProxyRules !== false && !reqBody.disabledGlobalRules;
384
+ reqBody.enableProxyRules = enableProxyRules;
385
+ if (!enableProxyRules || isTest) {
401
386
  headers[config.DISABLE_RULES_HEADER] = '1';
402
387
  if (isTest) {
403
388
  testId = ++testIndex + '';