mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
fix(chromium): get headers from browser process when intercepting (#1809)
This commit is contained in:
parent
ba36860d79
commit
1b0467fb86
@ -33,7 +33,7 @@ export class CRNetworkManager {
|
|||||||
private _attemptedAuthentications = new Set<string>();
|
private _attemptedAuthentications = new Set<string>();
|
||||||
private _userRequestInterceptionEnabled = false;
|
private _userRequestInterceptionEnabled = false;
|
||||||
private _protocolRequestInterceptionEnabled = false;
|
private _protocolRequestInterceptionEnabled = false;
|
||||||
private _requestIdToInterceptionId = new Map<string, string>();
|
private _requestIdToRequestPausedEvent = new Map<string, Protocol.Fetch.requestPausedPayload>();
|
||||||
private _eventListeners: RegisteredListener[];
|
private _eventListeners: RegisteredListener[];
|
||||||
|
|
||||||
constructor(client: CRSession, page: Page) {
|
constructor(client: CRSession, page: Page) {
|
||||||
@ -106,10 +106,10 @@ export class CRNetworkManager {
|
|||||||
// Request interception doesn't happen for data URLs with Network Service.
|
// Request interception doesn't happen for data URLs with Network Service.
|
||||||
if (this._protocolRequestInterceptionEnabled && !event.request.url.startsWith('data:')) {
|
if (this._protocolRequestInterceptionEnabled && !event.request.url.startsWith('data:')) {
|
||||||
const requestId = event.requestId;
|
const requestId = event.requestId;
|
||||||
const interceptionId = this._requestIdToInterceptionId.get(requestId);
|
const requestPausedEvent = this._requestIdToRequestPausedEvent.get(requestId);
|
||||||
if (interceptionId) {
|
if (requestPausedEvent) {
|
||||||
this._onRequest(workerFrame, event, interceptionId);
|
this._onRequest(workerFrame, event, requestPausedEvent);
|
||||||
this._requestIdToInterceptionId.delete(requestId);
|
this._requestIdToRequestPausedEvent.delete(requestId);
|
||||||
} else {
|
} else {
|
||||||
this._requestIdToRequestWillBeSentEvent.set(event.requestId, event);
|
this._requestIdToRequestWillBeSentEvent.set(event.requestId, event);
|
||||||
}
|
}
|
||||||
@ -143,49 +143,56 @@ export class CRNetworkManager {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
const requestId = event.networkId;
|
const requestId = event.networkId;
|
||||||
const interceptionId = event.requestId;
|
|
||||||
const requestWillBeSentEvent = this._requestIdToRequestWillBeSentEvent.get(requestId);
|
const requestWillBeSentEvent = this._requestIdToRequestWillBeSentEvent.get(requestId);
|
||||||
if (requestWillBeSentEvent) {
|
if (requestWillBeSentEvent) {
|
||||||
this._onRequest(workerFrame, requestWillBeSentEvent, interceptionId);
|
this._onRequest(workerFrame, requestWillBeSentEvent, event);
|
||||||
this._requestIdToRequestWillBeSentEvent.delete(requestId);
|
this._requestIdToRequestWillBeSentEvent.delete(requestId);
|
||||||
} else {
|
} else {
|
||||||
this._requestIdToInterceptionId.set(requestId, interceptionId);
|
this._requestIdToRequestPausedEvent.set(requestId, event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_onRequest(workerFrame: frames.Frame | undefined, event: Protocol.Network.requestWillBeSentPayload, interceptionId: string | null) {
|
_onRequest(workerFrame: frames.Frame | undefined, requestWillBeSentEvent: Protocol.Network.requestWillBeSentPayload, requestPausedEvent: Protocol.Fetch.requestPausedPayload | null) {
|
||||||
if (event.request.url.startsWith('data:'))
|
if (requestWillBeSentEvent.request.url.startsWith('data:'))
|
||||||
return;
|
return;
|
||||||
let redirectedFrom: network.Request | null = null;
|
let redirectedFrom: network.Request | null = null;
|
||||||
if (event.redirectResponse) {
|
if (requestWillBeSentEvent.redirectResponse) {
|
||||||
const request = this._requestIdToRequest.get(event.requestId);
|
const request = this._requestIdToRequest.get(requestWillBeSentEvent.requestId);
|
||||||
// If we connect late to the target, we could have missed the requestWillBeSent event.
|
// If we connect late to the target, we could have missed the requestWillBeSent event.
|
||||||
if (request) {
|
if (request) {
|
||||||
this._handleRequestRedirect(request, event.redirectResponse);
|
this._handleRequestRedirect(request, requestWillBeSentEvent.redirectResponse);
|
||||||
redirectedFrom = request.request;
|
redirectedFrom = request.request;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let frame = event.frameId ? this._page._frameManager.frame(event.frameId) : workerFrame;
|
let frame = requestWillBeSentEvent.frameId ? this._page._frameManager.frame(requestWillBeSentEvent.frameId) : workerFrame;
|
||||||
|
|
||||||
// Check if it's main resource request interception (targetId === main frame id).
|
// Check if it's main resource request interception (targetId === main frame id).
|
||||||
if (!frame && interceptionId && event.frameId === (this._page._delegate as CRPage)._targetId) {
|
if (!frame && requestPausedEvent && requestWillBeSentEvent.frameId === (this._page._delegate as CRPage)._targetId) {
|
||||||
// Main resource request for the page is being intercepted so the Frame is not created
|
// Main resource request for the page is being intercepted so the Frame is not created
|
||||||
// yet. Precreate it here for the purposes of request interception. It will be updated
|
// yet. Precreate it here for the purposes of request interception. It will be updated
|
||||||
// later as soon as the request contnues and we receive frame tree from the page.
|
// later as soon as the request contnues and we receive frame tree from the page.
|
||||||
frame = this._page._frameManager.frameAttached(event.frameId, null);
|
frame = this._page._frameManager.frameAttached(requestWillBeSentEvent.frameId, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!frame) {
|
if (!frame) {
|
||||||
if (interceptionId)
|
if (requestPausedEvent)
|
||||||
this._client.send('Fetch.continueRequest', { requestId: interceptionId }).catch(debugError);
|
this._client.send('Fetch.continueRequest', { requestId: requestPausedEvent.requestId }).catch(debugError);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const isNavigationRequest = event.requestId === event.loaderId && event.type === 'Document';
|
const isNavigationRequest = requestWillBeSentEvent.requestId === requestWillBeSentEvent.loaderId && requestWillBeSentEvent.type === 'Document';
|
||||||
const documentId = isNavigationRequest ? event.loaderId : undefined;
|
const documentId = isNavigationRequest ? requestWillBeSentEvent.loaderId : undefined;
|
||||||
if (isNavigationRequest)
|
if (isNavigationRequest)
|
||||||
this._page._frameManager.frameUpdatedDocumentIdForNavigation(event.frameId!, documentId!);
|
this._page._frameManager.frameUpdatedDocumentIdForNavigation(requestWillBeSentEvent.frameId!, documentId!);
|
||||||
const request = new InterceptableRequest(this._client, frame, interceptionId, documentId, this._userRequestInterceptionEnabled, event, redirectedFrom);
|
const request = new InterceptableRequest({
|
||||||
this._requestIdToRequest.set(event.requestId, request);
|
client: this._client,
|
||||||
|
frame,
|
||||||
|
documentId,
|
||||||
|
allowInterception: this._userRequestInterceptionEnabled,
|
||||||
|
requestWillBeSentEvent,
|
||||||
|
requestPausedEvent,
|
||||||
|
redirectedFrom
|
||||||
|
});
|
||||||
|
this._requestIdToRequest.set(requestWillBeSentEvent.requestId, request);
|
||||||
this._page._frameManager.requestStarted(request.request);
|
this._page._frameManager.requestStarted(request.request);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -258,14 +265,29 @@ class InterceptableRequest implements network.RouteDelegate {
|
|||||||
_documentId: string | undefined;
|
_documentId: string | undefined;
|
||||||
private _client: CRSession;
|
private _client: CRSession;
|
||||||
|
|
||||||
constructor(client: CRSession, frame: frames.Frame, interceptionId: string | null, documentId: string | undefined, allowInterception: boolean, event: Protocol.Network.requestWillBeSentPayload, redirectedFrom: network.Request | null) {
|
constructor(options: {
|
||||||
|
client: CRSession;
|
||||||
|
frame: frames.Frame;
|
||||||
|
documentId?: string;
|
||||||
|
allowInterception: boolean;
|
||||||
|
requestWillBeSentEvent: Protocol.Network.requestWillBeSentPayload;
|
||||||
|
requestPausedEvent: Protocol.Fetch.requestPausedPayload | null;
|
||||||
|
redirectedFrom: network.Request | null;
|
||||||
|
}) {
|
||||||
|
const { client, frame, documentId, allowInterception, requestWillBeSentEvent, requestPausedEvent, redirectedFrom } = options;
|
||||||
this._client = client;
|
this._client = client;
|
||||||
this._requestId = event.requestId;
|
this._requestId = requestWillBeSentEvent.requestId;
|
||||||
this._interceptionId = interceptionId;
|
this._interceptionId = requestPausedEvent && requestPausedEvent.requestId;
|
||||||
this._documentId = documentId;
|
this._documentId = documentId;
|
||||||
|
|
||||||
this.request = new network.Request(allowInterception ? this : null, frame, redirectedFrom, documentId,
|
const {
|
||||||
event.request.url, (event.type || '').toLowerCase(), event.request.method, event.request.postData || null, headersObject(event.request.headers));
|
headers,
|
||||||
|
method,
|
||||||
|
url,
|
||||||
|
postData = null,
|
||||||
|
} = requestPausedEvent ? requestPausedEvent.request : requestWillBeSentEvent.request;
|
||||||
|
const type = (requestWillBeSentEvent.type || '').toLowerCase();
|
||||||
|
this.request = new network.Request(allowInterception ? this : null, frame, redirectedFrom, documentId, url, type, method, postData, headersObject(headers));
|
||||||
}
|
}
|
||||||
|
|
||||||
async continue(overrides: { method?: string; headers?: network.Headers; postData?: string } = {}) {
|
async continue(overrides: { method?: string; headers?: network.Headers; postData?: string } = {}) {
|
||||||
|
@ -511,6 +511,65 @@ describe('Request.fulfill', function() {
|
|||||||
expect(headers.foo).toBe('true');
|
expect(headers.foo).toBe('true');
|
||||||
expect(await page.evaluate(() => document.body.textContent)).toBe('Yo, page!');
|
expect(await page.evaluate(() => document.body.textContent)).toBe('Yo, page!');
|
||||||
});
|
});
|
||||||
|
it('should not modify the headers sent to the server', async({page, server}) => {
|
||||||
|
await page.goto(server.PREFIX + '/empty.html');
|
||||||
|
const interceptedRequests = [];
|
||||||
|
|
||||||
|
//this is just to enable request interception, which disables caching in chromium
|
||||||
|
await page.route(server.PREFIX + '/unused');
|
||||||
|
|
||||||
|
server.setRoute('/something', (request, response) => {
|
||||||
|
interceptedRequests.push(request);
|
||||||
|
response.writeHead(200, { 'Access-Control-Allow-Origin': '*' });
|
||||||
|
response.end('done');
|
||||||
|
});
|
||||||
|
|
||||||
|
const text = await page.evaluate(async url => {
|
||||||
|
const data = await fetch(url);
|
||||||
|
return data.text();
|
||||||
|
}, server.CROSS_PROCESS_PREFIX + '/something');
|
||||||
|
expect(text).toBe('done');
|
||||||
|
|
||||||
|
let playwrightRequest;
|
||||||
|
await page.route(server.CROSS_PROCESS_PREFIX + '/something', (route, request) => {
|
||||||
|
playwrightRequest = request;
|
||||||
|
route.continue({
|
||||||
|
headers: {
|
||||||
|
...request.headers()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const textAfterRoute = await page.evaluate(async url => {
|
||||||
|
const data = await fetch(url);
|
||||||
|
return data.text();
|
||||||
|
}, server.CROSS_PROCESS_PREFIX + '/something');
|
||||||
|
expect(textAfterRoute).toBe('done');
|
||||||
|
|
||||||
|
expect(interceptedRequests.length).toBe(2);
|
||||||
|
expect(interceptedRequests[1].headers).toEqual(interceptedRequests[0].headers);
|
||||||
|
});
|
||||||
|
it('should include the origin header', async({page, server}) => {
|
||||||
|
await page.goto(server.PREFIX + '/empty.html');
|
||||||
|
let interceptedRequest;
|
||||||
|
await page.route(server.CROSS_PROCESS_PREFIX + '/something', (route, request) => {
|
||||||
|
interceptedRequest = request;
|
||||||
|
route.fulfill({
|
||||||
|
headers: {
|
||||||
|
'Access-Control-Allow-Origin': '*',
|
||||||
|
},
|
||||||
|
contentType: 'text/plain',
|
||||||
|
body: 'done'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const text = await page.evaluate(async url => {
|
||||||
|
const data = await fetch(url);
|
||||||
|
return data.text();
|
||||||
|
}, server.CROSS_PROCESS_PREFIX + '/something');
|
||||||
|
expect(text).toBe('done');
|
||||||
|
expect(interceptedRequest.headers()['origin']).toEqual(server.PREFIX);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Interception vs isNavigationRequest', () => {
|
describe('Interception vs isNavigationRequest', () => {
|
||||||
|
@ -80,6 +80,23 @@ describe('Request.headers', function() {
|
|||||||
else if (WEBKIT)
|
else if (WEBKIT)
|
||||||
expect(response.request().headers()['user-agent']).toContain('WebKit');
|
expect(response.request().headers()['user-agent']).toContain('WebKit');
|
||||||
});
|
});
|
||||||
|
it.fail(CHROMIUM||WEBKIT)('should get the same headers as the server', async({page, server}) => {
|
||||||
|
await page.goto(server.PREFIX + '/empty.html');
|
||||||
|
let serverRequest;
|
||||||
|
await server.setRoute('/something', (request, response) => {
|
||||||
|
serverRequest = request;
|
||||||
|
response.writeHead(200, { 'Access-Control-Allow-Origin': '*' });
|
||||||
|
response.end('done');
|
||||||
|
});
|
||||||
|
const requestPromise = page.waitForEvent('request');
|
||||||
|
const text = await page.evaluate(async url => {
|
||||||
|
const data = await fetch(url);
|
||||||
|
return data.text();
|
||||||
|
}, server.CROSS_PROCESS_PREFIX + '/something');
|
||||||
|
const request = await requestPromise;
|
||||||
|
expect(text).toBe('done');
|
||||||
|
expect(request.headers()).toEqual(serverRequest.headers);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Response.headers', function() {
|
describe('Response.headers', function() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user