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:
@@ -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'],
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user