fix(cors): allow intercepting cors requests on chromium (#2643)
This commit is contained in:
parent
2251f9bedb
commit
eac7dab8b7
|
|
@ -10,7 +10,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "webkit",
|
"name": "webkit",
|
||||||
"revision": "1290"
|
"revision": "1292"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -187,8 +187,28 @@ export class CRNetworkManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!frame) {
|
if (!frame) {
|
||||||
if (requestPausedEvent)
|
if (requestPausedEvent) {
|
||||||
this._client._sendMayFail('Fetch.continueRequest', { requestId: requestPausedEvent.requestId });
|
// 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._isRouted(requestPausedEvent.request.url)) {
|
||||||
|
const requestHeaders = requestPausedEvent.request.headers;
|
||||||
|
const responseHeaders: Protocol.Fetch.HeaderEntry[] = [
|
||||||
|
{ name: 'Access-Control-Allow-Origin', value: requestHeaders['Access-Control-Allow-Methods'] || '*' },
|
||||||
|
{ name: 'Access-Control-Allow-Methods', value: requestHeaders['Access-Control-Request-Method'] || 'GET, POST, OPTIONS, DELETE' }
|
||||||
|
];
|
||||||
|
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 });
|
||||||
|
}
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let allowInterception = this._userRequestInterceptionEnabled;
|
let allowInterception = this._userRequestInterceptionEnabled;
|
||||||
|
|
|
||||||
12
src/page.ts
12
src/page.ts
|
|
@ -425,6 +425,18 @@ export class Page extends EventEmitter {
|
||||||
route.continue();
|
route.continue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_isRouted(requestURL: string): boolean {
|
||||||
|
for (const { url } of this._routes) {
|
||||||
|
if (helper.urlMatches(requestURL, url))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for (const { url } of this._browserContext._routes) {
|
||||||
|
if (helper.urlMatches(requestURL, url))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
async screenshot(options?: types.ScreenshotOptions): Promise<Buffer> {
|
async screenshot(options?: types.ScreenshotOptions): Promise<Buffer> {
|
||||||
return this._screenshotter.screenshotPage(options);
|
return this._screenshotter.screenshotPage(options);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -397,6 +397,95 @@ describe('Page.route', function() {
|
||||||
}, server.PREFIX + '/redirect_this');
|
}, server.PREFIX + '/redirect_this');
|
||||||
expect(text).toBe('');
|
expect(text).toBe('');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should support cors with GET', async({page, server}) => {
|
||||||
|
await page.goto(server.EMPTY_PAGE);
|
||||||
|
await page.route('**/cars*', async (route, request) => {
|
||||||
|
const headers = request.url().endsWith('allow') ? { 'access-control-allow-origin': '*' } : {};
|
||||||
|
await route.fulfill({
|
||||||
|
contentType: 'application/json',
|
||||||
|
headers,
|
||||||
|
status: 200,
|
||||||
|
body: JSON.stringify(['electric', 'gas']),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
{
|
||||||
|
// Should succeed
|
||||||
|
const resp = await page.evaluate(async () => {
|
||||||
|
const response = await fetch('https://example.com/cars?allow', { mode: 'cors' });
|
||||||
|
return response.json();
|
||||||
|
});
|
||||||
|
expect(resp).toEqual(['electric', 'gas']);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Should be rejected
|
||||||
|
const error = await page.evaluate(async () => {
|
||||||
|
const response = await fetch('https://example.com/cars?reject', { mode: 'cors' });
|
||||||
|
return response.json();
|
||||||
|
}).catch(e => e);
|
||||||
|
expect(error.message).toContain('failed');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support cors with POST', async({page, server}) => {
|
||||||
|
await page.goto(server.EMPTY_PAGE);
|
||||||
|
await page.route('**/cars', async (route) => {
|
||||||
|
await route.fulfill({
|
||||||
|
contentType: 'application/json',
|
||||||
|
headers: { 'Access-Control-Allow-Origin': '*' },
|
||||||
|
status: 200,
|
||||||
|
body: JSON.stringify(['electric', 'gas']),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
const resp = await page.evaluate(async () => {
|
||||||
|
const response = await fetch('https://example.com/cars', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
mode: 'cors',
|
||||||
|
body: JSON.stringify({ 'number': 1 })
|
||||||
|
});
|
||||||
|
return response.json();
|
||||||
|
});
|
||||||
|
expect(resp).toEqual(['electric', 'gas']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support cors for different methods', async({page, server}) => {
|
||||||
|
await page.goto(server.EMPTY_PAGE);
|
||||||
|
await page.route('**/cars', async (route, request) => {
|
||||||
|
await route.fulfill({
|
||||||
|
contentType: 'application/json',
|
||||||
|
headers: { 'Access-Control-Allow-Origin': '*' },
|
||||||
|
status: 200,
|
||||||
|
body: JSON.stringify([request.method(), 'electric', 'gas']),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// First POST
|
||||||
|
{
|
||||||
|
const resp = await page.evaluate(async () => {
|
||||||
|
const response = await fetch('https://example.com/cars', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
mode: 'cors',
|
||||||
|
body: JSON.stringify({ 'number': 1 })
|
||||||
|
});
|
||||||
|
return response.json();
|
||||||
|
});
|
||||||
|
expect(resp).toEqual(['POST', 'electric', 'gas']);
|
||||||
|
}
|
||||||
|
// Then DELETE
|
||||||
|
{
|
||||||
|
const resp = await page.evaluate(async () => {
|
||||||
|
const response = await fetch('https://example.com/cars', {
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: {},
|
||||||
|
mode: 'cors',
|
||||||
|
body: ''
|
||||||
|
});
|
||||||
|
return response.json();
|
||||||
|
});
|
||||||
|
expect(resp).toEqual(['DELETE', 'electric', 'gas']);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Request.continue', function() {
|
describe('Request.continue', function() {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue