Mercurial > hg > nginx-tests
comparison body_discard.t @ 1961:fe6f22da53ec
Tests: tests for usage of discarded body.
The client_max_body_size limit should be ignored when the request body
is already discarded. In HTTP/1.x, this is done by checking the
r->discard_body flag when the body is being discarded, and because
r->headers_in.content_length_n is 0 when it's already discarded. This,
however, does not happen with HTTP/2 and HTTP/3, and therefore
"error_page 413" does not work without relaxing the limit.
Further, with proxy_pass, r->headers_in.content_length_n is used to determine
length of the request body, and therefore is not correct if discarding of
the request body isn't yet complete. While discarding the request body,
r->headers_in.content_length_n contains the rest of the body to discard
(or, in case of chunked request body, the rest of the current chunk to
discard).
Similarly, the $content_length variable uses r->headers_in.content_length
if available, and also incorrect. The $content_length variable is used
when proxying with fastcgi_pass, grpc_pass, and uwsgi_pass (scgi_pass uses
the value calculated based on the actual request body buffers, and therefore
works correctly).
author | Maxim Dounin <mdounin@mdounin.ru> |
---|---|
date | Sat, 27 Apr 2024 18:55:50 +0300 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
1960:e44ee916b959 | 1961:fe6f22da53ec |
---|---|
1 #!/usr/bin/perl | |
2 | |
3 # (C) Maxim Dounin | |
4 | |
5 # Tests for discarding request body. | |
6 | |
7 ############################################################################### | |
8 | |
9 use warnings; | |
10 use strict; | |
11 | |
12 use Test::More; | |
13 use Socket qw/ CRLF /; | |
14 | |
15 BEGIN { use FindBin; chdir($FindBin::Bin); } | |
16 | |
17 use lib 'lib'; | |
18 use Test::Nginx; | |
19 | |
20 ############################################################################### | |
21 | |
22 select STDERR; $| = 1; | |
23 select STDOUT; $| = 1; | |
24 | |
25 my $t = Test::Nginx->new() | |
26 ->has(qw/http proxy rewrite addition memcached/); | |
27 | |
28 plan(skip_all => 'not yet') unless $t->has_version('1.27.0'); | |
29 | |
30 $t->plan(33)->write_file_expand('nginx.conf', <<'EOF'); | |
31 | |
32 %%TEST_GLOBALS%% | |
33 | |
34 daemon off; | |
35 | |
36 events { | |
37 } | |
38 | |
39 http { | |
40 %%TEST_GLOBALS_HTTP%% | |
41 | |
42 server { | |
43 listen 127.0.0.1:8080; | |
44 server_name localhost; | |
45 | |
46 lingering_timeout 1s; | |
47 add_header X-Body body:$content_length:$request_body:; | |
48 | |
49 client_max_body_size 1k; | |
50 | |
51 error_page 400 /proxy/error400; | |
52 | |
53 location / { | |
54 error_page 413 /error413; | |
55 proxy_pass http://127.0.0.1:8082; | |
56 } | |
57 | |
58 location /error413 { | |
59 return 200 "custom error 413"; | |
60 } | |
61 | |
62 location /add { | |
63 return 200 "main response"; | |
64 add_before_body /add/before; | |
65 addition_types *; | |
66 client_max_body_size 1m; | |
67 } | |
68 | |
69 location /add/before { | |
70 proxy_pass http://127.0.0.1:8081; | |
71 } | |
72 | |
73 location /memcached { | |
74 client_max_body_size 1m; | |
75 error_page 502 /memcached/error502; | |
76 memcached_pass 127.0.0.1:8083; | |
77 set $memcached_key $request_uri; | |
78 } | |
79 | |
80 location /memcached/error502 { | |
81 proxy_pass http://127.0.0.1:8081; | |
82 } | |
83 | |
84 location /proxy { | |
85 client_max_body_size 1; | |
86 error_page 413 /proxy/error413; | |
87 error_page 400 /proxy/error400; | |
88 error_page 502 /proxy/error502; | |
89 proxy_pass http://127.0.0.1:8083; | |
90 } | |
91 | |
92 location /proxy/error413 { | |
93 proxy_pass http://127.0.0.1:8081; | |
94 } | |
95 | |
96 location /proxy/error400 { | |
97 proxy_pass http://127.0.0.1:8081; | |
98 } | |
99 | |
100 location /proxy/error502 { | |
101 proxy_pass http://127.0.0.1:8081; | |
102 } | |
103 | |
104 location /unbuf { | |
105 client_max_body_size 1m; | |
106 error_page 502 /unbuf/error502; | |
107 proxy_pass http://127.0.0.1:8083; | |
108 proxy_request_buffering off; | |
109 proxy_http_version 1.1; | |
110 } | |
111 | |
112 location /unbuf/error502 { | |
113 client_max_body_size 1m; | |
114 proxy_pass http://127.0.0.1:8081; | |
115 } | |
116 | |
117 location /length { | |
118 client_max_body_size 1; | |
119 error_page 413 /length/error413; | |
120 error_page 502 /length/error502; | |
121 proxy_pass http://127.0.0.1:8083; | |
122 } | |
123 | |
124 location /length/error413 { | |
125 return 200 "frontend body:$content_length:$request_body:"; | |
126 } | |
127 | |
128 location /length/error502 { | |
129 return 200 "frontend body:$content_length:$request_body:"; | |
130 } | |
131 } | |
132 | |
133 server { | |
134 listen 127.0.0.1:8081; | |
135 server_name localhost; | |
136 | |
137 location / { | |
138 proxy_pass http://127.0.0.1:8082; | |
139 proxy_set_header X-Body body:$content_length:$request_body:; | |
140 } | |
141 } | |
142 | |
143 server { | |
144 listen 127.0.0.1:8082; | |
145 server_name localhost; | |
146 | |
147 return 200 "backend $http_x_body"; | |
148 } | |
149 | |
150 server { | |
151 listen 127.0.0.1:8083; | |
152 server_name localhost; | |
153 | |
154 return 444; | |
155 } | |
156 } | |
157 | |
158 EOF | |
159 | |
160 $t->run(); | |
161 | |
162 ############################################################################### | |
163 | |
164 # error_page 413 should work without redefining client_max_body_size | |
165 | |
166 like(http( | |
167 'POST / HTTP/1.0' . CRLF . | |
168 'Content-Length: 10000' . CRLF . CRLF . | |
169 '0123456789' | |
170 ), qr/ 413 .*custom error 413/s, 'custom error 413'); | |
171 | |
172 # subrequest after discarding body | |
173 | |
174 like(http( | |
175 'GET /add HTTP/1.0' . CRLF . CRLF | |
176 ), qr/backend body:::.*main response/s, 'add'); | |
177 | |
178 like(http( | |
179 'POST /add HTTP/1.0' . CRLF . | |
180 'Content-Length: 10' . CRLF . CRLF . | |
181 '0123456789' | |
182 ), qr/backend body:::.*main response/s, 'add small'); | |
183 | |
184 like(http( | |
185 'POST /add HTTP/1.0' . CRLF . | |
186 'Content-Length: 10000' . CRLF . CRLF . | |
187 '0123456789' | |
188 ), qr/backend body:::.*main response/s, 'add long'); | |
189 | |
190 like(http( | |
191 'POST /add HTTP/1.1' . CRLF . | |
192 'Host: localhost' . CRLF . | |
193 'Connection: close' . CRLF . | |
194 'Transfer-Encoding: chunked' . CRLF . CRLF . | |
195 'a' . CRLF . | |
196 '0123456789' . CRLF . | |
197 '0' . CRLF . CRLF | |
198 ), qr/backend body:::.*main response/s, 'add chunked'); | |
199 | |
200 like(http( | |
201 'POST /add HTTP/1.1' . CRLF . | |
202 'Host: localhost' . CRLF . | |
203 'Connection: close' . CRLF . | |
204 'Transfer-Encoding: chunked' . CRLF . CRLF . | |
205 '1' . CRLF . | |
206 'X' . CRLF . | |
207 '9' . CRLF . | |
208 '123456789' . CRLF . | |
209 '0' . CRLF . CRLF | |
210 ), qr/backend body:::.*main response/s, 'add chunked multi'); | |
211 | |
212 like(http( | |
213 'POST /add HTTP/1.1' . CRLF . | |
214 'Host: localhost' . CRLF . | |
215 'Connection: close' . CRLF . | |
216 'Transfer-Encoding: chunked' . CRLF . CRLF . | |
217 'ffff' . CRLF . | |
218 '0123456789' | |
219 ), qr/backend body:::.*main response/s, 'add chunked long'); | |
220 | |
221 # error_page 502 with proxy_pass after discarding body | |
222 | |
223 like(http( | |
224 'GET /memcached HTTP/1.0' . CRLF . CRLF | |
225 ), qr/ 502 .*backend body:::/s, 'memcached'); | |
226 | |
227 like(http( | |
228 'GET /memcached HTTP/1.0' . CRLF . | |
229 'Content-Length: 10' . CRLF . CRLF . | |
230 '0123456789' | |
231 ), qr/ 502 .*backend body:::/s, 'memcached small'); | |
232 | |
233 like(http( | |
234 'GET /memcached HTTP/1.0' . CRLF . | |
235 'Content-Length: 10000' . CRLF . CRLF . | |
236 '0123456789' | |
237 ), qr/ 502 .*backend body:::/s, 'memcached long'); | |
238 | |
239 like(http( | |
240 'GET /memcached HTTP/1.1' . CRLF . | |
241 'Host: localhost' . CRLF . | |
242 'Connection: close' . CRLF . | |
243 'Transfer-Encoding: chunked' . CRLF . CRLF . | |
244 'a' . CRLF . | |
245 '0123456789' . CRLF . | |
246 '0' . CRLF . CRLF | |
247 ), qr/ 502 .*backend body:::/s, 'memcached chunked'); | |
248 | |
249 like(http( | |
250 'GET /memcached HTTP/1.1' . CRLF . | |
251 'Host: localhost' . CRLF . | |
252 'Connection: close' . CRLF . | |
253 'Transfer-Encoding: chunked' . CRLF . CRLF . | |
254 '1' . CRLF . | |
255 'X' . CRLF . | |
256 '9' . CRLF . | |
257 '123456789' . CRLF . | |
258 '0' . CRLF . CRLF | |
259 ), qr/ 502 .*backend body:::/s, 'memcached chunked multi'); | |
260 | |
261 like(http( | |
262 'GET /memcached HTTP/1.1' . CRLF . | |
263 'Host: localhost' . CRLF . | |
264 'Connection: close' . CRLF . | |
265 'Transfer-Encoding: chunked' . CRLF . CRLF . | |
266 'ffff' . CRLF . | |
267 '0123456789' | |
268 ), qr/ 502 .*backend body:::/s, 'memcached chunked long'); | |
269 | |
270 # error_page 413 with proxy_pass | |
271 | |
272 like(http( | |
273 'GET /proxy HTTP/1.0' . CRLF . CRLF | |
274 ), qr/ 502 .*backend body:::/s, 'proxy'); | |
275 | |
276 like(http( | |
277 'POST /proxy HTTP/1.0' . CRLF . | |
278 'Content-Length: 10' . CRLF . CRLF . | |
279 '0123456789' | |
280 ), qr/ 413 .*backend body:::/s, 'proxy small'); | |
281 | |
282 like(http( | |
283 'POST /proxy HTTP/1.0' . CRLF . | |
284 'Content-Length: 10000' . CRLF . CRLF . | |
285 '0123456789' | |
286 ), qr/ 413 .*backend body:::/s, 'proxy long'); | |
287 | |
288 like(http( | |
289 'POST /proxy HTTP/1.1' . CRLF . | |
290 'Host: localhost' . CRLF . | |
291 'Connection: close' . CRLF . | |
292 'Transfer-Encoding: chunked' . CRLF . CRLF . | |
293 'a' . CRLF . | |
294 '0123456789' . CRLF . | |
295 '0' . CRLF . CRLF | |
296 ), qr/ 413 .*backend body:::/s, 'proxy chunked'); | |
297 | |
298 like(http( | |
299 'POST /proxy HTTP/1.1' . CRLF . | |
300 'Host: localhost' . CRLF . | |
301 'Connection: close' . CRLF . | |
302 'Transfer-Encoding: chunked' . CRLF . CRLF . | |
303 '1' . CRLF . | |
304 'X' . CRLF . | |
305 '9' . CRLF . | |
306 '123456789' . CRLF . | |
307 '0' . CRLF . CRLF | |
308 ), qr/ 413 .*backend body:::/s, 'proxy chunked multi'); | |
309 | |
310 like(http( | |
311 'POST /proxy HTTP/1.1' . CRLF . | |
312 'Host: localhost' . CRLF . | |
313 'Connection: close' . CRLF . | |
314 'Transfer-Encoding: chunked' . CRLF . CRLF . | |
315 'ffff' . CRLF . | |
316 '0123456789' | |
317 ), qr/ 413 .*backend body:::/s, 'proxy chunked long'); | |
318 | |
319 # error_page 400 with proxy_pass | |
320 | |
321 # note that "chunked and length" test triggers 400 during parsing | |
322 # request headers, and therefore needs error_page at server level | |
323 | |
324 like(http( | |
325 'POST /proxy HTTP/1.1' . CRLF . | |
326 'Host: localhost' . CRLF . | |
327 'Connection: close' . CRLF . | |
328 'Transfer-Encoding: chunked' . CRLF . CRLF . | |
329 '1' . CRLF . | |
330 'X' . CRLF . | |
331 'X' . CRLF | |
332 ), qr/ 400 .*backend body:::/s, 'proxy chunked bad'); | |
333 | |
334 like(http( | |
335 'POST /proxy HTTP/1.1' . CRLF . | |
336 'Host: localhost' . CRLF . | |
337 'Connection: close' . CRLF . | |
338 'Content-Length: 10' . CRLF . | |
339 'Transfer-Encoding: chunked' . CRLF . CRLF . | |
340 '0' . CRLF . CRLF | |
341 ), qr/ 400 .*backend body:::/s, 'proxy chunked and length'); | |
342 | |
343 # error_page 502 after proxy with request buffering disabled | |
344 | |
345 like(http( | |
346 'GET /unbuf HTTP/1.0' . CRLF . CRLF | |
347 ), qr/ 502 .*backend body:::/s, 'unbuf proxy'); | |
348 | |
349 like(http( | |
350 'POST /unbuf HTTP/1.0' . CRLF . | |
351 'Content-Length: 10' . CRLF . CRLF . | |
352 '0', | |
353 sleep => 0.1, | |
354 body => | |
355 '123456789' | |
356 ), qr/ 502 .*backend body:::/s, 'unbuf proxy small'); | |
357 | |
358 like(http( | |
359 'POST /unbuf HTTP/1.0' . CRLF . | |
360 'Content-Length: 10000' . CRLF . CRLF . | |
361 '0123456789' | |
362 ), qr/ 502 .*backend body:::/s, 'unbuf proxy long'); | |
363 | |
364 like(http( | |
365 'POST /unbuf HTTP/1.1' . CRLF . | |
366 'Host: localhost' . CRLF . | |
367 'Connection: close' . CRLF . | |
368 'Transfer-Encoding: chunked' . CRLF . CRLF, | |
369 sleep => 0.1, | |
370 body => | |
371 'a' . CRLF . | |
372 '0123456789' . CRLF . | |
373 '0' . CRLF . CRLF | |
374 ), qr/ 502 .*backend body:::/s, 'unbuf proxy chunked'); | |
375 | |
376 like(http( | |
377 'POST /unbuf HTTP/1.1' . CRLF . | |
378 'Host: localhost' . CRLF . | |
379 'Connection: close' . CRLF . | |
380 'Transfer-Encoding: chunked' . CRLF . CRLF . | |
381 '1' . CRLF . | |
382 'X' . CRLF, | |
383 sleep => 0.1, | |
384 body => | |
385 '9' . CRLF . | |
386 '123456789' . CRLF . | |
387 '0' . CRLF . CRLF | |
388 ), qr/ 502 .*backend body:::/s, 'unbuf proxy chunked multi'); | |
389 | |
390 like(http( | |
391 'POST /unbuf HTTP/1.1' . CRLF . | |
392 'Host: localhost' . CRLF . | |
393 'Connection: close' . CRLF . | |
394 'Transfer-Encoding: chunked' . CRLF . CRLF . | |
395 'ffff' . CRLF . | |
396 '0123456789' | |
397 ), qr/ 502 .*backend body:::/s, 'unbuf proxy chunked long'); | |
398 | |
399 # error_page 413 and $content_length | |
400 # (used in fastcgi_pass, grpc_pass, uwsgi_pass) | |
401 | |
402 like(http( | |
403 'GET /length HTTP/1.0' . CRLF . CRLF | |
404 ), qr/ 502 .*frontend body:::/s, '$content_length'); | |
405 | |
406 like(http( | |
407 'POST /length HTTP/1.0' . CRLF . | |
408 'Content-Length: 10' . CRLF . CRLF . | |
409 '0123456789' | |
410 ), qr/ 413 .*frontend body:::/s, '$content_length small'); | |
411 | |
412 like(http( | |
413 'POST /length HTTP/1.0' . CRLF . | |
414 'Content-Length: 10000' . CRLF . CRLF . | |
415 '0123456789' | |
416 ), qr/ 413 .*frontend body:::/s, '$content_length long'); | |
417 | |
418 like(http( | |
419 'POST /length HTTP/1.1' . CRLF . | |
420 'Host: localhost' . CRLF . | |
421 'Connection: close' . CRLF . | |
422 'Transfer-Encoding: chunked' . CRLF . CRLF . | |
423 'a' . CRLF . | |
424 '0123456789' . CRLF . | |
425 '0' . CRLF . CRLF | |
426 ), qr/ 413 .*frontend body:::/s, '$content_length chunked'); | |
427 | |
428 like(http( | |
429 'POST /length HTTP/1.1' . CRLF . | |
430 'Host: localhost' . CRLF . | |
431 'Connection: close' . CRLF . | |
432 'Transfer-Encoding: chunked' . CRLF . CRLF . | |
433 '1' . CRLF . | |
434 'X' . CRLF . | |
435 '9' . CRLF . | |
436 '123456789' . CRLF . | |
437 '0' . CRLF . CRLF | |
438 ), qr/ 413 .*frontend body:::/s, '$content_length chunked multi'); | |
439 | |
440 like(http( | |
441 'POST /length HTTP/1.1' . CRLF . | |
442 'Host: localhost' . CRLF . | |
443 'Connection: close' . CRLF . | |
444 'Transfer-Encoding: chunked' . CRLF . CRLF . | |
445 'ffff' . CRLF . | |
446 '0123456789' | |
447 ), qr/ 413 .*frontend body:::/s, '$content_length chunked long'); | |
448 | |
449 ############################################################################### |