001/*
002 * ====================================================================
003 * Licensed to the Apache Software Foundation (ASF) under one
004 * or more contributor license agreements.  See the NOTICE file
005 * distributed with this work for additional information
006 * regarding copyright ownership.  The ASF licenses this file
007 * to you under the Apache License, Version 2.0 (the
008 * "License"); you may not use this file except in compliance
009 * with the License.  You may obtain a copy of the License at
010 *
011 *   http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing,
014 * software distributed under the License is distributed on an
015 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
016 * KIND, either express or implied.  See the License for the
017 * specific language governing permissions and limitations
018 * under the License.
019 * ====================================================================
020 *
021 * This software consists of voluntary contributions made by many
022 * individuals on behalf of the Apache Software Foundation.  For more
023 * information on the Apache Software Foundation, please see
024 * <http://www.apache.org/>.
025 *
026 */
027
028package org.apache.http.impl.client;
029
030import java.io.Closeable;
031import java.io.IOException;
032import java.net.URI;
033
034import org.apache.commons.logging.Log;
035import org.apache.commons.logging.LogFactory;
036import org.apache.http.HttpEntity;
037import org.apache.http.HttpHost;
038import org.apache.http.HttpRequest;
039import org.apache.http.annotation.Contract;
040import org.apache.http.annotation.ThreadingBehavior;
041import org.apache.http.client.ClientProtocolException;
042import org.apache.http.client.HttpClient;
043import org.apache.http.client.ResponseHandler;
044import org.apache.http.client.methods.CloseableHttpResponse;
045import org.apache.http.client.methods.HttpUriRequest;
046import org.apache.http.client.utils.URIUtils;
047import org.apache.http.protocol.HttpContext;
048import org.apache.http.util.Args;
049import org.apache.http.util.EntityUtils;
050
051/**
052 * Base implementation of {@link HttpClient} that also implements {@link Closeable}.
053 *
054 * @since 4.3
055 */
056@Contract(threading = ThreadingBehavior.SAFE)
057public abstract class CloseableHttpClient implements HttpClient, Closeable {
058
059    private final Log log = LogFactory.getLog(getClass());
060
061    protected abstract CloseableHttpResponse doExecute(HttpHost target, HttpRequest request,
062            HttpContext context) throws IOException, ClientProtocolException;
063
064    /**
065     * {@inheritDoc}
066     */
067    @Override
068    public CloseableHttpResponse execute(
069            final HttpHost target,
070            final HttpRequest request,
071            final HttpContext context) throws IOException, ClientProtocolException {
072        return doExecute(target, request, context);
073    }
074
075    /**
076     * {@inheritDoc}
077     */
078    @Override
079    public CloseableHttpResponse execute(
080            final HttpUriRequest request,
081            final HttpContext context) throws IOException, ClientProtocolException {
082        Args.notNull(request, "HTTP request");
083        return doExecute(determineTarget(request), request, context);
084    }
085
086    private static HttpHost determineTarget(final HttpUriRequest request) throws ClientProtocolException {
087        // A null target may be acceptable if there is a default target.
088        // Otherwise, the null target is detected in the director.
089        HttpHost target = null;
090
091        final URI requestURI = request.getURI();
092        if (requestURI.isAbsolute()) {
093            target = URIUtils.extractHost(requestURI);
094            if (target == null) {
095                throw new ClientProtocolException("URI does not specify a valid host name: "
096                        + requestURI);
097            }
098        }
099        return target;
100    }
101
102    /**
103     * {@inheritDoc}
104     */
105    @Override
106    public CloseableHttpResponse execute(
107            final HttpUriRequest request) throws IOException, ClientProtocolException {
108        return execute(request, (HttpContext) null);
109    }
110
111    /**
112     * {@inheritDoc}
113     */
114    @Override
115    public CloseableHttpResponse execute(
116            final HttpHost target,
117            final HttpRequest request) throws IOException, ClientProtocolException {
118        return doExecute(target, request, null);
119    }
120
121    /**
122     * Executes a request using the default context and processes the
123     * response using the given response handler. The content entity associated
124     * with the response is fully consumed and the underlying connection is
125     * released back to the connection manager automatically in all cases
126     * relieving individual {@link ResponseHandler}s from having to manage
127     * resource deallocation internally.
128     *
129     * @param request   the request to execute
130     * @param responseHandler the response handler
131     *
132     * @return  the response object as generated by the response handler.
133     * @throws IOException in case of a problem or the connection was aborted
134     * @throws ClientProtocolException in case of an http protocol error
135     */
136    @Override
137    public <T> T execute(final HttpUriRequest request,
138            final ResponseHandler<? extends T> responseHandler) throws IOException,
139            ClientProtocolException {
140        return execute(request, responseHandler, null);
141    }
142
143    /**
144     * Executes a request using the default context and processes the
145     * response using the given response handler. The content entity associated
146     * with the response is fully consumed and the underlying connection is
147     * released back to the connection manager automatically in all cases
148     * relieving individual {@link ResponseHandler}s from having to manage
149     * resource deallocation internally.
150     *
151     * @param request   the request to execute
152     * @param responseHandler the response handler
153     * @param context   the context to use for the execution, or
154     *                  {@code null} to use the default context
155     *
156     * @return  the response object as generated by the response handler.
157     * @throws IOException in case of a problem or the connection was aborted
158     * @throws ClientProtocolException in case of an http protocol error
159     */
160    @Override
161    public <T> T execute(final HttpUriRequest request,
162            final ResponseHandler<? extends T> responseHandler, final HttpContext context)
163            throws IOException, ClientProtocolException {
164        final HttpHost target = determineTarget(request);
165        return execute(target, request, responseHandler, context);
166    }
167
168    /**
169     * Executes a request using the default context and processes the
170     * response using the given response handler. The content entity associated
171     * with the response is fully consumed and the underlying connection is
172     * released back to the connection manager automatically in all cases
173     * relieving individual {@link ResponseHandler}s from having to manage
174     * resource deallocation internally.
175     *
176     * @param target    the target host for the request.
177     *                  Implementations may accept {@code null}
178     *                  if they can still determine a route, for example
179     *                  to a default target or by inspecting the request.
180     * @param request   the request to execute
181     * @param responseHandler the response handler
182     *
183     * @return  the response object as generated by the response handler.
184     * @throws IOException in case of a problem or the connection was aborted
185     * @throws ClientProtocolException in case of an http protocol error
186     */
187    @Override
188    public <T> T execute(final HttpHost target, final HttpRequest request,
189            final ResponseHandler<? extends T> responseHandler) throws IOException,
190            ClientProtocolException {
191        return execute(target, request, responseHandler, null);
192    }
193
194    /**
195     * Executes a request using the default context and processes the
196     * response using the given response handler. The content entity associated
197     * with the response is fully consumed and the underlying connection is
198     * released back to the connection manager automatically in all cases
199     * relieving individual {@link ResponseHandler}s from having to manage
200     * resource deallocation internally.
201     *
202     * @param target    the target host for the request.
203     *                  Implementations may accept {@code null}
204     *                  if they can still determine a route, for example
205     *                  to a default target or by inspecting the request.
206     * @param request   the request to execute
207     * @param responseHandler the response handler
208     * @param context   the context to use for the execution, or
209     *                  {@code null} to use the default context
210     *
211     * @return  the response object as generated by the response handler.
212     * @throws IOException in case of a problem or the connection was aborted
213     * @throws ClientProtocolException in case of an http protocol error
214     */
215    @Override
216    public <T> T execute(final HttpHost target, final HttpRequest request,
217            final ResponseHandler<? extends T> responseHandler, final HttpContext context)
218            throws IOException, ClientProtocolException {
219        Args.notNull(responseHandler, "Response handler");
220
221        final CloseableHttpResponse response = execute(target, request, context);
222        try {
223            final T result = responseHandler.handleResponse(response);
224            final HttpEntity entity = response.getEntity();
225            EntityUtils.consume(entity);
226            return result;
227        } catch (final ClientProtocolException t) {
228            // Try to salvage the underlying connection in case of a protocol exception
229            final HttpEntity entity = response.getEntity();
230            try {
231                EntityUtils.consume(entity);
232            } catch (final Exception t2) {
233                // Log this exception. The original exception is more
234                // important and will be thrown to the caller.
235                this.log.warn("Error consuming content after an exception.", t2);
236            }
237            throw t;
238        } finally {
239            response.close();
240        }
241    }
242
243}