1use Mojo::Base -strict;
2
3use Test::More;
4use Mojo::Content::Single;
5use Mojo::Content::MultiPart;
6use Mojo::Cookie::Request;
7use Mojo::File qw(tempdir);
8use Mojo::Message::Request;
9use Mojo::URL;
10use Mojo::Util qw(encode gzip);
11
12subtest 'Defaults' => sub {
13  my $req = Mojo::Message::Request->new;
14  is $req->max_message_size, 16777216, 'right default';
15};
16
17subtest 'Request ID' => sub {
18  my $id = Mojo::Message::Request->new->request_id;
19  ok length $id >= 8, 'at least 8 characters';
20  my $id2 = Mojo::Message::Request->new->request_id;
21  ok length $id >= 8, 'at least 8 characters';
22  isnt $id, $id2, 'different id';
23};
24
25subtest 'Parse HTTP 1.1 message with huge "Cookie" header exceeding all limits' => sub {
26  my $req = Mojo::Message::Request->new;
27  my $finished;
28  $req->max_message_size($req->headers->max_line_size);
29  my $huge = 'a=b; ' x $req->max_message_size;
30  $req->on(finish => sub { $finished = shift->is_finished });
31  ok !$req->is_limit_exceeded, 'limit is not exceeded';
32  $req->parse("PUT /upload HTTP/1.1\x0d\x0aCookie: $huge\x0d\x0a");
33  ok $req->is_limit_exceeded, 'limit is exceeded';
34  $req->parse("Content-Length: 0\x0d\x0a\x0d\x0a");
35  ok $finished, 'finish event has been emitted';
36  ok $req->is_finished, 'request is finished';
37  is $req->content->leftovers, '', 'no leftovers';
38  is $req->error->{message}, 'Maximum message size exceeded', 'right error';
39  ok $req->is_limit_exceeded, 'limit is exceeded';
40  is $req->method,            'PUT',     'right method';
41  is $req->version,           '1.1',     'right version';
42  is $req->url,               '/upload', 'right URL';
43  is $req->cookie('a'), undef, 'no value';
44};
45
46subtest 'Parse HTTP 1.1 message with huge "Cookie" header exceeding line limit' => sub {
47  my $req = Mojo::Message::Request->new;
48  is $req->headers->max_line_size, 8192, 'right size';
49  is $req->headers->max_lines,     100,  'right number';
50  $req->parse("GET / HTTP/1.1\x0d\x0a");
51  $req->parse("Cookie: @{['a=b; ' x 131072]}\x0d\x0a");
52  $req->parse("Content-Length: 0\x0d\x0a\x0d\x0a");
53  ok $req->is_finished, 'request is finished';
54  is $req->error->{message}, 'Maximum header size exceeded', 'right error';
55  ok $req->is_limit_exceeded, 'limit is exceeded';
56  is $req->method,            'GET', 'right method';
57  is $req->version,           '1.1', 'right version';
58  is $req->url,               '/',   'right URL';
59  is $req->cookie('a'), undef, 'no value';
60  is $req->body, '', 'no content';
61};
62
63subtest 'Parse HTTP 1.1 message with huge "Cookie" header exceeding line limit (alternative)' => sub {
64  my $req = Mojo::Message::Request->new;
65  $req->parse("GET / HTTP/1.1\x0d\x0a");
66  $req->parse("Content-Length: 4\x0d\x0aCookie: @{['a=b; ' x 131072]}\x0d\x0a" . "X-Test: 23\x0d\x0a\x0d\x0aabcd");
67  ok $req->is_finished, 'request is finished';
68  is $req->error->{message}, 'Maximum header size exceeded', 'right error';
69  ok $req->is_limit_exceeded, 'limit is exceeded';
70  is $req->method,            'GET', 'right method';
71  is $req->version,           '1.1', 'right version';
72  is $req->url,               '/',   'right URL';
73  is $req->cookie('a'), undef, 'no value';
74  is $req->body, '', 'no content';
75};
76
77subtest 'Parse HTTP 1.1 message with content exceeding line limit' => sub {
78  my $req = Mojo::Message::Request->new;
79  is $req->max_message_size, 16777216, 'right size';
80  $req->parse("GET / HTTP/1.1\x0d\x0a");
81  $req->parse("Content-Length: 655360\x0d\x0a\x0d\x0a@{['a=b; ' x 131072]}");
82  ok $req->is_finished, 'request is finished';
83  is $req->error,       undef, 'no error';
84  is $req->method,      'GET', 'right method';
85  is $req->version,     '1.1', 'right version';
86  is $req->url,         '/',   'right URL';
87  is $req->body,        'a=b; ' x 131072, 'right content';
88};
89
90subtest 'Parse broken start-line' => sub {
91  my $req = Mojo::Message::Request->new;
92  $req->parse("12345\x0d\x0a");
93  ok $req->is_finished, 'request is finished';
94  is $req->error->{message}, 'Bad request start-line', 'right error';
95  ok !$req->is_limit_exceeded, 'limit is not exceeded';
96};
97
98subtest 'Parse broken HTTP 1.1 message with header exceeding line limit' => sub {
99  my $req = Mojo::Message::Request->new;
100  $req->parse("GET / HTTP/1.1\x0d\x0a");
101  $req->parse("Content-Length: 0\x0d\x0a");
102  ok !$req->is_limit_exceeded, 'limit is not exceeded';
103  $req->parse("Foo: @{['a' x 8192]}");
104  ok $req->is_finished, 'request is finished';
105  is $req->error->{message}, 'Maximum header size exceeded', 'right error';
106  ok $req->is_limit_exceeded, 'limit is exceeded';
107  is $req->method,            'GET', 'right method';
108  is $req->version,           '1.1', 'right version';
109  is $req->url,               '/',   'right URL';
110  is $req->headers->header('Foo'), undef, 'no "Foo" value';
111  is $req->body, '', 'no content';
112};
113
114subtest 'Parse broken HTTP 1.1 message with start-line exceeding line limit' => sub {
115  my $req = Mojo::Message::Request->new;
116  is $req->max_line_size, 8192, 'right size';
117  is $req->headers->max_lines, 100, 'right number';
118  $req->parse("GET /@{['abcd' x 131072]} HTTP/1.1");
119  ok $req->is_finished, 'request is finished';
120  is $req->error->{message}, 'Maximum start-line size exceeded', 'right error';
121  is $req->method,  'GET', 'right method';
122  is $req->version, '1.1', 'right version';
123  is $req->url,     '',    'no URL';
124  is $req->cookie('a'), undef, 'no value';
125  is $req->body, '', 'no content';
126};
127
128subtest 'Parse broken HTTP 1.1 message with start-line exceeding line limit (alternative)' => sub {
129  my $req = Mojo::Message::Request->new;
130  $req->parse('GET /');
131  $req->parse('abcd' x 131072);
132  ok $req->is_finished, 'request is finished';
133  is $req->error->{message}, 'Maximum start-line size exceeded', 'right error';
134  is $req->method,  'GET', 'right method';
135  is $req->version, '1.1', 'right version';
136  is $req->url,     '',    'no URL';
137  is $req->cookie('a'), undef, 'no value';
138  is $req->body, '', 'no content';
139};
140
141subtest 'Parse pipelined HTTP 1.1 messages exceeding leftover limit' => sub {
142  my $req = Mojo::Message::Request->new;
143  is $req->content->max_leftover_size, 262144, 'right size';
144  $req->parse("GET /one HTTP/1.1\x0d\x0a");
145  $req->parse("Content-Length: 120000\x0d\x0a\x0d\x0a@{['a' x 120000]}");
146  ok $req->is_finished, 'request is finished';
147  is $req->content->leftovers, '', 'no leftovers';
148  $req->parse("GET /two HTTP/1.1\x0d\x0a");
149  $req->parse("Content-Length: 120000\x0d\x0a\x0d\x0a@{['b' x 120000]}");
150  is length($req->content->leftovers), 120045, 'right size';
151  $req->parse("GET /three HTTP/1.1\x0d\x0a");
152  $req->parse("Content-Length: 120000\x0d\x0a\x0d\x0a@{['c' x 120000]}");
153  is length($req->content->leftovers), 240092, 'right size';
154  $req->parse("GET /four HTTP/1.1\x0d\x0a");
155  $req->parse("Content-Length: 120000\x0d\x0a\x0d\x0a@{['d' x 120000]}");
156  is length($req->content->leftovers), 360138, 'right size';
157  $req->parse("GET /five HTTP/1.1\x0d\x0a");
158  $req->parse("Content-Length: 120000\x0d\x0a\x0d\x0a@{['e' x 120000]}");
159  is length($req->content->leftovers), 360138, 'right size';
160  is $req->error,   undef,  'no error';
161  is $req->method,  'GET',  'right method';
162  is $req->version, '1.1',  'right version';
163  is $req->url,     '/one', 'right URL';
164  is $req->body,    'a' x 120000, 'right content';
165};
166
167subtest 'Parse pipelined HTTP 1.1 messages exceeding leftover limit (chunked)' => sub {
168  my $req = Mojo::Message::Request->new;
169  $req->parse("GET /one HTTP/1.1\x0d\x0a");
170  $req->parse("Transfer-Encoding: chunked\x0d\x0a\x0d\x0a");
171  $req->parse("ea60\x0d\x0a");
172  $req->parse('a' x 60000);
173  $req->parse("\x0d\x0aea60\x0d\x0a");
174  $req->parse('a' x 60000);
175  $req->parse("\x0d\x0a0\x0d\x0a\x0d\x0a");
176  ok $req->is_finished, 'request is finished';
177  is $req->content->leftovers, '', 'no leftovers';
178  $req->parse("GET /two HTTP/1.1\x0d\x0a");
179  $req->parse("Content-Length: 120000\x0d\x0a\x0d\x0a@{['b' x 120000]}");
180  is length($req->content->leftovers), 120045, 'right size';
181  $req->parse("GET /three HTTP/1.1\x0d\x0a");
182  $req->parse("Content-Length: 120000\x0d\x0a\x0d\x0a@{['c' x 120000]}");
183  is length($req->content->leftovers), 240092, 'right size';
184  $req->parse("GET /four HTTP/1.1\x0d\x0a");
185  $req->parse("Content-Length: 120000\x0d\x0a\x0d\x0a@{['d' x 120000]}");
186  is length($req->content->leftovers), 360138, 'right size';
187  $req->parse("GET /five HTTP/1.1\x0d\x0a");
188  $req->parse("Content-Length: 120000\x0d\x0a\x0d\x0a@{['e' x 120000]}");
189  is length($req->content->leftovers), 360138, 'right size';
190  is $req->error,   undef,  'no error';
191  is $req->method,  'GET',  'right method';
192  is $req->version, '1.1',  'right version';
193  is $req->url,     '/one', 'right URL';
194  is $req->body,    'a' x 120000, 'right content';
195};
196
197subtest 'Parse HTTP 1.1 start-line, no headers and body' => sub {
198  my $req = Mojo::Message::Request->new;
199  $req->parse("GET / HTTP/1.1\x0d\x0a\x0d\x0a");
200  ok $req->is_finished, 'request is finished';
201  is $req->method,      'GET', 'right method';
202  is $req->version,     '1.1', 'right version';
203  is $req->url,         '/',   'right URL';
204};
205
206subtest 'Parse HTTP 1.1 start-line, no headers and body (small chunks)' => sub {
207  my $req = Mojo::Message::Request->new;
208  $req->parse('G');
209  ok !$req->is_finished, 'request is not finished';
210  $req->parse('E');
211  ok !$req->is_finished, 'request is not finished';
212  $req->parse('T');
213  ok !$req->is_finished, 'request is not finished';
214  $req->parse(' ');
215  ok !$req->is_finished, 'request is not finished';
216  $req->parse('/');
217  ok !$req->is_finished, 'request is not finished';
218  $req->parse(' ');
219  ok !$req->is_finished, 'request is not finished';
220  $req->parse('H');
221  ok !$req->is_finished, 'request is not finished';
222  $req->parse('T');
223  ok !$req->is_finished, 'request is not finished';
224  $req->parse('T');
225  ok !$req->is_finished, 'request is not finished';
226  $req->parse('P');
227  ok !$req->is_finished, 'request is not finished';
228  $req->parse('/');
229  ok !$req->is_finished, 'request is not finished';
230  $req->parse('1');
231  ok !$req->is_finished, 'request is not finished';
232  $req->parse('.');
233  ok !$req->is_finished, 'request is not finished';
234  $req->parse('1');
235  ok !$req->is_finished, 'request is not finished';
236  $req->parse("\x0d");
237  ok !$req->is_finished, 'request is not finished';
238  $req->parse("\x0a");
239  ok !$req->is_finished, 'request is not finished';
240  $req->parse("\x0d");
241  ok !$req->is_finished, 'request is not finished';
242  $req->parse("\x0a");
243  ok $req->is_finished, 'request is finished';
244  is $req->method,      'GET', 'right method';
245  is $req->version,     '1.1', 'right version';
246  is $req->url,         '/',   'right URL';
247};
248
249subtest 'Parse pipelined HTTP 1.1 start-line, no headers and body' => sub {
250  my $req = Mojo::Message::Request->new;
251  $req->parse("GET / HTTP/1.1\x0d\x0a\x0d\x0aGET / HTTP/1.1\x0d\x0a\x0d\x0a");
252  ok $req->is_finished, 'request is finished';
253  is $req->content->leftovers, "GET / HTTP/1.1\x0d\x0a\x0d\x0a", 'second request in leftovers';
254};
255
256subtest 'Parse HTTP 1.1 start-line, no headers and body with leading CRLFs' => sub {
257  my $req = Mojo::Message::Request->new;
258  $req->parse("\x0d\x0a GET / HTTP/1.1\x0d\x0a\x0d\x0a");
259  ok $req->is_finished, 'request is finished';
260  is $req->method,      'GET', 'right method';
261  is $req->version,     '1.1', 'right version';
262  is $req->url,         '/',   'right URL';
263};
264
265subtest 'Parse WebSocket handshake request' => sub {
266  my $req = Mojo::Message::Request->new;
267  $req->parse("GET /demo HTTP/1.1\x0d\x0a");
268  $req->parse("Host: example.com\x0d\x0a");
269  $req->parse("Connection: Upgrade\x0d\x0a");
270  $req->parse("Sec-WebSocket-Key: abcdef=\x0d\x0a");
271  $req->parse("Sec-WebSocket-Protocol: sample\x0d\x0a");
272  $req->parse("Upgrade: websocket\x0d\x0a\x0d\x0a");
273  ok $req->is_finished,  'request is finished';
274  ok $req->is_handshake, 'request is WebSocket handshake';
275  is $req->method,       'GET',   'right method';
276  is $req->version,      '1.1',   'right version';
277  is $req->url,          '/demo', 'right URL';
278  is $req->headers->host,                   'example.com', 'right "Host" value';
279  is $req->headers->connection,             'Upgrade',     'right "Connection" value';
280  is $req->headers->sec_websocket_protocol, 'sample',      'right "Sec-WebSocket-Protocol" value';
281  is $req->headers->upgrade,                'websocket',   'right "Upgrade" value';
282  is $req->headers->sec_websocket_key,      'abcdef=',     'right "Sec-WebSocket-Key" value';
283  is $req->body, '', 'no content';
284};
285
286subtest 'Parse HTTP 1.0 start-line and headers, no body' => sub {
287  my $req = Mojo::Message::Request->new;
288  $req->parse("GET /foo/bar/baz.html HTTP/1.0\x0d\x0a");
289  $req->parse("Content-Type: text/plain;charset=UTF-8\x0d\x0a");
290  $req->parse("Content-Length: 0\x0d\x0a\x0d\x0a");
291  ok $req->is_finished, 'request is finished';
292  ok !$req->is_handshake, 'request is not a WebSocket handshake';
293  is $req->method,  'GET',               'right method';
294  is $req->version, '1.0',               'right version';
295  is $req->url,     '/foo/bar/baz.html', 'right URL';
296  is $req->headers->content_type,   'text/plain;charset=UTF-8', 'right "Content-Type" value';
297  is $req->headers->content_length, 0,                          'right "Content-Length" value';
298  is $req->content->charset,        'UTF-8',                    'right charset';
299};
300
301subtest 'Parse HTTP 1.0 start-line and headers, no body (missing Content-Length)' => sub {
302  my $req = Mojo::Message::Request->new;
303  $req->parse("GET /foo/bar/baz.html HTTP/1.0\x0d\x0a");
304  $req->parse("Content-Type: text/plain\x0d\x0a\x0d\x0a");
305  ok $req->is_finished, 'request is finished';
306  is $req->method,      'GET',               'right method';
307  is $req->version,     '1.0',               'right version';
308  is $req->url,         '/foo/bar/baz.html', 'right URL';
309  is $req->headers->content_type,   'text/plain', 'right "Content-Type" value';
310  is $req->headers->content_length, undef,        'no "Content-Length" value';
311};
312
313subtest 'Parse full HTTP 1.0 request (file storage)' => sub {
314  local $ENV{MOJO_MAX_MEMORY_SIZE} = 12;
315  my $req = Mojo::Message::Request->new;
316  my ($upgrade, $size);
317  $req->content->asset->on(
318    upgrade => sub {
319      my ($mem, $file) = @_;
320      $upgrade = $file->is_file;
321      $size    = $file->size;
322    }
323  );
324  is $req->content->asset->max_memory_size, 12, 'right size';
325  is $req->content->progress, 0, 'right progress';
326  $req->parse('GET /foo/bar/baz.html?fo');
327  is $req->content->progress, 0, 'right progress';
328  $req->parse("o=13 HTTP/1.0\x0d\x0aContent");
329  $req->parse('-Type: text/');
330  is $req->content->progress, 0, 'right progress';
331  $req->parse("plain\x0d\x0aContent-Length: 27\x0d\x0a\x0d\x0aHell");
332  is $req->content->progress, 4, 'right progress';
333  ok !$req->content->asset->is_file, 'stored in memory';
334  ok !$upgrade, 'upgrade event has not been emitted';
335  $req->parse("o World!\n");
336  ok $upgrade, 'upgrade event has been emitted';
337  is $size, 0, 'file had no content yet';
338  is $req->content->asset->size, 13, 'file has content';
339  is $req->content->progress, 13, 'right progress';
340  ok $req->content->asset->is_file, 'stored in file';
341  $req->parse("1234\nlalalala\n");
342  is $req->content->progress, 27, 'right progress';
343  ok $req->content->asset->is_file, 'stored in file';
344  ok $req->is_finished, 'request is finished';
345  is $req->method,      'GET',                      'right method';
346  is $req->version,     '1.0',                      'right version';
347  is $req->url,         '/foo/bar/baz.html?foo=13', 'right URL';
348  is $req->headers->content_type,   'text/plain', 'right "Content-Type" value';
349  is $req->headers->content_length, 27,           'right "Content-Length" value';
350};
351
352subtest 'Parse HTTP 1.0 start-line and headers, no body (missing Content-Length)' => sub {
353  my $req = Mojo::Message::Request->new;
354  $req->parse("GET /foo/bar/baz.html HTTP/1.0\x0d\x0a");
355  $req->parse("Content-Type: text/plain\x0d\x0a");
356  $req->parse("Connection: Close\x0d\x0a\x0d\x0a");
357  ok $req->is_finished, 'request is finished';
358  is $req->method,      'GET',               'right method';
359  is $req->version,     '1.0',               'right version';
360  is $req->url,         '/foo/bar/baz.html', 'right URL';
361  is $req->headers->content_type,   'text/plain', 'right "Content-Type" value';
362  is $req->headers->content_length, undef,        'no "Content-Length" value';
363};
364
365subtest 'Parse HTTP 1.0 start-line (with line size limit)' => sub {
366  local $ENV{MOJO_MAX_LINE_SIZE} = 5;
367  my $req = Mojo::Message::Request->new;
368  my $limit;
369  $req->on(finish => sub { $limit = shift->is_limit_exceeded });
370  ok !$req->is_limit_exceeded, 'limit is not exceeded';
371  is $req->headers->max_line_size, 5, 'right size';
372  $req->parse('GET /foo/bar/baz.html HTTP/1');
373  ok $req->is_finished, 'request is finished';
374  is $req->error->{message}, 'Maximum start-line size exceeded', 'right error';
375  ok $req->is_limit_exceeded, 'limit is exceeded';
376  ok $limit, 'limit is exceeded';
377  $req->error({message => 'Nothing important.'});
378  is $req->error->{message}, 'Nothing important.', 'right error';
379  ok $req->is_limit_exceeded, 'limit is still exceeded';
380};
381
382subtest 'Parse HTTP 1.0 start-line and headers (with line size limit)' => sub {
383  local $ENV{MOJO_MAX_LINE_SIZE} = 20;
384  my $req = Mojo::Message::Request->new;
385  is $req->max_line_size, 20, 'right size';
386  $req->parse("GET / HTTP/1.0\x0d\x0a");
387  $req->parse("Content-Type: text/plain\x0d\x0a");
388  ok $req->is_finished, 'request is finished';
389  is $req->error->{message}, 'Maximum header size exceeded', 'right error';
390  ok $req->is_limit_exceeded, 'limit is exceeded';
391};
392
393subtest 'Parse HTTP 1.0 start-line (with message size limit)' => sub {
394  local $ENV{MOJO_MAX_MESSAGE_SIZE} = 5;
395  my $req = Mojo::Message::Request->new;
396  my $limit;
397  $req->on(finish => sub { $limit = shift->is_limit_exceeded });
398  is $req->max_message_size, 5, 'right size';
399  $req->parse('GET /foo/bar/baz.html HTTP/1');
400  ok $req->is_finished, 'request is finished';
401  is $req->error->{message}, 'Maximum message size exceeded', 'right error';
402  ok $req->is_limit_exceeded, 'limit is exceeded';
403  ok $limit, 'limit is exceeded';
404};
405
406subtest 'Parse HTTP 1.0 start-line and headers (with message size limit)' => sub {
407  local $ENV{MOJO_MAX_MESSAGE_SIZE} = 20;
408  my $req = Mojo::Message::Request->new;
409  $req->parse("GET / HTTP/1.0\x0d\x0a");
410  $req->parse("Content-Type: text/plain\x0d\x0a");
411  ok $req->is_finished, 'request is finished';
412  is $req->error->{message}, 'Maximum message size exceeded', 'right error';
413  ok $req->is_limit_exceeded, 'limit is exceeded';
414};
415
416subtest 'Parse HTTP 1.0 start-line, headers and body (with message size limit)' => sub {
417  local $ENV{MOJO_MAX_MESSAGE_SIZE} = 50;
418  my $req = Mojo::Message::Request->new;
419  $req->parse("GET / HTTP/1.0\x0d\x0a");
420  $req->parse("Content-Length: 24\x0d\x0a\x0d\x0a");
421  $req->parse('Hello World!');
422  $req->parse('Hello World!');
423  ok $req->is_finished, 'request is finished';
424  is $req->error->{message}, 'Maximum message size exceeded', 'right error';
425  ok $req->is_limit_exceeded, 'limit is exceeded';
426};
427
428subtest 'Parse HTTP 1.1 message with headers exceeding line limit' => sub {
429  local $ENV{MOJO_MAX_LINES} = 5;
430  my $req = Mojo::Message::Request->new;
431  is $req->headers->max_lines, 5, 'right number';
432  $req->parse("GET / HTTP/1.1\x0d\x0a");
433  $req->parse("A: a\x0d\x0aB: b\x0d\x0aC: c\x0d\x0aD: d\x0d\x0a");
434  ok !$req->is_limit_exceeded, 'limit is not exceeded';
435  $req->parse("D: d\x0d\x0a\x0d\x0a");
436  ok $req->is_finished, 'request is finished';
437  is $req->error->{message}, 'Maximum header size exceeded', 'right error';
438  ok $req->is_limit_exceeded, 'limit is exceeded';
439  is $req->method,            'GET', 'right method';
440  is $req->version,           '1.1', 'right version';
441  is $req->url,               '/',   'right URL';
442};
443
444subtest 'Parse full HTTP 1.0 request (solitary LF)' => sub {
445  my $req  = Mojo::Message::Request->new;
446  my $body = '';
447  $req->content->on(read => sub { $body .= pop });
448  $req->parse('GET /foo/bar/baz.html?fo');
449  $req->parse("o=13 HTTP/1.0\x0aContent");
450  $req->parse('-Type: text/');
451  $req->parse("plain\x0aContent-Length: 27\x0a\x0aH");
452  $req->parse("ello World!\n1234\nlalalala\n");
453  ok $req->is_finished, 'request is finished';
454  is $req->method,      'GET',                      'right method';
455  is $req->version,     '1.0',                      'right version';
456  is $req->url,         '/foo/bar/baz.html?foo=13', 'right URL';
457  is $req->headers->content_type,   'text/plain', 'right "Content-Type" value';
458  is $req->headers->content_length, 27,           'right "Content-Length" value';
459  is $req->body, "Hello World!\n1234\nlalalala\n", 'right content';
460  is $body, "Hello World!\n1234\nlalalala\n", 'right content';
461};
462
463subtest 'Parse full HTTP 1.0 request (no scheme and empty elements in path)' => sub {
464  my $req = Mojo::Message::Request->new;
465  $req->parse('GET //foo/bar//baz.html?fo');
466  $req->parse("o=13 HTTP/1.0\x0d\x0aContent");
467  $req->parse('-Type: text/');
468  $req->parse("plain\x0d\x0aContent-Length: 27\x0d\x0a\x0d\x0aHell");
469  $req->parse("o World!\n1234\nlalalala\n");
470  ok $req->is_finished, 'request is finished';
471  is $req->method,      'GET', 'right method';
472  is $req->version,     '1.0', 'right version';
473  is $req->url->host, undef,                 'no host';
474  is $req->url->path, '//foo/bar//baz.html', 'right path';
475  is $req->url, '//foo/bar//baz.html?foo=13', 'right URL';
476  is $req->headers->content_type,   'text/plain', 'right "Content-Type" value';
477  is $req->headers->content_length, 27,           'right "Content-Length" value';
478};
479
480subtest 'Parse full HTTP 1.0 request (behind reverse proxy)' => sub {
481  my $req = Mojo::Message::Request->new;
482  $req->parse('GET /foo/bar/baz.html?fo');
483  $req->parse("o=13 HTTP/1.0\x0d\x0aContent");
484  $req->parse('-Type: text/');
485  $req->parse("plain\x0d\x0aContent-Length: 27\x0d\x0a");
486  $req->parse("Host: mojolicious.org\x0d\x0a");
487  $req->parse("X-Forwarded-For: 192.168.2.1, 127.0.0.1\x0d\x0a\x0d\x0a");
488  $req->parse("Hello World!\n1234\nlalalala\n");
489  ok $req->is_finished, 'request is finished';
490  is $req->method,      'GET',                      'right method';
491  is $req->version,     '1.0',                      'right version';
492  is $req->url,         '/foo/bar/baz.html?foo=13', 'right URL';
493  is $req->url->to_abs,             'http://mojolicious.org/foo/bar/baz.html?foo=13', 'right absolute URL';
494  is $req->headers->content_type,   'text/plain',                                     'right "Content-Type" value';
495  is $req->headers->content_length, 27,                                               'right "Content-Length" value';
496};
497
498subtest 'Parse full HTTP 1.0 request with zero chunk' => sub {
499  my $req      = Mojo::Message::Request->new;
500  my $finished = undef;
501  $req->on(finish => sub { $finished = shift->is_finished });
502  $req->parse('GET /foo/bar/baz.html?fo');
503  $req->parse("o=13 HTTP/1.0\x0d\x0aContent");
504  $req->parse('-Type: text/');
505  $req->parse("plain\x0d\x0aContent-Length: 27\x0d\x0a\x0d\x0aHell");
506  $req->parse("o World!\n123");
507  $req->parse('0');
508  $req->parse("\nlalalala\n");
509  ok $finished, 'finish event has been emitted';
510  ok $req->is_finished, 'request is finished';
511  is $req->method,      'GET',                      'right method';
512  is $req->version,     '1.0',                      'right version';
513  is $req->url,         '/foo/bar/baz.html?foo=13', 'right URL';
514  is $req->headers->content_type,   'text/plain', 'right "Content-Type" value';
515  is $req->headers->content_length, 27,           'right "Content-Length" value';
516};
517
518subtest 'Parse full HTTP 1.0 request with UTF-8 form input' => sub {
519  my $req = Mojo::Message::Request->new;
520  $req->parse('GET /foo/bar/baz.html?fo');
521  $req->parse("o=13 HTTP/1.0\x0d\x0aContent");
522  $req->parse('-Type: application/');
523  $req->parse("x-www-form-urlencoded\x0d\x0aContent-Length: 14");
524  $req->parse("\x0d\x0a\x0d\x0a");
525  $req->parse('name=%E2%98%83');
526  ok $req->is_finished, 'request is finished';
527  is $req->method,      'GET',                      'right method';
528  is $req->version,     '1.0',                      'right version';
529  is $req->url,         '/foo/bar/baz.html?foo=13', 'right URL';
530  is $req->headers->content_type,   'application/x-www-form-urlencoded', 'right "Content-Type" value';
531  is $req->headers->content_length, 14,                                  'right "Content-Length" value';
532  is $req->param('name'), '☃', 'right value';
533};
534
535subtest 'Parse HTTP 1.1 gzip compressed request (no decompression)' => sub {
536  my $compressed = gzip my $uncompressed = 'abc' x 1000;
537  my $req        = Mojo::Message::Request->new;
538  $req->parse("POST /foo HTTP/1.1\x0d\x0a");
539  $req->parse("Content-Type: text/plain\x0d\x0a");
540  $req->parse("Content-Length: @{[length $compressed]}\x0d\x0a");
541  $req->parse("Content-Encoding: GZip\x0d\x0a\x0d\x0a");
542  ok $req->content->is_compressed, 'content is compressed';
543  $req->parse($compressed);
544  ok $req->content->is_compressed, 'content is still compressed';
545  ok $req->is_finished, 'request is finished';
546  is $req->method,      'POST', 'right method';
547  is $req->version,     '1.1',  'right version';
548  is $req->url,         '/foo', 'right URL';
549  is $req->headers->content_type, 'text/plain', 'right "Content-Type" value';
550  is $req->headers->content_length, length($compressed), 'right "Content-Length" value';
551  is $req->body, $compressed, 'right content';
552};
553
554subtest 'Parse HTTP 1.1 chunked request' => sub {
555  my $req = Mojo::Message::Request->new;
556  is $req->content->progress, 0, 'right progress';
557  $req->parse("POST /foo/bar/baz.html?foo=13 HTTP/1.1\x0d\x0a");
558  is $req->content->progress, 0, 'right progress';
559  $req->parse("Content-Type: text/plain\x0d\x0a");
560  $req->parse("Transfer-Encoding: chunked\x0d\x0a\x0d\x0a");
561  is $req->content->progress, 0, 'right progress';
562  $req->parse("4\x0d\x0a");
563  is $req->content->progress, 3, 'right progress';
564  $req->parse("abcd\x0d\x0a");
565  is $req->content->progress, 9, 'right progress';
566  $req->parse("9\x0d\x0a");
567  is $req->content->progress, 12, 'right progress';
568  $req->parse("abcdefghi\x0d\x0a");
569  is $req->content->progress, 23, 'right progress';
570  $req->parse("0\x0d\x0a\x0d\x0a");
571  is $req->content->progress, 28, 'right progress';
572  ok $req->is_finished, 'request is finished';
573  is $req->method,      'POST',                     'right method';
574  is $req->version,     '1.1',                      'right version';
575  is $req->url,         '/foo/bar/baz.html?foo=13', 'right URL';
576  is $req->headers->content_length, 13,           'right "Content-Length" value';
577  is $req->headers->content_type,   'text/plain', 'right "Content-Type" value';
578  is $req->content->asset->size,  13,              'right size';
579  is $req->content->asset->slurp, 'abcdabcdefghi', 'right content';
580};
581
582subtest 'Parse HTTP 1.1 chunked request with callbacks' => sub {
583  my $req      = Mojo::Message::Request->new;
584  my $progress = my $buffer = my $finish = '';
585  $req->on(
586    progress => sub {
587      my $self = shift;
588      $progress ||= $self->url->path if $self->content->is_parsing_body;
589    }
590  );
591  $req->content->unsubscribe('read')->on(read => sub { $buffer .= pop });
592  $req->on(finish => sub { $finish .= shift->url->query });
593  $req->parse("POST /foo/bar/baz.html?foo=13 HTTP/1.1\x0d\x0a");
594  is $progress, '', 'no progress';
595  $req->parse("Content-Type: text/plain\x0d\x0a");
596  is $progress, '', 'no progress';
597  $req->parse("Transfer-Encoding: chunked\x0d\x0a\x0d\x0a");
598  is $progress, '/foo/bar/baz.html', 'made progress';
599  $req->parse("4\x0d\x0a");
600  $req->parse("abcd\x0d\x0a");
601  $req->parse("9\x0d\x0a");
602  $req->parse("abcdefghi\x0d\x0a");
603  is $finish, '', 'not finished yet';
604  $req->parse("0\x0d\x0a\x0d\x0a");
605  is $finish,   'foo=13',            'finished';
606  is $progress, '/foo/bar/baz.html', 'made progress';
607  ok $req->is_finished, 'request is finished';
608  is $req->method,      'POST',                     'right method';
609  is $req->version,     '1.1',                      'right version';
610  is $req->url,         '/foo/bar/baz.html?foo=13', 'right URL';
611  is $req->headers->content_length, 13,           'right "Content-Length" value';
612  is $req->headers->content_type,   'text/plain', 'right "Content-Type" value';
613  is $buffer, 'abcdabcdefghi', 'right content';
614};
615
616subtest 'Parse HTTP 1.1 "application/x-www-form-urlencoded"' => sub {
617  my $req = Mojo::Message::Request->new;
618  $req->parse("POST /foo/bar/baz.html?foo=13 HTTP/1.1\x0d\x0a");
619  $req->parse("Content-Length: 25\x0d\x0a");
620  $req->parse("Content-Type: application/x-www-form-urlencoded\x0d\x0a");
621  $req->parse("\x0d\x0afoo=bar&+tset=23+&foo=bar");
622  ok $req->is_finished, 'request is finished';
623  is $req->method,      'POST',                     'right method';
624  is $req->version,     '1.1',                      'right version';
625  is $req->url,         '/foo/bar/baz.html?foo=13', 'right URL';
626  is $req->headers->content_type, 'application/x-www-form-urlencoded', 'right "Content-Type" value';
627  is $req->content->asset->size,  25,                          'right size';
628  is $req->content->asset->slurp, 'foo=bar&+tset=23+&foo=bar', 'right content';
629  is_deeply $req->body_params->to_hash->{foo}, [qw(bar bar)], 'right values';
630  is $req->body_params->to_hash->{' tset'}, '23 ', 'right value';
631  is $req->body_params, 'foo=bar&+tset=23+&foo=bar', 'right parameters';
632  is_deeply $req->params->to_hash->{foo}, [qw(bar bar 13)], 'right values';
633  is_deeply $req->every_param('foo'), [qw(bar bar 13)], 'right values';
634  is $req->param(' tset'), '23 ', 'right value';
635  $req->param('set', 'single');
636  is $req->param('set'), 'single', 'setting single param works';
637  $req->param('multi', 1, 2, 3);
638  is_deeply $req->every_param('multi'), [qw(1 2 3)], 'setting multiple value param works';
639  is $req->param('test23'), undef, 'no value';
640};
641
642subtest 'Parse HTTP 1.1 chunked request with trailing headers' => sub {
643  my $req = Mojo::Message::Request->new;
644  $req->parse("POST /foo/bar/baz.html?foo=13&bar=23 HTTP/1.1\x0d\x0a");
645  $req->parse("Content-Type: text/plain\x0d\x0a");
646  $req->parse("Transfer-Encoding: whatever\x0d\x0a");
647  $req->parse("Trailer: X-Trailer1; X-Trailer2\x0d\x0a\x0d\x0a");
648  $req->parse("4\x0d\x0a");
649  $req->parse("abcd\x0d\x0a");
650  $req->parse("9\x0d\x0a");
651  $req->parse("abcdefghi\x0d\x0a");
652  $req->parse("0\x0d\x0a");
653  $req->parse("X-Trailer1: test\x0d\x0a");
654  $req->parse("X-Trailer2: 123\x0d\x0a\x0d\x0a");
655  ok $req->is_finished,  'request is finished';
656  is $req->method,       'POST',                            'right method';
657  is $req->version,      '1.1',                             'right version';
658  is $req->url,          '/foo/bar/baz.html?foo=13&bar=23', 'right URL';
659  is $req->query_params, 'foo=13&bar=23',                   'right parameters';
660  is $req->headers->content_type, 'text/plain', 'right "Content-Type" value';
661  is $req->headers->header('X-Trailer1'), 'test', 'right "X-Trailer1" value';
662  is $req->headers->header('X-Trailer2'), '123',  'right "X-Trailer2" value';
663  is $req->headers->content_length, 13, 'right "Content-Length" value';
664  is $req->content->asset->size,  13,              'right size';
665  is $req->content->asset->slurp, 'abcdabcdefghi', 'right content';
666};
667
668subtest 'Parse HTTP 1.1 chunked request with trailing headers (different variation)' => sub {
669  my $req = Mojo::Message::Request->new;
670  $req->parse("POST /foo/bar/baz.html?foo=13&bar=23 HTTP/1.1\x0d\x0a");
671  $req->parse("Content-Type: text/plain\x0d\x0aTransfer-Enc");
672  $req->parse("oding: chunked\x0d\x0a");
673  $req->parse("Trailer: X-Trailer\x0d\x0a\x0d\x0a");
674  $req->parse("4\x0d\x0a");
675  $req->parse("abcd\x0d\x0a");
676  $req->parse("9\x0d\x0a");
677  $req->parse("abcdefghi\x0d\x0a");
678  $req->parse("0\x0d\x0aX-Trailer: 777\x0d\x0a\x0d\x0aLEFTOVER");
679  ok $req->is_finished,  'request is finished';
680  is $req->method,       'POST',                            'right method';
681  is $req->version,      '1.1',                             'right version';
682  is $req->url,          '/foo/bar/baz.html?foo=13&bar=23', 'right URL';
683  is $req->query_params, 'foo=13&bar=23',                   'right parameters';
684  ok !defined $req->headers->transfer_encoding, 'no "Transfer-Encoding" value';
685  is $req->headers->content_type, 'text/plain', 'right "Content-Type" value';
686  is $req->headers->header('X-Trailer'), '777', 'right "X-Trailer" value';
687  is $req->headers->content_length, 13, 'right "Content-Length" value';
688  is $req->content->asset->size,  13,              'right size';
689  is $req->content->asset->slurp, 'abcdabcdefghi', 'right content';
690};
691
692subtest 'Parse HTTP 1.1 chunked request with trailing headers (different variation)' => sub {
693  my $req = Mojo::Message::Request->new;
694  $req->parse("POST /foo/bar/baz.html?foo=13&bar=23 HTTP/1.1\x0d\x0a");
695  $req->parse("Content-Type: text/plain\x0d\x0a");
696  $req->parse("Transfer-Encoding: chunked\x0d\x0a");
697  $req->parse("Trailer: X-Trailer1; X-Trailer2\x0d\x0a\x0d\x0a");
698  $req->parse("4\x0d\x0a");
699  $req->parse("abcd\x0d\x0a");
700  $req->parse("9\x0d\x0a");
701  $req->parse("abcdefghi\x0d\x0a");
702  $req->parse("0\x0d\x0aX-Trailer1: test\x0d\x0aX-Trailer2: 123\x0d\x0a\x0d\x0a");
703  ok $req->is_finished,  'request is finished';
704  is $req->method,       'POST',                            'right method';
705  is $req->version,      '1.1',                             'right version';
706  is $req->url,          '/foo/bar/baz.html?foo=13&bar=23', 'right URL';
707  is $req->query_params, 'foo=13&bar=23',                   'right parameters';
708  is $req->headers->content_type, 'text/plain', 'right "Content-Type" value';
709  is $req->headers->header('X-Trailer1'), 'test', 'right "X-Trailer1" value';
710  is $req->headers->header('X-Trailer2'), '123',  'right "X-Trailer2" value';
711  is $req->headers->content_length, 13, 'right "Content-Length" value';
712  is $req->content->asset->size,  13,              'right size';
713  is $req->content->asset->slurp, 'abcdabcdefghi', 'right content';
714};
715
716subtest 'Parse HTTP 1.1 chunked request with trailing headers (no Trailer header)' => sub {
717  my $req = Mojo::Message::Request->new;
718  $req->parse("POST /foo/bar/baz.html?foo=13&bar=23 HTTP/1.1\x0d\x0a");
719  $req->parse("Content-Type: text/plain\x0d\x0a");
720  $req->parse("Transfer-Encoding: chunked\x0d\x0a\x0d\x0a");
721  $req->parse("4\x0d\x0a");
722  $req->parse("abcd\x0d\x0a");
723  $req->parse("9\x0d\x0a");
724  $req->parse("abcdefghi\x0d\x0a");
725  $req->parse("0\x0d\x0aX-Trailer1: test\x0d\x0aX-Trailer2: 123\x0d\x0a\x0d\x0a");
726  ok $req->is_finished,  'request is finished';
727  is $req->method,       'POST',                            'right method';
728  is $req->version,      '1.1',                             'right version';
729  is $req->url,          '/foo/bar/baz.html?foo=13&bar=23', 'right URL';
730  is $req->query_params, 'foo=13&bar=23',                   'right parameters';
731  is $req->headers->content_type, 'text/plain', 'right "Content-Type" value';
732  is $req->headers->header('X-Trailer1'), 'test', 'right "X-Trailer1" value';
733  is $req->headers->header('X-Trailer2'), '123',  'right "X-Trailer2" value';
734  is $req->headers->content_length, 13, 'right "Content-Length" value';
735  is $req->content->asset->size,  13,              'right size';
736  is $req->content->asset->slurp, 'abcdabcdefghi', 'right content';
737};
738
739subtest 'Parse HTTP 1.1 multipart request' => sub {
740  my $req = Mojo::Message::Request->new;
741  is $req->content->progress, 0, 'right progress';
742  $req->parse("GET /foo/bar/baz.html?foo13 HTTP/1.1\x0d\x0a");
743  is $req->content->progress, 0, 'right progress';
744  $req->parse("Content-Length: 416\x0d\x0a");
745  $req->parse('Content-Type: multipart/form-data; bo');
746  is $req->content->progress, 0, 'right progress';
747  $req->parse("undary=----------0xKhTmLbOuNdArY\x0d\x0a\x0d\x0a");
748  is $req->content->progress, 0, 'right progress';
749  $req->parse("\x0d\x0a------------0xKhTmLbOuNdArY\x0d\x0a");
750  is $req->content->progress, 31, 'right progress';
751  $req->parse("Content-Disposition: form-data; name=\"text1\"\x0d\x0a");
752  $req->parse("\x0d\x0ahallo welt test123\n");
753  $req->parse("\x0d\x0a------------0xKhTmLbOuNdArY\x0d\x0a");
754  $req->parse("Content-Disposition: form-data; name=\"text2\"\x0d\x0a");
755  $req->parse("\x0d\x0a\x0d\x0a------------0xKhTmLbOuNdArY\x0d\x0a");
756  $req->parse('Content-Disposition: form-data;name="upload";file');
757  $req->parse("name=\"hello.pl\"\x0d\x0a");
758  $req->parse("Content-Type: application/octet-stream\x0d\x0a\x0d\x0a");
759  $req->parse("#!/usr/bin/perl\n\n");
760  $req->parse("use strict;\n");
761  $req->parse("use warnings;\n\n");
762  $req->parse("print \"Hello World :)\\n\"\n");
763  $req->parse("\x0d\x0a------------0xKhTmLbOuNdArY--");
764  is $req->content->progress, 416, 'right progress';
765  ok $req->is_finished, 'request is finished';
766  ok $req->content->is_multipart, 'multipart content';
767  is $req->body,         '',                        'no content';
768  is $req->method,       'GET',                     'right method';
769  is $req->version,      '1.1',                     'right version';
770  is $req->url,          '/foo/bar/baz.html?foo13', 'right URL';
771  is $req->query_params, 'foo13',                   'right parameters';
772  is $req->headers->content_type, 'multipart/form-data; boundary=----------0xKhTmLbOuNdArY',
773    'right "Content-Type" value';
774  is $req->headers->content_length, 416, 'right "Content-Length" value';
775  ok !$req->content->parts->[0]->is_multipart, 'no multipart content';
776  ok !$req->content->parts->[1]->is_multipart, 'no multipart content';
777  ok !$req->content->parts->[2]->is_multipart, 'no multipart content';
778  ok !$req->content->parts->[0]->asset->is_file, 'stored in memory';
779  is $req->content->parts->[0]->asset->slurp, "hallo welt test123\n", 'right content';
780  is $req->body_params->to_hash->{text1}, "hallo welt test123\n", 'right value';
781  is $req->body_params->to_hash->{text2}, '',                     'right value';
782  is $req->upload('upload')->filename, 'hello.pl', 'right filename';
783  ok !$req->upload('upload')->asset->is_file, 'stored in memory';
784  is $req->upload('upload')->asset->size, 69, 'right size';
785  my $tempdir = tempdir;
786  my $file    = $tempdir->child('MOJO_TMP.' . time . '.txt');
787  is $req->upload('upload')->move_to($file)->filename, 'hello.pl', 'right filename';
788  ok unlink($file), 'unlinked file';
789  is $req->content->boundary, '----------0xKhTmLbOuNdArY', 'right boundary';
790};
791
792subtest 'Parse HTTP 1.1 multipart request (too big for memory)' => sub {
793  my $req = Mojo::Message::Request->new;
794  $req->content->on(
795    body => sub {
796      my $single = shift;
797      $single->on(
798        upgrade => sub {
799          my ($single, $multi) = @_;
800          $multi->on(
801            part => sub {
802              my ($multi, $part) = @_;
803              $part->asset->max_memory_size(5);
804            }
805          );
806        }
807      );
808    }
809  );
810  $req->parse("GET /foo/bar/baz.html?foo13 HTTP/1.1\x0d\x0a");
811  $req->parse("Content-Length: 562\x0d\x0a");
812  $req->parse('Content-Type: multipart/form-data; bo');
813  $req->parse("undary=----------0xKhTmLbOuNdArY\x0d\x0a\x0d\x0a");
814  $req->parse("\x0d\x0a------------0xKhTmLbOuNdArY\x0d\x0a");
815  $req->parse("Content-Disposition: form-data; name=\"text1\"\x0d\x0a");
816  $req->parse("\x0d\x0ahallo welt test123\n");
817  $req->parse("\x0d\x0a------------0xKhTmLbOuNdArY\x0d\x0a");
818  $req->parse("Content-Disposition: form-data; name=\"text2\"\x0d\x0a");
819  $req->parse("\x0d\x0a\x0d\x0a------------0xKhTmLbOuNdArY\x0d\x0a");
820  $req->parse('Content-Disposition: form-data; name="upload"; file');
821  $req->parse("name=\"hello.pl\"\x0d\x0a");
822  $req->parse("Content-Type: application/octet-stream\x0d\x0a\x0d\x0a");
823  $req->parse("#!/usr/bin/perl\n\n");
824  $req->parse("use strict;\n");
825  $req->parse("use warnings;\n\n");
826  $req->parse("print \"Hello World :)\\n\"\n");
827  $req->parse("\x0d\x0a------------0xKhTmLbOuNdArY\x0d\x0a");
828  $req->parse('Content-Disposition: form-data; name="upload"; filename');
829  $req->parse("=\"bye.txt\"\x0d\x0a");
830  $req->parse("Content-Type: application/octet-stream\x0d\x0a\x0d\x0a");
831  $req->parse("Bye!\x0d\x0a------------0xKhTmLbOuNdArY--");
832  ok $req->is_finished, 'request is finished';
833  ok $req->content->is_multipart, 'multipart content';
834  is $req->body,         '',                        'no content';
835  is $req->method,       'GET',                     'right method';
836  is $req->version,      '1.1',                     'right version';
837  is $req->url,          '/foo/bar/baz.html?foo13', 'right URL';
838  is $req->query_params, 'foo13',                   'right parameters';
839  is $req->headers->content_type, 'multipart/form-data; boundary=----------0xKhTmLbOuNdArY',
840    'right "Content-Type" value';
841  is $req->headers->content_length, 562, 'right "Content-Length" value';
842  ok !$req->content->parts->[0]->is_multipart, 'no multipart content';
843  ok !$req->content->parts->[1]->is_multipart, 'no multipart content';
844  ok !$req->content->parts->[2]->is_multipart, 'no multipart content';
845  ok $req->content->parts->[0]->asset->is_file, 'stored in file';
846  is $req->content->parts->[0]->asset->slurp, "hallo welt test123\n", 'right content';
847  is $req->body_params->to_hash->{text1}, "hallo welt test123\n", 'right value';
848  is $req->body_params->to_hash->{text2}, '',                     'right value';
849  is $req->upload('upload')->filename, 'bye.txt', 'right filename';
850  is $req->upload('upload')->asset->size, 4, 'right size';
851  is $req->every_upload('upload')->[0]->filename, 'hello.pl', 'right filename';
852  ok $req->every_upload('upload')->[0]->asset->is_file, 'stored in file';
853  is $req->every_upload('upload')->[0]->asset->size, 69, 'right size';
854  is $req->every_upload('upload')->[1]->filename, 'bye.txt', 'right filename';
855  ok !$req->every_upload('upload')->[1]->asset->is_file, 'stored in memory';
856  is $req->every_upload('upload')->[1]->asset->size, 4, 'right size';
857};
858
859subtest 'Parse HTTP 1.1 multipart request (with callbacks and stream)' => sub {
860  my $req    = Mojo::Message::Request->new;
861  my $stream = '';
862  $req->content->on(
863    body => sub {
864      my $single = shift;
865      $single->on(
866        upgrade => sub {
867          my ($single, $multi) = @_;
868          $multi->on(
869            part => sub {
870              my ($multi, $part) = @_;
871              $part->on(
872                body => sub {
873                  my $part = shift;
874                  return unless $part->headers->content_disposition =~ /hello\.pl/;
875                  $part->on(
876                    read => sub {
877                      my ($part, $chunk) = @_;
878                      $stream .= $chunk;
879                    }
880                  );
881                }
882              );
883            }
884          );
885        }
886      );
887    }
888  );
889  $req->parse("GET /foo/bar/baz.html?foo13 HTTP/1.1\x0d\x0a");
890  $req->parse("Content-Length: 418\x0d\x0a");
891  $req->parse('Content-Type: multipart/form-data; bo');
892  $req->parse("undary=----------0xKhTmLbOuNdArY\x0d\x0a\x0d\x0a");
893  $req->parse("\x0d\x0a------------0xKhTmLbOuNdArY\x0d\x0a");
894  $req->parse("Content-Disposition: form-data; name=\"text1\"\x0d\x0a");
895  $req->parse("\x0d\x0ahallo welt test123\n");
896  $req->parse("\x0d\x0a------------0xKhTmLbOuNdArY\x0d\x0a");
897  $req->parse("Content-Disposition: form-data; name=\"text2\"\x0d\x0a");
898  $req->parse("\x0d\x0a\x0d\x0a------------0xKhTmLbOuNdArY\x0d\x0a");
899  $req->parse('Content-Disposition: form-data; name="upload"; file');
900  $req->parse("name=\"hello.pl\"\x0d\x0a");
901  $req->parse("Content-Type: application/octet-stream\x0d\x0a\x0d\x0a");
902  is $stream, '', 'no content';
903  $req->parse("#!/usr/bin/perl\n\n");
904  is $stream, '', 'no content';
905  $req->parse("use strict;\n");
906  is $stream, '', 'no content';
907  $req->parse("use warnings;\n\n");
908  is $stream, '#!/usr/bin/', 'right content';
909  $req->parse("print \"Hello World :)\\n\"\n");
910  is $stream, "#!/usr/bin/perl\n\nuse strict;\nuse war", 'right content';
911  $req->parse("\x0d\x0a------------0xKhTmLbOuNdArY--");
912  ok $req->is_finished, 'request is finished';
913  ok $req->content->is_multipart, 'multipart content';
914  is $req->method,       'GET',                     'right method';
915  is $req->version,      '1.1',                     'right version';
916  is $req->url,          '/foo/bar/baz.html?foo13', 'right URL';
917  is $req->query_params, 'foo13',                   'right parameters';
918  is $req->headers->content_type, 'multipart/form-data; boundary=----------0xKhTmLbOuNdArY',
919    'right "Content-Type" value';
920  is $req->headers->content_length, 418, 'right "Content-Length" value';
921  ok !$req->content->parts->[0]->is_multipart, 'no multipart content';
922  ok !$req->content->parts->[1]->is_multipart, 'no multipart content';
923  ok !$req->content->parts->[2]->is_multipart, 'no multipart content';
924  is $req->content->parts->[0]->asset->slurp, "hallo welt test123\n", 'right content';
925  is $req->body_params->to_hash->{text1}, "hallo welt test123\n", 'right value';
926  is $req->body_params->to_hash->{text2}, '',                     'right value';
927  is $stream, "#!/usr/bin/perl\n\n" . "use strict;\n" . "use warnings;\n\n" . "print \"Hello World :)\\n\"\n",
928    'right content';
929};
930
931subtest 'Parse HTTP 1.1 multipart request (without upgrade)' => sub {
932  my $req = Mojo::Message::Request->new;
933  $req->content->auto_upgrade(0);
934  $req->parse("GET /foo/bar/baz.html?foo13 HTTP/1.1\x0d\x0a");
935  $req->parse("Content-Length: 418\x0d\x0a");
936  $req->parse('Content-Type: multipart/form-data; bo');
937  $req->parse("undary=----------0xKhTmLbOuNdArY\x0d\x0a\x0d\x0a");
938  $req->parse("\x0d\x0a------------0xKhTmLbOuNdArY\x0d\x0a");
939  $req->parse("Content-Disposition: form-data; name=\"text1\"\x0d\x0a");
940  $req->parse("\x0d\x0ahallo welt test123\n");
941  $req->parse("\x0d\x0a------------0xKhTmLbOuNdArY\x0d\x0a");
942  $req->parse("Content-Disposition: form-data; name=\"text2\"\x0d\x0a");
943  $req->parse("\x0d\x0a\x0d\x0a------------0xKhTmLbOuNdArY\x0d\x0a");
944  $req->parse('Content-Disposition: form-data; name="upload"; file');
945  $req->parse("name=\"hello.pl\"\x0d\x0a");
946  $req->parse("Content-Type: application/octet-stream\x0d\x0a\x0d\x0a");
947  $req->parse("#!/usr/bin/perl\n\n");
948  $req->parse("use strict;\n");
949  $req->parse("use warnings;\n\n");
950  $req->parse("print \"Hello World :)\\n\"\n");
951  $req->parse("\x0d\x0a------------0xKhTmLbOuNdArY--");
952  ok $req->is_finished, 'request is finished';
953  ok !$req->content->is_multipart, 'no multipart content';
954  is $req->method,       'GET',                     'right method';
955  is $req->version,      '1.1',                     'right version';
956  is $req->url,          '/foo/bar/baz.html?foo13', 'right URL';
957  is $req->query_params, 'foo13',                   'right parameters';
958  is $req->headers->content_type, 'multipart/form-data; boundary=----------0xKhTmLbOuNdArY',
959    'right "Content-Type" value';
960  is $req->headers->content_length, 418, 'right "Content-Length" value';
961  ok !$req->content->is_multipart, 'no multipart content';
962  like $req->content->asset->slurp, qr/------------0xKhTmLbOuNdArY--$/, 'right content';
963};
964
965subtest 'Parse HTTP 1.1 multipart request with "0" filename' => sub {
966  my $req = Mojo::Message::Request->new;
967  $req->parse("GET /foo/bar/baz.html?foo13 HTTP/1.1\x0d\x0a");
968  $req->parse("Content-Length: 410\x0d\x0a");
969  $req->parse('Content-Type: multipart/form-data; bo');
970  $req->parse("undary=----------0xKhTmLbOuNdArY\x0d\x0a\x0d\x0a");
971  $req->parse("\x0d\x0a------------0xKhTmLbOuNdArY\x0d\x0a");
972  $req->parse("Content-Disposition: form-data; name=\"text1\"\x0d\x0a");
973  $req->parse("\x0d\x0ahallo welt test123\n");
974  $req->parse("\x0d\x0a------------0xKhTmLbOuNdArY\x0d\x0a");
975  $req->parse("Content-Disposition: form-data; name=\"text2\"\x0d\x0a");
976  $req->parse("\x0d\x0a\x0d\x0a------------0xKhTmLbOuNdArY\x0d\x0a");
977  $req->parse('Content-Disposition: form-data; name="upload"; file');
978  $req->parse("name=\"0\"\x0d\x0a");
979  $req->parse("Content-Type: application/octet-stream\x0d\x0a\x0d\x0a");
980  $req->parse("#!/usr/bin/perl\n\n");
981  $req->parse("use strict;\n");
982  $req->parse("use warnings;\n\n");
983  $req->parse("print \"Hello World :)\\n\"\n");
984  $req->parse("\x0d\x0a------------0xKhTmLbOuNdArY--");
985  ok $req->is_finished, 'request is finished';
986  ok $req->content->is_multipart, 'no multipart content';
987  is $req->method,       'GET',                     'right method';
988  is $req->version,      '1.1',                     'right version';
989  is $req->url,          '/foo/bar/baz.html?foo13', 'right URL';
990  is $req->query_params, 'foo13',                   'right parameters';
991  is $req->headers->content_type, 'multipart/form-data; boundary=----------0xKhTmLbOuNdArY',
992    'right "Content-Type" value';
993  is $req->headers->content_length, 410, 'right "Content-Length" value';
994  ok !$req->content->parts->[0]->is_multipart, 'no multipart content';
995  ok !$req->content->parts->[1]->is_multipart, 'no multipart content';
996  ok !$req->content->parts->[2]->is_multipart, 'no multipart content';
997  is $req->content->parts->[0]->asset->slurp, "hallo welt test123\n", 'right content';
998  is $req->body_params->to_hash->{text1},  "hallo welt test123\n", 'right value';
999  is $req->body_params->to_hash->{text2},  '',                     'right value';
1000  is $req->body_params->to_hash->{upload}, undef,                  'not a body parameter';
1001  is $req->upload('upload')->filename, '0', 'right filename';
1002  ok !$req->upload('upload')->asset->is_file, 'stored in memory';
1003  is $req->upload('upload')->asset->size, 69, 'right size';
1004};
1005
1006subtest 'Parse full HTTP 1.1 proxy request with basic authentication' => sub {
1007  my $req = Mojo::Message::Request->new;
1008  $req->parse("GET http://127.0.0.1/foo/bar#baz HTTP/1.1\x0d\x0a");
1009  $req->parse("Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==\x0d\x0a");
1010  $req->parse("Host: 127.0.0.1\x0d\x0a");
1011  $req->parse("Proxy-Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==\x0d\x0a");
1012  $req->parse("Content-Length: 13\x0d\x0a\x0d\x0a");
1013  $req->parse("Hello World!\n");
1014  ok $req->is_finished, 'request is finished';
1015  is $req->method,      'GET', 'right method';
1016  is $req->version,     '1.1', 'right version';
1017  is $req->url->base, 'http://127.0.0.1', 'right base URL';
1018  is $req->url->base->userinfo, 'Aladdin:open sesame', 'right base userinfo';
1019  is $req->url, 'http://127.0.0.1/foo/bar', 'right URL';
1020  is $req->proxy->userinfo, 'Aladdin:open sesame', 'right proxy userinfo';
1021};
1022
1023subtest 'Parse full HTTP 1.1 proxy connect request with basic authentication' => sub {
1024  my $req = Mojo::Message::Request->new;
1025  $req->parse("CONNECT 127.0.0.1:3000 HTTP/1.1\x0d\x0a");
1026  $req->parse("Host: 127.0.0.1:3000\x0d\x0a");
1027  $req->parse("Proxy-Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==\x0d\x0a");
1028  $req->parse("Content-Length: 0\x0d\x0a\x0d\x0a");
1029  ok $req->is_finished, 'request is finished';
1030  is $req->method,      'CONNECT',          'right method';
1031  is $req->version,     '1.1',              'right version';
1032  is $req->url,         '//127.0.0.1:3000', 'right URL';
1033  is $req->url->host,       '127.0.0.1',           'right host';
1034  is $req->url->port,       '3000',                'right port';
1035  is $req->proxy->userinfo, 'Aladdin:open sesame', 'right proxy userinfo';
1036};
1037
1038subtest 'Build minimal HTTP 1.1 request' => sub {
1039  my $req = Mojo::Message::Request->new;
1040  $req->method('GET');
1041  $req->url->parse('http://127.0.0.1/');
1042  $req = Mojo::Message::Request->new->parse($req->to_string);
1043  ok $req->is_finished, 'request is finished';
1044  is $req->method,      'GET', 'right method';
1045  is $req->version,     '1.1', 'right version';
1046  is $req->url,         '/',   'right URL';
1047  is $req->url->to_abs,   'http://127.0.0.1/', 'right absolute URL';
1048  is $req->headers->host, '127.0.0.1',         'right "Host" value';
1049};
1050
1051subtest 'Build HTTP 1.1 start-line and header' => sub {
1052  my $req = Mojo::Message::Request->new;
1053  $req->method('GET');
1054  $req->url->parse('http://127.0.0.1/foo/bar');
1055  $req->headers->expect('100-continue');
1056  $req = Mojo::Message::Request->new->parse($req->to_string);
1057  ok $req->is_finished, 'request is finished';
1058  is $req->method,      'GET',      'right method';
1059  is $req->version,     '1.1',      'right version';
1060  is $req->url,         '/foo/bar', 'right URL';
1061  is $req->url->to_abs,     'http://127.0.0.1/foo/bar', 'right absolute URL';
1062  is $req->headers->expect, '100-continue',             'right "Expect" value';
1063  is $req->headers->host,   '127.0.0.1',                'right "Host" value';
1064};
1065
1066subtest 'Build HTTP 1.1 start-line and header (with clone)' => sub {
1067  my $req = Mojo::Message::Request->new;
1068  $req->method('GET');
1069  $req->url->parse('http://127.0.0.1/foo/bar');
1070  $req->headers->expect('100-continue');
1071  my $clone = $req->clone;
1072  $req = Mojo::Message::Request->new->parse($req->to_string);
1073  ok $req->is_finished, 'request is finished';
1074  is $req->method,      'GET',      'right method';
1075  is $req->version,     '1.1',      'right version';
1076  is $req->url,         '/foo/bar', 'right URL';
1077  is $req->url->to_abs,     'http://127.0.0.1/foo/bar', 'right absolute URL';
1078  is $req->headers->expect, '100-continue',             'right "Expect" value';
1079  is $req->headers->host,   '127.0.0.1',                'right "Host" value';
1080  $clone = Mojo::Message::Request->new->parse($clone->to_string);
1081  ok $clone->is_finished, 'request is finished';
1082  is $clone->method,      'GET',      'right method';
1083  is $clone->version,     '1.1',      'right version';
1084  is $clone->url,         '/foo/bar', 'right URL';
1085  is $clone->url->to_abs,     'http://127.0.0.1/foo/bar', 'right absolute URL';
1086  is $clone->headers->expect, '100-continue',             'right "Expect" value';
1087  is $clone->headers->host,   '127.0.0.1',                'right "Host" value';
1088};
1089
1090subtest 'Build HTTP 1.1 start-line and header (with clone and changes)' => sub {
1091  my $req = Mojo::Message::Request->new;
1092  $req->method('GET');
1093  $req->url->parse('http://127.0.0.1/foo/bar');
1094  $req->headers->expect('100-continue');
1095  my $clone = $req->clone;
1096  $clone->method('POST');
1097  $clone->headers->expect('nothing');
1098  $clone->version('1.2');
1099  push @{$clone->url->path->parts}, 'baz';
1100  $req = Mojo::Message::Request->new->parse($req->to_string);
1101  ok $req->is_finished, 'request is finished';
1102  is $req->method,      'GET',      'right method';
1103  is $req->version,     '1.1',      'right version';
1104  is $req->url,         '/foo/bar', 'right URL';
1105  is $req->url->to_abs,     'http://127.0.0.1/foo/bar', 'right absolute URL';
1106  is $req->headers->expect, '100-continue',             'right "Expect" value';
1107  is $req->headers->host,   '127.0.0.1',                'right "Host" value';
1108  $clone = Mojo::Message::Request->new->parse($clone->to_string);
1109  ok $clone->is_finished, 'request is finished';
1110  is $clone->method,      'POST',         'right method';
1111  is $clone->version,     '1.2',          'right version';
1112  is $clone->url,         '/foo/bar/baz', 'right URL';
1113  is $clone->url->to_abs,     'http://127.0.0.1/foo/bar/baz', 'right absolute URL';
1114  is $clone->headers->expect, 'nothing',                      'right "Expect" value';
1115  is $clone->headers->host,   '127.0.0.1',                    'right "Host" value';
1116};
1117
1118subtest 'Build full HTTP 1.1 request' => sub {
1119  my $req      = Mojo::Message::Request->new;
1120  my $finished = undef;
1121  $req->on(finish => sub { $finished = shift->is_finished });
1122  $req->method('get');
1123  $req->url->parse('http://127.0.0.1/foo/bar');
1124  $req->headers->expect('100-continue');
1125  $req->body("Hello World!\n");
1126  $req = Mojo::Message::Request->new->parse($req->to_string);
1127  ok $req->is_finished, 'request is finished';
1128  is $req->method,      'GET',      'right method';
1129  is $req->version,     '1.1',      'right version';
1130  is $req->url,         '/foo/bar', 'right URL';
1131  is $req->url->to_abs,             'http://127.0.0.1/foo/bar', 'right absolute URL';
1132  is $req->headers->expect,         '100-continue',             'right "Expect" value';
1133  is $req->headers->host,           '127.0.0.1',                'right "Host" value';
1134  is $req->headers->content_length, '13',                       'right "Content-Length" value';
1135  is $req->body, "Hello World!\n", 'right content';
1136  ok $finished, 'finish event has been emitted';
1137  ok $req->is_finished, 'request is finished';
1138};
1139
1140subtest 'Build HTTP 1.1 request parts with progress' => sub {
1141  my $req = Mojo::Message::Request->new;
1142  my $state;
1143  my $finished = undef;
1144  my $progress = 0;
1145  $req->on(finish => sub { $finished = shift->is_finished });
1146  $req->on(
1147    progress => sub {
1148      my ($req, $part, $offset) = @_;
1149      $state = $part;
1150      $progress += $offset;
1151    }
1152  );
1153  $req->method('get');
1154  $req->url->parse('http://127.0.0.1/foo/bar');
1155  $req->headers->expect('100-continue');
1156  $req->body("Hello World!\n");
1157  ok !$state,    'no state';
1158  ok !$progress, 'no progress';
1159  ok !$finished, 'not finished';
1160  ok $req->build_start_line, 'built start-line';
1161  is $state, 'start_line', 'made progress on start_line';
1162  ok $progress, 'made progress';
1163  $progress = 0;
1164  ok !$finished, 'not finished';
1165  ok $req->build_headers, 'built headers';
1166  is $state, 'headers', 'made progress on headers';
1167  ok $progress, 'made progress';
1168  $progress = 0;
1169  ok !$finished, 'not finished';
1170  ok $req->build_body, 'built body';
1171  is $state,    'body', 'made progress on headers';
1172  ok $progress, 'made progress';
1173  ok $finished, 'finished';
1174};
1175
1176subtest 'Build full HTTP 1.1 request (with clone)' => sub {
1177  my $req      = Mojo::Message::Request->new;
1178  my $finished = undef;
1179  $req->on(finish => sub { $finished = shift->is_finished });
1180  $req->method('get');
1181  $req->url->parse('http://127.0.0.1/foo/bar');
1182  $req->headers->expect('100-continue');
1183  $req->body("Hello World!\n");
1184  my $clone = $req->clone;
1185  $req = Mojo::Message::Request->new->parse($req->to_string);
1186  ok $req->is_finished, 'request is finished';
1187  is $req->method,      'GET',      'right method';
1188  is $req->version,     '1.1',      'right version';
1189  is $req->url,         '/foo/bar', 'right URL';
1190  is $req->url->to_abs,             'http://127.0.0.1/foo/bar', 'right absolute URL';
1191  is $req->headers->expect,         '100-continue',             'right "Expect" value';
1192  is $req->headers->host,           '127.0.0.1',                'right "Host" value';
1193  is $req->headers->content_length, '13',                       'right "Content-Length" value';
1194  is $req->body, "Hello World!\n", 'right content';
1195  ok $finished, 'finish event has been emitted';
1196  ok $req->is_finished, 'request is finished';
1197  $finished = undef;
1198  $clone    = Mojo::Message::Request->new->parse($clone->to_string);
1199  ok $clone->is_finished, 'request is finished';
1200  is $clone->method,      'GET',      'right method';
1201  is $clone->version,     '1.1',      'right version';
1202  is $clone->url,         '/foo/bar', 'right URL';
1203  is $clone->url->to_abs,             'http://127.0.0.1/foo/bar', 'right absolute URL';
1204  is $clone->headers->expect,         '100-continue',             'right "Expect" value';
1205  is $clone->headers->host,           '127.0.0.1',                'right "Host" value';
1206  is $clone->headers->content_length, '13',                       'right "Content-Length" value';
1207  is $clone->body, "Hello World!\n", 'right content';
1208  ok !$finished, 'finish event has been emitted';
1209  ok $clone->is_finished, 'request is finished';
1210};
1211
1212subtest 'Build full HTTP 1.1 request (roundtrip)' => sub {
1213  my $req = Mojo::Message::Request->new;
1214  $req->method('GET');
1215  $req->url->parse('http://127.0.0.1/foo/bar');
1216  $req->headers->expect('100-continue');
1217  $req->body("Hello World!\n");
1218  $req = Mojo::Message::Request->new->parse($req->to_string);
1219  ok $req->is_finished, 'request is finished';
1220  is $req->method,      'GET',      'right method';
1221  is $req->version,     '1.1',      'right version';
1222  is $req->url,         '/foo/bar', 'right URL';
1223  is $req->url->to_abs,             'http://127.0.0.1/foo/bar', 'right absolute URL';
1224  is $req->headers->expect,         '100-continue',             'right "Expect" value';
1225  is $req->headers->host,           '127.0.0.1',                'right "Host" value';
1226  is $req->headers->content_length, '13',                       'right "Content-Length" value';
1227  is $req->body, "Hello World!\n", 'right content';
1228  my $req2 = Mojo::Message::Request->new->parse($req->to_string);
1229  is $req->content->leftovers, '', 'no leftovers';
1230  is $req->error, undef, 'no error';
1231  is $req2->content->leftovers, '', 'no leftovers';
1232  is $req2->error,       undef, 'no error';
1233  ok $req2->is_finished, 'request is finished';
1234  is $req2->method,      'GET',      'right method';
1235  is $req2->version,     '1.1',      'right version';
1236  is $req2->url,         '/foo/bar', 'right URL';
1237  is $req2->url->to_abs,            'http://127.0.0.1/foo/bar', 'right absolute URL';
1238  is $req2->headers->expect,        '100-continue',             'right "Expect" value';
1239  is $req2->headers->host,          '127.0.0.1',                'right "Host" value';
1240  is $req->headers->content_length, '13',                       'right "Content-Length" value';
1241  is $req->body, "Hello World!\n", 'right content';
1242};
1243
1244subtest 'Build HTTP 1.1 request body' => sub {
1245  my $req      = Mojo::Message::Request->new;
1246  my $finished = undef;
1247  $req->on(finish => sub { $finished = shift->is_finished });
1248  $req->method('get');
1249  $req->url->parse('http://127.0.0.1/foo/bar');
1250  $req->headers->expect('100-continue');
1251  $req->body("Hello World!\n");
1252  my $i = 0;
1253  while (my $chunk = $req->get_body_chunk($i)) { $i += length $chunk }
1254  ok $finished, 'finish event has been emitted';
1255  ok $req->is_finished, 'request is finished';
1256};
1257
1258subtest 'Build HTTP 1.1 POST request without body' => sub {
1259  my $req = Mojo::Message::Request->new;
1260  $req->method('POST');
1261  $req->url->parse('http://127.0.0.1/foo/bar');
1262  $req = Mojo::Message::Request->new->parse($req->to_string);
1263  is $req->method,  'POST',     'right method';
1264  is $req->version, '1.1',      'right version';
1265  is $req->url,     '/foo/bar', 'right URL';
1266  is $req->url->to_abs,             'http://127.0.0.1/foo/bar', 'right absolute URL';
1267  is $req->headers->host,           '127.0.0.1',                'right "Host" value';
1268  is $req->headers->content_length, 0,                          'right "Content-Length" value';
1269  is $req->body, '', 'no content';
1270  ok $req->is_finished, 'request is finished';
1271};
1272
1273subtest 'Build WebSocket handshake request' => sub {
1274  my $req      = Mojo::Message::Request->new;
1275  my $finished = undef;
1276  $req->on(finish => sub { $finished = shift->is_finished });
1277  $req->method('GET');
1278  $req->url->parse('http://example.com/demo');
1279  $req->headers->host('example.com');
1280  $req->headers->connection('Upgrade');
1281  $req->headers->sec_websocket_accept('abcdef=');
1282  $req->headers->sec_websocket_protocol('sample');
1283  $req->headers->upgrade('websocket');
1284  $req = Mojo::Message::Request->new->parse($req->to_string);
1285  ok $req->is_finished, 'request is finished';
1286  is $req->method,      'GET',   'right method';
1287  is $req->version,     '1.1',   'right version';
1288  is $req->url,         '/demo', 'right URL';
1289  is $req->url->to_abs,                     'http://example.com/demo', 'right absolute URL';
1290  is $req->headers->connection,             'Upgrade',                 'right "Connection" value';
1291  is $req->headers->upgrade,                'websocket',               'right "Upgrade" value';
1292  is $req->headers->host,                   'example.com',             'right "Host" value';
1293  is $req->headers->content_length,         undef,                     'no "Content-Length" value';
1294  is $req->headers->sec_websocket_accept,   'abcdef=',                 'right "Sec-WebSocket-Key" value';
1295  is $req->headers->sec_websocket_protocol, 'sample',                  'right "Sec-WebSocket-Protocol" value';
1296  is $req->body, '', 'no content';
1297  ok $finished, 'finish event has been emitted';
1298  ok $req->is_finished, 'request is finished';
1299};
1300
1301subtest 'Build WebSocket handshake request (with clone)' => sub {
1302  my $req = Mojo::Message::Request->new;
1303  $req->method('GET');
1304  $req->url->parse('http://example.com/demo');
1305  $req->headers->host('example.com');
1306  $req->headers->connection('Upgrade');
1307  $req->headers->sec_websocket_accept('abcdef=');
1308  $req->headers->sec_websocket_protocol('sample');
1309  $req->headers->upgrade('websocket');
1310  my $clone = $req->clone;
1311  $req = Mojo::Message::Request->new->parse($req->to_string);
1312  ok $req->is_finished, 'request is finished';
1313  is $req->method,      'GET',   'right method';
1314  is $req->version,     '1.1',   'right version';
1315  is $req->url,         '/demo', 'right URL';
1316  is $req->url->to_abs,                     'http://example.com/demo', 'right absolute URL';
1317  is $req->headers->connection,             'Upgrade',                 'right "Connection" value';
1318  is $req->headers->upgrade,                'websocket',               'right "Upgrade" value';
1319  is $req->headers->host,                   'example.com',             'right "Host" value';
1320  is $req->headers->content_length,         undef,                     'no "Content-Length" value';
1321  is $req->headers->sec_websocket_accept,   'abcdef=',                 'right "Sec-WebSocket-Key" value';
1322  is $req->headers->sec_websocket_protocol, 'sample',                  'right "Sec-WebSocket-Protocol" value';
1323  is $req->body, '', 'no content';
1324  ok $req->is_finished, 'request is finished';
1325  $clone = Mojo::Message::Request->new->parse($clone->to_string);
1326  ok $clone->is_finished, 'request is finished';
1327  is $clone->method,      'GET',   'right method';
1328  is $clone->version,     '1.1',   'right version';
1329  is $clone->url,         '/demo', 'right URL';
1330  is $clone->url->to_abs,                     'http://example.com/demo', 'right absolute URL';
1331  is $clone->headers->connection,             'Upgrade',                 'right "Connection" value';
1332  is $clone->headers->upgrade,                'websocket',               'right "Upgrade" value';
1333  is $clone->headers->host,                   'example.com',             'right "Host" value';
1334  is $req->headers->content_length,           undef,                     'no "Content-Length" value';
1335  is $clone->headers->sec_websocket_accept,   'abcdef=',                 'right "Sec-WebSocket-Key" value';
1336  is $clone->headers->sec_websocket_protocol, 'sample',                  'right "Sec-WebSocket-Protocol" value';
1337  is $clone->body, '', 'no content';
1338  ok $clone->is_finished, 'request is finished';
1339};
1340
1341subtest 'Build WebSocket handshake proxy request' => sub {
1342  my $req      = Mojo::Message::Request->new;
1343  my $finished = undef;
1344  $req->on(finish => sub { $finished = shift->is_finished });
1345  $req->method('GET');
1346  $req->url->parse('http://example.com/demo');
1347  $req->headers->host('example.com');
1348  $req->headers->connection('Upgrade');
1349  $req->headers->sec_websocket_accept('abcdef=');
1350  $req->headers->sec_websocket_protocol('sample');
1351  $req->headers->upgrade('websocket');
1352  $req->proxy(Mojo::URL->new('http://127.0.0.2:8080'));
1353  $req = Mojo::Message::Request->new->parse($req->to_string);
1354  ok $req->is_finished, 'request is finished';
1355  is $req->method,      'GET',   'right method';
1356  is $req->version,     '1.1',   'right version';
1357  is $req->url,         '/demo', 'right URL';
1358  is $req->url->to_abs,                     'http://example.com/demo', 'right absolute URL';
1359  is $req->headers->connection,             'Upgrade',                 'right "Connection" value';
1360  is $req->headers->upgrade,                'websocket',               'right "Upgrade" value';
1361  is $req->headers->host,                   'example.com',             'right "Host" value';
1362  is $req->headers->content_length,         undef,                     'no "Content-Length" value';
1363  is $req->headers->sec_websocket_accept,   'abcdef=',                 'right "Sec-WebSocket-Key" value';
1364  is $req->headers->sec_websocket_protocol, 'sample',                  'right "Sec-WebSocket-Protocol" value';
1365  is $req->body, '', 'no content';
1366  ok $finished, 'finish event has been emitted';
1367  ok $req->is_finished, 'request is finished';
1368};
1369
1370subtest 'Build full HTTP 1.1 proxy request' => sub {
1371  my $req = Mojo::Message::Request->new;
1372  $req->method('GET');
1373  $req->url->parse('http://127.0.0.1/foo/bar');
1374  $req->headers->expect('100-continue');
1375  $req->body("Hello World!\n");
1376  $req->proxy(Mojo::URL->new('http://127.0.0.2:8080'));
1377  $req = Mojo::Message::Request->new->parse($req->to_string);
1378  ok $req->is_finished, 'request is finished';
1379  is $req->method,      'GET',                      'right method';
1380  is $req->version,     '1.1',                      'right version';
1381  is $req->url,         'http://127.0.0.1/foo/bar', 'right URL';
1382  is $req->url->to_abs,             'http://127.0.0.1/foo/bar', 'right absolute URL';
1383  is $req->headers->expect,         '100-continue',             'right "Expect" value';
1384  is $req->headers->host,           '127.0.0.1',                'right "Host" value';
1385  is $req->headers->content_length, '13',                       'right "Content-Length" value';
1386  is $req->body, "Hello World!\n", 'right content';
1387};
1388
1389subtest 'Build full HTTP 1.1 proxy request (proxy disabled)' => sub {
1390  my $req = Mojo::Message::Request->new;
1391  $req->method('GET');
1392  $req->url->parse('http://127.0.0.1/foo/bar');
1393  $req->body("Hello World!\n");
1394  $req->via_proxy(0)->proxy(Mojo::URL->new('http://127.0.0.2:8080'));
1395  $req = Mojo::Message::Request->new->parse($req->to_string);
1396  ok $req->is_finished, 'request is finished';
1397  is $req->method,      'GET',      'right method';
1398  is $req->version,     '1.1',      'right version';
1399  is $req->url,         '/foo/bar', 'right URL';
1400  is $req->url->to_abs,             'http://127.0.0.1/foo/bar', 'right absolute URL';
1401  is $req->headers->host,           '127.0.0.1',                'right "Host" value';
1402  is $req->headers->content_length, '13',                       'right "Content-Length" value';
1403  is $req->body, "Hello World!\n", 'right content';
1404};
1405
1406subtest 'Build full HTTP 1.1 proxy request (HTTPS)' => sub {
1407  my $req = Mojo::Message::Request->new;
1408  $req->method('GET');
1409  $req->url->parse('https://127.0.0.1/foo/bar');
1410  $req->headers->expect('100-continue');
1411  $req->body("Hello World!\n");
1412  $req->proxy(Mojo::URL->new('http://127.0.0.2:8080'));
1413  $req = Mojo::Message::Request->new->parse($req->to_string);
1414  ok $req->is_finished, 'request is finished';
1415  is $req->method,      'GET',      'right method';
1416  is $req->version,     '1.1',      'right version';
1417  is $req->url,         '/foo/bar', 'right URL';
1418  is $req->url->to_abs,             'http://127.0.0.1/foo/bar', 'right absolute URL';
1419  is $req->headers->expect,         '100-continue',             'right "Expect" value';
1420  is $req->headers->host,           '127.0.0.1',                'right "Host" value';
1421  is $req->headers->content_length, '13',                       'right "Content-Length" value';
1422  is $req->body, "Hello World!\n", 'right content';
1423};
1424
1425subtest 'Build full HTTP 1.1 proxy request with basic authentication' => sub {
1426  my $req = Mojo::Message::Request->new;
1427  $req->method('GET');
1428  $req->url->parse('http://Aladdin:open%20sesame@127.0.0.1/foo/bar');
1429  $req->headers->expect('100-continue');
1430  $req->body("Hello World!\n");
1431  $req->proxy(Mojo::URL->new('http://Aladdin:open%20sesame@127.0.0.2:8080'));
1432  $req = Mojo::Message::Request->new->parse($req->to_string);
1433  ok $req->is_finished, 'request is finished';
1434  is $req->method,      'GET',                      'right method';
1435  is $req->version,     '1.1',                      'right version';
1436  is $req->url,         'http://127.0.0.1/foo/bar', 'right URL';
1437  is $req->url->to_abs,                  'http://127.0.0.1/foo/bar',           'right absolute URL';
1438  is $req->proxy->userinfo,              'Aladdin:open sesame',                'right proxy userinfo';
1439  is $req->headers->authorization,       'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==', 'right "Authorization" value';
1440  is $req->headers->expect,              '100-continue',                       'right "Expect" value';
1441  is $req->headers->host,                '127.0.0.1',                          'right "Host" value';
1442  is $req->headers->proxy_authorization, 'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==', 'right "Proxy-Authorization" value';
1443  is $req->headers->content_length,      '13',                                 'right "Content-Length" value';
1444  is $req->body, "Hello World!\n", 'right content';
1445};
1446
1447subtest 'Build full HTTP 1.1 proxy request with basic authentication (and clone)' => sub {
1448  my $req = Mojo::Message::Request->new;
1449  $req->method('GET');
1450  $req->url->parse('http://Aladdin:open%20sesame@127.0.0.1/foo/bar');
1451  $req->headers->expect('100-continue');
1452  $req->body("Hello World!\n");
1453  $req->proxy(Mojo::URL->new('http://Aladdin:open%20sesame@127.0.0.2:8080'));
1454  my $clone = $req->clone;
1455  $req = Mojo::Message::Request->new->parse($req->to_string);
1456  ok $req->is_finished, 'request is finished';
1457  is $req->method,      'GET',                      'right method';
1458  is $req->version,     '1.1',                      'right version';
1459  is $req->url,         'http://127.0.0.1/foo/bar', 'right URL';
1460  is $req->url->to_abs,                  'http://127.0.0.1/foo/bar',           'right absolute URL';
1461  is $req->proxy->userinfo,              'Aladdin:open sesame',                'right proxy userinfo';
1462  is $req->headers->authorization,       'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==', 'right "Authorization" value';
1463  is $req->headers->expect,              '100-continue',                       'right "Expect" value';
1464  is $req->headers->host,                '127.0.0.1',                          'right "Host" value';
1465  is $req->headers->proxy_authorization, 'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==', 'right "Proxy-Authorization" value';
1466  is $req->headers->content_length,      '13',                                 'right "Content-Length" value';
1467  is $req->body, "Hello World!\n", 'right content';
1468  $clone = Mojo::Message::Request->new->parse($clone->to_string);
1469  ok $clone->is_finished, 'request is finished';
1470  is $clone->method,      'GET',                      'right method';
1471  is $clone->version,     '1.1',                      'right version';
1472  is $clone->url,         'http://127.0.0.1/foo/bar', 'right URL';
1473  is $clone->url->to_abs,                  'http://127.0.0.1/foo/bar',           'right absolute URL';
1474  is $clone->proxy->userinfo,              'Aladdin:open sesame',                'right proxy userinfo';
1475  is $clone->headers->authorization,       'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==', 'right "Authorization" value';
1476  is $clone->headers->expect,              '100-continue',                       'right "Expect" value';
1477  is $clone->headers->host,                '127.0.0.1',                          'right "Host" value';
1478  is $clone->headers->proxy_authorization, 'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==', 'right "Proxy-Authorization" value';
1479  is $clone->headers->content_length,      '13',                                 'right "Content-Length" value';
1480  is $clone->body, "Hello World!\n", 'right content';
1481};
1482
1483subtest 'Build full HTTP 1.1 proxy connect request with basic authentication' => sub {
1484  my $req = Mojo::Message::Request->new;
1485  $req->method('CONNECT');
1486  $req->url->parse('http://Aladdin:open%20sesame@bücher.ch:3000/foo/bar');
1487  $req->proxy(Mojo::URL->new('http://Aladdin:open%20sesame@127.0.0.2:8080'));
1488  $req = Mojo::Message::Request->new->parse($req->to_string);
1489  ok $req->is_finished, 'request is finished';
1490  is $req->method,      'CONNECT',                 'right method';
1491  is $req->version,     '1.1',                     'right version';
1492  is $req->url,         '//xn--bcher-kva.ch:3000', 'right URL';
1493  is $req->url->host,                    'xn--bcher-kva.ch',                   'right host';
1494  is $req->url->port,                    '3000',                               'right port';
1495  is $req->url->to_abs,                  'http://xn--bcher-kva.ch:3000',       'right absolute URL';
1496  is $req->proxy->userinfo,              'Aladdin:open sesame',                'right proxy userinfo';
1497  is $req->headers->authorization,       'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==', 'right "Authorization" value';
1498  is $req->headers->host,                'xn--bcher-kva.ch:3000',              'right "Host" value';
1499  is $req->headers->proxy_authorization, 'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==', 'right "Proxy-Authorization" value';
1500};
1501
1502subtest 'Build HTTP 1.1 multipart request' => sub {
1503  my $req = Mojo::Message::Request->new;
1504  $req->method('GET');
1505  $req->url->parse('http://127.0.0.1/foo/bar');
1506  $req->content(Mojo::Content::MultiPart->new);
1507  $req->headers->content_type('multipart/mixed; boundary=7am1X');
1508  push @{$req->content->parts}, Mojo::Content::Single->new;
1509  $req->content->parts->[-1]->asset->add_chunk('Hallo Welt lalalala!');
1510  my $content = Mojo::Content::Single->new;
1511  $content->asset->add_chunk("lala\nfoobar\nperl rocks\n");
1512  $content->headers->content_type('text/plain');
1513  push @{$req->content->parts}, $content;
1514  $req = Mojo::Message::Request->new->parse($req->to_string);
1515  ok $req->is_finished, 'request is finished';
1516  is $req->method,      'GET',      'right method';
1517  is $req->version,     '1.1',      'right version';
1518  is $req->url,         '/foo/bar', 'right URL';
1519  is $req->url->to_abs,             'http://127.0.0.1/foo/bar',        'right absolute URL';
1520  is $req->headers->host,           '127.0.0.1',                       'right "Host" value';
1521  is $req->headers->content_length, '106',                             'right "Content-Length" value';
1522  is $req->headers->content_type,   'multipart/mixed; boundary=7am1X', 'right "Content-Type" value';
1523  is $req->content->parts->[0]->asset->slurp,          'Hallo Welt lalalala!',       'right content';
1524  is $req->content->parts->[1]->headers->content_type, 'text/plain',                 'right "Content-Type" value';
1525  is $req->content->parts->[1]->asset->slurp,          "lala\nfoobar\nperl rocks\n", 'right content';
1526};
1527
1528subtest 'Build HTTP 1.1 multipart request (with clone)' => sub {
1529  my $req = Mojo::Message::Request->new;
1530  $req->method('GET');
1531  $req->url->parse('http://127.0.0.1/foo/bar');
1532  $req->content(Mojo::Content::MultiPart->new);
1533  $req->headers->content_type('multipart/mixed; boundary=7am1X');
1534  push @{$req->content->parts}, Mojo::Content::Single->new;
1535  $req->content->parts->[-1]->asset->add_chunk('Hallo Welt lalalala!');
1536  my $content = Mojo::Content::Single->new;
1537  $content->asset->add_chunk("lala\nfoobar\nperl rocks\n");
1538  $content->headers->content_type('text/plain');
1539  push @{$req->content->parts}, $content;
1540  my $clone = $req->clone;
1541  $req = Mojo::Message::Request->new->parse($req->to_string);
1542  ok $req->is_finished, 'request is finished';
1543  is $req->method,      'GET',      'right method';
1544  is $req->version,     '1.1',      'right version';
1545  is $req->url,         '/foo/bar', 'right URL';
1546  is $req->url->to_abs,             'http://127.0.0.1/foo/bar',        'right absolute URL';
1547  is $req->headers->host,           '127.0.0.1',                       'right "Host" value';
1548  is $req->headers->content_length, '106',                             'right "Content-Length" value';
1549  is $req->headers->content_type,   'multipart/mixed; boundary=7am1X', 'right "Content-Type" value';
1550  is $req->content->parts->[0]->asset->slurp,          'Hallo Welt lalalala!',       'right content';
1551  is $req->content->parts->[1]->headers->content_type, 'text/plain',                 'right "Content-Type" value';
1552  is $req->content->parts->[1]->asset->slurp,          "lala\nfoobar\nperl rocks\n", 'right content';
1553  $clone = Mojo::Message::Request->new->parse($clone->to_string);
1554  ok $clone->is_finished, 'request is finished';
1555  is $clone->method,      'GET',      'right method';
1556  is $clone->version,     '1.1',      'right version';
1557  is $clone->url,         '/foo/bar', 'right URL';
1558  is $clone->url->to_abs,             'http://127.0.0.1/foo/bar',        'right absolute URL';
1559  is $clone->headers->host,           '127.0.0.1',                       'right "Host" value';
1560  is $clone->headers->content_length, '106',                             'right "Content-Length" value';
1561  is $clone->headers->content_type,   'multipart/mixed; boundary=7am1X', 'right "Content-Type" value';
1562  is $clone->content->parts->[0]->asset->slurp,          'Hallo Welt lalalala!',       'right content';
1563  is $clone->content->parts->[1]->headers->content_type, 'text/plain',                 'right "Content-Type" value';
1564  is $clone->content->parts->[1]->asset->slurp,          "lala\nfoobar\nperl rocks\n", 'right content';
1565};
1566
1567subtest 'Build HTTP 1.1 chunked request' => sub {
1568  my $req = Mojo::Message::Request->new;
1569  $req->method('GET');
1570  $req->url->parse('http://127.0.0.1:8080/foo/bar');
1571  $req->headers->transfer_encoding('chunked');
1572  my $counter;
1573  $req->on(progress => sub { $counter++ });
1574  $req->content->write_chunk(
1575    'hello world!' => sub {
1576      shift->write_chunk(
1577        "hello world2!\n\n" => sub {
1578          my $self = shift;
1579          $self->write_chunk('');
1580        }
1581      );
1582    }
1583  );
1584  is $req->clone, undef, 'dynamic requests cannot be cloned';
1585  $req = Mojo::Message::Request->new->parse($req->to_string);
1586  ok $req->is_finished, 'request is finished';
1587  is $req->method,      'GET',      'right method';
1588  is $req->version,     '1.1',      'right version';
1589  is $req->url,         '/foo/bar', 'right URL';
1590  is $req->url->to_abs,                'http://127.0.0.1:8080/foo/bar', 'right absolute URL';
1591  is $req->headers->host,              '127.0.0.1:8080',                'right "Host" value';
1592  is $req->headers->transfer_encoding, undef,                           'no "Transfer-Encoding" value';
1593  is $req->body, "hello world!hello world2!\n\n", 'right content';
1594  ok $counter, 'right counter';
1595};
1596
1597subtest 'Build HTTP 1.1 chunked request' => sub {
1598  my $req = Mojo::Message::Request->new;
1599  $req->method('GET');
1600  $req->url->parse('http://127.0.0.1');
1601  $req->content->write_chunk('hello world!');
1602  $req->content->write_chunk("hello world2!\n\n");
1603  $req->content->write_chunk('');
1604  is $req->clone, undef, 'dynamic requests cannot be cloned';
1605  $req = Mojo::Message::Request->new->parse($req->to_string);
1606  ok $req->is_finished, 'request is finished';
1607  is $req->method,      'GET', 'right method';
1608  is $req->version,     '1.1', 'right version';
1609  is $req->url,         '/',   'right URL';
1610  is $req->url->to_abs,                'http://127.0.0.1/', 'right absolute URL';
1611  is $req->headers->host,              '127.0.0.1',         'right "Host" value';
1612  is $req->headers->transfer_encoding, undef,               'no "Transfer-Encoding" value';
1613  is $req->body, "hello world!hello world2!\n\n", 'right content';
1614};
1615
1616subtest 'Build full HTTP 1.1 request with cookies' => sub {
1617  my $req = Mojo::Message::Request->new;
1618  $req->method('GET');
1619  $req->url->parse('http://127.0.0.1/foo/bar?0');
1620  $req->headers->expect('100-continue');
1621  $req->cookies({name => 'foo', value => 'bar'}, {name => 'bar', value => 'baz'});
1622  $req->cookies(Mojo::Cookie::Request->new(name => 'baz', value => 'yada'));
1623  $req->body("Hello World!\n");
1624  ok !!$req->to_string, 'message built';
1625  my $req2 = Mojo::Message::Request->new;
1626  $req2->parse($req->to_string);
1627  ok $req2->is_finished, 'request is finished';
1628  is $req2->method,      'GET', 'right method';
1629  is $req2->version,     '1.1', 'right version';
1630  is $req2->headers->expect,         '100-continue',               'right "Expect" value';
1631  is $req2->headers->host,           '127.0.0.1',                  'right "Host" value';
1632  is $req2->headers->content_length, 13,                           'right "Content-Length" value';
1633  is $req2->headers->cookie,         'foo=bar; bar=baz; baz=yada', 'right "Cookie" value';
1634  is $req2->url, '/foo/bar?0', 'right URL';
1635  is $req2->url->to_abs, 'http://127.0.0.1/foo/bar?0', 'right absolute URL';
1636  ok defined $req2->cookie('foo'),   'cookie "foo" exists';
1637  ok defined $req2->cookie('bar'),   'cookie "bar" exists';
1638  ok defined $req2->cookie('baz'),   'cookie "baz" exists';
1639  ok !defined $req2->cookie('yada'), 'cookie "yada" does not exist';
1640  is $req2->cookie('foo')->value, 'bar',  'right value';
1641  is $req2->cookie('bar')->value, 'baz',  'right value';
1642  is $req2->cookie('baz')->value, 'yada', 'right value';
1643  is $req2->body, "Hello World!\n", 'right content';
1644};
1645
1646subtest 'Build HTTP 1.1 request with cookies sharing the same name' => sub {
1647  my $req = Mojo::Message::Request->new;
1648  $req->method('GET');
1649  $req->url->parse('http://127.0.0.1/foo/bar');
1650  $req->cookies(
1651    {name => 'foo', value => 'bar'},
1652    {name => 'foo', value => 'baz'},
1653    {name => 'foo', value => 'yada'},
1654    {name => 'bar', value => 'foo'}
1655  );
1656  my $req2 = Mojo::Message::Request->new;
1657  $req2->parse($req->to_string);
1658  ok $req2->is_finished, 'request is finished';
1659  is $req2->method,      'GET', 'right method';
1660  is $req2->version,     '1.1', 'right version';
1661  is $req2->headers->host,   '127.0.0.1',                           'right "Host" value';
1662  is $req2->headers->cookie, 'foo=bar; foo=baz; foo=yada; bar=foo', 'right "Cookie" value';
1663  is $req2->url, '/foo/bar', 'right URL';
1664  is $req2->url->to_abs, 'http://127.0.0.1/foo/bar', 'right absolute URL';
1665  is_deeply [map { $_->value } @{$req2->every_cookie('foo')}], [qw(bar baz yada)], 'right values';
1666  is_deeply [map { $_->value } @{$req2->every_cookie('bar')}], ['foo'], 'right values';
1667};
1668
1669subtest 'Parse full HTTP 1.0 request with cookies and progress callback' => sub {
1670  my $req     = Mojo::Message::Request->new;
1671  my $counter = 0;
1672  $req->on(progress => sub { $counter++ });
1673  is $counter, 0, 'right count';
1674  ok !$req->content->is_parsing_body, 'is not parsing body';
1675  ok !$req->is_finished, 'request is not finished';
1676  $req->parse('GET /foo/bar/baz.html?fo');
1677  is $counter, 1, 'right count';
1678  ok !$req->content->is_parsing_body, 'is not parsing body';
1679  ok !$req->is_finished, 'request is not finished';
1680  $req->parse("o=13 HTTP/1.0\x0d\x0aContent");
1681  is $counter, 2, 'right count';
1682  ok !$req->content->is_parsing_body, 'is not parsing body';
1683  ok !$req->is_finished, 'request is not finished';
1684  $req->parse('-Type: text/');
1685  is $counter, 3, 'right count';
1686  ok !$req->content->is_parsing_body, 'is not parsing body';
1687  ok !$req->is_finished, 'request is not finished';
1688  $req->parse("plain\x0d\x0a");
1689  is $counter, 4, 'right count';
1690  ok !$req->content->is_parsing_body, 'is not parsing body';
1691  ok !$req->is_finished, 'request is not finished';
1692  $req->parse('Cookie: foo=bar; bar=baz');
1693  is $counter, 5, 'right count';
1694  ok !$req->content->is_parsing_body, 'is not parsing body';
1695  ok !$req->is_finished, 'request is not finished';
1696  $req->parse("\x0d\x0a");
1697  is $counter, 6, 'right count';
1698  ok !$req->content->is_parsing_body, 'is not parsing body';
1699  ok !$req->is_finished, 'request is not finished';
1700  $req->parse("Content-Length: 27\x0d\x0a\x0d\x0aHell");
1701  is $counter, 7, 'right count';
1702  ok $req->content->is_parsing_body, 'is parsing body';
1703  ok !$req->is_finished, 'request is not finished';
1704  $req->parse("o World!\n1234\nlalalala\n");
1705  is $counter, 8, 'right count';
1706  ok !$req->content->is_parsing_body, 'is not parsing body';
1707  ok $req->is_finished, 'request is finished';
1708  ok $req->is_finished, 'request is finished';
1709  is $req->method,      'GET',                      'right method';
1710  is $req->version,     '1.0',                      'right version';
1711  is $req->url,         '/foo/bar/baz.html?foo=13', 'right URL';
1712  is $req->headers->content_type,   'text/plain', 'right "Content-Type" value';
1713  is $req->headers->content_length, 27,           'right "Content-Length" value';
1714  my $cookies = $req->cookies;
1715  is $cookies->[0]->name,  'foo', 'right name';
1716  is $cookies->[0]->value, 'bar', 'right value';
1717  is $cookies->[1]->name,  'bar', 'right name';
1718  is $cookies->[1]->value, 'baz', 'right value';
1719};
1720
1721subtest 'Parse and clone multipart/form-data request (changing size)' => sub {
1722  my $req = Mojo::Message::Request->new;
1723  $req->parse("POST /example/testform_handler HTTP/1.1\x0d\x0a");
1724  $req->parse("User-Agent: Mozilla/5.0\x0d\x0a");
1725  $req->parse('Content-Type: multipart/form-data; ');
1726  $req->parse("boundary=----WebKitFormBoundaryi5BnD9J9zoTMiSuP\x0d\x0a");
1727  $req->parse("Content-Length: 318\x0d\x0aConnection: keep-alive\x0d\x0a");
1728  $req->parse("Host: 127.0.0.1:3000\x0d\x0a\x0d\x0a");
1729  $req->parse("------WebKitFormBoundaryi5BnD9J9zoTMiSuP\x0d\x0a");
1730  $req->parse("Content-Disposition: form-data; name=\"Vorname\"\x0a");
1731  $req->parse("\x0d\x0aT\x0d\x0a------WebKitFormBoundaryi5BnD9J9zoTMiSuP\x0d");
1732  $req->parse("\x0aContent-Disposition: form-data; name=\"Zuname\"\x0a");
1733  $req->parse("\x0d\x0a\x0d\x0a------WebKitFormBoundaryi5BnD9J9zoTMiSuP\x0d");
1734  $req->parse("\x0aContent-Disposition: form-data; name=\"Text\"\x0a");
1735  $req->parse("\x0d\x0a\x0d\x0a------WebKitFormBoundaryi5BnD9J9zoTMiSuP--");
1736  ok $req->is_finished, 'request is finished';
1737  is $req->method,      'POST',                      'right method';
1738  is $req->version,     '1.1',                       'right version';
1739  is $req->url,         '/example/testform_handler', 'right URL';
1740  is $req->headers->content_length, 318, 'right "Content-Length" value';
1741  is $req->param('Vorname'), 'T', 'right value';
1742  is $req->param('Zuname'),  '',  'right value';
1743  is $req->param('Text'),    '',  'right value';
1744  is $req->content->parts->[0]->asset->slurp, 'T', 'right content';
1745  is $req->content->leftovers, '', 'no leftovers';
1746  $req = Mojo::Message::Request->new->parse($req->clone->to_string);
1747  ok $req->is_finished, 'request is finished';
1748  is $req->method,      'POST',                      'right method';
1749  is $req->version,     '1.1',                       'right version';
1750  is $req->url,         '/example/testform_handler', 'right URL';
1751  is $req->headers->content_length, 323, 'right "Content-Length" value';
1752  is $req->param('Vorname'), 'T', 'right value';
1753  is $req->param('Zuname'),  '',  'right value';
1754  is $req->param('Text'),    '',  'right value';
1755  is $req->content->parts->[0]->asset->slurp, 'T', 'right content';
1756  is $req->content->leftovers, '', 'no leftovers';
1757  is $req->content->get_body_chunk(322), "\x0a",      'right chunk';
1758  is $req->content->get_body_chunk(321), "\x0d\x0a",  'right chunk';
1759  is $req->content->get_body_chunk(320), "-\x0d\x0a", 'right chunk';
1760};
1761
1762subtest 'Parse multipart/form-data request with charset' => sub {
1763  my $req = Mojo::Message::Request->new;
1764  is $req->default_charset, 'UTF-8', 'default charset is UTF-8';
1765  my $yatta      = 'やった';
1766  my $yatta_sjis = encode 'Shift_JIS', $yatta;
1767  my $multipart
1768    = "------1234567890\x0d\x0a"
1769    . "Content-Disposition: form-data; name=\"$yatta_sjis\"\x0d\x0a\x0d\x0a"
1770    . "$yatta_sjis\x0d\x0a------1234567890--";
1771  $req->parse("POST /example/yatta HTTP/1.1\x0d\x0a"
1772      . "User-Agent: Mozilla/5.0\x0d\x0a"
1773      . 'Content-Type: multipart/form-data; charset=Shift_JIS; '
1774      . "boundary=----1234567890\x0d\x0a"
1775      . "Content-Length: @{[length $multipart]}\x0d\x0a"
1776      . "Connection: keep-alive\x0d\x0a"
1777      . "Host: 127.0.0.1:3000\x0d\x0a\x0d\x0a"
1778      . $multipart);
1779  ok $req->is_finished, 'request is finished';
1780  is $req->method,      'POST',           'right method';
1781  is $req->version,     '1.1',            'right version';
1782  is $req->url,         '/example/yatta', 'right URL';
1783  is $req->param($yatta), $yatta, 'right value';
1784  is $req->content->parts->[0]->asset->slurp, $yatta_sjis, 'right content';
1785};
1786
1787subtest 'WebKit multipart/form-data request' => sub {
1788  my $req = Mojo::Message::Request->new;
1789  $req->parse("POST /example/testform_handler HTTP/1.1\x0d\x0a");
1790  $req->parse("User-Agent: Mozilla/5.0\x0d\x0a");
1791  $req->parse('Content-Type: multipart/form-data; ');
1792  $req->parse("boundary=----WebKitFormBoundaryi5BnD9J9zoTMiSuP\x0d\x0a");
1793  $req->parse("Content-Length: 323\x0d\x0aConnection: keep-alive\x0d\x0a");
1794  $req->parse("Host: 127.0.0.1:3000\x0d\x0a\x0d\x0a");
1795  $req->parse("------WebKitFormBoundaryi5BnD9J9zoTMiSuP\x0d\x0a");
1796  $req->parse("Content-Disposition: form-data; name=\"Vorname\"\x0d\x0a");
1797  $req->parse("\x0d\x0aT\x0d\x0a------WebKitFormBoundaryi5BnD9J9zoTMiSuP\x0d");
1798  $req->parse("\x0aContent-Disposition: form-data; name=\"Zuname\"\x0d\x0a");
1799  $req->parse("\x0d\x0a\x0d\x0a------WebKitFormBoundaryi5BnD9J9zoTMiSuP\x0d");
1800  $req->parse("\x0aContent-Disposition: form-data; name=\"Text\"\x0d\x0a");
1801  $req->parse("\x0d\x0a\x0d\x0a------WebKitFormBoundaryi5BnD9J9zoTMiSuP-");
1802  ok !$req->is_finished, 'request is not finished';
1803  $req->parse('-');
1804  ok !$req->is_finished, 'request is not finished';
1805  $req->parse("\x0d\x0a");
1806  ok $req->is_finished, 'request is finished';
1807  is $req->method,      'POST',                      'right method';
1808  is $req->version,     '1.1',                       'right version';
1809  is $req->url,         '/example/testform_handler', 'right URL';
1810  is $req->param('Vorname'), 'T', 'right value';
1811  is $req->param('Zuname'),  '',  'right value';
1812  is $req->param('Text'),    '',  'right value';
1813  is $req->content->parts->[0]->asset->slurp, 'T', 'right content';
1814};
1815
1816subtest 'Chrome 35 multipart/form-data request (with quotation marks)' => sub {
1817  my $req = Mojo::Message::Request->new;
1818  $req->parse("POST / HTTP/1.1\x0d\x0a");
1819  $req->parse("Host: 127.0.0.1:3000\x0d\x0a");
1820  $req->parse("Connection: keep-alive\x0d\x0a");
1821  $req->parse("Content-Length: 180\x0d\x0a");
1822  $req->parse('Accept: text/html,application/xhtml+xml,application/xml;q=');
1823  $req->parse("0.9,image/webp,*/*;q=0.8\x0d\x0a");
1824  $req->parse("Origin: http://127.0.0.1:3000\x0d\x0a");
1825  $req->parse('User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_0) App');
1826  $req->parse('leWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari');
1827  $req->parse("/537.36\x0d\x0a");
1828  $req->parse('Content-Type: multipart/form-data; boundary=----WebKitFormBoun');
1829  $req->parse("daryMTelhBLWA9N3KXAR\x0d\x0a");
1830  $req->parse("Referer: http://127.0.0.1:3000/\x0d\x0a");
1831  $req->parse("Accept-Encoding: gzip,deflate,sdch\x0d\x0a");
1832  $req->parse("Accept-Language: en-US,en;q=0.8\x0d\x0a\x0d\x0a");
1833  $req->parse("------WebKitFormBoundaryMTelhBLWA9N3KXAR\x0d\x0a");
1834  $req->parse('Content-Disposition: form-data; na');
1835  $req->parse('me="foo \\%22bar%22 baz\"; filename="fo\\%22o%22.txt\"');
1836  $req->parse("\x0d\x0a\x0d\x0atest\x0d\x0a");
1837  $req->parse("------WebKitFormBoundaryMTelhBLWA9N3KXAR--\x0d\x0a");
1838  ok $req->is_finished, 'request is finished';
1839  is $req->method,      'POST', 'right method';
1840  is $req->version,     '1.1',  'right version';
1841  is $req->url,         '/',    'right URL';
1842  is $req->upload('foo \\%22bar%22 baz\\')->filename, 'fo\\%22o%22.txt\\', 'right filename';
1843  is $req->upload('foo \\%22bar%22 baz\\')->slurp,    'test',              'right content';
1844};
1845
1846subtest 'Firefox 24 multipart/form-data request (with quotation marks)' => sub {
1847  my $req = Mojo::Message::Request->new;
1848  $req->parse("POST / HTTP/1.1\x0d\x0a");
1849  $req->parse("Host: 127.0.0.1:3000\x0d\x0a");
1850  $req->parse('User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:24.');
1851  $req->parse("0) Gecko/20100101 Firefox/24.0\x0d\x0a");
1852  $req->parse('Accept: text/html,application/xhtml+xml,application/xml;q=0.9');
1853  $req->parse(",*/*;q=0.8\x0d\x0a");
1854  $req->parse("Accept-Language: de-de,de;q=0.8,en-us;q=0.5,en;q=0.3\x0d\x0a");
1855  $req->parse("Accept-Encoding: gzip, deflate\x0d\x0a");
1856  $req->parse("Referer: http://127.0.0.1:3000/\x0d\x0a");
1857  $req->parse("Connection: keep-alive\x0d\x0a");
1858  $req->parse('Content-Type: multipart/form-data; boundary=-----------------');
1859  $req->parse("----------20773201241877674789807986058\x0d\x0a");
1860  $req->parse("Content-Length: 212\x0d\x0a\x0d\x0a");
1861  $req->parse('-----------------------------2077320124187767478980');
1862  $req->parse("7986058\x0d\x0aContent-Disposition: form-data; na");
1863  $req->parse('me="foo \\\\"bar\\" baz"; filename="fo\\\\"o\\".txt"');
1864  $req->parse("\x0d\x0a\x0d\x0atest\x0d\x0a");
1865  $req->parse('-----------------------------2077320124187767');
1866  $req->parse("4789807986058--\x0d\x0a");
1867  ok $req->is_finished, 'request is finished';
1868  is $req->method,      'POST', 'right method';
1869  is $req->version,     '1.1',  'right version';
1870  is $req->url,         '/',    'right URL';
1871  is $req->upload('foo \\\"bar\" baz')->filename, 'fo\\\\"o\\".txt', 'right filename';
1872  is $req->upload('foo \\\"bar\" baz')->slurp,    'test',            'right content';
1873};
1874
1875subtest 'Chrome 5 multipart/form-data request (UTF-8)' => sub {
1876  my $req = Mojo::Message::Request->new;
1877  my ($fname, $sname, $sex, $avatar, $submit) = map { encode 'UTF-8', $_ } 'Иван', 'Иванов', 'мужской', 'аватар.jpg',
1878    'Сохранить';
1879  my $chrome
1880    = "------WebKitFormBoundaryYGjwdkpB6ZLCZQbX\x0d\x0a"
1881    . "Content-Disposition: form-data; name=\"fname\"\x0d\x0a\x0d\x0a"
1882    . "$fname\x0d\x0a------WebKitFormBoundaryYGjwdkpB6ZLCZQbX\x0d\x0a"
1883    . "Content-Disposition: form-data; name=\"sname\"\x0d\x0a\x0d\x0a"
1884    . "$sname\x0d\x0a------WebKitFormBoundaryYGjwdkpB6ZLCZQbX\x0d\x0a"
1885    . "Content-Disposition: form-data; name=\"sex\"\x0d\x0a\x0d\x0a"
1886    . "$sex\x0d\x0a------WebKitFormBoundaryYGjwdkpB6ZLCZQbX\x0d\x0a"
1887    . "Content-Disposition: form-data; name=\"bdate\"\x0d\x0a\x0d\x0a"
1888    . "16.02.1987\x0d\x0a------WebKitFormBoundaryYGjwdkpB6ZLCZQbX\x0d\x0a"
1889    . "Content-Disposition: form-data; name=\"phone\"\x0d\x0a\x0d\x0a"
1890    . "1234567890\x0d\x0a------WebKitFormBoundaryYGjwdkpB6ZLCZQbX\x0d\x0a"
1891    . "Content-Disposition: form-data; name=\"avatar\"; filename=\"$avatar\""
1892    . "\x0d\x0aContent-Type: image/jpeg\x0d\x0a\x0d\x0a1234"
1893    . "\x0d\x0a------WebKitFormBoundaryYGjwdkpB6ZLCZQbX\x0d\x0a"
1894    . "Content-Disposition: form-data; name=\"submit\"\x0d\x0a\x0d\x0a"
1895    . "$submit\x0d\x0a------WebKitFormBoundaryYGjwdkpB6ZLCZQbX--";
1896  $req->parse("POST / HTTP/1.0\x0d\x0a"
1897      . "Host: 127.0.0.1:10002\x0d\x0a"
1898      . "Connection: close\x0d\x0a"
1899      . 'User-Agent: Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/5'
1900      . "32.9 (KHTML, like Gecko) Chrome/5.0.307.11 Safari/532.9\x0d\x0a"
1901      . "Referer: http://example.org/\x0d\x0a"
1902      . "Content-Length: @{[length $chrome]}\x0d\x0a"
1903      . "Cache-Control: max-age=0\x0d\x0a"
1904      . "Origin: http://example.org\x0d\x0a"
1905      . 'Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryY'
1906      . "GjwdkpB6ZLCZQbX\x0d\x0a"
1907      . 'Accept: application/xml,application/xhtml+xml,text/html;q=0.9,text/'
1908      . "plain;q=0.8,image/png,*/*;q=0.5\x0d\x0a"
1909      . "Accept-Encoding: gzip,deflate,sdch\x0d\x0a"
1910      . 'Cookie: mojolicious=BAcIMTIzNDU2NzgECAgIAwIAAAAXDGFsZXgudm9yb25vdgQ'
1911      . 'AAAB1c2VyBp6FjksAAAAABwAAAGV4cGlyZXM=--1641adddfe885276cda0deb7475f'
1912      . "153a\x0d\x0a"
1913      . "Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.6,en;q=0.4\x0d\x0a"
1914      . "Accept-Charset: windows-1251,utf-8;q=0.7,*;q=0.3\x0d\x0a\x0d\x0a"
1915      . $chrome);
1916  ok $req->is_finished, 'request is finished';
1917  is $req->method,      'POST', 'right method';
1918  is $req->version,     '1.0',  'right version';
1919  is $req->url,         '/',    'right URL';
1920  is $req->cookie('mojolicious')->value, 'BAcIMTIzNDU2NzgECAgIAwIAAAAXDGFsZXgudm9yb25vdgQAAAB1c2VyBp6FjksAAAAABwA'
1921    . 'AAGV4cGlyZXM=--1641adddfe885276cda0deb7475f153a', 'right value';
1922  like $req->headers->content_type, qr!multipart/form-data!, 'right "Content-Type" value';
1923  is $req->param('fname'),  'Иван',       'right value';
1924  is $req->param('sname'),  'Иванов',     'right value';
1925  is $req->param('sex'),    'мужской',    'right value';
1926  is $req->param('bdate'),  '16.02.1987', 'right value';
1927  is $req->param('phone'),  '1234567890', 'right value';
1928  is $req->param('submit'), 'Сохранить',  'right value';
1929  my $upload = $req->upload('avatar');
1930  is $upload->isa('Mojo::Upload'), 1, 'right upload';
1931  is $upload->headers->content_type, 'image/jpeg', 'right "Content-Type" value';
1932  is $upload->filename, 'аватар.jpg', 'right filename';
1933  is $upload->size,     4,            'right size';
1934  is $upload->slurp,    '1234',       'right content';
1935  is $req->content->parts->[0]->asset->slurp, $fname, 'right content';
1936};
1937
1938subtest 'Firefox 3.5.8 multipart/form-data request (UTF-8)' => sub {
1939  my $req = Mojo::Message::Request->new;
1940  my ($fname, $sname, $sex, $avatar, $submit) = map { encode 'UTF-8', $_ } 'Иван', 'Иванов', 'мужской', 'аватар.jpg',
1941    'Сохранить';
1942  my $firefox
1943    = "-----------------------------213090722714721300002030499922\x0d\x0a"
1944    . "Content-Disposition: form-data; name=\"fname\"\x0d\x0a\x0d\x0a"
1945    . "$fname\x0d\x0a"
1946    . "-----------------------------213090722714721300002030499922\x0d\x0a"
1947    . "Content-Disposition: form-data; name=\"sname\"\x0d\x0a\x0d\x0a"
1948    . "$sname\x0d\x0a"
1949    . "-----------------------------213090722714721300002030499922\x0d\x0a"
1950    . "Content-Disposition: form-data; name=\"sex\"\x0d\x0a\x0d\x0a"
1951    . "$sex\x0d\x0a"
1952    . "-----------------------------213090722714721300002030499922\x0d\x0a"
1953    . "Content-Disposition: form-data; name=\"bdate\"\x0d\x0a\x0d\x0a"
1954    . "16.02.1987\x0d\x0a"
1955    . "-----------------------------213090722714721300002030499922\x0d\x0a"
1956    . "Content-Disposition: form-data; name=\"phone\"\x0d\x0a\x0d\x0a"
1957    . "1234567890\x0d\x0a"
1958    . "-----------------------------213090722714721300002030499922\x0d\x0a"
1959    . "Content-Disposition: form-data; name=\"avatar\"; filename=\"$avatar\""
1960    . "\x0d\x0aContent-Type: image/jpeg\x0d\x0a\x0d\x0a1234\x0d\x0a"
1961    . "-----------------------------213090722714721300002030499922\x0d\x0a"
1962    . "Content-Disposition: form-data; name=\"submit\"\x0d\x0a\x0d\x0a"
1963    . "$submit\x0d\x0a"
1964    . '-----------------------------213090722714721300002030499922--';
1965  $req->parse("POST / HTTP/1.0\x0d\x0a"
1966      . "Host: 127.0.0.1:10002\x0d\x0a"
1967      . "Connection: close\x0d\x0a"
1968      . 'User-Agent: Mozilla/5.0 (X11; U; Linux x86_64; ru; rv:1.9.1.8) Geck'
1969      . "o/20100214 Ubuntu/9.10 (karmic) Firefox/3.5.8\x0d\x0a"
1970      . 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q'
1971      . "=0.8\x0d\x0a"
1972      . "Accept-Language: ru,en-us;q=0.7,en;q=0.3\x0d\x0a"
1973      . "Accept-Encoding: gzip,deflate\x0d\x0a"
1974      . "Accept-Charset: windows-1251,utf-8;q=0.7,*;q=0.7\x0d\x0a"
1975      . "Referer: http://example.org/\x0d\x0a"
1976      . 'Cookie: mojolicious=BAcIMTIzNDU2NzgECAgIAwIAAAAXDGFsZXgudm9yb25vdgQ'
1977      . 'AAAB1c2VyBiWFjksAAAAABwAAAGV4cGlyZXM=--cd933a37999e0fa8d7804205e891'
1978      . "93a7\x0d\x0a"
1979      . 'Content-Type: multipart/form-data; boundary=-----------------------'
1980      . "----213090722714721300002030499922\x0d\x0a"
1981      . "Content-Length: @{[length $firefox]}\x0d\x0a\x0d\x0a"
1982      . $firefox);
1983  ok $req->is_finished, 'request is finished';
1984  is $req->method,      'POST', 'right method';
1985  is $req->version,     '1.0',  'right version';
1986  is $req->url,         '/',    'right URL';
1987  is $req->cookie('mojolicious')->value, 'BAcIMTIzNDU2NzgECAgIAwIAAAAXDGFsZXgudm9yb25vdgQAAAB1c2VyBiWFjksAAAAABwA'
1988    . 'AAGV4cGlyZXM=--cd933a37999e0fa8d7804205e89193a7', 'right value';
1989  like $req->headers->content_type, qr!multipart/form-data!, 'right "Content-Type" value';
1990  is $req->param('fname'),  'Иван',       'right value';
1991  is $req->param('sname'),  'Иванов',     'right value';
1992  is $req->param('sex'),    'мужской',    'right value';
1993  is $req->param('bdate'),  '16.02.1987', 'right value';
1994  is $req->param('phone'),  '1234567890', 'right value';
1995  is $req->param('submit'), 'Сохранить',  'right value';
1996  my $upload = $req->upload('avatar');
1997  is $upload->isa('Mojo::Upload'), 1, 'right upload';
1998  is $upload->headers->content_type, 'image/jpeg', 'right "Content-Type" value';
1999  is $upload->filename, 'аватар.jpg', 'right filename';
2000  is $upload->size,     4,            'right size';
2001  is $upload->slurp,    '1234',       'right content';
2002  is $req->content->parts->[0]->asset->slurp, $fname, 'right content';
2003};
2004
2005subtest 'Opera 9.8 multipart/form-data request (UTF-8)' => sub {
2006  my $req = Mojo::Message::Request->new;
2007  my ($fname, $sname, $sex, $avatar, $submit) = map { encode 'UTF-8', $_ } 'Иван', 'Иванов', 'мужской', 'аватар.jpg',
2008    'Сохранить';
2009  my $opera
2010    = "------------IWq9cR9mYYG668xwSn56f0\x0d\x0a"
2011    . "Content-Disposition: form-data; name=\"fname\"\x0d\x0a\x0d\x0a"
2012    . "$fname\x0d\x0a------------IWq9cR9mYYG668xwSn56f0\x0d\x0a"
2013    . "Content-Disposition: form-data; name=\"sname\"\x0d\x0a\x0d\x0a"
2014    . "$sname\x0d\x0a------------IWq9cR9mYYG668xwSn56f0\x0d\x0a"
2015    . "Content-Disposition: form-data; name=\"sex\"\x0d\x0a\x0d\x0a"
2016    . "$sex\x0d\x0a------------IWq9cR9mYYG668xwSn56f0\x0d\x0a"
2017    . "Content-Disposition: form-data; name=\"bdate\"\x0d\x0a\x0d\x0a"
2018    . "16.02.1987\x0d\x0a------------IWq9cR9mYYG668xwSn56f0\x0d\x0a"
2019    . "Content-Disposition: form-data; name=\"phone\"\x0d\x0a\x0d\x0a"
2020    . "1234567890\x0d\x0a------------IWq9cR9mYYG668xwSn56f0\x0d\x0a"
2021    . "Content-Disposition: form-data; name=\"avatar\"; filename=\"$avatar\""
2022    . "\x0d\x0aContent-Type: image/jpeg\x0d\x0a\x0d\x0a"
2023    . "1234\x0d\x0a------------IWq9cR9mYYG668xwSn56f0\x0d\x0a"
2024    . "Content-Disposition: form-data; name=\"submit\"\x0d\x0a\x0d\x0a"
2025    . "$submit\x0d\x0a------------IWq9cR9mYYG668xwSn56f0--";
2026  $req->parse("POST / HTTP/1.0\x0d\x0a"
2027      . "Host: 127.0.0.1:10002\x0d\x0a"
2028      . "Connection: close\x0d\x0a"
2029      . 'User-Agent: Opera/9.80 (X11; Linux x86_64; U; ru) Presto/2.2.15 Ver'
2030      . "sion/10.10\x0d\x0a"
2031      . 'Accept: text/html, application/xml;q=0.9, application/xhtml+xml, im'
2032      . "age/png, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1\x0d\x0a"
2033      . "Accept-Language: ru-RU,ru;q=0.9,en;q=0.8\x0d\x0a"
2034      . "Accept-Charset: iso-8859-1, utf-8, utf-16, *;q=0.1\x0d\x0a"
2035      . "Accept-Encoding: deflate, gzip, x-gzip, identity, *;q=0\x0d\x0a"
2036      . "Referer: http://example.org/\x0d\x0a"
2037      . 'Cookie: mojolicious=BAcIMTIzNDU2NzgECAgIAwIAAAAXDGFsZXgudm9yb25vdgQ'
2038      . 'AAAB1c2VyBhaIjksAAAAABwAAAGV4cGlyZXM=--78a58a94f98ae5b75a489be1189f'
2039      . "2672\x0d\x0a"
2040      . "Cookie2: \$Version=1\x0d\x0a"
2041      . "TE: deflate, gzip, chunked, identity, trailers\x0d\x0a"
2042      . "Content-Length: @{[length $opera]}\x0d\x0a"
2043      . 'Content-Type: multipart/form-data; boundary=----------IWq9cR9mYYG66'
2044      . "8xwSn56f0\x0d\x0a\x0d\x0a"
2045      . $opera);
2046  ok $req->is_finished, 'request is finished';
2047  is $req->method,      'POST', 'right method';
2048  is $req->version,     '1.0',  'right version';
2049  is $req->url,         '/',    'right URL';
2050  is $req->cookie('mojolicious')->value, 'BAcIMTIzNDU2NzgECAgIAwIAAAAXDGFsZXgudm9yb25vdgQAAAB1c2VyBhaIjksAAAAABwA'
2051    . 'AAGV4cGlyZXM=--78a58a94f98ae5b75a489be1189f2672', 'right value';
2052  like $req->headers->content_type, qr!multipart/form-data!, 'right "Content-Type" value';
2053  is $req->param('fname'),  'Иван',       'right value';
2054  is $req->param('sname'),  'Иванов',     'right value';
2055  is $req->param('sex'),    'мужской',    'right value';
2056  is $req->param('bdate'),  '16.02.1987', 'right value';
2057  is $req->param('phone'),  '1234567890', 'right value';
2058  is $req->param('submit'), 'Сохранить',  'right value';
2059  my $upload = $req->upload('avatar');
2060  is $upload->isa('Mojo::Upload'), 1, 'right upload';
2061  is $upload->headers->content_type, 'image/jpeg', 'right "Content-Type" value';
2062  is $upload->filename, 'аватар.jpg', 'right filename';
2063  is $upload->size,     4,            'right size';
2064  is $upload->slurp,    '1234',       'right content';
2065  is $req->content->parts->[0]->asset->slurp, $fname, 'right content';
2066};
2067
2068subtest 'Firefox 14 multipart/form-data request (UTF-8)' => sub {
2069  my $req = Mojo::Message::Request->new;
2070  my ($name, $filename) = map { encode 'UTF-8', $_ } '☃', 'foo bär ☃.txt';
2071  my $firefox
2072    = "-----------------------------1264369278903768281481663536\x0d\x0a"
2073    . "Content-Disposition: form-data; name=\"$name\"; filename=\"$filename\""
2074    . "\x0d\x0aContent-Type: text/plain\x0d\x0a\x0d\x0atest 123\x0d\x0a"
2075    . '-----------------------------1264369278903768281481663536--';
2076  $req->parse("POST /foo HTTP/1.1\x0d\x0a");
2077  $req->parse("Host: 127.0.0.1:3000\x0d\x0a");
2078  $req->parse('User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv');
2079  $req->parse(":14.0) Gecko/20100101 Firefox/14.0.1\x0d\x0a");
2080  $req->parse('Accept: text/html,application/xhtml+xml,application/xml;');
2081  $req->parse("q=0.9,*/*;q=0.8\x0d\x0a");
2082  $req->parse("Accept-Language: en-us,en;q=0.5\x0d\x0a");
2083  $req->parse("Accept-Encoding: gzip, deflate\x0d\x0a");
2084  $req->parse("Connection: keep-alive\x0d\x0a");
2085  $req->parse("Referer: http://127.0.0.1:3000/\x0d\x0a");
2086  $req->parse('Content-Type: multipart/form-data;');
2087  $req->parse('boundary=---------------------------126436927890376828148');
2088  $req->parse("1663536\x0d\x0a");
2089  $req->parse("Content-Length: @{[length $firefox]}\x0d\x0a\x0d\x0a");
2090  $req->parse($firefox);
2091  ok $req->is_finished, 'request is finished';
2092  is $req->method,      'POST', 'right method';
2093  is $req->version,     '1.1',  'right version';
2094  is $req->url,         '/foo', 'right URL';
2095  like $req->headers->content_type, qr!multipart/form-data!, 'right "Content-Type" value';
2096  is $req->upload('☃')->name,     '☃',             'right name';
2097  is $req->upload('☃')->filename, 'foo bär ☃.txt', 'right filename';
2098  is $req->upload('☃')->headers->content_type, 'text/plain', 'right "Content-Type" value';
2099  is $req->upload('☃')->asset->size,           8,            'right size';
2100  is $req->upload('☃')->asset->slurp,          'test 123',   'right content';
2101};
2102
2103subtest 'Parse "~" in URL' => sub {
2104  my $req = Mojo::Message::Request->new;
2105  $req->parse("GET /~foobar/ HTTP/1.1\x0d\x0a\x0d\x0a");
2106  ok $req->is_finished, 'request is finished';
2107  is $req->method,      'GET',       'right method';
2108  is $req->version,     '1.1',       'right version';
2109  is $req->url,         '/~foobar/', 'right URL';
2110};
2111
2112subtest 'Parse ":" in URL' => sub {
2113  my $req = Mojo::Message::Request->new;
2114  $req->parse("GET /perldoc?Mojo::Message::Request HTTP/1.1\x0d\x0a\x0d\x0a");
2115  ok $req->is_finished, 'request is finished';
2116  is $req->method,      'GET',                             'right method';
2117  is $req->version,     '1.1',                             'right version';
2118  is $req->url,         '/perldoc?Mojo::Message::Request', 'right URL';
2119  is $req->url->query->pairs->[0], 'Mojo::Message::Request', 'right value';
2120};
2121
2122subtest 'Parse lots of special characters in URL' => sub {
2123  my $req = Mojo::Message::Request->new;
2124  $req->parse("GET /09azAZ!\$%&'()*+,-./:;=?@[\\]^_`{|}~\xC3\x9F#abc ");
2125  $req->parse("HTTP/1.1\x0d\x0a\x0d\x0a");
2126  ok $req->is_finished, 'request is finished';
2127  is $req->method,      'GET',                                                              'right method';
2128  is $req->version,     '1.1',                                                              'right version';
2129  is $req->url,         "/09azAZ!\$%&'()*+,-./:;=?@%5B%5C%5D%5E_%60%7B%7C%7D~%C3%83%C2%9F", 'right URL';
2130};
2131
2132subtest 'Abstract methods' => sub {
2133  eval { Mojo::Message->cookies };
2134  like $@, qr/Method "cookies" not implemented by subclass/, 'right error';
2135  eval { Mojo::Message->extract_start_line };
2136  like $@, qr/Method "extract_start_line" not implemented by subclass/, 'right error';
2137  eval { Mojo::Message->get_start_line_chunk };
2138  like $@, qr/Method "get_start_line_chunk" not implemented by subclass/, 'right error';
2139  eval { Mojo::Message->start_line_size };
2140  like $@, qr/Method "start_line_size" not implemented by subclass/, 'right error';
2141};
2142
2143done_testing();
2144