diff options
Diffstat (limited to 'lib/inets/src')
36 files changed, 1648 insertions, 926 deletions
diff --git a/lib/inets/src/ftp/Makefile b/lib/inets/src/ftp/Makefile index 19b93870df..2c3d2b6d13 100644 --- a/lib/inets/src/ftp/Makefile +++ b/lib/inets/src/ftp/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2005-2010. All Rights Reserved. +# Copyright Ericsson AB 2005-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 @@ -88,11 +88,11 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/src/ftp - $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src/ftp - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/src/ftp" + $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) "$(RELSYSDIR)/src/ftp" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/inets/src/ftp/ftp.erl b/lib/inets/src/ftp/ftp.erl index 3028cd800f..fe25c23316 100644 --- a/lib/inets/src/ftp/ftp.erl +++ b/lib/inets/src/ftp/ftp.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 @@ -55,9 +55,10 @@ -include("ftp_internal.hrl"). %% Constante used in internal state definition --define(CONNECTION_TIMEOUT, 60*1000). --define(DEFAULT_MODE, passive). --define(PROGRESS_DEFAULT, ignore). +-define(CONNECTION_TIMEOUT, 60*1000). +-define(DATA_ACCEPT_TIMEOUT, infinity). +-define(DEFAULT_MODE, passive). +-define(PROGRESS_DEFAULT, ignore). %% Internal Constants -define(FTP_PORT, 21). @@ -88,7 +89,8 @@ %% data needed further on. caller = undefined, % term() ipfamily, % inet | inet6 | inet6fb4 - progress = ignore % ignore | pid() + progress = ignore, % ignore | pid() + dtimeout = ?DATA_ACCEPT_TIMEOUT % non_neg_integer() | infinity }). @@ -847,6 +849,7 @@ start_options(Options) -> %% host %% port %% timeout +%% dtimeout %% progress open_options(Options) -> ?fcrt("open_options", [{options, Options}]), @@ -875,7 +878,12 @@ open_options(Options) -> (_) -> false end, ValidateTimeout = - fun(Timeout) when is_integer(Timeout) andalso (Timeout > 0) -> true; + fun(Timeout) when is_integer(Timeout) andalso (Timeout >= 0) -> true; + (_) -> false + end, + ValidateDTimeout = + fun(DTimeout) when is_integer(DTimeout) andalso (DTimeout >= 0) -> true; + (infinity) -> true; (_) -> false end, ValidateProgress = @@ -893,6 +901,7 @@ open_options(Options) -> {port, ValidatePort, false, ?FTP_PORT}, {ipfamily, ValidateIpFamily, false, inet}, {timeout, ValidateTimeout, false, ?CONNECTION_TIMEOUT}, + {dtimeout, ValidateDTimeout, false, ?DATA_ACCEPT_TIMEOUT}, {progress, ValidateProgress, false, ?PROGRESS_DEFAULT}], validate_options(Options, ValidOptions, []). @@ -1037,13 +1046,15 @@ handle_call({_, {open, ip_comm, Opts}}, From, State) -> Mode = key_search(mode, Opts, ?DEFAULT_MODE), Port = key_search(port, Opts, ?FTP_PORT), Timeout = key_search(timeout, Opts, ?CONNECTION_TIMEOUT), + DTimeout = key_search(dtimeout, Opts, ?DATA_ACCEPT_TIMEOUT), Progress = key_search(progress, Opts, ignore), IpFamily = key_search(ipfamily, Opts, inet), - + State2 = State#state{client = From, mode = Mode, progress = progress(Progress), - ipfamily = IpFamily}, + ipfamily = IpFamily, + dtimeout = DTimeout}, ?fcrd("handle_call(open) -> setup ctrl connection with", [{host, Host}, {port, Port}, {timeout, Timeout}]), @@ -1064,11 +1075,13 @@ handle_call({_, {open, ip_comm, Host, Opts}}, From, State) -> Mode = key_search(mode, Opts, ?DEFAULT_MODE), Port = key_search(port, Opts, ?FTP_PORT), Timeout = key_search(timeout, Opts, ?CONNECTION_TIMEOUT), + DTimeout = key_search(dtimeout, Opts, ?DATA_ACCEPT_TIMEOUT), Progress = key_search(progress, Opts, ignore), State2 = State#state{client = From, mode = Mode, - progress = progress(Progress)}, + progress = progress(Progress), + dtimeout = DTimeout}, case setup_ctrl_connection(Host, Port, Timeout, State2) of {ok, State3, WaitTimeout} -> @@ -1657,9 +1670,19 @@ handle_ctrl_result({pos_compl, Lines}, %%-------------------------------------------------------------------------- %% Directory listing handle_ctrl_result({pos_prel, _}, #state{caller = {dir, Dir}} = State) -> - NewState = accept_data_connection(State), - activate_data_connection(NewState), - {noreply, NewState#state{caller = {handle_dir_result, Dir}}}; + case accept_data_connection(State) of + {ok, NewState} -> + activate_data_connection(NewState), + {noreply, NewState#state{caller = {handle_dir_result, Dir}}}; + {error, _Reason} = ERROR -> + case State#state.client of + undefined -> + {stop, ERROR, State}; + From -> + gen_server:reply(From, ERROR), + {stop, normal, State#state{client = undefined}} + end + end; handle_ctrl_result({pos_compl, _}, #state{caller = {handle_dir_result, Dir, Data}, client = From} @@ -1756,9 +1779,19 @@ handle_ctrl_result({Status, _}, %%-------------------------------------------------------------------------- %% File handling - recv_bin handle_ctrl_result({pos_prel, _}, #state{caller = recv_bin} = State) -> - NewState = accept_data_connection(State), - activate_data_connection(NewState), - {noreply, NewState}; + case accept_data_connection(State) of + {ok, NewState} -> + activate_data_connection(NewState), + {noreply, NewState}; + {error, _Reason} = ERROR -> + case State#state.client of + undefined -> + {stop, ERROR, State}; + From -> + gen_server:reply(From, ERROR), + {stop, normal, State#state{client = undefined}} + end + end; handle_ctrl_result({pos_compl, _}, #state{caller = {recv_bin, Data}, client = From} = State) -> @@ -1780,16 +1813,37 @@ handle_ctrl_result({Status, _}, #state{caller = {recv_bin, _}} = State) -> handle_ctrl_result({pos_prel, _}, #state{client = From, caller = start_chunk_transfer} = State) -> - NewState = accept_data_connection(State), - gen_server:reply(From, ok), - {noreply, NewState#state{chunk = true, client = undefined, - caller = undefined}}; + case accept_data_connection(State) of + {ok, NewState} -> + gen_server:reply(From, ok), + {noreply, NewState#state{chunk = true, client = undefined, + caller = undefined}}; + {error, _Reason} = ERROR -> + case State#state.client of + undefined -> + {stop, ERROR, State}; + From -> + gen_server:reply(From, ERROR), + {stop, normal, State#state{client = undefined}} + end + end; + %%-------------------------------------------------------------------------- %% File handling - recv_file handle_ctrl_result({pos_prel, _}, #state{caller = {recv_file, _}} = State) -> - NewState = accept_data_connection(State), - activate_data_connection(NewState), - {noreply, NewState}; + case accept_data_connection(State) of + {ok, NewState} -> + activate_data_connection(NewState), + {noreply, NewState}; + {error, _Reason} = ERROR -> + case State#state.client of + undefined -> + {stop, ERROR, State}; + From -> + gen_server:reply(From, ERROR), + {stop, normal, State#state{client = undefined}} + end + end; handle_ctrl_result({Status, _}, #state{caller = {recv_file, Fd}} = State) -> file_close(Fd), @@ -1800,17 +1854,38 @@ handle_ctrl_result({Status, _}, #state{caller = {recv_file, Fd}} = State) -> %% File handling - transfer_* handle_ctrl_result({pos_prel, _}, #state{caller = {transfer_file, Fd}} = State) -> - NewState = accept_data_connection(State), - send_file(Fd, NewState); + case accept_data_connection(State) of + {ok, NewState} -> + send_file(Fd, NewState); + {error, _Reason} = ERROR -> + case State#state.client of + undefined -> + {stop, ERROR, State}; + From -> + gen_server:reply(From, ERROR), + {stop, normal, State#state{client = undefined}} + end + end; handle_ctrl_result({pos_prel, _}, #state{caller = {transfer_data, Bin}} = State) -> - NewState = accept_data_connection(State), - send_data_message(NewState, Bin), - close_data_connection(NewState), - activate_ctrl_connection(NewState), - {noreply, NewState#state{caller = transfer_data_second_phase, - dsock = undefined}}; + case accept_data_connection(State) of + {ok, NewState} -> + send_data_message(NewState, Bin), + close_data_connection(NewState), + activate_ctrl_connection(NewState), + {noreply, NewState#state{caller = transfer_data_second_phase, + dsock = undefined}}; + {error, _Reason} = ERROR -> + case State#state.client of + undefined -> + {stop, ERROR, State}; + From -> + gen_server:reply(From, ERROR), + {stop, normal, State#state{client = undefined}} + end + end; + %%-------------------------------------------------------------------------- %% Default handle_ctrl_result({Status, Lines}, #state{client = From} = State) @@ -2006,16 +2081,20 @@ connect2(Host, Port, IpFam, Timeout) -> Error end. - -accept_data_connection(#state{mode = active, - dsock = {lsock, LSock}} = State) -> - {ok, Socket} = gen_tcp:accept(LSock), - gen_tcp:close(LSock), - State#state{dsock = Socket}; +accept_data_connection(#state{mode = active, + dtimeout = DTimeout, + dsock = {lsock, LSock}} = State) -> + case gen_tcp:accept(LSock, DTimeout) of + {ok, Socket} -> + gen_tcp:close(LSock), + {ok, State#state{dsock = Socket}}; + {error, Reason} -> + {error, {data_connect_failed, Reason}} + end; accept_data_connection(#state{mode = passive} = State) -> - State. + {ok, State}. send_ctrl_message(#state{csock = Socket, verbose = Verbose}, Message) -> %% io:format("send control message: ~n~p~n", [lists:flatten(Message)]), diff --git a/lib/inets/src/http_client/Makefile b/lib/inets/src/http_client/Makefile index 0397b48ab2..f0d4ce139e 100644 --- a/lib/inets/src/http_client/Makefile +++ b/lib/inets/src/http_client/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2005-2010. All Rights Reserved. +# Copyright Ericsson AB 2005-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 @@ -41,7 +41,6 @@ RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN) # Target Specs # ---------------------------------------------------- MODULES = \ - http \ httpc \ httpc_cookie \ httpc_handler \ @@ -90,11 +89,11 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/src/http_client - $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src/http_client - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/src/http_client" + $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) "$(RELSYSDIR)/src/http_client" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/inets/src/http_client/http.erl b/lib/inets/src/http_client/http.erl deleted file mode 100644 index bbe2fec267..0000000000 --- a/lib/inets/src/http_client/http.erl +++ /dev/null @@ -1,132 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2002-2010. 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% -%% -%% - -%%% Description: OLD API MODULE - USE httpc INSTEAD - --module(http). - --deprecated({request, 1, next_major_release}). --deprecated({request, 2, next_major_release}). --deprecated({request, 4, next_major_release}). --deprecated({request, 5, next_major_release}). --deprecated({cancel_request, 1, next_major_release}). --deprecated({cancel_request, 2, next_major_release}). --deprecated({set_option, 2, next_major_release}). --deprecated({set_option, 3, next_major_release}). --deprecated({set_options, 1, next_major_release}). --deprecated({set_options, 2, next_major_release}). --deprecated({verify_cookies, 2, next_major_release}). --deprecated({verify_cookies, 3, next_major_release}). --deprecated({cookie_header, 1, next_major_release}). --deprecated({cookie_header, 2, next_major_release}). --deprecated({stream_next, 1, next_major_release}). --deprecated({default_profile, 0, next_major_release}). - -%% Deprecated --export([ - request/1, request/2, request/4, request/5, - cancel_request/1, cancel_request/2, - set_option/2, set_option/3, - set_options/1, set_options/2, - verify_cookies/2, verify_cookies/3, - cookie_header/1, cookie_header/2, - stream_next/1, - default_profile/0 - ]). - - -%%%========================================================================= -%%% API -%%%========================================================================= - -%%-------------------------------------------------------------------------- -%% request(Url [, Profile]) -> -%% request(Method, Request, HTTPOptions, Options [, Profile]) -%%-------------------------------------------------------------------------- - -request(Url) -> httpc:request(Url). -request(Url, Profile) -> httpc:request(Url, Profile). - -request(Method, Request, HttpOptions, Options) -> - httpc:request(Method, Request, HttpOptions, Options). -request(Method, Request, HttpOptions, Options, Profile) -> - httpc:request(Method, Request, HttpOptions, Options, Profile). - - -%%-------------------------------------------------------------------------- -%% cancel_request(RequestId [, Profile]) -%%------------------------------------------------------------------------- - -cancel_request(RequestId) -> - httpc:cancel_request(RequestId). -cancel_request(RequestId, Profile) -> - httpc:cancel_request(RequestId, Profile). - - -%%-------------------------------------------------------------------------- -%% set_options(Options [, Profile]) -%% set_option(Key, Value [, Profile]) -%%------------------------------------------------------------------------- - -set_options(Options) -> - httpc:set_options(Options). -set_options(Options, Profile) -> - httpc:set_options(Options, Profile). - -set_option(Key, Value) -> - httpc:set_option(Key, Value). -set_option(Key, Value, Profile) -> - httpc:set_option(Key, Value, Profile). - - -%%-------------------------------------------------------------------------- -%% verify_cookies(SetCookieHeaders, Url [, Profile]) -%%------------------------------------------------------------------------- - -verify_cookies(SetCookieHeaders, Url) -> - httpc:store_cookies(SetCookieHeaders, Url). -verify_cookies(SetCookieHeaders, Url, Profile) -> - httpc:store_cookies(SetCookieHeaders, Url, Profile). - - -%%-------------------------------------------------------------------------- -%% cookie_header(Url [, Profile]) -%%------------------------------------------------------------------------- - -cookie_header(Url) -> - httpc:cookie_header(Url). -cookie_header(Url, Profile) -> - httpc:cookie_header(Url, Profile). - - -%%-------------------------------------------------------------------------- -%% stream_next(Pid) -%%------------------------------------------------------------------------- - -stream_next(Pid) -> - httpc:stream_next(Pid). - - -%%-------------------------------------------------------------------------- -%% default_profile() -%%------------------------------------------------------------------------- - -default_profile() -> - httpc:default_profile(). diff --git a/lib/inets/src/http_client/httpc.erl b/lib/inets/src/http_client/httpc.erl index fe8e93af1f..b6e7708353 100644 --- a/lib/inets/src/http_client/httpc.erl +++ b/lib/inets/src/http_client/httpc.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009-2011. All Rights Reserved. +%% Copyright Ericsson AB 2009-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 @@ -31,12 +31,15 @@ -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/1, cookie_header/2, cookie_header/3, which_cookies/0, which_cookies/1, reset_cookies/0, reset_cookies/1, + which_sessions/0, which_sessions/1, stream_next/1, default_profile/0, profile_name/1, profile_name/2, @@ -105,7 +108,6 @@ request(Url, Profile) -> %% {ssl, SSLOptions} | {proxy_auth, {User, Password}} %% Ssloptions = ssl_options() | %% {ssl, ssl_options()} | -%% {ossl, ssl_options()} | %% {essl, ssl_options()} %% ssl_options() = [ssl_option()] %% ssl_option() = {verify, code()} | @@ -142,7 +144,9 @@ request(Url, Profile) -> request(Method, Request, HttpOptions, Options) -> request(Method, Request, HttpOptions, Options, default_profile()). -request(Method, {Url, Headers}, HTTPOptions, Options, Profile) +request(Method, + {Url, Headers}, + HTTPOptions, Options, Profile) when (Method =:= options) orelse (Method =:= get) orelse (Method =:= head) orelse @@ -155,15 +159,17 @@ request(Method, {Url, Headers}, HTTPOptions, Options, Profile) {http_options, HTTPOptions}, {options, Options}, {profile, Profile}]), - case http_uri:parse(Url) of + case uri_parse(Url, Options) of {error, Reason} -> {error, Reason}; - ParsedUrl -> + {ok, ParsedUrl} -> handle_request(Method, Url, ParsedUrl, Headers, [], [], HTTPOptions, Options, Profile) end; -request(Method, {Url,Headers,ContentType,Body}, HTTPOptions, Options, Profile) +request(Method, + {Url, Headers, ContentType, Body}, + HTTPOptions, Options, Profile) when ((Method =:= post) orelse (Method =:= put)) andalso (is_atom(Profile) orelse is_pid(Profile)) -> ?hcrt("request", [{method, Method}, @@ -174,10 +180,10 @@ request(Method, {Url,Headers,ContentType,Body}, HTTPOptions, Options, Profile) {http_options, HTTPOptions}, {options, Options}, {profile, Profile}]), - case http_uri:parse(Url) of + case uri_parse(Url, Options) of {error, Reason} -> {error, Reason}; - ParsedUrl -> + {ok, ParsedUrl} -> handle_request(Method, Url, ParsedUrl, Headers, ContentType, Body, HTTPOptions, Options, Profile) @@ -227,7 +233,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 @@ -250,6 +256,59 @@ 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} %% %% @@ -268,7 +327,10 @@ store_cookies(SetCookieHeaders, Url, Profile) {profile, Profile}]), try begin - {_, _, Host, Port, Path, _} = http_uri:parse(Url), + %% 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, _}} = uri_parse(Url), Address = {Host, Port}, ProfileName = profile_name(Profile), Cookies = httpc_cookie:cookies(SetCookieHeaders, Path, Host), @@ -284,26 +346,35 @@ store_cookies(SetCookieHeaders, Url, Profile) %%-------------------------------------------------------------------------- -%% cookie_header(Url [, Profile]) -> Header | {error, Reason} -%% +%% cookie_header(Url) -> Header | {error, Reason} +%% cookie_header(Url, Profile) -> Header | {error, Reason} +%% cookie_header(Url, Opts, Profile) -> Header | {error, Reason} +%% %% Description: Returns the cookie header that would be sent when making %% a request to <Url>. %%------------------------------------------------------------------------- cookie_header(Url) -> cookie_header(Url, default_profile()). -cookie_header(Url, Profile) -> +cookie_header(Url, Profile) when is_atom(Profile) orelse is_pid(Profile) -> + cookie_header(Url, [], Profile); +cookie_header(Url, Opts) when is_list(Opts) -> + cookie_header(Url, Opts, default_profile()). + +cookie_header(Url, Opts, Profile) + when (is_list(Opts) andalso (is_atom(Profile) orelse is_pid(Profile))) -> ?hcrt("cookie header", [{url, Url}, + {opts, Opts}, {profile, Profile}]), try begin - httpc_manager:which_cookies(Url, profile_name(Profile)) + httpc_manager:which_cookies(Url, Opts, profile_name(Profile)) end catch exit:{noproc, _} -> {error, {not_started, Profile}} end. - + %%-------------------------------------------------------------------------- %% which_cookies() -> [cookie()] @@ -327,10 +398,32 @@ which_cookies(Profile) -> %%-------------------------------------------------------------------------- +%% which_sessions() -> {GoodSession, BadSessions, NonSessions} +%% which_sessions(Profile) -> {GoodSession, BadSessions, NonSessions} +%% +%% Description: Debug function, dumping the sessions database, sorted +%% into three groups (Good-, Bad- and Non-sessions). +%%------------------------------------------------------------------------- +which_sessions() -> + which_sessions(default_profile()). + +which_sessions(Profile) -> + ?hcrt("which sessions", [{profile, Profile}]), + try + begin + httpc_manager:which_sessions(profile_name(Profile)) + end + catch + exit:{noproc, _} -> + {[], [], []} + end. + + +%%-------------------------------------------------------------------------- %% 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()). @@ -465,6 +558,8 @@ handle_request(Method, Url, HeadersRecord = header_record(NewHeaders, Host2, HTTPOptions), Receiver = proplists:get_value(receiver, Options), SocketOpts = proplists:get_value(socket_opts, Options), + BracketedHost = proplists:get_value(ipv6_host_with_brackets, + Options), MaybeEscPath = maybe_encode_uri(HTTPOptions, Path), MaybeEscQuery = maybe_encode_uri(HTTPOptions, Query), AbsUri = maybe_encode_uri(HTTPOptions, Url), @@ -483,7 +578,8 @@ handle_request(Method, Url, stream = Stream, headers_as_is = headers_as_is(Headers0, Options), socket_opts = SocketOpts, - started = Started}, + started = Started, + ipv6_host_with_brackets = BracketedHost}, case httpc_manager:request(Request, profile_name(Profile)) of {ok, RequestId} -> @@ -644,8 +740,6 @@ http_options_default() -> {ok, {?HTTP_DEFAULT_SSL_KIND, Value}}; ({ssl, SslOptions}) when is_list(SslOptions) -> {ok, {?HTTP_DEFAULT_SSL_KIND, SslOptions}}; - ({ossl, SslOptions}) when is_list(SslOptions) -> - {ok, {ossl, SslOptions}}; ({essl, SslOptions}) when is_list(SslOptions) -> {ok, {essl, SslOptions}}; (_) -> @@ -742,14 +836,17 @@ request_options_defaults() -> error end, + VerifyBrackets = VerifyBoolean, + [ - {sync, true, VerifySync}, - {stream, none, VerifyStream}, - {body_format, string, VerifyBodyFormat}, - {full_result, true, VerifyFullResult}, - {headers_as_is, false, VerifyHeaderAsIs}, - {receiver, self(), VerifyReceiver}, - {socket_opts, undefined, VerifySocketOpts} + {sync, true, VerifySync}, + {stream, none, VerifyStream}, + {body_format, string, VerifyBodyFormat}, + {full_result, true, VerifyFullResult}, + {headers_as_is, false, VerifyHeaderAsIs}, + {receiver, self(), VerifyReceiver}, + {socket_opts, undefined, VerifySocketOpts}, + {ipv6_host_with_brackets, false, VerifyBrackets} ]. request_options(Options) -> @@ -1123,6 +1220,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 7784060a2b..923213d34d 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 @@ -55,7 +55,7 @@ status_line, % {Version, StatusCode, ReasonPharse} headers, % #http_response_h{} body, % binary() - mfa, % {Moduel, Function, Args} + mfa, % {Module, Function, Args} pipeline = queue:new(), % queue() keep_alive = queue:new(), % queue() status, % undefined | new | pipeline | keep_alive | close | ssl_tunnel @@ -763,7 +763,7 @@ deliver_answer(Request) -> code_change(_, #state{session = OldSession, profile_name = ProfileName} = State, - upgrade_from_pre_5_7_3) -> + upgrade_from_pre_5_8_1) -> case OldSession of {session, Id, ClientClose, Scheme, Socket, SocketType, QueueLen, Type} -> @@ -783,7 +783,7 @@ code_change(_, code_change(_, #state{session = OldSession, profile_name = ProfileName} = State, - downgrade_to_pre_5_7_3) -> + downgrade_to_pre_5_8_1) -> case OldSession of #session{id = Id, client_close = ClientClose, @@ -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) -> @@ -1704,7 +1713,32 @@ update_session(ProfileName, #session{id = SessionId} = Session, Pos, Value) -> catch error:undef -> % This could happen during code upgrade Session2 = erlang:setelement(Pos, Session, Value), - insert_session(Session2, ProfileName) + insert_session(Session2, ProfileName); + T:E -> + error_logger:error_msg("Failed updating session: " + "~n ProfileName: ~p" + "~n SessionId: ~p" + "~n Pos: ~p" + "~n Value: ~p" + "~nwhen" + "~n Session (db) info: ~p" + "~n Session (db): ~p" + "~n Session (record): ~p" + "~n T: ~p" + "~n E: ~p", + [ProfileName, SessionId, Pos, Value, + (catch httpc_manager:which_session_info(ProfileName)), + Session, + (catch httpc_manager:lookup_session(ProfileName, SessionId)), + T, E]), + exit({failed_updating_session, + [{profile, ProfileName}, + {session_id, SessionId}, + {pos, Pos}, + {value, Value}, + {etype, T}, + {error, E}, + {stacktrace, erlang:get_stacktrace()}]}) end. diff --git a/lib/inets/src/http_client/httpc_internal.hrl b/lib/inets/src/http_client/httpc_internal.hrl index 3261061d61..8af752546c 100644 --- a/lib/inets/src/http_client/httpc_internal.hrl +++ b/lib/inets/src/http_client/httpc_internal.hrl @@ -90,25 +90,28 @@ %%% All data associated to a specific HTTP request -record(request, { - id, % ref() - Request Id - from, % pid() - Caller - redircount = 0,% Number of redirects made for this request - scheme, % http | https - address, % ({Host,Port}) Destination Host and Port - path, % string() - Path of parsed URL - pquery, % string() - Rest of parsed URL - method, % atom() - HTTP request Method - headers, % #http_request_h{} - content, % {ContentType, Body} - Current HTTP request - settings, % #http_options{} - User defined settings - abs_uri, % string() ex: "http://www.erlang.org" - userinfo, % string() - optinal "<userinfo>@<host>:<port>" - stream, % Boolean() - stream async reply? - headers_as_is, % Boolean() - workaround for servers that does - % not honor the http standard, can also be used for testing purposes. - started, % integer() > 0 - When we started processing the request - timer, % undefined | ref() - socket_opts % undefined | [socket_option()] + id, % ref() - Request Id + from, % pid() - Caller + redircount = 0,% Number of redirects made for this request + scheme, % http | https + address, % ({Host,Port}) Destination Host and Port + path, % string() - Path of parsed URL + pquery, % string() - Rest of parsed URL + method, % atom() - HTTP request Method + headers, % #http_request_h{} + content, % {ContentType, Body} - Current HTTP request + settings, % #http_options{} - User defined settings + abs_uri, % string() ex: "http://www.erlang.org" + userinfo, % string() - optinal "<userinfo>@<host>:<port>" + stream, % boolean() - stream async reply? + headers_as_is, % boolean() - workaround for servers that does + % not honor the http standard, can also be used + % for testing purposes. + started, % integer() > 0 - When we started processing the + % request + timer, % undefined | ref() + socket_opts, % undefined | [socket_option()] + ipv6_host_with_brackets % boolean() } ). diff --git a/lib/inets/src/http_client/httpc_manager.erl b/lib/inets/src/http_client/httpc_manager.erl index 3d846c2bff..3612b331e7 100644 --- a/lib/inets/src/http_client/httpc_manager.erl +++ b/lib/inets/src/http_client/httpc_manager.erl @@ -34,11 +34,15 @@ retry_request/2, redirect_request/2, insert_session/2, + lookup_session/2, update_session/4, delete_session/2, + which_sessions/1, + which_session_info/1, set_options/2, + get_options/2, store_cookies/3, - which_cookies/1, which_cookies/2, + which_cookies/1, which_cookies/2, which_cookies/3, reset_cookies/1, session_type/1, info/1 @@ -58,17 +62,9 @@ options = #options{} }). --record(handler_info, - { - id, % Id of the request: request_id() - starter, % Pid of the handler starter process (temp): pid() - handler, % Pid of the handler process: pid() - from, % From for the request: from() - state % State of the handler: initiating | started | operational | canceled - }). - -define(DELAY, 500). + %%==================================================================== %% Internal Application API %%==================================================================== @@ -194,13 +190,28 @@ insert_session(Session, ProfileName) -> %%-------------------------------------------------------------------- +%% Function: lookup_session(SessionId, ProfileName) -> _ +%% SessionId - term() +%% ProfileName - atom() +%% +%% Description: Looks up a session record in the httpc manager +%% table <ProfileName>__session_db. +%%-------------------------------------------------------------------- + +lookup_session(SessionId, ProfileName) -> + SessionDbName = session_db_name(ProfileName), + ?hcrt("lookup session", [{session_id, SessionId}, {profile, ProfileName}]), + ets:lookup(SessionDbName, SessionId). + + +%%-------------------------------------------------------------------- %% Function: update_session(ProfileName, SessionId, Pos, Value) -> _ %% Session - #session{} %% ProfileName - atom() %% %% Description: Update, only one field (Pos) of the session record %% identified by the SessionId, the session information -%% of the httpc manager table <ProfileName>_session_db. +%% of the httpc manager table <ProfileName>__session_db. %% Intended to be called by the httpc request handler process. %%-------------------------------------------------------------------- @@ -215,12 +226,12 @@ update_session(ProfileName, SessionId, Pos, Value) -> %%-------------------------------------------------------------------- -%% Function: delete_session(SessionId, ProfileName) -> _ +%% Function: delete_session(SessionId, ProfileName) -> void() %% SessionId - {{Host, Port}, HandlerPid} %% ProfileName - atom() %% %% Description: Deletes session information from the httpc manager -%% table httpc_manager_session_db_<Profile>. Intended to be called by +%% table <ProfileName>__session_db. Intended to be called by %% the httpc request handler process. %%-------------------------------------------------------------------- @@ -231,6 +242,57 @@ delete_session(SessionId, ProfileName) -> %%-------------------------------------------------------------------- +%% Function: which sessions(ProfileName) -> SessionsInfo +%% ProfileName - atom() +%% SessionsInfo - {GoodSessions, BadSessions, NonSessions} +%% GoodSessions - [#session{}] +%% BadSessions - [tuple()] +%% NonSessions - [term()] +%% +%% Description: Produces a list of all sessions in the session db. +%% Used for debugging and since that is the intent, there is some +%% checking and transforming done, which produces the results. +%%-------------------------------------------------------------------- + +which_sessions(ProfileName) -> + ?hcrt("which_sessions", [{profile, ProfileName}]), + SessionDbName = session_db_name(ProfileName), + which_sessions2(SessionDbName). + +which_sessions2(SessionDbName) -> + Sessions = which_sessions_order(ets:tab2list(SessionDbName)), + GoodSessions = [GoodSession || {good_session, GoodSession} <- Sessions], + BadSessions = [BadSession || {bad_session, BadSession} <- Sessions], + NonSessions = [NonSession || {non_session, NonSession} <- Sessions], + {lists:keysort(#session.id, GoodSessions), + lists:keysort(#session.id, BadSessions), + lists:sort(NonSessions)}. + +which_sessions_order([]) -> + []; +which_sessions_order([Session|Sessions]) when is_record(Session, session) -> + [{good_session, Session} | which_sessions_order(Sessions)]; +which_sessions_order([BadSession|Sessions]) + when is_tuple(BadSession) andalso + (element(1, BadSession) =:= session) -> + [{bad_session, BadSession} | which_sessions_order(Sessions)]; +which_sessions_order([NonSession|Sessions]) -> + [{non_session, NonSession} | which_sessions_order(Sessions)]. + + +%%-------------------------------------------------------------------- +%% Function: which session_info(ProfileName) -> list() +%% +%% Description: Produces a ets table info list of the sessions table +%%-------------------------------------------------------------------- + +which_session_info(ProfileName) -> + SessionDbName = session_db_name(ProfileName), + ?hcrt("which_session_info", [{profile, ProfileName}]), + ets:info(SessionDbName). + + +%%-------------------------------------------------------------------- %% Function: set_options(Options, ProfileName) -> ok %% %% Options = [Option] @@ -250,6 +312,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] @@ -278,19 +355,29 @@ reset_cookies(ProfileName) -> %%-------------------------------------------------------------------- -%% Function: which_cookies(Url, ProfileName) -> [cookie()] +%% Function: which_cookies(ProfileName) -> [cookie()] +%% which_cookies(Url, ProfileName) -> [cookie()] +%% which_cookies(Url, Options, ProfileName) -> [cookie()] %% %% Url = string() +%% Options = [option()] %% ProfileName = atom() +%% option() = {ipv6_host_with_brackets, boolean()} %% %% Description: Retrieves the cookies that would be sent when %% requesting <Url>. %%-------------------------------------------------------------------- -which_cookies(ProfileName) -> +which_cookies(ProfileName) when is_atom(ProfileName) -> call(ProfileName, which_cookies). -which_cookies(Url, ProfileName) -> - call(ProfileName, {which_cookies, Url}). + +which_cookies(Url, ProfileName) + when is_list(Url) andalso is_atom(ProfileName) -> + which_cookies(Url, [], ProfileName). + +which_cookies(Url, Options, ProfileName) + when is_list(Url) andalso is_list(Options) andalso is_atom(ProfileName) -> + call(ProfileName, {which_cookies, Url, Options}). %%-------------------------------------------------------------------- @@ -353,8 +440,7 @@ do_init(ProfileName, CookiesDir) -> %% Create handler db ?hcrt("create handler/request db", []), HandlerDbName = handler_db_name(ProfileName), - ets:new(HandlerDbName, - [protected, set, named_table, {keypos, #handler_info.id}]), + ets:new(HandlerDbName, [protected, set, named_table, {keypos, 1}]), %% Cookie DB ?hcrt("create cookie db", []), @@ -388,9 +474,10 @@ handle_call({request, Request}, _, State) -> {stop, Error, httpc_response:error(Request, Error), State} end; -handle_call({cancel_request, RequestId}, From, State) -> +handle_call({cancel_request, RequestId}, From, + #state{handler_db = HandlerDb} = State) -> ?hcri("cancel_request", [{request_id, RequestId}]), - case ets:lookup(State#state.handler_db, RequestId) of + case ets:lookup(HandlerDb, RequestId) of [] -> %% The request has allready compleated make sure %% it is deliverd to the client process queue so @@ -402,9 +489,9 @@ handle_call({cancel_request, RequestId}, From, State) -> {noreply, State}; [{_, Pid, _}] -> httpc_handler:cancel(RequestId, Pid, From), - {noreply, State#state{cancel = - [{RequestId, Pid, From} | - State#state.cancel]}} + {noreply, + State#state{cancel = + [{RequestId, Pid, From} | State#state.cancel]}} end; handle_call(reset_cookies, _, #state{cookie_db = CookieDb} = State) -> @@ -417,17 +504,24 @@ handle_call(which_cookies, _, #state{cookie_db = CookieDb} = State) -> CookieHeaders = httpc_cookie:which_cookies(CookieDb), {reply, CookieHeaders, State}; -handle_call({which_cookies, Url}, _, #state{cookie_db = CookieDb} = State) -> - ?hcrv("which cookies", [{url, Url}]), - case http_uri:parse(Url) of - {Scheme, _, Host, Port, Path, _} -> +handle_call({which_cookies, Url, Options}, _, + #state{cookie_db = CookieDb} = State) -> + ?hcrv("which cookies", [{url, Url}, {options, Options}]), + case uri_parse(Url, Options) of + {ok, {Scheme, _, Host, Port, Path, _}} -> CookieHeaders = httpc_cookie:header(CookieDb, Scheme, {Host, Port}, Path), {reply, CookieHeaders, State}; - Msg -> - {reply, Msg, State} + {error, _} = ERROR -> + {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), @@ -572,7 +666,7 @@ terminate(_, State) -> %%-------------------------------------------------------------------- code_change(_, #state{session_db = SessionDB} = State, - upgrade_from_pre_5_7_3) -> + upgrade_from_pre_5_8_1) -> Upgrade = fun({session, Id, ClientClose, Scheme, Socket, SocketType, QueueLen, Type}) -> @@ -591,7 +685,7 @@ code_change(_, code_change(_, #state{session_db = SessionDB} = State, - downgrade_to_pre_5_7_3) -> + downgrade_to_pre_5_8_1) -> Downgrade = fun(#session{id = Id, client_close = ClientClose, @@ -612,7 +706,7 @@ code_change(_, code_change(_, State, _) -> {ok, State}. -%% This function is to catch everything that calls through the cracks... +%% This function is used to catch everything that falls through the cracks... update_session_table(SessionDB, Transform) -> ets:safe_fixtable(SessionDB, true), update_session_table(SessionDB, ets:first(SessionDB), Transform), @@ -640,40 +734,56 @@ update_session_table(SessionDB, Key, Transform) -> %%-------------------------------------------------------------------- get_manager_info(#state{handler_db = HDB, - cookie_db = CDB} = _State) -> + session_db = SDB, + cookie_db = CDB, + options = Options} = _State) -> HandlerInfo = get_handler_info(HDB), + SessionInfo = which_sessions2(SDB), + OptionsInfo = + [{Item, get_option(Item, Options)} || + Item <- record_info(fields, options)], CookieInfo = httpc_cookie:which_cookies(CDB), - [{handlers, HandlerInfo}, {cookies, CookieInfo}]. + [{handlers, HandlerInfo}, + {sessions, SessionInfo}, + {options, OptionsInfo}, + {cookies, CookieInfo}]. + +sort_handlers(Unsorted) -> + sort_handlers2(lists:keysort(1, Unsorted)). + +sort_handlers2([]) -> + []; +sort_handlers2([{HandlerPid, RequestId}|L]) -> + {Handler, Rest} = sort_handlers2(HandlerPid, [RequestId], L), + [Handler | sort_handlers2(Rest)]. + +sort_handlers2(HandlerPid, Reqs, []) -> + {{HandlerPid, lists:sort(Reqs)}, []}; +sort_handlers2(HandlerPid, Reqs, [{HandlerPid, ReqId}|Rest]) -> + sort_handlers2(HandlerPid, [ReqId|Reqs], Rest); +sort_handlers2(HandlerPid1, Reqs, [{HandlerPid2, _}|_] = Rest) + when HandlerPid1 =/= HandlerPid2 -> + {{HandlerPid1, lists:sort(Reqs)}, Rest}. get_handler_info(Tab) -> - Pattern = #handler_info{handler = '$1', - state = '$2', - _ = '_'}, - Handlers1 = [{Pid, State} || [Pid, State] <- ets:match(Tab, Pattern)], - F = fun({Pid, State} = Elem, Acc) when State =/= canceled -> - case lists:keymember(Pid, 1, Acc) of - true -> - Acc; - false -> - [Elem | Acc] - end; - (_, Acc) -> - Acc - end, - Handlers2 = lists:foldl(F, [], Handlers1), - Handlers3 = [{Pid, State, - case (catch httpc_handler:info(Pid)) of - {'EXIT', _} -> + Pattern = {'$2', '$1', '_'}, + Handlers1 = [{Pid, Id} || [Pid, Id] <- ets:match(Tab, Pattern)], + Handlers2 = sort_handlers(Handlers1), + Handlers3 = [{Pid, Reqs, + try + begin + httpc_handler:info(Pid) + end + catch + _:_ -> %% Why would this crash? %% Only if the process has died, but we don't %% know about it? - []; - Else -> - Else - end} || - {Pid, State} <- Handlers2], + [] + end} || {Pid, Reqs} <- Handlers2], Handlers3. + handle_request(#request{settings = #http_options{version = "HTTP/0.9"}} = Request, State) -> @@ -725,19 +835,21 @@ handle_request(Request, State = #state{options = Options}) -> {reply, {ok, NewRequest#request.id}, State}. -start_handler(Request, State) -> +start_handler(#request{id = Id, + from = From} = Request, + #state{profile_name = ProfileName, + handler_db = HandlerDb, + options = Options}) -> {ok, Pid} = case is_inets_manager() of true -> httpc_handler_sup:start_child([whereis(httpc_handler_sup), - Request, State#state.options, - State#state.profile_name]); + Request, Options, ProfileName]); false -> - httpc_handler:start_link(self(), Request, State#state.options, - State#state.profile_name) + httpc_handler:start_link(self(), Request, Options, ProfileName) end, - ets:insert(State#state.handler_db, {Request#request.id, - Pid, Request#request.from}), + HandlerInfo = {Id, Pid, From}, + ets:insert(HandlerDb, HandlerInfo), erlang:monitor(process, Pid). @@ -794,12 +906,14 @@ select_session(Candidates, Max) -> {ok, HandlerPid} end. -pipeline_or_keep_alive(Request, HandlerPid, State) -> +pipeline_or_keep_alive(#request{id = Id, + from = From} = Request, + HandlerPid, + #state{handler_db = HandlerDb} = State) -> case (catch httpc_handler:send(Request, HandlerPid)) of ok -> - ets:insert(State#state.handler_db, {Request#request.id, - HandlerPid, - Request#request.from}); + HandlerInfo = {Id, HandlerPid, From}, + ets:insert(HandlerDb, HandlerInfo); _ -> % timeout pipelining failed start_handler(Request, State) end. @@ -861,6 +975,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, @@ -872,6 +999,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 207b96271c..04976447cc 100644 --- a/lib/inets/src/http_client/httpc_response.erl +++ b/lib/inets/src/http_client/httpc_response.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2010. 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 @@ -340,7 +340,9 @@ redirect(Response = {StatusLine, Headers, Body}, Request) -> undefined -> transparent(Response, Request); RedirUrl -> - case http_uri:parse(RedirUrl) of + UrlParseOpts = [{ipv6_host_with_brackets, + Request#request.ipv6_host_with_brackets}], + case uri_parse(RedirUrl, UrlParseOpts) of {error, no_scheme} when (Request#request.settings)#http_options.relaxed -> NewLocation = fix_relative_uri(Request, RedirUrl), @@ -350,10 +352,9 @@ redirect(Response = {StatusLine, Headers, Body}, Request) -> {error, Reason} -> {ok, error(Request, Reason), Data}; %% Automatic redirection - {Scheme, _, Host, Port, Path, Query} -> + {ok, {Scheme, _, Host, Port, Path, Query}} -> NewHeaders = - (Request#request.headers)#http_request_h{host = - Host}, + (Request#request.headers)#http_request_h{host = Host}, NewRequest = Request#request{redircount = Request#request.redircount+1, @@ -436,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/Makefile b/lib/inets/src/http_lib/Makefile index aaf3cfb995..51167b34fa 100644 --- a/lib/inets/src/http_lib/Makefile +++ b/lib/inets/src/http_lib/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2005-2010. All Rights Reserved. +# Copyright Ericsson AB 2005-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 @@ -87,11 +87,11 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/src/http_lib - $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src/http_lib - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/src/http_lib" + $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) "$(RELSYSDIR)/src/http_lib" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/inets/src/http_lib/http_internal.hrl b/lib/inets/src/http_lib/http_internal.hrl index 2e924667c6..97cf474ab9 100644 --- a/lib/inets/src/http_lib/http_internal.hrl +++ b/lib/inets/src/http_lib/http_internal.hrl @@ -28,7 +28,6 @@ -define(HTTP_MAX_URI_SIZE, nolimit). -ifndef(HTTP_DEFAULT_SSL_KIND). -%% -define(HTTP_DEFAULT_SSL_KIND, ossl). -define(HTTP_DEFAULT_SSL_KIND, essl). -endif. % -ifdef(HTTP_DEFAULT_SSL_KIND). diff --git a/lib/inets/src/http_lib/http_transport.erl b/lib/inets/src/http_lib/http_transport.erl index 9b8190ebed..5eb827032f 100644 --- a/lib/inets/src/http_lib/http_transport.erl +++ b/lib/inets/src/http_lib/http_transport.erl @@ -62,8 +62,6 @@ start(ip_comm) -> %% This is just for backward compatibillity start({ssl, _}) -> do_start_ssl(); -start({ossl, _}) -> - do_start_ssl(); start({essl, _}) -> do_start_ssl(). @@ -126,22 +124,6 @@ connect(ip_comm = _SocketType, {Host, Port}, Opts0, Timeout) connect({ssl, SslConfig}, Address, Opts, Timeout) -> connect({?HTTP_DEFAULT_SSL_KIND, SslConfig}, Address, Opts, Timeout); -connect({ossl, SslConfig}, {Host, Port}, _, Timeout) -> - Opts = [binary, {active, false}, {ssl_imp, old}] ++ SslConfig, - ?hlrt("connect using ossl", - [{host, Host}, - {port, Port}, - {ssl_config, SslConfig}, - {timeout, Timeout}]), - case (catch ssl:connect(Host, Port, Opts, Timeout)) of - {'EXIT', Reason} -> - {error, {eoptions, Reason}}; - {ok, _} = OK -> - OK; - {error, _} = ERROR -> - ERROR - end; - connect({essl, SslConfig}, {Host, Port}, Opts0, Timeout) -> Opts = [binary, {active, false}, {ssl_imp, new} | Opts0] ++ SslConfig, ?hlrt("connect using essl", @@ -187,13 +169,6 @@ listen({ssl, SSLConfig}, Addr, Port) -> {ssl_config, SSLConfig}]), listen({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Addr, Port); -listen({ossl, SSLConfig}, Addr, Port) -> - ?hlrt("listen (ossl)", - [{addr, Addr}, - {port, Port}, - {ssl_config, SSLConfig}]), - listen_ssl(Addr, Port, [{ssl_imp, old} | SSLConfig]); - listen({essl, SSLConfig}, Addr, Port) -> ?hlrt("listen (essl)", [{addr, Addr}, @@ -353,8 +328,6 @@ accept(ip_comm, ListenSocket, Timeout) -> accept({ssl, SSLConfig}, ListenSocket, Timeout) -> accept({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, ListenSocket, Timeout); -accept({ossl, _SSLConfig}, ListenSocket, Timeout) -> - ssl:transport_accept(ListenSocket, Timeout); accept({essl, _SSLConfig}, ListenSocket, Timeout) -> ssl:transport_accept(ListenSocket, Timeout). @@ -374,9 +347,6 @@ controlling_process(ip_comm, Socket, NewOwner) -> controlling_process({ssl, SSLConfig}, Socket, NewOwner) -> controlling_process({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket, NewOwner); -controlling_process({ossl, _}, Socket, NewOwner) -> - ssl:controlling_process(Socket, NewOwner); - controlling_process({essl, _}, Socket, NewOwner) -> ssl:controlling_process(Socket, NewOwner). @@ -397,13 +367,6 @@ setopts(ip_comm, Socket, Options) -> setopts({ssl, SSLConfig}, Socket, Options) -> setopts({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket, Options); -setopts({ossl, _}, Socket, Options) -> - ?hlrt("[o]ssl setopts", [{socket, Socket}, {options, Options}]), - Reason = (catch ssl:setopts(Socket, Options)), - ?hlrt("[o]ssl setopts result", [{reason, Reason}]), - Reason; - - setopts({essl, _}, Socket, Options) -> ?hlrt("[e]ssl setopts", [{socket, Socket}, {options, Options}]), Reason = (catch ssl:setopts(Socket, Options)), @@ -435,10 +398,6 @@ getopts(ip_comm, Socket, Options) -> getopts({ssl, SSLConfig}, Socket, Options) -> getopts({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket, Options); -getopts({ossl, _}, Socket, Options) -> - ?hlrt("ssl getopts", [{socket, Socket}, {options, Options}]), - getopts_ssl(Socket, Options); - getopts({essl, _}, Socket, Options) -> ?hlrt("essl getopts", [{socket, Socket}, {options, Options}]), getopts_ssl(Socket, Options). @@ -472,9 +431,6 @@ getstat(ip_comm = _SocketType, Socket) -> getstat({ssl, SSLConfig}, Socket) -> getstat({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket); -getstat({ossl, _} = _SocketType, _Socket) -> - []; - getstat({essl, _} = _SocketType, _Socket) -> []. @@ -493,9 +449,6 @@ send(ip_comm, Socket, Message) -> send({ssl, SSLConfig}, Socket, Message) -> send({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket, Message); -send({ossl, _}, Socket, Message) -> - ssl:send(Socket, Message); - send({essl, _}, Socket, Message) -> ssl:send(Socket, Message). @@ -514,9 +467,6 @@ close(ip_comm, Socket) -> close({ssl, SSLConfig}, Socket) -> close({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket); -close({ossl, _}, Socket) -> - ssl:close(Socket); - close({essl, _}, Socket) -> ssl:close(Socket). @@ -538,9 +488,6 @@ peername(ip_comm, Socket) -> peername({ssl, SSLConfig}, Socket) -> peername({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket); -peername({ossl, _}, Socket) -> - do_peername(ssl:peername(Socket)); - peername({essl, _}, Socket) -> do_peername(ssl:peername(Socket)). @@ -573,9 +520,6 @@ sockname(ip_comm, Socket) -> sockname({ssl, SSLConfig}, Socket) -> sockname({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket); -sockname({ossl, _}, Socket) -> - do_sockname(ssl:sockname(Socket)); - sockname({essl, _}, Socket) -> do_sockname(ssl:sockname(Socket)). @@ -651,9 +595,6 @@ negotiate(ip_comm,_,_) -> negotiate({ssl, SSLConfig}, Socket, Timeout) -> ?hlrt("negotiate(ssl)", []), negotiate({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket, Timeout); -negotiate({ossl, _}, Socket, Timeout) -> - ?hlrt("negotiate(ossl)", []), - negotiate_ssl(Socket, Timeout); negotiate({essl, _}, Socket, Timeout) -> ?hlrt("negotiate(essl)", []), negotiate_ssl(Socket, Timeout). diff --git a/lib/inets/src/http_lib/http_uri.erl b/lib/inets/src/http_lib/http_uri.erl index 607475c359..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 @@ -16,35 +16,96 @@ %% %% %CopyrightEnd% %% -%% +%% +%% 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]). --export([encode/1, decode/1]). +-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) -> - case parse_scheme(AbsURI) of + parse(AbsURI, []). + +parse(AbsURI, Opts) -> + case parse_scheme(AbsURI, Opts) of {error, Reason} -> {error, Reason}; - {Scheme, Rest} -> - case (catch parse_uri_rest(Scheme, Rest)) of - {UserInfo, Host, Port, Path, Query} -> - {Scheme, 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) -> @@ -62,21 +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; 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) -> - +parse_uri_rest(Scheme, DefaultPort, "//" ++ URIPart, Opts) -> {Authority, PathQuery} = case split_uri(URIPart, "/", URIPart, 1, 0) of Split = {_, _} -> @@ -89,25 +160,25 @@ parse_uri_rest(Scheme, "//" ++ URIPart) -> {URIPart,""} end end, - {UserInfo, HostPort} = split_uri(Authority, "@", {"", Authority}, 1, 1), - {Host, Port} = parse_host_port(Scheme, HostPort), - {Path, Query} = parse_path_query(PathQuery), - {UserInfo, Host, Port, Path, Query}. + {Host, Port} = parse_host_port(Scheme, DefaultPort, HostPort, Opts), + {Path, Query} = parse_path_query(PathQuery), + {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) -> %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), - {Host, int_port(Port)}; + {Host2, int_port(Port)}; -parse_host_port(Scheme, HostPort) -> - DefaultPort = default_port(Scheme), +parse_host_port(_Scheme, DefaultPort, HostPort, _Opts) -> {Host, Port} = split_uri(HostPort, ":", {HostPort, DefaultPort}, 1, 1), {Host, int_port(Port)}. @@ -120,15 +191,22 @@ split_uri(UriPart, SplitChar, NoMatchResult, SkipLeft, SkipRight) -> NoMatchResult end. -default_port(http) -> - 80; -default_port(https) -> - 443. +maybe_ipv6_host_with_brackets(Host, Opts) -> + case lists:keysearch(ipv6_host_with_brackets, 1, Opts) of + {value, {ipv6_host_with_brackets, true}} -> + "[" ++ Host ++ "]"; + _ -> + Host + end. + 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..67555d5f1c 100644 --- a/lib/inets/src/http_server/Makefile +++ b/lib/inets/src/http_server/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2005-2010. All Rights Reserved. +# Copyright Ericsson AB 2005-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 @@ -88,6 +88,8 @@ ERL_FILES = $(MODULES:%=%.erl) TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) +INETS_FLAGS = -D'SERVER_SOFTWARE="$(APPLICATION)/$(VSN)"' + # ---------------------------------------------------- # FLAGS @@ -121,11 +123,11 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/src/http_server - $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src/http_server - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/src/http_server" + $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) "$(RELSYSDIR)/src/http_server" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/inets/src/http_server/httpd_acceptor.erl b/lib/inets/src/http_server/httpd_acceptor.erl index bcebb6a9e3..08ee9ee0d0 100644 --- a/lib/inets/src/http_server/httpd_acceptor.erl +++ b/lib/inets/src/http_server/httpd_acceptor.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2010. All Rights Reserved. +%% Copyright Ericsson AB 2001-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 @@ -139,11 +139,11 @@ acceptor_loop(Manager, SocketType, ListenSocket, ConfigDb, AcceptTimeout) -> handle_error(Reason, ConfigDb), ?MODULE:acceptor_loop(Manager, SocketType, ListenSocket, ConfigDb, AcceptTimeout); - {'EXIT', _Reason} = EXIT -> - ?hdri("accept exited", [{reason, _Reason}]), - handle_error(EXIT, ConfigDb), - ?MODULE:acceptor_loop(Manager, SocketType, ListenSocket, - ConfigDb, AcceptTimeout) + {'EXIT', Reason} -> + ?hdri("accept exited", [{reason, Reason}]), + ReasonString = + lists:flatten(io_lib:format("Accept exit: ~p", [Reason])), + accept_failed(ConfigDb, ReasonString) end. @@ -189,15 +189,13 @@ handle_error(esslaccept, _) -> %% not write an error message. ok; -handle_error({'EXIT', Reason}, ConfigDb) -> - String = lists:flatten(io_lib:format("Accept exit: ~p", [Reason])), - accept_failed(ConfigDb, String); - handle_error(Reason, ConfigDb) -> String = lists:flatten(io_lib:format("Accept error: ~p", [Reason])), accept_failed(ConfigDb, String). --spec accept_failed(_, string()) -> no_return(). + +-spec accept_failed(ConfigDB :: term(), + ReasonString :: string()) -> no_return(). accept_failed(ConfigDb, String) -> error_logger:error_report(String), diff --git a/lib/inets/src/http_server/httpd_conf.erl b/lib/inets/src/http_server/httpd_conf.erl index 5352eb8bb9..884e3defb8 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,18 +210,37 @@ 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 - %% ossl is ssl based on OpenSSL (the "old" ssl) %% essl is the pure Erlang-based ssl (the "new" ssl) - case check_enum(clean(SocketType), ["ssl", "ossl", "essl", "ip_comm"]) of + case check_enum(clean(SocketType), ["ssl", "essl", "ip_comm"]) of {ok, ValidSocketType} -> {ok, [], {socket_type, ValidSocketType}}; {error,_} -> @@ -464,7 +483,7 @@ validate_properties(Properties) -> case mandatory_properties(Properties) of ok -> %% Second, check that property dependency are ok - {ok, validate_properties2(Properties)}; + {ok, check_minimum_bytes_per_second(validate_properties2(Properties))}; Error -> throw(Error) end. @@ -476,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 -> @@ -503,7 +522,18 @@ validate_properties2(Properties) -> throw(Error) end end. - +check_minimum_bytes_per_second(Properties) -> + case proplists:get_value(minimum_bytes_per_second, Properties, false) of + false -> + Properties; + Nr -> + case is_integer(Nr) of + false -> + throw({error, {minimum_bytes_per_second, is_not_integer}}); + _ -> + Properties + end + end. mandatory_properties(ConfigList) -> a_must(ConfigList, [server_name, port, server_root, document_root]). @@ -538,10 +568,23 @@ 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 - (Value =:= ossl) orelse (Value =:= essl) -> validate_config_params(Rest); validate_config_params([{socket_type, Value} | _]) -> @@ -739,9 +782,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,[]), @@ -811,7 +918,7 @@ lookup_socket_type(ConfigDB) -> case httpd_util:lookup(ConfigDB, socket_type, ip_comm) of ip_comm -> ip_comm; - SSL when (SSL =:= ssl) orelse (SSL =:= ossl) orelse (SSL =:= essl) -> + SSL when (SSL =:= ssl) orelse (SSL =:= essl) -> SSLTag = if (SSL =:= ssl) -> @@ -1161,6 +1268,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_log.erl b/lib/inets/src/http_server/httpd_log.erl index db1e2c627a..a34435e0e8 100644 --- a/lib/inets/src/http_server/httpd_log.erl +++ b/lib/inets/src/http_server/httpd_log.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2011. All Rights Reserved. +%% Copyright Ericsson AB 2008-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 @@ -24,68 +24,110 @@ -export([access_entry/8, error_entry/5, error_report_entry/5, security_entry/5]). + %%%========================================================================= %%% Internal Application API %%%========================================================================= -access_entry(Log, NoLog, Info, RFC931, AuthUser, Date, StatusCode, Bytes) -> - ConfigDB = Info#mod.config_db, - case httpd_util:lookup(ConfigDB, Log) of - undefined -> - NoLog; - LogRef -> - {_, RemoteHost} - = (Info#mod.init_data)#init_data.peername, - RequestLine = Info#mod.request_line, - Headers = Info#mod.parsed_header, - Entry = do_access_entry(ConfigDB, Headers, RequestLine, - RemoteHost, RFC931, AuthUser, - Date, StatusCode, Bytes), - {LogRef, Entry} - end. -error_entry(Log, NoLog, Info, Date, Reason) -> - ConfigDB = Info#mod.config_db, - case httpd_util:lookup(ConfigDB, Log) of - undefined -> - NoLog; - LogRef -> - {_, RemoteHost} = - (Info#mod.init_data)#init_data.peername, - URI = Info#mod.request_uri, - Entry = do_error_entry(ConfigDB, RemoteHost, URI, Date, Reason), - {LogRef, Entry} - end. +-spec access_entry(Log :: term(), % Id of the log + NoLog :: term(), % What to return when no log is found + Info :: #mod{}, + RFC931 :: string(), + AuthUser :: string(), + Date :: string(), + StatusCode :: pos_integer(), + Size :: 0 | pos_integer() | string()) -> + {Log :: atom() | pid(), Entry :: string()} | term() . + +access_entry(Log, NoLog, Info, RFC931, AuthUser, Date, StatusCode, SizeStr) + when is_list(SizeStr) -> + Size = + case (catch list_to_integer(SizeStr)) of + I when is_integer(I) -> + I; + _ -> + SizeStr % This is better then nothing + end, + access_entry(Log, NoLog, Info, RFC931, AuthUser, Date, StatusCode, Size); +access_entry(Log, NoLog, + #mod{config_db = ConfigDB, + init_data = #init_data{peername = {_, RemoteHost}}, + request_line = RequestLine, + parsed_header = Headers}, + RFC931, AuthUser, Date, StatusCode, Size) -> + MakeEntry = + fun() -> + do_access_entry(ConfigDB, Headers, RequestLine, + RemoteHost, RFC931, AuthUser, + Date, StatusCode, Size) + end, + log_entry(Log, NoLog, ConfigDB, MakeEntry). + + +-spec error_entry(Log :: term(), % Id of the log + NoLog :: term(), % What to return when no log is found + Info :: #mod{}, + Date :: string(), + Reason :: term()) -> + {Log :: atom() | pid(), Entry :: string()} | term(). + +error_entry(Log, NoLog, + #mod{config_db = ConfigDB, + init_data = #init_data{peername = {_, RemoteHost}}, + request_uri = URI}, Date, Reason) -> + MakeEntry = + fun() -> + do_error_entry(ConfigDB, RemoteHost, URI, Date, Reason) + end, + log_entry(Log, NoLog, ConfigDB, MakeEntry). + + +-spec error_report_entry(Log :: term(), + NoLog :: term(), + ConfigDB :: term(), + Date :: string(), + ErrroStr :: string()) -> + {Log :: atom() | pid(), Entry :: string()} | term(). error_report_entry(Log, NoLog, ConfigDb, Date, ErrorStr) -> - case httpd_util:lookup(ConfigDb, Log) of - undefined -> - NoLog; - LogRef -> - Entry = io_lib:format("[~s], ~s~n", [Date, ErrorStr]), - {LogRef, Entry} - end. + MakeEntry = fun() -> io_lib:format("[~s], ~s~n", [Date, ErrorStr]) end, + log_entry(Log, NoLog, ConfigDb, MakeEntry). + -security_entry(Log, NoLog, #mod{config_db = ConfigDb}, Date, Reason) -> +-spec security_entry(Log :: term(), + NoLog :: term(), + ConfigDB :: term(), + Date :: string(), + Reason :: term()) -> + {Log :: atom() | pid(), Entry :: string()} | term(). + +security_entry(Log, NoLog, #mod{config_db = ConfigDB}, Date, Reason) -> + MakeEntry = fun() -> io_lib:format("[~s] ~s~n", [Date, Reason]) end, + log_entry(Log, NoLog, ConfigDB, MakeEntry). + + +log_entry(Log, NoLog, ConfigDb, MakeEntry) when is_function(MakeEntry) -> case httpd_util:lookup(ConfigDb, Log) of undefined -> NoLog; LogRef -> - Entry = io_lib:format("[~s] ~s~n", [Date, Reason]), - {LogRef, Entry} + {LogRef, MakeEntry()} end. - + + %%%======================================================================== %%% Internal functions %%%======================================================================== + do_access_entry(ConfigDB, Headers, RequestLine, - RemoteHost, RFC931, AuthUser, Date, StatusCode, - Bytes) -> + RemoteHost, RFC931, AuthUser, Date, StatusCode, + Size) -> case httpd_util:lookup(ConfigDB, log_format, common) of common -> lists:flatten(io_lib:format("~s ~s ~s [~s] \"~s\" ~w ~w~n", [RemoteHost, RFC931, AuthUser, Date, RequestLine, - StatusCode, Bytes])); + StatusCode, Size])); combined -> Referer = proplists:get_value("referer", Headers, "-"), @@ -94,7 +136,7 @@ do_access_entry(ConfigDB, Headers, RequestLine, Headers, "-"), io_lib:format("~s ~s ~s [~s] \"~s\" ~w ~w ~s ~s~n", [RemoteHost, RFC931, AuthUser, Date, - RequestLine, StatusCode, Bytes, + RequestLine, StatusCode, Size, Referer, UserAgent]) end. diff --git a/lib/inets/src/http_server/httpd_request_handler.erl b/lib/inets/src/http_server/httpd_request_handler.erl index d2f22fce93..0f47d785ef 100644 --- a/lib/inets/src/http_server/httpd_request_handler.erl +++ b/lib/inets/src/http_server/httpd_request_handler.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 @@ -44,7 +44,9 @@ timeout, %% infinity | integer() > 0 timer, %% ref() - Request timer headers, %% #http_request_h{} - body %% binary() + body, %% binary() + data, %% The total data received in bits, checked after 10s + byte_limit %% Bit limit per second before kick out }). %%==================================================================== @@ -98,7 +100,6 @@ init([Manager, ConfigDB, AcceptTimeout]) -> [{socket_type, SocketType}, {socket, Socket}]), TimeOut = httpd_util:lookup(ConfigDB, keep_alive_timeout, 150000), - Then = erlang:now(), ?hdrd("negotiate", []), @@ -139,12 +140,11 @@ continue_init(Manager, ConfigDB, SocketType, Socket, TimeOut) -> mfa = MFA}, ?hdrt("activate request timeout", []), - NewState = activate_request_timeout(State), ?hdrt("set socket options (binary, packet & active)", []), http_transport:setopts(SocketType, Socket, [binary, {packet, 0}, {active, once}]), - + NewState = data_receive_counter(activate_request_timeout(State), httpd_util:lookup(ConfigDB, minimum_bytes_per_second, false)), ?hdrt("init done", []), gen_server:enter_loop(?MODULE, [], NewState). @@ -162,7 +162,14 @@ continue_init(Manager, ConfigDB, SocketType, Socket, TimeOut) -> %% {stop, Reason, State} %% Description: Handling call messages %%-------------------------------------------------------------------- -handle_call(Request, From, State) -> +handle_call(Request, From, #state{mod = ModData} = State) -> + Error = + lists:flatten( + io_lib:format("Unexpected request: " + "~n~p" + "~nto request handler (~p) from ~p" + "~n", [Request, self(), From])), + error_log(Error, ModData), {stop, {call_api_violation, Request, From}, State}. %%-------------------------------------------------------------------- @@ -171,8 +178,15 @@ handle_call(Request, From, State) -> %% {stop, Reason, State} %% Description: Handling cast messages %%-------------------------------------------------------------------- -handle_cast(Msg, State) -> - {reply, {cast_api_violation, Msg}, State}. +handle_cast(Msg, #state{mod = ModData} = State) -> + Error = + lists:flatten( + io_lib:format("Unexpected message: " + "~n~p" + "~nto request handler (~p)" + "~n", [Msg, self()])), + error_log(Error, ModData), + {noreply, State}. %%-------------------------------------------------------------------- %% handle_info(Info, State) -> {noreply, State} | @@ -191,16 +205,25 @@ handle_info({Proto, Socket, Data}, ?hdrd("received data", [{data, Data}, {proto, Proto}, {socket, Socket}, {socket_type, SockType}, {mfa, MFA}]), - + %% case (catch Module:Function([Data | Args])) of PROCESSED = (catch Module:Function([Data | Args])), - + NewDataSize = case State#state.byte_limit of + undefined -> + undefined; + _ -> + State#state.data + byte_size(Data) + end, ?hdrt("data processed", [{processing_result, PROCESSED}]), - case PROCESSED of {ok, Result} -> ?hdrd("data processed", [{result, Result}]), - NewState = cancel_request_timeout(State), + NewState = case NewDataSize of + undefined -> + cancel_request_timeout(State); + _ -> + set_new_data_size(cancel_request_timeout(State), NewDataSize) + end, handle_http_msg(Result, NewState); {error, {uri_too_long, MaxSize}, Version} -> @@ -225,7 +248,12 @@ handle_info({Proto, Socket, Data}, NewMFA -> ?hdrd("data processed - reactivate socket", [{new_mfa, NewMFA}]), http_transport:setopts(SockType, Socket, [{active, once}]), - {noreply, State#state{mfa = NewMFA}} + case NewDataSize of + undefined -> + {noreply, State#state{mfa = NewMFA}}; + _ -> + {noreply, State#state{mfa = NewMFA, data = NewDataSize}} + end end; %% Error cases @@ -249,11 +277,29 @@ handle_info(timeout, #state{mod = ModData} = State) -> error_log("The client did not send the whole request before the " "server side timeout", ModData), {stop, normal, State#state{response_sent = true}}; - +handle_info(check_data_first, #state{data = Data, byte_limit = Byte_Limit} = State) -> + case Data >= (Byte_Limit*3) of + true -> + erlang:send_after(1000, self(), check_data), + {noreply, State#state{data = 0}}; + _ -> + {stop, normal, State#state{response_sent = true}} + end; +handle_info(check_data, #state{data = Data, byte_limit = Byte_Limit} = State) -> + case Data >= Byte_Limit of + true -> + erlang:send_after(1000, self(), check_data), + {noreply, State#state{data = 0}}; + _ -> + {stop, normal, State#state{response_sent = true}} + end; %% Default case handle_info(Info, #state{mod = ModData} = State) -> Error = lists:flatten( - io_lib:format("Unexpected message received: ~n~p~n", [Info])), + io_lib:format("Unexpected info: " + "~n~p" + "~nto request handler (~p)" + "~n", [Info, self()])), error_log(Error, ModData), {noreply, State}. @@ -294,6 +340,8 @@ code_change(_OldVsn, State, _Extra) -> %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- +set_new_data_size(State, NewData) -> + State#state{data = NewData}. await_socket_ownership_transfer(AcceptTimeout) -> receive {socket_ownership_transfered, SocketType, Socket} -> @@ -586,7 +634,14 @@ activate_request_timeout(#state{timeout = Time} = State) -> ?hdrt("activate request timeout", [{time, Time}]), Ref = erlang:send_after(Time, self(), timeout), State#state{timer = Ref}. - +data_receive_counter(State, Byte_limit) -> + case Byte_limit of + false -> + State#state{data = 0}; + Nr -> + erlang:send_after(3000, self(), check_data_first), + State#state{data = 0, byte_limit = Nr} + end. cancel_request_timeout(#state{timer = undefined} = State) -> State; cancel_request_timeout(#state{timer = Timer} = State) -> 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..ac79a8cc63 100644 --- a/lib/inets/src/http_server/httpd_script_env.erl +++ b/lib/inets/src/http_server/httpd_script_env.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2010. All Rights Reserved. +%% Copyright Ericsson AB 2005-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 @@ -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..758985f330 100644 --- a/lib/inets/src/http_server/mod_get.erl +++ b/lib/inets/src/http_server/mod_get.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2010. 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 @@ -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/http_server/mod_log.erl b/lib/inets/src/http_server/mod_log.erl index c8a2ec0dc4..a912f5616c 100644 --- a/lib/inets/src/http_server/mod_log.erl +++ b/lib/inets/src/http_server/mod_log.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2010. All Rights Reserved. +%% Copyright Ericsson AB 1997-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 @@ -100,7 +100,7 @@ do(Info) -> transfer_log(Info,"-",AuthUser,Date,StatusCode,Size), {proceed,Info#mod.data}; {response, Head, _Body} -> - Size = proplists:get_value(content_length,Head,unknown), + Size = content_length(Head), Code = proplists:get_value(code,Head,unknown), transfer_log(Info, "-", AuthUser, Date, Code, Size), {proceed, Info#mod.data}; @@ -254,4 +254,10 @@ auth_user(Data) -> RemoteUser end. - +content_length(Head) -> + case proplists:get_value(content_length, Head) of + undefined -> + unknown; + Size -> + list_to_integer(Size) + end. diff --git a/lib/inets/src/http_server/mod_responsecontrol.erl b/lib/inets/src/http_server/mod_responsecontrol.erl index 989f45db20..6af5f6211e 100644 --- a/lib/inets/src/http_server/mod_responsecontrol.erl +++ b/lib/inets/src/http_server/mod_responsecontrol.erl @@ -211,7 +211,7 @@ compare_etags(Tag,Etags) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% -%% Control if the file is modificated %% +%% Control if the file is modified %% %% %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/lib/inets/src/inets_app/Makefile b/lib/inets/src/inets_app/Makefile index 20e22917e2..7d68145287 100644 --- a/lib/inets/src/inets_app/Makefile +++ b/lib/inets/src/inets_app/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2005-2010. All Rights Reserved. +# Copyright Ericsson AB 2005-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 @@ -30,6 +30,7 @@ include ../../vsn.mk VSN = $(INETS_VSN) + # ---------------------------------------------------- # Release directory specification # ---------------------------------------------------- @@ -41,11 +42,12 @@ RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN) # ---------------------------------------------------- MODULES = \ - inets_service \ inets \ + inets_service \ inets_app \ inets_sup \ - inets_regexp + inets_regexp \ + inets_trace INTERNAL_HRL_FILES = inets_internal.hrl EXTERNAL_HRL_FILES = ../../include/httpd.hrl \ @@ -109,13 +111,13 @@ $(APPUP_TARGET): $(APPUP_SRC) ../../vsn.mk include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/src/inets_app - $(INSTALL_DATA) $(INTERNAL_HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src/inets_app - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(EXTERNAL_HRL_FILES) $(RELSYSDIR)/include - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/src/inets_app" + $(INSTALL_DATA) $(INTERNAL_HRL_FILES) $(ERL_FILES) "$(RELSYSDIR)/src/inets_app" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(EXTERNAL_HRL_FILES) "$(RELSYSDIR)/include" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/inets/src/inets_app/inets.app.src b/lib/inets/src/inets_app/inets.app.src index cb036157a5..4aea2ef3d7 100644 --- a/lib/inets/src/inets_app/inets.app.src +++ b/lib/inets/src/inets_app/inets.app.src @@ -1,7 +1,7 @@ %% This is an -*- erlang -*- file. %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2010. All Rights Reserved. +%% Copyright Ericsson AB 1997-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 @@ -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, @@ -34,8 +35,7 @@ ftp_sup, %% HTTP client: - http, %% Old client API module - httpc, %% New client API module + httpc, httpc_handler, httpc_handler_sup, httpc_manager, diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src index 84217eac78..ffd0ed622f 100644 --- a/lib/inets/src/inets_app/inets.appup.src +++ b/lib/inets/src/inets_app/inets.appup.src @@ -18,91 +18,109 @@ {"%VSN%", [ - {"5.7.2", + {"5.9.1", [ - {load_module, ftp, soft_purge, soft_purge, []}, - {update, httpc_handler, {advanced, upgrade_from_pre_5_7_3}, - soft_purge, soft_purge, []}, - {update, httpc_manager, {advanced, upgrade_from_pre_5_7_3}, - soft_purge, soft_purge, [httpc_handler]} + {load_module, httpd_request_handler, soft_purge, soft_purge, []} + ] + }, + {"5.9", + [ + {load_module, httpd_request_handler, soft_purge, soft_purge, []}, + {load_module, tftp, soft_purge, soft_purge, [inets_service]}, + {load_module, inets_service, soft_purge, soft_purge, []}, + {load_module, httpc, soft_purge, soft_purge, [httpc_manager]}, + {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_manager]}, + {update, httpc_manager, soft, soft_purge, soft_purge, []} ] }, - {"5.7.1", + {"5.8.1", [ - {load_module, ftp, soft_purge, soft_purge, []}, - {load_module, http_uri, soft_purge, soft_purge, []}, - {load_module, http_util, soft_purge, soft_purge, []}, - {load_module, httpd_util, soft_purge, soft_purge, [http_util]}, - {load_module, httpd_file, soft_purge, soft_purge, []}, - {load_module, httpd_request, soft_purge, soft_purge, []}, - {load_module, mod_responsecontrol, soft_purge, soft_purge, []}, - {load_module, httpd_response, soft_purge, soft_purge, [mod_responsecontrol]}, - {update, httpc_handler, {advanced, upgrade_from_pre_5_7_3}, - soft_purge, soft_purge, []}, - {update, httpc_manager, {advanced, upgrade_from_pre_5_7_3}, - soft_purge, soft_purge, [httpc_handler]} + {load_module, httpd_request_handler, soft_purge, soft_purge, []}, + {load_module, tftp, soft_purge, soft_purge, [inets_service]}, + {load_module, inets_service, soft_purge, soft_purge, []}, + + {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, 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_manager, soft, soft_purge, soft_purge, [http_uri]}, + {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_manager]}, + {update, httpd_sup, soft, soft_purge, soft_purge, []}, + {add_module, inets_trace} ] }, - {"5.7", + {"5.8", [ - {load_module, ftp, soft_purge, soft_purge, []}, - {load_module, http_uri, soft_purge, soft_purge, []}, - {load_module, httpd_util, soft_purge, soft_purge, [http_util]}, - {load_module, httpd_file, soft_purge, soft_purge, []}, - {load_module, httpd_request, soft_purge, soft_purge, []}, - {load_module, httpc_cookie, soft_purge, soft_purge, [http_util]}, - {load_module, http_util, soft_purge, soft_purge, []}, - {load_module, mod_responsecontrol, soft_purge, soft_purge, []}, - {load_module, httpd_response, soft_purge, soft_purge, [mod_responsecontrol]}, - {update, httpc_handler, {advanced, upgrade_from_pre_5_7_3}, - soft_purge, soft_purge, []}, - {update, httpc_manager, {advanced, upgrade_from_pre_5_7_3}, - soft_purge, soft_purge, [httpc_handler]} + {restart_application, inets} + ] + }, + {"5.7.2", + [ + {restart_application, inets} ] } ], [ - {"5.7.2", + {"5.9.1", + [ + {load_module, httpd_request_handler, soft_purge, soft_purge, []} + ] + }, + {"5.9", [ - {load_module, ftp, soft_purge, soft_purge, []}, - {update, httpc_handler, {advanced, downgrade_to_pre_5_7_3}, - soft_purge, soft_purge, []}, - {update, httpc_manager, {advanced, downgrade_to_pre_5_7_3}, - soft_purge, soft_purge, [httpc_handler]} + {load_module, httpd_request_handler, soft_purge, soft_purge, []}, + {load_module, tftp, soft_purge, soft_purge, [inets_service]}, + {load_module, inets_service, soft_purge, soft_purge, []}, + {load_module, httpc, soft_purge, soft_purge, [httpc_manager]}, + {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_manager]}, + {update, httpc_manager, soft, soft_purge, soft_purge, []} ] }, - {"5.7.1", + {"5.8.1", [ - {load_module, ftp, soft_purge, soft_purge, []}, - {load_module, http_uri, soft_purge, soft_purge, []}, - {load_module, http_util, soft_purge, soft_purge, []}, - {load_module, httpd_util, soft_purge, soft_purge, [http_util]}, - {load_module, httpd_file, soft_purge, soft_purge, []}, - {load_module, httpd_request, soft_purge, soft_purge, []}, - {load_module, mod_responsecontrol, soft_purge, soft_purge, []}, - {load_module, httpd_response, soft_purge, soft_purge, [mod_responsecontrol]}, - {update, httpc_handler, {advanced, downgrade_to_pre_5_7_3}, - soft_purge, soft_purge, []}, - {update, httpc_manager, {advanced, downgrade_to_pre_5_7_3}, - soft_purge, soft_purge, [httpc_handler]} + {load_module, httpd_request_handler, soft_purge, soft_purge, []}, + {load_module, tftp, soft_purge, soft_purge, [inets_service]}, + {load_module, inets_service, soft_purge, soft_purge, []}, + + {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, 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_manager, soft, soft_purge, soft_purge, [http_uri]}, + {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_manager]}, + {update, httpd_sup, soft, soft_purge, soft_purge, []}, + {remove, {inets_trace, soft_purge, brutal_purge}} ] }, - {"5.7", - [ - {load_module, ftp, soft_purge, soft_purge, []}, - {load_module, http_uri, soft_purge, soft_purge, []}, - {load_module, httpd_util, soft_purge, soft_purge, [http_util]}, - {load_module, httpd_file, soft_purge, soft_purge, []}, - {load_module, httpd_request, soft_purge, soft_purge, []}, - {load_module, httpc_cookie, soft_purge, soft_purge, [http_util]}, - {load_module, http_util, soft_purge, soft_purge, []}, - {load_module, mod_responsecontrol, soft_purge, soft_purge, []}, - {load_module, httpd_response, soft_purge, soft_purge, [mod_responsecontrol]}, - {update, httpc_handler, {advanced, downgrade_to_pre_5_7_3}, - soft_purge, soft_purge, []}, - {update, httpc_manager, {advanced, downgrade_to_pre_5_7_3}, - soft_purge, soft_purge, [httpc_handler]} + {"5.8", + [ + {restart_application, inets} ] - } + }, + {"5.7.2", + [ + {restart_application, inets} + ] + } ] }. diff --git a/lib/inets/src/inets_app/inets.erl b/lib/inets/src/inets_app/inets.erl index 054468e445..f33e0abe27 100644 --- a/lib/inets/src/inets_app/inets.erl +++ b/lib/inets/src/inets_app/inets.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2010. 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 @@ -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 b6e9fe1d96..adef32dc19 100644 --- a/lib/inets/src/inets_app/inets.mk +++ b/lib/inets/src/inets_app/inets.mk @@ -2,7 +2,7 @@ # %CopyrightBegin% # -# Copyright Ericsson AB 2010. All Rights Reserved. +# Copyright Ericsson AB 2010-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 @@ -37,8 +37,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..ee3359c4c3 100644 --- a/lib/inets/src/inets_app/inets_app.erl +++ b/lib/inets/src/inets_app/inets_app.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2009. 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 @@ -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_service.erl b/lib/inets/src/inets_app/inets_service.erl index e9eb9892f2..d17fdfe13e 100644 --- a/lib/inets/src/inets_app/inets_service.erl +++ b/lib/inets/src/inets_app/inets_service.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2010. All Rights Reserved. +%% Copyright Ericsson AB 2007-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 @@ -20,24 +20,21 @@ -module(inets_service). --export([behaviour_info/1]). - -behaviour_info(callbacks) -> - [{start_standalone, 1}, - {start_service, 1}, - {stop_service, 1}, - {services, 0}, - {service_info, 1}]; -behaviour_info(_) -> - undefined. %% Starts service stand-alone %% start_standalone(Config) -> % {ok, Pid} | {error, Reason} %% <service>:start_link(Config). +-callback start_standalone(Config :: term()) -> + {ok, pid()} | {error, Reason :: term()}. + %% Starts service as part of inets %% start_service(Config) -> % {ok, Pid} | {error, Reason} %% <service_sup>:start_child(Config). + +-callback start_service(Config :: term()) -> + {ok, pid()} | {error, Reason :: term()}. + %% Stop service %% stop_service(Pid) -> % ok | {error, Reason} %% <service_sup>:stop_child(maybe_map_pid_to_other_ref(Pid)). @@ -51,6 +48,9 @@ behaviour_info(_) -> %% Error %% end. +-callback stop_service(Service :: term()) -> + ok | {error, Reason :: term()}. + %% Returns list of running services. Services started as stand alone %% are not listed %% services() -> % [{Service, Pid}] @@ -59,7 +59,13 @@ behaviour_info(_) -> %% [{httpc, Pid} || {_, Pid, _, _} <- %% supervisor:which_children(httpc_profile_sup)]. +-callback services() -> + [{Service :: term(), pid()}]. -%% service_info() -> [{Property, Value}] | {error, Reason} +%% service_info() -> {ok, [{Property, Value}]} | {error, Reason} %% ex: httpc:service_info() -> [{profile, ProfileName}] %% httpd:service_info() -> [{host, Host}, {port, Port}] + +-callback service_info(Service :: term()) -> + {ok, [{Property :: term(), Value :: term()}]} | {error, Reason :: term()}. + diff --git a/lib/inets/src/inets_app/inets_sup.erl b/lib/inets/src/inets_app/inets_sup.erl index 20d5ef343e..66a0cdf785 100644 --- a/lib/inets/src/inets_app/inets_sup.erl +++ b/lib/inets/src/inets_app/inets_sup.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. 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 @@ -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). + + diff --git a/lib/inets/src/tftp/Makefile b/lib/inets/src/tftp/Makefile index 759b70c8e4..b368b12462 100644 --- a/lib/inets/src/tftp/Makefile +++ b/lib/inets/src/tftp/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2005-2010. All Rights Reserved. +# Copyright Ericsson AB 2005-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 @@ -39,8 +39,10 @@ RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN) # ---------------------------------------------------- # Target Specs # ---------------------------------------------------- +BEHAVIOUR_MODULES= \ + tftp + MODULES = \ - tftp \ tftp_binary \ tftp_engine \ tftp_file \ @@ -50,10 +52,13 @@ MODULES = \ HRL_FILES = tftp.hrl -ERL_FILES = $(MODULES:%=%.erl) +ERL_FILES= \ + $(MODULES:%=%.erl) \ + $(BEHAVIOUR_MODULES:%=%.erl) TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) +BEHAVIOUR_TARGET_FILES= $(BEHAVIOUR_MODULES:%=$(EBIN)/%.$(EMULATOR)) # ---------------------------------------------------- # FLAGS @@ -72,10 +77,12 @@ ERL_COMPILE_FLAGS += \ # Targets # ---------------------------------------------------- +$(TARGET_FILES): $(BEHAVIOUR_TARGET_FILES) + debug opt: $(TARGET_FILES) clean: - rm -f $(TARGET_FILES) + rm -f $(TARGET_FILES) $(BEHAVIOUR_TARGET_FILES) rm -f core docs: @@ -86,11 +93,11 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/src/tftp - $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src/tftp - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/src/tftp" + $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) "$(RELSYSDIR)/src/tftp" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) $(BEHAVIOUR_TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/inets/src/tftp/tftp.erl b/lib/inets/src/tftp/tftp.erl index bfdb4c0030..1621add246 100644 --- a/lib/inets/src/tftp/tftp.erl +++ b/lib/inets/src/tftp/tftp.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2009. All Rights Reserved. +%% Copyright Ericsson AB 2005-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 @@ -215,8 +215,6 @@ start/0 ]). --export([behaviour_info/1]). - %% Application local functions -export([ start_standalone/1, @@ -227,13 +225,50 @@ ]). -behaviour_info(callbacks) -> - [{prepare, 6}, {open, 6}, {read, 1}, {write, 2}, {abort, 3}]; -behaviour_info(_) -> - undefined. +-type peer() :: {PeerType :: inet | inet6, + PeerHost :: inet:ip_address(), + PeerPort :: port()}. + +-type access() :: read | write. + +-type options() :: [{Key :: string(), Value :: string()}]. + +-type error_code() :: undef | enoent | eacces | enospc | + badop | eexist | baduser | badopt | + integer(). + +-callback prepare(Peer :: peer(), + Access :: access(), + Filename :: file:name(), + Mode :: string(), + SuggestedOptions :: options(), + InitialState :: [] | [{root_dir, string()}]) -> + {ok, AcceptedOptions :: options(), NewState :: term()} | + {error, {Code :: error_code(), string()}}. + +-callback open(Peer :: peer(), + Access :: access(), + Filename :: file:name(), + Mode :: string(), + SuggestedOptions :: options(), + State :: [] | [{root_dir, string()}] | term()) -> + {ok, AcceptedOptions :: options(), NewState :: term()} | + {error, {Code :: error_code(), string()}}. + +-callback read(State :: term()) -> {more, binary(), NewState :: term()} | + {last, binary(), integer()} | + {error, {Code :: error_code(), string()}}. + +-callback write(binary(), State :: term()) -> + {more, NewState :: term()} | + {last, FileSize :: integer()} | + {error, {Code :: error_code(), string()}}. + +-callback abort(Code :: error_code(), string(), State :: term()) -> 'ok'. -include("tftp.hrl"). + %%------------------------------------------------------------------- %% read_file(RemoteFilename, LocalFilename, Options) -> %% {ok, LastCallbackState} | {error, Reason} |