@@ -10,6 +10,8 @@ our $HttpConfig = qq{
1010 lua_package_path "$ pwd /lib/?.lua;/usr/local/share/lua/5.1/?.lua;;";
1111 error_log logs/error.log debug;
1212
13+ lua_shared_dict test_dict 1m;
14+
1315 init_by_lua_block {
1416 if $ ENV {TEST_COVERAGE} == 1 then
1517 jit. off()
@@ -120,3 +122,96 @@ bar\r
120122chunked request bodies not supported yet
121123--- no_error_log
122124[warn]
125+
126+
127+ === TEST 4: Read a body in a timer context with explicitly provided socket and headers
128+ --- http_config eval: $ ::HttpConfig
129+ --- config
130+ location = /b {
131+ content_by_lua_block {
132+ ngx. header[" Content-Length" ] = 3
133+ ngx. print (" foo" )
134+ }
135+ }
136+
137+ location = /a {
138+ content_by_lua_block {
139+ -- ngx. var / ngx. req.* are not available inside a timer, so
140+ -- capture what we need and pass it in explicitly.
141+ local port = ngx. var. server_port
142+
143+ local function handler(premature, port)
144+ local sock = ngx. socket. tcp()
145+ local ok , err = sock: connect(" 127.0.0.1" , port)
146+ if not ok then
147+ ngx. shared. test_dict: set(" body" , " connect failed: " .. err)
148+ return
149+ end
150+
151+ sock: send(" GET /b HTTP/1.0\r\n Host: localhost\r\n\r\n " )
152+
153+ -- Read the status line and response headers off the socket.
154+ local content_length
155+ while true do
156+ local line = sock: receive(" *l" )
157+ if not line or line == " " then
158+ break
159+ end
160+ local k, v = line: match(" ^([^:]+):% s *(.+)$" )
161+ if k and k: lower() == " content-length" then
162+ content_length = tonumber(v)
163+ end
164+ end
165+
166+ -- Use the explicit socket + headers overloads since
167+ -- ngx. req. socket() / ngx. req. get_headers() do not work here.
168+ local httpc = require (" resty.http" ). new ()
169+ local headers = { [" Content-Length" ] = content_length }
170+ local reader, err = httpc: get_client_body_reader(8192 , sock, headers)
171+ if not reader then
172+ ngx. shared. test_dict: set(" body" , " no reader: " .. (err or " nil" ))
173+ return
174+ end
175+
176+ local body = {}
177+ repeat
178+ local buffer, err = reader()
179+ if err then
180+ ngx. shared. test_dict: set(" body" , " read error: " .. err)
181+ return
182+ end
183+ if buffer then
184+ body [# body + 1] = buffer
185+ end
186+ until not buffer
187+
188+ sock: close()
189+ ngx. shared. test_dict: set(" body" , table. concat(body ))
190+ end
191+
192+ local ok , err = ngx. timer. at(0 , handler, port)
193+ if not ok then
194+ ngx. say (" failed to create timer: " , err)
195+ return
196+ end
197+
198+ -- Wait for the timer to finish and store its result.
199+ local body
200+ for _ = 1 , 100 do
201+ body = ngx. shared. test_dict: get(" body" )
202+ if body then
203+ break
204+ end
205+ ngx. sleep (0.01 )
206+ end
207+
208+ ngx. say (body )
209+ }
210+ }
211+ --- request
212+ GET /a
213+ -- - response_body
214+ foo
215+ -- - no_error_log
216+ [error]
217+ [warn ]
0 commit comments