Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

I'm trying to retrieve data from a websocket using my own web server. The websocket server on the remote host runs websocket-rails. From the Websocket-rails github project page (https://github.com/websocket-rails) I used the javascript client code to put together a web socket client in a html file. That is, I copied the contents of all CoffeeScript files in /lib/assets/javascripts/websocket_rails on github to a single file and then compiled it with js2coffee.org (probably better solutions, but i'm quite a noob as you may have noticed by now). Then added a few lines to connect to the remote web socket and subscribe to a channel. (the channel part is actually why I use the Websocket-rails client, instead of a "normal" web socket connection. As I couldn't figure out how to subscribe to a channel after connecting through a "normal" websocket connection.)

so far so good. When I I use a browser and the code below, I can connect to a channel from the websocket on the remote host and the messages are send to the console.

<!DOCTYPE html>
<meta charset="utf-8" />
<title>WebSocket Test</title>

<script language="javascript" type="text/javascript">


(function() {
  var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
    __hasProp = {}.hasOwnProperty,
    __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };

  this.WebSocketRails = (function() {
    function WebSocketRails(url, use_websockets) {
      this.url = url;
      this.use_websockets = use_websockets != null ? use_websockets : true;
      this.connection_stale = __bind(this.connection_stale, this);
      this.pong = __bind(this.pong, this);
      this.supports_websockets = __bind(this.supports_websockets, this);
      this.dispatch_channel = __bind(this.dispatch_channel, this);
      this.unsubscribe = __bind(this.unsubscribe, this);
      this.subscribe_private = __bind(this.subscribe_private, this);
      this.subscribe = __bind(this.subscribe, this);
      this.dispatch = __bind(this.dispatch, this);
      this.trigger_event = __bind(this.trigger_event, this);
      this.trigger = __bind(this.trigger, this);
      this.unbind = __bind(this.unbind, this);
      this.bind = __bind(this.bind, this);
      this.connection_established = __bind(this.connection_established, this);
      this.new_message = __bind(this.new_message, this);
      this.reconnect = __bind(this.reconnect, this);
      this.callbacks = {};
      this.channels = {};
      this.queue = {};
      this.connect();
    }

    WebSocketRails.prototype.connect = function() {
      this.state = 'connecting';
      if (!(this.supports_websockets() && this.use_websockets)) {
        this._conn = new WebSocketRails.HttpConnection(this.url, this);
      } else {
        this._conn = new WebSocketRails.WebSocketConnection(this.url, this);
      }
      return this._conn.new_message = this.new_message;
    };

    WebSocketRails.prototype.disconnect = function() {
      if (this._conn) {
        this._conn.close();
        delete this._conn._conn;
        delete this._conn;
      }
      return this.state = 'disconnected';
    };

    WebSocketRails.prototype.reconnect = function() {
      var event, id, old_connection_id, _ref, _ref1;
      old_connection_id = (_ref = this._conn) != null ? _ref.connection_id : void 0;
      this.disconnect();
      this.connect();
      _ref1 = this.queue;
      for (id in _ref1) {
        event = _ref1[id];
        if (event.connection_id === old_connection_id && !event.is_result()) {
          this.trigger_event(event);
        }
      }
      return this.reconnect_channels();
    };

    WebSocketRails.prototype.new_message = function(data) {
      var event, socket_message, _i, _len, _ref, _results;
      _results = [];
      for (_i = 0, _len = data.length; _i < _len; _i++) {
        socket_message = data[_i];
        event = new WebSocketRails.Event(socket_message);
        if (event.is_result()) {
          if ((_ref = this.queue[event.id]) != null) {
            _ref.run_callbacks(event.success, event.data);
          }
          delete this.queue[event.id];
        } else if (event.is_channel()) {
          this.dispatch_channel(event);
        } else if (event.is_ping()) {
          this.pong();
        } else {
          this.dispatch(event);
        }
        if (this.state === 'connecting' && event.name === 'client_connected') {
          _results.push(this.connection_established(event.data));
        } else {
          _results.push(void 0);
        }
      }
      return _results;
    };

    WebSocketRails.prototype.connection_established = function(data) {
      this.state = 'connected';
      this._conn.setConnectionId(data.connection_id);
      this._conn.flush_queue();
      if (this.on_open != null) {
        return this.on_open(data);
      }
    };

    WebSocketRails.prototype.bind = function(event_name, callback) {
      var _base;
      if ((_base = this.callbacks)[event_name] == null) {
        _base[event_name] = [];
      }
      return this.callbacks[event_name].push(callback);
    };

    WebSocketRails.prototype.unbind = function(event_name) {
      return delete this.callbacks[event_name];
    };

    WebSocketRails.prototype.trigger = function(event_name, data, success_callback, failure_callback) {
      var event, _ref;
      event = new WebSocketRails.Event([event_name, data, (_ref = this._conn) != null ? _ref.connection_id : void 0], success_callback, failure_callback);
      return this.trigger_event(event);
    };

    WebSocketRails.prototype.trigger_event = function(event) {
      var _base, _name;
      if ((_base = this.queue)[_name = event.id] == null) {
        _base[_name] = event;
      }
      if (this._conn) {
        this._conn.trigger(event);
      }
      return event;
    };

    WebSocketRails.prototype.dispatch = function(event) {
      var callback, _i, _len, _ref, _results;
      if (this.callbacks[event.name] == null) {
        return;
      }
      _ref = this.callbacks[event.name];
      _results = [];
      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
        callback = _ref[_i];
        _results.push(callback(event.data));
      }
      return _results;
    };

    WebSocketRails.prototype.subscribe = function(channel_name, success_callback, failure_callback) {
      var channel;
      if (this.channels[channel_name] == null) {
        channel = new WebSocketRails.Channel(channel_name, this, false, success_callback, failure_callback);
        this.channels[channel_name] = channel;
        return channel;
      } else {
        return this.channels[channel_name];
      }
    };

    WebSocketRails.prototype.subscribe_private = function(channel_name, success_callback, failure_callback) {
      var channel;
      if (this.channels[channel_name] == null) {
        channel = new WebSocketRails.Channel(channel_name, this, true, success_callback, failure_callback);
        this.channels[channel_name] = channel;
        return channel;
      } else {
        return this.channels[channel_name];
      }
    };

    WebSocketRails.prototype.unsubscribe = function(channel_name) {
      if (this.channels[channel_name] == null) {
        return;
      }
      this.channels[channel_name].destroy();
      return delete this.channels[channel_name];
    };

    WebSocketRails.prototype.dispatch_channel = function(event) {
      if (this.channels[event.channel] == null) {
        return;
      }
      return this.channels[event.channel].dispatch(event.name, event.data);
    };

    WebSocketRails.prototype.supports_websockets = function() {
      return typeof WebSocket === "function" || typeof WebSocket === "object";
    };

    WebSocketRails.prototype.pong = function() {
      var pong, _ref;
      pong = new WebSocketRails.Event(['websocket_rails.pong', {}, (_ref = this._conn) != null ? _ref.connection_id : void 0]);
      return this._conn.trigger(pong);
    };

    WebSocketRails.prototype.connection_stale = function() {
      return this.state !== 'connected';
    };

    WebSocketRails.prototype.reconnect_channels = function() {
      var callbacks, channel, name, _ref, _results;
      _ref = this.channels;
      _results = [];
      for (name in _ref) {
        channel = _ref[name];
        callbacks = channel._callbacks;
        channel.destroy();
        delete this.channels[name];
        channel = channel.is_private ? this.subscribe_private(name) : this.subscribe(name);
        channel._callbacks = callbacks;
        _results.push(channel);
      }
      return _results;
    };

    return WebSocketRails;

  })();


  /*
  The Event object stores all the relevant event information.
   */

  WebSocketRails.Event = (function() {
    function Event(data, success_callback, failure_callback) {
      var attr;
      this.success_callback = success_callback;
      this.failure_callback = failure_callback;
      this.name = data[0];
      attr = data[1];
      if (attr != null) {
        this.id = attr['id'] != null ? attr['id'] : ((1 + Math.random()) * 0x10000) | 0;
        this.channel = attr.channel != null ? attr.channel : void 0;
        this.data = attr.data != null ? attr.data : attr;
        this.token = attr.token != null ? attr.token : void 0;
        this.connection_id = data[2];
        if (attr.success != null) {
          this.result = true;
          this.success = attr.success;
        }
      }
    }

    Event.prototype.is_channel = function() {
      return this.channel != null;
    };

    Event.prototype.is_result = function() {
      return typeof this.result !== 'undefined';
    };

    Event.prototype.is_ping = function() {
      return this.name === 'websocket_rails.ping';
    };

    Event.prototype.serialize = function() {
      return JSON.stringify([this.name, this.attributes()]);
    };

    Event.prototype.attributes = function() {
      return {
        id: this.id,
        channel: this.channel,
        data: this.data,
        token: this.token
      };
    };

    Event.prototype.run_callbacks = function(success, result) {
      this.success = success;
      this.result = result;
      if (this.success === true) {
        return typeof this.success_callback === "function" ? this.success_callback(this.result) : void 0;
      } else {
        return typeof this.failure_callback === "function" ? this.failure_callback(this.result) : void 0;
      }
    };

    return Event;

  })();


  /*
   Abstract Interface for the WebSocketRails client.
   */

  WebSocketRails.AbstractConnection = (function() {
    function AbstractConnection(url, dispatcher) {
      this.dispatcher = dispatcher;
      this.message_queue = [];
    }

    AbstractConnection.prototype.close = function() {};

    AbstractConnection.prototype.trigger = function(event) {
      if (this.dispatcher.state !== 'connected') {
        return this.message_queue.push(event);
      } else {
        return this.send_event(event);
      }
    };

    AbstractConnection.prototype.send_event = function(event) {
      if (this.connection_id != null) {
        return event.connection_id = this.connection_id;
      }
    };

    AbstractConnection.prototype.on_close = function(event) {
      var close_event;
      if (this.dispatcher && this.dispatcher._conn === this) {
        close_event = new WebSocketRails.Event(['connection_closed', event]);
        this.dispatcher.state = 'disconnected';
        return this.dispatcher.dispatch(close_event);
      }
    };

    AbstractConnection.prototype.on_error = function(event) {
      var error_event;
      if (this.dispatcher && this.dispatcher._conn === this) {
        error_event = new WebSocketRails.Event(['connection_error', event]);
        this.dispatcher.state = 'disconnected';
        return this.dispatcher.dispatch(error_event);
      }
    };

    AbstractConnection.prototype.on_message = function(event_data) {
      if (this.dispatcher && this.dispatcher._conn === this) {
        return this.dispatcher.new_message(event_data);
      }
    };

    AbstractConnection.prototype.setConnectionId = function(connection_id) {
      this.connection_id = connection_id;
    };

    AbstractConnection.prototype.flush_queue = function() {
      var event, _i, _len, _ref;
      _ref = this.message_queue;
      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
        event = _ref[_i];
        this.trigger(event);
      }
      return this.message_queue = [];
    };

    return AbstractConnection;

  })();


  /*
   HTTP Interface for the WebSocketRails client.
   */

  WebSocketRails.HttpConnection = (function(_super) {
    __extends(HttpConnection, _super);

    HttpConnection.prototype.connection_type = 'http';

    HttpConnection.prototype._httpFactories = function() {
      return [
        function() {
          return new XDomainRequest();
        }, function() {
          return new XMLHttpRequest();
        }, function() {
          return new ActiveXObject("Msxml2.XMLHTTP");
        }, function() {
          return new ActiveXObject("Msxml3.XMLHTTP");
        }, function() {
          return new ActiveXObject("Microsoft.XMLHTTP");
        }
      ];
    };

    function HttpConnection(url, dispatcher) {
      var e;
      this.dispatcher = dispatcher;
      HttpConnection.__super__.constructor.apply(this, arguments);
      this._url = "http://" + url;
      this._conn = this._createXMLHttpObject();
      this.last_pos = 0;
      try {
        this._conn.onreadystatechange = (function(_this) {
          return function() {
            return _this._parse_stream();
          };
        })(this);
        this._conn.addEventListener("load", this.on_close, false);
      } catch (_error) {
        e = _error;
        this._conn.onprogress = (function(_this) {
          return function() {
            return _this._parse_stream();
          };
        })(this);
        this._conn.onload = this.on_close;
        this._conn.readyState = 3;
      }
      this._conn.open("GET", this._url, true);
      this._conn.send();
    }

    HttpConnection.prototype.close = function() {
      return this._conn.abort();
    };

    HttpConnection.prototype.send_event = function(event) {
      HttpConnection.__super__.send_event.apply(this, arguments);
      return this._post_data(event.serialize());
    };

    HttpConnection.prototype._post_data = function(payload) {
      return $.ajax(this._url, {
        type: 'POST',
        data: {
          client_id: this.connection_id,
          data: payload
        },
        success: function() {}
      });
    };

    HttpConnection.prototype._createXMLHttpObject = function() {
      var e, factories, factory, xmlhttp, _i, _len;
      xmlhttp = false;
      factories = this._httpFactories();
      for (_i = 0, _len = factories.length; _i < _len; _i++) {
        factory = factories[_i];
        try {
          xmlhttp = factory();
        } catch (_error) {
          e = _error;
          continue;
        }
        break;
      }
      return xmlhttp;
    };

    HttpConnection.prototype._parse_stream = function() {
      var data, e, event_data;
      if (this._conn.readyState === 3) {
        data = this._conn.responseText.substring(this.last_pos);
        this.last_pos = this._conn.responseText.length;
        data = data.replace(/\]\]\[\[/g, "],[");
        try {
          event_data = JSON.parse(data);
          return this.on_message(event_data);
        } catch (_error) {
          e = _error;
        }
      }
    };

    return HttpConnection;

  })(WebSocketRails.AbstractConnection);


  /*
  WebSocket Interface for the WebSocketRails client.
   */

  WebSocketRails.WebSocketConnection = (function(_super) {
    __extends(WebSocketConnection, _super);

    WebSocketConnection.prototype.connection_type = 'websocket';

    function WebSocketConnection(url, dispatcher) {
      this.url = url;
      this.dispatcher = dispatcher;
      WebSocketConnection.__super__.constructor.apply(this, arguments);
      if (this.url.match(/^wss?:\/\//)) {
        console.log("WARNING: Using connection urls with protocol specified is depricated");
      } else if (window.location.protocol === 'https:') {
        this.url = "wss://" + this.url;
      } else {
        this.url = "wss://" + this.url;
      }
      this._conn = new WebSocket(this.url);
      this._conn.onmessage = (function(_this) {
        return function(event) {
          var event_data;
          event_data = JSON.parse(event.data);
          return _this.on_message(event_data);
        };
      })(this);
      this._conn.onclose = (function(_this) {
        return function(event) {
          return _this.on_close(event);
        };
      })(this);
      this._conn.onerror = (function(_this) {
        return function(event) {
          return _this.on_error(event);
        };
      })(this);
    }

    WebSocketConnection.prototype.close = function() {
      return this._conn.close();
    };

    WebSocketConnection.prototype.send_event = function(event) {
      WebSocketConnection.__super__.send_event.apply(this, arguments);
      return this._conn.send(event.serialize());
    };

    return WebSocketConnection;

  })(WebSocketRails.AbstractConnection);


  /*
  The channel object is returned when you subscribe to a channel.

  For instance:
    var dispatcher = new WebSocketRails('localhost:3000/websocket');
    var awesome_channel = dispatcher.subscribe('awesome_channel');
    awesome_channel.bind('event', function(data) { console.log('channel event!'); });
    awesome_channel.trigger('awesome_event', awesome_object);

  If you want to unbind an event, you can use the unbind function :
    awesome_channel.unbind('event')
   */

  WebSocketRails.Channel = (function() {
    function Channel(name, _dispatcher, is_private, on_success, on_failure) {
      var event, event_name, _ref;
      this.name = name;
      this._dispatcher = _dispatcher;
      this.is_private = is_private != null ? is_private : false;
      this.on_success = on_success;
      this.on_failure = on_failure;
      this._failure_launcher = __bind(this._failure_launcher, this);
      this._success_launcher = __bind(this._success_launcher, this);
      this._callbacks = {};
      this._token = void 0;
      this._queue = [];
      if (this.is_private) {
        event_name = 'websocket_rails.subscribe_private';
      } else {
        event_name = 'websocket_rails.subscribe';
      }
      this.connection_id = (_ref = this._dispatcher._conn) != null ? _ref.connection_id : void 0;
      event = new WebSocketRails.Event([
        event_name, {
          data: {
            channel: this.name
          }
        }, this.connection_id
      ], this._success_launcher, this._failure_launcher);
      this._dispatcher.trigger_event(event);
    }

    Channel.prototype.destroy = function() {
      var event, event_name, _ref;
      if (this.connection_id === ((_ref = this._dispatcher._conn) != null ? _ref.connection_id : void 0)) {
        event_name = 'websocket_rails.unsubscribe';
        event = new WebSocketRails.Event([
          event_name, {
            data: {
              channel: this.name
            }
          }, this.connection_id
        ]);
        this._dispatcher.trigger_event(event);
      }
      return this._callbacks = {};
    };

    Channel.prototype.bind = function(event_name, callback) {
      var _base;
      if ((_base = this._callbacks)[event_name] == null) {
        _base[event_name] = [];
      }
      return this._callbacks[event_name].push(callback);
    };

    Channel.prototype.unbind = function(event_name) {
      return delete this._callbacks[event_name];
    };

    Channel.prototype.trigger = function(event_name, message) {
      var event;
      event = new WebSocketRails.Event([
        event_name, {
          channel: this.name,
          data: message,
          token: this._token
        }, this.connection_id
      ]);
      if (!this._token) {
        return this._queue.push(event);
      } else {
        return this._dispatcher.trigger_event(event);
      }
    };

    Channel.prototype.dispatch = function(event_name, message) {
      var callback, _i, _len, _ref, _ref1, _results;
      if (event_name === 'websocket_rails.channel_token') {
        this.connection_id = (_ref = this._dispatcher._conn) != null ? _ref.connection_id : void 0;
        this._token = message['token'];
        return this.flush_queue();
      } else {
        if (this._callbacks[event_name] == null) {
          return;
        }
        _ref1 = this._callbacks[event_name];
        _results = [];
        for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
          callback = _ref1[_i];
          _results.push(callback(message));
        }
        return _results;
      }
    };

    Channel.prototype._success_launcher = function(data) {
      if (this.on_success != null) {
        return this.on_success(data);
      }
    };

    Channel.prototype._failure_launcher = function(data) {
      if (this.on_failure != null) {
        return this.on_failure(data);
      }
    };

    Channel.prototype.flush_queue = function() {
      var event, _i, _len, _ref;
      _ref = this._queue;
      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
        event = _ref[_i];
        this._dispatcher.trigger_event(event);
      }
      return this._queue = [];
    };

    return Channel;

  })();

}).call(this);

//// Below is the code I added, the rest is compiled coffeeScript from the WebSocket-Rails github repository


testWebSocket = function() {
  var channelTicker, websocket;
  websocket = new WebSocketRails(wsUri);
  websocket.on_open = function(data) {
    console.log("Socket opened on " + wsUri + " :)");
  };
  channelTicker = websocket.subscribe("ticker");
  channelTicker.bind("ticker.new", function(data) {
    console.log(data);
  });
};

wsUri = "ws.remotehost.com:3456/websocket";

testWebSocket();




</script>



<h2>WebSocket Test</h2>
<p>all output in console</p>

So after this I thought, nice, lets figure out how to use a server to open the web socket connection and store all output in a mysql database. But this was a bit harder then I expected... I tried node.js to run the javascript from my html file on my web server. that doesn't work. It throws an error:

WebSocketRails.Event = (function() {
^
ReferenceError: WebSocketRails is not defined
    at Object.<anonymous> (/Users/user/Desktop/tmp/wsBrowserClient.js:242:1)
    at Module._compile (module.js:456:26)
    at Object.Module._extensions..js (module.js:474:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Function.Module.runMain (module.js:497:10)
    at startup (node.js:119:16)
    at node.js:906:3

As far as I understand it now, the big difference between running js in the browser and in node.js is having or not having a DOM. The browser assigns variables to the window object but node.js can't do this.

So I have to change the javascript code to "node.js" javascript but can't find out where I have to start. My best guess would be with line

    this.WebSocketRails = (function() {

but to what should I change it? Hope someone can give me a hand. If someone can tell my how to subscribe to a channel with a "normal" webwocket and then use a web socket client written for node js, of course that would also be fine.

Or a way to 'just' run the code as it is now server-side with another method, that would also help.

To be clear I'm a noob at javascript. Do know a thing or two about php, but that don't help me much here...

BIG thanks for any help!

share|improve this question
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Browse other questions tagged or ask your own question.