Mercurial > hg > nginx-tests
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 ############################################################################### |