Building simple nodejs API with streaming JSON output - javascript

I am trying to build a simple node.js based streaming API. All I want to do is as I hit the server url, the output should stream a set of test data(JSON) like twitter streaming API.
var app = require('express')();
var server = require('http').Server(app);
var io = require('socket.io')(server);
server.listen(8083);
app.get('/', function (req, res) {
res.write(io.on('connection', function (socket) {
socket.emit('item', { hello: 'world' });
}));
});
So, If i do curl http://localhost:8083/, I want output something like:
$ curl http://localhost:8083/
{hello: 'world'}
{hello: 'world'}
{hello: 'world'}
{hello: 'world'}
...
I am new to node.js and web sockets. I might be horribly wrong on the basics of how node works, let me know the best solution.

First, it's better to put the JSONStream part inside a middleware like so:
var _ = require('lodash');
// https://github.com/smurthas/Express-JSONStream/blob/master/index.js
function jsonStream(bytes) {
return function jsonStream(req, res, next) {
// for pushing out jsonstream data via a GET request
var first = true;
var noop = function () {};
res.jsonStream = function (object, f) {
f = _.isFunction(f) ? f : noop;
if (!(object && object instanceof Object)) {
return f();
}
try {
if (first) {
first = false;
res.writeHead(200, {
'Content-Type': 'application/json',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'
});
}
res.write(JSON.stringify(object) + '\n');
} catch (err) {
return _.defer(f.bind(null, err));
}
f();
};
next();
};
}
Then, let's say you want to be notified via this API each time someone connects to socket.io
var app = require('express')();
var server = require('http').Server(app);
var io = require('socket.io')(server);
var _ = require('lodash');
var EventEmitter = require('events').EventEmitter;
server.listen(8083);
var mediator = new EventEmitter();
io.on('connection', function (socket) {
mediator.emit('io:connection:new', socket);
});
// the second parameter, specify an array of middleware,
// here we use our previously defined jsonStream
app.get('/', [jsonStream()], function (req, res) {
function onNewConnection(socket) {
res.jsonStream({
type: 'newConnection',
message: 'got a new connection',
socket: {
id: socket.id
}
});
}
// bind `onNewConnection` on the mediator, we have to use an mediator gateway
// because socket.io does not offer a nice implementation of "removeListener" in 1.1.0
// this way each time someone will connect to socket.io
// the current route will add an entry in the stream
mediator.on('io:connection:new', onNewConnection);
// unbind `onNewConnection` from the mediator
// when the user disconnects
req.on('close', function () {
mediator.removeListener('connection', onNewConnection);
});
res.jsonStream({
type: 'welcome',
message: 'waiting for connection'
});
});
Finally, if you want to test this code without connecting to socket.io use the following simulator:
// Simulate socket.io connections using mediator
(function simulate() {
var dummySocket = {
id: ~~(Math.random() * 1000)
};
mediator.emit('io:connection:new', dummySocket);
setTimeout(simulate, Math.random() * 1000);
})();

Related

Can't make a websocket connection between react client and express server

I'm trying to make a connection between a react client and an express server with websockets. Every time I try this i get an error. I think I'm missing something.
Server code:
var http = require('http');
var ws = require('ws');
var theHttpServer = http.createServer();
var theWebSocketServer = new ws.Server({
server: theHttpServer,
verifyClient: true
});
theHttpServer.on('request', app);
theHttpServer.listen(9000,
function () {
console.log("The Server is lisening on port 9000.")
});
theWebSocketServer.on('connection', function connection(msg) {
console.log("CONNECTION CREATED");
websocket.on('message', function incoming(message) {
});
});
Client code:
let wsConnection = new WebSocket("ws://localhost:9000");
wsConnection.onopen = function(eventInfo) {
console.log("Socket connection is open!");
}
The error:
if (!this.options.verifyClient(info)) return abortHandshake(socket, 401);
^
TypeError: this.options.verifyClient is not a function
You're passing verifyClient as a boolean, not a function. What you would maybe want to do is change this to:
function verifyClient(info) {
// ...Insert your validation code here
};
var theWebSocketServer = new ws.Server({
server: theHttpServer,
verifyClient: verifyClient
});

xterm.js reconnect with same PWD

I am using xterm.js in my web project to have a terminal on the web page. Every time I refresh my page or reconnect socket when a socket connection is broken due to internet fluctuation from the client. The current PWD directory is lost and it falls to specified CWD directory which is user home in my case. So again I have to do cd where I was working.
How can I connect and remain at same PWD where I was last time before page refreshing or socket disconnect?
One of the things I tried is to store term object and connect through the same object when reconnecting if it is already present. Not deleting process and object in on WebSocket disconnect.
var http = require('http');
var express = require('express');
var app = express();
var expressWs = require('express-ws')(app);
var pty = require('node-pty');
var cors = require('cors');
app.use(cors());
app.options('*', cors());
var terminals = {}; //global terminals
function getUser(token) {
return new Promise((resolve, reject) => {
try {
return http.get({
host: '',
path: '',
headers: {'token': token}
}, function(response) {
// Continuously update stream with data
var body = '';
response.on('data', function(d) {
body += d;
});
response.on('end', function() {
return resolve(JSON.parse(body));
});
});
} catch (err) {
console.log('Api failed');
console.log(err);
reject;
}
})
}
app.ws('/terminals/:user_id', function (ws, req) {
try {
getUser(req.params.user_id) /* cheking with api if user exist in my database*/
.then(user_info => {
if(terminals[parseInt(req.params.user_id)]){
var term = terminals[parseInt(req.params.user_id)];
}else {
var term = pty.spawn(process.platform === 'win32' ? 'cmd.exe' : 'bash', [], {
name: 'xterm-color',
cwd: cwd,
env: process.env
});
terminals[parseInt(req.params.user_id)] = term;
}
term.on('data', function(data) {
ws.send(data);
});
ws.on('message', function(msg) {
term.write(msg);
});
ws.on('close', function () {
// process.kill(term.pid);
// delete terminals[parseInt(req.params.pid)];
// delete logs[req.params.pid];
});
})
.catch(err => {
})
} catch (err) {
console.log('Terminal webSocket failed');
console.log(err);
}
});
app.listen(3000);
This is not working for me. This gets me connect only first time but when I refresh my page terminal does not connect with existing store object.
Also, this has a problem if the spawned process is killed by the system but it still remains in javascript object and script try to reconnect with same term object it will fail.
Any guidelines how to achieve reconnect with same PWD.
Details
OS version: Mac OS ,
xterm.js version: 2.2.3
This can be solved very easily by just updating the ~/.bashrc on server
Putting below two line in ~/.bashrc file worked for me
PROMPT_COMMAND+='printf %s "$PWD" > ~/.storepwd'
[ -s ~/.lastdirectory ] && cd `cat ~/.lastdirectory`
Ref Save last working directory on Bash logout

ember-cli-mirage redirects socket.io client, which is injected in mirage

the issue that occurs here, is that, when i connect between sample socekt.io client with this socket.io server by node.js ( just running two terminals and opening socket connection between client and server)
I have no problems. But, when I am trying to inject this socket.io-client into my Ember.js application, precisely to ember-cli-mirage it redirects my client from given address : ( 'http: //localhost:8080') to something like http: //localhost:8080/socket.io/?EIO=3&transport=polling&.....
also Mirage displays me an error that I cannot handle, even by setting up precise namespace, routing the wsClient.connect() method or calling this.passthrough() , before calling wsClient.connect() .
I also paste the the screenshot of error from inspect console in browser:
error image
Do you have any idea how to resolve this problem? Thank you in advance and I also hope that the topic is not duplicated.
// server.js
var app = require('http').createServer(handler);
var io = require('socket.io')(app);
app.listen(8080);
function handler(req, res) {
res.writeHead(200);
res.end('default.index');
}
var rooms = {
'room1': [
],
'room2': [
]
};
io.on('connection', function(socket) {
console.log('client connected');
socket.on('join', function(roomName) {
rooms[roomName].push(socket.id);
socket.join(roomName);
});
socket.on('leave', function(roomName) {
var toRemove = rooms[roomName].indexOf(socket.id);
rooms[roomName].splice(toRemove, 1);
socket.leave('roomName');
});
socket.on('eNotification', function(data) {
console.log(data);
io.to(socket.id).emit('eNotificationCall', data);
io.to('room2').emit('eventNotification', data);
});
socket.on('gNotification', function(data) {
console.log(data);
io.to(socket.id).emit('gNotificationCall', data);
io.to('room1').emit('diagram1Notification', data);
});
socket.on('close', function() {
console.log('client disconnected');
});
});
//client.js
var wsClient = {
socket: null,
connect: function() {
this.socket = io.connect('http://localhost:8080');
this.socket.on('connect', function() {
console.log('mirage client connected!');
});
},
send: function(eventData, graphData) {
this.socket.emit('eNotification', eventData);
this.socket.emit('gNotification', graphData);
}
};
export default wsClient;
//config.js
import wsClient from './websockets/client';
export default function() {
wsClient.connect();
console.log(wsClient.socket);
var graphData = {X: "2", Y: "3"};
var eventData = {myDAta: 'myDAta', message: 'message'};
setInterval(function() {
wsClient.send(graphData, eventData);
}, 5000);
}
If you call this.passthrough() with no args it only allows requests on the current domain to passthrough. It looks like the websocket connection is on a different port, so try specifying it directly:
this.passthrough('http://localhost:8080/**');
See the docs for more information.

Testing a node.js server in the same process as the test

I am wondering if there is any disadvantage to starting a server in a process and then running tests against that server in the same process.
Obviously there are some performance concerns, but if we are testing accuracy instead of performance, are there any major concerns with code like the following?
var fs = require('fs');
var path = require('path');
var http = require('http');
var supertest = require('supertest');
var assert = require('assert');
describe('#Test - handleXml()*', function() {
var self = this;
var server;
var payload = ''; // stringified XML
var xmlPath = path.resolve('test', 'test_data', 'xml_payloads', 'IVR_OnDemandErrorCode.xml');
before(function(done) {
var config = self.config = require('univ-config')(module, this.test.parent.title, 'config/test-config');
server = createServer().on('listening', function() {
done(null);
});
});
beforeEach(function(done) {
fs.readFile(xmlPath, 'utf8', function(err, content) {
assert(err == null);
payload = content;
done();
});
});
it('should accept request Content-type "text/xml or application/xml"', function(done) {
supertest(server)
.post('/event')
.set('Content-Type', 'application/xml')
.send(payload)
.expect(200, done);
});
it('should transform XML payload into JSON object', function(done) {
supertest(server)
.post('/event')
.set('Content-type', 'application/xml')
.send(payload)
.expect(200)
.end(function(err, res) {
assert(err == null,'Error is not null');
var jsonifiedXml = JSON.parse(res.text);
assert(typeof jsonifiedXml === 'object','jsonifiedXml not an object');
done();
});
});
describe('JSONified XML', function() {
it('should have proper key casing', function(done) {
supertest(server)
.post('/event')
.set('Content-type', 'application/xml')
.send(payload)
.expect(200)
.end(function(err, res) {
assert(err == null);
var payload = JSON.parse(res.text);
payload = payload.events[0].data;
assert(payload.hasOwnProperty('ppv'),'Bad value for ppv');
assert(payload.hasOwnProperty('mac'),'Bad value for mac');
assert(payload.hasOwnProperty('appName'),'Bad value for appName');
assert(payload.hasOwnProperty('divisionId'),'Bad value for divisionId');
assert(payload.hasOwnProperty('callTime'),'Bad value for callTime');
assert(payload.hasOwnProperty('callDate'),'Bad value for callDate');
assert(payload.hasOwnProperty('ivrLOB'),'Bad value for ivrLOB');
done();
});
});
});
});
function createServer(opts) {
//Note: this is a good pattern, definitely
var handleXml = require(path.resolve('lib', 'handleXml'));
var server = http.createServer(function(req, res) {
handleXml(req, res, function(err) {
res.statusCode = err ? (err.status || 500) : 200;
res.end(err ? err.message : JSON.stringify(req.body));
});
});
server.listen(5999); //TODO: which port should this be listening on? a unused port, surely
return server;
}
That's the standard way of testing a the http endpoints in a node application. But you are not going to want to have a createServer() function in each test. You will have a common function that creates a server that you can use through out your application, including to start the production server.
You right in noticing the having the server listen to a port doesn't actually do anything for you.
For this reason, it's common to have what I call an application factory that starts everything about a server, but does not listen to a port. That way I can access the server from a test or a script. The production app gets booted from a minimal index file:
var createServer = require('./AppFactory');
var server = createServer();
server.listen(5999);

How to use middleware that requires socket connection?

I am using express.io and am trying to craft a middleware that requires a connection to a remote server via two sockets. However, I am having a problem.
var net = require('net');
module.exports = function (host, port) {
return function (req, res, next) {
req._messages = net.connect(port, host);
req._commands = net.connect(port, host);
req._messages.on('data', function (data) {
req.io.broadcast('data', data.toString('ascii'));
});
req._messages.write('CF I\r'); // initialization command
next();
}
}
then in my main app:
var port = process.env.CYLON_PORT;
var host = process.env.CYLON_HOST;
var app = require('express.io').http().io();
app.use(require('./cylon/controller')(host, port));
module.exports = app;
However, I am coming across a problem. On each request, it attempts to reconnect. This causes an Error: connect ECONNREFUSED. Ideally, I would like this to connect once when the application starts and maintain that socket, but it needs to intercept each connect.
How can I use sockets in middleware?
You can try that way:
var net = require('net');
module.exports = function (host, port) {
var messagesConnection = net.connect(port, host);
var commandsConnection = net.connect(port, host);
return function (req, res, next) {
req._messages = messagesConnection;
req._commands = commandsConnection;
req._messages.on('data', function (data) {
req.io.broadcast('data', data.toString('ascii'));
});
req._messages.write('CF I\r'); // initialization command
next();
}
}
The call to require('./cylon/controller')(host, port) will start the connections and they will then be reused each time the middleware is called.
Edit: I'm wondering whether you really need to set all those new fields on you req object. You could have some files that export io, messagesConnection and commandsConnection for instance, and you could require them when needed.
Here you end up adding an even listener on messagesConnection each time the middleware is called, which is everything but good.
Edit2: What you could do instead, because you seem to be using express-io:
var net = require('net');
module.exports = function (io, host, port) {
var messagesConnection = net.connect(port, host);
var commandsConnection = net.connect(port, host);
messagesConnection.on('data', function (data) {
io.broadcast('data', data.toString('ascii'));
});
messagesConnection.write('CF I\r'); // initialization command
return function (req, res, next) {
req._messages = messagesConnection;
req._commands = commandsConnection;
next();
}
}
And in your main file:
var port = process.env.CYLON_PORT;
var host = process.env.CYLON_HOST;
var app = require('express.io').http().io();
app.use(require('./cylon/controller')(app.io, host, port));
module.exports = app;

Categories