[PATCH] Tests: respecting Cache-Control stale-while-revalidate directive

Chris Branch cbranch at cloudflare.com
Mon Feb 13 16:50:39 UTC 2017


# HG changeset patch
# User Chris Branch <cbranch at cloudflare.com>
# Date 1487004211 0
#      Mon Feb 13 16:43:31 2017 +0000
# Node ID cc76d621a5f95e9297cf38fd3e721f4a392c9bd2
# Parent  bef8be8a622429f4ce07753f9133b15e5f08cac1
Tests: respecting Cache-Control stale-while-revalidate directive.

diff -r bef8be8a6224 -r cc76d621a5f9 proxy_cache_stale.t
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/proxy_cache_stale.t	Mon Feb 13 16:43:31 2017 +0000
@@ -0,0 +1,197 @@
+#!/usr/bin/perl
+
+# Tests for http proxy cache use stale.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+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/)->plan(22)
+	->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;
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+
+        location / {
+            proxy_pass    http://127.0.0.1:8081;
+            proxy_cache   NAME;
+
+            proxy_cache_lock on;
+            proxy_cache_use_stale updating;
+            proxy_cache_background_update on;
+        }
+
+        location /nolock {
+            proxy_pass    http://127.0.0.1:8081;
+            proxy_cache   NAME;
+
+            proxy_cache_use_stale updating;
+            proxy_cache_background_update on;
+        }
+    }
+}
+
+EOF
+
+$t->run_daemon(\&http_fake_daemon);
+
+$t->run();
+
+$t->waitforsocket('127.0.0.1:' . port(8081));
+
+###############################################################################
+
+# sequential requests
+for my $i (1 .. 3) {
+	like(http_get('/seq'), qr/response 1/, 'sequential request ' . $i);
+    # response 1 to request 1 is cached
+	# requests 2 & 3 can be served from cache instantly
+}
+
+# wait for first cached request to become stale (max age = 2)
+sleep 3;
+
+# request 4 should get stale response while revalidating in background
+like(http_get('/seq'), qr/response 1/, 'stale response');
+
+# wait for revalidated response to enter cache
+sleep 1;
+
+# response to request 4 enters cache and is younger than max age
+for my $i (1 .. 3) {
+    # request 5-7 should get response to request 4 from cache
+	like(http_get('/seq'), qr/response 2/, 'fresh response after stale');
+}
+
+# wait for response to exceed max-age and stale time (2 + 2 seconds)
+sleep 5;
+
+# response must come from origin
+like(http_get('/seq'), qr/response 3/, 'fresh response after invalidation');
+
+# tests without cache lock
+
+my @sockets;
+
+# start 3 concurrent requests without locking; all go to origin simultaneously
+for my $i (1 .. 3) {
+	$sockets[$i] = http_get('/nolock', start => 1);
+}
+
+# combine results into one string, because we cannot assume ordering
+my $rest = join '', map { http_end($sockets[$_]) } (1 .. 3);
+
+# each request should get a unique response as they missed cache
+like($rest, qr/response 1/, 'nolock - first');
+like($rest, qr/response 2/, 'nolock - second');
+like($rest, qr/response 3/, 'nolock - third');
+
+# making a new request immediately should get a cached response
+like(http_get('/nolock'), qr/response 3/, 'nolock - last cached');
+
+# wait for last cached request to become stale (max age = 2)
+sleep 4;
+
+# start 3 concurrent requests without locking; all should get stale responses
+# while revalidating, but go to origin in the background.
+for my $i (1 .. 3) {
+	$sockets[$i] = http_get('/nolock', start => 1);
+}
+for my $i (1 .. 3) {
+	like(http_end($sockets[$i]), qr/response 3/, 'nolock - stale ' . $i);
+}
+
+# wait for revalidated response to first request to enter cache
+sleep 1;
+like(http_get('/nolock'), qr/response 4/, 'nolock - one revalidated');
+
+# wait for response to exceed max-age and stale time (2 + 2 seconds, and we
+# slept for 1 second earlier)
+sleep 4;
+
+# start 3 concurrent requests without locking; all go to origin simultaneously
+for my $i (1 .. 3) {
+	$sockets[$i] = http_get('/nolock', start => 1);
+}
+
+# combine results into one string, because we cannot assume ordering
+$rest = join '', map { http_end($sockets[$_]) } (1 .. 3);
+
+# each request should get a unique response as they missed cache
+like($rest, qr/response 5/, 'nolock - first after invalidation');
+like($rest, qr/response 6/, 'nolock - second after invalidation');
+like($rest, qr/response 7/, 'nolock - third after invalidation');
+
+# making a new request immediately should get a cached response
+like(http_get('/nolock'), qr/response 7/, 'nolock - last cached after invalidation');
+
+###############################################################################
+
+sub http_fake_daemon {
+	my $server = IO::Socket::INET->new(
+		Proto => 'tcp',
+		LocalAddr => '127.0.0.1:' . port(8081),
+		Listen => 5,
+		Reuse => 1
+	)
+		or die "Can't create listening socket: $!\n";
+
+	my $num = 0;
+	my $uri = '';
+
+	while (my $client = $server->accept()) {
+		$client->autoflush(1);
+
+		while (<$client>) {
+			if (/GET (.*) HTTP/ && $1 ne $uri) {
+				$uri = $1;
+				$num = 0;
+			}
+
+			$uri = $1 if /GET (.*) HTTP/;
+			last if /^\x0d?\x0a?$/;
+		}
+
+		next unless $uri;
+
+		select(undef, undef, undef, 0.3);
+
+		$num++;
+		print $client <<"EOF";
+HTTP/1.1 200 OK
+Cache-Control: max-age=2; stale-while-revalidate=2;
+Connection: close
+
+response $num
+EOF
+	}
+}
+
+###############################################################################


More information about the nginx-devel mailing list