Created
April 8, 2024 21:05
-
-
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
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
// 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