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.http.impl.execchain;
29
30 import java.io.IOException;
31 import java.io.InterruptedIOException;
32 import java.net.URI;
33 import java.util.concurrent.ExecutionException;
34 import java.util.concurrent.TimeUnit;
35
36 import org.apache.commons.logging.Log;
37 import org.apache.commons.logging.LogFactory;
38 import org.apache.http.ConnectionReuseStrategy;
39 import org.apache.http.HttpClientConnection;
40 import org.apache.http.HttpEntity;
41 import org.apache.http.HttpException;
42 import org.apache.http.HttpHost;
43 import org.apache.http.HttpRequest;
44 import org.apache.http.HttpRequestInterceptor;
45 import org.apache.http.HttpResponse;
46 import org.apache.http.annotation.Immutable;
47 import org.apache.http.client.config.RequestConfig;
48 import org.apache.http.client.methods.CloseableHttpResponse;
49 import org.apache.http.client.methods.HttpExecutionAware;
50 import org.apache.http.client.methods.HttpRequestWrapper;
51 import org.apache.http.client.methods.HttpUriRequest;
52 import org.apache.http.client.protocol.HttpClientContext;
53 import org.apache.http.client.protocol.RequestClientConnControl;
54 import org.apache.http.conn.ConnectionKeepAliveStrategy;
55 import org.apache.http.conn.ConnectionRequest;
56 import org.apache.http.conn.HttpClientConnectionManager;
57 import org.apache.http.conn.routing.HttpRoute;
58 import org.apache.http.impl.conn.ConnectionShutdownException;
59 import org.apache.http.protocol.HttpProcessor;
60 import org.apache.http.protocol.HttpRequestExecutor;
61 import org.apache.http.protocol.ImmutableHttpProcessor;
62 import org.apache.http.protocol.RequestContent;
63 import org.apache.http.protocol.RequestTargetHost;
64 import org.apache.http.protocol.RequestUserAgent;
65 import org.apache.http.util.Args;
66 import org.apache.http.util.VersionInfo;
67
68
69
70
71 @Immutable
72 public class MinimalClientExec implements ClientExecChain {
73
74 private final Log log = LogFactory.getLog(getClass());
75
76 private final HttpRequestExecutor requestExecutor;
77 private final HttpClientConnectionManager connManager;
78 private final ConnectionReuseStrategy reuseStrategy;
79 private final ConnectionKeepAliveStrategy keepAliveStrategy;
80 private final HttpProcessor httpProcessor;
81
82 public MinimalClientExec(
83 final HttpRequestExecutor requestExecutor,
84 final HttpClientConnectionManager connManager,
85 final ConnectionReuseStrategy reuseStrategy,
86 final ConnectionKeepAliveStrategy keepAliveStrategy) {
87 Args.notNull(requestExecutor, "HTTP request executor");
88 Args.notNull(connManager, "Client connection manager");
89 Args.notNull(reuseStrategy, "Connection reuse strategy");
90 Args.notNull(keepAliveStrategy, "Connection keep alive strategy");
91 this.httpProcessor = new ImmutableHttpProcessor(new HttpRequestInterceptor[] {
92 new RequestContent(),
93 new RequestTargetHost(),
94 new RequestClientConnControl(),
95 new RequestUserAgent(VersionInfo.getUserAgent(
96 "Apache-HttpClient", "org.apache.http.client", getClass())),
97 } );
98 this.requestExecutor = requestExecutor;
99 this.connManager = connManager;
100 this.reuseStrategy = reuseStrategy;
101 this.keepAliveStrategy = keepAliveStrategy;
102 }
103
104 public CloseableHttpResponse execute(
105 final HttpRoute route,
106 final HttpRequestWrapper request,
107 final HttpClientContext context,
108 final HttpExecutionAware execAware) throws IOException, HttpException {
109 Args.notNull(route, "HTTP route");
110 Args.notNull(request, "HTTP request");
111 Args.notNull(context, "HTTP context");
112
113 final ConnectionRequest connRequest = connManager.requestConnection(route, null);
114 if (execAware != null) {
115 if (execAware.isAborted()) {
116 connRequest.cancel();
117 throw new RequestAbortedException("Request aborted");
118 } else {
119 execAware.setCancellable(connRequest);
120 }
121 }
122
123 final RequestConfig config = context.getRequestConfig();
124
125 HttpClientConnection managedConn;
126 try {
127 final int timeout = config.getConnectionRequestTimeout();
128 managedConn = connRequest.get(timeout > 0 ? timeout : 0, TimeUnit.MILLISECONDS);
129 } catch(final InterruptedException interrupted) {
130 Thread.currentThread().interrupt();
131 throw new RequestAbortedException("Request aborted", interrupted);
132 } catch(final ExecutionException ex) {
133 Throwable cause = ex.getCause();
134 if (cause == null) {
135 cause = ex;
136 }
137 throw new RequestAbortedException("Request execution failed", cause);
138 }
139
140 final ConnectionHolder releaseTrigger = new ConnectionHolder(log, connManager, managedConn);
141 try {
142 if (execAware != null) {
143 if (execAware.isAborted()) {
144 releaseTrigger.close();
145 throw new RequestAbortedException("Request aborted");
146 } else {
147 execAware.setCancellable(releaseTrigger);
148 }
149 }
150
151 if (!managedConn.isOpen()) {
152 final int timeout = config.getConnectTimeout();
153 this.connManager.connect(
154 managedConn,
155 route,
156 timeout > 0 ? timeout : 0,
157 context);
158 this.connManager.routeComplete(managedConn, route, context);
159 } else {
160 final int timeout = config.getSocketTimeout();
161 if (timeout >= 0) {
162 managedConn.setSocketTimeout(timeout);
163 }
164 }
165
166 HttpHost target = null;
167 final HttpRequest original = request.getOriginal();
168 if (original instanceof HttpUriRequest) {
169 final URI uri = ((HttpUriRequest) original).getURI();
170 if (uri.isAbsolute()) {
171 target = new HttpHost(uri.getHost(), uri.getPort(), uri.getScheme());
172 }
173 }
174 if (target == null) {
175 target = route.getTargetHost();
176 }
177
178 context.setAttribute(HttpClientContext.HTTP_TARGET_HOST, target);
179 context.setAttribute(HttpClientContext.HTTP_REQUEST, request);
180 context.setAttribute(HttpClientContext.HTTP_CONNECTION, managedConn);
181 context.setAttribute(HttpClientContext.HTTP_ROUTE, route);
182
183 httpProcessor.process(request, context);
184 final HttpResponse response = requestExecutor.execute(request, managedConn, context);
185 httpProcessor.process(response, context);
186
187
188 if (reuseStrategy.keepAlive(response, context)) {
189
190 final long duration = keepAliveStrategy.getKeepAliveDuration(response, context);
191 releaseTrigger.setValidFor(duration, TimeUnit.MILLISECONDS);
192 releaseTrigger.markReusable();
193 } else {
194 releaseTrigger.markNonReusable();
195 }
196
197
198 final HttpEntity entity = response.getEntity();
199 if (entity == null || !entity.isStreaming()) {
200
201 releaseTrigger.releaseConnection();
202 return Proxies.enhanceResponse(response, null);
203 } else {
204 return Proxies.enhanceResponse(response, releaseTrigger);
205 }
206 } catch (final ConnectionShutdownException ex) {
207 final InterruptedIOException ioex = new InterruptedIOException(
208 "Connection has been shut down");
209 ioex.initCause(ex);
210 throw ioex;
211 } catch (final HttpException ex) {
212 releaseTrigger.abortConnection();
213 throw ex;
214 } catch (final IOException ex) {
215 releaseTrigger.abortConnection();
216 throw ex;
217 } catch (final RuntimeException ex) {
218 releaseTrigger.abortConnection();
219 throw ex;
220 }
221 }
222
223 }