mirror of
https://github.com/louislam/uptime-kuma.git
synced 2025-03-04 08:25:57 +00:00
tls: server: Improve heartbeat and log messages
This commit is contained in:
parent
84bbda21ab
commit
00c0563d55
1 changed files with 44 additions and 22 deletions
|
@ -4,6 +4,26 @@ const net = require("net");
|
||||||
const tls = require("tls");
|
const tls = require("tls");
|
||||||
const unescape = require("unescape-js");
|
const unescape = require("unescape-js");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encloses a string in double quotes.
|
||||||
|
* @param {string} s String to quote
|
||||||
|
* @returns {string} Quoted string
|
||||||
|
*/
|
||||||
|
function quote(s) {
|
||||||
|
return JSON.stringify(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Truncates and appends an ellipsis (…) to a string if it is longer than a threshold, and encloses
|
||||||
|
* the resulting string in double quotes.
|
||||||
|
* @param {string} s String to ellipsize and/or quote
|
||||||
|
* @returns {string} Ellipsized and/or quoted string
|
||||||
|
*/
|
||||||
|
function ellipsize(s) {
|
||||||
|
const maxLen = 32;
|
||||||
|
return quote(s.length <= maxLen ? s : `${s.substring(0, maxLen)}…`);
|
||||||
|
}
|
||||||
|
|
||||||
class TlsMonitorType extends MonitorType {
|
class TlsMonitorType extends MonitorType {
|
||||||
name = "port-tls";
|
name = "port-tls";
|
||||||
|
|
||||||
|
@ -79,17 +99,17 @@ class TlsMonitorType extends MonitorType {
|
||||||
*/
|
*/
|
||||||
processResponse(response, monitor, heartbeat) {
|
processResponse(response, monitor, heartbeat) {
|
||||||
if (response instanceof Error) {
|
if (response instanceof Error) {
|
||||||
log.info(this.name, "check failed: " + response.message);
|
log.info(this.name, "ERROR: " + response.message);
|
||||||
throw response;
|
throw response;
|
||||||
}
|
}
|
||||||
|
|
||||||
const [ success, message ] = this.checkResponseSuccess(response, monitor);
|
const [ success, message ] = this.checkResponseSuccess(response, monitor);
|
||||||
if (success) {
|
if (success) {
|
||||||
log.info(this.name, "check successful: " + message);
|
log.info(this.name, "SUCCESS: " + message);
|
||||||
heartbeat.msg = message;
|
heartbeat.msg = message;
|
||||||
heartbeat.status = UP;
|
heartbeat.status = UP;
|
||||||
} else {
|
} else {
|
||||||
log.info(this.name, "check failed: " + message);
|
log.info(this.name, "FAILURE: " + message);
|
||||||
throw new Error(message);
|
throw new Error(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -105,7 +125,7 @@ class TlsMonitorType extends MonitorType {
|
||||||
// Monitor keyword present => Check if keyword is present/absent in response, depending
|
// Monitor keyword present => Check if keyword is present/absent in response, depending
|
||||||
// on whether the 'Invert Keyword' option is enabled.
|
// on whether the 'Invert Keyword' option is enabled.
|
||||||
const keywordContained = response.includes(monitor.keyword);
|
const keywordContained = response.includes(monitor.keyword);
|
||||||
const message = keywordContained ? "Data contains keyword" : "Data does not contain keyword";
|
const message = `Keyword ${quote(monitor.keyword)} ${keywordContained ? "" : "not "}contained in response ${ellipsize(response)}`;
|
||||||
return [ keywordContained === !monitor.invertKeyword, message ];
|
return [ keywordContained === !monitor.invertKeyword, message ];
|
||||||
} else {
|
} else {
|
||||||
// No monitor keyword => Any response is considered a success.
|
// No monitor keyword => Any response is considered a success.
|
||||||
|
@ -122,11 +142,11 @@ class TlsMonitorType extends MonitorType {
|
||||||
*/
|
*/
|
||||||
async getResponseFromTlsPort(aborter, tlsSocket, request) {
|
async getResponseFromTlsPort(aborter, tlsSocket, request) {
|
||||||
if (request) {
|
if (request) {
|
||||||
log.debug(this.name, `sending request: ${JSON.stringify(request)}`);
|
log.debug(this.name, `sending request: ${quote(request)}`);
|
||||||
tlsSocket.write(request);
|
tlsSocket.write(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
return await this.readData(aborter, tlsSocket);
|
return await this.readData(aborter, tlsSocket, "request response");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -166,9 +186,10 @@ class TlsMonitorType extends MonitorType {
|
||||||
* Reads available data from the given socket.
|
* Reads available data from the given socket.
|
||||||
* @param {AbortController} aborter Abort controller used to abort the read
|
* @param {AbortController} aborter Abort controller used to abort the read
|
||||||
* @param {net.Socket | tls.TLSSocket} socket Socket instance to use
|
* @param {net.Socket | tls.TLSSocket} socket Socket instance to use
|
||||||
|
* @param {string} what Human-readable name of the data we're waiting for
|
||||||
* @returns {Promise<string>} Data read from the socket or rejected promise on error
|
* @returns {Promise<string>} Data read from the socket or rejected promise on error
|
||||||
*/
|
*/
|
||||||
readData(aborter, socket) {
|
readData(aborter, socket, what) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const cleanup = function () {
|
const cleanup = function () {
|
||||||
// Pause reading of data (i.e. emission of 'data' events), so that we don't lose
|
// Pause reading of data (i.e. emission of 'data' events), so that we don't lose
|
||||||
|
@ -181,20 +202,20 @@ class TlsMonitorType extends MonitorType {
|
||||||
};
|
};
|
||||||
|
|
||||||
const onAbort = (_) => {
|
const onAbort = (_) => {
|
||||||
log.debug(this.name, "read aborted");
|
log.debug(this.name, `reading of ${what} aborted`);
|
||||||
cleanup();
|
cleanup();
|
||||||
reject(new Error("Timeout"));
|
reject(new Error(`Timeout while reading ${what}`));
|
||||||
};
|
};
|
||||||
|
|
||||||
const onError = (error) => {
|
const onError = (error) => {
|
||||||
log.debug(this.name, `unable to read data: ${error}`);
|
log.debug(this.name, `unable to read ${what} data: ${error.message}`);
|
||||||
cleanup();
|
cleanup();
|
||||||
reject(new Error(`Failed to read data: ${error}`));
|
reject(new Error(`Failed to read ${what}: ${error.message}`));
|
||||||
};
|
};
|
||||||
|
|
||||||
const onData = (data) => {
|
const onData = (data) => {
|
||||||
const dataString = data.toString().trim();
|
const dataString = data.toString().trim();
|
||||||
log.debug(this.name, `read data: '${dataString}'`);
|
log.debug(this.name, `read ${what} data: ${quote(dataString)}`);
|
||||||
cleanup();
|
cleanup();
|
||||||
resolve(dataString);
|
resolve(dataString);
|
||||||
};
|
};
|
||||||
|
@ -212,15 +233,16 @@ class TlsMonitorType extends MonitorType {
|
||||||
* Reads available data from the given socket if it starts with a given prefix.
|
* Reads available data from the given socket if it starts with a given prefix.
|
||||||
* @param {AbortController} aborter Abort controller used to abort the read
|
* @param {AbortController} aborter Abort controller used to abort the read
|
||||||
* @param {net.Socket | tls.TLSSocket} socket Socket instance to use
|
* @param {net.Socket | tls.TLSSocket} socket Socket instance to use
|
||||||
|
* @param {string} what Human-readable name of the data we're waiting for
|
||||||
* @param {string} expected Prefix the response is expected to start with
|
* @param {string} expected Prefix the response is expected to start with
|
||||||
* @returns {Promise<string>} Data read from the socket or rejected promise if the response does
|
* @returns {Promise<string>} Data read from the socket or rejected promise if the response does
|
||||||
* not start with the prefix
|
* not start with the prefix
|
||||||
*/
|
*/
|
||||||
async expectDataStartsWith(aborter, socket, expected) {
|
async expectDataStartsWith(aborter, socket, what, expected) {
|
||||||
const data = await this.readData(aborter, socket);
|
const data = await this.readData(aborter, socket, what);
|
||||||
log.debug(this.name, `response data: '${data}', expected: '${expected}'…`);
|
log.debug(this.name, `${what} data: ${quote(data)}, expected prefix: ${quote(expected)}`);
|
||||||
if (!data.startsWith(expected)) {
|
if (!data.startsWith(expected)) {
|
||||||
throw new Error(`Unexpected response. Data: '${data}', Expected: '${expected}'…`);
|
throw new Error(`Unexpected ${what}: ${ellipsize(data)} does not start with ${quote(expected)}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,13 +254,13 @@ class TlsMonitorType extends MonitorType {
|
||||||
* @returns {Promise<void>} Rejected promise if the STARTTLS process failed
|
* @returns {Promise<void>} Rejected promise if the STARTTLS process failed
|
||||||
*/
|
*/
|
||||||
async startTls(aborter, socket, tlsOptions) {
|
async startTls(aborter, socket, tlsOptions) {
|
||||||
log.debug(this.name, `waiting for prompt ${JSON.stringify(tlsOptions.prompt)}…`);
|
log.debug(this.name, `starttls: waiting for prompt ${quote(tlsOptions.prompt)}…`);
|
||||||
await this.expectDataStartsWith(aborter, socket, tlsOptions.prompt);
|
await this.expectDataStartsWith(aborter, socket, "STARTTLS prompt", tlsOptions.prompt);
|
||||||
log.debug(this.name, `got prompt. sending STARTTLS command ${JSON.stringify(tlsOptions.command)}`);
|
log.debug(this.name, `starttls: got prompt. sending command ${quote(tlsOptions.command)}`);
|
||||||
socket.write(tlsOptions.command);
|
socket.write(tlsOptions.command);
|
||||||
log.debug(this.name, `sent command. waiting for response ${JSON.stringify(tlsOptions.response)}…`);
|
log.debug(this.name, `starttls: sent command. waiting for response ${quote(tlsOptions.response)}…`);
|
||||||
await this.expectDataStartsWith(aborter, socket, tlsOptions.response);
|
await this.expectDataStartsWith(aborter, socket, "STARTTLS response", tlsOptions.response);
|
||||||
log.debug(this.name, "got response");
|
log.debug(this.name, "starttls: got response");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Add table
Reference in a new issue