【发布时间】:2011-03-08 01:31:18
【问题描述】:
奇怪的错误:在提供视频文件的 PHP 脚本中,在发送“video/mp4”标头并输出 MP4 文件之前,我有一些测试条件(验证令牌、确保文件存在等)。
如果任何测试失败,$fail 将被赋予非假值。
在测试结束时有这个if 声明:
if ($fail) {
exit;
}
此代码在 Chrome 中按预期工作,但在 Safari 中不正常。但是(相信我,我已经通过各种方式对此进行了测试),如果我只是注释掉 exit;,如下所示:
if ($fail) {
//exit;
}
...代码在 Safari 中完美运行——视频立即开始加载。
我确定永远不会输入 if 块,否则脚本将停止执行,并且我不会看到 video/mp4 标头(更不用说,它在 Chrome 中不起作用)。此外,无论 PHP 在幕后做什么,都应该对浏览器完全透明。我以为输出可能有问题,但如果我在标题之前输出了任何内容,我会收到警告。
我已经连续几天看到这种行为了——我可能已经检查了 25 次,难以置信。
肯定有我遗漏的东西吗?
更新
为了澄清问题,我稍微修改了代码:
$fail = true;
if ($fail) {
die('Fail');
}
现在我们可以保证命中die() 语句,并输出“失败”。以下是 Safari 看到的标题:
Connection:Keep-Alive
Content-Type:text/html
Date:Thu, 24 Jun 2010 23:31:28 GMT
Keep-Alive:timeout=10, max=29
Server:Apache/2.2.15 (CentOS) mod_ssl/2.2.15 0.9.8l DAV/2 mod_auth_passthrough/2.1 FrontPage/5.0.2.2635
Transfer-Encoding:Identity
X-Powered-By:PHP/5.2.13
(“失败”也按预期输出。)
现在,当我注释掉 $fail = true; 时,标题变为:
Connection:Keep-Alive
Content-Length:47406944
Content-Type:video/mp4
Date:Thu, 24 Jun 2010 23:32:58 GMT
Keep-Alive:timeout=10, max=30
Server:Apache/2.2.15 (CentOS) mod_ssl/2.2.15 0.9.8l DAV/2 mod_auth_passthrough/2.1 FrontPage/5.0.2.2635
X-Powered-By:PHP/5.2.13
但视频仍然无法播放! (带有问号的 QuickTime 徽标。)
我认为这足以证明$fail 仍然是假的,并且die() 永远不会被执行。
现在,得到这个:如果我再次注释掉die()(功能等同于exit),那么我的最终代码是:
//$fail = true;
if ($fail) {
//die('Fail');
}
...视频在 Safari 中播放!
更新 2
如果我将代码更改为:
$fail = false;
if ($fail) {
die('Fail');
}
...绝对保证$fail 是false,它在Safari 中播放!
这种行为对我来说毫无意义,b/c 如果 $fail 由于我的验证条件之一被设置,那么它永远不会输出 video/mp4 标头,就像我将 $fail 明确设置为 @ 987654345@——它会输出一个带有“失败”字样的text/html页面——对吗?
更新 3
这是所有相关代码,只是为了完全清楚:
// verify
$fail = false;
$token = false;
$file_name = [rest assured that $file_name is correct];
if (!$file_name) {
$fail = true;
} else {
$file_name = '../video/'.$file_name;
}
if (!isset($_REQUEST['ts'])) {
$fail = true;
}
if (isset($_POST['token']) || isset($_GET['token'])) {
$token = isset($_POST['token']) ? $_POST['token'] : $_GET['token'];
} else if (isset($_COOKIE['token'])) {
$token = $_COOKIE['token'];
}
if ($token != md5(SALT_HASH.$_REQUEST['ts'])) {
$fail = true;
}
if (((int)($_REQUEST['ts']) + 60 * 10) < mktime()) {
$fail = true;
}
if (!is_file($file_name)) {
$fail = true;
}
if ($fail) {
die('Fail');
}
// output
$file_size = (string)(filesize($file_name));
header('Content-Type: video/mp4');
header('Content-Length: '.$file_size);
readfile_chunked($file_name);
exit;
希望是最后的更新/总结
这个问题可能已经太长了,但我想我会尝试最后一次来总结一下这有多奇怪。有 3 种不同的响应:
1) 如果我在if ($fail) die('Fail'); 之前硬连线$fail = true;,这样我就有了失败的基线,我会得到一个text/html 标头,并且按预期输出单词“Fail”。
2) 如果我保留上面的代码,我会得到一个 video/mp4 标头,但 Safari 中的视频损坏(它将在 Chrome 中播放)。
3) 最后(这是基于我今天所做的新测试),如果我在令牌验证条件中注释掉 $fail = true;,我会得到一个 video/mp4 标头,并且视频会在 Safari 中播放。现在,我认为令牌验证一定有问题 - 但是当我在另一个测试中硬连线以在测试后回显 $fail 的值时,它仍然是错误的!从未输入条件(我也只是直接输入了die('!'); 而不是$fail = true;——我仍然得到一个video/mp4 标头)。
我简直不敢相信,注释掉永远不会被执行的代码会引起明显的反应——而且,它会在 Chrome 中播放,而不是在 Safari 中; PHP 服务器端发生的任何事情都应该对浏览器完全透明。
疯了。
啊哈!
我在脚本中添加了一些日志记录,结果发现在提供 HTML5 视频时,浏览器发出 两个 请求。
这是来自 Chrome 的两个(成功的)请求:
Fri Jun 25 17:41:22 2010 Browser: [Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; en-US) AppleWebKit/533.4 (KHTML, like Gecko) Chrome/5.0.375.86 Safari/533.4] Fail: [0] Token: [83e50b519c0ed4662b6b7fabb8f6671e] Timestamp: [1277509282]
Fri Jun 25 17:41:22 2010 Verification passed
Fri Jun 25 17:41:22 2010 Browser: [Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; en-US) AppleWebKit/533.4 (KHTML, like Gecko) Chrome/5.0.375.86 Safari/533.4] Fail: [0] Token: [83e50b519c0ed4662b6b7fabb8f6671e] Timestamp: [1277509282]
Fri Jun 25 17:41:22 2010 Verification passed
这是来自 Safari 的两个(第一次成功,第二次失败):
Fri Jun 25 17:41:32 2010 Browser: [Apple Mac OS X v10.6.4 CoreMedia v1.0.0.10F569] Fail: [0] Token: [6374fba3d9eac7d94de9741db76953c6] Timestamp: [1277509291]
Fri Jun 25 17:41:32 2010 Verification passed
Fri Jun 25 17:41:33 2010 Browser: [QuickTime/7.6.6 (qtver=7.6.6;cpu=IA32;os=Mac 10.6.4)] Fail: [1] Token: [] Timestamp: [1277509291]
我现在必须出去,但我几乎可以肯定这种行为是错误的根源。出于某种原因,第二次 Safari 请求找不到令牌。
【问题讨论】:
-
使用Wireshark 或其他工具查看网络上的实际情况。
-
指定你在做什么:不要使用 $_REQUEST --- 使用 $_GET 和 $_POST 和 $_COOKIE
-
是否有充分的理由不使用 $_REQUEST?无论如何,当我在最后的“if ($fail) {}”之前更改 $fail 的值时,实际的验证条件都不重要。
-
(实际上,我明白你对 $_REQUEST 的看法——我没有意识到它包括 $_COOKIE。尽管如此,我不认为这就是我在这里调试的内容。 )
-
我对此非常怀疑。您应该尝试从命令行
curl完整响应,包括标题,一次使用exit并注释掉一次。如果响应是相同的(它们应该是相同的),那么它必须与缓存、cookie、一天中的时间、蝴蝶或通常 Safari 不喜欢您的测试方法有关。