comparison h2_fastcgi_request_buffering.t @ 879:127a602f36c8

Tests: HTTP/2 tests for unbuffered request body.
author Sergey Kandaurov <pluknet@nginx.com>
date Wed, 23 Mar 2016 20:08:20 +0300
parents
children 29aa547dd963
comparison
equal deleted inserted replaced
878:327044615c87 879:127a602f36c8
1 #!/usr/bin/perl
2
3 # (C) Sergey Kandaurov
4 # (C) Nginx, Inc.
5
6 # Tests for HTTP/2 protocol with unbuffered request body and fastcgi 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 qw/ :DEFAULT :frame :io /;
20
21 ###############################################################################
22
23 select STDERR; $| = 1;
24 select STDOUT; $| = 1;
25
26 my $t = Test::Nginx->new()->has(qw/http http_v2 fastcgi/);
27
28 $t->write_file_expand('nginx.conf', <<'EOF');
29
30 %%TEST_GLOBALS%%
31
32 daemon off;
33
34 events {
35 }
36
37 http {
38 %%TEST_GLOBALS_HTTP%%
39
40 server {
41 listen 127.0.0.1:8080 http2;
42 server_name localhost;
43
44 location / {
45 fastcgi_request_buffering off;
46 fastcgi_pass 127.0.0.1:8081;
47 fastcgi_param REQUEST_URI $request_uri;
48 client_body_buffer_size 1k;
49 }
50 }
51 }
52
53 EOF
54
55 $t->run();
56
57 plan(skip_all => 'no unbuffered request body') unless get_body('/');
58
59 $t->plan(48);
60
61 ###############################################################################
62
63 my ($f);
64
65 # unbuffered request body to fastcgi
66
67 $f = get_body('/');
68 ok($f->{headers}, 'request');
69 is($f->{upload}('01234', body_more => 1), '01234', 'part');
70 is($f->{upload}('56789'), '56789_eos', 'part 2');
71 is($f->{http_end}(), 200, 'response');
72
73 $f = get_body('/');
74 ok($f->{headers}, 'buffer');
75 is($f->{upload}('0123' x 128, body_more => 1), '0123' x 128, 'buffer - below');
76 is($f->{upload}('4567' x 128, body_more => 1), '4567' x 128, 'buffer - equal');
77 is($f->{upload}('89AB' x 128), '89AB' x 128 . '_eos', 'buffer - above');
78 is($f->{http_end}(), 200, 'buffer - response');
79
80 $f = get_body('/');
81 ok($f->{headers}, 'many');
82 is($f->{upload}('01234many', body_split => [ 5 ], body_more => 1),
83 '01234many', 'many - part');
84 is($f->{upload}('56789many', body_split => [ 5 ]),
85 '56789many_eos', 'many - part 2');
86 is($f->{http_end}(), 200, 'many - response');
87
88 $f = get_body('/');
89 ok($f->{headers}, 'empty');
90 is($f->{upload}('', body_more => 1), '', 'empty - part');
91 is($f->{upload}(''), '_eos', 'empty - part 2');
92 is($f->{http_end}(), 200, 'empty - response');
93
94 $f = get_body('/');
95 ok($f->{headers}, 'split');
96 is($f->{upload}('0123456789', split => [ 14 ]), '0123456789_eos',
97 'split - part');
98 is($f->{http_end}(), 200, 'split - response');
99
100 # unbuffered request body to fastcgi, content-length
101
102 $f = get_body('/', 'content-length' => 10);
103 ok($f->{headers}, 'cl');
104
105 is($f->{upload}('01234', body_more => 1), '01234', 'cl - part');
106 is($f->{upload}('56789'), '56789_eos', 'cl - part 2');
107 is($f->{http_end}(), 200, 'cl - response');
108
109 $f = get_body('/', 'content-length' => 1536);
110 ok($f->{headers}, 'cl buffer');
111 is($f->{upload}('0123' x 128, body_more => 1), '0123' x 128,
112 'cl buffer - below');
113 is($f->{upload}('4567' x 128, body_more => 1), '4567' x 128,
114 'cl buffer - equal');
115 is($f->{upload}('89AB' x 128), '89AB' x 128 . '_eos', 'cl buffer - above');
116 is($f->{http_end}(), 200, 'cl buffer - response');
117
118 $f = get_body('/', 'content-length' => 10);
119 ok($f->{headers}, 'cl much');
120 is($f->{upload}('0123456789', body_more => 1), '0123456789', 'cl much - part');
121 is($f->{upload}('many'), '', 'cl much - part 2');
122 is($f->{http_end}(), 400, 'cl much - response');
123
124 $f = get_body('/', 'content-length' => 10);
125 ok($f->{headers}, 'cl less');
126 is($f->{upload}('0123', body_more => 1), '0123', 'cl less - part');
127 is($f->{upload}('56789'), '', 'cl less - part 2');
128 is($f->{http_end}(), 400, 'cl less - response');
129
130 $f = get_body('/', 'content-length' => 18);
131 ok($f->{headers}, 'cl many');
132 is($f->{upload}('01234many', body_split => [ 5 ], body_more => 1),
133 '01234many', 'cl many - part');
134 is($f->{upload}('56789many', body_split => [ 5 ]), '56789many_eos',
135 'cl many - part 2');
136 is($f->{http_end}(), 200, 'cl many - response');
137
138 $f = get_body('/', 'content-length' => 0);
139 ok($f->{headers}, 'cl empty');
140 is($f->{upload}('', body_more => 1), '', 'cl empty - part');
141 is($f->{upload}(''), '_eos', 'cl empty - part 2');
142 is($f->{http_end}(), 200, 'cl empty - response');
143
144 $f = get_body('/', 'content-length' => 10);
145 ok($f->{headers}, 'cl split');
146 is($f->{upload}('0123456789', split => [ 14 ]), '0123456789_eos', 'cl split');
147 is($f->{http_end}(), 200, 'cl split - response');
148
149 ###############################################################################
150
151 # Simple FastCGI responder implementation.
152
153 # http://www.fastcgi.com/devkit/doc/fcgi-spec.html
154
155 sub fastcgi_read_record($) {
156 my ($buf) = @_;
157
158 my ($n, $h, $header);
159
160 return undef unless length $$buf;
161
162 @{$h}{qw/ version type id clen plen /} = unpack("CCnnC", $$buf);
163
164 $h->{content} = substr $$buf, 8, $h->{clen};
165 $h->{padding} = substr $$buf, 8 + $h->{clen}, $h->{plen};
166
167 $$buf = substr $$buf, 8 + $h->{clen} + $h->{plen};
168
169 return $h;
170 }
171
172 sub fastcgi_respond($$$$) {
173 my ($socket, $version, $id, $body) = @_;
174
175 # stdout
176 $socket->write(pack("CCnnCx", $version, 6, $id, length($body), 0));
177 $socket->write($body);
178
179 # close stdout
180 $socket->write(pack("CCnnCx", $version, 6, $id, 0, 0));
181
182 # end request
183 $socket->write(pack("CCnnCx", $version, 3, $id, 8, 0));
184 $socket->write(pack("NCxxx", 0, 0));
185 }
186
187 sub get_body {
188 my ($url, %extra) = @_;
189 my ($server, $client, $f);
190
191 $server = IO::Socket::INET->new(
192 Proto => 'tcp',
193 LocalHost => '127.0.0.1',
194 LocalPort => 8081,
195 Listen => 5,
196 Timeout => 3,
197 Reuse => 1
198 )
199 or die "Can't create listening socket: $!\n";
200
201 my $sess = new_session(8080);
202 my $sid = exists $extra{'content-length'}
203 ? new_stream($sess, { headers => [
204 { name => ':method', value => 'GET' },
205 { name => ':scheme', value => 'http' },
206 { name => ':path', value => $url, },
207 { name => ':authority', value => 'localhost' },
208 { name => 'content-length',
209 value => $extra{'content-length'} }],
210 body_more => 1 })
211 : new_stream($sess, { path => $url, body_more => 1 });
212
213 $client = $server->accept() or return;
214
215 log2c("(new connection $client)");
216
217 $f->{headers} = raw_read($client, '', 1, \&log2i);
218
219 my $h = fastcgi_read_record(\$f->{headers});
220 my $version = $h->{version};
221 my $id = $h->{id};
222
223 $f->{upload} = sub {
224 my ($body, %extra) = @_;
225 my $len = length($body);
226
227 h2_body($sess, $body, { %extra });
228
229 $body = '';
230
231 for (1 .. 10) {
232 my $buf = raw_read($client, '', 1, \&log2i)
233 or return '';
234
235 while (my $h = fastcgi_read_record(\$buf)) {
236
237 # skip everything unless stdin
238 next if $h->{type} != 5;
239
240 $body .= $h->{content};
241
242 # mark the end-of-stream indication
243 $body .= "_eos" if $h->{clen} == 0;
244 }
245
246 last if length($body) >= $len;
247 }
248
249 return $body;
250 };
251 $f->{http_end} = sub {
252 local $SIG{PIPE} = 'IGNORE';
253
254 fastcgi_respond($client, $version, $id, <<EOF);
255 Status: 200 OK
256 Connection: close
257
258 OK
259 EOF
260
261 $client->close;
262
263 my $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]);
264 my ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
265 return $frame->{headers}->{':status'};
266 };
267 return $f;
268 }
269
270 sub log2i { Test::Nginx::log_core('|| <<', @_); }
271 sub log2o { Test::Nginx::log_core('|| >>', @_); }
272 sub log2c { Test::Nginx::log_core('||', @_); }
273
274 ###############################################################################