From ef7b41c8ce4ce6dd5aadac8116980fed9dacbdd5 Mon Sep 17 00:00:00 2001 From: Magnus Klaar Date: Fri, 9 Dec 2011 20:53:04 +0100 Subject: add cowboy_http:urlencode/2 and urlencode/1 This function complements the cowboy_http:urldecode/2 and urldecode/1 functions. We should have this encoding covered now. urldecode is only expected to be invoked by user code, no further changes needed to integrate this. --- src/cowboy_http.erl | 77 +++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 69 insertions(+), 8 deletions(-) (limited to 'src/cowboy_http.erl') diff --git a/src/cowboy_http.erl b/src/cowboy_http.erl index fd0d142..485affa 100644 --- a/src/cowboy_http.erl +++ b/src/cowboy_http.erl @@ -23,7 +23,8 @@ digits/1, token/2, token_ci/2, quoted_string/2]). %% Interpretation. --export([connection_to_atom/1, urldecode/1, urldecode/2]). +-export([connection_to_atom/1, urldecode/1, urldecode/2, urlencode/1, + urlencode/2]). -include("include/http.hrl"). -include_lib("eunit/include/eunit.hrl"). @@ -710,6 +711,51 @@ unhex(C) when C >= $A, C =< $F -> C - $A + 10; unhex(C) when C >= $a, C =< $f -> C - $a + 10; unhex(_) -> error. + +%% @doc URL encode a string binary. +%% @equiv urlencode(Bin, []) +-spec urlencode(binary()) -> binary(). +urlencode(Bin) -> + urlencode(Bin, []). + +%% @doc URL encode a string binary. +%% The `noplus' option disables the default behaviour of quoting space +%% characters, `\s', as `+'. The `upper' option overrides the default behaviour +%% of writing hex numbers using lowecase letters to using uppercase letters +%% instead. +-spec urlencode(binary(), [noplus|upper]) -> binary(). +urlencode(Bin, Opts) -> + Plus = not proplists:get_value(noplus, Opts, false), + Upper = proplists:get_value(upper, Opts, false), + urlencode(Bin, <<>>, Plus, Upper). + +-spec urlencode(binary(), binary(), boolean(), boolean()) -> binary(). +urlencode(<>, Acc, P=Plus, U=Upper) -> + if C >= $0, C =< $9 -> urlencode(Rest, <>, P, U); + C >= $A, C =< $Z -> urlencode(Rest, <>, P, U); + C >= $a, C =< $z -> urlencode(Rest, <>, P, U); + C =:= $.; C =:= $-; C =:= $~; C =:= $_ -> + urlencode(Rest, <>, P, U); + C =:= $ , Plus -> + urlencode(Rest, <>, P, U); + true -> + H = C band 16#F0 bsr 4, L = C band 16#0F, + H1 = if Upper -> tohexu(H); true -> tohexl(H) end, + L1 = if Upper -> tohexu(L); true -> tohexl(L) end, + urlencode(Rest, <>, P, U) + end; +urlencode(<<>>, Acc, _Plus, _Upper) -> + Acc. + +-spec tohexu(byte()) -> byte(). +tohexu(C) when C < 10 -> $0 + C; +tohexu(C) when C < 17 -> $A + C - 10. + +-spec tohexl(byte()) -> byte(). +tohexl(C) when C < 10 -> $0 + C; +tohexl(C) when C < 17 -> $a + C - 10. + + %% Tests. -ifdef(TEST). @@ -877,12 +923,27 @@ digits_test_() -> [{V, fun() -> R = digits(V) end} || {V, R} <- Tests]. urldecode_test_() -> - Tests = [ - {<<" ">>, <<"%20">>}, - {<<" ">>, <<"+">>}, - {<<0>>, <<"%00">>}, - {<<255>>, <<"%fF">>} - ], - [{I, ?_assertEqual(E, urldecode(I))} || {E, I} <- Tests]. + U = fun urldecode/2, + [?_assertEqual(<<" ">>, U(<<"%20">>, crash)), + ?_assertEqual(<<" ">>, U(<<"+">>, crash)), + ?_assertEqual(<<0>>, U(<<"%00">>, crash)), + ?_assertEqual(<<255>>, U(<<"%fF">>, crash)), + ?_assertEqual(<<"123">>, U(<<"123">>, crash)), + ?_assertEqual(<<"%i5">>, U(<<"%i5">>, skip)), + ?_assertEqual(<<"%5">>, U(<<"%5">>, skip)), + ?_assertError(badarg, U(<<"%i5">>, crash)), + ?_assertError(badarg, U(<<"%5">>, crash)) + ]. + +urlencode_test_() -> + U = fun urlencode/2, + [?_assertEqual(<<"%ff%00">>, U(<<255,0>>, [])), + ?_assertEqual(<<"%FF%00">>, U(<<255,0>>, [upper])), + ?_assertEqual(<<"+">>, U(<<" ">>, [])), + ?_assertEqual(<<"%20">>, U(<<" ">>, [noplus])), + ?_assertEqual(<<"aBc">>, U(<<"aBc">>, [])), + ?_assertEqual(<<".-~_">>, U(<<".-~_">>, [])), + ?_assertEqual(<<"%ff+">>, urlencode(<<255, " ">>)) + ]. -endif. -- cgit v1.2.3