• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/HttpRequestExecutor.java $
3  * $Revision: 576073 $
4  * $Date: 2007-09-16 03:53:13 -0700 (Sun, 16 Sep 2007) $
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.HttpEntityEnclosingRequest;
39 import org.apache.http.HttpException;
40 import org.apache.http.HttpRequest;
41 import org.apache.http.HttpResponse;
42 import org.apache.http.HttpStatus;
43 import org.apache.http.HttpVersion;
44 import org.apache.http.ProtocolVersion;
45 import org.apache.http.params.CoreProtocolPNames;
46 
47 /**
48  * Sends HTTP requests and receives the responses.
49  * Takes care of request preprocessing and response postprocessing
50  * by the respective interceptors.
51  *
52  * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
53  *
54  * @version $Revision: 576073 $
55  *
56  * @since 4.0
57  */
58 public class HttpRequestExecutor {
59 
60     /**
61      * Create a new request executor.
62      */
HttpRequestExecutor()63     public HttpRequestExecutor() {
64         super();
65     }
66 
67     /**
68      * Decide whether a response comes with an entity.
69      * The implementation in this class is based on RFC 2616.
70      * Unknown methods and response codes are supposed to
71      * indicate responses with an entity.
72      * <br/>
73      * Derived executors can override this method to handle
74      * methods and response codes not specified in RFC 2616.
75      *
76      * @param request   the request, to obtain the executed method
77      * @param response  the response, to obtain the status code
78      */
canResponseHaveBody(final HttpRequest request, final HttpResponse response)79     protected boolean canResponseHaveBody(final HttpRequest request,
80                                           final HttpResponse response) {
81 
82         if ("HEAD".equalsIgnoreCase(request.getRequestLine().getMethod())) {
83             return false;
84         }
85         int status = response.getStatusLine().getStatusCode();
86         return status >= HttpStatus.SC_OK
87             && status != HttpStatus.SC_NO_CONTENT
88             && status != HttpStatus.SC_NOT_MODIFIED
89             && status != HttpStatus.SC_RESET_CONTENT;
90     }
91 
92     /**
93      * Synchronously send a request and obtain the response.
94      *
95      * @param request   the request to send. It will be preprocessed.
96      * @param conn      the open connection over which to send
97      *
98      * @return  the response to the request, postprocessed
99      *
100      * @throws HttpException      in case of a protocol or processing problem
101      * @throws IOException        in case of an I/O problem
102      */
execute( final HttpRequest request, final HttpClientConnection conn, final HttpContext context)103     public HttpResponse execute(
104             final HttpRequest request,
105             final HttpClientConnection conn,
106             final HttpContext context)
107                 throws IOException, HttpException {
108         if (request == null) {
109             throw new IllegalArgumentException("HTTP request may not be null");
110         }
111         if (conn == null) {
112             throw new IllegalArgumentException("Client connection may not be null");
113         }
114         if (context == null) {
115             throw new IllegalArgumentException("HTTP context may not be null");
116         }
117 
118         try {
119             HttpResponse response = doSendRequest(request, conn, context);
120             if (response == null) {
121                 response = doReceiveResponse(request, conn, context);
122             }
123             return response;
124         } catch (IOException ex) {
125             conn.close();
126             throw ex;
127         } catch (HttpException ex) {
128             conn.close();
129             throw ex;
130         } catch (RuntimeException ex) {
131             conn.close();
132             throw ex;
133         }
134     }
135 
136     /**
137      * Prepare a request for sending.
138      *
139      * @param request   the request to prepare
140      * @param processor the processor to use
141      * @param context   the context for sending the request
142      *
143      * @throws HttpException      in case of a protocol or processing problem
144      * @throws IOException        in case of an I/O problem
145      */
preProcess( final HttpRequest request, final HttpProcessor processor, final HttpContext context)146     public void preProcess(
147             final HttpRequest request,
148             final HttpProcessor processor,
149             final HttpContext context)
150                 throws HttpException, IOException {
151         if (request == null) {
152             throw new IllegalArgumentException("HTTP request may not be null");
153         }
154         if (processor == null) {
155             throw new IllegalArgumentException("HTTP processor may not be null");
156         }
157         if (context == null) {
158             throw new IllegalArgumentException("HTTP context may not be null");
159         }
160         processor.process(request, context);
161     }
162 
163     /**
164      * Send a request over a connection.
165      * This method also handles the expect-continue handshake if necessary.
166      * If it does not have to handle an expect-continue handshake, it will
167      * not use the connection for reading or anything else that depends on
168      * data coming in over the connection.
169      *
170      * @param request   the request to send, already
171      *                  {@link #preProcess preprocessed}
172      * @param conn      the connection over which to send the request,
173      *                  already established
174      * @param context   the context for sending the request
175      *
176      * @return  a terminal response received as part of an expect-continue
177      *          handshake, or
178      *          <code>null</code> if the expect-continue handshake is not used
179      *
180      * @throws HttpException      in case of a protocol or processing problem
181      * @throws IOException        in case of an I/O problem
182      */
doSendRequest( final HttpRequest request, final HttpClientConnection conn, final HttpContext context)183     protected HttpResponse doSendRequest(
184             final HttpRequest request,
185             final HttpClientConnection conn,
186             final HttpContext context)
187                 throws IOException, HttpException {
188         if (request == null) {
189             throw new IllegalArgumentException("HTTP request may not be null");
190         }
191         if (conn == null) {
192             throw new IllegalArgumentException("HTTP connection may not be null");
193         }
194         if (context == null) {
195             throw new IllegalArgumentException("HTTP context may not be null");
196         }
197 
198         HttpResponse response = null;
199         context.setAttribute(ExecutionContext.HTTP_REQ_SENT, Boolean.FALSE);
200 
201         conn.sendRequestHeader(request);
202         if (request instanceof HttpEntityEnclosingRequest) {
203             // Check for expect-continue handshake. We have to flush the
204             // headers and wait for an 100-continue response to handle it.
205             // If we get a different response, we must not send the entity.
206             boolean sendentity = true;
207             final ProtocolVersion ver =
208                 request.getRequestLine().getProtocolVersion();
209             if (((HttpEntityEnclosingRequest) request).expectContinue() &&
210                 !ver.lessEquals(HttpVersion.HTTP_1_0)) {
211 
212                 conn.flush();
213                 // As suggested by RFC 2616 section 8.2.3, we don't wait for a
214                 // 100-continue response forever. On timeout, send the entity.
215                 int tms = request.getParams().getIntParameter(
216                         CoreProtocolPNames.WAIT_FOR_CONTINUE, 2000);
217 
218                 if (conn.isResponseAvailable(tms)) {
219                     response = conn.receiveResponseHeader();
220                     if (canResponseHaveBody(request, response)) {
221                         conn.receiveResponseEntity(response);
222                     }
223                     int status = response.getStatusLine().getStatusCode();
224                     if (status < 200) {
225                         if (status != HttpStatus.SC_CONTINUE) {
226                             throw new ProtocolException(
227                                     "Unexpected response: " + response.getStatusLine());
228                         }
229                         // discard 100-continue
230                         response = null;
231                     } else {
232                         sendentity = false;
233                     }
234                 }
235             }
236             if (sendentity) {
237                 conn.sendRequestEntity((HttpEntityEnclosingRequest) request);
238             }
239         }
240         conn.flush();
241         context.setAttribute(ExecutionContext.HTTP_REQ_SENT, Boolean.TRUE);
242         return response;
243     }
244 
245     /**
246      * Wait for and receive a response.
247      * This method will automatically ignore intermediate responses
248      * with status code 1xx.
249      *
250      * @param request   the request for which to obtain the response
251      * @param conn      the connection over which the request was sent
252      * @param context   the context for receiving the response
253      *
254      * @return  the final response, not yet post-processed
255      *
256      * @throws HttpException      in case of a protocol or processing problem
257      * @throws IOException        in case of an I/O problem
258      */
doReceiveResponse( final HttpRequest request, final HttpClientConnection conn, final HttpContext context)259     protected HttpResponse doReceiveResponse(
260             final HttpRequest          request,
261             final HttpClientConnection conn,
262             final HttpContext          context)
263                 throws HttpException, IOException {
264         if (request == null) {
265             throw new IllegalArgumentException("HTTP request may not be null");
266         }
267         if (conn == null) {
268             throw new IllegalArgumentException("HTTP connection may not be null");
269         }
270         if (context == null) {
271             throw new IllegalArgumentException("HTTP context may not be null");
272         }
273 
274         HttpResponse response = null;
275         int statuscode = 0;
276 
277         while (response == null || statuscode < HttpStatus.SC_OK) {
278 
279             response = conn.receiveResponseHeader();
280             if (canResponseHaveBody(request, response)) {
281                 conn.receiveResponseEntity(response);
282             }
283             statuscode = response.getStatusLine().getStatusCode();
284 
285         } // while intermediate response
286 
287         return response;
288 
289     }
290 
291     /**
292      * Finish a response.
293      * This includes post-processing of the response object.
294      * It does <i>not</i> read the response entity, if any.
295      * It does <i>not</i> allow for immediate re-use of the
296      * connection over which the response is coming in.
297      *
298      * @param response  the response object to finish
299      * @param processor the processor to use
300      * @param context   the context for post-processing the response
301      *
302      * @throws HttpException      in case of a protocol or processing problem
303      * @throws IOException        in case of an I/O problem
304      */
postProcess( final HttpResponse response, final HttpProcessor processor, final HttpContext context)305     public void postProcess(
306             final HttpResponse response,
307             final HttpProcessor processor,
308             final HttpContext context)
309                 throws HttpException, IOException {
310         if (response == null) {
311             throw new IllegalArgumentException("HTTP response may not be null");
312         }
313         if (processor == null) {
314             throw new IllegalArgumentException("HTTP processor may not be null");
315         }
316         if (context == null) {
317             throw new IllegalArgumentException("HTTP context may not be null");
318         }
319         processor.process(response, context);
320     }
321 
322 } // class HttpRequestExecutor
323