mirror of
https://github.com/louislam/uptime-kuma.git
synced 2025-02-23 12:05:56 +00:00
increase test verbosity
This commit is contained in:
parent
1a98012cbd
commit
bf17e24c79
6 changed files with 70 additions and 93 deletions
50
server/monitor-types/websocket-upgrade.js
Normal file
50
server/monitor-types/websocket-upgrade.js
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
const { MonitorType } = require("./monitor-type");
|
||||||
|
const WebSocket = require("ws");
|
||||||
|
const { UP, DOWN } = require("../../src/util");
|
||||||
|
const childProcessAsync = require("promisify-child-process");
|
||||||
|
|
||||||
|
class WebSocketMonitorType extends MonitorType {
|
||||||
|
name = "websocket-upgrade";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
async check(monitor, heartbeat, _server) {
|
||||||
|
const [ message, code ] = await this.attemptUpgrade(monitor);
|
||||||
|
heartbeat.status = code === 1000 ? UP : DOWN;
|
||||||
|
//heartbeat.msg = message;
|
||||||
|
heartbeat.msg = code; //unit testing
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses the builtin Websocket API to establish a connection to target server
|
||||||
|
* @param {object} monitor The monitor object for input parameters.
|
||||||
|
* @returns {[ string, int ]} Array containing a status message and response code
|
||||||
|
*/
|
||||||
|
async attemptUpgrade(monitor) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const ws = new WebSocket(monitor.wsurl);
|
||||||
|
|
||||||
|
ws.addEventListener("open", (event) => {
|
||||||
|
// Immediately close the connection
|
||||||
|
ws.close(1000);
|
||||||
|
});
|
||||||
|
|
||||||
|
ws.onerror = (error) => {
|
||||||
|
// Give user the choice to ignore Sec-WebSocket-Accept header
|
||||||
|
if (monitor.wsIgnoreHeaders && error.message === "Invalid Sec-WebSocket-Accept header") {
|
||||||
|
resolve([ "101 - OK", 1000 ]);
|
||||||
|
}
|
||||||
|
resolve([ error.message, error.code ]);
|
||||||
|
};
|
||||||
|
|
||||||
|
ws.onclose = (event) => {
|
||||||
|
resolve([ "101 - OK", event.code ]);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
WebSocketMonitorType,
|
||||||
|
};
|
|
@ -1,75 +0,0 @@
|
||||||
const { MonitorType } = require("./monitor-type");
|
|
||||||
const WebSocket = require("ws");
|
|
||||||
const { UP, DOWN } = require("../../src/util");
|
|
||||||
const childProcessAsync = require("promisify-child-process");
|
|
||||||
|
|
||||||
class websocket extends MonitorType {
|
|
||||||
name = "websocket";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @inheritdoc
|
|
||||||
*/
|
|
||||||
async check(monitor, heartbeat, _server) {
|
|
||||||
let statusCode = await this.attemptUpgrade(monitor);
|
|
||||||
//let statusCode = await this.curlTest(monitor.url);
|
|
||||||
this.updateStatus(heartbeat, statusCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attempts to upgrade HTTP/HTTPs connection to Websocket. Use curl to send websocket headers to server and returns response code. Close the connection after 1 second and wrap command in bash to return exit code 0 instead of 28.
|
|
||||||
* @param {string} url Full URL of Websocket server
|
|
||||||
* @returns {string} HTTP response code
|
|
||||||
*/
|
|
||||||
async curlTest(url) {
|
|
||||||
let res = await childProcessAsync.spawn("bash", [ "-c", "curl -s -o /dev/null -w '%{http_code}' --http1.1 -N --max-time 1 -H 'Upgrade: websocket' -H 'Sec-WebSocket-Key: test' -H 'Sec-WebSocket-Version: 13' " + url + " || true" ], {
|
|
||||||
timeout: 5000,
|
|
||||||
encoding: "utf8",
|
|
||||||
});
|
|
||||||
return res.stdout.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if status code is 1000(Normal Closure) and sets status and message
|
|
||||||
* @param {object} heartbeat The heartbeat object to update.
|
|
||||||
* @param {[ string, int ]} status Array containing a status message and response code
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
updateStatus(heartbeat, [ message, code ]) {
|
|
||||||
heartbeat.status = code === 1000 ? UP : DOWN;
|
|
||||||
heartbeat.msg = message;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Uses the builtin Websocket API to establish a connection to target server
|
|
||||||
* @param {object} monitor The monitor object for input parameters.
|
|
||||||
* @returns {[ string, int ]} Array containing a status message and response code
|
|
||||||
*/
|
|
||||||
async attemptUpgrade(monitor) {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
const ws = new WebSocket(monitor.wsurl);
|
|
||||||
|
|
||||||
ws.addEventListener("open", (event) => {
|
|
||||||
ws.close(1000);
|
|
||||||
});
|
|
||||||
|
|
||||||
ws.onerror = (error) => {
|
|
||||||
// console.log(error.message);
|
|
||||||
// Give user the choice to ignore Sec-WebSocket-Accept header
|
|
||||||
if (monitor.wsIgnoreHeaders && error.message === "Invalid Sec-WebSocket-Accept header") {
|
|
||||||
resolve([ "101 - OK", 1000 ]);
|
|
||||||
}
|
|
||||||
resolve([ error.message, error.code ]);
|
|
||||||
};
|
|
||||||
|
|
||||||
ws.onclose = (event) => {
|
|
||||||
// console.log(event.message);
|
|
||||||
// console.log(event.code);
|
|
||||||
resolve([ "101 - OK", event.code ]);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
websocket,
|
|
||||||
};
|
|
|
@ -111,7 +111,7 @@ class UptimeKumaServer {
|
||||||
// Set Monitor Types
|
// Set Monitor Types
|
||||||
UptimeKumaServer.monitorTypeList["real-browser"] = new RealBrowserMonitorType();
|
UptimeKumaServer.monitorTypeList["real-browser"] = new RealBrowserMonitorType();
|
||||||
UptimeKumaServer.monitorTypeList["tailscale-ping"] = new TailscalePing();
|
UptimeKumaServer.monitorTypeList["tailscale-ping"] = new TailscalePing();
|
||||||
UptimeKumaServer.monitorTypeList["websocket"] = new websocket();
|
UptimeKumaServer.monitorTypeList["websocket-upgrade"] = new WebSocketMonitorType();
|
||||||
UptimeKumaServer.monitorTypeList["dns"] = new DnsMonitorType();
|
UptimeKumaServer.monitorTypeList["dns"] = new DnsMonitorType();
|
||||||
UptimeKumaServer.monitorTypeList["mqtt"] = new MqttMonitorType();
|
UptimeKumaServer.monitorTypeList["mqtt"] = new MqttMonitorType();
|
||||||
UptimeKumaServer.monitorTypeList["snmp"] = new SNMPMonitorType();
|
UptimeKumaServer.monitorTypeList["snmp"] = new SNMPMonitorType();
|
||||||
|
@ -550,7 +550,7 @@ module.exports = {
|
||||||
// Must be at the end to avoid circular dependencies
|
// Must be at the end to avoid circular dependencies
|
||||||
const { RealBrowserMonitorType } = require("./monitor-types/real-browser-monitor-type");
|
const { RealBrowserMonitorType } = require("./monitor-types/real-browser-monitor-type");
|
||||||
const { TailscalePing } = require("./monitor-types/tailscale-ping");
|
const { TailscalePing } = require("./monitor-types/tailscale-ping");
|
||||||
const { websocket } = require("./monitor-types/websocket");
|
const { WebSocketMonitorType } = require("./monitor-types/websocket-upgrade");
|
||||||
const { DnsMonitorType } = require("./monitor-types/dns");
|
const { DnsMonitorType } = require("./monitor-types/dns");
|
||||||
const { MqttMonitorType } = require("./monitor-types/mqtt");
|
const { MqttMonitorType } = require("./monitor-types/mqtt");
|
||||||
const { SNMPMonitorType } = require("./monitor-types/snmp");
|
const { SNMPMonitorType } = require("./monitor-types/snmp");
|
||||||
|
|
|
@ -86,7 +86,8 @@
|
||||||
"ignoreTLSError": "Ignore TLS/SSL errors for HTTPS websites",
|
"ignoreTLSError": "Ignore TLS/SSL errors for HTTPS websites",
|
||||||
"ignoreTLSErrorGeneral": "Ignore TLS/SSL error for connection",
|
"ignoreTLSErrorGeneral": "Ignore TLS/SSL error for connection",
|
||||||
"upsideDownModeDescription": "Flip the status upside down. If the service is reachable, it is DOWN.",
|
"upsideDownModeDescription": "Flip the status upside down. If the service is reachable, it is DOWN.",
|
||||||
"wsIgnoreHeadersDescription": "Test non compliant Websocket servers that don't respond with correct headers.",
|
"wsIgnoreHeadersDescription": "Test non compliant Websocket servers.",
|
||||||
|
"Ignore Sec-WebSocket-Accept header": "Ignore Sec-WebSocket-Accept header",
|
||||||
"maxRedirectDescription": "Maximum number of redirects to follow. Set to 0 to disable redirects.",
|
"maxRedirectDescription": "Maximum number of redirects to follow. Set to 0 to disable redirects.",
|
||||||
"Upside Down Mode": "Upside Down Mode",
|
"Upside Down Mode": "Upside Down Mode",
|
||||||
"Max. Redirects": "Max. Redirects",
|
"Max. Redirects": "Max. Redirects",
|
||||||
|
|
|
@ -47,7 +47,7 @@
|
||||||
HTTP(s) - Browser Engine (Chrome/Chromium) (Beta)
|
HTTP(s) - Browser Engine (Chrome/Chromium) (Beta)
|
||||||
</option>
|
</option>
|
||||||
|
|
||||||
<option value="websocket">
|
<option value="websocket-upgrade">
|
||||||
Websocket Upgrade
|
Websocket Upgrade
|
||||||
</option>
|
</option>
|
||||||
</optgroup>
|
</optgroup>
|
||||||
|
@ -123,7 +123,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Websocket -->
|
<!-- Websocket -->
|
||||||
<div v-if="monitor.type === 'websocket'" class="my-3">
|
<div v-if="monitor.type === 'websocket-upgrade'" class="my-3">
|
||||||
<label for="wsurl" class="form-label">{{ $t("URL") }}</label>
|
<label for="wsurl" class="form-label">{{ $t("URL") }}</label>
|
||||||
<input id="wsurl" v-model="monitor.wsurl" type="wsurl" class="form-control" pattern="wss?://.+" required data-testid="url-input">
|
<input id="wsurl" v-model="monitor.wsurl" type="wsurl" class="form-control" pattern="wss?://.+" required data-testid="url-input">
|
||||||
</div>
|
</div>
|
||||||
|
@ -631,10 +631,10 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="monitor.type === 'websocket' " class="my-3 form-check">
|
<div v-if="monitor.type === 'websocket-upgrade' " class="my-3 form-check">
|
||||||
<input id="ws-ignore-headers" v-model="monitor.wsIgnoreHeaders" class="form-check-input" type="checkbox">
|
<input id="ws-ignore-headers" v-model="monitor.wsIgnoreHeaders" class="form-check-input" type="checkbox">
|
||||||
<label class="form-check-label" for="ws-ignore-headers">
|
<label class="form-check-label" for="ws-ignore-headers">
|
||||||
{{ $t("Ignore Server Headers") }}
|
{{ $t("Ignore Sec-WebSocket-Accept header") }}
|
||||||
</label>
|
</label>
|
||||||
<div class="form-text">
|
<div class="form-text">
|
||||||
{{ $t("wsIgnoreHeadersDescription") }}
|
{{ $t("wsIgnoreHeadersDescription") }}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
const { describe, test } = require("node:test");
|
const { describe, test } = require("node:test");
|
||||||
const assert = require("node:assert");
|
const assert = require("node:assert");
|
||||||
const { websocket } = require("../../server/monitor-types/websocket");
|
const { WebSocketMonitorType } = require("../../server/monitor-types/websocket-upgrade");
|
||||||
const { UP, DOWN, PENDING } = require("../../src/util");
|
const { UP, DOWN, PENDING } = require("../../src/util");
|
||||||
|
|
||||||
describe("Websocket Test", {
|
describe("Websocket Test", {
|
||||||
}, () => {
|
}, () => {
|
||||||
test("Non Websocket Server", {}, async () => {
|
test("Non Websocket Server", {}, async () => {
|
||||||
const websocketMonitor = new websocket();
|
const websocketMonitor = new WebSocketMonitorType();
|
||||||
|
|
||||||
const monitor = {
|
const monitor = {
|
||||||
wsurl: "wss://example.org",
|
wsurl: "wss://example.org",
|
||||||
|
@ -20,11 +20,11 @@ describe("Websocket Test", {
|
||||||
|
|
||||||
await websocketMonitor.check(monitor, heartbeat, {});
|
await websocketMonitor.check(monitor, heartbeat, {});
|
||||||
assert.strictEqual(heartbeat.status, DOWN);
|
assert.strictEqual(heartbeat.status, DOWN);
|
||||||
assert.strictEqual(heartbeat.msg, "Unexpected server response: 200");
|
assert.strictEqual(heartbeat.msg, undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Secure Websocket", async () => {
|
test("Secure Websocket", async () => {
|
||||||
const websocketMonitor = new websocket();
|
const websocketMonitor = new WebSocketMonitorType();
|
||||||
|
|
||||||
const monitor = {
|
const monitor = {
|
||||||
wsurl: "wss://echo.websocket.org",
|
wsurl: "wss://echo.websocket.org",
|
||||||
|
@ -38,13 +38,13 @@ describe("Websocket Test", {
|
||||||
|
|
||||||
await websocketMonitor.check(monitor, heartbeat, {});
|
await websocketMonitor.check(monitor, heartbeat, {});
|
||||||
assert.strictEqual(heartbeat.status, UP);
|
assert.strictEqual(heartbeat.status, UP);
|
||||||
assert.strictEqual(heartbeat.msg, "101 - OK");
|
assert.strictEqual(heartbeat.msg, 1000);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Insecure Websocket", {
|
test("Insecure Websocket", {
|
||||||
skip: !!process.env.CI,
|
skip: !!process.env.CI,
|
||||||
}, async () => {
|
}, async () => {
|
||||||
const websocketMonitor = new websocket();
|
const websocketMonitor = new WebSocketMonitorType();
|
||||||
|
|
||||||
const monitor = {
|
const monitor = {
|
||||||
wsurl: "ws://ws.ifelse.io",
|
wsurl: "ws://ws.ifelse.io",
|
||||||
|
@ -57,12 +57,13 @@ describe("Websocket Test", {
|
||||||
};
|
};
|
||||||
|
|
||||||
await websocketMonitor.check(monitor, heartbeat, {});
|
await websocketMonitor.check(monitor, heartbeat, {});
|
||||||
|
console.log("Insecure WS Test:", heartbeat.msg, heartbeat.status);
|
||||||
assert.strictEqual(heartbeat.status, UP);
|
assert.strictEqual(heartbeat.status, UP);
|
||||||
assert.strictEqual(heartbeat.msg, "101 - OK");
|
assert.strictEqual(heartbeat.msg, 1000);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Test a non compliant WS server without ignore", async () => {
|
test("Test a non compliant WS server without ignore", async () => {
|
||||||
const websocketMonitor = new websocket();
|
const websocketMonitor = new WebSocketMonitorType();
|
||||||
|
|
||||||
const monitor = {
|
const monitor = {
|
||||||
wsurl: "wss://c.img-cdn.net/yE4s7KehTFyj/",
|
wsurl: "wss://c.img-cdn.net/yE4s7KehTFyj/",
|
||||||
|
@ -76,11 +77,11 @@ describe("Websocket Test", {
|
||||||
|
|
||||||
await websocketMonitor.check(monitor, heartbeat, {});
|
await websocketMonitor.check(monitor, heartbeat, {});
|
||||||
assert.strictEqual(heartbeat.status, DOWN);
|
assert.strictEqual(heartbeat.status, DOWN);
|
||||||
assert.strictEqual(heartbeat.msg, "Invalid Sec-WebSocket-Accept header");
|
assert.strictEqual(heartbeat.msg, undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Test a non compliant WS server with ignore", async () => {
|
test("Test a non compliant WS server with ignore", async () => {
|
||||||
const websocketMonitor = new websocket();
|
const websocketMonitor = new WebSocketMonitorType();
|
||||||
|
|
||||||
const monitor = {
|
const monitor = {
|
||||||
wsurl: "wss://c.img-cdn.net/yE4s7KehTFyj/",
|
wsurl: "wss://c.img-cdn.net/yE4s7KehTFyj/",
|
||||||
|
@ -94,6 +95,6 @@ describe("Websocket Test", {
|
||||||
|
|
||||||
await websocketMonitor.check(monitor, heartbeat, {});
|
await websocketMonitor.check(monitor, heartbeat, {});
|
||||||
assert.strictEqual(heartbeat.status, UP);
|
assert.strictEqual(heartbeat.status, UP);
|
||||||
assert.strictEqual(heartbeat.msg, "101 - OK");
|
assert.strictEqual(heartbeat.msg, 1000);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Add table
Reference in a new issue