comparison proxy_upgrade.t @ 263:71bb2a896c7a

Tests: more Upgrade handling tests.
author Maxim Dounin <mdounin@mdounin.ru>
date Mon, 18 Feb 2013 19:33:34 +0400
parents
children ccebd3168b5b
comparison
equal deleted inserted replaced
262:d4d9bbe627a8 263:71bb2a896c7a
1 #!/usr/bin/perl
2
3 # (C) Maxim Dounin
4
5 # Tests for http proxy upgrade support. In contrast to proxy_websocket.t
6 # this test doesn't try to use binary WebSocket protocol, but uses simple
7 # plain text protol instead.
8
9 ###############################################################################
10
11 use warnings;
12 use strict;
13
14 use Test::More;
15
16 use IO::Poll;
17 use IO::Select;
18 use IO::Socket::INET;
19 use Socket qw/ CRLF /;
20
21 BEGIN { use FindBin; chdir($FindBin::Bin); }
22
23 use lib 'lib';
24 use Test::Nginx;
25
26 ###############################################################################
27
28 select STDERR; $| = 1;
29 select STDOUT; $| = 1;
30
31 my $t = Test::Nginx->new()->has(qw/http proxy/)
32 ->write_file_expand('nginx.conf', <<'EOF')->plan(28);
33
34 %%TEST_GLOBALS%%
35
36 daemon off;
37
38 events {
39 }
40
41 http {
42 %%TEST_GLOBALS_HTTP%%
43
44 server {
45 listen 127.0.0.1:8080;
46 server_name localhost;
47
48 location / {
49 proxy_pass http://127.0.0.1:8081;
50 proxy_http_version 1.1;
51 proxy_set_header Upgrade $http_upgrade;
52 proxy_set_header Connection "Upgrade";
53 proxy_read_timeout 2s;
54 send_timeout 2s;
55 }
56 }
57 }
58
59 EOF
60
61 $t->run_daemon(\&upgrade_fake_daemon);
62 $t->run();
63
64 ###############################################################################
65
66 TODO: {
67 local $TODO = 'not yet' unless $t->has_version('1.3.13');
68
69 # establish connection
70
71 my $s = upgrade_connect();
72 ok($s, "handshake");
73
74 SKIP: {
75 skip "handshake failed", 22 unless $s;
76
77 # send a frame
78
79 upgrade_write($s, 'foo');
80 is(upgrade_read($s), 'bar', "upgrade response");
81
82 # send some big frame
83
84 upgrade_write($s, 'foo' x 16384);
85 like(upgrade_read($s), qr/^(bar){16384}$/, "upgrade big response");
86
87 # send multiple frames
88
89 for my $i (1 .. 10) {
90 upgrade_write($s, ('foo' x 16384) . $i);
91 upgrade_write($s, 'bazz' . $i);
92 }
93
94 for my $i (1 .. 10) {
95 like(upgrade_read($s), qr/^(bar){16384}\d+$/, "upgrade $i");
96 is(upgrade_read($s), 'bazz' . $i, "upgrade small $i");
97 }
98 }
99
100 # establish connection with some pipelined data
101 # and make sure they are correctly passed upstream
102
103 undef $s;
104 $s = upgrade_connect(message => "foo");
105 ok($s, "handshake pipelined");
106
107 SKIP: {
108 skip "handshake failed", 2 unless $s;
109
110 is(upgrade_read($s), "bar", "response pipelined");
111
112 upgrade_write($s, "foo");
113 is(upgrade_read($s), "bar", "next to pipelined");
114 }
115
116 # connection should not be upgraded unless upgrade was actually
117 # requested and allowed by configuration
118
119 undef $s;
120 $s = upgrade_connect(noheader => 1);
121 ok($s, "handshake noupgrade");
122
123 SKIP: {
124 skip "handshake failed", 2 unless $s;
125
126 upgrade_write($s, "foo");
127 isnt(upgrade_read($s), "bar", "after handshake noupgrade");
128 }
129
130 }
131
132 ###############################################################################
133
134 sub upgrade_connect {
135 my (%opts) = @_;
136
137 my $s = IO::Socket::INET->new(
138 Proto => 'tcp',
139 PeerAddr => '127.0.0.1:8080'
140 )
141 or die "Can't connect to nginx: $!\n";
142
143 # send request, $h->to_string
144
145 my $buf = "GET / HTTP/1.1" . CRLF
146 . "Host: localhost" . CRLF
147 . ($opts{noheader} ? '' : "Upgrade: foo" . CRLF)
148 . "Connection: Upgade" . CRLF . CRLF;
149
150 $buf .= $opts{message} . CRLF if defined $opts{message};
151
152 local $SIG{PIPE} = 'IGNORE';
153
154 log_out($buf);
155 $s->print($buf);
156
157 # read response
158
159 my $got = '';
160 $buf = '';
161
162 while (1) {
163 $buf = $s->getline();
164 last unless length $buf;
165 log_in($buf);
166 $got .= $buf;
167 last if $got =~ /\x0d?\x0a\x0d?\x0a$/;
168 }
169
170 # parse server response
171
172 return $s if $got =~ m!HTTP/1.1 101!;
173 }
174
175 sub upgrade_write {
176 my ($s, $message) = @_;
177 $s->print($message . CRLF);
178 }
179
180 sub upgrade_read {
181 my ($s) = @_;
182 my $m = $s->getline();
183 $m =~ s/\x0d?\x0a// if defined $m;
184 return $m;
185 }
186
187 ###############################################################################
188
189 sub upgrade_fake_daemon {
190 my $server = IO::Socket::INET->new(
191 Proto => 'tcp',
192 LocalAddr => '127.0.0.1:8081',
193 Listen => 5,
194 Reuse => 1
195 )
196 or die "Can't create listening socket: $!\n";
197
198 while (my $client = $server->accept()) {
199 upgrade_handle_client($client);
200 }
201 }
202
203 sub upgrade_handle_client {
204 my ($client) = @_;
205
206 $client->autoflush(1);
207 $client->blocking(0);
208
209 my $poll = IO::Poll->new;
210
211 my $handshake = 1;
212 my $unfinished = '';
213 my $buffer = '';
214 my $n;
215
216 log2c("(new connection $client)");
217
218 while (1) {
219 $poll->mask($client => ($buffer ? POLLIN|POLLOUT : POLLIN));
220 my $p = $poll->poll(0.5);
221 log2c("(poll $p)");
222
223 foreach my $reader ($poll->handles(POLLIN)) {
224 $n = $client->sysread(my $chunk, 65536);
225 return unless $n;
226
227 log2i($chunk);
228
229 if ($handshake) {
230 $buffer .= $chunk;
231 next unless $buffer =~ /\x0d?\x0a\x0d?\x0a$/;
232
233 log2c("(handshake done)");
234
235 $handshake = 0;
236 $buffer = 'HTTP/1.1 101 Switching' . CRLF
237 . 'Upgrade: foo' . CRLF
238 . 'Connection: Upgrade' . CRLF . CRLF;
239
240 next;
241 }
242
243 $unfinished .= $chunk;
244
245 if ($unfinished =~ m/\x0d?\x0a\z/) {
246 $unfinished =~ s/foo/bar/g;
247 $buffer .= $unfinished;
248 $unfinished = '';
249 }
250 }
251
252 foreach my $writer ($poll->handles(POLLOUT)) {
253 next unless length $buffer;
254 $n = $writer->syswrite($buffer);
255 substr $buffer, 0, $n, '';
256 }
257 }
258 }
259
260 sub log2i { Test::Nginx::log_core('|| <<', @_); }
261 sub log2o { Test::Nginx::log_core('|| >>', @_); }
262 sub log2c { Test::Nginx::log_core('||', @_); }
263
264 ###############################################################################