aboutsummaryrefslogtreecommitdiffstats
path: root/lib/inets/src
diff options
context:
space:
mode:
Diffstat (limited to 'lib/inets/src')
-rw-r--r--lib/inets/src/http_client/httpc.erl82
-rw-r--r--lib/inets/src/http_client/httpc_handler.erl23
-rw-r--r--lib/inets/src/http_client/httpc_manager.erl62
-rw-r--r--lib/inets/src/http_client/httpc_response.erl16
-rw-r--r--lib/inets/src/http_lib/http_uri.erl127
-rw-r--r--lib/inets/src/http_server/Makefile2
-rw-r--r--lib/inets/src/http_server/httpd_conf.erl110
-rw-r--r--lib/inets/src/http_server/httpd_response.erl27
-rw-r--r--lib/inets/src/http_server/httpd_script_env.erl57
-rw-r--r--lib/inets/src/http_server/httpd_sup.erl19
-rw-r--r--lib/inets/src/http_server/mod_get.erl16
-rw-r--r--lib/inets/src/inets_app/Makefile3
-rw-r--r--lib/inets/src/inets_app/inets.app.src5
-rw-r--r--lib/inets/src/inets_app/inets.appup.src114
-rw-r--r--lib/inets/src/inets_app/inets.erl267
-rw-r--r--lib/inets/src/inets_app/inets.mk2
-rw-r--r--lib/inets/src/inets_app/inets_app.erl2
-rw-r--r--lib/inets/src/inets_app/inets_internal.hrl6
-rw-r--r--lib/inets/src/inets_app/inets_sup.erl10
-rw-r--r--lib/inets/src/inets_app/inets_trace.erl357
20 files changed, 920 insertions, 387 deletions
diff --git a/lib/inets/src/http_client/httpc.erl b/lib/inets/src/http_client/httpc.erl
index ae87ceed93..0a30fe1e20 100644
--- a/lib/inets/src/http_client/httpc.erl
+++ b/lib/inets/src/http_client/httpc.erl
@@ -31,8 +31,10 @@
-export([
request/1, request/2, request/4, request/5,
cancel_request/1, cancel_request/2,
- set_option/2, set_option/3,
+ set_option/2, set_option/3,
set_options/1, set_options/2,
+ get_option/1, get_option/2,
+ get_options/1, get_options/2,
store_cookies/2, store_cookies/3,
cookie_header/1, cookie_header/2, cookie_header/3,
which_cookies/0, which_cookies/1,
@@ -156,7 +158,7 @@ request(Method,
{http_options, HTTPOptions},
{options, Options},
{profile, Profile}]),
- case http_uri:parse(Url, Options) of
+ case uri_parse(Url, Options) of
{error, Reason} ->
{error, Reason};
{ok, ParsedUrl} ->
@@ -177,7 +179,7 @@ request(Method,
{http_options, HTTPOptions},
{options, Options},
{profile, Profile}]),
- case http_uri:parse(Url, Options) of
+ case uri_parse(Url, Options) of
{error, Reason} ->
{error, Reason};
{ok, ParsedUrl} ->
@@ -230,7 +232,7 @@ cancel_request(RequestId, Profile)
set_options(Options) ->
set_options(Options, default_profile()).
set_options(Options, Profile) when is_atom(Profile) orelse is_pid(Profile) ->
- ?hcrt("set cookies", [{options, Options}, {profile, Profile}]),
+ ?hcrt("set options", [{options, Options}, {profile, Profile}]),
case validate_options(Options) of
{ok, Opts} ->
try
@@ -253,6 +255,58 @@ set_option(Key, Value, Profile) ->
%%--------------------------------------------------------------------------
+%% get_options(OptionItems) -> {ok, Values} | {error, Reason}
+%% get_options(OptionItems, Profile) -> {ok, Values} | {error, Reason}
+%% OptionItems - all | [option_item()]
+%% option_item() - proxy | pipeline_timeout | max_pipeline_length |
+%% keep_alive_timeout | max_keep_alive_length |
+%% max_sessions | verbose |
+%% cookies | ipfamily | ip | port | socket_opts
+%% Profile - atom()
+%% Values - [{option_item(), term()}]
+%% Reason - term()
+%% Description: Retrieves the current options.
+%%-------------------------------------------------------------------------
+get_options() ->
+ record_info(fields, options).
+
+get_options(Options) ->
+ get_options(Options, default_profile()).
+
+get_options(all = _Options, Profile) ->
+ get_options(get_options(), Profile);
+get_options(Options, Profile)
+ when (is_list(Options) andalso
+ (is_atom(Profile) orelse is_pid(Profile))) ->
+ ?hcrt("get options", [{options, Options}, {profile, Profile}]),
+ case Options -- get_options() of
+ [] ->
+ try
+ begin
+ {ok, httpc_manager:get_options(Options,
+ profile_name(Profile))}
+ end
+ catch
+ exit:{noproc, _} ->
+ {error, inets_not_started}
+ end;
+ InvalidGetOptions ->
+ {error, {invalid_options, InvalidGetOptions}}
+ end.
+
+get_option(Key) ->
+ get_option(Key, default_profile()).
+
+get_option(Key, Profile) ->
+ case get_options([Key], Profile) of
+ {ok, [{Key, Value}]} ->
+ {ok, Value};
+ Error ->
+ Error
+ end.
+
+
+%%--------------------------------------------------------------------------
%% store_cookies(SetCookieHeaders, Url [, Profile]) -> ok | {error, reason}
%%
%%
@@ -274,7 +328,7 @@ store_cookies(SetCookieHeaders, Url, Profile)
%% Since the Address part is not actually used
%% by the manager when storing cookies, we dont
%% care about ipv6-host-with-brackets.
- {ok, {_, _, Host, Port, Path, _}} = http_uri:parse(Url),
+ {ok, {_, _, Host, Port, Path, _}} = uri_parse(Url),
Address = {Host, Port},
ProfileName = profile_name(Profile),
Cookies = httpc_cookie:cookies(SetCookieHeaders, Path, Host),
@@ -347,7 +401,7 @@ which_cookies(Profile) ->
%% info() -> list()
%% info(Profile) -> list()
%%
-%% Description: Debug function, retreive info about the profile
+%% Description: Debug function, retrieve info about the profile
%%-------------------------------------------------------------------------
info() ->
info(default_profile()).
@@ -1144,6 +1198,22 @@ validate_headers(RequestHeaders, _, _) ->
RequestHeaders.
+%%--------------------------------------------------------------------------
+%% These functions is just simple wrappers to parse specifically HTTP URIs
+%%--------------------------------------------------------------------------
+
+scheme_defaults() ->
+ [{http, 80}, {https, 443}].
+
+uri_parse(URI) ->
+ http_uri:parse(URI, [{scheme_defaults, scheme_defaults()}]).
+
+uri_parse(URI, Opts) ->
+ http_uri:parse(URI, [{scheme_defaults, scheme_defaults()} | Opts]).
+
+
+%%--------------------------------------------------------------------------
+
child_name2info(undefined) ->
{error, no_such_service};
child_name2info(httpc_manager) ->
diff --git a/lib/inets/src/http_client/httpc_handler.erl b/lib/inets/src/http_client/httpc_handler.erl
index bfe9b14ef6..b8c34bd99b 100644
--- a/lib/inets/src/http_client/httpc_handler.erl
+++ b/lib/inets/src/http_client/httpc_handler.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2012. 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
@@ -851,14 +851,17 @@ connect(SocketType, ToAddress,
case IpFamily of
inet6fb4 ->
Opts3 = [inet6 | Opts2],
- case http_transport:connect(SocketType, ToAddress, Opts3, Timeout) of
- {error, _Reason} = Error ->
+ case http_transport:connect(SocketType,
+ ToAddress, Opts3, Timeout) of
+ {error, Reason6} ->
Opts4 = [inet | Opts2],
case http_transport:connect(SocketType,
ToAddress, Opts4, Timeout) of
- {error, _} ->
- %% Reply with the "original" error
- Error;
+ {error, Reason4} ->
+ {error, {failed_connect,
+ [{to_address, ToAddress},
+ {inet6, Opts3, Reason6},
+ {inet, Opts4, Reason4}]}};
OK ->
OK
end;
@@ -867,7 +870,13 @@ connect(SocketType, ToAddress,
end;
_ ->
Opts3 = [IpFamily | Opts2],
- http_transport:connect(SocketType, ToAddress, Opts3, Timeout)
+ case http_transport:connect(SocketType, ToAddress, Opts3, Timeout) of
+ {error, Reason} ->
+ {error, {failed_connect, [{to_address, ToAddress},
+ {IpFamily, Opts3, Reason}]}};
+ Else ->
+ Else
+ end
end.
connect_and_send_first_request(Address, Request, #state{options = Options} = State) ->
diff --git a/lib/inets/src/http_client/httpc_manager.erl b/lib/inets/src/http_client/httpc_manager.erl
index 453081da21..b225b43214 100644
--- a/lib/inets/src/http_client/httpc_manager.erl
+++ b/lib/inets/src/http_client/httpc_manager.erl
@@ -37,6 +37,7 @@
update_session/4,
delete_session/2,
set_options/2,
+ get_options/2,
store_cookies/3,
which_cookies/1, which_cookies/2, which_cookies/3,
reset_cookies/1,
@@ -250,6 +251,21 @@ set_options(Options, ProfileName) ->
%%--------------------------------------------------------------------
+%% Function: get_options(OptionItems, ProfileName) -> Values
+%%
+%% OptionItems = [OptionItem]
+%% OptionItem = Any or all fields of the current #options{} record
+%% Values = [{OptionItem, Value}]
+%% Value = term()
+%%
+%% Description: Gets the specified options used by the client.
+%%--------------------------------------------------------------------
+
+get_options(Options, ProfileName) ->
+ call(ProfileName, {get_options, Options}).
+
+
+%%--------------------------------------------------------------------
%% Function: store_cookies(Cookies, Address, ProfileName) -> ok
%%
%% Cookies = [Cookie]
@@ -430,7 +446,7 @@ handle_call(which_cookies, _, #state{cookie_db = CookieDb} = State) ->
handle_call({which_cookies, Url, Options}, _,
#state{cookie_db = CookieDb} = State) ->
?hcrv("which cookies", [{url, Url}, {options, Options}]),
- case http_uri:parse(Url, Options) of
+ case uri_parse(Url, Options) of
{ok, {Scheme, _, Host, Port, Path, _}} ->
CookieHeaders =
httpc_cookie:header(CookieDb, Scheme, {Host, Port}, Path),
@@ -439,6 +455,12 @@ handle_call({which_cookies, Url, Options}, _,
{reply, ERROR, State}
end;
+handle_call({get_options, OptionItems}, _, #state{options = Options} = State) ->
+ ?hcrv("get options", [{option_items, OptionItems}]),
+ Reply = [{OptionItem, get_option(OptionItem, Options)} || OptionItem <-
+ OptionItems],
+ {reply, Reply, State};
+
handle_call(info, _, State) ->
?hcrv("info", []),
Info = get_manager_info(State),
@@ -872,6 +894,19 @@ make_db_name(ProfileName, Post) ->
list_to_atom(atom_to_list(ProfileName) ++ Post).
+%%--------------------------------------------------------------------------
+%% These functions is just simple wrappers to parse specifically HTTP URIs
+%%--------------------------------------------------------------------------
+
+scheme_defaults() ->
+ [{http, 80}, {https, 443}].
+
+uri_parse(URI, Opts) ->
+ http_uri:parse(URI, [{scheme_defaults, scheme_defaults()} | Opts]).
+
+
+%%--------------------------------------------------------------------------
+
call(ProfileName, Msg) ->
Timeout = infinity,
@@ -883,6 +918,31 @@ cast(ProfileName, Msg) ->
gen_server:cast(ProfileName, Msg).
+get_option(proxy, #options{proxy = Proxy}) ->
+ Proxy;
+get_option(pipeline_timeout, #options{pipeline_timeout = Timeout}) ->
+ Timeout;
+get_option(max_pipeline_length, #options{max_pipeline_length = Length}) ->
+ Length;
+get_option(keep_alive_timeout, #options{keep_alive_timeout = Timeout}) ->
+ Timeout;
+get_option(max_keep_alive_length, #options{max_keep_alive_length = Length}) ->
+ Length;
+get_option(max_sessions, #options{max_sessions = MaxSessions}) ->
+ MaxSessions;
+get_option(cookies, #options{cookies = Cookies}) ->
+ Cookies;
+get_option(verbose, #options{verbose = Verbose}) ->
+ Verbose;
+get_option(ipfamily, #options{ipfamily = IpFamily}) ->
+ IpFamily;
+get_option(ip, #options{ip = IP}) ->
+ IP;
+get_option(port, #options{port = Port}) ->
+ Port;
+get_option(socket_opts, #options{socket_opts = SocketOpts}) ->
+ SocketOpts.
+
get_proxy(Opts, #options{proxy = Default}) ->
proplists:get_value(proxy, Opts, Default).
diff --git a/lib/inets/src/http_client/httpc_response.erl b/lib/inets/src/http_client/httpc_response.erl
index 919115a23a..23924e355e 100644
--- a/lib/inets/src/http_client/httpc_response.erl
+++ b/lib/inets/src/http_client/httpc_response.erl
@@ -342,7 +342,7 @@ redirect(Response = {StatusLine, Headers, Body}, Request) ->
RedirUrl ->
UrlParseOpts = [{ipv6_host_with_brackets,
Request#request.ipv6_host_with_brackets}],
- case http_uri:parse(RedirUrl, UrlParseOpts) of
+ case uri_parse(RedirUrl, UrlParseOpts) of
{error, no_scheme} when
(Request#request.settings)#http_options.relaxed ->
NewLocation = fix_relative_uri(Request, RedirUrl),
@@ -437,3 +437,17 @@ format_response({StatusLine, Headers, Body}) ->
end,
{{StatusLine, http_response:header_list(Headers), NewBody}, Data}.
+%%--------------------------------------------------------------------------
+%% These functions is just simple wrappers to parse specifically HTTP URIs
+%%--------------------------------------------------------------------------
+
+scheme_defaults() ->
+ [{http, 80}, {https, 443}].
+
+uri_parse(URI, Opts) ->
+ http_uri:parse(URI, [{scheme_defaults, scheme_defaults()} | Opts]).
+
+
+%%--------------------------------------------------------------------------
+
+
diff --git a/lib/inets/src/http_lib/http_uri.erl b/lib/inets/src/http_lib/http_uri.erl
index 32c6305a79..5962001c3a 100644
--- a/lib/inets/src/http_lib/http_uri.erl
+++ b/lib/inets/src/http_lib/http_uri.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2012. 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
@@ -17,39 +17,95 @@
%% %CopyrightEnd%
%%
%%
-%% RFC 3986
+%% This is from chapter 3, Syntax Components, of RFC 3986:
+%%
+%% The generic URI syntax consists of a hierarchical sequence of
+%% components referred to as the scheme, authority, path, query, and
+%% fragment.
+%%
+%% URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
+%%
+%% hier-part = "//" authority path-abempty
+%% / path-absolute
+%% / path-rootless
+%% / path-empty
+%%
+%% The scheme and path components are required, though the path may be
+%% empty (no characters). When authority is present, the path must
+%% either be empty or begin with a slash ("/") character. When
+%% authority is not present, the path cannot begin with two slash
+%% characters ("//"). These restrictions result in five different ABNF
+%% rules for a path (Section 3.3), only one of which will match any
+%% given URI reference.
+%%
+%% The following are two example URIs and their component parts:
+%%
+%% foo://example.com:8042/over/there?name=ferret#nose
+%% \_/ \______________/\_________/ \_________/ \__/
+%% | | | | |
+%% scheme authority path query fragment
+%% | _____________________|__
+%% / \ / \
+%% urn:example:animal:ferret:nose
+%%
+%% scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
+%% authority = [ userinfo "@" ] host [ ":" port ]
+%% userinfo = *( unreserved / pct-encoded / sub-delims / ":" )
+%%
%%
-module(http_uri).
-export([parse/1, parse/2,
+ scheme_defaults/0,
encode/1, decode/1]).
+-export_type([scheme/0, default_scheme_port_number/0]).
+
%%%=========================================================================
%%% API
%%%=========================================================================
+
+-type scheme() :: atom().
+-type default_scheme_port_number() :: pos_integer().
+
+-spec scheme_defaults() ->
+ [{scheme(), default_scheme_port_number()}].
+
+scheme_defaults() ->
+ [{http, 80},
+ {https, 443},
+ {ftp, 21},
+ {ssh, 22},
+ {sftp, 22},
+ {tftp, 69}].
+
parse(AbsURI) ->
parse(AbsURI, []).
parse(AbsURI, Opts) ->
- case parse_scheme(AbsURI) of
+ case parse_scheme(AbsURI, Opts) of
{error, Reason} ->
{error, Reason};
- {Scheme, Rest} ->
- case (catch parse_uri_rest(Scheme, Rest, Opts)) of
- {UserInfo, Host, Port, Path, Query} ->
+ {Scheme, DefaultPort, Rest} ->
+ case (catch parse_uri_rest(Scheme, DefaultPort, Rest, Opts)) of
+ {ok, {UserInfo, Host, Port, Path, Query}} ->
{ok, {Scheme, UserInfo, Host, Port, Path, Query}};
+ {error, Reason} ->
+ {error, {Reason, Scheme, AbsURI}};
_ ->
- {error, {malformed_url, AbsURI}}
+ {error, {malformed_url, Scheme, AbsURI}}
end
end.
+reserved() ->
+ sets:from_list([$;, $:, $@, $&, $=, $+, $,, $/, $?,
+ $#, $[, $], $<, $>, $\", ${, $}, $|,
+ $\\, $', $^, $%, $ ]).
+
encode(URI) ->
- Reserved = sets:from_list([$;, $:, $@, $&, $=, $+, $,, $/, $?,
- $#, $[, $], $<, $>, $\", ${, $}, $|,
- $\\, $', $^, $%, $ ]),
- %% lists:append(lists:map(fun(Char) -> uri_encode(Char, Reserved) end, URI)).
+ Reserved = reserved(),
lists:append([uri_encode(Char, Reserved) || Char <- URI]).
decode(String) ->
@@ -67,20 +123,31 @@ do_decode([]) ->
%%% Internal functions
%%%========================================================================
-parse_scheme(AbsURI) ->
+which_scheme_defaults(Opts) ->
+ Key = scheme_defaults,
+ case lists:keysearch(Key, 1, Opts) of
+ {value, {Key, SchemeDefaults}} ->
+ SchemeDefaults;
+ false ->
+ scheme_defaults()
+ end.
+
+parse_scheme(AbsURI, Opts) ->
case split_uri(AbsURI, ":", {error, no_scheme}, 1, 1) of
{error, no_scheme} ->
{error, no_scheme};
- {StrScheme, Rest} ->
- case list_to_atom(http_util:to_lower(StrScheme)) of
- Scheme when (Scheme =:= http) orelse (Scheme =:= https) ->
- {Scheme, Rest};
- Scheme ->
- {error, {not_supported_scheme, Scheme}}
+ {SchemeStr, Rest} ->
+ Scheme = list_to_atom(http_util:to_lower(SchemeStr)),
+ SchemeDefaults = which_scheme_defaults(Opts),
+ case lists:keysearch(Scheme, 1, SchemeDefaults) of
+ {value, {Scheme, DefaultPort}} ->
+ {Scheme, DefaultPort, Rest};
+ false ->
+ {Scheme, no_default_port, Rest}
end
end.
-parse_uri_rest(Scheme, "//" ++ URIPart, Opts) ->
+parse_uri_rest(Scheme, DefaultPort, "//" ++ URIPart, Opts) ->
{Authority, PathQuery} =
case split_uri(URIPart, "/", URIPart, 1, 0) of
Split = {_, _} ->
@@ -93,26 +160,25 @@ parse_uri_rest(Scheme, "//" ++ URIPart, Opts) ->
{URIPart,""}
end
end,
-
{UserInfo, HostPort} = split_uri(Authority, "@", {"", Authority}, 1, 1),
- {Host, Port} = parse_host_port(Scheme, HostPort, Opts),
+ {Host, Port} = parse_host_port(Scheme, DefaultPort, HostPort, Opts),
{Path, Query} = parse_path_query(PathQuery),
- {UserInfo, Host, Port, Path, Query}.
+ {ok, {UserInfo, Host, Port, Path, Query}}.
parse_path_query(PathQuery) ->
{Path, Query} = split_uri(PathQuery, "\\?", {PathQuery, ""}, 1, 0),
{path(Path), Query}.
-parse_host_port(Scheme,"[" ++ HostPort, Opts) -> %ipv6
- DefaultPort = default_port(Scheme),
+%% In this version of the function, we no longer need
+%% the Scheme argument, but just in case...
+parse_host_port(_Scheme, DefaultPort, "[" ++ HostPort, Opts) -> %ipv6
{Host, ColonPort} = split_uri(HostPort, "\\]", {HostPort, ""}, 1, 1),
Host2 = maybe_ipv6_host_with_brackets(Host, Opts),
{_, Port} = split_uri(ColonPort, ":", {"", DefaultPort}, 0, 1),
{Host2, int_port(Port)};
-parse_host_port(Scheme, HostPort, _Opts) ->
- DefaultPort = default_port(Scheme),
+parse_host_port(_Scheme, DefaultPort, HostPort, _Opts) ->
{Host, Port} = split_uri(HostPort, ":", {HostPort, DefaultPort}, 1, 1),
{Host, int_port(Port)}.
@@ -133,15 +199,14 @@ maybe_ipv6_host_with_brackets(Host, Opts) ->
Host
end.
-default_port(http) ->
- 80;
-default_port(https) ->
- 443.
int_port(Port) when is_integer(Port) ->
Port;
int_port(Port) when is_list(Port) ->
- list_to_integer(Port).
+ list_to_integer(Port);
+%% This is the case where no port was found and there was no default port
+int_port(no_default_port) ->
+ throw({error, no_default_port}).
path("") ->
"/";
diff --git a/lib/inets/src/http_server/Makefile b/lib/inets/src/http_server/Makefile
index 55cc68dede..c341a2cec7 100644
--- a/lib/inets/src/http_server/Makefile
+++ b/lib/inets/src/http_server/Makefile
@@ -88,6 +88,8 @@ ERL_FILES = $(MODULES:%=%.erl)
TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR))
+INETS_FLAGS = -D'SERVER_SOFTWARE="$(APPLICATION)/$(VSN)"'
+
# ----------------------------------------------------
# FLAGS
diff --git a/lib/inets/src/http_server/httpd_conf.erl b/lib/inets/src/http_server/httpd_conf.erl
index 7646300409..b575d7331b 100644
--- a/lib/inets/src/http_server/httpd_conf.erl
+++ b/lib/inets/src/http_server/httpd_conf.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. 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
@@ -210,12 +210,32 @@ load("MaxBodySize " ++ MaxBodySize, []) ->
{ok, Integer} ->
{ok, [], {max_body_size,Integer}};
{error, _} ->
- {error, ?NICE(clean(MaxBodySize)++
+ {error, ?NICE(clean(MaxBodySize) ++
" is an invalid number of MaxBodySize")}
end;
load("ServerName " ++ ServerName, []) ->
- {ok,[],{server_name,clean(ServerName)}};
+ {ok,[], {server_name, clean(ServerName)}};
+
+load("ServerTokens " ++ ServerTokens, []) ->
+ %% These are the valid *plain* server tokens:
+ %% sprod, major, minor, minimum, os, full
+ %% It can also be a "private" server token: private:<any string>
+ case string:tokens(ServerTokens, [$:]) of
+ ["private", Private] ->
+ {ok,[], {server_tokens, clean(Private)}};
+ [TokStr] ->
+ Tok = list_to_atom(clean(TokStr)),
+ case lists:member(Tok, [prod, major, minor, minimum, os, full]) of
+ true ->
+ {ok,[], {server_tokens, Tok}};
+ false ->
+ {error, ?NICE(clean(ServerTokens) ++
+ " is an invalid ServerTokens")}
+ end;
+ _ ->
+ {error, ?NICE(clean(ServerTokens) ++ " is an invalid ServerTokens")}
+ end;
load("SocketType " ++ SocketType, []) ->
%% ssl is the same as HTTP_DEFAULT_SSL_KIND
@@ -475,7 +495,7 @@ validate_properties(Properties) ->
validate_properties2(Properties) ->
case proplists:get_value(bind_address, Properties) of
undefined ->
- case proplists:get_value(sock_type, Properties, ip_comm) of
+ case proplists:get_value(sock_type, Properties, ip_comm) of
ip_comm ->
case proplists:get_value(ipfamily, Properties) of
undefined ->
@@ -537,6 +557,20 @@ validate_config_params([{server_name, Value} | Rest])
validate_config_params([{server_name, Value} | _]) ->
throw({server_name, Value});
+validate_config_params([{server_tokens, Value} | Rest])
+ when is_atom(Value) ->
+ case lists:member(Value, plain_server_tokens()) of
+ true ->
+ validate_config_params(Rest);
+ false ->
+ throw({server_tokens, Value})
+ end;
+validate_config_params([{server_tokens, {private, Value}} | Rest])
+ when is_list(Value) ->
+ validate_config_params(Rest);
+validate_config_params([{server_tokens, Value} | _]) ->
+ throw({server_tokens, Value});
+
validate_config_params([{socket_type, Value} | Rest])
when (Value =:= ip_comm) orelse
(Value =:= ssl) orelse
@@ -737,9 +771,73 @@ store({log_format, LogFormat}, _ConfigList)
store({log_format, LogFormat}, _ConfigList)
when (LogFormat =:= compact) orelse (LogFormat =:= pretty) ->
{ok, {log_format, LogFormat}};
+store({server_tokens, ServerTokens} = Entry, _ConfigList) ->
+ Server = server(ServerTokens),
+ {ok, [Entry, {server, Server}]};
store(ConfigListEntry, _ConfigList) ->
{ok, ConfigListEntry}.
+
+%% The SERVER_SOFTWARE macro has the following structure:
+%% <product>/<version>
+%% Example: "inets/1.2.3"
+%% So, with this example (on a linux machine, with OTP R15B),
+%% this will result in:
+%% prod: "inets"
+%% major: "inets/1"
+%% minor: "inets/1.2"
+%% minimal: "inets/1.2.3"
+%% os: "inets/1.2.3 (unix)
+%% full: "inets/1.2.3 (unix/linux) OTP/R15B"
+%% Note that the format of SERVER_SOFTWARE is that of 'minimal'.
+%% Also, there will always be atleast two digits in a version:
+%% Not just 1 but 1.0
+%%
+%% We have already checked that the value is valid,
+%% so there is no need to check enything here.
+%%
+server(prod = _ServerTokens) ->
+ [Prod|_Version] = string:tokens(?SERVER_SOFTWARE, [$/]),
+ Prod;
+server(major = _ServerTokens) ->
+ [Prod|Version] = string:tokens(?SERVER_SOFTWARE, [$/]),
+ [Major|_] = string:tokens(Version, [$.]),
+ Prod ++ "/" ++ Major;
+server(minor = _ServerTokens) ->
+ [Prod|Version] = string:tokens(?SERVER_SOFTWARE, [$/]),
+ [Major,Minor|_] = string:tokens(Version, [$.]),
+ Prod ++ "/" ++ Major ++ "." ++ Minor;
+server(minimal = _ServerTokens) ->
+ %% This is the default
+ ?SERVER_SOFTWARE;
+server(os = _ServerTokens) ->
+ OS = os_info(partial),
+ lists:flatten(io_lib:format("~s ~s", [?SERVER_SOFTWARE, OS]));
+server(full = _ServerTokens) ->
+ OTPRelease = otp_release(),
+ OS = os_info(full),
+ lists:flatten(
+ io_lib:format("~s ~s OTP/~s", [?SERVER_SOFTWARE, OS, OTPRelease]));
+server({private, Server} = _ServerTokens) when is_list(Server) ->
+ %% The user provide its own
+ Server;
+server(_) ->
+ ?SERVER_SOFTWARE.
+
+os_info(Info) ->
+ case os:type() of
+ {OsFamily, _OsName} when Info =:= partial ->
+ lists:flatten(io_lib:format("(~w)", [OsFamily]));
+ {OsFamily, OsName} ->
+ lists:flatten(io_lib:format("(~w/~w)", [OsFamily, OsName]));
+ OsFamily ->
+ lists:flatten(io_lib:format("(~w)", [OsFamily]))
+ end.
+
+otp_release() ->
+ erlang:system_info(otp_release).
+
+
%% Phase 3: Remove
remove_all(ConfigDB) ->
Modules = httpd_util:lookup(ConfigDB,modules,[]),
@@ -1159,6 +1257,10 @@ ssl_ca_certificate_file(ConfigDB) ->
[{cacertfile, File}]
end.
+plain_server_tokens() ->
+ [prod, major, minor, minimum, os, full].
+
error_report(Where,M,F,Error) ->
error_logger:error_report([{?MODULE, Where},
{apply, {M, F, []}}, Error]).
+
diff --git a/lib/inets/src/http_server/httpd_response.erl b/lib/inets/src/http_server/httpd_response.erl
index dd7223876e..2dedb088e4 100644
--- a/lib/inets/src/http_server/httpd_response.erl
+++ b/lib/inets/src/http_server/httpd_response.erl
@@ -144,10 +144,14 @@ send_response(ModData, Header, Body) ->
end
end.
-send_header(#mod{socket_type = Type, socket = Sock,
- http_version = Ver, connection = Conn} = _ModData,
+send_header(#mod{socket_type = Type,
+ socket = Sock,
+ http_version = Ver,
+ connection = Conn,
+ config_db = ConfigDb} = _ModData,
StatusCode, KeyValueTupleHeaders) ->
- Headers = create_header(lists:map(fun transform/1, KeyValueTupleHeaders)),
+ Headers = create_header(ConfigDb,
+ lists:map(fun transform/1, KeyValueTupleHeaders)),
NewVer = case {Ver, StatusCode} of
{[], _} ->
%% May be implicit!
@@ -275,13 +279,20 @@ cache_headers(#mod{config_db = Db}) ->
[]
end.
-create_header(KeyValueTupleHeaders) ->
- NewHeaders = add_default_headers([{"date", httpd_util:rfc1123_date()},
- {"content-type", "text/html"},
- {"server", ?SERVER_SOFTWARE}],
- KeyValueTupleHeaders),
+create_header(ConfigDb, KeyValueTupleHeaders) ->
+ Date = httpd_util:rfc1123_date(),
+ ContentType = "text/html",
+ Server = server(ConfigDb),
+ NewHeaders = add_default_headers([{"date", Date},
+ {"content-type", ContentType},
+ {"server", Server}],
+ KeyValueTupleHeaders),
lists:map(fun fix_header/1, NewHeaders).
+
+server(ConfigDb) ->
+ httpd_util:lookup(ConfigDb, server, ?SERVER_SOFTWARE).
+
fix_header({Key0, Value}) ->
%% make sure first letter is capital
Words1 = string:tokens(Key0, "-"),
diff --git a/lib/inets/src/http_server/httpd_script_env.erl b/lib/inets/src/http_server/httpd_script_env.erl
index d3115150b0..a5613ba4a4 100644
--- a/lib/inets/src/http_server/httpd_script_env.erl
+++ b/lib/inets/src/http_server/httpd_script_env.erl
@@ -50,29 +50,44 @@ create_env(ScriptType, ModData, ScriptElements) ->
%%%========================================================================
%%% Internal functions
%%%========================================================================
+
+which_server(#mod{config_db = ConfigDb}) ->
+ httpd_util:lookup(ConfigDb, server, ?SERVER_SOFTWARE).
+
+which_port(#mod{config_db = ConfigDb}) ->
+ httpd_util:lookup(ConfigDb, port, 80).
+
+which_peername(#mod{init_data = #init_data{peername = {_, RemoteAddr}}}) ->
+ RemoteAddr.
+
+which_resolve(#mod{init_data = #init_data{resolve = Resolve}}) ->
+ Resolve.
+
+which_method(#mod{method = Method}) ->
+ Method.
+
+which_request_uri(#mod{request_uri = RUri}) ->
+ RUri.
+
create_basic_elements(esi, ModData) ->
- {_, RemoteAddr} = (ModData#mod.init_data)#init_data.peername,
- [{server_software, ?SERVER_SOFTWARE},
- {server_name, (ModData#mod.init_data)#init_data.resolve},
- {gateway_interface,?GATEWAY_INTERFACE},
- {server_protocol, ?SERVER_PROTOCOL},
- {server_port, httpd_util:lookup(ModData#mod.config_db,port,80)},
- {request_method, ModData#mod.method},
- {remote_addr, RemoteAddr},
- {script_name, ModData#mod.request_uri}];
+ [{server_software, which_server(ModData)},
+ {server_name, which_resolve(ModData)},
+ {gateway_interface, ?GATEWAY_INTERFACE},
+ {server_protocol, ?SERVER_PROTOCOL},
+ {server_port, which_port(ModData)},
+ {request_method, which_method(ModData)},
+ {remote_addr, which_peername(ModData)},
+ {script_name, which_request_uri(ModData)}];
create_basic_elements(cgi, ModData) ->
- {_, RemoteAddr} = (ModData#mod.init_data)#init_data.peername,
- [{"SERVER_SOFTWARE",?SERVER_SOFTWARE},
- {"SERVER_NAME", (ModData#mod.init_data)#init_data.resolve},
- {"GATEWAY_INTERFACE",?GATEWAY_INTERFACE},
- {"SERVER_PROTOCOL",?SERVER_PROTOCOL},
- {"SERVER_PORT",
- integer_to_list(httpd_util:lookup(
- ModData#mod.config_db, port, 80))},
- {"REQUEST_METHOD", ModData#mod.method},
- {"REMOTE_ADDR", RemoteAddr},
- {"SCRIPT_NAME", ModData#mod.request_uri}].
+ [{"SERVER_SOFTWARE", which_server(ModData)},
+ {"SERVER_NAME", which_resolve(ModData)},
+ {"GATEWAY_INTERFACE", ?GATEWAY_INTERFACE},
+ {"SERVER_PROTOCOL", ?SERVER_PROTOCOL},
+ {"SERVER_PORT", integer_to_list(which_port(ModData))},
+ {"REQUEST_METHOD", which_method(ModData)},
+ {"REMOTE_ADDR", which_peername(ModData)},
+ {"SCRIPT_NAME", which_request_uri(ModData)}].
create_http_header_elements(ScriptType, Headers) ->
create_http_header_elements(ScriptType, Headers, []).
@@ -80,7 +95,7 @@ create_http_header_elements(ScriptType, Headers) ->
create_http_header_elements(_, [], Acc) ->
Acc;
create_http_header_elements(ScriptType, [{Name, [Value | _] = Values } |
- Headers], Acc)
+ Headers], Acc)
when is_list(Value) ->
NewName = lists:map(fun(X) -> if X == $- -> $_; true -> X end end, Name),
Element = http_env_element(ScriptType, NewName, multi_value(Values)),
diff --git a/lib/inets/src/http_server/httpd_sup.erl b/lib/inets/src/http_server/httpd_sup.erl
index 264dc9f006..8f3e8f9500 100644
--- a/lib/inets/src/http_server/httpd_sup.erl
+++ b/lib/inets/src/http_server/httpd_sup.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2012. 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
@@ -162,17 +162,30 @@ httpd_config([Value| _] = Config) when is_tuple(Value) ->
httpd_child_spec([Value| _] = Config, AcceptTimeout, Debug)
when is_tuple(Value) ->
+ ?hdrt("httpd_child_spec - entry", [{accept_timeout, AcceptTimeout},
+ {debug, Debug}]),
Address = proplists:get_value(bind_address, Config, any),
Port = proplists:get_value(port, Config, 80),
httpd_child_spec(Config, AcceptTimeout, Debug, Address, Port);
-httpd_child_spec(ConfigFile, AcceptTimeout, Debug) ->
+%% In this case the AcceptTimeout and Debug will only have default values...
+httpd_child_spec(ConfigFile, AcceptTimeoutDef, DebugDef) ->
+ ?hdrt("httpd_child_spec - entry", [{config_file, ConfigFile},
+ {accept_timeout_def, AcceptTimeoutDef},
+ {debug_def, DebugDef}]),
case httpd_conf:load(ConfigFile) of
{ok, ConfigList} ->
+ ?hdrt("httpd_child_spec - loaded", [{config_list, ConfigList}]),
case (catch httpd_conf:validate_properties(ConfigList)) of
{ok, Config} ->
+ ?hdrt("httpd_child_spec - validated", [{config, Config}]),
Address = proplists:get_value(bind_address, Config, any),
Port = proplists:get_value(port, Config, 80),
+ AcceptTimeout =
+ proplists:get_value(accept_timeout, Config,
+ AcceptTimeoutDef),
+ Debug =
+ proplists:get_value(debug, Config, DebugDef),
httpd_child_spec([{file, ConfigFile} | Config],
AcceptTimeout, Debug, Address, Port);
Error ->
@@ -183,7 +196,7 @@ httpd_child_spec(ConfigFile, AcceptTimeout, Debug) ->
end.
httpd_child_spec(Config, AcceptTimeout, Debug, Addr, Port) ->
- case Port == 0 orelse proplists:is_defined(fd, Config) of
+ case (Port =:= 0) orelse proplists:is_defined(fd, Config) of
true ->
httpd_child_spec_listen(Config, AcceptTimeout, Debug, Addr, Port);
false ->
diff --git a/lib/inets/src/http_server/mod_get.erl b/lib/inets/src/http_server/mod_get.erl
index 5cb30e3d97..c58d1f3508 100644
--- a/lib/inets/src/http_server/mod_get.erl
+++ b/lib/inets/src/http_server/mod_get.erl
@@ -18,9 +18,16 @@
%%
%%
-module(mod_get).
+
-export([do/1]).
+
-include("httpd.hrl").
-include("httpd_internal.hrl").
+-include("inets_internal.hrl").
+
+-define(VMODULE,"GET").
+
+
%% do
do(Info) ->
@@ -84,15 +91,16 @@ send_response(_Socket, _SocketType, Path, Info)->
file:close(FileDescriptor),
{proceed,[{response,{already_sent,200,
FileInfo#file_info.size}},
- {mime_type,MimeType}|Info#mod.data]};
+ {mime_type,MimeType} | Info#mod.data]};
{error, Reason} ->
+ ?hdrt("send_response -> failed open file",
+ [{path, Path}, {reason, Reason}]),
Status = httpd_file:handle_error(Reason, "open", Info, Path),
- {proceed,
- [{status, Status}| Info#mod.data]}
+ {proceed, [{status, Status} | Info#mod.data]}
end.
%% send
-
+
send(#mod{socket = Socket, socket_type = SocketType} = Info,
StatusCode, Headers, FileDescriptor) ->
?DEBUG("send -> send header",[]),
diff --git a/lib/inets/src/inets_app/Makefile b/lib/inets/src/inets_app/Makefile
index d99e33b4ea..6da6a1d79f 100644
--- a/lib/inets/src/inets_app/Makefile
+++ b/lib/inets/src/inets_app/Makefile
@@ -46,7 +46,8 @@ MODULES = \
inets_service \
inets_app \
inets_sup \
- inets_regexp
+ inets_regexp \
+ inets_trace
INTERNAL_HRL_FILES = inets_internal.hrl
EXTERNAL_HRL_FILES = ../../include/httpd.hrl \
diff --git a/lib/inets/src/inets_app/inets.app.src b/lib/inets/src/inets_app/inets.app.src
index 1db7ed2c30..4aea2ef3d7 100644
--- a/lib/inets/src/inets_app/inets.app.src
+++ b/lib/inets/src/inets_app/inets.app.src
@@ -18,14 +18,15 @@
%%
{application,inets,
- [{description,"INETS CXC 138 49"},
- {vsn,"%VSN%"},
+ [{description, "INETS CXC 138 49"},
+ {vsn, "%VSN%"},
{modules,[
inets,
inets_sup,
inets_app,
inets_service,
inets_regexp,
+ inets_trace,
%% FTP
ftp,
diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src
index e80cb2a23b..c7029f7b31 100644
--- a/lib/inets/src/inets_app/inets.appup.src
+++ b/lib/inets/src/inets_app/inets.appup.src
@@ -18,65 +18,115 @@
{"%VSN%",
[
+ {"5.8.1",
+ [
+ {load_module, http_uri, soft_purge, soft_purge, []},
+ {load_module, httpc_response, soft_purge, soft_purge, [http_uri]},
+
+ {load_module, httpc, soft_purge, soft_purge,
+ [http_uri, httpc_manager]},
+ {update, httpc_manager, soft, soft_purge, soft_purge, [http_uri]},
+
+ {load_module, inets_app, soft_purge, soft_purge, [inets_sup]},
+ {update, inets_sup, soft, soft_purge, soft_purge, []},
+
+ {load_module, httpd_conf, soft_purge, soft_purge, []},
+ {load_module, httpd_response, soft_purge, soft_purge, []},
+ {load_module, httpd_script_env, soft_purge, soft_purge, []},
+
+ {load_module, inets, soft_purge, soft_purge, [inets_trace]},
+ {update, httpc_handler, soft, soft_purge, soft_purge, []},
+ {update, httpd_sup, soft, soft_purge, soft_purge, []},
+ {add_module, inets_trace}
+ ]
+ },
{"5.8",
[
+ {load_module, http_uri, soft_purge, soft_purge, []},
+ {load_module, httpc_response, soft_purge, soft_purge, [http_uri]},
+
+ {load_module, httpc, soft_purge, soft_purge,
+ [http_uri, httpc_manager]},
+
+ {load_module, inets_app, soft_purge, soft_purge, [inets_sup]},
+ {update, inets_sup, soft, soft_purge, soft_purge, []},
+
+ {load_module, inets, soft_purge, soft_purge, [inets_trace]},
+
+ {load_module, httpd_conf, soft_purge, soft_purge, []},
+ {load_module, httpd_response, soft_purge, soft_purge, []},
+ {load_module, httpd_script_env, soft_purge, soft_purge, []},
+
{load_module, ftp, soft_purge, soft_purge, []},
{update, httpc_handler, {advanced, upgrade_from_pre_5_8_1},
soft_purge, soft_purge, []},
{update, httpc_manager, {advanced, upgrade_from_pre_5_8_1},
- soft_purge, soft_purge, [httpc_handler]}
+ soft_purge, soft_purge, [http_uri, httpc_handler]},
+ {update, httpd_sup, soft, soft_purge, soft_purge, []},
+
+ {add_module, inets_trace}
]
},
{"5.7.2",
[
{restart_application, inets}
]
- },
- {"5.7.1",
- [
- {restart_application, inets}
- ]
- },
- {"5.7",
+ }
+ ],
+ [
+ {"5.8.1",
[
- {restart_application, inets}
+ {load_module, http_uri, soft_purge, soft_purge, []},
+ {load_module, httpc_response, soft_purge, soft_purge, [http_uri]},
+
+ {load_module, httpc, soft_purge, soft_purge,
+ [http_uri, httpc_manager]},
+ {update, httpc_manager, soft, soft_purge, soft_purge, [http_uri]},
+
+ {load_module, inets_app, soft_purge, soft_purge, [inets_sup]},
+ {update, inets_sup, soft, soft_purge, soft_purge, []},
+
+ {load_module, httpd_conf, soft_purge, soft_purge, []},
+ {load_module, httpd_response, soft_purge, soft_purge, []},
+ {load_module, httpd_script_env, soft_purge, soft_purge, []},
+
+ {load_module, inets, soft_purge, soft_purge, []},
+ {update, httpc_handler, soft, soft_purge, soft_purge, []},
+ {update, httpd_sup, soft, soft_purge, soft_purge, []},
+ {remove, {inets_trace, soft_purge, brutal_purge}}
]
},
- {"5.6",
- [
- {restart_application, inets}
- ]
- }
- ],
- [
{"5.8",
[
+ {load_module, http_uri, soft_purge, soft_purge, []},
+ {load_module, httpc_response, soft_purge, soft_purge, [http_uri]},
+
+ {load_module, httpc, soft_purge, soft_purge,
+ [http_uri, httpc_manager]},
+
+ {load_module, inets_app, soft_purge, soft_purge, [inets_sup]},
+ {update, inets_sup, soft, soft_purge, soft_purge, []},
+
+ {load_module, inets, soft_purge, soft_purge, []},
+
+ {load_module, httpd_conf, soft_purge, soft_purge, []},
+ {load_module, httpd_response, soft_purge, soft_purge, []},
+ {load_module, httpd_script_env, soft_purge, soft_purge, []},
+
{load_module, ftp, soft_purge, soft_purge, []},
{update, httpc_handler, {advanced, upgrade_from_pre_5_8_1},
soft_purge, soft_purge, []},
{update, httpc_manager, {advanced, upgrade_from_pre_5_8_1},
- soft_purge, soft_purge, [httpc_handler]}
+ soft_purge, soft_purge, [http_uri, httpc_handler]},
+ {update, httpd_sup, soft, soft_purge, soft_purge, []},
+
+ {remove, {inets_trace, soft_purge, brutal_purge}}
]
},
{"5.7.2",
[
{restart_application, inets}
]
- },
- {"5.7.1",
- [
- {restart_application, inets}
- ]
- },
- {"5.7",
- [
- {restart_application, inets}
- ]
- },
- {"5.6",
- [
- {restart_application, inets}
- ]
}
]
}.
diff --git a/lib/inets/src/inets_app/inets.erl b/lib/inets/src/inets_app/inets.erl
index 054468e445..8e79f8d456 100644
--- a/lib/inets/src/inets_app/inets.erl
+++ b/lib/inets/src/inets_app/inets.erl
@@ -28,7 +28,7 @@
stop/0, stop/2,
services/0, services_info/0,
service_names/0]).
--export([enable_trace/2, enable_trace/3, disable_trace/0, set_trace/1,
+-export([enable_trace/2, enable_trace/3, disable_trace/0, set_trace/1,
report_event/4]).
-export([versions/0,
print_version_info/0, print_version_info/1]).
@@ -409,47 +409,8 @@ service_names() ->
%% Severity withing Limit) will be written to stdout using io:format.
%%
%%-----------------------------------------------------------------
-enable_trace(Level, Dest) ->
- enable_trace(Level, Dest, all).
-
-enable_trace(Level, Dest, Service) ->
- case valid_trace_service(Service) of
- true ->
- enable_trace2(Level, Dest, Service);
- false ->
- {error, {invalid_service, Service}}
- end.
-
-enable_trace2(Level, File, Service)
- when is_list(File) ->
- case file:open(File, [write]) of
- {ok, Fd} ->
- HandleSpec = {fun handle_trace/2, {Service, Fd}},
- do_enable_trace(Level, process, HandleSpec);
- Err ->
- Err
- end;
-enable_trace2(Level, Port, _) when is_integer(Port) ->
- do_enable_trace(Level, port, dbg:trace_port(ip, Port));
-enable_trace2(Level, io, Service) ->
- HandleSpec = {fun handle_trace/2, {Service, standard_io}},
- do_enable_trace(Level, process, HandleSpec);
-enable_trace2(Level, {Fun, _Data} = HandleSpec, _) when is_function(Fun) ->
- do_enable_trace(Level, process, HandleSpec).
-
-do_enable_trace(Level, Type, HandleSpec) ->
- case dbg:tracer(Type, HandleSpec) of
- {ok, _} ->
- set_trace(Level),
- ok;
- Error ->
- Error
- end.
-
-valid_trace_service(all) ->
- true;
-valid_trace_service(Service) ->
- lists:member(Service, [httpc, httpd, ftpc, tftp]).
+enable_trace(Level, Dest) -> inets_trace:enable(Level, Dest).
+enable_trace(Level, Dest, Service) -> inets_trace:enable(Level, Dest, Service).
%%-----------------------------------------------------------------
@@ -458,12 +419,7 @@ valid_trace_service(Service) ->
%% Description:
%% This function is used to stop tracing.
%%-----------------------------------------------------------------
-disable_trace() ->
- %% This is to make handle_trace/2 close the output file (if the
- %% event gets there before dbg closes)
- inets:report_event(100, "stop trace", stop_trace, [stop_trace]),
- dbg:stop().
-
+disable_trace() -> inets_trace:disable().
%%-----------------------------------------------------------------
@@ -476,60 +432,7 @@ disable_trace() ->
%% This function is used to change the trace level when tracing has
%% already been started.
%%-----------------------------------------------------------------
-set_trace(Level) ->
- set_trace(Level, all).
-
-set_trace(Level, Service) ->
- Pat = make_pattern(?MODULE, Service, Level),
- change_pattern(Pat).
-
-make_pattern(Mod, Service, Level)
- when is_atom(Mod) andalso is_atom(Service) ->
- case Level of
- min ->
- {Mod, Service, []};
- max ->
- Head = ['$1', '_', '_', '_'],
- Body = [],
- Cond = [],
- {Mod, Service, [{Head, Cond, Body}]};
- DetailLevel when is_integer(DetailLevel) ->
- Head = ['$1', '_', '_', '_'],
- Body = [],
- Cond = [{ '=<', '$1', DetailLevel}],
- {Mod, Service, [{Head, Cond, Body}]};
- _ ->
- exit({bad_level, Level})
- end.
-
-change_pattern({Mod, Service, Pattern})
- when is_atom(Mod) andalso is_atom(Service) ->
- MFA = {Mod, report_event, 4},
- case Pattern of
- [] ->
- try
- error_to_exit(ctp, dbg:ctp(MFA)),
- error_to_exit(p, dbg:p(all, clear))
- catch
- exit:{Where, Reason} ->
- {error, {Where, Reason}}
- end;
- List when is_list(List) ->
- try
- error_to_exit(ctp, dbg:ctp(MFA)),
- error_to_exit(tp, dbg:tp(MFA, Pattern)),
- error_to_exit(p, dbg:p(all, [call, timestamp]))
- catch
- exit:{Where, Reason} ->
- {error, {Where, Reason}}
- end
- end,
- ok.
-
-error_to_exit(_Where, {ok, _} = OK) ->
- OK;
-error_to_exit(Where, {error, Reason}) ->
- exit({Where, Reason}).
+set_trace(Level) -> inets_trace:set_level(Level).
%%-----------------------------------------------------------------
@@ -546,164 +449,8 @@ error_to_exit(Where, {error, Reason}) ->
%% put trace on this function.
%%-----------------------------------------------------------------
-report_event(Severity, Label, Service, Content)
- when (is_integer(Severity) andalso
- (Severity >= 0) andalso (100 >= Severity)) andalso
- is_list(Label) andalso
- is_atom(Service) andalso
- is_list(Content) ->
- hopefully_traced.
-
-
-%% ----------------------------------------------------------------------
-%% handle_trace(Event, Fd) -> Verbosity
-%%
-%% Parameters:
-%% Event -> The trace event (only megaco 'trace_ts' events are printed)
-%% Fd -> standard_io | file_descriptor() | trace_port()
-%%
-%% Description:
-%% This function is used to "receive" and print the trace events.
-%% Events are printed if:
-%% - Verbosity is max
-%% - Severity is =< Verbosity (e.g. Severity = 30, and Verbosity = 40)
-%% Events are not printed if:
-%% - Verbosity is min
-%% - Severity is > Verbosity
-%%-----------------------------------------------------------------
-
-handle_trace(_, closed_file = Fd) ->
- Fd;
-handle_trace({trace_ts, _Who, call,
- {?MODULE, report_event,
- [_Sev, "stop trace", stop_trace, [stop_trace]]},
- Timestamp},
- {_, standard_io} = Fd) ->
- (catch io:format(standard_io, "stop trace at ~s~n", [format_timestamp(Timestamp)])),
- Fd;
-handle_trace({trace_ts, _Who, call,
- {?MODULE, report_event,
- [_Sev, "stop trace", stop_trace, [stop_trace]]},
- Timestamp},
- standard_io = Fd) ->
- (catch io:format(Fd, "stop trace at ~s~n", [format_timestamp(Timestamp)])),
- Fd;
-handle_trace({trace_ts, _Who, call,
- {?MODULE, report_event,
- [_Sev, "stop trace", stop_trace, [stop_trace]]},
- Timestamp},
- {_Service, Fd}) ->
- (catch io:format(Fd, "stop trace at ~s~n", [format_timestamp(Timestamp)])),
- (catch file:close(Fd)),
- closed_file;
-handle_trace({trace_ts, _Who, call,
- {?MODULE, report_event,
- [_Sev, "stop trace", stop_trace, [stop_trace]]},
- Timestamp},
- Fd) ->
- (catch io:format(Fd, "stop trace at ~s~n", [format_timestamp(Timestamp)])),
- (catch file:close(Fd)),
- closed_file;
-handle_trace({trace_ts, Who, call,
- {?MODULE, report_event,
- [Sev, Label, Service, Content]}, Timestamp},
- Fd) ->
- (catch print_inets_trace(Fd, Sev, Timestamp, Who,
- Label, Service, Content)),
- Fd;
-handle_trace(Event, Fd) ->
- (catch print_trace(Fd, Event)),
- Fd.
-
-
-print_inets_trace({Service, Fd},
- Sev, Timestamp, Who, Label, Service, Content) ->
- do_print_inets_trace(Fd, Sev, Timestamp, Who, Label, Service, Content);
-print_inets_trace({ServiceA, Fd},
- Sev, Timestamp, Who, Label, ServiceB, Content)
- when (ServiceA =:= all) ->
- do_print_inets_trace(Fd, Sev, Timestamp, Who, Label, ServiceB, Content);
-print_inets_trace({ServiceA, _Fd},
- _Sev, _Timestamp, _Who, _Label, ServiceB, _Content)
- when ServiceA =/= ServiceB ->
- ok;
-print_inets_trace(Fd, Sev, Timestamp, Who, Label, Service, Content) ->
- do_print_inets_trace(Fd, Sev, Timestamp, Who, Label, Service, Content).
-
-do_print_inets_trace(Fd, Sev, Timestamp, Who, Label, Service, Content) ->
- Ts = format_timestamp(Timestamp),
- io:format(Fd, "[inets ~w trace ~w ~w ~s] ~s "
- "~n Content: ~p"
- "~n",
- [Service, Sev, Who, Ts, Label, Content]).
-
-print_trace({_, Fd}, Event) ->
- do_print_trace(Fd, Event);
-print_trace(Fd, Event) ->
- do_print_trace(Fd, Event).
-
-do_print_trace(Fd, {trace, Who, What, Where}) ->
- io:format(Fd, "[trace]"
- "~n Who: ~p"
- "~n What: ~p"
- "~n Where: ~p"
- "~n", [Who, What, Where]);
-
-do_print_trace(Fd, {trace, Who, What, Where, Extra}) ->
- io:format(Fd, "[trace]"
- "~n Who: ~p"
- "~n What: ~p"
- "~n Where: ~p"
- "~n Extra: ~p"
- "~n", [Who, What, Where, Extra]);
-
-do_print_trace(Fd, {trace_ts, Who, What, Where, When}) ->
- Ts = format_timestamp(When),
- io:format(Fd, "[trace ~s]"
- "~n Who: ~p"
- "~n What: ~p"
- "~n Where: ~p"
- "~n", [Ts, Who, What, Where]);
-
-do_print_trace(Fd, {trace_ts, Who, What, Where, Extra, When}) ->
- Ts = format_timestamp(When),
- io:format(Fd, "[trace ~s]"
- "~n Who: ~p"
- "~n What: ~p"
- "~n Where: ~p"
- "~n Extra: ~p"
- "~n", [Ts, Who, What, Where, Extra]);
-
-do_print_trace(Fd, {seq_trace, What, Where}) ->
- io:format(Fd, "[seq trace]"
- "~n What: ~p"
- "~n Where: ~p"
- "~n", [What, Where]);
-
-do_print_trace(Fd, {seq_trace, What, Where, When}) ->
- Ts = format_timestamp(When),
- io:format(Fd, "[seq trace ~s]"
- "~n What: ~p"
- "~n Where: ~p"
- "~n", [Ts, What, Where]);
-
-do_print_trace(Fd, {drop, Num}) ->
- io:format(Fd, "[drop trace] ~p~n", [Num]);
-
-do_print_trace(Fd, Trace) ->
- io:format(Fd, "[trace] "
- "~n ~p"
- "~n", [Trace]).
-
-
-format_timestamp({_N1, _N2, N3} = Now) ->
- {Date, Time} = calendar:now_to_datetime(Now),
- {YYYY,MM,DD} = Date,
- {Hour,Min,Sec} = Time,
- FormatDate =
- io_lib:format("~.4w:~.2.0w:~.2.0w ~.2.0w:~.2.0w:~.2.0w 4~w",
- [YYYY,MM,DD,Hour,Min,Sec,round(N3/1000)]),
- lists:flatten(FormatDate).
+report_event(Severity, Label, Service, Content) ->
+ inets_trace:report_event(Severity, Label, Service, Content).
%%--------------------------------------------------------------------
diff --git a/lib/inets/src/inets_app/inets.mk b/lib/inets/src/inets_app/inets.mk
index 194b4ca2b1..d24cc0aea3 100644
--- a/lib/inets/src/inets_app/inets.mk
+++ b/lib/inets/src/inets_app/inets.mk
@@ -41,8 +41,6 @@ INETS_APP_VSN_COMPILE_FLAGS = \
+'{parse_transform,sys_pre_attributes}' \
+'{attribute,insert,app_vsn,$(APP_VSN)}'
-INETS_FLAGS = -D'SERVER_SOFTWARE="$(APPLICATION)/$(VSN)"'
-
INETS_ERL_COMPILE_FLAGS += \
-pa $(ERL_TOP)/lib/inets/ebin \
$(INETS_APP_VSN_COMPILE_FLAGS)
diff --git a/lib/inets/src/inets_app/inets_app.erl b/lib/inets/src/inets_app/inets_app.erl
index cae79a6767..77c39d85eb 100644
--- a/lib/inets/src/inets_app/inets_app.erl
+++ b/lib/inets/src/inets_app/inets_app.erl
@@ -24,7 +24,7 @@
-export([start/2, stop/1]).
start(_Type, _State) ->
- supervisor:start_link({local, inets_sup}, inets_sup, []).
+ inets_sup:start_link().
stop(_State) ->
ok.
diff --git a/lib/inets/src/inets_app/inets_internal.hrl b/lib/inets/src/inets_app/inets_internal.hrl
index 55c3669e4a..e56af3b59d 100644
--- a/lib/inets/src/inets_app/inets_internal.hrl
+++ b/lib/inets/src/inets_app/inets_internal.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2011. 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
@@ -24,8 +24,8 @@
%% Various trace macros
-define(report(Severity, Label, Service, Content),
- inets:report_event(Severity, Label, Service,
- [{module, ?MODULE}, {line, ?LINE} | Content])).
+ inets_trace:report_event(Severity, Label, Service,
+ [{module, ?MODULE}, {line, ?LINE} | Content])).
-define(report_important(Label, Service, Content),
?report(20, Label, Service, Content)).
-define(report_verbose(Label, Service, Content),
diff --git a/lib/inets/src/inets_app/inets_sup.erl b/lib/inets/src/inets_app/inets_sup.erl
index 20d5ef343e..0382362778 100644
--- a/lib/inets/src/inets_app/inets_sup.erl
+++ b/lib/inets/src/inets_app/inets_sup.erl
@@ -25,9 +25,19 @@
-behaviour(supervisor).
+%% External API
+-export([start_link/0]).
+
+%% Supervisor callbacks
-export([init/1]).
%%%=========================================================================
+%%% External functions
+%%%=========================================================================
+start_link() ->
+ supervisor:start_link({local, ?MODULE}, ?MODULE, []).
+
+%%%=========================================================================
%%% Supervisor callback
%%%=========================================================================
init([]) ->
diff --git a/lib/inets/src/inets_app/inets_trace.erl b/lib/inets/src/inets_app/inets_trace.erl
new file mode 100644
index 0000000000..8911f65897
--- /dev/null
+++ b/lib/inets/src/inets_app/inets_trace.erl
@@ -0,0 +1,357 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2011-2012. 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%
+%%
+%%
+%%----------------------------------------------------------------------
+%% Purpose: Module for debug trace functions of the inets application
+%%----------------------------------------------------------------------
+
+-module(inets_trace).
+
+%% API
+-export([enable/2, enable/3,
+ disable/0,
+ set_level/1, set_level/2,
+ report_event/4]).
+
+
+%%====================================================================
+%% API
+%%====================================================================
+
+%%-----------------------------------------------------------------
+%% enable(Level, Destination) -> void()
+%% enable(Level, Destination, Service) -> void()
+%%
+%% Parameters:
+%% Level -> max | min | integer()
+%% Destination -> File | Port | io | HandlerSpec
+%% Service -> httpc | httpd | ftpc | tftp | all
+%% File -> string()
+%% Port -> integer()
+%% Verbosity -> true | false
+%% HandlerSpec = {function(), Data}
+%% Data = term()
+%%
+%% Description:
+%% This function is used to start tracing at level Level and send
+%% the result either to the file File, the port Port or to a
+%% trace handler.
+%% Note that it starts a tracer server.
+%% When Destination is the atom io (or the tuple {io, Verbosity}),
+%% all (printable) inets trace events (trace_ts events which has
+%% Severity withing Limit) will be written to stdout using io:format.
+%%
+%%-----------------------------------------------------------------
+enable(Level, Dest) ->
+ enable(Level, Dest, all).
+
+enable(Level, Dest, Service) ->
+ case valid_trace_service(Service) of
+ true ->
+ enable2(Level, Dest, Service);
+ false ->
+ {error, {invalid_service, Service}}
+ end.
+
+enable2(Level, File, Service)
+ when is_list(File) ->
+ case file:open(File, [write]) of
+ {ok, Fd} ->
+ HandleSpec = {fun handle_trace/2, {Service, Fd}},
+ do_enable(Level, process, HandleSpec);
+ Err ->
+ Err
+ end;
+enable2(Level, Port, _) when is_integer(Port) ->
+ do_enable(Level, port, dbg:trace_port(ip, Port));
+enable2(Level, io, Service) ->
+ HandleSpec = {fun handle_trace/2, {Service, standard_io}},
+ do_enable(Level, process, HandleSpec);
+enable2(Level, {Fun, _Data} = HandleSpec, _) when is_function(Fun) ->
+ do_enable(Level, process, HandleSpec).
+
+do_enable(Level, Type, HandleSpec) ->
+ case dbg:tracer(Type, HandleSpec) of
+ {ok, _} ->
+ set_level(Level),
+ ok;
+ Error ->
+ Error
+ end.
+
+valid_trace_service(all) ->
+ true;
+valid_trace_service(Service) ->
+ lists:member(Service, [httpc, httpd, ftpc, tftp]).
+
+
+%%-----------------------------------------------------------------
+%% disable() -> void()
+%%
+%% Description:
+%% This function is used to stop tracing.
+%%-----------------------------------------------------------------
+
+disable() ->
+ %% This is to make handle_trace/2 close the output file (if the
+ %% event gets there before dbg closes)
+ inets_trace:report_event(100, "stop trace", stop_trace, [stop_trace]),
+ dbg:stop().
+
+
+
+%%-----------------------------------------------------------------
+%% set_level(Level) -> void()
+%%
+%% Parameters:
+%% Level -> max | min | integer()
+%%
+%% Description:
+%% This function is used to change the trace level when tracing has
+%% already been started.
+%%-----------------------------------------------------------------
+set_level(Level) ->
+ set_level(Level, all).
+
+set_level(Level, Service) ->
+ Pat = make_pattern(?MODULE, Service, Level),
+ change_pattern(Pat).
+
+make_pattern(Mod, Service, Level)
+ when is_atom(Mod) andalso is_atom(Service) ->
+ case Level of
+ min ->
+ {Mod, Service, []};
+ max ->
+ Head = ['$1', '_', '_', '_'],
+ Body = [],
+ Cond = [],
+ {Mod, Service, [{Head, Cond, Body}]};
+ DetailLevel when is_integer(DetailLevel) ->
+ Head = ['$1', '_', '_', '_'],
+ Body = [],
+ Cond = [{ '=<', '$1', DetailLevel}],
+ {Mod, Service, [{Head, Cond, Body}]};
+ _ ->
+ exit({bad_level, Level})
+ end.
+
+change_pattern({Mod, Service, Pattern})
+ when is_atom(Mod) andalso is_atom(Service) ->
+ MFA = {Mod, report_event, 4},
+ case Pattern of
+ [] ->
+ try
+ error_to_exit(ctp, dbg:ctp(MFA)),
+ error_to_exit(p, dbg:p(all, clear))
+ catch
+ exit:{Where, Reason} ->
+ {error, {Where, Reason}}
+ end;
+ List when is_list(List) ->
+ try
+ error_to_exit(ctp, dbg:ctp(MFA)),
+ error_to_exit(tp, dbg:tp(MFA, Pattern)),
+ error_to_exit(p, dbg:p(all, [call, timestamp]))
+ catch
+ exit:{Where, Reason} ->
+ {error, {Where, Reason}}
+ end
+ end.
+
+error_to_exit(_Where, {ok, _} = OK) ->
+ OK;
+error_to_exit(Where, {error, Reason}) ->
+ exit({Where, Reason}).
+
+
+%%-----------------------------------------------------------------
+%% report_event(Severity, Label, Service, Content)
+%%
+%% Parameters:
+%% Severity -> 0 =< integer() =< 100
+%% Label -> string()
+%% Service -> httpd | httpc | ftp | tftp
+%% Content -> [{tag, term()}]
+%%
+%% Description:
+%% This function is used to generate trace events, that is,
+%% put trace on this function.
+%%-----------------------------------------------------------------
+
+report_event(Severity, Label, Service, Content)
+ when (is_integer(Severity) andalso
+ (Severity >= 0) andalso (100 >= Severity)) andalso
+ is_list(Label) andalso
+ is_atom(Service) andalso
+ is_list(Content) ->
+ hopefully_traced.
+
+
+%% ----------------------------------------------------------------------
+%% handle_trace(Event, Fd) -> Verbosity
+%%
+%% Parameters:
+%% Event -> The trace event
+%% Fd -> standard_io | file_descriptor() | trace_port()
+%%
+%% Description:
+%% This function is used to "receive" and pretty print the trace events.
+%% Events are printed if:
+%% - Verbosity is max
+%% - Severity is =< Verbosity (e.g. Severity = 30, and Verbosity = 40)
+%% Events are not printed if:
+%% - Verbosity is min
+%% - Severity is > Verbosity
+%%-----------------------------------------------------------------
+
+handle_trace(_, closed_file = Fd) ->
+ Fd;
+handle_trace({trace_ts, _Who, call,
+ {?MODULE, report_event,
+ [_Sev, "stop trace", stop_trace, [stop_trace]]},
+ Timestamp},
+ {_, standard_io} = Fd) ->
+ (catch io:format(standard_io, "stop trace at ~s~n", [format_timestamp(Timestamp)])),
+ Fd;
+handle_trace({trace_ts, _Who, call,
+ {?MODULE, report_event,
+ [_Sev, "stop trace", stop_trace, [stop_trace]]},
+ Timestamp},
+ standard_io = Fd) ->
+ (catch io:format(Fd, "stop trace at ~s~n", [format_timestamp(Timestamp)])),
+ Fd;
+handle_trace({trace_ts, _Who, call,
+ {?MODULE, report_event,
+ [_Sev, "stop trace", stop_trace, [stop_trace]]},
+ Timestamp},
+ {_Service, Fd}) ->
+ (catch io:format(Fd, "stop trace at ~s~n", [format_timestamp(Timestamp)])),
+ (catch file:close(Fd)),
+ closed_file;
+handle_trace({trace_ts, _Who, call,
+ {?MODULE, report_event,
+ [_Sev, "stop trace", stop_trace, [stop_trace]]},
+ Timestamp},
+ Fd) ->
+ (catch io:format(Fd, "stop trace at ~s~n", [format_timestamp(Timestamp)])),
+ (catch file:close(Fd)),
+ closed_file;
+handle_trace({trace_ts, Who, call,
+ {?MODULE, report_event,
+ [Sev, Label, Service, Content]}, Timestamp},
+ Fd) ->
+ (catch print_inets_trace(Fd, Sev, Timestamp, Who,
+ Label, Service, Content)),
+ Fd;
+handle_trace(Event, Fd) ->
+ (catch print_trace(Fd, Event)),
+ Fd.
+
+
+print_inets_trace({Service, Fd},
+ Sev, Timestamp, Who, Label, Service, Content) ->
+ do_print_inets_trace(Fd, Sev, Timestamp, Who, Label, Service, Content);
+print_inets_trace({ServiceA, Fd},
+ Sev, Timestamp, Who, Label, ServiceB, Content)
+ when (ServiceA =:= all) ->
+ do_print_inets_trace(Fd, Sev, Timestamp, Who, Label, ServiceB, Content);
+print_inets_trace({ServiceA, _Fd},
+ _Sev, _Timestamp, _Who, _Label, ServiceB, _Content)
+ when ServiceA =/= ServiceB ->
+ ok;
+print_inets_trace(Fd, Sev, Timestamp, Who, Label, Service, Content) ->
+ do_print_inets_trace(Fd, Sev, Timestamp, Who, Label, Service, Content).
+
+do_print_inets_trace(Fd, Sev, Timestamp, Who, Label, Service, Content) ->
+ Ts = format_timestamp(Timestamp),
+ io:format(Fd, "[inets ~w trace ~w ~w ~s] ~s "
+ "~n Content: ~p"
+ "~n",
+ [Service, Sev, Who, Ts, Label, Content]).
+
+print_trace({_, Fd}, Event) ->
+ do_print_trace(Fd, Event);
+print_trace(Fd, Event) ->
+ do_print_trace(Fd, Event).
+
+do_print_trace(Fd, {trace, Who, What, Where}) ->
+ io:format(Fd, "[trace]"
+ "~n Who: ~p"
+ "~n What: ~p"
+ "~n Where: ~p"
+ "~n", [Who, What, Where]);
+
+do_print_trace(Fd, {trace, Who, What, Where, Extra}) ->
+ io:format(Fd, "[trace]"
+ "~n Who: ~p"
+ "~n What: ~p"
+ "~n Where: ~p"
+ "~n Extra: ~p"
+ "~n", [Who, What, Where, Extra]);
+
+do_print_trace(Fd, {trace_ts, Who, What, Where, When}) ->
+ Ts = format_timestamp(When),
+ io:format(Fd, "[trace ~s]"
+ "~n Who: ~p"
+ "~n What: ~p"
+ "~n Where: ~p"
+ "~n", [Ts, Who, What, Where]);
+
+do_print_trace(Fd, {trace_ts, Who, What, Where, Extra, When}) ->
+ Ts = format_timestamp(When),
+ io:format(Fd, "[trace ~s]"
+ "~n Who: ~p"
+ "~n What: ~p"
+ "~n Where: ~p"
+ "~n Extra: ~p"
+ "~n", [Ts, Who, What, Where, Extra]);
+
+do_print_trace(Fd, {seq_trace, What, Where}) ->
+ io:format(Fd, "[seq trace]"
+ "~n What: ~p"
+ "~n Where: ~p"
+ "~n", [What, Where]);
+
+do_print_trace(Fd, {seq_trace, What, Where, When}) ->
+ Ts = format_timestamp(When),
+ io:format(Fd, "[seq trace ~s]"
+ "~n What: ~p"
+ "~n Where: ~p"
+ "~n", [Ts, What, Where]);
+
+do_print_trace(Fd, {drop, Num}) ->
+ io:format(Fd, "[drop trace] ~p~n", [Num]);
+
+do_print_trace(Fd, Trace) ->
+ io:format(Fd, "[trace] "
+ "~n ~p"
+ "~n", [Trace]).
+
+
+format_timestamp({_N1, _N2, N3} = Now) ->
+ {Date, Time} = calendar:now_to_datetime(Now),
+ {YYYY,MM,DD} = Date,
+ {Hour,Min,Sec} = Time,
+ FormatDate =
+ io_lib:format("~.4w:~.2.0w:~.2.0w ~.2.0w:~.2.0w:~.2.0w 4~w",
+ [YYYY,MM,DD,Hour,Min,Sec,round(N3/1000)]),
+ lists:flatten(FormatDate).
+
+