From 63c001a7aacea0cf8b95014e71921e61e97e2fee Mon Sep 17 00:00:00 2001 From: Zaiming Shi Date: Sun, 28 Feb 2021 21:54:41 +0100 Subject: [PATCH] refactor(http-lib): Add emqx_http_lib So far only uri_encode and uri_decode APIs --- .../test/emqx_acl_mnesia_SUITE.erl | 6 +- apps/emqx_coap/src/emqx_coap_ps_resource.erl | 10 +-- src/emqx_http_lib.erl | 69 +++++++++++++++++++ test/emqx_http_lib_tests.erl | 46 +++++++++++++ 4 files changed, 117 insertions(+), 14 deletions(-) create mode 100644 src/emqx_http_lib.erl create mode 100644 test/emqx_http_lib_tests.erl diff --git a/apps/emqx_auth_mnesia/test/emqx_acl_mnesia_SUITE.erl b/apps/emqx_auth_mnesia/test/emqx_acl_mnesia_SUITE.erl index b2c0b8b5f..13b041491 100644 --- a/apps/emqx_auth_mnesia/test/emqx_acl_mnesia_SUITE.erl +++ b/apps/emqx_auth_mnesia/test/emqx_acl_mnesia_SUITE.erl @@ -250,8 +250,4 @@ uri(Parts) when is_list(Parts) -> NParts = [b2l(E) || E <- Parts], ?HOST ++ filename:join([?BASE_PATH, ?API_VERSION, "acl"| NParts]). -%% @private -b2l(B) when is_binary(B) -> - http_uri:encode(binary_to_list(B)); -b2l(L) when is_list(L) -> - http_uri:encode(L). +b2l(B) -> binary_to_list(emqx_http_lib:uri_encode(iolist_to_binary(B))). diff --git a/apps/emqx_coap/src/emqx_coap_ps_resource.erl b/apps/emqx_coap/src/emqx_coap_ps_resource.erl index 144dba1bd..b2169521a 100644 --- a/apps/emqx_coap/src/emqx_coap_ps_resource.erl +++ b/apps/emqx_coap/src/emqx_coap_ps_resource.erl @@ -241,7 +241,7 @@ handle_received_publish(Topic, MaxAge, Format, Payload) -> handle_received_create(TopicPrefix, MaxAge, Payload) -> case core_link:decode(Payload) of [{rootless, [Topic], [{ct, CT}]}] when is_binary(Topic), Topic =/= <<>> -> - TrueTopic = percent_decode(Topic), + TrueTopic = emqx_http_lib:uri_decode(Topic), ?LOG(debug, "decoded link-format payload, the Topic=~p, CT=~p~n", [TrueTopic, CT]), LocPath = concatenate_location_path([<<"ps">>, TopicPrefix, TrueTopic]), FullTopic = binary:part(LocPath, 4, byte_size(LocPath)-4), @@ -259,14 +259,6 @@ handle_received_create(TopicPrefix, MaxAge, Payload) -> {error, bad_request} end. -%% @private Copy from http_uri.erl which has been deprecated since OTP-23 -percent_decode(<<$%, Hex:2/binary, Rest/bits>>) -> - <<(binary_to_integer(Hex, 16)), (percent_decode(Rest))/binary>>; -percent_decode(<>) -> - <>; -percent_decode(<<>>) -> - <<>>. - %% When topic is timeout, server should return nocontent here, %% but gen_coap only receive return value of #coap_content from coap_get, so temporarily we can't give the Code 2.07 {ok, nocontent} out.TBC!!! return_resource(Topic, Payload, MaxAge, TimeStamp, Content) -> diff --git a/src/emqx_http_lib.erl b/src/emqx_http_lib.erl new file mode 100644 index 000000000..695879236 --- /dev/null +++ b/src/emqx_http_lib.erl @@ -0,0 +1,69 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2021 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_http_lib). + +-export([uri_encode/1, uri_decode/1]). + +%% @doc Decode percent-encoded URI. +%% This is copied from http_uri.erl which has been deprecated since OTP-23 +%% The recommended replacement uri_string function is not quite equivalent +%% and not backward compatible. +-spec uri_decode(binary()) -> binary(). +uri_decode(<<$%, Hex:2/binary, Rest/bits>>) -> + <<(binary_to_integer(Hex, 16)), (uri_decode(Rest))/binary>>; +uri_decode(<>) -> + <>; +uri_decode(<<>>) -> + <<>>. + +%% @doc Encode URI. +-spec uri_encode(binary()) -> binary(). +uri_encode(URI) when is_binary(URI) -> + << <<(uri_encode_binary(Char))/binary>> || <> <= URI >>. + +uri_encode_binary(Char) -> + case reserved(Char) of + true -> + << $%, (integer_to_binary(Char, 16))/binary >>; + false -> + <> + end. + +reserved($;) -> true; +reserved($:) -> true; +reserved($@) -> true; +reserved($&) -> true; +reserved($=) -> true; +reserved($+) -> true; +reserved($,) -> true; +reserved($/) -> true; +reserved($?) -> true; +reserved($#) -> true; +reserved($[) -> true; +reserved($]) -> true; +reserved($<) -> true; +reserved($>) -> true; +reserved($\") -> true; +reserved(${) -> true; +reserved($}) -> true; +reserved($|) -> true; +reserved($\\) -> true; +reserved($') -> true; +reserved($^) -> true; +reserved($%) -> true; +reserved($\s) -> true; +reserved(_) -> false. diff --git a/test/emqx_http_lib_tests.erl b/test/emqx_http_lib_tests.erl new file mode 100644 index 000000000..78a647417 --- /dev/null +++ b/test/emqx_http_lib_tests.erl @@ -0,0 +1,46 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2021 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_http_lib_tests). + +-include_lib("proper/include/proper.hrl"). +-include_lib("eunit/include/eunit.hrl"). + +uri_encode_decode_test_() -> + Opts = [{numtests, 1000}, {to_file, user}], + {timeout, 10, + fun() -> ?assert(proper:quickcheck(prop_run(), Opts)) end}. + +prop_run() -> + ?FORALL(Generated, prop_uri(), test_prop_uri(iolist_to_binary(Generated))). + +prop_uri() -> + proper_types:non_empty(proper_types:list(proper_types:union([prop_char(), prop_reserved()]))). + +prop_char() -> proper_types:integer(32, 126). + +prop_reserved() -> + proper_types:oneof([$;, $:, $@, $&, $=, $+, $,, $/, $?, + $#, $[, $], $<, $>, $\", ${, $}, $|, + $\\, $', $^, $%, $ ]). + +test_prop_uri(URI) -> + Encoded = emqx_http_lib:uri_encode(URI), + Decoded1 = emqx_http_lib:uri_decode(Encoded), + ?assertEqual(URI, Decoded1), + Decoded2 = uri_string:percent_decode(Encoded), + ?assertEqual(URI, Decoded2), + true.