Skip to content

Commit

Permalink
fixed a few multipart bugs
Browse files Browse the repository at this point in the history
  • Loading branch information
kraih committed Sep 11, 2012
1 parent 5c0fddf commit eeed95f
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 49 deletions.
1 change: 1 addition & 0 deletions Changes
Expand Up @@ -3,6 +3,7 @@
- Improved tests.
- Fixed Perl 5.10.1 compatibility.
- Fixed FindBin support in Mojolicious applications.
- Fixed a few multipart bugs.

3.39 2012-09-10
- Improved Mojo::URL and Mojo::Parameters performance.
Expand Down
10 changes: 3 additions & 7 deletions lib/Mojo/Message.pm
Expand Up @@ -9,7 +9,7 @@ use Mojo::JSON;
use Mojo::JSON::Pointer;
use Mojo::Parameters;
use Mojo::Upload;
use Mojo::Util qw(decode url_unescape);
use Mojo::Util 'decode';
use Scalar::Util 'weaken';

has content => sub { Mojo::Content::Single->new };
Expand Down Expand Up @@ -342,19 +342,15 @@ sub _parse_formdata {
next;
}

# Charset
my $charset = $part->charset || $default;

# Content-Disposition header
my $disposition = $part->headers->content_disposition;
next unless $disposition;
my ($name) = $disposition =~ /[; ]name="?([^";]+)"?/;
my ($filename) = $disposition =~ /[; ]filename="?([^"]*)"?/;
my $value = $part;

# Unescape
$name = url_unescape $name if $name;
$filename = url_unescape $filename if $filename;
# Decode
my $charset = $part->charset || $default;
if ($charset) {
$name = decode($charset, $name) // $name if $name;
$filename = decode($charset, $filename) // $filename if $filename;
Expand Down
118 changes: 76 additions & 42 deletions t/mojo/request.t
Expand Up @@ -2,7 +2,7 @@ use Mojo::Base -strict;

use utf8;

use Test::More tests => 848;
use Test::More tests => 858;

use File::Spec::Functions 'catfile';
use File::Temp 'tempdir';
Expand Down Expand Up @@ -1657,44 +1657,44 @@ $req = Mojo::Message::Request->new;
$req->parse("POST / HTTP/1.0\x0d\x0a"
. "Host: 127.0.0.1:10002\x0d\x0a"
. "Connection: close\x0d\x0a"
. "User-Agent: Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/5"
. 'User-Agent: Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/5'
. "32.9 (KHTML, like Gecko) Chrome/5.0.307.11 Safari/532.9\x0d\x0a"
. "Referer: http://example.org/\x0d\x0a"
. "Content-Length: 819\x0d\x0a"
. "Cache-Control: max-age=0\x0d\x0a"
. "Origin: http://example.org\x0d\x0a"
. "Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryY"
. 'Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryY'
. "GjwdkpB6ZLCZQbX\x0d\x0a"
. "Accept: application/xml,application/xhtml+xml,text/html;q=0.9,text/"
. 'Accept: application/xml,application/xhtml+xml,text/html;q=0.9,text/'
. "plain;q=0.8,image/png,*/*;q=0.5\x0d\x0a"
. "Accept-Encoding: gzip,deflate,sdch\x0d\x0a"
. "Cookie: mojolicious=BAcIMTIzNDU2NzgECAgIAwIAAAAXDGFsZXgudm9yb25vdgQ"
. "AAAB1c2VyBp6FjksAAAAABwAAAGV4cGlyZXM=--1641adddfe885276cda0deb7475f"
. 'Cookie: mojolicious=BAcIMTIzNDU2NzgECAgIAwIAAAAXDGFsZXgudm9yb25vdgQ'
. 'AAAB1c2VyBp6FjksAAAAABwAAAGV4cGlyZXM=--1641adddfe885276cda0deb7475f'
. "153a\x0d\x0a"
. "Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.6,en;q=0.4\x0d\x0a"
. "Accept-Charset: windows-1251,utf-8;q=0.7,*;q=0.3\x0d\x0a\x0d\x0a"
. "------WebKitFormBoundaryYGjwdkpB6ZLCZQbX\x0d\x0a"
. "Content-Disposition: form-data; name=\"fname\"\x0d\x0a\x0d\x0a"
. "Иван"
. 'Иван'
. "\x0d\x0a------WebKitFormBoundaryYGjwdkpB6ZLCZQbX\x0d\x0a"
. "Content-Disposition: form-data; name=\"sname\"\x0d\x0a\x0d\x0a"
. "Иванов"
. 'Иванов'
. "\x0d\x0a------WebKitFormBoundaryYGjwdkpB6ZLCZQbX\x0d\x0a"
. "Content-Disposition: form-data; name=\"sex\"\x0d\x0a\x0d\x0a"
. "мужской"
. 'мужской'
. "\x0d\x0a------WebKitFormBoundaryYGjwdkpB6ZLCZQbX\x0d\x0a"
. "Content-Disposition: form-data; name=\"bdate\"\x0d\x0a\x0d\x0a"
. "16.02.1987"
. '16.02.1987'
. "\x0d\x0a------WebKitFormBoundaryYGjwdkpB6ZLCZQbX\x0d\x0a"
. "Content-Disposition: form-data; name=\"phone\"\x0d\x0a\x0d\x0a"
. "1234567890"
. '1234567890'
. "\x0d\x0a------WebKitFormBoundaryYGjwdkpB6ZLCZQbX\x0d\x0a"
. "Content-Disposition: form-data; name=\"avatar\"; filename=\"аватар."
. 'Content-Disposition: form-data; name="avatar"; filename="аватар.'
. "jpg\"\x0d\x0a"
. "Content-Type: image/jpeg\x0d\x0a\x0d\x0a" . "1234"
. "Content-Type: image/jpeg\x0d\x0a\x0d\x0a1234"
. "\x0d\x0a------WebKitFormBoundaryYGjwdkpB6ZLCZQbX\x0d\x0a"
. "Content-Disposition: form-data; name=\"submit\"\x0d\x0a\x0d\x0a"
. "Сохранить"
. 'Сохранить'
. "\x0d\x0a------WebKitFormBoundaryYGjwdkpB6ZLCZQbX--\x0d\x0a");
ok $req->is_finished, 'request is finished';
is $req->method, 'POST', 'right method';
Expand Down Expand Up @@ -1722,50 +1722,50 @@ $req = Mojo::Message::Request->new;
$req->parse("POST / HTTP/1.0\x0d\x0a"
. "Host: 127.0.0.1:10002\x0d\x0a"
. "Connection: close\x0d\x0a"
. "User-Agent: Mozilla/5.0 (X11; U; Linux x86_64; ru; rv:1.9.1.8) Geck"
. 'User-Agent: Mozilla/5.0 (X11; U; Linux x86_64; ru; rv:1.9.1.8) Geck'
. "o/20100214 Ubuntu/9.10 (karmic) Firefox/3.5.8\x0d\x0a"
. "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q"
. 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q'
. "=0.8\x0d\x0a"
. "Accept-Language: ru,en-us;q=0.7,en;q=0.3\x0d\x0a"
. "Accept-Encoding: gzip,deflate\x0d\x0a"
. "Accept-Charset: windows-1251,utf-8;q=0.7,*;q=0.7\x0d\x0a"
. "Referer: http://example.org/\x0d\x0a"
. "Cookie: mojolicious=BAcIMTIzNDU2NzgECAgIAwIAAAAXDGFsZXgudm9yb25vdgQ"
. "AAAB1c2VyBiWFjksAAAAABwAAAGV4cGlyZXM=--cd933a37999e0fa8d7804205e891"
. 'Cookie: mojolicious=BAcIMTIzNDU2NzgECAgIAwIAAAAXDGFsZXgudm9yb25vdgQ'
. 'AAAB1c2VyBiWFjksAAAAABwAAAGV4cGlyZXM=--cd933a37999e0fa8d7804205e891'
. "93a7\x0d\x0a"
. "Content-Type: multipart/form-data; boundary=-----------------------"
. 'Content-Type: multipart/form-data; boundary=-----------------------'
. "----213090722714721300002030499922\x0d\x0a"
. "Content-Length: 971\x0d\x0a\x0d\x0a"
. "-----------------------------213090722714721300002030499922\x0d\x0a"
. "Content-Disposition: form-data; name=\"fname\"\x0d\x0a\x0d\x0a"
. "Иван"
. 'Иван'
. "\x0d\x0a-----------------------------213090722714721300002030499922"
. "\x0d\x0a"
. "Content-Disposition: form-data; name=\"sname\"\x0d\x0a\x0d\x0a"
. "Иванов"
. 'Иванов'
. "\x0d\x0a-----------------------------213090722714721300002030499922"
. "\x0d\x0a"
. "Content-Disposition: form-data; name=\"sex\"\x0d\x0a\x0d\x0a"
. "мужской"
. 'мужской'
. "\x0d\x0a-----------------------------213090722714721300002030499922"
. "\x0d\x0a"
. "Content-Disposition: form-data; name=\"bdate\"\x0d\x0a\x0d\x0a"
. "16.02.1987"
. '16.02.1987'
. "\x0d\x0a-----------------------------213090722714721300002030499922"
. "\x0d\x0a"
. "Content-Disposition: form-data; name=\"phone\"\x0d\x0a\x0d\x0a"
. "1234567890"
. '1234567890'
. "\x0d\x0a-----------------------------213090722714721300002030499922"
. "\x0d\x0a"
. "Content-Disposition: form-data; name=\"avatar\"; filename=\"аватар."
. 'Content-Disposition: form-data; name="avatar"; filename="аватар.'
. "jpg\"\x0d\x0a"
. "Content-Type: image/jpeg\x0d\x0a\x0d\x0a" . "1234"
. "Content-Type: image/jpeg\x0d\x0a\x0d\x0a1234"
. "\x0d\x0a-----------------------------213090722714721300002030499922"
. "\x0d\x0a"
. "Content-Disposition: form-data; name=\"submit\"\x0d\x0a\x0d\x0a"
. "Сохранить"
. 'Сохранить'
. "\x0d\x0a-----------------------------2130907227147213000020304999"
. "22--");
. '22--');
ok $req->is_finished, 'request is finished';
is $req->method, 'POST', 'right method';
is $req->version, '1.0', 'right version';
Expand All @@ -1792,44 +1792,44 @@ $req = Mojo::Message::Request->new;
$req->parse("POST / HTTP/1.0\x0d\x0a"
. "Host: 127.0.0.1:10002\x0d\x0a"
. "Connection: close\x0d\x0a"
. "User-Agent: Opera/9.80 (X11; Linux x86_64; U; ru) Presto/2.2.15 Ver"
. 'User-Agent: Opera/9.80 (X11; Linux x86_64; U; ru) Presto/2.2.15 Ver'
. "sion/10.10\x0d\x0a"
. "Accept: text/html, application/xml;q=0.9, application/xhtml+xml, im"
. 'Accept: text/html, application/xml;q=0.9, application/xhtml+xml, im'
. "age/png, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1\x0d\x0a"
. "Accept-Language: ru-RU,ru;q=0.9,en;q=0.8\x0d\x0a"
. "Accept-Charset: iso-8859-1, utf-8, utf-16, *;q=0.1\x0d\x0a"
. "Accept-Encoding: deflate, gzip, x-gzip, identity, *;q=0\x0d\x0a"
. "Referer: http://example.org/\x0d\x0a"
. "Cookie: mojolicious=BAcIMTIzNDU2NzgECAgIAwIAAAAXDGFsZXgudm9yb25vdgQ"
. "AAAB1c2VyBhaIjksAAAAABwAAAGV4cGlyZXM=--78a58a94f98ae5b75a489be1189f"
. 'Cookie: mojolicious=BAcIMTIzNDU2NzgECAgIAwIAAAAXDGFsZXgudm9yb25vdgQ'
. 'AAAB1c2VyBhaIjksAAAAABwAAAGV4cGlyZXM=--78a58a94f98ae5b75a489be1189f'
. "2672\x0d\x0a"
. "Cookie2: \$Version=1\x0d\x0a"
. "TE: deflate, gzip, chunked, identity, trailers\x0d\x0a"
. "Content-Length: 771\x0d\x0a"
. "Content-Type: multipart/form-data; boundary=----------IWq9cR9mYYG66"
. 'Content-Type: multipart/form-data; boundary=----------IWq9cR9mYYG66'
. "8xwSn56f0\x0d\x0a\x0d\x0a"
. "------------IWq9cR9mYYG668xwSn56f0\x0d\x0a"
. "Content-Disposition: form-data; name=\"fname\"\x0d\x0a\x0d\x0a"
. "Иван"
. 'Иван'
. "\x0d\x0a------------IWq9cR9mYYG668xwSn56f0\x0d\x0a"
. "Content-Disposition: form-data; name=\"sname\"\x0d\x0a\x0d\x0a"
. "Иванов"
. 'Иванов'
. "\x0d\x0a------------IWq9cR9mYYG668xwSn56f0\x0d\x0a"
. "Content-Disposition: form-data; name=\"sex\"\x0d\x0a\x0d\x0a"
. "мужской"
. 'мужской'
. "\x0d\x0a------------IWq9cR9mYYG668xwSn56f0\x0d\x0a"
. "Content-Disposition: form-data; name=\"bdate\"\x0d\x0a\x0d\x0a"
. "16.02.1987"
. '16.02.1987'
. "\x0d\x0a------------IWq9cR9mYYG668xwSn56f0\x0d\x0a"
. "Content-Disposition: form-data; name=\"phone\"\x0d\x0a\x0d\x0a"
. "1234567890"
. '1234567890'
. "\x0d\x0a------------IWq9cR9mYYG668xwSn56f0\x0d\x0a"
. "Content-Disposition: form-data; name=\"avatar\"; filename=\"аватар."
. 'Content-Disposition: form-data; name="avatar"; filename="аватар.'
. "jpg\"\x0d\x0a"
. "Content-Type: image/jpeg\x0d\x0a\x0d\x0a" . "1234"
. "\x0d\x0a------------IWq9cR9mYYG668xwSn56f0\x0d\x0a"
. "Content-Disposition: form-data; name=\"submit\"\x0d\x0a\x0d\x0a"
. "Сохранить"
. 'Сохранить'
. "\x0d\x0a------------IWq9cR9mYYG668xwSn56f0--");
ok $req->is_finished, 'request is finished';
is $req->method, 'POST', 'right method';
Expand All @@ -1852,15 +1852,49 @@ is $upload->filename, 'аватар.jpg', 'right filename';
is $upload->size, 4, 'right size';
is $upload->slurp, '1234', 'right content';

# Parse ~ in URL
# Firefox multipart/form-data request (UTF-8)
$req = Mojo::Message::Request->new;
$req->parse("POST /foo HTTP/1.1\x0d\x0a");
$req->parse("Host: 127.0.0.1:3000\x0d\x0a");
$req->parse('User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv');
$req->parse(":14.0) Gecko/20100101 Firefox/14.0.1\x0d\x0a");
$req->parse('Accept: text/html,application/xhtml+xml,application/xml;');
$req->parse("q=0.9,*/*;q=0.8\x0d\x0a");
$req->parse("Accept-Language: en-us,en;q=0.5\x0d\x0a");
$req->parse("Accept-Encoding: gzip, deflate\x0d\x0a");
$req->parse("Connection: keep-alive\x0d\x0a");
$req->parse("Referer: http://127.0.0.1:3000/\x0d\x0a");
$req->parse('Content-Type: multipart/form-data;');
$req->parse('boundary=---------------------------126436927890376828148');
$req->parse("1663536\x0d\x0a");
$req->parse("Content-Length: 233\x0d\x0a\x0d\x0a--------");
$req->parse("---------------------1264369278903768281481663536\x0d\x0a");
$req->parse('Content-Disposition: form-data; name="☃"; filen');
$req->parse("ame=\"foo bär ☃.txt\"\x0d\x0aContent-Type: text/plain");
$req->parse("\x0d\x0a\x0d\x0atest 123\x0d\x0a-------");
$req->parse('----------------------1264369278903768281481663536--');
ok $req->is_finished, 'request is finished';
is $req->method, 'POST', 'right method';
is $req->version, '1.1', 'right version';
is $req->url, '/foo', 'right URL';
like $req->headers->content_type, qr!multipart/form-data!,
'right "Content-Type" value';
is $req->upload('')->name, '', 'right name';
is $req->upload('')->filename, 'foo bär ☃.txt', 'right filename';
is $req->upload('')->headers->content_type, 'text/plain',
'right "Content-Type" value';
is $req->upload('')->asset->size, 8, 'right size';
is $req->upload('')->asset->slurp, 'test 123', 'right content';

# Parse "~" in URL
$req = Mojo::Message::Request->new;
$req->parse("GET /~foobar/ HTTP/1.1\x0d\x0a\x0d\x0a");
ok $req->is_finished, 'request is finished';
is $req->method, 'GET', 'right method';
is $req->version, '1.1', 'right version';
is $req->url, '/~foobar/', 'right URL';

# Parse : in URL
# Parse ":" in URL
$req = Mojo::Message::Request->new;
$req->parse("GET /perldoc?Mojo::Message::Request HTTP/1.1\x0d\x0a\x0d\x0a");
ok $req->is_finished, 'request is finished';
Expand Down

0 comments on commit eeed95f

Please sign in to comment.