11const { MonitorType } = require ( "./monitor-type" ) ;
22const WebSocket = require ( "ws" ) ;
33const { UP } = require ( "../../src/util" ) ;
4+ const { checkStatusCode } = require ( "../util-server" ) ;
5+ // Define closing error codes https://www.iana.org/assignments/websocket/websocket.xml#close-code-number
6+ const WS_ERR_CODE = {
7+ 1002 : "Protocol error" ,
8+ 1003 : "Unsupported Data" ,
9+ 1005 : "No Status Received" ,
10+ 1006 : "Abnormal Closure" ,
11+ 1007 : "Invalid frame payload data" ,
12+ 1008 : "Policy Violation" ,
13+ 1009 : "Message Too Big" ,
14+ 1010 : "Mandatory Extension Missing" ,
15+ 1011 : "Internal Error" ,
16+ 1012 : "Service Restart" ,
17+ 1013 : "Try Again Later" ,
18+ 1014 : "Bad Gateway" ,
19+ 1015 : "TLS Handshake Failed" ,
20+ 3000 : "Unauthorized" ,
21+ 3003 : "Forbidden" ,
22+ 3008 : "Timeout" ,
23+ } ;
424
525class WebSocketMonitorType extends MonitorType {
626 name = "websocket-upgrade" ;
@@ -11,43 +31,56 @@ class WebSocketMonitorType extends MonitorType {
1131 async check ( monitor , heartbeat , _server ) {
1232 const [ message , code ] = await this . attemptUpgrade ( monitor ) ;
1333
14- if ( code === 1000 ) {
15- heartbeat . status = UP ;
16- heartbeat . msg = message ;
17- } else {
18- throw new Error ( message ) ;
34+ if ( typeof code !== "undefined" ) {
35+ // If returned status code matches user controlled accepted status code(default 1000), return success
36+ if ( checkStatusCode ( code , JSON . parse ( monitor . accepted_statuscodes_json ) ) ) {
37+ heartbeat . status = UP ;
38+ heartbeat . msg = message ;
39+ return ; // success at this point
40+ }
41+
42+ // Throw an error using friendly name if defined, fallback to generic msg
43+ throw new Error ( WS_ERR_CODE [ code ] || `Unexpected status code: ${ code } ` ) ;
44+ }
45+ // If no close code, then an error has occurred, display to user
46+ if ( typeof message !== "undefined" ) {
47+ throw new Error ( `${ message } ` ) ;
1948 }
49+ // Throw generic error if nothing is defined, should never happen
50+ throw new Error ( "Unknown Websocket Error" ) ;
2051 }
2152
2253 /**
23- * Uses the builtin Websocket API to establish a connection to target server
54+ * Uses the ws Node.js library to establish a connection to target server
2455 * @param {object } monitor The monitor object for input parameters.
2556 * @returns {[ string, int ] } Array containing a status message and response code
2657 */
2758 async attemptUpgrade ( monitor ) {
2859 return new Promise ( ( resolve ) => {
29- let ws ;
30- //If user selected a subprotocol, sets Sec-WebSocket-Protocol header. Subprotocol Identifier column: https://www.iana.org/assignments/websocket/websocket.xml#subprotocol-name
31- ws = monitor . wsSubprotocol === "" ? new WebSocket ( monitor . url ) : new WebSocket ( monitor . url , monitor . wsSubprotocol ) ;
60+ const timeoutMs = ( monitor . timeout ?? 20 ) * 1000 ;
61+ // If user inputs subprotocol(s), convert to array, set Sec-WebSocket-Protocol header, timeout in ms. Subprotocol Identifier column: https://www.iana.org/assignments/websocket/websocket.xml#subprotocol-name
62+ const subprotocol = monitor . wsSubprotocol ? monitor . wsSubprotocol . replace ( / \s / g, "" ) . split ( "," ) : undefined ;
63+ const ws = new WebSocket ( monitor . url , subprotocol , { handshakeTimeout : timeoutMs } ) ;
3264
3365 ws . addEventListener ( "open" , ( event ) => {
3466 // Immediately close the connection
3567 ws . close ( 1000 ) ;
3668 } ) ;
3769
3870 ws . onerror = ( error ) => {
39- // Give user the choice to ignore Sec-WebSocket-Accept header
71+ // Give user the choice to ignore Sec-WebSocket-Accept header for non compliant servers
72+ // Header in HTTP 101 Switching Protocols response from server, technically already upgraded to WS
4073 if ( monitor . wsIgnoreSecWebsocketAcceptHeader && error . message === "Invalid Sec-WebSocket-Accept header" ) {
41- resolve ( [ "101 - OK" , 1000 ] ) ;
74+ resolve ( [ "1000 - OK" , 1000 ] ) ;
4275 return ;
4376 }
4477 // Upgrade failed, return message to user
4578 resolve ( [ error . message , error . code ] ) ;
4679 } ;
4780
4881 ws . onclose = ( event ) => {
49- // Upgrade success, connection closed successfully
50- resolve ( [ "101 - OK", event . code ] ) ;
82+ // Return the close code, if connection didn't close cleanly, return the reason if present
83+ resolve ( [ event . wasClean ? event . code . toString ( ) + " - OK" : event . reason , event . code ] ) ;
5184 } ;
5285 } ) ;
5386 }
0 commit comments