1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 package org.apache.hc.client5.http.impl.classic;
29
30 import java.io.IOException;
31 import java.io.InterruptedIOException;
32
33 import org.apache.hc.client5.http.ClientProtocolException;
34 import org.apache.hc.client5.http.HttpRoute;
35 import org.apache.hc.client5.http.SchemePortResolver;
36 import org.apache.hc.client5.http.classic.ExecRuntime;
37 import org.apache.hc.client5.http.config.Configurable;
38 import org.apache.hc.client5.http.config.RequestConfig;
39 import org.apache.hc.client5.http.impl.ConnectionShutdownException;
40 import org.apache.hc.client5.http.impl.DefaultSchemePortResolver;
41 import org.apache.hc.client5.http.impl.ExecSupport;
42 import org.apache.hc.client5.http.io.HttpClientConnectionManager;
43 import org.apache.hc.client5.http.protocol.HttpClientContext;
44 import org.apache.hc.client5.http.protocol.RequestClientConnControl;
45 import org.apache.hc.client5.http.routing.RoutingSupport;
46 import org.apache.hc.core5.annotation.Contract;
47 import org.apache.hc.core5.annotation.ThreadingBehavior;
48 import org.apache.hc.core5.concurrent.CancellableDependency;
49 import org.apache.hc.core5.http.ClassicHttpRequest;
50 import org.apache.hc.core5.http.ClassicHttpResponse;
51 import org.apache.hc.core5.http.ConnectionReuseStrategy;
52 import org.apache.hc.core5.http.HttpEntity;
53 import org.apache.hc.core5.http.HttpException;
54 import org.apache.hc.core5.http.HttpHost;
55 import org.apache.hc.core5.http.impl.DefaultConnectionReuseStrategy;
56 import org.apache.hc.core5.http.impl.io.HttpRequestExecutor;
57 import org.apache.hc.core5.http.protocol.BasicHttpContext;
58 import org.apache.hc.core5.http.protocol.DefaultHttpProcessor;
59 import org.apache.hc.core5.http.protocol.HttpContext;
60 import org.apache.hc.core5.http.protocol.HttpCoreContext;
61 import org.apache.hc.core5.http.protocol.HttpProcessor;
62 import org.apache.hc.core5.http.protocol.RequestContent;
63 import org.apache.hc.core5.http.protocol.RequestTargetHost;
64 import org.apache.hc.core5.http.protocol.RequestUserAgent;
65 import org.apache.hc.core5.io.CloseMode;
66 import org.apache.hc.core5.net.URIAuthority;
67 import org.apache.hc.core5.util.Args;
68 import org.apache.hc.core5.util.TimeValue;
69 import org.apache.hc.core5.util.VersionInfo;
70 import org.slf4j.Logger;
71 import org.slf4j.LoggerFactory;
72
73
74
75
76
77
78
79
80
81
82
83
84
85 @Contract(threading = ThreadingBehavior.SAFE_CONDITIONAL)
86 public class MinimalHttpClient extends CloseableHttpClient {
87
88 private static final Logger LOG = LoggerFactory.getLogger(MinimalHttpClient.class);
89
90 private final HttpClientConnectionManager connManager;
91 private final ConnectionReuseStrategy reuseStrategy;
92 private final SchemePortResolver schemePortResolver;
93 private final HttpRequestExecutor requestExecutor;
94 private final HttpProcessor httpProcessor;
95
96 MinimalHttpClient(final HttpClientConnectionManager connManager) {
97 super();
98 this.connManager = Args.notNull(connManager, "HTTP connection manager");
99 this.reuseStrategy = DefaultConnectionReuseStrategy.INSTANCE;
100 this.schemePortResolver = DefaultSchemePortResolver.INSTANCE;
101 this.requestExecutor = new HttpRequestExecutor(this.reuseStrategy);
102 this.httpProcessor = new DefaultHttpProcessor(
103 new RequestContent(),
104 new RequestTargetHost(),
105 new RequestClientConnControl(),
106 new RequestUserAgent(VersionInfo.getSoftwareInfo(
107 "Apache-HttpClient", "org.apache.hc.client5", getClass())));
108 }
109
110 @Override
111 protected CloseableHttpResponse doExecute(
112 final HttpHost target,
113 final ClassicHttpRequest request,
114 final HttpContext context) throws IOException {
115 Args.notNull(target, "Target host");
116 Args.notNull(request, "HTTP request");
117 if (request.getScheme() == null) {
118 request.setScheme(target.getSchemeName());
119 }
120 if (request.getAuthority() == null) {
121 request.setAuthority(new URIAuthority(target));
122 }
123 final HttpClientContext clientContext = HttpClientContext.adapt(
124 context != null ? context : new BasicHttpContext());
125 RequestConfig config = null;
126 if (request instanceof Configurable) {
127 config = ((Configurable) request).getConfig();
128 }
129 if (config != null) {
130 clientContext.setRequestConfig(config);
131 }
132
133 final HttpRoute/http/HttpRoute.html#HttpRoute">HttpRoute route = new HttpRoute(RoutingSupport.normalize(target, schemePortResolver));
134 final String exchangeId = ExecSupport.getNextExchangeId();
135 final ExecRuntime execRuntime = new InternalExecRuntime(LOG, connManager, requestExecutor,
136 request instanceof CancellableDependency ? (CancellableDependency) request : null);
137 try {
138 if (!execRuntime.isEndpointAcquired()) {
139 execRuntime.acquireEndpoint(exchangeId, route, null, clientContext);
140 }
141 if (!execRuntime.isEndpointConnected()) {
142 execRuntime.connectEndpoint(clientContext);
143 }
144
145 clientContext.setAttribute(HttpCoreContext.HTTP_REQUEST, request);
146 clientContext.setAttribute(HttpClientContext.HTTP_ROUTE, route);
147
148 httpProcessor.process(request, request.getEntity(), clientContext);
149 final ClassicHttpResponse response = execRuntime.execute(exchangeId, request, clientContext);
150 httpProcessor.process(response, response.getEntity(), clientContext);
151
152 if (reuseStrategy.keepAlive(request, response, clientContext)) {
153 execRuntime.markConnectionReusable(null, TimeValue.NEG_ONE_MILLISECOND);
154 } else {
155 execRuntime.markConnectionNonReusable();
156 }
157
158
159 final HttpEntity entity = response.getEntity();
160 if (entity == null || !entity.isStreaming()) {
161
162 execRuntime.releaseEndpoint();
163 return new CloseableHttpResponse(response, null);
164 }
165 ResponseEntityProxy.enhance(response, execRuntime);
166 return new CloseableHttpResponse(response, execRuntime);
167 } catch (final ConnectionShutdownException ex) {
168 final InterruptedIOException ioex = new InterruptedIOException("Connection has been shut down");
169 ioex.initCause(ex);
170 execRuntime.discardEndpoint();
171 throw ioex;
172 } catch (final HttpException httpException) {
173 execRuntime.discardEndpoint();
174 throw new ClientProtocolException(httpException);
175 } catch (final RuntimeException | IOException ex) {
176 execRuntime.discardEndpoint();
177 throw ex;
178 } catch (final Error error) {
179 connManager.close(CloseMode.IMMEDIATE);
180 throw error;
181 }
182 }
183
184 @Override
185 public void close() throws IOException {
186 this.connManager.close();
187 }
188
189 @Override
190 public void close(final CloseMode closeMode) {
191 this.connManager.close(closeMode);
192 }
193
194 }