mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpRequestExecutor.java

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

mercurial