Socket.io: io.on('connection) repeats three times - node.js

The following is an excerpt from some server side code located in the entry point of the application:
const app = express()
const chatServer = require('http').Server(app);
chatServer.listen(3000)
const io = require('socket.io')(chatServer);
io.on('connection', (socket) => {
console.log('ABC')
socket.on('send-chat-message', message => {
socket.broadcast.emit('chat-message', message)
})
});
Upon running my dev server I expected to see 'ABC' print once to the console. However, it is printing three times (ABC, ABC, ABC) instead. Why does this happen? The io variable is NOT used anywhere else in the application. I'm pretty sure it has nothing to do with client side logic (since no requests are made by the client at this point) And for complete reference, the entire app.js code is displayed below:
const express = require('express')
require('./db/mongoose')
const playerRouter = require('./routers/player')
const contractRouter = require('./routers/contract')
const bodyParser = require('body-parser');
const hbs = require('express-handlebars')
const path = require('path')
const passport = require('passport');
const flash = require('connect-flash');
const session = require('express-session');
// Define paths for Express config
const viewsPath = path.join(__dirname, '../templates/views')
const partialsPath = path.join(__dirname, '../templates/partials')
const layoutPath = path.join(__dirname, '../templates/layouts')
const app = express()
const chatServer = require('http').Server(app);
chatServer.listen(3000)
const io = require('socket.io')(chatServer);
// Passport Config
require('./middleware/passport')(passport);
//Setup handlebars engine and views location
app.engine('hbs', hbs({
extname: 'hbs',
defaultLayout: 'main',
layoutsDir: layoutPath,
partialsDir: partialsPath,
}));
app.set('view engine', 'hbs')
app.use(express.static('public'))
app.set('views', viewsPath)
io.on('connection', (socket) => {
console.log('ABC')
socket.on('send-chat-message', message => {
socket.broadcast.emit('chat-message', message)
})
});
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.json())
// Express session
app.use(
session({
secret: 'secret',
resave: true,
saveUninitialized: true
})
);
// Passport middleware
app.use(passport.initialize());
app.use(passport.session());
// Connect flash
app.use(flash());
// Global variables
app.use(function (req, res, next) {
res.locals.success_msg = req.flash('success_msg');
res.locals.error_msg = req.flash('error_msg');
res.locals.error = req.flash('error');
res.locals.currentUser = req.user
next();
});
app.use(playerRouter)
app.use(contractRouter)
module.exports = app
Here is the client side code (for brevity I did not include the entire HTML file):
const socket = io('http://localhost:3000')
const messageContainer = document.getElementById('message-container')
const messageForm = document.getElementById('send-container')
const messageInput = document.getElementById('message-input')
socket.on('chat-message', message => {
appendMessage(`Opponent: ${message}`)
})
messageForm.addEventListener('submit', e => {
e.preventDefault()
const message = messageInput.value
appendMessage(`You: ${message}`)
socket.emit('send-chat-message', message)
messageInput.value = ''
})
const appendMessage = (message) => {
const messageElement = document.createElement('div')
messageElement.innerText = message
messageContainer.append(messageElement)
}

Sure, the message 'ABC' is rendered every time a connection to the client is established.
const express = require("express");
const path = require("path");
const app = express();
const http = require("http").Server(app);
const io = require("socket.io")(http);
// Here declare fiel static, as index.html, css, javascript.
app.use(express.static(path.resolve(__dirname, "../public")));
io.on("connection", socket => {
console.log("ABC");
socket.on("send-chat-message", message => {
socket.broadcast.emit("chat-message", message);
});
});
http.listen(8080, () => {
console.log("Starting...");
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
</head>
<body>
<div id="message-container"></div>
<form id="send-container">
<input id="message-input" type="text" />
<button type="submit">submit</button>
</form>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js"></script>
<script>
const socket = io();
const messageContainer = document.getElementById("message-container");
const messageForm = document.getElementById("send-container");
const messageInput = document.getElementById("message-input");
socket.on("chat-message", message => {
appendMessage(`Opponent: ${message}`);
});
messageForm.addEventListener("submit", e => {
e.preventDefault();
const message = messageInput.value;
console.log(message);
appendMessage(`You: ${message}`);
socket.emit("send-chat-message", message);
messageInput.value = "";
});
const appendMessage = message => {
const messageElement = document.createElement("div");
messageElement.innerText = message;
messageContainer.append(messageElement);
};
</script>
</body>
</html>
Here you can see the code working live.
I hope it helps. Any questions comment.
https://codesandbox.io/s/expressjs-chat-9v8lo

Related

Problems configuring server

I'm just learning how to use Node.JS and I'm trying to set up an express server, but when I try to test the route and do the GET request in Postman it throws an error. What is wrong with my code below?
const express = require("express");
const app = express();
const http = require("http");
const server = http.createServer(app);
const logger = require("morgan");
const cors = requiere("cors");
const port = process.env.PORT || 3000;
app.use(logger("dev"));
app.use(express.json());
app.use(express.urlencoded({
extended: true
}));
app.use(cors());
app.disable("x-powered-by");
app.set("port", port);
server.listen(3000, "192.168.1.36" || "localhost", function() {
console.log("aplicacion de NodeJS " + port +" iniciada...")
});
app.get("/", (req, res) => {
res.send("Ruta raíz del backend");
});
// ERROR HANDLER
app.use((err, req, res, next) => {
console.log(err);
res.status(err.status || 500).send(err.stack);
});
What Postman tells me does not clarify much where the error is:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error</title>
</head>
<body>
<pre>Cannot GET /</pre>
</body>
</html>
you made a typo here requiere("cors") it must be require and also you must import express const express = require('express')
const express = require('express')
const app = express();
const logger = require("morgan");
const port = process.env.PORT || 3001;
app.use(logger("dev"));
app.use(express.json());
app.use(express.urlencoded({
extended: true
}));
app.listen(port, function () {
console.log("aplicacion de NodeJS " + port + " iniciada...")
});
app.get("/", (req, res) => {
res.send("Ruta raíz del backend");
});

Post request to Express.js server in postman gives the following error

When I make this post request on postman -
http://localhost:5000/api/edit/links?editData={"editData"[{"link":"www.github.com/","name":"link name"},{"name":"link name","link":"linkdata"}]}
I get the following error:
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error</title>
</head>
<body>
<pre>Cannot POST /api/edit/links</pre>
</body>
</html>
My server Index file looks like this:
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
const cors = require('cors');
require('dotenv').config();
require('./db/connectDB');
//Import routes
const authRoute = require('./routes/auth');
const editRoute = require('./routes/edit');
const publicRoute = require('./routes/public');
//Middleware
app.use(express.json());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
//Route Middlewares
app.use(cors());
app.use('/api/user', authRoute);
app.use('/', publicRoute);
const PORT = 5000;
app.listen(PORT, () => console.log('server is up and running '));
The route I'm trying to reach is in this file
edit.js
const router = require('express').Router();
const verify = require('./verifyToken');
const {editLinksValidation} = require('../validation');
const ObjectId = require('mongoose').Types.ObjectId;
const User = require('../model/User');
const fetchFavicon = require('#meltwater/fetch-favicon').fetchFavicon;
router.post('/api/edit/links/:editData', verify, (req, res) => {
const editData = JSON.parse(req.params.editData);
User.updateOne(
{_id: id},
{
$set:
{
links: editData
}
}
).then(metadata => res.send(metadata))
.catch(err => console.log(err));
});
What am I doing wrong here?
As the error says, there is no route defined for /api/edit/links for the POST method. And indeed your code shows that your route is /api/edit/links/:editData -- that last path element cannot be empty. Not sure what your intention was with that, but I have the hunch that your intention was to get the value from the query instead, in which case you'd want to use:
router.post('/api/edit/links/', verify, (req, res) => {
const editData = JSON.parse(req.query.editData);
You don't use imported editRoute in the server index file at all.
I suppose you should add this line:
app.use('/', editRoute);

Cannot start NodeJS Server with Socket.io

So, I have the simple dream to start the Server with Socket.io library, but for some reasons it's does not happen... I cannot understand what is wrong, because I do not gets even the standart message on server run - Server running on port ${port}.
Help, please...
'use strict'
const express = require('express');
const http = require('http');
const io = require('socket.io');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser');
const bcrypt = require('bcrypt-nodejs');
const session = require('express-session');
const MongoStore = require('connect-mongo')(session);
const app = express();
const server = http.Server(app);
const socketServer = io(server);
const router = express.Router();
const chat = require('./routes/chat');
const profile = require('./routes/profile');
const registration = require('./routes/registration');
const login = require('./routes/login');
const logout = require('./routes/logout');
const EmployersSchemaDB = require('./SchemaDB/EmployersSchemaDB');
const MessagesSchemaDB = require('./SchemaDB/MessagesSchemaDB');
const User = require('./SchemaDB/ShemaAuthtificaion');
mongoose.connect('mongodb://test_db');
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(cookieParser());
app.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:3000');
res.setHeader('Access-Control-Allow-Credentials', 'true');
res.setHeader('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE');
res.setHeader('Access-Control-Allow-Headers', 'Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers');
res.setHeader('Cache-Control', 'no-cache');
next();
});
app.use(session({
secret: 'work hard',
resave: true,
saveUninitialized: false,
store: new MongoStore({ mongooseConnection: mongoose.connection })
}));
router.route('/')
.get((req, res) => {
res.json('Hello on the Homepage!');
});
app.use('/', router);
app.use('/chat', chat);
app.use('/profile', profile);
app.use('/auth/login', login);
app.use('/auth/logout', logout);
app.use('/auth/registration', registration);
const port = process.env.API_PORT || 8989;
socketServer.listen(port, () => {
console.log(`Server running on port ${port}`);
});
Change from this:
socketServer.listen(port, () => {
console.log(`Server running on port ${port}`);
});
to this:
server.listen(port, () => {
console.log(`Server running on port ${port}`);
});
You need to start your http server. That's the core server here that works with both Express and socket.io and fields all incoming requests.
Your variable socketServer and your io variable are misnamed (which is probably part of your confusion). socketServer is the actual server, that's the socket.io server-side instance which is typically named io. See the socket.io server doc for details.
You should start with a simpler server:
const express = require('express');
const socketio = require('socket.io');
const http = require('http');
const app = express();
const server = http.createServer(app);
const socket = socketio(server);
app.use(express.static(`./public`));
socket.on('connect', socket => {
socket.emit('identify', {'server':'server data'});
});
socket.on('data', data => {
console.log(data);
});
server.listen(4000, () => {
console.log('Server up and running on port 4000');
});
In the front end you can test this like this:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Test</title>
</head>
<body>test
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.1.1/socket.io.js"></script>
<script type="text/javascript">
'use strict';
var socket = io('http://localhost:4000');
socket.on('identify', function (socket) {
console.log(socket);
});
</script>
</body>
</html>

Run socket.io 2.0 and Express 4.0+ on the same port

I have installed socket.io before on a Node project without the need of express templating framework.
Issue
if I add this line of code: http.listen(3001, function() { console.log('listening on port 3001'); }); then on visiting localhost:3000 I have my app in which sockets does not work and on localhost:3001 I have sockets working. How do I merge both of these?
Error
socket.io.js:2 GET http://localhost:3000/socket.io/?EIO=3&transport=polling&t=LpQ6zKP net::ERR_CONNECTION_REFUSED
I am trying to add socket.io in express framework like below:
app.js file
const express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var expressValidator = require('express-validator');
var cookieParser = require('cookie-parser');
var session = require('express-session');
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var bodyParser = require('body-parser');
var flash = require('connect-flash');
var multer = require('multer');
var upload = multer({ dest: './uploads' });
var mongo = require('mongodb');
var mongoose = require('mongoose');
var db = mongoose.connection;
var index = require('./routes/index');
var app = express();
var http = require( "http" ).createServer( app );
var io = require( "socket.io" )( http );
http.listen(3000, "127.0.0.1");
var httpk = require('http');
var nsp = io.of('/channel1');
var connectCounter = 0;
var interval = undefined;
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.use('/js', express.static(__dirname + '/node_modules/bootstrap/dist/js')); // redirect bootstrap JS
app.use('/js', express.static(__dirname + '/node_modules/jquery/dist')); // redirect jQuery JS
app.use('/css', express.static(__dirname + '/node_modules/bootstrap/dist/css')); // redirect Bootstrap CSS
// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
//Handle express sessions
app.use(session({
secret:'secret',
saveUninitialized: true,
resave:true
}));
// Passport
app.use(passport.initialize());
app.use(passport.session());
// Validator
app.use(expressValidator({
errorFormatter: function(param, msg, value) {
var namespace = param.split('.')
, root = namespace.shift()
, formParam = root;
while(namespace.length) {
formParam += '[' + namespace.shift() + ']';
}
return {
param : formParam,
msg : msg,
value : value
};
}
}));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
//For flash messages
app.use(flash());
app.use(function (req, res, next) {
res.locals.messages = require('express-messages')(req, res);
next();
});
app.get('*', function(req, res, next){
res.locals.user = req.user || null;
next();
});
app.use('/', index);
function test() {
httpk.get("api-url", function(res) {
var body = '';
res.on('data', function(data) {
body += data;
});
res.on('end', function() {
var parsed = JSON.parse(body);
//console.log(parsed.data.product1);
var dataArray = [];
dataArray.push((parseFloat(parsed.data.product1) + Math.random() * 0.1 + 0.01).toFixed(2));
dataArray.push((parseFloat(parsed.data.product2) + Math.random() * 0.1 + 0.01).toFixed(2));
console.log(dataArray);
nsp.emit('live-quote', dataArray);
});
});
}
nsp.on('connection', function(socket) {
socket.on('pass_data', function(my) {
console.log(my);
});
//Make a http call
connectCounter++;
if (interval === undefined) interval = setInterval(test, 1000);
nsp.emit('live-users', connectCounter);
console.log('1 user connected, Total Joined: ' + connectCounter);
socket.on('disconnect', function() {
connectCounter--;
if (connectCounter <= 0 && interval !== undefined) interval = clearInterval(interval);
nsp.emit('live-users', connectCounter);
console.log('1 user disconnected, Total Left: ' + connectCounter);
});
console.log("total clients: " + io.engine.clientsCount);
});
// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;
head.ejs
<title><%= title %></title>
<link rel="stylesheet" href="/css/bootstrap.min.css">
<script src="/js/jquery.min.js"></script>
<script src="/js/bootstrap.min.js"></script>
<link rel='stylesheet' href='/stylesheets/style.css' />
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" >
jsdefaults.ejs
<script src="/javascripts/script.js" ></script>
<script src="/socket.io/socket.io.js"></script>
<script src="https://code.jquery.com/jquery-1.11.1.js"></script>
<script>
$(function() {
var socket = io('/channel1');
var currentTime;
socket.on('live', function(msg) {
console.log("msg: "+msg);
});
socket.on('live-users', function(users) {
$('#total').text(users);
});
});
</script>
index.ejs
<!DOCTYPE html>
<html>
<head><% include partials/head.ejs %></head>
<body>
<% include partials/header.ejs %>
<div class="container" style="margin-top:20px;">
</div>
<% include partials/footer.ejs %>
<% include partials/jsdefaults.ejs %>
</body>
</html>
It looks like you used express-generator to set up your Express application, in which case the HTTP server will be set up in bin/www and that's where the socket.io server should be set up as well (since you want to share the HTTP server between Express and socket.io).
The default bin/www contains this:
/**
* Create HTTP server.
*/
var server = http.createServer(app);
That's where you add the socket.io server:
var server = http.createServer(app);
var io = require('socket.io')(server);
var nsp = io.of('/channel1');
nsp.on('connection', ...);

Invalid Token using expressjs csurf middleware example

I've tried using the expressjs csurf example from https://github.com/expressjs/csurf When using the first example in the Readme, (using Ejs template language), the token validation works fine. When I try using the 'Ignoring Routes' example, on the 'GET /form' to 'POST /process' execution(just as I did in the first example), I get 'invalid token' on the 'POST /process'. The token is being passed to the form on the GET. Any ideas?
Is 'app.use(csrfProtection)' not working? (used in the non working example, if I remove the 'use(csrfP..' and use the methodology from the working example to use the csrf module, IE, passing 'csrfProtection' to the 'get' and 'post' methods, the second example works)
Works:
var cookieParser = require('cookie-parser')
var csrf = require('csurf')
var bodyParser = require('body-parser')
var express = require('express')
// setup route middlewares
var csrfProtection = csrf({ cookie: true })
var parseForm = bodyParser.urlencoded({ extended: false })
// create express app
var app = express()
app.set('view engine', 'ejs')
// parse cookies
// we need this because "cookie" is true in csrfProtection
app.use(cookieParser())
app.get('/form', csrfProtection, function(req, res) {
// pass the csrfToken to the view
var tkn = req.csrfToken()
console.log(tkn)
res.render('index', { csrfToken: tkn })
})
app.post('/process', parseForm, csrfProtection, function(req, res) {
res.send('data is being processed')
})
var server = app.listen(8081, function () {
var host = server.address().address
var port = server.address().port
console.log("Example app listening at http://%s:%s", host, port)
})
html/ejs:
<!DOCTYPE html>
<html lang="en">
<head>
</head>
<body>
<form action="/process" method="POST">
<input type="hidden" name="_csrf" value="<%= csrfToken %>">
Favorite color: <input type="text" name="favoriteColor">
<button type="submit">Submit</button>
</form>
</body>
</html>
Does not work:
var cookieParser = require('cookie-parser')
var csrf = require('csurf')
var bodyParser = require('body-parser')
var express = require('express')
// setup route middlewares
var csrfProtection = csrf({ cookie: true })
var parseForm = bodyParser.urlencoded({ extended: false })
// create express app
var app = express()
app.set('view engine', 'ejs')
// parse cookies
// we need this because "cookie" is true in csrfProtection
app.use(cookieParser())
// create api router
var api = createApiRouter()
// mount api before csrf is appended to the app stack
app.use('/api', api)
// now add csrf, after the "/api" was mounted
app.use(csrfProtection)
app.get('/form', function(req, res) {
// pass the csrfToken to the view
var tkn = req.csrfToken()
console.log(tkn)
res.render('index', { csrfToken: tkn })
})
app.post('/process', parseForm, function(req, res) {
res.send('csrf was required to get here')
})
function createApiRouter() {
var router = new express.Router()
router.post('/getProfile', function(req, res) {
res.send('no csrf to get here')
})
return router
}
var server = app.listen(8081, function () {
var host = server.address().address
var port = server.address().port
console.log("Example app2 listening at http://%s:%s", host, port)
})
In your second example, you are not passing the csrfProtection middleware to the POST processing chain. It should be
app.post('/process', parseForm, csrfProtection, function(req, res) {
res.send('csrf was required to get here')
})

Resources