whistle v1.6.0
(Github地址:github.com/avwo/whistl…) 開始支援WebSocket和一般Socket的抓包、構造請求、以及修改傳送或接收的資料。
請求抓包
開啟whistle的Network,選中左側請求列表中的WebSocket(Socket)請求,點選右側的 Response -> Frames
。
WebSocket抓包:
Socket(TCP)請求,需要通過Tunnel代理連線whistle,再通過whistle轉發,未避免whistle把普通Socket請求當成https或websocket請求,需要代理請求頭新增個欄位 x-whistle-policy: tunnel
,下面以Node為例說明如何通過whistle轉發Socket(TCP)請求(其它語言同理:先發一個http請求給whistle代理把請求資訊帶給whistle,whistle請求目標伺服器成功後把連線返回):
模擬後臺Socket Server程式碼:
const net = require('net');
const server = net.createServer();
server.on('connection', (client) => {
client.on('error', () => {});
client.on('data', (data) => {
client.write(`Response: ${data}`);
})
});
server.listen(9999);
複製程式碼
客戶端程式碼:
const http = require('http');
const net = require('net');
// whistle監聽埠,根據實際情況設定
const PROXY_PORT = 8899;
// whistle所在伺服器IP
const PROXY_HOST = '127.0.0.1';
// 連線whistle代理
const connect = (options) => {
options = {
method: 'CONNECT',
host: PROXY_HOST,
port: PROXY_PORT,
path: `${options.host}:${options.port}`,
agent: false,
headers: {
'x-whistle-policy': 'tunnel', // 必填,否則無法檢視抓包資料
}
};
return new Promise((resolve, reject) => {
const client = http.request(options);
client.once('connect', (req, socket) => resolve(socket));
client.once('error', reject);
client.end();
});
};
const init = async () => {
const options = { host: '127.0.0.1', port: 9999 };
const socket = await connect(options);
let index = 0;
const send = () => {
++index;
socket.write(`${index}. This is a JSON Object: {"test": "index=${index}"}`);
};
socket.on('data', (data) => {
setTimeout(send, 3000);
});
send();
};
init().catch(err => {
throw err;
});
複製程式碼
構造請求
在Network的右側Composer可以構造各種請求,包括http、https、WebSocket、Socket請求,可以直接填寫要請求的url、方法、請求頭、請求內容等,也可以直接從左側列表把對應的資料拖過來。
以WebSocket Demo網站為例(demos.kaazing.com/echo/),建立如下連線,併傳送資料(用Composer構造的WebSocket在Frames下面會出現一個Composer選項,用於向Server傳送資料,也可以通過直接上傳檔案傳送):
GET /echo HTTP/1.1
WebSocket-Protocol:
Sec-WebSocket-Protocol:
Sec-WebSocket-Extensions: x-kaazing-idle-timeout,x-kaazing-ping-pong,x-kaazing-http-revalidate
1
複製程式碼
後面通過Frames裡面的Composer傳送什麼資料後臺就返回什麼:
一般Socket請求只需把請求方法改為 CONNECT
,或者用這些協議的url conn://
、connect://
、socket://
、、tunnel://
:
修改資料
從上面的插圖可以發現,如果請求不是通過whistle的Composer傳送的,WebSocket和Socket請求都無法新增或修改接收及傳送資料(Composer建立的連線可以傳送資料到服務端),要修改WebSocket或Socket的傳送或接收資料,需要藉助whistle的外掛whistle.script,其原理是通過配置whistle規則把請求轉發到whistle.script裡面的WebSocket或Socket伺服器,再通過whistle.script攔截或傳送到指定後臺。
npm i -g whistle.script
# 或
npm i -g whistle.script --registry=https://registry.npm.taobao.org
複製程式碼
除錯WebSocket請求,開啟外掛script的介面local.whistlejs.com/whistle.scr…,新建名為handleWebSocket的script:
exports.handleWebSocket = async (ws, connect) => {
// 將請求繼續轉發到目標後臺,如果不加則直接響應
const res = await connect();
// 獲取客戶端的請求資料
ws.on('message', (data) => {
// 在script的Console列印出客戶端傳送的資料
console.log(`Client: ${data}`);
// 可以修改後再傳送到Server
res.send(data);
});
res.on('message', (data) => {
// 在script的Console列印出服務端傳送的資料
console.log(`Server: ${data}`);
// 可以修改後再傳送到Server
ws.send(data);
});
// 接收通過whistle.script頁面Console的dataSource.emit('toSocketClient', {name: 'toSocketClient'})的資料
ws.dataSource.on('toSocketClient', (data) => {
ws.send(data);
});
// 接收通過whistle.script頁面Console的dataSource.emit('toSocketServer', {name: 'toSocketClient'})的資料
ws.dataSource.on('toSocketServer', (data) => {
res.send(data);
});
};
複製程式碼
在whistle上配置規則:
ws://demos.kaazing.com/echo script://handleWebSocket
複製程式碼
開啟demos.kaazing.com/echo/,點選 Connect
按鈕:
除錯Socket請求,同上操作在whistle.script外掛上新建一個名為 handleSocket
的指令碼:
exports.handleTunnel = async (client, next) => {
// 將請求繼續轉發到目標後臺,如果不加則直接響應
const res = await next();
// 獲取客戶端的請求資料
client.on('data', (data) => {
// 在script的Console列印出客戶端傳送的資料
console.log(`Client: ${data}`);
// 修改後再傳送到Server
res.write(`Client>>>Server: ${data}+client`);
});
res.on('data', (data) => {
// 在script的Console列印出服務端傳送的資料
console.log(`Server: ${data}`);
// 修改後再傳送到Server
client.write(`Server>>>Client: ${data}`);
});
// 接收通過whistle.script頁面Console的dataSource.emit('toSocketClient', {name: 'toSocketClient'})的資料
client.dataSource.on('toSocketClient', (data) => {
client.write(JSON.stringify(data));
});
// 接收通過whistle.script頁面Console的dataSource.emit('toSocketServer', {name: 'toSocketClient'})的資料
client.dataSource.on('toSocketServer', (data) => {
res.write(JSON.stringify(data));
});
};
複製程式碼
在whistle上配置規則:
127.0.0.1:9999 script://handleSocket
複製程式碼
啟動文章剛開始的Socket連線的例子,開啟whistle.script介面的Console,在裡面分別執行下面兩個語句:
dataSource.emit('toSocketClient', 'Mock Server');
dataSource.emit('toSocketServer', 'Mock Client');
複製程式碼
whistle.script會把對應的事件名稱及引數值傳給後臺 ctx.dataSource
的對應監聽方法。