[erlang-bugs] httpc: function_clause crash in http_transport:close/2

Ingela Anderton Andin <>
Thu Apr 3 11:26:41 CEST 2014


Hi!

The following diff against current master should solve the problem. I 
have however not investigated if you can apply this directly to R16B01.

diff --git a/lib/inets/src/http_client/httpc_handler.erl 
b/lib/inets/src/http_client/httpc_handler.erl
index 88e08be..6e17678 100644
--- a/lib/inets/src/http_client/httpc_handler.erl
+++ b/lib/inets/src/http_client/httpc_handler.erl
@@ -1378,6 +1378,8 @@ case_insensitive_header(Str) ->
  activate_once(#session{socket = Socket, socket_type = SocketType}) ->
      http_transport:setopts(SocketType, Socket, [{active, once}]).

+close_socket(#session{socket = {remote_close,_}}) ->
+    ok;
  close_socket(#session{socket = Socket, socket_type = SocketType}) ->
      http_transport:close(SocketType, Socket).


Regards Ingela Erlang/OTP team - Ericsson AB



On 04/02/2014 12:39 PM, Magnus Mueller wrote:
> Hello List,
>
> we detected an error in httpc. It happened in R16B01 on Mac OS X, but should be around in new versions as well if my argument below is valid. I could not reproduce the issue yet and think there is a race condition. The error occurred while requesting a page over http. I attached the error report at the end of the mail.
>
>
> ====== Preliminary Analysis
>
> `http_transport:close/2` crashes due to a function_clause error. As the socket argument of the function equals `{remote_close,Socket}`,  and because this is run over http, we can be sure that the following `handle_info/2` clause was run before `terminate/2` was called:
>
> handle_info({tcp_closed, _}, #state{session = Session0} = State) ->
>      Socket  = Session0#session.socket,
>      Session = Session0#session{socket = {remote_close, Socket}},
>      %% {stop, session_remotly_closed, State};
>      {stop, normal, State#state{session = Session}};
>
> This leaves the question why the argument `SocketType` in the call to `http_transport:close/2` is `undefined`. The only reason I could think of was that the initial send  request in  `httpc_handler:connect_and_send_first_request/3` failed. If that happens, `SocketType` is not added to `#session{}`. Also, the field `#session.id` is  `undefined`. The value of `#session.id` leads to the execution of the following `httpc_handler:terminate/2` clause:
>
> terminate(normal,
>            #state{session = #session{id = undefined} = Session}) ->
>      close_socket(Session);
>
> The call `close_socket/1` here then leads to the error reported below.
>
>
> ====== Order of events which might trigger the issue
>
> Summarizing, I think that the following order of events triggers the error report below:
>
> 1. A call to `httpc_handler:connect_and_send_first_request/3`
> 2. The socket is opened successfully, but closes immediately after that, which leads to a `{tcp_closed,Socket}` message being send to the handler process
> 3. The handler tries to send the initial request over the socket and fails, leaving `#session.socket_type` and `#session.id` undefined
> 4. The handler sends `{init_error,error_sending,_}` to itself. This message is BEHIND the `{tcp_closed,_}` message in the queue.
> 5. The handler enters its loop and processes `{tcp_closed,Socket}`
> 6.  The `handle_info/2` call results in `{stop,normal,_}`. `#session.socket` is set to `{ remote_close,Socket}`
> 7. `terminate/2` is called and the clause which matches `#session.id=undefined` is chosen
> 8. This clause runs `httpc_handler:close_socket/1`, which then calls `http_transport:close_socket/2`. This last call fails then.
>
> ====== Possible Fix
>
> If the analysis is correct, fixing this problem should be possible by ensuring that `#session.socket_type` is not set to undefined when `#session.socket` is set. There are also two `terminate/2` clauses to handle initialization errors, but as far as I see they are not called here:
>
> %% Init error there is no socket to be closed.
> terminate(normal,
>            #state{request = Request,
>                   session = {send_failed, AReason} = Reason} = State) ->
>      ?hcrd("terminate", [{send_reason, AReason}, {request, Request}]),
>      maybe_send_answer(Request,
>                        httpc_response:error(Request, Reason),
>                        State),
>      ok;
>
> terminate(normal,
>            #state{request = Request,
>                   session = {connect_failed, AReason} = Reason} = State) ->
>      ?hcrd("terminate", [{connect_reason, AReason}, {request, Request}]),
>      maybe_send_answer(Request,
>                        httpc_response:error(Request, Reason),
>                        State),
>      ok;
>
> ====== Ending notes
>
> As noted in the beginning of the mail, I was not able to reproduce the error below. I tried to add `timer:sleep/1` calls to `httpc_handler` in order to force the order of events above, but that did not help either. Thus, I could be mistaken with my analysis above. I hope someone else can shed some light on this issu
>
> Regards, Magnus
>
> P.s. Here goes the lengthy error report:
>
> =ERROR REPORT==== 10-Mar-2014::16:34:36 ===
> ** Generic server <0.12244.2> terminating
> ** Last message in was {tcp_closed,#Port<0.112265>}
> ** When Server state == {state,
>                              {request,#Ref<0.0.9.188247>,<0.11566.2>,0,http,
>                                  {"127.0.0.1",63178},
>                                  "<retracted>",
>                                  "?<retracted>",get,
>                                  {http_request_h,undefined,"close",undefined,
>                                      undefined,undefined,undefined,undefined,
>                                      undefined,undefined,undefined,undefined,
>                                      undefined,undefined,undefined,undefined,
>                                      undefined,undefined,undefined,undefined,
>                                      undefined,undefined,undefined,undefined,
>                                      undefined,undefined,undefined,undefined,
>                                      undefined,undefined,undefined,undefined,
>                                      "0",undefined,undefined,undefined,
>                                      undefined,undefined,undefined,[]},
>                                  {[],[]},
>                                  {http_options,"HTTP/1.0",4700,true,
>                                      {essl,[]},
>                                      undefined,false,4700,false},
>                                  "http://ip:port/<retracted>",
>                                  [],none,[],1394465676042,undefined,
>                                  [{active,once}],
>                                  false},
>                              {session,undefined,undefined,undefined,
>                                  {remote_close,#Port<0.112265>},
>                                  undefined,1,undefined,false},
>                              undefined,undefined,undefined,undefined,
>                              {[],[]},
>                              {[],[]},
>                              undefined,[],nolimit,nolimit,
>                              {options,
>                                  {undefined,[]},
>                                  {undefined,[]},
>                                  0,0,0,120000,0,disabled,false,inet,default,
>                                  default,[]},
>                              {timers,[],undefined},
>                              httpc_manager,inactive}
> ** Reason for termination ==
> ** {function_clause,[{http_transport,close,
>                                       [undefined,
>                                        {remote_close,#Port<0.112265>}],
>                                       [{file,"http_transport.erl"},{line,422}]},
>                       {gen_server,terminate,6,
>                                   [{file,"gen_server.erl"},{line,719}]},
>                       {proc_lib,init_p_do_apply,3,
>                                 [{file,"proc_lib.erl"},{line,239}]}]}
> _______________________________________________
> erlang-bugs mailing list
> 
> http://erlang.org/mailman/listinfo/erlang-bugs
>



More information about the erlang-bugs mailing list