socketio / socket.io

Realtime application framework (Node.JS server)
https://socket.io
MIT License
61.22k stars 10.12k forks source link

Frequent connections and disconnections after an existing connection broken due to network issues #3938

Closed fahadpf closed 3 years ago

fahadpf commented 3 years ago

Describe the bug If I turn off my WiFI for a second and then turn it ON again, my socket.io-client gets stuck in some kind of connection and disconnection loop. I can see the console logs for successful reconnection but then suddenly my connection closes with the reason "transport close". After this, reconnection attempts are again started. I have attached a screenshot from my browser console here:

Screenshot of Chrome DevTools

At first, I thought that this might be some server issue because I first encountered this issue when I restarted my socket server. My connected clients became unable to connect with this restarted server and there started a never-ending loop of connections and disconnection. When I tried to debug the issue, I found out that my handshake requests were being allowed but my request was not going any further. Here is the screenshot of my server logs:

Screenshot of iTerm

To Reproduce

Please fill in the following code example:

Socket.IO server version: 4.1.2

Server

const httpServer = require('http').createServer();
const { Server } = require('socket.io');

const ioOptions = {
    allowEIO3: true,
    cors: {
        origin(origin, callback) {
            const isOriginAllowed = originsWhitelist.some((o) => origin.includes(o));
            if (isOriginAllowed) {
              callback(null, true)
            } else {
              callback(new Error('Not allowed by CORS'))
            }
          }
    },
    allowRequest(req, callback) {
        const { origin } = req.headers;
        const isOriginAllowed = originsWhitelist.some((o) => origin.includes(o));

        if (isOriginAllowed) {
            console.log(`Origin allowed: ${origin}`);
            callback(null, true);
        } else {
            console.log(`Origin declined: ${origin}`);
            callback('Request origin not allowed');
        }
    },
};

const io = new Server(httpServer, ioOptions);

// a middleware for validating incoming request's session
io.use(async (socket, next) => {
    const { sessionID } = socket.handshake.auth;

    // find existing session
    if (sessionID) {
        const session = sessionStore.findSession(sessionID);
        if (session) {
            socket.sessionID = sessionID;
            socket.userID = session.userID;
            return next();
        }
    }

    // else, create a new session
    socket.sessionID = await generateRandomId();
    socket.userID = await generateRandomId();
    next();
});

httpServer.listen(process.env.SOCKET_SERVER_PORT, () => {
    console.log(`Server started listening at port ${process.env.SOCKET_SERVER_PORT}`);
});

io.sockets.on("connection", (socket) => {
  console.log(`connect ${socket.id}`);

  socket.on("disconnect", () => {
    console.log(`disconnect ${socket.id}`);
  });
});

Socket.IO client version: 4.1.2

Client

import SocketIO from 'socket.io-client';
import VueSocketIO from 'vue-socket.io';

const ioOptions = { // Socket.io Manager options
    path: '/v1.2', // My socket.io server is working from behind a reverse proxy (Nginx)
    autoConnect: false, // we shall make this connection when user's auth session is validated
    transports: ['websocket', 'polling'],
    reconnectionAttempts: process.env.NODE_ENV === 'development' ? 3 : Infinity,
};

Vue.use(new VueSocketIO({
    debug: false,
    connection: SocketIO(config.locations.BASE_URI, ioOptions),
    vuex: {
        store,
        actionPrefix: 'SOCKET_',
        mutationPrefix: 'SOCKET_',
    },
}));

Then, in my Vue.js main app, I subscribe for socket emits as described in Vue-Socket.io package documentation:

new Vue({
    // ...
    sockets: {
        disconnect(reason) {
            this.$store.commit('SOCKET_CONNECT', false);
            console.log({ reason });
        },
        connect() {
            this.$store.commit('SOCKET_CONNECT', true);
        },
        connect_error(error) {
            console.log({ error });
        },
        session({ sessionID, userID }) {
            // Attach the session ID with app socket instance for next reconnection attempts
            this.$socket.auth = sessionID;

            // Also, store the same session ID in sessionStorage to persist session between page reloads
            sessionStorage.setItem('sessionID', sessionID);

            // Attach the returned user ID as well with socket instance
            this.$socket.userID = userID;
        },
    },
    // ...
});

Everything works as expected until I switch my network.

Expected behavior The socket client should automatically be connected to the server as soon as it comes online. This was the behavior before upgrading to v4 (v2.3.1)

Platform:

Additional context My socket.io client is reconnected when I refresh my webpage. But since I'm working on a SPA, reloading the page again and again does not seem to be a nice solution. Kindly suggest is this really some issue or am I missing something very important as I have been stuck on this issue for 2 days now. Thanks!

anshulnegitc commented 3 years ago

Can you change transport to WebSocket only and recheck the issue?

darrachequesne commented 3 years ago

@fahadpf Hi! Unfortunately, your example is not runnable as is, could you please try to reproduce by forking the fiddle here?

Can you change transport to WebSocket only and recheck the issue?

@anshulnegitc this is already the case with transports: ['websocket', 'polling']

fahadpf commented 3 years ago

Hey @darrachequesne, I'm not sure what exactly was my issue with this scenario. My server was actually running inside a Docker container behind a reverse proxy (Nginx). When I restarted my MacBook and restarted my Docker container, this issue didn't appear again.

I shall surely try to reproduce this issue if I ever face it again.