Problem with socket when using it in express - javascript

I have a problem with the socket in my program. export the "io" connection and although I can broadcast from any part of the normal program (just by referring to "io"), I cannot use "socket" and broadcast with it unless it is within the same connection of "io." I would like to know how to use socket from any other part of the program, with functions such as "socket.emit ()" or "socket.broadcast.emit" etc. Or how to call this one.
This is my index.js:
const express = require('express');
const restApi = express();
const cors = require('cors');
const server = require('http').createServer(restApi);
const rutas = require('./src/routes/Routes.js');
const io = require('./src/socket/SocketManager.js');
io.attach(server);
restApi.use(express.json());
restApi.use(express.static('public'));
restApi.use(cors());
restApi.options('*', cors());
server.listen(4040, () => console.log('Listening server in port 4040'));
restApi.all('/*', function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "X-Requested-With");
next();
});
restApi.use('/api',rutas);
my managersocket.js:
const io = require('socket.io')();
io.on('connection', function (socket) {
console.log('NUEVA CONEXION: ',socket.id);
socket.emit('recoger',socket.id);
});
module.exports = io;
This is where I would like to use the socket or at least call the function.
and User.js:
var io = require('../../socket/SocketManager');
var RouterUser = function(){
this.practicafuncion = function (req,res){
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "X-Requested-With");
console.log('probando entrar por rest y que salga un mensaje por socket');
res.send({
status:'Aprobado',
msj:'Enviado por rest'
});
//this works correctly. io.sockets.emit('mensaje','**MENSAJESERVIDORPORSOCKET**');
//This is not the problem when using "socket". socket.broadcast.emit('mensaje','**MENSAJESERVIDORPORSOCKET**');
};
}
module.exports = function(){
var instancia = new RouterUser();
return instancia;
};
same is the repository where the whole code is
https://github.com/proxirv/Socket.io-router

socket is a temporary object that exists only for the duration of one particular client connection. And, there can be zillions of them (one or more for each connected client). As such, you don't just export one socket or stuff it in a global and try to use that everywhere.
So, if what you're trying to do is to access the socket.io connection for the user that you just received an http request for, that's a bit more complicated and there are several different ways to approach it.
One approach, I've used before is shown below:
const express = require('express');
const app = express();
const server = app.listen(80);
const io = require('socket.io')(server);
const expsession = require('express-session');
const path = require('path');
// install session middleware
const sessionMiddleware = expsession({
secret: 'random secret',
saveUninitialized: true,
resave: true
});
// run session middleware for regular http connections
app.use(sessionMiddleware);
// run session middleware for socket.io connections
io.use(function(socket, next) {
sessionMiddleware(socket.request, socket.request.res, next);
});
// when a socket.io connection connects, put the socket.id into the session
// so it can be accessed from other http requests from that client
io.on('connection', function(socket) {
// socket.handshake.headers
console.log(`socket.io connected: ${socket.id}`);
// save socket.io socket in the session
console.log("session at socket.io connection:\n", socket.request.session);
socket.request.session.socketio = socket.id;
socket.request.session.save();
});
// any arbitrary express route definition
// Note: you can't send socket.io data in a request that loads an HTML page
// because that client hasn't yet established the socket.io connection
// for that page. The socket.io connections will be created after
// the browser loads this page.
app.get("/", function(req, res) {
const session = req.session;
console.log("\n\npage load\n---------------------------\n");
console.log("session:\n", session);
res.sendFile(path.join(__dirname, "socket-io-session.html"));
});
// Upon an API call from a page that already has a socket.io connection,
// respond to the API call and send something to that page's socket.io socket
app.get("/api/test", function(req, res) {
const session = req.session;
io.sockets.connected[session.socketio].emit('show', "sending some data");
console.log("session:\n", session);
console.log("session.socketio:\n", session.socketio);
res.json({greeting: "hello"});
});
Here are the steps in that concept:
Set up express-session for regular http connections. This gives you a place to store stuff that belongs to one particular client (keyed by a cookie)
Set up express-session for the socket.io connection so you can also have access to express-session data in socket.io connections.
When a socket.io connection happens, store the socket.id in the sesssion. This makes the socket.id available for future http requests from that specific client
When some future http request comes in from that client, you can reach into the session, get the socket.id value (which is just a string) and then use that to get the socket for that user and once you have the socket, you can use socket.emit() to send data just to that user.
If you didn't have other reasons for using express-session, you could also just put the socket.id into a cookie all by itself when the socket.io connection connects and then get the socket.id from the cookie during your http request.

Related

Running a continuous function in nodejs after user logs in

Here is my issue - I have a user database on mongo which also has a topicsList to which a user is subscribed to. I need to continuously listen to the topics in the topicsList via Mqtt.
The topic list can be fetched from the database once the user logs in & is authenticated. How do I pass the topics list in a call back function in the main app.js file in the app.listen function.
For example :
app.listen(3000, function(){
console.log('Subscribed to the following topics : ", topics)
}
So that this function runs perpetually and listens to any incoming data to the topics, unless the user is logged out, where the topics list is back to being null.
I hope I understood your question correctly.
Firstly, need to up socket.
Server:
let app = express();
let http = require('http');
let server = http.Server(app);
let socketIO = require('socket.io');
let io = socketIO(server, {
cors: {
origin: ip,
methods: ["GET", "POST"]
}
});
server.listen(3000, function(){}
Client:
let socket = io.connect( ip );
Now we need to handle the user connection event on the server:
io.on('connection', function(socket) {
socket.emit('sendData', topics);
}
Handling the socket event on the client:
socket.on('sendData', function(data) {
console.log(data);
});

Integrating socket.io with express is it a good idea? and if so how to go about it?

I'm must say I'm very new to back end development,
I'm currently working on an exercise project of making a fake money poker website. I use Node.js socket.io/express-session/passport
At first, I mainly used express with a HTTP server listening on one port. Averagely Like this:
const express = require("express")
const app = express()
app.get('/home',connectEnsureLogin.ensureLoggedIn("/loginPage"),function(req, res) {
//console.log(req.user.username+": sessionId: "+req.sessionID);
return res.sendFile( __dirname+"/website/index.html");
}
);
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log("Poker site Server started on ${PORT})")
The website wasn't working very fast. When a client joined a poker table they needed to ask the server every second for new updates on the state of the game so that was a lot of HTTP requests coming into my server. So I decided without much theoretical certitude that it seemed like a good idea: To have the server use socket.io sockets to hand info for clients that are in poker tables, but when they are not in poker tables and are just browsing the site I use a HTTP server to handle their request. Code wise I feel I haven't really managed to do this correctly. My code with Express, express-session, and passport combined makes sure only to hand information to users authenticated. But since The socket.io servers seem totally separate from all the express code, they don't share the same authentication functionality as the express code. So I need to somehow link my express and socket.io code so I can check if a client is authenticated before handing him any info via sockets. here is the system I'm currently using I didn't put all my code but I tried to summarize the essential parts:
const express = require('express');
const app = express();
//i creat the http server that is somehow linked with my express app when this server is listening
//it will call express handling methods.
const http = require('http').Server(app);
const io = require('socket.io')(http);
const path = require("path");
const passport = require("passport");
const connectEnsureLogin = require('connect-ensure-login');
const AccountInfo = require("./AccountInfo").AcccountInfo;
const expressSession = require('express-session')({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false
});
//passport setup
passport.use(AccountInfo.createStrategy());
passport.serializeUser(AccountInfo.serializeUser());
passport.deserializeUser(AccountInfo.deserializeUser());
//body parser
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
//Sessions
app.use(expressSession);
//!!!!here is where I connect socket.io with the sessions i found this in another forum.
// thanks to this code I can access the session that a client is using when their socket connects.
io.use(function(socket, next) {
expressSession(socket.request, socket.request.res, next);
});
//so when a clients socket connects i save his socket.id to his session.
io.on('connection',function(socket) {
console.log(`socket.io connected: ${socket.id}`);
// save socket.io socket in the session
socket.request.session.socketio = socket.id;
socket.request.session.save();
});
//once the clients socket is connected directly after the clients sends a HTTP "PUT" request
//and this code answers it.
app.post('/Table/ConnectSocketToTable',Utilities.ensureLoggedIn(),function(req, res)
{
//I retrieve the socket using the socket.id I had saved in the session.
let socket = io.sockets.sockets.get(req.session.socketio);
let player = GetPlayerFromAnyTable(req.user.username);
if(player==null)//the player can't be in two tables at once
{
//since now we are in an express callback, express made sure that the client is indeed
//authenticated with the middle-ware: "Utilities.ensureLoggedIn()" also just before I made sure
//the client is not in another table. So we are good to go we can now link the socket to the table
//and have the client receive all the info about the state of his table
socket.join("table-"+req.session.passport.table);
req.user.socket = socket;
let table = GetTable(req.session.passport.table);
table.sitPlayer(req.user);
}
else
{
//the player is already connected so we just update his socket to a new one
player.requestUnseat=false;
player.account.socket =io.sockets.sockets.get(req.session.socketio);
}
socket.on('chatMessage', function(data,time) {
socket.to("table-"+req.session.passport.table).emit("chatMessage",req.user.username,data,time);
console.log(`send chat message : ${data}`);
});
socket.on('disconnect', function() {
GetTable(req.session.passport.table).requestUnsitUsername(req.user.username);
console.log(req.user.username +" was disconnected so now requesting unsit");
});
console.log("the socket of "+req.user.username+" has being connected to table-"+req.session.passport.table);
return res.sendStatus(200);
});
So for me, the way I'm doing this seems pretty bad since "app.post('/Table/ConnectSocketToTable'...)" and "io.on('connection',...)" are two different request listening functions I feel I should probably just do everything in one.
So should I do all the checks in the "io.on('connection',...)" function and somehow manage to make sure the client is authenticated within the callback of io.on('connection',callback) ?
or should I find a way to make the socket connection happen in the initial HTTP call the client uses to join a table, which is what I initially wanted?
But really I'm kinda lost because I'm telling myself maybe I don't even need Express anymore and I should just use socket.io for everything. I seem to clearly lack the general understanding that would allow me to know what approach I should be going for so any help is welcome. I started doing this self-made exercise to get into server-side development but also if there is any other recommended exercise to start up with back-end development I'm definitely interested in hearing about it.
From random testing I found out how to authenticate to my express session from the socket code you don't actually have to do it in the callback of io.on('connection',callback) you just need to add a few more middleware functions like this:
//connecting express sessions
io.use(function(socket, next) {
expressSession(socket.request, socket.request.res, next);
});
//connecting passport
io.use(function(socket, next) {
passport.initialize()(socket.request, socket.request.res, next);
});
//connecting passport sessions
io.use(function(socket, next) {
passport.session()(socket.request, socket.request.res, next);
});
//check if client is authenticated returns error if authentication failed
io.use((socket, next) => {
console.log("started socket Connection");
if(!socket.request.isAuthenticated&&socket.request.isAuthenticated())
{
socket.request.session.socketio = socket.id;
socket.request.session.save();
console.log("table "+socket.request.session.passport.table);
console.log("user.username "+socket.request.user.username);
console.log(`is authentificated`);
next();
}
else
{
console.log(`failed socket connection`);
next(new Error("unauthorized"));
}
});```

How to secure a simple GET request using Express js

I have build a simple web server using Express js. There I have one GET request to send any json response. Now this request can be accessed from anywhere by anyone.
How can I restrict this GET request from having public access and what approach should I follow to restrict this public access?
Please note, I don't have the login or logout feature, only simple GET request.
Below is my code ---
const express = require('express');
const app = express();
app.get('/', (req, res) => { res.send('Test response'); });
app.listen(3000, () => console.log('Listening on port 3000!'));
There are multiple ways to secure a route. One way can be IP whitelisting.
So basically, you can give particular IPs access to the route. For that you can use express-ipfilter
// Init dependencies
const express = require('express')
const ipfilter = require('express-ipfilter').IpFilter
// Whitelist the following IPs
const ips = ['127.0.0.1']//add the IPs here
// Create the server
app.use(ipfilter(ips, { mode: 'allow' }))
app.get('/', (req, res) => { res.send('Test response'); });
app.listen(3000, () => console.log('Listening on port 3000!'));
There are countless ways to give access to certain person your route:
Private key encryption, sharing a secret key with someone you want access. Whenever your route is called you check the secret key
Public key, You can share your certificate with them, they need to pin the certificate in their request module and hit the route etc.

Make a cookie when a user connects with socket. io - node js

have a server that uses socket.io. When a user connects it will assign them the user id thats made on the server then increment it by 1 so the next user with have a different id.
I want to use cookies for this, to check if they have previously logged in, if so, use that id, if not, use the one on the server.
the way to create a cookie is by using
res.cookie('cookie', 'monster')
but im not where i would put it, i tried putting it in the connect function but res wouldnt exist. and if i put it outside the function, how would i call it? Here is my code. This is the start of my server:
//Require npm modules
var express = require('express');
var http = require('http');
var events = require('events');
var io = require('socket.io');
var ejs = require('ejs');
var app = express();
//Set the default user Id to 1 and the default username to Guest
exports.Server = Server = function()
{
this.userId = 1;
this.userName = "Guest";
};
app.set('view engine', 'ejs');
app.get('/game/:id', function (req, res)
{
res.render('game', {game: req.params.id});
});
Server.prototype.initialise = function(port)
{
//Create the server using the express module
this.server = http.createServer(app);
//Declare the 'public' folder and its contents public
app.use(express.static('public'));
//Listen to any incoming connections on the declared port and start using websockets
this.server.listen(port);
this.startSockets();
this.em = new events();
consoleLog('SERVER', 'Running on port: ' + port);
};
Server.prototype.startSockets = function()
{
//When a user connects to the server on the 'game' socket
this.socket = io.listen(this.server);
this.socket.of('game').on('connection', function(user)
{
res.cookie('cookie', 'monster')
//Set their usedId and username
user.userId = this.userId;
user.userName = this.userName + " " + this.userId;
//Increment the user id by 1 so each user with get a unique id
this.userId++;
//Send a response back to the client with the assigned username and user id and initialise them
user.emit('connected', user.userId, user.userName);
this.em.emit('initialiseUser', user.userId, user.userName);
So where i have the res.cookie is where i want to be able to read and write cookies, any help is appriciated
I think what you are looking for is the middleware pattern employed by express. You can define as many of these middleware calls as you wish, and they are the perfect scope for calling any other functions which may need the res instance (or the req instance for that matter).
app.use(function (req, res, next) {
// call function, passing in res here
next();
})
Reference: https://expressjs.com/en/guide/using-middleware.html
EDIT:
This answer is not correct for your situation. In a node/express server not using socket connections, then yes, you could easily use the above pattern anywhere you need the request and response objects in scope.
However, once you setup the socket io server, the game changes. During the socket communications, there are no express request and response objects in scope anymore, everything is handled directly between your socket handling code and the client. So the answer is you need to handle the situation in a socket io way, not in an express way.
Please see: Adding a cookie value on Socket.IO

Websocket server on subdomain only

I'm trying to have a websocket server on a subdomain so the client would point to something like 'ws://ws.mydomain.com'.
I'm using the subdomain module to handle normal get requests to subdomains but not sure how to consolidate the two. Any ideas on how I can approach this?
The WebSocketServer can take a server object, but can't figure it out.
var WebSocketServer = require('ws').Server
var express = require('express');
var subdomain = require('subdomain');
var app = express();
var wss = new WebSocketServer({port: 8081, server: http.Server});
app.use(subdomain({ base: 'mydomain.com', removeWWW: true}));
wss.on('connection', function(ws){
console.log('a connection!');
});
app.get('/subdomain/unrelatedsub', function(req, res){
res.send("hello unrelated subdomain page");
});
app.listen(80);
Link to WS documentation
From the web socket protocol:
The WebSocket Protocol attempts to address the goals of existing
bidirectional HTTP technologies in the context of the existing HTTP
infrastructure; as such, it is designed to work over HTTP ports 80
and 443 as well as to support HTTP proxies and intermediaries, even
if this implies some complexity specific to the current
environment.
Express 3 exposes the app as a request handler, you must instantiate a http.Server first which you can pass the express app into, and then you setup your sockets, as the ws:// protocal can share the HTTP port you listen on, just as wss:// would share the HTTPS port.
Try something closer to this, I will test when get a chance if you haven't responded:
var WebSocketServer = require('ws').Server
var express = require('express');
var http = require('http')
var app = express();
app.use(subdomain({ base: 'mydomain.com', removeWWW: true}));
var server = http.createServer(app);
server.listen(8080);
var wss = new WebSocketServer({server: server});
wss.on('connection', function(ws){
console.log('a connection!');
});
app.get('/subdomain/unrelatedsub', function(req, res){
res.send("hello unrelated subdomain page");
});

Categories

Resources