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

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial