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"}]}]).