aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/dialyzer/doc/src/dialyzer.xml7
-rw-r--r--lib/dialyzer/src/dialyzer_cl.erl64
-rw-r--r--lib/dialyzer/src/dialyzer_cl_parse.erl10
-rw-r--r--lib/diameter/doc/src/notes.xml32
-rw-r--r--lib/diameter/src/base/diameter_traffic.erl187
-rw-r--r--lib/diameter/src/diameter.appup.src10
-rw-r--r--lib/diameter/src/transport/diameter_sctp.erl2
-rw-r--r--lib/diameter/test/diameter_3xxx_SUITE.erl72
-rw-r--r--lib/diameter/test/diameter_config_SUITE.erl2
-rw-r--r--lib/diameter/test/diameter_relay_SUITE.erl115
-rw-r--r--lib/diameter/test/diameter_tls_SUITE.erl18
-rw-r--r--lib/diameter/test/diameter_traffic_SUITE.erl46
-rw-r--r--lib/diameter/test/diameter_transport_SUITE.erl17
-rw-r--r--lib/diameter/test/diameter_util.erl15
-rw-r--r--lib/diameter/test/diameter_watchdog_SUITE.erl3
-rw-r--r--lib/diameter/vsn.mk2
-rw-r--r--lib/et/src/Makefile2
-rw-r--r--lib/et/src/et_collector.erl2
-rw-r--r--lib/et/src/et_selector.erl2
-rw-r--r--lib/eunit/doc/overview.edoc15
-rw-r--r--lib/eunit/include/eunit.hrl20
-rw-r--r--lib/hipe/cerl/cerl_pmatch.erl10
-rw-r--r--lib/hipe/cerl/erl_bif_types.erl8
-rw-r--r--lib/inets/doc/src/Makefile3
-rw-r--r--lib/inets/doc/src/httpd.xml10
-rw-r--r--lib/inets/doc/src/httpd_custom_api.xml63
-rw-r--r--lib/inets/doc/src/notes.xml18
-rw-r--r--lib/inets/doc/src/ref_man.xml3
-rw-r--r--lib/inets/src/http_server/Makefile3
-rw-r--r--lib/inets/src/http_server/httpd_custom.erl69
-rw-r--r--lib/inets/src/http_server/httpd_request.erl140
-rw-r--r--lib/inets/src/http_server/httpd_request_handler.erl11
-rw-r--r--lib/inets/src/http_server/httpd_response.erl52
-rw-r--r--lib/inets/src/inets_app/inets.app.src1
-rw-r--r--lib/inets/test/httpc_SUITE.erl23
-rw-r--r--lib/inets/test/httpd_SUITE.erl43
-rw-r--r--lib/kernel/doc/src/error_logger.xml31
-rw-r--r--lib/kernel/doc/src/gen_tcp.xml15
-rw-r--r--lib/kernel/src/inet.erl24
-rw-r--r--lib/kernel/src/inet_sctp.erl19
-rw-r--r--lib/kernel/test/code_SUITE.erl4
-rw-r--r--lib/kernel/test/error_logger_warn_SUITE.erl79
-rw-r--r--lib/kernel/test/gen_tcp_api_SUITE.erl32
-rw-r--r--lib/megaco/src/app/megaco.app.src2
-rw-r--r--lib/megaco/src/app/megaco.appup.src4
-rw-r--r--lib/megaco/src/engine/megaco_trans_sender.erl3
-rw-r--r--lib/os_mon/c_src/cpu_sup.c94
-rw-r--r--lib/os_mon/doc/src/cpu_sup.xml2
-rw-r--r--lib/os_mon/src/cpu_sup.erl5
-rw-r--r--lib/os_mon/test/cpu_sup_SUITE.erl2
-rw-r--r--lib/sasl/doc/src/sasl_app.xml7
-rw-r--r--lib/sasl/src/sasl.erl8
-rw-r--r--lib/sasl/src/sasl_report_file_h.erl4
-rw-r--r--lib/sasl/test/sasl_SUITE.erl31
-rw-r--r--lib/ssh/doc/src/notes.xml27
-rw-r--r--lib/ssh/src/ssh.erl9
-rw-r--r--lib/ssh/src/ssh_auth.erl80
-rw-r--r--lib/ssh/src/ssh_connection_handler.erl51
-rw-r--r--lib/ssh/src/ssh_transport.erl9
-rw-r--r--lib/ssl/doc/src/notes.xml18
-rw-r--r--lib/ssl/src/ssl.appup.src10
-rw-r--r--lib/ssl/src/ssl_handshake.erl32
-rw-r--r--lib/ssl/src/ssl_tls_dist_proxy.erl9
-rw-r--r--lib/stdlib/doc/src/orddict.xml7
-rw-r--r--lib/stdlib/doc/src/supervisor.xml12
-rw-r--r--lib/stdlib/src/erl_lint.erl5
-rw-r--r--lib/stdlib/src/orddict.erl108
-rw-r--r--lib/stdlib/src/supervisor.erl2
-rw-r--r--lib/stdlib/test/qlc_SUITE.erl18
-rw-r--r--lib/stdlib/test/supervisor_SUITE.erl23
-rw-r--r--lib/syntax_tools/src/Makefile3
-rw-r--r--lib/syntax_tools/src/syntax_tools.app.src3
72 files changed, 1343 insertions, 549 deletions
diff --git a/lib/dialyzer/doc/src/dialyzer.xml b/lib/dialyzer/doc/src/dialyzer.xml
index 5f52906625..fc076c24a6 100644
--- a/lib/dialyzer/doc/src/dialyzer.xml
+++ b/lib/dialyzer/doc/src/dialyzer.xml
@@ -70,7 +70,7 @@
[--build_plt] [--add_to_plt] [--remove_from_plt]
[--check_plt] [--no_check_plt] [--plt_info] [--get_warnings]
[--dump_callgraph file] [--no_native] [--fullpath]
- [--statistics]</code>
+ [--statistics] [--no_native_cache]</code>
<p>Options:</p>
<taglist>
<tag><c><![CDATA[files_or_dirs]]></c> (for backwards compatibility also
@@ -198,6 +198,11 @@
heuristically performs when dialyzing many files; this avoids the
compilation time but it may result in (much) longer analysis
time.</item>
+ <tag><c><![CDATA[--no_native_cache]]></c></tag>
+ <item>By default, Dialyzer caches the results of native compilation in the
+ <c>$XDG_CACHE_HOME/erlang/dialyzer_hipe_cache</c> directory.
+ <c>XDG_CACHE_HOME</c> defaults to <c>$HOME/.cache</c>.
+ Use this option to disable caching.</item>
<tag><c><![CDATA[--fullpath]]></c></tag>
<item>Display the full path names of files for which warnings are emitted.</item>
<tag><c><![CDATA[--gui]]></c></tag>
diff --git a/lib/dialyzer/src/dialyzer_cl.erl b/lib/dialyzer/src/dialyzer_cl.erl
index 4386a8d52a..55fcd15641 100644
--- a/lib/dialyzer/src/dialyzer_cl.erl
+++ b/lib/dialyzer/src/dialyzer_cl.erl
@@ -512,32 +512,82 @@ hipe_compile(Files, #options{erlang_mode = ErlangMode} = Options) ->
dialyzer_worker],
report_native_comp(Options),
{T1, _} = statistics(wall_clock),
- native_compile(Mods),
+ Cache = (get(dialyzer_options_native_cache) =/= false),
+ native_compile(Mods, Cache),
{T2, _} = statistics(wall_clock),
report_elapsed_time(T1, T2, Options)
end
end.
-native_compile(Mods) ->
+native_compile(Mods, Cache) ->
case dialyzer_utils:parallelism() > ?MIN_PARALLELISM of
true ->
Parent = self(),
- Pids = [spawn(fun () -> Parent ! {self(), hc(M)} end) || M <- Mods],
+ Pids = [spawn(fun () -> Parent ! {self(), hc(M, Cache)} end) || M <- Mods],
lists:foreach(fun (Pid) -> receive {Pid, Res} -> Res end end, Pids);
false ->
- lists:foreach(fun (Mod) -> hc(Mod) end, Mods)
+ lists:foreach(fun (Mod) -> hc(Mod, Cache) end, Mods)
end.
-hc(Mod) ->
+hc(Mod, Cache) ->
{module, Mod} = code:ensure_loaded(Mod),
case code:is_module_native(Mod) of
true -> ok;
false ->
%% io:format(" ~w", [Mod]),
- {ok, Mod} = hipe:c(Mod),
- ok
+ case Cache of
+ false ->
+ {ok, Mod} = hipe:c(Mod),
+ ok;
+ true ->
+ hc_cache(Mod)
+ end
end.
+hc_cache(Mod) ->
+ CacheBase = cache_base_dir(),
+ %% Use HiPE architecture and version in directory name, to avoid
+ %% clashes between incompatible binaries.
+ HipeArchVersion =
+ lists:concat(
+ [erlang:system_info(hipe_architecture), "-",
+ hipe:version(), "-",
+ hipe_bifs:system_crc()]),
+ CacheDir = filename:join(CacheBase, HipeArchVersion),
+ OrigBeamFile = code:which(Mod),
+ {ok, {Mod, <<Checksum:128>>}} = beam_lib:md5(OrigBeamFile),
+ CachedBeamFile = filename:join(CacheDir, lists:concat([Mod, "-", Checksum, ".beam"])),
+ ok = filelib:ensure_dir(CachedBeamFile),
+ ModBin =
+ case filelib:is_file(CachedBeamFile) of
+ true ->
+ {ok, BinFromFile} = file:read_file(CachedBeamFile),
+ BinFromFile;
+ false ->
+ {ok, Mod, CompiledBin} = compile:file(OrigBeamFile, [from_beam, native, binary]),
+ ok = file:write_file(CachedBeamFile, CompiledBin),
+ CompiledBin
+ end,
+ code:unstick_dir(filename:dirname(OrigBeamFile)),
+ {module, Mod} = code:load_binary(Mod, CachedBeamFile, ModBin),
+ true = code:is_module_native(Mod),
+ ok.
+
+cache_base_dir() ->
+ %% http://standards.freedesktop.org/basedir-spec/basedir-spec-0.7.html
+ %% If XDG_CACHE_HOME is set to an absolute path, use it as base.
+ XdgCacheHome = os:getenv("XDG_CACHE_HOME"),
+ CacheHome =
+ case is_list(XdgCacheHome) andalso filename:pathtype(XdgCacheHome) =:= absolute of
+ true ->
+ XdgCacheHome;
+ false ->
+ %% Otherwise, the default is $HOME/.cache.
+ {ok, [[Home]]} = init:get_argument(home),
+ filename:join(Home, ".cache")
+ end,
+ filename:join([CacheHome, "dialyzer_hipe_cache"]).
+
new_state() ->
#cl_state{}.
diff --git a/lib/dialyzer/src/dialyzer_cl_parse.erl b/lib/dialyzer/src/dialyzer_cl_parse.erl
index 21fc424a1b..fae88ed6e8 100644
--- a/lib/dialyzer/src/dialyzer_cl_parse.erl
+++ b/lib/dialyzer/src/dialyzer_cl_parse.erl
@@ -75,6 +75,9 @@ cl(["-nn"|T]) ->
cl(["--no_native"|T]) ->
put(dialyzer_options_native, false),
cl(T);
+cl(["--no_native_cache"|T]) ->
+ put(dialyzer_options_native_cache, false),
+ cl(T);
cl(["--plt_info"|T]) ->
put(dialyzer_options_analysis_type, plt_info),
cl(T);
@@ -363,7 +366,7 @@ help_message() ->
[--build_plt] [--add_to_plt] [--remove_from_plt]
[--check_plt] [--no_check_plt] [--plt_info] [--get_warnings]
[--dump_callgraph file] [--no_native] [--fullpath]
- [--statistics]
+ [--statistics] [--no_native_cache]
Options:
files_or_dirs (for backwards compatibility also as: -c files_or_dirs)
Use Dialyzer from the command line to detect defects in the
@@ -468,6 +471,11 @@ Options:
Bypass the native code compilation of some key files that Dialyzer
heuristically performs when dialyzing many files; this avoids the
compilation time but it may result in (much) longer analysis time.
+ --no_native_cache
+ By default, Dialyzer caches the results of native compilation in the
+ $XDG_CACHE_HOME/erlang/dialyzer_hipe_cache directory.
+ XDG_CACHE_HOME defaults to $HOME/.cache. Use this option to disable
+ caching.
--fullpath
Display the full path names of files for which warnings are emitted.
--gui
diff --git a/lib/diameter/doc/src/notes.xml b/lib/diameter/doc/src/notes.xml
index 6931788c83..c5df63a7f0 100644
--- a/lib/diameter/doc/src/notes.xml
+++ b/lib/diameter/doc/src/notes.xml
@@ -42,6 +42,36 @@ first.</p>
<!-- ===================================================================== -->
+<section><title>diameter 1.9.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix broken relay counters.</p>
+ <p>
+ OTP-12654 in OTP 17.5.3 broke counters in the case of
+ answer messages received in the relay application.
+ Counters were accumulated as unknown messages or
+ no_result_code instead of as relayed messages on the
+ intended Result-Code and 'Experimental-Result' tuples.</p>
+ <p>
+ Own Id: OTP-12741</p>
+ </item>
+ <item>
+ <p>
+ Fix diameter_sctp listener race.</p>
+ <p>
+ An oversight in OTP-12428 made it possible to start a
+ transport process that could not establish associations.</p>
+ <p>
+ Own Id: OTP-12744</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>diameter 1.9.1</title>
<section><title>Known Bugs and Problems</title>
@@ -65,7 +95,7 @@ first.</p>
received in an answer not setting the E-bit. The correct
AVP is now extracted from the incoming message.</p>
<p>
- Own Id: OTP-12654 Aux Id: seq12851 </p>
+ Own Id: OTP-12654</p>
</item>
<item>
<p>
diff --git a/lib/diameter/src/base/diameter_traffic.erl b/lib/diameter/src/base/diameter_traffic.erl
index ffd2c0afa2..eb4bbae931 100644
--- a/lib/diameter/src/base/diameter_traffic.erl
+++ b/lib/diameter/src/base/diameter_traffic.erl
@@ -131,11 +131,11 @@ peer_down(TPid) ->
%% incr/4
%% ---------------------------------------------------------------------------
-incr(Dir, #diameter_packet{header = H}, TPid, Dict) ->
- incr(Dir, H, TPid, Dict);
+incr(Dir, #diameter_packet{header = H}, TPid, AppDict) ->
+ incr(Dir, H, TPid, AppDict);
-incr(Dir, #diameter_header{} = H, TPid, Dict) ->
- incr(TPid, {msg_id(H, Dict), Dir}).
+incr(Dir, #diameter_header{} = H, TPid, AppDict) ->
+ incr(TPid, {msg_id(H, AppDict), Dir}).
%% ---------------------------------------------------------------------------
%% incr_error/4
@@ -143,26 +143,26 @@ incr(Dir, #diameter_header{} = H, TPid, Dict) ->
%% Identify messages using the application dictionary, not the encode
%% dictionary, which may differ in the case of answer-message.
-incr_error(Dir, T, Pid, {_Dict, AppDict}) ->
+incr_error(Dir, T, Pid, {_MsgDict, AppDict}) ->
incr_error(Dir, T, Pid, AppDict);
%% Decoded message without errors.
incr_error(recv, #diameter_packet{errors = []}, _, _) ->
ok;
-incr_error(recv = D, #diameter_packet{header = H}, TPid, Dict) ->
- incr_error(D, H, TPid, Dict);
+incr_error(recv = D, #diameter_packet{header = H}, TPid, AppDict) ->
+ incr_error(D, H, TPid, AppDict);
%% Encoded message with errors and an identifiable header ...
-incr_error(send = D, {_, _, #diameter_header{} = H}, TPid, Dict) ->
- incr_error(D, H, TPid, Dict);
+incr_error(send = D, {_, _, #diameter_header{} = H}, TPid, AppDict) ->
+ incr_error(D, H, TPid, AppDict);
%% ... or not.
incr_error(send = D, {_,_}, TPid, _) ->
incr_error(D, unknown, TPid);
-incr_error(Dir, #diameter_header{} = H, TPid, Dict) ->
- incr_error(Dir, msg_id(H, Dict), TPid);
+incr_error(Dir, #diameter_header{} = H, TPid, AppDict) ->
+ incr_error(Dir, msg_id(H, AppDict), TPid);
incr_error(Dir, Id, TPid, _) ->
incr_error(Dir, Id, TPid).
@@ -179,18 +179,20 @@ incr_error(Dir, Id, TPid) ->
| Reason
when Pkt :: #diameter_packet{},
TPid :: pid(),
- DictT :: module() | {module(), module(), module()},
+ DictT :: module() | {MsgDict :: module(),
+ AppDict :: module(),
+ CommonDict:: module()},
Counter :: {'Result-Code', integer()}
| {'Experimental-Result', integer(), integer()},
Reason :: atom().
-incr_rc(Dir, Pkt, TPid, {Dict, _, _} = DictT) ->
+incr_rc(Dir, Pkt, TPid, {_, AppDict, _} = DictT) ->
try
incr_result(Dir, Pkt, TPid, DictT)
catch
exit: {E,_} when E == no_result_code;
E == invalid_error_bit ->
- incr(TPid, {msg_id(Pkt#diameter_packet.header, Dict), Dir, E}),
+ incr(TPid, {msg_id(Pkt#diameter_packet.header, AppDict), Dir, E}),
E
end;
@@ -259,7 +261,8 @@ recv(false, #request{ref = Ref, handler = Pid} = Req, _, Pkt, Dict0, _) ->
%% any others are discarded.
%% ... or not.
-recv(false, false, _, _, _, _) ->
+recv(false, false, TPid, _, _, _) ->
+ incr(TPid, {{unknown, 0}, recv, discarded}),
ok.
%% spawn_request/4
@@ -307,14 +310,14 @@ recv_request(TPid, Pkt, Dict0, RecvData) -> %% from old code
%% recv_R/5
-recv_R({#diameter_app{id = Id, dictionary = Dict} = App, Caps},
+recv_R({#diameter_app{id = Id, dictionary = AppDict} = App, Caps},
TPid,
Pkt0,
Dict0,
RecvData) ->
- incr(recv, Pkt0, TPid, Dict),
- Pkt = errors(Id, diameter_codec:decode(Id, Dict, Pkt0)),
- incr_error(recv, Pkt, TPid, Dict),
+ incr(recv, Pkt0, TPid, AppDict),
+ Pkt = errors(Id, diameter_codec:decode(Id, AppDict, Pkt0)),
+ incr_error(recv, Pkt, TPid, AppDict),
{Caps, Pkt, App, recv_R(App, TPid, Dict0, Caps, RecvData, Pkt)};
%% Note that the decode is different depending on whether or not Id is
%% ?APP_ID_RELAY.
@@ -522,14 +525,17 @@ send_A(_, _, _, _) ->
%% send_A/6
-send_A(T, TPid, DictT, ReqPkt, EvalPktFs, EvalFs) ->
- reply(T, TPid, DictT, EvalPktFs, ReqPkt),
+send_A(T, TPid, {AppDict, Dict0} = DictT0, ReqPkt, EvalPktFs, EvalFs) ->
+ {MsgDict, Pkt} = reply(T, TPid, DictT0, EvalPktFs, ReqPkt),
+ incr(send, Pkt, TPid, AppDict),
+ incr_rc(send, Pkt, TPid, {MsgDict, AppDict, Dict0}), %% count outgoing
+ send(TPid, Pkt),
lists:foreach(fun diameter_lib:eval/1, EvalFs).
%% answer/6
answer({reply, Ans}, _Caps, _Pkt, App, Dict0, _RecvData) ->
- {dict(App#diameter_app.dictionary, Dict0, Ans), Ans};
+ {msg_dict(App#diameter_app.dictionary, Dict0, Ans), Ans};
answer({call, Opts}, Caps, Pkt, App, Dict0, RecvData) ->
#diameter_caps{origin_host = {OH,_}}
@@ -552,27 +558,37 @@ answer({answer_message, RC} = T, Caps, Pkt, App, Dict0, _RecvData) ->
orelse ?ERROR({invalid_return, T, handle_request, App}),
answer_message(RC, Caps, Dict0, Pkt).
-%% dict/3
+%% msg_dict/3
+%%
+%% Return the dictionary defining the message grammar in question: the
+%% application dictionary or the common dictionary.
+
+msg_dict(AppDict, Dict0, [Msg])
+ when is_list(Msg);
+ is_tuple(Msg) ->
+ msg_dict(AppDict, Dict0, Msg);
-%% An incoming answer, not yet decoded.
-dict(Dict, Dict0, #diameter_packet{header
- = #diameter_header{is_request = false,
- is_error = E},
- msg = undefined}) ->
- if E -> Dict0; true -> Dict end;
+msg_dict(AppDict, Dict0, Msg) ->
+ choose(is_answer_message(Msg, Dict0), Dict0, AppDict).
-dict(Dict, Dict0, [Msg]) ->
- dict(Dict, Dict0, Msg);
+%% Incoming, not yet decoded.
+is_answer_message(#diameter_packet{header = #diameter_header{} = H,
+ msg = undefined},
+ Dict0) ->
+ is_answer_message([H], Dict0);
-dict(Dict, Dict0, #diameter_packet{msg = Msg}) ->
- dict(Dict, Dict0, Msg);
+is_answer_message(#diameter_packet{msg = Msg}, Dict0) ->
+ is_answer_message(Msg, Dict0);
-dict(Dict, Dict0, Msg) ->
- choose(is_answer_message(Msg, Dict0), Dict0, Dict).
+%% Message sent as a header/avps list.
+is_answer_message([#diameter_header{is_request = R, is_error = E} | _], _) ->
+ E andalso not R;
+%% Message sent as a tagged avp/value list.
is_answer_message([Name | _], _) ->
Name == 'answer-message';
+%% Message sent as a record.
is_answer_message(Rec, Dict) ->
try
'answer-message' == Dict:rec2msg(element(1,Rec))
@@ -642,7 +658,7 @@ resend(false,
%%
%% Relay a reply to a relayed request.
-%% Answer from the peer: reset the hop by hop identifier and send.
+%% Answer from the peer: reset the hop by hop identifier.
resend(#diameter_packet{bin = B}
= Pkt,
_Caps,
@@ -681,13 +697,13 @@ is_loop(Code, Vid, OH, Dict0, Avps) ->
%% reply/5
%% Local answer ...
-reply({Dict, Ans}, TPid, {AppDict, Dict0}, Fs, ReqPkt) ->
- local(Ans, TPid, {Dict, AppDict, Dict0}, Fs, ReqPkt);
+reply({MsgDict, Ans}, TPid, {AppDict, Dict0}, Fs, ReqPkt) ->
+ local(Ans, TPid, {MsgDict, AppDict, Dict0}, Fs, ReqPkt);
%% ... or relayed.
-reply(#diameter_packet{} = Pkt, TPid, _Dict0, Fs, _ReqPkt) ->
+reply(#diameter_packet{} = Pkt, _TPid, {AppDict, Dict0}, Fs, _ReqPkt) ->
eval_packet(Pkt, Fs),
- send(TPid, Pkt).
+ {msg_dict(AppDict, Dict0, Pkt), Pkt}.
%% local/5
%%
@@ -700,14 +716,12 @@ local([Msg], TPid, DictT, Fs, ReqPkt)
is_tuple(Msg) ->
local(Msg, TPid, DictT, Fs, ReqPkt#diameter_packet{errors = []});
-local(Msg, TPid, {Dict, AppDict, Dict0} = DictT, Fs, ReqPkt) ->
- Pkt = encode({Dict, AppDict},
+local(Msg, TPid, {MsgDict, AppDict, Dict0}, Fs, ReqPkt) ->
+ Pkt = encode({MsgDict, AppDict},
TPid,
- reset(make_answer_packet(Msg, ReqPkt), Dict, Dict0),
+ reset(make_answer_packet(Msg, ReqPkt), MsgDict, Dict0),
Fs),
- incr(send, Pkt, TPid, AppDict),
- incr_rc(send, Pkt, TPid, DictT), %% count outgoing
- send(TPid, Pkt).
+ {MsgDict, Pkt}.
%% reset/3
@@ -1067,54 +1081,75 @@ find_avp(Code, VId, [_ | Avps]) ->
%% Increment a stats counter for result codes in incoming and outgoing
%% answers.
+%% Message sent as a header/avps list.
+incr_result(send = Dir,
+ #diameter_packet{msg = [#diameter_header{} = H | _]}
+ = Pkt,
+ TPid,
+ DictT) ->
+ incr_res(Dir, Pkt#diameter_packet{header = H}, TPid, DictT);
+
%% Outgoing message as binary: don't count. (Sending binaries is only
%% partially supported.)
-incr_result(_, #diameter_packet{msg = undefined = No}, _, _) ->
+incr_result(send, #diameter_packet{header = undefined = No}, _, _) ->
No;
%% Incoming or outgoing. Outgoing with encode errors never gets here
%% since encode fails.
-incr_result(Dir, Pkt, TPid, {Dict, AppDict, Dict0}) ->
- #diameter_packet{header = #diameter_header{is_error = E}
- = Hdr,
- errors = Es}
- = Pkt,
+incr_result(Dir, Pkt, TPid, DictT) ->
+ incr_res(Dir, Pkt, TPid, DictT).
+
+incr_res(Dir,
+ #diameter_packet{header = #diameter_header{is_error = E}
+ = Hdr,
+ errors = Es}
+ = Pkt,
+ TPid,
+ DictT) ->
+ {MsgDict, AppDict, Dict0} = DictT,
Id = msg_id(Hdr, AppDict),
+ %% Could be {relay, 0}, in which case the R-bit is redundant since
+ %% only answers are being counted. Let it be however, so that the
+ %% same tuple is in both send/recv and result code counters.
%% Count incoming decode errors.
recv /= Dir orelse [] == Es orelse incr_error(Dir, Id, TPid, AppDict),
%% Exit on a missing result code.
- T = rc_counter(Dict, Dir, Pkt),
- T == false andalso ?LOGX(no_result_code, {Dict, Dir, Hdr}),
+ T = rc_counter(MsgDict, Dir, Pkt),
+ T == false andalso ?LOGX(no_result_code, {MsgDict, Dir, Hdr}),
{Ctr, RC, Avp} = T,
%% Or on an inappropriate value.
is_result(RC, E, Dict0)
- orelse ?LOGX(invalid_error_bit, {Dict, Dir, Hdr, Avp}),
+ orelse ?LOGX(invalid_error_bit, {MsgDict, Dir, Hdr, Avp}),
incr(TPid, {Id, Dir, Ctr}),
Ctr.
%% msg_id/2
-msg_id(#diameter_packet{header = H}, Dict) ->
- msg_id(H, Dict);
+msg_id(#diameter_packet{header = H}, AppDict) ->
+ msg_id(H, AppDict);
%% Only count on known keys so as not to be vulnerable to attack:
%% there are 2^32 (application ids) * 2^24 (command codes) = 2^56
%% pairs for an attacker to choose from.
-msg_id(Hdr, Dict) ->
+msg_id(Hdr, AppDict) ->
{Aid, Code, R} = Id = diameter_codec:msg_id(Hdr),
- if Aid == ?APP_ID_RELAY ->
+ case AppDict:id() of
+ ?APP_ID_RELAY ->
{relay, R};
- true ->
- choose(Aid /= Dict:id() orelse '' == Dict:msg_name(Code, 0 == R),
- unknown,
- Id)
+ A ->
+ unknown(A /= Aid orelse '' == AppDict:msg_name(Code, 0 == R), Id)
end.
+unknown(true, {_, _, R}) ->
+ {unknown, R};
+unknown(false, Id) ->
+ Id.
+
%% No E-bit: can't be 3xxx.
is_result(RC, false, _Dict0) ->
RC < 3000 orelse 4000 =< RC;
@@ -1142,7 +1177,11 @@ incr(TPid, Counter) ->
%% applications MUST include either one Result-Code AVP or one
%% Experimental-Result AVP.
-rc_counter(Dict, recv, #diameter_packet{header = H, avps = As}) ->
+rc_counter(Dict, Dir, #diameter_packet{header = H,
+ avps = As,
+ msg = Msg})
+ when Dir == recv; %% decoded incoming
+ Msg == undefined -> %% relayed outgoing
rc_counter(Dict, [H|As]);
rc_counter(Dict, _, #diameter_packet{msg = Msg}) ->
@@ -1434,12 +1473,12 @@ fold_record(Rec, R) ->
%% send_R/6
send_R(Pkt0,
- {TPid, Caps, #diameter_app{dictionary = Dict} = App},
+ {TPid, Caps, #diameter_app{dictionary = AppDict} = App},
Opts,
{Pid, Ref},
SvcName,
Fs) ->
- Pkt = encode(Dict, TPid, Pkt0, Fs),
+ Pkt = encode(AppDict, TPid, Pkt0, Fs),
#options{timeout = Timeout}
= Opts,
@@ -1452,7 +1491,7 @@ send_R(Pkt0,
packet = Pkt0},
try
- incr(send, Pkt, TPid, Dict),
+ incr(send, Pkt, TPid, AppDict),
TRef = send_request(TPid, Pkt, Req, SvcName, Timeout),
Pid ! Ref, %% tell caller a send has been attempted
handle_answer(SvcName,
@@ -1492,10 +1531,10 @@ handle_answer(SvcName,
id = Id}
= App,
{answer, Req, Dict0, Pkt}) ->
- Dict = dict(AppDict, Dict0, Pkt),
- handle_A(errors(Id, diameter_codec:decode({Dict, AppDict}, Pkt)),
+ MsgDict = msg_dict(AppDict, Dict0, Pkt),
+ handle_A(errors(Id, diameter_codec:decode({MsgDict, AppDict}, Pkt)),
SvcName,
- Dict,
+ MsgDict,
Dict0,
App,
Req).
@@ -1765,19 +1804,19 @@ retransmit(T, {_, _, App}, _, _, _, _) ->
?ERROR({invalid_return, T, prepare_retransmit, App}).
resend_request(Pkt0,
- {TPid, Caps, #diameter_app{dictionary = Dict}},
+ {TPid, Caps, #diameter_app{dictionary = AppDict}},
Req0,
SvcName,
Tmo,
Fs) ->
- Pkt = encode(Dict, TPid, Pkt0, Fs),
+ Pkt = encode(AppDict, TPid, Pkt0, Fs),
Req = Req0#request{transport = TPid,
packet = Pkt0,
caps = Caps},
?LOG(retransmission, Pkt#diameter_packet.header),
- incr(TPid, {msg_id(Pkt, Dict), send, retransmission}),
+ incr(TPid, {msg_id(Pkt, AppDict), send, retransmission}),
TRef = send_request(TPid, Pkt, Req, SvcName, Tmo),
{TRef, Req}.
@@ -1887,7 +1926,7 @@ get_avp(Dict, Name, [#diameter_header{} | Avps]) ->
find_avp(Code, VId, Avps)
of
A ->
- avp_decode(Dict, Name, ungroup(A))
+ (avp_decode(Dict, Name, ungroup(A)))#diameter_avp{name = Name}
catch
error: _ ->
undefined
diff --git a/lib/diameter/src/diameter.appup.src b/lib/diameter/src/diameter.appup.src
index 0ef0fd35a9..b89859ed24 100644
--- a/lib/diameter/src/diameter.appup.src
+++ b/lib/diameter/src/diameter.appup.src
@@ -65,11 +65,14 @@
{update, diameter_sup, supervisor}]},
{"1.9", [{load_module, diameter_codec}, %% 17.5
{load_module, diameter_traffic},
+ {load_module, diameter_sctp},
{load_module, diameter_gen_base_rfc6733},
{load_module, diameter_gen_acct_rfc6733},
{load_module, diameter_gen_base_rfc3588},
{load_module, diameter_gen_base_accounting},
- {load_module, diameter_gen_relay}]}
+ {load_module, diameter_gen_relay}]},
+ {"1.9.1", [{load_module, diameter_traffic}, %% 17.5.3
+ {load_module, diameter_sctp}]}
],
[
{"0.9", [{restart_application, diameter}]},
@@ -120,7 +123,10 @@
{load_module, diameter_gen_base_rfc3588},
{load_module, diameter_gen_acct_rfc6733},
{load_module, diameter_gen_base_rfc6733},
+ {load_module, diameter_sctp},
{load_module, diameter_traffic},
- {load_module, diameter_codec}]}
+ {load_module, diameter_codec}]},
+ {"1.9.1", [{load_module, diameter_sctp},
+ {load_module, diameter_traffic}]}
]
}.
diff --git a/lib/diameter/src/transport/diameter_sctp.erl b/lib/diameter/src/transport/diameter_sctp.erl
index 2c8d6f0a14..f80de0a816 100644
--- a/lib/diameter/src/transport/diameter_sctp.erl
+++ b/lib/diameter/src/transport/diameter_sctp.erl
@@ -223,9 +223,9 @@ init(T) ->
i({listen, Ref, {Opts, Addrs}}) ->
{[Matches], Rest} = proplists:split(Opts, [accept]),
{LAs, Sock} = AS = open(Addrs, Rest, ?DEFAULT_PORT),
- proc_lib:init_ack({ok, self(), LAs}),
ok = gen_sctp:listen(Sock, true),
true = diameter_reg:add_new({?MODULE, listener, {Ref, AS}}),
+ proc_lib:init_ack({ok, self(), LAs}),
start_timer(#listener{ref = Ref,
socket = Sock,
accept = accept(Matches)});
diff --git a/lib/diameter/test/diameter_3xxx_SUITE.erl b/lib/diameter/test/diameter_3xxx_SUITE.erl
index 44fc3a60aa..44cb0cc484 100644
--- a/lib/diameter/test/diameter_3xxx_SUITE.erl
+++ b/lib/diameter/test/diameter_3xxx_SUITE.erl
@@ -195,13 +195,13 @@ counters(_, _, _, _) ->
stats(?CLIENT, E, rfc3588, L)
when E == answer;
E == answer_3xxx ->
- [{{unknown,recv},2},
+ [{{{unknown,0},recv},2},
{{{0,257,0},recv},1},
{{{0,257,1},send},1},
{{{0,275,0},recv},6},
{{{0,275,1},send},10},
- {{unknown,recv,{'Result-Code',3001}},1},
- {{unknown,recv,{'Result-Code',3007}},1},
+ {{{unknown,0},recv,{'Result-Code',3001}},1},
+ {{{unknown,0},recv,{'Result-Code',3007}},1},
{{{0,257,0},recv,{'Result-Code',2001}},1},
{{{0,275,0},recv,{'Result-Code',2001}},1},
{{{0,275,0},recv,{'Result-Code',3008}},2},
@@ -213,15 +213,15 @@ stats(?CLIENT, E, rfc3588, L)
stats(?SERVER, E, rfc3588, L)
when E == answer;
E == answer_3xxx ->
- [{{unknown,recv},1},
- {{unknown,send},2},
+ [{{{unknown,0},send},2},
+ {{{unknown,1},recv},1},
{{{0,257,0},send},1},
{{{0,257,1},recv},1},
{{{0,275,0},send},6},
{{{0,275,1},recv},8},
- {{unknown,recv,error},1},
- {{unknown,send,{'Result-Code',3001}},1},
- {{unknown,send,{'Result-Code',3007}},1},
+ {{{unknown,0},send,{'Result-Code',3001}},1},
+ {{{unknown,0},send,{'Result-Code',3007}},1},
+ {{{unknown,1},recv,error},1},
{{{0,257,0},send,{'Result-Code',2001}},1},
{{{0,275,0},send,{'Result-Code',2001}},1},
{{{0,275,0},send,{'Result-Code',3008}},2},
@@ -232,13 +232,13 @@ stats(?SERVER, E, rfc3588, L)
= L;
stats(?CLIENT, answer, rfc6733, L) ->
- [{{unknown,recv},2},
+ [{{{unknown,0},recv},2},
{{{0,257,0},recv},1},
{{{0,257,1},send},1},
{{{0,275,0},recv},8},
{{{0,275,1},send},10},
- {{unknown,recv,{'Result-Code',3001}},1},
- {{unknown,recv,{'Result-Code',3007}},1},
+ {{{unknown,0},recv,{'Result-Code',3001}},1},
+ {{{unknown,0},recv,{'Result-Code',3007}},1},
{{{0,257,0},recv,{'Result-Code',2001}},1},
{{{0,275,0},recv,{'Result-Code',3008}},2},
{{{0,275,0},recv,{'Result-Code',3999}},1},
@@ -248,15 +248,15 @@ stats(?CLIENT, answer, rfc6733, L) ->
= L;
stats(?SERVER, answer, rfc6733, L) ->
- [{{unknown,recv},1},
- {{unknown,send},2},
+ [{{{unknown,0},send},2},
+ {{{unknown,1},recv},1},
{{{0,257,0},send},1},
{{{0,257,1},recv},1},
{{{0,275,0},send},8},
{{{0,275,1},recv},8},
- {{unknown,recv,error},1},
- {{unknown,send,{'Result-Code',3001}},1},
- {{unknown,send,{'Result-Code',3007}},1},
+ {{{unknown,0},send,{'Result-Code',3001}},1},
+ {{{unknown,0},send,{'Result-Code',3007}},1},
+ {{{unknown,1},recv,error},1},
{{{0,257,0},send,{'Result-Code',2001}},1},
{{{0,275,0},send,{'Result-Code',3008}},2},
{{{0,275,0},send,{'Result-Code',3999}},1},
@@ -267,13 +267,13 @@ stats(?SERVER, answer, rfc6733, L) ->
= L;
stats(?CLIENT, answer_3xxx, rfc6733, L) ->
- [{{unknown,recv},2},
+ [{{{unknown,0},recv},2},
{{{0,257,0},recv},1},
{{{0,257,1},send},1},
{{{0,275,0},recv},8},
{{{0,275,1},send},10},
- {{unknown,recv,{'Result-Code',3001}},1},
- {{unknown,recv,{'Result-Code',3007}},1},
+ {{{unknown,0},recv,{'Result-Code',3001}},1},
+ {{{unknown,0},recv,{'Result-Code',3007}},1},
{{{0,257,0},recv,{'Result-Code',2001}},1},
{{{0,275,0},recv,{'Result-Code',2001}},1},
{{{0,275,0},recv,{'Result-Code',3008}},2},
@@ -284,15 +284,15 @@ stats(?CLIENT, answer_3xxx, rfc6733, L) ->
= L;
stats(?SERVER, answer_3xxx, rfc6733, L) ->
- [{{unknown,recv},1},
- {{unknown,send},2},
+ [{{{unknown,0},send},2},
+ {{{unknown,1},recv},1},
{{{0,257,0},send},1},
{{{0,257,1},recv},1},
{{{0,275,0},send},8},
{{{0,275,1},recv},8},
- {{unknown,recv,error},1},
- {{unknown,send,{'Result-Code',3001}},1},
- {{unknown,send,{'Result-Code',3007}},1},
+ {{{unknown,0},send,{'Result-Code',3001}},1},
+ {{{unknown,0},send,{'Result-Code',3007}},1},
+ {{{unknown,1},recv,error},1},
{{{0,257,0},send,{'Result-Code',2001}},1},
{{{0,275,0},send,{'Result-Code',2001}},1},
{{{0,275,0},send,{'Result-Code',3008}},2},
@@ -304,12 +304,12 @@ stats(?SERVER, answer_3xxx, rfc6733, L) ->
= L;
stats(?CLIENT, callback, rfc3588, L) ->
- [{{unknown,recv},1},
+ [{{{unknown,0},recv},1},
{{{0,257,0},recv},1},
{{{0,257,1},send},1},
{{{0,275,0},recv},6},
{{{0,275,1},send},10},
- {{unknown,recv,{'Result-Code',3007}},1},
+ {{{unknown,0},recv,{'Result-Code',3007}},1},
{{{0,257,0},recv,{'Result-Code',2001}},1},
{{{0,275,0},recv,{'Result-Code',2001}},2},
{{{0,275,0},recv,{'Result-Code',3999}},1},
@@ -318,14 +318,14 @@ stats(?CLIENT, callback, rfc3588, L) ->
= L;
stats(?SERVER, callback, rfc3588, L) ->
- [{{unknown,recv},1},
- {{unknown,send},1},
+ [{{{unknown,0},send},1},
+ {{{unknown,1},recv},1},
{{{0,257,0},send},1},
{{{0,257,1},recv},1},
{{{0,275,0},send},6},
{{{0,275,1},recv},8},
- {{unknown,recv,error},1},
- {{unknown,send,{'Result-Code',3007}},1},
+ {{{unknown,0},send,{'Result-Code',3007}},1},
+ {{{unknown,1},recv,error},1},
{{{0,257,0},send,{'Result-Code',2001}},1},
{{{0,275,0},send,{'Result-Code',2001}},2},
{{{0,275,0},send,{'Result-Code',3999}},1},
@@ -335,12 +335,12 @@ stats(?SERVER, callback, rfc3588, L) ->
= L;
stats(?CLIENT, callback, rfc6733, L) ->
- [{{unknown,recv},1},
+ [{{{unknown,0},recv},1},
{{{0,257,0},recv},1},
{{{0,257,1},send},1},
{{{0,275,0},recv},8},
{{{0,275,1},send},10},
- {{unknown,recv,{'Result-Code',3007}},1},
+ {{{unknown,0},recv,{'Result-Code',3007}},1},
{{{0,257,0},recv,{'Result-Code',2001}},1},
{{{0,275,0},recv,{'Result-Code',2001}},2},
{{{0,275,0},recv,{'Result-Code',3999}},1},
@@ -350,14 +350,14 @@ stats(?CLIENT, callback, rfc6733, L) ->
= L;
stats(?SERVER, callback, rfc6733, L) ->
- [{{unknown,recv},1},
- {{unknown,send},1},
+ [{{{unknown,0},send},1},
+ {{{unknown,1},recv},1},
{{{0,257,0},send},1},
{{{0,257,1},recv},1},
{{{0,275,0},send},8},
{{{0,275,1},recv},8},
- {{unknown,recv,error},1},
- {{unknown,send,{'Result-Code',3007}},1},
+ {{{unknown,0},send,{'Result-Code',3007}},1},
+ {{{unknown,1},recv,error},1},
{{{0,257,0},send,{'Result-Code',2001}},1},
{{{0,275,0},send,{'Result-Code',2001}},2},
{{{0,275,0},send,{'Result-Code',3999}},1},
diff --git a/lib/diameter/test/diameter_config_SUITE.erl b/lib/diameter/test/diameter_config_SUITE.erl
index bbdf672291..4bcaa8119f 100644
--- a/lib/diameter/test/diameter_config_SUITE.erl
+++ b/lib/diameter/test/diameter_config_SUITE.erl
@@ -50,7 +50,7 @@
{request_errors, RE},
{call_mutates_state, C}]]
|| D <- [diameter_gen_base_rfc3588, diameter_gen_base_rfc6733],
- M <- [?MODULE, [?MODULE, now()]],
+ M <- [?MODULE, [?MODULE, diameter_lib:now()]],
A <- [0, common, make_ref()],
S <- [[], make_ref()],
AE <- [report, callback, discard],
diff --git a/lib/diameter/test/diameter_relay_SUITE.erl b/lib/diameter/test/diameter_relay_SUITE.erl
index 735a908d97..7142239bbb 100644
--- a/lib/diameter/test/diameter_relay_SUITE.erl
+++ b/lib/diameter/test/diameter_relay_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2015. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -49,6 +49,7 @@
send_timeout_1/1,
send_timeout_2/1,
info/1,
+ counters/1,
disconnect/1,
stop_services/1,
stop/1]).
@@ -120,6 +121,7 @@ all() ->
start_services,
connect,
{group, all},
+ counters,
{group, all, [parallel]},
disconnect,
stop_services,
@@ -201,8 +203,8 @@ send3(_Config) ->
send4(_Config) ->
call(?SERVER4).
-%% Send an ASR that loops between the relays and expect the loop to
-%% be detected.
+%% Send an ASR that loops between the relays (RELAY1 -> RELAY2 ->
+%% RELAY1) and expect the loop to be detected.
send_loop(_Config) ->
Req = ['ASR', {'Destination-Realm', realm(?SERVER1)},
{'Destination-Host', ?SERVER1},
@@ -227,8 +229,103 @@ send_timeout(Tmo) ->
call(Req, [{filter, realm}, {timeout, Tmo}]).
info(_Config) ->
+ %% Wait for RELAY1 to have answered all requests, so that the
+ %% suite doesn't end before all answers are sent and counted.
+ receive after 6000 -> ok end,
[] = ?util:info().
+counters(_Config) ->
+ [] = ?util:run([[fun counters/2, K, S]
+ || K <- [statistics, transport, connections],
+ S <- ?SERVICES]).
+
+counters(Key, Svc) ->
+ counters(Key, Svc, [_|_] = diameter:service_info(Svc, Key)).
+
+counters(statistics, Svc, Stats) ->
+ stats(Svc, lists:foldl(fun({K,N},D) -> orddict:update_counter(K, N, D) end,
+ orddict:new(),
+ lists:append([L || {P,L} <- Stats, is_pid(P)])));
+
+counters(_, _, _) ->
+ todo.
+
+stats(?CLIENT, L) ->
+ [{{{0,257,0},recv},2}, %% CEA
+ {{{0,257,1},send},2}, %% CER
+ {{{0,258,0},recv},1}, %% RAA (send_timeout_1)
+ {{{0,258,1},send},2}, %% RAR (send_timeout_[12])
+ {{{0,274,0},recv},1}, %% ASA (send_loop)
+ {{{0,274,1},send},1}, %% ASR (send_loop)
+ {{{0,275,0},recv},4}, %% STA (send[1-4])
+ {{{0,275,1},send},4}, %% STR (send[1-4])
+ {{{unknown,0},recv,discarded},1}, %% RAR (send_timeout_2)
+ {{{0,257,0},recv,{'Result-Code',2001}},2}, %% CEA
+ {{{0,258,0},recv,{'Result-Code',3002}},1}, %% RAA (send_timeout_1)
+ {{{0,274,0},recv,{'Result-Code',3005}},1}, %% ASA (send_loop)
+ {{{0,275,0},recv,{'Result-Code',2001}},4}] %% STA (send[1-4])
+ = L;
+
+stats(S, L)
+ when S == ?SERVER1;
+ S == ?SERVER2;
+ S == ?SERVER3;
+ S == ?SERVER4 ->
+ [{{{0,257,0},send},1}, %% CEA
+ {{{0,257,1},recv},1}, %% CER
+ {{{0,275,0},send},1}, %% STA (send[1-4])
+ {{{0,275,1},recv},1}, %% STR (send[1-4])
+ {{{0,257,0},send,{'Result-Code',2001}},1}, %% CEA
+ {{{0,275,0},send,{'Result-Code',2001}},1}] %% STA (send[1-4])
+ = L;
+
+stats(?RELAY1, L) ->
+ [{{{relay,0},recv},3}, %% STA x 2 (send[12])
+ %% ASA (send_loop)
+ {{{relay,0},send},6}, %% STA x 2 (send[12])
+ %% ASA x 2 (send_loop)
+ %% RAA x 2 (send_timeout_[12])
+ {{{relay,1},recv},6}, %% STR x 2 (send[12])
+ %% ASR x 2 (send_loop)
+ %% RAR x 2 (send_timeout_[12])
+ {{{relay,1},send},5}, %% STR x 2 (send[12])
+ %% ASR (send_loop)
+ %% RAR x 2 (send_timeout_[12])
+ {{{0,257,0},recv},3}, %% CEA
+ {{{0,257,0},send},1}, %% "
+ {{{0,257,1},recv},1}, %% CER
+ {{{0,257,1},send},3}, %% "
+ {{{relay,0},recv,{'Result-Code',2001}},2}, %% STA x 2 (send[34])
+ {{{relay,0},recv,{'Result-Code',3005}},1}, %% ASA (send_loop)
+ {{{relay,0},send,{'Result-Code',2001}},2}, %% STA x 2 (send[34])
+ {{{relay,0},send,{'Result-Code',3002}},2}, %% RAA (send_timeout_[12])
+ {{{relay,0},send,{'Result-Code',3005}},2}, %% ASA (send_loop)
+ {{{0,257,0},recv,{'Result-Code',2001}},3}, %% CEA
+ {{{0,257,0},send,{'Result-Code',2001}},1}] %% "
+ = L;
+
+stats(?RELAY2, L) ->
+ [{{{relay,0},recv},3}, %% STA x 2 (send[34])
+ %% ASA (send_loop)
+ {{{relay,0},send},3}, %% STA x 2 (send[34])
+ %% ASA (send_loop)
+ {{{relay,1},recv},5}, %% STR x 2 (send[34])
+ %% RAR x 2 (send_timeout_[12])
+ %% ASR (send_loop)
+ {{{relay,1},send},3}, %% STR x 2 (send[34])
+ %% ASR (send_loop)
+ {{{0,257,0},recv},2}, %% CEA
+ {{{0,257,0},send},2}, %% "
+ {{{0,257,1},recv},2}, %% CER
+ {{{0,257,1},send},2}, %% "
+ {{{relay,0},recv,{'Result-Code',2001}},2}, %% STA x 2 (send[34])
+ {{{relay,0},recv,{'Result-Code',3005}},1}, %% ASA (send_loop)
+ {{{relay,0},send,{'Result-Code',2001}},2}, %% STA x 2 (send[34])
+ {{{relay,0},send,{'Result-Code',3005}},1}, %% ASA (send_loop)
+ {{{0,257,0},recv,{'Result-Code',2001}},2}, %% CEA
+ {{{0,257,0},send,{'Result-Code',2001}},2}] %% "
+ = L.
+
%% ===========================================================================
realm(Host) ->
@@ -303,18 +400,24 @@ handle_request(Pkt, OH, {_Ref, #diameter_caps{origin_host = {OH,_}} = Caps})
when OH /= ?CLIENT ->
request(Pkt, Caps).
-%% RELAY1 routes any ASR or RAR to RELAY2 ...
+%% RELAY1 answers ACR after it's timed out at the client.
+request(#diameter_packet{header = #diameter_header{cmd_code = 271}},
+ #diameter_caps{origin_host = {?RELAY1, _}}) ->
+ receive after 1000 -> {answer_message, 3004} end; %% TOO_BUSY
+
+%% RELAY1 routes any ASR or RAR to RELAY2.
request(#diameter_packet{header = #diameter_header{cmd_code = C}},
#diameter_caps{origin_host = {?RELAY1, _}})
when C == 274; %% ASR
C == 258 -> %% RAR
{relay, [{filter, {realm, realm(?RELAY2)}}]};
-%% ... which in turn routes it back. Expect diameter to either answer
-%% either with DIAMETER_LOOP_DETECTED/DIAMETER_UNABLE_TO_COMPLY.
+%% RELAY2 routes ASR back to RELAY1 to induce DIAMETER_LOOP_DETECTED.
request(#diameter_packet{header = #diameter_header{cmd_code = 274}},
#diameter_caps{origin_host = {?RELAY2, _}}) ->
{relay, [{filter, {host, ?RELAY1}}]};
+
+%% RELAY2 discards RAR to induce DIAMETER_UNABLE_TO_DELIVER.
request(#diameter_packet{header = #diameter_header{cmd_code = 258}},
#diameter_caps{origin_host = {?RELAY2, _}}) ->
discard;
diff --git a/lib/diameter/test/diameter_tls_SUITE.erl b/lib/diameter/test/diameter_tls_SUITE.erl
index 55565692ec..e5bbda9c91 100644
--- a/lib/diameter/test/diameter_tls_SUITE.erl
+++ b/lib/diameter/test/diameter_tls_SUITE.erl
@@ -319,19 +319,19 @@ make_cert(Dir, Base) ->
make_cert(Dir, Base ++ "_key.pem", Base ++ "_ca.pem").
make_cert(Dir, Keyfile, Certfile) ->
- [K,C] = Paths = [filename:join([Dir, F]) || F <- [Keyfile, Certfile]],
+ [KP,CP] = [filename:join([Dir, F]) || F <- [Keyfile, Certfile]],
- KCmd = join(["openssl genrsa -out", K, "2048"]),
- CCmd = join(["openssl req -new -x509 -key", K, "-out", C, "-days 7",
- "-subj /C=SE/ST=./L=Stockholm/CN=www.erlang.org"]),
+ KC = join(["openssl genrsa -out", KP, "2048"]),
+ CC = join(["openssl req -new -x509 -key", KP, "-out", CP, "-days 7",
+ "-subj /C=SE/ST=./L=Stockholm/CN=www.erlang.org"]),
%% Hope for the best and only check that files are written.
- os:cmd(KCmd),
- os:cmd(CCmd),
+ [{_, _, {ok,_}},{_, _, {ok,_}}]
+ = [{P,O,T} || {P,C} <- [{KP,KC}, {CP,CC}],
+ O <- [os:cmd(C)],
+ T <- [file:read_file_info(P)]],
- [_,_] = [T || P <- Paths, {ok, T} <- [file:read_file_info(P)]],
-
- {K,C}.
+ {KP,CP}.
join(Strs) ->
string:join(Strs, " ").
diff --git a/lib/diameter/test/diameter_traffic_SUITE.erl b/lib/diameter/test/diameter_traffic_SUITE.erl
index 7ff6ba7ab9..17faf30a9b 100644
--- a/lib/diameter/test/diameter_traffic_SUITE.erl
+++ b/lib/diameter/test/diameter_traffic_SUITE.erl
@@ -145,8 +145,12 @@
%% them as binary.
-define(STRING_DECODES, [true, false]).
+%% Which transport protocol to use.
+-define(TRANSPORTS, [tcp, sctp]).
+
-record(group,
- {client_service,
+ {transport,
+ client_service,
client_encoding,
client_dict0,
client_strings,
@@ -234,19 +238,20 @@
%% ===========================================================================
suite() ->
- [{timetrap, {seconds, 60}}].
+ [{timetrap, {seconds, 10}}].
all() ->
[start, result_codes, {group, traffic}, outstanding, empty, stop].
groups() ->
Ts = tc(),
+ Sctp = ?util:have_sctp(),
[{?util:name([R,D,A,C]), [parallel], Ts} || R <- ?ENCODINGS,
D <- ?RFCS,
A <- ?ENCODINGS,
C <- ?CONTAINERS]
++
- [{?util:name([R,D,A,C,SD,CD]),
+ [{?util:name([T,R,D,A,C,SD,CD]),
[],
[start_services,
add_transports,
@@ -254,15 +259,19 @@ groups() ->
{group, ?util:name([R,D,A,C])},
remove_transports,
stop_services]}
- || R <- ?ENCODINGS,
+ || T <- ?TRANSPORTS,
+ T /= sctp orelse Sctp,
+ R <- ?ENCODINGS,
D <- ?RFCS,
A <- ?ENCODINGS,
C <- ?CONTAINERS,
SD <- ?STRING_DECODES,
CD <- ?STRING_DECODES]
++
- [{traffic, [parallel], [{group, ?util:name([R,D,A,C,SD,CD])}
- || R <- ?ENCODINGS,
+ [{traffic, [parallel], [{group, ?util:name([T,R,D,A,C,SD,CD])}
+ || T <- ?TRANSPORTS,
+ T /= sctp orelse Sctp,
+ R <- ?ENCODINGS,
D <- ?RFCS,
A <- ?ENCODINGS,
C <- ?CONTAINERS,
@@ -271,8 +280,9 @@ groups() ->
init_per_group(Name, Config) ->
case ?util:name(Name) of
- [R,D,A,C,SD,CD] ->
- G = #group{client_service = [$C|?util:unique_string()],
+ [T,R,D,A,C,SD,CD] ->
+ G = #group{transport = T,
+ client_service = [$C|?util:unique_string()],
client_encoding = R,
client_dict0 = dict0(D),
client_strings = CD,
@@ -288,8 +298,18 @@ init_per_group(Name, Config) ->
end_per_group(_, _) ->
ok.
+%% Skip testcases that can reasonably fail under SCTP.
init_per_testcase(Name, Config) ->
- [{testcase, Name} | Config].
+ case [skip || #group{transport = sctp}
+ <- [proplists:get_value(group, Config)],
+ send_maxlen == Name
+ orelse send_long == Name]
+ of
+ [skip] ->
+ {skip, sctp};
+ [] ->
+ [{testcase, Name} | Config]
+ end.
end_per_testcase(_, _) ->
ok.
@@ -367,16 +387,18 @@ start_services(Config) ->
| ?SERVICE(CN, CD)]).
add_transports(Config) ->
- #group{client_service = CN,
+ #group{transport = T,
+ client_service = CN,
server_service = SN}
= group(Config),
LRef = ?util:listen(SN,
- tcp,
+ T,
[{capabilities_cb, fun capx/2},
+ {pool_size, 8},
{spawn_opt, [{min_heap_size, 8096}]},
{applications, apps(rfc3588)}]),
Cs = [?util:connect(CN,
- tcp,
+ T,
LRef,
[{id, Id},
{capabilities, [{'Origin-State-Id', origin(Id)}]},
diff --git a/lib/diameter/test/diameter_transport_SUITE.erl b/lib/diameter/test/diameter_transport_SUITE.erl
index f098851bea..78bddbd1cf 100644
--- a/lib/diameter/test/diameter_transport_SUITE.erl
+++ b/lib/diameter/test/diameter_transport_SUITE.erl
@@ -64,7 +64,7 @@
= #diameter_caps{host_ip_address
= Addrs}}).
-%% The term we register after open a listening port with gen_tcp.
+%% The term we register after open a listening port with gen_{tcp,sctp}.
-define(TEST_LISTENER(Ref, PortNr),
{?MODULE, listen, Ref, PortNr}).
@@ -85,7 +85,7 @@
%% ===========================================================================
suite() ->
- [{timetrap, {minutes, 2}}].
+ [{timetrap, {seconds, 15}}].
all() ->
[start,
@@ -401,12 +401,13 @@ gen_listen(tcp) ->
%% gen_accept/2
gen_accept(sctp, Sock) ->
- Assoc = ?RECV(?SCTP(Sock, {_, #sctp_assoc_change{state = comm_up,
- outbound_streams = O,
- inbound_streams = I,
- assoc_id = A}}),
- {O, I, A}),
- putr(assoc, Assoc),
+ #sctp_assoc_change{state = comm_up,
+ outbound_streams = OS,
+ inbound_streams = IS,
+ assoc_id = Id}
+ = ?RECV(?SCTP(Sock, {_, #sctp_assoc_change{} = S}), S),
+
+ putr(assoc, {OS, IS, Id}),
{ok, Sock};
gen_accept(tcp, LSock) ->
gen_tcp:accept(LSock).
diff --git a/lib/diameter/test/diameter_util.erl b/lib/diameter/test/diameter_util.erl
index c496876ee1..df7d268429 100644
--- a/lib/diameter/test/diameter_util.erl
+++ b/lib/diameter/test/diameter_util.erl
@@ -204,13 +204,14 @@ seed() ->
%% unique_string/0
unique_string() ->
- us(diameter_lib:now()).
-
-us({M,S,U}) ->
- tl(lists:append(["-" ++ integer_to_list(N) || N <- [M,S,U]]));
-
-us(MonoT) ->
- integer_to_list(MonoT).
+ try erlang:unique_integer() of
+ N ->
+ integer_to_list(N)
+ catch
+ error: undef -> %% OTP < 18
+ {M,S,U} = timestamp(),
+ tl(lists:append(["-" ++ integer_to_list(N) || N <- [M,S,U]]))
+ end.
%% ---------------------------------------------------------------------------
%% have_sctp/0
diff --git a/lib/diameter/test/diameter_watchdog_SUITE.erl b/lib/diameter/test/diameter_watchdog_SUITE.erl
index 5a3ff2c92f..f39e12686e 100644
--- a/lib/diameter/test/diameter_watchdog_SUITE.erl
+++ b/lib/diameter/test/diameter_watchdog_SUITE.erl
@@ -673,8 +673,7 @@ jitter(T,D) ->
%% Generate a unique hostname for the faked peer.
hostname() ->
- {M,S,U} = diameter_util:timestamp(),
- lists:flatten(io_lib:format("~p-~p-~p", [M,S,U])).
+ ?util:unique_string().
putr(Key, Val) ->
put({?MODULE, Key}, Val).
diff --git a/lib/diameter/vsn.mk b/lib/diameter/vsn.mk
index db7f72c44e..c278e74dca 100644
--- a/lib/diameter/vsn.mk
+++ b/lib/diameter/vsn.mk
@@ -16,5 +16,5 @@
# %CopyrightEnd%
APPLICATION = diameter
-DIAMETER_VSN = 1.9.1
+DIAMETER_VSN = 1.9.2
APP_VSN = $(APPLICATION)-$(DIAMETER_VSN)$(PRE_VSN)
diff --git a/lib/et/src/Makefile b/lib/et/src/Makefile
index 377e593712..b6873371ed 100644
--- a/lib/et/src/Makefile
+++ b/lib/et/src/Makefile
@@ -65,7 +65,7 @@ APPUP_TARGET = $(EBIN)/$(APPUP_FILE)
# ----------------------------------------------------
# FLAGS
# ----------------------------------------------------
-ERL_COMPILE_FLAGS += -pa $(ERL_TOP)/lib/et/ebin -I../include
+ERL_COMPILE_FLAGS += -pa $(ERL_TOP)/lib/et/ebin -I../include -Werror
# ----------------------------------------------------
# Special Build Targets
diff --git a/lib/et/src/et_collector.erl b/lib/et/src/et_collector.erl
index e05c67be60..1f60dee8ca 100644
--- a/lib/et/src/et_collector.erl
+++ b/lib/et/src/et_collector.erl
@@ -64,6 +64,8 @@
-export([init/1,terminate/2, code_change/3,
handle_call/3, handle_cast/2, handle_info/2]).
+-compile([{nowarn_deprecated_function,[{erlang,now,0}]}]).
+
-include("et_internal.hrl").
-include("../include/et.hrl").
diff --git a/lib/et/src/et_selector.erl b/lib/et/src/et_selector.erl
index c8e9c907b2..5497096377 100644
--- a/lib/et/src/et_selector.erl
+++ b/lib/et/src/et_selector.erl
@@ -28,6 +28,8 @@
parse_event/2
]).
+-compile([{nowarn_deprecated_function,[{erlang,now,0}]}]).
+
-include("../include/et.hrl").
%%----------------------------------------------------------------------
diff --git a/lib/eunit/doc/overview.edoc b/lib/eunit/doc/overview.edoc
index eb60f673ef..df716cdeea 100644
--- a/lib/eunit/doc/overview.edoc
+++ b/lib/eunit/doc/overview.edoc
@@ -572,21 +572,6 @@ Examples:
<dt>`assertNotMatch(GuardedPattern, Expr)'</dt>
<dd>The inverse case of assertMatch, for convenience.
</dd>
-<dt>`assertReceive(GuardedPattern, Timeout)'</dt>
-<dd>Waits for up to the `Timeout' milliseconds for a message to arrive
-in the mailbox of the current process that matches against the
-`GuardedPattern' if testing is enabled.
-If no message matching the `GuardedPattern' is received in the specified
-`Timeout' interval, the assertion fails and an informative exception will
-be generated; see the `assert' macro for further details. `GuardedPattern'
-can be anything that you can write on the left hand side of the `->'
-symbol in a case-clause, except that it cannot contain comma-separated
-guard tests.
-
-Examples:
-```?assertReceive(done, 1000)'''
-```?assertReceive(Bin when byte_size(Bin) > 10, 1000)'''
-</dd>
<dt>`assertEqual(Expect, Expr)'</dt>
<dd>Evaluates the expressions `Expect' and `Expr' and compares the
results for equality, if testing is enabled. If the values are not
diff --git a/lib/eunit/include/eunit.hrl b/lib/eunit/include/eunit.hrl
index 8a829396ec..53d291430d 100644
--- a/lib/eunit/include/eunit.hrl
+++ b/lib/eunit/include/eunit.hrl
@@ -166,26 +166,6 @@
%% This is mostly a convenience which gives more detailed reports.
%% Note: Guard is a guarded pattern, and can not be used for value.
-ifdef(NOASSERT).
--define(assertReceive(Guard, Timeout), ok).
--else.
--define(assertReceive(Guard, Timeout),
- begin
- ((fun () ->
- receive (Guard) -> ok
- after Timeout -> erlang:error({assertReceive_timedout,
- [{module, ?MODULE},
- {line, ?LINE},
- {pattern, (??Guard)},
- {timeout, __V}]})
- end
- end)())
- end).
--endif.
--define(_assertReceive(Guard, Timeout), ?_test(?assertReceive(Guard, Timeout))).
-
-%% This is mostly a convenience which gives more detailed reports.
-%% Note: Guard is a guarded pattern, and can not be used for value.
--ifdef(NOASSERT).
-define(assertMatch(Guard, Expr), ok).
-else.
-define(assertMatch(Guard, Expr),
diff --git a/lib/hipe/cerl/cerl_pmatch.erl b/lib/hipe/cerl/cerl_pmatch.erl
index 3bc93e80dd..4f04b0a7ed 100644
--- a/lib/hipe/cerl/cerl_pmatch.erl
+++ b/lib/hipe/cerl/cerl_pmatch.erl
@@ -31,7 +31,7 @@
-module(cerl_pmatch).
--define(NO_UNUSED, true).
+%%-define(NO_UNUSED, true).
-export([clauses/2]).
-ifndef(NO_UNUSED).
@@ -59,6 +59,8 @@
%% @see transform/2
-ifndef(NO_UNUSED).
+-spec core_transform(cerl:c_module(), [_]) -> cerl:c_module().
+
core_transform(M, Opts) ->
cerl:to_records(transform(cerl:from_records(M), Opts)).
-endif. % NO_UNUSED
@@ -76,6 +78,8 @@ core_transform(M, Opts) ->
%% @see core_transform/2
-ifndef(NO_UNUSED).
+-spec transform(cerl:cerl(), [_]) -> cerl:cerl().
+
transform(M, _Opts) ->
expr(M, env__empty()).
-endif. % NO_UNUSED
@@ -109,7 +113,7 @@ transform(M, _Opts) ->
%% @see expr/2
%% @see transform/2
--spec clauses([cerl:cerl()], rec_env:environment()) ->
+-spec clauses([cerl:cerl(),...], rec_env:environment()) ->
{cerl:cerl(), [cerl:cerl()]}.
clauses(Cs, Env) ->
@@ -406,6 +410,8 @@ make_let(Vs, A, B) ->
%% @see rec_env
-ifndef(NO_UNUSED).
+-spec expr(cerl:cerl(), rec_env:environment()) -> cerl:cerl().
+
expr(E, Env) ->
case cerl:type(E) of
literal ->
diff --git a/lib/hipe/cerl/erl_bif_types.erl b/lib/hipe/cerl/erl_bif_types.erl
index 5b1401b34a..ee77d65932 100644
--- a/lib/hipe/cerl/erl_bif_types.erl
+++ b/lib/hipe/cerl/erl_bif_types.erl
@@ -1113,8 +1113,8 @@ type(hipe_bifs, set_native_address, 3, Xs, Opaques) ->
type(hipe_bifs, set_native_address_in_fe, 2, Xs, Opaques) ->
strict(hipe_bifs, set_native_address_in_fe, 2, Xs,
fun (_) -> t_atom('true') end, Opaques);
-type(hipe_bifs, system_crc, 1, Xs, Opaques) ->
- strict(hipe_bifs, system_crc, 1, Xs, fun (_) -> t_crc32() end, Opaques);
+type(hipe_bifs, system_crc, 0, _, _Opaques) ->
+ t_crc32();
type(hipe_bifs, term_to_word, 1, Xs, Opaques) ->
strict(hipe_bifs, term_to_word, 1, Xs,
fun (_) -> t_integer() end, Opaques);
@@ -2490,8 +2490,8 @@ arg_types(hipe_bifs, set_native_address, 3) ->
[t_mfa(), t_integer(), t_boolean()];
arg_types(hipe_bifs, set_native_address_in_fe, 2) ->
[t_integer(), t_integer()];
-arg_types(hipe_bifs, system_crc, 1) ->
- [t_crc32()];
+arg_types(hipe_bifs, system_crc, 0) ->
+ [];
arg_types(hipe_bifs, term_to_word, 1) ->
[t_any()];
arg_types(hipe_bifs, update_code_size, 3) ->
diff --git a/lib/inets/doc/src/Makefile b/lib/inets/doc/src/Makefile
index 1a8e1c7ca8..961bfa838d 100644
--- a/lib/inets/doc/src/Makefile
+++ b/lib/inets/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2012. All Rights Reserved.
+# Copyright Ericsson AB 1997-2015. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -52,6 +52,7 @@ XML_REF3_FILES = \
httpc.xml\
httpd.xml \
httpd_conf.xml \
+ httpd_custom_api.xml \
httpd_socket.xml \
httpd_util.xml \
mod_alias.xml \
diff --git a/lib/inets/doc/src/httpd.xml b/lib/inets/doc/src/httpd.xml
index e40660ab39..435f99ee23 100644
--- a/lib/inets/doc/src/httpd.xml
+++ b/lib/inets/doc/src/httpd.xml
@@ -204,7 +204,15 @@
<marker id="props_limit"></marker>
<p><em>Limit properties</em> </p>
- <taglist>
+ <taglist>
+
+ <marker id="prop_customize"></marker>
+ <tag>{customize, atom()}</tag>
+ <item>
+ <p>A callback module to customize the inets HTTP servers behaviour
+ see <seealso marker="http_custom_api"> httpd_custom_api</seealso> </p>
+ </item>
+
<marker id="prop_disable_chunked_encoding"></marker>
<tag>{disable_chunked_transfer_encoding_send, boolean()}</tag>
<item>
diff --git a/lib/inets/doc/src/httpd_custom_api.xml b/lib/inets/doc/src/httpd_custom_api.xml
new file mode 100644
index 0000000000..faf1d277df
--- /dev/null
+++ b/lib/inets/doc/src/httpd_custom_api.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE erlref SYSTEM "erlref.dtd">
+
+<erlref>
+ <header>
+ <copyright>
+ <year>2015</year><year>2015</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ </legalnotice>
+
+ <title>httpd_custom_api</title>
+ <file>httpd_custom_api.xml</file>
+ </header>
+ <module>httpd_custom_api</module>
+ <modulesummary>Behaviour with optional callbacks to customize the inets HTTP server.</modulesummary>
+ <description>
+ <p> The module implementing this behaviour shall be supplied to to the servers
+ configuration with the option <seealso marker="httpd:prop_customize"> customize</seealso></p>
+
+ </description>
+ <funcs>
+ <func>
+ <name>response_header({HeaderName, HeaderValue}) -> {true, Header} | false </name>
+ <fsummary>Filter and possible alter HTTP response headers.</fsummary>
+ <type>
+ <v>Header = {HeaderName :: string(), HeaderValue::string()}</v>
+ <d>The header name will be in lower case and should not be altered.</d>
+ </type>
+ <desc>
+ <p> Filter and possible alter HTTP response headers before they are sent to the client.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name>request_header({HeaderName, HeaderValue}) -> {true, Header} | false </name>
+ <fsummary>Filter and possible alter HTTP request headers.</fsummary>
+ <type>
+ <v>Header = {HeaderName :: string(), HeaderValue::string()}</v>
+ <d>The header name will be in lower case and should not be altered.</d>
+ </type>
+ <desc>
+ <p> Filter and possible alter HTTP request headers before they are processed by the server.
+ </p>
+ </desc>
+ </func>
+ </funcs>
+</erlref>
+
+
diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml
index bae8e327a3..f563a8c4b0 100644
--- a/lib/inets/doc/src/notes.xml
+++ b/lib/inets/doc/src/notes.xml
@@ -32,7 +32,23 @@
<file>notes.xml</file>
</header>
- <section><title>Inets 5.10.8</title>
+ <section><title>Inets 5.10.9</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Add behaviour with optional callbacks to customize the
+ inets HTTP server.</p>
+ <p>
+ Own Id: OTP-12776</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 5.10.8</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/inets/doc/src/ref_man.xml b/lib/inets/doc/src/ref_man.xml
index aaedf330b4..3afb020431 100644
--- a/lib/inets/doc/src/ref_man.xml
+++ b/lib/inets/doc/src/ref_man.xml
@@ -4,7 +4,7 @@
<application xmlns:xi="http://www.w3.org/2001/XInclude">
<header>
<copyright>
- <year>1997</year><year>2013</year>
+ <year>1997</year><year>2015</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -39,6 +39,7 @@
<xi:include href="httpc.xml"/>
<xi:include href="httpd.xml"/>
<xi:include href="httpd_conf.xml"/>
+ <xi:include href="httpd_custom_api.xml"/>
<xi:include href="httpd_socket.xml"/>
<xi:include href="httpd_util.xml"/>
<xi:include href="mod_alias.xml"/>
diff --git a/lib/inets/src/http_server/Makefile b/lib/inets/src/http_server/Makefile
index 51e3dd9212..00bad51ff9 100644
--- a/lib/inets/src/http_server/Makefile
+++ b/lib/inets/src/http_server/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2005-2013. All Rights Reserved.
+# Copyright Ericsson AB 2005-2015. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -46,6 +46,7 @@ MODULES = \
httpd_connection_sup\
httpd_cgi \
httpd_conf \
+ httpd_custom \
httpd_example \
httpd_esi \
httpd_file\
diff --git a/lib/inets/src/http_server/httpd_custom.erl b/lib/inets/src/http_server/httpd_custom.erl
new file mode 100644
index 0000000000..342469a579
--- /dev/null
+++ b/lib/inets/src/http_server/httpd_custom.erl
@@ -0,0 +1,69 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2015-2015. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+-module(httpd_custom).
+
+-export([response_header/1, request_header/1]).
+-export([customize_headers/3]).
+
+-include_lib("inets/src/inets_app/inets_internal.hrl").
+
+response_header(Header) ->
+ {true, httpify(Header)}.
+request_header(Header) ->
+ {true, Header}.
+
+customize_headers(?MODULE, Function, Arg) ->
+ ?MODULE:Function(Arg);
+customize_headers(Module, Function, Arg) ->
+ try Module:Function(Arg) of
+ {true, Value} ->
+ ?MODULE:Function(Value);
+ false ->
+ false
+ catch
+ _:_ ->
+ ?MODULE:Function(Arg)
+ end.
+
+httpify({Key0, Value}) ->
+ %% make sure first letter is capital (defacto standard)
+ Words1 = string:tokens(Key0, "-"),
+ Words2 = upify(Words1, []),
+ Key = new_key(Words2),
+ Key ++ ": " ++ Value ++ ?CRLF .
+
+new_key([]) ->
+ "";
+new_key([W]) ->
+ W;
+new_key([W1,W2]) ->
+ W1 ++ "-" ++ W2;
+new_key([W|R]) ->
+ W ++ "-" ++ new_key(R).
+
+upify([], Acc) ->
+ lists:reverse(Acc);
+upify([Key|Rest], Acc) ->
+ upify(Rest, [upify2(Key)|Acc]).
+
+upify2([C|Rest]) when (C >= $a) andalso (C =< $z) ->
+ [C-($a-$A)|Rest];
+upify2(Str) ->
+ Str.
diff --git a/lib/inets/src/http_server/httpd_request.erl b/lib/inets/src/http_server/httpd_request.erl
index 3ff07616f9..782120c284 100644
--- a/lib/inets/src/http_server/httpd_request.erl
+++ b/lib/inets/src/http_server/httpd_request.erl
@@ -42,28 +42,28 @@
%%%=========================================================================
%%% Internal application API
%%%=========================================================================
-parse([Bin, MaxSizes]) ->
- ?hdrt("parse", [{bin, Bin}, {max_sizes, MaxSizes}]),
- parse_method(Bin, [], 0, proplists:get_value(max_method, MaxSizes), MaxSizes, []);
+parse([Bin, Options]) ->
+ ?hdrt("parse", [{bin, Bin}, {max_sizes, Options}]),
+ parse_method(Bin, [], 0, proplists:get_value(max_method, Options), Options, []);
parse(Unknown) ->
?hdrt("parse", [{unknown, Unknown}]),
exit({bad_args, Unknown}).
%% Functions that may be returned during the decoding process
%% if the input data is incompleate.
-parse_method([Bin, Method, Current, Max, MaxSizes, Result]) ->
- parse_method(Bin, Method, Current, Max, MaxSizes, Result).
+parse_method([Bin, Method, Current, Max, Options, Result]) ->
+ parse_method(Bin, Method, Current, Max, Options, Result).
-parse_uri([Bin, URI, Current, Max, MaxSizes, Result]) ->
- parse_uri(Bin, URI, Current, Max, MaxSizes, Result).
+parse_uri([Bin, URI, Current, Max, Options, Result]) ->
+ parse_uri(Bin, URI, Current, Max, Options, Result).
-parse_version([Bin, Rest, Version, Current, Max, MaxSizes, Result]) ->
- parse_version(<<Rest/binary, Bin/binary>>, Version, Current, Max, MaxSizes,
+parse_version([Bin, Rest, Version, Current, Max, Options, Result]) ->
+ parse_version(<<Rest/binary, Bin/binary>>, Version, Current, Max, Options,
Result).
-parse_headers([Bin, Rest, Header, Headers, Current, Max, MaxSizes, Result]) ->
+parse_headers([Bin, Rest, Header, Headers, Current, Max, Options, Result]) ->
parse_headers(<<Rest/binary, Bin/binary>>,
- Header, Headers, Current, Max, MaxSizes, Result).
+ Header, Headers, Current, Max, Options, Result).
whole_body([Bin, Body, Length]) ->
whole_body(<<Body/binary, Bin/binary>>, Length).
@@ -134,13 +134,13 @@ update_mod_data(ModData, Method, RequestURI, HTTPVersion, Headers)->
%%%========================================================================
%%% Internal functions
%%%========================================================================
-parse_method(<<>>, Method, Current, Max, MaxSizes, Result) ->
- {?MODULE, parse_method, [Method, Current, Max, MaxSizes, Result]};
-parse_method(<<?SP, Rest/binary>>, Method, _Current, _Max, MaxSizes, Result) ->
- parse_uri(Rest, [], 0, proplists:get_value(max_uri, MaxSizes), MaxSizes,
+parse_method(<<>>, Method, Current, Max, Options, Result) ->
+ {?MODULE, parse_method, [Method, Current, Max, Options, Result]};
+parse_method(<<?SP, Rest/binary>>, Method, _Current, _Max, Options, Result) ->
+ parse_uri(Rest, [], 0, proplists:get_value(max_uri, Options), Options,
[string:strip(lists:reverse(Method)) | Result]);
-parse_method(<<Octet, Rest/binary>>, Method, Current, Max, MaxSizes, Result) when Current =< Max ->
- parse_method(Rest, [Octet | Method], Current + 1, Max, MaxSizes, Result);
+parse_method(<<Octet, Rest/binary>>, Method, Current, Max, Options, Result) when Current =< Max ->
+ parse_method(Rest, [Octet | Method], Current + 1, Max, Options, Result);
parse_method(_, _, _, Max, _, _) ->
%% We do not know the version of the client as it comes after the
%% method send the lowest version in the response so that the client
@@ -153,30 +153,30 @@ parse_uri(_, _, Current, MaxURI, _, _)
%% uri send the lowest version in the response so that the client
%% will be able to handle it.
{error, {size_error, MaxURI, 414, "URI unreasonably long"},lowest_version()};
-parse_uri(<<>>, URI, Current, Max, MaxSizes, Result) ->
- {?MODULE, parse_uri, [URI, Current, Max, MaxSizes, Result]};
-parse_uri(<<?SP, Rest/binary>>, URI, _, _, MaxSizes, Result) ->
- parse_version(Rest, [], 0, proplists:get_value(max_version, MaxSizes), MaxSizes,
+parse_uri(<<>>, URI, Current, Max, Options, Result) ->
+ {?MODULE, parse_uri, [URI, Current, Max, Options, Result]};
+parse_uri(<<?SP, Rest/binary>>, URI, _, _, Options, Result) ->
+ parse_version(Rest, [], 0, proplists:get_value(max_version, Options), Options,
[string:strip(lists:reverse(URI)) | Result]);
%% Can happen if it is a simple HTTP/0.9 request e.i "GET /\r\n\r\n"
-parse_uri(<<?CR, _Rest/binary>> = Data, URI, _, _, MaxSizes, Result) ->
- parse_version(Data, [], 0, proplists:get_value(max_version, MaxSizes), MaxSizes,
+parse_uri(<<?CR, _Rest/binary>> = Data, URI, _, _, Options, Result) ->
+ parse_version(Data, [], 0, proplists:get_value(max_version, Options), Options,
[string:strip(lists:reverse(URI)) | Result]);
-parse_uri(<<Octet, Rest/binary>>, URI, Current, Max, MaxSizes, Result) ->
- parse_uri(Rest, [Octet | URI], Current + 1, Max, MaxSizes, Result).
+parse_uri(<<Octet, Rest/binary>>, URI, Current, Max, Options, Result) ->
+ parse_uri(Rest, [Octet | URI], Current + 1, Max, Options, Result).
-parse_version(<<>>, Version, Current, Max, MaxSizes, Result) ->
- {?MODULE, parse_version, [<<>>, Version, Current, Max, MaxSizes, Result]};
-parse_version(<<?LF, Rest/binary>>, Version, Current, Max, MaxSizes, Result) ->
+parse_version(<<>>, Version, Current, Max, Options, Result) ->
+ {?MODULE, parse_version, [<<>>, Version, Current, Max, Options, Result]};
+parse_version(<<?LF, Rest/binary>>, Version, Current, Max, Options, Result) ->
%% If ?CR is is missing RFC2616 section-19.3
- parse_version(<<?CR, ?LF, Rest/binary>>, Version, Current, Max, MaxSizes, Result);
-parse_version(<<?CR, ?LF, Rest/binary>>, Version, _, _, MaxSizes, Result) ->
- parse_headers(Rest, [], [], 0, proplists:get_value(max_header, MaxSizes), MaxSizes,
+ parse_version(<<?CR, ?LF, Rest/binary>>, Version, Current, Max, Options, Result);
+parse_version(<<?CR, ?LF, Rest/binary>>, Version, _, _, Options, Result) ->
+ parse_headers(Rest, [], [], 0, proplists:get_value(max_header, Options), Options,
[string:strip(lists:reverse(Version)) | Result]);
-parse_version(<<?CR>> = Data, Version, Current, Max, MaxSizes, Result) ->
- {?MODULE, parse_version, [Data, Version, Current, Max, MaxSizes, Result]};
-parse_version(<<Octet, Rest/binary>>, Version, Current, Max, MaxSizes, Result) when Current =< Max ->
- parse_version(Rest, [Octet | Version], Current + 1, Max, MaxSizes, Result);
+parse_version(<<?CR>> = Data, Version, Current, Max, Options, Result) ->
+ {?MODULE, parse_version, [Data, Version, Current, Max, Options, Result]};
+parse_version(<<Octet, Rest/binary>>, Version, Current, Max, Options, Result) when Current =< Max ->
+ parse_version(Rest, [Octet | Version], Current + 1, Max, Options, Result);
parse_version(_, _, _, Max,_,_) ->
{error, {size_error, Max, 413, "Version string unreasonably long"}, lowest_version()}.
@@ -185,34 +185,42 @@ parse_headers(_, _, _, Current, Max, _, Result)
HttpVersion = lists:nth(3, lists:reverse(Result)),
{error, {size_error, Max, 413, "Headers unreasonably long"}, HttpVersion};
-parse_headers(<<>>, Header, Headers, Current, Max, MaxSizes, Result) ->
+parse_headers(<<>>, Header, Headers, Current, Max, Options, Result) ->
{?MODULE, parse_headers, [<<>>, Header, Headers, Current, Max,
- MaxSizes, Result]};
-parse_headers(<<?CR,?LF,?LF,Body/binary>>, [], [], Current, Max, MaxSizes, Result) ->
+ Options, Result]};
+parse_headers(<<?CR,?LF,?LF,Body/binary>>, [], [], Current, Max, Options, Result) ->
%% If ?CR is is missing RFC2616 section-19.3
parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, [], [], Current, Max,
- MaxSizes, Result);
+ Options, Result);
-parse_headers(<<?LF,?LF,Body/binary>>, [], [], Current, Max, MaxSizes, Result) ->
+parse_headers(<<?LF,?LF,Body/binary>>, [], [], Current, Max, Options, Result) ->
%% If ?CR is is missing RFC2616 section-19.3
parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, [], [], Current, Max,
- MaxSizes, Result);
+ Options, Result);
parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, [], [], _, _, _, Result) ->
NewResult = list_to_tuple(lists:reverse([Body, {#http_request_h{}, []} |
Result])),
{ok, NewResult};
parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, Header, Headers, _, _,
- MaxSizes, Result) ->
+ Options, Result) ->
+ Customize = proplists:get_value(customize, Options),
case http_request:key_value(lists:reverse(Header)) of
undefined -> %% Skip headers with missing :
- {ok, list_to_tuple(lists:reverse([Body, {http_request:headers(Headers, #http_request_h{}), Headers} | Result]))};
+ FinalHeaders = lists:filtermap(fun(H) ->
+ httpd_custom:customize_headers(Customize, request_header, H)
+ end,
+ Headers),
+ {ok, list_to_tuple(lists:reverse([Body, {http_request:headers(FinalHeaders, #http_request_h{}), FinalHeaders} | Result]))};
NewHeader ->
- case check_header(NewHeader, MaxSizes) of
+ case check_header(NewHeader, Options) of
ok ->
- {ok, list_to_tuple(lists:reverse([Body, {http_request:headers([NewHeader | Headers],
+ FinalHeaders = lists:filtermap(fun(H) ->
+ httpd_custom:customize_headers(Customize, request_header, H)
+ end, [NewHeader | Headers]),
+ {ok, list_to_tuple(lists:reverse([Body, {http_request:headers(FinalHeaders,
#http_request_h{}),
- [NewHeader | Headers]} | Result]))};
+ FinalHeaders} | Result]))};
{error, Reason} ->
HttpVersion = lists:nth(3, lists:reverse(Result)),
@@ -221,12 +229,12 @@ parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, Header, Headers, _, _,
end;
parse_headers(<<?CR,?LF,?CR>> = Data, Header, Headers, Current, Max,
- MaxSizes, Result) ->
+ Options, Result) ->
{?MODULE, parse_headers, [Data, Header, Headers, Current, Max,
- MaxSizes, Result]};
-parse_headers(<<?LF>>, [], [], Current, Max, MaxSizes, Result) ->
+ Options, Result]};
+parse_headers(<<?LF>>, [], [], Current, Max, Options, Result) ->
%% If ?CR is is missing RFC2616 section-19.3
- parse_headers(<<?CR,?LF>>, [], [], Current, Max, MaxSizes, Result);
+ parse_headers(<<?CR,?LF>>, [], [], Current, Max, Options, Result);
%% There where no headers, which is unlikely to happen.
parse_headers(<<?CR,?LF>>, [], [], _, _, _, Result) ->
@@ -235,30 +243,30 @@ parse_headers(<<?CR,?LF>>, [], [], _, _, _, Result) ->
{ok, NewResult};
parse_headers(<<?LF>>, Header, Headers, Current, Max,
- MaxSizes, Result) ->
+ Options, Result) ->
%% If ?CR is is missing RFC2616 section-19.3
- parse_headers(<<?CR,?LF>>, Header, Headers, Current, Max, MaxSizes, Result);
+ parse_headers(<<?CR,?LF>>, Header, Headers, Current, Max, Options, Result);
parse_headers(<<?CR,?LF>> = Data, Header, Headers, Current, Max,
- MaxSizes, Result) ->
+ Options, Result) ->
{?MODULE, parse_headers, [Data, Header, Headers, Current, Max,
- MaxSizes, Result]};
+ Options, Result]};
parse_headers(<<?LF, Octet, Rest/binary>>, Header, Headers, Current, Max,
- MaxSizes, Result) ->
+ Options, Result) ->
%% If ?CR is is missing RFC2616 section-19.3
parse_headers(<<?CR,?LF, Octet, Rest/binary>>, Header, Headers, Current, Max,
- MaxSizes, Result);
+ Options, Result);
parse_headers(<<?CR,?LF, Octet, Rest/binary>>, Header, Headers, _, Max,
- MaxSizes, Result) ->
+ Options, Result) ->
case http_request:key_value(lists:reverse(Header)) of
undefined -> %% Skip headers with missing :
parse_headers(Rest, [Octet], Headers,
- 0, Max, MaxSizes, Result);
+ 0, Max, Options, Result);
NewHeader ->
- case check_header(NewHeader, MaxSizes) of
+ case check_header(NewHeader, Options) of
ok ->
parse_headers(Rest, [Octet], [NewHeader | Headers],
- 0, Max, MaxSizes, Result);
+ 0, Max, Options, Result);
{error, Reason} ->
HttpVersion = lists:nth(3, lists:reverse(Result)),
{error, Reason, HttpVersion}
@@ -266,19 +274,19 @@ parse_headers(<<?CR,?LF, Octet, Rest/binary>>, Header, Headers, _, Max,
end;
parse_headers(<<?CR>> = Data, Header, Headers, Current, Max,
- MaxSizes, Result) ->
+ Options, Result) ->
{?MODULE, parse_headers, [Data, Header, Headers, Current, Max,
- MaxSizes, Result]};
+ Options, Result]};
parse_headers(<<?LF>>, Header, Headers, Current, Max,
- MaxSizes, Result) ->
+ Options, Result) ->
%% If ?CR is is missing RFC2616 section-19.3
parse_headers(<<?CR, ?LF>>, Header, Headers, Current, Max,
- MaxSizes, Result);
+ Options, Result);
parse_headers(<<Octet, Rest/binary>>, Header, Headers, Current,
- Max, MaxSizes, Result) ->
+ Max, Options, Result) ->
parse_headers(Rest, [Octet | Header], Headers, Current + 1, Max,
- MaxSizes, Result).
+ Options, Result).
whole_body(Body, Length) ->
case size(Body) of
diff --git a/lib/inets/src/http_server/httpd_request_handler.erl b/lib/inets/src/http_server/httpd_request_handler.erl
index f7a9fe5d49..9947e17b47 100644
--- a/lib/inets/src/http_server/httpd_request_handler.erl
+++ b/lib/inets/src/http_server/httpd_request_handler.erl
@@ -121,13 +121,15 @@ continue_init(Manager, ConfigDB, SocketType, Socket, TimeOut) ->
MaxURISize = max_uri_size(ConfigDB),
NrOfRequest = max_keep_alive_request(ConfigDB),
MaxContentLen = max_content_length(ConfigDB),
+ Customize = customize(ConfigDB),
{_, Status} = httpd_manager:new_connection(Manager),
MFA = {httpd_request, parse, [[{max_uri, MaxURISize}, {max_header, MaxHeaderSize},
{max_version, ?HTTP_MAX_VERSION_STRING},
{max_method, ?HTTP_MAX_METHOD_STRING},
- {max_content_length, MaxContentLen}
+ {max_content_length, MaxContentLen},
+ {customize, Customize}
]]},
State = #state{mod = Mod,
@@ -550,11 +552,13 @@ handle_next_request(#state{mod = #mod{connection = true} = ModData,
MaxHeaderSize = max_header_size(ModData#mod.config_db),
MaxURISize = max_uri_size(ModData#mod.config_db),
MaxContentLen = max_content_length(ModData#mod.config_db),
+ Customize = customize(ModData#mod.config_db),
MFA = {httpd_request, parse, [[{max_uri, MaxURISize}, {max_header, MaxHeaderSize},
{max_version, ?HTTP_MAX_VERSION_STRING},
{max_method, ?HTTP_MAX_METHOD_STRING},
- {max_content_length, MaxContentLen}
+ {max_content_length, MaxContentLen},
+ {customize, Customize}
]]},
TmpState = State#state{mod = NewModData,
mfa = MFA,
@@ -640,3 +644,6 @@ max_keep_alive_request(ConfigDB) ->
max_content_length(ConfigDB) ->
httpd_util:lookup(ConfigDB, max_content_length, ?HTTP_MAX_CONTENT_LENGTH).
+
+customize(ConfigDB) ->
+ httpd_util:lookup(ConfigDB, customize, httpd_custom).
diff --git a/lib/inets/src/http_server/httpd_response.erl b/lib/inets/src/http_server/httpd_response.erl
index 2fa91d47a0..71dc05e46d 100644
--- a/lib/inets/src/http_server/httpd_response.erl
+++ b/lib/inets/src/http_server/httpd_response.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2015. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -176,7 +176,7 @@ send_header(#mod{socket_type = Type,
StatusLine = [NewVer, " ", io_lib:write(NewStatusCode), " ",
httpd_util:reason_phrase(NewStatusCode), ?CRLF],
ConnectionHeader = get_connection(Conn, NewVer),
- Head = list_to_binary([StatusLine, Headers, ConnectionHeader , ?CRLF]),
+ Head = [StatusLine, Headers, ConnectionHeader , ?CRLF],
httpd_socket:deliver(Type, Sock, Head).
map_status_code("HTTP/1.0", Code)
@@ -286,45 +286,21 @@ create_header(ConfigDb, KeyValueTupleHeaders) ->
Date = httpd_util:rfc1123_date(),
ContentType = "text/html",
Server = server(ConfigDb),
- NewHeaders = add_default_headers([{"date", Date},
- {"content-type", ContentType}
- | if Server=="" -> [];
- true -> [{"server", Server}]
- end
- ],
- KeyValueTupleHeaders),
- lists:map(fun fix_header/1, NewHeaders).
-
-
+ Headers0 = add_default_headers([{"date", Date},
+ {"content-type", ContentType}
+ | if Server=="" -> [];
+ true -> [{"server", Server}]
+ end
+ ],
+ KeyValueTupleHeaders),
+ CustomizeCB = httpd_util:lookup(ConfigDb, customize, httpd_custom),
+ lists:filtermap(fun(H) ->
+ httpd_custom:customize_headers(CustomizeCB, response_header, H)
+ end,
+ [Header || Header <- Headers0]).
server(ConfigDb) ->
httpd_util:lookup(ConfigDb, server, ?SERVER_SOFTWARE).
-fix_header({Key0, Value}) ->
- %% make sure first letter is capital
- Words1 = string:tokens(Key0, "-"),
- Words2 = upify(Words1, []),
- Key = new_key(Words2),
- Key ++ ": " ++ Value ++ ?CRLF .
-
-new_key([]) ->
- "";
-new_key([W]) ->
- W;
-new_key([W1,W2]) ->
- W1 ++ "-" ++ W2;
-new_key([W|R]) ->
- W ++ "-" ++ new_key(R).
-
-upify([], Acc) ->
- lists:reverse(Acc);
-upify([Key|Rest], Acc) ->
- upify(Rest, [upify2(Key)|Acc]).
-
-upify2([C|Rest]) when (C >= $a) andalso (C =< $z) ->
- [C-($a-$A)|Rest];
-upify2(Str) ->
- Str.
-
add_default_headers([], Headers) ->
Headers;
diff --git a/lib/inets/src/inets_app/inets.app.src b/lib/inets/src/inets_app/inets.app.src
index b7c3e341e8..6ba9795d9e 100644
--- a/lib/inets/src/inets_app/inets.app.src
+++ b/lib/inets/src/inets_app/inets.app.src
@@ -63,6 +63,7 @@
httpd_cgi,
httpd_connection_sup,
httpd_conf,
+ httpd_custom,
httpd_esi,
httpd_example,
httpd_file,
diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl
index 0dfc65e8f7..ab7ffadf75 100644
--- a/lib/inets/test/httpc_SUITE.erl
+++ b/lib/inets/test/httpc_SUITE.erl
@@ -1289,7 +1289,9 @@ dummy_server_init(Caller, ip_comm, Inet, _) ->
{max_header, ?HTTP_MAX_HEADER_SIZE},
{max_version,?HTTP_MAX_VERSION_STRING},
{max_method, ?HTTP_MAX_METHOD_STRING},
- {max_content_length, ?HTTP_MAX_CONTENT_LENGTH}]]},
+ {max_content_length, ?HTTP_MAX_CONTENT_LENGTH},
+ {customize, httpd_custom}
+ ]]},
[], ListenSocket);
dummy_server_init(Caller, ssl, Inet, SSLOptions) ->
@@ -1305,7 +1307,8 @@ dummy_ssl_server_init(Caller, BaseOpts, Inet) ->
{max_method, ?HTTP_MAX_METHOD_STRING},
{max_version,?HTTP_MAX_VERSION_STRING},
{max_method, ?HTTP_MAX_METHOD_STRING},
- {max_content_length, ?HTTP_MAX_CONTENT_LENGTH}
+ {max_content_length, ?HTTP_MAX_CONTENT_LENGTH},
+ {customize, httpd_custom}
]]},
[], ListenSocket).
@@ -1384,18 +1387,20 @@ handle_request(Module, Function, Args, Socket) ->
stop;
<<>> ->
{httpd_request, parse, [[{max_uri,?HTTP_MAX_URI_SIZE},
- {max_header, ?HTTP_MAX_HEADER_SIZE},
- {max_version,?HTTP_MAX_VERSION_STRING},
- {max_method, ?HTTP_MAX_METHOD_STRING},
- {max_content_length, ?HTTP_MAX_CONTENT_LENGTH}
- ]]};
+ {max_header, ?HTTP_MAX_HEADER_SIZE},
+ {max_version,?HTTP_MAX_VERSION_STRING},
+ {max_method, ?HTTP_MAX_METHOD_STRING},
+ {max_content_length, ?HTTP_MAX_CONTENT_LENGTH},
+ {customize, httpd_custom}
+ ]]};
Data ->
handle_request(httpd_request, parse,
[Data, [{max_uri, ?HTTP_MAX_URI_SIZE},
- {max_header, ?HTTP_MAX_HEADER_SIZE},
+ {max_header, ?HTTP_MAX_HEADER_SIZE},
{max_version,?HTTP_MAX_VERSION_STRING},
{max_method, ?HTTP_MAX_METHOD_STRING},
- {max_content_length, ?HTTP_MAX_CONTENT_LENGTH}
+ {max_content_length, ?HTTP_MAX_CONTENT_LENGTH},
+ {customize, httpd_custom}
]], Socket)
end;
NewMFA ->
diff --git a/lib/inets/test/httpd_SUITE.erl b/lib/inets/test/httpd_SUITE.erl
index 7670c2cc60..bc6b0d5c79 100644
--- a/lib/inets/test/httpd_SUITE.erl
+++ b/lib/inets/test/httpd_SUITE.erl
@@ -53,6 +53,8 @@ all() ->
{group, https_basic},
{group, http_limit},
{group, https_limit},
+ {group, http_custom},
+ {group, https_custom},
{group, http_basic_auth},
{group, https_basic_auth},
{group, http_auth_api},
@@ -76,6 +78,8 @@ groups() ->
{https_basic, [], basic_groups()},
{http_limit, [], [{group, limit}]},
{https_limit, [], [{group, limit}]},
+ {http_custom, [], [{group, custom}]},
+ {https_custom, [], [{group, custom}]},
{http_basic_auth, [], [{group, basic_auth}]},
{https_basic_auth, [], [{group, basic_auth}]},
{http_auth_api, [], [{group, auth_api}]},
@@ -92,6 +96,7 @@ groups() ->
{https_reload, [], [{group, reload}]},
{http_mime_types, [], [alias_1_1, alias_1_0, alias_0_9]},
{limit, [], [max_clients_1_1, max_clients_1_0, max_clients_0_9]},
+ {custom, [], [customize]},
{reload, [], [non_disturbing_reconfiger_dies,
disturbing_reconfiger_dies,
non_disturbing_1_1,
@@ -178,6 +183,7 @@ end_per_suite(_Config) ->
%%--------------------------------------------------------------------
init_per_group(Group, Config0) when Group == https_basic;
Group == https_limit;
+ Group == https_custom;
Group == https_basic_auth;
Group == https_auth_api;
Group == https_auth_api_dets;
@@ -188,6 +194,7 @@ init_per_group(Group, Config0) when Group == https_basic;
init_ssl(Group, Config0);
init_per_group(Group, Config0) when Group == http_basic;
Group == http_limit;
+ Group == http_custom;
Group == http_basic_auth;
Group == http_auth_api;
Group == http_auth_api_dets;
@@ -977,6 +984,30 @@ missing_CR(Config) ->
{version, Version}]).
%%-------------------------------------------------------------------------
+customize() ->
+ [{doc, "Test filtering of headers with custom callback"}].
+
+customize(Config) when is_list(Config) ->
+ Version = "HTTP/1.1",
+ Host = ?config(host, Config),
+ Type = ?config(type, Config),
+ ok = httpd_test_lib:verify_request(?config(type, Config), Host,
+ ?config(port, Config),
+ transport_opts(Type, Config),
+ ?config(node, Config),
+ http_request("GET /index.html ", Version, Host),
+ [{statuscode, 200},
+ {header, "Content-Type", "text/html"},
+ {header, "Date"},
+ {no_header, "Server"},
+ {version, Version}]).
+
+response_header({"server", _}) ->
+ false;
+response_header(Header) ->
+ {true, Header}.
+
+%%-------------------------------------------------------------------------
max_header() ->
["Denial Of Service (DOS) attack, prevented by max_header"].
max_header(Config) when is_list(Config) ->
@@ -1320,17 +1351,19 @@ setup_server_dirs(ServerRoot, DocRoot, DataDir) ->
start_apps(Group) when Group == https_basic;
Group == https_limit;
+ Group == https_custom;
Group == https_basic_auth;
Group == https_auth_api;
Group == https_auth_api_dets;
Group == https_auth_api_mnesia;
- Group == http_htaccess;
- Group == http_security;
- Group == http_reload
+ Group == https_htaccess;
+ Group == https_security;
+ Group == https_reload
->
inets_test_lib:start_apps([inets, asn1, crypto, public_key, ssl]);
start_apps(Group) when Group == http_basic;
Group == http_limit;
+ Group == http_custom;
Group == http_basic_auth;
Group == http_auth_api;
Group == http_auth_api_dets;
@@ -1390,6 +1423,10 @@ server_config(http_limit, Config) ->
[{max_clients, 1},
%% Make sure option checking code is run
{max_content_length, 100000002}] ++ server_config(http, Config);
+server_config(http_custom, Config) ->
+ [{custom, ?MODULE}] ++ server_config(http, Config);
+server_config(https_custom, Config) ->
+ [{custom, ?MODULE}] ++ server_config(https, Config);
server_config(https_limit, Config) ->
[{max_clients, 1}] ++ server_config(https, Config);
server_config(http_basic_auth, Config) ->
diff --git a/lib/kernel/doc/src/error_logger.xml b/lib/kernel/doc/src/error_logger.xml
index df2f0b01ee..f49d63b5a6 100644
--- a/lib/kernel/doc/src/error_logger.xml
+++ b/lib/kernel/doc/src/error_logger.xml
@@ -58,12 +58,11 @@
specific events. (<c>add_report_handler/1,2</c>). Also, there is
a useful event handler in STDLIB for multi-file logging of events,
see <c>log_mf_h(3)</c>.</p>
- <p>Warning events were introduced in Erlang/OTP R9C. To retain
- backwards compatibility, these are by default tagged as errors,
- thus showing up as error reports in the logs. By using
- the command line flag <c><![CDATA[+W <w | i>]]></c>, they can instead
- be tagged as warnings or info. Tagging them as warnings may
- require rewriting existing user defined event handlers.</p>
+ <p>Warning events were introduced in Erlang/OTP R9C and are enabled
+ by default as of 18.0. To retain backwards compatibility with existing
+ user defined event handlers, these may be tagged as errors or info
+ using the command line flag <c><![CDATA[+W <e | i | w>]]></c>, thus
+ showing up as error or info reports in the logs.</p>
</description>
<datatypes>
<datatype>
@@ -132,7 +131,7 @@ ok</pre>
<desc>
<p>Returns the current mapping for warning events. Events sent
using <c>warning_msg/1,2</c> or <c>warning_report/1,2</c>
- are tagged as errors (default), warnings or info, depending
+ are tagged as errors, warnings (default) or info, depending
on the value of the command line flag <c>+W</c>.</p>
<pre>
os$ <input>erl</input>
@@ -140,25 +139,25 @@ Erlang (BEAM) emulator version 5.4.8 [hipe] [threads:0] [kernel-poll]
Eshell V5.4.8 (abort with ^G)
1> <input>error_logger:warning_map().</input>
-error
-2> <input>error_logger:warning_msg("Warnings tagged as: ~p~n", [error]).</input>
+warning
+2> <input>error_logger:warning_msg("Warnings tagged as: ~p~n", [warning]).</input>
-=ERROR REPORT==== 11-Aug-2005::15:31:23 ===
-Warnings tagged as: error
+=WARNING REPORT==== 11-Aug-2005::15:31:55 ===
+Warnings tagged as: warning
ok
3>
User switch command
--> q
-os$ <input>erl +W w</input>
+os$ <input>erl +W e</input>
Erlang (BEAM) emulator version 5.4.8 [hipe] [threads:0] [kernel-poll]
Eshell V5.4.8 (abort with ^G)
1> <input>error_logger:warning_map().</input>
-warning
-2> <input>error_logger:warning_msg("Warnings tagged as: ~p~n", [warning]).</input>
+error
+2> <input>error_logger:warning_msg("Warnings tagged as: ~p~n", [error]).</input>
-=WARNING REPORT==== 11-Aug-2005::15:31:55 ===
-Warnings tagged as: warning
+=ERROR REPORT==== 11-Aug-2005::15:31:23 ===
+Warnings tagged as: error
ok</pre>
</desc>
</func>
diff --git a/lib/kernel/doc/src/gen_tcp.xml b/lib/kernel/doc/src/gen_tcp.xml
index 820ecd1e30..71ef5cd48f 100644
--- a/lib/kernel/doc/src/gen_tcp.xml
+++ b/lib/kernel/doc/src/gen_tcp.xml
@@ -347,11 +347,22 @@ do_recv(Sock, Bs) ->
</func>
<func>
<name name="shutdown" arity="2"/>
- <fsummary>Immediately close a socket</fsummary>
+ <fsummary>Asynchronously close a socket</fsummary>
<desc>
- <p>Immediately close a socket in one or two directions.</p>
+ <p>Close a socket in one or two directions.</p>
<p><c><anno>How</anno> == write</c> means closing the socket for writing,
reading from it is still possible.</p>
+ <p>If <c><anno>How</anno> == read</c>, or there is no outgoing
+ data buffered in the <c><anno>Socket</anno></c> port,
+ then the socket is shutdown immediately and any error encountered
+ is returned in <c><anno>Reason</anno></c>.</p>
+ <p>If there is data buffered in the socket port, then the attempt
+ to shutdown the socket is postponed until that data is written to the
+ kernel socket send buffer. Any errors encountered will result
+ in the socket being closed and <c>{error, closed}</c> being returned
+ on the next
+ <seealso marker="gen_tcp#recv/2">recv/2</seealso> or
+ <seealso marker="gen_tcp#send/2">send/2</seealso>.</p>
<p>To be able to handle that the peer has done a shutdown on
the write side, the <c>{exit_on_close, false}</c> option
is useful.</p>
diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl
index ec2c350931..d668738109 100644
--- a/lib/kernel/src/inet.erl
+++ b/lib/kernel/src/inet.erl
@@ -1527,26 +1527,28 @@ tcp_controlling_process(S, NewOwner) when is_port(S), is_pid(NewOwner) ->
_ ->
case prim_inet:getopt(S, active) of
{ok, A0} ->
- case A0 of
- false -> ok;
- _ -> ok = prim_inet:setopt(S, active, false)
- end,
- case tcp_sync_input(S, NewOwner, false) of
- true -> %% socket already closed,
+ SetOptRes =
+ case A0 of
+ false -> ok;
+ _ -> prim_inet:setopt(S, active, false)
+ end,
+ case {tcp_sync_input(S, NewOwner, false), SetOptRes} of
+ {true, _} -> %% socket already closed
ok;
- false ->
+ {false, ok} ->
try erlang:port_connect(S, NewOwner) of
true ->
unlink(S), %% unlink from port
case A0 of
false -> ok;
- _ -> ok = prim_inet:setopt(S, active, A0)
- end,
- ok
+ _ -> prim_inet:setopt(S, active, A0)
+ end
catch
error:Reason ->
{error, Reason}
- end
+ end;
+ {false, Error} ->
+ Error
end;
Error ->
Error
diff --git a/lib/kernel/src/inet_sctp.erl b/lib/kernel/src/inet_sctp.erl
index 93528d305d..f0f13c8d4a 100644
--- a/lib/kernel/src/inet_sctp.erl
+++ b/lib/kernel/src/inet_sctp.erl
@@ -133,15 +133,18 @@ connect_get_assoc(S, Addr, Port, Active, Timer) ->
Timeout = inet:timeout(Timer),
receive
{sctp,S,Addr,Port,{_,#sctp_assoc_change{state=St}=Ev}} ->
- case Active of
- once ->
- ok = prim_inet:setopt(S, active, once);
- _ -> ok
- end,
- if St =:= comm_up ->
+ SetOptRes =
+ case Active of
+ once -> prim_inet:setopt(S, active, once);
+ _ -> ok
+ end,
+ case {St, SetOptRes} of
+ {comm_up, ok} ->
{ok,Ev};
- true ->
- {error,Ev}
+ {_, ok} ->
+ {error,Ev};
+ {_, Error} ->
+ Error
end
after Timeout ->
{error,timeout}
diff --git a/lib/kernel/test/code_SUITE.erl b/lib/kernel/test/code_SUITE.erl
index 549c65d034..c82aaf0582 100644
--- a/lib/kernel/test/code_SUITE.erl
+++ b/lib/kernel/test/code_SUITE.erl
@@ -1654,9 +1654,7 @@ get_mode(Config) when is_list(Config) ->
init(Tester) ->
{ok, Tester}.
-handle_event({error, _GL, {emulator, _, _}}, Tester) ->
- {ok, Tester};
-handle_event({error, _GL, Msg}, Tester) ->
+handle_event({warning_msg, _GL, Msg}, Tester) ->
Tester ! Msg,
{ok, Tester};
handle_event(_Event, State) ->
diff --git a/lib/kernel/test/error_logger_warn_SUITE.erl b/lib/kernel/test/error_logger_warn_SUITE.erl
index 2bf467610e..fb576d77a3 100644
--- a/lib/kernel/test/error_logger_warn_SUITE.erl
+++ b/lib/kernel/test/error_logger_warn_SUITE.erl
@@ -21,8 +21,8 @@
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2,
init_per_testcase/2,end_per_testcase/2,
- basic/1,warnings_info/1,warnings_warnings/1,
- rb_basic/1,rb_warnings_info/1,rb_warnings_warnings/1,
+ basic/1,warnings_info/1,warnings_errors/1,
+ rb_basic/1,rb_warnings_info/1,rb_warnings_errors/1,
rb_trunc/1,rb_utc/1,file_utc/1]).
%% Internal exports.
@@ -48,8 +48,8 @@
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [basic, warnings_info, warnings_warnings, rb_basic,
- rb_warnings_info, rb_warnings_warnings, rb_trunc,
+ [basic, warnings_info, warnings_errors, rb_basic,
+ rb_warnings_info, rb_warnings_errors, rb_trunc,
rb_utc, file_utc].
groups() ->
@@ -88,11 +88,11 @@ warnings_info(Config) when is_list(Config) ->
put(elw_config,Config),
warnings_info().
-warnings_warnings(doc) ->
- ["Tests mapping warnings to warnings functionality"];
-warnings_warnings(Config) when is_list(Config) ->
+warnings_errors(doc) ->
+ ["Tests mapping warnings to errors functionality"];
+warnings_errors(Config) when is_list(Config) ->
put(elw_config,Config),
- warnings_warnings().
+ warnings_errors().
rb_basic(doc) ->
["Tests basic rb functionality"];
@@ -106,11 +106,11 @@ rb_warnings_info(Config) when is_list(Config) ->
put(elw_config,Config),
rb_warnings_info().
-rb_warnings_warnings(doc) ->
- ["Tests warnings as warnings rb functionality"];
-rb_warnings_warnings(Config) when is_list(Config) ->
+rb_warnings_errors(doc) ->
+ ["Tests warnings as errors rb functionality"];
+rb_warnings_errors(Config) when is_list(Config) ->
put(elw_config,Config),
- rb_warnings_warnings().
+ rb_warnings_errors().
rb_trunc(doc) ->
["Tests rb functionality on truncated data"];
@@ -159,6 +159,9 @@ install_relay(Node) ->
rpc:call(Node,error_logger,add_report_handler,[?MODULE,[self()]]).
+warning_map(Node) ->
+ rpc:call(Node,error_logger,warning_map,[]).
+
format(Node,A,B) ->
rpc:call(Node,error_logger,format,[A,B]).
error_msg(Node,A,B) ->
@@ -185,19 +188,20 @@ basic() ->
?line ok = install_relay(Node),
?line Self = self(),
?line GL = group_leader(),
+ ?line warning = warning_map(Node),
?line format(Node,"~p~n",[Self]),
?line ?EXPECT({handle_event,{error,GL,{_,"~p~n",[Self]}}}),
?line error_msg(Node,"~p~n",[Self]),
?line ?EXPECT({handle_event,{error,GL,{_,"~p~n",[Self]}}}),
?line warning_msg(Node,"~p~n",[Self]),
- ?line ?EXPECT({handle_event,{error,GL,{_,"~p~n",[Self]}}}),
+ ?line ?EXPECT({handle_event,{warning_msg,GL,{_,"~p~n",[Self]}}}),
?line info_msg(Node,"~p~n",[Self]),
?line ?EXPECT({handle_event,{info_msg,GL,{_,"~p~n",[Self]}}}),
?line Report = [{self,Self},{gl,GL},make_ref()],
?line error_report(Node,Report),
?line ?EXPECT({handle_event,{error_report,GL,{_,std_error,Report}}}),
?line warning_report(Node,Report),
- ?line ?EXPECT({handle_event,{error_report,GL,{_,std_error,Report}}}),
+ ?line ?EXPECT({handle_event,{warning_report,GL,{_,std_warning,Report}}}),
?line info_report(Node,Report),
?line ?EXPECT({handle_event,{info_report,GL,{_,std_info,Report}}}),
@@ -209,6 +213,7 @@ warnings_info() ->
?line ok = install_relay(Node),
?line Self = self(),
?line GL = group_leader(),
+ ?line info = warning_map(Node),
?line Report = [{self,Self},{gl,GL},make_ref()],
?line warning_msg(Node,"~p~n",[Self]),
?line ?EXPECT({handle_event,{info_msg,GL,{_,"~p~n",[Self]}}}),
@@ -217,16 +222,17 @@ warnings_info() ->
?line stop_node(Node),
ok.
-warnings_warnings() ->
- ?line Node = start_node(nn(),"+Ww"),
+warnings_errors() ->
+ ?line Node = start_node(nn(),"+We"),
?line ok = install_relay(Node),
?line Self = self(),
?line GL = group_leader(),
+ ?line error = warning_map(Node),
?line Report = [{self,Self},{gl,GL},make_ref()],
?line warning_msg(Node,"~p~n",[Self]),
- ?line ?EXPECT({handle_event,{warning_msg,GL,{_,"~p~n",[Self]}}}),
+ ?line ?EXPECT({handle_event,{error,GL,{_,"~p~n",[Self]}}}),
?line warning_report(Node,Report),
- ?line ?EXPECT({handle_event,{warning_report,GL,{_,std_warning,Report}}}),
+ ?line ?EXPECT({handle_event,{error_report,GL,{_,std_error,Report}}}),
?line stop_node(Node),
ok.
@@ -356,6 +362,7 @@ rb_basic() ->
"error_logger_mf_maxfiles 5"),
?line Self = self(),
?line GL = group_leader(),
+ ?line warning = warning_map(Node),
?line Report = [{self,Self},{gl,GL},make_ref()],
?line fake_gl(Node,warning_msg,"~p~n",[Self]),
?line fake_gl(Node,warning_report,Report),
@@ -363,10 +370,14 @@ rb_basic() ->
?line application:start(sasl),
?line rb:start([{report_dir, rd()}]),
?line rb:list(),
- ?line true = (one_rb_lines([error]) > 1),
- ?line true = (one_rb_lines([error_report]) > 1),
- ?line 1 = one_rb_findstr([error],pid_to_list(Self)),
- ?line 1 = one_rb_findstr([error_report],pid_to_list(Self)),
+ ?line true = (one_rb_lines([error]) =:= 0),
+ ?line true = (one_rb_lines([error_report]) =:= 0),
+ ?line 0 = one_rb_findstr([error],pid_to_list(Self)),
+ ?line 0 = one_rb_findstr([error_report],pid_to_list(Self)),
+ ?line 1 = one_rb_findstr([warning_msg],pid_to_list(Self)),
+ ?line 1 = one_rb_findstr([warning_report],pid_to_list(Self)),
+ ?line 0 = one_rb_findstr([info_msg],pid_to_list(Self)),
+ ?line 0 = one_rb_findstr([info_report],pid_to_list(Self)),
?line 2 = one_rb_findstr([],pid_to_list(Self)),
?line true = (one_rb_findstr([progress],"===") > 4),
?line rb:stop(),
@@ -381,6 +392,7 @@ rb_warnings_info() ->
"error_logger_mf_maxfiles 5"),
?line Self = self(),
?line GL = group_leader(),
+ ?line info = warning_map(Node),
?line Report = [{self,Self},{gl,GL},make_ref()],
?line fake_gl(Node,warning_msg,"~p~n",[Self]),
?line fake_gl(Node,warning_report,Report),
@@ -403,13 +415,14 @@ rb_warnings_info() ->
?line stop_node(Node),
ok.
-rb_warnings_warnings() ->
+rb_warnings_errors() ->
?line clean_rd(),
- ?line Node = start_node(nn(),"+W w -boot start_sasl -sasl error_logger_mf_dir "++
+ ?line Node = start_node(nn(),"+W e -boot start_sasl -sasl error_logger_mf_dir "++
quote(rd())++" error_logger_mf_maxbytes 5000 "
"error_logger_mf_maxfiles 5"),
?line Self = self(),
?line GL = group_leader(),
+ ?line error = warning_map(Node),
?line Report = [{self,Self},{gl,GL},make_ref()],
?line fake_gl(Node,warning_msg,"~p~n",[Self]),
?line fake_gl(Node,warning_report,Report),
@@ -417,12 +430,12 @@ rb_warnings_warnings() ->
?line application:start(sasl),
?line rb:start([{report_dir, rd()}]),
?line rb:list(),
- ?line true = (one_rb_lines([error]) =:= 0),
- ?line true = (one_rb_lines([error_report]) =:= 0),
- ?line 0 = one_rb_findstr([error],pid_to_list(Self)),
- ?line 0 = one_rb_findstr([error_report],pid_to_list(Self)),
- ?line 1 = one_rb_findstr([warning_msg],pid_to_list(Self)),
- ?line 1 = one_rb_findstr([warning_report],pid_to_list(Self)),
+ ?line true = (one_rb_lines([error]) > 1),
+ ?line true = (one_rb_lines([error_report]) > 1),
+ ?line 1 = one_rb_findstr([error],pid_to_list(Self)),
+ ?line 1 = one_rb_findstr([error_report],pid_to_list(Self)),
+ ?line 0 = one_rb_findstr([warning_msg],pid_to_list(Self)),
+ ?line 0 = one_rb_findstr([warning_report],pid_to_list(Self)),
?line 0 = one_rb_findstr([info_msg],pid_to_list(Self)),
?line 0 = one_rb_findstr([info_report],pid_to_list(Self)),
?line 2 = one_rb_findstr([],pid_to_list(Self)),
@@ -434,7 +447,7 @@ rb_warnings_warnings() ->
rb_trunc() ->
?line clean_rd(),
- ?line Node = start_node(nn(),"+W w -boot start_sasl -sasl error_logger_mf_dir "++
+ ?line Node = start_node(nn(),"-boot start_sasl -sasl error_logger_mf_dir "++
quote(rd())++" error_logger_mf_maxbytes 5000 "
"error_logger_mf_maxfiles 5"),
?line Self = self(),
@@ -467,7 +480,7 @@ rb_trunc() ->
rb_utc() ->
?line clean_rd(),
- ?line Node = start_node(nn(),"+W w -boot start_sasl -sasl error_logger_mf_dir "++
+ ?line Node = start_node(nn(),"-boot start_sasl -sasl error_logger_mf_dir "++
quote(rd())++" error_logger_mf_maxbytes 5000 "
"error_logger_mf_maxfiles 5 -sasl utc_log true"),
?line Self = self(),
@@ -500,7 +513,7 @@ rb_utc() ->
file_utc() ->
?line file:delete(lf()),
- ?line SS="+W w -stdlib utc_log true -kernel error_logger "++ oquote("{file,"++iquote(lf())++"}"),
+ ?line SS="-stdlib utc_log true -kernel error_logger "++ oquote("{file,"++iquote(lf())++"}"),
%erlang:display(SS),
?line Node = start_node(nn(),SS),
%erlang:display(rpc:call(Node,application,get_env,[kernel,error_logger])),
diff --git a/lib/kernel/test/gen_tcp_api_SUITE.erl b/lib/kernel/test/gen_tcp_api_SUITE.erl
index c27d265550..4a527e2f51 100644
--- a/lib/kernel/test/gen_tcp_api_SUITE.erl
+++ b/lib/kernel/test/gen_tcp_api_SUITE.erl
@@ -32,6 +32,7 @@
t_connect_bad/1,
t_recv_timeout/1, t_recv_eof/1,
t_shutdown_write/1, t_shutdown_both/1, t_shutdown_error/1,
+ t_shutdown_async/1,
t_fdopen/1, t_fdconnect/1, t_implicit_inet6/1]).
-export([getsockfd/0,closesockfd/1]).
@@ -41,7 +42,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[{group, t_accept}, {group, t_connect}, {group, t_recv},
t_shutdown_write, t_shutdown_both, t_shutdown_error,
- t_fdopen, t_fdconnect, t_implicit_inet6].
+ t_shutdown_async, t_fdopen, t_fdconnect, t_implicit_inet6].
groups() ->
[{t_accept, [], [t_accept_timeout]},
@@ -155,7 +156,34 @@ t_shutdown_error(Config) when is_list(Config) ->
?line ok = gen_tcp:close(L),
?line {error, closed} = gen_tcp:shutdown(L, read_write),
ok.
-
+
+t_shutdown_async(Config) when is_list(Config) ->
+ ?line {OS, _} = os:type(),
+ ?line {ok, L} = gen_tcp:listen(0, [{sndbuf, 4096}]),
+ ?line {ok, Port} = inet:port(L),
+ ?line {ok, Client} = gen_tcp:connect(localhost, Port,
+ [{recbuf, 4096},
+ {active, false}]),
+ ?line {ok, S} = gen_tcp:accept(L),
+ ?line PayloadSize = 1024 * 1024,
+ ?line Payload = lists:duplicate(PayloadSize, $.),
+ ?line ok = gen_tcp:send(S, Payload),
+ ?line case erlang:port_info(S, queue_size) of
+ {queue_size, N} when N > 0 -> ok;
+ {queue_size, 0} when OS =:= win32 -> ok;
+ {queue_size, 0} = T -> ?t:fail({unexpected, T})
+ end,
+
+ ?line ok = gen_tcp:shutdown(S, write),
+ ?line {ok, Buf} = gen_tcp:recv(Client, PayloadSize),
+ ?line {error, closed} = gen_tcp:recv(Client, 0),
+ ?line case length(Buf) of
+ PayloadSize -> ok;
+ Sz -> ?t:fail({payload_size,
+ {expected, PayloadSize},
+ {received, Sz}})
+ end.
+
%%% gen_tcp:fdopen/2
diff --git a/lib/megaco/src/app/megaco.app.src b/lib/megaco/src/app/megaco.app.src
index 3720b1109e..573b1857f6 100644
--- a/lib/megaco/src/app/megaco.app.src
+++ b/lib/megaco/src/app/megaco.app.src
@@ -114,7 +114,7 @@
{env, []},
{mod, {megaco_sup, []}},
{runtime_dependencies, ["stdlib-2.5","runtime_tools-1.8.14","kernel-3.0",
- "et-1.5","erts-6.0","debugger-4.0",
+ "et-1.5","erts-7.0","debugger-4.0",
"asn1-3.0"]}
]}.
diff --git a/lib/megaco/src/app/megaco.appup.src b/lib/megaco/src/app/megaco.appup.src
index 86ad09f639..1c55a92b55 100644
--- a/lib/megaco/src/app/megaco.appup.src
+++ b/lib/megaco/src/app/megaco.appup.src
@@ -191,7 +191,7 @@
{"%VSN%",
[
- {"3.17.3", []},
+ {"3.17.3", [{restart_application,megaco}]},
{"3.17.2", []},
{"3.17.1", [{restart_application,megaco}]},
{"3.17.0.3", [{restart_application,megaco}]},
@@ -206,7 +206,7 @@
}
],
[
- {"3.17.3", []},
+ {"3.17.3", [{restart_application,megaco}]},
{"3.17.2", []},
{"3.17.1", [{restart_application,megaco}]},
{"3.17.0.3", [{restart_application,megaco}]},
diff --git a/lib/megaco/src/engine/megaco_trans_sender.erl b/lib/megaco/src/engine/megaco_trans_sender.erl
index 710fef405a..e07f404289 100644
--- a/lib/megaco/src/engine/megaco_trans_sender.erl
+++ b/lib/megaco/src/engine/megaco_trans_sender.erl
@@ -672,8 +672,7 @@ to(To, Start) ->
%% Time in milli seconds
t() ->
- {A,B,C} = erlang:now(),
- A*1000000000+B*1000+(C div 1000).
+ erlang:monotonic_time(milli_seconds).
warning_msg(F, A) ->
?megaco_warning("Transaction sender: " ++ F, A).
diff --git a/lib/os_mon/c_src/cpu_sup.c b/lib/os_mon/c_src/cpu_sup.c
index 20bb9ce391..9e217db105 100644
--- a/lib/os_mon/c_src/cpu_sup.c
+++ b/lib/os_mon/c_src/cpu_sup.c
@@ -53,7 +53,6 @@
#endif
#if defined(__linux__)
-#include <string.h> /* strlen */
#define PROCSTAT "/proc/stat"
#define BUFFERSIZE (256)
@@ -73,6 +72,13 @@ typedef struct {
#endif
+#if defined(__FreeBSD__)
+#include <sys/resource.h>
+#include <sys/sysctl.h>
+#define CU_BSD_VALUES (6)
+#endif
+
+
#define FD_IN (0)
#define FD_OUT (1)
#define FD_ERR (2)
@@ -157,12 +163,16 @@ static int processors_online() {
}
#endif
+#if defined(__FreeBSD__)
+void getsysctl(const char *, void *, size_t);
+#endif
+
int main(int argc, char** argv) {
char cmd;
int rc;
int sz;
unsigned int *rv;
-#if defined(__linux__)
+#if defined(__linux__) || defined(__FreeBSD__)
unsigned int no_of_cpus = 0;
#endif
@@ -175,7 +185,14 @@ int main(int argc, char** argv) {
#if defined(__linux__)
no_of_cpus = processors_online();
if ( (rv = (unsigned int*)malloc(sizeof(unsigned int)*(2 + 2*no_of_cpus*CU_VALUES))) == NULL) {
- error("cpu_cup: malloc error");
+ error("cpu_sup: malloc error");
+ }
+#endif
+
+#if defined(__FreeBSD__)
+ getsysctl("hw.ncpu", &no_of_cpus, sizeof(int));
+ if ( (rv = (unsigned int*)malloc(sizeof(unsigned int)*(2 + 2*no_of_cpus*CU_BSD_VALUES))) == NULL) {
+ error("cpu_sup: malloc error");
}
#endif
@@ -204,14 +221,14 @@ int main(int argc, char** argv) {
case AVG5: bsd_loadavg(1); break;
case AVG15: bsd_loadavg(2); break;
#endif
-#if defined(__sun__) || defined(__linux__)
+#if defined(__sun__) || defined(__linux__) || defined(__FreeBSD__)
case UTIL: util_measure(&rv,&sz); sendv(rv, sz); break;
#endif
case QUIT: free((void*)rv); return 0;
default: error("Bad command"); break;
}
}
- return 0; /* supress warnings */
+ return 0; /* suppress warnings */
}
/* ---------------------------- *
@@ -520,6 +537,71 @@ static void util_measure(unsigned int **result_vec, int *result_sz) {
#endif
/* ---------------------------- *
+ * FreeBSD stat functions *
+ * ---------------------------- */
+
+#if defined(__FreeBSD__)
+
+#define EXIT_WITH(msg) (rich_error(msg, __FILE__, __LINE__))
+#define RICH_BUFLEN (213) /* left in error(char*) */
+
+void rich_error(const char *reason, const char *file, const int line) {
+ char buf[RICH_BUFLEN];
+ snprintf(buf, RICH_BUFLEN, "%s (%s:%i)", reason, file, line);
+ error(buf);
+}
+#undef RICH_BUFLEN
+
+static void util_measure(unsigned int **result_vec, int *result_sz) {
+ int no_of_cpus;
+ size_t size_cpu_times;
+ unsigned long *cpu_times;
+ unsigned int *rv = NULL;
+ int i;
+
+ getsysctl("hw.ncpu", &no_of_cpus, sizeof(int));
+ /* Header constant CPUSTATES = #long values per cpu. */
+ size_cpu_times = sizeof(long) * CPUSTATES * no_of_cpus;
+ cpu_times = malloc(size_cpu_times);
+ if (!cpu_times) {
+ EXIT_WITH("badalloc");
+ }
+ getsysctl("kern.cp_times", cpu_times, size_cpu_times);
+
+ rv = *result_vec;
+ rv[0] = no_of_cpus;
+ rv[1] = CU_BSD_VALUES;
+ ++rv; /* first value is number of cpus */
+ ++rv; /* second value is number of entries */
+
+ for (i = 0; i < no_of_cpus; ++i) {
+ int offset = i * CPUSTATES;
+ rv[ 0] = CU_CPU_ID; rv[ 1] = i;
+ rv[ 2] = CU_USER; rv[ 3] = cpu_times[CP_USER + offset];
+ rv[ 4] = CU_NICE_USER; rv[ 5] = cpu_times[CP_NICE + offset];
+ rv[ 6] = CU_KERNEL; rv[ 7] = cpu_times[CP_SYS + offset];
+ rv[ 8] = CU_IDLE; rv[ 9] = cpu_times[CP_IDLE + offset];
+ rv[10] = CU_HARD_IRQ; rv[11] = cpu_times[CP_INTR + offset];
+ rv += CU_BSD_VALUES*2;
+ }
+
+ *result_sz = 2 + 2*CU_BSD_VALUES * no_of_cpus;
+}
+
+void getsysctl(const char *name, void *ptr, size_t len)
+{
+ size_t gotlen = len;
+ if (sysctlbyname(name, ptr, &gotlen, NULL, 0) != 0) {
+ EXIT_WITH("sysctlbyname failed");
+ }
+ if (gotlen != len) {
+ EXIT_WITH("sysctlbyname: unexpected length");
+ }
+}
+#endif
+
+
+/* ---------------------------- *
* Generic functions *
* ---------------------------- */
@@ -581,5 +663,3 @@ static void error(char* err_msg) {
;
exit(-1);
}
-
-
diff --git a/lib/os_mon/doc/src/cpu_sup.xml b/lib/os_mon/doc/src/cpu_sup.xml
index 59da876208..4a8f5bffa0 100644
--- a/lib/os_mon/doc/src/cpu_sup.xml
+++ b/lib/os_mon/doc/src/cpu_sup.xml
@@ -34,7 +34,7 @@
and CPU utilization. It is part of the OS_Mon application, see
<seealso marker="os_mon_app">os_mon(6)</seealso>. Available for Unix,
although CPU utilization values (<c>util/0,1</c>) are only
- available for Solaris and Linux.</p>
+ available for Solaris, Linux and FreeBSD.</p>
<p>The load values are proportional to how long time a runnable
Unix process has to spend in the run queue before it is scheduled.
Accordingly, higher values mean more system load. The returned
diff --git a/lib/os_mon/src/cpu_sup.erl b/lib/os_mon/src/cpu_sup.erl
index 0c26956c57..d8cfd845bc 100644
--- a/lib/os_mon/src/cpu_sup.erl
+++ b/lib/os_mon/src/cpu_sup.erl
@@ -121,7 +121,7 @@ util(Args) when is_list (Args) ->
util(_) ->
erlang:error(badarg).
--spec util() -> float().
+-spec util() -> float() | {'error', any()}.
util() ->
case util([]) of
@@ -160,7 +160,8 @@ handle_call(?quit, _From, State) ->
handle_call({?util, D, PC}, {Client, _Tag},
#state{os_type = {unix, Flavor}} = State)
when Flavor == sunos;
- Flavor == linux ->
+ Flavor == linux;
+ Flavor == freebsd ->
case measurement_server_call(State#state.server, {?util, D, PC, Client}) of
{error, Reason} ->
{ reply,
diff --git a/lib/os_mon/test/cpu_sup_SUITE.erl b/lib/os_mon/test/cpu_sup_SUITE.erl
index 9f58e043db..7da819379c 100644
--- a/lib/os_mon/test/cpu_sup_SUITE.erl
+++ b/lib/os_mon/test/cpu_sup_SUITE.erl
@@ -64,6 +64,8 @@ all() ->
[load_api, util_api, util_values, port, unavailable];
{unix, linux} ->
[load_api, util_api, util_values, port, unavailable];
+ {unix, freebsd} ->
+ [load_api, util_api, util_values, port, unavailable];
{unix, _OSname} -> [load_api];
_OS -> [unavailable]
end.
diff --git a/lib/sasl/doc/src/sasl_app.xml b/lib/sasl/doc/src/sasl_app.xml
index 9c3c80bd13..572e550061 100644
--- a/lib/sasl/doc/src/sasl_app.xml
+++ b/lib/sasl/doc/src/sasl_app.xml
@@ -92,6 +92,13 @@
<item>Installs <c>sasl_report_file_h</c> in the error logger.
This makes all reports go to the file <c>FileName</c>.
<c>FileName</c> is a string.</item>
+ <tag><c>{file,FileName,Modes}</c></tag>
+ <item>Same as <c>{file,FileName}</c> except that the <c>Modes</c>
+ allows to specify the modes used for opening the <c>FileName</c>
+ given to the <seealso marker="kernel:file#open/2">file:open/2</seealso>
+ call. When not specified, the <c>Modes</c> defaults to <c>[write]</c>.
+ Use <c>[append]</c> for having the <c>FileName</c> open in append mode.
+ <c>FileName</c> is a string.</item>
<tag><c>false</c></tag>
<item>
<p>No SASL error logger handler is installed.</p>
diff --git a/lib/sasl/src/sasl.erl b/lib/sasl/src/sasl.erl
index fdea6da13e..4a220f0511 100644
--- a/lib/sasl/src/sasl.erl
+++ b/lib/sasl/src/sasl.erl
@@ -55,7 +55,9 @@ get_sasl_error_logger() ->
case application:get_env(sasl, sasl_error_logger) of
{ok, false} -> undefined;
{ok, tty} -> tty;
- {ok, {file, File}} when is_list(File) -> {file, File};
+ {ok, {file, File}} when is_list(File) -> {file, File, [write]};
+ {ok, {file, File, Modes}} when is_list(File), is_list(Modes) ->
+ {file, File, Modes};
{ok, Bad} -> exit({bad_config, {sasl, {sasl_error_logger, Bad}}});
_ -> undefined
end.
@@ -125,9 +127,9 @@ delete_sasl_error_logger(Type) ->
error_logger:delete_report_handler(mod(Type)).
mod(tty) -> sasl_report_tty_h;
-mod({file, _File}) -> sasl_report_file_h.
+mod({file, _File, _Modes}) -> sasl_report_file_h.
-args({file, File}, Type) -> {File, type(Type)};
+args({file, File, Modes}, Type) -> {File, Modes, type(Type)};
args(_, Type) -> type(Type).
type(error) -> error;
diff --git a/lib/sasl/src/sasl_report_file_h.erl b/lib/sasl/src/sasl_report_file_h.erl
index f42b4b5ff2..a5bd0ac055 100644
--- a/lib/sasl/src/sasl_report_file_h.erl
+++ b/lib/sasl/src/sasl_report_file_h.erl
@@ -28,9 +28,9 @@
handle_event/2, handle_call/2, handle_info/2,
terminate/2]).
-init({File, Type}) ->
+init({File, Modes, Type}) when is_list(Modes) ->
process_flag(trap_exit, true),
- case file:open(File, [write]) of
+ case file:open(File, Modes) of
{ok,Fd} ->
{ok, {Fd, File, Type}};
What ->
diff --git a/lib/sasl/test/sasl_SUITE.erl b/lib/sasl/test/sasl_SUITE.erl
index d7b99d506e..d9ab9e551c 100644
--- a/lib/sasl/test/sasl_SUITE.erl
+++ b/lib/sasl/test/sasl_SUITE.erl
@@ -26,10 +26,11 @@
%% Test cases must be exported.
-export([app_test/1,
appup_test/1,
- log_mf_h_env/1]).
+ log_mf_h_env/1,
+ log_file/1]).
all() ->
- [log_mf_h_env, app_test, appup_test].
+ [log_mf_h_env, log_file, app_test, appup_test].
groups() ->
[].
@@ -151,10 +152,9 @@ check_appup([],_,_) ->
log_mf_h_env(Config) ->
PrivDir = ?config(priv_dir,Config),
LogDir = filename:join(PrivDir,sasl_SUITE_log_dir),
- ok = file:make_dir(LogDir),
+ ok = filelib:ensure_dir(LogDir),
application:stop(sasl),
- SaslEnv = application:get_all_env(sasl),
- lists:foreach(fun({E,_V}) -> application:unset_env(sasl,E) end, SaslEnv),
+ clear_env(sasl),
ok = application:set_env(sasl,error_logger_mf_dir,LogDir),
match_error(missing_config,application:start(sasl)),
@@ -178,6 +178,23 @@ log_mf_h_env(Config) ->
ok = application:set_env(sasl,error_logger_mf_dir,LogDir),
ok = application:start(sasl).
+log_file(Config) ->
+ PrivDir = ?config(priv_dir,Config),
+ LogDir = filename:join(PrivDir,sasl_SUITE_log_dir),
+ ok = filelib:ensure_dir(LogDir),
+ File = filename:join(LogDir, "file.log"),
+ application:stop(sasl),
+ clear_env(sasl),
+
+ ok = application:set_env(sasl,sasl_error_logger,{file, File}, [{persistent, true}]),
+ ok = application:start(sasl),
+ application:stop(sasl),
+ ok = application:set_env(sasl,sasl_error_logger,{file, File, [append]}, [{persistent, true}]),
+ ok = application:start(sasl),
+ application:stop(sasl),
+ ok = application:set_env(sasl,sasl_error_logger, tty, [{persistent, false}]),
+ ok = application:start(sasl).
+
%%-----------------------------------------------------------------
%% Internal
@@ -185,3 +202,7 @@ match_error(Expected,{error,{bad_return,{_,{'EXIT',{Expected,{sasl,_}}}}}}) ->
ok;
match_error(Expected,Actual) ->
?t:fail({unexpected_return,Expected,Actual}).
+
+clear_env(App) ->
+ [application:unset_env(App,Opt) || {Opt,_} <- application:get_all_env(App)],
+ ok.
diff --git a/lib/ssh/doc/src/notes.xml b/lib/ssh/doc/src/notes.xml
index 579a3ae4a8..c77ee1e77a 100644
--- a/lib/ssh/doc/src/notes.xml
+++ b/lib/ssh/doc/src/notes.xml
@@ -29,6 +29,33 @@
<file>notes.xml</file>
</header>
+<section><title>Ssh 3.2.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Gracefully terminate if sockets is unexpectedly closed.</p>
+ <p>
+ Own Id: OTP-12782</p>
+ </item>
+ <item>
+ <p>
+ Made Codenomicon Defensics test suite pass: <list>
+ <item>limit number of algorithms in kexinit
+ message</item> <item>check 'e' and 'f' parameters in
+ kexdh</item> <item>implement 'keyboard-interactive' user
+ authentication on server side</item> <item> return plain
+ text message to bad version exchange message</item>
+ </list></p>
+ <p>
+ Own Id: OTP-12784</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Ssh 3.2.3</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl
index 57f7ae8b5e..4a07473f74 100644
--- a/lib/ssh/src/ssh.erl
+++ b/lib/ssh/src/ssh.erl
@@ -360,6 +360,8 @@ handle_option([{exec, _} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([{auth_methods, _} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{auth_method_kb_interactive_data, _} = Opt | Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([{preferred_algorithms,_} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([{quiet_mode, _} = Opt|Rest], SocketOptions, SshOptions) ->
@@ -429,6 +431,13 @@ handle_ssh_option({exec, Function} = Opt) when is_function(Function) ->
Opt;
handle_ssh_option({auth_methods, Value} = Opt) when is_list(Value) ->
Opt;
+handle_ssh_option({auth_method_kb_interactive_data, {Name,Instruction,Prompt,Echo}} = Opt) when is_list(Name),
+ is_list(Instruction),
+ is_list(Prompt),
+ is_boolean(Echo) ->
+ Opt;
+handle_ssh_option({auth_method_kb_interactive_data, F} = Opt) when is_function(F,3) ->
+ Opt;
handle_ssh_option({infofun, Value} = Opt) when is_function(Value) ->
Opt;
handle_ssh_option({connectfun, Value} = Opt) when is_function(Value) ->
diff --git a/lib/ssh/src/ssh_auth.erl b/lib/ssh/src/ssh_auth.erl
index 197808754c..df9a97c8f8 100644
--- a/lib/ssh/src/ssh_auth.erl
+++ b/lib/ssh/src/ssh_auth.erl
@@ -243,6 +243,54 @@ handle_userauth_request(#ssh_msg_userauth_request{user = User,
handle_userauth_request(#ssh_msg_userauth_request{user = User,
service = "ssh-connection",
+ method = "keyboard-interactive",
+ data = _},
+ _, #ssh{opts = Opts} = Ssh) ->
+ %% RFC4256
+ %% The data field contains:
+ %% - language tag (deprecated). If =/=[] SHOULD use it however. We skip
+ %% it for simplicity.
+ %% - submethods. "... the user can give a hint of which actual methods
+ %% he wants to use. ...". It's a "MAY use" so we skip
+ %% it. It also needs an understanding between the client
+ %% and the server.
+ %%
+ %% "The server MUST reply with an SSH_MSG_USERAUTH_SUCCESS,
+ %% SSH_MSG_USERAUTH_FAILURE, or SSH_MSG_USERAUTH_INFO_REQUEST message."
+ Default = {"SSH server",
+ "Enter password for \""++User++"\"",
+ "pwd: ",
+ false},
+
+ {Name, Instruction, Prompt, Echo} =
+ case proplists:get_value(auth_method_kb_interactive_data, Opts) of
+ undefined ->
+ Default;
+ {_,_,_,_}=V ->
+ V;
+ F when is_function(F) ->
+ {_,PeerName} = Ssh#ssh.peer,
+ F(PeerName, User, "ssh-connection")
+ end,
+ EchoEnc = case Echo of
+ true -> <<?TRUE>>;
+ false -> <<?FALSE>>
+ end,
+ Msg = #ssh_msg_userauth_info_request{name = unicode:characters_to_list(Name),
+ instruction = unicode:characters_to_list(Instruction),
+ language_tag = "",
+ num_prompts = 1,
+ data = <<?STRING(unicode:characters_to_binary(Prompt)),
+ EchoEnc/binary
+ >>
+ },
+ {not_authorized, {User, undefined},
+ ssh_transport:ssh_packet(Msg, Ssh#ssh{user = User,
+ opts = [{max_kb_tries,3},{kb_userauth_info_msg,Msg}|Opts]
+ })};
+
+handle_userauth_request(#ssh_msg_userauth_request{user = User,
+ service = "ssh-connection",
method = Other}, _,
#ssh{userauth_supported_methods = Methods} = Ssh) ->
{not_authorized, {User, {authmethod, Other}},
@@ -264,6 +312,38 @@ handle_userauth_info_request(
#ssh_msg_userauth_info_response{num_responses = NumPrompts,
data = Responses}, Ssh)}.
+handle_userauth_info_response(#ssh_msg_userauth_info_response{num_responses = 1,
+ data = <<?UINT32(Sz), Password:Sz/binary>>},
+ #ssh{opts = Opts0,
+ user = User} = Ssh) ->
+ NumTriesLeft = proplists:get_value(max_kb_tries, Opts0, 0) - 1,
+ Opts = lists:keydelete(max_kb_tries,1,Opts0),
+ case check_password(User, unicode:characters_to_list(Password), Opts) of
+ true ->
+ {authorized, User,
+ ssh_transport:ssh_packet(#ssh_msg_userauth_success{}, Ssh)};
+ false when NumTriesLeft > 0 ->
+ UserAuthInfoMsg =
+ (proplists:get_value(kb_userauth_info_msg,Opts))
+ #ssh_msg_userauth_info_request{name = "",
+ instruction =
+ lists:concat(
+ ["Bad user or password, try again. ",
+ integer_to_list(NumTriesLeft),
+ " tries left."])},
+ {not_authorized, {User, undefined},
+ ssh_transport:ssh_packet(UserAuthInfoMsg,
+ Ssh#ssh{opts = [{max_kb_tries,NumTriesLeft}|Opts]})};
+
+ false ->
+ {not_authorized, {User, {error,"Bad user or password"}},
+ ssh_transport:ssh_packet(#ssh_msg_userauth_failure{
+ authentications = "",
+ partial_success = false},
+ Ssh#ssh{opts = lists:keydelete(kb_userauth_info_msg,1,Opts)}
+ )}
+ end;
+
handle_userauth_info_response(#ssh_msg_userauth_info_response{},
_Auth) ->
throw(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_SERVICE_NOT_AVAILABLE,
diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl
index ca63d2194f..3bdca4ba94 100644
--- a/lib/ssh/src/ssh_connection_handler.erl
+++ b/lib/ssh/src/ssh_connection_handler.erl
@@ -333,22 +333,25 @@ info(ConnectionHandler, ChannelProcess) ->
hello(socket_control, #state{socket = Socket, ssh_params = Ssh} = State) ->
VsnMsg = ssh_transport:hello_version_msg(string_version(Ssh)),
send_msg(VsnMsg, State),
- {ok, [{recbuf, Size}]} = inet:getopts(Socket, [recbuf]),
- inet:setopts(Socket, [{packet, line}, {active, once}, {recbuf, ?MAX_PROTO_VERSION}]),
- {next_state, hello, State#state{recbuf = Size}};
+ case getopt(recbuf, Socket) of
+ {ok, Size} ->
+ inet:setopts(Socket, [{packet, line}, {active, once}, {recbuf, ?MAX_PROTO_VERSION}]),
+ {next_state, hello, State#state{recbuf = Size}};
+ {error, Reason} ->
+ {stop, {shutdown, Reason}, State}
+ end;
hello({info_line, _Line},#state{role = client, socket = Socket} = State) ->
%% The server may send info lines before the version_exchange
inet:setopts(Socket, [{active, once}]),
{next_state, hello, State};
-hello({info_line, _Line},#state{role = server} = State) ->
- DisconnectMsg =
- #ssh_msg_disconnect{code =
- ?SSH_DISCONNECT_PROTOCOL_ERROR,
- description = "Did not receive expected protocol version exchange",
- language = "en"},
- handle_disconnect(DisconnectMsg, State);
+hello({info_line, _Line},#state{role = server,
+ socket = Socket,
+ transport_cb = Transport } = State) ->
+ %% as openssh
+ Transport:send(Socket, "Protocol mismatch."),
+ {stop, {shutdown,"Protocol mismatch in version exchange."}, State};
hello({version_exchange, Version}, #state{ssh_params = Ssh0,
socket = Socket,
@@ -501,10 +504,21 @@ userauth(#ssh_msg_userauth_info_request{} = Msg,
{next_state, userauth, next_packet(State#state{ssh_params = Ssh})};
userauth(#ssh_msg_userauth_info_response{} = Msg,
- #state{ssh_params = #ssh{role = server} = Ssh0} = State) ->
- {ok, {Reply, Ssh}} = ssh_auth:handle_userauth_info_response(Msg, Ssh0),
- send_msg(Reply, State),
- {next_state, userauth, next_packet(State#state{ssh_params = Ssh})};
+ #state{ssh_params = #ssh{role = server,
+ peer = {_, Address}} = Ssh0,
+ opts = Opts, starter = Pid} = State) ->
+ case ssh_auth:handle_userauth_info_response(Msg, Ssh0) of
+ {authorized, User, {Reply, Ssh}} ->
+ send_msg(Reply, State),
+ Pid ! ssh_connected,
+ connected_fun(User, Address, "keyboard-interactive", Opts),
+ {next_state, connected,
+ next_packet(State#state{auth_user = User, ssh_params = Ssh})};
+ {not_authorized, {User, Reason}, {Reply, Ssh}} ->
+ retry_fun(User, Address, Reason, Opts),
+ send_msg(Reply, State),
+ {next_state, userauth, next_packet(State#state{ssh_params = Ssh})}
+ end;
userauth(#ssh_msg_userauth_success{}, #state{ssh_params = #ssh{role = client} = Ssh,
starter = Pid} = State) ->
@@ -1763,3 +1777,12 @@ start_timeout(_,_, infinity) ->
ok;
start_timeout(Channel, From, Time) ->
erlang:send_after(Time, self(), {timeout, {Channel, From}}).
+
+getopt(Opt, Socket) ->
+ case inet:getopts(Socket, [Opt]) of
+ {ok, [{Opt, Value}]} ->
+ {ok, Value};
+ Other ->
+ {error, {unexpected_getopts_return, Other}}
+ end.
+
diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl
index 7162d18b19..ea9bca2390 100644
--- a/lib/ssh/src/ssh_transport.erl
+++ b/lib/ssh/src/ssh_transport.erl
@@ -585,10 +585,15 @@ alg_final(SSH0) ->
{ok,SSH6} = decompress_final(SSH5),
SSH6.
-select_all(CL, SL) ->
+select_all(CL, SL) when length(CL) + length(SL) < 50 ->
A = CL -- SL, %% algortihms only used by client
%% algorithms used by client and server (client pref)
- lists:map(fun(ALG) -> list_to_atom(ALG) end, (CL -- A)).
+ lists:map(fun(ALG) -> list_to_atom(ALG) end, (CL -- A));
+select_all(_CL, _SL) ->
+ throw(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_PROTOCOL_ERROR,
+ description = "Too many algorithms",
+ language = "en"}).
+
select([], []) ->
none;
diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml
index 352563700b..fe0606b1a3 100644
--- a/lib/ssl/doc/src/notes.xml
+++ b/lib/ssl/doc/src/notes.xml
@@ -25,7 +25,23 @@
<file>notes.xml</file>
</header>
<p>This document describes the changes made to the SSL application.</p>
- <section><title>SSL 6.0</title>
+ <section><title>SSL 6.0.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Terminate gracefully when receving bad input to premaster
+ secret calculation</p>
+ <p>
+ Own Id: OTP-12783</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SSL 6.0</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/ssl/src/ssl.appup.src b/lib/ssl/src/ssl.appup.src
index 1476336039..d100e41930 100644
--- a/lib/ssl/src/ssl.appup.src
+++ b/lib/ssl/src/ssl.appup.src
@@ -1,14 +1,16 @@
%% -*- erlang -*-
{"%VSN%",
[
- {<<"6\\..*">>, [{restart_application, ssl}]},
- {<<"5\\..*">>, [{restart_application, ssl}]},
+ {<<"6.0">>, [{load_module, ssl_handshake, soft_purge, soft_purge, []}]},
+ {<<"5\\.3\\.[1-7]($|\\..*)">>, [{restart_application, ssl}]},
+ {<<"5\\.[0-2]($|\\..*)">>, [{restart_application, ssl}]},
{<<"4\\..*">>, [{restart_application, ssl}]},
{<<"3\\..*">>, [{restart_application, ssl}]}
],
[
- {<<"6\\..*">>, [{restart_application, ssl}]},
- {<<"5\\..*">>, [{restart_application, ssl}]},
+ {<<"6.0">>, [{load_module, ssl_handshake, soft_purge, soft_purge, []}]},
+ {<<"5\\.3\\.[1-7]($|\\..*)">>, [{restart_application, ssl}]},
+ {<<"5\\.[0-2]($|\\..*)">>, [{restart_application, ssl}]},
{<<"4\\..*">>, [{restart_application, ssl}]},
{<<"3\\..*">>, [{restart_application, ssl}]}
]
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index b538fefe53..12a17cb6ac 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -476,19 +476,27 @@ update_handshake_history({Handshake0, _Prev}, Data) ->
%% end.
premaster_secret(OtherPublicDhKey, MyPrivateKey, #'DHParameter'{} = Params) ->
- public_key:compute_key(OtherPublicDhKey, MyPrivateKey, Params);
-
+ try
+ public_key:compute_key(OtherPublicDhKey, MyPrivateKey, Params)
+ catch
+ error:computation_failed ->
+ throw(?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER))
+ end;
premaster_secret(PublicDhKey, PrivateDhKey, #server_dh_params{dh_p = Prime, dh_g = Base}) ->
- crypto:compute_key(dh, PublicDhKey, PrivateDhKey, [Prime, Base]);
+ try
+ crypto:compute_key(dh, PublicDhKey, PrivateDhKey, [Prime, Base])
+ catch
+ error:computation_failed ->
+ throw(?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER))
+ end;
premaster_secret(#client_srp_public{srp_a = ClientPublicKey}, ServerKey, #srp_user{prime = Prime,
verifier = Verifier}) ->
case crypto:compute_key(srp, ClientPublicKey, ServerKey, {host, [Verifier, Prime, '6a']}) of
error ->
- ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER);
+ throw(?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER));
PremasterSecret ->
PremasterSecret
end;
-
premaster_secret(#server_srp_params{srp_n = Prime, srp_g = Generator, srp_s = Salt, srp_b = Public},
ClientKeys, {Username, Password}) ->
case ssl_srp_primes:check_srp_params(Generator, Prime) of
@@ -496,21 +504,19 @@ premaster_secret(#server_srp_params{srp_n = Prime, srp_g = Generator, srp_s = Sa
DerivedKey = crypto:hash(sha, [Salt, crypto:hash(sha, [Username, <<$:>>, Password])]),
case crypto:compute_key(srp, Public, ClientKeys, {user, [DerivedKey, Prime, Generator, '6a']}) of
error ->
- ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER);
+ throw(?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER));
PremasterSecret ->
PremasterSecret
end;
_ ->
- ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER)
+ throw(?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER))
end;
-
premaster_secret(#client_rsa_psk_identity{
identity = PSKIdentity,
exchange_keys = #encrypted_premaster_secret{premaster_secret = EncPMS}
}, #'RSAPrivateKey'{} = Key, PSKLookup) ->
PremasterSecret = premaster_secret(EncPMS, Key),
psk_secret(PSKIdentity, PSKLookup, PremasterSecret);
-
premaster_secret(#server_dhe_psk_params{
hint = IdentityHint,
dh_params = #server_dh_params{dh_y = PublicDhKey} = Params},
@@ -518,7 +524,6 @@ premaster_secret(#server_dhe_psk_params{
LookupFun) ->
PremasterSecret = premaster_secret(PublicDhKey, PrivateDhKey, Params),
psk_secret(IdentityHint, LookupFun, PremasterSecret);
-
premaster_secret({rsa_psk, PSKIdentity}, PSKLookup, RSAPremasterSecret) ->
psk_secret(PSKIdentity, PSKLookup, RSAPremasterSecret).
@@ -527,13 +532,10 @@ premaster_secret(#client_dhe_psk_identity{
dh_public = PublicDhKey}, PrivateKey, #'DHParameter'{} = Params, PSKLookup) ->
PremasterSecret = premaster_secret(PublicDhKey, PrivateKey, Params),
psk_secret(PSKIdentity, PSKLookup, PremasterSecret).
-
premaster_secret(#client_psk_identity{identity = PSKIdentity}, PSKLookup) ->
psk_secret(PSKIdentity, PSKLookup);
-
premaster_secret({psk, PSKIdentity}, PSKLookup) ->
psk_secret(PSKIdentity, PSKLookup);
-
premaster_secret(#'ECPoint'{} = ECPoint, #'ECPrivateKey'{} = ECDHKeys) ->
public_key:compute_key(ECPoint, ECDHKeys);
premaster_secret(EncSecret, #'RSAPrivateKey'{} = RSAPrivateKey) ->
@@ -2036,7 +2038,7 @@ psk_secret(PSKIdentity, PSKLookup) ->
#alert{} = Alert ->
Alert;
_ ->
- ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER)
+ throw(?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER))
end.
psk_secret(PSKIdentity, PSKLookup, PremasterSecret) ->
@@ -2048,7 +2050,7 @@ psk_secret(PSKIdentity, PSKLookup, PremasterSecret) ->
#alert{} = Alert ->
Alert;
_ ->
- ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER)
+ throw(?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER))
end.
handle_psk_identity(_PSKIdentity, LookupFun)
diff --git a/lib/ssl/src/ssl_tls_dist_proxy.erl b/lib/ssl/src/ssl_tls_dist_proxy.erl
index a22af6b960..d23b42ace5 100644
--- a/lib/ssl/src/ssl_tls_dist_proxy.erl
+++ b/lib/ssl/src/ssl_tls_dist_proxy.erl
@@ -227,7 +227,10 @@ loop_conn_setup(World, Erts) ->
{tcp_closed, Erts} ->
ssl:close(World);
{ssl_closed, World} ->
- gen_tcp:close(Erts)
+ gen_tcp:close(Erts);
+ {ssl_error, World, _} ->
+
+ ssl:close(World)
end.
loop_conn(World, Erts) ->
@@ -241,7 +244,9 @@ loop_conn(World, Erts) ->
{tcp_closed, Erts} ->
ssl:close(World);
{ssl_closed, World} ->
- gen_tcp:close(Erts)
+ gen_tcp:close(Erts);
+ {ssl_error, World, _} ->
+ ssl:close(World)
end.
get_ssl_options(Type) ->
diff --git a/lib/stdlib/doc/src/orddict.xml b/lib/stdlib/doc/src/orddict.xml
index ec1e43f29c..c853b402d4 100644
--- a/lib/stdlib/doc/src/orddict.xml
+++ b/lib/stdlib/doc/src/orddict.xml
@@ -48,8 +48,11 @@
<datatypes>
<datatype>
- <name name="orddict"/>
- <desc><p>As returned by new/0.</p></desc>
+ <name name="orddict" n_vars="2"/>
+ <desc><p>Dictionary as returned by <c>new/0</c>.</p></desc>
+ </datatype>
+ <datatype>
+ <name name="orddict" n_vars="0"/>
</datatype>
</datatypes>
diff --git a/lib/stdlib/doc/src/supervisor.xml b/lib/stdlib/doc/src/supervisor.xml
index ffac1c0bd7..6ff477a42d 100644
--- a/lib/stdlib/doc/src/supervisor.xml
+++ b/lib/stdlib/doc/src/supervisor.xml
@@ -386,9 +386,15 @@
added to the supervisor and the function returns the same
value.</p>
<p>If the child process start function returns <c>ignore</c>,
- the child specification is added to the supervisor, the pid
- is set to <c>undefined</c>, and the function returns
- <c>{ok,undefined}</c>.</p>
+ the child specification is added to the supervisor (unless the
+ supervisor is a <c>simple_one_for_one</c> supervisor, see below),
+ the pid is set to <c>undefined</c> and the function returns
+ <c>{ok,undefined}</c>.
+ </p>
+ <p>In the case of a <c>simple_one_for_one</c> supervisor, when a child
+ process start function returns <c>ignore</c> the functions returns
+ <c>{ok,undefined}</c> and no child is added to the supervisor.
+ </p>
<p>If the child process start function returns an error tuple or
an erroneous value, or if it fails, the child specification is
discarded, and the function returns <c>{error,Error}</c> where
diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl
index ac92004061..b13848c501 100644
--- a/lib/stdlib/src/erl_lint.erl
+++ b/lib/stdlib/src/erl_lint.erl
@@ -2843,10 +2843,9 @@ check_record_types([{type, _, field_type, [{atom, AL, FName}, Type]}|Left],
check_record_types([], _Name, _DefFields, SeenVars, St, _SeenFields) ->
{SeenVars, St}.
-used_type(TypePair, L, St) ->
- Usage = St#lint.usage,
+used_type(TypePair, L, #lint{usage = Usage, file = File} = St) ->
OldUsed = Usage#usage.used_types,
- UsedTypes = dict:store(TypePair, L, OldUsed),
+ UsedTypes = dict:store(TypePair, erl_anno:set_file(File, L), OldUsed),
St#lint{usage=Usage#usage{used_types=UsedTypes}}.
is_default_type({Name, NumberOfTypeVariables}) ->
diff --git a/lib/stdlib/src/orddict.erl b/lib/stdlib/src/orddict.erl
index af5d917840..cbdf25d757 100644
--- a/lib/stdlib/src/orddict.erl
+++ b/lib/stdlib/src/orddict.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2015. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -25,11 +25,13 @@
-export([store/3,append/3,append_list/3,update/3,update/4,update_counter/3]).
-export([fold/3,map/2,filter/2,merge/3]).
--export_type([orddict/0]).
+-export_type([orddict/0, orddict/2]).
%%---------------------------------------------------------------------------
--type orddict() :: [{Key :: term(), Value :: term()}].
+-type orddict() :: orddict(_, _).
+
+-type orddict(Key, Value) :: [{Key, Value}].
%%---------------------------------------------------------------------------
@@ -38,8 +40,7 @@
new() -> [].
-spec is_key(Key, Orddict) -> boolean() when
- Key :: term(),
- Orddict :: orddict().
+ Orddict :: orddict(Key, Value :: term()).
is_key(Key, [{K,_}|_]) when Key < K -> false;
is_key(Key, [{K,_}|Dict]) when Key > K -> is_key(Key, Dict);
@@ -47,14 +48,14 @@ is_key(_Key, [{_K,_Val}|_]) -> true; %Key == K
is_key(_, []) -> false.
-spec to_list(Orddict) -> List when
- Orddict :: orddict(),
- List :: [{Key :: term(), Value :: term()}].
+ Orddict :: orddict(Key, Value),
+ List :: [{Key, Value}].
to_list(Dict) -> Dict.
-spec from_list(List) -> Orddict when
- List :: [{Key :: term(), Value :: term()}],
- Orddict :: orddict().
+ List :: [{Key, Value}],
+ Orddict :: orddict(Key, Value).
from_list([]) -> [];
from_list([{_,_}]=Pair) -> Pair;
@@ -73,17 +74,13 @@ is_empty([]) -> true;
is_empty([_|_]) -> false.
-spec fetch(Key, Orddict) -> Value when
- Key :: term(),
- Value :: term(),
- Orddict :: orddict().
+ Orddict :: orddict(Key, Value).
fetch(Key, [{K,_}|D]) when Key > K -> fetch(Key, D);
fetch(Key, [{K,Value}|_]) when Key == K -> Value.
-spec find(Key, Orddict) -> {'ok', Value} | 'error' when
- Key :: term(),
- Orddict :: orddict(),
- Value :: term().
+ Orddict :: orddict(Key, Value).
find(Key, [{K,_}|_]) when Key < K -> error;
find(Key, [{K,_}|D]) when Key > K -> find(Key, D);
@@ -91,17 +88,16 @@ find(_Key, [{_K,Value}|_]) -> {ok,Value}; %Key == K
find(_, []) -> error.
-spec fetch_keys(Orddict) -> Keys when
- Orddict :: orddict(),
- Keys :: [term()].
+ Orddict :: orddict(Key, Value :: term()),
+ Keys :: [Key].
fetch_keys([{Key,_}|Dict]) ->
[Key|fetch_keys(Dict)];
fetch_keys([]) -> [].
-spec erase(Key, Orddict1) -> Orddict2 when
- Key :: term(),
- Orddict1 :: orddict(),
- Orddict2 :: orddict().
+ Orddict1 :: orddict(Key, Value),
+ Orddict2 :: orddict(Key, Value).
erase(Key, [{K,_}=E|Dict]) when Key < K -> [E|Dict];
erase(Key, [{K,_}=E|Dict]) when Key > K ->
@@ -110,10 +106,8 @@ erase(_Key, [{_K,_Val}|Dict]) -> Dict; %Key == K
erase(_, []) -> [].
-spec store(Key, Value, Orddict1) -> Orddict2 when
- Key :: term(),
- Value :: term(),
- Orddict1 :: orddict(),
- Orddict2 :: orddict().
+ Orddict1 :: orddict(Key, Value),
+ Orddict2 :: orddict(Key, Value).
store(Key, New, [{K,_}|_]=Dict) when Key < K ->
[{Key,New}|Dict];
@@ -124,10 +118,8 @@ store(Key, New, [{_K,_Old}|Dict]) -> %Key == K
store(Key, New, []) -> [{Key,New}].
-spec append(Key, Value, Orddict1) -> Orddict2 when
- Key :: term(),
- Value :: term(),
- Orddict1 :: orddict(),
- Orddict2 :: orddict().
+ Orddict1 :: orddict(Key, Value),
+ Orddict2 :: orddict(Key, Value).
append(Key, New, [{K,_}|_]=Dict) when Key < K ->
[{Key,[New]}|Dict];
@@ -138,10 +130,9 @@ append(Key, New, [{_K,Old}|Dict]) -> %Key == K
append(Key, New, []) -> [{Key,[New]}].
-spec append_list(Key, ValList, Orddict1) -> Orddict2 when
- Key :: term(),
- ValList :: [Value :: term()],
- Orddict1 :: orddict(),
- Orddict2 :: orddict().
+ ValList :: [Value],
+ Orddict1 :: orddict(Key, Value),
+ Orddict2 :: orddict(Key, Value).
append_list(Key, NewList, [{K,_}|_]=Dict) when Key < K ->
[{Key,NewList}|Dict];
@@ -153,10 +144,9 @@ append_list(Key, NewList, []) ->
[{Key,NewList}].
-spec update(Key, Fun, Orddict1) -> Orddict2 when
- Key :: term(),
- Fun :: fun((Value1 :: term()) -> Value2 :: term()),
- Orddict1 :: orddict(),
- Orddict2 :: orddict().
+ Fun :: fun((Value1 :: Value) -> Value2 :: Value),
+ Orddict1 :: orddict(Key, Value),
+ Orddict2 :: orddict(Key, Value).
update(Key, Fun, [{K,_}=E|Dict]) when Key > K ->
[E|update(Key, Fun, Dict)];
@@ -164,11 +154,10 @@ update(Key, Fun, [{K,Val}|Dict]) when Key == K ->
[{Key,Fun(Val)}|Dict].
-spec update(Key, Fun, Initial, Orddict1) -> Orddict2 when
- Key :: term(),
- Initial :: term(),
- Fun :: fun((Value1 :: term()) -> Value2 :: term()),
- Orddict1 :: orddict(),
- Orddict2 :: orddict().
+ Initial :: Value,
+ Fun :: fun((Value1 :: Value) -> Value2 :: Value),
+ Orddict1 :: orddict(Key, Value),
+ Orddict2 :: orddict(Key, Value).
update(Key, _, Init, [{K,_}|_]=Dict) when Key < K ->
[{Key,Init}|Dict];
@@ -179,10 +168,9 @@ update(Key, Fun, _Init, [{_K,Val}|Dict]) -> %Key == K
update(Key, _, Init, []) -> [{Key,Init}].
-spec update_counter(Key, Increment, Orddict1) -> Orddict2 when
- Key :: term(),
- Increment :: number(),
- Orddict1 :: orddict(),
- Orddict2 :: orddict().
+ Orddict1 :: orddict(Key, Value),
+ Orddict2 :: orddict(Key, Value),
+ Increment :: number().
update_counter(Key, Incr, [{K,_}|_]=Dict) when Key < K ->
[{Key,Incr}|Dict];
@@ -193,28 +181,30 @@ update_counter(Key, Incr, [{_K,Val}|Dict]) -> %Key == K
update_counter(Key, Incr, []) -> [{Key,Incr}].
-spec fold(Fun, Acc0, Orddict) -> Acc1 when
- Fun :: fun((Key :: term(), Value :: term(), AccIn :: term()) -> AccOut :: term()),
- Acc0 :: term(),
- Acc1 :: term(),
- Orddict :: orddict().
+ Fun :: fun((Key, Value, AccIn) -> AccOut),
+ Orddict :: orddict(Key, Value),
+ Acc0 :: Acc,
+ Acc1 :: Acc,
+ AccIn :: Acc,
+ AccOut :: Acc.
fold(F, Acc, [{Key,Val}|D]) ->
fold(F, F(Key, Val, Acc), D);
fold(F, Acc, []) when is_function(F, 3) -> Acc.
-spec map(Fun, Orddict1) -> Orddict2 when
- Fun :: fun((Key :: term(), Value1 :: term()) -> Value2 :: term()),
- Orddict1 :: orddict(),
- Orddict2 :: orddict().
+ Fun :: fun((Key, Value1) -> Value2),
+ Orddict1 :: orddict(Key, Value1),
+ Orddict2 :: orddict(Key, Value2).
map(F, [{Key,Val}|D]) ->
[{Key,F(Key, Val)}|map(F, D)];
map(F, []) when is_function(F, 2) -> [].
-spec filter(Pred, Orddict1) -> Orddict2 when
- Pred :: fun((Key :: term(), Value :: term()) -> boolean()),
- Orddict1 :: orddict(),
- Orddict2 :: orddict().
+ Pred :: fun((Key, Value) -> boolean()),
+ Orddict1 :: orddict(Key, Value),
+ Orddict2 :: orddict(Key, Value).
filter(F, [{Key,Val}=E|D]) ->
case F(Key, Val) of
@@ -224,10 +214,10 @@ filter(F, [{Key,Val}=E|D]) ->
filter(F, []) when is_function(F, 2) -> [].
-spec merge(Fun, Orddict1, Orddict2) -> Orddict3 when
- Fun :: fun((Key :: term(), Value1 :: term(), Value2 :: term()) -> Value :: term()),
- Orddict1 :: orddict(),
- Orddict2 :: orddict(),
- Orddict3 :: orddict().
+ Fun :: fun((Key, Value1, Value2) -> Value),
+ Orddict1 :: orddict(Key, Value1),
+ Orddict2 :: orddict(Key, Value2),
+ Orddict3 :: orddict(Key, Value).
merge(F, [{K1,_}=E1|D1], [{K2,_}=E2|D2]) when K1 < K2 ->
[E1|merge(F, D1, [E2|D2])];
diff --git a/lib/stdlib/src/supervisor.erl b/lib/stdlib/src/supervisor.erl
index 67655b1145..1d7396adee 100644
--- a/lib/stdlib/src/supervisor.erl
+++ b/lib/stdlib/src/supervisor.erl
@@ -381,7 +381,7 @@ handle_call({start_child, EArgs}, _From, State) when ?is_simple(State) ->
#child{mfargs = {M, F, A}} = Child,
Args = A ++ EArgs,
case do_start_child_i(M, F, Args) of
- {ok, undefined} when Child#child.restart_type =:= temporary ->
+ {ok, undefined} ->
{reply, {ok, undefined}, State};
{ok, Pid} ->
NState = save_dynamic_child(Child#child.restart_type, Pid, Args, State),
diff --git a/lib/stdlib/test/qlc_SUITE.erl b/lib/stdlib/test/qlc_SUITE.erl
index 348c308f5d..56829fac5c 100644
--- a/lib/stdlib/test/qlc_SUITE.erl
+++ b/lib/stdlib/test/qlc_SUITE.erl
@@ -6120,7 +6120,7 @@ otp_6964(Config) when is_list(Config) ->
lists:flatten(qlc:format_error(ErrReply)),
qlc_SUITE:install_error_logger(),
20000 = length(F(warning_msg)),
- {error, joining} = qlc_SUITE:read_error_logger(),
+ {warning, joining} = qlc_SUITE:read_error_logger(),
20000 = length(F(info_msg)),
{info, joining} = qlc_SUITE:read_error_logger(),
20000 = length(F(error_msg)),
@@ -6155,8 +6155,8 @@ otp_6964(Config) when is_list(Config) ->
{error, caching} = qlc_SUITE:read_error_logger(),
{error, caching} = qlc_SUITE:read_error_logger(),
1 = length(F(warning_msg)),
- {error, caching} = qlc_SUITE:read_error_logger(),
- {error, caching} = qlc_SUITE:read_error_logger(),
+ {warning, caching} = qlc_SUITE:read_error_logger(),
+ {warning, caching} = qlc_SUITE:read_error_logger(),
1 = length(F(info_msg)),
{info, caching} = qlc_SUITE:read_error_logger(),
{info, caching} = qlc_SUITE:read_error_logger(),
@@ -6188,7 +6188,7 @@ otp_6964(Config) when is_list(Config) ->
L = F(info_msg),
{info, sorting} = qlc_SUITE:read_error_logger(),
L = F(warning_msg),
- {error, sorting} = qlc_SUITE:read_error_logger(),
+ {warning, sorting} = qlc_SUITE:read_error_logger(),
qlc_SUITE:uninstall_error_logger(),
ets:delete(E1),
ets:delete(E2)">>],
@@ -6215,7 +6215,7 @@ otp_6964(Config) when is_list(Config) ->
R = lists:sort(F(error_msg)),
{error, caching} = qlc_SUITE:read_error_logger(),
R = lists:sort(F(warning_msg)),
- {error, caching} = qlc_SUITE:read_error_logger(),
+ {warning, caching} = qlc_SUITE:read_error_logger(),
qlc_SUITE:uninstall_error_logger(),
ErrReply = F(not_allowed),
{error,qlc,{tmpdir_usage,caching}} = ErrReply,
@@ -8178,6 +8178,8 @@ read_error_logger() ->
{error, Why};
{info, Why} ->
{info, Why};
+ {warning, Why} ->
+ {warning, Why};
{error, Pid, Tuple} ->
{error, Pid, Tuple}
after 1000 ->
@@ -8192,8 +8194,7 @@ read_error_logger() ->
init(Tester) ->
{ok, Tester}.
-handle_event({error, _GL, {_Pid, _Msg, [Why, _]}}, Tester)
- when is_atom(Why) ->
+handle_event({error, _GL, {_Pid, _Msg, [Why, _]}}, Tester) when is_atom(Why) ->
Tester ! {error, Why},
{ok, Tester};
handle_event({error, _GL, {_Pid, _Msg, [P, T]}}, Tester) when is_pid(P) ->
@@ -8202,6 +8203,9 @@ handle_event({error, _GL, {_Pid, _Msg, [P, T]}}, Tester) when is_pid(P) ->
handle_event({info_msg, _GL, {_Pid, _Msg, [Why, _]}}, Tester) ->
Tester ! {info, Why},
{ok, Tester};
+handle_event({warning_msg, _GL, {_Pid, _Msg, [Why, _]}}, Tester) when is_atom(Why) ->
+ Tester ! {warning, Why},
+ {ok, Tester};
handle_event(_Event, State) ->
{ok, State}.
diff --git a/lib/stdlib/test/supervisor_SUITE.erl b/lib/stdlib/test/supervisor_SUITE.erl
index 9dcf19707c..015b09f35e 100644
--- a/lib/stdlib/test/supervisor_SUITE.erl
+++ b/lib/stdlib/test/supervisor_SUITE.erl
@@ -37,6 +37,7 @@
sup_start_ignore_child/1, sup_start_ignore_temporary_child/1,
sup_start_ignore_temporary_child_start_child/1,
sup_start_ignore_temporary_child_start_child_simple/1,
+ sup_start_ignore_permanent_child_start_child_simple/1,
sup_start_error_return/1, sup_start_fail/1,
sup_start_map/1, sup_start_map_faulty_specs/1,
sup_stop_infinity/1, sup_stop_timeout/1, sup_stop_brutal_kill/1,
@@ -99,6 +100,7 @@ groups() ->
sup_start_ignore_child, sup_start_ignore_temporary_child,
sup_start_ignore_temporary_child_start_child,
sup_start_ignore_temporary_child_start_child_simple,
+ sup_start_ignore_permanent_child_start_child_simple,
sup_start_error_return, sup_start_fail]},
{sup_start_map, [],
[sup_start_map, sup_start_map_faulty_specs]},
@@ -250,6 +252,27 @@ sup_start_ignore_temporary_child_start_child_simple(Config)
[1,1,0,1] = get_child_counts(sup_test).
%%-------------------------------------------------------------------------
+%% Tests what happens if child's init-callback returns ignore for a
+%% permanent child when child is started with start_child/2, and the
+%% supervisor is simple_one_for_one.
+%% Child spec shall NOT be saved!!!
+sup_start_ignore_permanent_child_start_child_simple(Config)
+ when is_list(Config) ->
+ process_flag(trap_exit, true),
+ Child1 = {child1, {supervisor_1, start_child, [ignore]},
+ permanent, 1000, worker, []},
+ {ok, Pid} = start_link({ok, {{simple_one_for_one, 2, 3600}, [Child1]}}),
+
+ {ok, undefined} = supervisor:start_child(sup_test, []),
+ {ok, CPid2} = supervisor:start_child(sup_test, []),
+
+ [{undefined, CPid2, worker, []}] = supervisor:which_children(sup_test),
+ [1,1,0,1] = get_child_counts(sup_test),
+
+ %% Regression test: check that the supervisor terminates without error.
+ exit(Pid, shutdown),
+ check_exit_reason(Pid, shutdown).
+%%-------------------------------------------------------------------------
%% Tests what happens if init-callback returns a invalid value.
sup_start_error_return(Config) when is_list(Config) ->
process_flag(trap_exit, true),
diff --git a/lib/syntax_tools/src/Makefile b/lib/syntax_tools/src/Makefile
index 2c565cee7f..2e91adf8af 100644
--- a/lib/syntax_tools/src/Makefile
+++ b/lib/syntax_tools/src/Makefile
@@ -75,7 +75,8 @@ $(EBIN)/%.$(EMULATOR):%.erl
# special rules and dependencies to apply the transform to itself
$(EBIN)/merl_transform.beam: $(EBIN)/merl.beam ./merl_transform.beam \
- ../include/merl.hrl
+ ../include/merl.hrl \
+ $(EBIN)/erl_syntax.beam $(EBIN)/erl_syntax_lib.beam
./merl_transform.beam: ./merl_transform.erl $(EBIN)/merl.beam \
../include/merl.hrl
$(V_ERLC) -DMERL_NO_TRANSFORM $(ERL_COMPILE_FLAGS) -o ./ $<
diff --git a/lib/syntax_tools/src/syntax_tools.app.src b/lib/syntax_tools/src/syntax_tools.app.src
index e207901def..dd4ac46055 100644
--- a/lib/syntax_tools/src/syntax_tools.app.src
+++ b/lib/syntax_tools/src/syntax_tools.app.src
@@ -17,4 +17,5 @@
{registered,[]},
{applications, [stdlib]},
{env, []},
- {runtime_dependencies, ["stdlib-2.5","kernel-3.0","erts-6.0"]}]}.
+ {runtime_dependencies,
+ ["compiler-6.0","erts-6.0","kernel-3.0","stdlib-2.5"]}]}.