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:
parent
fdccd59e04
commit
33cafe695c
@ -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
|
||||
|
@ -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 */}
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
67
src/components/httprequestviewer/lib/connectionUtils.ts
Normal file
67
src/components/httprequestviewer/lib/connectionUtils.ts
Normal 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
|
||||
}
|
@ -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(
|
||||
|
@ -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
|
||||
|
@ -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.",
|
||||
|
Loading…
Reference in New Issue
Block a user