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