diff --git a/src/components/httprequestviewer/lib/httpRequestProcessor.ts b/src/components/httprequestviewer/lib/httpRequestProcessor.ts index 69ea4ab..a99c115 100644 --- a/src/components/httprequestviewer/lib/httpRequestProcessor.ts +++ b/src/components/httprequestviewer/lib/httpRequestProcessor.ts @@ -121,40 +121,15 @@ export const calculateDurations = (request: HTTPRequest): void => { lastDataTimestamp = request.events.finishLoading.ts } - // Chrome DevTools Duration Calculation: - // Duration = Total time from request start to completion (excludes queuing) - // This matches Chrome DevTools "Duration" column in Network tab - - if (timingData?.requestTime && timingData?.sendStart !== undefined && timingData?.receiveHeadersEnd !== undefined) { - // Chrome DevTools standard method: sendStart to receiveHeadersEnd - // This excludes queuing/DNS/connection time and measures actual network time - const networkDurationMs = timingData.receiveHeadersEnd - timingData.sendStart - request.timing.duration = networkDurationMs * 1000 // Convert to microseconds - - // If we have response body data, extend duration to last data chunk - if (lastDataTimestamp) { - const sendStartTimeUs = (timingData.requestTime * 1000000) + (timingData.sendStart * 1000) - const bodyDuration = lastDataTimestamp - sendStartTimeUs - - // Use the longer of header completion or body completion - request.timing.duration = Math.max(request.timing.duration, bodyDuration) - } - - } else if (timingData?.requestTime && timingData?.receiveHeadersEnd !== undefined) { - // Fallback: use receiveHeadersEnd from requestTime (includes all phases) - request.timing.duration = timingData.receiveHeadersEnd * 1000 // Convert to microseconds - - } else if (lastDataTimestamp && timingData?.requestTime && timingData?.sendStart !== undefined) { - // Fallback: sendStart to last data (body download completion) - const sendStartTimeUs = (timingData.requestTime * 1000000) + (timingData.sendStart * 1000) - request.timing.duration = lastDataTimestamp - sendStartTimeUs - - } else if (lastDataTimestamp && timingData?.requestTime) { - // Final fallback: requestTime to last data (includes everything) - const requestTimeUs = timingData.requestTime * 1000000 - request.timing.duration = lastDataTimestamp - requestTimeUs + // Calculate Download Time (response body transfer time) + // This is the time from first response byte to last data chunk + if (timingData?.receiveHeadersStart !== undefined && lastDataTimestamp) { + const receiveHeadersStartUs = (timingData.requestTime * 1000000) + (timingData.receiveHeadersStart * 1000) + request.timing.downloadTime = Math.max(0, lastDataTimestamp - receiveHeadersStartUs) } + // Note: Duration will be calculated after Total Response Time and Server Latency are available + // Calculate Total Response Time (wall clock time from request initiation to completion) // Use the earliest request start time (timing.start) which includes queuing, not sendRequest.ts if (request.timing.start && lastDataTimestamp) { @@ -184,6 +159,12 @@ export const calculateDurations = (request: HTTPRequest): void => { request.timing.totalResponseTime = totalTime } + // Calculate Duration as Client-Side Time (Total Response Time - Server Latency) + // This represents time spent on the client side: queuing, network setup, and download + if (request.timing.totalResponseTime && request.timing.serverLatency) { + request.timing.duration = Math.max(0, request.timing.totalResponseTime - request.timing.serverLatency) + } + // Fallback to trace event timestamps if timing data unavailable if (!request.timing.duration && request.timing.end) { request.timing.duration = request.timing.end - request.timing.start diff --git a/src/components/httprequestviewer/types/httpRequest.ts b/src/components/httprequestviewer/types/httpRequest.ts index f47865a..36d2e04 100644 --- a/src/components/httprequestviewer/types/httpRequest.ts +++ b/src/components/httprequestviewer/types/httpRequest.ts @@ -46,6 +46,7 @@ export interface HTTPRequest { end?: number duration?: number totalResponseTime?: number + downloadTime?: number queueTime?: number serverLatency?: number networkDuration?: number diff --git a/src/components/shared/tooltipDefinitions.ts b/src/components/shared/tooltipDefinitions.ts index 89677fa..1d00109 100644 --- a/src/components/shared/tooltipDefinitions.ts +++ b/src/components/shared/tooltipDefinitions.ts @@ -156,9 +156,9 @@ export const TOOLTIP_DEFINITIONS: Record = [TooltipType.REQUEST_DURATION]: { title: "Request Duration", - description: "Total time from request start to completion, including all network phases and data transfer.", - lighthouseRelation: "Long durations for critical resources directly impact LCP, FCP, and Speed Index. Background resource duration affects overall performance score.", - calculation: "responseEnd - requestStart from Navigation Timing API.", + description: "Client-side time including queuing, network setup, and download. This is Total Response Time minus Server Latency.", + lighthouseRelation: "Long durations for critical resources directly impact LCP, FCP, and Speed Index. This represents time the browser spends waiting and downloading.", + calculation: "Total Response Time - Server Latency. Includes queuing, DNS, connection, SSL, and download time.", links: [ { text: "Optimize Resource Loading", url: "https://web.dev/articles/critical-rendering-path" } ]