diff --git a/server/monitor-types/websocket-upgrade.js b/server/monitor-types/websocket-upgrade.js new file mode 100644 index 000000000..308ed983a --- /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"); +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, +}; diff --git a/server/monitor-types/websocket.js b/server/monitor-types/websocket.js deleted file mode 100644 index 3e84b5b83..000000000 --- a/server/monitor-types/websocket.js +++ /dev/null @@ -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, -}; diff --git a/server/uptime-kuma-server.js b/server/uptime-kuma-server.js index dcf803270..f65275311 100644 --- a/server/uptime-kuma-server.js +++ b/server/uptime-kuma-server.js @@ -111,7 +111,7 @@ class UptimeKumaServer { // Set Monitor Types UptimeKumaServer.monitorTypeList["real-browser"] = new RealBrowserMonitorType(); UptimeKumaServer.monitorTypeList["tailscale-ping"] = new TailscalePing(); - UptimeKumaServer.monitorTypeList["websocket"] = new websocket(); + UptimeKumaServer.monitorTypeList["websocket-upgrade"] = new WebSocketMonitorType(); UptimeKumaServer.monitorTypeList["dns"] = new DnsMonitorType(); UptimeKumaServer.monitorTypeList["mqtt"] = new MqttMonitorType(); UptimeKumaServer.monitorTypeList["snmp"] = new SNMPMonitorType(); @@ -550,7 +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 { websocket } = require("./monitor-types/websocket"); +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 81adc0736..1f1b473c5 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -86,7 +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": "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.", "Upside Down Mode": "Upside Down Mode", "Max. Redirects": "Max. Redirects", diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue index 679eac298..0a418be09 100644 --- a/src/pages/EditMonitor.vue +++ b/src/pages/EditMonitor.vue @@ -47,7 +47,7 @@ HTTP(s) - Browser Engine (Chrome/Chromium) (Beta) - @@ -123,7 +123,7 @@ -