Skip to content

Commit 5fed930

Browse files
committed
Tests: add proxy_http2_ssl.t for HTTP/2 proxy with SSL support.
1 parent 5ec3a57 commit 5fed930

File tree

1 file changed

+352
-0
lines changed

1 file changed

+352
-0
lines changed

proxy_http2_ssl.t

Lines changed: 352 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,352 @@
1+
#!/usr/bin/perl
2+
3+
# (C) Sergey Kandaurov
4+
# (C) Nginx, Inc.
5+
6+
# Tests for HTTP/2 proxy backend with ssl.
7+
8+
###############################################################################
9+
10+
use warnings;
11+
use strict;
12+
13+
use Test::More;
14+
15+
BEGIN { use FindBin; chdir($FindBin::Bin); }
16+
17+
use lib 'lib';
18+
use Test::Nginx;
19+
use Test::Nginx::HTTP2;
20+
21+
###############################################################################
22+
23+
select STDERR; $| = 1;
24+
select STDOUT; $| = 1;
25+
26+
my $t = Test::Nginx->new()->has(qw/http rewrite http_v2 proxy/)
27+
->has(qw/upstream_keepalive http_ssl/)
28+
->has_daemon('openssl');
29+
30+
plan(skip_all => 'no ALPN support in OpenSSL')
31+
if $t->has_module('OpenSSL') and not $t->has_feature('openssl:1.0.2');
32+
33+
$t->write_file_expand('nginx.conf', <<'EOF')->plan(38);
34+
35+
%%TEST_GLOBALS%%
36+
37+
daemon off;
38+
39+
events {
40+
}
41+
42+
http {
43+
%%TEST_GLOBALS_HTTP%%
44+
45+
upstream u {
46+
server 127.0.0.1:8081;
47+
keepalive 1;
48+
}
49+
50+
server {
51+
listen 127.0.0.1:8081 http2 ssl;
52+
server_name localhost;
53+
54+
ssl_certificate_key localhost.key;
55+
ssl_certificate localhost.crt;
56+
57+
ssl_verify_client optional;
58+
ssl_client_certificate client.crt;
59+
60+
http2_body_preread_size 128k;
61+
62+
location / {
63+
proxy_pass http://127.0.0.1:8082;
64+
proxy_http_version 2.0;
65+
proxy_request_buffering off;
66+
proxy_set_header TE "trailers";
67+
proxy_pass_trailers on;
68+
add_header X-Connection $connection;
69+
}
70+
}
71+
72+
server {
73+
listen 127.0.0.1:8080 http2;
74+
server_name localhost;
75+
76+
http2_body_preread_size 128k;
77+
78+
location / {
79+
proxy_pass https://127.0.0.1:8081;
80+
proxy_http_version 2.0;
81+
proxy_request_buffering off;
82+
proxy_set_header TE "trailers";
83+
proxy_pass_trailers on;
84+
proxy_ssl_name localhost;
85+
proxy_ssl_verify on;
86+
proxy_ssl_trusted_certificate localhost.crt;
87+
88+
proxy_ssl_certificate client.crt;
89+
proxy_ssl_certificate_key client.key;
90+
proxy_ssl_password_file password;
91+
92+
if ($arg_if) {
93+
# nothing
94+
}
95+
96+
limit_except GET {
97+
# nothing
98+
}
99+
}
100+
101+
location /KeepAlive {
102+
proxy_pass https://u;
103+
proxy_http_version 2.0;
104+
proxy_request_buffering off;
105+
proxy_set_header TE "trailers";
106+
proxy_pass_trailers on;
107+
}
108+
}
109+
}
110+
111+
EOF
112+
113+
$t->write_file('openssl.conf', <<EOF);
114+
[ req ]
115+
default_bits = 2048
116+
encrypt_key = no
117+
distinguished_name = req_distinguished_name
118+
[ req_distinguished_name ]
119+
EOF
120+
121+
my $d = $t->testdir();
122+
123+
foreach my $name ('localhost') {
124+
system('openssl req -x509 -new '
125+
. "-config $d/openssl.conf -subj /CN=$name/ "
126+
. "-out $d/$name.crt -keyout $d/$name.key "
127+
. ">>$d/openssl.out 2>&1") == 0
128+
or die "Can't create certificate for $name: $!\n";
129+
}
130+
131+
foreach my $name ('client') {
132+
system("openssl genrsa -out $d/$name.key -passout pass:$name "
133+
. "-aes128 2048 >>$d/openssl.out 2>&1") == 0
134+
or die "Can't create private key: $!\n";
135+
system('openssl req -x509 -new '
136+
. "-config $d/openssl.conf -subj /CN=$name/ "
137+
. "-out $d/$name.crt "
138+
. "-key $d/$name.key -passin pass:$name"
139+
. ">>$d/openssl.out 2>&1") == 0
140+
or die "Can't create certificate for $name: $!\n";
141+
}
142+
143+
sleep 1 if $^O eq 'MSWin32';
144+
145+
$t->write_file('password', 'client');
146+
147+
# suppress deprecation warning
148+
149+
open OLDERR, ">&", \*STDERR; close STDERR;
150+
$t->run();
151+
open STDERR, ">&", \*OLDERR;
152+
153+
###############################################################################
154+
155+
my $p = port(8082);
156+
my $f = proxy();
157+
158+
my $frames = $f->{http_start}('/SayHello');
159+
my ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
160+
is($frame->{flags}, 4, 'request - HEADERS flags');
161+
ok((my $sid = $frame->{sid}) % 2, 'request - HEADERS sid odd');
162+
is($frame->{headers}{':method'}, 'POST', 'request - method');
163+
is($frame->{headers}{':scheme'}, 'http', 'request - scheme');
164+
is($frame->{headers}{':path'}, '/SayHello', 'request - path');
165+
is($frame->{headers}{':authority'}, "127.0.0.1:$p", 'request - authority');
166+
is($frame->{headers}{'content-type'}, 'text/plain',
167+
'request - content type');
168+
is($frame->{headers}{te}, 'trailers', 'request - te');
169+
170+
$frames = $f->{data}('Hello');
171+
($frame) = grep { $_->{type} eq "SETTINGS" } @$frames;
172+
is($frame->{flags}, 1, 'request - SETTINGS ack');
173+
is($frame->{sid}, 0, 'request - SETTINGS sid');
174+
is($frame->{length}, 0, 'request - SETTINGS length');
175+
176+
($frame) = grep { $_->{type} eq "DATA" } @$frames;
177+
is($frame->{data}, 'Hello', 'request - DATA');
178+
is($frame->{length}, 5, 'request - DATA length');
179+
is($frame->{flags}, 1, 'request - DATA flags');
180+
is($frame->{sid}, $sid, 'request - DATA sid match');
181+
182+
$frames = $f->{http_end}();
183+
($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
184+
is($frame->{flags}, 4, 'response - HEADERS flags');
185+
is($frame->{sid}, 1, 'response - HEADERS sid');
186+
is($frame->{headers}{':status'}, '200', 'response - status');
187+
is($frame->{headers}{'content-type'}, 'text/plain',
188+
'response - content type');
189+
ok($frame->{headers}{server}, 'response - server');
190+
ok($frame->{headers}{date}, 'response - date');
191+
ok(my $c = $frame->{headers}{'x-connection'}, 'response - connection');
192+
193+
($frame) = grep { $_->{type} eq "DATA" } @$frames;
194+
is($frame->{data}, 'Hello world', 'response - DATA');
195+
is($frame->{length}, 11, 'response - DATA length');
196+
is($frame->{flags}, 0, 'response - DATA flags');
197+
is($frame->{sid}, 1, 'response - DATA sid');
198+
199+
(undef, $frame) = grep { $_->{type} eq "HEADERS" } @$frames;
200+
is($frame->{flags}, 5, 'response - trailers flags');
201+
is($frame->{sid}, 1, 'response - trailers sid');
202+
is($frame->{headers}{'x-message'}, '', 'response - trailers message');
203+
is($frame->{headers}{'x-status'}, '0', 'response - trailers status');
204+
205+
# next request is on a new backend connection, no sid incremented
206+
207+
$f->{http_start}('/SayHello');
208+
$f->{data}('Hello');
209+
$frames = $f->{http_end}();
210+
($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
211+
cmp_ok($frame->{headers}{'x-connection'}, '>', $c, 'response 2 - connection');
212+
213+
# flow control
214+
215+
$f->{http_start}('/FlowControl');
216+
$frames = $f->{data_len}(('Hello' x 13000) . ('x' x 550), 65535);
217+
my $sum = eval join '+', map { $_->{type} eq "DATA" && $_->{length} } @$frames;
218+
is($sum, 65535, 'flow control - iws length');
219+
220+
$f->{update}(10);
221+
$f->{update_sid}(10);
222+
223+
$frames = $f->{data_len}(undef, 10);
224+
($frame) = grep { $_->{type} eq "DATA" } @$frames;
225+
is($frame->{length}, 10, 'flow control - update length');
226+
is($frame->{flags}, 0, 'flow control - update flags');
227+
228+
$f->{update_sid}(10);
229+
$f->{update}(10);
230+
231+
$frames = $f->{data_len}(undef, 5);
232+
($frame) = grep { $_->{type} eq "DATA" } @$frames;
233+
is($frame->{length}, 5, 'flow control - rest length');
234+
is($frame->{flags}, 1, 'flow control - rest flags');
235+
236+
$f->{http_end}();
237+
238+
# upstream keepalive
239+
240+
$f->{http_start}('/KeepAlive');
241+
$f->{data}('Hello');
242+
$frames = $f->{http_end}();
243+
($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
244+
ok($c = $frame->{headers}{'x-connection'}, 'keepalive - connection');
245+
246+
$f->{http_start}('/KeepAlive');
247+
$f->{data}('Hello');
248+
$frames = $f->{http_end}();
249+
($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
250+
is($frame->{headers}{'x-connection'}, $c, 'keepalive - connection reuse');
251+
252+
###############################################################################
253+
254+
sub proxy {
255+
my ($server, $client, $f, $s, $c, $sid, $uri);
256+
257+
$server = IO::Socket::INET->new(
258+
Proto => 'tcp',
259+
LocalHost => '127.0.0.1',
260+
LocalPort => $p,
261+
Listen => 5,
262+
Reuse => 1
263+
)
264+
or die "Can't create listening socket: $!\n";
265+
266+
$f->{http_start} = sub {
267+
($uri, my %extra) = @_;
268+
my $body_more = 1 if $uri !~ /LongHeader/;
269+
$s = Test::Nginx::HTTP2->new() if !defined $s;
270+
$s->new_stream({ body_more => $body_more, headers => [
271+
{ name => ':method', value => 'POST', mode => 0 },
272+
{ name => ':scheme', value => 'http', mode => 0 },
273+
{ name => ':path', value => $uri, },
274+
{ name => ':authority', value => 'localhost' },
275+
{ name => 'content-type', value => 'text/plain' },
276+
{ name => 'te', value => 'trailers', mode => 2 }]});
277+
278+
if (!$extra{reuse}) {
279+
eval {
280+
local $SIG{ALRM} = sub { die "timeout\n" };
281+
alarm(5);
282+
283+
$client = $server->accept() or return;
284+
285+
alarm(0);
286+
};
287+
alarm(0);
288+
if ($@) {
289+
log_in("died: $@");
290+
return undef;
291+
}
292+
293+
log2c("(new connection $client)");
294+
295+
$client->sysread(my $buf, 24) == 24 or return; # preface
296+
297+
$c = Test::Nginx::HTTP2->new(1, socket => $client,
298+
pure => 1, preface => "") or return;
299+
}
300+
301+
my $frames = $c->read(all => [{ fin => 4 }]);
302+
303+
if (!$extra{reuse}) {
304+
$c->h2_settings(0);
305+
$c->h2_settings(1);
306+
}
307+
308+
my ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
309+
$sid = $frame->{sid};
310+
return $frames;
311+
};
312+
$f->{data_len} = sub {
313+
my ($body, $len) = @_;
314+
$s->h2_body($body) if defined $body;
315+
return $c->read(all => [{ sid => $sid, length => $len }]);
316+
};
317+
$f->{update} = sub {
318+
$c->h2_window(shift);
319+
};
320+
$f->{update_sid} = sub {
321+
$c->h2_window(shift, $sid);
322+
};
323+
$f->{data} = sub {
324+
my ($body, %extra) = @_;
325+
$s->h2_body($body, { %extra });
326+
return $c->read(all => [{ sid => $sid,
327+
length => length($body) }]);
328+
};
329+
$f->{http_end} = sub {
330+
$c->new_stream({ body_more => 1, headers => [
331+
{ name => ':status', value => '200', mode => 0 },
332+
{ name => 'content-type', value => 'text/plain',
333+
mode => 1, huff => 1 },
334+
]}, $sid);
335+
$c->h2_body('Hello world', { body_more => 1 });
336+
$c->new_stream({ headers => [
337+
{ name => 'x-status', value => '0',
338+
mode => 2, huff => 1 },
339+
{ name => 'x-message', value => '',
340+
mode => 2, huff => 1 },
341+
]}, $sid);
342+
343+
return $s->read(all => [{ fin => 1 }]);
344+
};
345+
return $f;
346+
}
347+
348+
sub log2i { Test::Nginx::log_core('|| <<', @_); }
349+
sub log2o { Test::Nginx::log_core('|| >>', @_); }
350+
sub log2c { Test::Nginx::log_core('||', @_); }
351+
352+
###############################################################################

0 commit comments

Comments
 (0)