Created
May 30, 2024 14:26
-
-
Save mykeels/ef523be3122286eaff2a981c109220b7 to your computer and use it in GitHub Desktop.
Intercept and transform websocket messages
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Page } from '@playwright/test'; | |
export async function transformWebsocketMessage( | |
page: Page, | |
fn: <TMessage extends Record<string, unknown>>( | |
url: string, | |
message: TMessage | |
) => TMessage | |
) { | |
fn = fn || ((url, message) => message); | |
return page.evaluate((fn) => { | |
const MESSAGE_DELIMITER = '\u001e'; | |
const transformFn = eval(`(${fn})`) as ( | |
url: string, | |
message: Record<string, unknown> | |
) => Record<string, unknown>; | |
const OriginalWebsocket = window.WebSocket; | |
const ProxiedWebSocket = function () { | |
const url = arguments[0]; | |
console.warn('Intercepting web socket creation', ...arguments); | |
// @ts-ignore | |
const ws = new OriginalWebsocket(...arguments); | |
const tryJson = (data: string) => { | |
try { | |
const parsed = JSON.parse( | |
data?.trim().replace(MESSAGE_DELIMITER, '') | |
); | |
return [true, parsed]; | |
} catch (err) { | |
// @ts-ignore | |
console.error('JSON.parse error', err.toString()); | |
return [false, data]; | |
} | |
}; | |
const originalAddEventListener = ws.addEventListener; | |
const proxiedAddEventListener = function () { | |
console.warn('Intercepting addEventListener', ...arguments); | |
if (arguments[0] === 'message') { | |
const cb = arguments[1]; | |
arguments[1] = function (e: { data: string }) { | |
// Here you can get the actual data from the incoming messages | |
// Here you can even change the data before calling the real message listener | |
const [isJson, parsed] = tryJson(e.data); | |
const originalData = isJson ? parsed : e.data; | |
const transformedData = isJson | |
? transformFn(url, originalData) | |
: originalData; | |
console.warn( | |
'transformed', | |
JSON.stringify({ | |
originalData, | |
transformedData, | |
}) | |
); | |
if (isJson && Object.keys(transformedData).length) { | |
Object.defineProperty(e, 'data', { | |
value: JSON.stringify(transformedData) + MESSAGE_DELIMITER, | |
}); | |
} | |
return cb.apply(this, arguments); | |
}; | |
} | |
// @ts-ignore | |
return originalAddEventListener.apply(this, arguments); | |
}; | |
ws.addEventListener = proxiedAddEventListener; | |
Object.defineProperty(ws, 'onmessage', { | |
set(func) { | |
// @ts-ignore | |
return proxiedAddEventListener.apply(this, ['message', func, false]); | |
}, | |
}); | |
return ws; | |
}; | |
Object.defineProperty(ProxiedWebSocket, 'OPEN', { value: 1 }); | |
// @ts-ignore | |
window.WebSocket = ProxiedWebSocket; | |
console.warn('WebSocket intercepted'); | |
}, fn.toString()); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment