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.http.impl.client;
29
30 import java.io.Closeable;
31 import java.io.IOException;
32 import java.lang.reflect.UndeclaredThrowableException;
33 import java.net.URI;
34
35 import org.apache.commons.logging.Log;
36 import org.apache.commons.logging.LogFactory;
37 import org.apache.http.HttpEntity;
38 import org.apache.http.HttpHost;
39 import org.apache.http.HttpRequest;
40 import org.apache.http.HttpResponse;
41 import org.apache.http.annotation.ThreadSafe;
42 import org.apache.http.client.ClientProtocolException;
43 import org.apache.http.client.HttpClient;
44 import org.apache.http.client.ResponseHandler;
45 import org.apache.http.client.methods.CloseableHttpResponse;
46 import org.apache.http.client.methods.HttpUriRequest;
47 import org.apache.http.client.utils.URIUtils;
48 import org.apache.http.protocol.HttpContext;
49 import org.apache.http.util.Args;
50 import org.apache.http.util.EntityUtils;
51
52 /**
53 * Minimal implementation of {@link HttpClient} that also implements {@link Closeable}.
54 *
55 * @since 4.3
56 */
57 @ThreadSafe
58 public abstract class CloseableHttpClient implements HttpClient, Closeable {
59
60 private final Log log = LogFactory.getLog(getClass());
61
62 protected abstract CloseableHttpResponse doExecute(HttpHost target, HttpRequest request,
63 HttpContext context) throws IOException, ClientProtocolException;
64
65 /**
66 * {@inheritDoc}
67 */
68 public CloseableHttpResponse execute(
69 final HttpHost target,
70 final HttpRequest request,
71 final HttpContext context) throws IOException, ClientProtocolException {
72 return doExecute(target, request, context);
73 }
74
75 /**
76 * {@inheritDoc}
77 */
78 public CloseableHttpResponse execute(
79 final HttpUriRequest request,
80 final HttpContext context) throws IOException, ClientProtocolException {
81 Args.notNull(request, "HTTP request");
82 return doExecute(determineTarget(request), request, context);
83 }
84
85 private static HttpHost determineTarget(final HttpUriRequest request) throws ClientProtocolException {
86 // A null target may be acceptable if there is a default target.
87 // Otherwise, the null target is detected in the director.
88 HttpHost target = null;
89
90 final URI requestURI = request.getURI();
91 if (requestURI.isAbsolute()) {
92 target = URIUtils.extractHost(requestURI);
93 if (target == null) {
94 throw new ClientProtocolException("URI does not specify a valid host name: "
95 + requestURI);
96 }
97 }
98 return target;
99 }
100
101 /**
102 * {@inheritDoc}
103 */
104 public CloseableHttpResponse execute(
105 final HttpUriRequest request) throws IOException, ClientProtocolException {
106 return execute(request, (HttpContext) null);
107 }
108
109 /**
110 * {@inheritDoc}
111 */
112 public CloseableHttpResponse execute(
113 final HttpHost target,
114 final HttpRequest request) throws IOException, ClientProtocolException {
115 return doExecute(target, request, (HttpContext) null);
116 }
117
118 /**
119 * Executes a request using the default context and processes the
120 * response using the given response handler. The content entity associated
121 * with the response is fully consumed and the underlying connection is
122 * released back to the connection manager automatically in all cases
123 * relieving individual {@link ResponseHandler}s from having to manage
124 * resource deallocation internally.
125 *
126 * @param request the request to execute
127 * @param responseHandler the response handler
128 *
129 * @return the response object as generated by the response handler.
130 * @throws IOException in case of a problem or the connection was aborted
131 * @throws ClientProtocolException in case of an http protocol error
132 */
133 public <T> T execute(final HttpUriRequest request,
134 final ResponseHandler<? extends T> responseHandler) throws IOException,
135 ClientProtocolException {
136 return execute(request, responseHandler, null);
137 }
138
139 /**
140 * Executes a request using the default context and processes the
141 * response using the given response handler. The content entity associated
142 * with the response is fully consumed and the underlying connection is
143 * released back to the connection manager automatically in all cases
144 * relieving individual {@link ResponseHandler}s from having to manage
145 * resource deallocation internally.
146 *
147 * @param request the request to execute
148 * @param responseHandler the response handler
149 * @param context the context to use for the execution, or
150 * <code>null</code> to use the default context
151 *
152 * @return the response object as generated by the response handler.
153 * @throws IOException in case of a problem or the connection was aborted
154 * @throws ClientProtocolException in case of an http protocol error
155 */
156 public <T> T execute(final HttpUriRequest request,
157 final ResponseHandler<? extends T> responseHandler, final HttpContext context)
158 throws IOException, ClientProtocolException {
159 final HttpHost target = determineTarget(request);
160 return execute(target, request, responseHandler, context);
161 }
162
163 /**
164 * Executes a request using the default context and processes the
165 * response using the given response handler. The content entity associated
166 * with the response is fully consumed and the underlying connection is
167 * released back to the connection manager automatically in all cases
168 * relieving individual {@link ResponseHandler}s from having to manage
169 * resource deallocation internally.
170 *
171 * @param target the target host for the request.
172 * Implementations may accept <code>null</code>
173 * if they can still determine a route, for example
174 * to a default target or by inspecting the request.
175 * @param request the request to execute
176 * @param responseHandler the response handler
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 public <T> T execute(final HttpHost target, final HttpRequest request,
183 final ResponseHandler<? extends T> responseHandler) throws IOException,
184 ClientProtocolException {
185 return execute(target, request, responseHandler, null);
186 }
187
188 /**
189 * Executes a request using the default context and processes the
190 * response using the given response handler. The content entity associated
191 * with the response is fully consumed and the underlying connection is
192 * released back to the connection manager automatically in all cases
193 * relieving individual {@link ResponseHandler}s from having to manage
194 * resource deallocation internally.
195 *
196 * @param target the target host for the request.
197 * Implementations may accept <code>null</code>
198 * if they can still determine a route, for example
199 * to a default target or by inspecting the request.
200 * @param request the request to execute
201 * @param responseHandler the response handler
202 * @param context the context to use for the execution, or
203 * <code>null</code> to use the default context
204 *
205 * @return the response object as generated by the response handler.
206 * @throws IOException in case of a problem or the connection was aborted
207 * @throws ClientProtocolException in case of an http protocol error
208 */
209 public <T> T execute(final HttpHost target, final HttpRequest request,
210 final ResponseHandler<? extends T> responseHandler, final HttpContext context)
211 throws IOException, ClientProtocolException {
212 Args.notNull(responseHandler, "Response handler");
213
214 final HttpResponse response = execute(target, request, context);
215
216 T result;
217 try {
218 result = responseHandler.handleResponse(response);
219 } catch (final Exception t) {
220 final HttpEntity entity = response.getEntity();
221 try {
222 EntityUtils.consume(entity);
223 } catch (final Exception t2) {
224 // Log this exception. The original exception is more
225 // important and will be thrown to the caller.
226 this.log.warn("Error consuming content after an exception.", t2);
227 }
228 if (t instanceof RuntimeException) {
229 throw (RuntimeException) t;
230 }
231 if (t instanceof IOException) {
232 throw (IOException) t;
233 }
234 throw new UndeclaredThrowableException(t);
235 }
236
237 // Handling the response was successful. Ensure that the content has
238 // been fully consumed.
239 final HttpEntity entity = response.getEntity();
240 EntityUtils.consume(entity);
241 return result;
242 }
243
244 }