使用 ngx.header 修改 Accept-Ranges 的问题
今天有位朋友在使用 OpenResty 时发现一个奇怪的事情。
他打算用 ngx.header
来修改响应的 Accept-Ranges 头,于是按照惯例做法,在 *_by_lua
脚本中写入 ngx.header["Accept-Ranges"] = "xxx"
。在 OpenResty 中,这是一种经常被使用到的方法。
他发现的问题是,当这么修改后,真实响应的 Accept-Ranges 头不是被改写了,而是出现了两个并列的 Accept-Ranges 头,其中第一个的值是他打算修改的值,而第二个 Accept-Ranges 头的值却是原始的值。
开源软件的好处就是,所谓“奇怪”的事情只要愿意探究,都能找到答案。下面就简单的解释一下为什么使用 ngx.header
修改 Accept-Ranges 响应头的时候,会导致两个并列的 Accept-Ranges 头。
首先,Nginx 在内部是通过一个单链表来管理一个请求的响应头。
其次,Nginx 自身的 Accept-Ranges 响应头的设置是由 ngx_http_range_header_filter_module
模块来完成的。我们知道,Nginx 对于 HTTP 请求的处理是分阶段的,header_filter 和 body_filter 的位置在处理链的位置中很靠后。
明确上面这两点之后,再通过阅读一下 nginx-lua 的相关代码,就可以解释这个问题了:
-
ngx.header
的处理方式是,在添加响应头之前,会查找响应头的链表中是否已经有了该响应头,如果没有,则添加,如果有,则覆盖。这种行为正是我们已知的。 -
由于
ngx_http_range_header_filter_module
的位置在 nginx-lua 之后,所以在我们使用ngx.header
添加/修改了 Accept-Ranges 头部之后,ngx_http_range_header_filter_module
依然要添加 Accept-Ranges 头。 -
ngx_http_range_header_filter_module
在添加 Accept-Ranges 头时的行为是不检查当前响应头链表里是否已经有了 Accept-Ranges 头部,不管三七二十一,直接在响应头的链表里添加一个 Accept-Ranges 响应头。
所以,ngx.header
和 ngx_http_range_header_filter_module
各自往响应头里添加了一个 Accept-Ranges 头,导致响应头中出现了两个并列的 Accept-Ranges。