prideout
prideout

Reputation: 3019

Check if a WebSocket server is available without erroring

My web app needs to periodically check if the server is up. This allows users to start the server and client in either order.

To do this, I'm simply calling the WebSocket constructor from inside a setTimeout handler.

This strategy works well from a usage standpoint, but it is annoying for me the developer, because I see a lot of console errors in Chrome:

remote.html:194 WebSocket connection to 'ws://localhost:8082/' failed: Connection closed before receiving a handshake response
remote.html:194 WebSocket connection to 'ws://localhost:8082/' failed: Connection closed before receiving a handshake response
remote.html:194 WebSocket connection to 'ws://localhost:8082/' failed: Connection closed before receiving a handshake response

These are not really errors because my web app is perfectly fine with the server being unavailable. It is repeatedly calling the WebSocket constructor (MDN) merely to check if the server has been started yet.

Is there a better way to do this? For context, my web app is a "remote control" for a native C++ app that runs on a device.

Upvotes: 3

Views: 4775

Answers (4)

Jytesh
Jytesh

Reputation: 825

Websockets are just upgraded HTTP/HTTPS Endpoints, so you can do a request and check if you can upgrade

Upvotes: -1

Gershom Maes
Gershom Maes

Reputation: 8140

Errors are suppressed when using a SharedWorker! Try the following:

main.html

<!doctype html>
<html>
  <head>
    <title>WS Test</title>
    <script type="text/javascript"> (async () => {
      
      await new Promise(r => window.addEventListener('load', r));
            
      let worker = new SharedWorker('worker.js');
      worker.port.onmessage = evt => console.log('MSG:', evt.data);
      
    })() </script>
  </head>
  <body></body>
</html>

worker.js

let ports = [];
onconnect = ({ ports: [ port ] }) => ports.push(port);

(async () => {
  
  while (true) {
    
    await new Promise(r => setTimeout(r, 1000));
    
    let ws = new WebSocket('ws://localhost:9999/');
    ws.addEventListener('error', evt => ports.forEach(port => port.postMessage('error')));
    ws.addEventListener('open', evt => ports.forEach(port => port.postMessage('open')));
    
  }
  
})();

This gives me feedback that the WebSocket is hitting an Error, but doesn't produce any additional noise in the logs: console showing ws errors with no additional noise

(Note that "some messages have been moved to the issues panel" is due to the hasty standards-incompliant test html - it will go away if you transfer this solution to your own code!)

A SharedWorker has no necessary link to any particular tab - for this reason I can see no justification why a SharedWorker's errors could ever appear as noise in a tab's console in any environment.

Upvotes: 9

Syed Mohib Uddin
Syed Mohib Uddin

Reputation: 716

All error is suppressed but this cannot be below is the reason

WebSocket connection to 'ws://localhost:5000/tech-support' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED

There is no way to trap that error message, which occurs asynchronously to the code where the WebSocket object is created.

More details here: Javascript doesn't catch error in WebSocket instantiation

Upvotes: 1

vkml
vkml

Reputation: 36

Please notice that connection error messages are mostly handled by the browser itself, not JS runtime.

If you wand to explicitly suppress runtime errors, you can use onerror event handler. Example:

window.onerror = function(message, url, lineNumber) {
        // you can also filter out certain messages in this handler
        return true; // prevents all error messages from displaying
};

Also, consider to use something like setting up health check HTTP endpoint, instead of opening WS connection.

As your error messages state, your are closing connection right after opening, you should not do that, instead you can keep one WS connection open and use ping/pong WebSocket messages to check if connection is alive, thus ensuring that server is alive too. If the connection is closed, you can periodically retry to connect with setInterval, and if connection attempt is successful, reuse previous WS object variable and destroy interval with clearInterval.

Upvotes: 1

Related Questions