Sending socket message when post request is received - node.js

I would like to emit a socket.io message to all connected clients when any client sends a post request. How can I keep the socket open so that my http request methods can access the connected socket.
I was able to get the following to work but if a client is not connected, the post method doesn't work.
io.on('connection', function(socket){
console.log('socket connected');
app.post('/api/guests', function(req, res){
socket.emit('newguest', {hello: 'world'});
});
})
I also tried saving the socket to a higher scope and even a global but that didn't seem to work either.
Thanks in advance!

You don't have to call the "socket emit" inside the on.("connection"...)
Try something like this:
app.post('/api/news', user.can('access private page'), function(req, res, next) {
io.sockets.emit("nuova:news", data);
});
In my case i pass the "io" variable from the "server.js" file (or app.js) like this:
require('./app/myRoute')(app, user, io);
And receive it in the controller like this:
module.exports = function(app, user, io) {
...
}
"io" is declared like this:
var wsServer = require('http').createServer(app);
var io = require('socket.io').listen(wsServer);
I hope these additional info can be useful to you...

Related

Trigger `socket.io` event from server(`node.js`), not from client

I am trying to make a game server with node.js, socket.io.
The basic idea likes below.
Initialize socket.io instance when the server starts
Store instance in global scope, so controllers can access it
When API calls, we trigger some socket.io event in the controller or some other points
Here is the implementation I made ...
First, in server.js - entry point
let GlobalVars = require('./state/GlobalVars');
const apiRouters = require('./router');
...
app.use('/api', apiRouters);
app.get('/', (req, res) => {
res.sendFile(`${__dirname}/test/simpleClient.html`)
});
const httpServer = http.createServer(app);
let socketIOInstance = socketIO(httpServer);
socketIOInstance.on('connection', (socket) => {
console.log('SOCKET.IO A USER CONNECTED');
socket.on('create', (data) => {
console.log('SOCKET.IO create called', socket);
socket.join(data.room);
socketIOInstance.emit('message', 'New people joined');
});
socket.on('join', (data) => {
console.log('SOCKET.IO join called', data);
})
socket.emit('message', 'Hi');
});
GlobalVars.socketIO = socketIOInstance;
// Add to global, so the controllers can manage own actions like create, join ...
httpServer.listen(port, () => {
console.log(`Server Listening on the port ${port}`);
})
...
When I access from a client, I am able to see SOCKET.IO A USER CONNECTED and Hi in the browser console.
Second, In api controller.
let GlobalVars = require('../state/GlobalVars');
...
router.post('/create', (req, res) => {
console.log('GenerateGameSokect');
let game = new Game();
let gameId = game.gameId;
// console.log('Global vars ', GlobalVars.socketIO);
GlobalVars.socketIO.emit('create', {
room: gameId
});
res.json({
result : 'SUCCESS',
game : game
})
});
I imported GlobalVars which contains socketIO instance. So what I expected was, socket create event triggered from the statement GlobalVars.socketIO.emit('create', Object) but could not find message in the server logs.
I got no clue what I was missing.
The final form I pursue is something like...
When user call create API, I creates socket connection and room
API will called in HTTP protocol, but in the API, the server publishes some events. - pubsub like.
Thanks for reading my questions b. Here is full source code till now(bitbucket public)
================== EDIT ====================
I got understood (maybe...)
The user-flow I wanted was ...
The client call API
(In the server) Checking validation in API and if valid emit to socket.io
If event accepted send new status to all clients
However, creating socket.io connection in the server looks strange for me, the solution is up to the client.
New user-flow I will change
The client call a validation API
If return is valid, the client emit socket.io event. This time server only do validation, not emit socket.io
In socket event, send new status to all other users
================== EDIT #2 ====================
This is a kind of conclusion. It looks I just misunderstanding the concept of socket communication. Like answer and replies say, Socket and HTTP are totally different channels, there is no way to connect both. (At least, without open new connection from http server to socket)
If this is wrong, you could add reply, Thanks
Now I understand you. Or at least I think!
Let's put it this way: there are two (asymetric) sides on a socket, server and client. What I called, respectively, "global manager" and "socket" in my comment to your post.
const server = require('socket.io')(yourHttpServer);
// client is installed as well when `npm i socket.io`
const client = require('socket.io-client')('http://localhost:' + yourServerPort);
// `socket` is the server side of the socket
server.on('connection', (socket) => {
// this will be triggered by client sides emitting 'create'
socket.on('create', (data) => {
console.log('a client socket just fired a "create" event!');
});
});
// this will be triggered by server side emitting 'create'
client.on('create', (data) => {
server.emit('create', {content: 'this will result in an infinite loop of "create" events!'});
});
In your /create route, when you GlobalVars.socketIO.emit('create', ...), the server-side socket handler isn't triggered, however if you have clients connected through a browser (or, like I showed above, if you connect a client socket directly from the server) then these will trigger their 'create' listener, if any.
Hope this helps you get on the right tracks!

NodeJS, Express, and eventful Socket.io

I want to build a NodeJS API so that when I hit an endpoint, the app will trigger an event that will cause its unique socket connection to emit a message to its listeners. I have built a solution before using Python/Django, Redis, and NodeJS/Socket.io with Django as the API and Redis as the 'event trigger', but I would like to consolidate the different technologies into NodeJS and Socket.io.
I tried moving the socket.emit() code into different modules and then app.use()'d those modules, but the code broke because it didn't have an instance of the socket.
I also know that you can broadcast to all socket connections inside on an endpoint, for example:
app.use('socket.io/help', function(req, res) {
console.log(req.query);
io.sockets.emit('help_message', 'You should help me.');
res.send('help msg sent');
});
But I am looking for a way that allows a client (that doesn't have a socket connection) to hit an endpoint and pass a query param that tells NodeJS which of its connected sockets to send a message to.
Is this possible? Or am I trying to fight the framework? e.g., is there a different way of doing this with different JS WebSocket frameworks/technologies?
I have been stuck on same situation but resolved easily
you have created socket on app.js
server = require('http').createServer(app),
io = require('socket.io').listen(server);
server.listen(port);
global.socketIO = io;
Now you can call this io instance to your any controller like
app.use('socket.io/help', function(req, res) {
console.log(req.query);
var io = global.socketIO;
// UID IS THE VARIABLE
io.sockets.emit('help_message'+UID, 'You should help me.');
res.send('help msg sent');
});
CLIENT SIDE
<script src="/socket.io/socket.io.js"></script>
<script>
window.socket.on("help_message_<%-UID%>", function(msg){
//// ACTION
});
You can join a room with the specific sockets you want to recieve the messages on.
see Rooms & Namespaces in the socket.io documentation
join a chan on your helpdesk conenctions:
io.on('connection', function(socket){
socket.join('helpdesk');
});
and broadcast to them:
app.use('socket.io/help', function(req, res) {
console.log(req.query);
var io = global.socketIO;
io.sockets.emit('adduser', req.body.uid);
io.to('helpdesk').emit('some event'):
res.send('help msg sent');
});

Nodejs express headers on the websocket scope

I've got a node.js + express + socket.io app.
i want to save the request headers in the socket, to use later.
io.sockets.on('connection', function (socket) {
socket.headers = {};
app.get('*', function (req, res) {
//fetch headers
socket.headers.ua = req.headers['user-agent'];
res.sendfile(__dirname + '/index.html');
});
....etc
but because i am in the app scope, socket isnt defined. I always get confused with the in and out scope.
i cannot app.get() it, because if another browser connects, the app will be changed, right?
You're doing it wrong. Every socket has a handshake object with it which contains request headers, domain, host, date etc. If you still want to fetch headers information then do this:
io.sockets.on('connection', function(socket) {
console.log(socket.handshake); //This would print handshake object in JSON format
// to get req-header, do this
socket.head = socket.handshake.headers['user-agent'];
});
And you can use this property later in some event like:
socket.on('EventName',function(data){
console.log(socket.head);
});

Scoping of socket.io server side and client side objects in express routes

In my app.js I have
var app = express();
var serv = http.createServer(app);
var io = require('socket.io').listen(serv);
io.sockets.on('connection', function(socket) {
//some code here
}
var SessionSockets = require('session.socket.io'),
sessionSockets = new SessionSockets(io, express_store, cookieParser);
sessionSockets.on('connection', function (err, socket, session) {
//set up some socket handlers here for the specific client that are
//only called when a client does a socket.emit.
//These handlers have access to io, sessionSockets, socket, session objects.
}
How can the express routes access a particular client's socket reference after processing a post/get which is not triggered by a client socket.emit but triggered by a client post/get. What is the best way to scope the socket.io server(io/sessionSockets)/client(socket) objects in routes so that I can get this client's socket reference easily?
These three steps helped me to the solve the problem. This identifies tabs also uniquely as that was one of my requirements.
On connection, join using socket.id and then send the socket.id back to the client using
io.sockets.on('connection', function(socket) {
socket.join(socket.id);
socket.emit('server_socket_id', {socket_id : socket.id});
}
Client receives the emit event using
socket.on('server_socket_id', function(data){
//assign some global here which can be sent back to the server whenever required.
server_socket_id = data.socket_id;
});
In app.js I fetch the corresponding socket like this and pass it on to the routes.
app.post('/update', function(req, res){
var socket_id = req.body.socket_id;
route.update(req, res, io.sockets.in(socket_id).sockets[socket_id]);
});
The best way to do this is to use the socket.io authorization setting, although the module session.socket.io was created specifically for that purpose. Each time a socket establishes a connection, there is handshake data that is stored (although I've heard that flashsockets won't pass the browser cookies). This is what it looks like (and is similarly written in the module you're using):
io.configure(function () {
io.set('authorization', function (handshakeData, callback) {
//error object, then boolean that allows/denies the auth
callback(null, true);
});
});
What you could do from here is parse the cookie, then store a reference to that socket by the cookie name. So you would add this to the authorization setting:
var data = handshakeData;
if (data.headers.cookie) {
//note that this is done differently if using signed cookies
data.cookie = parseCookie(data.headers.cookie);
data.sessionID = data.cookie['express.sid'];
}
Then, when you listen on connections, store the client by session identifier:
var clients = {};
io.sockets.on('connection', function(socket) {
//store the reference based on session ID
clients[socket.handshake.sessionID] = socket;
});
And when you receive an HTTP request in Express, you can fetch it like this:
app.get('/', function(req, res) {
//I've currently forgotten how to get session ID from request,
//will go find after returning from school
var socket = clients[sessionID];
});

firing socket.emit() on http request

Right now I have the following code: foo
sIo.sockets.on('connection', function(socket){
socket.emit('hello', 'world');
});
I would like to be able to emit this when somebody opens a page from my routes, like this:
//app.js
app.get('/send', routes.index);
//routes.js
exports.index = function(req, res){
socket.emit('hello', 'world');
};
How can I achieve this? Thanks in advance
To send a socket message to all connected sockets, you can just call io.sockets.emit instead of socket.emit. There are a few ways to send messages using socket.io which I'll outline below.
// Send the message to all connected clients
io.sockets.emit('message', data);
// Send the message to only this client
socket.emit('message', data);
// Send the messages to all clients, except this one.
socket.broadcast.emit('message', data);
There is also a concept of rooms which you can use to segment your clients.
// Have a client join a room.
socket.join('room')
// Send a message to all users in a particular room
io.sockets.in('room').emit('message', data);
All of that covers how to send messages, but it's clear now you're asking about how to access the socket and / or io objects from inside a separate file. One options just has you pass those dependencies to the specified file. Your require line will end up looking something like this.
var routes = require('./routes')(io);
Where io is the socket.io object created from .listen. To handle that in your route file you'll have to change how you're defining your exports.
module.exports = function(io) {
return {
index: function(req, res) {
io.sockets.emit('hello', 'world');
res.send('hello world');
}
};
}
A cleaner implementation would have your routes expose events that your socket code can bind to. The following is untested, but should be very close.
var util = require("util"),
events = require("events");
function MyRoute() {
events.EventEmitter.call(this);
}
util.inherits(MyRoute, events.EventEmitter);
MyRoute.prototype.index = function(req, res) {
this.emit('index');
res.send('hello world');
}
module.exports = new MyRoute();
And then in your app.js file when you're binding express routes and socket.io.
app.get('/send', routes.index);
routes.on('index', function() {
io.sockets.emit('hello', 'world');
});
There are many other ways to accomplish this, but the best one depends on what you're trying to do. As I alluded to before, calling broadcasting to everyone is going to be far more simple than broadcasting to a particular user.

Resources