whistle 2.9.76 → 2.9.77
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.
- package/README.md +3 -2
- package/assets/js/log.js +18 -1
- package/assets/js/weinre.js +12 -2
- package/lib/handlers/file-proxy.js +13 -2
- package/lib/inspectors/log.js +2 -2
- package/lib/inspectors/res.js +1 -1
- package/lib/inspectors/weinre.js +3 -7
- package/lib/rules/rules.js +5 -16
- package/lib/util/common.js +27 -0
- package/lib/util/index.js +26 -5
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
[](https://codecov.io/gh/avwo/whistle)
|
|
11
11
|
[](https://npmjs.org/package/whistle)
|
|
12
12
|
[](https://www.npmjs.com/package/whistle)
|
|
13
|
-
[](https://www.npmjs.com/package/whistle)
|
|
14
14
|
|
|
15
15
|
**Mac 或 Windows 系统推荐使用客户端版本:https://github.com/avwo/whistle-client**
|
|
16
16
|
|
|
@@ -29,7 +29,8 @@ Whistle 是基于 Node 实现的跨平台抓包调试工具,其主要特点:
|
|
|
29
29
|
* 项目可以自带代理规则配置并一键设置到本地 Whistle 代理,也可以通过定制插件简化操作
|
|
30
30
|
|
|
31
31
|
# 一键安装
|
|
32
|
-
> 已安装 `brew` 的 PC,可以省略以下 1、2 步骤,直接通过以下方式一键安装:`brew install whistle && w2 start --init
|
|
32
|
+
> 已安装 `brew` 的 PC,可以省略以下 1、2 步骤,直接通过以下方式一键安装:`brew install whistle && w2 start --init`
|
|
33
|
+
|
|
33
34
|
1. 安装 Node(建议安装**最新的 LTS 版本**,如已安装忽略此步骤):https://nodejs.org/
|
|
34
35
|
2. 一键安装,在命令行执行以下命令:
|
|
35
36
|
``` sh
|
package/assets/js/log.js
CHANGED
|
@@ -220,6 +220,22 @@
|
|
|
220
220
|
|
|
221
221
|
return obj === undefined ? 'undefined' : obj;
|
|
222
222
|
}
|
|
223
|
+
var prefixPath;
|
|
224
|
+
function getPathPrefix() {
|
|
225
|
+
if (prefixPath) {
|
|
226
|
+
return prefixPath;
|
|
227
|
+
}
|
|
228
|
+
prefixPath = window.__WHISTLE_PATH_PREFIX__;
|
|
229
|
+
if (/^\/[\w./-]+$/.test(prefixPath) && prefixPath.length <= 128) {
|
|
230
|
+
var len = prefixPath.length - 1;
|
|
231
|
+
if (prefixPath[len] === '/') {
|
|
232
|
+
prefixPath = prefixPath.substring(0, len);
|
|
233
|
+
}
|
|
234
|
+
} else {
|
|
235
|
+
prefixPath = '';
|
|
236
|
+
}
|
|
237
|
+
return prefixPath;
|
|
238
|
+
}
|
|
223
239
|
|
|
224
240
|
var index = 0;
|
|
225
241
|
var MAX_LEN = 1024 * 56;
|
|
@@ -237,7 +253,8 @@
|
|
|
237
253
|
logStr = logStr.substring(0, percIndex);
|
|
238
254
|
}
|
|
239
255
|
}
|
|
240
|
-
|
|
256
|
+
var baseUrl = '$BASE_URL' + getPathPrefix();
|
|
257
|
+
img.src = baseUrl + '$LOG_CGI?id=$LOG_ID&level=' + level + '&text=' + logStr
|
|
241
258
|
+ '&t=' + new Date().getTime() + '&' + ++index;
|
|
242
259
|
var preventGC = function() {
|
|
243
260
|
img.onload = img.onerror = null;
|
package/assets/js/weinre.js
CHANGED
|
@@ -3,12 +3,22 @@
|
|
|
3
3
|
if (typeof window === 'undefined' || window.WeinreServerURL) {
|
|
4
4
|
return;
|
|
5
5
|
}
|
|
6
|
-
|
|
6
|
+
var prefixPath = window.__WHISTLE_PATH_PREFIX__;
|
|
7
|
+
if (/^\/[\w./-]+$/.test(prefixPath) && prefixPath.length <= 128) {
|
|
8
|
+
var len = prefixPath.length - 1;
|
|
9
|
+
if (prefixPath[len] === '/') {
|
|
10
|
+
prefixPath = prefixPath.substring(0, len);
|
|
11
|
+
}
|
|
12
|
+
} else {
|
|
13
|
+
prefixPath = '';
|
|
14
|
+
}
|
|
15
|
+
var baseUrl = '$BASE_URL' + prefixPath;
|
|
16
|
+
window.WeinreServerURL = baseUrl + '$WEINRE_PATH';
|
|
7
17
|
var head = document.head || document.getElementsByTagName('head')[0] || document.documentElement;
|
|
8
18
|
var script = document.createElement('script');
|
|
9
19
|
script.async = true;
|
|
10
20
|
script.charset = 'utf8';
|
|
11
|
-
script.src = '$WEINRE_URL';
|
|
21
|
+
script.src = baseUrl + '$WEINRE_URL';
|
|
12
22
|
if (head.firstChild) {
|
|
13
23
|
head.insertBefore(script, head.firstChild);
|
|
14
24
|
} else {
|
|
@@ -22,6 +22,7 @@ var QUOTE_RE = /"/g;
|
|
|
22
22
|
var JS_RE = /^js:/i;
|
|
23
23
|
var HTML_RE = /^html:/i;
|
|
24
24
|
var BLANK_RE = /\s/g;
|
|
25
|
+
var HASH_RE = /#.*$/;
|
|
25
26
|
|
|
26
27
|
function urlToStr(url) {
|
|
27
28
|
return url.replace(SLASH_RE, '').replace(QUOTE_RE, '\\$&').replace(BLANK_RE, ' ');
|
|
@@ -198,7 +199,11 @@ function handleLocHref(req, rule, handleRes) {
|
|
|
198
199
|
}
|
|
199
200
|
var type = isJs ? 'application/javascript' : 'text/html';
|
|
200
201
|
if (body) {
|
|
201
|
-
body =
|
|
202
|
+
body = urlToStr(body);
|
|
203
|
+
if (util.compareUrl(body.replace(HASH_RE, ''), req.fullUrl)) {
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
body = 'window.location.href = "' + body + '";';
|
|
202
207
|
if (!isJs) {
|
|
203
208
|
body = '<script>' + body + '</script>';
|
|
204
209
|
}
|
|
@@ -211,6 +216,7 @@ function handleLocHref(req, rule, handleRes) {
|
|
|
211
216
|
'content-type': type + '; charset=utf-8'
|
|
212
217
|
}
|
|
213
218
|
});
|
|
219
|
+
return true;
|
|
214
220
|
}
|
|
215
221
|
|
|
216
222
|
module.exports = function (req, res, next) {
|
|
@@ -223,7 +229,12 @@ module.exports = function (req, res, next) {
|
|
|
223
229
|
var rules = req.rules;
|
|
224
230
|
var rule = rules.rule;
|
|
225
231
|
if (protoMgr.isLocHref(protocol)) {
|
|
226
|
-
|
|
232
|
+
if (handleLocHref(req, rule, render)) {
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
req.isWebProtocol = true;
|
|
236
|
+
req.options = util.parseUrl(req.fullUrl);
|
|
237
|
+
return next();
|
|
227
238
|
}
|
|
228
239
|
if (isAutoCors(req, rule) && req.method === 'OPTIONS') {
|
|
229
240
|
var optsRes = util.wrapResponse({ statusCode: 200 });
|
package/lib/inspectors/log.js
CHANGED
|
@@ -19,8 +19,8 @@ function getScript(host, isHtml, req) {
|
|
|
19
19
|
var logCgiPath =
|
|
20
20
|
config.WEBUI_PATH + 'log.' + config.port + '/cgi-bin/log/set';
|
|
21
21
|
var result = isHtml ? logHtmlScript : logScript;
|
|
22
|
-
|
|
23
|
-
return result.replace('$LOG_CGI', logCgiPath);
|
|
22
|
+
var baseUrl = (req.isHttps ? 'https://' : 'http://') + host;
|
|
23
|
+
return result.replace('$BASE_URL', baseUrl).replace('$LOG_CGI', logCgiPath);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
module.exports = function (req, res, next) {
|
package/lib/inspectors/res.js
CHANGED
|
@@ -1338,7 +1338,7 @@ module.exports = function (req, res, next) {
|
|
|
1338
1338
|
});
|
|
1339
1339
|
};
|
|
1340
1340
|
var resHeaders = {};
|
|
1341
|
-
var svRes = util.getStatusCodeFromRule(resRules);
|
|
1341
|
+
var svRes = util.getStatusCodeFromRule(resRules, req);
|
|
1342
1342
|
if (svRes) {
|
|
1343
1343
|
req.hostIp = LOCALHOST;
|
|
1344
1344
|
resHeaders.Connection = 'close';
|
package/lib/inspectors/weinre.js
CHANGED
|
@@ -10,16 +10,12 @@ var weinreHtmlScript = '\r\n<script>' + weinreScript + '</script>\r\n';
|
|
|
10
10
|
|
|
11
11
|
function getScript(host, name, isHtml, req) {
|
|
12
12
|
host = util.getInternalHost(req, host);
|
|
13
|
-
var
|
|
14
|
-
|
|
15
|
-
host +
|
|
16
|
-
config.WEBUI_PATH +
|
|
17
|
-
'weinre.' +
|
|
18
|
-
config.port;
|
|
13
|
+
var baseUrl = (req.isHttps ? 'https://' : 'http://') + host;
|
|
14
|
+
var weinrePath = config.WEBUI_PATH + 'weinre.' + config.port;
|
|
19
15
|
var result = isHtml ? weinreHtmlScript : weinreScript;
|
|
20
16
|
var weinreUrl =
|
|
21
17
|
weinrePath + '/target/target-script-min.js#' + (name || 'anonymous');
|
|
22
|
-
return result
|
|
18
|
+
return result.replace('$BASE_URL', baseUrl)
|
|
23
19
|
.replace('$WEINRE_PATH', weinrePath)
|
|
24
20
|
.replace('$WEINRE_URL', weinreUrl);
|
|
25
21
|
}
|
package/lib/rules/rules.js
CHANGED
|
@@ -245,17 +245,6 @@ function formatShorthand(url) {
|
|
|
245
245
|
return url;
|
|
246
246
|
}
|
|
247
247
|
|
|
248
|
-
function formatUrl(pattern) {
|
|
249
|
-
var queryString = '';
|
|
250
|
-
var queryIndex = pattern.indexOf('?');
|
|
251
|
-
if (queryIndex != -1) {
|
|
252
|
-
queryString = pattern.substring(queryIndex);
|
|
253
|
-
pattern = pattern.substring(0, queryIndex);
|
|
254
|
-
}
|
|
255
|
-
var index = pattern.indexOf('://');
|
|
256
|
-
index = pattern.indexOf('/', index == -1 ? 0 : index + 3);
|
|
257
|
-
return (index == -1 ? pattern + '/' : pattern) + queryString;
|
|
258
|
-
}
|
|
259
248
|
|
|
260
249
|
function getKey(url) {
|
|
261
250
|
if (url.indexOf('{') == 0) {
|
|
@@ -354,7 +343,7 @@ function join(first, second) {
|
|
|
354
343
|
}
|
|
355
344
|
}
|
|
356
345
|
var query = joinQuery(firstQuery, secondQuery);
|
|
357
|
-
return WEB_PROTOCOL_RE.test(first) ? formatUrl(first + query) : first + query;
|
|
346
|
+
return WEB_PROTOCOL_RE.test(first) ? util.formatUrl(first + query) : first + query;
|
|
358
347
|
}
|
|
359
348
|
|
|
360
349
|
function toLine(_, line) {
|
|
@@ -844,7 +833,7 @@ function replaceSubMatcher(url, regExp, req) {
|
|
|
844
833
|
}
|
|
845
834
|
|
|
846
835
|
function resolveRuleList(req, list, vals, index, isFilter, isEnableProxy, host) {
|
|
847
|
-
var curUrl = formatUrl(req.curUrl);
|
|
836
|
+
var curUrl = util.formatUrl(req.curUrl);
|
|
848
837
|
var notHttp = list.isRuleProto && curUrl[0] !== 'h';
|
|
849
838
|
//支持域名匹配
|
|
850
839
|
var domainUrl = curUrl.replace(
|
|
@@ -1255,7 +1244,7 @@ function parseRule(rulesMgr, pattern, matcher, raw, root, options) {
|
|
|
1255
1244
|
isRegExp: isRegExp,
|
|
1256
1245
|
isExact: isExact,
|
|
1257
1246
|
protocol: isRegExp ? null : util.getProtocol(pattern),
|
|
1258
|
-
pattern: isRegExp ? pattern : formatUrl(pattern),
|
|
1247
|
+
pattern: isRegExp ? pattern : util.formatUrl(pattern),
|
|
1259
1248
|
matcher: matcher,
|
|
1260
1249
|
port: port,
|
|
1261
1250
|
raw: raw,
|
|
@@ -2021,7 +2010,7 @@ proto.resolveHost = function (
|
|
|
2021
2010
|
};
|
|
2022
2011
|
|
|
2023
2012
|
proto.lookupHost = function (req, callback) {
|
|
2024
|
-
req.curUrl = formatUrl(util.setProtocol(req.curUrl));
|
|
2013
|
+
req.curUrl = util.formatUrl(util.setProtocol(req.curUrl));
|
|
2025
2014
|
var options = parseUrl(req.curUrl);
|
|
2026
2015
|
lookup(
|
|
2027
2016
|
options.hostname,
|
|
@@ -2060,7 +2049,7 @@ function getHostFromList(list, req, vals) {
|
|
|
2060
2049
|
}
|
|
2061
2050
|
|
|
2062
2051
|
proto.getHost = function (req, pluginRulesMgr, rulesFileMgr, headerRulesMgr) {
|
|
2063
|
-
var curUrl = formatUrl(util.setProtocol(req.curUrl));
|
|
2052
|
+
var curUrl = util.formatUrl(util.setProtocol(req.curUrl));
|
|
2064
2053
|
req.curUrl = curUrl;
|
|
2065
2054
|
var filter = {};
|
|
2066
2055
|
var filterHost =
|
package/lib/util/common.js
CHANGED
|
@@ -254,11 +254,38 @@ function replaceProtocol(url, protocol) {
|
|
|
254
254
|
return (protocol || 'http:') + removeProtocol(url);
|
|
255
255
|
}
|
|
256
256
|
|
|
257
|
+
function formatUrl(pattern) {
|
|
258
|
+
var queryString = '';
|
|
259
|
+
var queryIndex = pattern.indexOf('?');
|
|
260
|
+
if (queryIndex != -1) {
|
|
261
|
+
queryString = pattern.substring(queryIndex);
|
|
262
|
+
pattern = pattern.substring(0, queryIndex);
|
|
263
|
+
}
|
|
264
|
+
var index = pattern.indexOf('://');
|
|
265
|
+
index = pattern.indexOf('/', index == -1 ? 0 : index + 3);
|
|
266
|
+
return (index == -1 ? pattern + '/' : pattern) + queryString;
|
|
267
|
+
}
|
|
268
|
+
|
|
257
269
|
exports.hasProtocol = hasProtocol;
|
|
258
270
|
exports.setProtocol = setProtocol;
|
|
259
271
|
exports.getProtocol = getProtocol;
|
|
260
272
|
exports.removeProtocol = removeProtocol;
|
|
261
273
|
exports.replaceProtocol = replaceProtocol;
|
|
274
|
+
exports.formatUrl = formatUrl;
|
|
275
|
+
|
|
276
|
+
var QUERY_RE = /\/[^/]*(?:\?.*)?$/;
|
|
277
|
+
exports.getAbsUrl = function(url, fullUrl) {
|
|
278
|
+
if (HTTP_RE.test(url)) {
|
|
279
|
+
return formatUrl(url);
|
|
280
|
+
}
|
|
281
|
+
if (url[0] === '/') {
|
|
282
|
+
var index = fullUrl.indexOf('/', 8);
|
|
283
|
+
url = fullUrl.substring(0, index) + url;
|
|
284
|
+
return formatUrl(url);
|
|
285
|
+
}
|
|
286
|
+
url = fullUrl.replace(QUERY_RE, '') + '/' + url;
|
|
287
|
+
return formatUrl(url);
|
|
288
|
+
};
|
|
262
289
|
|
|
263
290
|
function getHomedir() {
|
|
264
291
|
//默认设置为`~`,防止Linux在开机启动时Node无法获取homedir
|
package/lib/util/index.js
CHANGED
|
@@ -607,6 +607,8 @@ function isEnable(req, name) {
|
|
|
607
607
|
|
|
608
608
|
exports.isEnable = isEnable;
|
|
609
609
|
|
|
610
|
+
exports.formatUrl = common.formatUrl;
|
|
611
|
+
|
|
610
612
|
exports.isKeepClientId = function(req, proxyUrl) {
|
|
611
613
|
if (isEnable(req, 'keepClientId')) {
|
|
612
614
|
return true;
|
|
@@ -926,6 +928,18 @@ exports.encodeNonLatin1Char = function (str) {
|
|
|
926
928
|
|
|
927
929
|
exports.encodeURIComponent = safeEncodeURIComponent;
|
|
928
930
|
|
|
931
|
+
function compareUrl(url, fullUrl) {
|
|
932
|
+
url = common.getAbsUrl(url, fullUrl);
|
|
933
|
+
if (url === fullUrl) {
|
|
934
|
+
return true;
|
|
935
|
+
}
|
|
936
|
+
try {
|
|
937
|
+
return url === decodeURIComponent(fullUrl);
|
|
938
|
+
} catch (e) {}
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
exports.compareUrl = compareUrl;
|
|
942
|
+
|
|
929
943
|
function getPath(url, noProtocol) {
|
|
930
944
|
if (url) {
|
|
931
945
|
url = url.replace(SEARCH_RE, '');
|
|
@@ -3780,19 +3794,26 @@ exports.setClientCert = function (options, key, cert, isPfx, cacheKey) {
|
|
|
3780
3794
|
}
|
|
3781
3795
|
};
|
|
3782
3796
|
|
|
3783
|
-
exports.getStatusCodeFromRule = function (rules) {
|
|
3797
|
+
exports.getStatusCodeFromRule = function (rules, req) {
|
|
3784
3798
|
var rule = rules.rule;
|
|
3785
3799
|
var isSpec = rule && rule.isSpec;
|
|
3786
3800
|
if (!isSpec) {
|
|
3787
3801
|
return;
|
|
3788
3802
|
}
|
|
3789
|
-
rule = getMatcherValue(rule) ||
|
|
3790
|
-
var
|
|
3791
|
-
if (
|
|
3803
|
+
rule = getMatcherValue(rule) || '';
|
|
3804
|
+
var isRedirect = isSpec === 2;
|
|
3805
|
+
if (req && isRedirect && rule && compareUrl(rule, req.fullUrl)) {
|
|
3806
|
+
req.isWebProtocol = true;
|
|
3807
|
+
req.options = parseUrl(req.fullUrl);
|
|
3808
|
+
return;
|
|
3809
|
+
}
|
|
3810
|
+
var code = rule || 200;
|
|
3811
|
+
var result = { statusCode: code, headers: {} };
|
|
3812
|
+
if (isRedirect) {
|
|
3792
3813
|
result.statusCode = 302;
|
|
3793
3814
|
result.headers.location = rule;
|
|
3794
3815
|
} else {
|
|
3795
|
-
handleStatusCode(
|
|
3816
|
+
handleStatusCode(code, result.headers);
|
|
3796
3817
|
}
|
|
3797
3818
|
return result;
|
|
3798
3819
|
};
|
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.
|
|
4
|
+
"version": "2.9.77",
|
|
5
5
|
"dataDirname": ".whistle",
|
|
6
6
|
"localUIHost": "local.whistlejs.com",
|
|
7
7
|
"port": 8899,
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"pfork": "^0.6.2",
|
|
54
54
|
"pipestream": "^0.7.3",
|
|
55
55
|
"safe-buffer": "^5.1.2",
|
|
56
|
-
"set-global-proxy": "^0.2.
|
|
56
|
+
"set-global-proxy": "^0.2.1",
|
|
57
57
|
"sni": "1.0.0",
|
|
58
58
|
"sockx": "^0.2.1",
|
|
59
59
|
"starting": "^8.0.2",
|