ngx.print return error "header only"

48 views
Skip to first unread message

shengliang zhu

unread,
Nov 6, 2018, 4:00:28 AM11/6/18
to openresty
实际应用是用了http模块(https://github.com/pintsized/lua-resty-http)中的proxy_response,在输出body给client时出的错。
发现这个错误后,查了一些资料,没啥结果,翻看源码发现 ngx_http_lua_ngx_echo 里是针对r->header_only的判断导致的。
在源码附带的用例(https://github.com/openresty/lua-nginx-module/blob/master/t/002-content.t)里看到这两个用例
=== TEST 33: headers_sent & HEAD
--- config
    location
/lua {
        content_by_lua
'
            ngx.say(ngx.headers_sent)
            local ok, err = ngx.flush()
            if not ok then
                ngx.log(ngx.WARN, "failed to flush: ", err)
                return
            end
            ngx.say(ngx.headers_sent)
        '
;
   
}
--- request
HEAD
/lua
--- response_body
--- no_error_log
[error]
--- error_log
failed to flush
: header only


=== TEST 34: HEAD & ngx.say
--- config
    location
/lua {
        content_by_lua
'
            ngx.send_headers()
            local ok, err = ngx.say(ngx.headers_sent)
            if not ok then
                ngx.log(ngx.WARN, "failed to say: ", err)
                return
            end
        '
;
   
}
--- request
HEAD
/lua
--- response_body
--- no_error_log
[error]
--- error_log
failed to say
: header only

没明白这两个用例背后的测试点的原理,为啥会出现“header only”?

tokers

unread,
Nov 6, 2018, 6:33:38 AM11/6/18
to openresty
Hello!

这里测试的点是 HTTP HEAD 请求。

shengliang zhu

unread,
Nov 6, 2018, 8:16:32 PM11/6/18
to openresty
嗯嗯,看到了。但是我的请求是GET,不是HEAD,这就有点诡异了

在 2018年11月6日星期二 UTC+8下午7:33:38,tokers写道:

tokers

unread,
Nov 6, 2018, 9:34:24 PM11/6/18
to openresty
Hello!

麻烦提供一个最小化的例子吧,能供大家分析使用。

shengliang zhu

unread,
Nov 6, 2018, 10:50:26 PM11/6/18
to openresty
因为是在生产环境中发现的,不确定client的request的详细参数,只知道request line是GET一个资源,属于下载业务,并且是从web浏览器发出的。

目前详查了一下日志,发现最终给client的响应是304,所以我猜测request中带了“Last-Modified” 或者 “If-Modified-Since”。
我又写了个case模拟这种情形,其他的都对上了,status由200自动变为304,没有body,就是ngx.print不出现错误。

下面是一个实际业务请求非模拟case的流程图:
QQ20181107-112033@2x.png



在 2018年11月7日星期三 UTC+8上午10:34:24,tokers写道:
Hello!

麻烦提供一个最小化的例子吧,能供大家分析使用。

shengliang zhu

unread,
Nov 6, 2018, 11:08:00 PM11/6/18
to openresty
我写的case如下:
        location /test/304 {
            content_by_lua_block {
                ngx.status = 200

                ngx.header["ETag"] = "\"00f29033f37df21bf5e7d233f1387c67\""
                ngx.header["Last-Modified"] = "Wed, 07 Nov 2018 02:34:43 GMT"

                ngx.log(ngx.DEBUG, ngx.print(123))
            }
        }

curl -i -H "If-Modified-Since:Wed, 07 Nov 2018 02:34:43 GMT" localhost:7000/test/304

HTTP/1.1 304 Not Modified
Server: openresty/1.13.6.1
Date: Wed, 07 Nov 2018 04:05:46 GMT
Connection: keep-alive
ETag: "00f29033f37df21bf5e7d233f1387c67"
Last-Modified: Wed, 07 Nov 2018 02:34:43 GMT


结果:status由200自动变为304,没有body(123),ngx.print不出现错误


在 2018年11月7日星期三 UTC+8上午11:50:26,shengliang zhu写道:

tokers

unread,
Nov 7, 2018, 3:13:57 AM11/7/18
to openresty
Hello!

HTTP 协议里对 304 响应应该是说了不能携带 body 的。
因此 nginx 的 ngx_http_header_filter_module 会设置 r->header_only = 1。

ngx.print 会在如果头部没发送时先发送头部。
在你这种情况下,ngx.print 内部发现发送完头部后 r->header_only = 1,然后就直接返回了。

如果你再尝试用 ngx.print  发送一次数据,我相信就会出现 header only 的错误了。

shengliang zhu

unread,
Nov 7, 2018, 4:22:27 AM11/7/18
to openresty
按照你的说明试了一下,确实出现了header only错误。
这里还有个疑问:在没有任意调用ngx.print/say/flush之前,status和header会自动先发过去吗?还是说必须由这几个方法触发?


在 2018年11月7日星期三 UTC+8下午4:13:57,tokers写道:
Hello!

HTTP 协议里对 304 响应应该是说了不能携带 body 的。
因此 nginx 的 ngx_http_header_filter_module 会设置 r->header_only = 1。

ngx.print 会在如果头部没发送时先发送头部。
在你这种情况下,ngx.print 内部发现发送完头部后 r->header_only = 1,然后就直接返回了。 ----这里的直接返回,就是说舍弃了上层传入的body??

tokers

unread,
Nov 7, 2018, 5:02:25 AM11/7/18
to openresty
Hello!

不会。

头部可以在使用 ngx.print/ngx.say 时隐式地被发送出去;也可以通过 ngx.send_headers 发送出去。 

shengliang zhu

unread,
Nov 7, 2018, 5:25:55 AM11/7/18
to openresty
我的疑问被完全解答了,谢谢你的帮助 !!!

在 2018年11月7日星期三 UTC+8下午6:02:25,tokers写道:
Reply all
Reply to author
Forward
0 new messages