aboutsummaryrefslogtreecommitdiffstats
path: root/src/cow_multipart.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/cow_multipart.erl')
-rw-r--r--src/cow_multipart.erl73
1 files changed, 50 insertions, 23 deletions
diff --git a/src/cow_multipart.erl b/src/cow_multipart.erl
index d2b45a4..276a689 100644
--- a/src/cow_multipart.erl
+++ b/src/cow_multipart.erl
@@ -1,4 +1,4 @@
-%% Copyright (c) 2014, Loïc Hoguin <[email protected]>
+%% Copyright (c) 2014-2015, Loïc Hoguin <[email protected]>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
@@ -98,6 +98,29 @@
>>).
-define(TEST4_BOUNDARY, <<"boundary">>).
+%% RFC 2046, Section 5.1.1
+-define(TEST5_MIME, <<
+ "This is the preamble. It is to be ignored, though it\r\n"
+ "is a handy place for composition agents to include an\r\n"
+ "explanatory note to non-MIME conformant readers.\r\n"
+ "\r\n"
+ "--simple boundary\r\n",
+ "\r\n"
+ "This is implicitly typed plain US-ASCII text.\r\n"
+ "It does NOT end with a linebreak."
+ "\r\n"
+ "--simple boundary\r\n",
+ "Content-type: text/plain; charset=us-ascii\r\n"
+ "\r\n"
+ "This is explicitly typed plain US-ASCII text.\r\n"
+ "It DOES end with a linebreak.\r\n"
+ "\r\n"
+ "--simple boundary--\r\n"
+ "\r\n"
+ "This is the epilogue. It is also to be ignored."
+>>).
+-define(TEST5_BOUNDARY, <<"simple boundary">>).
+
%% Parsing.
%%
%% The multipart format is defined in RFC 2045.
@@ -179,12 +202,11 @@ skip_preamble(Stream, Boundary) ->
end
end.
-%% There is a line break right after the boundary, skip it.
-%%
-%% We only skip it now because there might be no headers at all,
-%% which means the \r\n\r\n indicating the end of headers also
-%% includes this line break.
+before_parse_headers(<< "\r\n\r\n", Stream/bits >>) ->
+ %% This indicates that there are no headers, so we can abort immediately.
+ {ok, [], Stream};
before_parse_headers(<< "\r\n", Stream/bits >>) ->
+ %% There is a line break right after the boundary, skip it.
parse_hd_name(Stream, [], <<>>).
parse_hd_name(<< C, Rest/bits >>, H, SoFar) ->
@@ -192,7 +214,7 @@ parse_hd_name(<< C, Rest/bits >>, H, SoFar) ->
$: -> parse_hd_before_value(Rest, H, SoFar);
$\s -> parse_hd_name_ws(Rest, H, SoFar);
$\t -> parse_hd_name_ws(Rest, H, SoFar);
- ?INLINE_LOWERCASE(parse_hd_name, Rest, H, SoFar)
+ _ -> ?LOWER(parse_hd_name, Rest, H, SoFar)
end.
parse_hd_name_ws(<< C, Rest/bits >>, H, Name) ->
@@ -340,6 +362,21 @@ parse_epilogue_crlf_test() ->
{done, Epilogue} = parse_headers(Rest2, ?TEST4_BOUNDARY),
ok.
+parse_rfc2046_test() ->
+ %% The following is an example included in RFC 2046, Section 5.1.1.
+ Body1 = <<"This is implicitly typed plain US-ASCII text.\r\n"
+ "It does NOT end with a linebreak.">>,
+ Body2 = <<"This is explicitly typed plain US-ASCII text.\r\n"
+ "It DOES end with a linebreak.\r\n">>,
+ H2 = [{<<"content-type">>, <<"text/plain; charset=us-ascii">>}],
+ Epilogue = <<"\r\n\r\nThis is the epilogue. It is also to be ignored.">>,
+ {ok, [], Rest} = parse_headers(?TEST5_MIME, ?TEST5_BOUNDARY),
+ {done, Body1, Rest2} = parse_body(Rest, ?TEST5_BOUNDARY),
+ {ok, H2, Rest3} = parse_headers(Rest2, ?TEST5_BOUNDARY),
+ {done, Body2, Rest4} = parse_body(Rest3, ?TEST5_BOUNDARY),
+ {done, Epilogue} = parse_headers(Rest4, ?TEST5_BOUNDARY),
+ ok.
+
parse_partial_test() ->
{ok, <<0:8000, "abcdef">>, <<"\rghij">>}
= parse_body(<<0:8000, "abcdef\rghij">>, <<"boundary">>),
@@ -362,9 +399,7 @@ parse_partial_test() ->
{ok, <<"boundary">>, <<"\r\n--">>}
= parse_body(<<"boundary\r\n--">>, <<"boundary">>),
ok.
--endif.
--ifdef(PERF).
perf_parse_multipart(Stream, Boundary) ->
case parse_headers(Stream, Boundary) of
{ok, _, Rest} ->
@@ -461,9 +496,7 @@ identity_test() ->
{done, Body2, M6} = parse_body(M5, B),
{done, Epilogue} = parse_headers(M6, B),
ok.
--endif.
--ifdef(PERF).
perf_build_multipart() ->
B = boundary(),
[
@@ -546,7 +579,7 @@ parse_cd_type(<< C, Rest/bits >>, Acc) ->
$; -> {Acc, parse_before_param(Rest, [])};
$\s -> {Acc, parse_before_param(Rest, [])};
$\t -> {Acc, parse_before_param(Rest, [])};
- ?INLINE_LOWERCASE(parse_cd_type, Rest, Acc)
+ _ -> ?LOWER(parse_cd_type, Rest, Acc)
end.
-ifdef(TEST).
@@ -575,9 +608,7 @@ parse_content_disposition_test_() ->
{<<"file">>, [{<<"filename">>, <<"file2.gif">>}]}}
],
[{V, fun() -> R = parse_content_disposition(V) end} || {V, R} <- Tests].
--endif.
--ifdef(PERF).
horse_parse_content_disposition_attachment() ->
horse:repeat(100000,
parse_content_disposition(<<"attachment; filename=genome.jpeg;"
@@ -600,7 +631,7 @@ horse_parse_content_disposition_inline() ->
-spec parse_content_transfer_encoding(binary()) -> binary().
parse_content_transfer_encoding(Bin) ->
- ?INLINE_LOWERCASE_BC(Bin).
+ ?LOWER(Bin).
-ifdef(TEST).
parse_content_transfer_encoding_test_() ->
@@ -617,9 +648,7 @@ parse_content_transfer_encoding_test_() ->
],
[{V, fun() -> R = parse_content_transfer_encoding(V) end}
|| {V, R} <- Tests].
--endif.
--ifdef(PERF).
horse_parse_content_transfer_encoding() ->
horse:repeat(100000,
parse_content_transfer_encoding(<<"QUOTED-PRINTABLE">>)
@@ -636,7 +665,7 @@ parse_content_type(Bin) ->
parse_ct_type(<< C, Rest/bits >>, Acc) ->
case C of
$/ -> parse_ct_subtype(Rest, Acc, <<>>);
- ?INLINE_LOWERCASE(parse_ct_type, Rest, Acc)
+ _ -> ?LOWER(parse_ct_type, Rest, Acc)
end.
parse_ct_subtype(<<>>, Type, Subtype) when Subtype =/= <<>> ->
@@ -646,7 +675,7 @@ parse_ct_subtype(<< C, Rest/bits >>, Type, Acc) ->
$; -> {Type, Acc, parse_before_param(Rest, [])};
$\s -> {Type, Acc, parse_before_param(Rest, [])};
$\t -> {Type, Acc, parse_before_param(Rest, [])};
- ?INLINE_LOWERCASE(parse_ct_subtype, Rest, Type, Acc)
+ _ -> ?LOWER(parse_ct_subtype, Rest, Type, Acc)
end.
-ifdef(TEST).
@@ -680,9 +709,7 @@ parse_content_type_test_() ->
],
[{V, fun() -> R = parse_content_type(V) end}
|| {V, R} <- Tests].
--endif.
--ifdef(PERF).
horse_parse_content_type_zero() ->
horse:repeat(100000,
parse_content_type(<<"text/plain">>)
@@ -710,7 +737,7 @@ parse_before_param(<< C, Rest/bits >>, Params) ->
$; -> parse_before_param(Rest, Params);
$\s -> parse_before_param(Rest, Params);
$\t -> parse_before_param(Rest, Params);
- ?INLINE_LOWERCASE(parse_param_name, Rest, Params, <<>>)
+ _ -> ?LOWER(parse_param_name, Rest, Params, <<>>)
end.
parse_param_name(<<>>, Params, Acc) ->
@@ -718,7 +745,7 @@ parse_param_name(<<>>, Params, Acc) ->
parse_param_name(<< C, Rest/bits >>, Params, Acc) ->
case C of
$= -> parse_param_value(Rest, Params, Acc);
- ?INLINE_LOWERCASE(parse_param_name, Rest, Params, Acc)
+ _ -> ?LOWER(parse_param_name, Rest, Params, Acc)
end.
parse_param_value(<<>>, Params, Name) ->