puppeteer-interceptor
Advanced tools
Comparing version 1.1.0 to 2.0.0
import Protocol from 'devtools-protocol'; | ||
import { Page } from 'puppeteer/lib/Page'; | ||
import { Page, CDPSession } from 'puppeteer'; | ||
export * from './types'; | ||
@@ -28,2 +28,13 @@ export * from './request-patterns'; | ||
} | ||
export declare function intercept(page: Page, patterns?: Protocol.Fetch.RequestPattern[], eventHandlers?: Interceptor.EventHandlers): Promise<void>; | ||
export declare class InterceptionHandler { | ||
page: Page; | ||
patterns: Protocol.Fetch.RequestPattern[]; | ||
eventHandlers: Interceptor.EventHandlers; | ||
client?: CDPSession; | ||
disabled: boolean; | ||
constructor(page: Page, patterns?: Protocol.Fetch.RequestPattern[], eventHandlers?: Interceptor.EventHandlers); | ||
disable(): void; | ||
enable(): void; | ||
initialize(): Promise<void>; | ||
} | ||
export declare function intercept(page: Page, patterns?: Protocol.Fetch.RequestPattern[], eventHandlers?: Interceptor.EventHandlers): Promise<InterceptionHandler>; |
@@ -16,3 +16,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.intercept = void 0; | ||
exports.intercept = exports.InterceptionHandler = void 0; | ||
const atob_1 = __importDefault(require("atob")); | ||
@@ -24,59 +24,86 @@ const btoa_1 = __importDefault(require("btoa")); | ||
__exportStar(require("./request-patterns"), exports); | ||
async function intercept(page, patterns = [], eventHandlers = {}) { | ||
debug(`Registering interceptors for ${patterns.length} patterns`); | ||
const client = await page.target().createCDPSession(); | ||
await client.send('Fetch.enable', { patterns }); | ||
client.on('Fetch.requestPaused', async (event) => { | ||
const { requestId, request } = event; | ||
debug(`Request ${event.request.url} (${requestId}) paused.`); | ||
if (eventHandlers.onInterception) { | ||
let errorReason = 'Aborted'; | ||
let shouldContinue = true; | ||
const control = { | ||
abort: (msg) => { | ||
shouldContinue = false; | ||
errorReason = msg; | ||
}, | ||
}; | ||
await eventHandlers.onInterception(event, control); | ||
if (!shouldContinue) { | ||
debug(`Aborting request ${requestId} with reason "${errorReason}"`); | ||
await client.send('Fetch.failRequest', { requestId, errorReason }); | ||
class InterceptionHandler { | ||
constructor(page, patterns = [], eventHandlers = {}) { | ||
this.patterns = []; | ||
this.eventHandlers = {}; | ||
this.disabled = false; | ||
this.page = page; | ||
this.patterns = patterns; | ||
this.eventHandlers = eventHandlers; | ||
} | ||
disable() { | ||
this.disabled = true; | ||
} | ||
enable() { | ||
this.disabled = false; | ||
} | ||
async initialize() { | ||
this.client = await this.page.target().createCDPSession(); | ||
await this.client.send('Fetch.enable', { patterns: this.patterns }); | ||
this.client.on('Fetch.requestPaused', async (event) => { | ||
const { requestId, request } = event; | ||
if (this.disabled) { | ||
debug(`Interception handler disabled, continuing request.`); | ||
await this.client.send('Fetch.continueRequest', { requestId }); | ||
return; | ||
} | ||
} | ||
let newResponse = null; | ||
if (eventHandlers.onResponseReceived) { | ||
if (!event.responseStatusCode) { | ||
debug(`Warning: onResponseReceived handler passed but ${requestId} intercepted at Request stage. Handler can not be called.`); | ||
debug(`Request ${event.request.url} (${requestId}) paused.`); | ||
if (this.eventHandlers.onInterception) { | ||
let errorReason = 'Aborted'; | ||
let shouldContinue = true; | ||
const control = { | ||
abort: (msg) => { | ||
shouldContinue = false; | ||
errorReason = msg; | ||
}, | ||
}; | ||
await this.eventHandlers.onInterception(event, control); | ||
if (!shouldContinue) { | ||
debug(`Aborting request ${requestId} with reason "${errorReason}"`); | ||
await this.client.send('Fetch.failRequest', { requestId, errorReason }); | ||
return; | ||
} | ||
} | ||
else { | ||
const responseCdp = (await client.send('Fetch.getResponseBody', { | ||
let newResponse = null; | ||
if (this.eventHandlers.onResponseReceived) { | ||
if (!event.responseStatusCode) { | ||
debug(`Warning: onResponseReceived handler passed but ${requestId} intercepted at Request stage. Handler can not be called.`); | ||
} | ||
else { | ||
const responseCdp = (await this.client.send('Fetch.getResponseBody', { | ||
requestId, | ||
})); | ||
const response = { | ||
body: responseCdp.base64Encoded ? atob_1.default(responseCdp.body) : responseCdp.body, | ||
headers: event.responseHeaders, | ||
errorReason: event.responseErrorReason, | ||
statusCode: event.responseStatusCode, | ||
}; | ||
newResponse = await this.eventHandlers.onResponseReceived({ response, request }); | ||
} | ||
} | ||
if (newResponse) { | ||
debug(`Fulfilling request ${requestId} with response returned from onResponseReceived`); | ||
await this.client.send('Fetch.fulfillRequest', { | ||
requestId, | ||
})); | ||
const response = { | ||
body: responseCdp.base64Encoded ? atob_1.default(responseCdp.body) : responseCdp.body, | ||
headers: event.responseHeaders, | ||
errorReason: event.responseErrorReason, | ||
statusCode: event.responseStatusCode, | ||
}; | ||
newResponse = await eventHandlers.onResponseReceived({ response, request }); | ||
responseCode: newResponse.statusCode, | ||
responseHeaders: newResponse.headers, | ||
body: newResponse.base64Body ? newResponse.base64Body : btoa_1.default(newResponse.body), | ||
responsePhrase: newResponse.statusMessage, | ||
}); | ||
} | ||
} | ||
if (newResponse) { | ||
debug(`Fulfilling request ${requestId} with response returned from onResponseReceived`); | ||
await client.send('Fetch.fulfillRequest', { | ||
requestId, | ||
responseCode: newResponse.statusCode, | ||
responseHeaders: newResponse.headers, | ||
body: newResponse.base64Body ? newResponse.base64Body : btoa_1.default(newResponse.body), | ||
responsePhrase: newResponse.statusMessage, | ||
}); | ||
} | ||
else { | ||
await client.send('Fetch.continueRequest', { requestId }); | ||
} | ||
}); | ||
else { | ||
await this.client.send('Fetch.continueRequest', { requestId }); | ||
} | ||
}); | ||
} | ||
} | ||
exports.InterceptionHandler = InterceptionHandler; | ||
async function intercept(page, patterns = [], eventHandlers = {}) { | ||
debug(`Registering interceptors for ${patterns.length} patterns`); | ||
const interceptionHandler = new InterceptionHandler(page, patterns, eventHandlers); | ||
await interceptionHandler.initialize(); | ||
return interceptionHandler; | ||
} | ||
exports.intercept = intercept; | ||
//# sourceMappingURL=index.js.map |
@@ -6,15 +6,12 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const puppeteer_1 = __importDefault(require("puppeteer")); | ||
const src_1 = require("../src"); | ||
const assert_1 = __importDefault(require("assert")); | ||
const puppeteer_1 = __importDefault(require("puppeteer")); | ||
const server_1 = require("./server"); | ||
const test_server_1 = require("@jsoverson/test-server"); | ||
describe('interceptor', function () { | ||
let browser, context, page; | ||
const port = 5599; | ||
let baseUrl = `http://127.0.0.1:${port}/`; | ||
before((done) => { | ||
puppeteer_1.default.launch().then((b) => { | ||
browser = b; | ||
server_1.start(port, done); | ||
}); | ||
let server; | ||
before(async () => { | ||
browser = await puppeteer_1.default.launch({ headless: true }); | ||
server = await test_server_1.start(__dirname, 'server_root'); | ||
}); | ||
@@ -28,7 +25,8 @@ beforeEach(async () => { | ||
}); | ||
after((done) => { | ||
browser.close().then((_) => server_1.stop(done)); | ||
after(async () => { | ||
await browser.close(); | ||
await server.stop(); | ||
}); | ||
it('should not cause problems on the page', async function () { | ||
await page.goto(baseUrl, {}); | ||
await page.goto(server.url('index.html'), {}); | ||
src_1.intercept(page, src_1.patterns.All('*')); | ||
@@ -50,5 +48,39 @@ const title = await page.title(); | ||
}); | ||
await page.goto(baseUrl, {}); | ||
await page.goto(server.url('index.html'), {}); | ||
return promise; | ||
}); | ||
it('should support adding multiple, unique interceptors', async function () { | ||
let dynamicIntercepted = 0; | ||
let consoleIntercepted = 0; | ||
src_1.intercept(page, src_1.patterns.Script('*dynamic.js'), { | ||
onResponseReceived: () => { | ||
dynamicIntercepted++; | ||
}, | ||
}); | ||
src_1.intercept(page, src_1.patterns.Script('*console.js'), { | ||
onResponseReceived: () => { | ||
consoleIntercepted++; | ||
}, | ||
}); | ||
await page.setCacheEnabled(false); | ||
await page.goto(server.url('index.html'), {}); | ||
assert_1.default.equal(dynamicIntercepted, 1); | ||
assert_1.default.equal(consoleIntercepted, 1); | ||
}); | ||
it('should support removing interceptions', async function () { | ||
let timesCalled = 0; | ||
const pattern = src_1.patterns.Script('*dynamic.js'); | ||
const handlers = { | ||
onResponseReceived: () => { | ||
timesCalled++; | ||
}, | ||
}; | ||
const handler = await src_1.intercept(page, pattern, handlers); | ||
await page.setCacheEnabled(false); | ||
await page.goto(server.url('index.html'), {}); | ||
assert_1.default.equal(timesCalled, 1); | ||
handler.disable(); | ||
await page.goto(server.url('index.html'), {}); | ||
assert_1.default.equal(timesCalled, 1); | ||
}); | ||
it('should pass response to onResponseReceived', async function () { | ||
@@ -63,3 +95,3 @@ const promise = new Promise((resolve, reject) => { | ||
}); | ||
await page.goto(baseUrl, {}); | ||
await page.goto(server.url('index.html'), {}); | ||
return promise; | ||
@@ -74,3 +106,3 @@ }); | ||
}); | ||
await page.goto(baseUrl, {}); | ||
await page.goto(server.url('index.html'), {}); | ||
const dynamicHeader = await page.$('#dynamic'); | ||
@@ -83,3 +115,5 @@ const dynamicContents = await page.evaluate((header) => header.innerHTML, dynamicHeader); | ||
onResponseReceived: async (event) => { | ||
const value = await new Promise((resolve) => { setTimeout(() => resolve('Delayed'), 100); }); | ||
const value = await new Promise((resolve) => { | ||
setTimeout(() => resolve('Delayed'), 100); | ||
}); | ||
event.response.body = event.response.body.replace('Dynamic', value); | ||
@@ -89,3 +123,3 @@ return event.response; | ||
}); | ||
await page.goto(baseUrl, {}); | ||
await page.goto(server.url('index.html'), {}); | ||
const dynamicHeader = await page.$('#dynamic'); | ||
@@ -102,3 +136,3 @@ const dynamicContents = await page.evaluate((header) => header.innerHTML, dynamicHeader); | ||
}); | ||
await page.goto(baseUrl, {}); | ||
await page.goto(server.url('index.html'), {}); | ||
const dynamicHeader = await page.$('#dynamic'); | ||
@@ -105,0 +139,0 @@ const dynamicContents = await page.evaluate((header) => header.innerHTML, dynamicHeader); |
{ | ||
"name": "puppeteer-interceptor", | ||
"version": "1.1.0", | ||
"version": "2.0.0", | ||
"description": "Makes intercepting and modifying traffic from Puppeteer easier", | ||
@@ -8,6 +8,5 @@ "main": "dist/src/index.js", | ||
"scripts": { | ||
"build": "tsc", | ||
"compile": "npm run clean && tsc --declaration", | ||
"build": "npm run clean && tsc --declaration", | ||
"clean": "rm -rf dist", | ||
"prepublishOnly": "npm run compile", | ||
"prepublishOnly": "npm run build", | ||
"format": "prettier --write 'src/**/*.ts' 'test/**/*.ts'", | ||
@@ -38,2 +37,3 @@ "watch": "tsc -w", | ||
"devDependencies": { | ||
"@jsoverson/test-server": "^1.1.1", | ||
"@types/atob": "^2.1.2", | ||
@@ -47,3 +47,3 @@ "@types/btoa": "^1.2.3", | ||
"prettier": "^2.0.5", | ||
"puppeteer": "^4.0.0", | ||
"puppeteer": "^5.1.0", | ||
"serve": "^11.3.0", | ||
@@ -50,0 +50,0 @@ "serve-handler": "^6.1.2", |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
32922
384
14