Home » HttpComponents-Core-4.0.1 » org.apache.http.protocol » [javadoc | source]

    1   /*
    2    * $HeadURL: https://svn.apache.org/repos/asf/httpcomponents/httpcore/tags/4.0.1/httpcore/src/main/java/org/apache/http/protocol/HttpRequestExecutor.java $
    3    * $Revision: 744532 $
    4    * $Date: 2009-02-14 18:12:18 +0100 (Sat, 14 Feb 2009) $
    5    *
    6    * ====================================================================
    7    * Licensed to the Apache Software Foundation (ASF) under one
    8    * or more contributor license agreements.  See the NOTICE file
    9    * distributed with this work for additional information
   10    * regarding copyright ownership.  The ASF licenses this file
   11    * to you under the Apache License, Version 2.0 (the
   12    * "License"); you may not use this file except in compliance
   13    * with the License.  You may obtain a copy of the License at
   14    *
   15    *   http://www.apache.org/licenses/LICENSE-2.0
   16    *
   17    * Unless required by applicable law or agreed to in writing,
   18    * software distributed under the License is distributed on an
   19    * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   20    * KIND, either express or implied.  See the License for the
   21    * specific language governing permissions and limitations
   22    * under the License.
   23    * ====================================================================
   24    *
   25    * This software consists of voluntary contributions made by many
   26    * individuals on behalf of the Apache Software Foundation.  For more
   27    * information on the Apache Software Foundation, please see
   28    * <http://www.apache.org/>.
   29    *
   30    */
   31   
   32   package org.apache.http.protocol;
   33   
   34   import java.io.IOException;
   35   import java.net.ProtocolException;
   36   
   37   import org.apache.http.HttpClientConnection;
   38   import org.apache.http.HttpEntity;
   39   import org.apache.http.HttpEntityEnclosingRequest;
   40   import org.apache.http.HttpException;
   41   import org.apache.http.HttpRequest;
   42   import org.apache.http.HttpResponse;
   43   import org.apache.http.HttpStatus;
   44   import org.apache.http.HttpVersion;
   45   import org.apache.http.ProtocolVersion;
   46   import org.apache.http.params.CoreProtocolPNames;
   47   
   48   /**
   49    * HttpRequestExecutor is a client side HTTP protocol handler based on the 
   50    * blocking I/O model that implements the essential requirements of the HTTP 
   51    * protocol for the client side message  processing, as described by RFC 2616. 
   52    * <br>
   53    * HttpRequestExecutor relies on {@link HttpProcessor} to generate mandatory 
   54    * protocol headers for all outgoing messages and apply common, cross-cutting 
   55    * message transformations to all incoming and outgoing messages. Application 
   56    * specific processing can be implemented outside HttpRequestExecutor once the 
   57    * request has been executed and a response has been received.
   58    *
   59    *
   60    * @version $Revision: 744532 $
   61    * 
   62    * @since 4.0
   63    */
   64   public class HttpRequestExecutor {
   65   
   66       /**
   67        * Create a new request executor.
   68        */
   69       public HttpRequestExecutor() {
   70           super();
   71       }
   72   
   73       /**
   74        * Decide whether a response comes with an entity.
   75        * The implementation in this class is based on RFC 2616.
   76        * <br/>
   77        * Derived executors can override this method to handle
   78        * methods and response codes not specified in RFC 2616.
   79        *
   80        * @param request   the request, to obtain the executed method
   81        * @param response  the response, to obtain the status code
   82        */
   83       protected boolean canResponseHaveBody(final HttpRequest request,
   84                                             final HttpResponse response) {
   85   
   86           if ("HEAD".equalsIgnoreCase(request.getRequestLine().getMethod())) {
   87               return false;
   88           }
   89           int status = response.getStatusLine().getStatusCode(); 
   90           return status >= HttpStatus.SC_OK 
   91               && status != HttpStatus.SC_NO_CONTENT 
   92               && status != HttpStatus.SC_NOT_MODIFIED
   93               && status != HttpStatus.SC_RESET_CONTENT; 
   94       }
   95   
   96       /**
   97        * Sends the request and obtain a response.
   98        *
   99        * @param request   the request to execute.
  100        * @param conn      the connection over which to execute the request.
  101        *
  102        * @return  the response to the request.
  103        *
  104        * @throws IOException in case of an I/O error.
  105        * @throws HttpException in case of HTTP protocol violation or a processing 
  106        *   problem.
  107        */    
  108       public HttpResponse execute(
  109               final HttpRequest request,
  110               final HttpClientConnection conn,
  111               final HttpContext context) 
  112                   throws IOException, HttpException {
  113           if (request == null) {
  114               throw new IllegalArgumentException("HTTP request may not be null");
  115           }
  116           if (conn == null) {
  117               throw new IllegalArgumentException("Client connection may not be null");
  118           }
  119           if (context == null) {
  120               throw new IllegalArgumentException("HTTP context may not be null");
  121           }
  122   
  123           try {
  124               HttpResponse response = doSendRequest(request, conn, context);
  125               if (response == null) {
  126                   response = doReceiveResponse(request, conn, context);
  127               }
  128               return response;
  129           } catch (IOException ex) {
  130               conn.close();
  131               throw ex;
  132           } catch (HttpException ex) {
  133               conn.close();
  134               throw ex;
  135           } catch (RuntimeException ex) {
  136               conn.close();
  137               throw ex;
  138           }
  139       }
  140   
  141       /**
  142        * Pre-process the given request using the given protocol processor and 
  143        * initiates the process of request execution.
  144        *
  145        * @param request   the request to prepare
  146        * @param processor the processor to use
  147        * @param context   the context for sending the request
  148        *
  149        * @throws IOException in case of an I/O error.
  150        * @throws HttpException in case of HTTP protocol violation or a processing 
  151        *   problem.
  152        */
  153       public void preProcess(
  154               final HttpRequest request,
  155               final HttpProcessor processor,
  156               final HttpContext context)
  157                   throws HttpException, IOException {
  158           if (request == null) {
  159               throw new IllegalArgumentException("HTTP request may not be null");
  160           }
  161           if (processor == null) {
  162               throw new IllegalArgumentException("HTTP processor may not be null");
  163           }
  164           if (context == null) {
  165               throw new IllegalArgumentException("HTTP context may not be null");
  166           }
  167           context.setAttribute(ExecutionContext.HTTP_REQUEST, request);
  168           processor.process(request, context);
  169       }
  170   
  171       /**
  172        * Send the given request over the given connection.
  173        * <p>
  174        * This method also handles the expect-continue handshake if necessary.
  175        * If it does not have to handle an expect-continue handshake, it will
  176        * not use the connection for reading or anything else that depends on
  177        * data coming in over the connection.
  178        *
  179        * @param request   the request to send, already
  180        *                  {@link #preProcess preprocessed}
  181        * @param conn      the connection over which to send the request,
  182        *                  already established
  183        * @param context   the context for sending the request
  184        *
  185        * @return  a terminal response received as part of an expect-continue
  186        *          handshake, or
  187        *          <code>null</code> if the expect-continue handshake is not used
  188        *
  189        * @throws IOException in case of an I/O error.
  190        * @throws HttpException in case of HTTP protocol violation or a processing 
  191        *   problem.
  192        */
  193       protected HttpResponse doSendRequest(
  194               final HttpRequest request,
  195               final HttpClientConnection conn,
  196               final HttpContext context)
  197                   throws IOException, HttpException {
  198           if (request == null) {
  199               throw new IllegalArgumentException("HTTP request may not be null");
  200           }
  201           if (conn == null) {
  202               throw new IllegalArgumentException("HTTP connection may not be null");
  203           }
  204           if (context == null) {
  205               throw new IllegalArgumentException("HTTP context may not be null");
  206           }
  207   
  208           HttpResponse response = null;
  209   
  210           context.setAttribute(ExecutionContext.HTTP_CONNECTION, conn);
  211           context.setAttribute(ExecutionContext.HTTP_REQ_SENT, Boolean.FALSE);
  212   
  213           conn.sendRequestHeader(request);
  214           if (request instanceof HttpEntityEnclosingRequest) {
  215               // Check for expect-continue handshake. We have to flush the
  216               // headers and wait for an 100-continue response to handle it.
  217               // If we get a different response, we must not send the entity.
  218               boolean sendentity = true;
  219               final ProtocolVersion ver =
  220                   request.getRequestLine().getProtocolVersion();
  221               if (((HttpEntityEnclosingRequest) request).expectContinue() &&
  222                   !ver.lessEquals(HttpVersion.HTTP_1_0)) {
  223   
  224                   conn.flush();
  225                   // As suggested by RFC 2616 section 8.2.3, we don't wait for a
  226                   // 100-continue response forever. On timeout, send the entity.
  227                   int tms = request.getParams().getIntParameter(
  228                           CoreProtocolPNames.WAIT_FOR_CONTINUE, 2000);
  229                   
  230                   if (conn.isResponseAvailable(tms)) {
  231                       response = conn.receiveResponseHeader();
  232                       if (canResponseHaveBody(request, response)) {
  233                           conn.receiveResponseEntity(response);
  234                       }
  235                       int status = response.getStatusLine().getStatusCode();
  236                       if (status < 200) {
  237                           if (status != HttpStatus.SC_CONTINUE) {
  238                               throw new ProtocolException(
  239                                       "Unexpected response: " + response.getStatusLine());
  240                           }
  241                           // discard 100-continue
  242                           response = null;
  243                       } else {
  244                           sendentity = false;
  245                       }
  246                   }
  247               }
  248               if (sendentity) {
  249                   conn.sendRequestEntity((HttpEntityEnclosingRequest) request);
  250               }
  251           }
  252           conn.flush();
  253           context.setAttribute(ExecutionContext.HTTP_REQ_SENT, Boolean.TRUE);
  254           return response;
  255       } 
  256   
  257       /**
  258        * Waits for and receives a response.
  259        * This method will automatically ignore intermediate responses
  260        * with status code 1xx.
  261        *
  262        * @param request   the request for which to obtain the response
  263        * @param conn      the connection over which the request was sent
  264        * @param context   the context for receiving the response
  265        *
  266        * @return  the terminal response, not yet post-processed
  267        *
  268        * @throws IOException in case of an I/O error.
  269        * @throws HttpException in case of HTTP protocol violation or a processing 
  270        *   problem.
  271        */
  272       protected HttpResponse doReceiveResponse(
  273               final HttpRequest          request,
  274               final HttpClientConnection conn,
  275               final HttpContext          context)
  276                   throws HttpException, IOException {
  277           if (request == null) {
  278               throw new IllegalArgumentException("HTTP request may not be null");
  279           }
  280           if (conn == null) {
  281               throw new IllegalArgumentException("HTTP connection may not be null");
  282           }
  283           if (context == null) {
  284               throw new IllegalArgumentException("HTTP context may not be null");
  285           }
  286   
  287           HttpResponse response = null;
  288           int statuscode = 0;
  289   
  290           while (response == null || statuscode < HttpStatus.SC_OK) {
  291   
  292               response = conn.receiveResponseHeader();
  293               if (canResponseHaveBody(request, response)) {
  294                   conn.receiveResponseEntity(response);
  295               }
  296               statuscode = response.getStatusLine().getStatusCode();
  297   
  298           } // while intermediate response
  299   
  300           return response;
  301   
  302       }
  303   
  304       /**
  305        * Post-processes the given response using the given protocol processor and 
  306        * completes the process of request execution.
  307        * <p>
  308        * This method does <i>not</i> read the response entity, if any.
  309        * The connection over which content of the response entity is being 
  310        * streamed from cannot be reused until {@link HttpEntity#consumeContent()} 
  311        * has been invoked.
  312        *
  313        * @param response  the response object to post-process
  314        * @param processor the processor to use
  315        * @param context   the context for post-processing the response
  316        *
  317        * @throws IOException in case of an I/O error.
  318        * @throws HttpException in case of HTTP protocol violation or a processing 
  319        *   problem.
  320        */
  321       public void postProcess(
  322               final HttpResponse response,
  323               final HttpProcessor processor,
  324               final HttpContext context)
  325                   throws HttpException, IOException {
  326           if (response == null) {
  327               throw new IllegalArgumentException("HTTP response may not be null");
  328           }
  329           if (processor == null) {
  330               throw new IllegalArgumentException("HTTP processor may not be null");
  331           }
  332           if (context == null) {
  333               throw new IllegalArgumentException("HTTP context may not be null");
  334           }
  335           context.setAttribute(ExecutionContext.HTTP_RESPONSE, response);
  336           processor.process(response, context);
  337       }
  338   
  339   } // class HttpRequestExecutor

Home » HttpComponents-Core-4.0.1 » org.apache.http.protocol » [javadoc | source]