Skip to content

Commit d306efc

Browse files
Connor ClarkDevtools-frontend LUCI CQ
authored andcommitted
Include requests with no response in NetworkRequests handler
Requests that never send a response are a thing that happens: * Long-polling requests that never end * The trace ended before the request did We should not exclude these from the trace processor. This helps with Lantern - see GoogleChrome/lighthouse#16492. Additionally, I think this handler wanted to filter out data urls, but it wasn't doing it directly so some got through. That should be fixed now. Bug: none Change-Id: I911211512055667fa2deb125dd64389ce8968324 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/6569818 Auto-Submit: Connor Clark <cjamcl@chromium.org> Reviewed-by: Paul Irish <paulirish@chromium.org> Commit-Queue: Connor Clark <cjamcl@chromium.org>
1 parent 9a2dd8e commit d306efc

File tree

11 files changed

+60
-45
lines changed

11 files changed

+60
-45
lines changed

front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,6 @@ MIME Type: ${mimeType}
495495
${priorityLines.join('\n')}
496496
Render blocking: ${renderBlocking ? 'Yes' : 'No'}
497497
From a service worker: ${fromServiceWorker ? 'Yes' : 'No'}
498-
${NetworkRequestFormatter.formatHeaders('Response headers', responseHeaders, true)}`;
498+
${NetworkRequestFormatter.formatHeaders('Response headers', responseHeaders ?? [], true)}`;
499499
}
500500
}

front_end/models/trace/LanternComputationData.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ function findWorkerThreads(trace: Lantern.Types.Trace): Map<number, number[]> {
8787
function createLanternRequest(
8888
parsedTrace: Readonly<Handlers.Types.ParsedTrace>, workerThreads: Map<number, number[]>,
8989
request: Types.Events.SyntheticNetworkRequest): NetworkRequest|undefined {
90-
if (request.args.data.connectionId === undefined || request.args.data.connectionReused === undefined) {
90+
if (request.args.data.hasResponse && request.args.data.connectionId === undefined) {
9191
throw new Lantern.Core.LanternError('Trace is too old');
9292
}
9393

@@ -166,8 +166,8 @@ function createLanternRequest(
166166
return {
167167
rawRequest: request,
168168
requestId: request.args.data.requestId,
169-
connectionId: request.args.data.connectionId,
170-
connectionReused: request.args.data.connectionReused,
169+
connectionId: request.args.data.connectionId ?? 0,
170+
connectionReused: request.args.data.connectionReused ?? false,
171171
url: request.args.data.url,
172172
protocol: request.args.data.protocol,
173173
parsedURL: createParsedUrl(url),

front_end/models/trace/handlers/NetworkRequestsHandler.ts

Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,9 @@ function storeTraceEventWithRequestId<K extends keyof TraceEventsForNetworkReque
106106
}
107107
}
108108

109-
function firstPositiveValueInList(entries: number[]): number {
109+
function firstPositiveValueInList(entries: Array<number|null>): number {
110110
for (const entry of entries) {
111-
if (entry > 0) {
111+
if (entry && entry > 0) {
112112
return entry;
113113
}
114114
}
@@ -204,7 +204,7 @@ export async function finalize(): Promise<void> {
204204
for (const [requestId, request] of requestMap.entries()) {
205205
// If we have an incomplete set of events here, we choose to drop the network
206206
// request rather than attempt to synthesize the missing data.
207-
if (!request.sendRequests || !request.receiveResponse) {
207+
if (!request.sendRequests) {
208208
continue;
209209
}
210210

@@ -244,12 +244,20 @@ export async function finalize(): Promise<void> {
244244
});
245245
}
246246

247+
const firstSendRequest = request.sendRequests[0];
248+
const finalSendRequest = request.sendRequests[request.sendRequests.length - 1];
249+
250+
// We currently do not want to include data URI requests. We may revisit this in the future.
251+
if (finalSendRequest.args.data.url.startsWith('data:')) {
252+
continue;
253+
}
254+
247255
// If a ResourceFinish event with an encoded data length is received,
248256
// then the resource was not cached; it was fetched before it was
249257
// requested, e.g. because it was pushed in this navigation.
250258
const isPushedResource = request.resourceFinish?.args.data.encodedDataLength !== 0;
251259
// This works around crbug.com/998397, which reports pushed resources, and resources served by a service worker as disk cached.
252-
const isDiskCached = request.receiveResponse.args.data.fromCache &&
260+
const isDiskCached = !!request.receiveResponse && request.receiveResponse.args.data.fromCache &&
253261
!request.receiveResponse.args.data.fromServiceWorker && !isPushedResource;
254262
// If the request contains a resourceMarkAsCached event, it was served from memory cache.
255263
// The timing data returned is from the original (uncached) request, which
@@ -265,15 +273,12 @@ export async function finalize(): Promise<void> {
265273
const isMemoryCached = request.resourceMarkAsCached !== undefined;
266274
// If a request has `resourceMarkAsCached` field, the `timing` field is not correct.
267275
// So let's discard it and override to 0 (which will be handled in later logic if timing field is undefined).
268-
const timing = isMemoryCached ? undefined : request.receiveResponse.args.data.timing;
269-
// If a non-cached request has no |timing| indicates data URLs, we ignore it.
270-
if (!timing && !isMemoryCached) {
276+
const timing = isMemoryCached ? undefined : request.receiveResponse?.args.data.timing;
277+
// If a non-cached response has no |timing|, we ignore it. An example of this is chrome://new-page / about:blank.
278+
if (request.receiveResponse && !timing && !isMemoryCached) {
271279
continue;
272280
}
273281

274-
const firstSendRequest = request.sendRequests[0];
275-
const finalSendRequest = request.sendRequests[request.sendRequests.length - 1];
276-
277282
const initialPriority = finalSendRequest.args.data.priority;
278283
let finalPriority = initialPriority;
279284
if (request.changePriority) {
@@ -343,13 +348,14 @@ export async function finalize(): Promise<void> {
343348
// Otherwise it is whichever positive number comes first from the following timing info:
344349
// DNS start, Connection start, Send Start, or the time duration between our start time and
345350
// receiving a response.
346-
const stalled = timing ? Types.Timing.Micro(firstPositiveValueInList([
347-
timing.dnsStart * MILLISECONDS_TO_MICROSECONDS,
348-
timing.connectStart * MILLISECONDS_TO_MICROSECONDS,
349-
timing.sendStart * MILLISECONDS_TO_MICROSECONDS,
350-
(request.receiveResponse.ts - endRedirectTime),
351-
])) :
352-
Types.Timing.Micro(request.receiveResponse.ts - startTime);
351+
const stalled = timing ?
352+
Types.Timing.Micro(firstPositiveValueInList([
353+
timing.dnsStart * MILLISECONDS_TO_MICROSECONDS,
354+
timing.connectStart * MILLISECONDS_TO_MICROSECONDS,
355+
timing.sendStart * MILLISECONDS_TO_MICROSECONDS,
356+
request.receiveResponse ? (request.receiveResponse.ts - endRedirectTime) : null,
357+
])) :
358+
(request.receiveResponse ? Types.Timing.Micro(request.receiveResponse.ts - startTime) : Types.Timing.Micro(0));
353359

354360
// Sending HTTP request
355361
// =======================
@@ -373,8 +379,9 @@ export async function finalize(): Promise<void> {
373379
Types.Timing.Micro(
374380
timing.requestTime * SECONDS_TO_MICROSECONDS + timing.receiveHeadersEnd * MILLISECONDS_TO_MICROSECONDS) :
375381
startTime;
376-
const download = timing ? Types.Timing.Micro(((finishTime || downloadStart) - downloadStart)) :
377-
Types.Timing.Micro(endTime - request.receiveResponse.ts);
382+
const download = timing ? Types.Timing.Micro(((finishTime || downloadStart) - downloadStart)) :
383+
request.receiveResponse ? Types.Timing.Micro(endTime - request.receiveResponse.ts) :
384+
Types.Timing.Micro(0);
378385

379386
const totalTime = Types.Timing.Micro(networkDuration + processingDuration);
380387

@@ -435,30 +442,31 @@ export async function finalize(): Promise<void> {
435442
decodedBodyLength,
436443
encodedDataLength,
437444
frame,
438-
fromServiceWorker: request.receiveResponse.args.data.fromServiceWorker,
445+
fromServiceWorker: request.receiveResponse?.args.data.fromServiceWorker,
439446
isLinkPreload: finalSendRequest.args.data.isLinkPreload || false,
440-
mimeType: request.receiveResponse.args.data.mimeType,
447+
mimeType: request.receiveResponse?.args.data.mimeType ?? '',
441448
priority: finalPriority,
442449
initialPriority,
443-
protocol: request.receiveResponse.args.data.protocol ?? 'unknown',
450+
protocol: request.receiveResponse?.args.data.protocol ?? 'unknown',
444451
redirects,
445452
// In the event the property isn't set, assume non-blocking.
446453
renderBlocking: renderBlocking ?? 'non_blocking',
447454
requestId,
448455
requestingFrameUrl,
449456
requestMethod: finalSendRequest.args.data.requestMethod,
450457
resourceType: finalSendRequest.args.data.resourceType ?? Protocol.Network.ResourceType.Other,
451-
statusCode: request.receiveResponse.args.data.statusCode,
452-
responseHeaders: request.receiveResponse.args.data.headers || [],
458+
statusCode: request.receiveResponse?.args.data.statusCode ?? 0,
459+
responseHeaders: request.receiveResponse?.args.data.headers ?? null,
453460
fetchPriorityHint: finalSendRequest.args.data.fetchPriorityHint ?? 'auto',
454461
initiator: finalSendRequest.args.data.initiator,
455462
stackTrace: finalSendRequest.args.data.stackTrace,
456463
timing,
457464
url,
458465
failed: request.resourceFinish?.args.data.didFail ?? false,
459466
finished: Boolean(request.resourceFinish),
460-
connectionId: request.receiveResponse.args.data.connectionId,
461-
connectionReused: request.receiveResponse.args.data.connectionReused,
467+
hasResponse: Boolean(request.receiveResponse),
468+
connectionId: request.receiveResponse?.args.data.connectionId,
469+
connectionReused: request.receiveResponse?.args.data.connectionReused,
462470
},
463471
},
464472
cat: 'loading',

front_end/models/trace/insights/Cache.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ export function generateInsight(
197197
let totalWastedBytes = 0;
198198
const wastedBytesByRequestId = new Map<string, number>();
199199
for (const req of contextRequests) {
200-
if (!isCacheable(req)) {
200+
if (!req.args.data.responseHeaders || !isCacheable(req)) {
201201
continue;
202202
}
203203

front_end/models/trace/insights/Common.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,10 @@ export function metricSavingsForWastedBytes(
300300
* Returns whether the network request was sent encoded.
301301
*/
302302
export function isRequestCompressed(request: Types.Events.SyntheticNetworkRequest): boolean {
303+
if (!request.args.data.responseHeaders) {
304+
return false;
305+
}
306+
303307
// FYI: In Lighthouse, older devtools logs (like our test fixtures) seems to be
304308
// lower case, while modern logs are Cased-Like-This.
305309
const patterns = [

front_end/models/trace/lantern/core/NetworkAnalyzer.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,8 +161,8 @@ describe('NetworkAnalyzer', () => {
161161
const requests = await createRequests(trace);
162162
const result = NetworkAnalyzer.estimateIfConnectionWasReused(requests);
163163
const distinctConnections = Array.from(result.values()).filter(item => !item).length;
164-
assert.strictEqual(result.size, 25);
165-
assert.strictEqual(distinctConnections, 9);
164+
assert.strictEqual(result.size, 24);
165+
assert.strictEqual(distinctConnections, 8);
166166
});
167167
});
168168

front_end/models/trace/lantern/metrics/Interactive.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ describe('Metrics: Lantern TTI', function() {
3838
timing: 1122,
3939
});
4040
assert.strictEqual(result.optimisticEstimate.nodeTimings.size, 14);
41-
assert.strictEqual(result.pessimisticEstimate.nodeTimings.size, 31);
41+
assert.strictEqual(result.pessimisticEstimate.nodeTimings.size, 29);
4242
assert.isOk(result.optimisticGraph, 'should have created optimistic graph');
4343
assert.isOk(result.pessimisticGraph, 'should have created pessimistic graph');
4444
});

front_end/models/trace/lantern/metrics/LargestContentfulPaint.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,11 @@ describe('Metrics: Lantern LCP', function() {
3030
pessimisticNodeTimings: result.pessimisticEstimate.nodeTimings.size,
3131
},
3232
{
33-
timing: 1536,
33+
timing: 1457,
3434
optimistic: 1457,
35-
pessimistic: 1616,
35+
pessimistic: 1457,
3636
optimisticNodeTimings: 8,
37-
pessimisticNodeTimings: 9,
37+
pessimisticNodeTimings: 8,
3838
});
3939
assert.isOk(result.optimisticGraph, 'should have created optimistic graph');
4040
assert.isOk(result.pessimisticGraph, 'should have created pessimistic graph');

front_end/models/trace/types/TraceEvents.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -373,8 +373,9 @@ export interface SyntheticNetworkRequest extends Complete, SyntheticBased<Phase.
373373
*/
374374
encodedDataLength: number,
375375
frame: string,
376-
fromServiceWorker: boolean,
376+
fromServiceWorker: boolean|undefined,
377377
isLinkPreload: boolean,
378+
/** Empty string if no response. */
378379
mimeType: string,
379380
priority: Protocol.Network.ResourcePriority,
380381
initialPriority: Protocol.Network.ResourcePriority,
@@ -390,17 +391,21 @@ export interface SyntheticNetworkRequest extends Complete, SyntheticBased<Phase.
390391
renderBlocking: RenderBlocking,
391392
requestId: string,
392393
requestingFrameUrl: string,
394+
/** 0 if no response. */
393395
statusCode: number,
394396
resourceType: Protocol.Network.ResourceType,
395-
responseHeaders: Array<{name: string, value: string}>,
397+
responseHeaders: Array<{name: string, value: string}>|null,
396398
fetchPriorityHint: FetchPriorityHint,
397399
url: string,
398400
/** True only if got a 'resourceFinish' event indicating a failure. */
399401
failed: boolean,
400-
/** True only if got a 'resourceFinish' event. */
402+
/** True only if got a 'resourceFinish' event. Note even failed requests with no response may be "finished". */
401403
finished: boolean,
402-
connectionId: number,
403-
connectionReused: boolean,
404+
hasResponse: boolean,
405+
/** If undefined, trace was either too old or had no response. */
406+
connectionId: number|undefined,
407+
/** If undefined, trace was either too old or had no response. */
408+
connectionReused: boolean|undefined,
404409
// Optional fields
405410
initiator?: Initiator,
406411
requestMethod?: string,

front_end/panels/timeline/TimelineTreeView.test.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -293,14 +293,12 @@ describeWithEnvironment('TimelineTreeView', function() {
293293
'https://web-dev.imgix.net/image/kheDArv5csY6rvQUJDbWRscckLr1/4i7JstVZvgTFk9dxCe4a.svg',
294294
'https://web-dev.imgix.net/image/jxu1OdD7LKOGIDU7jURMpSH2lyK2/zrBPJq27O4Hs8haszVnK.svg',
295295
'https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/3164So5aDk7vKTkhx9Vm.png?auto=format&w=1140',
296-
'data:image/svg+xml,%3Csvg viewBox=\'0 0 18 18\' fill=\'%23191919\' xmlns=\'http://www.w3.org/2000/svg\'%3E%3Cpath fill-rule=\'evenodd\' clip-rule=\'evenodd\' d=\'M16 2V16H2V2H16ZM16 0H2C0.9 0 0 0.9 0 2V16C0 17.1 0.9 18 2 18H16C17.1 18 18 17.1 18 16V2C18 0.9 17.1 0 16 0Z\' /%3E%3C/svg%3E',
297296
'https://web.dev/js/app.js?v=fedf5fbe',
298297
'https://web.dev/js/home.js?v=73b0d143',
299298
'https://web.dev/js/index-7e29abb6.js',
300299
'https://web.dev/js/index-578d2db7.js',
301300
'https://web.dev/js/actions-f0eb5c8e.js',
302301
'https://web.dev/js/index-f45448ab.js',
303-
'data:image/svg+xml,%3Csvg width=\'24\' height=\'24\' viewBox=\'0 0 24 24\' fill=\'none\' xmlns=\'http://www.w3.org/2000/svg\'%3E%3Cpath d=\'M7 10L12 15L17 10H7Z\' fill=\'%235F6368\'/%3E%3C/svg%3E%0A',
304302
'https://www.googletagmanager.com/gtm.js?id=GTM-MZWCJPP',
305303
'https://www.google-analytics.com/analytics.js',
306304
'https://www.googletagmanager.com/gtag/js?id=G-18JR3Q8PJ8&l=dataLayer&cx=c',

0 commit comments

Comments
 (0)