Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Issues with long polling XMLHttpRequests and intermittent network connections

I've implemented a long polling connection to allow me to do server-side push (comet) using a Tomcat web server and standard javascript on the frontend. To keep the connection going, I have a simple keep-alive loop that initiates a new request as soon as the last one completes/fails.

The vast majority of the time, this connection works perfectly fine and keeps alive as I expect. But, I've noticed that when a user's internet connection drops out (e.g. they disconnect from a VPN, unplug their ethernet, etc.) AND I have a pending XMLHttpRequest out to the server, I get no indication of failure. Because of this, the connection dies silently, and I can't know that it's happened unless I constantly send requests to the server to test the connection (which seems to defeat the purpose of using long polling).

Here's the request object that I see in Chrome when it dies this silent death:

request: XMLHttpRequest
    onabort: function ()
    onerror: function ()
    onload: null
    onloadend: null
    onloadstart: null
    onprogress: null
    onreadystatechange: function ()
    readyState: 1
    response: ""
    responseText: ""
    responseType: ""
    responseXML: null
    status: [Exception: DOMException]
    statusText: [Exception: DOMException]
    upload: XMLHttpRequestUpload
    withCredentials: false

I have the three listeners (onabort, onerror, onreadystatechange) setup to alert a message if they ever get fired, but I get nothing whenever I take my connection to the server down. Here's how I'm forming the request:

var request = new XMLHttpRequest();

//url is just the url to my servlet to handle this
request.open("GET", url, true);

//handlestatechange is just my standard handling code 
//that I've put an alert at the top of
request.onreadystatechange = handleStateChange;

request.onerror = function()
{
    alert("We encountered an error");
}

request.onabort = function()
{
    alert("I've had an abortion");
}

request.send(null);

It seems like this would be a pretty standard situation, but I've not seen any conversations on how to allow a long polling connection to recover from this sort of disconnection.

Am I doing something wrong? Is there some other more standard approach to doing long polling/comet that circumvents this issue?

Any help with this would be appreciated, Thanks

like image 839
dallas Avatar asked Jan 07 '13 23:01

dallas


1 Answers

I believe the best way to handle this is to limit the duration of long-poll requests to some time, T (e.g. 60 seconds is a not-unreasonable value), and then use a timeout in the client to detect stalled states. Ideally you'd do this with the XHR timeout property, but it isn't supported x-browser, despite being in the W3C spec. Thus, you'll want to implement your own support using setTimeout and xhr.abort().

Clients can then assume that if they don't get a response w/in T seconds the connection has stalled and it's appropriate to abort the current request and attempt to reconnect.

This works reasonably well, but does mean there's some latency in detecting when a client's connection has stalled (up to T seconds). With long-poll requests I don't know that there's much to be done about this. If you really need a better solution, then you might want to look at a streaming comet connection that sends back heartbeat messages or WebSockets.

That's the simple answer. Unfortunately this turns out to be a non-trivial problem with all sorts of oddball edge cases. For example, you may or may not care about proxy servers with connection timeouts, or the various ways in which the default TCP timeout might be changed on your users' computers. This is why we've seen frameworks like Socket.IO emerge that hide a lot of this ugliness.

P.S. It's probably worth noting that you can monitor online state in some browsers via navigator.onLine, but it's poorly supported. The only browser it really behaves properly in is Chrome. The only thing it really tells you is if the user is known to be offline for some reason. It can't tell you that they have a working connection.

like image 157
broofa Avatar answered Oct 19 '22 13:10

broofa