whistle 2.9.35 → 2.9.36-beta

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.
@@ -18,6 +18,7 @@ var setProxy = require('./proxy');
18
18
  var rulesUtil = require('../../../lib/rules/util');
19
19
  var getRootCAFile = require('../../../lib/https/ca').getRootCAFile;
20
20
  var config = require('../../../lib/config');
21
+ var getWorker = require('../../../lib/plugins/util').getWorker;
21
22
  var loadAuthPlugins = require('../../../lib/plugins').loadAuthPlugins;
22
23
 
23
24
  var PARSE_CONF = { extended: true, limit: '3mb'};
@@ -422,6 +423,17 @@ app.get('/values', function(req, res) {
422
423
  sendText(res, rulesUtil.values.get(name));
423
424
  });
424
425
 
426
+ app.get('/web-worker.js', urlencodedParser, function(req, res) {
427
+ var body = getWorker(req.query.id);
428
+ if (!body) {
429
+ return res.status(404).end('Not Found');
430
+ }
431
+ res.writeHead(200, {
432
+ 'Content-Type': 'application/javascript; charset=utf-8'
433
+ });
434
+ res.end(body);
435
+ });
436
+
425
437
  app.all('/cgi-bin/*', function(req, res, next) {
426
438
  req.isUploadReq = UPLOAD_URLS.indexOf(req.path) !== -1;
427
439
  return req.isUploadReq ? uploadUrlencodedParser(req, res, next) : urlencodedParser(req, res, next);
@@ -676,11 +676,10 @@ function resolveWebsocket(socket, wss) {
676
676
  newUrl = util.parsePathReplace(newUrl, urlReplace) || newUrl;
677
677
  if (newUrl !== fullUrl) {
678
678
  fullUrl = newUrl;
679
- options = util.parseUrl(fullUrl);
680
679
  socket._realUrl = newUrl;
681
- } else {
682
- options = util.parseUrl(fullUrl);
683
680
  }
681
+ options = util.parseUrl(fullUrl);
682
+ socket._w2hostname = options.hostname;
684
683
  data.realUrl = fullUrl;
685
684
  var host = (headers.host = options.host);
686
685
  socket._origin = headers.origin;
@@ -726,9 +725,8 @@ function resolveWebsocket(socket, wss) {
726
725
  if (socket._origin && origin && origin !== '*') {
727
726
  curResHeaders['access-control-allow-origin'] = socket._origin;
728
727
  }
729
- var resRuleData = { headers: curResHeaders };
730
- util.setResCors(resRuleData, cors, socket);
731
- util.setResCookies(resRuleData, cookies, socket);
728
+ util.setResCors(res, cors, socket);
729
+ util.setResCookies(res, cookies, socket);
732
730
  util.setResponseFor(_rules, curResHeaders, socket, socket.hostIp);
733
731
  var delResHeaders = util.parseDelProps(socket).resHeaders;
734
732
  Object.keys(delResHeaders).forEach(function (name) {
@@ -860,8 +860,8 @@ module.exports = function (req, res, next) {
860
860
  );
861
861
  }
862
862
 
863
- util.setResCookies(data, cookies, req);
864
- util.setResCors(data, cors, req);
863
+ util.setResCookies(_res, cookies, req);
864
+ util.setResCors(_res, cors, req);
865
865
 
866
866
  var cache = util.getMatcherValue(resRules.cache);
867
867
  var enable = req.enable;
@@ -113,6 +113,8 @@ module.exports = function () {
113
113
  tunnelKey: util.getTunnelKey(conf),
114
114
  staticDir: util.getStaticDir(conf),
115
115
  pluginVars: util.getPluginVarsConf(conf),
116
+ networkColumn: util.getNetworkColumn(conf),
117
+ webWorker: pluginUtil.readWorkerSync(dir, conf),
116
118
  networkMenus: util.getPluginMenu(
117
119
  conf.networkMenus || conf.networkMenu,
118
120
  simpleName
@@ -375,6 +375,7 @@ function readPackages(obj, callback) {
375
375
  0;
376
376
  newPkg.rulesUrl = util.getCgiUrl(conf.rulesUrl);
377
377
  newPkg.valuesUrl = util.getCgiUrl(conf.valuesUrl);
378
+ newPkg.networkColumn = util.getNetworkColumn(conf);
378
379
  newPkg.networkMenus = util.getPluginMenu(
379
380
  conf.networkMenus || conf.networkMenu,
380
381
  simpleName
@@ -436,7 +437,10 @@ function readPackages(obj, callback) {
436
437
  result,
437
438
  simpleName
438
439
  );
439
- callbackHandler();
440
+ pluginUtil.readWorker(newPkg.path, conf, function(hash) {
441
+ newPkg.webWorker = hash;
442
+ callbackHandler();
443
+ });
440
444
  }
441
445
  );
442
446
  }
@@ -512,6 +516,7 @@ setTimeout(checkUpdate, 5000);
512
516
  uninstallPlugins && pluginMgr.emit('uninstall', uninstallPlugins);
513
517
  updatePlugins && pluginMgr.emit('update', updatePlugins);
514
518
  pluginMgr.emit('updateRules');
519
+ pluginUtil.resetWorkers(allPlugins);
515
520
  }
516
521
  update();
517
522
  });
@@ -899,6 +899,19 @@ function toBinary(data) {
899
899
  return Buffer.from(data);
900
900
  }
901
901
 
902
+ function getCustomBody(body, headers) {
903
+ if (body && typeof body.pipe === 'function') {
904
+ delete headers['content-length'];
905
+ return body;
906
+ }
907
+ body = toBinary(body) || '';
908
+ if (headers['content-length'] != null) {
909
+ headers['content-length'] = body ? body.length : 0;
910
+ delete headers['transfer-encoding'];
911
+ }
912
+ return body;
913
+ }
914
+
902
915
  function wrapTunnelWriter(socket, toServer) {
903
916
  var write = socket.write;
904
917
  var end = socket.end;
@@ -1569,6 +1582,7 @@ module.exports = async function (options, callback) {
1569
1582
  wrapWsWriter: wrapWsWriter
1570
1583
  }, cb);
1571
1584
  };
1585
+
1572
1586
  var initServers = function (_ctx) {
1573
1587
  ctx = _ctx || ctx;
1574
1588
  var execPlugin = require(options.value) || '';
@@ -2066,16 +2080,70 @@ module.exports = async function (options, callback) {
2066
2080
  return client;
2067
2081
  };
2068
2082
  req.passThrough = function (uri, newTrailers) {
2069
- var client = req.request(uri, function (_res) {
2070
- res.writeHead(
2083
+ var handleReq;
2084
+ var handleRes;
2085
+ if (typeof uri === 'function') {
2086
+ handleReq = uri;
2087
+ uri = null;
2088
+ }
2089
+ if (typeof newTrailers === 'function') {
2090
+ handleRes = newTrailers;
2091
+ newTrailers = null;
2092
+ }
2093
+ var _request = function(reqBody, _uri) {
2094
+ var client = req.request(_uri || uri, function (_res) {
2095
+ var _response = function(resBody, trailers) {
2096
+ res.writeHead(
2071
2097
  _res.statusCode,
2072
2098
  _res.statusMessage,
2073
2099
  _res.headers
2074
2100
  );
2075
- appendTrailers(_res, res, newTrailers, req);
2076
- _res.pipe(res);
2077
- });
2078
- req.pipe(client);
2101
+ appendTrailers(_res, res, trailers || newTrailers, req);
2102
+ if (resBody === undefined) {
2103
+ _res.pipe(res);
2104
+ } else if (resBody.pipe) {
2105
+ resBody.pipe(res);
2106
+ } else {
2107
+ res.end(resBody);
2108
+ }
2109
+ };
2110
+ if (handleRes) {
2111
+ common.readStream(_res, function(buffer) {
2112
+ handleRes(buffer, function(result) {
2113
+ if (result) {
2114
+ req.setResRules(result.rules);
2115
+ _response(getCustomBody(result.body, _res.headers), result.trailers);
2116
+ } else {
2117
+ _response('');
2118
+ }
2119
+ });
2120
+ });
2121
+ } else {
2122
+ _response();
2123
+ }
2124
+ });
2125
+ if (reqBody === undefined) {
2126
+ req.pipe(client);
2127
+ } else if (reqBody.pipe) {
2128
+ reqBody.pipe(client);
2129
+ } else {
2130
+ client.end(reqBody);
2131
+ }
2132
+ };
2133
+ if (handleReq) {
2134
+ common.readStream(req, function(buffer) {
2135
+ handleReq(buffer, function(result) {
2136
+ if (result) {
2137
+ req.setReqRules(result.rules);
2138
+ _request(getCustomBody(result.body, req.headers) || '', result.uri || result.url);
2139
+ } else {
2140
+ _request('');
2141
+ }
2142
+ });
2143
+ });
2144
+ } else {
2145
+ _request();
2146
+ }
2079
2147
  };
2080
2148
  httpServer.emit('request', req, res);
2081
2149
  }
@@ -12,6 +12,51 @@ var PLUGIN_NAME_RE = /^(?:@[\w-]+\/)?(whistle\.[a-z\d_\-]+)$/;
12
12
  var DEV_PLUGINS_PATH = config.DEV_PLUGINS_PATH;
13
13
  var MAX_EXPIRE = 36000;
14
14
  var UTF8_OPTIONS = { encoding: 'utf8' };
15
+ var workerScript = util.readFileSync(path.join(__dirname, '../../assets/js/worker.js'));
16
+ var workers = {};
17
+
18
+ function replaceScript(ctn) {
19
+ return workerScript.replace('/*sourcecode*/', ctn);
20
+ }
21
+
22
+ exports.readWorkerSync = function(root, conf) {
23
+ var file = util.getWebWorker(conf);
24
+ file = file && util.readFileSync(path.join(root, file));
25
+ if (!file) {
26
+ return;
27
+ }
28
+ var id = util.createHash(file);
29
+ workers[id] = replaceScript(file);
30
+ return id;
31
+ };
32
+
33
+ exports.readWorker = function(root, conf, callback) {
34
+ var file = util.getWebWorker(conf);
35
+ if (!file) {
36
+ return callback();
37
+ }
38
+ readFile(path.join(root, file), function(err, ctn) {
39
+ if (err || !ctn) {
40
+ return callback();
41
+ }
42
+ var id = util.createHash(ctn);
43
+ workers[id] = replaceScript(ctn);
44
+ callback(id);
45
+ });
46
+ };
47
+
48
+ exports.getWorker = function(id) {
49
+ return workers[id];
50
+ };
51
+
52
+ exports.resetWorkers = function(plugins) {
53
+ var _workers = {};
54
+ Object.keys(plugins).forEach(function(key) {
55
+ var plugin = plugins[key];
56
+ _workers[plugin.webWorker] = workers[plugin.webWorker];
57
+ });
58
+ workers = _workers;
59
+ };
15
60
 
16
61
  function isOrgModule(name) {
17
62
  return ORG_RE.test(name);
@@ -324,3 +324,13 @@ exports.wrapRuleValue = function(key, value, size, policy) {
324
324
  exports.isGroup = function(name) {
325
325
  return name && name[0] === '\r';
326
326
  };
327
+
328
+ exports.readStream = function(stream, callback) {
329
+ var buffer = null;
330
+ stream.on('data', function(chunk) {
331
+ buffer = buffer ? Buffer.concat([buffer, chunk]) : chunk;
332
+ });
333
+ stream.once('end', function() {
334
+ callback(buffer);
335
+ });
336
+ };
package/lib/util/index.js CHANGED
@@ -61,6 +61,11 @@ var HTTP_RE = /^https?:\/\/[^/?]/;
61
61
  var SEP_RE = /[|&]/;
62
62
  var ctxTimer;
63
63
  var END_RE = /[/\\]$/;
64
+ var EXPIRED_SEC = -123456;
65
+ var EXP_COOKIE = { maxAge: EXPIRED_SEC, path: '/' };
66
+ var EXP_SECURE_COOKIE = { maxAge: EXPIRED_SEC, path: '/', secure: true };
67
+ var EXP_COOKIE_D = { maxAge: EXPIRED_SEC, path: '/' };
68
+ var EXP_SECURE_COOKIE_D = { maxAge: EXPIRED_SEC, path: '/', secure: true };
64
69
  var resetContext = function () {
65
70
  ctxTimer = null;
66
71
  CONTEXT = vm.createContext();
@@ -705,6 +710,12 @@ if (isString(hostname)) {
705
710
  simpleHostname = simpleHostname ? simpleHostname + '.' : '';
706
711
  }
707
712
 
713
+ function createHash(str) {
714
+ return crypto.createHash('sha256').update(str).digest('hex');
715
+ }
716
+
717
+ exports.createHash = createHash;
718
+
708
719
  var clientId = [
709
720
  hostname,
710
721
  os.platform(),
@@ -1927,7 +1938,9 @@ exports.replaceUrlQueryString = replaceUrlQueryString;
1927
1938
  exports.decodeBuffer = fileMgr.decode;
1928
1939
 
1929
1940
  function setHeaders(data, obj) {
1930
- data.headers = data.headers || {};
1941
+ if (!data.headers) {
1942
+ data.headers = {};
1943
+ }
1931
1944
  for (var i in obj) {
1932
1945
  data.headers[i] = obj[i];
1933
1946
  }
@@ -1937,7 +1950,9 @@ function setHeaders(data, obj) {
1937
1950
  exports.setHeaders = setHeaders;
1938
1951
 
1939
1952
  function setHeader(data, name, value) {
1940
- data.headers = data.headers || {};
1953
+ if (!data.headers) {
1954
+ data.headers = {};
1955
+ }
1941
1956
  data.headers[name] = value;
1942
1957
  return data;
1943
1958
  }
@@ -2539,6 +2554,30 @@ exports.getPluginMenu = function (menus, pluginName) {
2539
2554
  return result;
2540
2555
  };
2541
2556
 
2557
+ exports.getNetworkColumn = function(conf) {
2558
+ var column = conf.networkColumn;
2559
+ var title = column && (column.title || column.name);
2560
+ var key = title && column.key;
2561
+ if (!isString(title) || !isString(key)) {
2562
+ return;
2563
+ }
2564
+ return {
2565
+ title: title.substring(0, 16),
2566
+ key: key.substring(0, 72),
2567
+ showTitle: (column.showTips || column.showTitle) ? 1 : undefined,
2568
+ width: Math.min(360, Math.max(parseInt(column.width) || 120, 70))
2569
+ };
2570
+ };
2571
+
2572
+ exports.getWebWorker = function(conf) {
2573
+ var webWorker = conf.webWorker;
2574
+ if (!isString(webWorker)) {
2575
+ return;
2576
+ }
2577
+ webWorker = toRelPath(webWorker);
2578
+ return webWorker && webWorker.length <= 120 ? webWorker : undefined;
2579
+ };
2580
+
2542
2581
  var MAX_HINT_LEN = 512;
2543
2582
  var MAX_VAR_LEN = 100;
2544
2583
 
@@ -2621,6 +2660,10 @@ exports.getPluginVarsConf = function (conf) {
2621
2660
  return true;
2622
2661
  };
2623
2662
 
2663
+ function toRelPath(str) {
2664
+ return str.replace(/^\/+/, '');
2665
+ }
2666
+
2624
2667
  exports.getStaticDir = function (conf) {
2625
2668
  var staticDir = conf.staticDir;
2626
2669
  if (
@@ -2631,7 +2674,7 @@ exports.getStaticDir = function (conf) {
2631
2674
  ) {
2632
2675
  return;
2633
2676
  }
2634
- return staticDir.replace(/^\/+/, '');
2677
+ return toRelPath(staticDir.replace(/^\/+/, ''));
2635
2678
  };
2636
2679
 
2637
2680
  function toString(str) {
@@ -2700,7 +2743,7 @@ var RES_HEADER_RE = /^res\.?H(?:eaders?)?\.(.+)$/i;
2700
2743
  var TRAILER_RE = /trailer\.(.+)$/;
2701
2744
  var HEADER_RE = /^headers\.(.+)$/;
2702
2745
  var REQ_COOKIE_RE = /^req\.?C(?:ookies?)?\.(.+)$/i;
2703
- var RES_COOKIE_RE = /^req\.?C(?:ookies?)?\.(.+)$/i;
2746
+ var RES_COOKIE_RE = /^res\.?C(?:ookies?)?\.(.+)$/i;
2704
2747
  var COOKIE_RE = /^cookies?\.(.+)$/i;
2705
2748
  var QUERY_RE = /^(?:query|urlParams?)\.(.+)$/i;
2706
2749
 
@@ -2727,14 +2770,30 @@ function parseDelReqCookies(req) {
2727
2770
  Object.keys(deleteRule).forEach(function (prop) {
2728
2771
  if (REQ_COOKIE_RE.test(prop) || COOKIE_RE.test(prop)) {
2729
2772
  reqCookies = reqCookies || {};
2730
- reqCookies[RegExp.$1.toLowerCase()] = 1;
2773
+ reqCookies[RegExp.$1] = 1;
2731
2774
  }
2732
2775
  });
2733
2776
  }
2734
2777
  return reqCookies;
2735
2778
  }
2736
2779
 
2737
- exports.parseDelReqCookies = parseDelReqCookies;
2780
+ function getDomain(req) {
2781
+ var host = req._w2hostname;
2782
+ if (typeof host !== 'string') {
2783
+ return;
2784
+ }
2785
+ host = host.split('.');
2786
+ var len = host.length;
2787
+ if (len < 3) {
2788
+ return;
2789
+ }
2790
+ if (len === 3) {
2791
+ host[0] = '';
2792
+ } else {
2793
+ host.shift();
2794
+ }
2795
+ return host.join('.');
2796
+ }
2738
2797
 
2739
2798
  function parseDelResCookies(req) {
2740
2799
  var resCookies;
@@ -2743,15 +2802,20 @@ function parseDelResCookies(req) {
2743
2802
  Object.keys(deleteRule).forEach(function (prop) {
2744
2803
  if (RES_COOKIE_RE.test(prop) || COOKIE_RE.test(prop)) {
2745
2804
  resCookies = resCookies || {};
2746
- resCookies[RegExp.$1.toLowerCase()] = 1;
2805
+ var list = [EXP_COOKIE, EXP_SECURE_COOKIE];
2806
+ resCookies[RegExp.$1] = list;
2807
+ var domain = getDomain(req);
2808
+ if (domain) {
2809
+ EXP_COOKIE_D.domain = domain;
2810
+ EXP_SECURE_COOKIE_D.domain = domain;
2811
+ list.push(EXP_COOKIE_D, EXP_SECURE_COOKIE_D);
2812
+ }
2747
2813
  }
2748
2814
  });
2749
2815
  }
2750
2816
  return resCookies;
2751
2817
  }
2752
2818
 
2753
- exports.parseDelResCookies = parseDelResCookies;
2754
-
2755
2819
  function parseDelProps(req) {
2756
2820
  var deleteRule = req['delete'];
2757
2821
  var reqHeaders = {};
@@ -2994,79 +3058,87 @@ exports.setReqCookies = function (data, cookies, curCookies, req) {
2994
3058
  cookies = Object.keys(result)
2995
3059
  .map(function (name) {
2996
3060
  var value = result[name];
2997
- return name + (value == null ? '' : '=' + value);
3061
+ return name + '=' + (value == null ? '' : value);
2998
3062
  })
2999
3063
  .join('; ');
3000
3064
  setHeader(data, 'cookie', cookies);
3001
3065
  };
3002
3066
 
3067
+ function getCookieItem(name, cookie) {
3068
+ if (!cookie || typeof cookie != 'object') {
3069
+ cookie = cookie ? escapeValue(cookie) : cookie;
3070
+ return name + '=' + (cookie == null ? '' : cookie);
3071
+ }
3072
+ var attrs = [escapeValue(cookie.value)];
3073
+ var maxAge = cookie.maxAge || cookie.maxage ||
3074
+ cookie.MaxAge || cookie['Max-Age'] || cookie['max-age'];
3075
+ maxAge = parseInt(cookie.maxAge, 10);
3076
+ if (!Number.isNaN(maxAge)) {
3077
+ attrs.push('Expires=' + new Date(Date.now() + maxAge * 1000).toGMTString());
3078
+ attrs.push('Max-Age=' + (maxAge === EXPIRED_SEC ? 0 : maxAge));
3079
+ }
3080
+
3081
+ (cookie.secure || cookie.Secure) && attrs.push('Secure');
3082
+ (cookie.httpOnly || cookie.HttpOnly || cookie.httponly) && attrs.push('HttpOnly');
3083
+ var path = cookie.path || cookie.Path;
3084
+ var domain = cookie.domain || cookie.Domain;
3085
+ var sameSite = cookie.sameSite || cookie.samesite || cookie.SameSite;
3086
+ path && attrs.push('Path=' + path);
3087
+ domain && attrs.push('Domain=' + domain);
3088
+ sameSite && attrs.push('SameSite=' + sameSite);
3089
+ return name + attrs.join('; ');
3090
+ }
3091
+
3092
+ function addMapArr(obj, key, value) {
3093
+ var arr = obj[key] || [];
3094
+ arr.push(value);
3095
+ obj[key] = arr;
3096
+ }
3097
+
3003
3098
  exports.setResCookies = function (data, cookies, req) {
3099
+ var delKeys = parseDelResCookies(req);
3100
+ if (delKeys) {
3101
+ cookies = cookies ? extend(cookies, delKeys) : delKeys;
3102
+ }
3004
3103
  var list = cookies && Object.keys(cookies);
3005
3104
  var notEmpty = list && list.length;
3006
- var delKeys = parseDelResCookies(req);
3007
- var curCookies = data.headers && data.headers['set-cookie'];
3008
- if (!notEmpty && (!delKeys || !curCookies)) {
3105
+ if (!notEmpty) {
3009
3106
  return;
3010
3107
  }
3108
+ var curCookies = data.headers && data.headers['set-cookie'];
3011
3109
  if (!Array.isArray(curCookies)) {
3012
3110
  curCookies = curCookies ? [curCookies + ''] : [];
3013
3111
  }
3014
3112
 
3015
- var result = {};
3113
+ var curData = {};
3016
3114
  curCookies.forEach(function (cookie) {
3017
3115
  var index = cookie.indexOf('=');
3018
3116
  var key = cookie;
3019
- var value = null;
3020
3117
  if (index !== -1) {
3021
3118
  key = cookie.substring(0, index);
3022
- value = cookie.substring(index + 1);
3023
- }
3024
- if (!delKeys || !delKeys[key]) {
3025
- result[key] = value;
3026
3119
  }
3120
+ addMapArr(curData, key, cookie);
3027
3121
  });
3028
-
3122
+ var result = {};
3029
3123
  list.forEach(function (name) {
3030
3124
  var cookie = cookies[name];
3031
- var origName = name;
3032
3125
  name = escapeName(name);
3033
- if (delKeys && (delKeys[name] || delKeys[origName])) {
3034
- return;
3035
- }
3036
- if (!cookie || typeof cookie != 'object') {
3037
- result[name] = cookie ? escapeValue(cookie) : cookie;
3126
+ if (Array.isArray(cookie)) {
3127
+ cookie.forEach(function(value) {
3128
+ addMapArr(result, name, getCookieItem(name, value));
3129
+ });
3038
3130
  } else {
3039
- var attrs = [];
3040
- var value = cookie.value;
3041
- attrs.push(escapeValue(value));
3042
- var maxAge =
3043
- cookie.maxAge ||
3044
- cookie.maxage ||
3045
- cookie['Max-Age'] ||
3046
- cookie['max-age'];
3047
- maxAge = parseInt(cookie.maxAge, 10);
3048
- if (!Number.isNaN(maxAge)) {
3049
- attrs.push(
3050
- 'Expires=' + new Date(Date.now() + maxAge * 1000).toGMTString()
3051
- );
3052
- attrs.push('Max-Age=' + maxAge);
3053
- }
3054
-
3055
- cookie.secure && attrs.push('Secure');
3056
- cookie.path && attrs.push('Path=' + cookie.path);
3057
- cookie.domain && attrs.push('Domain=' + cookie.domain);
3058
- (cookie.httpOnly || cookie.httponly) && attrs.push('HttpOnly');
3059
- var sameSite = cookie.sameSite || cookie.samesite || cookie.SameSite;
3060
- sameSite && attrs.push('SameSite=' + sameSite);
3061
- result[name] = attrs.join('; ');
3131
+ addMapArr(result, name, getCookieItem(name, cookie));
3062
3132
  }
3063
3133
  });
3064
-
3065
- cookies = Object.keys(result).map(function (name) {
3066
- var value = result[name];
3067
- return name + (value == null ? '' : '=' + value);
3134
+ extend(curData, result);
3135
+ result = [];
3136
+ Object.keys(curData).forEach(function (name) {
3137
+ curData[name].forEach(function(value) {
3138
+ result.push(value);
3139
+ });
3068
3140
  });
3069
- setHeader(data, 'set-cookie', cookies);
3141
+ setHeader(data, 'set-cookie', result);
3070
3142
  };
3071
3143
 
3072
3144
  var SPEC_CHAR_RE = /[|\\{}()[\]^$+?.]/g;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "whistle",
3
3
  "description": "HTTP, HTTP2, HTTPS, Websocket debugging proxy",
4
- "version": "2.9.35",
4
+ "version": "2.9.36-beta",
5
5
  "dataDirname": ".whistle",
6
6
  "localUIHost": "local.whistlejs.com",
7
7
  "port": 8899,
@@ -59,7 +59,7 @@
59
59
  "safe-buffer": "^5.1.2",
60
60
  "set-global-proxy": "^0.1.7",
61
61
  "sni": "1.0.0",
62
- "sockx": "^0.2.0",
62
+ "sockx": "^0.2.1",
63
63
  "starting": "^8.0.1",
64
64
  "weinre2": "^1.3.2",
65
65
  "ws-parser": "^0.6.2",