mirror of
https://github.com/puppeteer/puppeteer
synced 2024-06-14 14:02:48 +00:00
feat(Network): implement request.redirectChain() api (#2194)
The request.redirectChain() returns all the requests issued to fetch the resource. Fixes #1816.
This commit is contained in:
parent
c4b1ac99b0
commit
ec3025fe84
28
docs/api.md
28
docs/api.md
@ -208,6 +208,7 @@
|
|||||||
* [request.headers()](#requestheaders)
|
* [request.headers()](#requestheaders)
|
||||||
* [request.method()](#requestmethod)
|
* [request.method()](#requestmethod)
|
||||||
* [request.postData()](#requestpostdata)
|
* [request.postData()](#requestpostdata)
|
||||||
|
* [request.redirectChain()](#requestredirectchain)
|
||||||
* [request.resourceType()](#requestresourcetype)
|
* [request.resourceType()](#requestresourcetype)
|
||||||
* [request.respond(response)](#requestrespondresponse)
|
* [request.respond(response)](#requestrespondresponse)
|
||||||
* [request.response()](#requestresponse)
|
* [request.response()](#requestresponse)
|
||||||
@ -2379,6 +2380,33 @@ page.on('requestfailed', request => {
|
|||||||
#### request.postData()
|
#### request.postData()
|
||||||
- returns: <[string]> Request's post body, if any.
|
- returns: <[string]> Request's post body, if any.
|
||||||
|
|
||||||
|
#### request.redirectChain()
|
||||||
|
- returns: <[Array]<[Request]>>
|
||||||
|
|
||||||
|
A `redirectChain` is a chain of requests initiated to fetch a resource.
|
||||||
|
- If there are no redirects and the request was successful, the chain will be empty.
|
||||||
|
- If a server responds with at least a single redirect, then the chain will
|
||||||
|
contain all the requests that were redirected.
|
||||||
|
|
||||||
|
`redirectChain` is shared between all the requests of the same chain.
|
||||||
|
|
||||||
|
For example, if the website `http://example.com` has a single redirect to
|
||||||
|
`https://example.com`, then the chain will contain one request:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const response = await page.goto('http://example.com');
|
||||||
|
const chain = response.request().redirectChain();
|
||||||
|
console.log(chain.length); // 1
|
||||||
|
console.log(chain[0].url()); // 'http://example.com'
|
||||||
|
```
|
||||||
|
|
||||||
|
If the website `https://google.com` has no redirects, then the chain will be empty:
|
||||||
|
```js
|
||||||
|
const response = await page.goto('https://google.com');
|
||||||
|
const chain = response.request().redirectChain();
|
||||||
|
console.log(chain.length); // 0
|
||||||
|
```
|
||||||
|
|
||||||
#### request.resourceType()
|
#### request.resourceType()
|
||||||
- returns: <[string]>
|
- returns: <[string]>
|
||||||
|
|
||||||
|
@ -154,7 +154,7 @@ class NetworkManager extends EventEmitter {
|
|||||||
const request = this._interceptionIdToRequest.get(event.interceptionId);
|
const request = this._interceptionIdToRequest.get(event.interceptionId);
|
||||||
if (request) {
|
if (request) {
|
||||||
this._handleRequestRedirect(request, event.responseStatusCode, event.responseHeaders, false /* fromDiskCache */, false /* fromServiceWorker */, null /* securityDetails */);
|
this._handleRequestRedirect(request, event.responseStatusCode, event.responseHeaders, false /* fromDiskCache */, false /* fromServiceWorker */, null /* securityDetails */);
|
||||||
this._handleRequestStart(request._requestId, event.interceptionId, event.redirectUrl, event.resourceType, event.request, event.frameId);
|
this._handleRequestStart(request._requestId, event.interceptionId, event.redirectUrl, event.resourceType, event.request, event.frameId, request._redirectChain);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -162,10 +162,10 @@ class NetworkManager extends EventEmitter {
|
|||||||
const requestId = this._requestHashToRequestIds.firstValue(requestHash);
|
const requestId = this._requestHashToRequestIds.firstValue(requestHash);
|
||||||
if (requestId) {
|
if (requestId) {
|
||||||
this._requestHashToRequestIds.delete(requestHash, requestId);
|
this._requestHashToRequestIds.delete(requestHash, requestId);
|
||||||
this._handleRequestStart(requestId, event.interceptionId, event.request.url, event.resourceType, event.request, event.frameId);
|
this._handleRequestStart(requestId, event.interceptionId, event.request.url, event.resourceType, event.request, event.frameId, []);
|
||||||
} else {
|
} else {
|
||||||
this._requestHashToInterceptionIds.set(requestHash, event.interceptionId);
|
this._requestHashToInterceptionIds.set(requestHash, event.interceptionId);
|
||||||
this._handleRequestStart(null, event.interceptionId, event.request.url, event.resourceType, event.request, event.frameId);
|
this._handleRequestStart(null, event.interceptionId, event.request.url, event.resourceType, event.request, event.frameId, []);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,6 +189,7 @@ class NetworkManager extends EventEmitter {
|
|||||||
_handleRequestRedirect(request, redirectStatus, redirectHeaders, fromDiskCache, fromServiceWorker, securityDetails) {
|
_handleRequestRedirect(request, redirectStatus, redirectHeaders, fromDiskCache, fromServiceWorker, securityDetails) {
|
||||||
const response = new Response(this._client, request, redirectStatus, redirectHeaders, fromDiskCache, fromServiceWorker, securityDetails);
|
const response = new Response(this._client, request, redirectStatus, redirectHeaders, fromDiskCache, fromServiceWorker, securityDetails);
|
||||||
request._response = response;
|
request._response = response;
|
||||||
|
request._redirectChain.push(request);
|
||||||
this._requestIdToRequest.delete(request._requestId);
|
this._requestIdToRequest.delete(request._requestId);
|
||||||
this._interceptionIdToRequest.delete(request._interceptionId);
|
this._interceptionIdToRequest.delete(request._interceptionId);
|
||||||
this._attemptedAuthentications.delete(request._interceptionId);
|
this._attemptedAuthentications.delete(request._interceptionId);
|
||||||
@ -203,12 +204,13 @@ class NetworkManager extends EventEmitter {
|
|||||||
* @param {string} resourceType
|
* @param {string} resourceType
|
||||||
* @param {!Object} requestPayload
|
* @param {!Object} requestPayload
|
||||||
* @param {?string} frameId
|
* @param {?string} frameId
|
||||||
|
* @param {!Array<!Request>} redirectChain
|
||||||
*/
|
*/
|
||||||
_handleRequestStart(requestId, interceptionId, url, resourceType, requestPayload, frameId) {
|
_handleRequestStart(requestId, interceptionId, url, resourceType, requestPayload, frameId, redirectChain) {
|
||||||
let frame = null;
|
let frame = null;
|
||||||
if (frameId)
|
if (frameId)
|
||||||
frame = this._frameManager.frame(frameId);
|
frame = this._frameManager.frame(frameId);
|
||||||
const request = new Request(this._client, requestId, interceptionId, this._userRequestInterceptionEnabled, url, resourceType, requestPayload, frame);
|
const request = new Request(this._client, requestId, interceptionId, this._userRequestInterceptionEnabled, url, resourceType, requestPayload, frame, redirectChain);
|
||||||
if (requestId)
|
if (requestId)
|
||||||
this._requestIdToRequest.set(requestId, request);
|
this._requestIdToRequest.set(requestId, request);
|
||||||
if (interceptionId)
|
if (interceptionId)
|
||||||
@ -236,13 +238,16 @@ class NetworkManager extends EventEmitter {
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
let redirectChain = [];
|
||||||
if (event.redirectResponse) {
|
if (event.redirectResponse) {
|
||||||
const request = this._requestIdToRequest.get(event.requestId);
|
const request = this._requestIdToRequest.get(event.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.status, event.redirectResponse.headers, event.redirectResponse.fromDiskCache, event.redirectResponse.fromServiceWorker, event.redirectResponse.securityDetails);
|
this._handleRequestRedirect(request, event.redirectResponse.status, event.redirectResponse.headers, event.redirectResponse.fromDiskCache, event.redirectResponse.fromServiceWorker, event.redirectResponse.securityDetails);
|
||||||
|
redirectChain = request._redirectChain;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this._handleRequestStart(event.requestId, null, event.request.url, event.type, event.request, event.frameId);
|
this._handleRequestStart(event.requestId, null, event.request.url, event.type, event.request, event.frameId, redirectChain);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -303,8 +308,9 @@ class Request {
|
|||||||
* @param {string} resourceType
|
* @param {string} resourceType
|
||||||
* @param {!Object} payload
|
* @param {!Object} payload
|
||||||
* @param {?Puppeteer.Frame} frame
|
* @param {?Puppeteer.Frame} frame
|
||||||
|
* @param {!Array<!Request>} redirectChain
|
||||||
*/
|
*/
|
||||||
constructor(client, requestId, interceptionId, allowInterception, url, resourceType, payload, frame) {
|
constructor(client, requestId, interceptionId, allowInterception, url, resourceType, payload, frame, redirectChain) {
|
||||||
this._client = client;
|
this._client = client;
|
||||||
this._requestId = requestId;
|
this._requestId = requestId;
|
||||||
this._interceptionId = interceptionId;
|
this._interceptionId = interceptionId;
|
||||||
@ -322,6 +328,7 @@ class Request {
|
|||||||
this._postData = payload.postData;
|
this._postData = payload.postData;
|
||||||
this._headers = {};
|
this._headers = {};
|
||||||
this._frame = frame;
|
this._frame = frame;
|
||||||
|
this._redirectChain = redirectChain;
|
||||||
for (const key of Object.keys(payload.headers))
|
for (const key of Object.keys(payload.headers))
|
||||||
this._headers[key.toLowerCase()] = payload.headers[key];
|
this._headers[key.toLowerCase()] = payload.headers[key];
|
||||||
|
|
||||||
@ -377,6 +384,13 @@ class Request {
|
|||||||
return this._frame;
|
return this._frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {!Array<!Request>}
|
||||||
|
*/
|
||||||
|
redirectChain() {
|
||||||
|
return this._redirectChain.slice();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {?{errorText: string}}
|
* @return {?{errorText: string}}
|
||||||
*/
|
*/
|
||||||
|
16
test/test.js
16
test/test.js
@ -1646,6 +1646,15 @@ describe('Page', function() {
|
|||||||
expect(response.url()).toContain('empty.html');
|
expect(response.url()).toContain('empty.html');
|
||||||
expect(requests.length).toBe(5);
|
expect(requests.length).toBe(5);
|
||||||
expect(requests[2].resourceType()).toBe('document');
|
expect(requests[2].resourceType()).toBe('document');
|
||||||
|
// Check redirect chain
|
||||||
|
const redirectChain = response.request().redirectChain();
|
||||||
|
expect(redirectChain.length).toBe(4);
|
||||||
|
expect(redirectChain[0].url()).toContain('/non-existing-page.html');
|
||||||
|
expect(redirectChain[2].url()).toContain('/non-existing-page-3.html');
|
||||||
|
for (let i = 0; i < redirectChain.length; ++i) {
|
||||||
|
const request = redirectChain[i];
|
||||||
|
expect(request.redirectChain().indexOf(request)).toBe(i);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
it('should be able to abort redirects', async({page, server}) => {
|
it('should be able to abort redirects', async({page, server}) => {
|
||||||
await page.setRequestInterception(true);
|
await page.setRequestInterception(true);
|
||||||
@ -2948,7 +2957,7 @@ describe('Page', function() {
|
|||||||
page.on('requestfailed', request => events.push(`FAIL ${request.url()}`));
|
page.on('requestfailed', request => events.push(`FAIL ${request.url()}`));
|
||||||
server.setRedirect('/foo.html', '/empty.html');
|
server.setRedirect('/foo.html', '/empty.html');
|
||||||
const FOO_URL = server.PREFIX + '/foo.html';
|
const FOO_URL = server.PREFIX + '/foo.html';
|
||||||
await page.goto(FOO_URL);
|
const response = await page.goto(FOO_URL);
|
||||||
expect(events).toEqual([
|
expect(events).toEqual([
|
||||||
`GET ${FOO_URL}`,
|
`GET ${FOO_URL}`,
|
||||||
`302 ${FOO_URL}`,
|
`302 ${FOO_URL}`,
|
||||||
@ -2957,6 +2966,11 @@ describe('Page', function() {
|
|||||||
`200 ${server.EMPTY_PAGE}`,
|
`200 ${server.EMPTY_PAGE}`,
|
||||||
`DONE ${server.EMPTY_PAGE}`
|
`DONE ${server.EMPTY_PAGE}`
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// Check redirect chain
|
||||||
|
const redirectChain = response.request().redirectChain();
|
||||||
|
expect(redirectChain.length).toBe(1);
|
||||||
|
expect(redirectChain[0].url()).toContain('/foo.html');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user