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 <noreply@anthropic.com>
This commit is contained in:
kappa
2026-02-12 08:49:21 +09:00
parent 8eb89da6c0
commit 04b322b378

View File

@@ -244,19 +244,29 @@ interface GlobalpingRawResult {
}; };
} }
interface MeasurementRequest {
type: 'ping' | 'http';
target: string;
eyeballOnly?: boolean;
}
async function createGlobalpingMeasurement( async function createGlobalpingMeasurement(
type: 'ping' | 'http', req: MeasurementRequest
target: string
): Promise<string | null> { ): Promise<string | null> {
try { try {
const location: Record<string, unknown> = { country: 'KR' };
if (req.eyeballOnly) {
location.tags = ['eyeball-network'];
}
const body: Record<string, unknown> = { const body: Record<string, unknown> = {
type, type: req.type,
target, target: req.target,
locations: [{ country: 'KR' }], locations: [location],
limit: 5, limit: req.eyeballOnly ? 5 : 5,
}; };
if (type === 'ping') { if (req.type === 'ping') {
body.measurementOptions = { packets: 3 }; body.measurementOptions = { packets: 3 };
} else { } else {
body.measurementOptions = { body.measurementOptions = {
@@ -273,7 +283,8 @@ async function createGlobalpingMeasurement(
if (!response.ok) { if (!response.ok) {
logger.warn('Globalping measurement creation failed', { logger.warn('Globalping measurement creation failed', {
type, type: req.type,
eyeballOnly: req.eyeballOnly,
status: response.status, status: response.status,
}); });
return null; return null;
@@ -314,20 +325,28 @@ async function pollGlobalpingResult(id: string): Promise<GlobalpingRawResult[]>
const SNI_BLOCK_PATTERNS = ['ECONNRESET', 'connection reset', 'socket hang up']; const SNI_BLOCK_PATTERNS = ['ECONNRESET', 'connection reset', 'socket hang up'];
async function checkKoreaProbes(domain: string): Promise<GlobalpingProbeResult[]> { async function checkKoreaProbes(domain: string): Promise<GlobalpingProbeResult[]> {
// Create ping and HTTP measurements in parallel // Create 3 measurements in parallel:
const [pingId, httpId] = await Promise.all([ // - ping: general probes for latency
createGlobalpingMeasurement('ping', domain), // - HTTP general: datacenter probes for performance baseline
createGlobalpingMeasurement('http', domain), // - 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 // Poll all results in parallel
const [pingResults, httpResults] = await Promise.all([ const [pingResults, httpResults, httpEyeballResults] = await Promise.all([
pingId ? pollGlobalpingResult(pingId) : Promise.resolve([]), pingId ? pollGlobalpingResult(pingId) : Promise.resolve([]),
httpId ? pollGlobalpingResult(httpId) : 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 // Merge ping and HTTP results by probe city+network
const probeMap = new Map<string, GlobalpingProbeResult>(); const probeMap = new Map<string, GlobalpingProbeResult>();
@@ -348,7 +367,7 @@ async function checkKoreaProbes(domain: string): Promise<GlobalpingProbeResult[]
}); });
} }
for (const r of httpResults) { for (const r of allHttpResults) {
const key = `${r.probe.city}:${r.probe.network}`; const key = `${r.probe.city}:${r.probe.network}`;
const isEyeball = (r.probe.tags || []).includes('eyeball-network'); const isEyeball = (r.probe.tags || []).includes('eyeball-network');
const existing = probeMap.get(key) || { const existing = probeMap.get(key) || {