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.DefaultClientConnectionReuseStrategy;
41 import org.apache.hc.client5.http.impl.DefaultSchemePortResolver;
42 import org.apache.hc.client5.http.impl.ExecSupport;
43 import org.apache.hc.client5.http.io.HttpClientConnectionManager;
44 import org.apache.hc.client5.http.protocol.HttpClientContext;
45 import org.apache.hc.client5.http.protocol.RequestClientConnControl;
46 import org.apache.hc.client5.http.routing.RoutingSupport;
47 import org.apache.hc.core5.annotation.Contract;
48 import org.apache.hc.core5.annotation.ThreadingBehavior;
49 import org.apache.hc.core5.concurrent.CancellableDependency;
50 import org.apache.hc.core5.http.ClassicHttpRequest;
51 import org.apache.hc.core5.http.ClassicHttpResponse;
52 import org.apache.hc.core5.http.ConnectionReuseStrategy;
53 import org.apache.hc.core5.http.HttpEntity;
54 import org.apache.hc.core5.http.HttpException;
55 import org.apache.hc.core5.http.HttpHost;
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 = DefaultClientConnectionReuseStrategy.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 clientContext.setExchangeId(exchangeId);
136 final ExecRuntime execRuntime = new InternalExecRuntime(LOG, connManager, requestExecutor,
137 request instanceof CancellableDependency ? (CancellableDependency) request : null);
138 try {
139 if (!execRuntime.isEndpointAcquired()) {
140 execRuntime.acquireEndpoint(exchangeId, route, null, clientContext);
141 }
142 if (!execRuntime.isEndpointConnected()) {
143 execRuntime.connectEndpoint(clientContext);
144 }
145
146 clientContext.setAttribute(HttpCoreContext.HTTP_REQUEST, request);
147 clientContext.setAttribute(HttpClientContext.HTTP_ROUTE, route);
148
149 httpProcessor.process(request, request.getEntity(), clientContext);
150 final ClassicHttpResponse response = execRuntime.execute(exchangeId, request, clientContext);
151 httpProcessor.process(response, response.getEntity(), clientContext);
152
153 if (reuseStrategy.keepAlive(request, response, clientContext)) {
154 execRuntime.markConnectionReusable(null, TimeValue.NEG_ONE_MILLISECOND);
155 } else {
156 execRuntime.markConnectionNonReusable();
157 }
158
159
160 final HttpEntity entity = response.getEntity();
161 if (entity == null || !entity.isStreaming()) {
162
163 execRuntime.releaseEndpoint();
164 return new CloseableHttpResponse(response, null);
165 }
166 ResponseEntityProxy.enhance(response, execRuntime);
167 return new CloseableHttpResponse(response, execRuntime);
168 } catch (final ConnectionShutdownException ex) {
169 final InterruptedIOException ioex = new InterruptedIOException("Connection has been shut down");
170 ioex.initCause(ex);
171 execRuntime.discardEndpoint();
172 throw ioex;
173 } catch (final HttpException httpException) {
174 execRuntime.discardEndpoint();
175 throw new ClientProtocolException(httpException);
176 } catch (final RuntimeException | IOException ex) {
177 execRuntime.discardEndpoint();
178 throw ex;
179 } catch (final Error error) {
180 connManager.close(CloseMode.IMMEDIATE);
181 throw error;
182 }
183 }
184
185 @Override
186 public void close() throws IOException {
187 this.connManager.close();
188 }
189
190 @Override
191 public void close(final CloseMode closeMode) {
192 this.connManager.close(closeMode);
193 }
194
195 }