summaryrefslogtreecommitdiff
path: root/src/mochiweb/mochiweb_multipart.erl
diff options
context:
space:
mode:
authorAdam Kocoloski <kocolosk@apache.org>2009-11-21 17:01:50 +0000
committerAdam Kocoloski <kocolosk@apache.org>2009-11-21 17:01:50 +0000
commit3a4fad07cb5097598eefd0cd7f588c5881a61c8f (patch)
tree6945d805eb72a37a5b27ae14a7ec2d1e8690fdac /src/mochiweb/mochiweb_multipart.erl
parenta2ebd6155b8171befd8f55b42c48bc563bb330b0 (diff)
upgrade mochiweb to r113, use hooks instead of forking mochijson2. COUCHDB-474
git-svn-id: https://svn.apache.org/repos/asf/couchdb/trunk@882941 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'src/mochiweb/mochiweb_multipart.erl')
-rw-r--r--src/mochiweb/mochiweb_multipart.erl165
1 files changed, 133 insertions, 32 deletions
diff --git a/src/mochiweb/mochiweb_multipart.erl b/src/mochiweb/mochiweb_multipart.erl
index b9631613..0368a9a6 100644
--- a/src/mochiweb/mochiweb_multipart.erl
+++ b/src/mochiweb/mochiweb_multipart.erl
@@ -76,15 +76,15 @@ parse_multipart_request(Req, Callback) ->
Boundary = iolist_to_binary(
get_boundary(Req:get_header_value("content-type"))),
Prefix = <<"\r\n--", Boundary/binary>>,
- BS = size(Boundary),
+ BS = byte_size(Boundary),
Chunk = read_chunk(Req, Length),
- Length1 = Length - size(Chunk),
+ Length1 = Length - byte_size(Chunk),
<<"--", Boundary:BS/binary, "\r\n", Rest/binary>> = Chunk,
- feed_mp(headers, #mp{boundary=Prefix,
- length=Length1,
- buffer=Rest,
- callback=Callback,
- req=Req}).
+ feed_mp(headers, flash_multipart_hack(#mp{boundary=Prefix,
+ length=Length1,
+ buffer=Rest,
+ callback=Callback,
+ req=Req})).
parse_headers(<<>>) ->
[];
@@ -117,8 +117,27 @@ read_chunk(Req, Length) when Length > 0 ->
read_more(State=#mp{length=Length, buffer=Buffer, req=Req}) ->
Data = read_chunk(Req, Length),
Buffer1 = <<Buffer/binary, Data/binary>>,
- State#mp{length=Length - size(Data),
- buffer=Buffer1}.
+ flash_multipart_hack(State#mp{length=Length - byte_size(Data),
+ buffer=Buffer1}).
+
+flash_multipart_hack(State=#mp{length=0, buffer=Buffer, boundary=Prefix}) ->
+ %% http://code.google.com/p/mochiweb/issues/detail?id=22
+ %% Flash doesn't terminate multipart with \r\n properly so we fix it up here
+ PrefixSize = size(Prefix),
+ case size(Buffer) - (2 + PrefixSize) of
+ Seek when Seek >= 0 ->
+ case Buffer of
+ <<_:Seek/binary, Prefix:PrefixSize/binary, "--">> ->
+ Buffer1 = <<Buffer/binary, "\r\n">>,
+ State#mp{buffer=Buffer1};
+ _ ->
+ State
+ end;
+ _ ->
+ State
+ end;
+flash_multipart_hack(State) ->
+ State.
feed_mp(headers, State=#mp{buffer=Buffer, callback=Callback}) ->
{State1, P} = case find_in_binary(<<"\r\n\r\n">>, Buffer) of
@@ -136,7 +155,8 @@ feed_mp(headers, State=#mp{buffer=Buffer, callback=Callback}) ->
feed_mp(body, State1#mp{buffer=Rest,
callback=NextCallback});
feed_mp(body, State=#mp{boundary=Prefix, buffer=Buffer, callback=Callback}) ->
- case find_boundary(Prefix, Buffer) of
+ Boundary = find_boundary(Prefix, Buffer),
+ case Boundary of
{end_boundary, Start, Skip} ->
<<Data:Start/binary, _:Skip/binary, Rest/binary>> = Buffer,
C1 = Callback({body, Data}),
@@ -158,7 +178,7 @@ feed_mp(body, State=#mp{boundary=Prefix, buffer=Buffer, callback=Callback}) ->
end.
get_boundary(ContentType) ->
- {"multipart/" ++ _, Opts} = mochiweb_util:parse_header(ContentType),
+ {"multipart/form-data", Opts} = mochiweb_util:parse_header(ContentType),
case proplists:get_value("boundary", Opts) of
S when is_list(S) ->
S
@@ -242,7 +262,11 @@ test_callback(Expect, [Expect | Rest]) ->
ok;
_ ->
fun (Next) -> test_callback(Next, Rest) end
- end.
+ end;
+test_callback({body, Got}, [{body, Expect} | Rest]) ->
+ GotSize = size(Got),
+ <<Got:GotSize/binary, Expect1/binary>> = Expect,
+ fun (Next) -> test_callback(Next, [{body, Expect1} | Rest]) end.
test_parse3() ->
ContentType = "multipart/form-data; boundary=---------------------------7386909285754635891697677882",
@@ -261,14 +285,12 @@ test_parse3() ->
eof],
TestCallback = fun (Next) -> test_callback(Next, Expect) end,
ServerFun = fun (Socket) ->
- case gen_tcp:send(Socket, BinContent) of
- ok ->
- exit(normal)
- end
+ ok = gen_tcp:send(Socket, BinContent),
+ exit(normal)
end,
ClientFun = fun (Socket) ->
Req = fake_request(Socket, ContentType,
- size(BinContent)),
+ byte_size(BinContent)),
Res = parse_multipart_request(Req, TestCallback),
{0, <<>>, ok} = Res,
ok
@@ -294,14 +316,12 @@ test_parse2() ->
eof],
TestCallback = fun (Next) -> test_callback(Next, Expect) end,
ServerFun = fun (Socket) ->
- case gen_tcp:send(Socket, BinContent) of
- ok ->
- exit(normal)
- end
+ ok = gen_tcp:send(Socket, BinContent),
+ exit(normal)
end,
ClientFun = fun (Socket) ->
Req = fake_request(Socket, ContentType,
- size(BinContent)),
+ byte_size(BinContent)),
Res = parse_multipart_request(Req, TestCallback),
{0, <<>>, ok} = Res,
ok
@@ -327,14 +347,12 @@ test_parse_form() ->
""], "\r\n"),
BinContent = iolist_to_binary(Content),
ServerFun = fun (Socket) ->
- case gen_tcp:send(Socket, BinContent) of
- ok ->
- exit(normal)
- end
+ ok = gen_tcp:send(Socket, BinContent),
+ exit(normal)
end,
ClientFun = fun (Socket) ->
Req = fake_request(Socket, ContentType,
- size(BinContent)),
+ byte_size(BinContent)),
Res = parse_form(Req),
[{"submit-name", "Larry"},
{"files", {"file1.txt", {"text/plain",[]},
@@ -376,14 +394,12 @@ test_parse() ->
eof],
TestCallback = fun (Next) -> test_callback(Next, Expect) end,
ServerFun = fun (Socket) ->
- case gen_tcp:send(Socket, BinContent) of
- ok ->
- exit(normal)
- end
+ ok = gen_tcp:send(Socket, BinContent),
+ exit(normal)
end,
ClientFun = fun (Socket) ->
Req = fake_request(Socket, ContentType,
- size(BinContent)),
+ byte_size(BinContent)),
Res = parse_multipart_request(Req, TestCallback),
{0, <<>>, ok} = Res,
ok
@@ -419,6 +435,89 @@ test_find_in_binary() ->
{partial, 1, 3} = find_in_binary(<<"foobar">>, <<"afoo">>),
ok.
+test_flash_parse() ->
+ ContentType = "multipart/form-data; boundary=----------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5",
+ "----------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5" = get_boundary(ContentType),
+ BinContent = <<"------------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5\r\nContent-Disposition: form-data; name=\"Filename\"\r\n\r\nhello.txt\r\n------------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5\r\nContent-Disposition: form-data; name=\"success_action_status\"\r\n\r\n201\r\n------------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5\r\nContent-Disposition: form-data; name=\"file\"; filename=\"hello.txt\"\r\nContent-Type: application/octet-stream\r\n\r\nhello\n\r\n------------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5\r\nContent-Disposition: form-data; name=\"Upload\"\r\n\r\nSubmit Query\r\n------------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5--">>,
+ Expect = [{headers,
+ [{"content-disposition",
+ {"form-data", [{"name", "Filename"}]}}]},
+ {body, <<"hello.txt">>},
+ body_end,
+ {headers,
+ [{"content-disposition",
+ {"form-data", [{"name", "success_action_status"}]}}]},
+ {body, <<"201">>},
+ body_end,
+ {headers,
+ [{"content-disposition",
+ {"form-data", [{"name", "file"}, {"filename", "hello.txt"}]}},
+ {"content-type", {"application/octet-stream", []}}]},
+ {body, <<"hello\n">>},
+ body_end,
+ {headers,
+ [{"content-disposition",
+ {"form-data", [{"name", "Upload"}]}}]},
+ {body, <<"Submit Query">>},
+ body_end,
+ eof],
+ TestCallback = fun (Next) -> test_callback(Next, Expect) end,
+ ServerFun = fun (Socket) ->
+ ok = gen_tcp:send(Socket, BinContent),
+ exit(normal)
+ end,
+ ClientFun = fun (Socket) ->
+ Req = fake_request(Socket, ContentType,
+ byte_size(BinContent)),
+ Res = parse_multipart_request(Req, TestCallback),
+ {0, <<>>, ok} = Res,
+ ok
+ end,
+ ok = with_socket_server(ServerFun, ClientFun),
+ ok.
+
+test_flash_parse2() ->
+ ContentType = "multipart/form-data; boundary=----------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5",
+ "----------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5" = get_boundary(ContentType),
+ Chunk = iolist_to_binary(string:copies("%", 4096)),
+ BinContent = <<"------------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5\r\nContent-Disposition: form-data; name=\"Filename\"\r\n\r\nhello.txt\r\n------------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5\r\nContent-Disposition: form-data; name=\"success_action_status\"\r\n\r\n201\r\n------------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5\r\nContent-Disposition: form-data; name=\"file\"; filename=\"hello.txt\"\r\nContent-Type: application/octet-stream\r\n\r\n", Chunk/binary, "\r\n------------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5\r\nContent-Disposition: form-data; name=\"Upload\"\r\n\r\nSubmit Query\r\n------------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5--">>,
+ Expect = [{headers,
+ [{"content-disposition",
+ {"form-data", [{"name", "Filename"}]}}]},
+ {body, <<"hello.txt">>},
+ body_end,
+ {headers,
+ [{"content-disposition",
+ {"form-data", [{"name", "success_action_status"}]}}]},
+ {body, <<"201">>},
+ body_end,
+ {headers,
+ [{"content-disposition",
+ {"form-data", [{"name", "file"}, {"filename", "hello.txt"}]}},
+ {"content-type", {"application/octet-stream", []}}]},
+ {body, Chunk},
+ body_end,
+ {headers,
+ [{"content-disposition",
+ {"form-data", [{"name", "Upload"}]}}]},
+ {body, <<"Submit Query">>},
+ body_end,
+ eof],
+ TestCallback = fun (Next) -> test_callback(Next, Expect) end,
+ ServerFun = fun (Socket) ->
+ ok = gen_tcp:send(Socket, BinContent),
+ exit(normal)
+ end,
+ ClientFun = fun (Socket) ->
+ Req = fake_request(Socket, ContentType,
+ byte_size(BinContent)),
+ Res = parse_multipart_request(Req, TestCallback),
+ {0, <<>>, ok} = Res,
+ ok
+ end,
+ ok = with_socket_server(ServerFun, ClientFun),
+ ok.
+
test() ->
test_find_in_binary(),
test_find_boundary(),
@@ -426,4 +525,6 @@ test() ->
test_parse2(),
test_parse3(),
test_parse_form(),
+ test_flash_parse(),
+ test_flash_parse2(),
ok.