view proxy_cache_use_stale.t @ 1940:aec72dcee93b

Tests: unconditional QUIC datagram expansion with Initial packets. Used to get rid of "quic UDP datagram is too small for initial packet" messages. In future, we may need this to reconsider to allow mocking with custom padding, but for now suppressing such messages is sufficiently good enough.
author Sergey Kandaurov <pluknet@nginx.com>
date Mon, 11 Dec 2023 14:01:49 +0400
parents e3dd111410fe
children
line wrap: on
line source

#!/usr/bin/perl

# (C) Sergey Kandaurov
# (C) Nginx, Inc.

# Tests for http proxy cache, proxy_cache_use_stale.

###############################################################################

use warnings;
use strict;

use Test::More;

use IO::Select;

BEGIN { use FindBin; chdir($FindBin::Bin); }

use lib 'lib';
use Test::Nginx qw/ :DEFAULT http_end /;

###############################################################################

select STDERR; $| = 1;
select STDOUT; $| = 1;

my $t = Test::Nginx->new()->has(qw/http proxy cache rewrite limit_req ssi/)
	->write_file_expand('nginx.conf', <<'EOF');

%%TEST_GLOBALS%%

daemon off;

events {
}

http {
    %%TEST_GLOBALS_HTTP%%

    proxy_cache_path   %%TESTDIR%%/cache  levels=1:2  keys_zone=NAME:1m;

    limit_req_zone  $binary_remote_addr  zone=one:1m  rate=10r/m;

    server {
        listen       127.0.0.1:8080;
        server_name  localhost;

        location /ssi.html {
            ssi on;
            sendfile_max_chunk  4k;
        }

        location /escape {
            proxy_pass    http://127.0.0.1:8081;
            proxy_cache   NAME;
            proxy_cache_background_update  on;
            add_header X-Cache-Status $upstream_cache_status;
        }

        location / {
            proxy_pass    http://127.0.0.1:8081;

            proxy_cache   NAME;

            proxy_cache_key  $uri;

            proxy_cache_revalidate  on;

            proxy_cache_background_update  on;

            add_header X-Cache-Status $upstream_cache_status;

            location /t4.html {
                proxy_pass    http://127.0.0.1:8081/t.html;

                proxy_cache_revalidate  off;
            }

            location /t5.html {
                proxy_pass    http://127.0.0.1:8081/t.html;

                proxy_cache_background_update  off;
            }

            location ~ /(reg)(?P<name>exp).html {
                proxy_pass    http://127.0.0.1:8081/$1$name.html;

                proxy_cache_background_update  on;
            }

            location /updating/ {
                proxy_pass    http://127.0.0.1:8081/;

                proxy_cache_use_stale  updating;
            }

            location /t7.html {
                proxy_pass    http://127.0.0.1:8081;

                sendfile_max_chunk  4k;
            }

            location /t8.html {
                proxy_pass    http://127.0.0.1:8081/t.html;

                proxy_cache_valid  1s;
            }

            if ($arg_if) {
                # nothing
            }
        }
    }
    server {
        listen       127.0.0.1:8081;
        server_name  localhost;

        add_header Cache-Control $http_x_cache_control;

        if ($arg_lim) {
            set $limit_rate 1k;
        }

        if ($arg_e) {
            return 500;
        }

        location / { }

        location /t6.html {
            limit_req zone=one burst=2;
        }

        location /t9.html {
            add_header Cache-Control "max-age=1, stale-while-revalidate=10";
        }
    }
}

EOF

$t->write_file('t.html', 'SEE-THIS');
$t->write_file('tt.html', 'SEE-THIS');
$t->write_file('t2.html', 'SEE-THIS');
$t->write_file('t3.html', 'SEE-THIS');
$t->write_file('t6.html', 'SEE-THIS');
$t->write_file('t7.html', 'SEE-THIS' x 1024);
$t->write_file('t9.html', 'SEE-THIS' x 1024);
$t->write_file('ssi.html', 'xxx <!--#include virtual="/t9.html" --> xxx');
$t->write_file('escape.html', 'SEE-THIS');
$t->write_file('regexp.html', 'SEE-THIS');

$t->run()->plan(34);

###############################################################################

like(get('/t.html', 'max-age=1, stale-if-error=5'), qr/MISS/, 'stale-if-error');
like(http_get('/t.html?e=1'), qr/HIT/, 's-i-e - cached');

like(get('/t2.html', 'max-age=1, stale-while-revalidate=10'), qr/MISS/,
	'stale-while-revalidate');
like(http_get('/t2.html'), qr/HIT/, 's-w-r - cached');

get('/tt.html', 'max-age=1, stale-if-error=3');
get('/t3.html', 'max-age=1, stale-while-revalidate=2');
get('/t4.html', 'max-age=1, stale-while-revalidate=3');
get('/t5.html', 'max-age=1, stale-while-revalidate=3');
get('/t6.html', 'max-age=1, stale-while-revalidate=4');
get('/t7.html', 'max-age=1, stale-while-revalidate=10');
http_get('/ssi.html');
get('/updating/t.html', 'max-age=1');
get('/updating/t2.html', 'max-age=1, stale-while-revalidate=2');
get('/updating/tt.html', 'max-age=1, stale-if-error=5');
get('/t8.html', 'stale-while-revalidate=10');
get('/escape.htm%6C', 'max-age=1, stale-while-revalidate=10');
get('/regexp.html', 'max-age=1, stale-while-revalidate=10');

sleep 2;

# stale 5xx response is ignored since 1.19.3,
# "proxy_cache_use_stale updating;" allows to get it still

like(http_get('/t.html?e=1'), qr/ 500 /, 's-i-e - stale 5xx ignore');
like(http_get('/tt.html?e=1'), qr/ 500 /, 's-i-e - stale 5xx ignore 2');
like(http_get('/updating/tt.html'), qr/STALE/, 's-i-e - stale 5xx updating');
like(http_get('/t.html'), qr/REVALIDATED/, 's-i-e - revalidated');

like(http_get('/t2.html?e=1'), qr/STALE/, 's-w-r - revalidate error');
like(http_get('/t2.html'), qr/STALE/, 's-w-r - stale while revalidate');
like(http_get('/t2.html'), qr/HIT/, 's-w-r - revalidated');

like(get('/t4.html', 'max-age=1, stale-while-revalidate=2'), qr/STALE/,
	's-w-r - unconditional revalidate');
like(http_get('/t4.html'), qr/HIT/, 's-w-r - unconditional revalidated');

like(http_get('/t5.html?e=1'), qr/ 500 /,
	's-w-r - foreground revalidate error');
like(http_get('/t5.html'), qr/REVALIDATED/, 's-w-r - foreground revalidated');

# proxy_pass to regular expression with named and positional captures

like(http_get('/regexp.html'), qr/STALE/, 's-w-r - regexp background update');
like(http_get('/regexp.html'), qr/HIT/, 's-w-r - regexp revalidated');

# UPDATING while s-w-r

$t->write_file('t6.html', 'SEE-THAT');

my $s = get('/t6.html', 'max-age=1, stale-while-revalidate=2', start => 1);
select undef, undef, undef, 0.2;
like(http_get('/t6.html'), qr/UPDATING.*SEE-THIS/s, 's-w-r - updating');
like(http_end($s), qr/STALE.*SEE-THIS/s, 's-w-r - updating stale');
like(http_get('/t6.html'), qr/HIT.*SEE-THAT/s, 's-w-r - updating revalidated');

# stale-while-revalidate with proxy_cache_use_stale updating

like(http_get('/updating/t.html'), qr/STALE/,
	's-w-r - use_stale updating stale');
like(http_get('/updating/t.html'), qr/HIT/,
	's-w-r - use_stale updating revalidated');

# stale-while-revalidate with proxy_cache_valid

like(http_get('/t8.html'), qr/STALE/, 's-w-r - proxy_cache_valid revalidate');
like(http_get('/t8.html'), qr/HIT/, 's-w-r - proxy_cache_valid revalidated');

sleep 2;

like(http_get('/t2.html?e=1'), qr/STALE/, 's-w-r - stale after revalidate');
like(http_get('/t3.html?e=1'), qr/ 500 /, 's-w-r - ceased');
like(http_get('/tt.html?e=1'), qr/ 500 /, 's-i-e - ceased');
like(http_get('/updating/t2.html'), qr/STALE/,
	's-w-r - overriden with use_stale updating');

# stale response not blocked by background update.
# before 1.13.1, if stale response was not sent in one pass, its remaining
# part was blocked and not sent until background update has been finished

$t->write_file('t7.html', 'SEE-THAT' x 256);

my $r = read_all(get('/t7.html?lim=1', 'max-age=1', start => 1));
like($r, qr/STALE.*^(SEE-THIS){1024}$/ms, 's-w-r - stale response not blocked');

$t->write_file('t9.html', 'SEE-THAT' x 256);
$t->write_file('ssi.html', 'xxx <!--#include virtual="/t9.html?lim=1" --> xxx');

$r = read_all(http_get('/ssi.html', start => 1));
like($r, qr/^xxx (SEE-THIS){1024} xxx$/ms, 's-w-r - not blocked in subrequest');

# "aio_write" is used to produce "open socket ... left in connection" alerts.

$t->todo_alerts() if $t->read_file('nginx.conf') =~ /aio_write on/
        and $t->read_file('nginx.conf') =~ /aio threads/ and $^O eq 'freebsd';

# due to the missing content_handler inheritance in a cloned subrequest,
# this used to access a static file in the update request

like(http_get('/t2.html?if=1'), qr/STALE/, 'background update in if');
like(http_get('/t2.html?if=1'), qr/HIT/, 'background update in if - updated');

# ticket #1430, uri escaping in cloned subrequests

$t->write_file('escape.html', 'SEE-THAT');

get('/escape.htm%6C', 'max-age=1');

like(http_get('/escape.htm%6C'), qr/HIT/, 'escaped after escaped');
like(http_get('/escape.html'), qr/MISS/, 'unescaped after escaped');

###############################################################################

sub get {
	my ($url, $extra, %extra) = @_;
	return http(<<EOF, %extra);
GET $url HTTP/1.1
Host: localhost
Connection: close
X-Cache-Control: $extra

EOF
}

# background update is known to postpone closing connection with client

sub read_all {
	my ($s) = @_;
	my $r = '';
	while (IO::Select->new($s)->can_read(1)) {
		$s->sysread(my $buf, 8192) or last;
		log_in($buf);
		$r .= $buf;
	}
	return $r;
}

###############################################################################