[erlang-bugs] httpc:request_cancel oddities

Chris King colanderman@REDACTED
Thu Sep 5 04:11:50 CEST 2013


Trying to send this again, seems to have been eaten by /dev/null last time.

The implementation of httpc:request_cancel is... strange.  In httpc.erl,
we have:

       ok = httpc_manager:cancel_request(RequestId, profile_name(Profile)),
       receive
           %% If the request was already fulfilled throw away the
           %% answer as the request has been canceled.
           {http, {RequestId, _}} ->
               ok
       after 0 ->
               ok
       end.

Which is already suspect because if the {stream, true} option is set, then
there will be *many* such messages, and only the first one will be eaten.

Let's look in httpc_manager.erl:

       %% The request has allready compleated make sure
       %% it is deliverd to the client process queue so
       %% it can be thrown away by httpc:cancel_request
       %% This delay is hopfully a temporary workaround.
       %% Note that it will not not delay the manager,
       %% only the client that called httpc:cancel_request
       timer:apply_after(?DELAY, gen_server, reply, [From, ok]),
       {noreply, State};

Ignoring the implied hope that this gets fixed real soon now, this code
relies on an arbitrary delay that guarantees nothing.  Hence, the behavior
of cancel_request is currently to *maybe* block the first message after it
is invoked, and to let through the rest.

Even worse, *this code needlessly blocks the caller for a full HALF  
SECOND*.  I
spent days tracking down a bug only to find it's caused by this same hacky
code which had bothered me months prior.

Taking streams into account, I feel the best remedy here would be to
simply allow messages from a completed request through, and require the
client to drop them.  This is backward-compatible as the above
nondeterministic behavior requires the client to handle stray messages
anyway.

Further, this request should be made asynchronous; there is no reason for  
it
to block the caller *at all*, let alone for half a second.

i.e.: drop the "timer:apply_after" call; change cancel_request from a call
to a cast; and drop the receive/after 0.



More information about the erlang-bugs mailing list