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.DefaultHttpProcessor;
58 import org.apache.hc.core5.http.protocol.HttpContext;
59 import org.apache.hc.core5.http.protocol.HttpProcessor;
60 import org.apache.hc.core5.http.protocol.RequestContent;
61 import org.apache.hc.core5.http.protocol.RequestTargetHost;
62 import org.apache.hc.core5.http.protocol.RequestUserAgent;
63 import org.apache.hc.core5.io.CloseMode;
64 import org.apache.hc.core5.net.URIAuthority;
65 import org.apache.hc.core5.util.Args;
66 import org.apache.hc.core5.util.TimeValue;
67 import org.apache.hc.core5.util.VersionInfo;
68 import org.slf4j.Logger;
69 import org.slf4j.LoggerFactory;
70
71
72
73
74
75
76
77
78
79
80
81
82
83 @Contract(threading = ThreadingBehavior.SAFE_CONDITIONAL)
84 public class MinimalHttpClient extends CloseableHttpClient {
85
86 private static final Logger LOG = LoggerFactory.getLogger(MinimalHttpClient.class);
87
88 private final HttpClientConnectionManager connManager;
89 private final ConnectionReuseStrategy reuseStrategy;
90 private final SchemePortResolver schemePortResolver;
91 private final HttpRequestExecutor requestExecutor;
92 private final HttpProcessor httpProcessor;
93
94 MinimalHttpClient(final HttpClientConnectionManager connManager) {
95 super();
96 this.connManager = Args.notNull(connManager, "HTTP connection manager");
97 this.reuseStrategy = DefaultClientConnectionReuseStrategy.INSTANCE;
98 this.schemePortResolver = DefaultSchemePortResolver.INSTANCE;
99 this.requestExecutor = new HttpRequestExecutor(this.reuseStrategy);
100 this.httpProcessor = new DefaultHttpProcessor(
101 new RequestContent(),
102 new RequestTargetHost(),
103 new RequestClientConnControl(),
104 new RequestUserAgent(VersionInfo.getSoftwareInfo(
105 "Apache-HttpClient", "org.apache.hc.client5", getClass())));
106 }
107
108 @Override
109 protected CloseableHttpResponse doExecute(
110 final HttpHost target,
111 final ClassicHttpRequest request,
112 final HttpContext context) throws IOException {
113 Args.notNull(target, "Target host");
114 Args.notNull(request, "HTTP request");
115 if (request.getScheme() == null) {
116 request.setScheme(target.getSchemeName());
117 }
118 if (request.getAuthority() == null) {
119 request.setAuthority(new URIAuthority(target));
120 }
121 final HttpClientContext clientContext = HttpClientContext.castOrCreate(context);
122 RequestConfig config = null;
123 if (request instanceof Configurable) {
124 config = ((Configurable) request).getConfig();
125 }
126 if (config != null) {
127 clientContext.setRequestConfig(config);
128 }
129
130 final HttpRoute route = new HttpRoute(RoutingSupport.normalize(target, schemePortResolver));
131 final String exchangeId = ExecSupport.getNextExchangeId();
132 clientContext.setExchangeId(exchangeId);
133 final ExecRuntime execRuntime = new InternalExecRuntime(LOG, connManager, requestExecutor,
134 request instanceof CancellableDependency ? (CancellableDependency) request : null);
135 try {
136 if (!execRuntime.isEndpointAcquired()) {
137 execRuntime.acquireEndpoint(exchangeId, route, null, clientContext);
138 }
139 if (!execRuntime.isEndpointConnected()) {
140 execRuntime.connectEndpoint(clientContext);
141 }
142
143 clientContext.setRequest(request);
144 clientContext.setRoute(route);
145
146 httpProcessor.process(request, request.getEntity(), clientContext);
147 final ClassicHttpResponse response = execRuntime.execute(exchangeId, request, clientContext);
148 httpProcessor.process(response, response.getEntity(), clientContext);
149
150 if (reuseStrategy.keepAlive(request, response, clientContext)) {
151 execRuntime.markConnectionReusable(null, TimeValue.NEG_ONE_MILLISECOND);
152 } else {
153 execRuntime.markConnectionNonReusable();
154 }
155
156
157 final HttpEntity entity = response.getEntity();
158 if (entity == null || !entity.isStreaming()) {
159
160 execRuntime.releaseEndpoint();
161 return new CloseableHttpResponse(response, null);
162 }
163 ResponseEntityProxy.enhance(response, execRuntime);
164 return new CloseableHttpResponse(response, execRuntime);
165 } catch (final ConnectionShutdownException ex) {
166 final InterruptedIOException ioex = new InterruptedIOException("Connection has been shut down");
167 ioex.initCause(ex);
168 execRuntime.discardEndpoint();
169 throw ioex;
170 } catch (final HttpException httpException) {
171 execRuntime.discardEndpoint();
172 throw new ClientProtocolException(httpException);
173 } catch (final RuntimeException | IOException ex) {
174 execRuntime.discardEndpoint();
175 throw ex;
176 } catch (final Error error) {
177 connManager.close(CloseMode.IMMEDIATE);
178 throw error;
179 }
180 }
181
182 @Override
183 public void close() throws IOException {
184 this.connManager.close();
185 }
186
187 @Override
188 public void close(final CloseMode closeMode) {
189 this.connManager.close(closeMode);
190 }
191
192 }