Mercurial > hg > nginx-tests
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 ############################################################################### |