Add connection number and request number columns to HTTP requests table

- Extract connectionId from Chrome DevTools trace data in ResourceSendRequest and ResourceReceiveResponse events
- Create connectionUtils to assign sequential connection numbers (1, 2, 3...) based on chronological first-seen order
- Add Connection # and Request # columns between URL and Start Time in requests table
- Implement request numbering within each connection ordered by start time
- Add comprehensive tooltips explaining connection behavior across HTTP versions:
  * HTTP/1.1: Traditional connections with 6-connection limit per domain
  * HTTP/2: Single connection with multiplexing capability
  * HTTP/3: QUIC stream groupings instead of TCP connections
- Update HTTPRequest interface with connectionId, connectionNumber, and requestNumberOnConnection fields
- Integrate connection processing into HTTPRequestViewer pipeline
- Update RequestRowDetails colSpan to accommodate new columns

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Michael Mainguy 2025-08-12 09:13:13 -05:00
parent fdccd59e04
commit 33cafe695c
8 changed files with 123 additions and 2 deletions

View File

@ -50,6 +50,7 @@ import { extractScreenshots, findUniqueScreenshots } from './lib/screenshotUtils
import { processHTTPRequests } from './lib/httpRequestProcessor'
import { analyzeCDN, analyzeQueueReason } from './lib/analysisUtils'
import { addRequestPostProcessing } from './lib/requestPostProcessor'
import { assignConnectionNumbers } from './lib/connectionUtils'
// Import types
import type { HTTPRequest, ScreenshotEvent } from './types/httpRequest'
@ -121,7 +122,8 @@ export default function HTTPRequestViewer({ traceId }: HTTPRequestViewerProps) {
const httpRequests = processHTTPRequests(traceData.traceEvents)
const sortedRequests = sortRequests(httpRequests)
const processedRequests = addRequestPostProcessing(sortedRequests, analyzeQueueReason, analyzeCDN)
return processedRequests
const requestsWithConnections = assignConnectionNumbers(processedRequests)
return requestsWithConnections
}, [traceData])
// Extract and process screenshots with SSIM analysis

View File

@ -19,7 +19,7 @@ interface RequestRowDetailsProps {
const RequestRowDetails: React.FC<RequestRowDetailsProps> = ({ request }) => {
return (
<tr key={`${request.requestId}-expanded`} className={styles.expandedRow}>
<td colSpan={18}>
<td colSpan={20}>
<div className={styles.expandedContent}>
{/* Request Details */}

View File

@ -61,6 +61,12 @@ const RequestRowSummary: React.FC<RequestRowSummaryProps> = ({
{truncateUrl(request.url)}
</a>
</td>
<td>
{request.connectionNumber || '-'}
</td>
<td>
{request.requestNumberOnConnection || '-'}
</td>
<td>
{formatDuration(request.timing.startOffset)}
</td>

View File

@ -71,6 +71,16 @@ const RequestsTable: React.FC<RequestsTableProps> = ({
URL
</Tooltip>
</th>
<th className={`${styles.tableHeaderCell} ${styles.center}`}>
<Tooltip type={TooltipType.CONNECTION_NUMBER}>
Connection #
</Tooltip>
</th>
<th className={`${styles.tableHeaderCell} ${styles.center}`}>
<Tooltip type={TooltipType.REQUEST_NUMBER}>
Request #
</Tooltip>
</th>
<th className={`${styles.tableHeaderCell} ${styles.right}`}>
<Tooltip type={TooltipType.START_TIME}>
Start Time

View File

@ -0,0 +1,67 @@
import type { HTTPRequest } from '../types/httpRequest'
/**
* Assigns connection numbers and request numbers to HTTP requests based on:
* 1. Actual connectionId from Chrome DevTools trace data
* 2. Sequential numbering (1, 2, 3...) based on chronological first-seen order
* 3. Request ordering within each connection by start time
*/
export const assignConnectionNumbers = (requests: HTTPRequest[]): HTTPRequest[] => {
// Sort all requests by start time to process in chronological order
const chronologicalRequests = [...requests].sort((a, b) => a.timing.start - b.timing.start)
// Track first-seen order of connection IDs
const connectionIdToNumber = new Map<number, number>()
let nextConnectionNumber = 1
// First pass: assign connection numbers based on first-seen order
chronologicalRequests.forEach(request => {
if (request.connectionId !== undefined && !connectionIdToNumber.has(request.connectionId)) {
connectionIdToNumber.set(request.connectionId, nextConnectionNumber++)
}
})
// Group requests by connectionId and sort within each connection by start time
const requestsByConnection = new Map<number, HTTPRequest[]>()
const requestsWithoutConnection: HTTPRequest[] = []
requests.forEach(request => {
if (request.connectionId !== undefined) {
if (!requestsByConnection.has(request.connectionId)) {
requestsByConnection.set(request.connectionId, [])
}
requestsByConnection.get(request.connectionId)!.push(request)
} else {
requestsWithoutConnection.push(request)
}
})
// Sort requests within each connection by start time
requestsByConnection.forEach(connectionRequests => {
connectionRequests.sort((a, b) => a.timing.start - b.timing.start)
})
// Assign connection numbers and request numbers
const processedRequests = requests.map(request => {
if (request.connectionId !== undefined) {
const connectionNumber = connectionIdToNumber.get(request.connectionId) || 0
const connectionRequests = requestsByConnection.get(request.connectionId) || []
const requestNumberOnConnection = connectionRequests.findIndex(r => r.requestId === request.requestId) + 1
return {
...request,
connectionNumber,
requestNumberOnConnection
}
} else {
// Handle requests without connection ID (show as unknown)
return {
...request,
connectionNumber: undefined,
requestNumberOnConnection: undefined
}
}
})
return processedRequests
}

View File

@ -43,6 +43,11 @@ export const processHTTPRequests = (traceEvents: TraceEvent[]): HTTPRequest[] =>
request.resourceType = args.data.resourceType || ''
request.priority = args.data.priority || ''
request.timing.start = Math.min(request.timing.start, event.ts)
// Extract connection ID if available
if (args.data.connectionId !== undefined) {
request.connectionId = args.data.connectionId
}
break
case 'ResourceReceiveResponse':
@ -52,6 +57,11 @@ export const processHTTPRequests = (traceEvents: TraceEvent[]): HTTPRequest[] =>
request.protocol = args.data.protocol
request.responseHeaders = args.data.headers
// Extract connection ID if available (fallback if not set in SendRequest)
if (args.data.connectionId !== undefined && !request.connectionId) {
request.connectionId = args.data.connectionId
}
// Extract content-length from response headers
if (request.responseHeaders) {
const contentLengthHeader = request.responseHeaders.find(

View File

@ -30,6 +30,9 @@ export interface HTTPRequest {
method: string
resourceType: string
priority: string
connectionId?: number
connectionNumber?: number
requestNumberOnConnection?: number
statusCode?: number
mimeType?: string
protocol?: string

View File

@ -12,6 +12,8 @@ export const TooltipType = {
CONNECTION_TIME: 'CONNECTION_TIME',
SERVER_LATENCY: 'SERVER_LATENCY',
REQUEST_URL: 'REQUEST_URL',
CONNECTION_NUMBER: 'CONNECTION_NUMBER',
REQUEST_NUMBER: 'REQUEST_NUMBER',
REQUEST_DURATION: 'REQUEST_DURATION',
DATA_RATE: 'DATA_RATE',
TOTAL_RESPONSE_TIME: 'TOTAL_RESPONSE_TIME',
@ -155,6 +157,27 @@ export const TOOLTIP_DEFINITIONS: Record<TooltipTypeValues, TooltipDefinition> =
]
},
[TooltipType.CONNECTION_NUMBER]: {
title: "Connection #",
description: "Sequential number assigned to each unique network connection based on first-seen order. HTTP/1.1 typically allows 6 connections per domain, HTTP/2 uses 1 connection with multiplexing.",
lighthouseRelation: "Connection limits can create bottlenecks in HTTP/1.1. HTTP/2 reduces this with multiplexing. HTTP/3 uses QUIC streams instead of traditional connections.",
calculation: "Based on Chrome's internal connectionId from trace data. For HTTP/3, this represents QUIC stream groupings rather than actual TCP connections.",
links: [
{ text: "HTTP Connection Management", url: "https://web.dev/articles/http-connection-management" },
{ text: "HTTP/3 and QUIC", url: "https://web.dev/articles/http3" }
]
},
[TooltipType.REQUEST_NUMBER]: {
title: "Request #",
description: "Sequential number of this request within its connection/stream group. Shows request ordering for each unique connection ID.",
lighthouseRelation: "Request ordering affects resource loading priority. In HTTP/1.1, earlier requests block later ones. HTTP/2/3 allow parallel processing.",
calculation: "Incremental counter starting from 1 for each connection ID, ordered by request start time. For HTTP/3, represents ordering within QUIC stream groups.",
links: [
{ text: "HTTP/2 Multiplexing", url: "https://web.dev/articles/http2" }
]
},
[TooltipType.REQUEST_DURATION]: {
title: "Request Duration",
description: "Client-side time including queuing, network setup, and download. This is Total Response Time minus Server Latency.",