thoov/ember-websockets

View on GitHub
addon/services/websockets.js

Summary

Maintainability
A
0 mins
Test Coverage
import Service from '@ember/service';
import { set, get, computed } from '@ember/object';
import WebsocketProxy from '../helpers/websocket-proxy';
import { normalizeURL, cleanURL } from '../helpers';

export default class WebSocketService extends Service {
  /*
    A hash of open websocket connections. This
    allows multiple components to share the same connection.

    {
      'websocket-url': WebSocket Proxy object
    }
  */
  @computed()
  get sockets() {
    return {};
  }

  /*
    socketFor returns a websocket proxy object. On this object there is a property `socket`
    which contains the actual websocket object. This websocket object is cached based off of the url meaning
    multiple requests for the same socket will return the same object.
  */
  socketFor(url, protocols = []) {
    /*
      Websockets allows either a string or array of strings to be passed as the second argument.
      Normalize both cases into an array of strings so we can just deal with arrays.
    */
    if (typeof protocols === 'string') {
      protocols = [protocols];
    }

    /*
      Normalize the url as native websockets add a / to the end of the url:
      http://example.com:8000 becomes: http://example.com:8000/

      Since the url will be used as a key will need to make sure that it does not
      contain '.' as it will throw ember off
    */
    const normalizedUrl = normalizeURL(url);
    const cleanedUrl = cleanURL(normalizedUrl);

    /*

    */
    const existingProxy = get(this, `sockets.${cleanedUrl}`);

    if (existingProxy && this.isWebSocketOpen(existingProxy.socket)) {
      return existingProxy;
    }

    /*
      we can get to this place if the websocket has been closed and we are trying to reopen
      or we are creating a proxy for the first time
    */
    const newWebSocket = this.createSocket(normalizedUrl, protocols);

    if (existingProxy) {
      /*
        If there is an existing socket in place we simply update the websocket object and not
        the whole proxy as we dont want to destroy the previous listeners.
      */
      set(existingProxy, 'socket', newWebSocket);
      return existingProxy;
    }

    const newProxy = this.createProxy(newWebSocket, protocols);

    set(this, `sockets.${cleanedUrl}`, newProxy);

    return newProxy;
  }

  closeSocketFor(url) {
    const cleanedUrl = cleanURL(normalizeURL(url));
    const existingSocket = get(this, `sockets.${cleanedUrl}`);
    if (existingSocket) {
      existingSocket.socket.close();
    }
    delete this.sockets[cleanedUrl];
  }

  isWebSocketOpen(websocket) {
    return websocket.readyState !== WebSocket.CLOSED;
  }

  createSocket(url, options) {
    return new WebSocket(url, options);
  }

  createProxy(socket, protocols) {
    return WebsocketProxy.create({ content: this, protocols, socket });
  }
}