Lasse
Lasse

Reputation: 601

Express response persists while using socket.io emit

Currently building a microservice application. The communication between each container is via web sockets and the gateway is a REST gateway.

I want to be able to use emit from within a post request, which is working fine as of right now. But when the the socket is to handle a on event and then reply back via the gateway using a express response object, that object is not closed with a response.json call. The object is still alive inside another on event inside the same route, which then makes the new request generate the error, headers cannot be set after they are send. This makes sense, but how do I ensure that the new request, gets a new response object ?

First request
gateway -> (ws) -> microserver -> (ws) -> gateway -> (http) user
Second request
gateway -> (ws) -> microserver -> (ws) -> gateway -> ERROR

Gateway snippet:

app.post("/api/user/create", function (req, res) {
  console.log("Create request");
  let token = req.get("Authentication");
  let userData = req.body;
  ioClientUsers.emit("create", {
    "token": token,
    "userData": userData
  });
  ioClientUsers.once("user created", success => {
    console.log(res.headersSent);
    if (success) {
      return res.json({
        "status": "created"
      }); 
    } else {
      return res.json({
        "status": "not created"
      });
    }
  });
  ioClientUsers.once("user not created", () => {
    console.log(res.headersSent);
    res.json({
      "status": "not created"
    });
  });
});

Microservice snippet

 socket.on("create", createObj => {
 console.log(createObj);
 userController.createUser(createObj.token, createObj.userData, (err, success) => {
   console.log(err, success);
   if (err) {
     socket.emit("user not created");
   } else {
     socket.emit("user created", success);
   }
 });
});

EDIT Just posting again, so that if anybody else has the same problem, either the one answer to this or this edit solves the problem:

 app.post("/api/user/create", function (req, res) {
  let token = req.get("Authentication");
  let userData = req.body;
  ioClientUsers.emit("create", {
    "token": token,
    "userData": userData
  });
  ioClientUsers.once("user created", userCreated);
  ioClientUsers.once("user not created", userNotCreated);

  function userCreated(success){
    if (success) {
      res.json({
        "status": "created"
      }); 
    } else {
      res.json({
        "status": "not created"
      });
    }  
    rmListner();
  }
  function userNotCreated(){
    console.log(res.headersSent);
    res.json({
      "status": "not created"
    });
    rmListner();
  }

  function rmListener(){
    ioClientUsers.removeListener("user created", userCreated);
    ioClientUsers.removeListener("user not created", userNotCreated);
  }
});

Upvotes: 0

Views: 301

Answers (1)

TKoL
TKoL

Reputation: 13902

I'd do something like this

app.post("/api/user/create", function (req, res) {
  console.log("Create request");
  let token = req.get("Authentication");
  let userData = req.body;
  ioClientUsers.emit("create", {
    "token": token,
    "userData": userData
  });

  function createdCallback(success) {
    if (success) {
      sendStatus('created')
    } else {
      sendStatus('not created')
    }
  }

  function notCreatedCallback() {
    sendStatus('not created')
  }

  function sendStatus(status) {
    console.log(res.headersSent);
    // UNSUBSCRIBE YOUR LISTENERS HERE
    ioClientUsers.removeListener("user created", createdCallback);
    ioClientUsers.removeListener("user not created", notCreatedCallback);
    res.json({'status': status})
  }

  ioClientUsers.once("user created", createdCallback);
  ioClientUsers.once("user not created", notCreatedCallback);
});

The socket listeners are all removed before res.json is called. See if that works.

Upvotes: 1

Related Questions