whistle.pastekitlab 1.0.0
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/EXAMPLES.md +254 -0
- package/IMPLEMENTATION.md +388 -0
- package/QUICKSTART.md +65 -0
- package/README.md +274 -0
- package/SERVER.md +59 -0
- package/config-whistle.sh +19 -0
- package/dashboard.html +352 -0
- package/index.js +269 -0
- package/package.json +27 -0
- package/server.js +165 -0
- package/start-plugin.js +127 -0
- package/test-full.js +51 -0
- package/test-ws.js +63 -0
package/EXAMPLES.md
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
# 使用示例
|
|
2
|
+
|
|
3
|
+
## 场景 1:拦截所有请求
|
|
4
|
+
|
|
5
|
+
### whistle 规则配置
|
|
6
|
+
```
|
|
7
|
+
http://*.* pastekitlab
|
|
8
|
+
https://*.* pastekitlab
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
### 效果
|
|
12
|
+
- 所有 HTTP/HTTPS 请求都会被拦截
|
|
13
|
+
- 请求和响应数据实时显示在 requestlistviewer 中
|
|
14
|
+
- 适合全局调试和监控
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 场景 2:只拦截特定域名
|
|
19
|
+
|
|
20
|
+
### whistle 规则配置
|
|
21
|
+
```
|
|
22
|
+
http://api.example.com/* pastekitlab
|
|
23
|
+
https://api.example.com/* pastekitlab
|
|
24
|
+
http://test.example.com/* pastekitlab
|
|
25
|
+
https://test.example.com/* pastekitlab
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### 效果
|
|
29
|
+
- 只拦截指定域名的请求
|
|
30
|
+
- 减少不必要的干扰
|
|
31
|
+
- 提高性能
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 场景 3:拦截特定路径
|
|
36
|
+
|
|
37
|
+
### whistle 规则配置
|
|
38
|
+
```
|
|
39
|
+
http://api.example.com/api/v1/* pastekitlab
|
|
40
|
+
https://api.example.com/api/v1/* pastekitlab
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### 效果
|
|
44
|
+
- 只拦截特定 API 路径
|
|
45
|
+
- 精确控制拦截范围
|
|
46
|
+
- 适合调试特定接口
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## 场景 4:排除某些域名
|
|
51
|
+
|
|
52
|
+
### whistle 规则配置
|
|
53
|
+
```
|
|
54
|
+
# 先排除不需要拦截的域名
|
|
55
|
+
http://exclude.com/* exclude
|
|
56
|
+
https://exclude.com/* exclude
|
|
57
|
+
|
|
58
|
+
# 然后拦截其他所有请求
|
|
59
|
+
http://*.* pastekitlab
|
|
60
|
+
https://*.* pastekitlab
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### 效果
|
|
64
|
+
- 排除敏感或不需要的域名
|
|
65
|
+
- 拦截其他所有请求
|
|
66
|
+
- 灵活控制拦截范围
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## 场景 5:结合 Mock 使用
|
|
71
|
+
|
|
72
|
+
### 步骤
|
|
73
|
+
|
|
74
|
+
1. **在 PasteKit Lab 中配置 Mock 规则**
|
|
75
|
+
- 打开 Options 页面
|
|
76
|
+
- 进入 Mock Manager
|
|
77
|
+
- 添加 Mock 规则,例如:
|
|
78
|
+
- URL Pattern: `/api/users`
|
|
79
|
+
- Method: `GET`
|
|
80
|
+
- Response: `{"users": [...]}`
|
|
81
|
+
|
|
82
|
+
2. **配置 whistle 规则**
|
|
83
|
+
```
|
|
84
|
+
http://api.example.com/* pastekitlab
|
|
85
|
+
https://api.example.com/* pastekitlab
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
3. **启用 Mock**
|
|
89
|
+
- 在 Mock Manager 中启用该规则
|
|
90
|
+
- requestlistviewer 会自动发送域名配置到 whistle 插件
|
|
91
|
+
|
|
92
|
+
4. **测试**
|
|
93
|
+
- 访问 `http://api.example.com/api/users`
|
|
94
|
+
- 看到返回的是 Mock 数据
|
|
95
|
+
- requestlistviewer 中显示 🎭 MOCK 标记
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## 场景 6:调试加密请求
|
|
100
|
+
|
|
101
|
+
### 步骤
|
|
102
|
+
|
|
103
|
+
1. **配置解密规则**
|
|
104
|
+
- 在 PasteKit Lab 中配置域名解密规则
|
|
105
|
+
- 设置请求和响应的密钥配置
|
|
106
|
+
|
|
107
|
+
2. **配置 whistle 规则**
|
|
108
|
+
```
|
|
109
|
+
http://encrypted-api.com/* pastekitlab
|
|
110
|
+
https://encrypted-api.com/* pastekitlab
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
3. **查看解密结果**
|
|
114
|
+
- 浏览网页触发请求
|
|
115
|
+
- requestlistviewer 自动解密并显示明文
|
|
116
|
+
- 可以看到 ✓ Request Decrypted 和 ✓ Response Decrypted 标记
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## 场景 7:多客户端同时连接
|
|
121
|
+
|
|
122
|
+
### 说明
|
|
123
|
+
whistle 插件支持多个客户端同时连接(默认最多 10 个)
|
|
124
|
+
|
|
125
|
+
### 使用场景
|
|
126
|
+
- 多个开发者同时查看请求
|
|
127
|
+
- 不同的视图展示相同的数据
|
|
128
|
+
- 开发和测试环境并行
|
|
129
|
+
|
|
130
|
+
### 配置
|
|
131
|
+
在 `index.js` 中修改:
|
|
132
|
+
```javascript
|
|
133
|
+
const CONFIG = {
|
|
134
|
+
maxConnections: 20 // 增加最大连接数
|
|
135
|
+
};
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## 常见问题示例
|
|
141
|
+
|
|
142
|
+
### Q1: 如何查看 WebSocket 连接状态?
|
|
143
|
+
|
|
144
|
+
在浏览器控制台查看:
|
|
145
|
+
```javascript
|
|
146
|
+
// requestlistviewer 会输出连接状态
|
|
147
|
+
[RequestList Viewer] 📊 检查连接状态:isConnected = true
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Q2: 如何确认请求被拦截?
|
|
151
|
+
|
|
152
|
+
查看 whistle 日志:
|
|
153
|
+
```bash
|
|
154
|
+
w2 log | grep "Whistle Plugin"
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
应该看到:
|
|
158
|
+
```
|
|
159
|
+
[Whistle Plugin] 拦截请求:GET https://api.example.com/data
|
|
160
|
+
[Whistle Plugin] 拦截响应:200 https://api.example.com/data
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Q3: 如何测试 WebSocket 连接?
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
cd plugins/whistle
|
|
167
|
+
npm test
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
应该看到:
|
|
171
|
+
```
|
|
172
|
+
✅ WebSocket 连接成功!
|
|
173
|
+
📨 收到消息:{ "msgType": "connected", "clientId": 1, ... }
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Q4: 请求未显示在 requestlistviewer 中?
|
|
177
|
+
|
|
178
|
+
检查清单:
|
|
179
|
+
1. ✅ whistle 插件已启动
|
|
180
|
+
2. ✅ whistle 规则已配置
|
|
181
|
+
3. ✅ requestlistviewer 已连接到 ws://127.0.0.1:8889/ws
|
|
182
|
+
4. ✅ 浏览器代理指向 whistle (127.0.0.1:8899)
|
|
183
|
+
5. ✅ HTTPS 证书已安装并信任
|
|
184
|
+
|
|
185
|
+
### Q5: 如何过滤特定请求?
|
|
186
|
+
|
|
187
|
+
在 requestlistviewer 中使用搜索框:
|
|
188
|
+
- 输入 URL 关键词
|
|
189
|
+
- 输入 HTTP 方法(GET, POST 等)
|
|
190
|
+
- 输入状态码(200, 404 等)
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## 高级用法
|
|
195
|
+
|
|
196
|
+
### 自定义消息处理
|
|
197
|
+
|
|
198
|
+
在 whistle 插件中添加自定义逻辑:
|
|
199
|
+
|
|
200
|
+
```javascript
|
|
201
|
+
// 在 beforeRequest 中
|
|
202
|
+
beforeRequest: function(req, res, next) {
|
|
203
|
+
// 添加自定义逻辑
|
|
204
|
+
if (req.url.includes('/sensitive')) {
|
|
205
|
+
// 跳过敏感请求
|
|
206
|
+
return next();
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// 正常处理
|
|
210
|
+
// ...
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### 性能优化
|
|
215
|
+
|
|
216
|
+
对于高流量场景:
|
|
217
|
+
|
|
218
|
+
1. **限制拦截范围**
|
|
219
|
+
```
|
|
220
|
+
# 只拦截必要的域名
|
|
221
|
+
http://api.example.com/* pastekitlab
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
2. **禁用日志**
|
|
225
|
+
```javascript
|
|
226
|
+
const CONFIG = {
|
|
227
|
+
logEnabled: false // 减少日志输出
|
|
228
|
+
};
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
3. **调整缓冲区大小**
|
|
232
|
+
```javascript
|
|
233
|
+
wss = new WebSocket.Server({
|
|
234
|
+
maxPayload: 50 * 1024 * 1024 // 增加到 50MB
|
|
235
|
+
});
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## 最佳实践
|
|
241
|
+
|
|
242
|
+
1. **开发环境**:拦截所有请求,全面监控
|
|
243
|
+
2. **测试环境**:只拦截测试域名,减少干扰
|
|
244
|
+
3. **生产环境**:谨慎使用,注意性能和隐私
|
|
245
|
+
4. **团队协作**:共享 whistle 规则配置
|
|
246
|
+
5. **定期清理**:清除 requestlistviewer 中的旧数据
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
## 相关资源
|
|
251
|
+
|
|
252
|
+
- [Whistle 官方文档](https://github.com/avwo/whistle)
|
|
253
|
+
- [PasteKit Lab 文档](../../README.md)
|
|
254
|
+
- [WebSocket 协议](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API)
|
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
# Whistle 插件实现说明
|
|
2
|
+
|
|
3
|
+
## 概述
|
|
4
|
+
|
|
5
|
+
本插件实现了 whistle 与 PasteKit Lab requestlistviewer 之间的 WebSocket 通信,用于实时展示拦截的 HTTP/HTTPS 请求。
|
|
6
|
+
|
|
7
|
+
## 架构设计
|
|
8
|
+
|
|
9
|
+
### 组件关系
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
13
|
+
│ Browser (Client) │
|
|
14
|
+
│ - Chrome/Firefox/etc │
|
|
15
|
+
└──────────────────────┬──────────────────────────────────────┘
|
|
16
|
+
│ HTTP/HTTPS Requests
|
|
17
|
+
▼
|
|
18
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
19
|
+
│ Whistle Proxy Server │
|
|
20
|
+
│ Port: 8899 │
|
|
21
|
+
│ │
|
|
22
|
+
│ ┌──────────────────────────────────────────────────────┐ │
|
|
23
|
+
│ │ PasteKit Lab Plugin (index.js) │ │
|
|
24
|
+
│ │ │ │
|
|
25
|
+
│ │ • beforeRequest: 拦截请求 │ │
|
|
26
|
+
│ │ • afterResponse: 拦截响应 │ │
|
|
27
|
+
│ │ • WebSocket Server: ws://0.0.0.0:8889/ws │ │
|
|
28
|
+
│ └──────────────────┬───────────────────────────────────┘ │
|
|
29
|
+
└─────────────────────┼──────────────────────────────────────┘
|
|
30
|
+
│ WebSocket Messages
|
|
31
|
+
│ (MOCK, RESPONSE, domain, etc.)
|
|
32
|
+
▼
|
|
33
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
34
|
+
│ PasteKit Lab Extension │
|
|
35
|
+
│ │
|
|
36
|
+
│ ┌────────────────────────────────────────────────────┐ │
|
|
37
|
+
│ │ requestlistviewer.jsx │ │
|
|
38
|
+
│ │ │ │
|
|
39
|
+
│ │ • ProxyWebSocketClient │ │
|
|
40
|
+
│ │ • 连接到 ws://127.0.0.1:8889/ws │ │
|
|
41
|
+
│ │ • 接收 MOCK/RESPONSE 消息 │ │
|
|
42
|
+
│ │ • 解密请求/响应 │ │
|
|
43
|
+
│ │ • 展示在 UI 中 │ │
|
|
44
|
+
│ └────────────────────────────────────────────────────┘ │
|
|
45
|
+
└─────────────────────────────────────────────────────────────┘
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## 核心功能
|
|
49
|
+
|
|
50
|
+
### 1. WebSocket 服务器
|
|
51
|
+
|
|
52
|
+
**位置**: `plugins/whistle/index.js`
|
|
53
|
+
|
|
54
|
+
**功能**:
|
|
55
|
+
- 监听端口 8889
|
|
56
|
+
- 路径: `/ws`
|
|
57
|
+
- 支持多客户端连接(默认最多 10 个)
|
|
58
|
+
- 自动管理客户端生命周期
|
|
59
|
+
|
|
60
|
+
**关键代码**:
|
|
61
|
+
```javascript
|
|
62
|
+
wss = new WebSocket.Server({
|
|
63
|
+
port: CONFIG.wsPort,
|
|
64
|
+
path: '/ws',
|
|
65
|
+
maxPayload: 10 * 1024 * 1024
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
wss.on('connection', (ws, req) => {
|
|
69
|
+
const clientId = ++clientIdCounter;
|
|
70
|
+
clients.set(clientId, { id: clientId, ws: ws, ... });
|
|
71
|
+
|
|
72
|
+
// 发送欢迎消息
|
|
73
|
+
ws.send(JSON.stringify({
|
|
74
|
+
msgType: 'connected',
|
|
75
|
+
clientId: clientId,
|
|
76
|
+
timestamp: Date.now()
|
|
77
|
+
}));
|
|
78
|
+
});
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### 2. 请求拦截
|
|
82
|
+
|
|
83
|
+
**Whistle 钩子**: `beforeRequest`
|
|
84
|
+
|
|
85
|
+
**流程**:
|
|
86
|
+
1. 生成唯一的 requestId
|
|
87
|
+
2. 提取请求信息(URL、Method、Headers、Body)
|
|
88
|
+
3. 将 Body 转换为字符串和 Base64
|
|
89
|
+
4. 广播 MOCK 消息给所有客户端
|
|
90
|
+
5. 保存请求信息用于后续关联响应
|
|
91
|
+
|
|
92
|
+
**数据结构**:
|
|
93
|
+
```javascript
|
|
94
|
+
{
|
|
95
|
+
eventId: "req_1234567890_abc123",
|
|
96
|
+
url: "https://api.example.com/data",
|
|
97
|
+
method: "POST",
|
|
98
|
+
requestHeaders: { ... },
|
|
99
|
+
requestBody: "{\"key\":\"value\"}",
|
|
100
|
+
requestBodyBase64: "eyJrZXkiOiJ2YWx1ZSJ9",
|
|
101
|
+
timestamp: 1234567890123
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### 3. 响应拦截
|
|
106
|
+
|
|
107
|
+
**Whistle 钩子**: `afterResponse`
|
|
108
|
+
|
|
109
|
+
**流程**:
|
|
110
|
+
1. 提取响应信息(StatusCode、Headers、Body)
|
|
111
|
+
2. 查找对应的请求 ID(通过 URL 和时间匹配)
|
|
112
|
+
3. 将 Body 转换为字符串和 Base64
|
|
113
|
+
4. 广播 RESPONSE 消息给所有客户端
|
|
114
|
+
|
|
115
|
+
**数据结构**:
|
|
116
|
+
```javascript
|
|
117
|
+
{
|
|
118
|
+
eventId: "req_1234567890_abc123",
|
|
119
|
+
url: "https://api.example.com/data",
|
|
120
|
+
method: "POST",
|
|
121
|
+
statusCode: 200,
|
|
122
|
+
responseHeaders: { ... },
|
|
123
|
+
responseBody: "{\"result\":\"success\"}",
|
|
124
|
+
responseBodyBase64: "eyJyZXN1bHQiOiJzdWNjZXNzIn0=",
|
|
125
|
+
timestamp: 1234567890456
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### 4. 域名配置管理
|
|
130
|
+
|
|
131
|
+
**消息类型**: `domain`
|
|
132
|
+
|
|
133
|
+
**流程**:
|
|
134
|
+
1. requestlistviewer 发送域名配置
|
|
135
|
+
2. 插件保存配置到对应客户端
|
|
136
|
+
3. 可用于后续的过滤逻辑
|
|
137
|
+
|
|
138
|
+
**数据结构**:
|
|
139
|
+
```javascript
|
|
140
|
+
{
|
|
141
|
+
msgType: "domain",
|
|
142
|
+
domain: ["api.example.com", "test.example.com"],
|
|
143
|
+
mockUrls: ["/api/mock/*"]
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### 5. 消息广播
|
|
148
|
+
|
|
149
|
+
**函数**: `broadcastToClients(msgType, data)`
|
|
150
|
+
|
|
151
|
+
**功能**:
|
|
152
|
+
- 遍历所有连接的客户端
|
|
153
|
+
- 检查 WebSocket 状态
|
|
154
|
+
- 发送 JSON 格式的消息
|
|
155
|
+
- 处理发送错误
|
|
156
|
+
|
|
157
|
+
**实现**:
|
|
158
|
+
```javascript
|
|
159
|
+
function broadcastToClients(msgType, data) {
|
|
160
|
+
const message = JSON.stringify({
|
|
161
|
+
msgType: msgType,
|
|
162
|
+
...data,
|
|
163
|
+
timestamp: Date.now()
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
clients.forEach((client, clientId) => {
|
|
167
|
+
if (client.ws.readyState === WebSocket.OPEN) {
|
|
168
|
+
client.ws.send(message);
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## 消息协议
|
|
175
|
+
|
|
176
|
+
### 插件 → 客户端
|
|
177
|
+
|
|
178
|
+
| 消息类型 | 说明 | 触发时机 |
|
|
179
|
+
|---------|------|---------|
|
|
180
|
+
| `connected` | 连接成功 | 客户端连接时 |
|
|
181
|
+
| `MOCK` | 请求数据 | 拦截到 HTTP 请求 |
|
|
182
|
+
| `RESPONSE` | 响应数据 | 拦截到 HTTP 响应 |
|
|
183
|
+
|
|
184
|
+
### 客户端 → 插件
|
|
185
|
+
|
|
186
|
+
| 消息类型 | 说明 | 用途 |
|
|
187
|
+
|---------|------|------|
|
|
188
|
+
| `domain` | 域名配置 | 设置需要拦截的域名和 Mock URL |
|
|
189
|
+
| `MOCK_RESPONSE` | Mock 响应 | 客户端返回的 Mock 数据 |
|
|
190
|
+
| `UNMOCK` | 未 Mock 标记 | 标记请求未使用 Mock |
|
|
191
|
+
|
|
192
|
+
## 关键技术点
|
|
193
|
+
|
|
194
|
+
### 1. Buffer 处理
|
|
195
|
+
|
|
196
|
+
**问题**: whistle 中的请求/响应体是 Buffer 格式
|
|
197
|
+
|
|
198
|
+
**解决方案**:
|
|
199
|
+
```javascript
|
|
200
|
+
// 转换为字符串(检查是否为文本)
|
|
201
|
+
function bufferToString(buffer) {
|
|
202
|
+
const result = buffer.toString('utf8');
|
|
203
|
+
if (/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F]/.test(result)) {
|
|
204
|
+
return null; // 二进制数据
|
|
205
|
+
}
|
|
206
|
+
return result;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// 转换为 Base64(用于二进制数据)
|
|
210
|
+
function bufferToBase64(buffer) {
|
|
211
|
+
return buffer.toString('base64');
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### 2. 请求-响应关联
|
|
216
|
+
|
|
217
|
+
**挑战**: whistle 的 beforeRequest 和 afterResponse 是分开的
|
|
218
|
+
|
|
219
|
+
**解决方案**:
|
|
220
|
+
```javascript
|
|
221
|
+
// 在 beforeRequest 中保存请求
|
|
222
|
+
requestMap.set(requestId, { requestData, startTime });
|
|
223
|
+
|
|
224
|
+
// 在 afterResponse 中查找
|
|
225
|
+
for (const [id, data] of requestMap.entries()) {
|
|
226
|
+
if (data.requestData.url === url &&
|
|
227
|
+
Math.abs(Date.now() - data.startTime) < 30000) {
|
|
228
|
+
requestId = id;
|
|
229
|
+
requestMap.delete(id);
|
|
230
|
+
break;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### 3. 多客户端支持
|
|
236
|
+
|
|
237
|
+
**实现**:
|
|
238
|
+
```javascript
|
|
239
|
+
let clients = new Map();
|
|
240
|
+
|
|
241
|
+
wss.on('connection', (ws, req) => {
|
|
242
|
+
const clientId = ++clientIdCounter;
|
|
243
|
+
clients.set(clientId, { id: clientId, ws, ... });
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
ws.on('close', () => {
|
|
247
|
+
clients.delete(clientId);
|
|
248
|
+
});
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### 4. 错误处理
|
|
252
|
+
|
|
253
|
+
**策略**:
|
|
254
|
+
- 所有异步操作都有 try-catch
|
|
255
|
+
- WebSocket 错误不会导致插件崩溃
|
|
256
|
+
- 详细的日志记录便于调试
|
|
257
|
+
|
|
258
|
+
## 文件结构
|
|
259
|
+
|
|
260
|
+
```
|
|
261
|
+
plugins/whistle/
|
|
262
|
+
├── index.js # 插件主文件(351 行)
|
|
263
|
+
│ ├── 配置部分
|
|
264
|
+
│ ├── 工具函数
|
|
265
|
+
│ ├── WebSocket 服务器
|
|
266
|
+
│ ├── 消息处理
|
|
267
|
+
│ └── Whistle 插件接口
|
|
268
|
+
│
|
|
269
|
+
├── package.json # 依赖和脚本配置
|
|
270
|
+
├── start-plugin.js # 启动脚本(129 行)
|
|
271
|
+
│ ├── 检查 whistle
|
|
272
|
+
│ ├── 安装插件
|
|
273
|
+
│ ├── 重启服务
|
|
274
|
+
│ └── 显示使用说明
|
|
275
|
+
│
|
|
276
|
+
├── test-ws.js # WebSocket 测试脚本(64 行)
|
|
277
|
+
├── README.md # 完整文档(275 行)
|
|
278
|
+
├── QUICKSTART.md # 快速开始指南(66 行)
|
|
279
|
+
├── EXAMPLES.md # 使用示例(255 行)
|
|
280
|
+
├── IMPLEMENTATION.md # 本文档
|
|
281
|
+
└── .gitignore # Git 忽略配置
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
## 性能考虑
|
|
285
|
+
|
|
286
|
+
### 1. 内存管理
|
|
287
|
+
- 请求映射表有清理机制(30秒超时)
|
|
288
|
+
- 客户端断开时自动清理
|
|
289
|
+
- 限制最大负载为 10MB
|
|
290
|
+
|
|
291
|
+
### 2. 并发控制
|
|
292
|
+
- 限制最大连接数为 10
|
|
293
|
+
- WebSocket 消息队列由 ws 库管理
|
|
294
|
+
- 异步处理避免阻塞
|
|
295
|
+
|
|
296
|
+
### 3. 网络优化
|
|
297
|
+
- JSON 序列化在发送前完成
|
|
298
|
+
- 批量广播减少系统调用
|
|
299
|
+
- 连接状态检查避免无效发送
|
|
300
|
+
|
|
301
|
+
## 安全考虑
|
|
302
|
+
|
|
303
|
+
### 1. 数据安全
|
|
304
|
+
- 只在本地运行(127.0.0.1)
|
|
305
|
+
- 不持久化敏感数据
|
|
306
|
+
- 可配置启用/禁用
|
|
307
|
+
|
|
308
|
+
### 2. 访问控制
|
|
309
|
+
- 限制本地连接
|
|
310
|
+
- 可配置最大连接数
|
|
311
|
+
- 客户端 ID 追踪
|
|
312
|
+
|
|
313
|
+
### 3. 证书管理
|
|
314
|
+
- HTTPS 拦截需要安装根证书
|
|
315
|
+
- 用户需手动信任证书
|
|
316
|
+
- 提供证书安装指引
|
|
317
|
+
|
|
318
|
+
## 扩展性
|
|
319
|
+
|
|
320
|
+
### 可能的改进方向
|
|
321
|
+
|
|
322
|
+
1. **过滤规则增强**
|
|
323
|
+
- 支持正则表达式
|
|
324
|
+
- 支持请求方法过滤
|
|
325
|
+
- 支持 Header 过滤
|
|
326
|
+
|
|
327
|
+
2. **数据持久化**
|
|
328
|
+
- 保存历史请求
|
|
329
|
+
- 导出为 HAR 格式
|
|
330
|
+
- 数据库存储
|
|
331
|
+
|
|
332
|
+
3. **高级功能**
|
|
333
|
+
- 请求重放
|
|
334
|
+
- 性能分析
|
|
335
|
+
- 自动化测试集成
|
|
336
|
+
|
|
337
|
+
4. **UI 增强**
|
|
338
|
+
- 实时统计图表
|
|
339
|
+
- 请求瀑布图
|
|
340
|
+
- 搜索高亮
|
|
341
|
+
|
|
342
|
+
## 故障排查指南
|
|
343
|
+
|
|
344
|
+
### 常见问题
|
|
345
|
+
|
|
346
|
+
| 问题 | 可能原因 | 解决方案 |
|
|
347
|
+
|-----|---------|---------|
|
|
348
|
+
| WebSocket 连接失败 | 插件未启动 | 运行 `node start-plugin.js` |
|
|
349
|
+
| 请求未被拦截 | 规则配置错误 | 检查 whistle Rules |
|
|
350
|
+
| 端口被占用 | 8889 已被使用 | `lsof -i :8889` 查看并关闭 |
|
|
351
|
+
| HTTPS 证书错误 | 证书未安装 | 访问 http://127.0.0.1:8899 安装 |
|
|
352
|
+
| 消息未收到 | 客户端未注册 handler | 检查 `wsClient.on()` 调用 |
|
|
353
|
+
|
|
354
|
+
### 调试技巧
|
|
355
|
+
|
|
356
|
+
1. **查看插件日志**
|
|
357
|
+
```bash
|
|
358
|
+
w2 log | grep "Whistle Plugin"
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
2. **测试 WebSocket**
|
|
362
|
+
```bash
|
|
363
|
+
npm test
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
3. **浏览器控制台**
|
|
367
|
+
- 打开 requestlistviewer
|
|
368
|
+
- 查看 Console 标签
|
|
369
|
+
- 搜索 "[RequestList Viewer]"
|
|
370
|
+
|
|
371
|
+
4. **网络监控**
|
|
372
|
+
```bash
|
|
373
|
+
lsof -i :8889
|
|
374
|
+
netstat -an | grep 8889
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
## 总结
|
|
378
|
+
|
|
379
|
+
本插件成功实现了:
|
|
380
|
+
- ✅ whistle 请求拦截
|
|
381
|
+
- ✅ WebSocket 实时通信
|
|
382
|
+
- ✅ 多客户端支持
|
|
383
|
+
- ✅ 完整的消息协议
|
|
384
|
+
- ✅ 请求-响应关联
|
|
385
|
+
- ✅ 错误处理和日志
|
|
386
|
+
- ✅ 详细的文档和示例
|
|
387
|
+
|
|
388
|
+
通过与 requestlistviewer 的配合,提供了强大的 HTTP/HTTPS 请求调试和监控能力。
|
package/QUICKSTART.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# 快速开始指南
|
|
2
|
+
|
|
3
|
+
## 一键启动
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
cd plugins/whistle
|
|
7
|
+
npm install
|
|
8
|
+
node start-plugin.js
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## 配置 whistle 规则
|
|
12
|
+
|
|
13
|
+
在浏览器打开 http://127.0.0.1:8899,在 Rules 标签添加:
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
http://*.* pastekitlab
|
|
17
|
+
https://*.* pastekitlab
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## 测试连接
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm test
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
应该看到:
|
|
27
|
+
```
|
|
28
|
+
✅ WebSocket 连接成功!
|
|
29
|
+
📨 收到消息:{ "msgType": "connected", "clientId": 1, ... }
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## 查看请求
|
|
33
|
+
|
|
34
|
+
打开 PasteKit Lab 的 Options 页面,找到 requestlistviewer 组件,浏览网页即可看到拦截的请求。
|
|
35
|
+
|
|
36
|
+
## 常用命令
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# 查看日志
|
|
40
|
+
w2 log
|
|
41
|
+
|
|
42
|
+
# 重启 whistle
|
|
43
|
+
w2 restart
|
|
44
|
+
|
|
45
|
+
# 停止 whistle
|
|
46
|
+
w2 stop
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## 问题排查
|
|
50
|
+
|
|
51
|
+
### 端口被占用
|
|
52
|
+
```bash
|
|
53
|
+
lsof -i :8889
|
|
54
|
+
kill <PID>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### 插件未加载
|
|
58
|
+
```bash
|
|
59
|
+
w2 stop
|
|
60
|
+
w2 start
|
|
61
|
+
w2 log
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### HTTPS 证书问题
|
|
65
|
+
访问 http://127.0.0.1:8899 安装根证书并在系统中信任
|