lighthouse 12.0.0-dev.20240603 → 12.0.0-dev.20240604
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/core/audits/byte-efficiency/byte-efficiency-audit.js +1 -1
- package/core/audits/byte-efficiency/offscreen-images.js +1 -1
- package/core/audits/byte-efficiency/render-blocking-resources.js +5 -5
- package/core/audits/dobetterweb/uses-http2.js +2 -2
- package/core/audits/prioritize-lcp-image.js +1 -1
- 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/metrics/lantern-metric.d.ts +2 -2
- package/core/computed/metrics/lantern-metric.js +2 -19
- package/core/computed/page-dependency-graph.d.ts +3 -1
- package/core/computed/page-dependency-graph.js +11 -4
- package/core/lib/lantern/network-node.d.ts +2 -2
- package/core/lib/lantern/network-node.js +3 -4
- package/core/lib/lantern/page-dependency-graph.d.ts +9 -9
- package/core/lib/lantern/page-dependency-graph.js +90 -58
- package/core/lib/lantern/simulator/connection-pool.d.ts +10 -10
- package/core/lib/lantern/simulator/connection-pool.js +22 -22
- package/core/lib/lantern/simulator/dns-cache.d.ts +1 -1
- package/core/lib/lantern/simulator/dns-cache.js +1 -1
- package/core/lib/lantern/simulator/network-analyzer.d.ts +12 -12
- package/core/lib/lantern/simulator/network-analyzer.js +53 -53
- package/core/lib/lantern/simulator/simulator.d.ts +4 -4
- package/core/lib/lantern/simulator/simulator.js +17 -17
- package/core/lib/lantern/types/lantern.d.ts +6 -5
- package/core/lib/lantern-trace-saver.js +1 -1
- package/core/lib/network-request.js +1 -1
- package/package.json +1 -1
|
@@ -32,19 +32,19 @@ const IGNORED_MIME_TYPES_REGEX = /^video/;
|
|
|
32
32
|
|
|
33
33
|
class PageDependencyGraph {
|
|
34
34
|
/**
|
|
35
|
-
* @param {Lantern.NetworkRequest}
|
|
35
|
+
* @param {Lantern.NetworkRequest} request
|
|
36
36
|
* @return {Array<string>}
|
|
37
37
|
*/
|
|
38
|
-
static getNetworkInitiators(
|
|
39
|
-
if (!
|
|
40
|
-
if (
|
|
41
|
-
if (
|
|
38
|
+
static getNetworkInitiators(request) {
|
|
39
|
+
if (!request.initiator) return [];
|
|
40
|
+
if (request.initiator.url) return [request.initiator.url];
|
|
41
|
+
if (request.initiator.type === 'script') {
|
|
42
42
|
// Script initiators have the stack of callFrames from all functions that led to this request.
|
|
43
43
|
// If async stacks are enabled, then the stack will also have the parent functions that asynchronously
|
|
44
44
|
// led to this request chained in the `parent` property.
|
|
45
45
|
/** @type {Set<string>} */
|
|
46
46
|
const scriptURLs = new Set();
|
|
47
|
-
let stack =
|
|
47
|
+
let stack = request.initiator.stack;
|
|
48
48
|
while (stack) {
|
|
49
49
|
const callFrames = stack.callFrames || [];
|
|
50
50
|
for (const frame of callFrames) {
|
|
@@ -61,10 +61,10 @@ class PageDependencyGraph {
|
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
/**
|
|
64
|
-
* @param {Array<Lantern.NetworkRequest>}
|
|
64
|
+
* @param {Array<Lantern.NetworkRequest>} networkRequests
|
|
65
65
|
* @return {NetworkNodeOutput}
|
|
66
66
|
*/
|
|
67
|
-
static getNetworkNodeOutput(
|
|
67
|
+
static getNetworkNodeOutput(networkRequests) {
|
|
68
68
|
/** @type {Array<NetworkNode>} */
|
|
69
69
|
const nodes = [];
|
|
70
70
|
/** @type {Map<string, NetworkNode>} */
|
|
@@ -74,35 +74,35 @@ class PageDependencyGraph {
|
|
|
74
74
|
/** @type {Map<string, NetworkNode|null>} */
|
|
75
75
|
const frameIdToNodeMap = new Map();
|
|
76
76
|
|
|
77
|
-
|
|
78
|
-
if (IGNORED_MIME_TYPES_REGEX.test(
|
|
79
|
-
if (
|
|
77
|
+
networkRequests.forEach(request => {
|
|
78
|
+
if (IGNORED_MIME_TYPES_REGEX.test(request.mimeType)) return;
|
|
79
|
+
if (request.fromWorker) return;
|
|
80
80
|
|
|
81
|
-
// Network
|
|
82
|
-
// Suffix all subsequent
|
|
81
|
+
// Network requestIds can be duplicated for an unknown reason
|
|
82
|
+
// Suffix all subsequent requests with `:duplicate` until it's unique
|
|
83
83
|
// NOTE: This should never happen with modern NetworkRequest library, but old fixtures
|
|
84
84
|
// might still have this issue.
|
|
85
|
-
while (idToNodeMap.has(
|
|
86
|
-
|
|
85
|
+
while (idToNodeMap.has(request.requestId)) {
|
|
86
|
+
request.requestId += ':duplicate';
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
const node = new NetworkNode(
|
|
89
|
+
const node = new NetworkNode(request);
|
|
90
90
|
nodes.push(node);
|
|
91
91
|
|
|
92
|
-
const urlList = urlToNodeMap.get(
|
|
92
|
+
const urlList = urlToNodeMap.get(request.url) || [];
|
|
93
93
|
urlList.push(node);
|
|
94
94
|
|
|
95
|
-
idToNodeMap.set(
|
|
96
|
-
urlToNodeMap.set(
|
|
95
|
+
idToNodeMap.set(request.requestId, node);
|
|
96
|
+
urlToNodeMap.set(request.url, urlList);
|
|
97
97
|
|
|
98
98
|
// If the request was for the root document of an iframe, save an entry in our
|
|
99
99
|
// map so we can link up the task `args.data.frame` dependencies later in graph creation.
|
|
100
|
-
if (
|
|
101
|
-
|
|
102
|
-
|
|
100
|
+
if (request.frameId &&
|
|
101
|
+
request.resourceType === NetworkRequestTypes.Document &&
|
|
102
|
+
request.documentURL === request.url) {
|
|
103
103
|
// If there's ever any ambiguity, permanently set the value to `false` to avoid loops in the graph.
|
|
104
|
-
const value = frameIdToNodeMap.has(
|
|
105
|
-
frameIdToNodeMap.set(
|
|
104
|
+
const value = frameIdToNodeMap.has(request.frameId) ? null : node;
|
|
105
|
+
frameIdToNodeMap.set(request.frameId, value);
|
|
106
106
|
}
|
|
107
107
|
});
|
|
108
108
|
|
|
@@ -427,7 +427,7 @@ class PageDependencyGraph {
|
|
|
427
427
|
}
|
|
428
428
|
|
|
429
429
|
for (const r of lanternRequests) {
|
|
430
|
-
delete r.
|
|
430
|
+
delete r.rawRequest;
|
|
431
431
|
if (r.initiatorRequest) {
|
|
432
432
|
// @ts-expect-error
|
|
433
433
|
r.initiatorRequest = {id: r.initiatorRequest.requestId};
|
|
@@ -508,25 +508,25 @@ class PageDependencyGraph {
|
|
|
508
508
|
|
|
509
509
|
/**
|
|
510
510
|
* @param {LH.TraceEvent[]} mainThreadEvents
|
|
511
|
-
* @param {
|
|
511
|
+
* @param {Lantern.NetworkRequest[]} networkRequests
|
|
512
512
|
* @param {URLArtifact} URL
|
|
513
513
|
* @return {Node}
|
|
514
514
|
*/
|
|
515
|
-
static createGraph(mainThreadEvents,
|
|
515
|
+
static createGraph(mainThreadEvents, networkRequests, URL) {
|
|
516
516
|
// This is for debugging trace/devtoolslog network records.
|
|
517
|
-
// const debug = PageDependencyGraph._debugNormalizeRequests(
|
|
518
|
-
const networkNodeOutput = PageDependencyGraph.getNetworkNodeOutput(
|
|
517
|
+
// const debug = PageDependencyGraph._debugNormalizeRequests(networkRequests);
|
|
518
|
+
const networkNodeOutput = PageDependencyGraph.getNetworkNodeOutput(networkRequests);
|
|
519
519
|
const cpuNodes = PageDependencyGraph.getCPUNodes(mainThreadEvents);
|
|
520
520
|
const {requestedUrl, mainDocumentUrl} = URL;
|
|
521
521
|
if (!requestedUrl) throw new Error('requestedUrl is required to get the root request');
|
|
522
522
|
if (!mainDocumentUrl) throw new Error('mainDocumentUrl is required to get the main resource');
|
|
523
523
|
|
|
524
|
-
const rootRequest = NetworkAnalyzer.findResourceForUrl(
|
|
524
|
+
const rootRequest = NetworkAnalyzer.findResourceForUrl(networkRequests, requestedUrl);
|
|
525
525
|
if (!rootRequest) throw new Error('rootRequest not found');
|
|
526
526
|
const rootNode = networkNodeOutput.idToNodeMap.get(rootRequest.requestId);
|
|
527
527
|
if (!rootNode) throw new Error('rootNode not found');
|
|
528
528
|
const mainDocumentRequest =
|
|
529
|
-
NetworkAnalyzer.findLastDocumentForUrl(
|
|
529
|
+
NetworkAnalyzer.findLastDocumentForUrl(networkRequests, mainDocumentUrl);
|
|
530
530
|
if (!mainDocumentRequest) throw new Error('mainDocumentRequest not found');
|
|
531
531
|
const mainDocumentNode = networkNodeOutput.idToNodeMap.get(mainDocumentRequest.requestId);
|
|
532
532
|
if (!mainDocumentNode) throw new Error('mainDocumentNode not found');
|
|
@@ -543,20 +543,20 @@ class PageDependencyGraph {
|
|
|
543
543
|
}
|
|
544
544
|
|
|
545
545
|
/**
|
|
546
|
-
* @param {Lantern.NetworkRequest}
|
|
547
|
-
* @param {Map<string, Lantern.NetworkRequest[]>}
|
|
546
|
+
* @param {Lantern.NetworkRequest} request The request to find the initiator of
|
|
547
|
+
* @param {Map<string, Lantern.NetworkRequest[]>} requestsByURL
|
|
548
548
|
* @return {Lantern.NetworkRequest|null}
|
|
549
549
|
*/
|
|
550
|
-
static chooseInitiatorRequest(
|
|
551
|
-
if (
|
|
552
|
-
return
|
|
550
|
+
static chooseInitiatorRequest(request, requestsByURL) {
|
|
551
|
+
if (request.redirectSource) {
|
|
552
|
+
return request.redirectSource;
|
|
553
553
|
}
|
|
554
554
|
|
|
555
|
-
const initiatorURL = PageDependencyGraph.getNetworkInitiators(
|
|
556
|
-
let candidates =
|
|
555
|
+
const initiatorURL = PageDependencyGraph.getNetworkInitiators(request)[0];
|
|
556
|
+
let candidates = requestsByURL.get(initiatorURL) || [];
|
|
557
557
|
// The (valid) initiator must come before the initiated request.
|
|
558
558
|
candidates = candidates.filter(c => {
|
|
559
|
-
return c.responseHeadersEndTime <=
|
|
559
|
+
return c.responseHeadersEndTime <= request.rendererStartTime &&
|
|
560
560
|
c.finished && !c.failed;
|
|
561
561
|
});
|
|
562
562
|
if (candidates.length > 1) {
|
|
@@ -570,12 +570,12 @@ class PageDependencyGraph {
|
|
|
570
570
|
}
|
|
571
571
|
if (candidates.length > 1) {
|
|
572
572
|
// Disambiguate based on frame. It's likely that the initiator comes from the same frame.
|
|
573
|
-
const sameFrameCandidates = candidates.filter(cand => cand.frameId ===
|
|
573
|
+
const sameFrameCandidates = candidates.filter(cand => cand.frameId === request.frameId);
|
|
574
574
|
if (sameFrameCandidates.length) {
|
|
575
575
|
candidates = sameFrameCandidates;
|
|
576
576
|
}
|
|
577
577
|
}
|
|
578
|
-
if (candidates.length > 1 &&
|
|
578
|
+
if (candidates.length > 1 && request.initiator.type === 'parser') {
|
|
579
579
|
// Filter to just Documents when initiator type is parser.
|
|
580
580
|
const documentCandidates = candidates.filter(cand =>
|
|
581
581
|
cand.resourceType === RESOURCE_TYPES.Document);
|
|
@@ -712,9 +712,23 @@ class PageDependencyGraph {
|
|
|
712
712
|
if (request.args.data.initiator?.fetchType === 'xmlhttprequest') {
|
|
713
713
|
// @ts-expect-error yes XHR is a valid ResourceType. TypeScript const enums are so unhelpful.
|
|
714
714
|
resourceType = 'XHR';
|
|
715
|
+
} else if (request.args.data.initiator?.fetchType === 'fetch') {
|
|
716
|
+
// @ts-expect-error yes Fetch is a valid ResourceType. TypeScript const enums are so unhelpful.
|
|
717
|
+
resourceType = 'Fetch';
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
// TODO: set decodedBodyLength for data urls in Trace Engine.
|
|
721
|
+
let resourceSize = request.args.data.decodedBodyLength ?? 0;
|
|
722
|
+
if (url.protocol === 'data:' && resourceSize === 0) {
|
|
723
|
+
const needle = 'base64,';
|
|
724
|
+
const index = url.pathname.indexOf(needle);
|
|
725
|
+
if (index !== -1) {
|
|
726
|
+
resourceSize = atob(url.pathname.substring(index + needle.length)).length;
|
|
727
|
+
}
|
|
715
728
|
}
|
|
716
729
|
|
|
717
730
|
return {
|
|
731
|
+
rawRequest: request,
|
|
718
732
|
requestId: request.args.data.requestId,
|
|
719
733
|
connectionId: request.args.data.connectionId,
|
|
720
734
|
connectionReused: request.args.data.connectionReused,
|
|
@@ -727,7 +741,7 @@ class PageDependencyGraph {
|
|
|
727
741
|
responseHeadersEndTime: request.args.data.syntheticData.downloadStart / 1000,
|
|
728
742
|
networkEndTime: request.args.data.syntheticData.finishTime / 1000,
|
|
729
743
|
transferSize: request.args.data.encodedDataLength,
|
|
730
|
-
resourceSize
|
|
744
|
+
resourceSize,
|
|
731
745
|
fromDiskCache: request.args.data.syntheticData.isDiskCached,
|
|
732
746
|
fromMemoryCache: request.args.data.syntheticData.isMemoryCached,
|
|
733
747
|
isLinkPreload: request.args.data.isLinkPreload,
|
|
@@ -741,7 +755,6 @@ class PageDependencyGraph {
|
|
|
741
755
|
priority: request.args.data.priority,
|
|
742
756
|
frameId: request.args.data.frame,
|
|
743
757
|
fromWorker,
|
|
744
|
-
record: request,
|
|
745
758
|
// Set later.
|
|
746
759
|
redirects: undefined,
|
|
747
760
|
redirectSource: undefined,
|
|
@@ -792,31 +805,50 @@ class PageDependencyGraph {
|
|
|
792
805
|
// TraceEngine consolidates all redirects into a single request object, but lantern needs
|
|
793
806
|
// an entry for each redirected request.
|
|
794
807
|
for (const request of [...lanternRequests]) {
|
|
795
|
-
if (!request.
|
|
808
|
+
if (!request.rawRequest) continue;
|
|
796
809
|
|
|
797
|
-
const redirects = request.
|
|
810
|
+
const redirects = request.rawRequest.args.data.redirects;
|
|
798
811
|
if (!redirects.length) continue;
|
|
799
812
|
|
|
800
813
|
const requestChain = [];
|
|
801
814
|
for (const redirect of redirects) {
|
|
802
815
|
const redirectedRequest = structuredClone(request);
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
redirectedRequest.
|
|
812
|
-
redirectedRequest.responseHeadersEndTime
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
+
|
|
817
|
+
redirectedRequest.networkRequestTime = redirect.ts / 1000;
|
|
818
|
+
redirectedRequest.rendererStartTime = redirectedRequest.networkRequestTime;
|
|
819
|
+
|
|
820
|
+
redirectedRequest.networkEndTime = (redirect.ts + redirect.dur) / 1000;
|
|
821
|
+
redirectedRequest.responseHeadersEndTime = redirectedRequest.networkEndTime;
|
|
822
|
+
|
|
823
|
+
redirectedRequest.timing = {
|
|
824
|
+
requestTime: redirectedRequest.networkRequestTime / 1000,
|
|
825
|
+
receiveHeadersStart: redirectedRequest.responseHeadersEndTime,
|
|
826
|
+
receiveHeadersEnd: redirectedRequest.responseHeadersEndTime,
|
|
827
|
+
proxyStart: -1,
|
|
828
|
+
proxyEnd: -1,
|
|
829
|
+
dnsStart: -1,
|
|
830
|
+
dnsEnd: -1,
|
|
831
|
+
connectStart: -1,
|
|
832
|
+
connectEnd: -1,
|
|
833
|
+
sslStart: -1,
|
|
834
|
+
sslEnd: -1,
|
|
835
|
+
sendStart: -1,
|
|
836
|
+
sendEnd: -1,
|
|
837
|
+
workerStart: -1,
|
|
838
|
+
workerReady: -1,
|
|
839
|
+
workerFetchStart: -1,
|
|
840
|
+
workerRespondWithSettled: -1,
|
|
841
|
+
pushStart: -1,
|
|
842
|
+
pushEnd: -1,
|
|
843
|
+
};
|
|
844
|
+
|
|
816
845
|
redirectedRequest.url = redirect.url;
|
|
817
846
|
redirectedRequest.parsedURL = this._createParsedUrl(redirect.url);
|
|
818
847
|
// TODO: TraceEngine is not retaining the actual status code.
|
|
819
848
|
redirectedRequest.statusCode = 302;
|
|
849
|
+
redirectedRequest.resourceType = undefined;
|
|
850
|
+
// TODO: TraceEngine is not retaining transfer size of redirected request.
|
|
851
|
+
redirectedRequest.transferSize = 400;
|
|
820
852
|
requestChain.push(redirectedRequest);
|
|
821
853
|
lanternRequests.push(redirectedRequest);
|
|
822
854
|
}
|
|
@@ -9,7 +9,7 @@ export class ConnectionPool {
|
|
|
9
9
|
/** @type {Map<string, TcpConnection[]>} */
|
|
10
10
|
_connectionsByOrigin: Map<string, TcpConnection[]>;
|
|
11
11
|
/** @type {Map<Lantern.NetworkRequest, TcpConnection>} */
|
|
12
|
-
|
|
12
|
+
_connectionsByRequest: Map<Lantern.NetworkRequest, TcpConnection>;
|
|
13
13
|
_connectionsInUse: Set<any>;
|
|
14
14
|
_connectionReusedByRequestId: Map<string, boolean>;
|
|
15
15
|
/**
|
|
@@ -22,26 +22,26 @@ export class ConnectionPool {
|
|
|
22
22
|
*/
|
|
23
23
|
_findAvailableConnectionWithLargestCongestionWindow(connections: Array<TcpConnection>): TcpConnection | null;
|
|
24
24
|
/**
|
|
25
|
-
* This method finds an available connection to the origin specified by the network
|
|
25
|
+
* This method finds an available connection to the origin specified by the network request or null
|
|
26
26
|
* if no connection was available. If returned, connection will not be available for other network
|
|
27
27
|
* records until release is called.
|
|
28
28
|
*
|
|
29
|
-
* @param {Lantern.NetworkRequest}
|
|
29
|
+
* @param {Lantern.NetworkRequest} request
|
|
30
30
|
* @return {?TcpConnection}
|
|
31
31
|
*/
|
|
32
|
-
acquire(
|
|
32
|
+
acquire(request: Lantern.NetworkRequest): TcpConnection | null;
|
|
33
33
|
/**
|
|
34
|
-
* Return the connection currently being used to fetch a
|
|
35
|
-
* currently being used for this
|
|
34
|
+
* Return the connection currently being used to fetch a request. If no connection
|
|
35
|
+
* currently being used for this request, an error will be thrown.
|
|
36
36
|
*
|
|
37
|
-
* @param {Lantern.NetworkRequest}
|
|
37
|
+
* @param {Lantern.NetworkRequest} request
|
|
38
38
|
* @return {TcpConnection}
|
|
39
39
|
*/
|
|
40
|
-
|
|
40
|
+
acquireActiveConnectionFromRequest(request: Lantern.NetworkRequest): TcpConnection;
|
|
41
41
|
/**
|
|
42
|
-
* @param {Lantern.NetworkRequest}
|
|
42
|
+
* @param {Lantern.NetworkRequest} request
|
|
43
43
|
*/
|
|
44
|
-
release(
|
|
44
|
+
release(request: Lantern.NetworkRequest): void;
|
|
45
45
|
}
|
|
46
46
|
import * as Lantern from '../types/lantern.js';
|
|
47
47
|
import { TcpConnection } from './tcp-connection.js';
|
|
@@ -27,7 +27,7 @@ export class ConnectionPool {
|
|
|
27
27
|
/** @type {Map<string, TcpConnection[]>} */
|
|
28
28
|
this._connectionsByOrigin = new Map();
|
|
29
29
|
/** @type {Map<Lantern.NetworkRequest, TcpConnection>} */
|
|
30
|
-
this.
|
|
30
|
+
this._connectionsByRequest = new Map();
|
|
31
31
|
this._connectionsInUse = new Set();
|
|
32
32
|
this._connectionReusedByRequestId = NetworkAnalyzer.estimateIfConnectionWasReused(records, {
|
|
33
33
|
forceCoarseEstimates: true,
|
|
@@ -49,16 +49,16 @@ export class ConnectionPool {
|
|
|
49
49
|
const serverResponseTimeByOrigin = this._options.serverResponseTimeByOrigin;
|
|
50
50
|
|
|
51
51
|
const recordsByOrigin = NetworkAnalyzer.groupByOrigin(this._records);
|
|
52
|
-
for (const [origin,
|
|
52
|
+
for (const [origin, requests] of recordsByOrigin.entries()) {
|
|
53
53
|
const connections = [];
|
|
54
54
|
const additionalRtt = additionalRttByOrigin.get(origin) || 0;
|
|
55
55
|
const responseTime = serverResponseTimeByOrigin.get(origin) || DEFAULT_SERVER_RESPONSE_TIME;
|
|
56
56
|
|
|
57
|
-
for (const
|
|
58
|
-
if (connectionReused.get(
|
|
57
|
+
for (const request of requests) {
|
|
58
|
+
if (connectionReused.get(request.requestId)) continue;
|
|
59
59
|
|
|
60
|
-
const isTLS = TLS_SCHEMES.includes(
|
|
61
|
-
const isH2 =
|
|
60
|
+
const isTLS = TLS_SCHEMES.includes(request.parsedURL.scheme);
|
|
61
|
+
const isH2 = request.protocol === 'h2';
|
|
62
62
|
const connection = new TcpConnection(
|
|
63
63
|
this._options.rtt + additionalRtt,
|
|
64
64
|
this._options.throughput,
|
|
@@ -106,47 +106,47 @@ export class ConnectionPool {
|
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
/**
|
|
109
|
-
* This method finds an available connection to the origin specified by the network
|
|
109
|
+
* This method finds an available connection to the origin specified by the network request or null
|
|
110
110
|
* if no connection was available. If returned, connection will not be available for other network
|
|
111
111
|
* records until release is called.
|
|
112
112
|
*
|
|
113
|
-
* @param {Lantern.NetworkRequest}
|
|
113
|
+
* @param {Lantern.NetworkRequest} request
|
|
114
114
|
* @return {?TcpConnection}
|
|
115
115
|
*/
|
|
116
|
-
acquire(
|
|
117
|
-
if (this.
|
|
116
|
+
acquire(request) {
|
|
117
|
+
if (this._connectionsByRequest.has(request)) throw new Error('Record already has a connection');
|
|
118
118
|
|
|
119
|
-
const origin =
|
|
119
|
+
const origin = request.parsedURL.securityOrigin;
|
|
120
120
|
const connections = this._connectionsByOrigin.get(origin) || [];
|
|
121
121
|
const connectionToUse = this._findAvailableConnectionWithLargestCongestionWindow(connections);
|
|
122
122
|
|
|
123
123
|
if (!connectionToUse) return null;
|
|
124
124
|
|
|
125
125
|
this._connectionsInUse.add(connectionToUse);
|
|
126
|
-
this.
|
|
126
|
+
this._connectionsByRequest.set(request, connectionToUse);
|
|
127
127
|
return connectionToUse;
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
/**
|
|
131
|
-
* Return the connection currently being used to fetch a
|
|
132
|
-
* currently being used for this
|
|
131
|
+
* Return the connection currently being used to fetch a request. If no connection
|
|
132
|
+
* currently being used for this request, an error will be thrown.
|
|
133
133
|
*
|
|
134
|
-
* @param {Lantern.NetworkRequest}
|
|
134
|
+
* @param {Lantern.NetworkRequest} request
|
|
135
135
|
* @return {TcpConnection}
|
|
136
136
|
*/
|
|
137
|
-
|
|
138
|
-
const activeConnection = this.
|
|
139
|
-
if (!activeConnection) throw new Error('Could not find an active connection for
|
|
137
|
+
acquireActiveConnectionFromRequest(request) {
|
|
138
|
+
const activeConnection = this._connectionsByRequest.get(request);
|
|
139
|
+
if (!activeConnection) throw new Error('Could not find an active connection for request');
|
|
140
140
|
|
|
141
141
|
return activeConnection;
|
|
142
142
|
}
|
|
143
143
|
|
|
144
144
|
/**
|
|
145
|
-
* @param {Lantern.NetworkRequest}
|
|
145
|
+
* @param {Lantern.NetworkRequest} request
|
|
146
146
|
*/
|
|
147
|
-
release(
|
|
148
|
-
const connection = this.
|
|
149
|
-
this.
|
|
147
|
+
release(request) {
|
|
148
|
+
const connection = this._connectionsByRequest.get(request);
|
|
149
|
+
this._connectionsByRequest.delete(request);
|
|
150
150
|
this._connectionsInUse.delete(connection);
|
|
151
151
|
}
|
|
152
152
|
}
|
|
@@ -25,7 +25,7 @@ export class DNSCache {
|
|
|
25
25
|
*/
|
|
26
26
|
_updateCacheResolvedAtIfNeeded(request: Lantern.NetworkRequest, resolvedAt: number): void;
|
|
27
27
|
/**
|
|
28
|
-
* Forcefully sets the DNS resolution time for a
|
|
28
|
+
* Forcefully sets the DNS resolution time for a request.
|
|
29
29
|
* Useful for testing and alternate execution simulations.
|
|
30
30
|
*
|
|
31
31
|
* @param {string} domain
|
|
@@ -46,14 +46,14 @@ export class NetworkAnalyzer {
|
|
|
46
46
|
* @return {Map<string, Summary>}
|
|
47
47
|
*/
|
|
48
48
|
static summarize(values: Map<string, number[]>): Map<string, Summary>;
|
|
49
|
-
/** @typedef {{
|
|
49
|
+
/** @typedef {{request: Lantern.NetworkRequest, timing: LH.Crdp.Network.ResourceTiming, connectionReused?: boolean}} RequestInfo */
|
|
50
50
|
/**
|
|
51
|
-
* @param {Lantern.NetworkRequest[]}
|
|
51
|
+
* @param {Lantern.NetworkRequest[]} requests
|
|
52
52
|
* @param {(e: RequestInfo) => number | number[] | undefined} iteratee
|
|
53
53
|
* @return {Map<string, number[]>}
|
|
54
54
|
*/
|
|
55
|
-
static _estimateValueByOrigin(
|
|
56
|
-
|
|
55
|
+
static _estimateValueByOrigin(requests: Lantern.NetworkRequest[], iteratee: (e: {
|
|
56
|
+
request: Lantern.NetworkRequest;
|
|
57
57
|
timing: LH.Crdp.Network.ResourceTiming;
|
|
58
58
|
connectionReused?: boolean | undefined;
|
|
59
59
|
}) => number | number[] | undefined): Map<string, number[]>;
|
|
@@ -69,7 +69,7 @@ export class NetworkAnalyzer {
|
|
|
69
69
|
* @return {number[]|number|undefined}
|
|
70
70
|
*/
|
|
71
71
|
static _estimateRTTViaConnectionTiming(info: {
|
|
72
|
-
|
|
72
|
+
request: Lantern.NetworkRequest;
|
|
73
73
|
timing: LH.Crdp.Network.ResourceTiming;
|
|
74
74
|
connectionReused?: boolean | undefined;
|
|
75
75
|
}): number[] | number | undefined;
|
|
@@ -82,7 +82,7 @@ export class NetworkAnalyzer {
|
|
|
82
82
|
* @return {number|undefined}
|
|
83
83
|
*/
|
|
84
84
|
static _estimateRTTViaDownloadTiming(info: {
|
|
85
|
-
|
|
85
|
+
request: Lantern.NetworkRequest;
|
|
86
86
|
timing: LH.Crdp.Network.ResourceTiming;
|
|
87
87
|
connectionReused?: boolean | undefined;
|
|
88
88
|
}): number | undefined;
|
|
@@ -96,7 +96,7 @@ export class NetworkAnalyzer {
|
|
|
96
96
|
* @return {number|undefined}
|
|
97
97
|
*/
|
|
98
98
|
static _estimateRTTViaSendStartTiming(info: {
|
|
99
|
-
|
|
99
|
+
request: Lantern.NetworkRequest;
|
|
100
100
|
timing: LH.Crdp.Network.ResourceTiming;
|
|
101
101
|
connectionReused?: boolean | undefined;
|
|
102
102
|
}): number | undefined;
|
|
@@ -110,7 +110,7 @@ export class NetworkAnalyzer {
|
|
|
110
110
|
* @return {number|undefined}
|
|
111
111
|
*/
|
|
112
112
|
static _estimateRTTViaHeadersEndTiming(info: {
|
|
113
|
-
|
|
113
|
+
request: Lantern.NetworkRequest;
|
|
114
114
|
timing: LH.Crdp.Network.ResourceTiming;
|
|
115
115
|
connectionReused?: boolean | undefined;
|
|
116
116
|
}): number | undefined;
|
|
@@ -123,10 +123,10 @@ export class NetworkAnalyzer {
|
|
|
123
123
|
*/
|
|
124
124
|
static _estimateResponseTimeByOrigin(records: Lantern.NetworkRequest[], rttByOrigin: Map<string, number>): Map<string, number[]>;
|
|
125
125
|
/**
|
|
126
|
-
* @param {Lantern.NetworkRequest[]}
|
|
126
|
+
* @param {Lantern.NetworkRequest[]} requests
|
|
127
127
|
* @return {boolean}
|
|
128
128
|
*/
|
|
129
|
-
static canTrustConnectionInformation(
|
|
129
|
+
static canTrustConnectionInformation(requests: Lantern.NetworkRequest[]): boolean;
|
|
130
130
|
/**
|
|
131
131
|
* Returns a map of requestId -> connectionReused, estimating the information if the information
|
|
132
132
|
* available in the records themselves appears untrustworthy.
|
|
@@ -160,9 +160,9 @@ export class NetworkAnalyzer {
|
|
|
160
160
|
rttByOrigin?: Map<string, number> | undefined;
|
|
161
161
|
}) | undefined): Map<string, Summary>;
|
|
162
162
|
/**
|
|
163
|
-
* Computes the average throughput for the given
|
|
163
|
+
* Computes the average throughput for the given requests in bits/second.
|
|
164
164
|
* Excludes data URI, failed or otherwise incomplete, and cached requests.
|
|
165
|
-
* Returns Infinity if there were no analyzable network
|
|
165
|
+
* Returns Infinity if there were no analyzable network requests.
|
|
166
166
|
*
|
|
167
167
|
* @param {Lantern.NetworkRequest[]} records
|
|
168
168
|
* @return {number}
|