Skip to content

Commit 495085c

Browse files
authored
fix(chromium): make interception work with dedicated workers (#4658)
1 parent b9c9597 commit 495085c

File tree

2 files changed

+52
-30
lines changed

2 files changed

+52
-30
lines changed

src/server/chromium/crNetworkManager.ts

Lines changed: 32 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,11 @@ export class CRNetworkManager {
200200
}
201201
}
202202
let frame = requestWillBeSentEvent.frameId ? this._page._frameManager.frame(requestWillBeSentEvent.frameId) : workerFrame;
203+
// Requests from workers lack frameId, because we receive Network.requestWillBeSent
204+
// on the worker target. However, we receive Fetch.requestPaused on the page target,
205+
// and lack workerFrame there. Luckily, Fetch.requestPaused provides a frameId.
206+
if (!frame && requestPausedEvent && requestPausedEvent.frameId)
207+
frame = this._page._frameManager.frame(requestPausedEvent.frameId);
203208

204209
// Check if it's main resource request interception (targetId === main frame id).
205210
if (!frame && requestPausedEvent && requestWillBeSentEvent.frameId === (this._page._delegate as CRPage)._targetId) {
@@ -209,32 +214,36 @@ export class CRNetworkManager {
209214
frame = this._page._frameManager.frameAttached(requestWillBeSentEvent.frameId, null);
210215
}
211216

217+
// CORS options request is generated by the network stack. If interception is enabled,
218+
// we accept all CORS options, assuming that this was intended when setting route.
219+
//
220+
// Note: it would be better to match the URL against interception patterns, but
221+
// that information is only available to the client. Perhaps we can just route to the client?
222+
if (requestPausedEvent && requestPausedEvent.request.method === 'OPTIONS' && this._page._needsRequestInterception()) {
223+
const requestHeaders = requestPausedEvent.request.headers;
224+
const responseHeaders: Protocol.Fetch.HeaderEntry[] = [
225+
{ name: 'Access-Control-Allow-Origin', value: requestHeaders['Origin'] || '*' },
226+
{ name: 'Access-Control-Allow-Methods', value: requestHeaders['Access-Control-Request-Method'] || 'GET, POST, OPTIONS, DELETE' },
227+
{ name: 'Access-Control-Allow-Credentials', value: 'true' }
228+
];
229+
if (requestHeaders['Access-Control-Request-Headers'])
230+
responseHeaders.push({ name: 'Access-Control-Allow-Headers', value: requestHeaders['Access-Control-Request-Headers'] });
231+
this._client._sendMayFail('Fetch.fulfillRequest', {
232+
requestId: requestPausedEvent.requestId,
233+
responseCode: 204,
234+
responsePhrase: network.STATUS_TEXTS['204'],
235+
responseHeaders,
236+
body: '',
237+
});
238+
return;
239+
}
240+
212241
if (!frame) {
213-
if (requestPausedEvent) {
214-
// CORS options request is generated by the network stack, it is not associated with the frame id.
215-
// If URL matches interception pattern, accept it, assuming that this was intended when setting route.
216-
if (requestPausedEvent.request.method === 'OPTIONS' && this._page._needsRequestInterception()) {
217-
const requestHeaders = requestPausedEvent.request.headers;
218-
const responseHeaders: Protocol.Fetch.HeaderEntry[] = [
219-
{ name: 'Access-Control-Allow-Origin', value: requestHeaders['Origin'] || '*' },
220-
{ name: 'Access-Control-Allow-Methods', value: requestHeaders['Access-Control-Request-Method'] || 'GET, POST, OPTIONS, DELETE' },
221-
{ name: 'Access-Control-Allow-Credentials', value: 'true' }
222-
];
223-
if (requestHeaders['Access-Control-Request-Headers'])
224-
responseHeaders.push({ name: 'Access-Control-Allow-Headers', value: requestHeaders['Access-Control-Request-Headers'] });
225-
this._client._sendMayFail('Fetch.fulfillRequest', {
226-
requestId: requestPausedEvent.requestId,
227-
responseCode: 204,
228-
responsePhrase: network.STATUS_TEXTS['204'],
229-
responseHeaders,
230-
body: '',
231-
});
232-
} else {
233-
this._client._sendMayFail('Fetch.continueRequest', { requestId: requestPausedEvent.requestId });
234-
}
235-
}
242+
if (requestPausedEvent)
243+
this._client._sendMayFail('Fetch.continueRequest', { requestId: requestPausedEvent.requestId });
236244
return;
237245
}
246+
238247
let allowInterception = this._userRequestInterceptionEnabled;
239248
if (redirectedFrom) {
240249
allowInterception = false;

test/interception.spec.ts

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -88,18 +88,15 @@ it('should work with glob', async () => {
8888
expect(globToRegex('**/*.{png,jpg,jpeg}').test('https://localhost:8080/c.css')).toBeFalsy();
8989
});
9090

91-
it('should intercept network activity from worker', (test, {browserName}) => {
92-
// @see https://github.com/microsoft/playwright/issues/4487
93-
test.fixme(browserName === 'chromium');
94-
}, async function({page, server}) {
91+
it('should intercept network activity from worker', async function({page, server}) {
9592
await page.goto(server.EMPTY_PAGE);
9693
server.setRoute('/data_for_worker', (req, res) => res.end('failed to intercept'));
9794
const url = server.PREFIX + '/data_for_worker';
98-
page.route(url, async route => {
99-
await route.fulfill({
95+
await page.route(url, route => {
96+
route.fulfill({
10097
status: 200,
10198
body: 'intercepted',
102-
});
99+
}).catch(e => null);
103100
});
104101
const [msg] = await Promise.all([
105102
page.waitForEvent('console'),
@@ -110,6 +107,22 @@ it('should intercept network activity from worker', (test, {browserName}) => {
110107
expect(msg.text()).toBe('intercepted');
111108
});
112109

110+
it('should intercept network activity from worker', async function({page, server}) {
111+
const url = server.PREFIX + '/worker/worker.js';
112+
await page.route(url, route => {
113+
route.fulfill({
114+
status: 200,
115+
body: 'console.log("intercepted");',
116+
contentType: 'application/javascript',
117+
}).catch(e => null);
118+
});
119+
const [msg] = await Promise.all([
120+
page.waitForEvent('console'),
121+
page.goto(server.PREFIX + '/worker/worker.html'),
122+
]);
123+
expect(msg.text()).toBe('intercepted');
124+
});
125+
113126
it('should work with regular expression passed from a different context', async ({page, server}) => {
114127
const ctx = vm.createContext();
115128
const regexp = vm.runInContext('new RegExp("empty\\.html")', ctx);

0 commit comments

Comments
 (0)