fix(chromium): make interception work with dedicated workers (#4658)

This commit is contained in:
Dmitry Gozman 2020-12-10 15:04:57 -08:00 committed by GitHub
parent b9c959768c
commit 495085cbb2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 53 additions and 31 deletions

View File

@ -200,6 +200,11 @@ export class CRNetworkManager {
}
}
let frame = requestWillBeSentEvent.frameId ? this._page._frameManager.frame(requestWillBeSentEvent.frameId) : workerFrame;
// Requests from workers lack frameId, because we receive Network.requestWillBeSent
// on the worker target. However, we receive Fetch.requestPaused on the page target,
// and lack workerFrame there. Luckily, Fetch.requestPaused provides a frameId.
if (!frame && requestPausedEvent && requestPausedEvent.frameId)
frame = this._page._frameManager.frame(requestPausedEvent.frameId);
// Check if it's main resource request interception (targetId === main frame id).
if (!frame && requestPausedEvent && requestWillBeSentEvent.frameId === (this._page._delegate as CRPage)._targetId) {
@ -209,32 +214,36 @@ export class CRNetworkManager {
frame = this._page._frameManager.frameAttached(requestWillBeSentEvent.frameId, null);
}
if (!frame) {
if (requestPausedEvent) {
// CORS options request is generated by the network stack, it is not associated with the frame id.
// If URL matches interception pattern, accept it, assuming that this was intended when setting route.
if (requestPausedEvent.request.method === 'OPTIONS' && this._page._needsRequestInterception()) {
const requestHeaders = requestPausedEvent.request.headers;
const responseHeaders: Protocol.Fetch.HeaderEntry[] = [
{ name: 'Access-Control-Allow-Origin', value: requestHeaders['Origin'] || '*' },
{ name: 'Access-Control-Allow-Methods', value: requestHeaders['Access-Control-Request-Method'] || 'GET, POST, OPTIONS, DELETE' },
{ name: 'Access-Control-Allow-Credentials', value: 'true' }
];
if (requestHeaders['Access-Control-Request-Headers'])
responseHeaders.push({ name: 'Access-Control-Allow-Headers', value: requestHeaders['Access-Control-Request-Headers'] });
this._client._sendMayFail('Fetch.fulfillRequest', {
requestId: requestPausedEvent.requestId,
responseCode: 204,
responsePhrase: network.STATUS_TEXTS['204'],
responseHeaders,
body: '',
});
} else {
this._client._sendMayFail('Fetch.continueRequest', { requestId: requestPausedEvent.requestId });
}
}
// CORS options request is generated by the network stack. If interception is enabled,
// we accept all CORS options, assuming that this was intended when setting route.
//
// Note: it would be better to match the URL against interception patterns, but
// that information is only available to the client. Perhaps we can just route to the client?
if (requestPausedEvent && requestPausedEvent.request.method === 'OPTIONS' && this._page._needsRequestInterception()) {
const requestHeaders = requestPausedEvent.request.headers;
const responseHeaders: Protocol.Fetch.HeaderEntry[] = [
{ name: 'Access-Control-Allow-Origin', value: requestHeaders['Origin'] || '*' },
{ name: 'Access-Control-Allow-Methods', value: requestHeaders['Access-Control-Request-Method'] || 'GET, POST, OPTIONS, DELETE' },
{ name: 'Access-Control-Allow-Credentials', value: 'true' }
];
if (requestHeaders['Access-Control-Request-Headers'])
responseHeaders.push({ name: 'Access-Control-Allow-Headers', value: requestHeaders['Access-Control-Request-Headers'] });
this._client._sendMayFail('Fetch.fulfillRequest', {
requestId: requestPausedEvent.requestId,
responseCode: 204,
responsePhrase: network.STATUS_TEXTS['204'],
responseHeaders,
body: '',
});
return;
}
if (!frame) {
if (requestPausedEvent)
this._client._sendMayFail('Fetch.continueRequest', { requestId: requestPausedEvent.requestId });
return;
}
let allowInterception = this._userRequestInterceptionEnabled;
if (redirectedFrom) {
allowInterception = false;

View File

@ -88,18 +88,15 @@ it('should work with glob', async () => {
expect(globToRegex('**/*.{png,jpg,jpeg}').test('https://localhost:8080/c.css')).toBeFalsy();
});
it('should intercept network activity from worker', (test, {browserName}) => {
// @see https://github.com/microsoft/playwright/issues/4487
test.fixme(browserName === 'chromium');
}, async function({page, server}) {
it('should intercept network activity from worker', async function({page, server}) {
await page.goto(server.EMPTY_PAGE);
server.setRoute('/data_for_worker', (req, res) => res.end('failed to intercept'));
const url = server.PREFIX + '/data_for_worker';
page.route(url, async route => {
await route.fulfill({
await page.route(url, route => {
route.fulfill({
status: 200,
body: 'intercepted',
});
}).catch(e => null);
});
const [msg] = await Promise.all([
page.waitForEvent('console'),
@ -110,6 +107,22 @@ it('should intercept network activity from worker', (test, {browserName}) => {
expect(msg.text()).toBe('intercepted');
});
it('should intercept network activity from worker', async function({page, server}) {
const url = server.PREFIX + '/worker/worker.js';
await page.route(url, route => {
route.fulfill({
status: 200,
body: 'console.log("intercepted");',
contentType: 'application/javascript',
}).catch(e => null);
});
const [msg] = await Promise.all([
page.waitForEvent('console'),
page.goto(server.PREFIX + '/worker/worker.html'),
]);
expect(msg.text()).toBe('intercepted');
});
it('should work with regular expression passed from a different context', async ({page, server}) => {
const ctx = vm.createContext();
const regexp = vm.runInContext('new RegExp("empty\\.html")', ctx);