aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/compiler/src/sys_core_fold.erl52
-rw-r--r--lib/compiler/test/warnings_SUITE.erl24
-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/hipe/llvm/hipe_llvm_main.erl2
-rw-r--r--lib/inets/doc/src/Makefile3
-rw-r--r--lib/inets/doc/src/httpc.xml2
-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/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/gen_tcp_api_SUITE.erl32
-rw-r--r--lib/megaco/doc/src/megaco.xml4
-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/snmp/doc/src/snmp_app.xml2
-rw-r--r--lib/snmp/doc/src/snmp_config.xml2
-rw-r--r--lib/snmp/src/manager/snmpm.erl8
-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/supervisor_SUITE.erl23
-rw-r--r--lib/syntax_tools/src/Makefile3
-rw-r--r--lib/syntax_tools/src/syntax_tools.app.src3
76 files changed, 1338 insertions, 518 deletions
diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl
index 102a6951e8..7f4184fd30 100644
--- a/lib/compiler/src/sys_core_fold.erl
+++ b/lib/compiler/src/sys_core_fold.erl
@@ -1130,11 +1130,23 @@ let_substs_1(Vs, #c_values{es=As}, Sub) ->
let_substs_1([V], A, Sub) -> let_subst_list([V], [A], Sub);
let_substs_1(Vs, A, _) -> {Vs,A,[]}.
-let_subst_list([V|Vs0], [A|As0], Sub) ->
+let_subst_list([V|Vs0], [A0|As0], Sub) ->
{Vs1,As1,Ss} = let_subst_list(Vs0, As0, Sub),
- case is_subst(A) of
- true -> {Vs1,As1,sub_subst_var(V, A, Sub) ++ Ss};
- false -> {[V|Vs1],[A|As1],Ss}
+ case is_subst(A0) of
+ true ->
+ A = case is_compiler_generated(V) andalso
+ not is_compiler_generated(A0) of
+ true ->
+ %% Propagate the 'compiler_generated' annotation
+ %% along with the value.
+ Ann = [compiler_generated|cerl:get_ann(A0)],
+ cerl:set_ann(A0, Ann);
+ false ->
+ A0
+ end,
+ {Vs1,As1,sub_subst_var(V, A, Sub) ++ Ss};
+ false ->
+ {[V|Vs1],[A0|As1],Ss}
end;
let_subst_list([], [], _) -> {[],[],[]}.
@@ -1900,8 +1912,8 @@ case_data_pat_alias(P, BindTo0, TypeSig, Bs0) ->
%% Here we will need to actually build the data and bind
%% it to the variable.
{Type,Arity} = TypeSig,
- Vars = make_vars([], Arity),
Ann = [compiler_generated],
+ Vars = make_vars(Ann, Arity),
Data = cerl:ann_make_data(Ann, Type, Vars),
Bs = [{BindTo0,P},{P,Data}|Bs0],
{Vars,Bs};
@@ -2393,8 +2405,9 @@ delay_build_1(Core0, TypeSig) ->
try delay_build_expr(Core0, TypeSig) of
Core ->
{Type,Arity} = TypeSig,
- Vars = make_vars([], Arity),
- Data = cerl:ann_make_data([compiler_generated], Type, Vars),
+ Ann = [compiler_generated],
+ Vars = make_vars(Ann, Arity),
+ Data = cerl:ann_make_data(Ann, Type, Vars),
{yes,Vars,Core,Data}
catch
throw:impossible ->
@@ -2481,7 +2494,7 @@ opt_simple_let_2(Let0, Vs0, Arg0, Body, PrevBody, Ctxt, Sub) ->
Arg1;
false ->
%% let <Var> = Arg in <OtherVar> ==> seq Arg OtherVar
- Arg = maybe_suppress_warnings(Arg1, Vs0, PrevBody, Ctxt),
+ Arg = maybe_suppress_warnings(Arg1, Vs0, PrevBody),
expr(#c_seq{arg=Arg,body=Body}, Ctxt,
sub_new_preserve_types(Sub))
end;
@@ -2489,7 +2502,7 @@ opt_simple_let_2(Let0, Vs0, Arg0, Body, PrevBody, Ctxt, Sub) ->
%% No variables left.
Body;
{Vs,Arg1,#c_literal{}} ->
- Arg = maybe_suppress_warnings(Arg1, Vs, PrevBody, Ctxt),
+ Arg = maybe_suppress_warnings(Arg1, Vs, PrevBody),
E = case Ctxt of
effect ->
%% Throw away the literal body.
@@ -2508,7 +2521,7 @@ opt_simple_let_2(Let0, Vs0, Arg0, Body, PrevBody, Ctxt, Sub) ->
%% seq Arg BodyWithoutVar
case is_any_var_used(Vs, Body) of
false ->
- Arg = maybe_suppress_warnings(Arg1, Vs, PrevBody, Ctxt),
+ Arg = maybe_suppress_warnings(Arg1, Vs, PrevBody),
expr(#c_seq{arg=Arg,body=Body}, Ctxt,
sub_new_preserve_types(Sub));
true ->
@@ -2518,7 +2531,7 @@ opt_simple_let_2(Let0, Vs0, Arg0, Body, PrevBody, Ctxt, Sub) ->
end
end.
-%% maybe_suppress_warnings(Arg, [#c_var{}], PreviousBody, Context) -> Arg'
+%% maybe_suppress_warnings(Arg, [#c_var{}], PreviousBody) -> Arg'
%% Try to suppress false warnings when a variable is not used.
%% For instance, we don't expect a warning for useless building in:
%%
@@ -2529,10 +2542,7 @@ opt_simple_let_2(Let0, Vs0, Arg0, Body, PrevBody, Ctxt, Sub) ->
%% referenced in the original unoptimized code. If they were, we will
%% consider the warning false and suppress it.
-maybe_suppress_warnings(Arg, _, _, effect) ->
- %% Don't suppress any warnings in effect context.
- Arg;
-maybe_suppress_warnings(Arg, Vs, PrevBody, value) ->
+maybe_suppress_warnings(Arg, Vs, PrevBody) ->
case should_suppress_warning(Arg) of
true ->
Arg; %Already suppressed.
@@ -2556,8 +2566,16 @@ suppress_warning([H|T]) ->
true ->
suppress_warning(cerl:data_es(H) ++ T);
false ->
- Arg = cerl:set_ann(H, [compiler_generated]),
- cerl:c_seq(Arg, suppress_warning(T))
+ %% Some other thing, such as a function call.
+ %% This cannot be the compiler's fault, so the
+ %% warning should not be suppressed. We must
+ %% be careful not to destroy tail-recursion.
+ case T of
+ [] ->
+ H;
+ [_|_] ->
+ cerl:c_seq(H, suppress_warning(T))
+ end
end
end;
suppress_warning([]) -> void().
diff --git a/lib/compiler/test/warnings_SUITE.erl b/lib/compiler/test/warnings_SUITE.erl
index 5742b7e6cf..4e266875ee 100644
--- a/lib/compiler/test/warnings_SUITE.erl
+++ b/lib/compiler/test/warnings_SUITE.erl
@@ -283,6 +283,7 @@ bad_arith(Config) when is_list(Config) ->
{3,sys_core_fold,{eval_failure,badarith}},
{9,sys_core_fold,nomatch_guard},
{9,sys_core_fold,{eval_failure,badarith}},
+ {9,sys_core_fold,{no_effect,{erlang,is_integer,1}}},
{10,sys_core_fold,nomatch_guard},
{10,sys_core_fold,{eval_failure,badarith}},
{15,sys_core_fold,{eval_failure,badarith}}
@@ -371,7 +372,7 @@ files(Config) when is_list(Config) ->
%% Test warnings for term construction and BIF calls in effect context.
effect(Config) when is_list(Config) ->
- Ts = [{lc,
+ Ts = [{effect,
<<"
t(X) ->
case X of
@@ -477,6 +478,19 @@ effect(Config) when is_list(Config) ->
m9(Bs) ->
[{B,ok} = {B,foo:bar(B)} || B <- Bs],
ok.
+
+ m10(ConfigTableSize) ->
+ case ConfigTableSize of
+ apa ->
+ CurrentConfig = {id(camel_phase3),id(sms)},
+ case CurrentConfig of
+ {apa, bepa} -> ok;
+ _ -> ok
+ end
+ end,
+ ok.
+
+ id(I) -> I.
">>,
[],
{warnings,[{5,sys_core_fold,{no_effect,{erlang,is_integer,1}}},
@@ -754,6 +768,14 @@ no_warnings(Config) when is_list(Config) ->
case R0 of
{r,V1,_V2,V3} -> {r,V1,\"def\",V3}
end.
+
+ d(In0, Bool) ->
+ {In1,Int} = case id(Bool) of
+ false -> {In0,0}
+ end,
+ [In1,Int].
+
+ id(I) -> I.
">>,
[],
[]}],
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/hipe/llvm/hipe_llvm_main.erl b/lib/hipe/llvm/hipe_llvm_main.erl
index 0e50c9539b..3c24425828 100644
--- a/lib/hipe/llvm/hipe_llvm_main.erl
+++ b/lib/hipe/llvm/hipe_llvm_main.erl
@@ -465,7 +465,7 @@ remove_temp_folder(Dir, Options) ->
end.
unique_id(FunName, Arity) ->
- integer_to_list(erlang:phash2({FunName, Arity, now()})).
+ integer_to_list(erlang:phash2({FunName, Arity, erlang:unique_integer()})).
unique_folder(FunName, Arity, Options) ->
DirName = "llvm_" ++ unique_id(FunName, Arity) ++ "/",
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/httpc.xml b/lib/inets/doc/src/httpc.xml
index 4178cb7d4c..6984408932 100644
--- a/lib/inets/doc/src/httpc.xml
+++ b/lib/inets/doc/src/httpc.xml
@@ -366,7 +366,7 @@ filename() = string()
<tag><c><![CDATA[receiver]]></c></tag>
<item>
<p>Defines how the client will deliver the result of an
- asynchroneous request (<c>sync</c> has the value
+ asynchronous request (<c>sync</c> has the value
<c>false</c>). </p>
<taglist>
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/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/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/doc/src/megaco.xml b/lib/megaco/doc/src/megaco.xml
index dff1c3afc6..0a8dfe8a13 100644
--- a/lib/megaco/doc/src/megaco.xml
+++ b/lib/megaco/doc/src/megaco.xml
@@ -336,7 +336,7 @@ megaco_incr_timer() = #megaco_incr_timer{}
<tag><c><![CDATA[request_keep_alive_timeout]]></c></tag>
<item>
<p>Specifies the timeout time for the request-keep-alive timer. </p>
- <p>This timer is started when the <em>first</em> reply to an asynchroneous
+ <p>This timer is started when the <em>first</em> reply to an asynchronous
request (issued using the
<seealso marker="megaco#cast">megaco:cast/3</seealso> function)
arrives. As long as this timer is running, replies will
@@ -837,7 +837,7 @@ megaco_incr_timer() = #megaco_incr_timer{}
<tag><c><![CDATA[request_keep_alive_timeout]]></c></tag>
<item>
<p>Specifies the timeout time for the request-keep-alive timer. </p>
- <p>This timer is started when the <em>first</em> reply to an asynchroneous
+ <p>This timer is started when the <em>first</em> reply to an asynchronous
request (issued using the
<seealso marker="megaco#cast">megaco:cast/3</seealso> function)
arrives. As long as this timer is running, replies will
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/snmp/doc/src/snmp_app.xml b/lib/snmp/doc/src/snmp_app.xml
index 86f0981988..e36908a5b9 100644
--- a/lib/snmp/doc/src/snmp_app.xml
+++ b/lib/snmp/doc/src/snmp_app.xml
@@ -587,7 +587,7 @@
<marker id="manager_server_timeout"></marker>
<tag><c><![CDATA[server_timeout() = integer() <optional>]]></c></tag>
<item>
- <p>Asynchroneous request cleanup time. For every requests,
+ <p>Asynchronous request cleanup time. For every requests,
some info is stored internally, in order to be able to
deliver the reply (when it arrives) to the proper destination.
If the reply arrives, this info will be deleted. But if
diff --git a/lib/snmp/doc/src/snmp_config.xml b/lib/snmp/doc/src/snmp_config.xml
index 0ec8bb91cf..d1ee6545dd 100644
--- a/lib/snmp/doc/src/snmp_config.xml
+++ b/lib/snmp/doc/src/snmp_config.xml
@@ -616,7 +616,7 @@ in so far as it will be converted to the new format if found.
<marker id="manager_server_timeout"></marker>
<tag><c><![CDATA[server_timeout() = integer() <optional>]]></c></tag>
<item>
- <p>Asynchroneous request cleanup time. For every requests,
+ <p>Asynchronous request cleanup time. For every requests,
some info is stored internally, in order to be able to
deliver the reply (when it arrives) to the proper destination.
If the reply arrives, this info will be deleted. But if
diff --git a/lib/snmp/src/manager/snmpm.erl b/lib/snmp/src/manager/snmpm.erl
index 8976322c4e..96e3d55b46 100644
--- a/lib/snmp/src/manager/snmpm.erl
+++ b/lib/snmp/src/manager/snmpm.erl
@@ -520,7 +520,7 @@ sync_get(UserId, TargetName, Context, Oids, Timeout, ExtraInfo) ->
-%% --- asynchroneous get-request ---
+%% --- asynchronous get-request ---
%%
%% The reply will be delivered to the user
%% through a call to handle_pdu/5
@@ -588,7 +588,7 @@ sync_get_next(UserId, TargetName, Context, Oids, Timeout, ExtraInfo) ->
%% </BACKWARD-COMPAT>
-%% --- asynchroneous get_next-request ---
+%% --- asynchronous get_next-request ---
%%
async_get_next2(UserId, TargetName, Oids) ->
@@ -654,7 +654,7 @@ sync_set(UserId, TargetName, Context, VarsAndVals, Timeout, ExtraInfo) ->
%% </BACKWARD-COMPAT>
-%% --- asynchroneous set-request ---
+%% --- asynchronous set-request ---
%%
async_set2(UserId, TargetName, VarsAndVals) ->
@@ -746,7 +746,7 @@ sync_get_bulk(UserId, TargetName, NonRep, MaxRep, Context, Oids, Timeout,
%% </BACKWARD-COMPAT>
-%% --- asynchroneous get-bulk ---
+%% --- asynchronous get-bulk ---
%%
async_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids) ->
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/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"]}]}.