diff --git a/lighthouse-core/lib/network-recorder.js b/lighthouse-core/lib/network-recorder.js index 5df4dab88056..be09b5a94dea 100644 --- a/lighthouse-core/lib/network-recorder.js +++ b/lighthouse-core/lib/network-recorder.js @@ -107,9 +107,15 @@ class NetworkRecorder extends EventEmitter { return; } + // QUIC network requests don't always "finish" even when they're done loading data + // Use receivedHeaders instead, see https://github.com/GoogleChrome/lighthouse/issues/5254 + const isQUIC = record._responseHeaders && record._responseHeaders + .find(header => header.name.toLowerCase() === 'alt-svc' && /quic/.test(header.value)); + const receivedHeaders = record._timing && record._timing.receiveHeadersEnd > 0; + // convert the network record timestamp to ms timeBoundaries.push({time: record.startTime * 1000, isStart: true}); - if (record.finished) { + if (record.finished || (isQUIC && receivedHeaders && record.endTime)) { timeBoundaries.push({time: record.endTime * 1000, isStart: false}); } }); @@ -119,7 +125,7 @@ class NetworkRecorder extends EventEmitter { .sort((a, b) => a.time - b.time); let numInflightRequests = 0; - let quietPeriodStart = 0; + let quietPeriodStart = -Infinity; /** @type {Array<{start: number, end: number}>} */ const quietPeriods = []; timeBoundaries.forEach(boundary => { diff --git a/lighthouse-core/test/gather/network-recorder-test.js b/lighthouse-core/test/gather/network-recorder-test.js index 8a0a4b54713a..93508682b4ce 100644 --- a/lighthouse-core/test/gather/network-recorder-test.js +++ b/lighthouse-core/test/gather/network-recorder-test.js @@ -16,4 +16,101 @@ describe('network recorder', function() { const records = NetworkRecorder.recordsFromLogs(devtoolsLogItems); assert.equal(records.length, 76); }); + + describe('#findNetworkQuietPeriods', () => { + function record(data) { + const url = data.url || 'https://example.com'; + const scheme = url.split(':')[0]; + return Object.assign({ + url, + finished: !!data.endTime, + parsedURL: {scheme}, + }, data); + } + + it('should find the 0-quiet periods', () => { + const records = [ + record({startTime: 0, endTime: 1}), + record({startTime: 2, endTime: 3}), + record({startTime: 4, endTime: 5}), + ]; + + const periods = NetworkRecorder.findNetworkQuietPeriods(records, 0); + assert.deepStrictEqual(periods, [ + {start: -Infinity, end: 0}, + {start: 1000, end: 2000}, + {start: 3000, end: 4000}, + {start: 5000, end: Infinity}, + ]); + }); + + it('should find the 2-quiet periods', () => { + const records = [ + record({startTime: 0, endTime: 1.5}), + record({startTime: 0, endTime: 2}), + record({startTime: 0, endTime: 2.5}), + record({startTime: 2, endTime: 3}), + record({startTime: 4, endTime: 5}), + ]; + + const periods = NetworkRecorder.findNetworkQuietPeriods(records, 2); + assert.deepStrictEqual(periods, [ + {start: -Infinity, end: 0}, + {start: 1500, end: Infinity}, + ]); + }); + + it('should handle unfinished requests', () => { + const records = [ + record({startTime: 0, endTime: 1.5}), + record({startTime: 0, endTime: 2}), + record({startTime: 0, endTime: 2.5}), + record({startTime: 2, endTime: 3}), + record({startTime: 2}), + record({startTime: 2}), + record({startTime: 4, endTime: 5}), + record({startTime: 5.5}), + ]; + + const periods = NetworkRecorder.findNetworkQuietPeriods(records, 2); + assert.deepStrictEqual(periods, [ + {start: -Infinity, end: 0}, + {start: 1500, end: 2000}, + {start: 3000, end: 4000}, + {start: 5000, end: 5500}, + ]); + }); + + it('should ignore data URIs', () => { + const records = [ + record({startTime: 0, endTime: 1}), + record({startTime: 0, endTime: 2, url: 'data:image/png;base64,'}), + ]; + + const periods = NetworkRecorder.findNetworkQuietPeriods(records, 0); + assert.deepStrictEqual(periods, [ + {start: -Infinity, end: 0}, + {start: 1000, end: Infinity}, + ]); + }); + + it('should handle QUIC requests', () => { + const quicRequest = { + finished: false, + _responseHeaders: [{name: 'ALT-SVC', value: 'hq=":49288";quic="1,1abadaba,51303334,0"'}], + _timing: {receiveHeadersEnd: 1.28}, + }; + + const records = [ + record({startTime: 0, endTime: 1}), + record({startTime: 0, endTime: 2, ...quicRequest}), + ]; + + const periods = NetworkRecorder.findNetworkQuietPeriods(records, 0); + assert.deepStrictEqual(periods, [ + {start: -Infinity, end: 0}, + {start: 2000, end: Infinity}, + ]); + }); + }); });