diff --git a/server/model/monitor.js b/server/model/monitor.js index 0f54ca3a9..c3b104dab 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -769,12 +769,14 @@ class Monitor extends BeanModel { let res = await axios.request(options); if (res.data.State.Running) { - if (res.data.State.Health && res.data.State.Health.Status !== "healthy") { - bean.status = PENDING; - bean.msg = res.data.State.Health.Status; - } else { + if (res.data.State.Health.Status === "healthy") { bean.status = UP; bean.msg = res.data.State.Health ? res.data.State.Health.Status : res.data.State.Status; + } else if (res.data.State.Health.Status === "unhealthy") { + throw Error("Container State is unhealthy"); + } else { + bean.status = PENDING; + bean.msg = res.data.State.Health.Status; } } else { throw Error("Container State is " + res.data.State.Status); diff --git a/server/monitor-types/tcp.js b/server/monitor-types/tcp.js index e71973b1f..5223d2e25 100644 --- a/server/monitor-types/tcp.js +++ b/server/monitor-types/tcp.js @@ -54,12 +54,32 @@ class TCPMonitorType extends MonitorType { const preTLS = () => new Promise((resolve, reject) => { - let timeout; + let dialogTimeout; + let bannerTimeout; socket_ = net.connect(monitor.port, monitor.hostname); const onTimeout = () => { log.debug(this.name, `[${monitor.name}] Pre-TLS connection timed out`); - reject("Connection timed out"); + doReject("Connection timed out"); + }; + + const onBannerTimeout = () => { + log.debug(this.name, `[${monitor.name}] Pre-TLS timed out waiting for banner`); + // No banner. Could be a XMPP server? + socket_.write(``); + }; + + const doResolve = () => { + dialogTimeout && clearTimeout(dialogTimeout); + bannerTimeout && clearTimeout(bannerTimeout); + resolve({ socket: socket_ }); + }; + + const doReject = (error) => { + dialogTimeout && clearTimeout(dialogTimeout); + bannerTimeout && clearTimeout(bannerTimeout); + socket_.end(); + reject(error); }; socket_.on("connect", () => { @@ -70,10 +90,10 @@ class TCPMonitorType extends MonitorType { const response = data.toString(); const response_ = response.toLowerCase(); log.debug(this.name, `[${monitor.name}] Pre-TLS response: ${response}`); + clearTimeout(bannerTimeout); switch (true) { case response_.includes("start tls") || response_.includes("begin tls"): - timeout && clearTimeout(timeout); - resolve({ socket: socket_ }); + doResolve(); break; case response.startsWith("* OK") || response.match(/CAPABILITY.+STARTTLS/): socket_.write("a001 STARTTLS\r\n"); @@ -84,8 +104,16 @@ class TCPMonitorType extends MonitorType { case response.includes("250-STARTTLS"): socket_.write("STARTTLS\r\n"); break; + case response_.includes(""); + break; + case response_.includes(""): + break; default: - reject(`Unexpected response: ${response}`); + doReject(`Unexpected response: ${response}`); } }); socket_.on("error", error => { @@ -93,7 +121,8 @@ class TCPMonitorType extends MonitorType { reject(error); }); socket_.setTimeout(1000 * TIMEOUT, onTimeout); - timeout = setTimeout(onTimeout, 1000 * TIMEOUT); + dialogTimeout = setTimeout(onTimeout, 1000 * TIMEOUT); + bannerTimeout = setTimeout(onBannerTimeout, 1000 * 1.5); }); const reuseSocket = monitor.smtpSecurity === "starttls" ? await preTLS() : {}; diff --git a/test/backend-test/test-tcp.js b/test/backend-test/test-tcp.js index 46a4d12dd..98f168c24 100644 --- a/test/backend-test/test-tcp.js +++ b/test/backend-test/test-tcp.js @@ -185,4 +185,26 @@ describe("TCP Monitor", () => { regex ); }); + test("XMPP server with valid certificate (STARTTLS)", async t => { + const tcpMonitor = new TCPMonitorType(); + + const monitor = { + hostname: "xmpp.earth", + port: 5222, + smtpSecurity: "starttls", + isEnabledExpiryNotification: () => true, + handleTlsInfo: async tlsInfo => { + return tlsInfo; + }, + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + await tcpMonitor.check(monitor, heartbeat, {}); + + assert.strictEqual(heartbeat.status, UP); + }); });