27
|
1 #!/usr/bin/perl
|
|
2
|
|
3 # (C) Maxim Dounin
|
|
4
|
|
5 # Tests for proxy with keepalive.
|
|
6
|
|
7 ###############################################################################
|
|
8
|
|
9 use warnings;
|
|
10 use strict;
|
|
11
|
|
12 use Test::More;
|
|
13 use IO::Socket::INET;
|
|
14 use Socket qw/ CRLF /;
|
|
15
|
|
16 use Test::Nginx;
|
|
17
|
|
18 ###############################################################################
|
|
19
|
|
20 select STDERR; $| = 1;
|
|
21 select STDOUT; $| = 1;
|
|
22
|
|
23 my $t = Test::Nginx->new()->has(qw/http proxy ssi rewrite/)
|
|
24 ->write_file_expand('nginx.conf', <<'EOF');
|
|
25
|
|
26 %%TEST_GLOBALS%%
|
|
27
|
|
28 daemon off;
|
|
29
|
|
30 events {
|
|
31 }
|
|
32
|
|
33 http {
|
|
34 %%TEST_GLOBALS_HTTP%%
|
|
35
|
|
36 upstream backend {
|
|
37 server 127.0.0.1:8081;
|
|
38 keepalive 1;
|
|
39 }
|
|
40
|
|
41 server {
|
|
42 listen 127.0.0.1:8080;
|
|
43 server_name localhost;
|
|
44
|
|
45 proxy_read_timeout 2s;
|
|
46 proxy_http_version 1.1;
|
|
47 proxy_set_header Connection "";
|
|
48
|
|
49 location / {
|
|
50 proxy_pass http://backend;
|
|
51 }
|
|
52
|
|
53 location /unbuffered/ {
|
|
54 proxy_pass http://backend;
|
|
55 proxy_buffering off;
|
|
56 }
|
|
57
|
|
58 location /inmemory/ {
|
|
59 ssi on;
|
|
60 rewrite ^ /ssi.html break;
|
|
61 }
|
|
62 }
|
|
63 }
|
|
64
|
|
65 EOF
|
|
66
|
|
67 $t->write_file('ssi.html',
|
|
68 '<!--#include virtual="/include$request_uri" set="x" -->' .
|
|
69 'set: <!--#echo var="x" -->');
|
|
70
|
|
71 $t->run_daemon(\&http_daemon);
|
|
72
|
|
73 eval {
|
|
74 open OLDERR, ">&", \*STDERR; close STDERR;
|
|
75 $t->run();
|
|
76 open STDERR, ">&", \*OLDERR;
|
|
77 };
|
|
78 plan(skip_all => 'no keepalive patches') if $@;
|
|
79
|
|
80 $t->plan(42);
|
|
81
|
|
82 ###############################################################################
|
|
83
|
|
84 # There are 3 mostly independend modes of upstream operation:
|
|
85 #
|
|
86 # 1. Buffered, i.e. normal mode with "proxy_buffering on;"
|
|
87 # 2. Unbuffered, i.e. "proxy_buffering off;".
|
|
88 # 3. In memory, i.e. ssi <!--#include ... set -->
|
|
89 #
|
|
90 # These all should be tested.
|
|
91
|
|
92 my ($r, $n);
|
|
93
|
|
94 # buffered
|
|
95
|
|
96 like($r = http_get('/buffered/length1'), qr/SEE-THIS/, 'buffered');
|
|
97 $r =~ m/X-Connection: (\d+)/; $n = $1;
|
|
98 like(http_get('/buffered/length2'), qr/X-Connection: $n.*SEE/ms, 'buffered 2');
|
|
99
|
|
100 like($r = http_get('/buffered/chunked1'), qr/SEE-THIS/, 'buffered chunked');
|
|
101 $r =~ m/X-Connection: (\d+)/; $n = $1;
|
|
102 like(http_get('/buffered/chunked2'), qr/X-Connection: $n/,
|
|
103 'buffered chunked 2');
|
|
104
|
|
105 like($r = http_get('/buffered/complex1'), qr/(0123456789){100}/,
|
|
106 'buffered complex chunked');
|
|
107 $r =~ m/X-Connection: (\d+)/; $n = $1;
|
|
108 like(http_get('/buffered/complex2'), qr/X-Connection: $n/,
|
|
109 'buffered complex chunked 2');
|
|
110
|
|
111 like($r = http_get('/buffered/chunk01'), qr/200 OK/, 'buffered 0 chunk');
|
|
112 $r =~ m/X-Connection: (\d+)/; $n = $1;
|
|
113 like(http_get('/buffered/chunk02'), qr/X-Connection: $n/, 'buffered 0 chunk 2');
|
|
114
|
|
115 like($r = http_head('/buffered/length/head1'), qr/(?!SEE-THIS)/,
|
|
116 'buffered head');
|
|
117 $r =~ m/X-Connection: (\d+)/; $n = $1;
|
|
118 like(http_head('/buffered/length/head2'), qr/X-Connection: $n/,
|
|
119 'buffered head 2');
|
|
120
|
|
121 like($r = http_get('/buffered/empty1'), qr/200 OK/, 'buffered empty');
|
|
122 $r =~ m/X-Connection: (\d+)/; $n = $1;
|
|
123 like(http_get('/buffered/empty2'), qr/X-Connection: $n/, 'buffered empty 2');
|
|
124
|
|
125 like($r = http_get('/buffered/304nolen1'), qr/304 Not/, 'buffered 304');
|
|
126 $r =~ m/X-Connection: (\d+)/; $n = $1;
|
|
127 like(http_get('/buffered/304nolen2'), qr/X-Connection: $n/, 'buffered 304 2');
|
|
128
|
|
129 like($r = http_get('/buffered/304len1'), qr/304 Not/,
|
|
130 'buffered 304 with length');
|
|
131 $r =~ m/X-Connection: (\d+)/; $n = $1;
|
|
132 like(http_get('/buffered/304len2'), qr/X-Connection: $n/,
|
|
133 'buffered 304 with length 2');
|
|
134
|
|
135 # unbuffered
|
|
136
|
|
137 like($r = http_get('/unbuffered/length1'), qr/SEE-THIS/, 'unbuffered');
|
|
138 $r =~ m/X-Connection: (\d+)/; $n = $1;
|
|
139 like(http_get('/unbuffered/length2'), qr/X-Connection: $n/, 'unbuffered 2');
|
|
140
|
|
141 like($r = http_get('/unbuffered/chunked1'), qr/SEE-THIS/, 'unbuffered chunked');
|
|
142 $r =~ m/X-Connection: (\d+)/; $n = $1;
|
|
143 like(http_get('/unbuffered/chunked2'), qr/X-Connection: $n/,
|
|
144 'unbuffered chunked 2');
|
|
145
|
|
146 like($r = http_get('/unbuffered/complex1'), qr/(0123456789){100}/,
|
|
147 'unbuffered complex chunked');
|
|
148 $r =~ m/X-Connection: (\d+)/; $n = $1;
|
|
149 like(http_get('/unbuffered/complex2'), qr/X-Connection: $n/,
|
|
150 'unbuffered complex chunked 2');
|
|
151
|
|
152 like($r = http_get('/unbuffered/chunk01'), qr/200 OK/, 'unbuffered 0 chunk');
|
|
153 $r =~ m/X-Connection: (\d+)/; $n = $1;
|
|
154 like(http_get('/unbuffered/chunk02'), qr/X-Connection: $n/,
|
|
155 'unbuffered 0 chunk 2');
|
|
156
|
|
157 like($r = http_get('/unbuffered/empty1'), qr/200 OK/, 'unbuffered empty');
|
|
158 $r =~ m/X-Connection: (\d+)/; $n = $1;
|
|
159 like(http_get('/unbuffered/empty2'), qr/X-Connection: $n/,
|
|
160 'unbuffered empty 2');
|
|
161
|
|
162 like($r = http_head('/unbuffered/length/head1'), qr/(?!SEE-THIS)/,
|
|
163 'unbuffered head');
|
|
164 $r =~ m/X-Connection: (\d+)/; $n = $1;
|
|
165 like(http_head('/unbuffered/length/head2'), qr/X-Connection: $n/,
|
|
166 'unbuffered head 2');
|
|
167
|
|
168 like($r = http_get('/unbuffered/304nolen1'), qr/304 Not/, 'unbuffered 304');
|
|
169 $r =~ m/X-Connection: (\d+)/; $n = $1;
|
|
170 like(http_get('/unbuffered/304nolen2'), qr/X-Connection: $n/,
|
|
171 'unbuffered 304 2');
|
|
172
|
|
173 like($r = http_get('/unbuffered/304len1'), qr/304 Not/,
|
|
174 'unbuffered 304 with length');
|
|
175 $r =~ m/X-Connection: (\d+)/; $n = $1;
|
|
176 like(http_get('/unbuffered/304len2'), qr/X-Connection: $n/,
|
|
177 'unbuffered 304 with length 2');
|
|
178
|
|
179 # in memory
|
|
180
|
|
181 like($r = http_get('/inmemory/length1'), qr/SEE-THIS/, 'inmemory');
|
|
182 $r =~ m/SEE-THIS(\d+)/; $n = $1;
|
|
183 like(http_get('/inmemory/length2'), qr/SEE-THIS$n/, 'inmemory 2');
|
|
184
|
|
185 like($r = http_get('/inmemory/empty1'), qr/200 OK/, 'inmemory empty');
|
|
186 $r =~ m/SEE-THIS(\d+)/; $n = $1;
|
|
187 like(http_get('/inmemory/empty2'), qr/200 OK/, 'inmemory empty 2');
|
|
188
|
|
189 like($r = http_get('/inmemory/chunked1'), qr/SEE-THIS/, 'inmemory chunked');
|
|
190 $r =~ m/SEE-THIS(\d+)/; $n = $1;
|
|
191 like(http_get('/inmemory/chunked2'), qr/SEE-THIS$n/, 'inmemory chunked 2');
|
|
192
|
|
193 like($r = http_get('/inmemory/complex1'), qr/(0123456789){100}/,
|
|
194 'inmemory complex chunked');
|
|
195 $r =~ m/SEE-THIS(\d+)/; $n = $1;
|
|
196 like(http_get('/inmemory/complex2'), qr/SEE-THIS$n/,
|
|
197 'inmemory complex chunked 2');
|
|
198
|
|
199 like(http_get('/inmemory/chunk01'), qr/set: $/, 'inmemory 0 chunk');
|
|
200 like(http_get('/inmemory/chunk02'), qr/set: $/, 'inmemory 0 chunk 2');
|
|
201
|
|
202 ###############################################################################
|
|
203
|
|
204 sub http_daemon {
|
|
205 my $server = IO::Socket::INET->new(
|
|
206 Proto => 'tcp',
|
|
207 LocalHost => '127.0.0.1:8081',
|
|
208 Listen => 5,
|
|
209 Reuse => 1
|
|
210 )
|
|
211 or die "Can't create listening socket: $!\n";
|
|
212
|
|
213 my $ccount = 0;
|
|
214 my $rcount = 0;
|
|
215
|
|
216 # dumb server which is able to keep connections alive
|
|
217
|
|
218 while (my $client = $server->accept()) {
|
|
219 Test::Nginx::log_core('||', "connection from " . $client->peerhost());
|
|
220 $client->autoflush(1);
|
|
221 $ccount++;
|
|
222
|
|
223 while (1) {
|
|
224 my $headers = '';
|
|
225 my $uri = '';
|
|
226
|
|
227 while (<$client>) {
|
|
228 Test::Nginx::log_core('||', $_);
|
|
229 $headers .= $_;
|
|
230 last if (/^\x0d?\x0a?$/);
|
|
231 }
|
|
232
|
|
233 last if $headers eq '';
|
|
234 $rcount++;
|
|
235
|
|
236 $uri = $1 if $headers =~ /^\S+\s+([^ ]+)\s+HTTP/i;
|
|
237
|
|
238 if ($uri =~ m/length/) {
|
|
239 print $client
|
|
240 "HTTP/1.1 200 OK" . CRLF .
|
|
241 "X-Request: $rcount" . CRLF .
|
|
242 "X-Connection: $ccount" . CRLF .
|
|
243 "Content-Length: 26" . CRLF . CRLF;
|
|
244 print $client "TEST-OK-IF-YOU-SEE-THIS" .
|
|
245 sprintf("%03d", $ccount)
|
|
246 unless $headers =~ /^HEAD/i;
|
|
247
|
|
248 } elsif ($uri =~ m/empty/) {
|
|
249 print $client
|
|
250 "HTTP/1.1 200 OK" . CRLF .
|
|
251 "X-Request: $rcount" . CRLF .
|
|
252 "X-Connection: $ccount" . CRLF .
|
|
253 "Content-Length: 0" . CRLF . CRLF;
|
|
254
|
|
255 } elsif ($uri =~ m/304nolen/) {
|
|
256 print $client
|
|
257 "HTTP/1.1 304 Not Modified" . CRLF .
|
|
258 "X-Request: $rcount" . CRLF .
|
|
259 "X-Connection: $ccount" . CRLF . CRLF;
|
|
260
|
|
261 } elsif ($uri =~ m/304len/) {
|
|
262 print $client
|
|
263 "HTTP/1.1 304 Not Modified" . CRLF .
|
|
264 "X-Request: $rcount" . CRLF .
|
|
265 "X-Connection: $ccount" . CRLF .
|
|
266 "Content-Length: 100" . CRLF . CRLF;
|
|
267
|
|
268 } elsif ($uri =~ m/chunked/) {
|
|
269 print $client
|
|
270 "HTTP/1.1 200 OK" . CRLF .
|
|
271 "X-Request: $rcount" . CRLF .
|
|
272 "X-Connection: $ccount" . CRLF .
|
|
273 "Transfer-Encoding: chunked" . CRLF .
|
|
274 CRLF;
|
|
275 print $client
|
|
276 "1a" . CRLF .
|
|
277 "TEST-OK-IF-YOU-SEE-THIS" .
|
|
278 sprintf("%03d", $ccount) . CRLF .
|
|
279 "0" . CRLF . CRLF
|
|
280 unless $headers =~ /^HEAD/i;
|
|
281
|
|
282 } elsif ($uri =~ m/complex/) {
|
|
283 print $client
|
|
284 "HTTP/1.1 200 OK" . CRLF .
|
|
285 "X-Request: $rcount" . CRLF .
|
|
286 "X-Connection: $ccount" . CRLF .
|
|
287 "Transfer-Encoding: chunked" . CRLF .
|
|
288 CRLF;
|
|
289
|
|
290 if ($headers !~ /^HEAD/i) {
|
|
291 for my $n (1..100) {
|
|
292 print $client
|
|
293 "a" . CRLF .
|
|
294 "0123456789" . CRLF;
|
|
295 select undef, undef, undef, 0.01
|
|
296 if $n % 50 == 0;
|
|
297 }
|
|
298 print $client
|
|
299 "1a" . CRLF .
|
|
300 "TEST-OK-IF-YOU-SEE-THIS" .
|
|
301 sprintf("%03d", $ccount) .
|
|
302 CRLF .
|
|
303 "0" . CRLF;
|
|
304 select undef, undef, undef, 0.05;
|
|
305 print $client CRLF;
|
|
306 }
|
|
307
|
|
308 } elsif ($uri =~ m/chunk0/) {
|
|
309 print $client
|
|
310 "HTTP/1.1 200 OK" . CRLF .
|
|
311 "X-Request: $rcount" . CRLF .
|
|
312 "X-Connection: $ccount" . CRLF .
|
|
313 "Transfer-Encoding: chunked" . CRLF .
|
|
314 CRLF;
|
|
315 print $client
|
|
316 "0" . CRLF . CRLF
|
|
317 unless $headers =~ /^HEAD/i;
|
|
318
|
|
319 } else {
|
|
320 print $client
|
|
321 "HTTP/1.1 404 Not Found" . CRLF .
|
|
322 "X-Request: $rcount" . CRLF .
|
|
323 "X-Connection: $ccount" . CRLF .
|
|
324 "Connection: close" . CRLF . CRLF .
|
|
325 "Oops, '$uri' not found" . CRLF;
|
|
326 last;
|
|
327 }
|
|
328 }
|
|
329
|
|
330 close $client;
|
|
331 }
|
|
332 }
|
|
333
|
|
334 ###############################################################################
|