openresty / lua-nginx-module

Embed the Power of Lua into NGINX HTTP servers
https://openresty.org/
11.33k stars 2.03k forks source link

ngx.location.capture_multi request hang when body is nil #2234

Closed shengbinxu closed 1 year ago

shengbinxu commented 1 year ago

nginx version: openresty/1.21.4.2

server {
    listen 8005;
    server_name localhost;

    location /backend {
        internal;
        proxy_connect_timeout 1;
        proxy_read_timeout 5;
        proxy_send_timeout 5;
        proxy_pass https://www.baidu.com;
    }

    location / {
        content_by_lua_block {
            local json = require "cjson"
            local requests ={};
            local body = "";
            -- local body = nil;
            requests[1] = {"/backend", {method=ngx.HTTP_POST, body=body, copy_all_vars=true }}
            res1, res2, res3 = ngx.location.capture_multi(requests);
            ngx.say(json.encode(res1));        
        }
    }
}

When the value of the parameter body is nil, ngx.location.capture_multi will block until timeout (in this case, 5s), which is very confusing when debugging this feature. As a comparison, when the value of the parameter body is an empty string, ngx.location.capture_multi will return soon.

Expected effect: When the value of the parameter body is nil, openrest can provide error or exception prompts instead of constantly blocking.

lynch1981 commented 1 year ago

body is allowed to be nil. I tried to reproduce this issue, but there is no difference between empty string and nil. can you attach the output of curl?

curl -s http://127.0.0.1:8005/

shengbinxu commented 1 year ago

@lynch1981 Thank you for your reply. There was an omission in my previous description of the problem. Only when a post request is used and the post body is not empty, will the exception be triggered.

curl -s  'http://127.0.0.1:8005' -d 'xxx'
image

nginx error log:

2023/10/14 20:45:21 [warn] 8688#0: *1336 upstream server temporarily disabled while reading response header from upstream, client: 127.0.0.1, server: localhost, request: "POST / HTTP/1.1", subrequest: "/backend", upstream: "https://153.3.238.110:443/backend", host: "127.0.0.1:8005"
2023/10/14 20:45:21 [error] 8688#0: *1336 upstream timed out (60: Operation timed out) while reading response header from upstream, client: 127.0.0.1, server: localhost, request: "POST / HTTP/1.1", subrequest: "/backend", upstream: "https://153.3.238.110:443/backend", host: "127.0.0.1:8005"
2023/10/14 20:45:21 [info] 8688#0: *1336 kevent() reported that client 127.0.0.1 closed keepalive connection
lynch1981 commented 1 year ago

When you call ngx.location.capture_multi you set body to nil, which means no request body is required when initializing the subrequest. Therefore, the request body carried by curl -d "xxx" will not be passed to the backend. But when you set the -d parameter, the request header will contain "Content-Length: 3", which requires the backend server to receive the request body, and the server waits until it times out.

shengbinxu commented 1 year ago

Okay, I understand a bit.

I understand the mechanism you described. And:

  1. What is the way to dump the request information of subrequest?
  2. Besides this mechanism of "the server waits until it times out", can it immediately fail and report an error to the user?
lynch1981 commented 1 year ago
  1. you can use wireshark to capture the packets, but the backend uses http protocol, not https
  2. No, you told the backend server that there will be a three-byte request body, so the backend server can only wait