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 }