| /* |
| * ==================================================================== |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * ==================================================================== |
| * |
| * This software consists of voluntary contributions made by many |
| * individuals on behalf of the Apache Software Foundation. For more |
| * information on the Apache Software Foundation, please see |
| * <http://www.apache.org/>. |
| * |
| */ |
| |
| package org.apache.hc.client5.http.impl.classic; |
| |
| import java.io.IOException; |
| import java.net.URI; |
| import java.net.URISyntaxException; |
| |
| import org.apache.hc.client5.http.ClientProtocolException; |
| import org.apache.hc.client5.http.classic.HttpClient; |
| import org.apache.hc.client5.http.utils.URIUtils; |
| import org.apache.hc.core5.annotation.Contract; |
| import org.apache.hc.core5.annotation.ThreadingBehavior; |
| import org.apache.hc.core5.http.ClassicHttpRequest; |
| import org.apache.hc.core5.http.HttpEntity; |
| import org.apache.hc.core5.http.HttpException; |
| import org.apache.hc.core5.http.HttpHost; |
| import org.apache.hc.core5.http.io.HttpClientResponseHandler; |
| import org.apache.hc.core5.http.io.entity.EntityUtils; |
| import org.apache.hc.core5.http.protocol.HttpContext; |
| import org.apache.hc.core5.io.ModalCloseable; |
| import org.apache.hc.core5.util.Args; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| /** |
| * Base implementation of {@link HttpClient} that also implements {@link ModalCloseable}. |
| * |
| * @since 4.3 |
| */ |
| @Contract(threading = ThreadingBehavior.SAFE) |
| public abstract class CloseableHttpClient implements HttpClient, ModalCloseable { |
| |
| private final Logger log = LoggerFactory.getLogger(getClass()); |
| |
| protected abstract CloseableHttpResponse doExecute(HttpHost target, ClassicHttpRequest request, |
| HttpContext context) throws IOException; |
| |
| @Override |
| public CloseableHttpResponse execute( |
| final HttpHost target, |
| final ClassicHttpRequest request, |
| final HttpContext context) throws IOException { |
| return doExecute(target, request, context); |
| } |
| |
| @Override |
| public CloseableHttpResponse execute( |
| final ClassicHttpRequest request, |
| final HttpContext context) throws IOException { |
| Args.notNull(request, "HTTP request"); |
| return doExecute(determineTarget(request), request, context); |
| } |
| |
| private static HttpHost determineTarget(final ClassicHttpRequest request) throws ClientProtocolException { |
| // A null target may be acceptable if there is a default target. |
| // Otherwise, the null target is detected in the director. |
| HttpHost target = null; |
| URI requestURI = null; |
| try { |
| requestURI = request.getUri(); |
| } catch (final URISyntaxException ignore) { |
| } |
| if (requestURI != null && requestURI.isAbsolute()) { |
| target = URIUtils.extractHost(requestURI); |
| if (target == null) { |
| throw new ClientProtocolException("URI does not specify a valid host name: " |
| + requestURI); |
| } |
| } |
| return target; |
| } |
| |
| @Override |
| public CloseableHttpResponse execute( |
| final ClassicHttpRequest request) throws IOException { |
| return execute(request, (HttpContext) null); |
| } |
| |
| @Override |
| public CloseableHttpResponse execute( |
| final HttpHost target, |
| final ClassicHttpRequest request) throws IOException { |
| return doExecute(target, request, null); |
| } |
| |
| /** |
| * Executes a request using the default context and processes the |
| * response using the given response handler. The content entity associated |
| * with the response is fully consumed and the underlying connection is |
| * released back to the connection manager automatically in all cases |
| * relieving individual {@link HttpClientResponseHandler}s from having to manage |
| * resource deallocation internally. |
| * |
| * @param request the request to execute |
| * @param responseHandler the response handler |
| * |
| * @return the response object as generated by the response handler. |
| * @throws IOException in case of a problem or the connection was aborted |
| * @throws ClientProtocolException in case of an http protocol error |
| */ |
| @Override |
| public <T> T execute(final ClassicHttpRequest request, |
| final HttpClientResponseHandler<? extends T> responseHandler) throws IOException { |
| return execute(request, null, responseHandler); |
| } |
| |
| /** |
| * Executes a request using the default context and processes the |
| * response using the given response handler. The content entity associated |
| * with the response is fully consumed and the underlying connection is |
| * released back to the connection manager automatically in all cases |
| * relieving individual {@link HttpClientResponseHandler}s from having to manage |
| * resource deallocation internally. |
| * |
| * @param request the request to execute |
| * @param responseHandler the response handler |
| * @param context the context to use for the execution, or |
| * {@code null} to use the default context |
| * |
| * @return the response object as generated by the response handler. |
| * @throws IOException in case of a problem or the connection was aborted |
| * @throws ClientProtocolException in case of an http protocol error |
| */ |
| @Override |
| public <T> T execute( |
| final ClassicHttpRequest request, |
| final HttpContext context, |
| final HttpClientResponseHandler<? extends T> responseHandler) throws IOException { |
| final HttpHost target = determineTarget(request); |
| return execute(target, request, context, responseHandler); |
| } |
| |
| /** |
| * Executes a request using the default context and processes the |
| * response using the given response handler. The content entity associated |
| * with the response is fully consumed and the underlying connection is |
| * released back to the connection manager automatically in all cases |
| * relieving individual {@link HttpClientResponseHandler}s from having to manage |
| * resource deallocation internally. |
| * |
| * @param target the target host for the request. |
| * Implementations may accept {@code null} |
| * if they can still determine a route, for example |
| * to a default target or by inspecting the request. |
| * @param request the request to execute |
| * @param responseHandler the response handler |
| * |
| * @return the response object as generated by the response handler. |
| * @throws IOException in case of a problem or the connection was aborted |
| * @throws ClientProtocolException in case of an http protocol error |
| */ |
| @Override |
| public <T> T execute(final HttpHost target, final ClassicHttpRequest request, |
| final HttpClientResponseHandler<? extends T> responseHandler) throws IOException { |
| return execute(target, request, null, responseHandler); |
| } |
| |
| /** |
| * Executes a request using the default context and processes the |
| * response using the given response handler. The content entity associated |
| * with the response is fully consumed and the underlying connection is |
| * released back to the connection manager automatically in all cases |
| * relieving individual {@link HttpClientResponseHandler}s from having to manage |
| * resource deallocation internally. |
| * |
| * @param target the target host for the request. |
| * Implementations may accept {@code null} |
| * if they can still determine a route, for example |
| * to a default target or by inspecting the request. |
| * @param request the request to execute |
| * @param context the context to use for the execution, or |
| * {@code null} to use the default context |
| * @param responseHandler the response handler |
| * |
| * @return the response object as generated by the response handler. |
| * @throws IOException in case of a problem or the connection was aborted |
| * @throws ClientProtocolException in case of an http protocol error |
| */ |
| @Override |
| public <T> T execute( |
| final HttpHost target, |
| final ClassicHttpRequest request, |
| final HttpContext context, |
| final HttpClientResponseHandler<? extends T> responseHandler) throws IOException { |
| Args.notNull(responseHandler, "Response handler"); |
| |
| try (final CloseableHttpResponse response = execute(target, request, context)) { |
| try { |
| final T result = responseHandler.handleResponse(response); |
| final HttpEntity entity = response.getEntity(); |
| EntityUtils.consume(entity); |
| return result; |
| } catch (final HttpException t) { |
| // Try to salvage the underlying connection in case of a protocol exception |
| final HttpEntity entity = response.getEntity(); |
| try { |
| EntityUtils.consume(entity); |
| } catch (final Exception t2) { |
| // Log this exception. The original exception is more |
| // important and will be thrown to the caller. |
| this.log.warn("Error consuming content after an exception.", t2); |
| } |
| throw new ClientProtocolException(t); |
| } |
| } |
| } |
| |
| } |