Skip to content

Commit b98f89d

Browse files
leonardbbenoitc
authored andcommitted
(fix) #521 trim leading and trailing empty values in parse_qs (#523)
* (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
1 parent cf15810 commit b98f89d

File tree

3 files changed

+91
-3
lines changed

3 files changed

+91
-3
lines changed

src/hackney_bstr.erl

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,15 @@
2121
word/2,
2222
trim/1]).
2323

24+
%% BEGIN: Remove when OTP 17 not officially supported
25+
-export([split/3]).
26+
27+
-export_type([cp/0]).
28+
29+
-opaque cp() :: {'am' | 'bm', binary()}.
30+
-type part() :: {Start :: non_neg_integer(), Length :: integer()}.
31+
%% END: Remove when OTP 17 not officially supported
32+
2433
-export([quoted_string/2]).
2534

2635
to_binary(V) when is_list(V) ->
@@ -361,3 +370,80 @@ quoted_string(<< $\\, C, Rest/binary >>, Fun, Acc) ->
361370
quoted_string(Rest, Fun, << Acc/binary, C >>);
362371
quoted_string(<< C, Rest/binary >>, Fun, Acc) ->
363372
quoted_string(Rest, Fun, << Acc/binary, C >>).
373+
374+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
375+
%% binary:split/3 from OTP 18
376+
%% remove when support for < 18
377+
%% formally dropped
378+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
379+
380+
-spec split(Subject, Pattern, Options) -> Parts when
381+
Subject :: binary(),
382+
Pattern :: binary() | [binary()] | cp(),
383+
Options :: [Option],
384+
Option :: {scope, part()} | trim | global | trim_all,
385+
Parts :: [binary()].
386+
387+
split(Haystack,Needles,Options) ->
388+
try
389+
{Part,Global,Trim,TrimAll} =
390+
get_opts_split(Options,{no,false,false,false}),
391+
Moptlist = case Part of
392+
no ->
393+
[];
394+
{A,B} ->
395+
[{scope,{A,B}}]
396+
end,
397+
MList = if
398+
Global ->
399+
binary:matches(Haystack,Needles,Moptlist);
400+
true ->
401+
case binary:match(Haystack,Needles,Moptlist) of
402+
nomatch -> [];
403+
Match -> [Match]
404+
end
405+
end,
406+
do_split(Haystack,MList,0,Trim,TrimAll)
407+
catch
408+
_:_ ->
409+
erlang:error(badarg)
410+
end.
411+
412+
do_split(H,[],N,true,_) when N >= byte_size(H) ->
413+
[];
414+
do_split(H,[],N,_,true) when N >= byte_size(H) ->
415+
[];
416+
do_split(H,[],N,_,_) ->
417+
[binary:part(H,{N,byte_size(H)-N})];
418+
do_split(H,[{A,B}|T],N,Trim,TrimAll) ->
419+
case binary:part(H,{N,A-N}) of
420+
<<>> when TrimAll == true ->
421+
do_split(H,T,A+B,Trim,TrimAll);
422+
<<>> ->
423+
Rest = do_split(H,T,A+B,Trim,TrimAll),
424+
case {Trim, Rest} of
425+
{true,[]} ->
426+
[];
427+
_ ->
428+
[<<>> | Rest]
429+
end;
430+
Oth ->
431+
[Oth | do_split(H,T,A+B,Trim,TrimAll)]
432+
end.
433+
434+
get_opts_split([],{Part,Global,Trim,TrimAll}) ->
435+
{Part,Global,Trim,TrimAll};
436+
get_opts_split([{scope,{A,B}} | T],{_Part,Global,Trim,TrimAll}) ->
437+
get_opts_split(T,{{A,B},Global,Trim,TrimAll});
438+
get_opts_split([global | T],{Part,_Global,Trim,TrimAll}) ->
439+
get_opts_split(T,{Part,true,Trim,TrimAll});
440+
get_opts_split([trim | T],{Part,Global,_Trim,TrimAll}) ->
441+
get_opts_split(T,{Part,Global,true,TrimAll});
442+
get_opts_split([trim_all | T],{Part,Global,Trim,_TrimAll}) ->
443+
get_opts_split(T,{Part,Global,Trim,true});
444+
get_opts_split(_,_) ->
445+
throw(badopt).
446+
447+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
448+
%% END binary:split from OTP 18
449+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

src/hackney_url.erl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -329,8 +329,8 @@ tohexl(C) when C < 16 -> $a + C - 10.
329329
parse_qs(<<>>) ->
330330
[];
331331
parse_qs(Bin) ->
332-
Tokens = binary:split(Bin, <<"&">>, [trim, global]),
333-
[case binary:split(Token, <<"=">>, [trim]) of
332+
Tokens = hackney_bstr:split(Bin, <<"&">>, [trim_all, global]),
333+
[case hackney_bstr:split(Token, <<"=">>, [trim_all]) of
334334
[T] ->
335335
{urldecode(T), true};
336336
[Name, Value] ->

test/hackney_url_tests.erl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,9 @@ parse_qs_test_() ->
249249
Tests = [
250250
{<<"a=b">>, [{<<"a">>,<<"b">>}]},
251251
{<<"a=b&c=d">>, [{<<"a">>,<<"b">>}, {<<"c">>, <<"d">>}]},
252-
{<<"a=b&c">>, [{<<"a">>,<<"b">>}, {<<"c">>, true}]}
252+
{<<"&a=b&&c=d&">>, [{<<"a">>,<<"b">>}, {<<"c">>, <<"d">>}]},
253+
{<<"a=b&c">>, [{<<"a">>,<<"b">>}, {<<"c">>, true}]},
254+
{<<"&a=b&c&&">>, [{<<"a">>,<<"b">>}, {<<"c">>, true}]}
253255
],
254256
[{V, fun() -> R = hackney_url:parse_qs(V) end} || {V, R} <- Tests].
255257

0 commit comments

Comments
 (0)