Wait for socketio event inside express route

2019-04-14 10:52发布

I'm using express,socketio and socketio-client in my application. (I not very comfortable with nodejs stack...)

to summarize my application flow : Client => node/express API + Socketoi server <=> nodejs (Socketio-client)

  1. browser send request to a nodejs/express (route /api)
  2. Do some request headers overwrites with middlewares
  3. In the route '/', server sends an emit to a nodejs (Socketio-client)
  4. after executing some logic, socketio-client emit an event with the logics result
  5. I need this result to be send in the response to the client

My code below:

router.get('/*', function (req, res) {
  //emit data for socketio-client to apply some logic
  app.io.sockets.emit('req', {
    reqheader : req.headers,
    requrl : req.protocol + "://" + req.headers.host + req.url,
    reqmethod : req.method
  });
  console.log("after emit");
  //I use callback to make response wait for socketio server to catch event from client
  waitforevent(req, res, function (__res) {
    console.log("callback" );
    res.end(__res.body);
    res.sendStatus(__res.statusCode);
    //res.end();
  });
  function waitforevent(req, res, callback) {
    console.log("waiting for event" );
    app.io.__socket.on('respp', function (data) {
        //console.log("no response yet \n" + JSON.parse(data) );
        __res = JSON.parse(data);
        console.log("event catched...");
        callback(__res);
    });
  }
});

My problem : This works only the first time I send a Get http://localhost:3000/api frome the browser. __res.body is printed in the browser.

req 1
after emit
waiting for event
event catched...
callback
Error: Can't set headers after they are sent.   
**GET /api 200 73.841 ms - -**

req 2
after emit
waiting for event

Next request will just wait for server to respond, which is, I suspect, not happening because the app.io.__socket.on('respp', function (data){...} is never catched by the server.

After sending more request (while the others are waiting), I noticed this warning in server logs:

(node) warning: possible EventEmitter memory leak detected. 11 respp listeners added. Use emitter.setMaxListeners() to increase limit.

Is there other ways to catch events in a route before sending response to clients?

2条回答
唯我独甜
2楼-- · 2019-04-14 11:05

You could remove the event listener when the socket closes to avoid the event listener leak:

router.get('/*', function (req, res) {
  app.io.sockets.emit('req', {
    reqheader : req.headers,
    requrl : req.protocol + "://" + req.headers.host + req.url,
    reqmethod : req.method
  });

  req.socket.on('close', function() {
    app.io.__socket.removeListener('respp', resppHandler);
  });
  app.io.__socket.on('respp', resppHandler);

  function resppHandler(data) {
    data = JSON.parse(data);
    res.statusCode = data.statusCode;
    res.end(data.body);
  }
});

I'm not sure if app.io.__socket should really be app.io.sockets or not, but I copied it as-is from your code, assuming you know what you're doing.

Additionally, you may wish to add some sort of timeout so as not to keep the request waiting indefinitely.

查看更多
狗以群分
3楼-- · 2019-04-14 11:24

I solved with once:

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

var socket;
io.on('connection', function (sock) {
    console.log('Connected');
    socket = sock;
});

server.listen(3000);

app.get('/*', function (req, res) {
    socket.once('event', function (data) {
        if (data.error) {
            console.log('is an error');
            res.status(400).json(data);
        } else {
            console.log('is ok');
            res.status(200).json(data);
        }
    });
    io.emit('ask-for-event', { data: data });
});
查看更多
登录 后发表回答