comparison h2_server_push.t @ 1279:da2ad1d72bb6

Tests: HTTP/2 server push.
author Sergey Kandaurov <pluknet@nginx.com>
date Thu, 08 Feb 2018 21:52:23 +0300
parents
children 8a88c0d6f80a
comparison
equal deleted inserted replaced
1278:6d065c9a3b1f 1279:da2ad1d72bb6
1 #!/usr/bin/perl
2
3 # (C) Sergey Kandaurov
4 # (C) Nginx, Inc.
5
6 # Tests for HTTP/2 server push.
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 http_v2 proxy rewrite/)
27 ->write_file_expand('nginx.conf', <<'EOF');
28
29 %%TEST_GLOBALS%%
30
31 daemon off;
32
33 events {
34 }
35
36 http {
37 %%TEST_GLOBALS_HTTP%%
38
39 server {
40 listen 127.0.0.1:8080 http2;
41 listen 127.0.0.1:8081;
42 server_name localhost;
43
44 location /prio {
45 http2_push /t1;
46 http2_push /t2;
47 return 204;
48 }
49
50 location /expl {
51 http2_push /push;
52 http2_push /push2;
53
54 location /expl/off {
55 http2_push off;
56 }
57 }
58
59 location /preload {
60 http2_push_preload on;
61 add_header Link "</push>; rel=preload";
62 add_header X-Link $sent_http_link;
63 return 200 SEE-THIS;
64 }
65
66 location /preload2 {
67 http2_push_preload on;
68 add_header Link "</push>; rel=preload"; # valid
69 add_header Link "</push2 >; rel=preload"; # valid
70 add_header Link "</push3>; rel=preloadX"; # not
71 add_header Link '</push4>; rel="preload"'; # valid
72 add_header Link '</push5>; rel="preloadX"'; # not
73 add_header Link "</push6>; rel=preload; nopush"; # not
74 add_header Link '</push7>; rel="foo"'; # not
75 add_header Link '</push7>; rel="foo preload"'; # valid
76 return 200 SEE-THIS;
77 }
78
79 location /preload/many {
80 http2_push_preload on;
81 add_header Link "</push>; rel=preload, </push2>; rel=preload";
82 add_header Link "</push3>, </push4>; rel=preload";
83 return 200 SEE-THIS;
84 }
85
86 location /preload/proxy {
87 http2_push_preload on;
88 proxy_pass http://127.0.0.1:8081/proxied;
89 }
90
91 location /proxied {
92 add_header Link "</push>; rel=preload";
93 add_header Link "</push2>; rel=preload";
94 return 200 SEE-THIS;
95 }
96
97 location /both {
98 http2_push /push;
99 http2_push_preload on;
100 add_header Link "</push>; rel=preload";
101 return 200 SEE-THIS;
102 }
103
104 location /arg {
105 http2_push $arg_push;
106 return 204;
107 }
108
109 location /push {
110 return 200 PROMISED;
111 }
112 }
113
114 server {
115 listen 127.0.0.1:8082 http2;
116 server_name max_pushes;
117
118 http2_max_concurrent_pushes 2;
119 http2_push /push;
120 http2_push /push;
121 http2_push /push;
122 }
123 }
124
125 EOF
126
127 $t->write_file('t1', join('', map { sprintf "X%04dXXX", $_ } (1 .. 8202)));
128 $t->write_file('t2', 'SEE-THIS');
129 $t->write_file('explf', join('', map { sprintf "X%06dXXX", $_ } (1 .. 6553)));
130
131 $t->try_run('no http2_push')->plan(38);
132
133 ###############################################################################
134
135 # preload & format
136
137 my $s = Test::Nginx::HTTP2->new();
138 my $sid = $s->new_stream({ path => '/preload' });
139 my $frames = $s->read(all => [{ sid => 1, fin => 1 }, { sid => 2, fin => 1 }]);
140
141 my ($frame) = grep { $_->{type} eq "PUSH_PROMISE" } @$frames;
142 ok($frame, 'push promise');
143 is($frame->{headers}->{':authority'}, 'localhost', 'authority');
144 is($frame->{headers}->{':scheme'}, 'http', 'scheme');
145 is($frame->{headers}->{':method'}, 'GET', 'method');
146 is($frame->{headers}->{':path'}, '/push', 'path');
147 is($frame->{flags}, 4, 'flags');
148 is($frame->{promised}, 2, 'promised stream');
149
150 ($frame) = grep { $_->{type} eq "DATA" && $_->{sid} eq 2 } @$frames;
151 is($frame->{data}, 'PROMISED', 'promised stream payload');
152
153 ($frame) = grep { $_->{type} eq "HEADERS" && $_->{sid} eq $sid } @$frames;
154 is($frame->{headers}->{'x-link'}, '</push>; rel=preload', 'sent_http_link');
155
156 $s = Test::Nginx::HTTP2->new();
157 $sid = $s->new_stream({ path => '/preload2' });
158 $frames = $s->read(all => [{ sid => 8, fin => 1 }], wait => 0.5);
159 is(grep({ $_->{type} eq "PUSH_PROMISE" } @$frames), 4, 'preload 2');
160
161 $s = Test::Nginx::HTTP2->new();
162 $sid = $s->new_stream({ path => '/preload/many' });
163 $frames = $s->read(all => [{ sid => 8, fin => 1 }], wait => 0.5);
164 is(grep({ $_->{type} eq "PUSH_PROMISE" } @$frames), 3, 'preload many');
165
166 # preload proxy
167
168 $s = Test::Nginx::HTTP2->new();
169 $sid = $s->new_stream({ path => '/preload/proxy' });
170 $frames = $s->read(all => [{ sid => 8, fin => 1 }], wait => 0.5);
171 is(grep({ $_->{type} eq "PUSH_PROMISE" } @$frames), 2, 'preload proxy');
172
173 # both h2_push & preload
174
175 $s = Test::Nginx::HTTP2->new();
176 $sid = $s->new_stream({ path => '/both' });
177 $frames = $s->read(all => [{ sid => 8, fin => 1 }], wait => 0.5);
178 is(grep({ $_->{type} eq "PUSH_PROMISE" } @$frames), 2, 'preload proxy');
179
180 # h2_push
181
182 $s = Test::Nginx::HTTP2->new();
183 $sid = $s->new_stream({ path => '/expl' });
184 $frames = $s->read(all => [{ sid => 1, fin => 1 }, { sid => 2, fin => 1 }]);
185
186 ($frame) = grep { $_->{type} eq "PUSH_PROMISE" } @$frames;
187 ok($frame, 'PUSH_PROMISE');
188
189 # h2_push off
190
191 $s = Test::Nginx::HTTP2->new();
192 $sid = $s->new_stream({ path => '/expl/off' });
193 $frames = $s->read(all => [{ type => 'PUSH_PROMISE' }], wait => 0.2);
194
195 ($frame) = grep { $_->{type} eq "PUSH_PROMISE" } @$frames;
196 ok(!$frame, 'h2_push off');
197
198 # h2_push $var
199
200 $s = Test::Nginx::HTTP2->new();
201 $sid = $s->new_stream({ path => '/arg?push=/push' });
202 $frames = $s->read(all => [{ type => 'PUSH_PROMISE' }], wait => 0.2);
203 ($frame) = grep { $_->{type} eq "PUSH_PROMISE" } @$frames;
204 ok($frame, 'h2_push variable');
205
206 $sid = $s->new_stream({ path => '/arg?push=' });
207 $frames = $s->read(all => [{ type => 'PUSH_PROMISE' }], wait => 0.2);
208 ($frame) = grep { $_->{type} eq "PUSH_PROMISE" } @$frames;
209 ok(!$frame, 'h2_push variable empty');
210
211 $sid = $s->new_stream({ path => '/arg?push=off' });
212 $frames = $s->read(all => [{ type => 'PUSH_PROMISE' }], wait => 0.2);
213 ($frame) = grep { $_->{type} eq "PUSH_PROMISE" } @$frames;
214 ok(!$frame, 'h2_push variable off');
215
216 $sid = $s->new_stream({ path => '/arg?push=foo' });
217 $frames = $s->read(all => [{ type => 'PUSH_PROMISE' }], wait => 0.2);
218 ($frame) = grep { $_->{type} eq "PUSH_PROMISE" } @$frames;
219 ok(!$frame, 'h2_push variable relative path');
220
221 # SETTINGS_ENABLE_PUSH
222
223 $s = Test::Nginx::HTTP2->new();
224 $s->h2_settings(0, 0x2 => 0);
225 $sid = $s->new_stream({ path => '/expl' });
226 $frames = $s->read(all => [{ type => 'PUSH_PROMISE' }], wait => 0.2);
227
228 ($frame) = grep { $_->{type} eq "PUSH_PROMISE" } @$frames;
229 ok(!$frame, 'push setting disabled');
230
231 $s->h2_settings(0, 0x2 => 1);
232 $sid = $s->new_stream({ path => '/expl' });
233 $frames = $s->read(all => [{ sid => $sid, fin => 1 }, { sid => 2, fin => 1 }]);
234
235 ($frame) = grep { $_->{type} eq "PUSH_PROMISE" } @$frames;
236 ok($frame, 'push setting enabled');
237
238 $s->h2_settings(0, 0x2 => 42);
239 $sid = $s->new_stream({ path => '/expl' });
240 $frames = $s->read(all => [{ type => 'PUSH_PROMISE' }]);
241
242 ($frame) = grep { $_->{type} =~ "PUSH_PROMISE" } @$frames;
243 ok(!$frame, 'push setting invalid - no promises');
244 ($frame) = grep { $_->{type} =~ "GOAWAY" } @$frames;
245 is($frame->{'code'}, 1, 'push setting invalid - GOAWAY protocol error');
246 cmp_ok($frame->{'last_sid'}, '<', 5, 'push setting invalid - last sid');
247
248 # SETTINGS_MAX_CONCURRENT_STREAMS
249
250 $s = Test::Nginx::HTTP2->new();
251 $sid = $s->new_stream({ path => '/expl' });
252 $frames = $s->read(all => [
253 { sid => 1, fin => 1 },
254 { sid => 2, fin => 1 },
255 { sid => 4, fin => 1 }]);
256 is(grep({ $_->{type} eq "PUSH_PROMISE" } @$frames), 2, 'max pushes default');
257
258 $s = Test::Nginx::HTTP2->new();
259 $s->h2_settings(0, 0x3 => 1);
260 $sid = $s->new_stream({ path => '/expl' });
261 $frames = $s->read(all => [{ sid => 1, fin => 1 }, { sid => 2, fin => 1 }]);
262 is(grep({ $_->{type} eq "PUSH_PROMISE" } @$frames), 1, 'max pushes limited');
263
264 $s = Test::Nginx::HTTP2->new();
265 $s->h2_settings(0, 0x3 => 0);
266 $sid = $s->new_stream({ path => '/expl' });
267 $frames = $s->read(all => [{ type => 'PUSH_PROMISE' }], wait => 0.2);
268 is(grep({ $_->{type} eq "PUSH_PROMISE" } @$frames), 0, 'max pushes disabled');
269
270 # server push flow control & rst
271
272 $s = Test::Nginx::HTTP2->new();
273 $sid = $s->new_stream({ path => '/explf' });
274 $frames = $s->read(all => [
275 { sid => 1, fin => 1 },
276 { sid => 2, length => 5 },
277 { sid => 4, fin => 4 }]);
278
279 ($frame) = grep { $_->{type} eq "DATA" && $_->{sid} == 2 } @$frames;
280 is($frame->{length}, 5, 'flow control - pushed stream limited');
281 is($frame->{flags}, 0, 'flow control - pushed stream flags');
282
283 ($frame) = grep { $_->{type} eq "DATA" && $_->{sid} == 4 } @$frames;
284 ok(!$frame, 'flow control - no window for next stream');
285
286 # window update
287
288 $s->h2_window(2);
289
290 $frames = $s->read(all => [{ length => 2 }]);
291 ($frame) = grep { $_->{type} eq "DATA" && $_->{sid} == 2 } @$frames;
292 is($frame->{length}, 2, 'window update');
293
294 # client refused stream
295
296 $s->h2_rst(4, 7);
297 $s->h2_window(2**16);
298
299 $frames = $s->read(all => [{ sid => 2, length => 1 }]);
300 push @$frames, @{ $s->read(all => [{ sid => 4, fin => 1 }], wait => 0.5) };
301
302 ($frame) = grep { $_->{type} eq "DATA" && $_->{sid} == 2 } @$frames;
303 is($frame->{length}, 1, 'pushed response flow control');
304 is($frame->{flags}, 1, 'pushed response END_STREAM');
305
306 ($frame) = grep { $_->{type} eq "DATA" && $_->{sid} == 4 } @$frames;
307 ok(!$frame, 'rst pushed stream');
308
309 # priority
310
311 $s = Test::Nginx::HTTP2->new();
312 $sid = $s->new_stream({ path => '/prio' });
313 $frames = $s->read(all => [{ length => 2**16 - 1 }, { sid => 4, fin => 4 }]);
314
315 $s->h2_priority(16, 2, 4);
316
317 $s->h2_window(2**17, 2);
318 $s->h2_window(2**17, 4);
319 $s->h2_window(2**17);
320
321 $frames = $s->read(all => [{ sid => 2, fin => 1 }, { sid => 4, fin => 1 }]);
322 my @data = grep { $_->{type} eq "DATA" } @$frames;
323 is(join(' ', map { $_->{sid} } @data), "4 2", 'priority 1');
324
325 $s = Test::Nginx::HTTP2->new();
326 $sid = $s->new_stream({ path => '/prio' });
327 $frames = $s->read(all => [{ length => 2**16 - 1 }, { sid => 4, fin => 4 }]);
328
329 $s->h2_priority(16, 4, 2);
330
331 $s->h2_window(2**17, 2);
332 $s->h2_window(2**17, 4);
333 $s->h2_window(2**17);
334
335 $frames = $s->read(all => [{ sid => 2, fin => 1 }, { sid => 4, fin => 1 }]);
336 @data = grep { $_->{type} eq "DATA" } @$frames;
337 is(join(' ', map { $_->{sid} } @data), "2 4", 'priority 2');
338
339 # http2_max_concurrent_pushes
340
341 $s = Test::Nginx::HTTP2->new(port(8082));
342 $sid = $s->new_stream({ headers => [
343 { name => ':method', value => 'GET', mode => 0 },
344 { name => ':scheme', value => 'http', mode => 0 },
345 { name => ':path', value => '/', mode => 0 },
346 { name => ':authority', value => 'max_pushes', mode => 1 }]});
347 $frames = $s->read(all => [{ sid => $sid, fin => 1 },
348 { sid => 2, fin => 1 }, { sid => 4, fin => 1 }]);
349 push @$frames, @{ $s->read(all => [{ sid => 6, fin => 1 }], wait => 0.2) };
350 is(grep({ $_->{type} eq "PUSH_PROMISE" } @$frames), 2, 'http2 max pushes lim');
351
352 $s = Test::Nginx::HTTP2->new(port(8082));
353 $s->h2_settings(0, 0x3 => 1);
354 $sid = $s->new_stream({ headers => [
355 { name => ':method', value => 'GET', mode => 0 },
356 { name => ':scheme', value => 'http', mode => 0 },
357 { name => ':path', value => '/', mode => 0 },
358 { name => ':authority', value => 'max_pushes', mode => 1 }]});
359 $frames = $s->read(all => [{ sid => $sid, fin => 1 }, { sid => 2, fin => 1 }]);
360 push @$frames, @{ $s->read(all => [{ sid => 4, fin => 1 }], wait => 0.2) };
361 is(grep({ $_->{type} eq "PUSH_PROMISE" } @$frames), 1, 'http2 max pushes 2');
362
363 ###############################################################################