我发觉研究的东西多了,就会看到各种神奇的 Bug……
今天写的这个 Bug 是关于 Apache 的 Rewrite 模块的。先来看一个很正常的 Rewrite 规则:
1 2 | RewriteCond %{REQUEST_URI} \.u$ RewriteRule ^(.*)\.u$ $1 |
这个什么意思呢?用过的人一定看得出来,就是把一个 .t 结尾的请求发送到一个去掉 .t 的文件上面。比如我如果请求 upsuper.u 就会自动调用 upsuper 这个文件来返回。这没有什么疑问。
然后,我们发现,.u 文件浏览器不知道是什么,而 upsuper 这个文件在服务器上又是没有类型的,于是 Apache 不知道告诉浏览器(或者错误地告诉了一个)MIME-Type。这样即使这个文件是个网页,浏览器上也会出现下载提示或者当作纯文本直接输出。
翻阅了一下 Rewrite 模块的资料,我们了解到可以使用 T 这个标志符来强制指定 MIME-Type 类型,于是规则变成了下面这样:
1 2 | RewriteCond %{REQUEST_URI} \.u$ RewriteRule ^(.*)\.u$ $1 [T=text/html] |
可是无效!
Bug 开始出现了~
在 Apache 基金会的 Bugzilla 里面翻到了关于这个 Bug 的内容。然后看一看日期,OMG,2005年的 Bug,很跨10个年头没有解决!
看了一下,里面有一个可能是开发人员,说这一问题可能是由于引发了一个内部跳转导致的。并且提供了一种解决方法,我参照该解决方法再次修改我的 Rewrite 规则,变成下面这样:
1 2 3 | RewriteCond %{REQUEST_URI} \.u$ RewriteRule ^(.*)\.u$ $1 RewriteRule . - [T=text/html] |
成功了!泪流满面……
不过,有趣的还没结束~仅能变出 MIME-Type 为 text/html 本来就不是我最初的想法。我的想法更大胆一些:识别 URL 中的一部分作为 MIME-Type!
我发现 Rewrite 规则中,标志 E 可以修改环境变量,而且在后面的过程中还可以使用~我觉得那这个来当中间变量是一个很好的想法,于是就动手了:
1 2 3 | RewriteCond %{REQUEST_URI} \.u$ RewriteRule ^([a-zA-Z]+/[a-zA-Z-]+)/(.*)\.u$ $2 [E=MIMETYPE:$1] RewriteRule . - [T=%{ENV:MIMETYPE}] |
稍加辨别不难看出,“([a-zA-Z]+/[a-zA-Z-]+)”部分正是识别一个 MIME-Type,在而后的 [E=MIMETYPE:$1] 将这一部分存入了 MIMETYPE 这一环境变量,然后在第三行将这个环境变量的值作为 MIME-Type。
可是,当我们尝试访问 text/html/upsuper.u 的时候,这个文件再次被直接输出了,MIME-Type 根本没有被指定!疯了……
看看 RewriteLog 里面都写了些什么(已去除无关部分):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | [rid#21e031f8/initial] (3) add path info postfix: ~/web/test/text -> ~/web/test/text/html/upsuper.u [rid#21e031f8/initial] (3) strip per-dir prefix: ~/web/test/text/html/upsuper.u -> text/html/upsuper.u [rid#21e031f8/initial] (3) applying pattern '^([a-zA-Z]+/[a-zA-Z-]+)/(.*)\.u$' to uri 'text/html/upsuper.u' [rid#21e031f8/initial] (2) rewrite 'text/html/upsuper.u' -> 'upsuper' [rid#21e031f8/initial] (3) add per-dir prefix: upsuper -> ~/web/test/upsuper [rid#21e031f8/initial] (3) add path info postfix: ~/web/test/upsuper -> ~/web/test/upsuper/html/upsuper.u [rid#21e031f8/initial] (3) strip per-dir prefix: ~/web/test/upsuper/html/upsuper.u -> upsuper/html/upsuper.u [rid#21e031f8/initial] (3) applying pattern '.' to uri 'upsuper/html/upsuper.u' [rid#21e031f8/initial] (2) remember ~/web/test/upsuper to have MIME-type 'text/html' [rid#21e031f8/initial] (2) strip document_root prefix: ~/web/test/upsuper -> /test/upsuper [rid#21e031f8/initial] (1) internal redirect with /test/upsuper [INTERNAL REDIRECT] [rid#21e031f8/initial] (1) force filename redirect:/test/upsuper to have MIME-type 'text/html' [rid#21e08c80/initial/redir#1] (3) strip per-dir prefix: ~/web/test/upsuper -> upsuper [rid#21e08c80/initial/redir#1] (3) applying pattern '^([a-zA-Z]+/[a-zA-Z-]+)/(.*)\.u$' to uri 'upsuper' [rid#21e08c80/initial/redir#1] (3) strip per-dir prefix: ~/web/test/upsuper -> upsuper [rid#21e08c80/initial/redir#1] (3) applying pattern '.' to uri 'upsuper' [rid#21e08c80/initial/redir#1] (1) pass through ~/web/test/upsuper |
注意看第9行,在这里 MIME-Type 已经被正确地设置了,但随后开始了内部重定向……orz
本质上说,最后这样修改和前面一次没有很大的本质不同,最后一句都是除了修改 MIME-Type 没有修改任何网址内容。可是表现却有如此大的不同,究竟是为什么呢?
“2005年的 Bug,很跨10个年头没有解决!”
难道你已经进入 #2015 了?