1303
|
1 #!/usr/bin/perl
|
|
2
|
|
3 # (C) Sergey Kandaurov
|
|
4 # (C) Nginx, Inc.
|
|
5
|
|
6 # Tests for grpc 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 proxy rewrite http_v2 grpc/)
|
|
27 ->has(qw/upstream_keepalive http_ssl/);
|
|
28
|
|
29 $t->{_configure_args} =~ /OpenSSL ([\d\.]+)/;
|
|
30 plan(skip_all => 'OpenSSL too old') unless defined $1 and $1 ge '1.0.2';
|
|
31
|
|
32 $t->write_file_expand('nginx.conf', <<'EOF');
|
|
33
|
|
34 %%TEST_GLOBALS%%
|
|
35
|
|
36 daemon off;
|
|
37
|
|
38 events {
|
|
39 }
|
|
40
|
|
41 http {
|
|
42 %%TEST_GLOBALS_HTTP%%
|
|
43
|
|
44 upstream u {
|
|
45 server 127.0.0.1:8081;
|
|
46 keepalive 1;
|
|
47 }
|
|
48
|
|
49 server {
|
|
50 listen 127.0.0.1:8081 http2 ssl;
|
|
51 server_name localhost;
|
|
52
|
|
53 ssl_certificate_key localhost.key;
|
|
54 ssl_certificate localhost.crt;
|
|
55
|
|
56 http2_max_field_size 128k;
|
|
57 http2_max_header_size 128k;
|
|
58 http2_body_preread_size 128k;
|
|
59
|
|
60 location / {
|
|
61 grpc_pass 127.0.0.1:8082;
|
|
62 add_header X-Connection $connection;
|
|
63 }
|
|
64 }
|
|
65
|
|
66 server {
|
|
67 listen 127.0.0.1:8080 http2;
|
|
68 server_name localhost;
|
|
69
|
|
70 http2_max_field_size 128k;
|
|
71 http2_max_header_size 128k;
|
|
72 http2_body_preread_size 128k;
|
|
73
|
|
74 location / {
|
|
75 grpc_pass grpcs://127.0.0.1:8081;
|
|
76
|
|
77 if ($arg_if) {
|
|
78 # nothing
|
|
79 }
|
|
80
|
|
81 limit_except GET {
|
|
82 # nothing
|
|
83 }
|
|
84 }
|
|
85
|
|
86 location /KeepAlive {
|
|
87 grpc_pass grpcs://u;
|
|
88 }
|
|
89 }
|
|
90 }
|
|
91
|
|
92 EOF
|
|
93
|
|
94 $t->write_file('openssl.conf', <<EOF);
|
|
95 [ req ]
|
|
96 default_bits = 1024
|
|
97 encrypt_key = no
|
|
98 distinguished_name = req_distinguished_name
|
|
99 [ req_distinguished_name ]
|
|
100 EOF
|
|
101
|
|
102 my $d = $t->testdir();
|
|
103
|
|
104 foreach my $name ('localhost') {
|
|
105 system('openssl req -x509 -new '
|
|
106 . "-config $d/openssl.conf -subj /CN=$name/ "
|
|
107 . "-out $d/$name.crt -keyout $d/$name.key "
|
|
108 . ">>$d/openssl.out 2>&1") == 0
|
|
109 or die "Can't create certificate for $name: $!\n";
|
|
110 }
|
|
111
|
|
112 $t->try_run('no grpc')->plan(33);
|
|
113
|
|
114 ###############################################################################
|
|
115
|
|
116 my $p = port(8082);
|
|
117 my $f = grpc();
|
|
118
|
|
119 my $frames = $f->{http_start}('/SayHello');
|
|
120 my ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
|
|
121 is($frame->{flags}, 4, 'request - HEADERS flags');
|
|
122 ok((my $sid = $frame->{sid}) % 2, 'request - HEADERS sid odd');
|
|
123 is($frame->{headers}{':method'}, 'POST', 'request - method');
|
|
124 is($frame->{headers}{':scheme'}, 'http', 'request - scheme');
|
|
125 is($frame->{headers}{':path'}, '/SayHello', 'request - path');
|
|
126 is($frame->{headers}{':authority'}, "127.0.0.1:$p", 'request - authority');
|
|
127 is($frame->{headers}{'content-type'}, 'application/grpc',
|
|
128 'request - content type');
|
|
129 is($frame->{headers}{te}, 'trailers', 'request - te');
|
|
130
|
|
131 $frames = $f->{data}('Hello');
|
|
132 ($frame) = grep { $_->{type} eq "SETTINGS" } @$frames;
|
|
133 is($frame->{flags}, 1, 'request - SETTINGS ack');
|
|
134 is($frame->{sid}, 0, 'request - SETTINGS sid');
|
|
135 is($frame->{length}, 0, 'request - SETTINGS length');
|
|
136
|
|
137 ($frame) = grep { $_->{type} eq "DATA" } @$frames;
|
|
138 is($frame->{data}, 'Hello', 'request - DATA');
|
|
139 is($frame->{length}, 5, 'request - DATA length');
|
|
140 is($frame->{flags}, 1, 'request - DATA flags');
|
|
141 is($frame->{sid}, $sid, 'request - DATA sid match');
|
|
142
|
|
143 $frames = $f->{http_end}();
|
|
144 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
|
|
145 is($frame->{flags}, 4, 'response - HEADERS flags');
|
|
146 is($frame->{sid}, 1, 'response - HEADERS sid');
|
|
147 is($frame->{headers}{':status'}, '200', 'response - status');
|
|
148 is($frame->{headers}{'content-type'}, 'application/grpc',
|
|
149 'response - content type');
|
|
150 ok($frame->{headers}{server}, 'response - server');
|
|
151 ok($frame->{headers}{date}, 'response - date');
|
|
152 ok(my $c = $frame->{headers}{'x-connection'}, 'response - connection');
|
|
153
|
|
154 ($frame) = grep { $_->{type} eq "DATA" } @$frames;
|
|
155 is($frame->{data}, 'Hello world', 'response - DATA');
|
|
156 is($frame->{length}, 11, 'response - DATA length');
|
|
157 is($frame->{flags}, 0, 'response - DATA flags');
|
|
158 is($frame->{sid}, 1, 'response - DATA sid');
|
|
159
|
|
160 (undef, $frame) = grep { $_->{type} eq "HEADERS" } @$frames;
|
|
161 is($frame->{flags}, 5, 'response - trailers flags');
|
|
162 is($frame->{sid}, 1, 'response - trailers sid');
|
|
163 is($frame->{headers}{'grpc-message'}, '', 'response - trailers message');
|
|
164 is($frame->{headers}{'grpc-status'}, '0', 'response - trailers status');
|
|
165
|
|
166 # next request is on a new backend connection, no sid incremented
|
|
167
|
|
168 $f->{http_start}('/SayHello');
|
|
169 $f->{data}('Hello');
|
|
170 $frames = $f->{http_end}();
|
|
171 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
|
|
172 cmp_ok($frame->{headers}{'x-connection'}, '>', $c, 'response 2 - connection');
|
|
173
|
|
174 # upstream keepalive
|
|
175
|
|
176 $f->{http_start}('/KeepAlive');
|
|
177 $f->{data}('Hello');
|
|
178 $frames = $f->{http_end}();
|
|
179 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
|
|
180 ok($c = $frame->{headers}{'x-connection'}, 'keepalive - connection');
|
|
181
|
|
182 TODO: {
|
|
183 local $TODO = 'not yet' if $^O eq 'MSWin32';
|
|
184
|
|
185 $f->{http_start}('/KeepAlive');
|
|
186 $f->{data}('Hello');
|
|
187 $frames = $f->{http_end}();
|
|
188 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
|
|
189 is($frame->{headers}{'x-connection'}, $c, 'keepalive - connection reuse');
|
|
190
|
|
191 }
|
|
192
|
|
193 ###############################################################################
|
|
194
|
|
195 sub grpc {
|
|
196 my ($server, $client, $f, $s, $c, $sid, $uri);
|
|
197
|
|
198 $server = IO::Socket::INET->new(
|
|
199 Proto => 'tcp',
|
|
200 LocalHost => '127.0.0.1',
|
|
201 LocalPort => $p,
|
|
202 Listen => 5,
|
|
203 Reuse => 1
|
|
204 )
|
|
205 or die "Can't create listening socket: $!\n";
|
|
206
|
|
207 $f->{http_start} = sub {
|
|
208 ($uri, my %extra) = @_;
|
|
209 my $body_more = 1 if $uri !~ /LongHeader/;
|
|
210 $s = Test::Nginx::HTTP2->new() if !defined $s;
|
|
211 $s->new_stream({ body_more => $body_more, headers => [
|
|
212 { name => ':method', value => 'POST', mode => 0 },
|
|
213 { name => ':scheme', value => 'http', mode => 0 },
|
|
214 { name => ':path', value => $uri, },
|
|
215 { name => ':authority', value => 'localhost' },
|
|
216 { name => 'content-type', value => 'application/grpc' },
|
|
217 { name => 'te', value => 'trailers', mode => 2 }]});
|
|
218
|
|
219 if (!$extra{reuse}) {
|
|
220 $client = $server->accept() or return;
|
|
221 log2c("(new connection $client)");
|
|
222
|
|
223 $client->sysread(my $buf, 24) == 24 or return; # preface
|
|
224
|
|
225 $c = Test::Nginx::HTTP2->new(1, socket => $client,
|
|
226 pure => 1, preface => "") or return;
|
|
227 }
|
|
228
|
|
229 my $frames = $c->read(all => [{ fin => 4 }]);
|
|
230
|
|
231 if (!$extra{reuse}) {
|
|
232 $c->h2_settings(0);
|
|
233 $c->h2_settings(1);
|
|
234 }
|
|
235
|
|
236 my ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
|
|
237 $sid = $frame->{sid};
|
|
238 return $frames;
|
|
239 };
|
|
240 $f->{data} = sub {
|
|
241 my ($body, %extra) = @_;
|
|
242 $s->h2_body($body, { %extra });
|
|
243 return $c->read(all => [{ sid => $sid,
|
|
244 length => length($body) }]);
|
|
245 };
|
|
246 $f->{http_end} = sub {
|
|
247 $c->new_stream({ body_more => 1, headers => [
|
|
248 { name => ':status', value => '200', mode => 0 },
|
|
249 { name => 'content-type', value => 'application/grpc',
|
|
250 mode => 1, huff => 1 },
|
|
251 ]}, $sid);
|
|
252 $c->h2_body('Hello world', { body_more => 1 });
|
|
253 $c->new_stream({ headers => [
|
|
254 { name => 'grpc-status', value => '0',
|
|
255 mode => 2, huff => 1 },
|
|
256 { name => 'grpc-message', value => '',
|
|
257 mode => 2, huff => 1 },
|
|
258 ]}, $sid);
|
|
259
|
|
260 return $s->read(all => [{ fin => 1 }]);
|
|
261 };
|
|
262 return $f;
|
|
263 }
|
|
264
|
|
265 sub log2i { Test::Nginx::log_core('|| <<', @_); }
|
|
266 sub log2o { Test::Nginx::log_core('|| >>', @_); }
|
|
267 sub log2c { Test::Nginx::log_core('||', @_); }
|
|
268
|
|
269 ###############################################################################
|