diff options
Diffstat (limited to 'lib/inets')
30 files changed, 968 insertions, 267 deletions
diff --git a/lib/inets/Makefile b/lib/inets/Makefile index 4765a2ca3c..d837a3396a 100644 --- a/lib/inets/Makefile +++ b/lib/inets/Makefile @@ -31,12 +31,16 @@ VSN = $(INETS_VSN) SPECIAL_TARGETS = +DIA_PLT = ./priv/plt/$(APPLICATION).plt +DIA_ANALYSIS = $(basename $(DIA_PLT)).dialyzer_analysis + + # ---------------------------------------------------- # Default Subdir Targets # ---------------------------------------------------- include $(ERL_TOP)/make/otp_subdir.mk -.PHONY: info gclean +.PHONY: info gclean dialyzer dialyzer_plt dclean info: @echo "OS: $(OS)" @@ -45,6 +49,29 @@ info: @echo "INETS_VSN: $(INETS_VSN)" @echo "APP_VSN: $(APP_VSN)" @echo "" + @echo "DIA_PLT: $(DIA_PLT)" + @echo "DIA_ANALYSIS: $(DIA_ANALYSIS)" + @echo "" gclean: git clean -fXd + +dclean: + rm -f $(DIA_PLT) + rm -f $(DIA_ANALYSIS) + +dialyzer_plt: $(DIA_PLT) + +$(DIA_PLT): + @echo "Building $(APPLICATION) plt file" + @dialyzer --build_plt \ + --output_plt $@ \ + -r ../$(APPLICATION)/ebin \ + --output $(DIA_ANALYSIS) \ + --verbose + +dialyzer: $(DIA_PLT) + @echo "Running dialyzer on $(APPLICATION)" + @dialyzer --plt $< \ + ../$(APPLICATION)/ebin \ + --verbose diff --git a/lib/inets/doc/src/Makefile b/lib/inets/doc/src/Makefile index 82f2a5829f..53d505b102 100644 --- a/lib/inets/doc/src/Makefile +++ b/lib/inets/doc/src/Makefile @@ -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 diff --git a/lib/inets/doc/src/httpc.xml b/lib/inets/doc/src/httpc.xml index b1f964ae69..48a2089605 100644 --- a/lib/inets/doc/src/httpc.xml +++ b/lib/inets/doc/src/httpc.xml @@ -178,13 +178,14 @@ filename() = string() <v>timeout() = integer() >= 0 | infinity</v> <v>Options = options()</v> <v>options() = [option()]</v> - <v>option() = {sync, boolean()} | - {stream, stream_to()} | - {body_format, body_format()} | - {full_result, boolean()} | - {headers_as_is, boolean() | - {socket_opts, socket_opts()} | - {receiver, receiver()}}</v> + <v>option() = {sync, boolean()} | + {stream, stream_to()} | + {body_format, body_format()} | + {full_result, boolean()} | + {headers_as_is, boolean() | + {socket_opts, socket_opts()} | + {receiver, receiver()}, + {ipv6_host_with_brackets, boolean()}}</v> <v>stream_to() = none | self | {self, once} | filename() </v> <v>socket_opts() = [socket_opt()]</v> <v>receiver() = pid() | function()/1 | {Module, Function, Args} </v> @@ -407,7 +408,18 @@ apply(Module, Function, [ReplyInfo | Args]) <p>Defaults to the <c>pid()</c> of the process calling the request function (<c>self()</c>). </p> + + <marker id="ipv6_host_with_brackets"></marker> + </item> + + <tag><c><![CDATA[ipv6_host_with_brackets]]></c></tag> + <item> + <p>When parsing the Host-Port part of an URI with a IPv6 address + with brackets, shall we retain those brackets (<c>true</c>) or + strip them (<c>false</c>). </p> + <p>Defaults to <c>false</c>. </p> </item> + </taglist> <marker id="cancel_request"></marker> @@ -572,17 +584,24 @@ apply(Module, Function, [ReplyInfo | Args]) <func> <name>cookie_header(Url) -> </name> - <name>cookie_header(Url, Profile) -> header() | {error, Reason}</name> + <name>cookie_header(Url, Profile | Opts) -> header() | {error, Reason}</name> + <name>cookie_header(Url, Opts, Profile) -> header() | {error, Reason}</name> <fsummary>Returns the cookie header that would be sent when making a request to Url using the profile <c>Profile</c>.</fsummary> <type> <v>Url = url()</v> + <v>Opts = [cookie_header_opt()]</v> <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v> + <v>cookie_header_opt() = {ipv6_host_with_brackets, boolean()}</v> </type> <desc> <p>Returns the cookie header that would be sent when making a request to <c>Url</c> using the profile <c>Profile</c>. If no profile is specified the default profile will be used. </p> + <p>The option <c>ipv6_host_with_bracket</c> deals with how to + parse IPv6 addresses. + See the <c>Options</c> argument of the + <seealso marker="#request2">request/4,5</seealso> for more info. </p> <marker id="reset_cookies"></marker> </desc> diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml index 7f0f61148c..cfc58b8ddb 100644 --- a/lib/inets/doc/src/notes.xml +++ b/lib/inets/doc/src/notes.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2002</year><year>2011</year> + <year>2002</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -32,6 +32,58 @@ <file>notes.xml</file> </header> + + <section><title>Inets 5.8.1</title> + <section><title>Improvements and New Features</title> + <p>-</p> + +<!-- + <list> + <item> + <p>[httpc|httpd] Added support for IPv6 with ssl. </p> + <p>Own Id: OTP-5566</p> + </item> + + </list> +--> + + </section> + + <section><title>Fixed Bugs and Malfunctions</title> +<!-- + <p>-</p> +--> + + <list> + <item> + <p>[ftp] Fails to open IPv6 connection due to badly formatted + IPv6 address in EPRT command. The address part of the command + incorrectly contained decimal elements instead of hexadecimal. </p> + <p>Own Id: OTP-9827</p> + <p>Aux Id: Seq 11970 </p> + </item> + + <item> + <p>[httpc] Bad Keep Alive Mode. When selecting a session, + the "state" of the session (specifically if the server has + responded) was not taken into account. </p> + <p>Own Id: OTP-9847</p> + </item> + + <item> + <p>[httpc] The client incorrectly streams 404 responses. + The documentation specifies that only 200 and 206 responses + shall be streamed. </p> + <p>Shane Evens</p> + <p>Own Id: OTP-9860</p> + </item> + + </list> + </section> + + </section> <!-- 5.8.1 --> + + <section><title>Inets 5.8</title> <section><title>Improvements and New Features</title> @@ -50,6 +102,21 @@ <p>Own Id: OTP-9545</p> </item> + <item> + <p>[httpc] Wrong Host header in IPv6 HTTP requests. + When a URI with a IPv6 host is parsed, the brackets that encapsulates + the address part is removed. This value is then supplied as the host + header. This can cause problems with some servers. + A workaround for this is to use headers_as_is and provide the host + header with the requst call. + To solve this a new option has been added, + <seealso marker="httpc#ipv6_host_with_brackets">ipv6_host_with_brackets</seealso>. + This option specifies if the host value of the host header shall + include the brackets or not. By default, it does not (as before). + </p> + <p>Own Id: OTP-9628</p> + </item> + </list> </section> @@ -66,6 +133,20 @@ <p>Own Id: OTP-9715</p> </item> + <item> + <p>[httpd] Sometimes entries in the transfer log was written + with the message size as list of numbers. This list was actually + the size as a string, e.g. "123", written with the control + sequence ~w. This has now been corrected so that any string is + converted to an integer (if possible). </p> + <p>Own Id: OTP-9733</p> + </item> + + <item> + <p>Fixed various problems detected by Dialyzer. </p> + <p>Own Id: OTP-9736</p> + </item> + </list> </section> diff --git a/lib/inets/priv/plt/.gitignore b/lib/inets/priv/plt/.gitignore new file mode 100644 index 0000000000..2051b52d48 --- /dev/null +++ b/lib/inets/priv/plt/.gitignore @@ -0,0 +1,2 @@ +/*.plt +/*.dialyzer_analysis diff --git a/lib/inets/src/ftp/ftp.erl b/lib/inets/src/ftp/ftp.erl index b6da92947c..560ee55271 100644 --- a/lib/inets/src/ftp/ftp.erl +++ b/lib/inets/src/ftp/ftp.erl @@ -1987,17 +1987,14 @@ setup_ctrl_connection(Host, Port, Timeout, State) -> setup_data_connection(#state{mode = active, caller = Caller, csock = CSock} = State) -> - IntToString = fun(Element) -> integer_to_list(Element) end, - case (catch inet:sockname(CSock)) of {ok, {{_, _, _, _, _, _, _, _} = IP, _}} -> {ok, LSock} = gen_tcp:listen(0, [{ip, IP}, {active, false}, inet6, binary, {packet, 0}]), {ok, Port} = inet:port(LSock), - Cmd = mk_cmd("EPRT |2|~s:~s:~s:~s:~s:~s:~s:~s|~s|", - lists:map(IntToString, - tuple_to_list(IP) ++ [Port])), + IpAddress = inet_parse:ntoa(IP), + Cmd = mk_cmd("EPRT |2|~s|~p|", [IpAddress, Port]), send_ctrl_message(State, Cmd), activate_ctrl_connection(State), {noreply, State#state{caller = {setup_data_connection, diff --git a/lib/inets/src/http_client/Makefile b/lib/inets/src/http_client/Makefile index 3960c36d00..d490e59929 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-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 diff --git a/lib/inets/src/http_client/httpc.erl b/lib/inets/src/http_client/httpc.erl index d72c34fa6b..ae87ceed93 100644 --- a/lib/inets/src/http_client/httpc.erl +++ b/lib/inets/src/http_client/httpc.erl @@ -34,7 +34,7 @@ set_option/2, set_option/3, set_options/1, set_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, stream_next/1, @@ -290,25 +290,36 @@ 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. + + %%-------------------------------------------------------------------------- diff --git a/lib/inets/src/http_client/httpc_handler.erl b/lib/inets/src/http_client/httpc_handler.erl index 587e24cc8d..bfe9b14ef6 100644 --- a/lib/inets/src/http_client/httpc_handler.erl +++ b/lib/inets/src/http_client/httpc_handler.erl @@ -157,12 +157,12 @@ info(Pid) -> %% memory in vain.) %%-------------------------------------------------------------------- %% Request should not be streamed -stream(BodyPart, Request = #request{stream = none}, _) -> +stream(BodyPart, #request{stream = none} = Request, _) -> ?hcrt("stream - none", []), {BodyPart, Request}; %% Stream to caller -stream(BodyPart, Request = #request{stream = Self}, Code) +stream(BodyPart, #request{stream = Self} = Request, Code) when ((Code =:= 200) orelse (Code =:= 206)) andalso ((Self =:= self) orelse (Self =:= {self, once})) -> ?hcrt("stream - self", [{stream, Self}, {code, Code}]), @@ -170,17 +170,10 @@ stream(BodyPart, Request = #request{stream = Self}, Code) {Request#request.id, stream, BodyPart}), {<<>>, Request}; -stream(BodyPart, Request = #request{stream = Self}, 404) - when (Self =:= self) orelse (Self =:= {self, once}) -> - ?hcrt("stream - self with 404", [{stream, Self}]), - httpc_response:send(Request#request.from, - {Request#request.id, stream, BodyPart}), - {<<>>, Request}; - %% Stream to file %% This has been moved to start_stream/3 %% We keep this for backward compatibillity... -stream(BodyPart, Request = #request{stream = Filename}, Code) +stream(BodyPart, #request{stream = Filename} = Request, Code) when ((Code =:= 200) orelse (Code =:= 206)) andalso is_list(Filename) -> ?hcrt("stream - filename", [{stream, Filename}, {code, Code}]), case file:open(Filename, [write, raw, append, delayed_write]) of @@ -192,7 +185,7 @@ stream(BodyPart, Request = #request{stream = Filename}, Code) end; %% Stream to file -stream(BodyPart, Request = #request{stream = Fd}, Code) +stream(BodyPart, #request{stream = Fd} = Request, Code) when ((Code =:= 200) orelse (Code =:= 206)) -> ?hcrt("stream to file", [{stream, Fd}, {code, Code}]), case file:write(Fd, BodyPart) of @@ -295,7 +288,7 @@ handle_call(#request{address = Addr} = Request, _, %% Queue + current queue:len(NewPipeline) + 1, client_close = ClientClose}, - httpc_manager:insert_session(NewSession, ProfileName), + insert_session(NewSession, ProfileName), ?hcrd("session updated", []), {reply, ok, State#state{pipeline = NewPipeline, session = NewSession, @@ -363,7 +356,7 @@ handle_call(#request{address = Addr} = Request, _, %% Queue + current queue:len(NewKeepAlive) + 1, client_close = ClientClose}, - httpc_manager:insert_session(NewSession, ProfileName), + insert_session(NewSession, ProfileName), ?hcrd("session updated", []), {reply, ok, State#state{keep_alive = NewKeepAlive, session = NewSession, @@ -377,7 +370,7 @@ handle_call(#request{address = Addr} = Request, _, NewSession = Session#session{queue_length = 1, client_close = ClientClose}, - httpc_manager:insert_session(NewSession, ProfileName), + insert_session(NewSession, ProfileName), Relaxed = (Request#request.settings)#http_options.relaxed, MFA = {httpc_response, parse, @@ -766,23 +759,52 @@ deliver_answer(Request) -> %% Func: code_change(_OldVsn, State, Extra) -> {ok, NewState} %% Purpose: Convert process state when code is changed %%-------------------------------------------------------------------- -%% code_change(_, #state{request = Request, pipeline = Queue} = State, -%% [{from, '5.0.1'}, {to, '5.0.2'}]) -> -%% Settings = new_http_options(Request#request.settings), -%% NewRequest = Request#request{settings = Settings}, -%% NewQueue = new_queue(Queue, fun new_http_options/1), -%% {ok, State#state{request = NewRequest, pipeline = NewQueue}}; - -%% code_change(_, #state{request = Request, pipeline = Queue} = State, -%% [{from, '5.0.2'}, {to, '5.0.1'}]) -> -%% Settings = old_http_options(Request#request.settings), -%% NewRequest = Request#request{settings = Settings}, -%% NewQueue = new_queue(Queue, fun old_http_options/1), -%% {ok, State#state{request = NewRequest, pipeline = NewQueue}}; + +code_change(_, + #state{session = OldSession, + profile_name = ProfileName} = State, + upgrade_from_pre_5_8_1) -> + case OldSession of + {session, + Id, ClientClose, Scheme, Socket, SocketType, QueueLen, Type} -> + NewSession = #session{id = Id, + client_close = ClientClose, + scheme = Scheme, + socket = Socket, + socket_type = SocketType, + queue_length = QueueLen, + type = Type}, + insert_session(NewSession, ProfileName), + {ok, State#state{session = NewSession}}; + _ -> + {ok, State} + end; + +code_change(_, + #state{session = OldSession, + profile_name = ProfileName} = State, + downgrade_to_pre_5_8_1) -> + case OldSession of + #session{id = Id, + client_close = ClientClose, + scheme = Scheme, + socket = Socket, + socket_type = SocketType, + queue_length = QueueLen, + type = Type} -> + NewSession = {session, + Id, ClientClose, Scheme, Socket, SocketType, + QueueLen, Type}, + insert_session(NewSession, ProfileName), + {ok, State#state{session = NewSession}}; + _ -> + {ok, State} + end; code_change(_, State, _) -> {ok, State}. + %% new_http_options({http_options, TimeOut, AutoRedirect, SslOpts, %% Auth, Relaxed}) -> %% {http_options, "HTTP/1.1", TimeOut, AutoRedirect, SslOpts, @@ -1181,7 +1203,7 @@ handle_pipeline(#state{status = pipeline, case queue:out(State#state.pipeline) of {empty, _} -> - ?hcrd("epmty pipeline queue", []), + ?hcrd("pipeline queue empty", []), %% The server may choose too teminate an idle pipeline %% in this case we want to receive the close message @@ -1191,9 +1213,8 @@ handle_pipeline(#state{status = pipeline, %% If a pipeline that has been idle for some time is not %% closed by the server, the client may want to close it. - NewState = activate_queue_timeout(TimeOut, State), - NewSession = Session#session{queue_length = 0}, - httpc_manager:insert_session(NewSession, ProfileName), + NewState = activate_queue_timeout(TimeOut, State), + update_session(ProfileName, Session, #session.queue_length, 0), %% Note mfa will be initilized when a new request %% arrives. {noreply, @@ -1203,6 +1224,7 @@ handle_pipeline(#state{status = pipeline, headers = undefined, body = undefined}}; {{value, NextRequest}, Pipeline} -> + ?hcrd("pipeline queue non-empty", []), case lists:member(NextRequest#request.id, State#state.canceled) of true -> @@ -1218,7 +1240,7 @@ handle_pipeline(#state{status = pipeline, Session#session{queue_length = %% Queue + current queue:len(Pipeline) + 1}, - httpc_manager:insert_session(NewSession, ProfileName), + insert_session(NewSession, ProfileName), Relaxed = (NextRequest#request.settings)#http_options.relaxed, MFA = {httpc_response, @@ -1257,7 +1279,7 @@ handle_keep_alive_queue( case queue:out(State#state.keep_alive) of {empty, _} -> - ?hcrd("empty keep_alive queue", []), + ?hcrd("keep_alive queue empty", []), %% The server may choose too terminate an idle keep_alive session %% in this case we want to receive the close message %% at once and not when trying to send the next @@ -1266,8 +1288,7 @@ handle_keep_alive_queue( %% If a keep_alive session has been idle for some time is not %% closed by the server, the client may want to close it. NewState = activate_queue_timeout(TimeOut, State), - NewSession = Session#session{queue_length = 0}, - httpc_manager:insert_session(NewSession, ProfileName), + update_session(ProfileName, Session, #session.queue_length, 0), %% Note mfa will be initilized when a new request %% arrives. {noreply, @@ -1279,6 +1300,7 @@ handle_keep_alive_queue( } }; {{value, NextRequest}, KeepAlive} -> + ?hcrd("keep_alive queue non-empty", []), case lists:member(NextRequest#request.id, State#state.canceled) of true -> @@ -1388,10 +1410,10 @@ try_to_enable_pipeline_or_keep_alive( case (is_pipeline_enabled_client(Session) andalso httpc_request:is_idempotent(Method)) of true -> - httpc_manager:insert_session(Session, ProfileName), + insert_session(Session, ProfileName), State#state{status = pipeline}; false -> - httpc_manager:insert_session(Session, ProfileName), + insert_session(Session, ProfileName), %% Make sure type is keep_alive in session %% as it in this case might be pipeline NewSession = Session#session{type = keep_alive}, @@ -1403,7 +1425,9 @@ try_to_enable_pipeline_or_keep_alive( end. answer_request(#request{id = RequestId, from = From} = Request, Msg, - #state{timers = Timers, profile_name = ProfileName} = State) -> + #state{session = Session, + timers = Timers, + profile_name = ProfileName} = State) -> ?hcrt("answer request", [{request, Request}, {msg, Msg}]), httpc_response:send(From, Msg), RequestTimers = Timers#timers.request_timers, @@ -1412,12 +1436,20 @@ answer_request(#request{id = RequestId, from = From} = Request, Msg, Timer = {RequestId, TimerRef}, cancel_timer(TimerRef, {timeout, Request#request.id}), httpc_manager:request_done(RequestId, ProfileName), - + NewSession = maybe_make_session_available(ProfileName, Session), + Timers2 = Timers#timers{request_timers = lists:delete(Timer, + RequestTimers)}, State#state{request = Request#request{from = answer_sent}, - timers = - Timers#timers{request_timers = - lists:delete(Timer, RequestTimers)}}. - + session = NewSession, + timers = Timers2}. + +maybe_make_session_available(ProfileName, + #session{available = false} = Session) -> + update_session(ProfileName, Session, #session.available, true), + Session#session{available = true}; +maybe_make_session_available(_ProfileName, Session) -> + Session. + cancel_timers(#timers{request_timers = ReqTmrs, queue_timer = QTmr}) -> cancel_timer(QTmr, timeout_queue), CancelTimer = fun({_, Timer}) -> cancel_timer(Timer, timeout) end, @@ -1656,6 +1688,28 @@ send_raw(SocketType, Socket, ProcessBody, Acc) -> end. +%% --------------------------------------------------------------------- +%% Session wrappers +%% --------------------------------------------------------------------- + +insert_session(Session, ProfileName) -> + httpc_manager:insert_session(Session, ProfileName). + + +update_session(ProfileName, #session{id = SessionId} = Session, Pos, Value) -> + try + begin + httpc_manager:update_session(ProfileName, SessionId, Pos, Value) + end + catch + error:undef -> % This could happen during code upgrade + Session2 = erlang:setelement(Pos, Session, Value), + insert_session(Session2, ProfileName) + end. + + +%% --------------------------------------------------------------------- + call(Msg, Pid) -> Timeout = infinity, call(Msg, Pid, Timeout). diff --git a/lib/inets/src/http_client/httpc_internal.hrl b/lib/inets/src/http_client/httpc_internal.hrl index e4127d992d..8af752546c 100644 --- a/lib/inets/src/http_client/httpc_internal.hrl +++ b/lib/inets/src/http_client/httpc_internal.hrl @@ -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 @@ -115,17 +115,37 @@ } ). + -record(session, { - id, % {{Host, Port}, HandlerPid} - client_close, % true | false - scheme, % http (HTTP/TCP) | https (HTTP/SSL/TCP) - socket, % Open socket, used by connection - socket_type, % socket-type, used by connection - queue_length = 1, % Current length of pipeline or keep-alive queue - type % pipeline | keep_alive (wait for response before sending new request) + %% {{Host, Port}, HandlerPid} + id, + + %% true | false + client_close, + + %% http (HTTP/TCP) | https (HTTP/SSL/TCP) + scheme, + + %% Open socket, used by connection + socket, + + %% socket-type, used by connection + socket_type, + + %% Current length of pipeline or keep-alive queue + queue_length = 1, + + %% pipeline | keep_alive (wait for response before sending new request) + type, + + %% true | false + %% This will be true, when a response has been received for + %% the first request. See type above. + available = false }). + -record(http_cookie, { domain, diff --git a/lib/inets/src/http_client/httpc_manager.erl b/lib/inets/src/http_client/httpc_manager.erl index ab575d867e..453081da21 100644 --- a/lib/inets/src/http_client/httpc_manager.erl +++ b/lib/inets/src/http_client/httpc_manager.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 @@ -34,10 +34,11 @@ retry_request/2, redirect_request/2, insert_session/2, + update_session/4, delete_session/2, set_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 @@ -193,6 +194,27 @@ insert_session(Session, ProfileName) -> %%-------------------------------------------------------------------- +%% 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. +%% Intended to be called by the httpc request handler process. +%%-------------------------------------------------------------------- + +update_session(ProfileName, SessionId, Pos, Value) -> + SessionDbName = session_db_name(ProfileName), + ?hcrt("update session", + [{id, SessionId}, + {pos, Pos}, + {value, Value}, + {profile, ProfileName}]), + ets:update_element(SessionDbName, SessionId, {Pos, Value}). + + +%%-------------------------------------------------------------------- %% Function: delete_session(SessionId, ProfileName) -> _ %% SessionId - {{Host, Port}, HandlerPid} %% ProfileName - atom() @@ -271,9 +293,11 @@ reset_cookies(ProfileName) -> which_cookies(ProfileName) when is_atom(ProfileName) -> call(ProfileName, which_cookies). + which_cookies(Url, ProfileName) when is_list(Url) andalso is_atom(ProfileName) -> - call(ProfileName, {which_cookies, Url, []}). + 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}). @@ -557,9 +581,70 @@ terminate(_, State) -> %% Func: code_change(_OldVsn, State, Extra) -> {ok, NewState} %% Purpose: Convert process state when code is changed %%-------------------------------------------------------------------- -code_change(_OldVsn, State, _Extra) -> +code_change(_, + #state{session_db = SessionDB} = State, + upgrade_from_pre_5_8_1) -> + Upgrade = + fun({session, + Id, ClientClose, Scheme, Socket, SocketType, QueueLen, Type}) -> + {ok, #session{id = Id, + client_close = ClientClose, + scheme = Scheme, + socket = Socket, + socket_type = SocketType, + queue_length = QueueLen, + type = Type}}; + (_) -> % Already upgraded (by handler) + ignore + end, + (catch update_session_table(SessionDB, Upgrade)), + {ok, State}; + +code_change(_, + #state{session_db = SessionDB} = State, + downgrade_to_pre_5_8_1) -> + Downgrade = + fun(#session{id = Id, + client_close = ClientClose, + scheme = Scheme, + socket = Socket, + socket_type = SocketType, + queue_length = QueueLen, + type = Type}) -> + {ok, {session, + Id, ClientClose, Scheme, Socket, SocketType, + QueueLen, Type}}; + (_) -> % Already downgraded (by handler) + ignore + end, + (catch update_session_table(SessionDB, Downgrade)), + {ok, State}; + +code_change(_, State, _) -> {ok, State}. +%% This function is to catch everything that calls through the cracks... +update_session_table(SessionDB, Transform) -> + ets:safe_fixtable(SessionDB, true), + update_session_table(SessionDB, ets:first(SessionDB), Transform), + ets:safe_fixtable(SessionDB, false). + +update_session_table(_SessionDB, '$end_of_table', _Transform) -> + ok; +update_session_table(SessionDB, Key, Transform) -> + case ets:lookup(SessionDB, Key) of + [OldSession] -> + case Transform(OldSession) of + {ok, NewSession} -> + ets:insert(SessionDB, NewSession); + ignore -> + ok + end; + _ -> + ok + end, + update_session_table(SessionDB, ets:next(SessionDB, Key), Transform). + %%-------------------------------------------------------------------- %% Internal functions @@ -688,6 +773,7 @@ select_session(Method, HostPort, Scheme, SessionType, scheme = Scheme, queue_length = '$2', type = SessionType, + available = true, _ = '_'}, %% {'_', {HostPort, '$1'}, false, Scheme, '_', '$2', SessionTyp}, Candidates = ets:match(SessionDb, Pattern), @@ -725,7 +811,7 @@ pipeline_or_keep_alive(Request, HandlerPid, State) -> ets:insert(State#state.handler_db, {Request#request.id, HandlerPid, Request#request.from}); - _ -> %timeout pipelining failed + _ -> % timeout pipelining failed start_handler(Request, State) end. diff --git a/lib/inets/src/http_client/httpc_response.erl b/lib/inets/src/http_client/httpc_response.erl index 2414ed0911..919115a23a 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-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 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_log.erl b/lib/inets/src/http_server/httpd_log.erl index db1e2c627a..60ab326a20 100644 --- a/lib/inets/src/http_server/httpd_log.erl +++ b/lib/inets/src/http_server/httpd_log.erl @@ -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 :: pos_integer() | string()) -> + {Log :: atom() | pid(), Entry :: string()}. + +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()}. + +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()}. 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()}. + +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..b62c10bbc7 100644 --- a/lib/inets/src/http_server/httpd_request_handler.erl +++ b/lib/inets/src/http_server/httpd_request_handler.erl @@ -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} | @@ -253,7 +267,10 @@ handle_info(timeout, #state{mod = ModData} = State) -> %% 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}. diff --git a/lib/inets/src/http_server/mod_log.erl b/lib/inets/src/http_server/mod_log.erl index 62faa285df..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 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 63ab0a5bae..d99e33b4ea 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-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 diff --git a/lib/inets/src/inets_app/inets.app.src b/lib/inets/src/inets_app/inets.app.src index 4d0defb329..1db7ed2c30 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 diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src index 779dd8e439..e80cb2a23b 100644 --- a/lib/inets/src/inets_app/inets.appup.src +++ b/lib/inets/src/inets_app/inets.appup.src @@ -1,7 +1,7 @@ %% This is an -*- erlang -*- file. %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2011. All Rights Reserved. +%% Copyright Ericsson AB 1999-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,6 +18,15 @@ {"%VSN%", [ + {"5.8", + [ + {load_module, ftp, soft_purge, soft_purge, []}, + {update, httpc_handler, {advanced, upgrade_from_pre_5_8_1}, + soft_purge, soft_purge, []}, + {update, httpc_manager, {advanced, upgrade_from_pre_5_8_1}, + soft_purge, soft_purge, [httpc_handler]} + ] + }, {"5.7.2", [ {restart_application, inets} @@ -40,6 +49,15 @@ } ], [ + {"5.8", + [ + {load_module, ftp, soft_purge, soft_purge, []}, + {update, httpc_handler, {advanced, upgrade_from_pre_5_8_1}, + soft_purge, soft_purge, []}, + {update, httpc_manager, {advanced, upgrade_from_pre_5_8_1}, + soft_purge, soft_purge, [httpc_handler]} + ] + }, {"5.7.2", [ {restart_application, inets} diff --git a/lib/inets/src/inets_app/inets.mk b/lib/inets/src/inets_app/inets.mk index 35fb0d7eca..194b4ca2b1 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-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 diff --git a/lib/inets/src/tftp/tftp.erl b/lib/inets/src/tftp/tftp.erl index 8d8fce388d..0d7ae1a89e 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-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 diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl index d86e52f3d5..881266b70a 100644 --- a/lib/inets/test/httpc_SUITE.erl +++ b/lib/inets/test/httpc_SUITE.erl @@ -90,6 +90,7 @@ all() -> parse_url, options, headers_as_is, + selecting_session, {group, proxy}, {group, ssl}, {group, stream}, @@ -145,14 +146,6 @@ groups() -> ]. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - %%-------------------------------------------------------------------- %% Function: init_per_suite(Config) -> Config %% Config - [tuple()] @@ -226,9 +219,7 @@ init_per_testcase(initial_server_connect = Case, Config) -> %% this test case does not work unless it does try begin - ensure_started(crypto), - ensure_started(public_key), - ensure_started(ssl), + ?ENSURE_STARTED([crypto, public_key, ssl]), inets:start(), Config end @@ -262,15 +253,17 @@ init_per_testcase(Case, Timeout, Config) -> %% inets:enable_trace(max, io, httpd), %% inets:enable_trace(max, io, httpc), - inets:enable_trace(max, io, all), + %% inets:enable_trace(max, io, all), NewConfig = case atom_to_list(Case) of [$s, $s, $l | _] -> + ?ENSURE_STARTED([crypto, public_key, ssl]), init_per_testcase_ssl(ssl, PrivDir, SslConfFile, [{watchdog, Dog} | TmpConfig]); [$e, $s, $s, $l | _] -> + ?ENSURE_STARTED([crypto, public_key, ssl]), init_per_testcase_ssl(essl, PrivDir, SslConfFile, [{watchdog, Dog} | TmpConfig]); @@ -282,7 +275,7 @@ init_per_testcase(Case, Timeout, Config) -> inets:start(), tsp("init_per_testcase -> " "[proxy case] start crypto, public_key and ssl"), - try ensure_started([crypto, public_key, ssl]) of + try ?ENSURE_STARTED([crypto, public_key, ssl]) of ok -> [{watchdog, Dog} | TmpConfig] catch @@ -335,8 +328,8 @@ init_per_testcase(Case, Timeout, Config) -> end; "ipv6_" ++ _Rest -> - %% Ensure needed apps (crypto, public_key and ssl) started - try ensure_started([crypto, public_key, ssl]) of + %% Ensure needed apps (crypto, public_key and ssl) are started + try ?ENSURE_STARTED([crypto, public_key, ssl]) of ok -> Profile = ipv6, %% A stand-alone profile is represented by a pid() @@ -370,7 +363,7 @@ init_per_testcase(Case, Timeout, Config) -> %% This will fail for the ipv6_ - cases (but that is ok) ProxyExceptions = ["localhost", ?IPV6_LOCAL_HOST], - http:set_options([{proxy, {{?PROXY, ?PROXY_PORT}, ProxyExceptions}}]), + httpc:set_options([{proxy, {{?PROXY, ?PROXY_PORT}, ProxyExceptions}}]), inets:enable_trace(max, io, httpc), %% inets:enable_trace(max, io, all), %% snmp:set_trace([gen_tcp]), @@ -1773,6 +1766,7 @@ http_stream(Config) when is_list(Config) -> %%------------------------------------------------------------------------- + http_stream_once(doc) -> ["Test the option stream for asynchrony requests"]; http_stream_once(suite) -> @@ -1780,12 +1774,12 @@ http_stream_once(suite) -> http_stream_once(Config) when is_list(Config) -> p("http_stream_once -> entry with" "~n Config: ~p", [Config]), - + p("http_stream_once -> set ipfamily to inet", []), ok = httpc:set_options([{ipfamily, inet}]), p("http_stream_once -> start dummy server", []), {DummyServerPid, Port} = dummy_server(ipv4), - + PortStr = integer_to_list(Port), p("http_stream_once -> once", []), once(?URL_START ++ PortStr ++ "/once.html"), @@ -1793,14 +1787,14 @@ http_stream_once(Config) when is_list(Config) -> once(?URL_START ++ PortStr ++ "/once_chunked.html"), p("http_stream_once -> dummy", []), once(?URL_START ++ PortStr ++ "/dummy.html"), - + p("http_stream_once -> stop dummy server", []), DummyServerPid ! stop, p("http_stream_once -> set ipfamily to inet6fb4", []), ok = httpc:set_options([{ipfamily, inet6fb4}]), p("http_stream_once -> done", []), ok. - + once(URL) -> p("once -> issue sync request for ~p", [URL]), {ok, {{_,200,_}, [_ | _], Body}} = @@ -2009,6 +2003,7 @@ ipv6(SocketType, Scheme, HTTPOptions, Extra, Config) -> %%------------------------------------------------------------------------- + headers_as_is(doc) -> ["Test the option headers_as_is"]; headers_as_is(suite) -> @@ -2026,6 +2021,321 @@ headers_as_is(Config) when is_list(Config) -> %%------------------------------------------------------------------------- + +selecting_session(doc) -> + ["Test selection of sessions - OTP-9847"]; +selecting_session(suite) -> + []; +selecting_session(Config) when is_list(Config) -> + tsp("selecting_session -> entry with" + "~n Config: ~p", [Config]), + + tsp("selecting_session -> set ipfamily to inet"), + ok = httpc:set_options([{ipfamily, inet}]), + + tsp("selecting_session -> start server"), + {ServerPid, Port} = otp_9847_server(), + + PortStr = integer_to_list(Port), + URL = ?URL_START ++ PortStr ++ "/index.html", + + tsp("selecting_session -> issue the first batch (three) requests"), + lists:foreach(fun(P) -> + tsp("selecting_session:fun1 -> " + "send stop request to ~p", [P]), + P ! stop + end, + reqs(URL, ServerPid, 3, 3, false)), + tsp("selecting_session -> sleep some (1) to make sure nothing lingers"), + ?SLEEP(5000), + tsp("selecting_session -> " + "instruct the server to reply to the first request"), + ServerPid ! {answer, true}, + receive + {answer, true} -> + tsp("selecting_session -> " + "received ack from server to reply to the first request"), + ok + end, + tsp("selecting_session -> issue the second batch (four) requests"), + lists:foreach(fun(P) -> + tsp("selecting_session:fun2 -> " + "send stop request to ~p", [P]), + P ! stop + end, + reqs(URL, ServerPid, 4, 1, true)), + tsp("selecting_session -> sleep some (2) to make sure nothing lingers"), + ?SLEEP(5000), + + tsp("selecting_session -> stop server"), + ServerPid ! stop, + tsp("selecting_session -> set ipfamily (back) to inet6fb4"), + ok = httpc:set_options([{ipfamily, inet6fb4}]), + tsp("selecting_session -> done"), + ok. + +reqs(URL, ServerPid, NumReqs, NumHandlers, InitialSync) -> + tsp("reqs -> entry with" + "~n URL: ~p" + "~n ServerPid: ~w" + "~n NumReqs: ~w" + "~n NumHandlers: ~w" + "~n InitialSync: ~w", + [URL, ServerPid, NumReqs, NumHandlers, InitialSync]), + Handlers = reqs2(URL, NumReqs, [], InitialSync), + tsp("reqs -> " + "~n Handlers: ~w", [Handlers]), + case length(Handlers) of + NumHandlers -> + tsp("reqs -> " + "~n NumHandlers: ~w", [NumHandlers]), + ServerPid ! num_handlers, + receive + {num_handlers, NumHandlers} -> + tsp("reqs -> received num_handlers with" + "~n NumHandlers: ~w", [NumHandlers]), + Handlers; + {num_handlers, WrongNumHandlers} -> + tsp("reqs -> received num_handlers with" + "~n WrongNumHandlers: ~w", [WrongNumHandlers]), + exit({wrong_num_handlers1, WrongNumHandlers, NumHandlers}) + end; + WrongNumHandlers -> + tsp("reqs -> " + "~n WrongNumHandlers: ~w", [WrongNumHandlers]), + exit({wrong_num_handlers2, WrongNumHandlers, NumHandlers}) + end. + + +reqs2(_URL, 0, Acc, _Sync) -> + lists:reverse(Acc); +reqs2(URL, Num, Acc, Sync) -> + tsp("reqs2 -> entry with" + "~n Num: ~w" + "~n Sync: ~w", [Num, Sync]), + case httpc:request(get, {URL, []}, [], [{sync, Sync}]) of + {ok, _Reply} -> + tsp("reqs2 -> successful request: ~p", [_Reply]), + receive + {handler, Handler, _Manager} -> + %% This is when a new handler is created + tsp("reqs2 -> received handler: ~p", [Handler]), + case lists:member(Handler, Acc) of + true -> + tsp("reqs2 -> duplicate handler"), + exit({duplicate_handler, Handler, Num, Acc}); + false -> + tsp("reqs2 -> wait for data ack"), + receive + {data_received, Handler} -> + tsp("reqs2 -> " + "received data ack from ~p", [Handler]), + case Sync of + true -> + reqs2(URL, Num-1, [Handler|Acc], + false); + false -> + reqs2(URL, Num-1, [Handler|Acc], + Sync) + end + end + end; + + {data_received, Handler} -> + tsp("reqs2 -> " + "received data ack from ~p", [Handler]), + reqs2(URL, Num-1, Acc, false) + + end; + + {error, Reason} -> + tsp("reqs2 -> request ~w failed: ~p", [Num, Reason]), + exit({request_failed, Reason, Num, Acc}) + end. + +otp_9847_server() -> + TC = self(), + Pid = spawn_link(fun() -> otp_9847_server_init(TC) end), + receive + {port, Port} -> + {Pid, Port} + end. + +otp_9847_server_init(TC) -> + tsp("otp_9847_server_init -> entry with" + "~n TC: ~p", [TC]), + {ok, ListenSocket} = + gen_tcp:listen(0, [binary, inet, {packet, 0}, + {reuseaddr,true}, + {active, false}]), + tsp("otp_9847_server_init -> listen socket created: " + "~n ListenSocket: ~p", [ListenSocket]), + {ok, Port} = inet:port(ListenSocket), + tsp("otp_9847_server_init -> Port: ~p", [Port]), + TC ! {port, Port}, + otp_9847_server_main(TC, ListenSocket, false, []). + +otp_9847_server_main(TC, ListenSocket, Answer, Handlers) -> + tsp("otp_9847_server_main -> entry with" + "~n TC: ~p" + "~n ListenSocket: ~p" + "~n Answer: ~p" + "~n Handlers: ~p", [TC, ListenSocket, Answer, Handlers]), + case gen_tcp:accept(ListenSocket, 1000) of + {ok, Sock} -> + tsp("otp_9847_server_main -> accepted" + "~n Sock: ~p", [Sock]), + {Handler, Mon, Port} = otp_9847_handler(TC, Sock, Answer), + tsp("otp_9847_server_main -> handler ~p created for ~w", + [Handler, Port]), + gen_tcp:controlling_process(Sock, Handler), + tsp("otp_9847_server_main -> control transfer"), + Handler ! owner, + tsp("otp_9847_server_main -> " + "handler ~p informed of owner transfer", [Handler]), + TC ! {handler, Handler, self()}, + tsp("otp_9847_server_main -> " + "TC ~p informed of handler ~p", [TC, Handler]), + otp_9847_server_main(TC, ListenSocket, Answer, + [{Handler, Mon, Sock, Port}|Handlers]); + + {error, timeout} -> + tsp("otp_9847_server_main -> timeout"), + receive + {answer, true} -> + tsp("otp_9847_server_main -> received answer request"), + TC ! {answer, true}, + otp_9847_server_main(TC, ListenSocket, true, Handlers); + + {'DOWN', _Mon, process, Pid, _Reason} -> + %% Could be one of the handlers + tsp("otp_9847_server_main -> received DOWN for ~p", [Pid]), + otp_9847_server_main(TC, ListenSocket, Answer, + lists:keydelete(Pid, 1, Handlers)); + + num_handlers -> + tsp("otp_9847_server_main -> " + "received request for number of handlers (~w)", + [length(Handlers)]), + TC ! {num_handlers, length(Handlers)}, + otp_9847_server_main(TC, ListenSocket, Answer, Handlers); + + stop -> + tsp("otp_9847_server_main -> received stop request"), + %% Stop all handlers (just in case) + Pids = [Handler || {Handler, _, _} <- Handlers], + lists:foreach(fun(Pid) -> Pid ! stop end, Pids), + exit(normal); + + Any -> + tsp("otp_9847_server_main -> received" + "~n Any: ~p", [Any]), + exit({crap, Any}) + + after 0 -> + tsp("otp_9847_server_main -> nothing in queue"), + otp_9847_server_main(TC, ListenSocket, Answer, Handlers) + end; + + Error -> + exit(Error) + end. + + +otp_9847_handler(TC, Sock, Answer) -> + tsp("otp_9847_handler -> entry with" + "~n TC: ~p" + "~n Sock: ~p" + "~n Answer: ~p", [TC, Sock, Answer]), + Self = self(), + {Pid, Mon} = + spawn_opt(fun() -> + otp_9847_handler_init(TC, Self, Sock, Answer) + end, + [monitor]), + receive + {port, Port} -> + tsp("otp_9847_handler -> received port message (from ~p)" + "~n Port: ~p", [Pid, Port]), + {Pid, Mon, Port} + end. + + +otp_9847_handler_init(TC, Server, Sock, Answer) -> + tsp("otp_9847_handler_init -> entry with" + "~n TC: ~p" + "~n Server: ~p" + "~n Sock: ~p" + "~n Answer: ~p", [TC, Server, Sock, Answer]), + {ok, Port} = inet:port(Sock), + Server ! {port, Port}, + receive + owner -> + tsp("otp_9847_handler_init -> " + "received owner message - activate socket"), + inet:setopts(Sock, [{active, true}]), + otp_9847_handler_main(TC, Server, Sock, Answer, [?HTTP_MAX_HEADER_SIZE]) + end. + +otp_9847_handler_main(TC, Server, Sock, Answer, ParseArgs) -> + tsp("otp_9847_handler_main -> entry with" + "~n TC: ~p" + "~n Server: ~p" + "~n Sock: ~p" + "~n Answer: ~p" + "~n ParseArgs: ~p", [TC, Server, Sock, Answer, ParseArgs]), + receive + stop -> + tsp("otp_9847_handler_main -> received stop request"), + exit(normal); + + {tcp, Sock, _Data} when Answer =:= false -> + tsp("otp_9847_handler_main -> received tcp data - no answer"), + TC ! {data_received, self()}, + inet:setopts(Sock, [{active, true}]), + %% Ignore all data + otp_9847_handler_main(TC, Server, Sock, Answer, ParseArgs); + + {tcp, Sock, Data} when Answer =:= true -> + tsp("otp_9847_handler_main -> received tcp data - answer"), + TC ! {data_received, self()}, + inet:setopts(Sock, [{active, true}]), + NewParseArgs = otp_9847_handler_request(Sock, [Data|ParseArgs]), + otp_9847_handler_main(TC, Server, Sock, Answer, NewParseArgs); + + {tcp_closed, Sock} -> + tsp("otp_9847_handler_main -> received tcp socket closed"), + exit(normal); + + {tcp_error, Sock, Reason} -> + tsp("otp_9847_handler_main -> socket error: ~p", [Reason]), + (catch gen_tcp:close(Sock)), + exit(normal) + + %% after 30000 -> + %% gen_tcp:close(Sock), + %% exit(normal) + end. + +otp_9847_handler_request(Sock, Args) -> + Msg = + case httpd_request:parse(Args) of + {ok, {_, "/index.html" = _RelUrl, _, _, _}} -> + B = + "<HTML><BODY>" ++ + "...some body part..." ++ + "</BODY></HTML>", + Len = integer_to_list(length(B)), + "HTTP/1.1 200 ok\r\n" ++ + "Content-Length:" ++ Len ++ "\r\n\r\n" ++ B + end, + gen_tcp:send(Sock, Msg), + [?HTTP_MAX_HEADER_SIZE]. + + + +%%------------------------------------------------------------------------- + options(doc) -> ["Test the option parameters."]; options(suite) -> @@ -2050,6 +2360,7 @@ options(Config) when is_list(Config) -> %%------------------------------------------------------------------------- + http_invalid_http(doc) -> ["Test parse error"]; http_invalid_http(suite) -> @@ -2863,6 +3174,7 @@ otp_8739_dummy_server_main(_Parent, ListenSocket) -> exit(Error) end. + %%------------------------------------------------------------------------- initial_server_connect(doc) -> @@ -3553,21 +3865,5 @@ dummy_ssl_server_hang_loop(_) -> end. -ensure_started([]) -> - ok; -ensure_started([App|Apps]) -> - ensure_started(App), - ensure_started(Apps); -ensure_started(App) when is_atom(App) -> - case (catch application:start(App)) of - ok -> - ok; - {error, {already_started, _}} -> - ok; - Error -> - throw({error, {failed_starting, App, Error}}) - end. - - skip(Reason) -> {skip, Reason}. diff --git a/lib/inets/test/httpd_SUITE.erl b/lib/inets/test/httpd_SUITE.erl index ba31788ccc..a4bb8f7159 100644 --- a/lib/inets/test/httpd_SUITE.erl +++ b/lib/inets/test/httpd_SUITE.erl @@ -497,6 +497,7 @@ init_per_testcase2(Case, Config) -> _ -> NewConfig end; + _ -> NewConfig end, @@ -528,7 +529,7 @@ init_per_testcase3(Case, Config) -> application:stop(ssl), cleanup_mnesia(), - %% Set trace + %% Set trace level case lists:reverse(atom_to_list(Case)) of "tset_emit" ++ _Rest -> % test-cases ending with time_test io:format(user, "~w:init_per_testcase3(~w) -> disabling trace", @@ -581,9 +582,10 @@ init_per_testcase3(Case, Config) -> Rest; [X, $s, $s, $l, $_, $m, $o, $d, $_, $h, $t, $a, $c, $c, $e, $s, $s] -> + ?ENSURE_STARTED([crypto, public_key, ssl]), SslTag = case X of - $p -> ssl; % plain + $p -> ssl; % Plain $e -> essl % Erlang based ssl end, case inets_test_lib:start_http_server_ssl( @@ -597,6 +599,7 @@ init_per_testcase3(Case, Config) -> {skip, "SSL does not seem to be supported"} end; [X, $s, $s, $l, $_ | Rest] -> + ?ENSURE_STARTED([crypto, public_key, ssl]), SslTag = case X of $p -> ssl; @@ -679,36 +682,6 @@ end_per_testcase2(Case, Config) -> %%------------------------------------------------------------------------- %%------------------------------------------------------------------------- - - - - - - -%%------------------------------------------------------------------------- -http_1_1_ip(doc) -> - ["HTTP/1.1"]; -http_1_1_ip(suite) -> - [ - ip_host, - ip_chunked, - ip_expect, - ip_range, - ip_if_test, - ip_http_trace, - ip_http1_1_head, - ip_mod_cgi_chunked_encoding_test - ]. - -%%------------------------------------------------------------------------- - -%%------------------------------------------------------------------------- - -%%------------------------------------------------------------------------- - -%%------------------------------------------------------------------------- - -%%------------------------------------------------------------------------- ip_mod_alias(doc) -> ["Module test: mod_alias"]; ip_mod_alias(suite) -> @@ -717,6 +690,7 @@ ip_mod_alias(Config) when is_list(Config) -> httpd_mod:alias(ip_comm, ?IP_PORT, ?config(host, Config), ?config(node, Config)), ok. + %%------------------------------------------------------------------------- ip_mod_actions(doc) -> ["Module test: mod_actions"]; @@ -726,6 +700,7 @@ ip_mod_actions(Config) when is_list(Config) -> httpd_mod:actions(ip_comm, ?IP_PORT, ?config(host, Config), ?config(node, Config)), ok. + %%------------------------------------------------------------------------- ip_mod_security(doc) -> ["Module test: mod_security"]; @@ -2252,6 +2227,7 @@ ticket_5865(doc) -> ticket_5865(suite) -> []; ticket_5865(Config) -> + ?SKIP(as_of_r15_behaviour_of_calendar_has_changed), Host = ?config(host,Config), ServerRoot = ?config(server_root, Config), DocRoot = filename:join([ServerRoot, "htdocs"]), diff --git a/lib/inets/test/httpd_test_lib.erl b/lib/inets/test/httpd_test_lib.erl index 1c7bb512cc..2f5867559a 100644 --- a/lib/inets/test/httpd_test_lib.erl +++ b/lib/inets/test/httpd_test_lib.erl @@ -80,14 +80,18 @@ %% API %%------------------------------------------------------------------ verify_request(SocketType, Host, Port, Node, RequestStr, Options) -> - verify_request(SocketType, Host, Port, Node, RequestStr, Options, 30000). + verify_request(SocketType, Host, Port, Node, RequestStr, + Options, 30000). verify_request(SocketType, Host, Port, TranspOpts, Node, RequestStr, Options) when is_list(TranspOpts) -> - verify_request(SocketType, Host, Port, TranspOpts, Node, RequestStr, Options, 30000); + verify_request(SocketType, Host, Port, TranspOpts, Node, RequestStr, + Options, 30000); verify_request(SocketType, Host, Port, Node, RequestStr, Options, TimeOut) when (is_integer(TimeOut) orelse (TimeOut =:= infinity)) -> - verify_request(SocketType, Host, Port, [], Node, RequestStr, Options, TimeOut). -verify_request(SocketType, Host, Port, TranspOpts, Node, RequestStr, Options, TimeOut) -> + verify_request(SocketType, Host, Port, [], Node, RequestStr, + Options, TimeOut). +verify_request(SocketType, Host, Port, TranspOpts, Node, RequestStr, + Options, TimeOut) -> tsp("verify_request -> entry with" "~n SocketType: ~p" "~n Host: ~p" @@ -259,10 +263,10 @@ validate(RequestStr, #state{status_line = {Version, StatusCode, _}, headers = Headers, body = Body}, Options, N, P) -> - %% tsp("validate -> entry with" - %% "~n StatusCode: ~p" - %% "~n Headers: ~p" - %% "~n Body: ~p", [StatusCode, Headers, Body]), + tsp("validate -> entry with" + "~n StatusCode: ~p" + "~n Headers: ~p" + "~n Body: ~p", [StatusCode, Headers, Body]), check_version(Version, Options), case lists:keysearch(statuscode, 1, Options) of @@ -320,9 +324,9 @@ do_validate(Header, [{header, HeaderField}|Rest], N, P) -> {value, {LowerHeaderField, _Value}} -> ok; false -> - test_server:fail({missing_header_field, LowerHeaderField, Header}); + tsf({missing_header_field, LowerHeaderField, Header}); _ -> - test_server:fail({missing_header_field, LowerHeaderField, Header}) + tsf({missing_header_field, LowerHeaderField, Header}) end, do_validate(Header, Rest, N, P); do_validate(Header, [{header, HeaderField, Value}|Rest],N,P) -> @@ -331,18 +335,15 @@ do_validate(Header, [{header, HeaderField, Value}|Rest],N,P) -> {value, {LowerHeaderField, Value}} -> ok; false -> - test_server:fail({wrong_header_field_value, LowerHeaderField, - Header}); + tsf({wrong_header_field_value, LowerHeaderField, Header}); _ -> - test_server:fail({wrong_header_field_value, LowerHeaderField, - Header}) + tsf({wrong_header_field_value, LowerHeaderField, Header}) end, do_validate(Header, Rest, N, P); do_validate(Header,[{no_last_modified, HeaderField}|Rest],N,P) -> case lists:keysearch(HeaderField,1,Header) of {value,_} -> - test_server:fail({wrong_header_field_value, HeaderField, - Header}); + tsf({wrong_header_field_value, HeaderField, Header}); _ -> ok end, diff --git a/lib/inets/test/httpd_time_test.erl b/lib/inets/test/httpd_time_test.erl index c54674be36..0bb457f9b9 100644 --- a/lib/inets/test/httpd_time_test.erl +++ b/lib/inets/test/httpd_time_test.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 @@ -333,51 +333,82 @@ poll(Error, _SocketType, _URI, _ExpRes) -> exit({failed_creating_socket, Error}). await_poll_response(ok, SocketType, Socket, ExpStatusCode) -> + await_poll_response2(SocketType, Socket, ExpStatusCode, []); +await_poll_response(Error, _SocketType, _Socket, _ExpStatusCode) -> + exit(Error). + +%% The reply *can* be split into two messages (this is a +%% result of OTP-9757 for ssl), so we read them all until +%% the sockets closes, then we analyze the response. +await_poll_response2(SocketType, Socket, ExpStatusCode, Data) -> receive %% SSL receives - {ssl, Socket, Data} -> - validate(ExpStatusCode, SocketType, Socket, Data); - {ssl_closed, Socket} -> - exit(connection_closed); + {ssl, Socket, NewData} -> + d("await_poll_response2 -> " + "received part (~w bytes) of the response", [sz(NewData)]), + await_poll_response2(SocketType, Socket, ExpStatusCode, + [NewData | Data]); + {ssl_closed, Socket} -> + %% We are done or we failed + d("await_poll_response2 -> " + "we are done after receiving ~w bytes data", [sz(Data)]), + validate(ExpStatusCode, SocketType, Socket, + lists:flatten(lists:reverse(Data))); {ssl_error, Socket, Error} -> exit({connection_error, Error}); %% TCP receives - {tcp, Socket, Response} -> - validate(ExpStatusCode, SocketType, Socket, Response); + {tcp, Socket, NewData} -> + d("await_poll_response2 -> " + "received part (~w bytes) of the response", [sz(NewData)]), + await_poll_response2(SocketType, Socket, ExpStatusCode, + [NewData | Data]); {tcp_closed, Socket} -> - exit(connection_closed); + %% We are done or we failed + d("await_poll_response2 -> " + "we are done after receiving ~w bytes data", [sz(Data)]), + validate(ExpStatusCode, SocketType, Socket, + lists:flatten(lists:reverse(Data))); {tcp_error, Socket, Error} -> exit({connection_error, Error}) after 10000 -> - exit(response_timed_out) - end; -await_poll_response(Error, _SocketType, _Socket, _ExpStatusCode) -> - exit(Error). - + d("we timed out while waiting for response, " + "validate whatever we got so far"), + validate(ExpStatusCode, SocketType, Socket, + lists:flatten(lists:reverse(Data))) + %% exit(response_timed_out) + end. -validate(ExpStatusCode, SocketType, Socket, Response) -> - Sz = sz(Response), - trash_the_rest(Socket, Sz), - inets_test_lib:close(SocketType, Socket), +validate(ExpStatusCode, _SocketType, _Socket, Response) -> + %% Sz = sz(Response), + %% trash_the_rest(Socket, Sz), + %% inets_test_lib:close(SocketType, Socket), case inets_regexp:split(Response," ") of - {ok,["HTTP/1.0", ExpStatusCode|_]} -> + {ok, ["HTTP/1.0", ExpStatusCode|_]} -> ok; - {ok,["HTTP/1.0", StatusCode|_]} -> + {ok, ["HTTP/1.0", StatusCode|_]} -> error_msg("Unexpected status code: ~p (~s). " "Expected status code: ~p (~s)", [StatusCode, status_to_message(StatusCode), ExpStatusCode, status_to_message(ExpStatusCode)]), exit({unexpected_response_code, StatusCode, ExpStatusCode}); - {ok,["HTTP/1.1", ExpStatusCode|_]} -> + {ok, ["HTTP/1.1", ExpStatusCode|_]} -> ok; - {ok,["HTTP/1.1", StatusCode|_]} -> + {ok, ["HTTP/1.1", StatusCode|_]} -> error_msg("Unexpected status code: ~p (~s). " "Expected status code: ~p (~s)", [StatusCode, status_to_message(StatusCode), ExpStatusCode, status_to_message(ExpStatusCode)]), - exit({unexpected_response_code, StatusCode, ExpStatusCode}) + exit({unexpected_response_code, StatusCode, ExpStatusCode}); + {ok, Unexpected} -> + error_msg("Unexpected response split: ~p (~s)", + [Unexpected, Response]), + exit({unexpected_response, Unexpected, Response}); + {error, Reason} -> + error_msg("Failed processing response: ~p (~s)", + [Reason, Response]), + exit({failed_response_processing, Reason, Response}) end. diff --git a/lib/inets/test/inets_app_test.erl b/lib/inets/test/inets_app_test.erl index 9d7202e087..db2218f3b6 100644 --- a/lib/inets/test/inets_app_test.erl +++ b/lib/inets/test/inets_app_test.erl @@ -45,8 +45,7 @@ end_per_testcase(_Case, Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% all() -> - [fields, modules, exportall, app_depend, - undef_funcs]. + [fields, modules, exportall, app_depend, undef_funcs]. groups() -> []. @@ -243,18 +242,11 @@ undef_funcs(doc) -> undef_funcs(Config) when is_list(Config) -> %% We need to check if there is a point to run this test. %% On some platforms, crypto will not build, which in turn - %% causes ssl to not to not build (at this time, this will + %% causes ssl to not build (at this time, this will %% change in the future). %% So, we first check if we can start crypto, and if not, %% we skip this test case! - case (catch crypto:start()) of - ok -> - ok; - {error, {already_started, crypto}} -> - ok; - _ -> - ?SKIP(crypto_start_check_failed) - end, + ?ENSURE_STARTED(crypto), App = inets, AppFile = key1search(app_file, Config), Mods = key1search(modules, AppFile), @@ -266,7 +258,7 @@ undef_funcs(Config) when is_list(Config) -> ok = xref:set_default(XRef, [{verbose,false},{warnings,false}]), XRefName = undef_funcs_make_name(App, xref_name), - {ok, XRefName} = xref:add_release(XRef, Root, {name,XRefName}), + {ok, XRefName} = xref:add_release(XRef, Root, {name, XRefName}), {ok, App} = xref:replace_application(XRef, App, EbinDir), {ok, Undefs} = xref:analyze(XRef, undefined_function_calls), xref:stop(XRef), diff --git a/lib/inets/test/inets_test_lib.erl b/lib/inets/test/inets_test_lib.erl index ddb1a49394..bbed35e1f8 100644 --- a/lib/inets/test/inets_test_lib.erl +++ b/lib/inets/test/inets_test_lib.erl @@ -35,6 +35,7 @@ -export([check_body/1]). -export([millis/0, millis_diff/2, hours/1, minutes/1, seconds/1, sleep/1]). -export([oscmd/1, has_ipv6_support/1]). +-export([ensure_started/1]). -export([non_pc_tc_maybe_skip/4, os_based_skip/1, skip/3, fail/3]). -export([flush/0]). -export([start_node/1, stop_node/1]). @@ -126,6 +127,37 @@ await_stopped(Node, N) -> %% ---------------------------------------------------------------- +%% Ensure apps are started +%% This to ensure we dont attempt to run teatcases on platforms +%% where there is no working ssl app. + +ensure_started([]) -> + ok; +ensure_started([App|Apps]) -> + ensure_started(App), + ensure_started(Apps); +ensure_started(crypto = App) -> + %% We have to treat crypto in this special way because + %% only this function ensures that the NIF lib is actually + %% loaded. And only by loading that lib can we know if it + %% is even possible to run crypto. + do_ensure_started(App, fun() -> crypto:start() end); +ensure_started(App) when is_atom(App) -> + do_ensure_started(App, fun() -> application:start(App) end). + +do_ensure_started(App, Start) when is_function(Start) -> + case (catch Start()) of + ok -> + ok; + {error, {already_started, _}} -> + ok; + Error -> + throw({error, {failed_starting, App, Error}}) + end. + + + +%% ---------------------------------------------------------------- %% HTTPD starter functions %% diff --git a/lib/inets/test/inets_test_lib.hrl b/lib/inets/test/inets_test_lib.hrl index 4dd81093a2..c578398c55 100644 --- a/lib/inets/test/inets_test_lib.hrl +++ b/lib/inets/test/inets_test_lib.hrl @@ -64,10 +64,11 @@ %% - Misc macros - --define(UPDATE(K,V,C), inets_test_lib:update_config(K,V,C)). --define(CONFIG(K,C), inets_test_lib:get_config(K,C)). --define(HOSTNAME(), inets_test_lib:hostname()). --define(SZ(X), inets_test_lib:sz(X)). +-define(ENSURE_STARTED(A), inets_test_lib:ensure_started(A)). +-define(UPDATE(K,V,C), inets_test_lib:update_config(K,V,C)). +-define(CONFIG(K,C), inets_test_lib:get_config(K,C)). +-define(HOSTNAME(), inets_test_lib:hostname()). +-define(SZ(X), inets_test_lib:sz(X)). %% - Test case macros - diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk index 1df4558e45..77eb43a7ed 100644 --- a/lib/inets/vsn.mk +++ b/lib/inets/vsn.mk @@ -2,7 +2,7 @@ # %CopyrightBegin% # -# Copyright Ericsson AB 2001-2011. All Rights Reserved. +# Copyright Ericsson AB 2001-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,7 +18,7 @@ # %CopyrightEnd% APPLICATION = inets -INETS_VSN = 5.8 +INETS_VSN = 5.8.1 PRE_VSN = APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)" |