Make interception work with redirects (#218)
This patch: - changes interception API so that it better aligns with what we'd like to see in #121 - fixes the issue with redirect interception Fixes #217.
This commit is contained in:
parent
e1c5b8d244
commit
34b0095c10
20
docs/api.md
20
docs/api.md
@ -113,7 +113,7 @@
|
|||||||
+ [response.url](#responseurl)
|
+ [response.url](#responseurl)
|
||||||
* [class: InterceptedRequest](#class-interceptedrequest)
|
* [class: InterceptedRequest](#class-interceptedrequest)
|
||||||
+ [interceptedRequest.abort()](#interceptedrequestabort)
|
+ [interceptedRequest.abort()](#interceptedrequestabort)
|
||||||
+ [interceptedRequest.continue()](#interceptedrequestcontinue)
|
+ [interceptedRequest.continue([overrides])](#interceptedrequestcontinueoverrides)
|
||||||
+ [interceptedRequest.headers](#interceptedrequestheaders)
|
+ [interceptedRequest.headers](#interceptedrequestheaders)
|
||||||
+ [interceptedRequest.method](#interceptedrequestmethod)
|
+ [interceptedRequest.method](#interceptedrequestmethod)
|
||||||
+ [interceptedRequest.postData](#interceptedrequestpostdata)
|
+ [interceptedRequest.postData](#interceptedrequestpostdata)
|
||||||
@ -1112,40 +1112,38 @@ Contains the URL of the response.
|
|||||||
|
|
||||||
### class: InterceptedRequest
|
### class: InterceptedRequest
|
||||||
|
|
||||||
[InterceptedRequest] represents an intercepted request, which can be mutated and either continued or aborted. [InterceptedRequest] which is not continued or aborted will be in a 'hanging' state.
|
[InterceptedRequest] represents an intercepted request, which can be either continued or aborted. [InterceptedRequest] which is not continued or aborted will be in a 'hanging' state.
|
||||||
|
|
||||||
#### interceptedRequest.abort()
|
#### interceptedRequest.abort()
|
||||||
|
|
||||||
Aborts request.
|
Aborts request.
|
||||||
|
|
||||||
#### interceptedRequest.continue()
|
#### interceptedRequest.continue([overrides])
|
||||||
|
- `overrides` <[Object]> Optional request overwrites, which could be one of the following:
|
||||||
|
- `url` <[string]> If set, the request url will be changed
|
||||||
|
- `method` <[string]> If set changes the request method (e.g. `GET` or `POST`)
|
||||||
|
- `postData` <[string]> If set changes the post data of request
|
||||||
|
- `headers` <[Map]> If set changes the request HTTP headers
|
||||||
|
|
||||||
Continues request.
|
Continues request with optional request overrides.
|
||||||
|
|
||||||
#### interceptedRequest.headers
|
#### interceptedRequest.headers
|
||||||
- <[Map]> A map of HTTP headers associated with the request.
|
- <[Map]> A map of HTTP headers associated with the request.
|
||||||
|
|
||||||
Headers could be mutated. Must not be changed in response to an authChallenge.
|
|
||||||
|
|
||||||
#### interceptedRequest.method
|
#### interceptedRequest.method
|
||||||
- <[string]>
|
- <[string]>
|
||||||
|
|
||||||
Contains the request's method (GET, POST, etc.)
|
Contains the request's method (GET, POST, etc.)
|
||||||
|
|
||||||
If set this allows the request method to be overridden. Must not be changed in response to an authChallenge.
|
|
||||||
|
|
||||||
#### interceptedRequest.postData
|
#### interceptedRequest.postData
|
||||||
- <[string]>
|
- <[string]>
|
||||||
|
|
||||||
Contains `POST` data for `POST` requests.
|
Contains `POST` data for `POST` requests.
|
||||||
|
|
||||||
`request.postData` is mutable and could be written to. Must not be changed in response to an authChallenge.
|
|
||||||
|
|
||||||
#### interceptedRequest.url
|
#### interceptedRequest.url
|
||||||
- <[string]>
|
- <[string]>
|
||||||
|
|
||||||
If changed, the request url will be modified in a way that's not observable by page. Must not be changed in response to an authChallenge.
|
|
||||||
|
|
||||||
|
|
||||||
[Array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array "Array"
|
[Array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array "Array"
|
||||||
[boolean]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Boolean_type "Boolean"
|
[boolean]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Boolean_type "Boolean"
|
||||||
|
@ -247,17 +247,23 @@ class InterceptedRequest {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
continue() {
|
/**
|
||||||
|
* @param {!Object} overrides
|
||||||
|
*/
|
||||||
|
continue(overrides = {}) {
|
||||||
console.assert(!this._handled, 'This request is already handled!');
|
console.assert(!this._handled, 'This request is already handled!');
|
||||||
this._handled = true;
|
this._handled = true;
|
||||||
let headers = {};
|
let headers = undefined;
|
||||||
for (let entry of this.headers.entries())
|
if (overrides.headers) {
|
||||||
headers[entry[0]] = entry[1];
|
headers = {};
|
||||||
|
for (let entry of overrides.headers.entries())
|
||||||
|
headers[entry[0]] = entry[1];
|
||||||
|
}
|
||||||
this._client.send('Network.continueInterceptedRequest', {
|
this._client.send('Network.continueInterceptedRequest', {
|
||||||
interceptionId: this._interceptionId,
|
interceptionId: this._interceptionId,
|
||||||
url: this.url,
|
url: overrides.url,
|
||||||
method: this.method,
|
method: overrides.method,
|
||||||
postData: this.postData,
|
postData: overrides.postData,
|
||||||
headers: headers
|
headers: headers
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -236,12 +236,16 @@ class WebPage {
|
|||||||
*/
|
*/
|
||||||
function resourceInterceptor(request) {
|
function resourceInterceptor(request) {
|
||||||
let requestData = new RequestData(request);
|
let requestData = new RequestData(request);
|
||||||
let phantomRequest = new PhantomRequest(request);
|
let phantomRequest = new PhantomRequest();
|
||||||
callback(requestData, phantomRequest);
|
callback(requestData, phantomRequest);
|
||||||
if (phantomRequest._aborted)
|
if (phantomRequest._aborted) {
|
||||||
request.abort();
|
request.abort();
|
||||||
else
|
} else {
|
||||||
request.continue();
|
request.continue({
|
||||||
|
url: phantomRequest._url,
|
||||||
|
headers: phantomRequest._headers,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -608,11 +612,9 @@ class WebPageSettings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class PhantomRequest {
|
class PhantomRequest {
|
||||||
/**
|
constructor() {
|
||||||
* @param {!InterceptedRequest} request
|
this._url = undefined;
|
||||||
*/
|
this._headers = undefined;
|
||||||
constructor(request) {
|
|
||||||
this._request = request;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -620,7 +622,9 @@ class PhantomRequest {
|
|||||||
* @param {string} value
|
* @param {string} value
|
||||||
*/
|
*/
|
||||||
setHeader(key, value) {
|
setHeader(key, value) {
|
||||||
this._request.headers.set(key, value);
|
if (!this._headers)
|
||||||
|
this._headers = new Map();
|
||||||
|
this._headers.set(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
abort() {
|
abort() {
|
||||||
@ -631,7 +635,7 @@ class PhantomRequest {
|
|||||||
* @param {string} url
|
* @param {string} url
|
||||||
*/
|
*/
|
||||||
changeUrl(newUrl) {
|
changeUrl(newUrl) {
|
||||||
this._request.url = newUrl;
|
this._url = newUrl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
23
test/test.js
23
test/test.js
@ -724,7 +724,7 @@ describe('Page', function() {
|
|||||||
|
|
||||||
describe('Page.setRequestInterceptor', function() {
|
describe('Page.setRequestInterceptor', function() {
|
||||||
it('should intercept', SX(async function() {
|
it('should intercept', SX(async function() {
|
||||||
page.setRequestInterceptor(request => {
|
await page.setRequestInterceptor(request => {
|
||||||
expect(request.url).toContain('empty.html');
|
expect(request.url).toContain('empty.html');
|
||||||
expect(request.headers.has('User-Agent')).toBeTruthy();
|
expect(request.headers.has('User-Agent')).toBeTruthy();
|
||||||
expect(request.method).toBe('GET');
|
expect(request.method).toBe('GET');
|
||||||
@ -738,7 +738,7 @@ describe('Page', function() {
|
|||||||
await page.setExtraHTTPHeaders(new Map(Object.entries({
|
await page.setExtraHTTPHeaders(new Map(Object.entries({
|
||||||
foo: 'bar'
|
foo: 'bar'
|
||||||
})));
|
})));
|
||||||
page.setRequestInterceptor(request => {
|
await page.setRequestInterceptor(request => {
|
||||||
expect(request.headers.get('foo')).toBe('bar');
|
expect(request.headers.get('foo')).toBe('bar');
|
||||||
request.continue();
|
request.continue();
|
||||||
});
|
});
|
||||||
@ -746,7 +746,7 @@ describe('Page', function() {
|
|||||||
expect(response.ok).toBe(true);
|
expect(response.ok).toBe(true);
|
||||||
}));
|
}));
|
||||||
it('should be abortable', SX(async function() {
|
it('should be abortable', SX(async function() {
|
||||||
page.setRequestInterceptor(request => {
|
await page.setRequestInterceptor(request => {
|
||||||
if (request.url.endsWith('.css'))
|
if (request.url.endsWith('.css'))
|
||||||
request.abort();
|
request.abort();
|
||||||
else
|
else
|
||||||
@ -759,11 +759,12 @@ describe('Page', function() {
|
|||||||
expect(failedRequests).toBe(1);
|
expect(failedRequests).toBe(1);
|
||||||
}));
|
}));
|
||||||
it('should amend HTTP headers', SX(async function() {
|
it('should amend HTTP headers', SX(async function() {
|
||||||
await page.navigate(EMPTY_PAGE);
|
await page.setRequestInterceptor(request => {
|
||||||
page.setRequestInterceptor(request => {
|
let headers = new Map(request.headers);
|
||||||
request.headers.set('foo', 'bar');
|
headers.set('foo', 'bar');
|
||||||
request.continue();
|
request.continue({headers});
|
||||||
});
|
});
|
||||||
|
await page.navigate(EMPTY_PAGE);
|
||||||
const [request] = await Promise.all([
|
const [request] = await Promise.all([
|
||||||
server.waitForRequest('/sleep.zzz'),
|
server.waitForRequest('/sleep.zzz'),
|
||||||
page.evaluate(() => fetch('/sleep.zzz'))
|
page.evaluate(() => fetch('/sleep.zzz'))
|
||||||
@ -771,7 +772,7 @@ describe('Page', function() {
|
|||||||
expect(request.headers['foo']).toBe('bar');
|
expect(request.headers['foo']).toBe('bar');
|
||||||
}));
|
}));
|
||||||
it('should fail navigation when aborting main resource', SX(async function() {
|
it('should fail navigation when aborting main resource', SX(async function() {
|
||||||
page.setRequestInterceptor(request => request.abort());
|
await page.setRequestInterceptor(request => request.abort());
|
||||||
let error = null;
|
let error = null;
|
||||||
try {
|
try {
|
||||||
await page.navigate(EMPTY_PAGE);
|
await page.navigate(EMPTY_PAGE);
|
||||||
@ -781,6 +782,12 @@ describe('Page', function() {
|
|||||||
expect(error).toBeTruthy();
|
expect(error).toBeTruthy();
|
||||||
expect(error.message).toContain('Failed to navigate');
|
expect(error.message).toContain('Failed to navigate');
|
||||||
}));
|
}));
|
||||||
|
it('should work with redirects', SX(async function() {
|
||||||
|
server.setRedirect('/non-existing-page.html', '/empty.html');
|
||||||
|
await page.setRequestInterceptor(request => request.continue());
|
||||||
|
let response = await page.navigate(PREFIX + '/non-existing-page.html');
|
||||||
|
expect(response.status).toBe(200);
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Page.Events.Dialog', function() {
|
describe('Page.Events.Dialog', function() {
|
||||||
|
Loading…
Reference in New Issue
Block a user