diff --git a/README.md b/README.md index 34e34020f..d0b1ac17e 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ It is a temporary live demo, all data will be deleted after 10 minutes. Sponsore ## ⭐ Features -- Monitoring uptime for HTTP(s) / TCP / HTTP(s) Keyword / HTTP(s) Json Query / Ping / DNS Record / Push / Steam Game Server / Docker Containers +- Monitoring uptime for HTTP(s) / TCP / HTTP(s) Keyword / HTTP(s) Json Query / Websocket / Ping / DNS Record / Push / Steam Game Server / Docker Containers - Fancy, Reactive, Fast UI/UX - Notifications via Telegram, Discord, Gotify, Slack, Pushover, Email (SMTP), and [90+ notification services, click here for the full list](https://github.com/louislam/uptime-kuma/tree/master/src/components/notifications) - 20-second intervals diff --git a/db/knex_migrations/2025-02-15-2312-add-wstest.js b/db/knex_migrations/2025-02-15-2312-add-wstest.js new file mode 100644 index 000000000..c2b640f3e --- /dev/null +++ b/db/knex_migrations/2025-02-15-2312-add-wstest.js @@ -0,0 +1,13 @@ +// Add websocket ignore headers +exports.up = function (knex) { + return knex.schema + .alterTable("monitor", function (table) { + table.boolean("ws_ignore_headers").notNullable().defaultTo(false); + }); +}; + +exports.down = function (knex) { + return knex.schema.alterTable("monitor", function (table) { + table.dropColumn("ws_ignore_headers"); + }); +}; diff --git a/server/model/monitor.js b/server/model/monitor.js index 3ad8cfafc..fecde2d24 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -96,6 +96,7 @@ class Monitor extends BeanModel { parent: this.parent, childrenIDs: preloadData.childrenIDs.get(this.id) || [], url: this.url, + wsIgnoreHeaders: this.getWsIgnoreHeaders(), method: this.method, hostname: this.hostname, port: this.port, @@ -255,6 +256,14 @@ class Monitor extends BeanModel { return Boolean(this.ignoreTls); } + /** + * Parse to boolean + * @returns {boolean} Should WS headers be ignored? + */ + getWsIgnoreHeaders() { + return Boolean(this.wsIgnoreHeaders); + } + /** * Parse to boolean * @returns {boolean} Is the monitor in upside down mode? diff --git a/server/monitor-types/websocket-upgrade.js b/server/monitor-types/websocket-upgrade.js new file mode 100644 index 000000000..aa18ef07a --- /dev/null +++ b/server/monitor-types/websocket-upgrade.js @@ -0,0 +1,50 @@ +const { MonitorType } = require("./monitor-type"); +const WebSocket = require("ws"); +const { UP, DOWN } = require("../../src/util"); + +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; + } + + /** + * 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.url); + + 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 ]); + } + // Upgrade failed, return message to user + resolve([ error.message, error.code ]); + }; + + ws.onclose = (event) => { + // Upgrade success, connection closed successfully + resolve([ "101 - OK", event.code ]); + }; + }); + } +} + +module.exports = { + WebSocketMonitorType, +}; diff --git a/server/server.js b/server/server.js index ec5ad49f6..d8aa67a38 100644 --- a/server/server.js +++ b/server/server.js @@ -790,6 +790,7 @@ let needSetup = false; bean.parent = monitor.parent; bean.type = monitor.type; bean.url = monitor.url; + bean.wsIgnoreHeaders = monitor.wsIgnoreHeaders; bean.method = monitor.method; bean.body = monitor.body; bean.headers = monitor.headers; diff --git a/server/uptime-kuma-server.js b/server/uptime-kuma-server.js index 062f098d7..f65275311 100644 --- a/server/uptime-kuma-server.js +++ b/server/uptime-kuma-server.js @@ -111,6 +111,7 @@ class UptimeKumaServer { // Set Monitor Types UptimeKumaServer.monitorTypeList["real-browser"] = new RealBrowserMonitorType(); UptimeKumaServer.monitorTypeList["tailscale-ping"] = new TailscalePing(); + UptimeKumaServer.monitorTypeList["websocket-upgrade"] = new WebSocketMonitorType(); UptimeKumaServer.monitorTypeList["dns"] = new DnsMonitorType(); UptimeKumaServer.monitorTypeList["mqtt"] = new MqttMonitorType(); UptimeKumaServer.monitorTypeList["snmp"] = new SNMPMonitorType(); @@ -549,6 +550,7 @@ module.exports = { // Must be at the end to avoid circular dependencies const { RealBrowserMonitorType } = require("./monitor-types/real-browser-monitor-type"); const { TailscalePing } = require("./monitor-types/tailscale-ping"); +const { WebSocketMonitorType } = require("./monitor-types/websocket-upgrade"); const { DnsMonitorType } = require("./monitor-types/dns"); const { MqttMonitorType } = require("./monitor-types/mqtt"); const { SNMPMonitorType } = require("./monitor-types/snmp"); diff --git a/src/lang/en.json b/src/lang/en.json index e215f1031..ff2923b58 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -86,6 +86,8 @@ "ignoreTLSError": "Ignore TLS/SSL errors for HTTPS websites", "ignoreTLSErrorGeneral": "Ignore TLS/SSL error for connection", "upsideDownModeDescription": "Flip the status upside down. If the service is reachable, it is DOWN.", + "wsIgnoreHeadersDescription": "The websocket upgrade succeeds, but the server does not reply with Sec-WebSocket-Accept header.", + "Ignore Sec-WebSocket-Accept header": "Ignore Sec-WebSocket-Accept header", "maxRedirectDescription": "Maximum number of redirects to follow. Set to 0 to disable redirects.", "Upside Down Mode": "Upside Down Mode", "Max. Redirects": "Max. Redirects", diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue index a83f91cab..c5a61f6bd 100644 --- a/src/pages/EditMonitor.vue +++ b/src/pages/EditMonitor.vue @@ -46,6 +46,10 @@ + + @@ -113,9 +117,9 @@ -
+
- +
@@ -621,6 +625,16 @@
+
+ + +
+ {{ $t("wsIgnoreHeadersDescription") }} +
+
+