From aea8f3fe77e80e166b3dff8e6c7a43d010fa7fa8 Mon Sep 17 00:00:00 2001 From: Andy Lester Date: Mon, 9 Aug 2021 13:30:28 -0500 Subject: [PATCH] Convert to subtests for issue #1520 --- t/mojo/response.t | 1915 +++++++++++++++++++++++---------------------- 1 file changed, 983 insertions(+), 932 deletions(-) diff --git a/t/mojo/response.t b/t/mojo/response.t index 2c85ef5731..69ef82b5df 100644 --- a/t/mojo/response.t +++ b/t/mojo/response.t @@ -9,218 +9,228 @@ use Mojo::JSON qw(encode_json); use Mojo::Message::Response; use Mojo::Util qw(encode gzip); -# Defaults -my $res = Mojo::Message::Response->new; -is $res->max_message_size, 2147483648, 'right default'; +subtest 'Defaults' => sub { + my $res = Mojo::Message::Response->new; + is $res->max_message_size, 2147483648, 'right default'; +}; -# Common status codes -$res = Mojo::Message::Response->new; -is $res->code(100)->default_message, 'Continue', 'right message'; -is $res->code(101)->default_message, 'Switching Protocols', 'right message'; -is $res->code(102)->default_message, 'Processing', 'right message'; -is $res->code(103)->default_message, 'Early Hints', 'right message'; -is $res->code(200)->default_message, 'OK', 'right message'; -is $res->code(201)->default_message, 'Created', 'right message'; -is $res->code(202)->default_message, 'Accepted', 'right message'; -is $res->code(203)->default_message, 'Non-Authoritative Information', 'right message'; -is $res->code(204)->default_message, 'No Content', 'right message'; -is $res->code(205)->default_message, 'Reset Content', 'right message'; -is $res->code(206)->default_message, 'Partial Content', 'right message'; -is $res->code(207)->default_message, 'Multi-Status', 'right message'; -is $res->code(208)->default_message, 'Already Reported', 'right message'; -is $res->code(226)->default_message, 'IM Used', 'right message'; -is $res->code(300)->default_message, 'Multiple Choices', 'right message'; -is $res->code(301)->default_message, 'Moved Permanently', 'right message'; -is $res->code(302)->default_message, 'Found', 'right message'; -is $res->code(303)->default_message, 'See Other', 'right message'; -is $res->code(304)->default_message, 'Not Modified', 'right message'; -is $res->code(305)->default_message, 'Use Proxy', 'right message'; -is $res->code(307)->default_message, 'Temporary Redirect', 'right message'; -is $res->code(308)->default_message, 'Permanent Redirect', 'right message'; -is $res->code(400)->default_message, 'Bad Request', 'right message'; -is $res->code(401)->default_message, 'Unauthorized', 'right message'; -is $res->code(402)->default_message, 'Payment Required', 'right message'; -is $res->code(403)->default_message, 'Forbidden', 'right message'; -is $res->code(404)->default_message, 'Not Found', 'right message'; -is $res->code(405)->default_message, 'Method Not Allowed', 'right message'; -is $res->code(406)->default_message, 'Not Acceptable', 'right message'; -is $res->code(407)->default_message, 'Proxy Authentication Required', 'right message'; -is $res->code(408)->default_message, 'Request Timeout', 'right message'; -is $res->code(409)->default_message, 'Conflict', 'right message'; -is $res->code(410)->default_message, 'Gone', 'right message'; -is $res->code(411)->default_message, 'Length Required', 'right message'; -is $res->code(412)->default_message, 'Precondition Failed', 'right message'; -is $res->code(413)->default_message, 'Request Entity Too Large', 'right message'; -is $res->code(414)->default_message, 'Request-URI Too Long', 'right message'; -is $res->code(415)->default_message, 'Unsupported Media Type', 'right message'; -is $res->code(416)->default_message, 'Request Range Not Satisfiable', 'right message'; -is $res->code(417)->default_message, 'Expectation Failed', 'right message'; -is $res->code(418)->default_message, "I'm a teapot", 'right message'; -is $res->code(421)->default_message, 'Misdirected Request', 'right message'; -is $res->code(422)->default_message, 'Unprocessable Entity', 'right message'; -is $res->code(423)->default_message, 'Locked', 'right message'; -is $res->code(424)->default_message, 'Failed Dependency', 'right message'; -is $res->code(425)->default_message, 'Too Early', 'right message'; -is $res->code(426)->default_message, 'Upgrade Required', 'right message'; -is $res->code(428)->default_message, 'Precondition Required', 'right message'; -is $res->code(429)->default_message, 'Too Many Requests', 'right message'; -is $res->code(431)->default_message, 'Request Header Fields Too Large', 'right message'; -is $res->code(451)->default_message, 'Unavailable For Legal Reasons', 'right message'; -is $res->code(500)->default_message, 'Internal Server Error', 'right message'; -is $res->code(501)->default_message, 'Not Implemented', 'right message'; -is $res->code(502)->default_message, 'Bad Gateway', 'right message'; -is $res->code(503)->default_message, 'Service Unavailable', 'right message'; -is $res->code(504)->default_message, 'Gateway Timeout', 'right message'; -is $res->code(505)->default_message, 'HTTP Version Not Supported', 'right message'; -is $res->code(506)->default_message, 'Variant Also Negotiates', 'right message'; -is $res->code(507)->default_message, 'Insufficient Storage', 'right message'; -is $res->code(508)->default_message, 'Loop Detected', 'right message'; -is $res->code(509)->default_message, 'Bandwidth Limit Exceeded', 'right message'; -is $res->code(510)->default_message, 'Not Extended', 'right message'; -is $res->code(511)->default_message, 'Network Authentication Required', 'right message'; -is $res->default_message(100), 'Continue', 'right message'; +subtest 'Common status codes' => sub { + my $res = Mojo::Message::Response->new; + is $res->code(100)->default_message, 'Continue', 'right message'; + is $res->code(101)->default_message, 'Switching Protocols', 'right message'; + is $res->code(102)->default_message, 'Processing', 'right message'; + is $res->code(103)->default_message, 'Early Hints', 'right message'; + is $res->code(200)->default_message, 'OK', 'right message'; + is $res->code(201)->default_message, 'Created', 'right message'; + is $res->code(202)->default_message, 'Accepted', 'right message'; + is $res->code(203)->default_message, 'Non-Authoritative Information', 'right message'; + is $res->code(204)->default_message, 'No Content', 'right message'; + is $res->code(205)->default_message, 'Reset Content', 'right message'; + is $res->code(206)->default_message, 'Partial Content', 'right message'; + is $res->code(207)->default_message, 'Multi-Status', 'right message'; + is $res->code(208)->default_message, 'Already Reported', 'right message'; + is $res->code(226)->default_message, 'IM Used', 'right message'; + is $res->code(300)->default_message, 'Multiple Choices', 'right message'; + is $res->code(301)->default_message, 'Moved Permanently', 'right message'; + is $res->code(302)->default_message, 'Found', 'right message'; + is $res->code(303)->default_message, 'See Other', 'right message'; + is $res->code(304)->default_message, 'Not Modified', 'right message'; + is $res->code(305)->default_message, 'Use Proxy', 'right message'; + is $res->code(307)->default_message, 'Temporary Redirect', 'right message'; + is $res->code(308)->default_message, 'Permanent Redirect', 'right message'; + is $res->code(400)->default_message, 'Bad Request', 'right message'; + is $res->code(401)->default_message, 'Unauthorized', 'right message'; + is $res->code(402)->default_message, 'Payment Required', 'right message'; + is $res->code(403)->default_message, 'Forbidden', 'right message'; + is $res->code(404)->default_message, 'Not Found', 'right message'; + is $res->code(405)->default_message, 'Method Not Allowed', 'right message'; + is $res->code(406)->default_message, 'Not Acceptable', 'right message'; + is $res->code(407)->default_message, 'Proxy Authentication Required', 'right message'; + is $res->code(408)->default_message, 'Request Timeout', 'right message'; + is $res->code(409)->default_message, 'Conflict', 'right message'; + is $res->code(410)->default_message, 'Gone', 'right message'; + is $res->code(411)->default_message, 'Length Required', 'right message'; + is $res->code(412)->default_message, 'Precondition Failed', 'right message'; + is $res->code(413)->default_message, 'Request Entity Too Large', 'right message'; + is $res->code(414)->default_message, 'Request-URI Too Long', 'right message'; + is $res->code(415)->default_message, 'Unsupported Media Type', 'right message'; + is $res->code(416)->default_message, 'Request Range Not Satisfiable', 'right message'; + is $res->code(417)->default_message, 'Expectation Failed', 'right message'; + is $res->code(418)->default_message, "I'm a teapot", 'right message'; + is $res->code(421)->default_message, 'Misdirected Request', 'right message'; + is $res->code(422)->default_message, 'Unprocessable Entity', 'right message'; + is $res->code(423)->default_message, 'Locked', 'right message'; + is $res->code(424)->default_message, 'Failed Dependency', 'right message'; + is $res->code(425)->default_message, 'Too Early', 'right message'; + is $res->code(426)->default_message, 'Upgrade Required', 'right message'; + is $res->code(428)->default_message, 'Precondition Required', 'right message'; + is $res->code(429)->default_message, 'Too Many Requests', 'right message'; + is $res->code(431)->default_message, 'Request Header Fields Too Large', 'right message'; + is $res->code(451)->default_message, 'Unavailable For Legal Reasons', 'right message'; + is $res->code(500)->default_message, 'Internal Server Error', 'right message'; + is $res->code(501)->default_message, 'Not Implemented', 'right message'; + is $res->code(502)->default_message, 'Bad Gateway', 'right message'; + is $res->code(503)->default_message, 'Service Unavailable', 'right message'; + is $res->code(504)->default_message, 'Gateway Timeout', 'right message'; + is $res->code(505)->default_message, 'HTTP Version Not Supported', 'right message'; + is $res->code(506)->default_message, 'Variant Also Negotiates', 'right message'; + is $res->code(507)->default_message, 'Insufficient Storage', 'right message'; + is $res->code(508)->default_message, 'Loop Detected', 'right message'; + is $res->code(509)->default_message, 'Bandwidth Limit Exceeded', 'right message'; + is $res->code(510)->default_message, 'Not Extended', 'right message'; + is $res->code(511)->default_message, 'Network Authentication Required', 'right message'; + is $res->default_message(100), 'Continue', 'right message'; +}; -# Status code ranges -ok $res->code(101)->is_info, 'is in range'; -ok $res->code(199)->is_info, 'is in range'; -ok $res->code(200)->is_success, 'is in range'; -ok $res->code(202)->is_success, 'is in range'; -ok $res->code(299)->is_success, 'is in range'; -ok $res->code(301)->is_redirect, 'is in range'; -ok $res->code(399)->is_redirect, 'is in range'; -ok $res->code(401)->is_client_error, 'is in range'; -ok $res->code(499)->is_client_error, 'is in range'; -ok $res->code(400)->is_error, 'is in range'; -ok $res->code(599)->is_error, 'is in range'; -ok $res->code(501)->is_server_error, 'is in range'; -ok $res->code(599)->is_server_error, 'is in range'; -ok !$res->code(200)->is_info, 'not in range'; -ok !$res->code(199)->is_success, 'not in range'; -ok !$res->code(300)->is_success, 'not in range'; -ok !$res->code(200)->is_redirect, 'not in range'; -ok !$res->code(200)->is_error, 'not in range'; -ok !$res->code(200)->is_client_error, 'not in range'; -ok !$res->code(200)->is_server_error, 'not in range'; -ok !$res->code(undef)->is_success, 'no range'; +subtest 'Status code ranges' => sub { + my $res = Mojo::Message::Response->new; + ok $res->code(101)->is_info, 'is in range'; + ok $res->code(199)->is_info, 'is in range'; + ok $res->code(200)->is_success, 'is in range'; + ok $res->code(202)->is_success, 'is in range'; + ok $res->code(299)->is_success, 'is in range'; + ok $res->code(301)->is_redirect, 'is in range'; + ok $res->code(399)->is_redirect, 'is in range'; + ok $res->code(401)->is_client_error, 'is in range'; + ok $res->code(499)->is_client_error, 'is in range'; + ok $res->code(400)->is_error, 'is in range'; + ok $res->code(599)->is_error, 'is in range'; + ok $res->code(501)->is_server_error, 'is in range'; + ok $res->code(599)->is_server_error, 'is in range'; + ok !$res->code(200)->is_info, 'not in range'; + ok !$res->code(199)->is_success, 'not in range'; + ok !$res->code(300)->is_success, 'not in range'; + ok !$res->code(200)->is_redirect, 'not in range'; + ok !$res->code(200)->is_error, 'not in range'; + ok !$res->code(200)->is_client_error, 'not in range'; + ok !$res->code(200)->is_server_error, 'not in range'; + ok !$res->code(undef)->is_success, 'no range'; +}; -# Status code and message -$res = Mojo::Message::Response->new; -is $res->code, undef, 'no status'; -is $res->default_message, 'Not Found', 'right default message'; -is $res->message, undef, 'no message'; -$res->message('Test'); -is $res->message, 'Test', 'right message'; -$res->code(500); -is $res->code, 500, 'right status'; -is $res->message, 'Test', 'right message'; -is $res->default_message, 'Internal Server Error', 'right default message'; -$res = Mojo::Message::Response->new; -is $res->code(400)->default_message, 'Bad Request', 'right default message'; -$res = Mojo::Message::Response->new; -is $res->code(1)->default_message, '', 'empty default message'; +subtest 'Status code and message' => sub { + my $res = Mojo::Message::Response->new; + is $res->code, undef, 'no status'; + is $res->default_message, 'Not Found', 'right default message'; + is $res->message, undef, 'no message'; + $res->message('Test'); + is $res->message, 'Test', 'right message'; + $res->code(500); + is $res->code, 500, 'right status'; + is $res->message, 'Test', 'right message'; + is $res->default_message, 'Internal Server Error', 'right default message'; + $res = Mojo::Message::Response->new; + is $res->code(400)->default_message, 'Bad Request', 'right default message'; + $res = Mojo::Message::Response->new; + is $res->code(1)->default_message, '', 'empty default message'; +}; -# Parse HTTP 1.1 response start-line, no headers and body -$res = Mojo::Message::Response->new; -$res->parse("HTTP/1.1 200 OK\x0d\x0a\x0d\x0a"); -ok !$res->is_finished, 'response is not finished'; -is $res->code, 200, 'right status'; -is $res->message, 'OK', 'right message'; -is $res->version, '1.1', 'right version'; +subtest 'Parse HTTP 1.1 response start-line, no headers and body' => sub { + my $res = Mojo::Message::Response->new; + $res->parse("HTTP/1.1 200 OK\x0d\x0a\x0d\x0a"); + ok !$res->is_finished, 'response is not finished'; + is $res->code, 200, 'right status'; + is $res->message, 'OK', 'right message'; + is $res->version, '1.1', 'right version'; +}; -# Parse HTTP 1.1 response start-line, no headers and body (small chunks) -$res = Mojo::Message::Response->new; -$res->parse('H'); -ok !$res->is_finished, 'response is not finished'; -$res->parse('T'); -ok !$res->is_finished, 'response is not finished'; -$res->parse('T'); -ok !$res->is_finished, 'response is not finished'; -$res->parse('P'); -ok !$res->is_finished, 'response is not finished'; -$res->parse('/'); -ok !$res->is_finished, 'response is not finished'; -$res->parse('1'); -ok !$res->is_finished, 'response is not finished'; -$res->parse('.'); -ok !$res->is_finished, 'response is not finished'; -$res->parse('1'); -ok !$res->is_finished, 'response is not finished'; -$res->parse(' '); -ok !$res->is_finished, 'response is not finished'; -$res->parse('2'); -ok !$res->is_finished, 'response is not finished'; -$res->parse('0'); -ok !$res->is_finished, 'response is not finished'; -$res->parse('0'); -ok !$res->is_finished, 'response is not finished'; -$res->parse(' '); -ok !$res->is_finished, 'response is not finished'; -$res->parse('O'); -ok !$res->is_finished, 'response is not finished'; -$res->parse('K'); -ok !$res->is_finished, 'response is not finished'; -$res->parse("\x0d"); -ok !$res->is_finished, 'response is not finished'; -$res->parse("\x0a"); -ok !$res->is_finished, 'response is not finished'; -$res->parse("\x0d"); -ok !$res->is_finished, 'response is not finished'; -$res->parse("\x0a"); -ok !$res->is_finished, 'response is not finished'; -is $res->code, 200, 'right status'; -is $res->message, 'OK', 'right message'; -is $res->version, '1.1', 'right version'; +subtest 'Parse HTTP 1.1 response start-line, no headers and body (small chunks)' => sub { + my $res = Mojo::Message::Response->new; + $res->parse('H'); + ok !$res->is_finished, 'response is not finished'; + $res->parse('T'); + ok !$res->is_finished, 'response is not finished'; + $res->parse('T'); + ok !$res->is_finished, 'response is not finished'; + $res->parse('P'); + ok !$res->is_finished, 'response is not finished'; + $res->parse('/'); + ok !$res->is_finished, 'response is not finished'; + $res->parse('1'); + ok !$res->is_finished, 'response is not finished'; + $res->parse('.'); + ok !$res->is_finished, 'response is not finished'; + $res->parse('1'); + ok !$res->is_finished, 'response is not finished'; + $res->parse(' '); + ok !$res->is_finished, 'response is not finished'; + $res->parse('2'); + ok !$res->is_finished, 'response is not finished'; + $res->parse('0'); + ok !$res->is_finished, 'response is not finished'; + $res->parse('0'); + ok !$res->is_finished, 'response is not finished'; + $res->parse(' '); + ok !$res->is_finished, 'response is not finished'; + $res->parse('O'); + ok !$res->is_finished, 'response is not finished'; + $res->parse('K'); + ok !$res->is_finished, 'response is not finished'; + $res->parse("\x0d"); + ok !$res->is_finished, 'response is not finished'; + $res->parse("\x0a"); + ok !$res->is_finished, 'response is not finished'; + $res->parse("\x0d"); + ok !$res->is_finished, 'response is not finished'; + $res->parse("\x0a"); + ok !$res->is_finished, 'response is not finished'; + is $res->code, 200, 'right status'; + is $res->message, 'OK', 'right message'; + is $res->version, '1.1', 'right version'; +}; -# Parse HTTP 1.1 response start-line, no headers and body (no message) -$res = Mojo::Message::Response->new; -$res->parse("HTTP/1.1 200\x0d\x0a\x0d\x0a"); -ok !$res->is_finished, 'response is not finished'; -is $res->code, 200, 'right status'; -is $res->message, undef, 'no message'; -is $res->version, '1.1', 'right version'; +subtest 'Parse HTTP 1.1 response start-line, no headers and body (no message)' => sub { + my $res = Mojo::Message::Response->new; + $res->parse("HTTP/1.1 200\x0d\x0a\x0d\x0a"); + ok !$res->is_finished, 'response is not finished'; + is $res->code, 200, 'right status'; + is $res->message, undef, 'no message'; + is $res->version, '1.1', 'right version'; +}; -# Parse HTTP 1.0 response start-line and headers but no body -$res = Mojo::Message::Response->new; -$res->parse("HTTP/1.0 404 Damn it\x0d\x0a"); -$res->parse("Content-Type: text/plain\x0d\x0a"); -$res->parse("Content-Length: 0\x0d\x0a\x0d\x0a"); -ok $res->is_finished, 'response is finished'; -is $res->code, 404, 'right status'; -is $res->message, 'Damn it', 'right message'; -is $res->version, '1.0', 'right version'; -is $res->headers->content_type, 'text/plain', 'right "Content-Type" value'; -is $res->headers->content_length, 0, 'right "Content-Length" value'; +subtest 'Parse HTTP 1.0 response start-line and headers but no body' => sub { + my $res = Mojo::Message::Response->new; + $res->parse("HTTP/1.0 404 Damn it\x0d\x0a"); + $res->parse("Content-Type: text/plain\x0d\x0a"); + $res->parse("Content-Length: 0\x0d\x0a\x0d\x0a"); + ok $res->is_finished, 'response is finished'; + is $res->code, 404, 'right status'; + is $res->message, 'Damn it', 'right message'; + is $res->version, '1.0', 'right version'; + is $res->headers->content_type, 'text/plain', 'right "Content-Type" value'; + is $res->headers->content_length, 0, 'right "Content-Length" value'; +}; -# Parse full HTTP 1.0 response -$res = Mojo::Message::Response->new; -$res->parse("HTTP/1.0 500 Internal Server Error\x0d\x0a"); -$res->parse("Content-Type: text/plain\x0d\x0a"); -$res->parse("Content-Length: 27\x0d\x0a\x0d\x0a"); -$res->parse("Hello World!\n1234\nlalalala\n"); -ok $res->is_finished, 'response is finished'; -is $res->code, 500, 'right status'; -is $res->message, 'Internal Server Error', 'right message'; -is $res->version, '1.0', 'right version'; -is $res->headers->content_type, 'text/plain', 'right "Content-Type" value'; -is $res->headers->content_length, 27, 'right "Content-Length" value'; -is $res->body, "Hello World!\n1234\nlalalala\n", 'right content'; +subtest 'Parse full HTTP 1.0 response' => sub { + my $res = Mojo::Message::Response->new; + $res->parse("HTTP/1.0 500 Internal Server Error\x0d\x0a"); + $res->parse("Content-Type: text/plain\x0d\x0a"); + $res->parse("Content-Length: 27\x0d\x0a\x0d\x0a"); + $res->parse("Hello World!\n1234\nlalalala\n"); + ok $res->is_finished, 'response is finished'; + is $res->code, 500, 'right status'; + is $res->message, 'Internal Server Error', 'right message'; + is $res->version, '1.0', 'right version'; + is $res->headers->content_type, 'text/plain', 'right "Content-Type" value'; + is $res->headers->content_length, 27, 'right "Content-Length" value'; + is $res->body, "Hello World!\n1234\nlalalala\n", 'right content'; +}; -# Parse full HTTP 1.0 response (keep-alive) -$res = Mojo::Message::Response->new; -$res->parse("HTTP/1.0 500 Internal Server Error\x0d\x0a"); -$res->parse("Connection: keep-alive\x0d\x0a\x0d\x0a"); -$res->parse("HTTP/1.0 200 OK\x0d\x0a\x0d\x0a"); -ok $res->is_finished, 'response is finished'; -is $res->code, 500, 'right status'; -is $res->message, 'Internal Server Error', 'right message'; -is $res->version, '1.0', 'right version'; -is $res->body, '', 'no content'; -is $res->content->leftovers, "HTTP/1.0 200 OK\x0d\x0a\x0d\x0a", 'next response in leftovers'; +subtest 'Parse full HTTP 1.0 response (keep-alive)' => sub { + my $res = Mojo::Message::Response->new; + $res->parse("HTTP/1.0 500 Internal Server Error\x0d\x0a"); + $res->parse("Connection: keep-alive\x0d\x0a\x0d\x0a"); + $res->parse("HTTP/1.0 200 OK\x0d\x0a\x0d\x0a"); + ok $res->is_finished, 'response is finished'; + is $res->code, 500, 'right status'; + is $res->message, 'Internal Server Error', 'right message'; + is $res->version, '1.0', 'right version'; + is $res->body, '', 'no content'; + is $res->content->leftovers, "HTTP/1.0 200 OK\x0d\x0a\x0d\x0a", 'next response in leftovers'; +}; -# Parse full HTTP 1.0 response (no limit) -{ +subtest 'Parse full HTTP 1.0 response (no limit)' => sub { local $ENV{MOJO_MAX_MESSAGE_SIZE} = 0; - $res = Mojo::Message::Response->new; + my $res = Mojo::Message::Response->new; is $res->max_message_size, 0, 'right size'; $res->parse("HTTP/1.0 500 Internal Server Error\x0d\x0a"); $res->parse("Content-Type: text/plain\x0d\x0a"); @@ -234,145 +244,153 @@ is $res->content->leftovers, "HTTP/1.0 200 OK\x0d\x0a\x0d\x0a", 'next response i is $res->headers->content_type, 'text/plain', 'right "Content-Type" value'; is $res->headers->content_length, 27, 'right "Content-Length" value'; is $res->body, "Hello World!\n1234\nlalalala\n", 'right content'; -} +}; -# Parse broken start-line -$res = Mojo::Message::Response->new; -$res->parse("12345\x0d\x0a"); -ok $res->is_finished, 'response is finished'; -is $res->error->{message}, 'Bad response start-line', 'right error'; +subtest 'Parse broken start-line' => sub { + my $res = Mojo::Message::Response->new; + $res->parse("12345\x0d\x0a"); + ok $res->is_finished, 'response is finished'; + is $res->error->{message}, 'Bad response start-line', 'right error'; +}; -# Parse full HTTP 1.0 response (missing Content-Length) -$res = Mojo::Message::Response->new; -$res->parse("HTTP/1.0 500 Internal Server Error\x0d\x0a"); -$res->parse("Content-Type: text/plain\x0d\x0a"); -$res->parse("Connection: close\x0d\x0a\x0d\x0a"); -$res->parse("Hello World!\n1234\nlalalala\n"); -ok !$res->is_finished, 'response is not finished'; -is $res->code, 500, 'right status'; -is $res->message, 'Internal Server Error', 'right message'; -is $res->version, '1.0', 'right version'; -is $res->headers->content_type, 'text/plain', 'right "Content-Type" value'; -is $res->headers->content_length, undef, 'no "Content-Length" value'; -is $res->body, "Hello World!\n1234\nlalalala\n", 'right content'; +subtest 'Parse full HTTP 1.0 response (missing Content-Length)' => sub { + my $res = Mojo::Message::Response->new; + $res->parse("HTTP/1.0 500 Internal Server Error\x0d\x0a"); + $res->parse("Content-Type: text/plain\x0d\x0a"); + $res->parse("Connection: close\x0d\x0a\x0d\x0a"); + $res->parse("Hello World!\n1234\nlalalala\n"); + ok !$res->is_finished, 'response is not finished'; + is $res->code, 500, 'right status'; + is $res->message, 'Internal Server Error', 'right message'; + is $res->version, '1.0', 'right version'; + is $res->headers->content_type, 'text/plain', 'right "Content-Type" value'; + is $res->headers->content_length, undef, 'no "Content-Length" value'; + is $res->body, "Hello World!\n1234\nlalalala\n", 'right content'; +}; -# Parse full HTTP 1.0 response (missing Content-Length and Connection) -$res = Mojo::Message::Response->new; -$res->parse("HTTP/1.0 500 Internal Server Error\x0d\x0a"); -$res->parse("Content-Type: text/plain\x0d\x0a\x0d\x0a"); -$res->parse("Hello World!\n1"); -$res->parse("234\nlala"); -$res->parse("lala\n"); -ok !$res->is_finished, 'response is not finished'; -is $res->code, 500, 'right status'; -is $res->message, 'Internal Server Error', 'right message'; -is $res->version, '1.0', 'right version'; -is $res->headers->content_type, 'text/plain', 'right "Content-Type" value'; -is $res->headers->content_length, undef, 'no "Content-Length" value'; -is $res->body, "Hello World!\n1234\nlalalala\n", 'right content'; +subtest 'Parse full HTTP 1.0 response (missing Content-Length and Connection)' => sub { + my $res = Mojo::Message::Response->new; + $res->parse("HTTP/1.0 500 Internal Server Error\x0d\x0a"); + $res->parse("Content-Type: text/plain\x0d\x0a\x0d\x0a"); + $res->parse("Hello World!\n1"); + $res->parse("234\nlala"); + $res->parse("lala\n"); + ok !$res->is_finished, 'response is not finished'; + is $res->code, 500, 'right status'; + is $res->message, 'Internal Server Error', 'right message'; + is $res->version, '1.0', 'right version'; + is $res->headers->content_type, 'text/plain', 'right "Content-Type" value'; + is $res->headers->content_length, undef, 'no "Content-Length" value'; + is $res->body, "Hello World!\n1234\nlalalala\n", 'right content'; +}; -# Parse full HTTP 1.1 response (missing Content-Length) -$res = Mojo::Message::Response->new; -$res->parse("HTTP/1.1 500 Internal Server Error\x0d\x0a"); -$res->parse("Content-Type: text/plain\x0d\x0a"); -$res->parse("Connection: close\x0d\x0a\x0d\x0a"); -$res->parse("Hello World!\n1234\nlalalala\n"); -ok !$res->is_finished, 'response is not finished'; -ok !$res->is_empty, 'response is not empty'; -ok !$res->content->skip_body, 'body has not been skipped'; -ok $res->content->relaxed, 'relaxed response'; -is $res->code, 500, 'right status'; -is $res->message, 'Internal Server Error', 'right message'; -is $res->version, '1.1', 'right version'; -is $res->headers->content_type, 'text/plain', 'right "Content-Type" value'; -is $res->headers->content_length, undef, 'no "Content-Length" value'; -is $res->body, "Hello World!\n1234\nlalalala\n", 'right content'; +subtest 'Parse full HTTP 1.1 response (missing Content-Length)' => sub { + my $res = Mojo::Message::Response->new; + $res->parse("HTTP/1.1 500 Internal Server Error\x0d\x0a"); + $res->parse("Content-Type: text/plain\x0d\x0a"); + $res->parse("Connection: close\x0d\x0a\x0d\x0a"); + $res->parse("Hello World!\n1234\nlalalala\n"); + ok !$res->is_finished, 'response is not finished'; + ok !$res->is_empty, 'response is not empty'; + ok !$res->content->skip_body, 'body has not been skipped'; + ok $res->content->relaxed, 'relaxed response'; + is $res->code, 500, 'right status'; + is $res->message, 'Internal Server Error', 'right message'; + is $res->version, '1.1', 'right version'; + is $res->headers->content_type, 'text/plain', 'right "Content-Type" value'; + is $res->headers->content_length, undef, 'no "Content-Length" value'; + is $res->body, "Hello World!\n1234\nlalalala\n", 'right content'; +}; -# Parse full HTTP 1.1 response (broken Content-Length) -$res = Mojo::Message::Response->new; -$res->parse("HTTP/1.1 200 OK\x0d\x0a"); -$res->parse("Content-Length: 123test\x0d\x0a\x0d\x0a"); -$res->parse('Hello World!'); -ok $res->is_finished, 'response is finished'; -is $res->code, 200, 'right status'; -is $res->message, 'OK', 'right message'; -is $res->version, '1.1', 'right version'; -is $res->headers->content_length, '123test', 'right "Content-Length" value'; -is $res->body, '', 'no content'; -is $res->content->leftovers, 'Hello World!', 'content in leftovers'; +subtest 'Parse full HTTP 1.1 response (broken Content-Length)' => sub { + my $res = Mojo::Message::Response->new; + $res->parse("HTTP/1.1 200 OK\x0d\x0a"); + $res->parse("Content-Length: 123test\x0d\x0a\x0d\x0a"); + $res->parse('Hello World!'); + ok $res->is_finished, 'response is finished'; + is $res->code, 200, 'right status'; + is $res->message, 'OK', 'right message'; + is $res->version, '1.1', 'right version'; + is $res->headers->content_length, '123test', 'right "Content-Length" value'; + is $res->body, '', 'no content'; + is $res->content->leftovers, 'Hello World!', 'content in leftovers'; +}; -# Parse full HTTP 1.1 response (100 Continue) -$res = Mojo::Message::Response->new; -$res->content->on(body => sub { shift->headers->header('X-Body' => 'one') }); -$res->on(progress => sub { shift->headers->header('X-Progress' => 'two') }); -$res->on(finish => sub { shift->headers->header('X-Finish' => 'three') }); -$res->parse("HTTP/1.1 100 Continue\x0d\x0a\x0d\x0a"); -ok $res->is_finished, 'response is finished'; -ok $res->is_empty, 'response is empty'; -ok $res->content->skip_body, 'body has been skipped'; -is $res->code, 100, 'right status'; -is $res->message, 'Continue', 'right message'; -is $res->version, '1.1', 'right version'; -is $res->headers->content_length, undef, 'no "Content-Length" value'; -is $res->headers->header('X-Body'), 'one', 'right "X-Body" value'; -is $res->headers->header('X-Progress'), 'two', 'right "X-Progress" value'; -is $res->headers->header('X-Finish'), 'three', 'right "X-Finish" value'; -is $res->body, '', 'no content'; +subtest 'Parse full HTTP 1.1 response (100 Continue)' => sub { + my $res = Mojo::Message::Response->new; + $res->content->on(body => sub { shift->headers->header('X-Body' => 'one') }); + $res->on(progress => sub { shift->headers->header('X-Progress' => 'two') }); + $res->on(finish => sub { shift->headers->header('X-Finish' => 'three') }); + $res->parse("HTTP/1.1 100 Continue\x0d\x0a\x0d\x0a"); + ok $res->is_finished, 'response is finished'; + ok $res->is_empty, 'response is empty'; + ok $res->content->skip_body, 'body has been skipped'; + is $res->code, 100, 'right status'; + is $res->message, 'Continue', 'right message'; + is $res->version, '1.1', 'right version'; + is $res->headers->content_length, undef, 'no "Content-Length" value'; + is $res->headers->header('X-Body'), 'one', 'right "X-Body" value'; + is $res->headers->header('X-Progress'), 'two', 'right "X-Progress" value'; + is $res->headers->header('X-Finish'), 'three', 'right "X-Finish" value'; + is $res->body, '', 'no content'; +}; -# Parse full HTTP 1.1 response (304 Not Modified) -$res = Mojo::Message::Response->new; -$res->parse("HTTP/1.1 304 Not Modified\x0d\x0a"); -$res->parse("Content-Type: text/html\x0d\x0a"); -$res->parse("Content-Length: 9000\x0d\x0a"); -$res->parse("Connection: keep-alive\x0d\x0a\x0d\x0a"); -ok $res->is_finished, 'response is finished'; -ok $res->is_empty, 'response is empty'; -ok $res->content->skip_body, 'body has been skipped'; -is $res->code, 304, 'right status'; -is $res->message, 'Not Modified', 'right message'; -is $res->version, '1.1', 'right version'; -is $res->headers->content_type, 'text/html', 'right "Content-Type" value'; -is $res->headers->content_length, 9000, 'right "Content-Length" value'; -is $res->headers->connection, 'keep-alive', 'right "Connection" value'; -is $res->body, '', 'no content'; +subtest 'Parse full HTTP 1.1 response (304 Not Modified)' => sub { + my $res = Mojo::Message::Response->new; + $res->parse("HTTP/1.1 304 Not Modified\x0d\x0a"); + $res->parse("Content-Type: text/html\x0d\x0a"); + $res->parse("Content-Length: 9000\x0d\x0a"); + $res->parse("Connection: keep-alive\x0d\x0a\x0d\x0a"); + ok $res->is_finished, 'response is finished'; + ok $res->is_empty, 'response is empty'; + ok $res->content->skip_body, 'body has been skipped'; + is $res->code, 304, 'right status'; + is $res->message, 'Not Modified', 'right message'; + is $res->version, '1.1', 'right version'; + is $res->headers->content_type, 'text/html', 'right "Content-Type" value'; + is $res->headers->content_length, 9000, 'right "Content-Length" value'; + is $res->headers->connection, 'keep-alive', 'right "Connection" value'; + is $res->body, '', 'no content'; +}; -# Parse full HTTP 1.1 response (204 No Content) -$res = Mojo::Message::Response->new; -$res->content->on(body => sub { shift->headers->header('X-Body' => 'one') }); -$res->on(finish => sub { shift->headers->header('X-Finish' => 'two') }); -$res->parse("HTTP/1.1 204 No Content\x0d\x0a"); -$res->parse("Content-Type: text/html\x0d\x0a"); -$res->parse("Content-Length: 9001\x0d\x0a"); -$res->parse("Connection: keep-alive\x0d\x0a\x0d\x0a"); -ok $res->is_finished, 'response is finished'; -ok $res->is_empty, 'response is empty'; -ok $res->content->skip_body, 'body has been skipped'; -is $res->code, 204, 'right status'; -is $res->message, 'No Content', 'right message'; -is $res->version, '1.1', 'right version'; -is $res->headers->content_type, 'text/html', 'right "Content-Type" value'; -is $res->headers->content_length, 9001, 'right "Content-Length" value'; -is $res->headers->connection, 'keep-alive', 'right "Connection" value'; -is $res->headers->header('X-Body'), 'one', 'right "X-Body" value'; -is $res->headers->header('X-Finish'), 'two', 'right "X-Finish" value'; -is $res->body, '', 'no content'; +subtest 'Parse full HTTP 1.1 response (204 No Content)' => sub { + my $res = Mojo::Message::Response->new; + $res->content->on(body => sub { shift->headers->header('X-Body' => 'one') }); + $res->on(finish => sub { shift->headers->header('X-Finish' => 'two') }); + $res->parse("HTTP/1.1 204 No Content\x0d\x0a"); + $res->parse("Content-Type: text/html\x0d\x0a"); + $res->parse("Content-Length: 9001\x0d\x0a"); + $res->parse("Connection: keep-alive\x0d\x0a\x0d\x0a"); + ok $res->is_finished, 'response is finished'; + ok $res->is_empty, 'response is empty'; + ok $res->content->skip_body, 'body has been skipped'; + is $res->code, 204, 'right status'; + is $res->message, 'No Content', 'right message'; + is $res->version, '1.1', 'right version'; + is $res->headers->content_type, 'text/html', 'right "Content-Type" value'; + is $res->headers->content_length, 9001, 'right "Content-Length" value'; + is $res->headers->connection, 'keep-alive', 'right "Connection" value'; + is $res->headers->header('X-Body'), 'one', 'right "X-Body" value'; + is $res->headers->header('X-Finish'), 'two', 'right "X-Finish" value'; + is $res->body, '', 'no content'; +}; -# Parse HTTP 1.1 response (413 error in one big chunk) -$res = Mojo::Message::Response->new; -$res->parse("HTTP/1.1 413 Request Entity Too Large\x0d\x0a" - . "Connection: Close\x0d\x0a" - . "Date: Tue, 09 Feb 2010 16:34:51 GMT\x0d\x0a" - . "Server: Mojolicious (Perl)\x0d\x0a\x0d\x0a"); -ok !$res->is_finished, 'response is not finished'; -is $res->code, 413, 'right status'; -is $res->message, 'Request Entity Too Large', 'right message'; -is $res->version, '1.1', 'right version'; -is $res->headers->content_length, undef, 'right "Content-Length" value'; +subtest 'Parse HTTP 1.1 response (413 error in one big chunk)' => sub { + my $res = Mojo::Message::Response->new; + $res->parse("HTTP/1.1 413 Request Entity Too Large\x0d\x0a" + . "Connection: Close\x0d\x0a" + . "Date: Tue, 09 Feb 2010 16:34:51 GMT\x0d\x0a" + . "Server: Mojolicious (Perl)\x0d\x0a\x0d\x0a"); + ok !$res->is_finished, 'response is not finished'; + is $res->code, 413, 'right status'; + is $res->message, 'Request Entity Too Large', 'right message'; + is $res->version, '1.1', 'right version'; + is $res->headers->content_length, undef, 'right "Content-Length" value'; +}; -# Parse HTTP 1.1 chunked response (exceeding limit) -{ +subtest 'Parse HTTP 1.1 chunked response (exceeding limit)' => sub { local $ENV{MOJO_MAX_BUFFER_SIZE} = 12; - $res = Mojo::Message::Response->new; + my $res = Mojo::Message::Response->new; is $res->content->max_buffer_size, 12, 'right size'; $res->parse("HTTP/1.1 200 OK\x0d\x0a"); $res->parse("Content-Type: text/plain\x0d\x0a"); @@ -387,12 +405,11 @@ is $res->headers->content_length, undef, 'right "Content-Length" value'; is $res->message, 'OK', 'right message'; is $res->version, '1.1', 'right version'; is $res->headers->content_type, 'text/plain', 'right "Content-Type" value'; -} +}; -# Parse HTTP 1.1 multipart response (exceeding limit) -{ +subtest 'Parse HTTP 1.1 multipart response (exceeding limit)' => sub { local $ENV{MOJO_MAX_BUFFER_SIZE} = 12; - $res = Mojo::Message::Response->new; + my $res = Mojo::Message::Response->new; is $res->content->max_buffer_size, 12, 'right size'; $res->parse("HTTP/1.1 200 OK\x0d\x0a"); $res->parse("Content-Length: 420\x0d\x0a"); @@ -409,12 +426,11 @@ is $res->headers->content_length, undef, 'right "Content-Length" value'; is $res->version, '1.1', 'right version'; is $res->headers->content_type, 'multipart/form-data; boundary=----------0xKhTmLbOuNdArY', 'right "Content-Type" value'; -} +}; -# Parse HTTP 1.1 gzip compressed response (garbage bytes exceeding limit) -{ +subtest 'Parse HTTP 1.1 gzip compressed response (garbage bytes exceeding limit)' => sub { local $ENV{MOJO_MAX_BUFFER_SIZE} = 12; - $res = Mojo::Message::Response->new; + my $res = Mojo::Message::Response->new; is $res->content->max_buffer_size, 12, 'right size'; $res->parse("HTTP/1.1 200 OK\x0d\x0a"); $res->parse("Content-Length: 1000\x0d\x0a"); @@ -430,638 +446,673 @@ is $res->headers->content_length, undef, 'right "Content-Length" value'; is $res->message, 'OK', 'right message'; is $res->version, '1.1', 'right version'; is $res->body, '', 'no content'; -} +}; + +subtest 'Parse HTTP 1.1 chunked response' => sub { + my $res = Mojo::Message::Response->new; + $res->parse("HTTP/1.1 500 Internal Server Error\x0d\x0a"); + $res->parse("Content-Type: text/plain\x0d\x0a"); + $res->parse("Transfer-Encoding: chunked\x0d\x0a\x0d\x0a"); + $res->parse("4\x0d\x0a"); + $res->parse("abcd\x0d\x0a"); + $res->parse("9\x0d\x0a"); + $res->parse("abcdefghi\x0d\x0a"); + $res->parse("0\x0d\x0a\x0d\x0a"); + ok $res->is_finished, 'response is finished'; + is $res->code, 500, 'right status'; + is $res->message, 'Internal Server Error', 'right message'; + is $res->version, '1.1', 'right version'; + is $res->headers->content_type, 'text/plain', 'right "Content-Type" value'; + is $res->headers->content_length, 13, 'right "Content-Length" value'; + is $res->headers->transfer_encoding, undef, 'no "Transfer-Encoding" value'; + is $res->body_size, 13, 'right size'; +}; -# Parse HTTP 1.1 chunked response -$res = Mojo::Message::Response->new; -$res->parse("HTTP/1.1 500 Internal Server Error\x0d\x0a"); -$res->parse("Content-Type: text/plain\x0d\x0a"); -$res->parse("Transfer-Encoding: chunked\x0d\x0a\x0d\x0a"); -$res->parse("4\x0d\x0a"); -$res->parse("abcd\x0d\x0a"); -$res->parse("9\x0d\x0a"); -$res->parse("abcdefghi\x0d\x0a"); -$res->parse("0\x0d\x0a\x0d\x0a"); -ok $res->is_finished, 'response is finished'; -is $res->code, 500, 'right status'; -is $res->message, 'Internal Server Error', 'right message'; -is $res->version, '1.1', 'right version'; -is $res->headers->content_type, 'text/plain', 'right "Content-Type" value'; -is $res->headers->content_length, 13, 'right "Content-Length" value'; -is $res->headers->transfer_encoding, undef, 'no "Transfer-Encoding" value'; -is $res->body_size, 13, 'right size'; +subtest 'Parse HTTP 1.1 multipart response' => sub { + my $res = Mojo::Message::Response->new; + $res->parse("HTTP/1.1 200 OK\x0d\x0a"); + $res->parse("Content-Length: 420\x0d\x0a"); + $res->parse('Content-Type: multipart/form-data; bo'); + $res->parse("undary=----------0xKhTmLbOuNdArY\x0d\x0a\x0d\x0a"); + $res->parse("\x0d\x0a------------0xKhTmLbOuNdArY\x0d\x0a"); + $res->parse("Content-Disposition: form-data; name=\"text1\"\x0d\x0a"); + $res->parse("\x0d\x0ahallo welt test123\n"); + $res->parse("\x0d\x0a------------0xKhTmLbOuNdArY\x0d\x0a"); + $res->parse("Content-Disposition: form-data; name=\"text2\"\x0d\x0a"); + $res->parse("\x0d\x0a\x0d\x0a------------0xKhTmLbOuNdArY\x0d\x0a"); + $res->parse('Content-Disposition: form-data; name="upload"; file'); + $res->parse("name=\"hello.pl\"\x0d\x0a\x0d\x0a"); + $res->parse("Content-Type: application/octet-stream\x0d\x0a\x0d\x0a"); + $res->parse("#!/usr/bin/perl\n\n"); + $res->parse("use strict;\n"); + $res->parse("use warnings;\n\n"); + $res->parse("print \"Hello World :)\\n\"\n"); + $res->parse("\x0d\x0a------------0xKhTmLbOuNdArY--"); + ok $res->is_finished, 'response is finished'; + is $res->code, 200, 'right status'; + is $res->message, 'OK', 'right message'; + is $res->version, '1.1', 'right version'; + is $res->headers->content_type, 'multipart/form-data; boundary=----------0xKhTmLbOuNdArY', + 'right "Content-Type" value'; + ok !$res->content->parts->[0]->is_multipart, 'no multipart content'; + ok !$res->content->parts->[1]->is_multipart, 'no multipart content'; + ok !$res->content->parts->[2]->is_multipart, 'no multipart content'; + is $res->content->parts->[0]->asset->slurp, "hallo welt test123\n", 'right content'; -# Parse HTTP 1.1 multipart response -$res = Mojo::Message::Response->new; -$res->parse("HTTP/1.1 200 OK\x0d\x0a"); -$res->parse("Content-Length: 420\x0d\x0a"); -$res->parse('Content-Type: multipart/form-data; bo'); -$res->parse("undary=----------0xKhTmLbOuNdArY\x0d\x0a\x0d\x0a"); -$res->parse("\x0d\x0a------------0xKhTmLbOuNdArY\x0d\x0a"); -$res->parse("Content-Disposition: form-data; name=\"text1\"\x0d\x0a"); -$res->parse("\x0d\x0ahallo welt test123\n"); -$res->parse("\x0d\x0a------------0xKhTmLbOuNdArY\x0d\x0a"); -$res->parse("Content-Disposition: form-data; name=\"text2\"\x0d\x0a"); -$res->parse("\x0d\x0a\x0d\x0a------------0xKhTmLbOuNdArY\x0d\x0a"); -$res->parse('Content-Disposition: form-data; name="upload"; file'); -$res->parse("name=\"hello.pl\"\x0d\x0a\x0d\x0a"); -$res->parse("Content-Type: application/octet-stream\x0d\x0a\x0d\x0a"); -$res->parse("#!/usr/bin/perl\n\n"); -$res->parse("use strict;\n"); -$res->parse("use warnings;\n\n"); -$res->parse("print \"Hello World :)\\n\"\n"); -$res->parse("\x0d\x0a------------0xKhTmLbOuNdArY--"); -ok $res->is_finished, 'response is finished'; -is $res->code, 200, 'right status'; -is $res->message, 'OK', 'right message'; -is $res->version, '1.1', 'right version'; -is $res->headers->content_type, 'multipart/form-data; boundary=----------0xKhTmLbOuNdArY', 'right "Content-Type" value'; -ok !$res->content->parts->[0]->is_multipart, 'no multipart content'; -ok !$res->content->parts->[1]->is_multipart, 'no multipart content'; -ok !$res->content->parts->[2]->is_multipart, 'no multipart content'; -is $res->content->parts->[0]->asset->slurp, "hallo welt test123\n", 'right content'; -my $dir = tempdir; -my $file = $dir->child('multipart.html'); -eval { $res->save_to($file) }; -like $@, qr/^Multipart content cannot be saved to files/, 'right error'; + my $dir = tempdir; + my $file = $dir->child('multipart.html'); + eval { $res->save_to($file) }; + like $@, qr/^Multipart content cannot be saved to files/, 'right error'; +}; -# Parse HTTP 1.1 chunked multipart response with leftovers (at once) -$res = Mojo::Message::Response->new; -my $multipart - = "HTTP/1.1 200 OK\x0d\x0a" - . "Transfer-Encoding: chunked\x0d\x0a" - . 'Content-Type: multipart/form-data; bo' - . "undary=----------0xKhTmLbOuNdArY\x0d\x0a\x0d\x0a" - . "19f\x0d\x0a------------0xKhTmLbOuNdArY\x0d\x0a" - . "Content-Disposition: form-data; name=\"text1\"\x0d\x0a" - . "\x0d\x0ahallo welt test123\n" - . "\x0d\x0a------------0xKhTmLbOuNdArY\x0d\x0a" - . "Content-Disposition: form-data; name=\"text2\"\x0d\x0a" - . "\x0d\x0a\x0d\x0a------------0xKhTmLbOuNdArY\x0d\x0a" - . 'Content-Disposition: form-data; name="upload"; file' - . "name=\"hello.pl\"\x0d\x0a" - . "Content-Type: application/octet-stream\x0d\x0a\x0d\x0a" - . "#!/usr/bin/perl\n\n" - . "use strict;\n" - . "use warnings;\n\n" - . "print \"Hello World :)\\n\"\n" - . "\x0d\x0a------------0xKhTmLbOuNdA" - . "r\x0d\x0a3\x0d\x0aY--\x0d\x0a" - . "0\x0d\x0a\x0d\x0a" - . "HTTP/1.0 200 OK\x0d\x0a\x0d\x0a"; -$res->parse($multipart); -ok $res->is_finished, 'response is finished'; -is $res->code, 200, 'right status'; -is $res->message, 'OK', 'right message'; -is $res->version, '1.1', 'right version'; -is $res->headers->content_type, 'multipart/form-data; boundary=----------0xKhTmLbOuNdArY', 'right "Content-Type" value'; -is $res->headers->content_length, 418, 'right "Content-Length" value'; -is $res->headers->transfer_encoding, undef, 'no "Transfer-Encoding" value'; -is $res->body_size, 418, 'right size'; -ok !$res->content->parts->[0]->is_multipart, 'no multipart content'; -ok !$res->content->parts->[1]->is_multipart, 'no multipart content'; -ok !$res->content->parts->[2]->is_multipart, 'no multipart content'; -is $res->content->parts->[0]->asset->slurp, "hallo welt test123\n", 'right content'; -is $res->upload('upload')->filename, 'hello.pl', 'right filename'; -ok !$res->upload('upload')->asset->is_file, 'stored in memory'; -is $res->upload('upload')->asset->size, 69, 'right size'; -is $res->content->parts->[2]->headers->content_type, 'application/octet-stream', 'right "Content-Type" value'; -is $res->content->leftovers, "HTTP/1.0 200 OK\x0d\x0a\x0d\x0a", 'next response in leftovers'; +subtest 'Parse HTTP 1.1 chunked multipart response with leftovers (at once)' => sub { + my $res = Mojo::Message::Response->new; + my $multipart + = "HTTP/1.1 200 OK\x0d\x0a" + . "Transfer-Encoding: chunked\x0d\x0a" + . 'Content-Type: multipart/form-data; bo' + . "undary=----------0xKhTmLbOuNdArY\x0d\x0a\x0d\x0a" + . "19f\x0d\x0a------------0xKhTmLbOuNdArY\x0d\x0a" + . "Content-Disposition: form-data; name=\"text1\"\x0d\x0a" + . "\x0d\x0ahallo welt test123\n" + . "\x0d\x0a------------0xKhTmLbOuNdArY\x0d\x0a" + . "Content-Disposition: form-data; name=\"text2\"\x0d\x0a" + . "\x0d\x0a\x0d\x0a------------0xKhTmLbOuNdArY\x0d\x0a" + . 'Content-Disposition: form-data; name="upload"; file' + . "name=\"hello.pl\"\x0d\x0a" + . "Content-Type: application/octet-stream\x0d\x0a\x0d\x0a" + . "#!/usr/bin/perl\n\n" + . "use strict;\n" + . "use warnings;\n\n" + . "print \"Hello World :)\\n\"\n" + . "\x0d\x0a------------0xKhTmLbOuNdA" + . "r\x0d\x0a3\x0d\x0aY--\x0d\x0a" + . "0\x0d\x0a\x0d\x0a" + . "HTTP/1.0 200 OK\x0d\x0a\x0d\x0a"; + $res->parse($multipart); + ok $res->is_finished, 'response is finished'; + is $res->code, 200, 'right status'; + is $res->message, 'OK', 'right message'; + is $res->version, '1.1', 'right version'; + is $res->headers->content_type, 'multipart/form-data; boundary=----------0xKhTmLbOuNdArY', + 'right "Content-Type" value'; + is $res->headers->content_length, 418, 'right "Content-Length" value'; + is $res->headers->transfer_encoding, undef, 'no "Transfer-Encoding" value'; + is $res->body_size, 418, 'right size'; + ok !$res->content->parts->[0]->is_multipart, 'no multipart content'; + ok !$res->content->parts->[1]->is_multipart, 'no multipart content'; + ok !$res->content->parts->[2]->is_multipart, 'no multipart content'; + is $res->content->parts->[0]->asset->slurp, "hallo welt test123\n", 'right content'; + is $res->upload('upload')->filename, 'hello.pl', 'right filename'; + ok !$res->upload('upload')->asset->is_file, 'stored in memory'; + is $res->upload('upload')->asset->size, 69, 'right size'; + is $res->content->parts->[2]->headers->content_type, 'application/octet-stream', 'right "Content-Type" value'; + is $res->content->leftovers, "HTTP/1.0 200 OK\x0d\x0a\x0d\x0a", 'next response in leftovers'; +}; -# Parse HTTP 1.1 chunked multipart response (in multiple small chunks) -$res = Mojo::Message::Response->new; -$res->parse("HTTP/1.1 200 OK\x0d\x0a"); -$res->parse("Transfer-Encoding: chunked\x0d\x0a"); -$res->parse('Content-Type: multipart/parallel; boundary=AAA; charset=utf-8'); -$res->parse("\x0d\x0a\x0d\x0a"); -$res->parse("7\x0d\x0a"); -$res->parse("--AAA\x0d\x0a"); -$res->parse("\x0d\x0a1a\x0d\x0a"); -$res->parse("Content-Type: image/jpeg\x0d\x0a"); -$res->parse("\x0d\x0a16\x0d\x0a"); -$res->parse("Content-ID: 600050\x0d\x0a\x0d\x0a"); -$res->parse("\x0d"); -$res->parse("\x0a6"); -$res->parse("\x0d\x0aabcd\x0d\x0a"); -$res->parse("\x0d\x0a7\x0d\x0a"); -$res->parse("--AAA\x0d\x0a"); -$res->parse("\x0d\x0a1a\x0d\x0a"); -$res->parse("Content-Type: image/jpeg\x0d\x0a"); -$res->parse("\x0d\x0a16\x0d\x0a"); -$res->parse("Content-ID: 600051\x0d\x0a\x0d\x0a"); -$res->parse("\x0d\x0a6\x0d\x0a"); -$res->parse("efgh\x0d\x0a"); -$res->parse("\x0d\x0a7\x0d\x0a"); -$res->parse('--AAA--'); -ok !$res->is_finished, 'response is not finished'; -$res->parse("\x0d\x0a0\x0d\x0a\x0d\x0a"); -ok $res->is_finished, 'response is finished'; -is $res->code, 200, 'right status'; -is $res->message, 'OK', 'right message'; -is $res->version, '1.1', 'right version'; -is $res->headers->content_type, 'multipart/parallel; boundary=AAA; charset=utf-8', 'right "Content-Type" value'; -is $res->headers->content_length, 129, 'right "Content-Length" value'; -is $res->headers->transfer_encoding, undef, 'no "Transfer-Encoding" value'; -is $res->body_size, 129, 'right size'; -ok !$res->content->parts->[0]->is_multipart, 'no multipart content'; -ok !$res->content->parts->[1]->is_multipart, 'no multipart content'; -is $res->content->parts->[0]->asset->slurp, 'abcd', 'right content'; -is $res->content->parts->[0]->headers->content_type, 'image/jpeg', 'right "Content-Type" value'; -is $res->content->parts->[0]->headers->header('Content-ID'), 600050, 'right "Content-ID" value'; -is $res->content->parts->[1]->asset->slurp, 'efgh', 'right content'; -is $res->content->parts->[1]->headers->content_type, 'image/jpeg', 'right "Content-Type" value'; -is $res->content->parts->[1]->headers->header('Content-ID'), 600051, 'right "Content-ID" value'; +subtest 'Parse HTTP 1.1 chunked multipart response (in multiple small chunks)' => sub { + my $res = Mojo::Message::Response->new; + $res->parse("HTTP/1.1 200 OK\x0d\x0a"); + $res->parse("Transfer-Encoding: chunked\x0d\x0a"); + $res->parse('Content-Type: multipart/parallel; boundary=AAA; charset=utf-8'); + $res->parse("\x0d\x0a\x0d\x0a"); + $res->parse("7\x0d\x0a"); + $res->parse("--AAA\x0d\x0a"); + $res->parse("\x0d\x0a1a\x0d\x0a"); + $res->parse("Content-Type: image/jpeg\x0d\x0a"); + $res->parse("\x0d\x0a16\x0d\x0a"); + $res->parse("Content-ID: 600050\x0d\x0a\x0d\x0a"); + $res->parse("\x0d"); + $res->parse("\x0a6"); + $res->parse("\x0d\x0aabcd\x0d\x0a"); + $res->parse("\x0d\x0a7\x0d\x0a"); + $res->parse("--AAA\x0d\x0a"); + $res->parse("\x0d\x0a1a\x0d\x0a"); + $res->parse("Content-Type: image/jpeg\x0d\x0a"); + $res->parse("\x0d\x0a16\x0d\x0a"); + $res->parse("Content-ID: 600051\x0d\x0a\x0d\x0a"); + $res->parse("\x0d\x0a6\x0d\x0a"); + $res->parse("efgh\x0d\x0a"); + $res->parse("\x0d\x0a7\x0d\x0a"); + $res->parse('--AAA--'); + ok !$res->is_finished, 'response is not finished'; + $res->parse("\x0d\x0a0\x0d\x0a\x0d\x0a"); + ok $res->is_finished, 'response is finished'; + is $res->code, 200, 'right status'; + is $res->message, 'OK', 'right message'; + is $res->version, '1.1', 'right version'; + is $res->headers->content_type, 'multipart/parallel; boundary=AAA; charset=utf-8', 'right "Content-Type" value'; + is $res->headers->content_length, 129, 'right "Content-Length" value'; + is $res->headers->transfer_encoding, undef, 'no "Transfer-Encoding" value'; + is $res->body_size, 129, 'right size'; + ok !$res->content->parts->[0]->is_multipart, 'no multipart content'; + ok !$res->content->parts->[1]->is_multipart, 'no multipart content'; + is $res->content->parts->[0]->asset->slurp, 'abcd', 'right content'; + is $res->content->parts->[0]->headers->content_type, 'image/jpeg', 'right "Content-Type" value'; + is $res->content->parts->[0]->headers->header('Content-ID'), 600050, 'right "Content-ID" value'; + is $res->content->parts->[1]->asset->slurp, 'efgh', 'right content'; + is $res->content->parts->[1]->headers->content_type, 'image/jpeg', 'right "Content-Type" value'; + is $res->content->parts->[1]->headers->header('Content-ID'), 600051, 'right "Content-ID" value'; +}; -# Parse HTTP 1.1 multipart response with missing boundary -$res = Mojo::Message::Response->new; -$res->parse("HTTP/1.1 200 OK\x0d\x0a"); -$res->parse("Content-Length: 420\x0d\x0a"); -$res->parse("Content-Type: multipart/form-data; bo\x0d\x0a\x0d\x0a"); -$res->parse("\x0d\x0a------------0xKhTmLbOuNdArY\x0d\x0a"); -$res->parse("Content-Disposition: form-data; name=\"text1\"\x0d\x0a"); -$res->parse("\x0d\x0ahallo welt test123\n"); -$res->parse("\x0d\x0a------------0xKhTmLbOuNdArY\x0d\x0a"); -$res->parse("Content-Disposition: form-data; name=\"text2\"\x0d\x0a"); -$res->parse("\x0d\x0a\x0d\x0a------------0xKhTmLbOuNdArY\x0d\x0a"); -$res->parse('Content-Disposition: form-data; name="upload"; file'); -$res->parse("name=\"hello.pl\"\x0d\x0a\x0d\x0a"); -$res->parse("Content-Type: application/octet-stream\x0d\x0a\x0d\x0a"); -$res->parse("#!/usr/bin/perl\n\n"); -$res->parse("use strict;\n"); -$res->parse("use warnings;\n\n"); -$res->parse("print \"Hello World :)\\n\"\n"); -$res->parse("\x0d\x0a------------0xKhTmLbOuNdArY--"); -ok $res->is_finished, 'response is finished'; -is $res->code, 200, 'right status'; -is $res->message, 'OK', 'right message'; -is $res->version, '1.1', 'right version'; -is $res->headers->content_type, 'multipart/form-data; bo', 'right "Content-Type" value'; -ok !$res->content->is_multipart, 'no multipart content'; -like $res->content->asset->slurp, qr/hallo welt/, 'right content'; +subtest 'Parse HTTP 1.1 multipart response with missing boundary' => sub { + my $res = Mojo::Message::Response->new; + $res->parse("HTTP/1.1 200 OK\x0d\x0a"); + $res->parse("Content-Length: 420\x0d\x0a"); + $res->parse("Content-Type: multipart/form-data; bo\x0d\x0a\x0d\x0a"); + $res->parse("\x0d\x0a------------0xKhTmLbOuNdArY\x0d\x0a"); + $res->parse("Content-Disposition: form-data; name=\"text1\"\x0d\x0a"); + $res->parse("\x0d\x0ahallo welt test123\n"); + $res->parse("\x0d\x0a------------0xKhTmLbOuNdArY\x0d\x0a"); + $res->parse("Content-Disposition: form-data; name=\"text2\"\x0d\x0a"); + $res->parse("\x0d\x0a\x0d\x0a------------0xKhTmLbOuNdArY\x0d\x0a"); + $res->parse('Content-Disposition: form-data; name="upload"; file'); + $res->parse("name=\"hello.pl\"\x0d\x0a\x0d\x0a"); + $res->parse("Content-Type: application/octet-stream\x0d\x0a\x0d\x0a"); + $res->parse("#!/usr/bin/perl\n\n"); + $res->parse("use strict;\n"); + $res->parse("use warnings;\n\n"); + $res->parse("print \"Hello World :)\\n\"\n"); + $res->parse("\x0d\x0a------------0xKhTmLbOuNdArY--"); + ok $res->is_finished, 'response is finished'; + is $res->code, 200, 'right status'; + is $res->message, 'OK', 'right message'; + is $res->version, '1.1', 'right version'; + is $res->headers->content_type, 'multipart/form-data; bo', 'right "Content-Type" value'; + ok !$res->content->is_multipart, 'no multipart content'; + like $res->content->asset->slurp, qr/hallo welt/, 'right content'; +}; -# Parse HTTP 1.1 gzip compressed response -my $uncompressed = 'abc' x 1000; -my $compressed = gzip $uncompressed; -$res = Mojo::Message::Response->new; -$res->parse("HTTP/1.1 200 OK\x0d\x0a"); -$res->parse("Content-Type: text/plain\x0d\x0a"); -$res->parse("Content-Length: @{[length $compressed]}\x0d\x0a"); -$res->parse("Content-Encoding: GZip\x0d\x0a\x0d\x0a"); -ok $res->content->is_compressed, 'content is compressed'; -is $res->content->progress, 0, 'right progress'; -$res->parse(substr($compressed, 0, 1)); -is $res->content->progress, 1, 'right progress'; -$res->parse(substr($compressed, 1, length($compressed))); -is $res->content->progress, length($compressed), 'right progress'; -ok !$res->content->is_compressed, 'content is not compressed anymore'; -ok $res->is_finished, 'response is finished'; -ok !$res->error, 'no error'; -is $res->code, 200, 'right status'; -is $res->message, 'OK', 'right message'; -is $res->version, '1.1', 'right version'; -is $res->headers->content_type, 'text/plain', 'right "Content-Type" value'; -is $res->headers->content_length, length($uncompressed), 'right "Content-Length" value'; -is $res->headers->content_encoding, undef, 'no "Content-Encoding" value'; -is $res->body, $uncompressed, 'right content'; +subtest 'Parse HTTP 1.1 gzip compressed response' => sub { + my $uncompressed = 'abc' x 1000; + my $compressed = gzip $uncompressed; + my $res = Mojo::Message::Response->new; + $res->parse("HTTP/1.1 200 OK\x0d\x0a"); + $res->parse("Content-Type: text/plain\x0d\x0a"); + $res->parse("Content-Length: @{[length $compressed]}\x0d\x0a"); + $res->parse("Content-Encoding: GZip\x0d\x0a\x0d\x0a"); + ok $res->content->is_compressed, 'content is compressed'; + is $res->content->progress, 0, 'right progress'; + $res->parse(substr($compressed, 0, 1)); + is $res->content->progress, 1, 'right progress'; + $res->parse(substr($compressed, 1, length($compressed))); + is $res->content->progress, length($compressed), 'right progress'; + ok !$res->content->is_compressed, 'content is not compressed anymore'; + ok $res->is_finished, 'response is finished'; + ok !$res->error, 'no error'; + is $res->code, 200, 'right status'; + is $res->message, 'OK', 'right message'; + is $res->version, '1.1', 'right version'; + is $res->headers->content_type, 'text/plain', 'right "Content-Type" value'; + is $res->headers->content_length, length($uncompressed), 'right "Content-Length" value'; + is $res->headers->content_encoding, undef, 'no "Content-Encoding" value'; + is $res->body, $uncompressed, 'right content'; +}; -# Parse HTTP 1.1 chunked gzip compressed response -$uncompressed = 'abc' x 1000; -$compressed = undef; -$compressed = gzip $uncompressed; -$res = Mojo::Message::Response->new; -$res->parse("HTTP/1.1 200 OK\x0d\x0a"); -$res->parse("Content-Type: text/plain\x0d\x0a"); -$res->parse("Content-Encoding: gzip\x0d\x0a"); -$res->parse("Transfer-Encoding: chunked\x0d\x0a\x0d\x0a"); -ok $res->content->is_chunked, 'content is chunked'; -ok $res->content->is_compressed, 'content is compressed'; -$res->parse("1\x0d\x0a"); -$res->parse(substr($compressed, 0, 1)); -$res->parse("\x0d\x0a"); -$res->parse(sprintf('%x', length($compressed) - 1)); -$res->parse("\x0d\x0a"); -$res->parse(substr($compressed, 1, length($compressed) - 1)); -$res->parse("\x0d\x0a"); -$res->parse("0\x0d\x0a\x0d\x0a"); -ok !$res->content->is_chunked, 'content is not chunked anymore'; -ok !$res->content->is_compressed, 'content is not compressed anymore'; -ok $res->is_finished, 'response is finished'; -ok !$res->error, 'no error'; -is $res->code, 200, 'right status'; -is $res->message, 'OK', 'right message'; -is $res->version, '1.1', 'right version'; -is $res->headers->content_type, 'text/plain', 'right "Content-Type" value'; -is $res->headers->content_length, length($uncompressed), 'right "Content-Length" value'; -is $res->headers->transfer_encoding, undef, 'no "Transfer-Encoding" value'; -is $res->headers->content_encoding, undef, 'no "Content-Encoding" value'; -is $res->body, $uncompressed, 'right content'; +subtest 'Parse HTTP 1.1 chunked gzip compressed response' => sub { + my $uncompressed = 'abc' x 1000; + my $compressed = undef; + $compressed = gzip $uncompressed; + my $res = Mojo::Message::Response->new; + $res->parse("HTTP/1.1 200 OK\x0d\x0a"); + $res->parse("Content-Type: text/plain\x0d\x0a"); + $res->parse("Content-Encoding: gzip\x0d\x0a"); + $res->parse("Transfer-Encoding: chunked\x0d\x0a\x0d\x0a"); + ok $res->content->is_chunked, 'content is chunked'; + ok $res->content->is_compressed, 'content is compressed'; + $res->parse("1\x0d\x0a"); + $res->parse(substr($compressed, 0, 1)); + $res->parse("\x0d\x0a"); + $res->parse(sprintf('%x', length($compressed) - 1)); + $res->parse("\x0d\x0a"); + $res->parse(substr($compressed, 1, length($compressed) - 1)); + $res->parse("\x0d\x0a"); + $res->parse("0\x0d\x0a\x0d\x0a"); + ok !$res->content->is_chunked, 'content is not chunked anymore'; + ok !$res->content->is_compressed, 'content is not compressed anymore'; + ok $res->is_finished, 'response is finished'; + ok !$res->error, 'no error'; + is $res->code, 200, 'right status'; + is $res->message, 'OK', 'right message'; + is $res->version, '1.1', 'right version'; + is $res->headers->content_type, 'text/plain', 'right "Content-Type" value'; + is $res->headers->content_length, length($uncompressed), 'right "Content-Length" value'; + is $res->headers->transfer_encoding, undef, 'no "Transfer-Encoding" value'; + is $res->headers->content_encoding, undef, 'no "Content-Encoding" value'; + is $res->body, $uncompressed, 'right content'; +}; -# Build HTTP 1.1 response start-line with minimal headers -$res = Mojo::Message::Response->new; -$res->code(404); -$res->headers->date('Sun, 17 Aug 2008 16:27:35 GMT'); -$res = Mojo::Message::Response->new->parse($res->to_string); -ok $res->is_finished, 'response is finished'; -is $res->code, '404', 'right status'; -is $res->message, 'Not Found', 'right message'; -is $res->version, '1.1', 'right version'; -is $res->headers->date, 'Sun, 17 Aug 2008 16:27:35 GMT', 'right "Date" value'; -is $res->headers->content_length, 0, 'right "Content-Length" value'; +subtest 'Build HTTP 1.1 response start-line with minimal headers' => sub { + my $res = Mojo::Message::Response->new; + $res->code(404); + $res->headers->date('Sun, 17 Aug 2008 16:27:35 GMT'); + $res = Mojo::Message::Response->new->parse($res->to_string); + ok $res->is_finished, 'response is finished'; + is $res->code, '404', 'right status'; + is $res->message, 'Not Found', 'right message'; + is $res->version, '1.1', 'right version'; + is $res->headers->date, 'Sun, 17 Aug 2008 16:27:35 GMT', 'right "Date" value'; + is $res->headers->content_length, 0, 'right "Content-Length" value'; +}; -# Build HTTP 1.1 response start-line with minimal headers (strange message) -$res = Mojo::Message::Response->new; -$res->code(404); -$res->message('Looks-0k!@ ;\':" #$%^<>,.\\o/ &*()'); -$res->headers->date('Sun, 17 Aug 2008 16:27:35 GMT'); -$res = Mojo::Message::Response->new->parse($res->to_string); -ok $res->is_finished, 'response is finished'; -is $res->code, '404', 'right status'; -is $res->message, 'Looks-0k!@ ;\':" #$%^<>,.\\o/ &*()', 'right message'; -is $res->version, '1.1', 'right version'; -is $res->headers->date, 'Sun, 17 Aug 2008 16:27:35 GMT', 'right "Date" value'; -is $res->headers->content_length, 0, 'right "Content-Length" value'; +subtest 'Build HTTP 1.1 response start-line with minimal headers (strange message)' => sub { + my $res = Mojo::Message::Response->new; + $res->code(404); + $res->message('Looks-0k!@ ;\':" #$%^<>,.\\o/ &*()'); + $res->headers->date('Sun, 17 Aug 2008 16:27:35 GMT'); + $res = Mojo::Message::Response->new->parse($res->to_string); + ok $res->is_finished, 'response is finished'; + is $res->code, '404', 'right status'; + is $res->message, 'Looks-0k!@ ;\':" #$%^<>,.\\o/ &*()', 'right message'; + is $res->version, '1.1', 'right version'; + is $res->headers->date, 'Sun, 17 Aug 2008 16:27:35 GMT', 'right "Date" value'; + is $res->headers->content_length, 0, 'right "Content-Length" value'; +}; -# Build HTTP 1.1 response start-line and header -$res = Mojo::Message::Response->new; -$res->code(200); -$res->headers->connection('keep-alive'); -$res->headers->date('Sun, 17 Aug 2008 16:27:35 GMT'); -$res = Mojo::Message::Response->new->parse($res->to_string); -ok $res->is_finished, 'response is finished'; -is $res->code, '200', 'right status'; -is $res->message, 'OK', 'right message'; -is $res->version, '1.1', 'right version'; -is $res->headers->connection, 'keep-alive', 'right "Connection" value'; -is $res->headers->date, 'Sun, 17 Aug 2008 16:27:35 GMT', 'right "Date" value'; +subtest 'Build HTTP 1.1 response start-line and header' => sub { + my $res = Mojo::Message::Response->new; + $res->code(200); + $res->headers->connection('keep-alive'); + $res->headers->date('Sun, 17 Aug 2008 16:27:35 GMT'); + $res = Mojo::Message::Response->new->parse($res->to_string); + ok $res->is_finished, 'response is finished'; + is $res->code, '200', 'right status'; + is $res->message, 'OK', 'right message'; + is $res->version, '1.1', 'right version'; + is $res->headers->connection, 'keep-alive', 'right "Connection" value'; + is $res->headers->date, 'Sun, 17 Aug 2008 16:27:35 GMT', 'right "Date" value'; +}; -# Build full HTTP 1.1 response -$res = Mojo::Message::Response->new; -$res->code(200); -$res->headers->connection('keep-alive'); -$res->headers->date('Sun, 17 Aug 2008 16:27:35 GMT'); -$res->body("Hello World!\n"); -$res = Mojo::Message::Response->new->parse($res->to_string); -ok $res->is_finished, 'response is finished'; -is $res->code, '200', 'right status'; -is $res->message, 'OK', 'right message'; -is $res->version, '1.1', 'right version'; -is $res->headers->connection, 'keep-alive', 'right "Connection" value'; -is $res->headers->date, 'Sun, 17 Aug 2008 16:27:35 GMT', 'right "Date" value'; -is $res->headers->content_length, '13', 'right "Content-Length" value'; -is $res->body, "Hello World!\n", 'right content'; +subtest 'Build full HTTP 1.1 response' => sub { + my $res = Mojo::Message::Response->new; + $res->code(200); + $res->headers->connection('keep-alive'); + $res->headers->date('Sun, 17 Aug 2008 16:27:35 GMT'); + $res->body("Hello World!\n"); + $res = Mojo::Message::Response->new->parse($res->to_string); + ok $res->is_finished, 'response is finished'; + is $res->code, '200', 'right status'; + is $res->message, 'OK', 'right message'; + is $res->version, '1.1', 'right version'; + is $res->headers->connection, 'keep-alive', 'right "Connection" value'; + is $res->headers->date, 'Sun, 17 Aug 2008 16:27:35 GMT', 'right "Date" value'; + is $res->headers->content_length, '13', 'right "Content-Length" value'; + is $res->body, "Hello World!\n", 'right content'; +}; -# Build HTTP 1.1 response parts with progress -$res = Mojo::Message::Response->new; -my ($finished, $state, $progress); -$res->on(finish => sub { $finished = shift->is_finished }); -$res->on( - progress => sub { - my ($res, $part, $offset) = @_; - $state = $part; - $progress += $offset; - } -); -$res->code(200); -$res->headers->connection('keep-alive'); -$res->headers->date('Sun, 17 Aug 2008 16:27:35 GMT'); -$res->body("Hello World!\n"); -ok !$state, 'no state'; -ok !$progress, 'no progress'; -ok !$finished, 'not finished'; -ok $res->build_start_line, 'built start-line'; -is $state, 'start_line', 'made progress on start_line'; -ok $progress, 'made progress'; -$progress = 0; -ok !$finished, 'not finished'; -ok $res->build_headers, 'built headers'; -is $state, 'headers', 'made progress on headers'; -ok $progress, 'made progress'; -$progress = 0; -ok !$finished, 'not finished'; -ok $res->build_body, 'built body'; -is $state, 'body', 'made progress on headers'; -ok $progress, 'made progress'; -ok $finished, 'finished'; +subtest 'Build HTTP 1.1 response parts with progress' => sub { + my $res = Mojo::Message::Response->new; + my ($finished, $state, $progress); + $res->on(finish => sub { $finished = shift->is_finished }); + $res->on( + progress => sub { + my ($res, $part, $offset) = @_; + $state = $part; + $progress += $offset; + } + ); + $res->code(200); + $res->headers->connection('keep-alive'); + $res->headers->date('Sun, 17 Aug 2008 16:27:35 GMT'); + $res->body("Hello World!\n"); + ok !$state, 'no state'; + ok !$progress, 'no progress'; + ok !$finished, 'not finished'; + ok $res->build_start_line, 'built start-line'; + is $state, 'start_line', 'made progress on start_line'; + ok $progress, 'made progress'; + $progress = 0; + ok !$finished, 'not finished'; + ok $res->build_headers, 'built headers'; + is $state, 'headers', 'made progress on headers'; + ok $progress, 'made progress'; + $progress = 0; + ok !$finished, 'not finished'; + ok $res->build_body, 'built body'; + is $state, 'body', 'made progress on headers'; + ok $progress, 'made progress'; + ok $finished, 'finished'; +}; -# Build HTTP 1.1 response with dynamic content -$res = Mojo::Message::Response->new; -$res->code(200); -$res->content->write_chunk( - 'Hello ' => sub { - shift->write_chunk(undef, sub { shift->write_chunk('World!')->write_chunk('') }); - } -); -ok $res->content->is_dynamic, 'dynamic content'; -$res = Mojo::Message::Response->new->parse($res->to_string); -ok !$res->content->is_dynamic, 'no dynamic content'; -ok $res->is_finished, 'response is finished'; -is $res->code, 200, 'right status'; -is $res->message, 'OK', 'right message'; -is $res->version, '1.1', 'right version'; -is $res->headers->content_length, '12', 'right "Content-Length" value'; -is $res->body, 'Hello World!', 'right content'; +subtest 'Build HTTP 1.1 response with dynamic content' => sub { + my $res = Mojo::Message::Response->new; + $res->code(200); + $res->content->write_chunk( + 'Hello ' => sub { + shift->write_chunk(undef, sub { shift->write_chunk('World!')->write_chunk('') }); + } + ); + ok $res->content->is_dynamic, 'dynamic content'; + $res = Mojo::Message::Response->new->parse($res->to_string); + ok !$res->content->is_dynamic, 'no dynamic content'; + ok $res->is_finished, 'response is finished'; + is $res->code, 200, 'right status'; + is $res->message, 'OK', 'right message'; + is $res->version, '1.1', 'right version'; + is $res->headers->content_length, '12', 'right "Content-Length" value'; + is $res->body, 'Hello World!', 'right content'; +}; -# Build HTTP 1.1 multipart response -$res = Mojo::Message::Response->new; -$res->content(Mojo::Content::MultiPart->new); -$res->code(200); -$res->headers->content_type('multipart/mixed; boundary=7am1X'); -$res->headers->date('Sun, 17 Aug 2008 16:27:35 GMT'); -push @{$res->content->parts}, Mojo::Content::Single->new(asset => Mojo::Asset::File->new); -$res->content->parts->[-1]->asset->add_chunk('Hallo Welt lalalalalala!'); -my $content = Mojo::Content::Single->new; -$content->asset->add_chunk("lala\nfoobar\nperl rocks\n"); -$content->headers->content_type('text/plain'); -push @{$res->content->parts}, $content; -$res = Mojo::Message::Response->new->parse($res->to_string); -ok $res->is_finished, 'response is finished'; -is $res->code, 200, 'right status'; -is $res->message, 'OK', 'right message'; -is $res->version, '1.1', 'right version'; -is $res->headers->date, 'Sun, 17 Aug 2008 16:27:35 GMT', 'right "Date" value'; -is $res->headers->content_length, '110', 'right "Content-Length" value'; -is $res->headers->content_type, 'multipart/mixed; boundary=7am1X', 'right "Content-Type" value'; -is $res->content->parts->[0]->asset->slurp, 'Hallo Welt lalalalalala!', 'right content'; -is $res->content->parts->[1]->headers->content_type, 'text/plain', 'right "Content-Type" value'; -is $res->content->parts->[1]->asset->slurp, "lala\nfoobar\nperl rocks\n", 'right content'; +subtest 'Build HTTP 1.1 multipart response' => sub { + my $res = Mojo::Message::Response->new; + $res->content(Mojo::Content::MultiPart->new); + $res->code(200); + $res->headers->content_type('multipart/mixed; boundary=7am1X'); + $res->headers->date('Sun, 17 Aug 2008 16:27:35 GMT'); + push @{$res->content->parts}, Mojo::Content::Single->new(asset => Mojo::Asset::File->new); + $res->content->parts->[-1]->asset->add_chunk('Hallo Welt lalalalalala!'); + my $content = Mojo::Content::Single->new; + $content->asset->add_chunk("lala\nfoobar\nperl rocks\n"); + $content->headers->content_type('text/plain'); + push @{$res->content->parts}, $content; + $res = Mojo::Message::Response->new->parse($res->to_string); + ok $res->is_finished, 'response is finished'; + is $res->code, 200, 'right status'; + is $res->message, 'OK', 'right message'; + is $res->version, '1.1', 'right version'; + is $res->headers->date, 'Sun, 17 Aug 2008 16:27:35 GMT', 'right "Date" value'; + is $res->headers->content_length, '110', 'right "Content-Length" value'; + is $res->headers->content_type, 'multipart/mixed; boundary=7am1X', 'right "Content-Type" value'; + is $res->content->parts->[0]->asset->slurp, 'Hallo Welt lalalalalala!', 'right content'; + is $res->content->parts->[1]->headers->content_type, 'text/plain', 'right "Content-Type" value'; + is $res->content->parts->[1]->asset->slurp, "lala\nfoobar\nperl rocks\n", 'right content'; +}; -# Parse response with cookie -$res = Mojo::Message::Response->new; -$res->parse("HTTP/1.0 200 OK\x0d\x0a"); -$res->parse("Content-Type: text/plain\x0d\x0a"); -$res->parse("Content-Length: 27\x0d\x0a"); -$res->parse("Set-Cookie: foo=bar; path=/test\x0d\x0a\x0d\x0a"); -$res->parse("Hello World!\n1234\nlalalala\n"); -ok $res->is_finished, 'response is finished'; -is $res->code, 200, 'right status'; -is $res->message, 'OK', 'right message'; -is $res->version, '1.0', 'right version'; -is $res->headers->content_type, 'text/plain', 'right "Content-Type" value'; -is $res->headers->content_length, 27, 'right "Content-Length" value'; -is $res->headers->set_cookie, 'foo=bar; path=/test', 'right "Set-Cookie" value'; -my $cookies = $res->cookies; -is $cookies->[0]->name, 'foo', 'right name'; -is $cookies->[0]->value, 'bar', 'right value'; -is $cookies->[0]->path, '/test', 'right path'; -is $res->cookie('foo')->value, 'bar', 'right value'; -is $res->cookie('foo')->path, '/test', 'right path'; +subtest 'Parse response with cookie' => sub { + my $res = Mojo::Message::Response->new; + $res->parse("HTTP/1.0 200 OK\x0d\x0a"); + $res->parse("Content-Type: text/plain\x0d\x0a"); + $res->parse("Content-Length: 27\x0d\x0a"); + $res->parse("Set-Cookie: foo=bar; path=/test\x0d\x0a\x0d\x0a"); + $res->parse("Hello World!\n1234\nlalalala\n"); + ok $res->is_finished, 'response is finished'; + is $res->code, 200, 'right status'; + is $res->message, 'OK', 'right message'; + is $res->version, '1.0', 'right version'; + is $res->headers->content_type, 'text/plain', 'right "Content-Type" value'; + is $res->headers->content_length, 27, 'right "Content-Length" value'; + is $res->headers->set_cookie, 'foo=bar; path=/test', 'right "Set-Cookie" value'; + my $cookies = $res->cookies; + is $cookies->[0]->name, 'foo', 'right name'; + is $cookies->[0]->value, 'bar', 'right value'; + is $cookies->[0]->path, '/test', 'right path'; + is $res->cookie('foo')->value, 'bar', 'right value'; + is $res->cookie('foo')->path, '/test', 'right path'; +}; -# Parse WebSocket handshake response -$res = Mojo::Message::Response->new; -$res->parse("HTTP/1.1 101 Switching Protocols\x0d\x0a"); -$res->parse("Upgrade: websocket\x0d\x0a"); -$res->parse("Connection: Upgrade\x0d\x0a"); -$res->parse("Sec-WebSocket-Accept: abcdef=\x0d\x0a"); -$res->parse("Sec-WebSocket-Protocol: sample\x0d\x0a\x0d\x0a"); -ok $res->is_finished, 'response is finished'; -ok $res->is_empty, 'response is empty'; -ok $res->content->skip_body, 'body has been skipped'; -is $res->code, 101, 'right status'; -is $res->message, 'Switching Protocols', 'right message'; -is $res->version, '1.1', 'right version'; -is $res->headers->upgrade, 'websocket', 'right "Upgrade" value'; -is $res->headers->connection, 'Upgrade', 'right "Connection" value'; -is $res->headers->sec_websocket_accept, 'abcdef=', 'right "Sec-WebSocket-Accept" value'; -is $res->headers->sec_websocket_protocol, 'sample', 'right "Sec-WebSocket-Protocol" value'; -is $res->body, '', 'no content'; -ok !defined $res->headers->content_length, '"Content-Length" does not exist'; +subtest 'Parse WebSocket handshake response' => sub { + my $res = Mojo::Message::Response->new; + $res->parse("HTTP/1.1 101 Switching Protocols\x0d\x0a"); + $res->parse("Upgrade: websocket\x0d\x0a"); + $res->parse("Connection: Upgrade\x0d\x0a"); + $res->parse("Sec-WebSocket-Accept: abcdef=\x0d\x0a"); + $res->parse("Sec-WebSocket-Protocol: sample\x0d\x0a\x0d\x0a"); + ok $res->is_finished, 'response is finished'; + ok $res->is_empty, 'response is empty'; + ok $res->content->skip_body, 'body has been skipped'; + is $res->code, 101, 'right status'; + is $res->message, 'Switching Protocols', 'right message'; + is $res->version, '1.1', 'right version'; + is $res->headers->upgrade, 'websocket', 'right "Upgrade" value'; + is $res->headers->connection, 'Upgrade', 'right "Connection" value'; + is $res->headers->sec_websocket_accept, 'abcdef=', 'right "Sec-WebSocket-Accept" value'; + is $res->headers->sec_websocket_protocol, 'sample', 'right "Sec-WebSocket-Protocol" value'; + is $res->body, '', 'no content'; + ok !defined $res->headers->content_length, '"Content-Length" does not exist'; +}; -# Parse WebSocket handshake response (with frame) -$res = Mojo::Message::Response->new; -$res->parse("HTTP/1.1 101 Switching Protocols\x0d\x0a"); -$res->parse("Upgrade: websocket\x0d\x0a"); -$res->parse("Connection: Upgrade\x0d\x0a"); -$res->parse("Sec-WebSocket-Accept: abcdef=\x0d\x0a"); -$res->parse("Sec-WebSocket-Protocol: sample\x0d\x0a"); -$res->parse("\x0d\x0a\x81\x08\x77\x68\x61\x74\x65\x76\x65\x72"); -ok $res->is_finished, 'response is finished'; -ok $res->is_empty, 'response is empty'; -ok $res->content->skip_body, 'body has been skipped'; -is $res->code, 101, 'right status'; -is $res->message, 'Switching Protocols', 'right message'; -is $res->version, '1.1', 'right version'; -is $res->headers->upgrade, 'websocket', 'right "Upgrade" value'; -is $res->headers->connection, 'Upgrade', 'right "Connection" value'; -is $res->headers->sec_websocket_accept, 'abcdef=', 'right "Sec-WebSocket-Accept" value'; -is $res->headers->sec_websocket_protocol, 'sample', 'right "Sec-WebSocket-Protocol" value'; -is $res->body, '', 'no content'; -is $res->content->leftovers, "\x81\x08\x77\x68\x61\x74\x65\x76\x65\x72", 'frame in leftovers'; -ok !defined $res->headers->content_length, '"Content-Length" does not exist'; +subtest 'Parse WebSocket handshake response (with frame)' => sub { + my $res = Mojo::Message::Response->new; + $res->parse("HTTP/1.1 101 Switching Protocols\x0d\x0a"); + $res->parse("Upgrade: websocket\x0d\x0a"); + $res->parse("Connection: Upgrade\x0d\x0a"); + $res->parse("Sec-WebSocket-Accept: abcdef=\x0d\x0a"); + $res->parse("Sec-WebSocket-Protocol: sample\x0d\x0a"); + $res->parse("\x0d\x0a\x81\x08\x77\x68\x61\x74\x65\x76\x65\x72"); + ok $res->is_finished, 'response is finished'; + ok $res->is_empty, 'response is empty'; + ok $res->content->skip_body, 'body has been skipped'; + is $res->code, 101, 'right status'; + is $res->message, 'Switching Protocols', 'right message'; + is $res->version, '1.1', 'right version'; + is $res->headers->upgrade, 'websocket', 'right "Upgrade" value'; + is $res->headers->connection, 'Upgrade', 'right "Connection" value'; + is $res->headers->sec_websocket_accept, 'abcdef=', 'right "Sec-WebSocket-Accept" value'; + is $res->headers->sec_websocket_protocol, 'sample', 'right "Sec-WebSocket-Protocol" value'; + is $res->body, '', 'no content'; + is $res->content->leftovers, "\x81\x08\x77\x68\x61\x74\x65\x76\x65\x72", 'frame in leftovers'; + ok !defined $res->headers->content_length, '"Content-Length" does not exist'; +}; -# Build WebSocket handshake response -$res = Mojo::Message::Response->new; -$res->code(101); -$res->headers->date('Sun, 17 Aug 2008 16:27:35 GMT'); -$res->headers->upgrade('websocket'); -$res->headers->connection('Upgrade'); -$res->headers->sec_websocket_accept('abcdef='); -$res->headers->sec_websocket_protocol('sample'); -$res = Mojo::Message::Response->new->parse($res->to_string); -ok $res->is_finished, 'response is finished'; -is $res->code, '101', 'right status'; -is $res->message, 'Switching Protocols', 'right message'; -is $res->version, '1.1', 'right version'; -is $res->headers->connection, 'Upgrade', 'right "Connection" value'; -is $res->headers->date, 'Sun, 17 Aug 2008 16:27:35 GMT', 'right "Date" value'; -ok !defined $res->headers->content_length, '"Content-Length" does not exist'; -is $res->headers->upgrade, 'websocket', 'right "Upgrade" value'; -is $res->headers->sec_websocket_accept, 'abcdef=', 'right "Sec-WebSocket-Accept" value'; -is $res->headers->sec_websocket_protocol, 'sample', 'right "Sec-WebSocket-Protocol" value'; -is $res->body, '', 'no content'; -ok !defined $res->headers->content_length, '"Content-Length" does not exist'; +subtest 'Build WebSocket handshake response' => sub { + my $res = Mojo::Message::Response->new; + $res->code(101); + $res->headers->date('Sun, 17 Aug 2008 16:27:35 GMT'); + $res->headers->upgrade('websocket'); + $res->headers->connection('Upgrade'); + $res->headers->sec_websocket_accept('abcdef='); + $res->headers->sec_websocket_protocol('sample'); + $res = Mojo::Message::Response->new->parse($res->to_string); + ok $res->is_finished, 'response is finished'; + is $res->code, '101', 'right status'; + is $res->message, 'Switching Protocols', 'right message'; + is $res->version, '1.1', 'right version'; + is $res->headers->connection, 'Upgrade', 'right "Connection" value'; + is $res->headers->date, 'Sun, 17 Aug 2008 16:27:35 GMT', 'right "Date" value'; + ok !defined $res->headers->content_length, '"Content-Length" does not exist'; + is $res->headers->upgrade, 'websocket', 'right "Upgrade" value'; + is $res->headers->sec_websocket_accept, 'abcdef=', 'right "Sec-WebSocket-Accept" value'; + is $res->headers->sec_websocket_protocol, 'sample', 'right "Sec-WebSocket-Protocol" value'; + is $res->body, '', 'no content'; + ok !defined $res->headers->content_length, '"Content-Length" does not exist'; +}; -# Build and parse HTTP 1.1 response with 3 cookies -$res = Mojo::Message::Response->new; -$res->code(404); -$res->headers->date('Sun, 17 Aug 2008 16:27:35 GMT'); -$res->cookies({name => 'foo', value => 'bar', path => '/foobar'}, {name => 'bar', value => 'baz', path => '/test/23'}); -$res->cookies({name => 'baz', value => 'yada', path => '/foobar'}); -ok !!$res->to_string, 'message built'; -my $res2 = Mojo::Message::Response->new; -$res2->parse($res->to_string); -ok $res2->is_finished, 'response is finished'; -is $res2->code, 404, 'right status'; -is $res2->version, '1.1', 'right version'; -is $res2->headers->content_length, 0, 'right "Content-Length" value'; -ok defined $res2->cookie('foo'), 'cookie "foo" exists'; -ok defined $res2->cookie('bar'), 'cookie "bar" exists'; -ok defined $res2->cookie('baz'), 'cookie "baz" exists'; -ok !defined $res2->cookie('yada'), 'cookie "yada" does not exist'; -is $res2->cookie('foo')->path, '/foobar', 'right path'; -is $res2->cookie('foo')->value, 'bar', 'right value'; -is $res2->cookie('bar')->path, '/test/23', 'right path'; -is $res2->cookie('bar')->value, 'baz', 'right value'; -is $res2->cookie('baz')->path, '/foobar', 'right path'; -is $res2->cookie('baz')->value, 'yada', 'right value'; +subtest 'Build and parse HTTP 1.1 response with 3 cookies' => sub { + my $res = Mojo::Message::Response->new; + $res->code(404); + $res->headers->date('Sun, 17 Aug 2008 16:27:35 GMT'); + $res->cookies({name => 'foo', value => 'bar', path => '/foobar'}, + {name => 'bar', value => 'baz', path => '/test/23'}); + $res->cookies({name => 'baz', value => 'yada', path => '/foobar'}); + ok !!$res->to_string, 'message built'; + my $res2 = Mojo::Message::Response->new; + $res2->parse($res->to_string); + ok $res2->is_finished, 'response is finished'; + is $res2->code, 404, 'right status'; + is $res2->version, '1.1', 'right version'; + is $res2->headers->content_length, 0, 'right "Content-Length" value'; + ok defined $res2->cookie('foo'), 'cookie "foo" exists'; + ok defined $res2->cookie('bar'), 'cookie "bar" exists'; + ok defined $res2->cookie('baz'), 'cookie "baz" exists'; + ok !defined $res2->cookie('yada'), 'cookie "yada" does not exist'; + is $res2->cookie('foo')->path, '/foobar', 'right path'; + is $res2->cookie('foo')->value, 'bar', 'right value'; + is $res2->cookie('bar')->path, '/test/23', 'right path'; + is $res2->cookie('bar')->value, 'baz', 'right value'; + is $res2->cookie('baz')->path, '/foobar', 'right path'; + is $res2->cookie('baz')->value, 'yada', 'right value'; +}; -# Build chunked response body -$res = Mojo::Message::Response->new; -$res->code(200); -my $invocant; -$res->content->write_chunk('hello!' => sub { $invocant = shift }); -$res->content->write_chunk('hello world!')->write_chunk(''); -ok $res->content->is_chunked, 'chunked content'; -ok $res->content->is_dynamic, 'dynamic content'; -is $res->build_body, "6\x0d\x0ahello!\x0d\x0ac\x0d\x0ahello world!\x0d\x0a0\x0d\x0a\x0d\x0a", 'right format'; -isa_ok $invocant, 'Mojo::Content::Single', 'right invocant'; +subtest 'Build chunked response body' => sub { + my $res = Mojo::Message::Response->new; + $res->code(200); + my $invocant; + $res->content->write_chunk('hello!' => sub { $invocant = shift }); + $res->content->write_chunk('hello world!')->write_chunk(''); + ok $res->content->is_chunked, 'chunked content'; + ok $res->content->is_dynamic, 'dynamic content'; + is $res->build_body, "6\x0d\x0ahello!\x0d\x0ac\x0d\x0ahello world!\x0d\x0a0\x0d\x0a\x0d\x0a", 'right format'; + isa_ok $invocant, 'Mojo::Content::Single', 'right invocant'; +}; -# Build dynamic response body -$res = Mojo::Message::Response->new; -$res->code(200); -$invocant = undef; -$res->content->write('hello!' => sub { $invocant = shift }); -$res->content->write('hello world!')->write(''); -ok !$res->content->is_chunked, 'no chunked content'; -ok $res->content->is_dynamic, 'dynamic content'; -is $res->build_body, "hello!hello world!", 'right format'; -isa_ok $invocant, 'Mojo::Content::Single', 'right invocant'; +subtest 'Build dynamic response body' => sub { + my $res = Mojo::Message::Response->new; + $res->code(200); + my $invocant; + $res->content->write('hello!' => sub { $invocant = shift }); + $res->content->write('hello world!')->write(''); + ok !$res->content->is_chunked, 'no chunked content'; + ok $res->content->is_dynamic, 'dynamic content'; + is $res->build_body, "hello!hello world!", 'right format'; + isa_ok $invocant, 'Mojo::Content::Single', 'right invocant'; +}; -# Build response with callback (make sure it's called) -$res = Mojo::Message::Response->new; -$res->code(200); -$res->headers->content_length(10); -$res->content->write('lala' => sub { die "Body callback was called properly\n" }); -$res->get_body_chunk(0); -eval { $res->get_body_chunk(3) }; -is $@, "Body callback was called properly\n", 'right error'; +subtest 'Build response with callback (make sure it is called)' => sub { + my $res = Mojo::Message::Response->new; + $res->code(200); + $res->headers->content_length(10); + $res->content->write('lala' => sub { die "Body callback was called properly\n" }); + $res->get_body_chunk(0); + eval { $res->get_body_chunk(3) }; + is $@, "Body callback was called properly\n", 'right error'; +}; -# Build response with callback (consistency calls) -$res = Mojo::Message::Response->new; -my $body = 'I is here'; -$res->headers->content_length(length($body)); -my $cb = sub { shift->write(substr($body, pop, 1), __SUB__) }; -$res->content->write('' => $cb); -my $full = ''; -my $count = 0; -my $offset = 0; +subtest 'Build response with callback (consistency calls)' => sub { + my $res = Mojo::Message::Response->new; + my $body = 'I is here'; + $res->headers->content_length(length($body)); + my $cb = sub { shift->write(substr($body, pop, 1), __SUB__) }; + $res->content->write('' => $cb); + my $full = ''; + my $count = 0; + my $offset = 0; -while (1) { - my $chunk = $res->get_body_chunk($offset); - last unless $chunk; - $full .= $chunk; - $offset = length($full); - $count++; -} -$res->fix_headers; -is $res->headers->connection, undef, 'no "Connection" value'; -ok $res->content->is_dynamic, 'dynamic content'; -is $count, length($body), 'right length'; -is $full, $body, 'right content'; + while (1) { + my $chunk = $res->get_body_chunk($offset); + last unless $chunk; + $full .= $chunk; + $offset = length($full); + $count++; + } + $res->fix_headers; + is $res->headers->connection, undef, 'no "Connection" value'; + ok $res->content->is_dynamic, 'dynamic content'; + is $count, length($body), 'right length'; + is $full, $body, 'right content'; +}; -# Build response with callback (no Content-Length header) -$res = Mojo::Message::Response->new; -$body = 'I is here'; -$cb = sub { shift->write(substr($body, pop, 1), $cb) }; -$res->content->write('' => $cb); -$res->fix_headers; -$full = ''; -$count = 0; -$offset = 0; +subtest 'Build response with callback (no Content-Length header)' => sub { + my $res = Mojo::Message::Response->new; + my $body = 'I is here'; + my $cb; + $cb = sub { shift->write(substr($body, pop, 1), $cb) }; + $res->content->write('' => $cb); + $res->fix_headers; + my $full = ''; + my $count = 0; + my $offset = 0; -while (1) { - my $chunk = $res->get_body_chunk($offset); - last unless $chunk; - $full .= $chunk; - $offset = length($full); - $count++; -} -is $res->headers->connection, 'close', 'right "Connection" value'; -ok $res->content->is_dynamic, 'dynamic content'; -is $count, length($body), 'right length'; -is $full, $body, 'right content'; + while (1) { + my $chunk = $res->get_body_chunk($offset); + last unless $chunk; + $full .= $chunk; + $offset = length($full); + $count++; + } + is $res->headers->connection, 'close', 'right "Connection" value'; + ok $res->content->is_dynamic, 'dynamic content'; + is $count, length($body), 'right length'; + is $full, $body, 'right content'; +}; -# Body -$res = Mojo::Message::Response->new; -$res->body('hi there!'); -ok !$res->content->asset->is_file, 'stored in memory'; -ok !$res->content->asset->auto_upgrade, 'no upgrade'; -is $res->body, 'hi there!', 'right content'; -$res->body(''); -is $res->body, '', 'no content'; -$res->body('hi there!'); -is $res->body, 'hi there!', 'right content'; -$res->body(0); -is $res->body, 0, 'right content'; -is $res->body('hello!')->body, 'hello!', 'right content'; -$res->content(Mojo::Content::MultiPart->new); -$res->body('hi!'); -is $res->body, 'hi!', 'right content'; +subtest 'Body' => sub { + my $res = Mojo::Message::Response->new; + $res->body('hi there!'); + ok !$res->content->asset->is_file, 'stored in memory'; + ok !$res->content->asset->auto_upgrade, 'no upgrade'; + is $res->body, 'hi there!', 'right content'; + $res->body(''); + is $res->body, '', 'no content'; + $res->body('hi there!'); + is $res->body, 'hi there!', 'right content'; + $res->body(0); + is $res->body, 0, 'right content'; + is $res->body('hello!')->body, 'hello!', 'right content'; + $res->content(Mojo::Content::MultiPart->new); + $res->body('hi!'); + is $res->body, 'hi!', 'right content'; +}; -# Text -$res = Mojo::Message::Response->new; -my $snowman = encode 'UTF-8', '☃'; -is $res->body($snowman)->text, '☃', 'right content'; -is $res->body($snowman)->dom->text, '☃', 'right text'; -$res = Mojo::Message::Response->new; -my $yatta = encode 'shift_jis', 'やった'; -is $res->body($yatta)->text, $yatta, 'right content'; -$res->headers->content_type('text/plain;charset=shift_jis'); -is $res->body($yatta)->text, 'やった', 'right content'; -is $res->body($yatta)->dom->text, 'やった', 'right text'; +subtest 'Text' => sub { + my $res = Mojo::Message::Response->new; + my $snowman = encode 'UTF-8', '☃'; + is $res->body($snowman)->text, '☃', 'right content'; + is $res->body($snowman)->dom->text, '☃', 'right text'; + $res = Mojo::Message::Response->new; + my $yatta = encode 'shift_jis', 'やった'; + is $res->body($yatta)->text, $yatta, 'right content'; + $res->headers->content_type('text/plain;charset=shift_jis'); + is $res->body($yatta)->text, 'やった', 'right content'; + is $res->body($yatta)->dom->text, 'やった', 'right text'; +}; -# Body exceeding memory limit (no upgrade) -{ +subtest 'Body exceeding memory limit (no upgrade)' => sub { local $ENV{MOJO_MAX_MEMORY_SIZE} = 8; - $res = Mojo::Message::Response->new; + my $res = Mojo::Message::Response->new; $res->body('hi there!'); is $res->body, 'hi there!', 'right content'; is $res->content->asset->max_memory_size, 8, 'right size'; is $res->content->asset->size, 9, 'right size'; ok !$res->content->asset->is_file, 'stored in memory'; -} +}; -# Parse response and extract JSON data -$res = Mojo::Message::Response->new; -$res->parse("HTTP/1.1 200 OK\x0a"); -$res->parse("Content-Type: application/json\x0a"); -$res->parse("Content-Length: 27\x0a\x0a"); -$res->parse(encode_json({foo => 'bar', baz => [1, 2, 3]})); -ok $res->is_finished, 'response is finished'; -is $res->code, 200, 'right status'; -is $res->message, 'OK', 'right message'; -is $res->version, '1.1', 'right version'; -is_deeply $res->json, {foo => 'bar', baz => [1, 2, 3]}, 'right JSON data'; -is $res->json('/foo'), 'bar', 'right result'; -is $res->json('/baz/1'), 2, 'right result'; -is_deeply $res->json('/baz'), [1, 2, 3], 'right result'; -$res->json->{baz}[1] = 4; -is_deeply $res->json('/baz'), [1, 4, 3], 'right result'; +subtest 'Parse response and extract JSON data' => sub { + my $res = Mojo::Message::Response->new; + $res->parse("HTTP/1.1 200 OK\x0a"); + $res->parse("Content-Type: application/json\x0a"); + $res->parse("Content-Length: 27\x0a\x0a"); + $res->parse(encode_json({foo => 'bar', baz => [1, 2, 3]})); + ok $res->is_finished, 'response is finished'; + is $res->code, 200, 'right status'; + is $res->message, 'OK', 'right message'; + is $res->version, '1.1', 'right version'; + is_deeply $res->json, {foo => 'bar', baz => [1, 2, 3]}, 'right JSON data'; + is $res->json('/foo'), 'bar', 'right result'; + is $res->json('/baz/1'), 2, 'right result'; + is_deeply $res->json('/baz'), [1, 2, 3], 'right result'; + $res->json->{baz}[1] = 4; + is_deeply $res->json('/baz'), [1, 4, 3], 'right result'; +}; + +subtest 'Parse response and extract HTML' => sub { + my $res = Mojo::Message::Response->new; + $res->parse("HTTP/1.1 200 OK\x0a"); + $res->parse("Content-Type: text/html\x0a"); + $res->parse("Content-Length: 51\x0a\x0a"); + $res->parse('

foobarbaz

'); + ok $res->is_finished, 'response is finished'; + is $res->code, 200, 'right status'; + is $res->message, 'OK', 'right message'; + is $res->version, '1.1', 'right version'; + is $res->dom->at('p')->text, 'foo', 'right value'; + is $res->dom->at('p > a')->text, 'bar', 'right value'; + is $res->dom('p')->first->text, 'foo', 'right value'; + is_deeply $res->dom('p > a')->map('text')->to_array, [qw(bar baz)], 'right values'; + my @text = $res->dom('a')->map(content => 'yada')->first->root->find('p > a')->map('text')->each; + is_deeply \@text, [qw(yada yada)], 'right values'; + is_deeply $res->dom('p > a')->map('text')->to_array, [qw(yada yada)], 'right values'; + @text = $res->dom->find('a')->map(content => 'test')->first->root->find('p > a')->map('text')->each; + is_deeply \@text, [qw(test test)], 'right values'; + is_deeply $res->dom->find('p > a')->map('text')->to_array, [qw(test test)], 'right values'; -# Parse response and extract HTML -$res = Mojo::Message::Response->new; -$res->parse("HTTP/1.1 200 OK\x0a"); -$res->parse("Content-Type: text/html\x0a"); -$res->parse("Content-Length: 51\x0a\x0a"); -$res->parse('

foobarbaz

'); -ok $res->is_finished, 'response is finished'; -is $res->code, 200, 'right status'; -is $res->message, 'OK', 'right message'; -is $res->version, '1.1', 'right version'; -is $res->dom->at('p')->text, 'foo', 'right value'; -is $res->dom->at('p > a')->text, 'bar', 'right value'; -is $res->dom('p')->first->text, 'foo', 'right value'; -is_deeply $res->dom('p > a')->map('text')->to_array, [qw(bar baz)], 'right values'; -my @text = $res->dom('a')->map(content => 'yada')->first->root->find('p > a')->map('text')->each; -is_deeply \@text, [qw(yada yada)], 'right values'; -is_deeply $res->dom('p > a')->map('text')->to_array, [qw(yada yada)], 'right values'; -@text = $res->dom->find('a')->map(content => 'test')->first->root->find('p > a')->map('text')->each; -is_deeply \@text, [qw(test test)], 'right values'; -is_deeply $res->dom->find('p > a')->map('text')->to_array, [qw(test test)], 'right values'; -$file = $dir->child('single.html'); -is $res->save_to($file)->body, '

foobarbaz

', 'right content'; -is $file->slurp, '

foobarbaz

', 'right content'; + my $dir = tempdir; + my $file = $dir->child('single.html'); + is $res->save_to($file)->body, '

foobarbaz

', 'right content'; + is $file->slurp, '

foobarbaz

', 'right content'; +}; -# Build DOM from response with charset -$res = Mojo::Message::Response->new; -$res->parse("HTTP/1.0 200 OK\x0a"); -$res->parse("Content-Type: application/atom+xml; charset=UTF-8; type=feed\x0a"); -$res->parse("\x0a"); -$res->body('

foo barbaz

'); -ok !$res->is_finished, 'response is not finished'; -is $res->headers->content_type, 'application/atom+xml; charset=UTF-8; type=feed', 'right "Content-Type" value'; -ok $res->dom, 'dom built'; -$count = 0; -$res->dom('a')->each(sub { $count++ }); -is $count, 2, 'all anchors found'; +subtest 'Build DOM from response with charset' => sub { + my $res = Mojo::Message::Response->new; + $res->parse("HTTP/1.0 200 OK\x0a"); + $res->parse("Content-Type: application/atom+xml; charset=UTF-8; type=feed\x0a"); + $res->parse("\x0a"); + $res->body('

foo barbaz

'); + ok !$res->is_finished, 'response is not finished'; + is $res->headers->content_type, 'application/atom+xml; charset=UTF-8; type=feed', 'right "Content-Type" value'; + ok $res->dom, 'dom built'; + my $count = 0; + $res->dom('a')->each(sub { $count++ }); + is $count, 2, 'all anchors found'; +}; done_testing();