单个PHP“退出;”声明阻止 Safari 中的 HTML5 视频

Posted

技术标签:

【中文标题】单个PHP“退出;”声明阻止 Safari 中的 HTML5 视频【英文标题】:Single PHP "exit;" statement prevents HTML5 video in Safari 【发布时间】: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');

...绝对保证$failfalse,它在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 不喜欢您的测试方法有关。 【参考方案1】:

事实证明,浏览器在播放之前会向&lt;video&gt; 源发出两个请求。我只看到第一个请求的标题,因此我很困惑。视频在 Chrome 而不是 Safari 中播放的原因是因为在 Safari 中发出第二个请求的代理 ("QuickTime/7.6.6 (qtver=7.6.6;cpu=IA32;os=Mac 10.6.4)") 显然可以'不读取相同的 $_COOKIE;而对于 Chrome,$_COOKIE 在两个请求中都是完整的。

我还有一个严重的问题,视频只能在桌面浏览器上播放,而不能在 iPhone 上播放。我确定这不是编码问题,因为如果我直接访问 MP4,它可以正常播放;仅当通过 PHP 输出视频时才会出现此问题。无论如何,这完全是另一个问题。

【讨论】:

谢谢。在 Win 7 上当前版本的 Firefox 27 和 IE 11 上似乎也会出现同样的问题。 感谢您的信息。但是有什么办法可以解决这个问题呢?【参考方案2】:

如果没有video/mp4 标头,服务器将发送默认的内容类型标头,通常是text/html,某些浏览器可能会查看正在提供的内容并忽略 MIME 类型。

error_reporting(E_ALL);添加到脚本的开头,并使用firebug或类似工具检查正在发送的标头。

【讨论】:

对——这正是它的作用:没有video/mp4 标头,它会恢复为text/html。但是在 Safari 中出现了这种奇怪的情况,它 确实 发送 video/mp4 标头,但不播放视频(除非我注释掉 exit/die)。把我的头撞在桌子上。 您是在一开始就定义$fail=FALSE,还是尝试评估(可能)未定义的变量? 是的,脚本开头有一个$fail = false;,即使我第二次在if 语句之前添加它。【参考方案3】:

它呈现空白页面的唯一原因是它确实进入了出口;陈述。您提到了一个身份验证令牌,在 safari 中进行测试时会不会丢失它? Firebug 和 Safari 的开发者工具还可以帮助您进行调试(检查响应标头等)。

【讨论】:

当我在die() 条件之前添加$fail = true; 时,它确实 显示空白页(请参阅原始问题中的说明)。有 3 种不同的响应:text/html、video/mp4(视频播放)和 video/mp4(视频损坏)。太奇怪了。

以上是关于单个PHP“退出;”声明阻止 Safari 中的 HTML5 视频的主要内容,如果未能解决你的问题,请参考以下文章

php 此MU插件将阻止任何网站管理员在单个站点中的多站点或主管理员配置文件中编辑超级管理员配置文件。这个mak

Android:仅阻止单个活动访问互联网

Safari 中的“不允许请求资源”和 Firefox 中的“阻止加载混合活动内容”。 Chrome 中的完美功能

阻止独立web应用中的链接打开Mobile Safari

如何阻止safari从网页直接跳转至app

苹果手机怎么阻止弹出网页