Search code examples
node.jsexpresssocket.ioibm-cloud

Socket.io is causing the error 'Error: Can't set headers after they are sent.'


I am trying to deploy to ibm-cloud a node web application with Angular 2, node.js,express and mongo. My app works fine without adding the socket.io require line:

const io = require("socket.io")(server);

However, every time i try to add the socket.io and execute the container, the console logs (for each request):

Error: Can't set headers after they are sent. at SendStream.headersAlreadySent (/app/node_modules/send/index.js:390:13) at SendStream.send (/app/node_modules/send/index.js:618:10) at onstat (/app/node_modules/send/index.js:730:10) at FSReqWrap.oncomplete (fs.js:153:5) Error: Can't set headers after they are sent. at SendStream.headersAlreadySent (/app/node_modules/send/index.js:390:13) at SendStream.send (/app/node_modules/send/index.js:618:10) at onstat (/app/node_modules/send/index.js:730:10) at FSReqWrap.oncomplete (fs.js:153:5) Error: Can't set headers after they are sent. at SendStream.headersAlreadySent (/app/node_modules/send/index.js:390:13) at SendStream.send (/app/node_modules/send/index.js:618:10) at onstat (/app/node_modules/send/index.js:730:10) at FSReqWrap.oncomplete (fs.js:153:5)

This is my code // Uncomment following to enable zipkin tracing, tailor to fit your network configuration: // var appzip = require('appmetrics-zipkin')({ // host: 'localhost', // port: 9411, // serviceName:'frontend' // });

    require('appmetrics-dash').attach();
    require('appmetrics-prometheus').attach();
    const appName = require('./../package').name;
    const http = require('http');
    const express = require('express');
    const log4js = require('log4js');
    const localConfig = require('./config/local.json');
    const path = require('path');
    const logger = log4js.getLogger(appName);
    logger.level = process.env.LOG_LEVEL || 'info'
    const app = express();
    const server = http.createServer(app);
    const io = require("socket.io")(server);


    app.use(log4js.connectLogger(logger, { level: logger.level }));
    require('./routers/index')(app, server);

    // Add your code here
    const bodyParser = require('body-parser');
    var cors = require('cors');
    const jwt = require("jsonwebtoken");

    const { mongoose } = require("./database");

    app.use(cors());
    app.use(bodyParser.json());
    app.use(bodyParser.urlencoded({
      extended: true
    }));

    app.use("/auth", require("./routes/auth.routes"));
    app.use(
      "/usuarios",
      (req, res, next) => {
        const bearerHeader = req.headers["authorization"];
        if (typeof bearerHeader !== "undefined") {
          const bearer = bearerHeader.split(" ");
          const bearerToken = bearer[1];
          jwt.verify(bearerToken, "loremipsum", (err, authData) => {
            if (err) return res.sendStatus(403);
            authData.rol == "admin" ? next() : res.sendStatus(403);
            return;
          });
        } else {
          res.sendStatus(403);
        }
      },
      require("./routes/usuarios.routes")
    );

    app.use("/api", (req, res, next) => {
      const bearerHeader = req.headers["authorization"];
      console.log(bearerHeader);
      if (typeof bearerHeader !== "undefined") {
        const bearer = bearerHeader.split(" ");
        const bearerToken = bearer[1];
        jwt.verify(bearerToken, "loremipsum", (err, authData) => {
          if (err) return res.sendStatus(403);
          req.authData = authData;
          next();
        });
      } else {
        res.sendStatus(403);
      }
    });
    app.use("/api/turnos", require("./routes/turns.routes"));
    app.use("/api/estado", require("./routes/estados.routes"));
    app.use("/api/incidencias", require("./routes/incidencia.routes"));
    app.use("/movil/api", require("./routes/movil.data.routes"));
    app.use("/movil/estadisticas", require("./routes/cola.routes"));
    app.use('/*', (req,res,next) => {
      res.sendFile(path.join(__dirname, '../public', 'index.html'));
      return;
    } )
    //end my code

    const port = process.env.PORT || localConfig.port;
    server.listen(port,'0.0.0.0', function(){
      logger.info(`tfgbff listening on http://localhost:${port}/appmetrics-dash`);
      logger.info(`OpenAPI (Swagger) spec is available at http://localhost:${port}/swagger/api`);
      logger.info(`Swagger UI is available at http://localhost:${port}/explorer`);
    });

    const turno = require("./controllers/turnos.controller");
    const io = require('socket.io')(server);
    io.on("connection", socket => {
      console.log("conectao!");
      turno.nextSubject.subscribe(turnos => {
        turnos = turnos.filter(t => t.atendido == false);
        const infoTurno = turnos.map(t => {
          return {
            incidenciaId: t.incidenciaId,
            turno: t.turno,
            siendoAtendido: t.siendoAtendido
          };
        });
        console.log(infoTurno);
        io.emit("socket-turnos", infoTurno);
      });
      socket.on("disconnect", () => console.log("desconectao!"));
    });
    // app.use(function (req, res, next) {
    //   res.sendFile(path.join(__dirname, '../public', '404.html'));
    // });

    // app.use(function (err, req, res, next) {
    //  res.sendFile(path.join(__dirname, '../public', '500.html'));
    // });

    module.exports = server;

Thank you very much!.


Solution

  • I know this is old but I have just had exactly the same problem, and have just worked out it's being caused by the appmetrics libraries.

    Once I figure out how to get it working I'll update this.

    EDIT:

    OK managed to get it working with appmetrics-dash. You need to use monitor() instead of attach() and move the monitor to the end of your routes as so. I have investigated appmetrics-prometheus and it only has an attach() at this stage so can't be used:

    const dash = require('appmetrics-dash');
    
    const express = require('express');
    const history = require('connect-history-api-fallback');
    
    const app = express();
    const server = require('http').Server(app);
    
    require('./routers/index')(app, server);
    require('./services/index')(app);
    
    // Add your code here
    
    dash.monitor({server});
    
    // catch 404 and forward to error handler
    app.use(function(req, res) {
        res.status(404).send('Not Found');
    });
    
    // error handler
    app.use(function(err, req, res) {
        res.status(err.status || 500).send('Error');
    });
    
    server.listen(env.port, () => {
        logger.info(`${env.name} listening on ${env.url}/appmetrics-dash`);
        logger.info(`${env.name} listening on ${env.url}`);
    });