From a0226b2ba1d3081ee0421df914da40efa67368b0 Mon Sep 17 00:00:00 2001 From: Ivan Ovchinnikov Date: Fri, 18 Sep 2026 21:57:46 +0000 Subject: [PATCH] Tests: proxy_next_upstream no_live and proxy_next_upstream_delay. Co-authored-by: Sourav Bhowmik --- proxy_next_upstream.t | 113 ++++++++++++++++++++++++++++- proxy_next_upstream_delay.t | 137 ++++++++++++++++++++++++++++++++++++ 2 files changed, 248 insertions(+), 2 deletions(-) create mode 100644 proxy_next_upstream_delay.t diff --git a/proxy_next_upstream.t b/proxy_next_upstream.t index b789cd72..f4ccb1d1 100644 --- a/proxy_next_upstream.t +++ b/proxy_next_upstream.t @@ -1,6 +1,7 @@ #!/usr/bin/perl # (C) Maxim Dounin +# (C) Nginx, Inc. # Tests for http proxy module, proxy_next_upstream directive. @@ -21,7 +22,7 @@ use Test::Nginx; select STDERR; $| = 1; select STDOUT; $| = 1; -my $t = Test::Nginx->new()->has(qw/http proxy rewrite/)->plan(8); +my $t = Test::Nginx->new()->has(qw/http proxy rewrite/); $t->write_file_expand('nginx.conf', <<'EOF'); @@ -50,6 +51,15 @@ http { server 127.0.0.1:8082 down; } + upstream u4 { + server 127.0.0.1:8081; + } + + upstream u5 { + server 127.0.0.1:8083; + server 127.0.0.1:8084; + } + server { listen 127.0.0.1:8080; server_name localhost; @@ -74,6 +84,83 @@ http { proxy_pass http://u3; proxy_next_upstream http_404; } + + # no_live reinitializes peer selection once every peer has been + # tried, so that retries continue up to proxy_next_upstream_tries + + location = /no_live { + proxy_pass http://u2/all/; + proxy_next_upstream http_404 no_live; + proxy_next_upstream_tries 3; + proxy_intercept_errors on; + error_page 404 = /no_live/status; + } + + # a single server, retried more times than there are servers + + location = /no_live/single { + proxy_pass http://u4/all/; + proxy_next_upstream http_404 no_live; + proxy_next_upstream_tries 3; + proxy_intercept_errors on; + error_page 404 = /no_live/addr; + } + + # an upstream created from an address rather than an upstream block + + location = /no_live/resolved { + set $back 127.0.0.1:%%PORT_8081%%; + proxy_pass http://$back/all/; + proxy_next_upstream http_404 no_live; + proxy_next_upstream_tries 3; + proxy_intercept_errors on; + error_page 404 = /no_live/status; + } + + # without proxy_next_upstream_tries there is nothing to retry up to, + # and peers are tried once + + location = /no_live/unlimited { + proxy_pass http://u2/all/; + proxy_next_upstream http_404 no_live; + proxy_intercept_errors on; + error_page 404 = /no_live/status; + } + + # peers disabled by max_fails are retried as well + + location = /no_live/dead { + proxy_pass http://u5; + proxy_next_upstream error no_live; + proxy_next_upstream_tries 4; + proxy_intercept_errors on; + error_page 502 = /no_live/status; + } + + location = /no_live/status { + return 200 "x${upstream_status}x"; + } + + location = /no_live/addr { + return 200 "x${upstream_addr}x"; + } + + # each upstream request has its own proxy_next_upstream_tries budget + + location = /chain { + proxy_pass http://u2/all/; + proxy_next_upstream http_404; + proxy_next_upstream_tries 2; + proxy_intercept_errors on; + error_page 404 = /chain/second; + } + + location = /chain/second { + proxy_pass http://u2/all/; + proxy_next_upstream http_404; + proxy_next_upstream_tries 2; + add_header X-Upstream-Status "x${upstream_status}x" always; + } } server { @@ -111,7 +198,7 @@ http { EOF -$t->run(); +$t->try_run('no no_live')->plan(14); ############################################################################### @@ -148,4 +235,26 @@ like(http_get('/all/rr'), like(http_get('/down/'), qr/Not Found/, 'all tried with down'); +# make sure peers are tried again once all of them have been tried + +like(http_get('/no_live'), qr/x404, 404, 404x/, 'no_live'); +like(http_get('/no_live/single'), + qr/x127.0.0.1:$p1, 127.0.0.1:$p1, 127.0.0.1:${p1}x/, + 'no_live single server'); +like(http_get('/no_live/resolved'), qr/x404, 404, 404x/, 'no_live resolved'); + +# make sure no_live alone does not change anything + +like(http_get('/no_live/unlimited'), qr/x404, 404x/, 'no_live no tries'); + +# make sure peers disabled by max_fails are retried as well + +like(http_get('/no_live/dead'), qr/x502, 502, 502, 502x/, 'no_live no live'); + +# make sure a second upstream request in the same request is not limited +# by the attempts for the first one + +like(http_get('/chain'), qr/x404, 404 : 404, 404x/, + 'tries per upstream request'); + ############################################################################### diff --git a/proxy_next_upstream_delay.t b/proxy_next_upstream_delay.t new file mode 100644 index 00000000..d3e2af82 --- /dev/null +++ b/proxy_next_upstream_delay.t @@ -0,0 +1,137 @@ +#!/usr/bin/perl + +# (C) Nginx, Inc. + +# Tests for http proxy module, proxy_next_upstream_delay directive. + +############################################################################### + +use warnings; +use strict; + +use Test::More; +use Time::HiRes qw(time); +use IO::Select; + +BEGIN { use FindBin; chdir($FindBin::Bin); } + +use lib 'lib'; +use Test::Nginx; + +############################################################################### + +select STDERR; $| = 1; +select STDOUT; $| = 1; + +my $t = Test::Nginx->new()->has(qw/http proxy rewrite ssi/); + +$t->write_file_expand('nginx.conf', <<'EOF'); + +%%TEST_GLOBALS%% + +daemon off; + +events { +} + +http { + %%TEST_GLOBALS_HTTP%% + + upstream u { + server 127.0.0.1:8081; + server 127.0.0.1:8082; + } + + upstream dead { + server 127.0.0.1:8083; + server 127.0.0.1:8084; + } + + server { + listen 127.0.0.1:8080; + server_name localhost; + + root %%TESTDIR%%; + + location = /delay { + proxy_pass http://u/all/; + proxy_next_upstream http_404 no_live; + proxy_next_upstream_tries 3; + proxy_next_upstream_delay 1s; + proxy_intercept_errors on; + error_page 404 = /status; + } + + location = /status { + return 200 "x${upstream_status}x"; + } + + location = /sub { + proxy_pass http://dead; + proxy_next_upstream error no_live; + proxy_next_upstream_tries 3; + proxy_next_upstream_delay 100ms; + } + + location / { + ssi on; + } + } + + server { + listen 127.0.0.1:8081; + listen 127.0.0.1:8082; + server_name localhost; + + location /all/ { + return 404; + } + } +} + +EOF + +$t->write_file('sub.html', ''); + +$t->try_run('no proxy_next_upstream_delay')->plan(5); + +############################################################################### + +# 3 tries over 2 peers and the delay is fired once + +my $t1 = time(); +like(http_get('/delay'), qr/x404, 404, 404x/, 'delayed retry'); + +my $elapsed = time() - $t1; +cmp_ok($elapsed, '>=', 1, 'delay applied'); +cmp_ok($elapsed, '<', 2, 'delay applied once'); + +# the pending retry timer has to be removed when the request finalized + +my $s = http_get('/delay', start => 1); +select undef, undef, undef, 0.2; +close $s; +select undef, undef, undef, 1.2; + +like(http_get('/delay'), qr/x404, 404, 404x/, 'client abort during delay'); + +# a subrequest finalized from the retry timer has to wake up its parent + +like(get_slow('/sub.html'), qr/502 Bad Gateway/, 'subrequest parent woken'); + +############################################################################### + +sub get_slow { + my ($uri) = @_; + + my $s = http_get($uri, start => 1); + + unless (IO::Select->new($s)->can_read(2)) { + close $s; + return ''; + } + + return http_end($s); +} + +###############################################################################