Mercurial > hg > nginx-tests
view js_subrequests.t @ 1618:cea0591b13dd
Tests: fixed TLSv1.3 session reuse in stream_ssl_certificate.t.
See 8b122b35703b for details. This is a missing part for stream tests.
author | Sergey Kandaurov <pluknet@nginx.com> |
---|---|
date | Mon, 23 Nov 2020 22:46:04 +0000 |
parents | cd0461e1e392 |
children | 5ac6efbe5552 |
line wrap: on
line source
#!/usr/bin/perl # # (C) Dmitry Volyntsev. # (C) Nginx, Inc. # Tests for subrequests in http njs module. ############################################################################### use warnings; use strict; use Test::More; use Socket qw/ CRLF /; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; eval { require JSON::PP; }; plan(skip_all => "JSON::PP not installed") if $@; my $t = Test::Nginx->new()->has(qw/http rewrite proxy cache/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } http { %%TEST_GLOBALS_HTTP%% proxy_cache_path %%TESTDIR%%/cache1 keys_zone=ON:1m use_temp_path=on; js_include test.js; js_set $async_var async_var; js_set $subrequest_var subrequest_var; server { listen 127.0.0.1:8080; server_name localhost; location /njs { js_content test_njs; } location /sr { js_content sr; } location /sr_pr { js_content sr_pr; } location /sr_args { js_content sr_args; } location /sr_options_args { js_content sr_options_args; } location /sr_options_args_pr { js_content sr_options_args_pr; } location /sr_options_method { js_content sr_options_method; } location /sr_options_method_pr { js_content sr_options_method_pr; } location /sr_options_body { js_content sr_options_body; } location /sr_options_method_head { js_content sr_options_method_head; } location /sr_body { js_content sr_body; } location /sr_body_pr { js_content sr_body_pr; } location /sr_body_special { js_content sr_body_special; } location /sr_in_variable_handler { set $_ $async_var; js_content sr_in_variable_handler; } location /sr_detached_in_variable_handler { return 200 $subrequest_var; } location /sr_async_var { set $_ $async_var; error_page 404 /return; return 404; } location /sr_error_page { js_content sr_error_page; } location /sr_js_in_subrequest { js_content sr_js_in_subrequest; } location /sr_js_in_subrequest_pr { js_content sr_js_in_subrequest_pr; } location /sr_file { js_content sr_file; } location /sr_cache { js_content sr_cache; } location /sr_unavail { js_content sr_unavail; } location /sr_unavail_pr { js_content sr_unavail_pr; } location /sr_broken { js_content sr_broken; } location /sr_too_large { js_content sr_too_large; } location /sr_out_of_order { js_content sr_out_of_order; } location /sr_except_not_a_func { js_content sr_except_not_a_func; } location /sr_except_failed_to_convert_options_arg { js_content sr_except_failed_to_convert_options_arg; } location /sr_except_invalid_options_header_only { js_content sr_except_invalid_options_header_only; } location /sr_in_sr_callback { js_content sr_in_sr_callback; } location /sr_uri_except { js_content sr_uri_except; } location /file/ { alias %%TESTDIR%%/; } location /p/ { proxy_cache $arg_c; proxy_pass http://127.0.0.1:8081/; } location /daemon/ { proxy_pass http://127.0.0.1:8082/; } location /too_large/ { subrequest_output_buffer_size 3; proxy_pass http://127.0.0.1:8081/; } location /sr_in_sr { js_content sr_in_sr; } location /unavail { proxy_pass http://127.0.0.1:8084/; } location /sr_parent { js_content sr_parent; } location /js_sub { js_content js_sub; } location /return { return 200 '["$request_method"]'; } location /error_page_404 { return 404; error_page 404 /404.html; } } server { listen 127.0.0.1:8081; server_name localhost; location /sub1 { add_header H $arg_h; return 206 '{"a": {"b": 1}}'; } location /sub2 { return 404 '{"e": "msg"}'; } location /method { return 200 '["$request_method"]'; } location /body { js_content body; } location /detached { js_content detached; } location /delayed { js_content delayed; } } server { listen 127.0.0.1:8084; server_name localhost; return 444; } } EOF $t->write_file('test.js', <<EOF); this.Failed = {get toConvert() { return {toString(){return {};}}}}; function test_njs(r) { r.return(200, njs.version); } function sr(r) { subrequest_fn(r, ['/p/sub2'], ['uri', 'status']) } function sr_pr(r) { r.subrequest('/p/sub1', 'h=xxx') .then(reply => r.return(200, JSON.stringify({h:reply.headersOut.h}))) } function sr_args(r) { r.subrequest('/p/sub1', 'h=xxx', reply => { r.return(200, JSON.stringify({h:reply.headersOut.h})); }); } function sr_options_args(r) { r.subrequest('/p/sub1', {args:'h=xxx'}, reply => { r.return(200, JSON.stringify({h:reply.headersOut.h})); }); } function sr_options_args_pr(r) { r.subrequest('/p/sub1', {args:'h=xxx'}) .then(reply => r.return(200, JSON.stringify({h:reply.headersOut.h}))) } function sr_options_method(r) { r.subrequest('/p/method', {method:r.args.m}, body_fwd_cb); } function sr_options_method_pr(r) { r.subrequest('/p/method', {method:r.args.m}) .then(body_fwd_cb); } function sr_options_body(r) { r.subrequest('/p/body', {method:'POST', body:'["REQ-BODY"]'}, body_fwd_cb); } function sr_options_method_head(r) { r.subrequest('/p/method', {method:'HEAD'}, reply => { r.return(200, JSON.stringify({c:reply.status})); }); } function sr_body(r) { r.subrequest('/p/sub1', body_fwd_cb); } function sr_body_pr(r) { r.subrequest('/p/sub1') .then(body_fwd_cb); } function sr_body_special(r) { r.subrequest('/p/sub2', body_fwd_cb); } function body(r) { r.return(200, r.variables.request_body); } function delayed(r) { setTimeout(r => r.return(200), 100, r); } function detached(r) { var method = r.variables.request_method; r.log(`DETACHED: \${method} args: \${r.variables.args}`); r.return(200); } function sr_in_variable_handler(r) { } function async_var(r) { r.subrequest('/p/delayed', reply => { r.return(200, JSON.stringify(["CB-VAR"])); }); return ""; } function sr_error_page(r) { r.subrequest('/error_page_404') .then(reply => {r.return(200, `reply.status:\${reply.status}`)}); } function subrequest_var(r) { r.subrequest('/p/detached', {detached:true}); r.subrequest('/p/detached', {detached:true, args:'a=yyy', method:'POST'}); return "subrequest_var"; } function sr_file(r) { r.subrequest('/file/t', body_fwd_cb); } function sr_cache(r) { r.subrequest('/p/t', body_fwd_cb); } function sr_unavail(req) { subrequest_fn(req, ['/unavail'], ['uri', 'status']); } function sr_unavail_pr(req) { subrequest_fn_pr(req, ['/unavail'], ['uri', 'status']); } function sr_broken(r) { r.subrequest('/daemon/unfinished', reply => { r.return(200, JSON.stringify({code:reply.status})); }); } function sr_too_large(r) { r.subrequest('/too_large/t', body_fwd_cb); } function sr_in_sr(r) { r.subrequest('/sr', body_fwd_cb); } function sr_js_in_subrequest(r) { r.subrequest('/js_sub', body_fwd_cb); } function sr_js_in_subrequest_pr(r) { r.subrequest('/js_sub') .then(body_fwd_cb); } function sr_in_sr_callback(r) { r.subrequest('/return', function (reply) { try { reply.subrequest('/return'); } catch (err) { r.return(200, JSON.stringify({e:err.message})); return; } r.return(200); }); } function sr_parent(r) { try { var parent = r.parent; } catch (err) { r.return(200, JSON.stringify({e:err.message})); return; } r.return(200); } function sr_out_of_order(r) { subrequest_fn(r, ['/p/delayed', '/p/sub1', '/unknown'], ['uri', 'status']); } function collect(replies, props, total, reply) { reply.log(`subrequest handler: \${reply.uri} status: \${reply.status}`) var rep = {}; props.forEach(p => {rep[p] = reply[p]}); replies.push(rep); if (replies.length == total) { reply.parent.return(200, JSON.stringify(replies)); } } function subrequest_fn(r, subs, props) { var replies = []; subs.forEach(sr => r.subrequest(sr, collect.bind(null, replies, props, subs.length))); } function subrequest_fn_pr(r, subs, props) { var replies = []; subs.forEach(sr => r.subrequest(sr) .then(collect.bind(null, replies, props, subs.length))); } function sr_except_not_a_func(r) { r.subrequest('/sub1', 'a=1', 'b'); } function sr_except_failed_to_convert_options_arg(r) { r.subrequest('/sub1', {args:Failed.toConvert}, ()=>{}); } function sr_uri_except(r) { r.subrequest(Failed.toConvert, 'a=1', 'b'); } function body_fwd_cb(r) { r.parent.return(200, JSON.stringify(JSON.parse(r.responseBody))); } function js_sub(r) { r.return(200, '["JS-SUB"]'); } EOF $t->write_file('t', '["SEE-THIS"]'); $t->try_run('no njs available')->plan(32); $t->run_daemon(\&http_daemon); ############################################################################### is(get_json('/sr'), '[{"status":404,"uri":"/p/sub2"}]', 'sr'); is(get_json('/sr_args'), '{"h":"xxx"}', 'sr_args'); is(get_json('/sr_options_args'), '{"h":"xxx"}', 'sr_options_args'); is(get_json('/sr_options_method?m=POST'), '["POST"]', 'sr method POST'); is(get_json('/sr_options_method?m=PURGE'), '["PURGE"]', 'sr method PURGE'); is(get_json('/sr_options_body'), '["REQ-BODY"]', 'sr_options_body'); is(get_json('/sr_options_method_head'), '{"c":200}', 'sr_options_method_head'); is(get_json('/sr_body'), '{"a":{"b":1}}', 'sr_body'); is(get_json('/sr_body_special'), '{"e":"msg"}', 'sr_body_special'); is(get_json('/sr_in_variable_handler'), '["CB-VAR"]', 'sr_in_variable_handler'); $t->todo_alerts() if $t->read_file('nginx.conf') =~ /aio (on|threads)/ and !$t->has_version('1.17.9'); TODO: { local $TODO = 'header already sent' if $t->read_file('nginx.conf') =~ /aio on/ and !$t->has_version('1.17.9'); local $TODO = 'open socket left' if $t->read_file('nginx.conf') =~ /aio thread/ and !$t->has_version('1.17.9'); is(get_json('/sr_file'), '["SEE-THIS"]', 'sr_file'); } is(get_json('/sr_cache?c=1'), '["SEE-THIS"]', 'sr_cache'); is(get_json('/sr_cache?c=1'), '["SEE-THIS"]', 'sr_cached'); is(get_json('/sr_js_in_subrequest'), '["JS-SUB"]', 'sr_js_in_subrequest'); is(get_json('/sr_unavail'), '[{"status":502,"uri":"/unavail"}]', 'sr_unavail'); is(get_json('/sr_out_of_order'), '[{"status":404,"uri":"/unknown"},' . '{"status":206,"uri":"/p/sub1"},' . '{"status":200,"uri":"/p/delayed"}]', 'sr_multi'); is(get_json('/sr_pr'), '{"h":"xxx"}', 'sr_promise'); is(get_json('/sr_options_args_pr'), '{"h":"xxx"}', 'sr_options_args_pr'); is(get_json('/sr_options_method_pr?m=PUT'), '["PUT"]', 'sr method PUT'); is(get_json('/sr_body_pr'), '{"a":{"b":1}}', 'sr_body_pr'); is(get_json('/sr_js_in_subrequest_pr'), '["JS-SUB"]', 'sr_js_in_subrequest_pr'); is(get_json('/sr_unavail_pr'), '[{"status":502,"uri":"/unavail"}]', 'sr_unavail_pr'); like(http_get('/sr_detached_in_variable_handler'), qr/subrequest_var/, 'sr_detached_in_variable_handler'); TODO: { todo_skip 'leaves coredump', 1 unless $ENV{TEST_NGINX_UNSAFE} or http_get('/njs') =~ /^([.0-9]+)$/m && $1 ge '0.5.0'; like(http_get('/sr_error_page'), qr/reply\.status:404/, 'sr_error_page'); } http_get('/sr_broken'); http_get('/sr_in_sr'); http_get('/sr_in_variable_handler'); http_get('/sr_async_var'); http_get('/sr_too_large'); http_get('/sr_except_not_a_func'); http_get('/sr_except_failed_to_convert_options_arg'); http_get('/sr_uri_except'); is(get_json('/sr_in_sr_callback'), '{"e":"subrequest can only be created for the primary request"}', 'subrequest for non-primary request'); $t->stop(); ok(index($t->read_file('error.log'), 'callback is not a function') > 0, 'subrequest cb exception'); ok(index($t->read_file('error.log'), 'failed to convert uri arg') > 0, 'subrequest uri exception'); ok(index($t->read_file('error.log'), 'failed to convert options.args') > 0, 'subrequest invalid args exception'); ok(index($t->read_file('error.log'), 'too big subrequest response') > 0, 'subrequest too large body'); ok(index($t->read_file('error.log'), 'subrequest creation failed') > 0, 'subrequest creation failed'); ok(index($t->read_file('error.log'), 'js subrequest: failed to get the parent context') > 0, 'zero parent ctx'); ok(index($t->read_file('error.log'), 'DETACHED') > 0, 'detached subrequest'); ############################################################################### sub recode { my $json; eval { $json = JSON::PP::decode_json(shift) }; if ($@) { return "<failed to parse JSON>"; } JSON::PP->new()->canonical()->encode($json); } sub get_json { http_get(shift) =~ /\x0d\x0a?\x0d\x0a?(.*)/ms; recode($1); } ############################################################################### sub http_daemon { my $server = IO::Socket::INET->new( Proto => 'tcp', LocalAddr => '127.0.0.1:' . port(8082), Listen => 5, Reuse => 1 ) or die "Can't create listening socket: $!\n"; local $SIG{PIPE} = 'IGNORE'; while (my $client = $server->accept()) { $client->autoflush(1); my $headers = ''; my $uri = ''; while (<$client>) { $headers .= $_; last if (/^\x0d?\x0a?$/); } $uri = $1 if $headers =~ /^\S+\s+([^ ]+)\s+HTTP/i; if ($uri eq '/unfinished') { print $client "HTTP/1.1 200 OK" . CRLF . "Transfer-Encoding: chunked" . CRLF . "Content-Length: 100" . CRLF . CRLF . "unfinished" . CRLF; close($client); } } } ###############################################################################