# HG changeset patch # User Maxim Dounin # Date 1311670354 -14400 # Node ID f3b50effc1d476b040908700bb772197d31fbd80 # Parent cb15f6f4d82061e368e9ae356e1ba73da0f90fef Keepalive: add proxy tests. diff --git a/t/proxy.t b/t/proxy.t new file mode 100644 --- /dev/null +++ b/t/proxy.t @@ -0,0 +1,334 @@ +#!/usr/bin/perl + +# (C) Maxim Dounin + +# Tests for proxy with keepalive. + +############################################################################### + +use warnings; +use strict; + +use Test::More; +use IO::Socket::INET; +use Socket qw/ CRLF /; + +use Test::Nginx; + +############################################################################### + +select STDERR; $| = 1; +select STDOUT; $| = 1; + +my $t = Test::Nginx->new()->has(qw/http proxy ssi rewrite/) + ->write_file_expand('nginx.conf', <<'EOF'); + +%%TEST_GLOBALS%% + +daemon off; + +events { +} + +http { + %%TEST_GLOBALS_HTTP%% + + upstream backend { + server 127.0.0.1:8081; + keepalive 1; + } + + server { + listen 127.0.0.1:8080; + server_name localhost; + + proxy_read_timeout 2s; + proxy_http_version 1.1; + proxy_set_header Connection ""; + + location / { + proxy_pass http://backend; + } + + location /unbuffered/ { + proxy_pass http://backend; + proxy_buffering off; + } + + location /inmemory/ { + ssi on; + rewrite ^ /ssi.html break; + } + } +} + +EOF + +$t->write_file('ssi.html', + '' . + 'set: '); + +$t->run_daemon(\&http_daemon); + +eval { + open OLDERR, ">&", \*STDERR; close STDERR; + $t->run(); + open STDERR, ">&", \*OLDERR; +}; +plan(skip_all => 'no keepalive patches') if $@; + +$t->plan(42); + +############################################################################### + +# There are 3 mostly independend modes of upstream operation: +# +# 1. Buffered, i.e. normal mode with "proxy_buffering on;" +# 2. Unbuffered, i.e. "proxy_buffering off;". +# 3. In memory, i.e. ssi +# +# These all should be tested. + +my ($r, $n); + +# buffered + +like($r = http_get('/buffered/length1'), qr/SEE-THIS/, 'buffered'); +$r =~ m/X-Connection: (\d+)/; $n = $1; +like(http_get('/buffered/length2'), qr/X-Connection: $n.*SEE/ms, 'buffered 2'); + +like($r = http_get('/buffered/chunked1'), qr/SEE-THIS/, 'buffered chunked'); +$r =~ m/X-Connection: (\d+)/; $n = $1; +like(http_get('/buffered/chunked2'), qr/X-Connection: $n/, + 'buffered chunked 2'); + +like($r = http_get('/buffered/complex1'), qr/(0123456789){100}/, + 'buffered complex chunked'); +$r =~ m/X-Connection: (\d+)/; $n = $1; +like(http_get('/buffered/complex2'), qr/X-Connection: $n/, + 'buffered complex chunked 2'); + +like($r = http_get('/buffered/chunk01'), qr/200 OK/, 'buffered 0 chunk'); +$r =~ m/X-Connection: (\d+)/; $n = $1; +like(http_get('/buffered/chunk02'), qr/X-Connection: $n/, 'buffered 0 chunk 2'); + +like($r = http_head('/buffered/length/head1'), qr/(?!SEE-THIS)/, + 'buffered head'); +$r =~ m/X-Connection: (\d+)/; $n = $1; +like(http_head('/buffered/length/head2'), qr/X-Connection: $n/, + 'buffered head 2'); + +like($r = http_get('/buffered/empty1'), qr/200 OK/, 'buffered empty'); +$r =~ m/X-Connection: (\d+)/; $n = $1; +like(http_get('/buffered/empty2'), qr/X-Connection: $n/, 'buffered empty 2'); + +like($r = http_get('/buffered/304nolen1'), qr/304 Not/, 'buffered 304'); +$r =~ m/X-Connection: (\d+)/; $n = $1; +like(http_get('/buffered/304nolen2'), qr/X-Connection: $n/, 'buffered 304 2'); + +like($r = http_get('/buffered/304len1'), qr/304 Not/, + 'buffered 304 with length'); +$r =~ m/X-Connection: (\d+)/; $n = $1; +like(http_get('/buffered/304len2'), qr/X-Connection: $n/, + 'buffered 304 with length 2'); + +# unbuffered + +like($r = http_get('/unbuffered/length1'), qr/SEE-THIS/, 'unbuffered'); +$r =~ m/X-Connection: (\d+)/; $n = $1; +like(http_get('/unbuffered/length2'), qr/X-Connection: $n/, 'unbuffered 2'); + +like($r = http_get('/unbuffered/chunked1'), qr/SEE-THIS/, 'unbuffered chunked'); +$r =~ m/X-Connection: (\d+)/; $n = $1; +like(http_get('/unbuffered/chunked2'), qr/X-Connection: $n/, + 'unbuffered chunked 2'); + +like($r = http_get('/unbuffered/complex1'), qr/(0123456789){100}/, + 'unbuffered complex chunked'); +$r =~ m/X-Connection: (\d+)/; $n = $1; +like(http_get('/unbuffered/complex2'), qr/X-Connection: $n/, + 'unbuffered complex chunked 2'); + +like($r = http_get('/unbuffered/chunk01'), qr/200 OK/, 'unbuffered 0 chunk'); +$r =~ m/X-Connection: (\d+)/; $n = $1; +like(http_get('/unbuffered/chunk02'), qr/X-Connection: $n/, + 'unbuffered 0 chunk 2'); + +like($r = http_get('/unbuffered/empty1'), qr/200 OK/, 'unbuffered empty'); +$r =~ m/X-Connection: (\d+)/; $n = $1; +like(http_get('/unbuffered/empty2'), qr/X-Connection: $n/, + 'unbuffered empty 2'); + +like($r = http_head('/unbuffered/length/head1'), qr/(?!SEE-THIS)/, + 'unbuffered head'); +$r =~ m/X-Connection: (\d+)/; $n = $1; +like(http_head('/unbuffered/length/head2'), qr/X-Connection: $n/, + 'unbuffered head 2'); + +like($r = http_get('/unbuffered/304nolen1'), qr/304 Not/, 'unbuffered 304'); +$r =~ m/X-Connection: (\d+)/; $n = $1; +like(http_get('/unbuffered/304nolen2'), qr/X-Connection: $n/, + 'unbuffered 304 2'); + +like($r = http_get('/unbuffered/304len1'), qr/304 Not/, + 'unbuffered 304 with length'); +$r =~ m/X-Connection: (\d+)/; $n = $1; +like(http_get('/unbuffered/304len2'), qr/X-Connection: $n/, + 'unbuffered 304 with length 2'); + +# in memory + +like($r = http_get('/inmemory/length1'), qr/SEE-THIS/, 'inmemory'); +$r =~ m/SEE-THIS(\d+)/; $n = $1; +like(http_get('/inmemory/length2'), qr/SEE-THIS$n/, 'inmemory 2'); + +like($r = http_get('/inmemory/empty1'), qr/200 OK/, 'inmemory empty'); +$r =~ m/SEE-THIS(\d+)/; $n = $1; +like(http_get('/inmemory/empty2'), qr/200 OK/, 'inmemory empty 2'); + +like($r = http_get('/inmemory/chunked1'), qr/SEE-THIS/, 'inmemory chunked'); +$r =~ m/SEE-THIS(\d+)/; $n = $1; +like(http_get('/inmemory/chunked2'), qr/SEE-THIS$n/, 'inmemory chunked 2'); + +like($r = http_get('/inmemory/complex1'), qr/(0123456789){100}/, + 'inmemory complex chunked'); +$r =~ m/SEE-THIS(\d+)/; $n = $1; +like(http_get('/inmemory/complex2'), qr/SEE-THIS$n/, + 'inmemory complex chunked 2'); + +like(http_get('/inmemory/chunk01'), qr/set: $/, 'inmemory 0 chunk'); +like(http_get('/inmemory/chunk02'), qr/set: $/, 'inmemory 0 chunk 2'); + +############################################################################### + +sub http_daemon { + my $server = IO::Socket::INET->new( + Proto => 'tcp', + LocalHost => '127.0.0.1:8081', + Listen => 5, + Reuse => 1 + ) + or die "Can't create listening socket: $!\n"; + + my $ccount = 0; + my $rcount = 0; + + # dumb server which is able to keep connections alive + + while (my $client = $server->accept()) { + Test::Nginx::log_core('||', "connection from " . $client->peerhost()); + $client->autoflush(1); + $ccount++; + + while (1) { + my $headers = ''; + my $uri = ''; + + while (<$client>) { + Test::Nginx::log_core('||', $_); + $headers .= $_; + last if (/^\x0d?\x0a?$/); + } + + last if $headers eq ''; + $rcount++; + + $uri = $1 if $headers =~ /^\S+\s+([^ ]+)\s+HTTP/i; + + if ($uri =~ m/length/) { + print $client + "HTTP/1.1 200 OK" . CRLF . + "X-Request: $rcount" . CRLF . + "X-Connection: $ccount" . CRLF . + "Content-Length: 26" . CRLF . CRLF; + print $client "TEST-OK-IF-YOU-SEE-THIS" . + sprintf("%03d", $ccount) + unless $headers =~ /^HEAD/i; + + } elsif ($uri =~ m/empty/) { + print $client + "HTTP/1.1 200 OK" . CRLF . + "X-Request: $rcount" . CRLF . + "X-Connection: $ccount" . CRLF . + "Content-Length: 0" . CRLF . CRLF; + + } elsif ($uri =~ m/304nolen/) { + print $client + "HTTP/1.1 304 Not Modified" . CRLF . + "X-Request: $rcount" . CRLF . + "X-Connection: $ccount" . CRLF . CRLF; + + } elsif ($uri =~ m/304len/) { + print $client + "HTTP/1.1 304 Not Modified" . CRLF . + "X-Request: $rcount" . CRLF . + "X-Connection: $ccount" . CRLF . + "Content-Length: 100" . CRLF . CRLF; + + } elsif ($uri =~ m/chunked/) { + print $client + "HTTP/1.1 200 OK" . CRLF . + "X-Request: $rcount" . CRLF . + "X-Connection: $ccount" . CRLF . + "Transfer-Encoding: chunked" . CRLF . + CRLF; + print $client + "1a" . CRLF . + "TEST-OK-IF-YOU-SEE-THIS" . + sprintf("%03d", $ccount) . CRLF . + "0" . CRLF . CRLF + unless $headers =~ /^HEAD/i; + + } elsif ($uri =~ m/complex/) { + print $client + "HTTP/1.1 200 OK" . CRLF . + "X-Request: $rcount" . CRLF . + "X-Connection: $ccount" . CRLF . + "Transfer-Encoding: chunked" . CRLF . + CRLF; + + if ($headers !~ /^HEAD/i) { + for my $n (1..100) { + print $client + "a" . CRLF . + "0123456789" . CRLF; + select undef, undef, undef, 0.01 + if $n % 50 == 0; + } + print $client + "1a" . CRLF . + "TEST-OK-IF-YOU-SEE-THIS" . + sprintf("%03d", $ccount) . + CRLF . + "0" . CRLF; + select undef, undef, undef, 0.05; + print $client CRLF; + } + + } elsif ($uri =~ m/chunk0/) { + print $client + "HTTP/1.1 200 OK" . CRLF . + "X-Request: $rcount" . CRLF . + "X-Connection: $ccount" . CRLF . + "Transfer-Encoding: chunked" . CRLF . + CRLF; + print $client + "0" . CRLF . CRLF + unless $headers =~ /^HEAD/i; + + } else { + print $client + "HTTP/1.1 404 Not Found" . CRLF . + "X-Request: $rcount" . CRLF . + "X-Connection: $ccount" . CRLF . + "Connection: close" . CRLF . CRLF . + "Oops, '$uri' not found" . CRLF; + last; + } + } + + close $client; + } +} + +###############################################################################