blob: 482159b034cc8ae5231925488789dff80aacbc5b [file] [log] [blame]
Sergey Kandaurovece20192016-03-23 20:08:20 +03001#!/usr/bin/perl
2
3# (C) Sergey Kandaurov
4# (C) Nginx, Inc.
5
6# Tests for HTTP/2 protocol with unbuffered request body.
7
8###############################################################################
9
10use warnings;
11use strict;
12
13use Test::More;
14
15use Socket qw/ CRLF /;
16
17BEGIN { use FindBin; chdir($FindBin::Bin); }
18
19use lib 'lib';
20use Test::Nginx;
Sergey Kandaurov67599f42016-06-17 11:36:33 +030021use Test::Nginx::HTTP2;
Sergey Kandaurovece20192016-03-23 20:08:20 +030022
23###############################################################################
24
25select STDERR; $| = 1;
26select STDOUT; $| = 1;
27
28my $t = Test::Nginx->new()->has(qw/http http_v2 proxy/);
29
30$t->write_file_expand('nginx.conf', <<'EOF');
31
32%%TEST_GLOBALS%%
33
34daemon off;
35
36events {
37}
38
39http {
40 %%TEST_GLOBALS_HTTP%%
41
42 server {
Andrey Zelenkove59bf362016-07-12 17:39:03 +030043 listen 127.0.0.1:8080 http2;
Sergey Kandaurovece15232016-07-06 15:57:26 +030044 listen 127.0.0.1:8082;
Sergey Kandaurovece20192016-03-23 20:08:20 +030045 server_name localhost;
46
47 location / {
48 proxy_request_buffering off;
Andrey Zelenkove59bf362016-07-12 17:39:03 +030049 proxy_pass http://127.0.0.1:8081/;
Sergey Kandaurovece20192016-03-23 20:08:20 +030050 client_body_buffer_size 1k;
51 }
52 location /chunked {
53 proxy_request_buffering off;
54 proxy_http_version 1.1;
Andrey Zelenkove59bf362016-07-12 17:39:03 +030055 proxy_pass http://127.0.0.1:8081/;
Sergey Kandaurovece20192016-03-23 20:08:20 +030056 client_body_buffer_size 1k;
57 }
Sergey Kandaurovece15232016-07-06 15:57:26 +030058 location /abort {
59 proxy_request_buffering off;
60 proxy_http_version 1.1;
61 proxy_pass http://127.0.0.1:8082/;
62 }
Sergey Kandaurovece20192016-03-23 20:08:20 +030063 }
64}
65
66EOF
67
68$t->run();
Sergey Kandaurovece15232016-07-06 15:57:26 +030069$t->plan(49);
Sergey Kandaurovece20192016-03-23 20:08:20 +030070
71###############################################################################
72
Sergey Kandaurovece20192016-03-23 20:08:20 +030073# unbuffered request body
74
Sergey Kandaurov9473f142017-11-24 19:58:40 +030075my $f = get_body('/', 'content-length' => 10);
Sergey Kandaurovece20192016-03-23 20:08:20 +030076ok($f->{headers}, 'request');
77is($f->{upload}('01234', body_more => 1), '01234', 'part');
78is($f->{upload}('56789'), '56789', 'part 2');
79is($f->{http_end}(), 200, 'response');
80
81$f = get_body('/', 'content-length' => 10);
82ok($f->{headers}, 'much');
83is($f->{upload}('0123456789', body_more => 1), '0123456789', 'much - part');
84is($f->{upload}('many'), '', 'much - part 2');
85is($f->{http_end}(), 400, 'much - response');
86
87$f = get_body('/', 'content-length' => 10);
88ok($f->{headers}, 'less');
89is($f->{upload}('0123', body_more => 1), '0123', 'less - part');
90is($f->{upload}('56789'), '', 'less - part 2');
91is($f->{http_end}(), 400, 'less - response');
92
93$f = get_body('/', 'content-length' => 18);
94ok($f->{headers}, 'many');
95is($f->{upload}('01234many', body_split => [ 5 ], body_more => 1),
96 '01234many', 'many - part');
97is($f->{upload}('56789many', body_split => [ 5 ]),
98 '56789many', 'many - part 2');
99is($f->{http_end}(), 200, 'many - response');
100
101$f = get_body('/', 'content-length' => 0);
102ok($f->{headers}, 'empty');
Sergey Kandaurov42e24142016-06-15 13:27:56 +0300103is($f->{upload}('', body_more => 1, wait => 0.2), '', 'empty - part');
104is($f->{upload}('', wait => 0.2), '', 'empty - part 2');
Sergey Kandaurovece20192016-03-23 20:08:20 +0300105is($f->{http_end}(), 200, 'empty - response');
106
107$f = get_body('/', 'content-length' => 1536);
108ok($f->{headers}, 'buffer');
109is($f->{upload}('0123' x 128, body_more => 1), '0123' x 128,
110 'buffer - below');
111is($f->{upload}('4567' x 128, body_more => 1), '4567' x 128,
112 'buffer - equal');
113is($f->{upload}('89AB' x 128), '89AB' x 128, 'buffer - above');
114is($f->{http_end}(), 200, 'buffer - response');
115
116$f = get_body('/', 'content-length' => 10);
117ok($f->{headers}, 'split');
118is($f->{upload}('0123456789', split => [ 14 ]), '0123456789', 'split');
119is($f->{http_end}(), 200, 'split - response');
120
121# unbuffered request body, chunked transfer-encoding
122
123$f = get_body('/chunked');
124ok($f->{headers}, 'chunked');
125is($f->{upload}('01234', body_more => 1), '5' . CRLF . '01234' . CRLF,
126 'chunked - part');
127is($f->{upload}('56789'), '5' . CRLF . '56789' . CRLF . '0' . CRLF . CRLF,
128 'chunked - part 2');
129is($f->{http_end}(), 200, 'chunked - response');
130
131$f = get_body('/chunked');
132ok($f->{headers}, 'chunked buffer');
133is($f->{upload}('0123' x 128, body_more => 1),
134 '200' . CRLF . '0123' x 128 . CRLF, 'chunked buffer - below');
135is($f->{upload}('4567' x 128, body_more => 1),
136 '200' . CRLF . '4567' x 128 . CRLF, 'chunked buffer - equal');
137is($f->{upload}('89AB' x 128),
138 '200' . CRLF . '89AB' x 128 . CRLF . '0' . CRLF . CRLF,
139 'chunked buffer - above');
140is($f->{http_end}(), 200, 'chunked buffer - response');
141
142$f = get_body('/chunked');
143ok($f->{headers}, 'chunked many');
144is($f->{upload}('01234many', body_split => [ 5 ], body_more => 1),
145 '9' . CRLF . '01234many' . CRLF, 'chunked many - part');
146is($f->{upload}('56789many', body_split => [ 5 ]),
147 '9' . CRLF . '56789many' . CRLF . '0' . CRLF . CRLF,
148 'chunked many - part 2');
149is($f->{http_end}(), 200, 'chunked many - response');
150
151$f = get_body('/chunked');
152ok($f->{headers}, 'chunked empty');
Sergey Kandaurov42e24142016-06-15 13:27:56 +0300153is($f->{upload}('', body_more => 1, wait => 0.2), '', 'chunked empty - part');
Sergey Kandaurovece20192016-03-23 20:08:20 +0300154is($f->{upload}(''), '0' . CRLF . CRLF, 'chunked empty - part 2');
155is($f->{http_end}(), 200, 'chunked empty - response');
156
157$f = get_body('/chunked');
158ok($f->{headers}, 'chunked split');
Sergey Kandaurov3f302bc2018-01-12 17:23:22 +0300159is(http_content($f->{upload}('0123456789', split => [ 14 ])),
160 '0123456789', 'chunked split');
Sergey Kandaurovece20192016-03-23 20:08:20 +0300161is($f->{http_end}(), 200, 'chunked split - response');
162
Sergey Kandaurovece15232016-07-06 15:57:26 +0300163# unbuffered request body, chunked transfer-encoding
164# client sends partial DATA frame and closes connection
165
Sergey Kandaurovece15232016-07-06 15:57:26 +0300166my $s = Test::Nginx::HTTP2->new();
167my $s2 = Test::Nginx::HTTP2->new();
168
169$s->new_stream({ path => '/abort', body_more => 1 });
170$s->h2_body('TEST', { split => [ 9 ], abort => 1 });
171
172close $s->{socket};
173
174$s2->h2_ping('PING');
175isnt(@{$s2->read()}, 0, 'chunked abort');
176
Sergey Kandaurovece20192016-03-23 20:08:20 +0300177###############################################################################
178
Sergey Kandaurov3f302bc2018-01-12 17:23:22 +0300179sub http_content {
180 my ($body) = @_;
181 my $content = '';
182
183 while ($body =~ /\G\x0d?\x0a?([0-9a-f]+)\x0d\x0a?/gcmsi) {
184 my $len = hex($1);
185 $content .= substr($body, pos($body), $len);
186 pos($body) += $len;
187 }
188
189 return $content;
190}
191
Sergey Kandaurovece20192016-03-23 20:08:20 +0300192sub get_body {
193 my ($url, %extra) = @_;
194 my ($server, $client, $f);
195
196 $server = IO::Socket::INET->new(
197 Proto => 'tcp',
198 LocalHost => '127.0.0.1',
Andrey Zelenkove59bf362016-07-12 17:39:03 +0300199 LocalPort => port(8081),
Sergey Kandaurovece20192016-03-23 20:08:20 +0300200 Listen => 5,
201 Timeout => 3,
202 Reuse => 1
203 )
204 or die "Can't create listening socket: $!\n";
205
Sergey Kandaurov67599f42016-06-17 11:36:33 +0300206 my $s = Test::Nginx::HTTP2->new();
Sergey Kandaurovece20192016-03-23 20:08:20 +0300207 my $sid = exists $extra{'content-length'}
Sergey Kandaurov67599f42016-06-17 11:36:33 +0300208 ? $s->new_stream({ headers => [
Sergey Kandaurovece20192016-03-23 20:08:20 +0300209 { name => ':method', value => 'GET' },
210 { name => ':scheme', value => 'http' },
211 { name => ':path', value => $url, },
212 { name => ':authority', value => 'localhost' },
213 { name => 'content-length',
214 value => $extra{'content-length'} }],
215 body_more => 1 })
Sergey Kandaurov67599f42016-06-17 11:36:33 +0300216 : $s->new_stream({ path => $url, body_more => 1 });
Sergey Kandaurovece20192016-03-23 20:08:20 +0300217
218 $client = $server->accept() or return;
219
220 log2c("(new connection $client)");
221
Sergey Kandaurov67599f42016-06-17 11:36:33 +0300222 $f->{headers} = backend_read($client);
Sergey Kandaurovece20192016-03-23 20:08:20 +0300223
224 my $chunked = $f->{headers} =~ /chunked/;
225
Sergey Kandaurov67599f42016-06-17 11:36:33 +0300226 $f->{upload} = sub {
227 my ($body, %extra) = @_;
228 my $len = length($body);
229 my $wait = $extra{wait};
230
231 $s->h2_body($body, { %extra });
232
233 $body = '';
Sergey Kandaurovece20192016-03-23 20:08:20 +0300234
235 for (1 .. 10) {
Sergey Kandaurov67599f42016-06-17 11:36:33 +0300236 my $buf = backend_read($client, $wait) or return '';
237 $body .= $buf;
Sergey Kandaurovece20192016-03-23 20:08:20 +0300238
239 my $got = 0;
240 $got += $chunked ? hex $_ : $_ for $chunked
Sergey Kandaurov67599f42016-06-17 11:36:33 +0300241 ? $body =~ /(\w+)\x0d\x0a?\w+\x0d\x0a?/g
242 : length($body);
Sergey Kandaurov64f8f6b2019-10-10 18:15:02 +0300243 next if $chunked && !$extra{body_more}
244 && $buf !~ /^0\x0d\x0a?/m;
Sergey Kandaurovece20192016-03-23 20:08:20 +0300245 last if $got >= $len;
246 }
247
Sergey Kandaurov67599f42016-06-17 11:36:33 +0300248 return $body;
Sergey Kandaurovece20192016-03-23 20:08:20 +0300249 };
250 $f->{http_end} = sub {
251 $client->write(<<EOF);
252HTTP/1.1 200 OK
253Connection: close
254
255EOF
256
257 $client->close;
258
Sergey Kandaurov67599f42016-06-17 11:36:33 +0300259 my $frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
Sergey Kandaurovece20192016-03-23 20:08:20 +0300260 my ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
261 return $frame->{headers}->{':status'};
262 };
263 return $f;
264}
265
Sergey Kandaurov67599f42016-06-17 11:36:33 +0300266sub backend_read {
267 my ($s, $timo) = @_;
268 my $buf = '';
269
Sergey Kandaurov4aa9b912018-12-24 14:24:51 +0300270 if (IO::Select->new($s)->can_read($timo || 8)) {
Sergey Kandaurov67599f42016-06-17 11:36:33 +0300271 $s->sysread($buf, 16384) or return;
272 log2i($buf);
273 }
274 return $buf;
275}
276
Sergey Kandaurovece20192016-03-23 20:08:20 +0300277sub log2i { Test::Nginx::log_core('|| <<', @_); }
278sub log2o { Test::Nginx::log_core('|| >>', @_); }
279sub log2c { Test::Nginx::log_core('||', @_); }
280
281###############################################################################