Skip to content

Instantly share code, notes, and snippets.

@westonruter
Created April 8, 2024 21:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save westonruter/11de17f8bd4079b5151dc09a93ccc0e2 to your computer and use it in GitHub Desktop.
Save westonruter/11de17f8bd4079b5151dc09a93ccc0e2 to your computer and use it in GitHub Desktop.
POC for how to hack Puppeteer to be able to use the JS Self-Profiling API
// Usage: node js-self-profile.mjs https://example.com/
import puppeteer from 'puppeteer';
import http from 'http';
import https from 'https';
const PROXY_SERVER_PORT = 8080;
// Create an HTTP server so that we can inject the Document-Policy header in the response. Otherwise, the JS Self-Profiling API is not available.
const server = http.createServer(async (req, res) => {
const requestUrl = new URL(req.url, 'http://localhost');
if (!requestUrl.searchParams.get('url')) {
return;
}
const url = new URL(requestUrl.searchParams.get('url'));
const options = {
headers: {
...req.headers,
...{
host: url.host
}
}
};
const proxyRequest = https.request(url, options, (proxyResponse) => {
res.setHeader('Document-Policy', 'js-profiling');
res.writeHead(proxyResponse.statusCode, proxyResponse.headers);
proxyResponse.pipe(res); // Forward the response to the client
});
req.pipe(proxyRequest); // Forward the request body
});
server.listen(PROXY_SERVER_PORT);
const browser = await puppeteer.launch({
devtools: false,
headless: true
});
const page = await browser.newPage();
await page.evaluateOnNewDocument(() => {
const profiler = new Profiler({sampleInterval: 1, maxBufferSize: 100000});
async function collectAndSendTrace() {
if (profiler.stopped) {
return;
}
window.selfProfilingTrace = await profiler.stop();
}
profiler.addEventListener('samplebufferfull', collectAndSendTrace);
window.addEventListener('load', collectAndSendTrace);
});
// Emulate mobile.
await page.emulate({
userAgent: 'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.99 Mobile Safari/537.36',
viewport: {width: 360, height: 800}
});
// await page.emulateCPUThrottling(4);
const url = new URL(`http://localhost:${PROXY_SERVER_PORT}`);
url.searchParams.append('url', process.argv[2]);
await page.goto(url.href, {timeout: 60000});
await page.waitForFunction(() => {
return window.selfProfilingTrace !== undefined;
});
const trace = await page.evaluate(() => {
return window.selfProfilingTrace;
});
process.stdout.write(JSON.stringify(trace));
await browser.close();
server.close();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment