View Javadoc
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 org.apache.hc.client5.http.impl.classic;
29  
30  import java.io.IOException;
31  
32  import org.apache.hc.client5.http.ClientProtocolException;
33  import org.apache.hc.client5.http.classic.HttpClient;
34  import org.apache.hc.client5.http.routing.RoutingSupport;
35  import org.apache.hc.core5.annotation.Contract;
36  import org.apache.hc.core5.annotation.ThreadingBehavior;
37  import org.apache.hc.core5.http.ClassicHttpRequest;
38  import org.apache.hc.core5.http.HttpEntity;
39  import org.apache.hc.core5.http.HttpException;
40  import org.apache.hc.core5.http.HttpHost;
41  import org.apache.hc.core5.http.io.HttpClientResponseHandler;
42  import org.apache.hc.core5.http.io.entity.EntityUtils;
43  import org.apache.hc.core5.http.protocol.HttpContext;
44  import org.apache.hc.core5.io.ModalCloseable;
45  import org.apache.hc.core5.util.Args;
46  import org.slf4j.Logger;
47  import org.slf4j.LoggerFactory;
48  
49  /**
50   * Base implementation of {@link HttpClient} that also implements {@link ModalCloseable}.
51   *
52   * @since 4.3
53   */
54  @Contract(threading = ThreadingBehavior.SAFE)
55  public abstract class CloseableHttpClient implements HttpClient, ModalCloseable {
56  
57      private static final Logger LOG = LoggerFactory.getLogger(CloseableHttpClient.class);
58  
59      protected abstract CloseableHttpResponse doExecute(HttpHost target, ClassicHttpRequest request,
60                                                       HttpContext context) throws IOException;
61  
62      @Override
63      public CloseableHttpResponse execute(
64              final HttpHost target,
65              final ClassicHttpRequest request,
66              final HttpContext context) throws IOException {
67          return doExecute(target, request, context);
68      }
69  
70      @Override
71      public CloseableHttpResponse execute(
72              final ClassicHttpRequest request,
73              final HttpContext context) throws IOException {
74          Args.notNull(request, "HTTP request");
75          return doExecute(determineTarget(request), request, context);
76      }
77  
78      private static HttpHost determineTarget(final ClassicHttpRequest request) throws ClientProtocolException {
79          try {
80              return RoutingSupport.determineHost(request);
81          } catch (final HttpException ex) {
82              throw new ClientProtocolException(ex);
83          }
84      }
85  
86      @Override
87      public CloseableHttpResponse execute(
88              final ClassicHttpRequest request) throws IOException {
89          return execute(request, (HttpContext) null);
90      }
91  
92      @Override
93      public CloseableHttpResponse execute(
94              final HttpHost target,
95              final ClassicHttpRequest request) throws IOException {
96          return doExecute(target, request, null);
97      }
98  
99      /**
100      * Executes a request using the default context and processes the
101      * response using the given response handler. The content entity associated
102      * with the response is fully consumed and the underlying connection is
103      * released back to the connection manager automatically in all cases
104      * relieving individual {@link HttpClientResponseHandler}s from having to manage
105      * resource deallocation internally.
106      *
107      * @param request   the request to execute
108      * @param responseHandler the response handler
109      *
110      * @return  the response object as generated by the response handler.
111      * @throws IOException in case of a problem or the connection was aborted
112      * @throws ClientProtocolException in case of an http protocol error
113      */
114     @Override
115     public <T> T execute(final ClassicHttpRequest request,
116             final HttpClientResponseHandler<? extends T> responseHandler) throws IOException {
117         return execute(request, null, responseHandler);
118     }
119 
120     /**
121      * Executes a request using the default context and processes the
122      * response using the given response handler. The content entity associated
123      * with the response is fully consumed and the underlying connection is
124      * released back to the connection manager automatically in all cases
125      * relieving individual {@link HttpClientResponseHandler}s from having to manage
126      * resource deallocation internally.
127      *
128      * @param request   the request to execute
129      * @param responseHandler the response handler
130      * @param context   the context to use for the execution, or
131      *                  {@code null} to use the default context
132      *
133      * @return  the response object as generated by the response handler.
134      * @throws IOException in case of a problem or the connection was aborted
135      * @throws ClientProtocolException in case of an http protocol error
136      */
137     @Override
138     public <T> T execute(
139             final ClassicHttpRequest request,
140             final HttpContext context,
141             final HttpClientResponseHandler<? extends T> responseHandler) throws IOException {
142         final HttpHost target = determineTarget(request);
143         return execute(target, request, context, responseHandler);
144     }
145 
146     /**
147      * Executes a request using the default context and processes the
148      * response using the given response handler. The content entity associated
149      * with the response is fully consumed and the underlying connection is
150      * released back to the connection manager automatically in all cases
151      * relieving individual {@link HttpClientResponseHandler}s from having to manage
152      * resource deallocation internally.
153      *
154      * @param target    the target host for the request.
155      *                  Implementations may accept {@code null}
156      *                  if they can still determine a route, for example
157      *                  to a default target or by inspecting the request.
158      * @param request   the request to execute
159      * @param responseHandler the response handler
160      *
161      * @return  the response object as generated by the response handler.
162      * @throws IOException in case of a problem or the connection was aborted
163      * @throws ClientProtocolException in case of an http protocol error
164      */
165     @Override
166     public <T> T execute(final HttpHost target, final ClassicHttpRequest request,
167             final HttpClientResponseHandler<? extends T> responseHandler) throws IOException {
168         return execute(target, request, null, responseHandler);
169     }
170 
171     /**
172      * Executes a request using the default context and processes the
173      * response using the given response handler. The content entity associated
174      * with the response is fully consumed and the underlying connection is
175      * released back to the connection manager automatically in all cases
176      * relieving individual {@link HttpClientResponseHandler}s from having to manage
177      * resource deallocation internally.
178      *
179      * @param target    the target host for the request.
180      *                  Implementations may accept {@code null}
181      *                  if they can still determine a route, for example
182      *                  to a default target or by inspecting the request.
183      * @param request   the request to execute
184      * @param context   the context to use for the execution, or
185      *                  {@code null} to use the default context
186      * @param responseHandler the response handler
187      *
188      * @return  the response object as generated by the response handler.
189      * @throws IOException in case of a problem or the connection was aborted
190      * @throws ClientProtocolException in case of an http protocol error
191      */
192     @Override
193     public <T> T execute(
194             final HttpHost target,
195             final ClassicHttpRequest request,
196             final HttpContext context,
197             final HttpClientResponseHandler<? extends T> responseHandler) throws IOException {
198         Args.notNull(responseHandler, "Response handler");
199 
200         try (final CloseableHttpResponse response = execute(target, request, context)) {
201             try {
202                 final T result = responseHandler.handleResponse(response);
203                 final HttpEntity entity = response.getEntity();
204                 EntityUtils.consume(entity);
205                 return result;
206             } catch (final HttpException t) {
207                 // Try to salvage the underlying connection in case of a protocol exception
208                 final HttpEntity entity = response.getEntity();
209                 try {
210                     EntityUtils.consume(entity);
211                 } catch (final Exception t2) {
212                     // Log this exception. The original exception is more
213                     // important and will be thrown to the caller.
214                     LOG.warn("Error consuming content after an exception.", t2);
215                 }
216                 throw new ClientProtocolException(t);
217             }
218         }
219     }
220 
221 }