Call specific functions with Websockets? - javascript

I'm trying to deploy an app with Heroku but I guess they don't allow use of Socket.IO so I have to use basic WebSockets. How do I send data to specific functions e.g.
With sockets it would be:
// Server
socket.on('testFunction', function(data) {
console.log(data);
});
// Client
socket.emit('testFunction', 'Hello!');
But with Websockets from what I've googled all I can find is
// Server
ws.onmessage = function(data) {};
// Client
ws.send('This is a string, what do I do with myself');
Any information would be great. Thanks!

See here for some documentation and examples.
Here are some rough equivalents to your Socket.IO examples:
// Server
ws.onmessage = function(event) {
var msg = JSON.parse(event.data);
switch(msg.type) {
case "testFunction":
console.log(msg.text);
break;
}
};
// Client
ws.send(JSON.stringify({type: "testFunction", text: "Hello!"}));

The solution to your problem depends on your server side code.
The way i solved the function calling problem, is by sending my data to server as an object( stringified), containing controller and action properties.
e.g in pseudo code
var request = {controller: "users", action: "login", params: {username: "loginuser", password: "333"} };
socket.emit(JSON.stringify( request) );
and on server side i get the controller value, and instantiate a new controller based on it...eg new users_controller(), and call the action on that controller with the params sent from emit.
Of course you need to test if controllers and actions exists, handle errors etc.

Related

Websockets in python and js

I am currently creating a website and I'm totally confused about Websockets.
I have some data in a database that is shown on my website. Now every once in a while there are new entries in the database, which should be shown on the website without reloading it, now I thought this could somehow be achieved using websockets.
I'm using web.py as framework for my website, and I use AngularJS.
In my app.py, I recieve the database entries and return them as JSON.
In js I want to receive the JSON message and save it in the $scope, which then gets "printed" on the website using AngularJS and I created a client side WebSocket for it like this:
var app = angular.module('web');
app.factory('runservice', function() {
var service = {};
service.connect = function() {
if(service.ws) { return; }
var ws = new WebSocket('wss://localhost:8080');
ws.onopen = function() {
service.callback("Success");
};
ws.onerror = function(evt) {
service.callback("Error: " + evt.data);
}
ws.onmessage = function(message) {
service.callback(message.data);
};
service.ws = ws;
}
service.subscribe = function(callback) {
service.callback = callback;
}
return service;
});
app.controller('runController', function($scope, runservice) {
runservice.connect();
runservice.subscribe(function(message) {
var data = JSON.parse(message);
$scope.runs = data;
});
});
Now, do I need a server side socket in my app.py or something else? If so, can anyone provide an example how I'd achieve this in web.py?
You definitely need to have websocket code on your server, or else your client isn't keeping a connection alive with your server and vice versa.
If your wish is to make use of realtime websockets, then this package for your web.py application server https://github.com/songdi/webpy-socketio will be very useful, as will this for you angular client application https://github.com/btford/angular-socket-io
Another option would be to simply long poll your server. AKA make asynchronous requests every ~10 seconds or so to your application and retrieve only the newest entries.
I hope this is of some help!

Exception when using a server route and onBeforeAction

I'm seeing strange behavior when trying to add pdf file generation.
The following code, on the if statement, throws:
both\routes.js
Router.onBeforeAction(function () { if (!Meteor.user() || Meteor.loggingIn()) {
this.redirect('welcome.view'); } else {
Meteor.call("userFileDirectory", function (error, result) {
if (error)
throw error;
else
console.log(result);
});
this.next(); } }, { except: ['welcome.view'] });
Error: Meteor.userId can only be invoked in method calls. Use
this.userId in publish functions. at Object.Meteor.userId
(packages/accounts-base/accounts_server.js:19:1) at Object.Meteor.user
(packages/accounts-base/accounts_server.js:24:1) at [object
Object].Router.onBeforeAction.except
(app/both/3-router/routes.js:10:15) at
packages/iron:router/lib/router.js:277:1 at [object
Object]._.extend.withValue (packages/meteor/dynamics_nodejs.js:56:1)
at [object Object].hookWithOptions
(packages/iron:router/lib/router.js:276:1) at boundNext
(packages/iron:middleware-stack/lib/middleware_stack.js:251:1) at
runWithEnvironment (packages/meteor/dynamics_nodejs.js:108:1) at
packages/meteor/dynamics_nodejs.js:121:1 at [object Object].dispatch
(packages/iron:middleware-stack/lib/middleware_stack.js:275:1)
Only when I add this code into the file, and the /pdf route is taken:
Router.route('/pdf', function() {
var filePath = process.env.PWD + "/server/.files/users/test.pdf";
console.log(filePath);
var fs = Npm.require('fs');
var data = fs.readFileSync(filePath);
this.response.write(data);
this.response.end();
}, {
where: 'server'
});
The above code works fine; the pdf is rendered to the screen and no exception is thrown, when I take out the onBeforeAction code.
The opposite is also true, if I take out the server route, there is no route that causes an exception.
This occurs because the route you're using is a server side route. The technique Meteor uses to authenticate a user is done via the DDP protocol, over websockets.
When your browser makes a GET/POST request to the server it doesn't have any information regarding the user's authentication state.
You use Meteor.user() in your Route.onBeforeAction but it has no access to this information.
The solution to this is find an alternative way to authenticate the user. One such method is to use cookie's.
This is known issue with Meteor's authentication system, see: https://github.com/EventedMind/iron-router/issues/649
A better way than cookies could be a named collection of Meteor that stores userId and some sessionId:
You can store current userId on the client side before the call to the server:
var sessionId = Random.id();
col = new Mongo.Collection('session');
col.insert({
sessionId: sid,
userId: Meteor.userId(),
issued: new Date()
});
And then pass sessionId to the server through a GET/POST request and read it on the server:
var sid = this.request.query.sid;
var user = col.findOne({sessionId: sid}); // returns an object
Using a separate parameter is better than using userId itself because you can revoke this sessionId after some time or immediately after the server call.
Proper allow/deny permissions are required to prevent anyone from updating the collection. Also, please note that you can't trust new Date() on the client's side.

Unit testing of websocket application by sinon.js

I have tried to do unit testing for a web socket application using sinon.js,
One of the users on github of sinon, did this, but I am not able to understand how it does help to unit test websocket applications for validating the received data which was sent to fake server.
var dummySocket = { send : sinon.spy()};
sinon.stub(window, 'WebSocket').returns(dummySocket);
dummySocket = new WebSocket('ws://html5rocks.websocket.org/echo');
dummySocket.onopen();
dummySocket.onmessage(JSON.stringify({ hello : 'from server' }));
// You can assert whether your code sent something to the server like this:
sinon.assert.calledWith(dummySocket.send, '{"the client":"says hi"}');
My questions are
How can I receive the same data from fake server which have been sent to server earlier.
How can I send data to fake server by using send method of fake socket object(eg:- socket.send())?
How can I get data from server on dummySocket.onmessage = function (msg){}
With sinon.js, I could not get the any process to create fake websocket object like for fake XMLHttpRequest and server by using respectively useFakeXMLHttpRequest() and fakeServer.create()
Is there any process to achieve this on sinon.js?
Normally, you would do ws = sinon.createStubInstance(WebSocket), but this isn't possible since properties on the WebSocket.prototype throw exceptions when reading them. There are two ways around this.
You could add a useFakeWebSocket to sinon to overwrite WebSocket. This would be similar to what useFakeXMLHttpRequest does to XMLHttpRequest.
Duck type out a WebSocket object by iterating over the prototype.
beforeEach(function () {
var ws = {};
for (var prop in WebSocket.prototype) {
ws[prop] = function () {}; // some properties aren't functions.
}
});
If you wanted to implement a mock echo WebSocket server so that you can test your event handlers, you could do that with this:
var ws;
beforeEach(function () {
ws = {
send: function (msg) {
this.onmessage({ data: msg });
},
onmessage: function (e) {
// stub
}
};
});
it('should echo', function () {
var spy = sinon.spy(ws, 'onmessage');
ws.send('this is a test');
assertEquals(spy.args[0][0].data, 'this is a test');
});
I hope this is what you're looking for.

REST API and Real Time client

I want to have the following architecture:
A JSON REST API where real time statistic data is pushed to and stored in a Redis server.
A JSON REST API call where any number of clients (native or web) can receive this data after it has been stored - i.e. in real time.
The first client will just be a web app and I may build a native app later.
I'm wondering if my only option is for the clients to poll the REST API for changes? Ideally, I'd like the server to push updates as they arrive so I don't need to manage this polling.
Is my architecture suitable for what I want to achieve, or am I missing something?
A more efficient way than polling is to use websockets, such as Faye or Socket.IO. You can place an emit event under a data store event to immediately send data that's been stored.
With Socket.IO, you'd do that like this:
var io = require('socket.io').listen(80);
//note that you can listen on HTTP servers
//can also be used with Express applications, etc
//when data is stored, run this
io.sockets.emit('event', {
object: 'that is sent to client'
});
You could then use this to tell the client that there is new data, or you could directly send the newly stored data. Custom events can be defined, such as
io.sockets.emit('data_receive', function (data) {...});
and would be received client side like so:
var socket = io.connect('http://socket.location');
socket.on('data_recieve, function (data) {
//data is whatever sent from server
});
In Faye you'd do something like this:
var http = require('http');
var faye = require('faye');
var bayeux = new faye.NodeAdapter({
mount: '/faye',
timeout: 45
});
bayeux.listen(8000);
Then when data is stored, you'd run:
client.publish('/path', {
data: 'Hello world'
});
Any client that has created a client like so:
var client = new Faye.Client('http://socket:port/path');
client.subscribe('/path', function(data) {
alert('Received data: ' + data.text);
});
Will receive the data.
You have the option of Node.js and the websocket for push and pull in realtime.
To don't manage the queuing you still have the option of MQ.

node.js server and client sideo code to connect

Im trying to set up a node.js server to send messages to the client, which will then display the messages using a jquery notification library, I'm using this notifcation library if anyone's interested: http://needim.github.com/noty/
At the minute I have a postgres database set up with a table which has a a trigger on it to write to a listener.
The trigger is as follows:
CREATE OR REPLACE FUNCTION new_noti() RETURNS trigger AS $$
DECLARE
BEGIN
PERFORM pg_notify('watchers', TG_TABLE_NAME || ',msg,' || NEW.msg );
RETURN new;
END;
$$ LANGUAGE plpgsql;
Then I have a node.js server as follows:
var pg = require ('pg');
var pgConString = "pg://aydin:password#localhost/test"
var app = require('http').createServer(handler)
, io = require('socket.io').listen(app)
, url = require('url')
app.listen(8080);
function handler (request, respsonse) {
var client = new pg.Client(pgConString);
client.connect();
client.query('LISTEN "watchers"');
client.on('notification', function(msg) {
console.log(msg.payload);
sendMessage(msg.payload);
});
}
function sendMessage(message) {
io.sockets.emit('notification', {'message': message});
}
Then I have some client code as follows:
<script type="text/javascript">
var socket = io.connect('http://localhost:8080');
socket.on('notification', function (data) {
console.log(data.message);
newNoty(data);
});
function newNoty(data) {
noty({
"text":data.message,
buttons: [{
type: 'button green',
text: 'Go to'
}],
"theme":"noty_theme_twitter",
"layout":"bottomRight",
"type":"information",
"animateOpen":{
"height":"toggle"
},
"animateClose":{
"height":"toggle"
},
"speed":500,
"timeout":7500,
"closeButton":true,
"closeOnSelfClick":true,
"closeOnSelfOver":false,
"modal":false,
});
}
</script>
This doesn't work, it seems the node.js never receives the postgres notifications, I think this is because I am using the function handler and I'm not actually firing any requests to it from the client code. I'm not sure how to do this and whether it is the correct way?
Is there a function on which can fire on connections and not requests?
And am I even doing it the right way round? should there be a server on the client side which node.js sends messages to? How does it know when a client is available? Any help or pointers to tutorials would be much appreciated. Thankyou.
You're not actually setting up your database connection until the client sends an HTTP request. It looks like that may never happen due to same-origin issues (your client code appears to be coming from somewhere other than the server you've shown).
In any case, you probably want to set up the connection in response to a "connection" event from io.sockets (i.e. move the stuff that's currently in the HTTP request handler there). That's how it "knows when a client is available". Or maybe you should be doing it as part of initialization. Your client-side code seems OK, but it's out of context so it's hard to tell whether it really fits your needs.

Categories

Resources