Home > Net >  Why is socketio server not processing my client request?
Why is socketio server not processing my client request?

Time:12-08

I am creating a node.js app with a react client and an express/socketio backend and passport authentication. I'm trying to emit a socketio event from the client and then have the backend respond but it seems that the request is getting lost somewhere between when it's emitted to when the backend processes it.

Below are all the files I thought relevant to the problem at hand. Backend is on localhost:5000 and client is on localhost:3000 As I said, everything goes well until the line marked in Organization.js. But at/after that line, the event is presumably sent but none of the corresponding routes in socket.js are hit.

Backend start file - app.js

'use strict';
import express from 'express'
import { createServer } from 'http'
import path from 'path'
import { fileURLToPath } from 'url';
import logger from 'morgan'
import cookieParser from 'cookie-parser'
import index from './routes/index.js'
import session from 'express-session'
import passport from './src/auth.js'
import { Server } from 'socket.io'
import { CrashCollector } from 'crashcollector'

new CrashCollector('crash_logs', (err, exit) => {
    console.log('Cleaning up...')
    console.log('Saving some stuff...')
    // Calls callback funtion executing process.exit()
    // You can also just write process.exit() here
    exit()
})

const app = express();
const server = createServer(app)
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const io = new Server(server, {
    cookie: { maxAge: 24 * 60 * 60 * 1000 },
    cors: {
        origin: 'http://localhost:3000',
        credentials: true
    }
})
const sessionMiddleware = session({
    secret: "changeit", resave: false,
    saveUninitialized: false
});



// uncomment after placing your favicon in /public
//app.use(favicon(__dirname   '/public/favicon.ico'));

app.use(sessionMiddleware)
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(passport.initialize());
app.use(passport.session());


app.use('/', index);

// catch 404 and forward to error handler
app.use(function (req, res, next) {
    let err = new Error('Not Found');
    err.status = 404;
    next(err);
});

// error handlers

// development error handler
if (app.get('env') === 'development') {
    app.use(function (err, req, res, next) {
        console.log('dev error handler')
        res.status(err.status || 500);
        res.end()
    });
}

// production error handler
// no stacktraces leaked to user
app.use(function (err, req, res, next) {
    console.log('prod error handler')
    res.status(err.status || 500);
    res.render('error', {
        message: err.message,
        error: {}
    });
});

app.set('port', process.env.PORT || 3000); //process.env.PORT = 5000


server.listen(app.get('port'), _ => {
    console.log(`Server listening on port ${app.get('port')}`)
})

export {
    io,
    sessionMiddleware
}

Backend - socket.js

import { io, sessionMiddleware } from '../app.js'
import passport from '../src/auth.js'
import { getSequel } from '../src/database.js'
const wrap = middleware => (socket, next) =>
    middleware(socket.request, {}, next);

io.use((_, next) => {
    console.log('connection attempt')
    next()
})
io.use(wrap(sessionMiddleware));
io.use(wrap(passport.initialize()));
io.use(wrap(passport.session()));

io.use((con, next) => {
    console.log('socket check', !!con.request.user)
    if (con.request.user) {
        next()
    } else {
        next(new Error('unauthorized'))
    }
});

io.on('connection', (con) => {
    console.log(`new connection ${con.id}`);
    con.on('confirm', cb => {
        cb(con.request.user.name);
    });

    con.on('eventTable', _ => {
        console.log('eventTable')
        con.emit(con.request?.user?.getEvents()?.map(e => e.toJSON()))
    })
});

Client start file - index.js

import React from 'react'
import { createRoot } from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import './index.css';
import App from './App/App';
import { io } from 'socket.io-client'
import configs from '../package.json'
const container = document.getElementById('root');
const root = createRoot(container);
export const socket = io.connect(configs.proxy, {  //configs.proxy = http://localhost:5000
    withCredentials: true
})
socket.on('connect', _ => {
    console.log('socket connect', socket.id)
})
socket.on('connect_error', _ => {
    console.log('socket connect_error')
})
socket.on('disconnect', _ => {
    console.log('socket disconnect')
})
socket.prependAny((eventName, ...args) => {
    console.log(eventName, args)
})

root.render((
    <React.StrictMode>
        <BrowserRouter>
            <App />
        </BrowserRouter>
    </React.StrictMode>
));

Client - App.js

import React from 'react'
import { Route, Routes } from 'react-router-dom'
import './App.css'
import Login from './pages/Login'
import Home from './pages/Home'
import LoginCheck from './helper/LoginCheck'

const App = () => {
    console.log('app')
    return (
        <Routes>
            <Route exact path="/" element={<LoginCheck />} >
                <Route index element={<Home />} />
            </Route>
            <Route path="/login" element={<Login />} />
        </Routes>
    )
}


export default App;

Client - Home.js

import React, { useState } from 'react';
import Org from './Organization'


const Home = () => {
    console.log('home')
    return localStorage.getItem('isOrg') ? (
        <Org />
    ) : (
        'Employee'
    )

}

export default Home;

Client - Organization.js

import React, { createElement, useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom'
import Countdown from 'ds-countdown'
import { socket } from '../../index'


const Org = () => {
    console.log('org')
        const setTable = (raffles) => {
        console.log('cb eventTable')
        JSON.parse(raffles).forEach(raffle => {
            const row = createElement('tr')
            const name = createElement('td')
            name.append(raffle.name)
            const num = createElement('td')
            num.append(raffle.num)
            const started = Date.parse(raffle.start) >= Date.now()
            const countdown =
                <><td>
                    <p style={{ color: started ? 'red' : 'green' }}>
                        {started ? 'Starts' : 'Ends'} in:
                        <div id={`timer${raffle.id}`} />
                    </p>
                </td></>
            const action = createElement('td')
            const edit = createElement('button')
            edit.addEventListener('click', e => {
                //goto edit at raffle.id
                alert('edit')
            })
            const end = createElement('button')
            end.addEventListener('click', e => {
                //set timer to 0
                alert('start/end')
            })

            action.append(edit)
            action.append(end)
            row.append(name)
            row.append(num)
            row.append(countdown)
            row.append(action)
            document.getElementById('eventTable').append(row)
            new Countdown({
                id: `timer${raffle.id}`,
                targetTime: started ?
                    raffle.end : raffle.start
            })
        })
    }

    useEffect(() => {
        console.log('useEffect')
        **socket.emit('eventTable')** //THIS IS WHERE IT BREAKS
        socket.on('eventTable', setTable)
        return () => {
            socket.off('eventTable', setTable)
        }
    })
    return (<>
        <h1 style={{ textAlign: 'center' }}>
            {localStorage.getItem('name')}
        </h1>
        <table style={{ marginLeft: 'auto', marginRight: 'auto' }}
            border="1"><caption>
            <h3>Scheduled Raffles</h3>
        </caption>
            <tbody id="eventTable">
                <tr>
                    <th>Name</th>
                    <th>Num Participants</th>
                    <th>Countdown</th>
                    <th>Action</th>
                </tr>
                <tr>
                    <td>Test</td>
                    <td>10</td>
                    <td>10:10:04</td>
                    <td><button> Edit </button> <button> End Now </button> <button> Stop </button></td>
                </tr>
            </tbody>
        </table>
    </>);
}

export default Org;

I get no errors on the front end. In fact, the client says that a connection has been established, thanks to the socket.on('connect') in index.js. None of the console.logs in the socket.js file are getting activated, so I have reason to believe that the route isn't getting hit. I also activated Debugging on socket but the logs there don't help me very much:

Server listening on port 5000
  socket.io:server incoming connection with id 1wpATq-xiQ1jHttwAAAA  2s
  socket.io-parser decoded 0 as {"type":0,"nsp":"/"}  0ms
  socket.io:client connecting to namespace /  0ms
  socket.io:namespace adding socket to nsp /  0ms
  socket.io:socket socket connected - writing packet  0ms
  socket.io:socket join room f6lna-kbntjuwffpAAAB  3ms
  socket.io-parser encoding packet {"type":0,"data":{"sid":"f6lna-kbntjuwffpAAAB"},"nsp":"/"}  7ms
  socket.io-parser encoded {"type":0,"data":{"sid":"f6lna-kbntjuwffpAAAB"},"nsp":"/"} as 0{"sid":"f6lna-kbntjuwffpAAAB"}  1ms
POST /login 200 653.887 ms - 37
  socket.io-parser decoded 2["eventTable"] as {"type":2,"nsp":"/","data":["eventTable"]}  3s
  socket.io:socket got packet {"type":2,"nsp":"/","data":["eventTable"]}  3s
  socket.io:socket emitting event ["eventTable"]  0ms
  socket.io:socket dispatching an event ["eventTable"]  1ms
  socket.io-parser decoded 2["eventTable"] as {"type":2,"nsp":"/","data":["eventTable"]}  15ms
  socket.io:socket got packet {"type":2,"nsp":"/","data":["eventTable"]}  2ms
  socket.io:socket emitting event ["eventTable"]  0ms
  socket.io:socket dispatching an event ["eventTable"]  1ms

CodePudding user response:

The server side socket.emit call should have an event name as the first argument.

con.emit('eventTable', con.request?.user?.getEvents()?.map(e => e.toJSON()))

CodePudding user response:

The problem was I was starting the server before initializing the sockets.js routes. I had to do some reconfiguring on the backend, but basically the way I fixed it was making an express router in sockets.js, importing said router to app.js, then doing app.use('/socket', sockets), BEFORE calling server.listen(PORT). Below is the final state of my backend:

new Backend file - init.js:

import express from 'express'
import { createServer } from 'http'
import session from 'express-session'
import { Server } from 'socket.io'
import { CrashCollector } from 'crashcollector'

new CrashCollector('crash_logs', (err, exit) => {
    console.log('Cleaning up...')
    console.log('Saving some stuff...')
    // Calls callback funtion executing process.exit()
    // You can also just write process.exit() here
    exit()
})

const app = express();
const server = createServer(app)
const io = new Server(server, {
    cookie: { maxAge: 24 * 60 * 60 * 1000 },
    cors: {
        origin: 'http://localhost:3000',
        credentials: true
    }
})
const sessionMiddleware = session({
    secret: "changeit", resave: false,
    saveUninitialized: false
});

export {
    server,
    app,
    io,
    sessionMiddleware
}

app.js:

'use strict';
import express from 'express'
import path from 'path'
import { fileURLToPath } from 'url';
import logger from 'morgan'
import cookieParser from 'cookie-parser'
import index from './routes/index.js'
import passport from './src/auth.js'
import { server, app, sessionMiddleware } from './init.js'
import sockets from './routes/sockets.js'

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

// uncomment after placing your favicon in /public
//app.use(favicon(__dirname   '/public/favicon.ico'));

app.use(sessionMiddleware)
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(passport.initialize());
app.use(passport.session());

app.use('/', index);
*app.use('/sockets', sockets)* //This is the bit I was missing

// catch 404 and forward to error handler
app.use(function (req, res, next) {
    let err = new Error('Not Found');
    err.status = 404;
    next(err);
});

// error handlers

// development error handler
if (app.get('env') === 'development') {
    app.use(function (err, req, res, next) {
        console.log('dev error handler')
        res.status(err.status || 500);
        res.end()
    });
}

// production error handler
// no stacktraces leaked to user
app.use(function (err, req, res, next) {
    console.log('prod error handler')
    res.status(err.status || 500);
    res.render('error', {
        message: err.message,
        error: {}
    });
});

app.set('port', process.env.PORT || 3000);


server.listen(app.get('port'), _ => {
    console.log(`Server listening on port ${app.get('port')}`)
})

sockets.js:

import { io, sessionMiddleware } from '../init.js'
import passport from '../src/auth.js'
import { getSequel } from '../src/database.js'
import { Router } from 'express'
const router = Router()
const wrap = middleware => (socket, next) =>
    middleware(socket.request, {}, next);

io.use((_, next) => {
    console.log('connection attempt')
    next()
})
io.use(wrap(sessionMiddleware));
io.use(wrap(passport.initialize()));
io.use(wrap(passport.session()));

io.use((con, next) => {
    console.log('socket check', !!con.request.user)
    if (con.request.user) {
        next()
    } else {
        next(new Error('unauthorized'))
    }
});

io.on('connection', (con) => {
    console.log(`new connection ${con.id}`);
    con.on('confirm', cb => {
        cb(con.request.user.name);
    });

    con.on('eventTable', cb => {
        con.request.user.getEvents()
        .then(events => events.map(e => e.JSON()))
//I switched to a callback here, rather than emitting a new event
        .then(mappedArray => cb(mappedArray)) 
    })
});

export default router
  • Related