Hi Bert,
I had a look at the problem you're trying to solve here (thx for the
private feedback btw).
(for the uninformed: Bert is trying to solve the Subversion 1.8.x
crashes caused by a change introduced in serf 1.3.5.)
I'm seeing the following situation:
A. Serf 0.3.1 and older
------------------------------
Before serf had its own authn implementation, it passed every (part of
an) incoming response to the application by calling the
handle_response callback.
If there was an error while reading the response, the application
always knew about it.
There are basically two ways serf uses the handle_respone callback:
1. there's more data incoming: serf passes the response bucket created
in the accept_response callback in its call to handle_response.
2. the request got cancelled (during a full reset of the connection):
serf calls handle_response with a NULL response and removes the
request from the queue.
B. Serf 0.4 and newer
-----------------------------
Since we implemented an authn layer, serf itself will read at least
the headers of each response (*). If an error happens while serf is
reading the response status line and headers, we have a problem. The
problem is that serf needs to inform the application about these
errors, just like it did before serf had the authn layer, or currently
when authn is disabled.
(*) authn layer is only activated when the application has set the
credentials callback.
You have been trying to implement that notification from serf to the
application, and I totally agree it's a promise serf should keep.
We should stick to the two ways of calling handle_response as
described above: either pass a response bucket, or pass a NULL
response and remove the request from the queue. Your r2258/r2319 tries
to introduce a third way, by calling handle_response with a NULL
response but then not removing the request from the queue. I now think
that was the wrong approach, and therefore I also think that this
r2360 is wrong.
There are 5 places where an error can happen when serf is reading the response:
1. While reading the response status line.
2. While reading the response headers.
3. While discarding the body of a 401/407 response.
4a. In case of 401/407 status code, when trying to find a matching authn scheme
b. When validation authentication with one of serf's authn scheme handlers
c. As a result of a call to the application's credentials callback
5. In case of a 2xx status code, with authn headers (ie. Digest or Negotiate)
I propose the following solution:
For 1, 2 and 3: just call the application's handle_response callback
and pass the response. The application will read from the response and
get the same error status code as serf already read before, but
there's nothing wrong with that. At least this way the application
knows there's an error.
For 4a, if serf doesn't have a matching authentication scheme
implementation, we could just call the application with the 401/407
response and let it try. This enables an application to implement
authn scheme's not implemented by serf.
5 is trickier. When an error happens when parsing the
Authentication-Info header from a 2xx response, we should treat the
response body as untrusted. In this scenario serf must not pass the
response to the application, because it has now way to tell the
application that this body should not be trusted.
I think we can solve this one by discarding the response body, and
then wrapping the response in a new bucket that always returns
SERF_ERROR_AUTHN_FAILED when one of the read or peek functions is
called. We then call the application with this wrapped response
bucket, and basically fallback in the same scenario as 1,2 and 3.
4b and 4c can IMO implemented just like 5. If there's an error during
e.g. Negotiate validation or from the authn callback, we can pass this
as an error to the application and let it do any error
handling/cleanup tasks needed.
In summary: serf will always call the application with a response, but
where an error occurred during authn handling, the response will be
nothing more than a means to provide the application with an error
status.
Does this makes sense to you, from the pov of Subversion/ra_serf also?
Now, I'm not convinced we should do all this in the 1.3.x line. I'm
inclined to release 1.3.6 as (1.3.5 minus r2319) and then implement
the above in serf 1.4.0, for which my plan is to release it before svn
1.9.
Feedback welcome.