Hi Oleg,
thanks for your replies. See my comments inline.
I have quoted sections from both your mails into this one.
>> That results in inconsistent behavior: some 1xx responses are handled
>> by the connection itself, others are returned to the caller. The logic
>> to handle (or ignore) 1xx responses is spread across the code base.
>>
> I do not think this is the case. Only status codes 200 and above are
> ever returned to the caller. See
> DefaultHttpClientConnection#receiveResponse.
Whoops, my mistake. I should have taken the time to check that,
though it was not the main issue I have with the current design.
>>>public interface HttpClientConnection {
>>>
>>> public void sendRequest(HttpRequest req, HttpParams params)
>>>
>>> public void sendEntity(HttpEntity entity, HttpParams params)
>>>
>>> public HttpResponse receiveResponse(HttpParams params)
>>>
>>> public HttpEntity receiveEntity(HttpParams params)
>>>
>>>} // interface HttpClientConnection
>>>
>>>
>>
>>I think the idea to decouple request and entity processing logic as a
>>remedy for the problems identified above is a reasonable one. However, I
>>am not entirely sure it solves all the problems. At some point you will
>>have to assemble the complete HTTP response by combining the entity to
>>the header. However, since HttpResponse represents an immutable message
>>this will not be possible, unless you cast HttpResponse to
>>HttpMutableResponse, thus completely defeating the whole idea of
>>separating those two interfaces in the first place.
My mistake. As you suggested below, I should have used the MutableXxx
interfaces. I'm not yet familiar enough with the new API, but that will
get better over time :-)
The general idea I have in mind is to isolate all knowledge about a
sequence of request/reply/entity particles behind a single interface,
which can be used for both synchronous and asynchronous operation.
Here "knowledge about sequences" includes:
- expect/continue handshake
- redirect handling
- authentication handling
- ... (in case I forgot something :-)
It's a very long way to go, but we could then have different
implementations of that interface for interactive user agents,
proxies, non-interactive robots or spiders, and so on. That's
why I would like to move the expect/continue handshake completely
out of the connection itself.
>>Les us try to redesign the HttpClientConnection interface, however, I do
>>suspect strongly the only clean solution to the problem may be having
>>different interfaces representing different types of connections: a
>>regular client connection, a proxy-side connection and a client
>>connection capable of pipelining requests at the expense of not
>>supporting 1xx status codes. I am afraid we may fail to come up with a
>>reasonably clean interface capable of representing all different types
>>of connections.
I'd rather distinguish the usage scenario on a level on top of the
connections, as mentioned above.
> The HttpClientConnection interface no longer has any notion of a special
> control logic such as the 'expect: continue' handshake. Just plain-vanilla
> send a request, get a response stuff
>
> public interface HttpClientConnection extends HttpConnection {
>
> ...
>
> void sendRequest(HttpRequest request) throws HttpException, IOException;
>
> HttpResponse receiveResponse() throws HttpException, IOException;
>
> }
This will not allow the control logic to be implemented outside of the
connection interface. The control logic for expect/continue, if implemented
outside of the connection, has to indicate "send the request header" versus
"send the request entity". Your suggestion would require a specialized
interface for this, or some hack like passing a flag in the parameters
that are associated with the request. That's not really an improvement
over the current situation.
I guess our different lines of thought are due to the fact that the new API
does not yet have a "sequencer" object like the method director. I would
like to introduce one now for supporting expect/continue (to be extended for
other uses later), while you would like to offer expect/continue without an
additional abstraction layer and a new interface that has to be instantiated,
right?
How about this:
- make the HttpClientConnection generic (following my initial proposal,
but with MutableHttpRequest and MutableHttpEntity)
- provide a static helper to handle the expect/continue handshake, which
provides an interface similar to the current HttpClientConnection
/**
* Helper for straightforward request/response processing.
* This class implements handling of 1xx responses in general and
* the expect/continue handshake in particular.
*/
public final class ExpectContinueHelper {
// disabled default constructor
private ExpectContinueHelper() {
// no body
}
/**
* Sends a request, using expect/continue handshake if applicable.
* If expect/continue handshake is applicable and the server refuses
* to serve the request, this method will not send the request entity,
* but return the response from the server.
* Otherwise, the request and optional request entity get send
* and the response has to be received explicitly using
* [EMAIL PROTECTED] #receiveResponse receiveResponse}.
*
* @param conn the connection for sending and receiving
* @param request the request to send
*
* @return <code>null</code> if the request was sent without problems,
* or a final (non-1xx) response if the server refuses to handle
* the request during an expect/continue handshake
*
* @throws HttpException in case of a protocol problem
* @throws IOException in case of an IO exception
*/
public final static HttpResponse sendRequest(
HttpClientConnection conn,
HttpRequest request
) throws HttpException, IOException {
...
}
/**
* Receives a response.
* This method receives a response after
* [EMAIL PROTECTED] #sendRequest sendRequest}
* has been called and returned <code>null</code>.
*
* @param conn the connection for receiving
* @param request the request for which to receive the response.
* It <i>must</i> have been sent before using
* [EMAIL PROTECTED] #sendRequest sendRequest} over the same
* connection, with <code>null</code>
* as the return value!
*
* @return the final (non-1xx) response to the request
*
* @throws HttpException in case of a protocol problem
* @throws IOException in case of an IO exception
*/
public final static HttpResponse receiveResponse(
HttpClientConnection conn,
HttpRequest request
) throws HttpException, IOException {
...
}
} // class ExpectContinueHelper
This would allow for moving the expect/continue logic outside of the
client connection, while leaving the complexity of the ElementalHttpGet
and ElementalHttpPost examples basically unchanged, and getting people
used to the fact the HTTP is just a little too complex to use an
HttpClientConnection directly.
Of course it would totally screw up the current usage model for the
HttpRequestExceutor, though I could live with the executor being
hard-wired against the ExpectContinueHelper for the time being.
And the name of the helper class is not intuitive, because it has to be
called for all requests and not just for those that will or might use
an expect/continue handshake.
Let me know what you think. I have a hunch we'll need some more
iterations to come up with a sound concept.
cheers,
Roland
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]