lighthouse 12.0.0 → 12.1.0-dev.20240619
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli/run.js +5 -38
- package/cli/test/smokehouse/lighthouse-runners/bundle.js +1 -1
- package/cli/test/smokehouse/smokehouse.js +11 -1
- package/core/audits/accessibility/aria-allowed-role.js +7 -9
- package/core/audits/byte-efficiency/byte-efficiency-audit.d.ts +2 -2
- package/core/audits/byte-efficiency/byte-efficiency-audit.js +3 -3
- package/core/audits/byte-efficiency/offscreen-images.js +1 -1
- package/core/audits/byte-efficiency/render-blocking-resources.d.ts +3 -3
- package/core/audits/byte-efficiency/render-blocking-resources.js +10 -10
- package/core/audits/dobetterweb/uses-http2.d.ts +2 -2
- package/core/audits/dobetterweb/uses-http2.js +5 -5
- package/core/audits/long-tasks.d.ts +7 -6
- package/core/audits/long-tasks.js +5 -4
- package/core/audits/metrics/first-meaningful-paint.d.ts +1 -17
- package/core/audits/metrics/first-meaningful-paint.js +5 -47
- package/core/audits/metrics/interactive.d.ts +1 -1
- package/core/audits/metrics/interactive.js +1 -1
- package/core/audits/predictive-perf.js +0 -6
- package/core/audits/prioritize-lcp-image.d.ts +1 -1
- package/core/audits/prioritize-lcp-image.js +2 -2
- package/core/audits/redirects.js +2 -3
- package/core/audits/third-party-facades.js +1 -1
- package/core/audits/third-party-summary.js +1 -1
- package/core/audits/uses-rel-preconnect.js +2 -2
- package/core/audits/uses-rel-preload.js +9 -9
- package/core/computed/critical-request-chains.js +3 -3
- package/core/computed/document-urls.js +3 -2
- package/core/computed/load-simulator.d.ts +4 -4
- package/core/computed/load-simulator.js +3 -54
- package/core/computed/main-resource.js +3 -2
- package/core/computed/metrics/interactive.d.ts +6 -0
- package/core/computed/metrics/interactive.js +2 -2
- package/core/computed/metrics/lantern-first-contentful-paint.d.ts +4 -4
- package/core/computed/metrics/lantern-first-contentful-paint.js +3 -3
- package/core/computed/metrics/lantern-interactive.d.ts +4 -4
- package/core/computed/metrics/lantern-interactive.js +6 -6
- package/core/computed/metrics/lantern-largest-contentful-paint.d.ts +4 -4
- package/core/computed/metrics/lantern-largest-contentful-paint.js +3 -3
- package/core/computed/metrics/lantern-max-potential-fid.d.ts +4 -4
- package/core/computed/metrics/lantern-max-potential-fid.js +3 -3
- package/core/computed/metrics/lantern-metric.d.ts +21 -2
- package/core/computed/metrics/lantern-metric.js +39 -4
- package/core/computed/metrics/lantern-speed-index.d.ts +4 -4
- package/core/computed/metrics/lantern-speed-index.js +4 -4
- package/core/computed/metrics/lantern-total-blocking-time.d.ts +4 -4
- package/core/computed/metrics/lantern-total-blocking-time.js +3 -3
- package/core/computed/metrics/timing-summary.js +0 -6
- package/core/computed/metrics/total-blocking-time.js +3 -1
- package/core/computed/navigation-insights.d.ts +2 -20
- package/core/computed/network-analysis.d.ts +0 -5
- package/core/computed/network-analysis.js +2 -42
- package/core/computed/page-dependency-graph.d.ts +5 -3
- package/core/computed/page-dependency-graph.js +17 -7
- package/core/computed/processed-navigation.d.ts +1 -1
- package/core/computed/tbt-impact-tasks.js +4 -2
- package/core/computed/trace-engine-result.d.ts +0 -11
- package/core/computed/trace-engine-result.js +5 -19
- package/core/config/constants.d.ts +28 -53
- package/core/config/constants.js +2 -43
- package/core/gather/driver/navigation.js +1 -1
- package/core/gather/driver/network-monitor.d.ts +2 -1
- package/core/gather/driver/network-monitor.js +4 -4
- package/core/gather/driver/target-manager.d.ts +1 -1
- package/core/gather/driver.d.ts +0 -12
- package/core/gather/driver.js +1 -26
- package/core/gather/gatherers/inspector-issues.js +1 -0
- package/core/gather/gatherers/root-causes.js +0 -1
- package/core/gather/session.d.ts +5 -3
- package/core/gather/session.js +22 -1
- package/core/lib/asset-saver.d.ts +13 -9
- package/core/lib/asset-saver.js +77 -30
- package/core/lib/bf-cache-strings.js +2 -2
- package/core/lib/lantern/{base-node.d.ts → BaseNode.d.ts} +7 -9
- package/core/lib/lantern/{base-node.js → BaseNode.js} +6 -6
- package/core/lib/lantern/BaseNode.test.js +385 -0
- package/core/lib/lantern/CpuNode.d.ts +43 -0
- package/core/lib/lantern/{cpu-node.js → CpuNode.js} +8 -9
- package/core/lib/lantern/{lantern-error.d.ts → LanternError.d.ts} +1 -1
- package/core/lib/lantern/{metric.d.ts → Metric.d.ts} +23 -25
- package/core/lib/lantern/{metric.js → Metric.js} +14 -16
- package/core/lib/lantern/{network-node.d.ts → NetworkNode.d.ts} +6 -7
- package/core/lib/lantern/{network-node.js → NetworkNode.js} +7 -9
- package/core/lib/lantern/PageDependencyGraph.d.ts +82 -0
- package/core/lib/lantern/{page-dependency-graph.js → PageDependencyGraph.js} +185 -50
- package/core/lib/lantern/PageDependencyGraph.test.js +654 -0
- package/core/{computed/metrics/tbt-utils.d.ts → lib/lantern/TBTUtils.d.ts} +1 -1
- package/core/lib/lantern/TBTUtils.test.d.ts +2 -0
- package/core/lib/lantern/TBTUtils.test.js +130 -0
- package/core/lib/lantern/TraceEngineComputationData.d.ts +25 -0
- package/core/lib/lantern/TraceEngineComputationData.js +466 -0
- package/core/lib/lantern/lantern.d.ts +19 -7
- package/core/lib/lantern/lantern.js +21 -1
- package/core/lib/lantern/metrics/{first-contentful-paint.d.ts → FirstContentfulPaint.d.ts} +11 -11
- package/core/lib/lantern/metrics/{first-contentful-paint.js → FirstContentfulPaint.js} +11 -13
- package/core/lib/lantern/metrics/FirstContentfulPaint.test.js +54 -0
- package/core/lib/lantern/metrics/Interactive.d.ts +20 -0
- package/core/lib/lantern/metrics/{interactive.js → Interactive.js} +23 -26
- package/core/lib/lantern/metrics/Interactive.test.js +56 -0
- package/core/lib/lantern/metrics/LargestContentfulPaint.d.ts +19 -0
- package/core/lib/lantern/metrics/{largest-contentful-paint.js → LargestContentfulPaint.js} +16 -19
- package/core/lib/lantern/metrics/LargestContentfulPaint.test.js +42 -0
- package/core/lib/lantern/metrics/MaxPotentialFID.d.ts +24 -0
- package/core/lib/lantern/metrics/{max-potential-fid.js → MaxPotentialFID.js} +12 -13
- package/core/lib/lantern/metrics/MetricTestUtils.d.ts +19 -0
- package/core/lib/lantern/metrics/MetricTestUtils.js +48 -0
- package/core/lib/lantern/metrics/{speed-index.d.ts → SpeedIndex.d.ts} +7 -13
- package/core/lib/lantern/metrics/{speed-index.js → SpeedIndex.js} +14 -16
- package/core/lib/lantern/metrics/SpeedIndex.test.js +83 -0
- package/core/lib/lantern/metrics/TotalBlockingTime.d.ts +25 -0
- package/core/lib/lantern/metrics/{total-blocking-time.js → TotalBlockingTime.js} +13 -16
- package/core/lib/lantern/metrics/__snapshots__/FirstContentfulPaint.test.js.snap +11 -0
- package/core/lib/lantern/metrics/__snapshots__/Interactive.test.js.snap +17 -0
- package/core/lib/lantern/metrics/metrics.d.ts +9 -0
- package/core/lib/lantern/metrics/metrics.js +16 -0
- package/core/lib/lantern/{simulator/connection-pool.d.ts → simulation/ConnectionPool.d.ts} +15 -15
- package/core/lib/lantern/{simulator/connection-pool.js → simulation/ConnectionPool.js} +25 -25
- package/core/lib/lantern/simulation/ConnectionPool.test.js +195 -0
- package/core/lib/lantern/simulation/Constants.d.ts +52 -0
- package/core/lib/lantern/simulation/Constants.js +48 -0
- package/core/lib/lantern/{simulator/dns-cache.d.ts → simulation/DNSCache.d.ts} +3 -3
- package/core/lib/lantern/{simulator/dns-cache.js → simulation/DNSCache.js} +2 -2
- package/core/lib/lantern/simulation/DNSCache.test.js +72 -0
- package/core/lib/lantern/{simulator/network-analyzer.d.ts → simulation/NetworkAnalyzer.d.ts} +34 -21
- package/core/lib/lantern/{simulator/network-analyzer.js → simulation/NetworkAnalyzer.js} +106 -57
- package/core/lib/lantern/simulation/NetworkAnalyzer.test.js +475 -0
- package/core/lib/lantern/{simulator/simulator-timing-map.d.ts → simulation/SimulationTimingMap.d.ts} +7 -7
- package/core/lib/lantern/{simulator/simulator-timing-map.js → simulation/SimulationTimingMap.js} +5 -6
- package/core/lib/lantern/{simulator/simulator.d.ts → simulation/Simulator.d.ts} +21 -19
- package/core/lib/lantern/{simulator/simulator.js → simulation/Simulator.js} +100 -45
- package/core/lib/lantern/simulation/Simulator.test.js +434 -0
- package/core/lib/lantern/simulation/TCPConnection.test.d.ts +2 -0
- package/core/lib/lantern/simulation/TCPConnection.test.js +374 -0
- package/core/lib/lantern/{simulator/tcp-connection.d.ts → simulation/TcpConnection.d.ts} +2 -2
- package/core/lib/lantern/{simulator/tcp-connection.js → simulation/TcpConnection.js} +1 -1
- package/core/lib/lantern/simulation/simulation.d.ts +21 -0
- package/core/lib/lantern/simulation/simulation.js +28 -0
- package/core/lib/lantern/types/lantern.d.ts +145 -20
- package/core/lib/lantern-trace-saver.d.ts +4 -4
- package/core/lib/lantern-trace-saver.js +4 -4
- package/core/lib/navigation-error.js +3 -3
- package/core/lib/network-recorder.js +2 -2
- package/core/lib/network-request.d.ts +29 -3
- package/core/lib/network-request.js +20 -13
- package/core/lib/tracehouse/trace-processor.d.ts +0 -2
- package/core/lib/tracehouse/trace-processor.js +1 -31
- package/core/lib/traces/metric-trace-events.js +0 -5
- package/dist/report/bundle.esm.js +1 -1
- package/dist/report/flow.js +1 -1
- package/dist/report/standalone.js +1 -1
- package/package.json +14 -13
- package/report/renderer/performance-category-renderer.js +1 -1
- package/shared/localization/locales/ar-XB.json +43 -4
- package/shared/localization/locales/ar.json +43 -4
- package/shared/localization/locales/bg.json +43 -4
- package/shared/localization/locales/ca.json +44 -5
- package/shared/localization/locales/cs.json +43 -4
- package/shared/localization/locales/da.json +43 -4
- package/shared/localization/locales/de.json +43 -4
- package/shared/localization/locales/el.json +43 -4
- package/shared/localization/locales/en-GB.json +42 -3
- package/shared/localization/locales/en-US.json +4 -4
- package/shared/localization/locales/en-XA.json +43 -4
- package/shared/localization/locales/en-XL.json +4 -4
- package/shared/localization/locales/es-419.json +43 -4
- package/shared/localization/locales/es.json +42 -3
- package/shared/localization/locales/fi.json +43 -4
- package/shared/localization/locales/fil.json +42 -3
- package/shared/localization/locales/fr.json +42 -3
- package/shared/localization/locales/he.json +48 -9
- package/shared/localization/locales/hi.json +43 -4
- package/shared/localization/locales/hr.json +43 -4
- package/shared/localization/locales/hu.json +42 -3
- package/shared/localization/locales/id.json +42 -3
- package/shared/localization/locales/it.json +43 -4
- package/shared/localization/locales/ja.json +42 -3
- package/shared/localization/locales/ko.json +43 -4
- package/shared/localization/locales/lt.json +42 -3
- package/shared/localization/locales/lv.json +43 -4
- package/shared/localization/locales/nl.json +44 -5
- package/shared/localization/locales/no.json +42 -3
- package/shared/localization/locales/pl.json +43 -4
- package/shared/localization/locales/pt-PT.json +43 -4
- package/shared/localization/locales/pt.json +43 -4
- package/shared/localization/locales/ro.json +43 -4
- package/shared/localization/locales/ru.json +43 -4
- package/shared/localization/locales/sk.json +43 -4
- package/shared/localization/locales/sl.json +42 -3
- package/shared/localization/locales/sr-Latn.json +43 -4
- package/shared/localization/locales/sr.json +43 -4
- package/shared/localization/locales/sv.json +42 -3
- package/shared/localization/locales/ta.json +42 -3
- package/shared/localization/locales/te.json +43 -4
- package/shared/localization/locales/th.json +44 -5
- package/shared/localization/locales/tr.json +43 -4
- package/shared/localization/locales/uk.json +43 -4
- package/shared/localization/locales/vi.json +43 -4
- package/shared/localization/locales/zh-HK.json +43 -4
- package/shared/localization/locales/zh-TW.json +43 -4
- package/shared/localization/locales/zh.json +43 -4
- package/third-party/chromium-synchronization/inspector-issueAdded-types-test.js +1 -0
- package/tsconfig-all.json +1 -0
- package/tsconfig.json +11 -7
- package/types/artifacts.d.ts +23 -25
- package/types/gatherer.d.ts +2 -3
- package/types/lhr/lhr.d.ts +2 -0
- package/core/computed/metrics/first-meaningful-paint.d.ts +0 -21
- package/core/computed/metrics/first-meaningful-paint.js +0 -44
- package/core/computed/metrics/lantern-first-meaningful-paint.d.ts +0 -25
- package/core/computed/metrics/lantern-first-meaningful-paint.js +0 -43
- package/core/lib/lantern/cpu-node.d.ts +0 -44
- package/core/lib/lantern/metrics/first-meaningful-paint.d.ts +0 -6
- package/core/lib/lantern/metrics/first-meaningful-paint.js +0 -64
- package/core/lib/lantern/metrics/interactive.d.ts +0 -26
- package/core/lib/lantern/metrics/largest-contentful-paint.d.ts +0 -19
- package/core/lib/lantern/metrics/max-potential-fid.d.ts +0 -30
- package/core/lib/lantern/metrics/total-blocking-time.d.ts +0 -31
- package/core/lib/lantern/page-dependency-graph.d.ts +0 -58
- /package/core/lib/lantern/{lantern-error.js → LanternError.js} +0 -0
- /package/core/{computed/metrics/tbt-utils.js → lib/lantern/TBTUtils.js} +0 -0
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import * as Lantern from '../
|
|
7
|
+
import * as Lantern from '../lantern.js';
|
|
8
8
|
import UrlUtils from '../../url-utils.js';
|
|
9
9
|
|
|
10
10
|
const INITIAL_CWD = 14 * 1024;
|
|
@@ -15,7 +15,7 @@ const DEFAULT_SERVER_RESPONSE_PERCENTAGE = 0.4;
|
|
|
15
15
|
/**
|
|
16
16
|
* For certain resource types, server response time takes up a greater percentage of TTFB (dynamic
|
|
17
17
|
* assets like HTML documents, XHR/API calls, etc)
|
|
18
|
-
* @type {Partial<Record<
|
|
18
|
+
* @type {Partial<Record<Lantern.ResourceType, number>>}
|
|
19
19
|
*/
|
|
20
20
|
const SERVER_RESPONSE_PERCENTAGE_OF_TTFB = {
|
|
21
21
|
Document: 0.9,
|
|
@@ -88,30 +88,30 @@ class NetworkAnalyzer {
|
|
|
88
88
|
return summaryByKey;
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
/** @typedef {{
|
|
91
|
+
/** @typedef {{request: Lantern.NetworkRequest, timing: Lantern.ResourceTiming, connectionReused?: boolean}} RequestInfo */
|
|
92
92
|
|
|
93
93
|
/**
|
|
94
|
-
* @param {Lantern.NetworkRequest[]}
|
|
94
|
+
* @param {Lantern.NetworkRequest[]} requests
|
|
95
95
|
* @param {(e: RequestInfo) => number | number[] | undefined} iteratee
|
|
96
96
|
* @return {Map<string, number[]>}
|
|
97
97
|
*/
|
|
98
|
-
static _estimateValueByOrigin(
|
|
99
|
-
const connectionWasReused = NetworkAnalyzer.estimateIfConnectionWasReused(
|
|
100
|
-
const groupedByOrigin = NetworkAnalyzer.groupByOrigin(
|
|
98
|
+
static _estimateValueByOrigin(requests, iteratee) {
|
|
99
|
+
const connectionWasReused = NetworkAnalyzer.estimateIfConnectionWasReused(requests);
|
|
100
|
+
const groupedByOrigin = NetworkAnalyzer.groupByOrigin(requests);
|
|
101
101
|
|
|
102
102
|
const estimates = new Map();
|
|
103
|
-
for (const [origin,
|
|
103
|
+
for (const [origin, originRequests] of groupedByOrigin.entries()) {
|
|
104
104
|
/** @type {number[]} */
|
|
105
105
|
let originEstimates = [];
|
|
106
106
|
|
|
107
|
-
for (const
|
|
108
|
-
const timing =
|
|
107
|
+
for (const request of originRequests) {
|
|
108
|
+
const timing = request.timing;
|
|
109
109
|
if (!timing) continue;
|
|
110
110
|
|
|
111
111
|
const value = iteratee({
|
|
112
|
-
|
|
112
|
+
request,
|
|
113
113
|
timing,
|
|
114
|
-
connectionReused: connectionWasReused.get(
|
|
114
|
+
connectionReused: connectionWasReused.get(request.requestId),
|
|
115
115
|
});
|
|
116
116
|
if (typeof value !== 'undefined') {
|
|
117
117
|
originEstimates = originEstimates.concat(value);
|
|
@@ -137,11 +137,11 @@ class NetworkAnalyzer {
|
|
|
137
137
|
* @return {number[]|number|undefined}
|
|
138
138
|
*/
|
|
139
139
|
static _estimateRTTViaConnectionTiming(info) {
|
|
140
|
-
const {timing, connectionReused,
|
|
140
|
+
const {timing, connectionReused, request} = info;
|
|
141
141
|
if (connectionReused) return;
|
|
142
142
|
|
|
143
143
|
const {connectStart, sslStart, sslEnd, connectEnd} = timing;
|
|
144
|
-
if (connectEnd >= 0 && connectStart >= 0 &&
|
|
144
|
+
if (connectEnd >= 0 && connectStart >= 0 && request.protocol.startsWith('h3')) {
|
|
145
145
|
// These values are equal to sslStart and sslEnd for h3.
|
|
146
146
|
return connectEnd - connectStart;
|
|
147
147
|
} else if (sslStart >= 0 && sslEnd >= 0 && sslStart !== connectStart) {
|
|
@@ -161,17 +161,17 @@ class NetworkAnalyzer {
|
|
|
161
161
|
* @return {number|undefined}
|
|
162
162
|
*/
|
|
163
163
|
static _estimateRTTViaDownloadTiming(info) {
|
|
164
|
-
const {timing, connectionReused,
|
|
164
|
+
const {timing, connectionReused, request} = info;
|
|
165
165
|
if (connectionReused) return;
|
|
166
166
|
|
|
167
167
|
// Only look at downloads that went past the initial congestion window
|
|
168
|
-
if (
|
|
168
|
+
if (request.transferSize <= INITIAL_CWD) return;
|
|
169
169
|
if (!Number.isFinite(timing.receiveHeadersEnd) || timing.receiveHeadersEnd < 0) return;
|
|
170
170
|
|
|
171
171
|
// Compute the amount of time downloading everything after the first congestion window took
|
|
172
|
-
const totalTime =
|
|
172
|
+
const totalTime = request.networkEndTime - request.networkRequestTime;
|
|
173
173
|
const downloadTimeAfterFirstByte = totalTime - timing.receiveHeadersEnd;
|
|
174
|
-
const numberOfRoundTrips = Math.log2(
|
|
174
|
+
const numberOfRoundTrips = Math.log2(request.transferSize / INITIAL_CWD);
|
|
175
175
|
|
|
176
176
|
// Ignore requests that required a high number of round trips since bandwidth starts to play
|
|
177
177
|
// a larger role than latency
|
|
@@ -190,7 +190,7 @@ class NetworkAnalyzer {
|
|
|
190
190
|
* @return {number|undefined}
|
|
191
191
|
*/
|
|
192
192
|
static _estimateRTTViaSendStartTiming(info) {
|
|
193
|
-
const {timing, connectionReused,
|
|
193
|
+
const {timing, connectionReused, request} = info;
|
|
194
194
|
if (connectionReused) return;
|
|
195
195
|
|
|
196
196
|
if (!Number.isFinite(timing.sendStart) || timing.sendStart < 0) return;
|
|
@@ -198,8 +198,8 @@ class NetworkAnalyzer {
|
|
|
198
198
|
// Assume everything before sendStart was just DNS + (SSL)? + TCP handshake
|
|
199
199
|
// 1 RT for DNS, 1 RT (maybe) for SSL, 1 RT for TCP
|
|
200
200
|
let roundTrips = 1;
|
|
201
|
-
if (!
|
|
202
|
-
if (
|
|
201
|
+
if (!request.protocol.startsWith('h3')) roundTrips += 1; // TCP
|
|
202
|
+
if (request.parsedURL.scheme === 'https') roundTrips += 1;
|
|
203
203
|
return timing.sendStart / roundTrips;
|
|
204
204
|
}
|
|
205
205
|
|
|
@@ -213,12 +213,12 @@ class NetworkAnalyzer {
|
|
|
213
213
|
* @return {number|undefined}
|
|
214
214
|
*/
|
|
215
215
|
static _estimateRTTViaHeadersEndTiming(info) {
|
|
216
|
-
const {timing, connectionReused,
|
|
216
|
+
const {timing, connectionReused, request} = info;
|
|
217
217
|
if (!Number.isFinite(timing.receiveHeadersEnd) || timing.receiveHeadersEnd < 0) return;
|
|
218
|
-
if (!
|
|
218
|
+
if (!request.resourceType) return;
|
|
219
219
|
|
|
220
220
|
const serverResponseTimePercentage =
|
|
221
|
-
SERVER_RESPONSE_PERCENTAGE_OF_TTFB[
|
|
221
|
+
SERVER_RESPONSE_PERCENTAGE_OF_TTFB[request.resourceType] ||
|
|
222
222
|
DEFAULT_SERVER_RESPONSE_PERCENTAGE;
|
|
223
223
|
const estimatedServerResponseTime = timing.receiveHeadersEnd * serverResponseTimePercentage;
|
|
224
224
|
|
|
@@ -230,8 +230,8 @@ class NetworkAnalyzer {
|
|
|
230
230
|
// TTFB = DNS + (SSL)? + TCP handshake + 1 RT for request + server response time
|
|
231
231
|
if (!connectionReused) {
|
|
232
232
|
roundTrips += 1; // DNS
|
|
233
|
-
if (!
|
|
234
|
-
if (
|
|
233
|
+
if (!request.protocol.startsWith('h3')) roundTrips += 1; // TCP
|
|
234
|
+
if (request.parsedURL.scheme === 'https') roundTrips += 1; // SSL
|
|
235
235
|
}
|
|
236
236
|
|
|
237
237
|
// subtract out our estimated server response time
|
|
@@ -246,28 +246,28 @@ class NetworkAnalyzer {
|
|
|
246
246
|
* @return {Map<string, number[]>}
|
|
247
247
|
*/
|
|
248
248
|
static _estimateResponseTimeByOrigin(records, rttByOrigin) {
|
|
249
|
-
return NetworkAnalyzer._estimateValueByOrigin(records, ({
|
|
250
|
-
if (
|
|
249
|
+
return NetworkAnalyzer._estimateValueByOrigin(records, ({request, timing}) => {
|
|
250
|
+
if (request.serverResponseTime !== undefined) return request.serverResponseTime;
|
|
251
251
|
|
|
252
252
|
if (!Number.isFinite(timing.receiveHeadersEnd) || timing.receiveHeadersEnd < 0) return;
|
|
253
253
|
if (!Number.isFinite(timing.sendEnd) || timing.sendEnd < 0) return;
|
|
254
254
|
|
|
255
255
|
const ttfb = timing.receiveHeadersEnd - timing.sendEnd;
|
|
256
|
-
const origin =
|
|
256
|
+
const origin = request.parsedURL.securityOrigin;
|
|
257
257
|
const rtt = rttByOrigin.get(origin) || rttByOrigin.get(NetworkAnalyzer.SUMMARY) || 0;
|
|
258
258
|
return Math.max(ttfb - rtt, 0);
|
|
259
259
|
});
|
|
260
260
|
}
|
|
261
261
|
|
|
262
262
|
/**
|
|
263
|
-
* @param {Lantern.NetworkRequest[]}
|
|
263
|
+
* @param {Lantern.NetworkRequest[]} requests
|
|
264
264
|
* @return {boolean}
|
|
265
265
|
*/
|
|
266
|
-
static canTrustConnectionInformation(
|
|
266
|
+
static canTrustConnectionInformation(requests) {
|
|
267
267
|
const connectionIdWasStarted = new Map();
|
|
268
|
-
for (const
|
|
269
|
-
const started = connectionIdWasStarted.get(
|
|
270
|
-
connectionIdWasStarted.set(
|
|
268
|
+
for (const request of requests) {
|
|
269
|
+
const started = connectionIdWasStarted.get(request.connectionId) || !request.connectionReused;
|
|
270
|
+
connectionIdWasStarted.set(request.connectionId, started);
|
|
271
271
|
}
|
|
272
272
|
|
|
273
273
|
// We probably can't trust the network information if all the connection IDs were the same
|
|
@@ -289,10 +289,10 @@ class NetworkAnalyzer {
|
|
|
289
289
|
|
|
290
290
|
// Check if we can trust the connection information coming from the protocol
|
|
291
291
|
if (!forceCoarseEstimates && NetworkAnalyzer.canTrustConnectionInformation(records)) {
|
|
292
|
-
return new Map(records.map(
|
|
292
|
+
return new Map(records.map(request => [request.requestId, !!request.connectionReused]));
|
|
293
293
|
}
|
|
294
294
|
|
|
295
|
-
// Otherwise we're on our own, a
|
|
295
|
+
// Otherwise we're on our own, a request may not have needed a fresh connection if...
|
|
296
296
|
// - It was not the first request to the domain
|
|
297
297
|
// - It was H2
|
|
298
298
|
// - It was after the first request to the domain ended
|
|
@@ -300,13 +300,13 @@ class NetworkAnalyzer {
|
|
|
300
300
|
const groupedByOrigin = NetworkAnalyzer.groupByOrigin(records);
|
|
301
301
|
for (const [_, originRecords] of groupedByOrigin.entries()) {
|
|
302
302
|
const earliestReusePossible = originRecords
|
|
303
|
-
.map(
|
|
303
|
+
.map(request => request.networkEndTime)
|
|
304
304
|
.reduce((a, b) => Math.min(a, b), Infinity);
|
|
305
305
|
|
|
306
|
-
for (const
|
|
306
|
+
for (const request of originRecords) {
|
|
307
307
|
connectionWasReused.set(
|
|
308
|
-
|
|
309
|
-
|
|
308
|
+
request.requestId,
|
|
309
|
+
request.networkRequestTime >= earliestReusePossible || request.protocol === 'h2'
|
|
310
310
|
);
|
|
311
311
|
}
|
|
312
312
|
|
|
@@ -343,7 +343,7 @@ class NetworkAnalyzer {
|
|
|
343
343
|
const groupedByOrigin = NetworkAnalyzer.groupByOrigin(records);
|
|
344
344
|
|
|
345
345
|
const estimatesByOrigin = new Map();
|
|
346
|
-
for (const [origin,
|
|
346
|
+
for (const [origin, originRequests] of groupedByOrigin.entries()) {
|
|
347
347
|
/** @type {number[]} */
|
|
348
348
|
const originEstimates = [];
|
|
349
349
|
|
|
@@ -352,14 +352,14 @@ class NetworkAnalyzer {
|
|
|
352
352
|
*/
|
|
353
353
|
// eslint-disable-next-line no-inner-declarations
|
|
354
354
|
function collectEstimates(estimator, multiplier = 1) {
|
|
355
|
-
for (const
|
|
356
|
-
const timing =
|
|
355
|
+
for (const request of originRequests) {
|
|
356
|
+
const timing = request.timing;
|
|
357
357
|
if (!timing) continue;
|
|
358
358
|
|
|
359
359
|
const estimates = estimator({
|
|
360
|
-
|
|
360
|
+
request,
|
|
361
361
|
timing,
|
|
362
|
-
connectionReused: connectionWasReused.get(
|
|
362
|
+
connectionReused: connectionWasReused.get(request.requestId),
|
|
363
363
|
});
|
|
364
364
|
if (estimates === undefined) continue;
|
|
365
365
|
|
|
@@ -427,32 +427,32 @@ class NetworkAnalyzer {
|
|
|
427
427
|
|
|
428
428
|
|
|
429
429
|
/**
|
|
430
|
-
* Computes the average throughput for the given
|
|
430
|
+
* Computes the average throughput for the given requests in bits/second.
|
|
431
431
|
* Excludes data URI, failed or otherwise incomplete, and cached requests.
|
|
432
|
-
* Returns Infinity if there were no analyzable network
|
|
432
|
+
* Returns Infinity if there were no analyzable network requests.
|
|
433
433
|
*
|
|
434
|
-
* @param {
|
|
434
|
+
* @param {Lantern.NetworkRequest[]} records
|
|
435
435
|
* @return {number}
|
|
436
436
|
*/
|
|
437
|
-
static estimateThroughput(
|
|
437
|
+
static estimateThroughput(records) {
|
|
438
438
|
let totalBytes = 0;
|
|
439
439
|
|
|
440
440
|
// We will measure throughput by summing the total bytes downloaded by the total time spent
|
|
441
|
-
// downloading those bytes. We slice up all the network
|
|
441
|
+
// downloading those bytes. We slice up all the network requests into start/end boundaries, so
|
|
442
442
|
// it's easier to deal with the gaps in downloading.
|
|
443
|
-
const timeBoundaries =
|
|
444
|
-
const scheme =
|
|
443
|
+
const timeBoundaries = records.reduce((boundaries, request) => {
|
|
444
|
+
const scheme = request.parsedURL?.scheme;
|
|
445
445
|
// Requests whose bodies didn't come over the network or didn't completely finish will mess
|
|
446
446
|
// with the computation, just skip over them.
|
|
447
|
-
if (scheme === 'data' ||
|
|
448
|
-
|
|
447
|
+
if (scheme === 'data' || request.failed || !request.finished ||
|
|
448
|
+
request.statusCode > 300 || !request.transferSize) {
|
|
449
449
|
return boundaries;
|
|
450
450
|
}
|
|
451
451
|
|
|
452
452
|
// If we've made it this far, all the times we need should be valid (i.e. not undefined/-1).
|
|
453
|
-
totalBytes +=
|
|
454
|
-
boundaries.push({time:
|
|
455
|
-
boundaries.push({time:
|
|
453
|
+
totalBytes += request.transferSize;
|
|
454
|
+
boundaries.push({time: request.responseHeadersEndTime / 1000, isStart: true});
|
|
455
|
+
boundaries.push({time: request.networkEndTime / 1000, isStart: false});
|
|
456
456
|
return boundaries;
|
|
457
457
|
}, /** @type {Array<{time: number, isStart: boolean}>} */([])).sort((a, b) => a.time - b.time);
|
|
458
458
|
|
|
@@ -483,6 +483,55 @@ class NetworkAnalyzer {
|
|
|
483
483
|
return totalBytes * 8 / totalDuration;
|
|
484
484
|
}
|
|
485
485
|
|
|
486
|
+
/**
|
|
487
|
+
* @param {Lantern.NetworkRequest[]} records
|
|
488
|
+
*/
|
|
489
|
+
static computeRTTAndServerResponseTime(records) {
|
|
490
|
+
// First pass compute the estimated observed RTT to each origin's servers.
|
|
491
|
+
/** @type {Map<string, number>} */
|
|
492
|
+
const rttByOrigin = new Map();
|
|
493
|
+
for (const [origin, summary] of NetworkAnalyzer.estimateRTTByOrigin(records).entries()) {
|
|
494
|
+
rttByOrigin.set(origin, summary.min);
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// We'll use the minimum RTT as the assumed connection latency since we care about how much addt'l
|
|
498
|
+
// latency each origin introduces as Lantern will be simulating with its own connection latency.
|
|
499
|
+
const minimumRtt = Math.min(...Array.from(rttByOrigin.values()));
|
|
500
|
+
// We'll use the observed RTT information to help estimate the server response time
|
|
501
|
+
const responseTimeSummaries = NetworkAnalyzer.estimateServerResponseTimeByOrigin(records, {
|
|
502
|
+
rttByOrigin,
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
/** @type {Map<string, number>} */
|
|
506
|
+
const additionalRttByOrigin = new Map();
|
|
507
|
+
/** @type {Map<string, number>} */
|
|
508
|
+
const serverResponseTimeByOrigin = new Map();
|
|
509
|
+
for (const [origin, summary] of responseTimeSummaries.entries()) {
|
|
510
|
+
// Not all origins have usable timing data, we'll default to using no additional latency.
|
|
511
|
+
const rttForOrigin = rttByOrigin.get(origin) || minimumRtt;
|
|
512
|
+
additionalRttByOrigin.set(origin, rttForOrigin - minimumRtt);
|
|
513
|
+
serverResponseTimeByOrigin.set(origin, summary.median);
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
return {
|
|
517
|
+
rtt: minimumRtt,
|
|
518
|
+
additionalRttByOrigin,
|
|
519
|
+
serverResponseTimeByOrigin,
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* @param {Lantern.NetworkRequest[]} records
|
|
525
|
+
* @return {Lantern.Simulation.Settings['networkAnalysis']}
|
|
526
|
+
*/
|
|
527
|
+
static analyze(records) {
|
|
528
|
+
const throughput = NetworkAnalyzer.estimateThroughput(records);
|
|
529
|
+
return {
|
|
530
|
+
throughput,
|
|
531
|
+
...NetworkAnalyzer.computeRTTAndServerResponseTime(records),
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
|
|
486
535
|
/**
|
|
487
536
|
* @template {Lantern.NetworkRequest} T
|
|
488
537
|
* @param {Array<T>} records
|