hc-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ol...@apache.org
Subject svn commit: r378636 - in /jakarta/httpcomponents/trunk: http-async/src/examples/org/apache/http/examples/ http-async/src/java/org/apache/http/async/ http-async/src/java/org/apache/http/async/impl/ http-core/src/examples/org/apache/http/examples/ http-c...
Date Fri, 17 Feb 2006 21:42:17 GMT
Author: olegk
Date: Fri Feb 17 13:42:16 2006
New Revision: 378636

URL: http://svn.apache.org/viewcvs?rev=378636&view=rev
Log:
PR #38624 (refactor HttpRequestExecutor and AsyncHttpProcessor)

Contributed by Roland Weber
Reviewed by Oleg Kalnichevski

Modified:
    jakarta/httpcomponents/trunk/http-async/src/examples/org/apache/http/examples/ElementalAsyncGet.java
    jakarta/httpcomponents/trunk/http-async/src/java/org/apache/http/async/AbstractHttpDispatcher.java
    jakarta/httpcomponents/trunk/http-async/src/java/org/apache/http/async/AsyncHttpProcessor.java
    jakarta/httpcomponents/trunk/http-async/src/java/org/apache/http/async/impl/SimpleHttpDispatcher.java
    jakarta/httpcomponents/trunk/http-core/src/examples/org/apache/http/examples/ElementalHttpServer.java
    jakarta/httpcomponents/trunk/http-core/src/java/org/apache/http/protocol/HttpRequestExecutor.java

Modified: jakarta/httpcomponents/trunk/http-async/src/examples/org/apache/http/examples/ElementalAsyncGet.java
URL: http://svn.apache.org/viewcvs/jakarta/httpcomponents/trunk/http-async/src/examples/org/apache/http/examples/ElementalAsyncGet.java?rev=378636&r1=378635&r2=378636&view=diff
==============================================================================
--- jakarta/httpcomponents/trunk/http-async/src/examples/org/apache/http/examples/ElementalAsyncGet.java
(original)
+++ jakarta/httpcomponents/trunk/http-async/src/examples/org/apache/http/examples/ElementalAsyncGet.java
Fri Feb 17 13:42:16 2006
@@ -80,12 +80,12 @@
         if ((targets == null) || (targets.length < 1)) {
             targets = new String[] {
                 "/",
-                "/manual/", 
+                "/servlets-examples/servlet/RequestInfoExample", 
                 "/somewhere%20in%20pampa"
             };
         }
 
-        HttpHost     host    = new HttpHost("localhost", 80);
+        HttpHost     host    = new HttpHost("localhost", 8080);
         HttpHandle[] handles = new HttpHandle[targets.length];
 
         for (int i = 0; i < targets.length; i++) {
@@ -159,6 +159,7 @@
         HttpClientConnection conn = new DefaultHttpClientConnection();
 
         AsyncHttpProcessor proc = new AsyncHttpProcessor();
+        proc.setParams(params);
         // Required request interceptors
         proc.addInterceptor(new RequestContent());
         proc.addInterceptor(new RequestTargetHost());
@@ -169,7 +170,7 @@
 
         ConnectionReuseStrategy crs = new DefaultConnectionReuseStrategy();
 
-        HttpDispatcher hdp = new SimpleHttpDispatcher(conn, proc, crs, params);
+        HttpDispatcher hdp = new SimpleHttpDispatcher(conn, proc, crs);
 
         return hdp;
 

Modified: jakarta/httpcomponents/trunk/http-async/src/java/org/apache/http/async/AbstractHttpDispatcher.java
URL: http://svn.apache.org/viewcvs/jakarta/httpcomponents/trunk/http-async/src/java/org/apache/http/async/AbstractHttpDispatcher.java?rev=378636&r1=378635&r2=378636&view=diff
==============================================================================
--- jakarta/httpcomponents/trunk/http-async/src/java/org/apache/http/async/AbstractHttpDispatcher.java
(original)
+++ jakarta/httpcomponents/trunk/http-async/src/java/org/apache/http/async/AbstractHttpDispatcher.java
Fri Feb 17 13:42:16 2006
@@ -39,10 +39,8 @@
 import org.apache.http.HttpHost;
 import org.apache.http.HttpMutableResponse;
 import org.apache.http.HttpRequest;
-import org.apache.http.params.HttpParams;
 import org.apache.http.protocol.HttpContext;
 
-
 /**
  * Abstract base for implementations of {@link HttpDispatcher HttpDispatcher}.
  * Provides access to protected methods in
@@ -126,7 +124,6 @@
      * @param proc        the processor to use for preparing
      * @param request     the request to prepare
      * @param target      the target host for the request
-     * @param params      the default parameters
      * @param context     the parent context for sending the request,
      *                    or <code>null</code> to use the default context
      *
@@ -138,11 +135,10 @@
     protected static HttpContext prepareRequest(AsyncHttpProcessor proc,
                                                 HttpRequest        request,
                                                 HttpHost           target,
-                                                HttpParams         params,
                                                 HttpContext        context)
         throws HttpException, IOException {
 
-        return proc.prepareRequest(request, target, params, context);
+        return proc.prepareRequest(request, target, context);
 
     } // prepareRequest
 
@@ -188,10 +184,11 @@
     protected static
         HttpMutableResponse obtainResponse(AsyncHttpProcessor   proc,
                                            HttpRequest          request,
+                                           HttpContext          context,
                                            HttpClientConnection connection)
         throws HttpException, IOException {
 
-        return proc.obtainResponse(request, connection);
+        return proc.obtainResponse(request, context, connection);
 
     } // obtainResponse
 

Modified: jakarta/httpcomponents/trunk/http-async/src/java/org/apache/http/async/AsyncHttpProcessor.java
URL: http://svn.apache.org/viewcvs/jakarta/httpcomponents/trunk/http-async/src/java/org/apache/http/async/AsyncHttpProcessor.java?rev=378636&r1=378635&r2=378636&view=diff
==============================================================================
--- jakarta/httpcomponents/trunk/http-async/src/java/org/apache/http/async/AsyncHttpProcessor.java
(original)
+++ jakarta/httpcomponents/trunk/http-async/src/java/org/apache/http/async/AsyncHttpProcessor.java
Fri Feb 17 13:42:16 2006
@@ -29,54 +29,37 @@
 
 package org.apache.http.async;
 
-
 import java.io.IOException;
 
-import org.apache.http.HttpHost;
-import org.apache.http.HttpRequest;
-import org.apache.http.HttpMutableRequest;
-import org.apache.http.HttpEntityEnclosingRequest;
-import org.apache.http.HttpResponse;
-import org.apache.http.HttpMutableResponse;
 import org.apache.http.HttpClientConnection;
-import org.apache.http.HttpStatus;
+import org.apache.http.HttpEntityEnclosingRequest;
 import org.apache.http.HttpException;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpMutableResponse;
+import org.apache.http.HttpRequest;
+import org.apache.http.ProtocolException;
 import org.apache.http.protocol.HttpContext;
 import org.apache.http.protocol.HttpExecutionContext;
-import org.apache.http.protocol.AbstractHttpProcessor;
-import org.apache.http.params.HttpParams;
-import org.apache.http.params.HttpConnectionParams;
-
-
+import org.apache.http.protocol.HttpRequestExecutor;
 
 /**
  * HTTP processor for asynchronously dispatched requests.
  * This is the asynchronous equivalent to
  * {@link org.apache.http.protocol.HttpRequestExecutor HttpRequestExecutor}.
- * <!-- @@@ review to factor out duplicate code -->
  *
  * @version $Revision$ $Date$
  * 
  * @since 4.0
  */
-public class AsyncHttpProcessor extends AbstractHttpProcessor {
-
-    /** The default (parent) context. */
-    private HttpContext default_context;
-
-
+public class AsyncHttpProcessor extends HttpRequestExecutor {
+    
     /**
      * Create a new HTTP processor with the given default context.
      *
      * @param context     the default context
      */
     public AsyncHttpProcessor(HttpContext context) {
-
-        if (context == null)
-            throw new IllegalArgumentException
-                ("default context must not be null");
-
-        default_context = context;
+        super(context);
     }
 
 
@@ -84,17 +67,7 @@
      * Create a new HTTP processor with empty default context.
      */
     public AsyncHttpProcessor() {
-        this(new HttpExecutionContext(null));
-    }
-
-    /**
-     * Obtain the default context.
-     *
-     * @return    the default context, or <code>null</code> if there is none
-     */
-    public final HttpContext getDefaultContext() {
-
-        return default_context;
+        this(null);
     }
 
 
@@ -103,7 +76,6 @@
      *
      * @param request     the request to prepare
      * @param target      the target host for the request
-     * @param params      the default parameters
      * @param context     the parent context for sending the request,
      *                    or <code>null</code> to use the default context
      *
@@ -114,30 +86,18 @@
      */
     protected HttpContext prepareRequest(HttpRequest request,
                                          HttpHost    target,
-                                         HttpParams  params,
                                          HttpContext context)
         throws HttpException, IOException {
 
-        if (request == null)
-            throw new IllegalArgumentException("request must not be null");
-        if (context == null)
-            context = default_context;
-
-        // see also HttpRequestExecutor.execute(), initial part
-
         HttpExecutionContext hxc = new HttpExecutionContext(context);
-        hxc.setAttribute(HttpExecutionContext.HTTP_REQUEST, request);
-        //@@@ behavior if proxying - set real target or proxy, or both?
-        hxc.setAttribute(HttpExecutionContext.HTTP_TARGET_HOST, target);
-
-        //@@@ Can't set connection in the context, it's not available yet.
-        //@@@ Is it required for one of the interceptors?
+        hxc.setAttribute(HttpExecutionContext.HTTP_TARGET_HOST, 
+                target);
 
-        // link default parameters
-        request.getParams().setDefaults(params);
+        // argument checking is done here...
+        super.prepareRequest(request, hxc);
 
-        if (request instanceof HttpMutableRequest)
-            preprocessRequest((HttpMutableRequest) request, hxc);
+        hxc.setAttribute(HttpExecutionContext.HTTP_REQUEST, 
+                request);
 
         return hxc;
 
@@ -163,68 +123,33 @@
 
         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");
+        if (context == null)
+            throw new IllegalArgumentException("context must not be null");
 
         HttpHost target = (HttpHost)
             context.getAttribute(HttpExecutionContext.HTTP_TARGET_HOST);
-        if (target == null)
+        if (target == null) {
             throw new IllegalStateException
                 ("target host missing in request context");
-
-        // see also HttpRequestExecutor.execute() and .doExecute()
-        // Retry handling should be implemented by caller, since the retry
-        // may be triggered by receiving the response, too.
-
-        // make sure the connection is open and points to the target host
-        HttpParams params = request.getParams();
-        if (target.equals(connection.getTargetHost())) {
-
-            // host and port ok, check whether connection needs to be opened
-            if (HttpConnectionParams.isStaleCheckingEnabled(params)) {
-                if (connection.isOpen() && connection.isStale()) {
-                    connection.close();
-                }
-            }
-            if (!connection.isOpen()) {
-                connection.open(params);
-                //TODO: Implement secure tunnelling (@@@ HttpRequestExecutor) 
-            }
-
-        } else {
-
-            // wrong target, point connection to target
-            if (connection.isOpen())
-                connection.close();
-            connection.setTargetHost(target);
-            connection.open(params);
-
-        } // if connection points to target else
-
-
-        // this is the initial part of HttpRequestExecutor.doExecute,
-        // minus the handling of expect/continue handshakes
-
-        context.setAttribute(HttpExecutionContext.HTTP_REQ_SENT,
-                             Boolean.FALSE);
-
-        connection.sendRequestHeader(request);
-        if (request instanceof HttpEntityEnclosingRequest) {
-            connection.sendRequestEntity((HttpEntityEnclosingRequest) request);
         }
-        connection.flush();
-
-        context.setAttribute(HttpExecutionContext.HTTP_REQ_SENT,
-                             Boolean.TRUE);
+        if (request instanceof HttpEntityEnclosingRequest 
+                && ((HttpEntityEnclosingRequest)request).expectContinue()) {
+            throw new ProtocolException("Expect-continue handshake not supported"); 
+        }
+        super.establishConnection(connection, target, request.getParams());
+        
+        context.setAttribute(HttpExecutionContext.HTTP_CONNECTION, 
+                connection);
+        
+        super.sendRequest(request, connection, context);
 
     } // transmitRequest
 
 
     /**
      * Wait for and receive a response.
-     * <i @@@>clarify sematics - will the response body be received?</i>
      *
      * @param request     the request for which to obtain the response
      * @param connection  the connection over which the request was sent
@@ -236,62 +161,17 @@
      */
     protected
         HttpMutableResponse obtainResponse(HttpRequest          request,
+                                           HttpContext          context,
                                            HttpClientConnection connection)
         throws HttpException, IOException {
 
-        if (request == null)
-            throw new IllegalArgumentException("request must not be null");
-        if (connection == null)
-            throw new IllegalArgumentException("connection must not be null");
-
-        // see HttpRequestExecutor.doExecute, final part
-        HttpMutableResponse response = null;
-        int statuscode = 0;
-
-        // skip 1xx responses
-        while (statuscode < HttpStatus.SC_OK) {
-            response = connection.receiveResponseHeader(request.getParams());
-
-            //@@@ does this actually receive, or just wrap the stream?
-            //@@@ how to make sure we get only a wrapped stream here?
-            //@@@ don't call receiveResponseEntity here at all?
-            //@@@ could there be 1xx responses with an entity? check RFC 2616
-            if (canResponseHaveBody(request, response)) {
-                connection.receiveResponseEntity(response);
-            }
-            statuscode = response.getStatusLine().getStatusCode();
-
-        } // while intermediate response
-
-        return response;
+        // argument checking is done here...
+        return super.receiveResponse(request, connection, context);
 
     } // obtainResponse
 
 
     /**
-     * Decide whether a response comes with an entity.
-     *
-     * @param request   the request responded to
-     * @param response  the response, initialized from headers only
-     *
-     * @return  <code>true</code> if the response should have an entity, or
-     *          <code>false</code> if it doesn't
-     */    
-    //@@@ duplicated from HttpRequestExecutor.canResponseHaveBody
-    private boolean canResponseHaveBody(final HttpRequest request,
-                                        final HttpResponse response) {
-        if ("HEAD".equalsIgnoreCase(request.getRequestLine().getMethod())) {
-            return false;
-        }
-        int status = response.getStatusLine().getStatusCode(); 
-        return status >= HttpStatus.SC_OK 
-            && status != HttpStatus.SC_NO_CONTENT 
-            && status != HttpStatus.SC_NOT_MODIFIED
-            && status != HttpStatus.SC_RESET_CONTENT; 
-        }
-
-
-    /**
      * Finish a response.
      * This includes post-processing of the response object.
      * It does <i>not</i> read the response entity (if any), nor allows
@@ -309,12 +189,8 @@
                                   HttpContext context)
         throws HttpException, IOException {
 
-        if (response == null)
-            throw new IllegalArgumentException("response must not be null");
-        if (context == null)
-            throw new IllegalArgumentException("context must not be null");
-
-        postprocessResponse(response, context);
+        // argument checking is done here...
+        super.finishResponse(response, context);
 
     } // finishResponse
 

Modified: jakarta/httpcomponents/trunk/http-async/src/java/org/apache/http/async/impl/SimpleHttpDispatcher.java
URL: http://svn.apache.org/viewcvs/jakarta/httpcomponents/trunk/http-async/src/java/org/apache/http/async/impl/SimpleHttpDispatcher.java?rev=378636&r1=378635&r2=378636&view=diff
==============================================================================
--- jakarta/httpcomponents/trunk/http-async/src/java/org/apache/http/async/impl/SimpleHttpDispatcher.java
(original)
+++ jakarta/httpcomponents/trunk/http-async/src/java/org/apache/http/async/impl/SimpleHttpDispatcher.java
Fri Feb 17 13:42:16 2006
@@ -29,7 +29,6 @@
 
 package org.apache.http.async.impl;
 
-
 import java.io.IOException;
 import java.util.List;
 import java.util.LinkedList;
@@ -45,15 +44,12 @@
 import org.apache.http.ConnectionReuseStrategy;
 import org.apache.http.HttpException;
 import org.apache.http.protocol.HttpContext;
-import org.apache.http.params.HttpParams;
 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;
 
-
-
 /**
  * Minimal implementation of an {@link HttpDispatcher HttpDispatcher}.
  * This dispatcher uses a single connection and background thread.
@@ -78,6 +74,7 @@
 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 one and only connection. */
     private final HttpClientConnection client_connection;
@@ -91,9 +88,6 @@
     /** The connection re-use strategy. */
     private ConnectionReuseStrategy reuse_strategy;
 
-    /** The parameters to use. */
-    private HttpParams http_params;
-
     /** The background thread. */
     private SimpleHttpDispatcherThread background_thread;
 
@@ -131,25 +125,20 @@
      *                  including the default context
      * @param reuse     the strategy for re-using connections, or
      *                  <code>null</code> to disable connection re-use
-     * @param params    the parameters to use
      */
     public SimpleHttpDispatcher(HttpClientConnection    conn,
                                 AsyncHttpProcessor      proc,
-                                ConnectionReuseStrategy reuse,
-                                HttpParams              params) {
+                                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");
-        if (params == null)
-            throw new IllegalArgumentException("parameters must not be null");
 
         client_connection = conn;
         async_processor   = proc;
         reuse_strategy    = reuse;
-        http_params       = params;
 
         request_handle_queue = new LinkedList();
         response_handles     = new LinkedList();
@@ -184,7 +173,7 @@
         //@@@ - for now: no proxy (how to indicate proxy in the first place?)
 
         HttpContext ctxt = prepareRequest
-            (async_processor, request, target, http_params, context);
+            (async_processor, request, target, context);
         SimpleHttpHandle hdl = new SimpleHttpHandle(this, request, ctxt);
 
         // linked handles need to be tracked for abortAll()
@@ -227,7 +216,7 @@
     // non-javadoc, see interface HttpDispatcher
     public final HttpContext getDefaultContext() {
 
-        return async_processor.getDefaultContext();
+        return async_processor.getContext();
     }
 
 
@@ -401,6 +390,7 @@
                         HttpMutableResponse response = 
                             obtainResponse(async_processor,
                                            handle.getRequest(),
+                                           handle.getContext(),
                                            client_connection);
                         handle.provideResponse(response);
 

Modified: jakarta/httpcomponents/trunk/http-core/src/examples/org/apache/http/examples/ElementalHttpServer.java
URL: http://svn.apache.org/viewcvs/jakarta/httpcomponents/trunk/http-core/src/examples/org/apache/http/examples/ElementalHttpServer.java?rev=378636&r1=378635&r2=378636&view=diff
==============================================================================
--- jakarta/httpcomponents/trunk/http-core/src/examples/org/apache/http/examples/ElementalHttpServer.java
(original)
+++ jakarta/httpcomponents/trunk/http-core/src/examples/org/apache/http/examples/ElementalHttpServer.java
Fri Feb 17 13:42:16 2006
@@ -94,7 +94,7 @@
                 StringEntity body = new StringEntity("File not found", "UTF-8");
                 response.setEntity(body);
                 System.out.println("File " + file.getPath() + " not found");
-            } else if (!file.canRead()) {
+            } else if (!file.canRead() || file.isDirectory()) {
                 response.setStatusCode(HttpStatus.SC_FORBIDDEN);
                 StringEntity body = new StringEntity("Access Denied", "UTF-8");
                 response.setEntity(body);

Modified: jakarta/httpcomponents/trunk/http-core/src/java/org/apache/http/protocol/HttpRequestExecutor.java
URL: http://svn.apache.org/viewcvs/jakarta/httpcomponents/trunk/http-core/src/java/org/apache/http/protocol/HttpRequestExecutor.java?rev=378636&r1=378635&r2=378636&view=diff
==============================================================================
--- jakarta/httpcomponents/trunk/http-core/src/java/org/apache/http/protocol/HttpRequestExecutor.java
(original)
+++ jakarta/httpcomponents/trunk/http-core/src/java/org/apache/http/protocol/HttpRequestExecutor.java
Fri Feb 17 13:42:16 2006
@@ -35,6 +35,7 @@
 import org.apache.http.HttpClientConnection;
 import org.apache.http.HttpEntityEnclosingRequest;
 import org.apache.http.HttpException;
+import org.apache.http.HttpHost;
 import org.apache.http.HttpMutableRequest;
 import org.apache.http.HttpMutableResponse;
 import org.apache.http.HttpRequest;
@@ -57,31 +58,97 @@
  */
 public class HttpRequestExecutor extends AbstractHttpProcessor {
 
-    private static final int WAIT_FOR_CONTINUE_MS = 10000;
-    
-    private final HttpContext context;
+    protected static final int WAIT_FOR_CONTINUE_MS = 10000;
+
+    /** The context holding the default context information. */    
+    protected final HttpContext defaultContext;
     
     private HttpParams params = null;
     private HttpRequestRetryHandler retryhandler = null;
-    
+
+    /**
+     * Create a new request executor with default context information.
+     * The attributes in the argument context will be made available
+     * in the context used for executing a request.
+     *
+     * @param parentContext     the default context information,
+     *                          or <code>null</code>
+     */    
     public HttpRequestExecutor(final HttpContext parentContext) {
         super();
-        this.context = new HttpExecutionContext(parentContext);
+        this.defaultContext = new HttpExecutionContext(parentContext);
     }
-    
+
+    /**
+     * Create a new request executor.
+     */
     public HttpRequestExecutor() {
         this(null);
     }
-    
-    public HttpParams getParams() {
+
+    /**
+     * Obtain the default context information.
+     * This is not necessarily the same object passed to the constructor,
+     * but the default context information will be available here.
+     *
+     * @return  the context holding the default context information
+     */
+    public final HttpContext getContext() {
+        return this.defaultContext;
+    }
+
+    /**
+     * Obtain the parameters for executing requests.
+     *
+     * @return  the currently installed parameters
+     */
+    public final HttpParams getParams() {
         return this.params;
     }
 
-    public void setParams(final HttpParams params) {
+    /**
+     * Set new parameters for executing requests.
+     *
+     * @param params    the new parameters to use from now on
+     */
+    public final void setParams(final HttpParams params) {
         this.params = params;
     }
-    
-    private boolean canResponseHaveBody(final HttpRequest request, final HttpResponse response)
{
+
+    /**
+     * Obtain the retry handler.
+     *
+     * @return  the handler deciding whether a request should be retried
+     */
+    public final HttpRequestRetryHandler getRetryHandler() {
+        return this.retryhandler;
+    }
+
+    /**
+     * Set the retry handler.
+     *
+     * @param retryhandler      the handler to decide whether a request
+     *                          should be retried
+     */
+    public final void setRetryHandler(final HttpRequestRetryHandler retryhandler) {
+        this.retryhandler = retryhandler;
+    }
+
+    /**
+     * Decide whether a response comes with an entity.
+     * The implementation in this class is based on RFC 2616.
+     * Unknown methods and response codes are supposed to
+     * indicate responses with an entity.
+     * <br/>
+     * Derived executors can override this method to handle
+     * methods and response codes not specified in RFC 2616.
+     *
+     * @param request   the request, to obtain the executed method
+     * @param response  the response, to obtain the status code
+     */
+    protected boolean canResponseHaveBody(final HttpRequest request,
+                                          final HttpResponse response) {
+
         if ("HEAD".equalsIgnoreCase(request.getRequestLine().getMethod())) {
             return false;
         }
@@ -92,92 +159,51 @@
             && status != HttpStatus.SC_RESET_CONTENT; 
     }
 
-    private HttpMutableResponse doExecute(
-            final HttpRequest request, final HttpClientConnection conn)
+    /**
+     * Synchronously send a request and obtain the response.
+     *
+     * @param request   the request to send. It will be preprocessed.
+     * @param conn      the connection over which to send.
+     *                  The {@link HttpClientConnection#setTargetHost target}
+     *                  host has to be set before calling this method.
+     *
+     * @return  the response to the request, postprocessed
+     *
+     * @throws HttpException      in case of a protocol or processing problem
+     * @throws IOException        in case of an I/O problem
+     */    
+    public HttpResponse execute(
+            final HttpRequest request,
+            final HttpClientConnection conn) 
                 throws IOException, HttpException {
-        HttpMutableResponse response = null;
-        this.context.setAttribute(HttpExecutionContext.HTTP_REQ_SENT, 
-                new Boolean(false));
-        // Send request header
-        conn.sendRequestHeader(request);
-        if (request instanceof HttpEntityEnclosingRequest) {
-            HttpVersion ver = request.getRequestLine().getHttpVersion();
-            if (ver.greaterEquals(HttpVersion.HTTP_1_1) 
-                    && ((HttpEntityEnclosingRequest)request).expectContinue()) {
-                // Flush headers
-                conn.flush();
-                if (conn.isResponseAvailable(WAIT_FOR_CONTINUE_MS)) {
-                    response = conn.receiveResponseHeader(this.params);
-                    if (canResponseHaveBody(request, response)) {
-                        conn.receiveResponseEntity(response);
-                    }
-                    int status = response.getStatusLine().getStatusCode();
-                    if (status < 200) {
-                        if (status != HttpStatus.SC_CONTINUE) {
-                            throw new ProtocolException("Unexpected response: " + 
-                                    response.getStatusLine());
-                        }
-                    } else {
-                        return response;
-                    }                    
-                }
-            }
-            conn.sendRequestEntity((HttpEntityEnclosingRequest) request);
-        }
-        conn.flush();
-        
-        this.context.setAttribute(HttpExecutionContext.HTTP_REQ_SENT, 
-                new Boolean(true)); 
-        for (;;) {
-            // Loop until non 1xx resposne is received
-            response = conn.receiveResponseHeader(this.params);
-            if (canResponseHaveBody(request, response)) {
-                conn.receiveResponseEntity(response);
-            }
-            int statuscode = response.getStatusLine().getStatusCode();
-            if (statuscode >= HttpStatus.SC_OK) {
-                break;
-            }
-        }
-        return response;
-    }
-    
-    public HttpResponse execute(final HttpRequest request, final HttpClientConnection conn)

-            throws IOException, HttpException {
-        
         if (request == null) {
             throw new IllegalArgumentException("HTTP request may not be null");
         }
         if (conn == null) {
             throw new IllegalArgumentException("Client connection may not be null");
         }
-        this.context.setAttribute(HttpExecutionContext.HTTP_REQUEST, request);
-        this.context.setAttribute(HttpExecutionContext.HTTP_CONNECTION, conn);
-        this.context.setAttribute(HttpExecutionContext.HTTP_TARGET_HOST, 
-        		conn.getTargetHost());
 
-        // Link own parameters as defaults 
-        request.getParams().setDefaults(this.params);
+        //@@@ behavior if proxying - set real target or proxy, or both?
+        this.defaultContext.setAttribute(HttpExecutionContext.HTTP_TARGET_HOST, 
+                conn.getTargetHost());
+        this.defaultContext.setAttribute(HttpExecutionContext.HTTP_CONNECTION, 
+                conn);
         
-        if (request instanceof HttpMutableRequest) {
-            preprocessRequest((HttpMutableRequest)request, this.context);
-        }
+        prepareRequest(request, this.defaultContext);
+
+        this.defaultContext.setAttribute(HttpExecutionContext.HTTP_REQUEST, 
+                request);
         
         HttpMutableResponse response = null;
         // loop until the method is successfully processed, the retryHandler 
         // returns false or a non-recoverable exception is thrown
         for (int execCount = 0; ; execCount++) {
             try {
-                if (HttpConnectionParams.isStaleCheckingEnabled(this.params)) {
-                    if (conn.isOpen() && conn.isStale()) {
-                        conn.close();
-                    }
+                establishConnection(conn, conn.getTargetHost(), request.getParams());
+                response = sendRequest(request, conn, this.defaultContext);
+                if (response == null) {
+                    response = receiveResponse(request, conn, this.defaultContext);
                 }
-                if (!conn.isOpen()) {
-                    conn.open(this.params);
-                    // TODO: Implement secure tunnelling
-                }
-                response = doExecute(request, conn);
                 // exit retry loop
                 break;
             } catch (IOException ex) {
@@ -196,16 +222,231 @@
                 throw ex;
             }
         }
-        postprocessResponse(response, this.context);
+
+        finishResponse(response, this.defaultContext);
+
         return response;
+
     }
 
-    public HttpRequestRetryHandler getRetryHandler() {
-        return this.retryhandler;
+    /**
+     * Prepare a request for sending.
+     *
+     * @param request   the request to prepare
+     * @param context   the context for sending the request
+     *
+     * @throws HttpException      in case of a protocol or processing problem
+     * @throws IOException        in case of an I/O problem
+     */
+    protected void prepareRequest(
+            final HttpRequest          request,
+            final HttpContext          context)
+                throws HttpException, IOException {
+        if (request == null) {
+            throw new IllegalArgumentException("HTTP request may not be null");
+        }
+        if (context == null) {
+            throw new IllegalArgumentException("HTTP context may not be null");
+        }
+        // link default parameters
+        request.getParams().setDefaults(this.params);
+        if (request instanceof HttpMutableRequest) {
+            preprocessRequest((HttpMutableRequest) request, context);
+        }
     }
 
-    public void setRetryHandler(final HttpRequestRetryHandler retryhandler) {
-        this.retryhandler = retryhandler;
+    /**
+     * Establish a connection with the target host.
+     *
+     * @param conn      the HTTP connection
+     * @param target    the target host for the request, or
+     *                  <code>null</code> to send to the host already
+     *                  set as the connection target
+     *
+     * @throws HttpException      in case of a problem
+     * @throws IOException        in case of an IO problem
+     */
+    protected void establishConnection(
+            final HttpClientConnection conn,
+            final HttpHost target,
+            final HttpParams params)
+                throws HttpException, IOException {
+        if (conn == null) {
+            throw new IllegalArgumentException("HTTP connection may not be null");
+        }
+        if (target == null) {
+            throw new IllegalArgumentException("Target host may not be null");
+        }
+        if (params == null) {
+            throw new IllegalArgumentException("HTTP parameters may not be null");
+        }
+        // make sure the connection is open and points to the target host
+        if ((target == null) || target.equals(conn.getTargetHost())) {
+            // host and port ok, check whether connection needs to be opened
+            if (HttpConnectionParams.isStaleCheckingEnabled(params)) {
+                if (conn.isOpen() && conn.isStale()) {
+                    conn.close();
+                }
+            }
+            if (!conn.isOpen()) {
+                conn.open(params);
+                //TODO: Implement secure tunnelling (@@@ HttpRequestExecutor) 
+            }
+
+        } else {
+            // wrong target, point connection to target
+            if (conn.isOpen()) {
+                conn.close();
+            }
+            conn.setTargetHost(target);
+            conn.open(params);
+
+        } // if connection points to target else        
     }
-    
-}
+
+    /**
+     * Send a request over a connection.
+     * This method also handles the expect-continue handshake, if requested.
+     *
+     * @param request   the request to send, already
+     *                  {@link #prepareRequest prepared}
+     * @param conn      the connection over which to send the request, already
+     *                  {@link #establishConnection established}
+     * @param context   the context for sending the request
+     *
+     * @return  a terminal response received as part of an expect-continue
+     *          handshake or <code>null</code> 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 HttpMutableResponse sendRequest(
+            final HttpRequest request,
+            final HttpClientConnection conn,
+            final HttpContext context)
+                throws IOException, HttpException {
+        if (request == null) {
+            throw new IllegalArgumentException("HTTP request may not be null");
+        }
+        if (conn == null) {
+            throw new IllegalArgumentException("HTTP connection may not be null");
+        }
+        if (context == null) {
+            throw new IllegalArgumentException("HTTP context may not be null");
+        }
+
+        HttpMutableResponse response = null;
+        context.setAttribute(HttpExecutionContext.HTTP_REQ_SENT, Boolean.FALSE);
+
+        conn.sendRequestHeader(request);
+        if (request instanceof HttpEntityEnclosingRequest) {
+            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))
{
+
+                conn.flush();
+                // As suggested by RFC 2616 section 8.2.3, we don't wait for a
+                // 100-continue response forever. On timeout, send the entity.
+                if (conn.isResponseAvailable(WAIT_FOR_CONTINUE_MS)) {
+                    response = conn.receiveResponseHeader(request.getParams());
+                    if (canResponseHaveBody(request, response)) {
+                        conn.receiveResponseEntity(response);
+                    }
+                    int status = response.getStatusLine().getStatusCode();
+                    if (status < 200) {
+                        //@@@ TODO: is this in line with RFC 2616, 10.1?
+                        if (status != HttpStatus.SC_CONTINUE) {
+                            throw new ProtocolException(
+                                    "Unexpected response: " + response.getStatusLine());
+                        }
+                    } else {
+                        sendentity = false;
+                    }
+                }
+            }
+            if (sendentity) {
+                conn.sendRequestEntity(entityEnclRequest);
+            }
+        }
+        conn.flush();
+        context.setAttribute(HttpExecutionContext.HTTP_REQ_SENT, Boolean.TRUE);
+        return response;
+    } 
+
+    /**
+     * Wait for and receive a response.
+     *
+     * @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
+     *
+     * @throws HttpException      in case of a protocol or processing problem
+     * @throws IOException        in case of an I/O problem
+     */
+    protected HttpMutableResponse receiveResponse(
+            final HttpRequest          request,
+            final HttpClientConnection conn,
+            final HttpContext          context)
+                throws HttpException, IOException {
+        if (request == null) {
+            throw new IllegalArgumentException("HTTP request may not be null");
+        }
+        if (conn == null) {
+            throw new IllegalArgumentException("HTTP connection may not be null");
+        }
+        if (context == null) {
+            throw new IllegalArgumentException("HTTP context may not be null");
+        }
+        // see HttpRequestExecutor.doExecute, final part
+        HttpMutableResponse response = null;
+        int statuscode = 0;
+
+        while (response == null || statuscode < HttpStatus.SC_OK) {
+
+            response = conn.receiveResponseHeader(request.getParams());
+            if (canResponseHaveBody(request, response)) {
+                conn.receiveResponseEntity(response);
+            }
+            statuscode = response.getStatusLine().getStatusCode();
+
+        } // while intermediate response
+
+        return response;
+
+    } // obtainResponse
+
+    /**
+     * Finish a response.
+     * This includes post-processing of the response object.
+     * It does <i>not</i> read the response entity, if any.
+     * It does <i>not</i> allow for immediate re-use of the
+     * connection over which the response is coming in.
+     *
+     * @param response  the response object to finish
+     * @param context   the context for post-processing the response
+     *
+     * @throws HttpException      in case of a protocol or processing problem
+     * @throws IOException        in case of an I/O problem
+     */
+    protected void finishResponse(
+            final HttpMutableResponse response,
+            final HttpContext context)
+                throws HttpException, IOException {
+        if (response == null) {
+            throw new IllegalArgumentException("HTTP response may not be null");
+        }
+        if (context == null) {
+            throw new IllegalArgumentException("HTTP context may not be null");
+        }
+        postprocessResponse(response, context);
+    }
+
+} // class HttpRequestExecutor



Mime
View raw message