Index: trunk/http-core/src/java/org/apache/http/protocol/HttpRequestExecutor.java =================================================================== --- trunk/http-core/src/java/org/apache/http/protocol/HttpRequestExecutor.java (revision 380929) +++ trunk/http-core/src/java/org/apache/http/protocol/HttpRequestExecutor.java (working copy) @@ -45,8 +45,13 @@ /** * Sends HTTP requests and receives the responses. - * Takes care of request preprocessing and response postprocessing - * by the respective interceptors. + * Applications typically call the {@link #execute execute} method, + * which takes care of all the necessary steps. + *
+ * The doXxx methods implement the individual steps in + * executing a request, like preprocessing of the request and + * postprocessing of the response by the respective interceptors. + * Those methods are typically not used directly by an application. * * @author Oleg Kalnichevski * @@ -159,6 +164,9 @@ /** * Synchronously send a request and obtain the response. + * This method uses the {@link #getParams parameters} and + * {@link #getContext context} stored in this request executor. + * It is therefore not thread safe. * * @param request the request to send. It will be preprocessed. * @param conn the connection over which to send. @@ -182,12 +190,12 @@ } //@@@ behavior if proxying - set real target or proxy, or both? - this.defaultContext.setAttribute(HttpExecutionContext.HTTP_TARGET_HOST, + this.defaultContext.setAttribute(HttpExecutionContext.HTTP_TARGET_HOST, conn.getTargetHost()); this.defaultContext.setAttribute(HttpExecutionContext.HTTP_CONNECTION, conn); - prepareRequest(request, this.defaultContext); + doPrepareRequest(request, this.defaultContext); this.defaultContext.setAttribute(HttpExecutionContext.HTTP_REQUEST, request); @@ -197,10 +205,12 @@ // returns false or a non-recoverable exception is thrown for (int execCount = 0; ; execCount++) { try { - establishConnection(conn, conn.getTargetHost(), request.getParams()); - response = sendRequest(request, conn, this.defaultContext); + doEstablishConnection(conn, conn.getTargetHost(), + request.getParams()); + response = doSendRequest(request, conn, this.defaultContext); if (response == null) { - response = receiveResponse(request, conn, this.defaultContext); + response = doReceiveResponse(request, conn, + this.defaultContext); } // exit retry loop break; @@ -221,7 +231,7 @@ } } - finishResponse(response, this.defaultContext); + doFinishResponse(response, this.defaultContext); return response; @@ -229,6 +239,12 @@ /** * Prepare a request for sending. + * This method is typically not called by an application directly. + * Use {@link #execute execute} instead. + *
+ * This method may be called by several threads simultaneously, + * but the {@link #getParams parameters} and request interceptors + * MUST NOT be modified concurrently. * * @param request the request to prepare * @param context the context for sending the request @@ -236,7 +252,7 @@ * @throws HttpException in case of a protocol or processing problem * @throws IOException in case of an I/O problem */ - protected void prepareRequest( + public void doPrepareRequest( final HttpRequest request, final HttpContext context) throws HttpException, IOException { @@ -253,6 +269,10 @@ /** * Establish a connection with the target host. + * This method is typically not called by an application directly. + * Use {@link #execute execute} instead. + *
+ * This method may be called by several threads simultaneously. * * @param conn the HTTP connection * @param target the target host for the request, or @@ -262,7 +282,7 @@ * @throws HttpException in case of a problem * @throws IOException in case of an IO problem */ - protected void establishConnection( + public void doEstablishConnection( final HttpClientConnection conn, final HttpHost target, final HttpParams params) @@ -302,22 +322,30 @@ /** * Send a request over a connection. - * This method also handles the expect-continue handshake, if requested. + * This method is typically not called by an application directly. + * Use {@link #execute execute} instead. + *
+ * This method handles the expect-continue handshake if necessary. + * If it does not have to handle an expect-continue handshake, it will + * not use the connection for reading or anything else that depends on + * data coming in over the connection. + *
+ * This method is thread safe. * * @param request the request to send, already - * {@link #prepareRequest prepared} + * {@link #doPrepareRequest prepared} * @param conn the connection over which to send the request, already - * {@link #establishConnection established} + * {@link #doEstablishConnection established} * @param context the context for sending the request * * @return a terminal response received as part of an expect-continue - * handshake or null if the expect-continue handshake - * is not used. + * handshake, or + * null if the expect-continue handshake is not used * * @throws HttpException in case of a protocol or processing problem * @throws IOException in case of an I/O problem */ - protected HttpResponse sendRequest( + public HttpResponse doSendRequest( final HttpRequest request, final HttpClientConnection conn, final HttpContext context) @@ -337,14 +365,16 @@ conn.sendRequestHeader(request); if (request instanceof HttpEntityEnclosingRequest) { - HttpEntityEnclosingRequest entityEnclRequest = (HttpEntityEnclosingRequest) request; + HttpEntityEnclosingRequest entityEnclRequest = + (HttpEntityEnclosingRequest) request; // Check for expect-continue handshake. We have to flush the // headers and wait for an 100-continue response to handle it. // If we get a different response, we must not send the entity. boolean sendentity = true; final HttpVersion ver = request.getRequestLine().getHttpVersion(); - if (entityEnclRequest.expectContinue() && ver.greaterEquals(HttpVersion.HTTP_1_1)) { + if (entityEnclRequest.expectContinue() && + ver.greaterEquals(HttpVersion.HTTP_1_1)) { conn.flush(); // As suggested by RFC 2616 section 8.2.3, we don't wait for a @@ -377,17 +407,23 @@ /** * Wait for and receive a response. + * This method is typically not called by an application directly. + * Use {@link #execute execute} instead. + *
+ * This method will automatically ignore intermediate responses + * with status code 1xx. + * It is thread safe. * * @param request the request for which to obtain the response * @param conn the connection over which the request was sent * @param context the context for receiving the response * - * @return the response, not yet post-processed + * @return the final response, not yet post-processed * * @throws HttpException in case of a protocol or processing problem * @throws IOException in case of an I/O problem */ - protected HttpResponse receiveResponse( + public HttpResponse doReceiveResponse( final HttpRequest request, final HttpClientConnection conn, final HttpContext context) @@ -401,7 +437,7 @@ if (context == null) { throw new IllegalArgumentException("HTTP context may not be null"); } - // see HttpRequestExecutor.doExecute, final part + HttpResponse response = null; int statuscode = 0; @@ -417,14 +453,20 @@ return response; - } // obtainResponse + } /** * Finish a response. - * This includes post-processing of the response object. + * This method is typically not called by an application directly. + * Use {@link #execute execute} instead. + *
+ * This method performs post-processing of the response object. * It does not read the response entity, if any. * It does not allow for immediate re-use of the * connection over which the response is coming in. + *
+ * This method may be called by several threads simultaneously, + * but the response interceptors MUST NOT be modified concurrently. * * @param response the response object to finish * @param context the context for post-processing the response @@ -432,7 +474,7 @@ * @throws HttpException in case of a protocol or processing problem * @throws IOException in case of an I/O problem */ - protected void finishResponse( + public void doFinishResponse( final HttpResponse response, final HttpContext context) throws HttpException, IOException { Index: trunk/http-async/src/java/org/apache/http/async/impl/SimpleHttpDispatcher.java =================================================================== --- trunk/http-async/src/java/org/apache/http/async/impl/SimpleHttpDispatcher.java (revision 380929) +++ trunk/http-async/src/java/org/apache/http/async/impl/SimpleHttpDispatcher.java (working copy) @@ -43,9 +43,9 @@ import org.apache.http.ConnectionReuseStrategy; import org.apache.http.HttpException; import org.apache.http.protocol.HttpContext; +import org.apache.http.protocol.HttpRequestExecutor; import org.apache.http.async.HttpHandle; import org.apache.http.async.HttpDispatcher; -import org.apache.http.async.AsyncHttpProcessor; import org.apache.http.async.AbstractHttpHandle; import org.apache.http.async.AbstractHttpDispatcher; @@ -73,16 +73,17 @@ public class SimpleHttpDispatcher extends AbstractHttpDispatcher implements HttpDispatcher { // @@@ move some of this stuff to an abstract base class? - // @@@ the AsyncHttpProcessor and getDefaultContext method, for example + // @@@ the HttpRequestExecutor and getDefaultContext method, for example /** The one and only connection. */ private final HttpClientConnection client_connection; /** - * The HTTP processor. + * The underlying request executor. * For pre- and postprocessing of requests and responses, respectively. + * Also holds the default context and HTTP parameters. */ - protected final AsyncHttpProcessor async_processor; + protected final HttpRequestExecutor request_executor; /** The connection re-use strategy. */ private ConnectionReuseStrategy reuse_strategy; @@ -120,23 +121,25 @@ * Create a new simple HTTP dispatcher. * * @param conn the connection to use - * @param proc the HTTP processor to use, - * including the default context + * @param reqexec the request executor to use, + * including the default context and params * @param reuse the strategy for re-using connections, or * null to disable connection re-use */ public SimpleHttpDispatcher(HttpClientConnection conn, - AsyncHttpProcessor proc, + HttpRequestExecutor reqexec, ConnectionReuseStrategy reuse) { super(null); if (conn == null) - throw new IllegalArgumentException("connection must not be null"); - if (proc == null) - throw new IllegalArgumentException("processor must not be null"); + throw new IllegalArgumentException + ("connection must not be null"); + if (reqexec == null) + throw new IllegalArgumentException + ("request executor must not be null"); client_connection = conn; - async_processor = proc; + request_executor = reqexec; reuse_strategy = reuse; request_handle_queue = new LinkedList(); @@ -172,7 +175,7 @@ //@@@ - for now: no proxy (how to indicate proxy in the first place?) HttpContext ctxt = prepareRequest - (async_processor, request, target, context); + (request_executor, request, target, context); SimpleHttpHandle hdl = new SimpleHttpHandle(this, request, ctxt); // linked handles need to be tracked for abortAll() @@ -206,7 +209,7 @@ throw new IllegalArgumentException("response must not be null"); handle.checkDispatcher(this); - finishResponse(async_processor, response, handle.getContext()); + finishResponse(request_executor, response, handle.getContext()); } // postprocessResponse @@ -215,7 +218,7 @@ // non-javadoc, see interface HttpDispatcher public final HttpContext getDefaultContext() { - return async_processor.getContext(); + return request_executor.getContext(); } @@ -380,14 +383,14 @@ } //@@@ TODO: retry handling! - transmitRequest(async_processor, + transmitRequest(request_executor, handle.getRequest(), handle.getContext(), client_connection); //@@@ TODO: timeout handling! HttpResponse response = - obtainResponse(async_processor, + obtainResponse(request_executor, handle.getRequest(), handle.getContext(), client_connection); Index: trunk/http-async/src/java/org/apache/http/async/AbstractHttpDispatcher.java =================================================================== --- trunk/http-async/src/java/org/apache/http/async/AbstractHttpDispatcher.java (revision 380929) +++ trunk/http-async/src/java/org/apache/http/async/AbstractHttpDispatcher.java (working copy) @@ -34,16 +34,19 @@ import java.util.LinkedList; import org.apache.http.HttpClientConnection; +import org.apache.http.HttpEntityEnclosingRequest; import org.apache.http.HttpException; import org.apache.http.HttpHost; import org.apache.http.HttpRequest; import org.apache.http.HttpResponse; +import org.apache.http.ProtocolException; import org.apache.http.protocol.HttpContext; +import org.apache.http.protocol.HttpExecutionContext; +import org.apache.http.protocol.HttpRequestExecutor; + /** * Abstract base for implementations of {@link HttpDispatcher HttpDispatcher}. - * Provides access to protected methods in - * {@link AsyncHttpProcessor AsyncHttpProcessor}. * * @version $Revision$ $Date$ * @@ -118,9 +121,8 @@ /** * Prepare a request for sending. - * Maps to {@link AsyncHttpProcessor#prepareRequest proc.prepareRequest}. * - * @param proc the processor to use for preparing + * @param hrx the request executor to use for preparing * @param request the request to prepare * @param target the target host for the request * @param context the parent context for sending the request, @@ -131,22 +133,32 @@ * @throws HttpException if the request can not be prepared * @throws IOException in case of an IO problem */ - protected static HttpContext prepareRequest(AsyncHttpProcessor proc, - HttpRequest request, - HttpHost target, - HttpContext context) + protected static HttpContext prepareRequest(HttpRequestExecutor hrx, + HttpRequest request, + HttpHost target, + HttpContext context) throws HttpException, IOException { - return proc.prepareRequest(request, target, context); + HttpExecutionContext hxc = new HttpExecutionContext(context); + hxc.setAttribute(HttpExecutionContext.HTTP_TARGET_HOST, + target); + + // argument checking is done here... + hrx.doPrepareRequest(request, hxc); + + hxc.setAttribute(HttpExecutionContext.HTTP_REQUEST, + request); + + return hxc; + } // prepareRequest /** - * Send a request. - * Maps to {@link AsyncHttpProcessor#transmitRequest proc.transmitRequest}. + * Establishe a connection and send a request. * - * @param proc the processor to use for transmission + * @param hrx the request executor to use for preparing * @param request the request to send, already prepared * @param context the request specific context returned by * {@link #prepareRequest prepareRequest} @@ -156,22 +168,45 @@ * @throws HttpException in case of a problem * @throws IOException in case of an IO problem */ - protected static void transmitRequest(AsyncHttpProcessor proc, + protected static void transmitRequest(HttpRequestExecutor hrx, HttpRequest request, HttpContext context, HttpClientConnection connection) throws HttpException, IOException { - proc.transmitRequest(request, context, connection); + if (request == null) + throw new IllegalArgumentException("request must not be null"); + if (context == null) + throw new IllegalArgumentException("context must not be null"); + if (connection == null) + throw new IllegalArgumentException("connection must not be null"); + HttpHost target = (HttpHost) + context.getAttribute(HttpExecutionContext.HTTP_TARGET_HOST); + if (target == null) { + throw new IllegalStateException + ("target host missing in request context"); + } + if (request instanceof HttpEntityEnclosingRequest + && ((HttpEntityEnclosingRequest)request).expectContinue()) { + throw new ProtocolException + ("expect-continue handshake not supported"); + } + + hrx.doEstablishConnection(connection, target, request.getParams()); + + context.setAttribute(HttpExecutionContext.HTTP_CONNECTION, + connection); + + hrx.doSendRequest(request, connection, context); + } // transmitRequest /** * Wait for and receive a response. - * Maps to {@link AsyncHttpProcessor#obtainResponse proc.obtainResponse}. * - * @param proc the processor to use for transmission + * @param hrx the request executor to use for preparing * @param request the request for which to obtain the response * @param connection the connection over which the request was sent * @@ -181,21 +216,21 @@ * @throws IOException in case of an IO problem */ protected static - HttpResponse obtainResponse(AsyncHttpProcessor proc, - HttpRequest request, - HttpContext context, - HttpClientConnection connection) + HttpResponse obtainResponse(HttpRequestExecutor hrx, + HttpRequest request, + HttpContext context, + HttpClientConnection connection) throws HttpException, IOException { - return proc.obtainResponse(request, context, connection); + return hrx.doReceiveResponse(request, connection, context); } // obtainResponse /** * Finish a response. - * Maps to {@link AsyncHttpProcessor#finishResponse proc.finishResponse}. * + * @param hrx the request executor to use for preparing * @param response the response object to finish * @param context the context obtained from * {@link #prepareRequest prepareRequest} @@ -204,12 +239,12 @@ * @throws HttpException in case of a problem * @throws IOException in case of an IO problem */ - protected static void finishResponse(AsyncHttpProcessor proc, - HttpResponse response, - HttpContext context) + protected static void finishResponse(HttpRequestExecutor hrx, + HttpResponse response, + HttpContext context) throws HttpException, IOException { - proc.finishResponse(response, context); + hrx.doFinishResponse(response, context); } // finishResponse Index: trunk/http-async/src/examples/org/apache/http/examples/ElementalAsyncGet.java =================================================================== --- trunk/http-async/src/examples/org/apache/http/examples/ElementalAsyncGet.java (revision 380929) +++ trunk/http-async/src/examples/org/apache/http/examples/ElementalAsyncGet.java (working copy) @@ -46,8 +46,8 @@ import org.apache.http.params.HttpProtocolParams; import org.apache.http.async.HttpDispatcher; import org.apache.http.async.HttpHandle; -import org.apache.http.async.AsyncHttpProcessor; import org.apache.http.async.impl.SimpleHttpDispatcher; +import org.apache.http.protocol.HttpRequestExecutor; import org.apache.http.protocol.RequestConnControl; import org.apache.http.protocol.RequestContent; import org.apache.http.protocol.RequestTargetHost; @@ -158,19 +158,19 @@ HttpClientConnection conn = new DefaultHttpClientConnection(); - AsyncHttpProcessor proc = new AsyncHttpProcessor(); - proc.setParams(params); + HttpRequestExecutor hrx = new HttpRequestExecutor(); + hrx.setParams(params); // Required request interceptors - proc.addInterceptor(new RequestContent()); - proc.addInterceptor(new RequestTargetHost()); + hrx.addInterceptor(new RequestContent()); + hrx.addInterceptor(new RequestTargetHost()); // Recommended request interceptors - proc.addInterceptor(new RequestConnControl()); - proc.addInterceptor(new RequestUserAgent()); - // not supported: proc.addInterceptor(new RequestExpectContinue()); + hrx.addInterceptor(new RequestConnControl()); + hrx.addInterceptor(new RequestUserAgent()); + // not supported: hrx.addInterceptor(new RequestExpectContinue()); ConnectionReuseStrategy crs = new DefaultConnectionReuseStrategy(); - HttpDispatcher hdp = new SimpleHttpDispatcher(conn, proc, crs); + HttpDispatcher hdp = new SimpleHttpDispatcher(conn, hrx, crs); return hdp;