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 <noreply@anthropic.com>
This commit is contained in:
kappa
2026-02-12 02:56:39 +09:00
parent 1ebe765691
commit 36d50525f1
2 changed files with 20 additions and 21 deletions

View File

@@ -163,7 +163,7 @@ export class TroubleshootAgent extends BaseAgent<TroubleshootSession> {
ports: {
type: 'array',
items: { type: 'number' },
description: '추가로 테스트할 TCP 포트 목록 (기본: 80, 443)',
description: '추가로 TCP 연결 테스트할 포트 목록 (예: [22, 3306, 5432]). HTTP 포트(80/443)는 HTTP 체크로 커버되므로 제외됨',
},
},
required: ['domain'],

View File

@@ -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<string[]> {
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<string[
}
} finally {
reader.releaseLock();
socket.close();
await socket.close();
}
// Combine chunks
@@ -357,15 +355,21 @@ async function checkHttp(domain: string): Promise<HttpCheckResult[]> {
// 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<TcpCheckResult[]> {
// 사용자가 포트를 지정하지 않으면 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<TcpCheck
}
return Promise.all(
(ports || DEFAULT_PORTS).map(async (port) => {
filteredPorts.map(async (port) => {
const start = Date.now();
try {
const timeoutPromise = new Promise<never>((_, reject) =>
setTimeout(() => reject(new Error('timeout')), CHECK_TIMEOUT_MS)
);
const socket = (globalThis as unknown as {
connect: (opts: { hostname: string; port: number }) => {
opened: Promise<unknown>;
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,