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:
+17
-8
@@ -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();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user