npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

websockhop

v2.2.1

Published

Convenience library for WebSocket that provides automatic reconnect, periodic pinging, and request/response interactions

Downloads

88

Readme

WebSockHop

Author: Katsuyuki Ohmuro [email protected]
Mailing List: http://lists.fanout.io/mailman/listinfo/fanout-users

WebSockHop is a convenience library for WebSocket clients that provides automatic reconnect, periodic pinging, and request/response interactions. This is the kind of core functionality that every WebSocket application needs, isolated into a reusable library. The name comes from "sock hop", a type of dance.

The project is inspired by https://github.com/joewalnes/reconnecting-websocket and taken further.

License

WebSockHop is offered under the MIT license. See the COPYING file.

Features

  • Automatic reconnect. WebSockHop tries its best to maintain a connection. If it fails to connect or gets disconnected, it will retry connecting on an interval, with exponentially increasing delays between attempts.
  • Periodic pinging. WebSockHop can periodically send pings to the server, and fail the connection if a pong is not received after a timeout. This helps keep the connection fresh and resilient to network failures.
  • Request/response interactions. WebSockHop can optionally associate incoming and outgoing messages, useful when making requests to a server.
  • Browser workarounds. This library includes consideration for the various issues Arnout Kazemier discusses in his "WebSuckets" presentation. https://speakerdeck.com/3rdeden/websuckets
  • Ability to substitute the underlying socket object. This can be handy if you want to use WebSockHop with SockJS or Engine.IO. Note: for Engine.IO you'll need engine.io-as-websocket.

Installation

Browsers

For use in a browser script tag, clone this repository and build it:

git clone https://github.com/fanout/websockhop.git
cd websockhop
npm install
npm run build

The resulting file will be available at dist/websockhop.js (and dist/websockhop.min.js for a minified version).

or get the npm package:

npm install websockhop

The file will be available at node_modules/websockhop/dist/websockhop.js (and node_modules/websockhop/dist/websockhop.min.js for a minified version).

WebSockHop will become available through the WebSockHop global variable.

Node.js (and Browserify/Webpack/etc)

Add to your project using npm:

npm install websockhop --save

And then reference WebSockHop from your code file:

import WebSockHop from "websockhop";

or

const WebSockHop = require("websockhop").default;

Usage

Here's an example of sending a message to websocket.org's echo service, receiving a reply, and closing the connection:

var wsh = new WebSockHop('ws://echo.websocket.org');

console.log('connecting...');

wsh.on('opened', function () {
  console.log('connected');

  // we're connected, send a message
  wsh.send('test message');
});

wsh.on('message', function (message) {
  console.log(message);

  // we've received a reply, now disconnect
  wsh.close();
});

wsh.on('closed', function() {
  console.log('finished');
  wsh = null;
});

WebSockHop tries to keep the underlying WebSocket connection open until the application explicitly closes it. If there is a failure connecting to the server, or if an existing connection is unexpectedly disconnected, then WebSockHop will automatically attempt to reconnect. Anytime the connection is successfully established or reestablished, the "opened" event will be triggered. The above code will only finish once the entire transaction of connect->send->receive->close has executed successfully.

Formatters

Formatters are a way to handle the data being sent and received. They convert the messages into usable data formats for your application. They may also be used for request tracking and ping handling. See the following sections and formatters.js in the source for more details.

If no formatter is specified, then StringFormatter is automatically constructed and used.

Requests

WebSockHop can perform request/response interactions. Whether this feature works or not depends on the formatter in use. JsonFormatter supports it. The main reason to have WebSockHop track requests is to simplify your code, and also so it can handle timing out requests. If the connection is lost, then all outstanding requests are automatically failed.

You can even configure WebSockHop to kill the connection if a request times out. Often if one request doesn't work, none are going to work, since the problem is almost always network related.

Here's how to use a request:

var wsh = new WebSockHop('ws://echo.websocket.org');

wsh.formatter = new WebSockHop.JsonFormatter();

// timeout request after 8 seconds with no reply
wsh.defaultRequestTimeoutMsecs = 8000;

// disconnect & reconnect if the request times out
wsh.defaultDisconnectOnRequestTimeout = true;

console.log('connecting...');

wsh.on('opened', function () {
  console.log('connected');

  // we're connected, send a test request
  wsh.request({hello: 'world'}, function (response) {
    console.log('got response');
  });
});

Pings

For additional durability, pings should be enabled. Pings allow WebSockHop to detect connection unresponsiveness quickly so that it may forcibly reconnect. Ping behavior is dependent on the formatter being used. Use with StringFormatter and JsonFormatter are described below. Special-purpose formatters may not require any setup for pings.

Suppose you're using StringFormatter, and you want the client and the server to be able to ping each other by sending the string "ping" and replying with the string "pong":

wsh.formatter = new WebSockHop.StringFormatter();

// the message to periodically send
wsh.formatter.pingMessage = 'ping';

// incoming messages to eat because they are considered responses to a ping
wsh.formatter.handlePong = function (message) {
  return (message == 'pong'); // return true if message was a pong
};

// code to handle incoming pings
wsh.formatter.handlePing = function (message) {
  if (message == 'ping') {
    wsh.send('pong');
    return true; // message was a ping, and we've handled it
  } else {
    return false; // message wasn't a ping. continue processing the message normally
  }
};

If handlePong is null, then any incoming message will count as a pong, and the message will be processed normally as well (not eaten). If handlePing is null, then there will be no special handling for incoming pings.

It is possible to use a request/response interaction for pinging instead of plain messages. In this case, handlePong is not used, and instead ping responses will be matched to requests using the formatter's normal behavior. This will only work with formatters that support requests, such as JsonFormatter. Suppose you want to send requests of {type: 'ping'} for pings:

wsh.formatter = new WebSockHop.JsonFormatter();

// the request to periodically send
wsh.formatter.pingRequest = {type: 'ping'};

// code to handle incoming pings
wsh.formatter.handlePing = function (message) {
  if (message.type == 'ping') {
    wsh.send({id: message.id});
    return true; // message was a ping, and we've handled it
  } else {
    return false; // message wasn't a ping. continue processing the message normally
  }
};

Examples

Here's how to connect to a Meteor server using the DDP protocol and SockJS. The code tries its best to maintain a subscription at all times and it uses pings to detect for unresponsive connections quickly.

var wsh = new WebSockHop('http://localhost:3000', {
  createSocket: function (url) {
    return new SockJS(url);
  }
});

wsh.formatter = new WebSockHop.JsonFormatter();

// the request to periodically send
wsh.formatter.pingRequest = {msg: 'ping'};

// code to handle incoming pings
wsh.formatter.handlePing = function (message) {
  if (message.msg == 'ping') {
    wsh.send({id: message.id});
    return true;
  } else {
    return false;
  }
};

wsh.on('opened'), function () {
  // connect
  wsh.request({msg: 'connect', version: 'pre2',"support":["pre2"]}, function (reply) {
    // set up subscription
    wsh.request({msg: 'sub', name: 'all-players', params: []}, function (reply) {
      console.log('subscription success');
    });
  });
});

wsh.on('message', function (message) {
  if (message.msg == 'added') {
    // handle added event
  } else if (message.msg == 'changed') {
    // handle changed event
  } else if (message.msg == 'removed') {
    // handle removed event
  } else if (message.msg == 'ready') {
    // handle ready event
  }
});

Browser Conditions

Unfortunately, WebSockets are not always usable under all conditions. For example, attempting to use WebSockets in Safari versions before 5.1.4 with HTTP proxies will crash the entire browser tab. Additionally, some mobile providers block the use of WebSockets.

In these situations, WebSockets cannot be used. WebSockHop provides a method to detect when this is the case. Additionally, its constructor will throw an error if an instance is created.

if (WebSockHop.isAvailable()) {
    // Use WebSockHop
    var wsh = new WebSockHop('ws://localhost:3000/websocket');
    ...
} else {
    // Fall back from WebSockets to XHR/JSONP/etc.
}

Currently WebSockHop will fail the creation under the following conditions, unless the createSocket function is provided in the options.

  • The browser is very old and does not support WebSockets (e.g., IE < 10)
  • When running on Safari versions before 5.1.4.
  • When running on mobile browsers.

These conditions can be suppressed by setting the following values:

// true by default, set to false to skip Safari version check
WebSockHop.disable.oldSafari = false;

// true by default, set to false to skip mobile browser check
WebSockHop.disable.mobile = false;

Logging

All logging coming through WebSockHop is performed through the WebSockHop.logger function, which is defined in a development build as

WebSockHop.logger = function(type, message) {
    console.log("WebSockHop: " + type + "-" + message);
};

In a production (minified) build it is set to null.

The consumer of the library is free to assign any function to WebSockHop.logger.