From 04b322b3783a470624a0938eb09c34e0a81573e2 Mon Sep 17 00:00:00 2001 From: kappa Date: Thu, 12 Feb 2026 08:49:21 +0900 Subject: [PATCH] Request eyeball probes separately for reliable ISP block detection Split HTTP measurements: general probes for performance + eyeball-only probes (KT, LG U+) for SNI blocking detection. Ensures ISP probes are always included regardless of random probe selection. Co-Authored-By: Claude Opus 4.6 --- src/services/network-diagnostic.ts | 51 ++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 16 deletions(-) diff --git a/src/services/network-diagnostic.ts b/src/services/network-diagnostic.ts index 8f97c1d..f054d4e 100644 --- a/src/services/network-diagnostic.ts +++ b/src/services/network-diagnostic.ts @@ -244,19 +244,29 @@ interface GlobalpingRawResult { }; } +interface MeasurementRequest { + type: 'ping' | 'http'; + target: string; + eyeballOnly?: boolean; +} + async function createGlobalpingMeasurement( - type: 'ping' | 'http', - target: string + req: MeasurementRequest ): Promise { try { + const location: Record = { country: 'KR' }; + if (req.eyeballOnly) { + location.tags = ['eyeball-network']; + } + const body: Record = { - type, - target, - locations: [{ country: 'KR' }], - limit: 5, + type: req.type, + target: req.target, + locations: [location], + limit: req.eyeballOnly ? 5 : 5, }; - if (type === 'ping') { + if (req.type === 'ping') { body.measurementOptions = { packets: 3 }; } else { body.measurementOptions = { @@ -273,7 +283,8 @@ async function createGlobalpingMeasurement( if (!response.ok) { logger.warn('Globalping measurement creation failed', { - type, + type: req.type, + eyeballOnly: req.eyeballOnly, status: response.status, }); return null; @@ -314,20 +325,28 @@ async function pollGlobalpingResult(id: string): Promise const SNI_BLOCK_PATTERNS = ['ECONNRESET', 'connection reset', 'socket hang up']; async function checkKoreaProbes(domain: string): Promise { - // Create ping and HTTP measurements in parallel - const [pingId, httpId] = await Promise.all([ - createGlobalpingMeasurement('ping', domain), - createGlobalpingMeasurement('http', domain), + // Create 3 measurements in parallel: + // - ping: general probes for latency + // - HTTP general: datacenter probes for performance baseline + // - HTTP eyeball: ISP probes (KT, LG) for SNI block detection + const [pingId, httpId, httpEyeballId] = await Promise.all([ + createGlobalpingMeasurement({ type: 'ping', target: domain }), + createGlobalpingMeasurement({ type: 'http', target: domain }), + createGlobalpingMeasurement({ type: 'http', target: domain, eyeballOnly: true }), ]); - if (!pingId && !httpId) return []; + if (!pingId && !httpId && !httpEyeballId) return []; - // Poll both results in parallel - const [pingResults, httpResults] = await Promise.all([ + // Poll all results in parallel + const [pingResults, httpResults, httpEyeballResults] = await Promise.all([ pingId ? pollGlobalpingResult(pingId) : Promise.resolve([]), httpId ? pollGlobalpingResult(httpId) : Promise.resolve([]), + httpEyeballId ? pollGlobalpingResult(httpEyeballId) : Promise.resolve([]), ]); + // Combine HTTP results (eyeball + general, dedup by key) + const allHttpResults = [...httpEyeballResults, ...httpResults]; + // Merge ping and HTTP results by probe city+network const probeMap = new Map(); @@ -348,7 +367,7 @@ async function checkKoreaProbes(domain: string): Promise