From 3f405a7a7c20f76e65202e6130f55f5b4015b754 Mon Sep 17 00:00:00 2001 From: Tommaso Date: Fri, 5 May 2023 14:26:13 +0200 Subject: [PATCH] perf: remove the usage of charlists in favor of binaries (#8) --- src/soap_client_util.erl | 2 +- src/soap_fault.erl | 53 ++++++++++++++++++++----------------- src/soap_server_handler.erl | 2 +- test/soap_SUITE.erl | 27 ++++++++++--------- 4 files changed, 46 insertions(+), 38 deletions(-) diff --git a/src/soap_client_util.erl b/src/soap_client_util.erl index 1c5ecd5..fa251a3 100644 --- a/src/soap_client_util.erl +++ b/src/soap_client_util.erl @@ -260,7 +260,7 @@ parse_xml(Message, Model, Http_status, Http_headers, #p_state{model = Model, version = Version, soap_ns = Ns, state = start, handler = Handler}, - fun xml_parser_cb_wrapped/2, []) of + fun xml_parser_cb_wrapped/2, [{output_encoding, utf8}]) of {ok, #p_state{is_fault = true, soap_headers = Decoded_headers, soap_body = Decoded_fault, diff --git a/src/soap_fault.erl b/src/soap_fault.erl index 95766cb..b080266 100644 --- a/src/soap_fault.erl +++ b/src/soap_fault.erl @@ -52,9 +52,9 @@ -type fault_code() :: fault_code_atom() | fault_code_object(). -export_type([fault_code/0]). --type fault_string() :: string() | fault_reason() | [fault_reason()]. +-type fault_string() :: binary() | fault_reason() | [fault_reason()]. --type fault_actor() :: string() | undefined. +-type fault_actor() :: binary() | undefined. -opaque fault_detail() :: iodata(). -export_type([fault_detail/0]). @@ -75,7 +75,7 @@ -record(pf_state, { version :: atom(), state :: atom(), - characters = "" :: string() | undefined, + characters = <<>> :: binary() | undefined, code :: fault_code_object() | undefined, fault_string :: fault_string() | undefined, actor :: fault_actor() | undefined, @@ -210,7 +210,7 @@ make_reasons(Fault_string) make_reasons(Fault_strings) when is_list(Fault_strings) andalso ((length(Fault_strings) == 0) orelse is_tuple(hd(Fault_strings))) -> - [make_reason(Text) || Text <- Fault_strings]; + [make_reason(Text) || Text <- Fault_strings]; make_reasons(Fault_string) -> make_reason(#fault_reason{text = Fault_string}). @@ -300,7 +300,7 @@ fault_detail(Details, '1.2') -> xml_string(String) -> soap_req:xml_string(String). -make_code(String, N_spaces) -> +make_code(String, N_spaces) when is_list(String) -> case string:tokens(String, ":") of [Prefix, Local] -> case lists:keyfind(Prefix, 1, N_spaces) of @@ -314,7 +314,9 @@ make_code(String, N_spaces) -> _ -> #faultcode{uri = "", code = String} - end. + end; +make_code(Bin, N_spaces) when is_binary(Bin) -> + make_code(unicode:characters_to_list(Bin), N_spaces). %%% ---------------------------------------------------------------------------- %%% Parsing faults @@ -337,13 +339,13 @@ parse_fault_1_1({startElement, _, "faultcode", _, _}, parse_fault_1_1({characters, Characters}, _Namespaces, #pf_state{characters = String} = S) -> - S#pf_state{characters = String ++ Characters}; + S#pf_state{characters = <>}; parse_fault_1_1({endElement, _, "faultcode", _}, Namespaces, #pf_state{state = code, characters = String} = S) -> S#pf_state{code = make_code(String, Namespaces), - characters = "", + characters = <<>>, state = code_done}; parse_fault_1_1({startElement, _, "faultstring", _, _}, _Namespaces, @@ -354,7 +356,7 @@ parse_fault_1_1({endElement, _, "faultstring", _}, #pf_state{state = faultstring, characters = String} = S) -> S#pf_state{fault_string = String, - characters = "", + characters = <<>>, state = faultstring_done}; parse_fault_1_1({startElement, _, "faultactor", _, _}, _Namespaces, @@ -367,7 +369,7 @@ parse_fault_1_1({endElement, _, "faultactor", _}, #pf_state{state = faultactor, characters = String} = S) -> S#pf_state{actor = String, - characters = "", + characters = <<>>, state = actor_done}; parse_fault_1_1({startElement, _, "detail", _, _}, _Namespaces, @@ -389,8 +391,8 @@ parse_fault_1_1({endElement, Namespace, Tag, _}, characters = String} = S) -> S#pf_state{details = [#faultdetail{tag = Tag, uri = Namespace, - text = String} | Details], - characters = "", + text = unicode:characters_to_list(String)} | Details], + characters = <<>>, state = details}; parse_fault_1_1({endElement, _, "detail", _}, _Namespaces, @@ -431,13 +433,13 @@ parse_fault_1_2({startElement, ?SOAP12_NS, "Value", _, _}, parse_fault_1_2({characters, Characters}, _Namespaces, #pf_state{characters = String} = S) -> - S#pf_state{characters = String ++ Characters}; + S#pf_state{characters = <>}; parse_fault_1_2({endElement, ?SOAP12_NS, "Value", _}, Namespaces, #pf_state{state = code_value, characters = String} = S) -> S#pf_state{code = make_code(String, Namespaces), - characters = "", + characters = <<>>, state = value_done}; parse_fault_1_2({startElement, ?SOAP12_NS, "Subcode", _, _}, _Namespaces, @@ -449,7 +451,7 @@ parse_fault_1_2({endElement, ?SOAP12_NS, "Value", _}, code = Code, characters = String} = S) -> S#pf_state{code = Code#faultcode{subcode = make_code(String, Namespaces)}, - characters = "", + characters = <<>>, state = value_done}; parse_fault_1_2({endElement, _, "Subcode", _}, _Namespaces, @@ -488,9 +490,9 @@ parse_fault_1_2({endElement, ?SOAP12_NS, "Text", _}, language = Language, reasons = Reasons, characters = String} = S) -> - S#pf_state{reasons = [#faultreason{language = Language, - text = String} | Reasons], - characters = "", + S#pf_state{reasons = [#faultreason{language = unicode:characters_to_list(Language), + text = unicode:characters_to_list(String)} | Reasons], + characters = <<>>, state = reasons}; parse_fault_1_2({startElement, ?SOAP12_NS, "Role", _, _}, _Namespaces, @@ -503,7 +505,7 @@ parse_fault_1_2({endElement, ?SOAP12_NS, "Role", _}, #pf_state{state = role, characters = String} = S) -> S#pf_state{actor = String, - characters = "", + characters = <<>>, state = role_done}; parse_fault_1_2({startElement, ?SOAP12_NS, "Detail", _, _}, _Namespaces, @@ -525,8 +527,8 @@ parse_fault_1_2({endElement, Namespace, Tag, _}, characters = String} = S) -> S#pf_state{details = [#faultdetail{tag = Tag, uri = Namespace, - text = String} | Details], - characters = "", + text = unicode:characters_to_list(String)} | Details], + characters = <<>>, state = details}; parse_fault_1_2({endElement, ?SOAP12_NS, "Detail", _}, _Namespaces, @@ -550,13 +552,16 @@ make_record(#pf_state{version = '1.1', code = Code, actor = Actor, details = Details, fault_string = String}) -> #soap_fault_1_1{faultcode = Code, - faultstring = String, - faultactor = Actor, + faultstring = characters_to_list(String), + faultactor = characters_to_list(Actor), detail = Details}; make_record(#pf_state{version = '1.2', code = Code, actor = Actor, details = Details, reasons = Reasons}) -> #soap_fault_1_2{code = Code, reason = Reasons, - role = Actor, + role = characters_to_list(Actor), detail = Details}. + +characters_to_list(undefined) -> undefined; +characters_to_list(Bin) -> unicode:characters_to_list(Bin). diff --git a/src/soap_server_handler.erl b/src/soap_server_handler.erl index 691efcc..ff0141d 100644 --- a/src/soap_server_handler.erl +++ b/src/soap_server_handler.erl @@ -524,7 +524,7 @@ handle(Parsed_body, Soap_req, Handler_s) -> Handler = soap_req:handler(Soap_req), case lists:keyfind(Record_type, #op.in_type, Operations) of false -> - {fault, soap_fault:fault(client, "Unknown operation", Soap_req), + {fault, soap_fault:fault(client, <<"Unknown operation">>, Soap_req), Soap_req, Handler_s}; #op{operation = Operation} -> Handler:Operation(Parsed_body, Soap_req, Handler_s) diff --git a/test/soap_SUITE.erl b/test/soap_SUITE.erl index 7d5de7f..06b0958 100644 --- a/test/soap_SUITE.erl +++ b/test/soap_SUITE.erl @@ -413,7 +413,7 @@ client_11_ok(_Config) -> %% Normally one should include the record definition and use %% record syntax. %% TODO: the P0 prefixes should not be necessary. - {ok, 200, _, _, {'P0:sendMessageResponse', "OK"}, [], _} = + {ok, 200, _, _, {'P0:sendMessageResponse', <<"OK">>}, [], _} = sendService_client:'SendMessage'({'P0:sendMessage', "+31234567890", "Hello there", "Text"}, [], []). @@ -434,7 +434,7 @@ client_11_fault(_Config) -> client_12_ok() -> [{userdata,[{doc,"use the generated client, receive OK answer"}]}]. client_12_ok(_Config) -> - {ok, 200, Http_headers, _, {'P0:sendMessageResponse', "OK"}, [], Raw} = + {ok, 200, Http_headers, _, {'P0:sendMessageResponse', <<"OK">>}, [], Raw} = sendService_client:'SendMessage'({'P0:sendMessage', "+31234567890", "Hello there", "Text"}, [], []), "application/soap+xml" = proplists:get_value("Content-Type", Http_headers), @@ -479,17 +479,19 @@ test_client(_Config) -> client_no_error(_Config) -> - {ok,200, Http_headers, [],#response_body{response = "ok"}, [], _} = + {ok,200, Http_headers, [],#response_body{response = <<"ok">>}, [], _} = test_service_client:do_test(#request_body{expected_response="ok"}, [], []), "text/xml" = proplists:get_value("Content-Type", Http_headers). client_unicode(_Config) -> - {ok,200, _, [],#response_body{response = "بِسْمِ ٱلرَّحْمـَبنِ ٱلرَّحِيمِ"}, [], _} = + Expected = unicode:characters_to_binary("بِسْمِ ٱلرَّحْمـَبنِ ٱلرَّحِيمِ"), + {ok,200, _, [],#response_body{response = Expected}, [], _} = test_service_client:do_test(#request_body{expected_response= "بِسْمِ ٱلرَّحْمـَبنِ ٱلرَّحِيمِ"}, [], []). client_unicode_binary(_Config) -> - {ok,200, _, [],#response_body{response = "بِسْمِ ٱلرَّحْمـَبنِ ٱلرَّحِيمِ"}, [], _} = + Expected = unicode:characters_to_binary("بِسْمِ ٱلرَّحْمـَبنِ ٱلرَّحِيمِ"), + {ok,200, _, [],#response_body{response = Expected}, [], _} = test_service_client:do_test(#request_body{ expected_response= unicode:characters_to_binary("بِسْمِ ٱلرَّحْمـَبنِ ٱلرَّحِيمِ")}, @@ -500,7 +502,8 @@ inets_client_no_error(_Config) -> test_inets_client:do_test(#request_body{expected_response="sleep:0"}, [], []). inets_client_unicode(_Config) -> - {ok,200, _, [],#response_body{response = "بِسْمِ ٱلرَّحْمـَبنِ ٱلرَّحِيمِ"}, [], _} = + Expected = unicode:characters_to_binary("بِسْمِ ٱلرَّحْمـَبنِ ٱلرَّحِيمِ"), + {ok,200, _, [],#response_body{response = Expected}, [], _} = test_inets_client:do_test(#request_body{expected_response= "بِسْمِ ٱلرَّحْمـَبنِ ٱلرَّحِيمِ"}, [], []). @@ -578,15 +581,15 @@ client_fault_encoding_header(_Config) -> test_service_client:do_test(#request_body{expected_response="fault_encoding_header"}, [], []). client_encoded_header(_Config) -> - {ok,200, _, [#header{header_field = "hello"}], #response_body{response = "ok"}, [], _} = + {ok,200, _, [#header{header_field = <<"hello">>}], #response_body{response = <<"ok">>}, [], _} = test_service_client:do_test(#request_body{expected_response="encoded_header"}, [], []). client_two_headers(_Config) -> - {ok,200, _, [_Hash,#header{header_field = "hello"}], #response_body{response = "ok"}, [], _} = + {ok,200, _, [_Hash,#header{header_field = <<"hello">>}], #response_body{response = <<"ok">>}, [], _} = test_service_client:do_test(#request_body{expected_response="two_headers"}, [], []). client_one_header_one_skipped(_Config) -> - {ok,200, _, [#header{header_field = "hello"}], #response_body{response = "ok"}, [], _} = + {ok,200, _, [#header{header_field = <<"hello">>}], #response_body{response = <<"ok">>}, [], _} = test_service_client:do_test(#request_body{expected_response="one_header_one_skipped"}, [], []). ibrowse_client_timeout(_Config) -> @@ -595,7 +598,7 @@ ibrowse_client_timeout(_Config) -> [{http_options, [{timeout, 1000}]}]). raw(_Config) -> - {ok,200, _, [], #response_body{response = "raw"}, [], _} = + {ok,200, _, [], #response_body{response = <<"raw">>}, [], _} = test_service_client:do_test(#request_body{expected_response="raw"}, [], []). raw_client(_Config) -> @@ -603,7 +606,7 @@ raw_client(_Config) -> <<"ap/envelope/\">>, <<"som=\"test\">raw">>, ""], - {ok,200, _, [], #response_body{response = "raw"}, [], _} = + {ok,200, _, [], #response_body{response = <<"raw">>}, [], _} = test_service_client:do_test(Message, [], []). raw_client_error(_Config) -> @@ -615,7 +618,7 @@ soap_req_no_headers(_Config) -> test_service_client:do_test(#request_body{expected_response="ok"}, [], []). soap_req_w_headers(_Config) -> - {ok,200, _, [],{response_body,"authenticated!"}, [], _} = + {ok,200, _, [],{response_body,<<"authenticated!">>}, [], _} = test_service_client:do_test(#request_body{expected_response="ok"}, [], [{http_headers, [{"authorization", "user:pwd"}]}]).