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 }