拦截 WebSocket 消息
Intercept WebSocket messages
对于 ajax 请求,可以使用以下代码完成:
let oldXHROpen = window.XMLHttpRequest.prototype.open;
window.lastXhr = '';
window.XMLHttpRequest.prototype.open = function(method, url, async, user, password) {
this.addEventListener('load', function() {
window.lastXhr = this.responseText;
});
return oldXHROpen.apply(this, arguments);
};
lastXhr
变量将保存最后一个响应。
但是对于 websockets 如何实现呢?
简介
question/bounty/op 特意要求有信誉的消息来源。
我的建议是应该使用一个已知的经过验证的库,而不是推出自定义解决方案——它已经被社区使用、审核、分叉,并且通常被社区使用,并且托管在 github.[=18= 上]
第二种选择是自己动手(虽然不推荐),关于如何做到这一点有很多很好的答案,涉及 addEventListener
wshook
Wshook 是一个库(托管在 github 上),可以轻松拦截和修改 WebSocket 请求和消息事件。已多次加星和分叉。
免责声明:本人与具体项目无任何关系强文
示例:
wsHook.before = function(data, url, wsObject) {
console.log("Sending message to " + url + " : " + data);
}
// Make sure your program calls `wsClient.onmessage` event handler somewhere.
wsHook.after = function(messageEvent, url, wsObject) {
console.log("Received message from " + url + " : " + messageEvent.data);
return messageEvent;
}
从文档中,您会发现:
wsHook.before - function(data, url, wsObject):
Invoked just before
calling the actual WebSocket's send() method.
This method must return data which can be modified as well.
wsHook.after - function(event, url, wsObject):
Invoked just after
receiving the MessageEvent from the WebSocket server and before
calling the WebSocket's onmessage Event Handler.
Websocket addEventListener
WebSocket对象支持.addEventListener()
.
请参阅:
在类似于您的解决方案中,window.XMLHttpRequest
被替换为提供 window.lastXhr
的包装版本,我们将 window.WebSockets
替换为提供 [=15= 的包装版本] 包含从此脚本之后创建的所有 websocket 收到的所有消息和时间戳。
window.watchedWebSockets = [];
window.WebSocketMessages = [];
function WebSocketAttachWatcher(websocket) {
websocket.addEventListener("message", (event)=>window.WebSocketMessages.push([event.data,Date.now()]));
window.watchedWebSockets.push(websocket);
}
// here we replace WebSocket with a wrapped one, that attach listeners on
window.WebSocketUnchanged = window.WebSocket;
window.WebSocket = function(...args) {
const websocket = new window.WebSocketUnchanged(...args);
WebSocketAttachWatcher(websocket);
return websocket;
}
与您的 XMLRequest 案例不同,websocket 可能已经存在。如果你需要保证所有的 websockets 都会被捕获,那么你需要尽快制作这个包装器。如果你不能,有一个不太好的技巧可以在它们发送消息后捕获已经存在的 websockets:
// here we detect existing websockets on send event... not so trustable
window.WebSocketSendUnchanged = window.WebSocketUnchanged.prototype.send;
window.WebSocket.prototype.send = function(...args) {
console.log("firstsend");
if (!(this in window.watchedWebSockets))
WebSocketAttachWatcher(this);
this.send = window.WebSocketSendUnchanged; // avoid passing here again on next send
window.WebSocketSendUnchanged.call(this, ...args);
}
它不太可信,因为如果他们不发送但接收,他们将不会被注意到。
如果你使用的是 nodejs 那么你可以使用 socket.io
yarn add socket.io
安装后,可以使用socket.io
的middleware
io.use(async (socket, next) => {
try {
const user = await fetchUser(socket);
socket.user = user;
} catch (e) {
next(new Error("unknown user"));
}
});
you would need to make this wrapper as soon as possible
对于那个答案@brunoff 你是对的..你总是可以在服务器之前通过傀儡window逻辑使用你的功能,或你可以劫持data
来自 MessageEvent
本身
function listen(fn){
fn=fn||console.log
let property=Object.getOwnPropertyDescriptor
(MessageEvent.prototype,"data")
const data=property.get
function lookAtMessage(){ //to replace get function
let socket=this.currentTarget instanceof WebSocket
if(!socket){return data.call(this)}
let msg=data.call(this)
Object.defineProperty(this,"data",{value:msg}) //anti-loop
fn({data:msg,socket:this.currentTarget,event:this})
return msg
}
property.get=lookAtMessage
Object.defineProperty
(MessageEvent.prototype,"data",property)
}
listen( ({data})=>console.log(data) )
您可以尝试在 this page 上的控制台中输入代码 运行 然后 运行 他们的 websocket 示例
要拦截消息,您必须监视 onmessage = fn
和 addEventListener("message", fn)
调用。
为了能够修改 onmessage
我们必须首先覆盖全局 WebSocket
。下面是拦截传入消息,但以类似的方式,您可以监视 send
方法来拦截传出消息(客户端发送到服务器的消息)。
我在使用 Firebase 的页面上对此进行了测试,它运行良好,但您必须在其他脚本之前对其进行初始化,以确保 websocket 库(可以是 socket.io、ws 等)正在使用重写的 WebSocket
构造函数。
侦测传入消息和修改data
最终您可以 在调用真正的消息侦听器之前覆盖 data
– 如果您无法控制页面功能并且想要注入您的信息,这将变得很方便在消息监听器中拥有自己的数据。
const OriginalWebsocket = window.WebSocket
const ProxiedWebSocket = function() {
console.log("Intercepting web socket creation")
const ws = new OriginalWebsocket(...arguments)
const originalAddEventListener = ws.addEventListener
const proxiedAddEventListener = function() {
if (arguments[0] === "message") {
const cb = arguments[1]
arguments[1] = function() {
// Here you can get the actual data from the incoming messages
// Here you can even change the data before calling the real message listener
Object.defineProperty(e, "data", { value: 'your injected data' })
console.log("intercepted", arguments[0].data)
return cb.apply(this, arguments)
}
}
return originalAddEventListener.apply(this, arguments)
}
ws.addEventListener = proxiedAddEventListener
Object.defineProperty(ws, "onmessage", {
set(func) {
return proxiedAddEventListener.apply(this, [
"message",
func,
false
]);
}
});
return ws;
};
window.WebSocket = ProxiedWebSocket;
如果不需要修改数据,可以按照第二部分的回答。
在不修改数据的情况下侦测传入消息
如果您只想监听消息,而不覆盖数据,事情就简单了:
const OriginalWebsocket = window.WebSocket
const ProxiedWebSocket = function() {
const ws = new OriginalWebsocket(...arguments)
ws.addEventListener("message", function (e) {
// Only intercept
console.log(e.data)
})
return ws;
};
window.WebSocket = ProxiedWebSocket;
监视外发消息
以非常相似的方式,您可以代理用于向服务器发送数据的 send
方法。
const OriginalWebsocket = window.WebSocket
const ProxiedWebSocket = function() {
const ws = new OriginalWebsocket(...arguments)
const originalSend = ws.send
const proxiedSend = function() {
console.log("Intercepted outgoing ws message", arguments)
// Eventually change the sent data
// arguments[0] = ...
// arguments[1] = ...
return originalSend.apply(this, arguments)
}
ws.send = proxiedSend
return ws;
};
window.WebSocket = ProxiedWebSocket;
如有任何不清楚的地方,请随时提出任何问题。
对于 ajax 请求,可以使用以下代码完成:
let oldXHROpen = window.XMLHttpRequest.prototype.open;
window.lastXhr = '';
window.XMLHttpRequest.prototype.open = function(method, url, async, user, password) {
this.addEventListener('load', function() {
window.lastXhr = this.responseText;
});
return oldXHROpen.apply(this, arguments);
};
lastXhr
变量将保存最后一个响应。
但是对于 websockets 如何实现呢?
简介
question/bounty/op 特意要求有信誉的消息来源。 我的建议是应该使用一个已知的经过验证的库,而不是推出自定义解决方案——它已经被社区使用、审核、分叉,并且通常被社区使用,并且托管在 github.[=18= 上]
第二种选择是自己动手(虽然不推荐),关于如何做到这一点有很多很好的答案,涉及 addEventListener
wshook
Wshook 是一个库(托管在 github 上),可以轻松拦截和修改 WebSocket 请求和消息事件。已多次加星和分叉。
免责声明:本人与具体项目无任何关系强文
示例:
wsHook.before = function(data, url, wsObject) {
console.log("Sending message to " + url + " : " + data);
}
// Make sure your program calls `wsClient.onmessage` event handler somewhere.
wsHook.after = function(messageEvent, url, wsObject) {
console.log("Received message from " + url + " : " + messageEvent.data);
return messageEvent;
}
从文档中,您会发现:
wsHook.before - function(data, url, wsObject):
Invoked just before calling the actual WebSocket's send() method.
This method must return data which can be modified as well.
wsHook.after - function(event, url, wsObject):
Invoked just after receiving the MessageEvent from the WebSocket server and before calling the WebSocket's onmessage Event Handler.
Websocket addEventListener
WebSocket对象支持.addEventListener()
.
请参阅:
在类似于您的解决方案中,window.XMLHttpRequest
被替换为提供 window.lastXhr
的包装版本,我们将 window.WebSockets
替换为提供 [=15= 的包装版本] 包含从此脚本之后创建的所有 websocket 收到的所有消息和时间戳。
window.watchedWebSockets = [];
window.WebSocketMessages = [];
function WebSocketAttachWatcher(websocket) {
websocket.addEventListener("message", (event)=>window.WebSocketMessages.push([event.data,Date.now()]));
window.watchedWebSockets.push(websocket);
}
// here we replace WebSocket with a wrapped one, that attach listeners on
window.WebSocketUnchanged = window.WebSocket;
window.WebSocket = function(...args) {
const websocket = new window.WebSocketUnchanged(...args);
WebSocketAttachWatcher(websocket);
return websocket;
}
与您的 XMLRequest 案例不同,websocket 可能已经存在。如果你需要保证所有的 websockets 都会被捕获,那么你需要尽快制作这个包装器。如果你不能,有一个不太好的技巧可以在它们发送消息后捕获已经存在的 websockets:
// here we detect existing websockets on send event... not so trustable
window.WebSocketSendUnchanged = window.WebSocketUnchanged.prototype.send;
window.WebSocket.prototype.send = function(...args) {
console.log("firstsend");
if (!(this in window.watchedWebSockets))
WebSocketAttachWatcher(this);
this.send = window.WebSocketSendUnchanged; // avoid passing here again on next send
window.WebSocketSendUnchanged.call(this, ...args);
}
它不太可信,因为如果他们不发送但接收,他们将不会被注意到。
如果你使用的是 nodejs 那么你可以使用 socket.io
yarn add socket.io
安装后,可以使用socket.io
io.use(async (socket, next) => {
try {
const user = await fetchUser(socket);
socket.user = user;
} catch (e) {
next(new Error("unknown user"));
}
});
you would need to make this wrapper as soon as possible
对于那个答案@brunoff 你是对的..你总是可以在服务器之前通过傀儡window逻辑使用你的功能,或你可以劫持data
来自 MessageEvent
本身
function listen(fn){
fn=fn||console.log
let property=Object.getOwnPropertyDescriptor
(MessageEvent.prototype,"data")
const data=property.get
function lookAtMessage(){ //to replace get function
let socket=this.currentTarget instanceof WebSocket
if(!socket){return data.call(this)}
let msg=data.call(this)
Object.defineProperty(this,"data",{value:msg}) //anti-loop
fn({data:msg,socket:this.currentTarget,event:this})
return msg
}
property.get=lookAtMessage
Object.defineProperty
(MessageEvent.prototype,"data",property)
}
listen( ({data})=>console.log(data) )
您可以尝试在 this page 上的控制台中输入代码 运行 然后 运行 他们的 websocket 示例
要拦截消息,您必须监视 onmessage = fn
和 addEventListener("message", fn)
调用。
为了能够修改 onmessage
我们必须首先覆盖全局 WebSocket
。下面是拦截传入消息,但以类似的方式,您可以监视 send
方法来拦截传出消息(客户端发送到服务器的消息)。
我在使用 Firebase 的页面上对此进行了测试,它运行良好,但您必须在其他脚本之前对其进行初始化,以确保 websocket 库(可以是 socket.io、ws 等)正在使用重写的 WebSocket
构造函数。
侦测传入消息和修改data
最终您可以 在调用真正的消息侦听器之前覆盖 data
– 如果您无法控制页面功能并且想要注入您的信息,这将变得很方便在消息监听器中拥有自己的数据。
const OriginalWebsocket = window.WebSocket
const ProxiedWebSocket = function() {
console.log("Intercepting web socket creation")
const ws = new OriginalWebsocket(...arguments)
const originalAddEventListener = ws.addEventListener
const proxiedAddEventListener = function() {
if (arguments[0] === "message") {
const cb = arguments[1]
arguments[1] = function() {
// Here you can get the actual data from the incoming messages
// Here you can even change the data before calling the real message listener
Object.defineProperty(e, "data", { value: 'your injected data' })
console.log("intercepted", arguments[0].data)
return cb.apply(this, arguments)
}
}
return originalAddEventListener.apply(this, arguments)
}
ws.addEventListener = proxiedAddEventListener
Object.defineProperty(ws, "onmessage", {
set(func) {
return proxiedAddEventListener.apply(this, [
"message",
func,
false
]);
}
});
return ws;
};
window.WebSocket = ProxiedWebSocket;
如果不需要修改数据,可以按照第二部分的回答。
在不修改数据的情况下侦测传入消息
如果您只想监听消息,而不覆盖数据,事情就简单了:
const OriginalWebsocket = window.WebSocket
const ProxiedWebSocket = function() {
const ws = new OriginalWebsocket(...arguments)
ws.addEventListener("message", function (e) {
// Only intercept
console.log(e.data)
})
return ws;
};
window.WebSocket = ProxiedWebSocket;
监视外发消息
以非常相似的方式,您可以代理用于向服务器发送数据的 send
方法。
const OriginalWebsocket = window.WebSocket
const ProxiedWebSocket = function() {
const ws = new OriginalWebsocket(...arguments)
const originalSend = ws.send
const proxiedSend = function() {
console.log("Intercepted outgoing ws message", arguments)
// Eventually change the sent data
// arguments[0] = ...
// arguments[1] = ...
return originalSend.apply(this, arguments)
}
ws.send = proxiedSend
return ws;
};
window.WebSocket = ProxiedWebSocket;
如有任何不清楚的地方,请随时提出任何问题。