From 36d50525f1d80853dd20597c521d46bea5bbe898 Mon Sep 17 00:00:00 2001 From: kappa Date: Thu, 12 Feb 2026 02:56:39 +0900 Subject: [PATCH] Fix TCP socket API and skip HTTP port testing in network diagnostic - Import connect from cloudflare:sockets instead of globalThis - Fix socket.opened / readable / writable API usage - Skip TCP test for HTTP ports (80/443) since fetch HEAD already covers them; only test user-specified non-HTTP ports (e.g. SSH, DB) Co-Authored-By: Claude Opus 4.6 --- src/agents/troubleshoot-agent.ts | 2 +- src/services/network-diagnostic.ts | 39 +++++++++++++++--------------- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/src/agents/troubleshoot-agent.ts b/src/agents/troubleshoot-agent.ts index ea2d3ef..75803ff 100644 --- a/src/agents/troubleshoot-agent.ts +++ b/src/agents/troubleshoot-agent.ts @@ -163,7 +163,7 @@ export class TroubleshootAgent extends BaseAgent { ports: { type: 'array', items: { type: 'number' }, - description: '추가로 테스트할 TCP 포트 목록 (기본: 80, 443)', + description: '추가로 TCP 연결 테스트할 포트 목록 (예: [22, 3306, 5432]). HTTP 포트(80/443)는 HTTP 체크로 커버되므로 제외됨', }, }, required: ['domain'], diff --git a/src/services/network-diagnostic.ts b/src/services/network-diagnostic.ts index d5b01b3..27b4b0e 100644 --- a/src/services/network-diagnostic.ts +++ b/src/services/network-diagnostic.ts @@ -8,6 +8,7 @@ * - TCP 포트 연결 테스트 */ +import { connect } from 'cloudflare:sockets'; import type { NetworkDiagnosticReport, DnsResolverResult, IspDnsBlockResult, HttpCheckResult, TcpCheckResult } from '../types'; import { createLogger } from '../utils/logger'; @@ -223,20 +224,17 @@ function parseDnsResponse(raw: Uint8Array): string[] { * TCP 소켓으로 DNS 서버에 질의 */ async function queryDnsTcp(serverIp: string, query: Uint8Array): Promise { - const socket = (globalThis as unknown as { - connect: (opts: { hostname: string; port: number }) => { - opened: Promise<{ readable: ReadableStream; writable: WritableStream }>; - close: () => void; - }; - }).connect({ hostname: serverIp, port: 53 }); + const socket = connect({ hostname: serverIp, port: 53 }); - const { readable, writable } = await socket.opened; + // Wait for connection to establish + await socket.opened; - const writer = writable.getWriter(); + // readable/writable are on the socket object directly + const writer = socket.writable.getWriter(); await writer.write(query); writer.releaseLock(); - const reader = readable.getReader(); + const reader = socket.readable.getReader(); const chunks: Uint8Array[] = []; let totalLength = 0; @@ -256,7 +254,7 @@ async function queryDnsTcp(serverIp: string, query: Uint8Array): Promise { // TCP Port Test (connect API — ping substitute) // ============================================================================ -const DEFAULT_PORTS = [80, 443]; +// Cloudflare Workers의 connect()는 HTTP 포트(80/443)에 직접 연결 불가 +// (프록시 차단됨). HTTP 체크로 커버되므로 TCP는 사용자 지정 포트만 테스트. +const HTTP_PORTS = new Set([80, 443, 8080, 8443]); async function checkTcpPorts(domain: string, ports?: number[]): Promise { + // 사용자가 포트를 지정하지 않으면 TCP 테스트 스킵 (HTTP 체크로 충분) + const filteredPorts = (ports || []).filter((p) => !HTTP_PORTS.has(p)); + if (filteredPorts.length === 0) return []; + // Resolve domain first to get IP for connect() const dohResult = await resolveDoh(domain); const ip = dohResult[0]?.records[0]?.value; if (!ip) { - return (ports || DEFAULT_PORTS).map((port) => ({ + return filteredPorts.map((port) => ({ host: domain, port, connected: false, @@ -375,22 +379,17 @@ async function checkTcpPorts(domain: string, ports?: number[]): Promise { + filteredPorts.map(async (port) => { const start = Date.now(); try { const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), CHECK_TIMEOUT_MS) ); - const socket = (globalThis as unknown as { - connect: (opts: { hostname: string; port: number }) => { - opened: Promise; - close: () => void; - }; - }).connect({ hostname: ip, port }); + const socket = connect({ hostname: ip, port }); await Promise.race([socket.opened, timeoutPromise]); - socket.close(); + await socket.close(); return { host: domain,