From bcc969ccc43846b23f52f88da71c50d009ee8e58 Mon Sep 17 00:00:00 2001 From: Andrey Lushnikov Date: Fri, 20 Oct 2017 16:55:15 -0700 Subject: [PATCH] feat(interception): Implement request.mockResponse method (#1064) feat(interception): Implement request.respond method This patch implements a new Request.respond method. This allows users to fulfill the intercepted request with a hand-crafted response if they wish so. References #1020. --- docs/api.md | 35 ++++++++- lib/NetworkManager.js | 111 +++++++++++++++++++++++++++ test/assets/pptr.png | Bin 0 -> 6138 bytes test/golden/mock-binary-response.png | Bin 0 -> 6789 bytes test/test.js | 49 ++++++++++++ 5 files changed, 193 insertions(+), 2 deletions(-) create mode 100644 test/assets/pptr.png create mode 100644 test/golden/mock-binary-response.png diff --git a/docs/api.md b/docs/api.md index f733f7f2..f182ee58 100644 --- a/docs/api.md +++ b/docs/api.md @@ -169,6 +169,7 @@ * [request.method](#requestmethod) * [request.postData](#requestpostdata) * [request.resourceType](#requestresourcetype) + * [request.respond(response)](#requestrespondresponse) * [request.response()](#requestresponse) * [request.url](#requesturl) - [class: Response](#class-response) @@ -974,9 +975,10 @@ The extra HTTP headers will be sent with every request the page initiates. - `value` <[boolean]> Whether to enable request interception. - returns: <[Promise]> -Activating request interception enables `request.abort` and `request.continue`. +Activating request interception enables `request.abort`, `request.continue` and +`request.respond` methods. -An example of a naïve request interceptor which aborts all image requests: +An example of a naïve request interceptor that aborts all image requests: ```js const puppeteer = require('puppeteer'); @@ -994,6 +996,9 @@ puppeteer.launch().then(async browser => { }); ``` +> **NOTE** Request interception doesn't work with data URLs. Calling `abort`, +> `continue` or `respond` on requests for data URLs is a noop. + #### page.setUserAgent(userAgent) - `userAgent` <[string]> Specific user agent to use in this page - returns: <[Promise]> Promise which resolves when the user agent is set. @@ -1899,6 +1904,32 @@ Contains the request's post body, if any. Contains the request's resource type as it was perceived by the rendering engine. ResourceType will be one of the following: `document`, `stylesheet`, `image`, `media`, `font`, `script`, `texttrack`, `xhr`, `fetch`, `eventsource`, `websocket`, `manifest`, `other`. +#### request.respond(response) +- `response` <[Object]> Response that will fulfill this request + - `status` <[number]> Response status code, defaults to `200`. + - `headers` <[Object]> Optional response headers + - `contentType` <[string]> If set, equals to setting `Content-Type` response header + - `body` <[Buffer]|[string]> Optional response body +- returns: <[Promise]> + +Fulfills request with given response. To use this, request interception should +be enabled with `page.setRequestInterceptionEnabled`. Exception is thrown if +request interception is not enabled. + +An example of fulfilling all requests with 404 responses: + +```js +await page.setRequestInterceptionEnabled(true); +page.on('request', request => { + request.respond({ + status: 404, + contentType: 'text/plain', + body: 'Not Found!' + }); +}); +``` + + #### request.response() - returns: <[Response]> A matching [Response] object, or `null` if the response has not been received yet. diff --git a/lib/NetworkManager.js b/lib/NetworkManager.js index f63bb82c..2454e240 100644 --- a/lib/NetworkManager.js +++ b/lib/NetworkManager.js @@ -340,6 +340,54 @@ class Request { }); } + /** + * @param {!{status: number, headers: Object, contentType: string, body: (string|Buffer)}} response + */ + async respond(response) { + // DataURL's are not interceptable. In this case, do nothing. + if (this.url.startsWith('data:')) + return; + console.assert(this._allowInterception, 'Request Interception is not enabled!'); + console.assert(!this._interceptionHandled, 'Request is already handled!'); + this._interceptionHandled = true; + + const responseBody = response.body && helper.isString(response.body) ? Buffer.from(/** @type {string} */(response.body)) : /** @type {?Buffer} */(response.body || null); + + const responseHeaders = {}; + if (response.headers) { + for (const header of Object.keys(response.headers)) + responseHeaders[header.toLowerCase()] = response.headers[header]; + } + if (response.contentType) + responseHeaders['content-type'] = response.contentType; + if (responseBody && !('content-length' in responseHeaders)) { + // @ts-ignore + responseHeaders['content-length'] = Buffer.byteLength(responseBody); + } + + const statusCode = response.status || 200; + const statusText = statusTexts[statusCode] || ''; + const statusLine = `HTTP/1.1 ${statusCode} ${statusText}`; + + const CRLF = '\r\n'; + let text = statusLine + CRLF; + for (const header of Object.keys(responseHeaders)) + text += header + ': ' + responseHeaders[header] + CRLF; + text += CRLF; + let responseBuffer = Buffer.from(text, 'utf8'); + if (responseBody) + responseBuffer = Buffer.concat([responseBuffer, responseBody]); + + await this._client.send('Network.continueInterceptedRequest', { + interceptionId: this._interceptionId, + rawResponse: responseBuffer.toString('base64') + }).catch(error => { + // In certain cases, protocol will return error if the request was already canceled + // or the page was closed. We should tolerate these errors. + debugError(error); + }); + } + /** * @param {string=} errorCode */ @@ -486,4 +534,67 @@ NetworkManager.Events = { RequestFinished: 'requestfinished', }; +const statusTexts = { + '100': 'Continue', + '101': 'Switching Protocols', + '102': 'Processing', + '200': 'OK', + '201': 'Created', + '202': 'Accepted', + '203': 'Non-Authoritative Information', + '204': 'No Content', + '206': 'Partial Content', + '207': 'Multi-Status', + '208': 'Already Reported', + '209': 'IM Used', + '300': 'Multiple Choices', + '301': 'Moved Permanently', + '302': 'Found', + '303': 'See Other', + '304': 'Not Modified', + '305': 'Use Proxy', + '306': 'Switch Proxy', + '307': 'Temporary Redirect', + '308': 'Permanent Redirect', + '400': 'Bad Request', + '401': 'Unauthorized', + '402': 'Payment Required', + '403': 'Forbidden', + '404': 'Not Found', + '405': 'Method Not Allowed', + '406': 'Not Acceptable', + '407': 'Proxy Authentication Required', + '408': 'Request Timeout', + '409': 'Conflict', + '410': 'Gone', + '411': 'Length Required', + '412': 'Precondition Failed', + '413': 'Payload Too Large', + '414': 'URI Too Long', + '415': 'Unsupported Media Type', + '416': 'Range Not Satisfiable', + '417': 'Expectation Failed', + '418': 'I\'m a teapot', + '421': 'Misdirected Request', + '422': 'Unprocessable Entity', + '423': 'Locked', + '424': 'Failed Dependency', + '426': 'Upgrade Required', + '428': 'Precondition Required', + '429': 'Too Many Requests', + '431': 'Request Header Fields Too Large', + '451': 'Unavailable For Legal Reasons', + '500': 'Internal Server Error', + '501': 'Not Implemented', + '502': 'Bad Gateway', + '503': 'Service Unavailable', + '504': 'Gateway Timeout', + '505': 'HTTP Version Not Supported', + '506': 'Variant Also Negotiates', + '507': 'Insufficient Storage', + '508': 'Loop Detected', + '510': 'Not Extended', + '511': 'Network Authentication Required', +}; + module.exports = NetworkManager; diff --git a/test/assets/pptr.png b/test/assets/pptr.png new file mode 100644 index 0000000000000000000000000000000000000000..65d87c68e65902c058af18d2a595fc89f423f4ff GIT binary patch literal 6138 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 W{|p>bpZvrb0Q9tsH0#uzQ2zrM7gd%3 literal 0 HcmV?d00001 diff --git a/test/golden/mock-binary-response.png b/test/golden/mock-binary-response.png new file mode 100644 index 0000000000000000000000000000000000000000..8595e0598edf24deb4f3f9c35c2b16cd86720a4d GIT binary patch literal 6789 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 S5dr?a1g9*oAy*+|8S+0LA10Fk literal 0 HcmV?d00001 diff --git a/test/test.js b/test/test.js index 96fd2eae..fae8865d 100644 --- a/test/test.js +++ b/test/test.js @@ -1337,6 +1337,55 @@ describe('Page', function() { await request.continue().catch(e => error = e); expect(error).toBe(null); })); + it('should throw if interception is not enabled', SX(async function() { + let error = null; + page.on('request', async request => { + try { + await request.continue(); + } catch (e) { + error = e; + } + }); + await page.goto(EMPTY_PAGE); + expect(error.message).toContain('Request Interception is not enabled'); + })); + }); + + describe('Request.respond', function() { + it('should work', SX(async function() { + await page.setRequestInterceptionEnabled(true); + page.on('request', request => { + request.respond({ + status: 201, + headers: { + foo: 'bar' + }, + body: 'Yo, page!' + }); + }); + const response = await page.goto(EMPTY_PAGE); + expect(response.status).toBe(201); + expect(response.headers.foo).toBe('bar'); + expect(await page.evaluate(() => document.body.textContent)).toBe('Yo, page!'); + })); + it('should allow mocking binary responses', SX(async function() { + await page.setRequestInterceptionEnabled(true); + page.on('request', request => { + const imageBuffer = fs.readFileSync(path.join(__dirname, 'assets', 'pptr.png')); + request.respond({ + contentType: 'image/png', + body: imageBuffer + }); + }); + await page.evaluate(PREFIX => { + const img = document.createElement('img'); + img.src = PREFIX + '/does-not-exist.png'; + document.body.appendChild(img); + return new Promise(fulfill => img.onload = fulfill); + }, PREFIX); + const img = await page.$('img'); + expect(await img.screenshot()).toBeGolden('mock-binary-response.png'); + })); }); describe('Page.Events.Dialog', function() {