如果不活动,Firefox 会随机关闭 XMLHttpRequest 连接。为什么?
Firefox randomly close XMLHttpRequest connection if inactive. Why?
在 JavaScript class 中,一个 XMLHttpRequest 连接到服务器。
服务器正在缓慢地发送数据。这在 Chromium 中工作正常,但 Firefox 在随机时间后关闭连接(在 ~4s 和 ~70s 之间)。
为什么 Firefox 会关闭连接?以及如何避免这种情况?
简化的 JS 代码:
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('');
对于 PHP 部分,类似于:
sleep(200); //wait long time, so firefox close the socket.
如果服务器每隔几秒 (<5s) 发送一些内容,连接将保持活动状态 "forever"。但是如果没有数据发送,Firefox 关闭连接。
连接关闭:
- 就绪状态 = 4
- 状态 = 0
服务器似乎是正确的,因为它在 Chromium 中工作正常。
完整测试代码:
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();
?>
补充说明:Firefox 43.0.03、Chromium 47.0.2526
已编辑:
设置超时回调不触发。我断定这不是超时。
this.http.timeout = 2000;
this.http.ontimeout = _streamingTimeout.bind(this);
听起来很像垃圾回收。
我看到你尝试过超时。但我不确定我是否理解您的结论:设置超时回调不会触发。
请务必遵守以下规则:
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.
检查在没有数据发送时设置了哪个状态、send() 标志和事件侦听器。
进一步搜索后,我在 Mozilla 中发现了一个错误,似乎是导致此行为的原因。它应该在45版本中解决,但在那之前,我们必须先解决它。
即使这个错误似乎只与 Ipv6 有关,我在使用 127.0.0.1 时也遇到了同样的问题。然而,使用 Firefox Developer Edition (V 45) 问题似乎解决了。
为什么 Firefox 会关闭连接?
不应该。参考:https://bugzilla.mozilla.org/show_bug.cgi?id=1240319
如何解决?
除了每3-4秒发送一次数据以保持连接打开,我不知道。
在 JavaScript class 中,一个 XMLHttpRequest 连接到服务器。 服务器正在缓慢地发送数据。这在 Chromium 中工作正常,但 Firefox 在随机时间后关闭连接(在 ~4s 和 ~70s 之间)。
为什么 Firefox 会关闭连接?以及如何避免这种情况?
简化的 JS 代码:
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('');
对于 PHP 部分,类似于:
sleep(200); //wait long time, so firefox close the socket.
如果服务器每隔几秒 (<5s) 发送一些内容,连接将保持活动状态 "forever"。但是如果没有数据发送,Firefox 关闭连接。
连接关闭: - 就绪状态 = 4 - 状态 = 0
服务器似乎是正确的,因为它在 Chromium 中工作正常。
完整测试代码:
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();
?>
补充说明:Firefox 43.0.03、Chromium 47.0.2526
已编辑:
设置超时回调不触发。我断定这不是超时。
this.http.timeout = 2000;
this.http.ontimeout = _streamingTimeout.bind(this);
听起来很像垃圾回收。
我看到你尝试过超时。但我不确定我是否理解您的结论:设置超时回调不会触发。
请务必遵守以下规则:
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.
检查在没有数据发送时设置了哪个状态、send() 标志和事件侦听器。
进一步搜索后,我在 Mozilla 中发现了一个错误,似乎是导致此行为的原因。它应该在45版本中解决,但在那之前,我们必须先解决它。
即使这个错误似乎只与 Ipv6 有关,我在使用 127.0.0.1 时也遇到了同样的问题。然而,使用 Firefox Developer Edition (V 45) 问题似乎解决了。
为什么 Firefox 会关闭连接?
不应该。参考:https://bugzilla.mozilla.org/show_bug.cgi?id=1240319
如何解决?
除了每3-4秒发送一次数据以保持连接打开,我不知道。