mirror of
https://github.com/ninenines/cowboy.git
synced 2025-07-14 12:20:24 +00:00
Fix infinite loop on incomplete multipart body
I have amended a lot of changes from the original commit to make it behave as expected, including returning a 400 error. LH
This commit is contained in:
parent
292e732abf
commit
6460e9d2d2
2 changed files with 48 additions and 9 deletions
|
@ -506,7 +506,7 @@ read_part(Req) ->
|
||||||
read_part(Req, Opts) ->
|
read_part(Req, Opts) ->
|
||||||
case maps:is_key(multipart, Req) of
|
case maps:is_key(multipart, Req) of
|
||||||
true ->
|
true ->
|
||||||
{Data, Req2} = stream_multipart(Req, Opts),
|
{Data, Req2} = stream_multipart(Req, Opts, headers),
|
||||||
read_part(Data, Opts, Req2);
|
read_part(Data, Opts, Req2);
|
||||||
false ->
|
false ->
|
||||||
read_part(init_multipart(Req), Opts)
|
read_part(init_multipart(Req), Opts)
|
||||||
|
@ -515,10 +515,10 @@ read_part(Req, Opts) ->
|
||||||
read_part(Buffer, Opts, Req=#{multipart := {Boundary, _}}) ->
|
read_part(Buffer, Opts, Req=#{multipart := {Boundary, _}}) ->
|
||||||
try cow_multipart:parse_headers(Buffer, Boundary) of
|
try cow_multipart:parse_headers(Buffer, Boundary) of
|
||||||
more ->
|
more ->
|
||||||
{Data, Req2} = stream_multipart(Req, Opts),
|
{Data, Req2} = stream_multipart(Req, Opts, headers),
|
||||||
read_part(<< Buffer/binary, Data/binary >>, Opts, Req2);
|
read_part(<< Buffer/binary, Data/binary >>, Opts, Req2);
|
||||||
{more, Buffer2} ->
|
{more, Buffer2} ->
|
||||||
{Data, Req2} = stream_multipart(Req, Opts),
|
{Data, Req2} = stream_multipart(Req, Opts, headers),
|
||||||
read_part(<< Buffer2/binary, Data/binary >>, Opts, Req2);
|
read_part(<< Buffer2/binary, Data/binary >>, Opts, Req2);
|
||||||
{ok, Headers0, Rest} ->
|
{ok, Headers0, Rest} ->
|
||||||
Headers = maps:from_list(Headers0),
|
Headers = maps:from_list(Headers0),
|
||||||
|
@ -557,7 +557,7 @@ read_part_body(Buffer, Opts, Req=#{multipart := {Boundary, _}}, Acc) ->
|
||||||
true ->
|
true ->
|
||||||
{more, Acc, Req#{multipart => {Boundary, Buffer}}};
|
{more, Acc, Req#{multipart => {Boundary, Buffer}}};
|
||||||
false ->
|
false ->
|
||||||
{Data, Req2} = stream_multipart(Req, Opts),
|
{Data, Req2} = stream_multipart(Req, Opts, body),
|
||||||
case cow_multipart:parse_body(<< Buffer/binary, Data/binary >>, Boundary) of
|
case cow_multipart:parse_body(<< Buffer/binary, Data/binary >>, Boundary) of
|
||||||
{ok, Body} ->
|
{ok, Body} ->
|
||||||
read_part_body(<<>>, Opts, Req2, << Acc/binary, Body/binary >>);
|
read_part_body(<<>>, Opts, Req2, << Acc/binary, Body/binary >>);
|
||||||
|
@ -583,12 +583,20 @@ init_multipart(Req) ->
|
||||||
'Missing boundary parameter for multipart media type.'})
|
'Missing boundary parameter for multipart media type.'})
|
||||||
end.
|
end.
|
||||||
|
|
||||||
stream_multipart(Req=#{multipart := done}, _) ->
|
stream_multipart(Req=#{multipart := done}, _, _) ->
|
||||||
{<<>>, Req};
|
{<<>>, Req};
|
||||||
stream_multipart(Req=#{multipart := {_, <<>>}}, Opts) ->
|
stream_multipart(Req=#{multipart := {_, <<>>}}, Opts, Type) ->
|
||||||
{_, Data, Req2} = read_body(Req, Opts),
|
case read_body(Req, Opts) of
|
||||||
{Data, Req2};
|
{more, Data, Req2} ->
|
||||||
stream_multipart(Req=#{multipart := {Boundary, Buffer}}, _) ->
|
{Data, Req2};
|
||||||
|
%% We crash when the data ends unexpectedly.
|
||||||
|
{ok, <<>>, _} ->
|
||||||
|
exit({request_error, {multipart, Type},
|
||||||
|
'Malformed body; multipart expected.'});
|
||||||
|
{ok, Data, Req2} ->
|
||||||
|
{Data, Req2}
|
||||||
|
end;
|
||||||
|
stream_multipart(Req=#{multipart := {Boundary, Buffer}}, _, _) ->
|
||||||
{Buffer, Req#{multipart => {Boundary, <<>>}}}.
|
{Buffer, Req#{multipart => {Boundary, <<>>}}}.
|
||||||
|
|
||||||
%% Response.
|
%% Response.
|
||||||
|
|
|
@ -475,6 +475,26 @@ do_multipart(Path, Config) ->
|
||||||
} = LargeHeaders,
|
} = LargeHeaders,
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
|
multipart_error_empty(Config) ->
|
||||||
|
doc("Multipart request body is empty."),
|
||||||
|
%% We use an empty list as a body to make sure Gun knows
|
||||||
|
%% we want to send an empty body.
|
||||||
|
%% @todo This is a terrible hack. Improve Gun!
|
||||||
|
Body = [],
|
||||||
|
%% Ensure an empty body results in a 400 error.
|
||||||
|
{400, _} = do_body_error("POST", "/multipart", [
|
||||||
|
{<<"content-type">>, <<"multipart/mixed; boundary=deadbeef">>}
|
||||||
|
], Body, Config),
|
||||||
|
ok.
|
||||||
|
|
||||||
|
multipart_error_preamble_only(Config) ->
|
||||||
|
doc("Multipart request body only contains a preamble."),
|
||||||
|
%% Ensure an empty body results in a 400 error.
|
||||||
|
{400, _} = do_body_error("POST", "/multipart", [
|
||||||
|
{<<"content-type">>, <<"multipart/mixed; boundary=deadbeef">>}
|
||||||
|
], <<"Preamble.">>, Config),
|
||||||
|
ok.
|
||||||
|
|
||||||
multipart_error_headers(Config) ->
|
multipart_error_headers(Config) ->
|
||||||
doc("Multipart request body with invalid part headers."),
|
doc("Multipart request body with invalid part headers."),
|
||||||
ReqBody = [
|
ReqBody = [
|
||||||
|
@ -490,6 +510,17 @@ multipart_error_headers(Config) ->
|
||||||
%% The function to parse the multipart body currently does not crash,
|
%% The function to parse the multipart body currently does not crash,
|
||||||
%% as far as I can tell. There is therefore no test for it.
|
%% as far as I can tell. There is therefore no test for it.
|
||||||
|
|
||||||
|
multipart_error_no_final_boundary(Config) ->
|
||||||
|
doc("Multipart request body with no final boundary."),
|
||||||
|
ReqBody = [
|
||||||
|
"--deadbeef\r\nContent-Type: text/plain\r\n\r\nCowboy is an HTTP server.\r\n"
|
||||||
|
],
|
||||||
|
%% Ensure parse errors result in a 400 response.
|
||||||
|
{400, _} = do_body_error("POST", "/multipart", [
|
||||||
|
{<<"content-type">>, <<"multipart/mixed; boundary=deadbeef">>}
|
||||||
|
], ReqBody, Config),
|
||||||
|
ok.
|
||||||
|
|
||||||
multipart_missing_boundary(Config) ->
|
multipart_missing_boundary(Config) ->
|
||||||
doc("Multipart request body without a boundary in the media type."),
|
doc("Multipart request body without a boundary in the media type."),
|
||||||
ReqBody = [
|
ReqBody = [
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue