skip to Main Content

I am getting started with node.js/express/socket.io and am having issues making the whole thing work. Not sure it is important but I am using an Express4 TypeScript project from Visual Studio 2022.

Apologies in advance for the long question but I spent a long time doing investigations and sharing what I found out so far may very well save you some time.

The symptoms are:

  • Chrome + MS Edge: I have the below 3 error messages (although only the first 2 are about socket.io)
    3 error messages
  • Firefox : Only the uncaught ReferenceError is there.
    Single error message

In all 3 browsers, http://localhost:3000/socket.io/socket.io.js shows the scripts successfully. The error/warning mention retrieving that file through port 1337 though.

That leads me to 2 questions:

  1. How come the Chromium-based browsers report they are unable to find the socket.io script file whereas Firefox does not?
  2. What can I do to make the errors disappear.
    I suppose, maybe incorrectly, that the uncaught ReferenceError is related to that line in the webpage: var socket = io();

Notes:
Because of the starting sentence of Handling CORS ("Since Socket.IO v3, you need to explicitly enable Cross-Origin Resource Sharing (CORS)."), I checked the change log page and figured my attempt with socket.io years ago, from which the below code was copied, was on socket.io v2. That would explain why it used to work, but no more does (additionally, the listening channel was on var port = process.env.PORT || 3000;, which now makes node.js crash completely with EADDRINUSE: address already in use).

I visited many questions on SO, socket.io and elsewhere, to no avail. I did not check when browsing but I assume most are not applicable to socket.io v3 and later.

Here is my code (developed under Visual studio):

index.ts

import express = require('express');

var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var port = 3000;

app.get('/', function (req, res) {
    res.sendFile(__dirname + '/index.html');
});

io.on('connection', function (socket) {
    console.log('New user connected');
    socket.on('disconnection', function () {
        console.log('User disconnected');
    });
    socket.on('chat message', function (msg) {
        io.emit('chat message', msg);
    });
});

http.listen(port, function () {
    console.log('listening on *:' + port);
});

const router = express.Router();
router.get('/', (req: express.Request, res: express.Response) => {
    res.render('index', { title: 'Express' });
});
export default router;

index.html (generated from a .pug file)

<!DOCTYPE html>
<html>
<head>
    <title>Express</title>
    <link rel="stylesheet" href="/stylesheets/main.css">
</head>
<body>
    <ul id="messages"></ul>
    <form id="form" action="">
        <input id="input" autocomplete="off">
        <button>Send</button>
    </form>
    <script src="/socket.io/socket.io.js"></script>
    <script>
        var socket = io()            ;
        var form = document.getElementById('form');
        var input = document.getElementById('input');

        form.addEventListener('submit', function(e) {
          e.preventDefault();
          if (input.value) {
            socket.emit('chat message', input.value);
            input.value = '';
          }
        });
    </script>
</body>
</html>

Edit: What I am really after is astandalone solution.

4 years and 2 major version later, the socket.io website suggests this is still possible, see:

  • Handling CORS
    That page starts with the following sentence:

    Note: this also applies to localhost if your web application and your server are not served from the same port

    const io = new Server(httpServer, {
      cors: {
        origin: "http://localhost:8080"
      }
    });
    
    httpServer.listen(3000);
    
  • AFAICT, the client code (here) could not be more straightforward and appears to match my code too.

    <script src="/socket.io/socket.io.js"></script>
    <script>
      const socket = io();
    </script>
    

I could not find a mention of a weird trick to put things together. Yet,
I tried to modify my server code as above (with cors: { origin: http://localhost:8080"} and cors: { origin: "http://localhost:1337" } since the latter is the port my http server listen to), I still get error 404.

2

Answers


  1. The main problem is the browser unable to load the socket.io library.

    Replace :

    <script src="/socket.io/socket.io.js"></script>
    

    with

    <script src="https://cdn.socket.io/4.7.2/socket.io.min.js"></script>
    

    as recommended in https://socket.io/get-started/chat#integrating-socketio or implement a way to get the local file in your node server.

    Login or Signup to reply.
  2. error 404 indicated that the browser is unable to find the socket.io.js file.

    Socket.io server exposes its clientside js file, try using that.

    here is your client.

    <!DOCTYPE html>
    <html>
    <head>
        <title>Express</title>
    </head>
    <body>
        <ul id="messages"></ul>
        <form id="form" action="">
            <input id="input" autocomplete="off">
            <button>Send</button>
        </form>
        <script src="http://localhost:3000/socket.io/socket.io.js"></script>
        <script>
            var socket = io('http://localhost:3000', {
                transports: [ 'websocket' ],
                'reconnection': true,
                'reconnectionDelay': 500,
                'reconnectionDelayMax': 1000,
                'reconnectionAttempts': 100
            });
            socket.on('connect', onConnect);
            socket.on('disconnect',onDisconnect);
    
            function onConnect(){
                console.log('Connected Successully...'); 
            }
            function onDisconnect(){
                console.log('Client disconnected');
            }
    
            var form = document.getElementById('form');
            var input = document.getElementById('input');
    
            form.addEventListener('submit', function(e) {
              e.preventDefault();
              if (input.value) {
                socket.emit('chat message', input.value);
                input.value = '';
              }
            });
        </script>
    </body>
    </html>

    regarding CORS, you should implement CORS and for production maybe you want to implement further security then use the helmet(or ignore the helmet and remove it)

    const express = require('express');
    
    var app = require('express')();
    var http = require('http').Server(app);
    var io = require('socket.io')(http);
    var port = 3000;
    
    const cors = require('cors');
    const helmet = require("helmet");
    
    app.use(cors({
        origin: ['http://localhost:3000/','https://your-host.com/', 'https://your-host-2.com/']
    }));
    
    // if you need to implement a content security policy and want to override
    app.use(
        helmet({
            contentSecurityPolicy: {
                directives: {
                    frameAncestors: [
                        'https://your-host.com/',
                        'https://your-host-2.com/'
                    ]
                }
            },
            frameguard: false,
            crossOriginResourcePolicy: { policy: "cross-origin" },
            crossOriginEmbedderPolicy: false,
        })
    );
    
    app.use((req,res,next) =>{
        //res.header('Access-Control-Allow-Origin','*');
        res.header('Access-Control-Allow-Headers','origin, x-requested-with, Content-Type, Accept, Authorization');
        if(req.method==='OPTIONS'){
            req.header('Access-Control-Allow-Methods','GET,POST,PATCH,DELETE,PUT');
            return res.status(200).json({});
        }
        next();
    });
    
    
    app.get('/', function (req, res) {
        res.sendFile(__dirname + '/index.html');
    });
    
    io.on('connection', function (socket) {
        console.log('New user connected');
        socket.on('disconnection', function () {
            console.log('User disconnected');
        });
        socket.on('chat message', function (msg) {
            io.emit('chat message', msg);
        });
    });
    
    http.listen(port, function () {
        console.log('listening on *:' + port);
    });
    
    const router = express.Router();
    router.get('/', (req,res) => {
        res.render('index', { title: 'Express' });
    });
    //export default router;

    in the server pay attention to a commented line

    //res.header('Access-Control-Allow-Origin','*');

    for testing only (not for production), you can simply ignore everything and enable this to avoid all CORS and security.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search