Skip to content

Commit

Permalink
(fix) #521 trim leading and trailing empty values in parse_qs (#523)
Browse files Browse the repository at this point in the history
* (fix) #521 trim leading and trailing empty values in parse_qs

* (feat) Add binary:split/3 from OTP 18 to support trim_all in releases with < 18
  • Loading branch information
leonardb authored and benoitc committed Sep 10, 2018
1 parent cf15810 commit b98f89d
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 3 deletions.
86 changes: 86 additions & 0 deletions src/hackney_bstr.erl
Expand Up @@ -21,6 +21,15 @@
word/2,
trim/1]).

%% BEGIN: Remove when OTP 17 not officially supported
-export([split/3]).

-export_type([cp/0]).

-opaque cp() :: {'am' | 'bm', binary()}.
-type part() :: {Start :: non_neg_integer(), Length :: integer()}.
%% END: Remove when OTP 17 not officially supported

-export([quoted_string/2]).

to_binary(V) when is_list(V) ->
Expand Down Expand Up @@ -361,3 +370,80 @@ quoted_string(<< $\\, C, Rest/binary >>, Fun, Acc) ->
quoted_string(Rest, Fun, << Acc/binary, C >>);
quoted_string(<< C, Rest/binary >>, Fun, Acc) ->
quoted_string(Rest, Fun, << Acc/binary, C >>).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% binary:split/3 from OTP 18
%% remove when support for < 18
%% formally dropped
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

-spec split(Subject, Pattern, Options) -> Parts when
Subject :: binary(),
Pattern :: binary() | [binary()] | cp(),
Options :: [Option],
Option :: {scope, part()} | trim | global | trim_all,
Parts :: [binary()].

split(Haystack,Needles,Options) ->
try
{Part,Global,Trim,TrimAll} =
get_opts_split(Options,{no,false,false,false}),
Moptlist = case Part of
no ->
[];
{A,B} ->
[{scope,{A,B}}]
end,
MList = if
Global ->
binary:matches(Haystack,Needles,Moptlist);
true ->
case binary:match(Haystack,Needles,Moptlist) of
nomatch -> [];
Match -> [Match]
end
end,
do_split(Haystack,MList,0,Trim,TrimAll)
catch
_:_ ->
erlang:error(badarg)
end.

do_split(H,[],N,true,_) when N >= byte_size(H) ->
[];
do_split(H,[],N,_,true) when N >= byte_size(H) ->
[];
do_split(H,[],N,_,_) ->
[binary:part(H,{N,byte_size(H)-N})];
do_split(H,[{A,B}|T],N,Trim,TrimAll) ->
case binary:part(H,{N,A-N}) of
<<>> when TrimAll == true ->
do_split(H,T,A+B,Trim,TrimAll);
<<>> ->
Rest = do_split(H,T,A+B,Trim,TrimAll),
case {Trim, Rest} of
{true,[]} ->
[];
_ ->
[<<>> | Rest]
end;
Oth ->
[Oth | do_split(H,T,A+B,Trim,TrimAll)]
end.

get_opts_split([],{Part,Global,Trim,TrimAll}) ->
{Part,Global,Trim,TrimAll};
get_opts_split([{scope,{A,B}} | T],{_Part,Global,Trim,TrimAll}) ->
get_opts_split(T,{{A,B},Global,Trim,TrimAll});
get_opts_split([global | T],{Part,_Global,Trim,TrimAll}) ->
get_opts_split(T,{Part,true,Trim,TrimAll});
get_opts_split([trim | T],{Part,Global,_Trim,TrimAll}) ->
get_opts_split(T,{Part,Global,true,TrimAll});
get_opts_split([trim_all | T],{Part,Global,Trim,_TrimAll}) ->
get_opts_split(T,{Part,Global,Trim,true});
get_opts_split(_,_) ->
throw(badopt).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% END binary:split from OTP 18
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4 changes: 2 additions & 2 deletions src/hackney_url.erl
Expand Up @@ -329,8 +329,8 @@ tohexl(C) when C < 16 -> $a + C - 10.
parse_qs(<<>>) ->
[];
parse_qs(Bin) ->
Tokens = binary:split(Bin, <<"&">>, [trim, global]),
[case binary:split(Token, <<"=">>, [trim]) of
Tokens = hackney_bstr:split(Bin, <<"&">>, [trim_all, global]),
[case hackney_bstr:split(Token, <<"=">>, [trim_all]) of
[T] ->
{urldecode(T), true};
[Name, Value] ->
Expand Down
4 changes: 3 additions & 1 deletion test/hackney_url_tests.erl
Expand Up @@ -249,7 +249,9 @@ parse_qs_test_() ->
Tests = [
{<<"a=b">>, [{<<"a">>,<<"b">>}]},
{<<"a=b&c=d">>, [{<<"a">>,<<"b">>}, {<<"c">>, <<"d">>}]},
{<<"a=b&c">>, [{<<"a">>,<<"b">>}, {<<"c">>, true}]}
{<<"&a=b&&c=d&">>, [{<<"a">>,<<"b">>}, {<<"c">>, <<"d">>}]},
{<<"a=b&c">>, [{<<"a">>,<<"b">>}, {<<"c">>, true}]},
{<<"&a=b&c&&">>, [{<<"a">>,<<"b">>}, {<<"c">>, true}]}
],
[{V, fun() -> R = hackney_url:parse_qs(V) end} || {V, R} <- Tests].

Expand Down

0 comments on commit b98f89d

Please sign in to comment.