Skip to content
This repository was archived by the owner on Nov 17, 2020. It is now read-only.

Commit 46b96f6

Browse files
committed
Remove unnecessary call to xmerl_ucs:from_utf8
This commit removes a call to xmerl_ucs:from_utf8 which was used for two purposes: converting the binary payload into an Erlang Unicode string (a list of integer code points), and validating that the payload is indeed utf8. The call was necessary when it was introduced because mochijson only supported Unicode strings. Nowadays JSON libraries also support utf8 binaries so converting is not necessary. The function also starts by doing a binary_to_list/1 call which creates a lot of garbage and can lead to OOM situations when the queue is large. We ended up with the payload binary, the temporary list and the final Unicode string in memory. With this patch we only ever have the binary in memory and the memory consumption is divided by more or less 3. To validate that the payload is utf8 and keep the functionality intact a small function was added that makes use of the /utf8 binary matching specifier. Since this was the only xmerl function used in the management plugin I have also removed xmerl from the LOCAL_DEPS. I have added a test for the different encodings that can be requested to make sure that nothing was broken when doing the change.
1 parent cc5c9a2 commit 46b96f6

File tree

3 files changed

+44
-7
lines changed

3 files changed

+44
-7
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ endef
2121

2222
DEPS = rabbit_common rabbit amqp_client cowboy cowlib rabbitmq_web_dispatch rabbitmq_management_agent
2323
TEST_DEPS = rabbitmq_ct_helpers rabbitmq_ct_client_helpers proper
24-
LOCAL_DEPS += xmerl mnesia ranch ssl crypto public_key
24+
LOCAL_DEPS += mnesia ranch ssl crypto public_key
2525

2626
# FIXME: Add Ranch as a BUILD_DEPS to be sure the correct version is picked.
2727
# See rabbitmq-components.mk.

src/rabbit_mgmt_wm_queue_get.erl

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -162,13 +162,14 @@ maybe_truncate(Payload, Len) ->
162162

163163
payload_part(Payload, Enc) ->
164164
{PL, E} = case Enc of
165-
auto -> try
166-
%% TODO mochijson does this but is it safe?
167-
_ = xmerl_ucs:from_utf8(Payload),
168-
{Payload, string}
169-
catch exit:{ucs, _} ->
170-
{base64:encode(Payload), base64}
165+
auto -> case is_utf8(Payload) of
166+
true -> {Payload, string};
167+
false -> {base64:encode(Payload), base64}
171168
end;
172169
_ -> {base64:encode(Payload), base64}
173170
end,
174171
[{payload, PL}, {payload_encoding, E}].
172+
173+
is_utf8(<<>>) -> true;
174+
is_utf8(<<_/utf8, Rest/bits>>) -> is_utf8(Rest);
175+
is_utf8(_) -> false.

test/rabbit_mgmt_http_SUITE.erl

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ all_tests() -> [
116116
format_output_test,
117117
columns_test,
118118
get_test,
119+
get_encoding_test,
119120
get_fail_test,
120121
publish_test,
121122
publish_accept_json_test,
@@ -2245,6 +2246,41 @@ get_test(Config) ->
22452246

22462247
passed.
22472248

2249+
get_encoding_test(Config) ->
2250+
Utf8Text = <<"Loïc was here!"/utf8>>,
2251+
Utf8Payload = base64:encode(Utf8Text),
2252+
BinPayload = base64:encode(<<0:64, 16#ff, 16#fd, 0:64>>),
2253+
Utf8Msg = msg(<<"get_encoding_test">>, #{}, Utf8Payload, <<"base64">>),
2254+
BinMsg = msg(<<"get_encoding_test">>, #{}, BinPayload, <<"base64">>),
2255+
http_put(Config, "/queues/%2f/get_encoding_test", #{}, {group, '2xx'}),
2256+
http_post(Config, "/exchanges/%2f/amq.default/publish", Utf8Msg, ?OK),
2257+
http_post(Config, "/exchanges/%2f/amq.default/publish", BinMsg, ?OK),
2258+
timer:sleep(250),
2259+
[RecvUtf8Msg1, RecvBinMsg1] = http_post(Config, "/queues/%2f/get_encoding_test/get",
2260+
[{ackmode, ack_requeue_false},
2261+
{count, 2},
2262+
{encoding, auto}], ?OK),
2263+
%% Utf-8 payload must be returned as a utf-8 string when auto encoding is used.
2264+
?assertEqual(<<"string">>, maps:get(payload_encoding, RecvUtf8Msg1)),
2265+
?assertEqual(Utf8Text, maps:get(payload, RecvUtf8Msg1)),
2266+
%% Binary payload must be base64-encoded when auto is used.
2267+
?assertEqual(<<"base64">>, maps:get(payload_encoding, RecvBinMsg1)),
2268+
?assertEqual(BinPayload, maps:get(payload, RecvBinMsg1)),
2269+
%% Good. Now try forcing the base64 encoding.
2270+
http_post(Config, "/exchanges/%2f/amq.default/publish", Utf8Msg, ?OK),
2271+
http_post(Config, "/exchanges/%2f/amq.default/publish", BinMsg, ?OK),
2272+
[RecvUtf8Msg2, RecvBinMsg2] = http_post(Config, "/queues/%2f/get_encoding_test/get",
2273+
[{ackmode, ack_requeue_false},
2274+
{count, 2},
2275+
{encoding, base64}], ?OK),
2276+
%% All payloads must be base64-encoded when base64 encoding is used.
2277+
?assertEqual(<<"base64">>, maps:get(payload_encoding, RecvUtf8Msg2)),
2278+
?assertEqual(Utf8Payload, maps:get(payload, RecvUtf8Msg2)),
2279+
?assertEqual(<<"base64">>, maps:get(payload_encoding, RecvBinMsg2)),
2280+
?assertEqual(BinPayload, maps:get(payload, RecvBinMsg2)),
2281+
http_delete(Config, "/queues/%2f/get_encoding_test", {group, '2xx'}),
2282+
passed.
2283+
22482284
get_fail_test(Config) ->
22492285
http_put(Config, "/users/myuser", [{password, <<"password">>},
22502286
{tags, <<"management">>}], {group, '2xx'}),

0 commit comments

Comments
 (0)