aboutsummaryrefslogtreecommitdiffstats
path: root/lib/inets/src
diff options
context:
space:
mode:
authorMicael Karlberg <bmk@erlang.org>2010-02-12 15:55:58 +0000
committerErlang/OTP <otp@erlang.org>2010-02-13 07:29:10 +0100
commitd9286a9a68e8b10dcf006a60ec84a0439e725fac (patch)
treed49cd8f54778f192593be6943b1661b9d1772d7e /lib/inets/src
parentba3758531a0b6dc6797457279e1ce0f99d282d20 (diff)
downloadotp-d9286a9a68e8b10dcf006a60ec84a0439e725fac.tar.gz
otp-d9286a9a68e8b10dcf006a60ec84a0439e725fac.tar.bz2
otp-d9286a9a68e8b10dcf006a60ec84a0439e725fac.zip
OTP-8431: Fix error handling of httpc_manager and its starter process
OTP-8431: jean-sebastien.pedron@dumbbell.fr
Diffstat (limited to 'lib/inets/src')
-rw-r--r--lib/inets/src/http_client/httpc_handler.erl11
-rw-r--r--lib/inets/src/http_client/httpc_manager.erl112
2 files changed, 96 insertions, 27 deletions
diff --git a/lib/inets/src/http_client/httpc_handler.erl b/lib/inets/src/http_client/httpc_handler.erl
index fec74932a2..31585537d4 100644
--- a/lib/inets/src/http_client/httpc_handler.erl
+++ b/lib/inets/src/http_client/httpc_handler.erl
@@ -267,9 +267,14 @@ handle_call({connect_and_send, #request{address = Address0,
%%send_ssl_tunnel_request(Address, Request,
%% #state{options = Options,
%% status = ssl_tunnel});
- Reason = https_through_proxy_is_not_currently_supported,
- Error = {error, Reason},
- {stop, Error, Error, State};
+ Reason = {failed_connecting,
+ https_through_proxy_is_not_currently_supported},
+ %% Send a reply to the original caller
+ ErrorResponse = httpc_response:error(Request, Reason),
+ httpc_response:send(Request#request.from, ErrorResponse),
+ %% Reply to the manager
+ ErrorReply = {error, Reason},
+ {stop, normal, ErrorReply, State};
true ->
case connect_and_send_first_request(Address, Request, State) of
{ok, NewState} ->
diff --git a/lib/inets/src/http_client/httpc_manager.erl b/lib/inets/src/http_client/httpc_manager.erl
index f8fc6322ed..f3cd81f4a7 100644
--- a/lib/inets/src/http_client/httpc_manager.erl
+++ b/lib/inets/src/http_client/httpc_manager.erl
@@ -62,7 +62,7 @@
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 | operational | canceled
+ state % State of the handler: initiating | started | operational | canceled
}).
%% Entries in the handler / request cross-ref table
@@ -539,6 +539,11 @@ handle_cast(Msg, #state{profile_name = ProfileName} = State) ->
%% {stop, Reason, State} (terminate/2 is called)
%% Description: Handling all non call/cast messages
%%---------------------------------------------------------
+
+handle_info({started, StarterPid, ReqId, HandlerPid}, State) ->
+ handle_started(StarterPid, ReqId, HandlerPid, State),
+ {noreply, State};
+
handle_info({connect_and_send, StarterPid, ReqId, HandlerPid, Res}, State) ->
handle_connect_and_send(StarterPid, ReqId, HandlerPid, Res, State),
{noreply, State};
@@ -633,6 +638,38 @@ get_handler_info(Tab) ->
%%
%% The request handler process is started asynchronously by a
+%% "starter process". When the handler has sucessfully been started,
+%% this message (started) is sent.
+%%
+
+handle_started(StarterPid, ReqId, HandlerPid,
+ #state{profile_name = Profile,
+ handler_db = HandlerDb}) ->
+ case ets:lookup(HandlerDb, ReqId) of
+ [#handler_info{state = initiating} = HandlerInfo] ->
+ ?hcri("received started ack for initiating handler", []),
+ HandlerInfo2 = HandlerInfo#handler_info{handler = HandlerPid,
+ state = started},
+ ets:insert(HandlerDb, HandlerInfo2),
+ ok;
+
+ [#handler_info{state = State}] ->
+ error_report(Profile,
+ "unexpected (started) message for handler (~p) in state "
+ "~p regarding request ~p - ignoring", [HandlerPid, State, ReqId]),
+ ?hcri("received unexpected started message", [{state, State}]),
+ ok;
+
+ [] ->
+ error_report(Profile,
+ "unknown handler ~p (~p) started for request ~w - canceling",
+ [HandlerPid, StarterPid, ReqId]),
+ httpc_handler:cancel(ReqId, HandlerPid)
+ end.
+
+
+%%
+%% The request handler process is started asynchronously by a
%% "starter process". When that process terminates it sends
%% one of two messages. These ara handled by the two functions
%% below.
@@ -642,8 +679,8 @@ handle_connect_and_send(_StarterPid, ReqId, HandlerPid, Result,
#state{profile_name = Profile,
handler_db = HandlerDb}) ->
case ets:lookup(HandlerDb, ReqId) of
- [#handler_info{state = initiating} = HandlerInfo] when Result =:= ok ->
- ?hcri("received connect-and-send ack for initiating handler", []),
+ [#handler_info{state = started} = HandlerInfo] when Result =:= ok ->
+ ?hcri("received connect-and-send ack for started handler", []),
HandlerInfo2 = HandlerInfo#handler_info{starter = undefined,
handler = HandlerPid,
state = operational},
@@ -658,28 +695,24 @@ handle_connect_and_send(_StarterPid, ReqId, HandlerPid, Result,
ets:insert(HandlerDb, HandlerInfo2),
ok;
- [#handler_info{from = From}] ->
+ [#handler_info{state = State}] when Result =/= ok ->
error_report(Profile,
- "handler (~p) failed to connect and/or "
+ "handler (~p, ~w) failed to connect and/or "
"send request ~p"
- "~n Error: ~p", [HandlerPid, ReqId, Result]),
- ?hcri("received connect-and-send error", [{result, Result}]),
- Reason2 =
- case Result of
- {error, Reason} ->
- {failed_connecting, Reason};
- _ ->
- {failed_connecting, Result}
- end,
- DummyReq = #request{id = ReqId},
- httpc_response:send(From, httpc_response:error(DummyReq, Reason2)),
- %% gen_server:reply(From, Error),
+ "~n Result: ~p",
+ [HandlerPid, State, ReqId, Result]),
+ ?hcri("received connect-and-send error",
+ [{result, Result}, {state, State}]),
+ %% We don't need to send a response to the original caller
+ %% because the handler already sent one in its terminate
+ %% function.
ets:delete(HandlerDb, ReqId),
ok;
[] ->
error_report(Profile,
- "handler successfully (~p) started for unknown request ~p",
+ "handler (~p) successfully started "
+ "for unknown request ~p => canceling",
[HandlerPid, ReqId]),
httpc_handler:cancel(ReqId, HandlerPid)
end.
@@ -689,13 +722,11 @@ handle_failed_starting_handler(_StarterPid, ReqId, Error,
#state{profile_name = Profile,
handler_db = HandlerDb}) ->
case ets:lookup(HandlerDb, ReqId) of
- [#handler_info{state = canceled,
- from = From}] ->
+ [#handler_info{state = canceled}] ->
error_report(Profile,
"failed starting handler for request ~p"
"~n Error: ~p", [ReqId, Error]),
request_canceled(Profile, ReqId), % Fake signal from handler
- gen_server:reply(From, Error),
ets:delete(HandlerDb, ReqId),
ok;
@@ -703,7 +734,15 @@ handle_failed_starting_handler(_StarterPid, ReqId, Error,
error_report(Profile,
"failed starting handler for request ~p"
"~n Error: ~p", [ReqId, Error]),
- gen_server:reply(From, Error),
+ Reason2 =
+ case Error of
+ {error, Reason} ->
+ {failed_connecting, Reason};
+ _ ->
+ {failed_connecting, Error}
+ end,
+ DummyReq = #request{id = ReqId},
+ httpc_response:send(From, httpc_response:error(DummyReq, Reason2)),
ets:delete(HandlerDb, ReqId),
ok;
@@ -719,10 +758,32 @@ maybe_handle_terminating_starter(MeybeStarterPid, Reason, HandlerDb) ->
Pattern = #handler_info{starter = MeybeStarterPid, _ = '_'},
case ets:match_object(HandlerDb, Pattern) of
[#handler_info{id = ReqId, from = From, state = initiating}] ->
- Error = {error, {failed_starting_request_handler, Reason}},
- gen_server:reply(From, Error),
+ %% The starter process crashed before it could start the
+ %% the handler process, therefor we need to answer the
+ %% original caller.
+ ?hcri("starter process crashed bfore starting handler",
+ [{starter, MeybeStarterPid}, {reason, Reason}]),
+ Reason2 =
+ case Reason of
+ {error, Error} ->
+ {failed_connecting, Error};
+ _ ->
+ {failed_connecting, Reason}
+ end,
+ DummyReq = #request{id = ReqId},
+ httpc_response:send(From, httpc_response:error(DummyReq, Reason2)),
ets:delete(HandlerDb, ReqId),
ok;
+
+ [#handler_info{state = State} = HandlerInfo] ->
+ %% The starter process crashed after the handler was started.
+ %% The handler will answer to the original caller.
+ ?hcri("starter process crashed after starting handler",
+ [{starter, MeybeStarterPid}, {reason, Reason}, {state, State}]),
+ HandlerInfo2 = HandlerInfo#handler_info{starter = undefined},
+ ets:insert(HandlerDb, HandlerInfo2),
+ ok;
+
_ ->
ok
end.
@@ -886,6 +947,9 @@ create_handler_starter(#request{id = Id,
[{id, Id}, {profile, ProfileName}, {result, Result1}]),
case Result1 of
{ok, HandlerPid} ->
+ StartedMessage =
+ {started, self(), Id, HandlerPid},
+ ManagerPid ! StartedMessage,
Result2 = httpc_handler:connect_and_send(Request,
HandlerPid),
?hcrd("handler starter - connected and sent",