Skip to main content

angular-websocket-rpc.js

angular websockets json-rpc2 service that uses angular-websockets. JSFiddle example: https://jsfiddle.net/master5o1/bxvauLcw/

gist link

Attachment Type Size
angular-websocket-rpc.js text/javascript 3.1KiB
guid.js text/javascript 548.0B

angular-websocket-rpc.js – text/javascript, 3.1KiB

angular.module('ngWebsocketRpc', ['ngWebSocket', 'guid'])
  .factory('$rpc', ['$websocket', '$q', 'guid', function($websocket, $q, guid) {
    var seconds = 1000;
    var history = {};
    var methods = {};
    var $ws;

    var handlers = {
      onOpen: angular.noop,
      onClose: angular.noop,
      onError: angular.noop
    };

    var rpcErrors = {
      ParseError: RpcError(-32700, 'Parse error'),
      InvalidRequest: RpcError(-32600, 'Invalid Request'),
      MethodNotFound: RpcError(-32601, 'Method not found'),
      InvalidParams: RpcError(-32602, 'Invalid params'),
      InternalError: RpcError(-32603, 'Internal error'),
      ServerError: RpcError(-32000, 'Server Error'),
    };

    function open(url) {
      $ws = $websocket(url);
      $ws.onMessage(messageRouter);
    }

    function close(force) {
      return $ws.close(force);
    }

    function messageRouter(event, message) {
      message = JSON.parse(event.data);
      if (!!message.method) {
        handlerMethodCall(event, message);
        return;
      }
      handleCallResponse(event, message);
    }

    function handleCallResponse(event, message){
      var call = history[message.id];
      if (!!message.result) {
        call.deferred.resolve(message.result, message, event);
        return;
      }
      call.deferred.reject(message.error, message, event);
    }

    function handlerMethodCall(event, message) {
      var response = {
        "jsonrpc": "2.0",
        "id": message.id
      };
      var params = message.params;
      var fn = methods[message.method];

      if (typeof fn === 'function'){
        response = angular.extend(response, fn(params));
      } else {
        response = angular.extend(response, {
          error: rpcErrors.MethodNotFound
        });
      }

      var promise = $ws.send(JSON.stringify(response));
    }

    function callMethod(method, parameters) {
      var id = guid();
      var data = {
        "jsonrpc": "2.0",
        "method": method,
        "params": parameters,
        "id": id
      };
      var deferred = $q.defer();
      var send = $ws.send(JSON.stringify(data));

      history[id] = {
        call: data,
        messages: [],
        send: send,
        deferred: deferred
      };
      // cancel if somehow it doesn't make it to the websocket.
      send.then(angular.noop, deferred.reject);
      return deferred.promise;
    }

    function exposeMethod(method, handler){
      methods[method] = handler;
    }

    function RpcError(code, message) {
      return {
        error: {
          code: code,
          message: message
        }
      };
    }

    function RpcResult(result) {
      return {
        result: result
      };
    }


    function onEventHandlerFactory(eventName) {
      return function(fn){
        $ws[eventName](fn);
      };
    }

    return {
      $ws: $ws,
      open: open,
      close: close,
      onOpen: onEventHandlerFactory('onOpen'),
      onClose: onEventHandlerFactory('onClose'),
      onError: onEventHandlerFactory('onError'),
      call: callMethod,
      expose: exposeMethod,
      _: {
        Result: RpcResult,
        Errors: rpcErrors
      }
    };
  }]);


guid.js – text/javascript, 548.0B

angular.module('guid', [])
  .factory('guid', [function() {
    return function Guid() {
      function S4() {
        var num = (((1 + Math.random()) * 0x10000) | 0);
        if (!!crypto && !!crypto.getRandomValues) {
          num = crypto.getRandomValues(new Uint32Array(1))[0];
        }
        return num.toString(16).substring(1, 5);
      }
      // x = 4 characters, y = 3 characters
      return 'xx-x-4y-x-xxx'.replace(/[xy]/g, function(v) {
        return v === 'x' ? S4() : S4().substring(0, 3);
      }).toLowerCase();
    };
  }]);