【发布时间】:2011-10-21 19:08:17
【问题描述】:
我整个上午都在研究这个问题,并决定作为最后的努力,也许 Stack Overflow 上的某个人对我有一个“一直在那里,完成那个”类型的答案。
背景
最近,我在我们的(面向内联网的)Apache (2.2) 服务器上使用过滤器实现了压缩,以便通过 mod_deflate 压缩所有基于文本的文件(css、js、txt、html 等) ,只字未提php脚本。在对如何最好地压缩 PHP 输出进行了大量研究之后,我决定使用 gzcompress() 风格,因为 PHP 文档建议使用 zlib 库和 gzip(使用 deflate 算法,等等等等)优于 ob_gzipwhatever()。所以我像这样复制了别人的方法:
<?php # start each page by enabling output buffering and disabling automatic flushes
ob_start();ob_implicit_flush(0);
(program logic)
print_gzipped_page();
function print_gzipped_page() {
if (headers_sent())
$encoding = false;
elseif(strpos($_SERVER['HTTP_ACCEPT_ENCODING'],'x-gzip') !== false )
$encoding = 'x-gzip';
elseif(strpos($_SERVER['HTTP_ACCEPT_ENCODING'],'gzip') !== false )
$encoding = 'gzip';
else
$encoding = false;
if($encoding){
$contents = ob_get_contents(); # get contents of buffer
ob_end_clean(); # turn off OB and flush buffer
$size = strlen($contents);
if ($size < 512) { # too small to be worth a compression
echo $contents;
exit();
} else {
header("Content-Encoding: $encoding");
header('Vary: Accept-Encoding');
# 8-byte file header: g-zip file (1f 8b) compression type deflate (08), next 5 bytes are padding
echo "\x1f\x8b\x08\x00\x00\x00\x00\x00";
$contents = gzcompress($contents, 9);
$contents = substr($contents, 0,$size); # faster than not using a substr, oddly
echo $contents;
exit();
}
} else {
ob_end_flush();
exit();
}
}
很标准的东西,对吧?
问题
在我们通过 Firefox 发送的所有 PHP 页面请求中,有 10% 到 33% 可以正常执行并以 g-zip 格式返回,只有 Firefox 会显示压缩的 ASCII 而不是解压缩它。而且,最奇怪的部分是,发回的内容大小总是比正确呈现的页面大小大 30 或 31 个字节。如,当脚本正确显示时,Firebug 显示内容大小为 1044;当 Firefox 显示一个巨大的二进制乱码屏幕时,Firebug 显示的内容大小为 1074。这发生在我们的一些运行 Firefox 3.3s 的旧版 32 位 Fedora 12s 上的用户身上......然后它发生在一个使用 FF5 的用户身上,一个使用 FF6 的用户,还有一些使用新的 7.1 的用户!无论如何,我一直打算将它们全部升级到 FF7.1,所以我一直在更新它们,因为它们有问题,但 FF7.1 仍然表现出相同的行为,只是频率降低了。
诊断
我一直在各种计算机上安装 Firebug 来查看标题,这就是我感到困惑的地方: 正常、正常运行的页面响应标头:- HTTP/1.1 200 正常
- 日期:格林威治标准时间 2011 年 10 月 21 日星期五 18:40:15
- 服务器:Apache/2.2.15 (Fedora)
- X-Powered-By:PHP/5.3.2
- 到期:1981 年 11 月 19 日星期四 08:52:00 GMT
- Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
- 编译指示:无缓存
- 内容编码:gzip
- 变化:接受编码
- 内容长度:1045
- 保持活动状态:超时=10,最大值=75
- 连接:保持活动状态
- 内容类型:文本/html; charset=UTF-8
(注意 content-length 是自动生成的)
损坏时相同的页面:
- HTTP/1.1 200 正常
- (其他一切都相同)
- 内容长度:1075
发送的标头总是包含 Accept-Encoding: gzip, deflate
我尝试解决的问题:
- 使用未压缩和压缩长度显式声明内容长度
- 不使用 $contents 的 substr()
- 删除 $contents 末尾的校验和
我真的不想使用 gzencode,因为我的测试表明它比 gzcompress 慢得多(9%),大概是因为它会生成额外的校验和以及我(假设)网络浏览器不需要或使用的东西.
我无法在运行 Firefox 7.1 的 64 位 Fedora 14 机器上复制该行为。 在实时推出压缩代码之前,我的测试中没有一次发生这种情况,无论是在 Chrome 还是 Firefox 中。 (编辑:在发布此消息后,我打开的一个每 30 秒发送一次元刷新的窗口最终在 Firefox 中刷新约 60 次后中断)我们的少数 Windows XP 机器的行为与 Fedora 12s 相同。通过 Firefox 的 Bugzilla 搜索引发了与这种情况有些相似的一两个错误请求,但这是针对 3.3 之前的版本并且包含所有 gzip 内容,而我们的 Apache gzip 压缩 css 和 js 文件正在下载和显示而没有错误每次。
内容长度每次返回大 30/31 字节的事实让我认为我的 script/gzcompress() 内部出现了问题,这在 Firefox 阻塞的响应中破坏了某些内容。自然,如果您更改 echo 的 gzip 标头,Firefox 会抛出“内容编码错误”,所以我真的倾向于 gzcompress() 内部的问题。
我注定要失败吗?我是否必须放弃这个实现并使用不喜欢的 ob_start("ob_gzhandler") 方法?
我想我的“适用于多种情况”的问题是:PHP 中的 zlib 压缩库中是否存在已知错误,在接收非常具体的输入时会做一些时髦的事情?
编辑:坚果。我读取了 Firefox 下载的损坏的、未压缩的页面之一,你瞧!它完美地回显了所有内容。 =(这意味着这一定是……不,我什么都没有。
【问题讨论】:
-
自然,我一发布,我机器上的 Firefox 终于从服务器检索到损坏的数据。 ‹xÚµ™moÛ6ÇßûS
标签: php http firefox compression zlib