From 5d4f149c64edd165308ad641018192d10ab096b2 Mon Sep 17 00:00:00 2001 From: Simon Knott Date: Tue, 17 Sep 2024 09:07:06 +0200 Subject: [PATCH] encapsulate details in happy-eyeballs --- packages/playwright-core/src/server/fetch.ts | 7 ++++--- .../playwright-core/src/utils/happy-eyeballs.ts | 13 +++++++++++-- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/packages/playwright-core/src/server/fetch.ts b/packages/playwright-core/src/server/fetch.ts index 3aebb26d01..6cefc87eb9 100644 --- a/packages/playwright-core/src/server/fetch.ts +++ b/packages/playwright-core/src/server/fetch.ts @@ -30,7 +30,7 @@ import { HttpsProxyAgent, SocksProxyAgent } from '../utilsBundle'; import { BrowserContext, verifyClientCertificates } from './browserContext'; import { CookieStore, domainMatches } from './cookieStore'; import { MultipartFormData } from './formData'; -import { httpHappyEyeballsAgent, httpsHappyEyeballsAgent } from '../utils/happy-eyeballs'; +import { httpHappyEyeballsAgent, httpsHappyEyeballsAgent, timingForSocket } from '../utils/happy-eyeballs'; import type { CallMetadata } from './instrumentation'; import { SdkObject } from './instrumentation'; import type { Playwright } from './playwright'; @@ -488,8 +488,9 @@ export abstract class APIRequestContext extends SdkObject { request.on('socket', socket => { // happy eyeballs don't emit lookup and connect events, so we use our custom ones - dnsLookupAt = (socket as any).dnsLookupAt; - tcpConnectionAt = (socket as any).tcpConnectionAt; + const happyEyeBallsTimings = timingForSocket(socket); + dnsLookupAt = happyEyeBallsTimings.dnsLookupAt; + tcpConnectionAt = happyEyeBallsTimings.tcpConnectionAt; // non-happy-eyeballs sockets socket.on('lookup', () => { dnsLookupAt = monotonicTime(); }); diff --git a/packages/playwright-core/src/utils/happy-eyeballs.ts b/packages/playwright-core/src/utils/happy-eyeballs.ts index 72917cdc5c..18de1a938c 100644 --- a/packages/playwright-core/src/utils/happy-eyeballs.ts +++ b/packages/playwright-core/src/utils/happy-eyeballs.ts @@ -29,6 +29,9 @@ import { monotonicTime } from './time'; // Same as in Chromium (https://source.chromium.org/chromium/chromium/src/+/5666ff4f5077a7e2f72902f3a95f5d553ea0d88d:net/socket/transport_connect_job.cc;l=102) const connectionAttemptDelayMs = 300; +const kDNSLookupAt = Symbol('kDNSLookupAt') +const kTCPConnectionAt = Symbol('kTCPConnectionAt') + class HttpHappyEyeballsAgent extends http.Agent { createConnection(options: http.ClientRequestArgs, oncreate?: (err: Error | null, socket?: net.Socket) => void): net.Socket | undefined { // There is no ambiguity in case of IP address. @@ -134,12 +137,12 @@ export async function createConnectionAsync( port: options.port as number, host: address }); - (socket as any).dnsLookupAt = dnsLookupAt; + (socket as any)[kDNSLookupAt] = dnsLookupAt; // Each socket may fire only one of 'connect', 'timeout' or 'error' events. // None of these events are fired after socket.destroy() is called. socket.on('connect', () => { - (socket as any).tcpConnectionAt = monotonicTime(); + (socket as any)[kTCPConnectionAt] = monotonicTime(); connected.resolve(); oncreate?.(null, socket); @@ -195,3 +198,9 @@ function clientRequestArgsToHostName(options: http.ClientRequestArgs): string { throw new Error('Either options.hostname or options.host must be provided'); } +export function timingForSocket(socket: net.Socket | tls.TLSSocket) { + return { + dnsLookupAt: (socket as any)[kDNSLookupAt] as number | undefined, + tcpConnectionAt: (socket as any)[kTCPConnectionAt] as number | undefined, + } +}