Mercurial > hg > nginx-tests
comparison js_subrequests.t @ 1305:8e593b068fc0
Tests: added njs subrequest tests.
author | Dmitry Volyntsev <xeioex@nginx.com> |
---|---|
date | Wed, 21 Mar 2018 17:30:44 +0300 |
parents | |
children | bab9dd2654c3 |
comparison
equal
deleted
inserted
replaced
1304:25de201c8a0d | 1305:8e593b068fc0 |
---|---|
1 #!/usr/bin/perl | |
2 # | |
3 # (C) Dmitry Volyntsev. | |
4 # (C) Nginx, Inc. | |
5 | |
6 # Tests for subrequests in http JavaScript module. | |
7 | |
8 ############################################################################### | |
9 | |
10 use warnings; | |
11 use strict; | |
12 | |
13 use Test::More; | |
14 | |
15 use Socket qw/ CRLF /; | |
16 | |
17 BEGIN { use FindBin; chdir($FindBin::Bin); } | |
18 | |
19 use lib 'lib'; | |
20 use Test::Nginx; | |
21 | |
22 ############################################################################### | |
23 | |
24 select STDERR; $| = 1; | |
25 select STDOUT; $| = 1; | |
26 | |
27 eval { require JSON::PP; }; | |
28 plan(skip_all => "JSON::PP not installed") if $@; | |
29 | |
30 my $t = Test::Nginx->new()->has(qw/http rewrite proxy cache/) | |
31 ->write_file_expand('nginx.conf', <<'EOF'); | |
32 | |
33 %%TEST_GLOBALS%% | |
34 | |
35 daemon off; | |
36 | |
37 events { | |
38 } | |
39 | |
40 http { | |
41 %%TEST_GLOBALS_HTTP%% | |
42 | |
43 proxy_cache_path %%TESTDIR%%/cache1 | |
44 keys_zone=ON:1m use_temp_path=on; | |
45 | |
46 js_include test.js; | |
47 | |
48 js_set $async_var async_var; | |
49 | |
50 server { | |
51 listen 127.0.0.1:8080; | |
52 server_name localhost; | |
53 | |
54 location /sr { | |
55 js_content sr; | |
56 } | |
57 | |
58 location /sr_args { | |
59 js_content sr_args; | |
60 } | |
61 | |
62 location /sr_options_args { | |
63 js_content sr_options_args; | |
64 } | |
65 | |
66 location /sr_options_method { | |
67 js_content sr_options_method; | |
68 } | |
69 | |
70 location /sr_options_body { | |
71 js_content sr_options_body; | |
72 } | |
73 | |
74 location /sr_options_method_head { | |
75 js_content sr_options_method_head; | |
76 } | |
77 | |
78 location /sr_body { | |
79 js_content sr_body; | |
80 } | |
81 | |
82 location /sr_body_special { | |
83 js_content sr_body_special; | |
84 } | |
85 | |
86 location /sr_background { | |
87 js_content sr_background; | |
88 } | |
89 | |
90 location /sr_in_variable_handler { | |
91 set $_ $async_var; | |
92 js_content sr_in_variable_handler; | |
93 } | |
94 | |
95 location /sr_error_page { | |
96 set $_ $async_var; | |
97 error_page 404 /return; | |
98 return 404; | |
99 } | |
100 | |
101 location /sr_js_in_subrequest { | |
102 js_content sr_js_in_subrequest; | |
103 } | |
104 | |
105 location /sr_file { | |
106 js_content sr_file; | |
107 } | |
108 | |
109 location /sr_cache { | |
110 js_content sr_cache; | |
111 } | |
112 | |
113 | |
114 location /sr_unavail { | |
115 js_content sr_unavail; | |
116 } | |
117 | |
118 location /sr_broken { | |
119 js_content sr_broken; | |
120 } | |
121 | |
122 location /sr_too_large { | |
123 js_content sr_too_large; | |
124 } | |
125 | |
126 location /sr_out_of_order { | |
127 js_content sr_out_of_order; | |
128 } | |
129 | |
130 location /sr_except_not_a_func { | |
131 js_content sr_except_not_a_func; | |
132 } | |
133 | |
134 location /sr_except_failed_to_convert_arg { | |
135 js_content sr_except_failed_to_convert_arg; | |
136 } | |
137 | |
138 location /sr_except_failed_to_convert_options_arg { | |
139 js_content sr_except_failed_to_convert_options_arg; | |
140 } | |
141 | |
142 location /sr_except_invalid_options_method { | |
143 js_content sr_except_invalid_options_method; | |
144 } | |
145 | |
146 location /sr_except_invalid_options_header_only { | |
147 js_content sr_except_invalid_options_header_only; | |
148 } | |
149 | |
150 location /sr_uri_except { | |
151 js_content sr_uri_except; | |
152 } | |
153 | |
154 | |
155 location /file/ { | |
156 alias %%TESTDIR%%/; | |
157 } | |
158 | |
159 location /p/ { | |
160 proxy_cache $arg_c; | |
161 proxy_pass http://127.0.0.1:8081/; | |
162 } | |
163 | |
164 location /daemon/ { | |
165 proxy_pass http://127.0.0.1:8082/; | |
166 } | |
167 | |
168 location /too_large/ { | |
169 subrequest_output_buffer_size 3; | |
170 proxy_pass http://127.0.0.1:8081/; | |
171 } | |
172 | |
173 location /sr_in_sr { | |
174 js_content sr_in_sr; | |
175 } | |
176 | |
177 location /unavail { | |
178 proxy_pass http://127.0.0.1:8084/; | |
179 } | |
180 | |
181 location /js_sub { | |
182 js_content js_sub; | |
183 } | |
184 | |
185 location /return { | |
186 return 200 '["$request_method"]'; | |
187 } | |
188 } | |
189 | |
190 server { | |
191 listen 127.0.0.1:8081; | |
192 server_name localhost; | |
193 | |
194 location /sub1 { | |
195 add_header H $arg_h; | |
196 return 206 '{"a": {"b": 1}}'; | |
197 } | |
198 | |
199 location /sub2 { | |
200 return 404 '{"e": "msg"}'; | |
201 } | |
202 | |
203 location /method { | |
204 return 200 '["$request_method"]'; | |
205 } | |
206 | |
207 location /body { | |
208 js_content body; | |
209 } | |
210 | |
211 location /background { | |
212 js_content background; | |
213 } | |
214 | |
215 location /delayed { | |
216 js_content delayed; | |
217 } | |
218 } | |
219 | |
220 server { | |
221 listen 127.0.0.1:8084; | |
222 server_name localhost; | |
223 | |
224 return 444; | |
225 } | |
226 } | |
227 | |
228 EOF | |
229 | |
230 $t->write_file('test.js', <<EOF); | |
231 | |
232 function sr(req) { | |
233 subrequest_fn(req, ['/p/sub2'], ['uri', 'status']) | |
234 } | |
235 | |
236 function sr_args(req, res) { | |
237 req.subrequest('/p/sub1', 'h=xxx', function(reply) { | |
238 res.status = 200; | |
239 res.sendHeader(); | |
240 res.send(JSON.stringify({h:reply.headers.h})) | |
241 res.finish(); | |
242 }); | |
243 } | |
244 | |
245 function sr_options_args(req, res) { | |
246 req.subrequest('/p/sub1', {args:'h=xxx'}, function(reply) { | |
247 res.status = 200; | |
248 res.sendHeader(); | |
249 res.send(JSON.stringify({h:reply.headers.h})) | |
250 res.finish(); | |
251 }); | |
252 } | |
253 | |
254 function sr_options_method(req, res) { | |
255 req.subrequest('/p/method', {method:'POST'}, body_fwd_cb); | |
256 } | |
257 | |
258 function sr_options_body(req, res) { | |
259 req.subrequest('/p/body', {method:'POST', body:'["REQ-BODY"]'}, | |
260 body_fwd_cb); | |
261 } | |
262 | |
263 function sr_options_method_head(req, res) { | |
264 req.subrequest('/p/method', {method:'HEAD'}, function(reply){ | |
265 res.status = 200; | |
266 res.sendHeader(); | |
267 res.send(JSON.stringify({c:reply.status, s:reply.body.length})) | |
268 res.finish(); | |
269 }); | |
270 } | |
271 | |
272 function sr_body(req, res) { | |
273 req.subrequest('/p/sub1', body_fwd_cb); | |
274 } | |
275 | |
276 function sr_body_special(req, res) { | |
277 req.subrequest('/p/sub2', body_fwd_cb); | |
278 } | |
279 | |
280 function sr_background(req, res) { | |
281 req.subrequest('/p/background'); | |
282 req.subrequest('/p/background', 'a=xxx'); | |
283 req.subrequest('/p/background', {args: 'a=yyy', method:'POST'}); | |
284 | |
285 res.status = 200; | |
286 res.sendHeader(); | |
287 res.finish(); | |
288 } | |
289 | |
290 function body(req, res) { | |
291 res.status = 200; | |
292 res.sendHeader(); | |
293 res.send(req.variables.request_body); | |
294 res.finish(); | |
295 } | |
296 | |
297 function delayed(req) { | |
298 setTimeout(function(res) { | |
299 res.status = 200; | |
300 res.sendHeader(); | |
301 res.finish(); | |
302 }, 100, req.response); | |
303 } | |
304 | |
305 function background(req, res) { | |
306 req.log("BACKGROUND: " + req.variables.request_method | |
307 + " args: " + req.variables.args); | |
308 | |
309 res.status = 200; | |
310 res.sendHeader(); | |
311 res.finish(); | |
312 } | |
313 | |
314 function sr_in_variable_handler(req, res) { | |
315 } | |
316 | |
317 function async_var(req, res) { | |
318 req.subrequest('/p/delayed', function(reply) { | |
319 res.status = 200; | |
320 res.sendHeader(); | |
321 res.send(JSON.stringify(["CB-VAR"])) | |
322 res.finish(); | |
323 }) | |
324 | |
325 return ""; | |
326 } | |
327 | |
328 function sr_file(req, res) { | |
329 req.subrequest('/file/t', body_fwd_cb); | |
330 } | |
331 | |
332 function sr_cache(req, res) { | |
333 req.subrequest('/p/t', body_fwd_cb); | |
334 } | |
335 | |
336 function sr_unavail(req) { | |
337 subrequest_fn(req, ['/unavail'], ['uri', 'status']); | |
338 } | |
339 | |
340 function sr_broken(req, res) { | |
341 req.subrequest('/daemon/unfinished', | |
342 function(reply) { | |
343 res.status = 200; | |
344 res.sendHeader(); | |
345 res.send(JSON.stringify({code:reply.status})) | |
346 res.finish(); | |
347 }); | |
348 } | |
349 | |
350 function sr_too_large(req, res) { | |
351 req.subrequest('/too_large/t', body_fwd_cb); | |
352 } | |
353 | |
354 function sr_in_sr(req, res) { | |
355 req.subrequest('/sr', body_fwd_cb); | |
356 } | |
357 | |
358 function sr_js_in_subrequest(req, res) { | |
359 req.subrequest('/js_sub', body_fwd_cb); | |
360 } | |
361 | |
362 function sr_out_of_order(req) { | |
363 subrequest_fn(req, ['/p/delayed', '/p/sub1', '/unknown'], | |
364 ['uri', 'status']) | |
365 } | |
366 | |
367 function subrequest_fn(req, subs, props) { | |
368 var r, replies = []; | |
369 | |
370 subs.forEach(function(sr) { | |
371 req.subrequest(sr, function(reply) { | |
372 req.log("subrequest handler: " + reply.uri | |
373 + " status: " + reply.status) | |
374 | |
375 r = {}; | |
376 props.forEach(function (p) {r[p] = reply[p]}); | |
377 | |
378 replies.push(r); | |
379 | |
380 if (replies.length == subs.length) { | |
381 var res = req.response; | |
382 res.status = 200; | |
383 res.sendHeader(); | |
384 res.send(JSON.stringify(replies)); | |
385 res.finish(); | |
386 } | |
387 }); | |
388 }) | |
389 } | |
390 | |
391 function sr_except_not_a_func(req, res) { | |
392 req.subrequest('/sub1', 'a=1', 'b') | |
393 } | |
394 | |
395 function sr_except_failed_to_convert_arg(req, res) { | |
396 req.subrequest('/sub1', req.args, function(){}) | |
397 } | |
398 | |
399 function sr_except_failed_to_convert_options_arg(req, res) { | |
400 req.subrequest('/sub1', {args:req.args}, function(){}) | |
401 } | |
402 | |
403 function sr_except_invalid_options_method(req, res) { | |
404 req.subrequest('/sub1', {method:'UNKNOWN_METHOD'}, function(){}) | |
405 } | |
406 | |
407 function sr_uri_except(req, res) { | |
408 req.subrequest(req, 'a=1', 'b') | |
409 } | |
410 | |
411 function body_fwd_cb(reply) { | |
412 var res = reply.parent.response; | |
413 res.status = 200; | |
414 res.sendHeader(); | |
415 res.send(JSON.stringify(JSON.parse(reply.body))); | |
416 res.finish(); | |
417 } | |
418 | |
419 function js_sub(req, res) { | |
420 res.status = 200; | |
421 res.sendHeader(); | |
422 res.send('["JS-SUB"]'); | |
423 res.finish(); | |
424 } | |
425 | |
426 EOF | |
427 | |
428 $t->write_file('t', '["SEE-THIS"]'); | |
429 | |
430 $t->run_daemon(\&http_daemon); | |
431 $t->try_run('no njs available')->plan(23); | |
432 | |
433 ############################################################################### | |
434 | |
435 is(get_json('/sr'), '[{"status":404,"uri":"/p/sub2"}]', 'sr'); | |
436 is(get_json('/sr_args'), '{"h":"xxx"}', 'sr_args'); | |
437 is(get_json('/sr_options_args'), '{"h":"xxx"}', 'sr_options_args'); | |
438 is(get_json('/sr_options_method'), '["POST"]', 'sr_options_method'); | |
439 is(get_json('/sr_options_body'), '["REQ-BODY"]', 'sr_options_body'); | |
440 is(get_json('/sr_options_method_head'), '{"c":200,"s":0}', | |
441 'sr_options_method_head'); | |
442 is(get_json('/sr_body'), '{"a":{"b":1}}', 'sr_body'); | |
443 is(get_json('/sr_body_special'), '{"e":"msg"}', 'sr_body_special'); | |
444 is(get_json('/sr_in_variable_handler'), '["CB-VAR"]', 'sr_in_variable_handler'); | |
445 is(get_json('/sr_file'), '["SEE-THIS"]', 'sr_file'); | |
446 is(get_json('/sr_cache?c=1'), '["SEE-THIS"]', 'sr_cache'); | |
447 is(get_json('/sr_cache?c=1'), '["SEE-THIS"]', 'sr_cached'); | |
448 is(get_json('/sr_js_in_subrequest'), '["JS-SUB"]', 'sr_js_in_subrequest'); | |
449 is(get_json('/sr_unavail'), '[{"status":502,"uri":"/unavail"}]', | |
450 'sr_unavail'); | |
451 is(get_json('/sr_out_of_order'), | |
452 '[{"status":404,"uri":"/unknown"},' . | |
453 '{"status":206,"uri":"/p/sub1"},' . | |
454 '{"status":200,"uri":"/p/delayed"}]', | |
455 'sr_multi'); | |
456 | |
457 http_get('/sr_background'); | |
458 | |
459 http_get('/sr_broken'); | |
460 http_get('/sr_in_sr'); | |
461 http_get('/sr_in_variable_handler'); | |
462 http_get('/sr_error_page'); | |
463 http_get('/sr_too_large'); | |
464 http_get('/sr_except_not_a_func'); | |
465 http_get('/sr_except_failed_to_convert_arg'); | |
466 http_get('/sr_except_failed_to_convert_options_arg'); | |
467 http_get('/sr_except_invalid_options_method'); | |
468 http_get('/sr_uri_except'); | |
469 | |
470 $t->stop(); | |
471 | |
472 ok(index($t->read_file('error.log'), 'callback is not a function') > 0, | |
473 'subrequest cb exception'); | |
474 ok(index($t->read_file('error.log'), 'failed to convert uri arg') > 0, | |
475 'subrequest uri exception'); | |
476 ok(index($t->read_file('error.log'), 'failed to convert args') > 0, | |
477 'subrequest invalid args exception'); | |
478 ok(index($t->read_file('error.log'), 'unknown method "UNKNOWN_METHOD"') > 0, | |
479 'subrequest unknown method exception'); | |
480 ok(index($t->read_file('error.log'), 'BACKGROUND') > 0, | |
481 'background subrequest'); | |
482 ok(index($t->read_file('error.log'), 'too big subrequest response') > 0, | |
483 'subrequest too large body'); | |
484 ok(index($t->read_file('error.log'), 'subrequest creation failed') > 0, | |
485 'subrequest creation failed'); | |
486 ok(index($t->read_file('error.log'), | |
487 'js subrequest: failed to get the parent context') > 0, | |
488 'zero parent ctx'); | |
489 | |
490 ############################################################################### | |
491 | |
492 sub recode { | |
493 my $json; | |
494 eval { $json = JSON::PP::decode_json(shift) }; | |
495 | |
496 if ($@) { | |
497 return "<failed to parse JSON>"; | |
498 } | |
499 | |
500 JSON::PP->new()->canonical()->encode($json); | |
501 } | |
502 | |
503 sub get_json { | |
504 http_get(shift) =~ /\x0d\x0a?\x0d\x0a?(.*)/ms; | |
505 recode($1); | |
506 } | |
507 | |
508 ############################################################################### | |
509 | |
510 sub http_daemon { | |
511 my $server = IO::Socket::INET->new( | |
512 Proto => 'tcp', | |
513 LocalAddr => '127.0.0.1:' . port(8082), | |
514 Listen => 5, | |
515 Reuse => 1 | |
516 ) | |
517 or die "Can't create listening socket: $!\n"; | |
518 | |
519 local $SIG{PIPE} = 'IGNORE'; | |
520 | |
521 while (my $client = $server->accept()) { | |
522 $client->autoflush(1); | |
523 | |
524 my $headers = ''; | |
525 my $uri = ''; | |
526 | |
527 while (<$client>) { | |
528 $headers .= $_; | |
529 last if (/^\x0d?\x0a?$/); | |
530 } | |
531 | |
532 $uri = $1 if $headers =~ /^\S+\s+([^ ]+)\s+HTTP/i; | |
533 | |
534 if ($uri eq '/unfinished') { | |
535 print $client | |
536 "HTTP/1.1 200 OK" . CRLF . | |
537 "Transfer-Encoding: chunked" . CRLF . | |
538 "Content-Length: 100" . CRLF . | |
539 CRLF . | |
540 "unfinished" . CRLF; | |
541 close($client); | |
542 } | |
543 } | |
544 } |