1303
|
1 #!/usr/bin/perl
|
|
2
|
|
3 # (C) Sergey Kandaurov
|
|
4 # (C) Nginx, Inc.
|
|
5
|
|
6 # Tests for grpc backend.
|
|
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/);
|
|
28
|
|
29 $t->write_file_expand('nginx.conf', <<'EOF');
|
|
30
|
|
31 %%TEST_GLOBALS%%
|
|
32
|
|
33 daemon off;
|
|
34
|
|
35 events {
|
|
36 }
|
|
37
|
|
38 http {
|
|
39 %%TEST_GLOBALS_HTTP%%
|
|
40
|
|
41 upstream u {
|
|
42 server 127.0.0.1:8081;
|
|
43 keepalive 1;
|
|
44 }
|
|
45
|
|
46 server {
|
|
47 listen 127.0.0.1:8080 http2;
|
|
48 server_name localhost;
|
|
49
|
|
50 http2_max_field_size 128k;
|
|
51 http2_max_header_size 128k;
|
|
52 http2_body_preread_size 128k;
|
|
53
|
|
54 location / {
|
|
55 grpc_pass grpc://127.0.0.1:8081;
|
|
56
|
|
57 if ($arg_if) {
|
|
58 # nothing
|
|
59 }
|
|
60
|
|
61 limit_except GET {
|
|
62 # nothing
|
|
63 }
|
|
64 }
|
|
65
|
|
66 location /KeepAlive {
|
|
67 grpc_pass u;
|
|
68 }
|
|
69
|
|
70 location /LongHeader {
|
|
71 grpc_pass 127.0.0.1:8081;
|
|
72 grpc_set_header X-LongHeader $arg_h;
|
|
73 }
|
|
74
|
|
75 location /LongField {
|
|
76 grpc_pass 127.0.0.1:8081;
|
|
77 grpc_buffer_size 65k;
|
|
78 }
|
|
79
|
|
80 location /SetHost {
|
|
81 grpc_pass 127.0.0.1:8081;
|
|
82 grpc_set_header Host custom;
|
|
83 }
|
|
84
|
|
85 location /SetArgs {
|
|
86 grpc_pass 127.0.0.1:8081;
|
|
87 set $args $arg_c;
|
|
88 }
|
|
89 }
|
|
90 }
|
|
91
|
|
92 EOF
|
|
93
|
|
94 $t->try_run('no grpc')->plan(97);
|
|
95
|
|
96 ###############################################################################
|
|
97
|
|
98 my $p = port(8081);
|
|
99 my $f = grpc();
|
|
100
|
|
101 my $frames = $f->{http_start}('/SayHello');
|
|
102 my ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
|
|
103 is($frame->{flags}, 4, 'request - HEADERS flags');
|
|
104 ok((my $sid = $frame->{sid}) % 2, 'request - HEADERS sid odd');
|
|
105 is($frame->{headers}{':method'}, 'POST', 'request - method');
|
|
106 is($frame->{headers}{':scheme'}, 'http', 'request - scheme');
|
|
107 is($frame->{headers}{':path'}, '/SayHello', 'request - path');
|
|
108 is($frame->{headers}{':authority'}, "127.0.0.1:$p", 'request - authority');
|
|
109 is($frame->{headers}{'content-type'}, 'application/grpc',
|
|
110 'request - content type');
|
|
111 is($frame->{headers}{te}, 'trailers', 'request - te');
|
|
112
|
|
113 $frames = $f->{data}('Hello');
|
|
114 ($frame) = grep { $_->{type} eq "SETTINGS" } @$frames;
|
|
115 is($frame->{flags}, 1, 'request - SETTINGS ack');
|
|
116 is($frame->{sid}, 0, 'request - SETTINGS sid');
|
|
117 is($frame->{length}, 0, 'request - SETTINGS length');
|
|
118
|
|
119 ($frame) = grep { $_->{type} eq "DATA" } @$frames;
|
|
120 is($frame->{data}, 'Hello', 'request - DATA');
|
|
121 is($frame->{length}, 5, 'request - DATA length');
|
|
122 is($frame->{flags}, 1, 'request - DATA flags');
|
|
123 is($frame->{sid}, $sid, 'request - DATA sid match');
|
|
124
|
|
125 $frames = $f->{http_end}();
|
|
126 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
|
|
127 is($frame->{flags}, 4, 'response - HEADERS flags');
|
|
128 is($frame->{sid}, 1, 'response - HEADERS sid');
|
|
129 is($frame->{headers}{':status'}, '200', 'response - status');
|
|
130 is($frame->{headers}{'content-type'}, 'application/grpc',
|
|
131 'response - content type');
|
|
132 ok($frame->{headers}{server}, 'response - server');
|
|
133 ok($frame->{headers}{date}, 'response - date');
|
|
134 ok(my $c = $frame->{headers}{'x-connection'}, 'response - connection');
|
|
135
|
|
136 ($frame) = grep { $_->{type} eq "DATA" } @$frames;
|
|
137 is($frame->{data}, 'Hello world', 'response - DATA');
|
|
138 is($frame->{length}, 11, 'response - DATA length');
|
|
139 is($frame->{flags}, 0, 'response - DATA flags');
|
|
140 is($frame->{sid}, 1, 'response - DATA sid');
|
|
141
|
|
142 (undef, $frame) = grep { $_->{type} eq "HEADERS" } @$frames;
|
|
143 is($frame->{flags}, 5, 'response - trailers flags');
|
|
144 is($frame->{sid}, 1, 'response - trailers sid');
|
|
145 is($frame->{headers}{'grpc-message'}, '', 'response - trailers message');
|
|
146 is($frame->{headers}{'grpc-status'}, '0', 'response - trailers status');
|
|
147
|
|
148 # next request is on a new backend connection, no sid incremented
|
|
149
|
|
150 $frames = $f->{http_start}('/SayHello');
|
|
151 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
|
|
152 is($frame->{sid}, $sid, 'request 2 - HEADERS sid again');
|
|
153 $f->{data}('Hello');
|
|
154 $frames = $f->{http_end}();
|
|
155 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
|
|
156 cmp_ok($frame->{headers}{'x-connection'}, '>', $c, 'response 2 - connection');
|
|
157
|
|
158 # upstream keepalive
|
|
159
|
|
160 $frames = $f->{http_start}('/KeepAlive');
|
|
161 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
|
|
162 is($frame->{sid}, $sid, 'keepalive - HEADERS sid');
|
|
163 $f->{data}('Hello');
|
|
164 $frames = $f->{http_end}();
|
|
165 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
|
|
166 ok($c = $frame->{headers}{'x-connection'}, 'keepalive - connection');
|
|
167
|
|
168 $frames = $f->{http_start}('/KeepAlive', reuse => 1);
|
|
169 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
|
|
170 cmp_ok($frame->{sid}, '>', $sid, 'keepalive - HEADERS sid next');
|
|
171 $f->{data}('Hello');
|
|
172 $frames = $f->{http_end}();
|
|
173 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
|
|
174 is($frame->{headers}{'x-connection'}, $c, 'keepalive - connection reuse');
|
|
175
|
|
176 # various header compression formats
|
|
177
|
|
178 $f->{http_start}('/SayHello');
|
|
179 $f->{data}('Hello');
|
|
180 $frames = $f->{http_end}(mode => 3);
|
|
181 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
|
|
182 is($frame->{headers}{':status'}, '200', 'without indexing');
|
|
183 is($frame->{headers}{'content-type'}, 'application/grpc',
|
|
184 'without indexing 2');
|
|
185
|
|
186 $f->{http_start}('/SayHello');
|
|
187 $f->{data}('Hello');
|
|
188 $frames = $f->{http_end}(mode => 4);
|
|
189 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
|
|
190 is($frame->{headers}{':status'}, '200', 'without indexing new');
|
|
191 is($frame->{headers}{'content-type'}, 'application/grpc',
|
|
192 'without indexing new 2');
|
|
193
|
|
194 $f->{http_start}('/SayHello');
|
|
195 $f->{data}('Hello');
|
|
196 $frames = $f->{http_end}(mode => 5);
|
|
197 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
|
|
198 is($frame->{headers}{':status'}, '200', 'never indexed');
|
|
199 is($frame->{headers}{'content-type'}, 'application/grpc',
|
|
200 'never indexed 2');
|
|
201
|
|
202 $f->{http_start}('/SayHello');
|
|
203 $f->{data}('Hello');
|
|
204 $frames = $f->{http_end}(mode => 6);
|
|
205 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
|
|
206 is($frame->{headers}{':status'}, '200', 'never indexed new');
|
|
207 is($frame->{headers}{'content-type'}, 'application/grpc',
|
|
208 'never indexed new 2');
|
|
209
|
|
210 # padding & priority
|
|
211
|
|
212 $f->{http_start}('/SayHello');
|
|
213 $f->{data}('Hello');
|
|
214 $frames = $f->{http_end}(padding => 7);
|
|
215 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
|
|
216 is($frame->{headers}{':status'}, '200', 'padding');
|
|
217
|
|
218 $f->{http_start}('/SayHello');
|
|
219 $f->{data}('Hello');
|
|
220 $frames = $f->{http_end}(prio => 137, dep => 0x01020304);
|
|
221 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
|
|
222 is($frame->{headers}{':status'}, '200', 'priority');
|
|
223
|
|
224 $f->{http_start}('/SayHello');
|
|
225 $f->{data}('Hello');
|
|
226 $frames = $f->{http_end}(padding => 7, prio => 137, dep => 0x01020304);
|
|
227 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
|
|
228 is($frame->{headers}{':status'}, '200', 'padding priority');
|
|
229
|
|
230 SKIP: {
|
|
231 skip 'long test', 1 unless $ENV{TEST_NGINX_UNSAFE};
|
|
232
|
|
233 $f->{http_start}('/SaySplit');
|
|
234 $f->{data}('Hello');
|
|
235 $frames = $f->{http_end}(padding => 7, prio => 137, dep => 0x01020304,
|
|
236 split => [(map{1}(1..20)), 30], split_delay => 0.1);
|
|
237 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
|
|
238 is($frame->{headers}{':status'}, '200', 'padding priority split');
|
|
239
|
|
240 }
|
|
241
|
|
242 # grpc error, no empty data frame expected
|
|
243
|
|
244 $f->{http_start}('/SayHello');
|
|
245 $f->{data}('Hello');
|
|
246 $frames = $f->{http_err}();
|
|
247 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
|
|
248 is($frame->{flags}, 5, 'grpc error - HEADERS flags');
|
|
249 ($frame) = grep { $_->{type} eq "DATA" } @$frames;
|
|
250 ok(!$frame, 'grpc error - no DATA frame');
|
|
251
|
|
252 # continuation from backend, expect parts assembled
|
|
253
|
|
254 $f->{http_start}('/SayHello');
|
|
255 $f->{data}('Hello');
|
|
256 $frames = $f->{continuation}();
|
|
257 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
|
|
258 is($frame->{flags}, 4, 'continuation - HEADERS flags');
|
|
259 is($frame->{headers}{':status'}, '200', 'continuation - status');
|
|
260 is($frame->{headers}{'content-type'}, 'application/grpc',
|
|
261 'continuation - content type');
|
|
262
|
|
263 ($frame) = grep { $_->{type} eq "DATA" } @$frames;
|
|
264 is($frame->{data}, 'Hello world', 'continuation - DATA');
|
|
265 is($frame->{length}, 11, 'continuation - DATA length');
|
|
266 is($frame->{flags}, 0, 'continuation - DATA flags');
|
|
267
|
|
268 (undef, $frame) = grep { $_->{type} eq "HEADERS" } @$frames;
|
|
269 is($frame->{flags}, 5, 'continuation - trailers flags');
|
|
270 is($frame->{headers}{'grpc-message'}, '', 'continuation - trailers message');
|
|
271 is($frame->{headers}{'grpc-status'}, '0', 'continuation - trailers status');
|
|
272
|
|
273 # continuation from backend, header split
|
|
274
|
|
275 $f->{http_start}('/SayHello');
|
|
276 $f->{data}('Hello');
|
|
277 $frames = $f->{http_end}(mode => 6, continuation => [map { 1 } (1 .. 42)]);
|
|
278 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
|
|
279 is($frame->{headers}{':status'}, '200', 'continuation - header split');
|
|
280
|
|
281 # continuation to backend
|
|
282
|
|
283 $frames = $f->{http_start}('/LongHeader?h=' . ('Z' x 31337));
|
|
284 @$frames = grep { $_->{type} =~ "HEADERS|CONTINUATION" } @$frames;
|
|
285 is(@$frames, 4, 'continuation - frames');
|
|
286
|
|
287 $frame = shift @$frames;
|
|
288 is($frame->{type}, 'HEADERS', 'continuation - HEADERS');
|
|
289 is($frame->{length}, 16384, 'continuation - HEADERS length');
|
|
290 is($frame->{flags}, 1, 'continuation - HEADERS flags');
|
|
291 ok($frame->{sid}, 'continuation - HEADERS sid');
|
|
292
|
|
293 $frame = shift @$frames;
|
|
294 is($frame->{type}, 'CONTINUATION', 'continuation - CONTINUATION');
|
|
295 is($frame->{length}, 16384, 'continuation - CONTINUATION length');
|
|
296 is($frame->{flags}, 0, 'continuation - CONTINUATION flags');
|
|
297 ok($frame->{sid}, 'continuation - CONTINUATION sid');
|
|
298
|
|
299 $frame = shift @$frames;
|
|
300 is($frame->{type}, 'CONTINUATION', 'continuation - CONTINUATION 2');
|
|
301 is($frame->{length}, 16384, 'continuation - CONTINUATION 2 length');
|
|
302 is($frame->{flags}, 0, 'continuation - CONTINUATION 2 flags');
|
|
303
|
|
304 $frame = shift @$frames;
|
|
305 is($frame->{type}, 'CONTINUATION', 'continuation - CONTINUATION n');
|
|
306 cmp_ok($frame->{length}, '<', 16384, 'continuation - CONTINUATION n length');
|
|
307 is($frame->{flags}, 4, 'continuation - CONTINUATION n flags');
|
|
308 is($frame->{headers}{':path'}, '/LongHeader?h=' . 'Z' x 31337,
|
|
309 'continuation - path');
|
|
310 is($frame->{headers}{'x-longheader'}, 'Z' x 31337, 'continuation - header');
|
|
311
|
|
312 $f->{http_end}();
|
|
313
|
|
314 # long header field
|
|
315
|
|
316 $f->{http_start}('/LongField');
|
|
317 $f->{data}('Hello');
|
|
318 $frames = $f->{field_len}(2**7);
|
|
319 ($frame) = grep { $_->{flags} & 0x4 } @$frames;
|
|
320 is($frame->{headers}{'x' x 2**7}, 'y' x 2**7, 'long header field 1');
|
|
321
|
|
322 $f->{http_start}('/LongField');
|
|
323 $f->{data}('Hello');
|
|
324 $frames = $f->{field_len}(2**8);
|
|
325 ($frame) = grep { $_->{flags} & 0x4 } @$frames;
|
|
326 is($frame->{headers}{'x' x 2**8}, 'y' x 2**8, 'long header field 2');
|
|
327
|
|
328 $f->{http_start}('/LongField');
|
|
329 $f->{data}('Hello');
|
|
330 $frames = $f->{field_len}(2**15);
|
|
331 ($frame) = grep { $_->{flags} & 0x4 } @$frames;
|
|
332 is($frame->{headers}{'x' x 2**15}, 'y' x 2**15, 'long header field 3');
|
|
333
|
|
334 # flow control
|
|
335
|
|
336 $f->{http_start}('/FlowControl');
|
|
337 $frames = $f->{data_len}(('Hello' x 13000) . ('x' x 550), 65535);
|
|
338 my $sum = eval join '+', map { $_->{type} eq "DATA" && $_->{length} } @$frames;
|
|
339 is($sum, 65535, 'flow control - iws length');
|
|
340
|
|
341 $f->{update}(10);
|
|
342 $f->{update_sid}(10);
|
|
343
|
|
344 $frames = $f->{data_len}(undef, 10);
|
|
345 ($frame) = grep { $_->{type} eq "DATA" } @$frames;
|
|
346 is($frame->{length}, 10, 'flow control - update length');
|
|
347 is($frame->{flags}, 0, 'flow control - update flags');
|
|
348
|
|
349 $f->{update_sid}(10);
|
|
350 $f->{update}(10);
|
|
351
|
|
352 $frames = $f->{data_len}(undef, 5);
|
|
353 ($frame) = grep { $_->{type} eq "DATA" } @$frames;
|
|
354 is($frame->{length}, 5, 'flow control - rest length');
|
|
355 is($frame->{flags}, 1, 'flow control - rest flags');
|
|
356
|
|
357 $f->{http_end}();
|
|
358
|
|
359 # DATA padding
|
|
360
|
|
361 $f->{http_start}('/SayPadding');
|
|
362 $f->{data}('Hello');
|
|
363 $frames = $f->{http_end}(body_padding => 42);
|
|
364 ($frame) = grep { $_->{type} eq "DATA" } @$frames;
|
|
365 is($frame->{data}, 'Hello world', 'response - DATA');
|
|
366 is($frame->{length}, 11, 'response - DATA length');
|
|
367 is($frame->{flags}, 0, 'response - DATA flags');
|
|
368
|
|
369 # :authority inheritance
|
|
370
|
|
371 $frames = $f->{http_start}('/SayHello?if=1');
|
|
372 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
|
|
373 is($frame->{headers}{':authority'}, "127.0.0.1:$p", 'authority in if');
|
|
374 $f->{data}('Hello');
|
|
375 $f->{http_end}();
|
|
376
|
|
377 # misc tests
|
|
378
|
|
379 $frames = $f->{http_start}('/SetHost');
|
|
380 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
|
|
381 ok(!$frame->{headers}{':authority'}, 'set host - authority');
|
|
382 is($frame->{headers}{'host'}, 'custom', 'set host - host');
|
|
383 $f->{data}('Hello');
|
|
384 $f->{http_end}();
|
|
385
|
|
386 $frames = $f->{http_start}('/SetArgs?f');
|
|
387 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
|
|
388 is($frame->{headers}{':path'}, '/SetArgs', 'set args');
|
|
389 $f->{data}('Hello');
|
|
390 $f->{http_end}();
|
|
391
|
|
392 $frames = $f->{http_start}('/SetArgs?c=1');
|
|
393 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
|
|
394 is($frame->{headers}{':path'}, '/SetArgs?1', 'set args len');
|
|
395 $f->{data}('Hello');
|
|
396 $f->{http_end}();
|
|
397
|
|
398 $frames = $f->{http_start}('/SetArgs esc');
|
|
399 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
|
|
400 is($frame->{headers}{':path'}, '/SetArgs%20esc', 'uri escape');
|
|
401 $f->{data}('Hello');
|
|
402 $f->{http_end}();
|
|
403
|
|
404 $frames = $f->{http_start}('/');
|
|
405 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
|
|
406 is($frame->{headers}{':path'}, '/', 'root index');
|
|
407 $f->{data}('Hello');
|
|
408 $f->{http_end}();
|
|
409
|
|
410 $frames = $f->{http_start}('/', method => 'GET');
|
|
411 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
|
|
412 is($frame->{headers}{':method'}, 'GET', 'method get');
|
|
413 $f->{data}('Hello');
|
|
414 $f->{http_end}();
|
|
415
|
|
416 $frames = $f->{http_start}('/', method => 'HEAD');
|
|
417 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
|
|
418 is($frame->{headers}{':method'}, 'HEAD', 'method head');
|
|
419 $f->{data}('Hello');
|
|
420 $f->{http_end}();
|
|
421
|
|
422 ###############################################################################
|
|
423
|
|
424 sub grpc {
|
|
425 my ($server, $client, $f, $s, $c, $sid, $uri);
|
|
426 my $n = 0;
|
|
427
|
|
428 $server = IO::Socket::INET->new(
|
|
429 Proto => 'tcp',
|
|
430 LocalHost => '127.0.0.1',
|
|
431 LocalPort => $p,
|
|
432 Listen => 5,
|
|
433 Reuse => 1
|
|
434 )
|
|
435 or die "Can't create listening socket: $!\n";
|
|
436
|
|
437 $f->{http_start} = sub {
|
|
438 ($uri, my %extra) = @_;
|
|
439 my $body_more = 1 if $uri !~ /LongHeader/;
|
|
440 my $meth = $extra{method} || 'POST';
|
|
441 $s = Test::Nginx::HTTP2->new() if !defined $s;
|
|
442 $s->new_stream({ body_more => $body_more, headers => [
|
|
443 { name => ':method', value => $meth, mode => !!$meth },
|
|
444 { name => ':scheme', value => 'http', mode => 0 },
|
|
445 { name => ':path', value => $uri, },
|
|
446 { name => ':authority', value => 'localhost' },
|
|
447 { name => 'content-type', value => 'application/grpc' },
|
|
448 { name => 'te', value => 'trailers', mode => 2 }]});
|
|
449
|
|
450 if (!$extra{reuse}) {
|
|
451 $client = $server->accept() or return;
|
|
452 log2c("(new connection $client)");
|
|
453 $n++;
|
|
454
|
|
455 $client->sysread(my $buf, 24) == 24 or return; # preface
|
|
456
|
|
457 $c = Test::Nginx::HTTP2->new(1, socket => $client,
|
|
458 pure => 1, preface => "") or return;
|
|
459 }
|
|
460
|
|
461 my $frames = $c->read(all => [{ fin => 4 }]);
|
|
462
|
|
463 if (!$extra{reuse}) {
|
|
464 $c->h2_settings(0);
|
|
465 $c->h2_settings(1);
|
|
466 }
|
|
467
|
|
468 my ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
|
|
469 $sid = $frame->{sid};
|
|
470 return $frames;
|
|
471 };
|
|
472 $f->{data} = sub {
|
|
473 my ($body, %extra) = @_;
|
|
474 $s->h2_body($body, { %extra });
|
|
475 return $c->read(all => [{ sid => $sid,
|
|
476 length => length($body) }]);
|
|
477 };
|
|
478 $f->{data_len} = sub {
|
|
479 my ($body, $len) = @_;
|
|
480 $s->h2_body($body) if defined $body;
|
|
481 return $c->read(all => [{ sid => $sid, length => $len }]);
|
|
482 };
|
|
483 $f->{update} = sub {
|
|
484 $c->h2_window(shift);
|
|
485 };
|
|
486 $f->{update_sid} = sub {
|
|
487 $c->h2_window(shift, $sid);
|
|
488 };
|
|
489 $f->{http_end} = sub {
|
|
490 my (%extra) = @_;
|
|
491 $c->new_stream({ body_more => 1, %extra, headers => [
|
|
492 { name => ':status', value => '200',
|
|
493 mode => $extra{mode} || 0 },
|
|
494 { name => 'content-type', value => 'application/grpc',
|
|
495 mode => $extra{mode} || 1, huff => 1 },
|
|
496 { name => 'x-connection', value => $n,
|
|
497 mode => 2, huff => 1 },
|
|
498 ]}, $sid);
|
|
499 $c->h2_body('Hello world', { body_more => 1,
|
|
500 body_padding => $extra{body_padding} });
|
|
501 $c->new_stream({ headers => [
|
|
502 { name => 'grpc-status', value => '0',
|
|
503 mode => 2, huff => 1 },
|
|
504 { name => 'grpc-message', value => '',
|
|
505 mode => 2, huff => 1 },
|
|
506 ]}, $sid);
|
|
507
|
|
508 return $s->read(all => [{ fin => 1 }]);
|
|
509 };
|
|
510 $f->{http_err} = sub {
|
|
511 $c->new_stream({ headers => [
|
|
512 { name => ':status', value => '200', mode => 0 },
|
|
513 { name => 'content-type', value => 'application/grpc',
|
|
514 mode => 1, huff => 1 },
|
|
515 { name => 'grpc-status', value => '12',
|
|
516 mode => 2, huff => 1 },
|
|
517 { name => 'grpc-message', value => 'unknown service',
|
|
518 mode => 2, huff => 1 },
|
|
519 ]}, $sid);
|
|
520
|
|
521 return $s->read(all => [{ fin => 1 }]);
|
|
522 };
|
|
523 $f->{continuation} = sub {
|
|
524 $c->new_stream({ continuation => 1, body_more => 1, headers => [
|
|
525 { name => ':status', value => '200', mode => 0 },
|
|
526 ]}, $sid);
|
|
527 $c->h2_continue($sid, { continuation => 1, headers => [
|
|
528 { name => 'content-type', value => 'application/grpc',
|
|
529 mode => 1, huff => 1 },
|
|
530 ]});
|
|
531 $c->h2_continue($sid, { headers => [
|
|
532 # an empty CONTINUATION frame is legitimate
|
|
533 ]});
|
|
534 $c->h2_body('Hello world', { body_more => 1 });
|
|
535 $c->new_stream({ continuation => 1, headers => [
|
|
536 { name => 'grpc-status', value => '0',
|
|
537 mode => 2, huff => 1 },
|
|
538 ]}, $sid);
|
|
539 $c->h2_continue($sid, { headers => [
|
|
540 { name => 'grpc-message', value => '',
|
|
541 mode => 2, huff => 1 },
|
|
542 ]});
|
|
543
|
|
544 return $s->read(all => [{ fin => 1 }]);
|
|
545 };
|
|
546 $f->{field_len} = sub {
|
|
547 my ($len) = @_;
|
|
548 $c->new_stream({ continuation => [map {2**14} (0..$len/2**13)],
|
|
549 body_more => 1, headers => [
|
|
550 { name => ':status', value => '200', mode => 0 },
|
|
551 { name => 'content-type', value => 'application/grpc',
|
|
552 mode => 1, huff => 1 },
|
|
553 { name => 'x' x $len, value => 'y' x $len, mode => 6 },
|
|
554 ]}, $sid);
|
|
555 $c->h2_body('Hello world', { body_more => 1 });
|
|
556 $c->new_stream({ headers => [
|
|
557 { name => 'grpc-status', value => '0',
|
|
558 mode => 2, huff => 1 },
|
|
559 { name => 'grpc-message', value => '',
|
|
560 mode => 2, huff => 1 },
|
|
561 ]}, $sid);
|
|
562
|
|
563 return $s->read(all => [{ fin => 1 }]);
|
|
564 };
|
|
565 return $f;
|
|
566 }
|
|
567
|
|
568 sub log2i { Test::Nginx::log_core('|| <<', @_); }
|
|
569 sub log2o { Test::Nginx::log_core('|| >>', @_); }
|
|
570 sub log2c { Test::Nginx::log_core('||', @_); }
|
|
571
|
|
572 ###############################################################################
|