From 97ec8d69aaae3207d865272ebc32baa0ad7e59b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E5=9D=A4=E5=9D=A4?= Date: Wed, 31 Jul 2019 15:47:03 +0800 Subject: [PATCH] manually merge pr #9170 --- lighthouse-cli/bin.js | 13 +++++ lighthouse-cli/cli-flags.js | 10 +++- .../test/cli/__snapshots__/index-test.js.snap | 2 + lighthouse-cli/test/cli/bin-test.js | 16 +++++ lighthouse-cli/test/fixtures/static-server.js | 13 +++++ lighthouse-core/config/constants.js | 1 + lighthouse-core/gather/driver.js | 12 ++++ lighthouse-core/gather/gather-runner.js | 23 +++++++- lighthouse-core/test/gather/driver-test.js | 26 +++++++++ lighthouse-core/test/gather/fake-driver.js | 3 + .../test/gather/gather-runner-test.js | 58 ++++++++++++++++++- .../test/results/artifacts/artifacts.json | 1 + lighthouse-core/test/results/sample_v2.json | 1 + readme.md | 3 + types/externs.d.ts | 2 + 15 files changed, 181 insertions(+), 3 deletions(-) diff --git a/lighthouse-cli/bin.js b/lighthouse-cli/bin.js index 887a533d8623..ed385963a7bd 100644 --- a/lighthouse-cli/bin.js +++ b/lighthouse-cli/bin.js @@ -122,6 +122,19 @@ async function begin() { cliFlags.extraHeaders = JSON.parse(extraHeadersStr); } + if (cliFlags.extraCookies) { + // TODO: LH.Flags.extraCookies is actually a string at this point, but needs to be + // copied over to LH.Settings.extraCookies, which is LH.Crdp.Network.Cookies. Force + // the conversion here, but long term either the CLI flag or the setting should have + // a different name. + // @ts-ignore + const extraCookiesStr = /** @type {string} */ (cliFlags.extraCookies); + cliFlags.extraCookies = JSON.parse(extraCookiesStr); + if (!Array.isArray(cliFlags.extraCookies)) { + throw new Error('extraCookies parameter must be a valid JSON array'); + } + } + if (cliFlags.precomputedLanternDataPath) { const lanternDataStr = fs.readFileSync(cliFlags.precomputedLanternDataPath, 'utf8'); /** @type {LH.PrecomputedLanternData} */ diff --git a/lighthouse-cli/cli-flags.js b/lighthouse-cli/cli-flags.js index 9e7188f042e1..2f3f94c8b3e0 100644 --- a/lighthouse-cli/cli-flags.js +++ b/lighthouse-cli/cli-flags.js @@ -53,11 +53,17 @@ function getFlags(manualArgv) { 'lighthouse --quiet --chrome-flags="--headless"', 'Launch Headless Chrome, turn off logging') .example( - 'lighthouse --extra-headers "{\\"Cookie\\":\\"monster=blue\\", \\"x-men\\":\\"wolverine\\"}"', + 'lighthouse --extra-headers "{\\"x-men\\":\\"wolverine\\"}"', 'Stringify\'d JSON HTTP Header key/value pairs to send in requests') .example( 'lighthouse --extra-headers=./path/to/file.json', 'Path to JSON file of HTTP Header key/value pairs to send in requests') + .example( + 'lighthouse --extra-cookies "[{\\"name\\":\\"session_id\\",\\"value\\":\\"x-men\\" }]"', + 'Stringify\'d JSON array of HTTP Cookies to send in requests') + .example( + 'lighthouse --extra-cookies=./path/to/file.json', + 'Path to JSON file of HTTP Cookies to send in requests') .example( 'lighthouse --only-categories=performance,pwa', 'Only run specific categories.') @@ -126,6 +132,7 @@ function getFlags(manualArgv) { 'max-wait-for-load': 'The timeout (in milliseconds) to wait before the page is considered done loading and the run should continue. WARNING: Very high values can lead to large traces and instability', 'extra-headers': 'Set extra HTTP Headers to pass with request', + 'extra-cookies': 'Set extra HTTP Cookies to pass with request', 'precomputed-lantern-data-path': 'Path to the file where lantern simulation data should be read from, overwriting the lantern observed estimates for RTT and server latency.', 'lantern-data-output-path': 'Path to the file where lantern simulation data should be written to, can be used in a future run with the `precomputed-lantern-data-path` flag.', 'only-audits': 'Only run the specified audits', @@ -166,6 +173,7 @@ function getFlags(manualArgv) { .array('output') .array('plugins') .string('extraHeaders') + .string('extraCookies') .string('channel') .string('precomputedLanternDataPath') .string('lanternDataOutputPath') diff --git a/lighthouse-cli/test/cli/__snapshots__/index-test.js.snap b/lighthouse-cli/test/cli/__snapshots__/index-test.js.snap index dbb9cbc02830..9be9ba64ad10 100644 --- a/lighthouse-cli/test/cli/__snapshots__/index-test.js.snap +++ b/lighthouse-cli/test/cli/__snapshots__/index-test.js.snap @@ -1245,6 +1245,7 @@ Object { "channel": "cli", "disableStorageReset": false, "emulatedFormFactor": "mobile", + "extraCookies": null, "extraHeaders": null, "gatherMode": false, "locale": "en-US", @@ -1379,6 +1380,7 @@ Object { "channel": "cli", "disableStorageReset": false, "emulatedFormFactor": "mobile", + "extraCookies": null, "extraHeaders": null, "gatherMode": false, "locale": "en-US", diff --git a/lighthouse-cli/test/cli/bin-test.js b/lighthouse-cli/test/cli/bin-test.js index d6b3ce96b3d6..b0fd94a2c8d5 100644 --- a/lighthouse-cli/test/cli/bin-test.js +++ b/lighthouse-cli/test/cli/bin-test.js @@ -170,6 +170,22 @@ describe('CLI bin', function() { }); }); + describe('extraCookies', () => { + it('should convert extra cookies to object', async () => { + // @ts-ignore - see TODO: in bin.js + cliFlags = { ...cliFlags, extraCookies: '[{"name":"foo", "value": "bar", "url": "http://localhost"}]' }; + await bin.begin(); + + expect(getRunLighthouseArgs()[1]).toHaveProperty('extraCookies', [{ 'name': 'foo', 'value': 'bar', 'url': 'http://localhost' }]); + }); + + it('should throw when invalid array is used', async () => { + // @ts-ignore - see TODO: in bin.js + cliFlags = { ...cliFlags, extraCookies: 'INVALID_JSON_ARRAY' }; + await expect(bin.begin()).rejects.toBeTruthy(); + }); + }); + describe('precomputedLanternData', () => { it('should read lantern data from file', async () => { const lanternDataFile = require.resolve('../fixtures/lantern-data.json'); diff --git a/lighthouse-cli/test/fixtures/static-server.js b/lighthouse-cli/test/fixtures/static-server.js index 2a0cdf6eacee..39c29ba6986e 100644 --- a/lighthouse-cli/test/fixtures/static-server.js +++ b/lighthouse-cli/test/fixtures/static-server.js @@ -105,6 +105,19 @@ function requestHandler(request, response) { } } + if (params.has('extra_cookie')) { + const extraCookies = new URLSearchParams(params.get('extra_cookie')); + let cookeString = ''; + for (const [cookieName, cookieValue] of extraCookies) { + cookeString += cookieName + '=' + cookieValue + ';'; + } + + // Extra cookie we allways override possible 'Set-Cookie' header + // which may be already present in request by extra_header + headers['Set-Cookie'] = []; + headers['Set-Cookie'].push(cookeString); + } + if (params.has('gzip')) { useGzip = Boolean(params.get('gzip')); } diff --git a/lighthouse-core/config/constants.js b/lighthouse-core/config/constants.js index 9f5ce283b313..8bac08564d95 100644 --- a/lighthouse-core/config/constants.js +++ b/lighthouse-core/config/constants.js @@ -60,6 +60,7 @@ const defaultSettings = { blockedUrlPatterns: null, additionalTraceCategories: null, extraHeaders: null, + extraCookies: null, precomputedLanternData: null, onlyAudits: null, onlyCategories: null, diff --git a/lighthouse-core/gather/driver.js b/lighthouse-core/gather/driver.js index 1d0cfea5de49..dcaf0f721290 100644 --- a/lighthouse-core/gather/driver.js +++ b/lighthouse-core/gather/driver.js @@ -1487,6 +1487,18 @@ class Driver { return this.sendCommand('Network.setExtraHTTPHeaders', {headers}); } + /** + * @param {LH.Crdp.Network.CookieParam[]|null} cookies key/value pairs of HTTP Cookies. + * @return {Promise} + */ + async setCookies(cookies) { + if (!cookies) { + return; + } + + return this.sendCommand('Network.setCookies', {cookies}); + } + /** * @param {string} url * @return {Promise} diff --git a/lighthouse-core/gather/gather-runner.js b/lighthouse-core/gather/gather-runner.js index 71b13a002513..001e8d73900f 100644 --- a/lighthouse-core/gather/gather-runner.js +++ b/lighthouse-core/gather/gather-runner.js @@ -230,7 +230,7 @@ class GatherRunner { /** * Initialize network settings for the pass, e.g. throttling, blocked URLs, - * and manual request headers. + * manual request headers and cookies. * @param {LH.Gatherer.PassContext} passContext * @return {Promise} */ @@ -249,10 +249,31 @@ class GatherRunner { // neccessary at the beginning of the next pass. await passContext.driver.blockUrlPatterns(blockedUrls); await passContext.driver.setExtraHTTPHeaders(passContext.settings.extraHeaders); + await GatherRunner.setupCookies(passContext); log.timeEnd(status); } + /** + * Initialize cookies settings for pass + * and manual request headers. + * @param {LH.Gatherer.PassContext} passContext + * @return {Promise} + */ + static async setupCookies(passContext) { + const extraCookies = passContext.settings.extraCookies; + if (!extraCookies) { + return; + } + extraCookies.forEach(cookie => { + if (!cookie.url || !cookie.domain) { + // Default cookie URL to to current URL, if neither domain nor url is specified + cookie.url = passContext.url; + } + }); + await passContext.driver.setCookies(extraCookies); + } + /** * Beging recording devtoolsLog and trace (if requested). * @param {LH.Gatherer.PassContext} passContext diff --git a/lighthouse-core/test/gather/driver-test.js b/lighthouse-core/test/gather/driver-test.js index d78ba2c9757a..a0c97acbe447 100644 --- a/lighthouse-core/test/gather/driver-test.js +++ b/lighthouse-core/test/gather/driver-test.js @@ -355,6 +355,32 @@ describe('.setExtraHTTPHeaders', () => { }); }); +describe('.setCookies', () => { + it('should call Network.setCookies when there are extra-cookies', async () => { + connectionStub.sendCommand = createMockSendCommandFn().mockResponse( + 'Network.setCookies', + {} + ); + + await driver.setCookies([{ + 'name': 'cookie1', + 'value': 'monster', + }]); + + expect(connectionStub.sendCommand).toHaveBeenCalledWith( + 'Network.setCookies', + expect.anything() + ); + }); + + it('should not call Network.setCookies when there aren\'t extra-cookies', async () => { + connectionStub.sendCommand = createMockSendCommandFn(); + await driver.setCookies(); + + expect(connectionStub.sendCommand).not.toHaveBeenCalled(); + }); +}); + describe('.getAppManifest', () => { it('should return null when no manifest', async () => { connectionStub.sendCommand = createMockSendCommandFn().mockResponse( diff --git a/lighthouse-core/test/gather/fake-driver.js b/lighthouse-core/test/gather/fake-driver.js index 108a40aa6d78..0c0a6414a05a 100644 --- a/lighthouse-core/test/gather/fake-driver.js +++ b/lighthouse-core/test/gather/fake-driver.js @@ -87,6 +87,9 @@ function makeFakeDriver({protocolGetVersionResponse}) { setExtraHTTPHeaders() { return Promise.resolve(); }, + setCookies() { + return Promise.resolve(); + } }; } diff --git a/lighthouse-core/test/gather/gather-runner-test.js b/lighthouse-core/test/gather/gather-runner-test.js index 8d8077df0be5..678a2efa4df5 100644 --- a/lighthouse-core/test/gather/gather-runner-test.js +++ b/lighthouse-core/test/gather/gather-runner-test.js @@ -40,7 +40,7 @@ const fakeDriver = require('./fake-driver.js'); const fakeDriverUsingRealMobileDevice = fakeDriver.fakeDriverUsingRealMobileDevice; function getMockedEmulationDriver(emulationFn, netThrottleFn, cpuThrottleFn, - blockUrlFn, extraHeadersFn) { + blockUrlFn, extraHeadersFn, extraCookiesFn) { const Driver = require('../../gather/driver.js'); const Connection = require('../../gather/connections/connection.js'); const EmulationDriver = class extends Driver { @@ -81,6 +81,9 @@ function getMockedEmulationDriver(emulationFn, netThrottleFn, cpuThrottleFn, case 'Network.setExtraHTTPHeaders': fn = extraHeadersFn; break; + case 'Network.setCookies': + fn = extraCookiesFn; + break; default: fn = null; break; @@ -397,6 +400,7 @@ describe('GatherRunner', function() { setThrottling: asyncFunc, blockUrlPatterns: asyncFunc, setExtraHTTPHeaders: asyncFunc, + setCookies: asyncFunc, endTrace: asyncFunc, endDevtoolsLog: () => [], getBrowserVersion: async () => ({userAgent: ''}), @@ -577,6 +581,58 @@ describe('GatherRunner', function() { )); }); + it('tells the driver to set additional cookies when extraCookies flag is given', () => { + let receivedCookies = null; + const driver = getMockedEmulationDriver(null, null, null, null, null, params => { + receivedCookies = params.cookies; + }); + const cookies = [{ + 'name': 'cookie1', + 'value': 'monster', + 'domain': 'test.com', + }]; + + return GatherRunner.setupPassNetwork({ + driver, + settings: { + extraCookies: cookies, + }, + passConfig: { gatherers: [] }, + }).then(() => assert.deepStrictEqual( + receivedCookies, + cookies + )); + }); + + it('uses current url as cookie\'s url if neither domain nor url is specified', () => { + let receivedCookies = null; + const driver = getMockedEmulationDriver(null, null, null, null, null, params => { + receivedCookies = params.cookies; + }); + const inputCookies = [{ + 'name': 'cookie1', + 'value': 'monster', + }]; + const expectedCookies = [{ + 'name': 'cookie1', + 'value': 'monster', + 'url': 'http://test.com/some_path', + }]; + + return GatherRunner.setupPassNetwork({ + url: 'http://test.com/some_path', + driver, + settings: { + extraCookies: inputCookies, + }, + passConfig: { gatherers: [] }, + }).then(() => assert.deepStrictEqual( + receivedCookies, + expectedCookies + )); + }); + + it('tells the driver to begin tracing', async () => { let calledTrace = false; const driver = { diff --git a/lighthouse-core/test/results/artifacts/artifacts.json b/lighthouse-core/test/results/artifacts/artifacts.json index 2cbcd6481379..a6819f0f74c4 100644 --- a/lighthouse-core/test/results/artifacts/artifacts.json +++ b/lighthouse-core/test/results/artifacts/artifacts.json @@ -60,6 +60,7 @@ "blockedUrlPatterns": null, "additionalTraceCategories": null, "extraHeaders": null, + "extraCookies": null, "precomputedLanternData": null, "onlyAudits": null, "onlyCategories": null, diff --git a/lighthouse-core/test/results/sample_v2.json b/lighthouse-core/test/results/sample_v2.json index 9b71f4455959..934d2e4cb416 100644 --- a/lighthouse-core/test/results/sample_v2.json +++ b/lighthouse-core/test/results/sample_v2.json @@ -3373,6 +3373,7 @@ "blockedUrlPatterns": null, "additionalTraceCategories": null, "extraHeaders": null, + "extraCookies": null, "precomputedLanternData": null, "onlyAudits": null, "onlyCategories": null, diff --git a/readme.md b/readme.md index 8883d4e2a331..cc72f52b7296 100644 --- a/readme.md +++ b/readme.md @@ -101,6 +101,7 @@ Options: --throttling.uploadThroughputKbps Controls emulated network upload throughput --throttling.cpuSlowdownMultiplier Controls simulated + emulated CPU throttling --extra-headers Set extra HTTP Headers to pass with request [string] + --extra-cookies Set extra HTTP Cookies to pass with request Examples: lighthouse --view Opens the HTML report in a browser after the run completes @@ -113,6 +114,8 @@ Examples: lighthouse --quiet --chrome-flags="--headless" Launch Headless Chrome, turn off logging lighthouse --extra-headers "{\"Cookie\":\"monster=blue\"}" Stringify\'d JSON HTTP Header key/value pairs to send in requests lighthouse --extra-headers=./path/to/file.json Path to JSON file of HTTP Header key/value pairs to send in requests + lighthouse --extra-cookies "[{\"name\":\"session_id\",\"value\":\"x-men\" }]" Stringify'd JSON array of HTTP Cookies to send in requests + lighthouse --extra-cookies=./path/to/file.json Path to JSON file of HTTP Cookies to send in requests For more information on Lighthouse, see https://developers.google.com/web/tools/lighthouse/. ``` diff --git a/types/externs.d.ts b/types/externs.d.ts index 6204567a83a7..81ce5b24cb69 100644 --- a/types/externs.d.ts +++ b/types/externs.d.ts @@ -113,6 +113,8 @@ declare global { onlyCategories?: string[] | null; /** If present, the run should skip this list of audits. */ skipAudits?: string[] | null; + /** List of extra HTTP Cookies to include. */ + extraCookies?: Crdp.Network.CookieParam[] | null; // See extraCookies TODO in bin.js /** List of extra HTTP Headers to include. */ extraHeaders?: Crdp.Network.Headers | null; // See extraHeaders TODO in bin.js /** How Lighthouse was run, e.g. from the Chrome extension or from the npm module */