diff --git a/lib/NetworkManager.js b/lib/NetworkManager.js index 9096d2921fd..12f6b4a3285 100644 --- a/lib/NetworkManager.js +++ b/lib/NetworkManager.js @@ -28,8 +28,8 @@ class NetworkManager extends EventEmitter { this._frameManager = frameManager; /** @type {!Map} */ this._requestIdToRequest = new Map(); - /** @type {!Map} */ - this._interceptionIdToRequest = new Map(); + /** @type {!Map} */ + this._requestIdToRequestWillBeSentEvent = new Map(); /** @type {!Object} */ this._extraHTTPHeaders = {}; @@ -125,6 +125,25 @@ class NetworkManager extends EventEmitter { ]); } + /** + * @param {!Protocol.Network.requestWillBeSentPayload} event + */ + _onRequestWillBeSent(event) { + if (this._protocolRequestInterceptionEnabled) { + const requestHash = generateRequestHash(event.request); + const interceptionId = this._requestHashToInterceptionIds.firstValue(requestHash); + if (interceptionId) { + this._onRequest(event, interceptionId); + this._requestHashToInterceptionIds.delete(requestHash, interceptionId); + } else { + this._requestHashToRequestIds.set(requestHash, event.requestId); + this._requestIdToRequestWillBeSentEvent.set(event.requestId, event); + } + return; + } + this._onRequest(event, null); + } + /** * @param {!Protocol.Network.requestInterceptedPayload} event */ @@ -151,25 +170,36 @@ class NetworkManager extends EventEmitter { }).catch(debugError); } - if (event.redirectUrl) { - const request = this._interceptionIdToRequest.get(event.interceptionId); - if (request) { - this._handleRequestRedirect(request, event.responseStatusCode, event.responseHeaders, false /* fromDiskCache */, false /* fromServiceWorker */, null /* securityDetails */); - this._handleRequestStart(request._requestId, event.interceptionId, event.redirectUrl, event.isNavigationRequest, event.resourceType, event.request, event.frameId, request._redirectChain); - } - return; - } const requestHash = generateRequestHash(event.request); const requestId = this._requestHashToRequestIds.firstValue(requestHash); if (requestId) { + const requestWillBeSentEvent = this._requestIdToRequestWillBeSentEvent.get(requestId); + this._onRequest(requestWillBeSentEvent, event.interceptionId); this._requestHashToRequestIds.delete(requestHash, requestId); - this._handleRequestStart(requestId, event.interceptionId, event.request.url, event.isNavigationRequest, event.resourceType, event.request, event.frameId, []); + this._requestIdToRequestWillBeSentEvent.delete(requestId); } else { this._requestHashToInterceptionIds.set(requestHash, event.interceptionId); - this._handleRequestStart(null, event.interceptionId, event.request.url, event.isNavigationRequest, event.resourceType, event.request, event.frameId, []); } } + /** + * @param {!Protocol.Network.requestWillBeSentPayload} event + * @param {?string} interceptionId + */ + _onRequest(event, interceptionId) { + let redirectChain = []; + if (event.redirectResponse) { + const request = this._requestIdToRequest.get(event.requestId); + // If we connect late to the target, we could have missed the requestWillBeSent event. + if (request) { + this._handleRequestRedirect(request, event.redirectResponse.status, event.redirectResponse.headers, event.redirectResponse.fromDiskCache, event.redirectResponse.fromServiceWorker, event.redirectResponse.securityDetails); + redirectChain = request._redirectChain; + } + } + const isNavigationRequest = event.requestId === event.loaderId && event.type === 'Document'; + this._handleRequestStart(event.requestId, interceptionId, event.request.url, isNavigationRequest, event.type, event.request, event.frameId, redirectChain); + } + /** * @param {!Protocol.Network.requestServedFromCachePayload} event */ @@ -193,14 +223,13 @@ class NetworkManager extends EventEmitter { request._redirectChain.push(request); response._bodyLoadedPromiseFulfill.call(null, new Error('Response body is unavailable for redirect responses')); this._requestIdToRequest.delete(request._requestId); - this._interceptionIdToRequest.delete(request._interceptionId); this._attemptedAuthentications.delete(request._interceptionId); this.emit(NetworkManager.Events.Response, response); this.emit(NetworkManager.Events.RequestFinished, request); } /** - * @param {?string} requestId + * @param {string} requestId * @param {?string} interceptionId * @param {string} url * @param {boolean} isNavigationRequest @@ -214,46 +243,10 @@ class NetworkManager extends EventEmitter { if (frameId) frame = this._frameManager.frame(frameId); const request = new Request(this._client, requestId, interceptionId, isNavigationRequest, this._userRequestInterceptionEnabled, url, resourceType, requestPayload, frame, redirectChain); - if (requestId) - this._requestIdToRequest.set(requestId, request); - if (interceptionId) - this._interceptionIdToRequest.set(interceptionId, request); + this._requestIdToRequest.set(requestId, request); this.emit(NetworkManager.Events.Request, request); } - /** - * @param {!Protocol.Network.requestWillBeSentPayload} event - */ - _onRequestWillBeSent(event) { - if (this._protocolRequestInterceptionEnabled) { - // All redirects are handled in requestIntercepted. - if (event.redirectResponse) - return; - const requestHash = generateRequestHash(event.request); - const interceptionId = this._requestHashToInterceptionIds.firstValue(requestHash); - const request = interceptionId ? this._interceptionIdToRequest.get(interceptionId) : null; - if (request) { - request._requestId = event.requestId; - this._requestIdToRequest.set(event.requestId, request); - this._requestHashToInterceptionIds.delete(requestHash, interceptionId); - } else { - this._requestHashToRequestIds.set(requestHash, event.requestId); - } - return; - } - let redirectChain = []; - if (event.redirectResponse) { - const request = this._requestIdToRequest.get(event.requestId); - // If we connect late to the target, we could have missed the requestWillBeSent event. - if (request) { - this._handleRequestRedirect(request, event.redirectResponse.status, event.redirectResponse.headers, event.redirectResponse.fromDiskCache, event.redirectResponse.fromServiceWorker, event.redirectResponse.securityDetails); - redirectChain = request._redirectChain; - } - } - const isNavigationRequest = event.requestId === event.loaderId && event.type === 'Document'; - this._handleRequestStart(event.requestId, null, event.request.url, isNavigationRequest, event.type, event.request, event.frameId, redirectChain); - } - /** * @param {!Protocol.Network.responseReceivedPayload} event */ @@ -279,7 +272,6 @@ class NetworkManager extends EventEmitter { return; request.response()._bodyLoadedPromiseFulfill.call(null); this._requestIdToRequest.delete(request._requestId); - this._interceptionIdToRequest.delete(request._interceptionId); this._attemptedAuthentications.delete(request._interceptionId); this.emit(NetworkManager.Events.RequestFinished, request); } @@ -298,7 +290,6 @@ class NetworkManager extends EventEmitter { if (response) response._bodyLoadedPromiseFulfill.call(null); this._requestIdToRequest.delete(request._requestId); - this._interceptionIdToRequest.delete(request._interceptionId); this._attemptedAuthentications.delete(request._interceptionId); this.emit(NetworkManager.Events.RequestFailed, request); } diff --git a/package.json b/package.json index 46767bbcbe7..b5fffb82182 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "node": ">=6.4.0" }, "puppeteer": { - "chromium_revision": "574897" + "chromium_revision": "579032" }, "scripts": { "unit": "node test/test.js", diff --git a/test/network.spec.js b/test/network.spec.js index beb91c621a4..1a6c1f09f11 100644 --- a/test/network.spec.js +++ b/test/network.spec.js @@ -389,6 +389,30 @@ module.exports.addTests = function({testRunner, expect}) { expect(request.redirectChain().indexOf(request)).toBe(i); } }); + it('should work with redirects for subresources', async({page, server}) => { + await page.setRequestInterception(true); + const requests = []; + page.on('request', request => { + request.continue(); + requests.push(request); + }); + server.setRedirect('/one-style.css', '/two-style.css'); + server.setRedirect('/two-style.css', '/three-style.css'); + server.setRedirect('/three-style.css', '/four-style.css'); + server.setRoute('/four-style.css', (req, res) => res.end('body {box-sizing: border-box; }')); + + const response = await page.goto(server.PREFIX + '/one-style.html'); + expect(response.status()).toBe(200); + expect(response.url()).toContain('one-style.html'); + expect(requests.length).toBe(5); + expect(requests[0].resourceType()).toBe('document'); + expect(requests[1].resourceType()).toBe('stylesheet'); + // Check redirect chain + const redirectChain = requests[1].redirectChain(); + expect(redirectChain.length).toBe(3); + expect(redirectChain[0].url()).toContain('/one-style.css'); + expect(redirectChain[2].url()).toContain('/three-style.css'); + }); it('should be able to abort redirects', async({page, server}) => { await page.setRequestInterception(true); server.setRedirect('/non-existing.json', '/non-existing-2.json');