diff --git a/src/client/network.ts b/src/client/network.ts index 218a2b2fb6..aa750344a3 100644 --- a/src/client/network.ts +++ b/src/client/network.ts @@ -51,7 +51,7 @@ export class Request extends ChannelOwner(); private _eventListeners: RegisteredListener[]; + private _requestIdToExtraInfo = new Map(); constructor(client: CRSession, page: Page, parentManager: CRNetworkManager | null) { this._client = client; @@ -50,6 +51,7 @@ export class CRNetworkManager { helper.addEventListener(session, 'Fetch.requestPaused', this._onRequestPaused.bind(this, workerFrame)), helper.addEventListener(session, 'Fetch.authRequired', this._onAuthRequired.bind(this)), helper.addEventListener(session, 'Network.requestWillBeSent', this._onRequestWillBeSent.bind(this, workerFrame)), + helper.addEventListener(session, 'Network.requestWillBeSentExtraInfo', this._onRequestWillBeSentExtraInfo.bind(this)), helper.addEventListener(session, 'Network.responseReceived', this._onResponseReceived.bind(this)), helper.addEventListener(session, 'Network.loadingFinished', this._onLoadingFinished.bind(this)), helper.addEventListener(session, 'Network.loadingFailed', this._onLoadingFailed.bind(this)), @@ -116,9 +118,22 @@ export class CRNetworkManager { } else { this._requestIdToRequestWillBeSentEvent.set(event.requestId, event); } - return; + } else { + this._onRequest(workerFrame, event, null); + } + const extraInfo = this._requestIdToExtraInfo.get(event.requestId); + if (extraInfo) + this._onRequestWillBeSentExtraInfo(extraInfo); + } + + _onRequestWillBeSentExtraInfo(event: Protocol.Network.requestWillBeSentExtraInfoPayload) { + const request = this._requestIdToRequest.get(event.requestId); + if (request) { + request.request._updateWithRawHeaders(headersObjectToArray(event.headers)); + this._requestIdToExtraInfo.delete(event.requestId); + } else { + this._requestIdToExtraInfo.set(event.requestId, event); } - this._onRequest(workerFrame, event, null); } _onAuthRequired(event: Protocol.Fetch.authRequiredPayload) { diff --git a/src/server/chromium/crPage.ts b/src/server/chromium/crPage.ts index 71de6f765c..428ced5340 100644 --- a/src/server/chromium/crPage.ts +++ b/src/server/chromium/crPage.ts @@ -128,19 +128,19 @@ export class CRPage implements PageDelegate { } async updateExtraHTTPHeaders(): Promise { - await this._forAllFrameSessions(frame => frame._updateExtraHTTPHeaders()); + await this._forAllFrameSessions(frame => frame._updateExtraHTTPHeaders(false)); } async updateGeolocation(): Promise { - await this._forAllFrameSessions(frame => frame._updateGeolocation()); + await this._forAllFrameSessions(frame => frame._updateGeolocation(false)); } async updateOffline(): Promise { - await this._forAllFrameSessions(frame => frame._updateOffline()); + await this._forAllFrameSessions(frame => frame._updateOffline(false)); } async updateHttpCredentials(): Promise { - await this._forAllFrameSessions(frame => frame._updateHttpCredentials()); + await this._forAllFrameSessions(frame => frame._updateHttpCredentials(false)); } async setViewportSize(viewportSize: types.Size): Promise { @@ -153,11 +153,11 @@ export class CRPage implements PageDelegate { } async updateEmulateMedia(): Promise { - await this._forAllFrameSessions(frame => frame._updateEmulateMedia()); + await this._forAllFrameSessions(frame => frame._updateEmulateMedia(false)); } async updateRequestInterception(): Promise { - await this._forAllFrameSessions(frame => frame._updateRequestInterception()); + await this._forAllFrameSessions(frame => frame._updateRequestInterception(false)); } async setFileChooserIntercepted(enabled: boolean) { @@ -446,12 +446,12 @@ class FrameSession { promises.push(emulateLocale(this._client, options.locale)); if (options.timezoneId) promises.push(emulateTimezone(this._client, options.timezoneId)); - promises.push(this._updateGeolocation()); - promises.push(this._updateExtraHTTPHeaders()); - promises.push(this._updateRequestInterception()); - promises.push(this._updateOffline()); - promises.push(this._updateHttpCredentials()); - promises.push(this._updateEmulateMedia()); + promises.push(this._updateGeolocation(true)); + promises.push(this._updateExtraHTTPHeaders(true)); + promises.push(this._updateRequestInterception(true)); + promises.push(this._updateOffline(true)); + promises.push(this._updateHttpCredentials(true)); + promises.push(this._updateEmulateMedia(true)); for (const binding of this._crPage._browserContext._pageBindings.values()) promises.push(this._initBinding(binding)); for (const binding of this._crPage._page._pageBindings.values()) @@ -794,27 +794,31 @@ class FrameSession { } } - async _updateExtraHTTPHeaders(): Promise { + async _updateExtraHTTPHeaders(initial: boolean): Promise { const headers = network.mergeHeaders([ this._crPage._browserContext._options.extraHTTPHeaders, this._page._state.extraHTTPHeaders ]); - await this._client.send('Network.setExtraHTTPHeaders', { headers: headersArrayToObject(headers, false /* lowerCase */) }); + if (!initial || headers.length) + await this._client.send('Network.setExtraHTTPHeaders', { headers: headersArrayToObject(headers, false /* lowerCase */) }); } - async _updateGeolocation(): Promise { + async _updateGeolocation(initial: boolean): Promise { const geolocation = this._crPage._browserContext._options.geolocation; - await this._client.send('Emulation.setGeolocationOverride', geolocation || {}); + if (!initial || geolocation) + await this._client.send('Emulation.setGeolocationOverride', geolocation || {}); } - async _updateOffline(): Promise { + async _updateOffline(initial: boolean): Promise { const offline = !!this._crPage._browserContext._options.offline; - await this._networkManager.setOffline(offline); + if (!initial || offline) + await this._networkManager.setOffline(offline); } - async _updateHttpCredentials(): Promise { + async _updateHttpCredentials(initial: boolean): Promise { const credentials = this._crPage._browserContext._options.httpCredentials || null; - await this._networkManager.authenticate(credentials); + if (!initial || credentials) + await this._networkManager.authenticate(credentials); } async _updateViewport(): Promise { @@ -855,13 +859,13 @@ class FrameSession { await Promise.all(promises); } - async _updateEmulateMedia(): Promise { + async _updateEmulateMedia(initial: boolean): Promise { const colorScheme = this._page._state.colorScheme || this._crPage._browserContext._options.colorScheme || 'light'; const features = colorScheme ? [{ name: 'prefers-color-scheme', value: colorScheme }] : []; await this._client.send('Emulation.setEmulatedMedia', { media: this._page._state.mediaType || '', features }); } - async _updateRequestInterception(): Promise { + async _updateRequestInterception(initial: boolean): Promise { await this._networkManager.setRequestInterception(this._page._needsRequestInterception()); } diff --git a/src/server/network.ts b/src/server/network.ts index 2939a63817..1e1d4d9de7 100644 --- a/src/server/network.ts +++ b/src/server/network.ts @@ -169,6 +169,10 @@ export class Request { return null; return new Route(this, this._routeDelegate); } + + _updateWithRawHeaders(headers: types.HeadersArray) { + this._headers = headers; + } } export class Route { diff --git a/test/network-request.spec.ts b/test/network-request.spec.ts index 7050a768e4..526656c5c7 100644 --- a/test/network-request.spec.ts +++ b/test/network-request.spec.ts @@ -82,7 +82,19 @@ it('should return headers', async ({page, server, isChromium, isFirefox, isWebKi }); it('should get the same headers as the server', (test, { browserName }) => { - test.fail(browserName === 'chromium' || browserName === 'webkit', 'Provisional headers differ from those in network stack'); + test.fail(browserName === 'webkit', 'Provisional headers differ from those in network stack'); +}, async ({ page, server }) => { + let serverRequest; + server.setRoute('/empty.html', (request, response) => { + serverRequest = request; + response.end('done'); + }); + const response = await page.goto(server.PREFIX + '/empty.html'); + expect(response.request().headers()).toEqual(serverRequest.headers); +}); + +it('should get the same headers as the server CORP', (test, { browserName }) => { + test.fail(browserName === 'webkit', 'Provisional headers differ from those in network stack'); }, async ({page, server}) => { await page.goto(server.PREFIX + '/empty.html'); let serverRequest; @@ -91,14 +103,14 @@ it('should get the same headers as the server', (test, { browserName }) => { response.writeHead(200, { 'Access-Control-Allow-Origin': '*' }); response.end('done'); }); - const requestPromise = page.waitForEvent('request'); + const responsePromise = page.waitForEvent('response'); const text = await page.evaluate(async url => { const data = await fetch(url); return data.text(); }, server.CROSS_PROCESS_PREFIX + '/something'); - const request = await requestPromise; + const response = await responsePromise; expect(text).toBe('done'); - expect(request.headers()).toEqual(serverRequest.headers); + expect(response.request().headers()).toEqual(serverRequest.headers); }); it('should return postData', async ({page, server}) => {