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