From 8795c5dff9d276ab213b1f95330994a8dc9ebfe6 Mon Sep 17 00:00:00 2001 From: bas0N Date: Tue, 18 Nov 2025 02:16:53 +0100 Subject: [PATCH 1/4] Fix UTF-8 encoding for trace file downloads Set charset=utf-8 in Content-Type header to ensure proper display of Cyrillic and other Unicode characters in browsers. Fixes #13952 --- .../src/rabbit_tracing_wm_file.erl | 5 ++- .../test/rabbit_tracing_SUITE.erl | 32 ++++++++++++++++++- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/deps/rabbitmq_tracing/src/rabbit_tracing_wm_file.erl b/deps/rabbitmq_tracing/src/rabbit_tracing_wm_file.erl index 5beec3a6e40a..2a7f1451bc1b 100644 --- a/deps/rabbitmq_tracing/src/rabbit_tracing_wm_file.erl +++ b/deps/rabbitmq_tracing/src/rabbit_tracing_wm_file.erl @@ -34,7 +34,10 @@ serve(ReqData, Context) -> Content = rabbit_tracing_util:apply_on_node(ReqData, Context, rabbit_tracing_wm_file, serve, [Name]), - {Content, ReqData, Context}. + ReqWithCharset = cowboy_req:set_resp_header(<<"content-type">>, + <<"text/plain; charset=utf-8">>, + ReqData), + {Content, ReqWithCharset, Context}. serve(Name) -> Path = rabbit_tracing_files:full_path(Name), diff --git a/deps/rabbitmq_tracing/test/rabbit_tracing_SUITE.erl b/deps/rabbitmq_tracing/test/rabbit_tracing_SUITE.erl index 534777103c58..88ebee3b4c09 100644 --- a/deps/rabbitmq_tracing/test/rabbit_tracing_SUITE.erl +++ b/deps/rabbitmq_tracing/test/rabbit_tracing_SUITE.erl @@ -24,7 +24,8 @@ groups() -> [ {non_parallel_tests, [], [ tracing_test, - tracing_validation_test + tracing_validation_test, + trace_file_content_type_test ]} ]. @@ -128,6 +129,35 @@ tracing_validation_test(Config) -> http_delete(Config, Path, ?NO_CONTENT), ok. +trace_file_content_type_test(Config) -> + case filelib:is_dir(?LOG_DIR) of + true -> {ok, Files} = file:list_dir(?LOG_DIR), + [ok = file:delete(?LOG_DIR ++ F) || F <- Files]; + _ -> ok + end, + + Args = #{format => <<"text">>, + pattern => <<"#">>}, + http_put(Config, "/traces/%2f/test-charset", Args, ?CREATED), + + Ch = rabbit_ct_client_helpers:open_channel(Config), + amqp_channel:cast(Ch, #'basic.publish'{ exchange = <<"amq.topic">>, + routing_key = <<"key">> }, + #amqp_msg{props = #'P_basic'{}, + payload = <<"Test message">>}), + rabbit_ct_client_helpers:close_channel(Ch), + + timer:sleep(100), + + http_delete(Config, "/traces/%2f/test-charset", ?NO_CONTENT), + {ok, {{_HTTP, 200, _}, Headers, _Body}} = + req(Config, get, "/trace-files/test-charset.log", [auth_header("guest", "guest")]), + ContentType = proplists:get_value("content-type", Headers), + ?assertEqual(match, re:run(ContentType, "text/plain", [{capture, none}])), + ?assertEqual(match, re:run(ContentType, "charset=utf-8", [{capture, none}])), + http_delete(Config, "/trace-files/test-charset.log", ?NO_CONTENT), + ok. + %%--------------------------------------------------------------------------- %% TODO: Below is copied from rabbit_mgmt_test_http, %% should be moved to use rabbit_mgmt_test_util once rabbitmq_management From be75522572d90881d661fae3aa588135606a25e0 Mon Sep 17 00:00:00 2001 From: bas0N Date: Tue, 18 Nov 2025 02:36:22 +0100 Subject: [PATCH 2/4] Add release note for UTF-8 encoding fix in trace files --- release-notes/4.3.0.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/release-notes/4.3.0.md b/release-notes/4.3.0.md index 6cbf7f9f301a..78fada04d35e 100644 --- a/release-notes/4.3.0.md +++ b/release-notes/4.3.0.md @@ -114,6 +114,16 @@ compared to other versions. GitHub issue: [#14923](https://github.com/rabbitmq/rabbitmq-server/discussions/14923) +### Tracing Plugin + +#### Bug Fixes + + * Trace file downloads now correctly display Cyrillic and other Unicode characters in browsers + by setting `charset=utf-8` in the `Content-Type` header. + + GitHub issue: [#13952](https://github.com/rabbitmq/rabbitmq-server/issues/13952) + + ### Shovel Plugin #### Bug Fixes From b606cc068287e3ec20108a60f1f63edd7b0f5a8e Mon Sep 17 00:00:00 2001 From: bas0N Date: Tue, 18 Nov 2025 16:41:32 +0100 Subject: [PATCH 3/4] Fix tests and improve test helpers in rabbitmq_tracing - Replace timer:sleep(100) with await_condition/1 to wait for trace files instead of using fixed delays, making tests more reliable - Remove explicit close_channel calls for managed CT helper channels - Add http_get_headers/5 helper function following the same pattern as http_get/5 and http_get_raw/5, replacing direct req/4 calls - Use helper function to validate HTTP status codes and return headers consistently with the module's existing helper pattern --- .../test/rabbit_tracing_SUITE.erl | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/deps/rabbitmq_tracing/test/rabbit_tracing_SUITE.erl b/deps/rabbitmq_tracing/test/rabbit_tracing_SUITE.erl index 88ebee3b4c09..8bc185b071fb 100644 --- a/deps/rabbitmq_tracing/test/rabbit_tracing_SUITE.erl +++ b/deps/rabbitmq_tracing/test/rabbit_tracing_SUITE.erl @@ -93,9 +93,10 @@ tracing_test(Config) -> #amqp_msg{props = #'P_basic'{}, payload = <<"Hello world">>}), - rabbit_ct_client_helpers:close_channel(Ch), - - timer:sleep(100), + rabbit_ct_helpers:await_condition(fun() -> + TraceFiles = http_get(Config, "/trace-files/"), + lists:any(fun(#{name := Name}) -> Name =:= <<"test.log">> end, TraceFiles) + end), http_delete(Config, "/traces/%2f/test", ?NO_CONTENT), [] = http_get(Config, "/traces/%2f/"), @@ -145,13 +146,14 @@ trace_file_content_type_test(Config) -> routing_key = <<"key">> }, #amqp_msg{props = #'P_basic'{}, payload = <<"Test message">>}), - rabbit_ct_client_helpers:close_channel(Ch), - timer:sleep(100), + rabbit_ct_helpers:await_condition(fun() -> + TraceFiles = http_get(Config, "/trace-files/"), + lists:any(fun(#{name := Name}) -> Name =:= <<"test-charset.log">> end, TraceFiles) + end), http_delete(Config, "/traces/%2f/test-charset", ?NO_CONTENT), - {ok, {{_HTTP, 200, _}, Headers, _Body}} = - req(Config, get, "/trace-files/test-charset.log", [auth_header("guest", "guest")]), + Headers = http_get_headers(Config, "/trace-files/test-charset.log"), ContentType = proplists:get_value("content-type", Headers), ?assertEqual(match, re:run(ContentType, "text/plain", [{capture, none}])), ?assertEqual(match, re:run(ContentType, "charset=utf-8", [{capture, none}])), @@ -184,6 +186,15 @@ http_get_raw(Config, Path, User, Pass, CodeExp) -> assert_code(CodeExp, CodeAct, "GET", Path, ResBody), ResBody. +http_get_headers(Config, Path) -> + http_get_headers(Config, Path, "guest", "guest", ?OK). + +http_get_headers(Config, Path, User, Pass, CodeExp) -> + {ok, {{_HTTP, CodeAct, _}, Headers, ResBody}} = + req(Config, get, Path, [auth_header(User, Pass)]), + assert_code(CodeExp, CodeAct, "GET", Path, ResBody), + Headers. + http_put(Config, Path, List, CodeExp) -> http_put_raw(Config, Path, format_for_upload(List), CodeExp). From c1cc4952310524035c4887245d04c03311664ffb Mon Sep 17 00:00:00 2001 From: bas0N Date: Tue, 18 Nov 2025 16:56:16 +0100 Subject: [PATCH 4/4] Use charsets_provided callback for charset negotiation Replace manual header setting with charsets_provided/2 callback to let Cowboy handle charset negotiation automatically. --- deps/rabbitmq_tracing/src/rabbit_tracing_wm_file.erl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/deps/rabbitmq_tracing/src/rabbit_tracing_wm_file.erl b/deps/rabbitmq_tracing/src/rabbit_tracing_wm_file.erl index 2a7f1451bc1b..daa0aeb3c065 100644 --- a/deps/rabbitmq_tracing/src/rabbit_tracing_wm_file.erl +++ b/deps/rabbitmq_tracing/src/rabbit_tracing_wm_file.erl @@ -7,7 +7,7 @@ -module(rabbit_tracing_wm_file). -export([init/2, resource_exists/2, serve/2, content_types_provided/2, - is_authorized/2, allowed_methods/2, delete_resource/2]). + charsets_provided/2, is_authorized/2, allowed_methods/2, delete_resource/2]). -export([serve/1]). -include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl"). @@ -20,6 +20,9 @@ init(Req, _State) -> content_types_provided(ReqData, Context) -> {[{<<"text/plain">>, serve}], ReqData, Context}. +charsets_provided(ReqData, Context) -> + {[<<"utf-8">>], ReqData, Context}. + allowed_methods(ReqData, Context) -> {[<<"HEAD">>, <<"GET">>, <<"DELETE">>], ReqData, Context}. @@ -34,10 +37,7 @@ serve(ReqData, Context) -> Content = rabbit_tracing_util:apply_on_node(ReqData, Context, rabbit_tracing_wm_file, serve, [Name]), - ReqWithCharset = cowboy_req:set_resp_header(<<"content-type">>, - <<"text/plain; charset=utf-8">>, - ReqData), - {Content, ReqWithCharset, Context}. + {Content, ReqData, Context}. serve(Name) -> Path = rabbit_tracing_files:full_path(Name),