7

In a JavaScript class, an XMLHttpRequest connect to the server. The server is sending data, slowly. This work fine in Chromium, but Firefox close the connection after random time (between ~4s and ~70s).

Why Firefox close the connection? and How to avoid that?

Simplified JS code:

    var options = {};
    options['header']=
        { 'Cache-Control':'no-cache, max-age=0', 
            'Content-type': 'application/octet-stream',
            'Content-Disposition': 'inline'
        };

    // Get request information
    this.http = new XMLHttpRequest();
    this.http.onreadystatechange = _streamingResponse.bind(this);
    this.http.open('post', url, true);
    for (var i in options['header'])
    {
        this.http.setRequestHeader(i, options['header'][i]);
    }
    this.http.send('');

for the PHP part, something like:

sleep(200); //wait long time, so firefox close the socket.

If the server send something every few seconds (<5s) the connection stay alive "forever". But if no data is sent, Firefox close the connection.

The connection close with: - readyState = 4 - status = 0

The server seem to be correct, as in Chromium it work correctly.

Full test code:

test.html

<html>
<header>
</header>
<body>
</body>

<script type="application/javascript">

    function log( msg )
    {
        document.body.appendChild(document.createElement('div').appendChild(document.createTextNode(msg)));
        document.body.appendChild(document.createElement('br'));
    }

    function request(url)
    {
        function _streamingResponse()
        {
            if (4==this.http.readyState)
            {
                log('Done: ' + this.http.status);
            }
            else if (3==this.http.readyState)
            {
                var text = this.http.response.substr(this.lastRequestPos);
                this.lastRequestPos = this.http.response.length;
                log('Update: ' + text);
            }
        }

        var options = {};
        options['header']=
            { 'Cache-Control':'no-cache, max-age=0', 
                'Content-type': 'application/octet-stream',
                'Content-Disposition': 'inline'
            };

        this.lastRequestPos=0;

        // Get request information
        this.http = new XMLHttpRequest();
        this.http.onreadystatechange = _streamingResponse.bind(this);
        this.http.open('post', url, true);
        for (var i in options['header'])
        {
            this.http.setRequestHeader(i, options['header'][i]);
        }
        this.http.send('');
        log('Request sent!');
    }

    req = new request('./test.php');
</script>
</html>

test.php

<?php

$timer = 60;

ignore_user_abort(true);
set_time_limit(0);

// Turn off output buffering and compression
ini_set('output_buffering', 'off');
ini_set('zlib.output_compression', false);
ini_set('implicit_flush', true);
ob_implicit_flush(true);
while (ob_get_level() > 0) {
    $level = ob_get_level();
    ob_end_clean();
    if (ob_get_level() == $level) break;
}
if (function_exists('apache_setenv')) {
    apache_setenv('no-gzip', '1');
    apache_setenv('dont-vary', '1');
}

// Set header for streaming
header('Content-type: application/octet-stream');
flush();

// Send information
sleep($timer);
echo '<yes></yes>';
flush();

?>

Additional note: Firefox 43.0.03, Chromium 47.0.2526

EDITED:

Setting a callback for timeout it do not trigger. I conclude it is not a timeout.

this.http.timeout = 2000;
this.http.ontimeout = _streamingTimeout.bind(this);
Adrian Maire
  • 14,354
  • 9
  • 45
  • 85
  • Let's me to know if any information is missing or I can improve the question. – Adrian Maire Jan 18 '16 at 17:12
  • I am creating a full example to simplify your testing. – Adrian Maire Jan 18 '16 at 17:43
  • 1
    this might be helpful.. http://stackoverflow.com/questions/1192375/timeout-behavior-of-different-browsers and http://stackoverflow.com/questions/1342310/where-can-i-find-the-default-timeout-settings-for-all-browsers/1343963#1343963 – Vishal Rajole Jan 18 '16 at 17:44
  • It could be related with firefox timeout of course, but I have not proxy in middle. Also, the timeout seem much shorter (less than 2min). – Adrian Maire Jan 18 '16 at 18:03
  • @VishalRajole: I made some tests, it is NOT the timeout, as setting a callback for the timeout it do not trigger: http://stackoverflow.com/questions/1523686/timeout-xmlhttprequest – Adrian Maire Jan 18 '16 at 18:18
  • What is it you actually want to do with that request? ... Keeping a request open like that for such a long time seems like you do it in the wrong way. – Asons Jan 19 '16 at 21:05
  • @LGSon: The class is an emulation of socket, it achieve full duplex communication between server an clients->A client call the server and the server is able to broadcast an answer to any set of connected clients. – Adrian Maire Jan 19 '16 at 21:18
  • May I suggest your read up on SignalR and get rid of your head ache :) ... http://blog.3d-logic.com/2015/03/29/signalr-on-the-wire-an-informal-description-of-the-signalr-protocol/ – Asons Jan 19 '16 at 21:35
  • Nop: I could make much better with just WebSocket, but I want to stay with normal hosting possibilities: Javascript + Php. No licenses limitations, not external libraries dependencies, not binding to stupid MS servers, I already have 50K+ lines of library, flexibility of server side implementation... – Adrian Maire Jan 19 '16 at 21:36
  • Do you need real time connections? – Asons Jan 19 '16 at 21:42
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/101131/discussion-between-adrian-maire-and-lgson). – Adrian Maire Jan 19 '16 at 21:42

2 Answers2

3

After searching further, I found a Bug in Mozilla which seem the responsible of this behaviour. It should be solved in the version 45, but until then, we have to lead with it.

Even if the bug seem related only to Ipv6, I have the same issue using 127.0.0.1. However, with Firefox Developer Edition (V 45) the problem seem solved.

Why Firefox close the connection?

It should not. ref: https://bugzilla.mozilla.org/show_bug.cgi?id=1240319

How to solve it?

Except to send data every 3-4 seconds to maintain the connection open, I have no idea.

Adrian Maire
  • 14,354
  • 9
  • 45
  • 85
  • Thanks much for taking time to file a Firefox bug about this, and for following up here with the link to it. – sideshowbarker Jan 26 '16 at 01:40
  • @sideshowbarker: I am unfortunately not part of this Firefox improvement. – Adrian Maire Jan 26 '16 at 06:43
  • Yeah, I understand—I just meant thanks for actually filing a browser bug about it. A lot of developers never take the time to report bugs to browser projects—nor even when they do raise bugs, don’t always bother to post a link to the bug to any related Stack Overflow questions. – sideshowbarker Jan 26 '16 at 09:11
1

Sounds an awful lot like garbage collection.

I see you have tried timeout. But I'm not sure I understand your conclusion: Setting a callback for timeout it do not trigger.

Just be absolutely certain that you follow these rules:

4.2 Garbage collection

An XMLHttpRequest object must not be garbage collected if its state is either opened with the send() flag set, headers received, or loading, and it has one or more event listeners registered whose type is one of readystatechange, progress, abort, error, load, timeout, and loadend.

If an XMLHttpRequest object is garbage collected while its connection is still open, the user agent must terminate the request.

xhr.spec.whatwg.org

Check which state, send() flag and event listeners are set while no data is sent.

Community
  • 1
  • 1
Payne
  • 344
  • 3
  • 8
  • Thanks for you great answer! Unfortunately, that is not the issue: even if the example could carry this conclusion, saving the request in a global variable do not make any difference. Also, the final event provide a correct object. – Adrian Maire Jan 19 '16 at 17:23
  • The bounty is for you, so at least someone uses those points. Thanks for your time! – Adrian Maire Jan 26 '16 at 06:45