1

I'm long-polling node.js route with JQuery's ajax request (xhr). This sends a GET request to my Express server and listens for message bus events. I'm setting a timeout on that GET request (as my proxy would kill long requests). So after the timeout, an abort event should be sent by the client to the server.

I want to catch that abort/close/finish event and remove the relevant message bus listener/subscriber.

But I struggle. I tried req.on("close"..) and the on-finished npm module, but that didn't work for me. I'm also not much more clever after reading the http documentation of node: https://nodejs.org/api/http.html.

Any ideas how to tackle this beast? Or better ways to remove listeners to prevent memory leaks?

Server side essentials:

// server.js
var express = require("express");
var EventEmitter = require("events").EventEmitter;
var messageBus = new EventEmitter();
messageBus.setMaxListeners(20);

var REST_PORT = (process.env.PORT || 5000);
var app = express();

app.get("/events", (req, res) => {
    var listener = function(res) {
        messageBus.once("message", function(data) {
            res.status(200).json(data);
        });
    };
    req.on("abort", function() { //I tried also "aborted", "close", "closed", "finish", "finished"..no luck
        messageBus.removeListener("message", listener);
    });

    listener(res);
    console.log("Total listeners to 'message' events:", messageBus.listeners("message").length);
});

// other messageBus.emit logic ..

app.listen(REST_PORT, () => {
    console.log("Application ready on port " + REST_PORT);
});

Client side essentials:

//client.js
$.ajax({
    method: "GET",
    async: true,
    url: "/events",
    success: function(data) {
        callback(data);
    },
    complete: function(request, status, err) {
        if (status == "timeout" || status == "success") {
            console.log("LOG: Normal long-polling timeout or successful poll, continuing.");
            longPoll();
        } else {
            console.warn("WARN: Server probably offline, retrying in 2 sec.");
            setTimeout(function() {
                longPoll();
            }, 2000);
        }
    },
    timeout: 30000
});

Thank you!

flaesh
  • 262
  • 5
  • 17

2 Answers2

1

If this helps someone, I finally decided to implement the long-polling differently and killing the client request at the server side after certain timeout. This works for me nicely and after reflection, is probably better mechanism than trusting the client to close the requests correctly.

setTimeout(() => {
    if (!responded) {
        messageBus.removeListener("message", listener);
        res.status(204).end();
    }
}, 30000);
flaesh
  • 262
  • 5
  • 17
  • This solution is how I handled it in a Python webserver I'm now porting to Node. Question: in your scheme, `get()` ties its handler to the emitter's event and... exits immediately? And the `res` object persists until handled? – Mike C May 01 '19 at 17:46
  • Never mind, I went ahead and assumed that was so, and it works a treat. Thanks for the code snippet, helped me. – Mike C May 01 '19 at 23:47
  • Glad it helped :-) – flaesh May 03 '19 at 21:09
0

I recommend aborting your custom long-polling system altogether and using one of the existing messaging/socket-type systems. There are many that are fully formed. socket.io is the most popular still and works well, but some alternatives like these might be better https://www.reddit.com/r/node/comments/4ktqae/socketio_alternatives/

Jason Livesay
  • 6,317
  • 3
  • 25
  • 31
  • I actually started with websockets (socket.io) and needed to move to long-polling. The websocket packages I tried do not support some basic security features I need. E.g. my question [here](http://stackoverflow.com/questions/43261827/node-js-socket-io-set-custom-headers-on-the-server). So my personal conclusion was that websockets are still immature (at least from security standpoint) and that I should go back to "good old" long-polling. – flaesh Apr 09 '17 at 08:13