From 1071f7205d198dce698e96937f0cd2bf61cf0b10 Mon Sep 17 00:00:00 2001 From: kim Date: Sat, 8 Aug 2026 23:43:56 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20wol-gate=20=ED=97=AC=EC=8A=A4=EC=B2=B4?= =?UTF-8?q?=ED=81=AC=EB=A5=BC=20TCP=20=EC=97=B0=EA=B2=B0=20=ED=99=95?= =?UTF-8?q?=EC=9D=B8=EC=97=90=EC=84=9C=20=EC=8B=A4=EC=A0=9C=20HTTP=20?= =?UTF-8?q?=EC=9D=91=EB=8B=B5=20=ED=99=95=EC=9D=B8=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TCP 연결만 확인하면, 대상이 부팅 중이라 포트는 열렸지만 실제 서비스가 아직 요청을 못 받는 순간에도 "살아있다"고 오판해서 실제 프록시를 시도하다 자체 타임아웃으로 "Bad gateway: read ETIMEDOUT"를 반환하는 문제가 있었음(HTML 깨우는 페이지 대신 이 502가 나가면 호출부가 재시도 로직을 못 탐) — 지서버 wol-gate에서 실제로 반복 관측됨(2026-08-08). 실제 HTTP GET으로 응답을 받는지 확인하도록 바꿔서, 상태코드 상관없이 진짜 HTTP 응답이 와야만 "살아있다"고 판단하게 수정. 두 인스턴스(클로서버 8099, 지서버 8100) 모두 재빌드·재배포 후 정상 동작 확인. Co-Authored-By: Claude Sonnet 5 --- wol-gate/server.js | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/wol-gate/server.js b/wol-gate/server.js index 6528c14..114e5c7 100644 --- a/wol-gate/server.js +++ b/wol-gate/server.js @@ -5,7 +5,6 @@ // Must run on an always-on host with L2 access to the target's broadcast // domain (host networking) so the magic packet actually reaches the target. const http = require('http'); -const net = require('net'); const dgram = require('dgram'); const krCidrs = require('./kr-cidrs.json'); @@ -140,21 +139,31 @@ function sendMagicPacket(mac, broadcastAddr, port) { }); } +// HTTP-level check, not just TCP connect: a target mid-boot can have its listener socket +// open (TCP connect succeeds) well before the actual service behind it is answering requests +// (e.g. Ollama's port opens before the model/runtime is ready) — a bare TCP check reports +// "alive" during that gap, so proxyRequest() gets sent through and times out with its own +// "Bad gateway: read ETIMEDOUT" instead of the caller seeing the (retryable) waking-up page. +// Observed live 2026-08-08 on a wol-gate-fronted Ollama target. Any real HTTP response +// (regardless of status code — even a 404 proves the app layer is serving, not just the +// socket) counts as alive; only a connection error/timeout counts as still-waking. function checkTargetAlive() { return new Promise((resolve) => { - const socket = new net.Socket(); let done = false; const finish = (alive) => { if (done) return; done = true; - socket.destroy(); resolve(alive); }; - socket.setTimeout(HEALTH_TIMEOUT_MS); - socket.once('connect', () => finish(true)); - socket.once('timeout', () => finish(false)); - socket.once('error', () => finish(false)); - socket.connect(TARGET_PORT, TARGET_HOST); + const req = http.request({ + host: TARGET_HOST, port: TARGET_PORT, method: 'GET', path: '/', timeout: HEALTH_TIMEOUT_MS, + }, (res) => { + res.destroy(); + finish(true); + }); + req.on('timeout', () => req.destroy()); + req.on('error', () => finish(false)); + req.end(); }); }