[PATCH 2 of 2] Tests: Update and add tests for Age header

Hiroaki Nakamura hnakamur at gmail.com
Mon Jul 1 13:47:39 UTC 2024


# HG changeset patch
# User Hiroaki Nakamura <hnakamur at gmail.com>
# Date 1719839239 -32400
#      Mon Jul 01 22:07:19 2024 +0900
# Node ID dcb24ed2502869338b8a1f591cbe586edfce53c1
# Parent  685f47b210263988886db6fe15a95e177616de1f
Tests: Update and add tests for Age header.

diff -r 685f47b21026 -r dcb24ed25028 h2_proxy_cache_age.t
--- /dev/null  Thu Jan 01 00:00:00 1970 +0000
+++ b/h2_proxy_cache_age.t    Mon Jul 01 22:07:19 2024 +0900
@@ -0,0 +1,228 @@
+#!/usr/bin/perl
+
+# (C) Sergey Kandaurov
+# (C) Nginx, Inc.
+# (C) Hiroaki Nakamura
+
+# Tests for age in HTTP/2 proxy cache.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+use Test::Nginx::HTTP2;
+
+use POSIX qw/ ceil /;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()->has(qw/http http_v2 proxy cache/)->plan(10)
+    ->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+daemon off;
+
+events {
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    proxy_cache_path %%TESTDIR%%/cache    keys_zone=NAME:1m;
+    proxy_cache_path %%TESTDIR%%/cache2   keys_zone=NAME2:1m;
+
+    map $arg_slow $rate {
+        default 8k;
+        1       90;
+    }
+
+    server {
+        listen       127.0.0.1:8080 http2;
+        server_name  localhost;
+        error_log    %%TESTDIR%%/error8080.log debug;
+
+        location / {
+            proxy_pass             http://127.0.0.1:8081;
+            proxy_cache            NAME;
+            proxy_http_version     1.1;
+            proxy_cache_revalidate on;
+        }
+    }
+
+    server {
+        listen       127.0.0.1:8081;
+        server_name  localhost;
+        error_log    %%TESTDIR%%/error8081.log debug;
+
+        location / {
+            proxy_pass             http://127.0.0.1:8082;
+            proxy_cache            NAME2;
+            proxy_http_version     1.1;
+            proxy_cache_revalidate on;
+        }
+    }
+
+    server {
+        listen       127.0.0.1:8082;
+        server_name  localhost;
+        error_log    %%TESTDIR%%/error8082.log debug;
+
+        location / {
+            add_header Cache-Control s-maxage=$arg_ttl;
+            limit_rate $rate;
+        }
+    }
+}
+
+EOF
+
+$t->write_file('t.html', 'SEE-THIS');
+$t->write_file('t2.html', 'SEE-THIS');
+$t->write_file('t3.html', 'SEE-THIS');
+
+# suppress deprecation warning
+
+open OLDERR, ">&", \*STDERR; close STDERR;
+$t->run();
+open STDERR, ">&", \*OLDERR;
+
+###############################################################################
+
+my $s = Test::Nginx::HTTP2->new();
+
+my ($path, $sid, $frames, $frame, $t1, $age);
+
+# normal origin
+
+wait_until_next_second();
+
+$path = '/t.html?ttl=2';
+
+$sid = $s->new_stream({ path => $path });
+$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
+is($frame->{headers}->{'age'}, undef, 'age first');
+
+$sid = $s->new_stream({ path => $path });
+$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
+is($frame->{headers}->{'age'}, 0, 'age hit');
+
+select undef, undef, undef, 2.0;
+
+$sid = $s->new_stream({ path => $path });
+$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
+is($frame->{headers}->{'age'}, 2, 'age hit');
+
+select undef, undef, undef, 1.0;
+
+$sid = $s->new_stream({ path => $path });
+$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
+is($frame->{headers}->{'age'}, undef, 'age updated');
+
+SKIP: {
+skip 'no exec on win32', 3 if $^O eq 'MSWin32';
+
+# slow origin
+
+wait_until_next_second();
+
+$path = '/t2.html?ttl=10&slow=1';
+
+$sid = $s->new_stream({ path => $path });
+$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
+is($frame->{headers}->{'age'}, undef, 'slow origin first');
+
+select undef, undef, undef, 2.0;
+
+$sid = $s->new_stream({ path => $path });
+$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
+is($frame->{headers}->{'age'}, 2, 'slow origin hit');
+
+select undef, undef, undef, 9.0;
+
+$sid = $s->new_stream({ path => $path });
+$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
+is($frame->{headers}->{'age'}, undef, 'slow origin updated');
+$t1 = time();
+
+$t->stop();
+
+select undef, undef, undef, 1.0;
+
+open OLDERR, ">&", \*STDERR; close STDERR;
+$t->run();
+open STDERR, ">&", \*OLDERR;
+
+$age = time() - $t1;
+
+$s = Test::Nginx::HTTP2->new();
+
+$sid = $s->new_stream({ path => $path });
+$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
+is($frame->{headers}->{'age'}, $age, 'age after restart');
+
+}
+
+# update age after restart
+
+wait_until_next_second();
+
+$path = '/t3.html?ttl=20';
+
+$sid = $s->new_stream({ path => $path });
+$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
+is($frame->{headers}->{'age'}, undef, 'age before restart');
+$t1 = time();
+
+$t->stop();
+
+select undef, undef, undef, 1.0;
+
+open OLDERR, ">&", \*STDERR; close STDERR;
+$t->run();
+open STDERR, ">&", \*OLDERR;
+
+$age = time() - $t1;
+
+$s = Test::Nginx::HTTP2->new();
+
+$sid = $s->new_stream({ path => $path });
+$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
+is($frame->{headers}->{'age'}, $age, 'age after restart');
+
+$t->stop();
+
+###############################################################################
+
+# Wait until the next second boundary.
+# Calling this before sending a request increases the likelihood that the
+# timestamp value does not cross into the next second while sending a request
+# and receiving a response.
+sub wait_until_next_second {
+    my $now = time();
+    my $next_second = ceil($now);
+    my $sleep = $next_second - $now;
+    select undef, undef, undef, $sleep;
+}
+
+###############################################################################
diff -r 685f47b21026 -r dcb24ed25028 h2_ssl_proxy_cache_age.t
--- /dev/null  Thu Jan 01 00:00:00 1970 +0000
+++ b/h2_ssl_proxy_cache_age.t     Mon Jul 01 22:07:19 2024 +0900
@@ -0,0 +1,274 @@
+#!/usr/bin/perl
+
+# (C) Sergey Kandaurov
+# (C) Nginx, Inc.
+# (C) Hiroaki Nakamura
+
+# Tests for age in HTTP/2 ssl proxy cache.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+use Test::Nginx::HTTP2;
+
+use POSIX qw/ ceil /;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()
+    ->has(qw/http http_ssl http_v2 proxy cache socket_ssl/)->plan(13)
+    ->has_daemon('openssl');
+
+$t->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+daemon off;
+
+events {
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    proxy_cache_path %%TESTDIR%%/cache   keys_zone=NAME:1m;
+    proxy_cache_path %%TESTDIR%%/cache2  keys_zone=NAME2:1m;
+
+    map $arg_slow $rate {
+        default 8k;
+        1       90;
+    }
+
+    server {
+        listen       127.0.0.1:8080 http2 ssl;
+        server_name  localhost;
+        error_log    %%TESTDIR%%/error8080.log debug;
+
+        ssl_certificate_key localhost.key;
+        ssl_certificate localhost.crt;
+
+        location / {
+            proxy_pass   http://127.0.0.1:8081;
+            proxy_cache  NAME;
+            proxy_http_version 1.1;
+            proxy_cache_revalidate on;
+        }
+    }
+
+    server {
+        listen       127.0.0.1:8081;
+        server_name  localhost;
+        error_log    %%TESTDIR%%/error8081.log debug;
+
+        location / {
+            proxy_pass             http://127.0.0.1:8082;
+            proxy_cache            NAME2;
+            proxy_http_version     1.1;
+            proxy_cache_revalidate on;
+        }
+    }
+
+    server {
+        listen       127.0.0.1:8082;
+        server_name  localhost;
+        error_log    %%TESTDIR%%/error8082.log debug;
+
+        location / {
+            add_header Cache-Control s-maxage=$arg_ttl;
+            limit_rate $rate;
+        }
+    }
+}
+
+EOF
+
+$t->write_file('openssl.conf', <<EOF);
+[ req ]
+default_bits = 2048
+encrypt_key = no
+distinguished_name = req_distinguished_name
+[ req_distinguished_name ]
+EOF
+
+my $d = $t->testdir();
+
+foreach my $name ('localhost') {
+    system('openssl req -x509 -new '
+         . "-config $d/openssl.conf -subj /CN=$name/ "
+         . "-out $d/$name.crt -keyout $d/$name.key "
+         . ">>$d/openssl.out 2>&1") == 0
+         or die "Can't create certificate for $name: $!\n";
+}
+
+$t->write_file('t.html', 'SEE-THIS');
+$t->write_file('t2.html', 'SEE-THIS');
+$t->write_file('t3.html', 'SEE-THIS');
+
+open OLDERR, ">&", \*STDERR; close STDERR;
+$t->run();
+open STDERR, ">&", \*OLDERR;
+
+###############################################################################
+
+my $s = getconn(port(8080));
+ok($s, 'ssl connection');
+
+my ($path, $sid, $frames, $frame, $t1, $age);
+
+# normal origin
+
+wait_until_next_second();
+
+$path = '/t.html?ttl=2';
+
+$sid = $s->new_stream({ path => $path });
+$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
+is($frame->{headers}->{'age'}, undef, 'age first');
+
+$sid = $s->new_stream({ path => $path });
+$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
+is($frame->{headers}->{'age'}, 0, 'age hit');
+
+select undef, undef, undef, 2.0;
+
+$sid = $s->new_stream({ path => $path });
+$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
+is($frame->{headers}->{'age'}, 2, 'age hit');
+
+select undef, undef, undef, 1.0;
+
+$sid = $s->new_stream({ path => $path });
+$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
+is($frame->{headers}->{'age'}, undef, 'age updated');
+
+# slow origin
+
+SKIP: {
+skip 'no exec on win32', 3 if $^O eq 'MSWin32';
+
+wait_until_next_second();
+
+$path = '/t2.html?ttl=10&slow=1';
+
+$sid = $s->new_stream({ path => $path });
+$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
+is($frame->{headers}->{'age'}, undef, 'slow origin first');
+
+select undef, undef, undef, 2.0;
+
+$sid = $s->new_stream({ path => $path });
+$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
+is($frame->{headers}->{'age'}, 2, 'slow origin hit');
+
+select undef, undef, undef, 9.0;
+
+$sid = $s->new_stream({ path => $path });
+$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
+is($frame->{headers}->{'age'}, undef, 'slow origin updated');
+$t1 = time();
+
+$t->stop();
+
+select undef, undef, undef, 1.0;
+
+open OLDERR, ">&", \*STDERR; close STDERR;
+$t->run();
+open STDERR, ">&", \*OLDERR;
+
+$age = time() - $t1;
+
+$s = getconn(port(8080));
+ok($s, 'ssl connection');
+
+$sid = $s->new_stream({ path => $path });
+$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
+is($frame->{headers}->{'age'}, $age, 'age after restart');
+
+}
+
+# update age after restart
+
+$path = '/t3.html?ttl=20';
+
+$sid = $s->new_stream({ path => $path });
+$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
+is($frame->{headers}->{'age'}, undef, 'age before restart');
+$t1 = time();
+
+$t->stop();
+
+select undef, undef, undef, 1.0;
+
+open OLDERR, ">&", \*STDERR; close STDERR;
+$t->run();
+open STDERR, ">&", \*OLDERR;
+
+$age = time() - $t1;
+
+$s = getconn(port(8080));
+ok($s, 'ssl connection');
+
+$sid = $s->new_stream({ path => $path });
+$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
+is($frame->{headers}->{'age'}, $age, 'age after restart');
+
+$t->stop();
+
+###############################################################################
+
+sub getconn {
+    my ($port) = @_;
+    my $s;
+
+    eval {
+         my $sock = Test::Nginx::HTTP2::new_socket($port, SSL => 1,
+              alpn => 'h2');
+         $s = Test::Nginx::HTTP2->new($port, socket => $sock)
+              if $sock->alpn_selected();
+    };
+
+    return $s if defined $s;
+
+    eval {
+         my $sock = Test::Nginx::HTTP2::new_socket($port, SSL => 1,
+              npn => 'h2');
+         $s = Test::Nginx::HTTP2->new($port, socket => $sock)
+              if $sock->next_proto_negotiated();
+    };
+
+    return $s;
+}
+
+# Wait until the next second boundary.
+# Calling this before sending a request increases the likelihood that the
+# timestamp value does not cross into the next second while sending a request
+# and receiving a response.
+sub wait_until_next_second {
+    my $now = time();
+    my $next_second = ceil($now);
+    my $sleep = $next_second - $now;
+    select undef, undef, undef, $sleep;
+}
+
+###############################################################################
diff -r 685f47b21026 -r dcb24ed25028 h3_proxy_cache_age.t
--- /dev/null  Thu Jan 01 00:00:00 1970 +0000
+++ b/h3_proxy_cache_age.t    Mon Jul 01 22:07:19 2024 +0900
@@ -0,0 +1,222 @@
+#!/usr/bin/perl
+
+# (C) Sergey Kandaurov
+# (C) Nginx, Inc.
+# (C) Hiroaki Nakamura
+
+# Tests for age in HTTP/3 proxy cache.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+use Test::Nginx::HTTP3;
+
+use POSIX qw/ ceil /;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()->has(qw/http http_v3 proxy cryptx/)
+    ->has_daemon('openssl')->plan(9)
+    ->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+daemon off;
+
+events {
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    ssl_certificate_key localhost.key;
+    ssl_certificate localhost.crt;
+
+    log_format test $uri:$status:$request_completion;
+
+    proxy_cache_path %%TESTDIR%%/cache    keys_zone=NAME:1m;
+
+    map $arg_slow $rate {
+        default 8k;
+        1       90;
+    }
+
+    server {
+        listen       127.0.0.1:%%PORT_8980_UDP%% quic;
+        server_name  localhost;
+        error_log    %%TESTDIR%%/error8080.log debug;
+
+        location / {
+            proxy_pass http://127.0.0.1:8081/;
+            proxy_cache NAME;
+            proxy_http_version 1.1;
+            proxy_cache_revalidate on;
+        }
+    }
+
+    server {
+        listen       127.0.0.1:8081;
+        server_name  localhost;
+        error_log    %%TESTDIR%%/error8081.log debug;
+
+        location / {
+            proxy_pass             http://127.0.0.1:8082;
+            proxy_cache            NAME;
+            proxy_http_version     1.1;
+            proxy_cache_revalidate on;
+        }
+    }
+
+    server {
+        listen       127.0.0.1:8082;
+        server_name  localhost;
+        error_log    %%TESTDIR%%/error8082.log debug;
+
+        location / {
+            add_header Cache-Control s-maxage=$arg_ttl;
+            limit_rate $rate;
+        }
+    }
+}
+
+EOF
+
+$t->write_file('openssl.conf', <<EOF);
+[ req ]
+default_bits = 2048
+encrypt_key = no
+distinguished_name = req_distinguished_name
+[ req_distinguished_name ]
+EOF
+
+my $d = $t->testdir();
+
+foreach my $name ('localhost') {
+    system('openssl req -x509 -new '
+         . "-config $d/openssl.conf -subj /CN=$name/ "
+         . "-out $d/$name.crt -keyout $d/$name.key "
+         . ">>$d/openssl.out 2>&1") == 0
+         or die "Can't create certificate for $name: $!\n";
+}
+
+my $content = 'SEE-THIS';
+$t->write_file('t.html', $content);
+$t->write_file('t2.html', $content);
+$t->write_file('t3.html', $content);
+$t->run();
+
+###############################################################################
+
+my $s = Test::Nginx::HTTP3->new();
+
+my ($path, $sid, $frames, $frame, $t1, $age);
+
+# normal origin
+
+wait_until_next_second();
+
+$path = '/t.html?ttl=2';
+
+$sid = $s->new_stream({ path => $path });
+$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
+is($frame->{headers}->{'age'}, undef, 'age first');
+
+$sid = $s->new_stream({ path => $path });
+$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
+is($frame->{headers}->{'age'}, 0, 'age hit');
+
+select undef, undef, undef, 2.0;
+
+$sid = $s->new_stream({ path => $path });
+$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
+is($frame->{headers}->{'age'}, 2, 'age hit');
+
+select undef, undef, undef, 1.0;
+
+$sid = $s->new_stream({ path => $path });
+$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
+is($frame->{headers}->{'age'}, undef, 'age updated');
+
+# slow origin
+
+wait_until_next_second();
+
+$path = '/t2.html?ttl=4&slow=1';
+
+$sid = $s->new_stream({ path => $path });
+$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
+is($frame->{headers}->{'age'}, undef, 'slow origin first');
+
+select undef, undef, undef, 2.0;
+
+$sid = $s->new_stream({ path => $path });
+$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
+is($frame->{headers}->{'age'}, 2, 'slow origin hit');
+
+select undef, undef, undef, 3.0;
+
+$sid = $s->new_stream({ path => $path });
+$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
+is($frame->{headers}->{'age'}, undef, 'slow origin updated');
+
+# update age after restart
+
+$path = '/t3.html?ttl=20';
+
+$sid = $s->new_stream({ path => $path });
+$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
+is($frame->{headers}->{'age'}, undef, 'age before restart');
+$t1 = time();
+
+$t->stop();
+
+select undef, undef, undef, 1.0;
+
+open OLDERR, ">&", \*STDERR; close STDERR;
+$t->run();
+open STDERR, ">&", \*OLDERR;
+
+$age = time() - $t1;
+
+$s = Test::Nginx::HTTP3->new();
+
+$sid = $s->new_stream({ path => $path });
+$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
+is($frame->{headers}->{'age'}, $age, 'age after restart');
+
+$t->stop();
+
+###############################################################################
+
+# Wait until the next second boundary.
+# Calling this before sending a request increases the likelihood that the
+# timestamp value does not cross into the next second while sending a request
+# and receiving a response.
+sub wait_until_next_second {
+    my $now = time();
+    my $next_second = ceil($now);
+    my $sleep = $next_second - $now;
+    select undef, undef, undef, $sleep;
+}
+
+###############################################################################
diff -r 685f47b21026 -r dcb24ed25028 proxy_cache_age.t
--- /dev/null  Thu Jan 01 00:00:00 1970 +0000
+++ b/proxy_cache_age.t  Mon Jul 01 22:07:19 2024 +0900
@@ -0,0 +1,195 @@
+#!/usr/bin/perl
+
+# (C) Maxim Dounin
+# (C) Hiroaki Nakamura
+
+# Tests for age in http proxy cache.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+use Socket qw/ CRLF /;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+
+use POSIX qw/ ceil /;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()->has(qw/http proxy cache/)->plan(10)
+    ->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;
+    proxy_cache_path   %%TESTDIR%%/cache2  levels=1:2
+                       keys_zone=NAME2:1m;
+
+    map $arg_slow $rate {
+        default 8k;
+        1       100;
+    }
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+        error_log    %%TESTDIR%%/error8080.log debug;
+
+        location / {
+            proxy_pass             http://127.0.0.1:8081;
+            proxy_cache            NAME;
+            proxy_http_version     1.1;
+            proxy_cache_revalidate on;
+            add_header parent_date $upstream_http_date;
+            add_header child_msec $msec;
+            add_header child_cache $upstream_cache_status;
+        }
+    }
+
+    server {
+        listen       127.0.0.1:8081;
+        server_name  localhost;
+        error_log    %%TESTDIR%%/error8081.log debug;
+
+        location / {
+            proxy_pass             http://127.0.0.1:8082;
+            proxy_cache            NAME2;
+            proxy_http_version     1.1;
+            proxy_cache_revalidate on;
+            add_header origin_date $upstream_http_date;
+            add_header parent_msec $msec;
+            add_header parent_cache $upstream_cache_status;
+        }
+    }
+
+    server {
+        listen       127.0.0.1:8082;
+        server_name  localhost;
+        error_log    %%TESTDIR%%/error8082.log debug;
+
+        location / {
+            add_header Cache-Control $http_x_cache_control;
+            limit_rate $rate;
+            add_header origin_msec $msec;
+        }
+    }
+}
+
+EOF
+
+$t->write_file('t.html', 'SEE-THIS');
+$t->write_file('t2.html', 'SEE-THIS');
+$t->write_file('t3.html', 'SEE-THIS');
+
+$t->run();
+
+###############################################################################
+
+# normal origin
+
+wait_until_next_second();
+
+unlike(get('/t.html', 's-maxage=2'), qr/\r\nAge: /, 'age first');
+like(get('/t.html', 's-maxage=2'), qr/\r\nAge: 0\r\n/, 'age hit');
+
+sleep 2;
+
+like(get('/t.html', 's-maxage=2'), qr/\r\nAge: 2\r\n/, 'age hit');
+
+sleep 1;
+
+unlike(http_get('/t.html'), qr/\r\nAge: /, 'age updated');
+
+# slow origin
+
+SKIP: {
+skip 'no exec on win32', 3 if $^O eq 'MSWin32';
+
+wait_until_next_second();
+
+unlike(get('/t2.html?slow=1', 's-maxage=4'), qr/\r\nAge: /,
+    'slow origin first');
+
+sleep 2;
+
+like(http_get('/t2.html?slow=1'), qr/\r\nAge: 2\r\n/, 'slow origin hit');
+
+sleep 3;
+
+unlike(http_get('/t2.html?slow=1'), qr/\r\nAge: /, 'slow origin updated');
+my $t1 = time();
+
+$t->stop();
+
+sleep 1;
+
+$t->run();
+
+my $age = time() - $t1;
+like(http_get('/t2.html?slow=1'), qr/\r\nAge: $age\r\n/,
+    'age after restart');
+
+}
+
+# update age after restart
+
+wait_until_next_second();
+
+unlike(get('/t3.html', 's-maxage=20'), qr/\r\nAge: /, 'age before restart');
+my $t1 = time();
+
+$t->stop();
+
+sleep 1;
+
+$t->run();
+
+my $age = time() - $t1;
+like(http_get('/t3.html'), qr/\r\nAge: $age\r\n/,
+    'age after restart');
+
+$t->stop();
+
+###############################################################################
+
+sub get {
+    my ($url, $extra, %extra) = @_;
+    return http(<<EOF, %extra);
+GET $url HTTP/1.1
+Host: localhost
+Connection: close
+X-Cache-Control: $extra
+
+EOF
+}
+
+# Wait until the next second boundary.
+# Calling this before sending a request increases the likelihood that the
+# timestamp value does not cross into the next second while sending a request
+# and receiving a response.
+sub wait_until_next_second {
+    my $now = time();
+    my $next_second = ceil($now);
+    my $sleep = $next_second - $now;
+    select undef, undef, undef, $sleep;
+}
+
+###############################################################################
diff -r 685f47b21026 -r dcb24ed25028 proxy_cache_use_stale.t
--- a/proxy_cache_use_stale.t Mon Jul 01 22:07:12 2024 +0900
+++ b/proxy_cache_use_stale.t Mon Jul 01 22:07:19 2024 +0900
@@ -206,7 +206,8 @@ like(http_get('/regexp.html'), qr/HIT/,

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

-my $s = get('/t6.html', 'max-age=1, stale-while-revalidate=2', start => 1);
+# max-age must be 5 here since response delay is 4 seconds.
+my $s = get('/t6.html', 'max-age=5, 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');


More information about the nginx-devel mailing list