fix: wol-gate 헬스체크를 TCP 연결 확인에서 실제 HTTP 응답 확인으로 변경

TCP 연결만 확인하면, 대상이 부팅 중이라 포트는 열렸지만 실제 서비스가
아직 요청을 못 받는 순간에도 "살아있다"고 오판해서 실제 프록시를
시도하다 자체 타임아웃으로 "Bad gateway: read ETIMEDOUT"를 반환하는
문제가 있었음(HTML 깨우는 페이지 대신 이 502가 나가면 호출부가 재시도
로직을 못 탐) — 지서버 wol-gate에서 실제로 반복 관측됨(2026-08-08).
실제 HTTP GET으로 응답을 받는지 확인하도록 바꿔서, 상태코드 상관없이
진짜 HTTP 응답이 와야만 "살아있다"고 판단하게 수정. 두 인스턴스(클로서버
8099, 지서버 8100) 모두 재빌드·재배포 후 정상 동작 확인.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
kim
2026-08-08 23:43:56 +09:00
co-authored by Claude Sonnet 5
parent 6a4b3f8d68
commit 1071f7205d
+17 -8
View File
@@ -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();
});
}