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

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpRequestExecutor.java	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,345 @@
     1.4 +/*
     1.5 + * ====================================================================
     1.6 + * Licensed to the Apache Software Foundation (ASF) under one
     1.7 + * or more contributor license agreements.  See the NOTICE file
     1.8 + * distributed with this work for additional information
     1.9 + * regarding copyright ownership.  The ASF licenses this file
    1.10 + * to you under the Apache License, Version 2.0 (the
    1.11 + * "License"); you may not use this file except in compliance
    1.12 + * with the License.  You may obtain a copy of the License at
    1.13 + *
    1.14 + *   http://www.apache.org/licenses/LICENSE-2.0
    1.15 + *
    1.16 + * Unless required by applicable law or agreed to in writing,
    1.17 + * software distributed under the License is distributed on an
    1.18 + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    1.19 + * KIND, either express or implied.  See the License for the
    1.20 + * specific language governing permissions and limitations
    1.21 + * under the License.
    1.22 + * ====================================================================
    1.23 + *
    1.24 + * This software consists of voluntary contributions made by many
    1.25 + * individuals on behalf of the Apache Software Foundation.  For more
    1.26 + * information on the Apache Software Foundation, please see
    1.27 + * <http://www.apache.org/>.
    1.28 + *
    1.29 + */
    1.30 +
    1.31 +package ch.boye.httpclientandroidlib.protocol;
    1.32 +
    1.33 +import java.io.IOException;
    1.34 +import java.net.ProtocolException;
    1.35 +
    1.36 +import ch.boye.httpclientandroidlib.HttpClientConnection;
    1.37 +import ch.boye.httpclientandroidlib.HttpEntity;
    1.38 +import ch.boye.httpclientandroidlib.HttpEntityEnclosingRequest;
    1.39 +import ch.boye.httpclientandroidlib.HttpException;
    1.40 +import ch.boye.httpclientandroidlib.HttpRequest;
    1.41 +import ch.boye.httpclientandroidlib.HttpResponse;
    1.42 +import ch.boye.httpclientandroidlib.HttpStatus;
    1.43 +import ch.boye.httpclientandroidlib.HttpVersion;
    1.44 +import ch.boye.httpclientandroidlib.ProtocolVersion;
    1.45 +import ch.boye.httpclientandroidlib.params.CoreProtocolPNames;
    1.46 +
    1.47 +/**
    1.48 + * HttpRequestExecutor is a client side HTTP protocol handler based on the
    1.49 + * blocking I/O model that implements the essential requirements of the HTTP
    1.50 + * protocol for the client side message  processing, as described by RFC 2616.
    1.51 + * <br>
    1.52 + * HttpRequestExecutor relies on {@link HttpProcessor} to generate mandatory
    1.53 + * protocol headers for all outgoing messages and apply common, cross-cutting
    1.54 + * message transformations to all incoming and outgoing messages. Application
    1.55 + * specific processing can be implemented outside HttpRequestExecutor once the
    1.56 + * request has been executed and a response has been received.
    1.57 + * <p>
    1.58 + * The following parameters can be used to customize the behavior of this
    1.59 + * class:
    1.60 + * <ul>
    1.61 + *  <li>{@link ch.boye.httpclientandroidlib.params.CoreProtocolPNames#WAIT_FOR_CONTINUE}</li>
    1.62 + * </ul>
    1.63 + *
    1.64 + * @since 4.0
    1.65 + */
    1.66 +public class HttpRequestExecutor {
    1.67 +
    1.68 +    /**
    1.69 +     * Create a new request executor.
    1.70 +     */
    1.71 +    public HttpRequestExecutor() {
    1.72 +        super();
    1.73 +    }
    1.74 +
    1.75 +    /**
    1.76 +     * Decide whether a response comes with an entity.
    1.77 +     * The implementation in this class is based on RFC 2616.
    1.78 +     * <br/>
    1.79 +     * Derived executors can override this method to handle
    1.80 +     * methods and response codes not specified in RFC 2616.
    1.81 +     *
    1.82 +     * @param request   the request, to obtain the executed method
    1.83 +     * @param response  the response, to obtain the status code
    1.84 +     */
    1.85 +    protected boolean canResponseHaveBody(final HttpRequest request,
    1.86 +                                          final HttpResponse response) {
    1.87 +
    1.88 +        if ("HEAD".equalsIgnoreCase(request.getRequestLine().getMethod())) {
    1.89 +            return false;
    1.90 +        }
    1.91 +        int status = response.getStatusLine().getStatusCode();
    1.92 +        return status >= HttpStatus.SC_OK
    1.93 +            && status != HttpStatus.SC_NO_CONTENT
    1.94 +            && status != HttpStatus.SC_NOT_MODIFIED
    1.95 +            && status != HttpStatus.SC_RESET_CONTENT;
    1.96 +    }
    1.97 +
    1.98 +    /**
    1.99 +     * Sends the request and obtain a response.
   1.100 +     *
   1.101 +     * @param request   the request to execute.
   1.102 +     * @param conn      the connection over which to execute the request.
   1.103 +     *
   1.104 +     * @return  the response to the request.
   1.105 +     *
   1.106 +     * @throws IOException in case of an I/O error.
   1.107 +     * @throws HttpException in case of HTTP protocol violation or a processing
   1.108 +     *   problem.
   1.109 +     */
   1.110 +    public HttpResponse execute(
   1.111 +            final HttpRequest request,
   1.112 +            final HttpClientConnection conn,
   1.113 +            final HttpContext context)
   1.114 +                throws IOException, HttpException {
   1.115 +        if (request == null) {
   1.116 +            throw new IllegalArgumentException("HTTP request may not be null");
   1.117 +        }
   1.118 +        if (conn == null) {
   1.119 +            throw new IllegalArgumentException("Client connection may not be null");
   1.120 +        }
   1.121 +        if (context == null) {
   1.122 +            throw new IllegalArgumentException("HTTP context may not be null");
   1.123 +        }
   1.124 +
   1.125 +        try {
   1.126 +            HttpResponse response = doSendRequest(request, conn, context);
   1.127 +            if (response == null) {
   1.128 +                response = doReceiveResponse(request, conn, context);
   1.129 +            }
   1.130 +            return response;
   1.131 +        } catch (IOException ex) {
   1.132 +            closeConnection(conn);
   1.133 +            throw ex;
   1.134 +        } catch (HttpException ex) {
   1.135 +            closeConnection(conn);
   1.136 +            throw ex;
   1.137 +        } catch (RuntimeException ex) {
   1.138 +            closeConnection(conn);
   1.139 +            throw ex;
   1.140 +        }
   1.141 +    }
   1.142 +
   1.143 +    private final static void closeConnection(final HttpClientConnection conn) {
   1.144 +        try {
   1.145 +            conn.close();
   1.146 +        } catch (IOException ignore) {
   1.147 +        }
   1.148 +    }
   1.149 +
   1.150 +    /**
   1.151 +     * Pre-process the given request using the given protocol processor and
   1.152 +     * initiates the process of request execution.
   1.153 +     *
   1.154 +     * @param request   the request to prepare
   1.155 +     * @param processor the processor to use
   1.156 +     * @param context   the context for sending the request
   1.157 +     *
   1.158 +     * @throws IOException in case of an I/O error.
   1.159 +     * @throws HttpException in case of HTTP protocol violation or a processing
   1.160 +     *   problem.
   1.161 +     */
   1.162 +    public void preProcess(
   1.163 +            final HttpRequest request,
   1.164 +            final HttpProcessor processor,
   1.165 +            final HttpContext context)
   1.166 +                throws HttpException, IOException {
   1.167 +        if (request == null) {
   1.168 +            throw new IllegalArgumentException("HTTP request may not be null");
   1.169 +        }
   1.170 +        if (processor == null) {
   1.171 +            throw new IllegalArgumentException("HTTP processor may not be null");
   1.172 +        }
   1.173 +        if (context == null) {
   1.174 +            throw new IllegalArgumentException("HTTP context may not be null");
   1.175 +        }
   1.176 +        context.setAttribute(ExecutionContext.HTTP_REQUEST, request);
   1.177 +        processor.process(request, context);
   1.178 +    }
   1.179 +
   1.180 +    /**
   1.181 +     * Send the given request over the given connection.
   1.182 +     * <p>
   1.183 +     * This method also handles the expect-continue handshake if necessary.
   1.184 +     * If it does not have to handle an expect-continue handshake, it will
   1.185 +     * not use the connection for reading or anything else that depends on
   1.186 +     * data coming in over the connection.
   1.187 +     *
   1.188 +     * @param request   the request to send, already
   1.189 +     *                  {@link #preProcess preprocessed}
   1.190 +     * @param conn      the connection over which to send the request,
   1.191 +     *                  already established
   1.192 +     * @param context   the context for sending the request
   1.193 +     *
   1.194 +     * @return  a terminal response received as part of an expect-continue
   1.195 +     *          handshake, or
   1.196 +     *          <code>null</code> if the expect-continue handshake is not used
   1.197 +     *
   1.198 +     * @throws IOException in case of an I/O error.
   1.199 +     * @throws HttpException in case of HTTP protocol violation or a processing
   1.200 +     *   problem.
   1.201 +     */
   1.202 +    protected HttpResponse doSendRequest(
   1.203 +            final HttpRequest request,
   1.204 +            final HttpClientConnection conn,
   1.205 +            final HttpContext context)
   1.206 +                throws IOException, HttpException {
   1.207 +        if (request == null) {
   1.208 +            throw new IllegalArgumentException("HTTP request may not be null");
   1.209 +        }
   1.210 +        if (conn == null) {
   1.211 +            throw new IllegalArgumentException("HTTP connection may not be null");
   1.212 +        }
   1.213 +        if (context == null) {
   1.214 +            throw new IllegalArgumentException("HTTP context may not be null");
   1.215 +        }
   1.216 +
   1.217 +        HttpResponse response = null;
   1.218 +
   1.219 +        context.setAttribute(ExecutionContext.HTTP_CONNECTION, conn);
   1.220 +        context.setAttribute(ExecutionContext.HTTP_REQ_SENT, Boolean.FALSE);
   1.221 +
   1.222 +        conn.sendRequestHeader(request);
   1.223 +        if (request instanceof HttpEntityEnclosingRequest) {
   1.224 +            // Check for expect-continue handshake. We have to flush the
   1.225 +            // headers and wait for an 100-continue response to handle it.
   1.226 +            // If we get a different response, we must not send the entity.
   1.227 +            boolean sendentity = true;
   1.228 +            final ProtocolVersion ver =
   1.229 +                request.getRequestLine().getProtocolVersion();
   1.230 +            if (((HttpEntityEnclosingRequest) request).expectContinue() &&
   1.231 +                !ver.lessEquals(HttpVersion.HTTP_1_0)) {
   1.232 +
   1.233 +                conn.flush();
   1.234 +                // As suggested by RFC 2616 section 8.2.3, we don't wait for a
   1.235 +                // 100-continue response forever. On timeout, send the entity.
   1.236 +                int tms = request.getParams().getIntParameter(
   1.237 +                        CoreProtocolPNames.WAIT_FOR_CONTINUE, 2000);
   1.238 +
   1.239 +                if (conn.isResponseAvailable(tms)) {
   1.240 +                    response = conn.receiveResponseHeader();
   1.241 +                    if (canResponseHaveBody(request, response)) {
   1.242 +                        conn.receiveResponseEntity(response);
   1.243 +                    }
   1.244 +                    int status = response.getStatusLine().getStatusCode();
   1.245 +                    if (status < 200) {
   1.246 +                        if (status != HttpStatus.SC_CONTINUE) {
   1.247 +                            throw new ProtocolException(
   1.248 +                                    "Unexpected response: " + response.getStatusLine());
   1.249 +                        }
   1.250 +                        // discard 100-continue
   1.251 +                        response = null;
   1.252 +                    } else {
   1.253 +                        sendentity = false;
   1.254 +                    }
   1.255 +                }
   1.256 +            }
   1.257 +            if (sendentity) {
   1.258 +                conn.sendRequestEntity((HttpEntityEnclosingRequest) request);
   1.259 +            }
   1.260 +        }
   1.261 +        conn.flush();
   1.262 +        context.setAttribute(ExecutionContext.HTTP_REQ_SENT, Boolean.TRUE);
   1.263 +        return response;
   1.264 +    }
   1.265 +
   1.266 +    /**
   1.267 +     * Waits for and receives a response.
   1.268 +     * This method will automatically ignore intermediate responses
   1.269 +     * with status code 1xx.
   1.270 +     *
   1.271 +     * @param request   the request for which to obtain the response
   1.272 +     * @param conn      the connection over which the request was sent
   1.273 +     * @param context   the context for receiving the response
   1.274 +     *
   1.275 +     * @return  the terminal response, not yet post-processed
   1.276 +     *
   1.277 +     * @throws IOException in case of an I/O error.
   1.278 +     * @throws HttpException in case of HTTP protocol violation or a processing
   1.279 +     *   problem.
   1.280 +     */
   1.281 +    protected HttpResponse doReceiveResponse(
   1.282 +            final HttpRequest          request,
   1.283 +            final HttpClientConnection conn,
   1.284 +            final HttpContext          context)
   1.285 +                throws HttpException, IOException {
   1.286 +        if (request == null) {
   1.287 +            throw new IllegalArgumentException("HTTP request may not be null");
   1.288 +        }
   1.289 +        if (conn == null) {
   1.290 +            throw new IllegalArgumentException("HTTP connection may not be null");
   1.291 +        }
   1.292 +        if (context == null) {
   1.293 +            throw new IllegalArgumentException("HTTP context may not be null");
   1.294 +        }
   1.295 +
   1.296 +        HttpResponse response = null;
   1.297 +        int statuscode = 0;
   1.298 +
   1.299 +        while (response == null || statuscode < HttpStatus.SC_OK) {
   1.300 +
   1.301 +            response = conn.receiveResponseHeader();
   1.302 +            if (canResponseHaveBody(request, response)) {
   1.303 +                conn.receiveResponseEntity(response);
   1.304 +            }
   1.305 +            statuscode = response.getStatusLine().getStatusCode();
   1.306 +
   1.307 +        } // while intermediate response
   1.308 +
   1.309 +        return response;
   1.310 +
   1.311 +    }
   1.312 +
   1.313 +    /**
   1.314 +     * Post-processes the given response using the given protocol processor and
   1.315 +     * completes the process of request execution.
   1.316 +     * <p>
   1.317 +     * This method does <i>not</i> read the response entity, if any.
   1.318 +     * The connection over which content of the response entity is being
   1.319 +     * streamed from cannot be reused until {@link HttpEntity#consumeContent()}
   1.320 +     * has been invoked.
   1.321 +     *
   1.322 +     * @param response  the response object to post-process
   1.323 +     * @param processor the processor to use
   1.324 +     * @param context   the context for post-processing the response
   1.325 +     *
   1.326 +     * @throws IOException in case of an I/O error.
   1.327 +     * @throws HttpException in case of HTTP protocol violation or a processing
   1.328 +     *   problem.
   1.329 +     */
   1.330 +    public void postProcess(
   1.331 +            final HttpResponse response,
   1.332 +            final HttpProcessor processor,
   1.333 +            final HttpContext context)
   1.334 +                throws HttpException, IOException {
   1.335 +        if (response == null) {
   1.336 +            throw new IllegalArgumentException("HTTP response may not be null");
   1.337 +        }
   1.338 +        if (processor == null) {
   1.339 +            throw new IllegalArgumentException("HTTP processor may not be null");
   1.340 +        }
   1.341 +        if (context == null) {
   1.342 +            throw new IllegalArgumentException("HTTP context may not be null");
   1.343 +        }
   1.344 +        context.setAttribute(ExecutionContext.HTTP_RESPONSE, response);
   1.345 +        processor.process(response, context);
   1.346 +    }
   1.347 +
   1.348 +} // class HttpRequestExecutor

mercurial