NGINX 408超时(读取客户端请求标头)

时间:2018-09-11 14:12:01

标签: nginx

我正在处理从IoT设备接收请求的服务器。他们在引导时执行HEAD请求。不幸的是,标题似乎有问题。

NGINX访问日志

[11/Sep/2018:13:41:11 +0000] "HEAD / HTTP/1.1" 408 0 "-" "-" --- "-" "-"

日志格式如下

log_format custom '[$time_local] "$request" $status $body_bytes_sent '
                  '"$http_referer" "$http_user_agent" ---'
                  '"$content_type" "$content_length"';

NGINX错误日志

2018/09/11 13:40:11 [debug] 31368#31368: *1 accept: SRCIP:33930 fd:32
2018/09/11 13:40:11 [debug] 31368#31368: *1 event timer add: 32: 60000:1536673271229
2018/09/11 13:40:11 [debug] 31368#31368: *1 reusable connection: 1
2018/09/11 13:40:11 [debug] 31368#31368: *1 epoll add event: fd:32 op:1 ev:80002001
2018/09/11 13:40:11 [debug] 31368#31368: *1 post event 000056011DBAA2C0
2018/09/11 13:40:11 [debug] 31368#31368: *1 delete posted event 000056011DBAA2C0
2018/09/11 13:40:11 [debug] 31368#31368: *1 http wait request handler
2018/09/11 13:40:11 [debug] 31368#31368: *1 malloc: 000056011DAAB650:1024
2018/09/11 13:40:11 [debug] 31368#31368: *1 recv: fd:32 71 of 1024
2018/09/11 13:40:11 [debug] 31368#31368: *1 reusable connection: 0
2018/09/11 13:40:11 [debug] 31368#31368: *1 posix_memalign: 000056011DB0DCD0:4096 @16
2018/09/11 13:40:11 [debug] 31368#31368: *1 http process request line
2018/09/11 13:40:11 [debug] 31368#31368: *1 http request line: "HEAD / HTTP/1.1"
2018/09/11 13:40:11 [debug] 31368#31368: *1 http uri: "/"
2018/09/11 13:40:11 [debug] 31368#31368: *1 http args: ""
2018/09/11 13:40:11 [debug] 31368#31368: *1 http exten: ""
2018/09/11 13:40:11 [debug] 31368#31368: *1 posix_memalign: 000056011DB690C0:4096 @16
2018/09/11 13:40:11 [debug] 31368#31368: *1 http process request header line
2018/09/11 13:40:11 [debug] 31368#31368: *1 http header: "Host: MYHOST"
2018/09/11 13:40:11 [debug] 31368#31368: *1 recv: fd:32 -1 of 953
2018/09/11 13:40:11 [debug] 31368#31368: *1 recv() not ready (11: Resource temporarily unavailable)
2018/09/11 13:41:11 [debug] 31368#31368: *1 event timer del: 32: 1536673271229
2018/09/11 13:41:11 [debug] 31368#31368: *1 http process request header line
2018/09/11 13:41:11 [info] 31368#31368: *1 client timed out (110: Connection timed out) while reading client request headers, client: SRCIP, server: MYHOST, request: "HEAD / HTTP/1.1", host: "MYHOST"
2018/09/11 13:41:11 [debug] 31368#31368: *1 http request count:1 blk:0
2018/09/11 13:41:11 [debug] 31368#31368: *1 http close request
2018/09/11 13:41:11 [debug] 31368#31368: *1 http log handler
2018/09/11 13:41:11 [debug] 31368#31368: *1 free: 000056011DB0DCD0, unused: 707
2018/09/11 13:41:11 [debug] 31368#31368: *1 free: 000056011DB690C0, unused: 3104
2018/09/11 13:41:11 [debug] 31368#31368: *1 close http connection: 32
2018/09/11 13:41:11 [debug] 31368#31368: *1 reusable connection: 0
2018/09/11 13:41:11 [debug] 31368#31368: *1 free: 000056011DAAB650
2018/09/11 13:41:11 [debug] 31368#31368: *1 free: 000056011DAFF960, unused: 128

Tcpdump sudo tcpdump -n -S -s 0 -A 'src SRCIP and port 80'显示

13:55:32.846408 IP SRCIP.39761 > DSTIP.80: Flags [S], seq 1846787, win 2920, options [mss 1460], length 0
E..,....p. *E......h.Q.P........`..h\;........
13:55:33.153456 IP SRCIP.39761 > DSTIP.80: Flags [.], ack 3538300854, win 2920, length 0
E..(....p..^E......h.Q.P....../.P..hqK........
13:55:33.314206 IP SRCIP.39761 > DSTIP.80: Flags [P.], seq 1846788:1846859, ack 3538300854, win 2920, length 71: HTTP: HEAD / HTTP/1.1
E..o&...p..CE......h.Q.P....../.P..hg...HEAD / HTTP/1.1
Host: MYHOST
Content-Length:
13:56:33.363048 IP SRCIP.39761 > DSTIP.80: Flags [F.], seq 1846859, ack 3538300855, win 2919, length 0
E..(....p...E......h.Q.P...K../.P..gq.........

我无法更改设备中的固件,因此我正在NGINX方面寻求解决方法。请让我知道是否可以提供更多信息来帮助您解答。

编辑:我没有添加服务器配置,因为尝试了太多,而且不确定在此处粘贴什么。

编辑2:第一个日志中的tcpdump

13:55:32.846408 IP SRCIP.39761 > DSTIP.80: Flags [S], seq 1846787, win 2920, options [mss 1460], length 0
E..,....p. *E......h.Q.P........`..h\;........
13:55:33.153456 IP SRCIP.39761 > DSTIP.80: Flags [.], ack 3538300854, win 2920, length 0
E..(....p..^E......h.Q.P....../.P..hqK........
13:55:33.314206 IP SRCIP.39761 > DSTIP.80: Flags [P.], seq 1846788:1846859, ack 3538300854, win 2920, length 71: HTTP: HEAD / HTTP/1.1
E..o&...p..CE......h.Q.P....../.P..hg...HEAD / HTTP/1.1
Host: MYHOST
Content-Length:

然后休息一段时间。我认为是在NGINX超时之后。

编辑3:我终于了解发生了什么。这确实令人困惑,因为在生产中只有一台Apache服务器可以使设备正常工作。尝试切换到NGINX时,一切停止了。

如上所述,启动时的IoT设备正在执行HEAD请求。他们希望收到带有Date:标头的响应,以便可以对其进行解析。

当前,该设备与Apache正常工作,因为在等待来自客户端的标头时触发超时时,Apache向客户端返回408响应,包括Date:标头。

  

此伪指令可以设置各种超时,以从客户端接收请求标头和请求正文。如果客户端未能在配置的时间内发送标头或正文,则会发送408 REQUEST TIME OUT错误。   (https://httpd.apache.org/docs/2.4/mod/mod_reqtimeout.html

另一方面,NGINX在等待来自客户端的标头时触发超时时,只是关闭连接而没有返回任何内容给客户端。即使它在访问日志中记录了408。

  

定义读取客户端请求标头的超时。如果客户端在此时间内未传输整个标头,则请求将以408(请求超时)错误终止。   (http://nginx.org/en/docs/http/ngx_http_core_module.html#client_header_timeout

已经https://trac.nginx.org/nginx/ticket/1005对此行为进行了讨论。

换句话说,来自IoT设备的HEAD请求始终是错误的。它仅适用于Apache,因为只要触发标题等待超时,就会发回带有Date:的408响应。

如上所述,很遗憾,我无法改变设备的工作方式。因此,我需要在NGINX中解决。我发现的唯一方法是更改​​源代码并自行编译。

这是我通过互联网复制/粘贴而想到的。不幸的是,我还没有时间去理解代码,也永远不会。如果有人帮助我了解该代码有多糟糕以及编写它的更好方法,那将是非常不错的。

NGINX的版本是1.14.0。

diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c
index 2db7a62..086701b 100644
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -1236,7 +1236,7 @@ ngx_http_process_request_headers(ngx_event_t *rev)
     if (rev->timedout) {
         ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
         c->timedout = 1;
-        ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT);
+        ngx_http_finalize_request(r, ngx_http_special_response_handler(r, NGX_HTTP_REQUEST_TIME_OUT));
         return;
     }

为验证代码是否正常运行,我使用了Telnet:

这是NGINX通常会做的

请求

$ telnet HOST 80
Trying IP...
Connected to HOST.
Escape character is '^]'.
HEAD / HTTP/1.1
Content-Length:

响应

Connection closed by foreign host.

这是NGINX对修改后的代码所做的

请求

$ telnet HOST 80
Trying IP...
Connected to HOST.
Escape character is '^]'.
HEAD / HTTP/1.1
Content-Length:

响应

HTTP/1.1 408 Request Time-out
Server: nginx
Date: Tue, 25 Sep 2018 08:18:41 GMT
Content-Type: text/html
Content-Length: 176
Connection: close

请注意,我可以使用另一个标头代替Content-Length:(例如Accept:,结果将相同)。如果要尝试复制,请记住在空白标题(在示例Content-Length:中)后按一次Enter键(并且仅一次)。

1 个答案:

答案 0 :(得分:0)

我最终使用了Apache。实际上,似乎无法配置NGINX将408发送回客户端(而不是只是关闭连接)。修补源极具风险,并且使服务器更新很痛苦。

相关问题