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.ConnectionKeepAliveStrategy;
34 import org.apache.hc.client5.http.HttpRoute;
35 import org.apache.hc.client5.http.UserTokenHandler;
36 import org.apache.hc.client5.http.classic.ExecChain;
37 import org.apache.hc.client5.http.classic.ExecChainHandler;
38 import org.apache.hc.client5.http.classic.ExecRuntime;
39 import org.apache.hc.client5.http.impl.ConnectionShutdownException;
40 import org.apache.hc.client5.http.io.HttpClientConnectionManager;
41 import org.apache.hc.client5.http.protocol.HttpClientContext;
42 import org.apache.hc.core5.annotation.Contract;
43 import org.apache.hc.core5.annotation.Internal;
44 import org.apache.hc.core5.annotation.ThreadingBehavior;
45 import org.apache.hc.core5.http.ClassicHttpRequest;
46 import org.apache.hc.core5.http.ClassicHttpResponse;
47 import org.apache.hc.core5.http.ConnectionReuseStrategy;
48 import org.apache.hc.core5.http.HttpEntity;
49 import org.apache.hc.core5.http.HttpException;
50 import org.apache.hc.core5.http.message.RequestLine;
51 import org.apache.hc.core5.io.CloseMode;
52 import org.apache.hc.core5.util.Args;
53 import org.apache.hc.core5.util.TimeValue;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
56
57
58
59
60
61
62
63
64 @Contract(threading = ThreadingBehavior.STATELESS)
65 @Internal
66 public final class MainClientExec implements ExecChainHandler {
67
68 private static final Logger LOG = LoggerFactory.getLogger(MainClientExec.class);
69
70 private final HttpClientConnectionManager connectionManager;
71 private final ConnectionReuseStrategy reuseStrategy;
72 private final ConnectionKeepAliveStrategy keepAliveStrategy;
73 private final UserTokenHandler userTokenHandler;
74
75
76
77
78 public MainClientExec(
79 final HttpClientConnectionManager connectionManager,
80 final ConnectionReuseStrategy reuseStrategy,
81 final ConnectionKeepAliveStrategy keepAliveStrategy,
82 final UserTokenHandler userTokenHandler) {
83 this.connectionManager = Args.notNull(connectionManager, "Connection manager");
84 this.reuseStrategy = Args.notNull(reuseStrategy, "Connection reuse strategy");
85 this.keepAliveStrategy = Args.notNull(keepAliveStrategy, "Connection keep alive strategy");
86 this.userTokenHandler = Args.notNull(userTokenHandler, "User token handler");
87 }
88
89 @Override
90 public ClassicHttpResponse execute(
91 final ClassicHttpRequest request,
92 final ExecChain.Scope scope,
93 final ExecChain chain) throws IOException, HttpException {
94 Args.notNull(request, "HTTP request");
95 Args.notNull(scope, "Scope");
96 final String exchangeId = scope.exchangeId;
97 final HttpRoute route = scope.route;
98 final HttpClientContext context = scope.clientContext;
99 final ExecRuntime execRuntime = scope.execRuntime;
100
101 if (LOG.isDebugEnabled()) {
102 LOG.debug("{}: executing {}", exchangeId, new RequestLine(request));
103 }
104 try {
105 RequestEntityProxy.enhance(request);
106
107 final ClassicHttpResponse response = execRuntime.execute(exchangeId, request, context);
108
109 Object userToken = context.getUserToken();
110 if (userToken == null) {
111 userToken = userTokenHandler.getUserToken(route, context);
112 context.setAttribute(HttpClientContext.USER_TOKEN, userToken);
113 }
114
115
116 if (reuseStrategy.keepAlive(request, response, context)) {
117
118 final TimeValue duration = keepAliveStrategy.getKeepAliveDuration(response, context);
119 if (LOG.isDebugEnabled()) {
120 final String s;
121 if (duration != null) {
122 s = "for " + duration;
123 } else {
124 s = "indefinitely";
125 }
126 LOG.debug("{}: connection can be kept alive {}", exchangeId, s);
127 }
128 execRuntime.markConnectionReusable(userToken, duration);
129 } else {
130 execRuntime.markConnectionNonReusable();
131 }
132
133 final HttpEntity entity = response.getEntity();
134 if (entity == null || !entity.isStreaming()) {
135
136 execRuntime.releaseEndpoint();
137 return new CloseableHttpResponse(response, null);
138 }
139 ResponseEntityProxy.enhance(response, execRuntime);
140 return new CloseableHttpResponse(response, execRuntime);
141 } catch (final ConnectionShutdownException ex) {
142 final InterruptedIOException ioex = new InterruptedIOException(
143 "Connection has been shut down");
144 ioex.initCause(ex);
145 execRuntime.discardEndpoint();
146 throw ioex;
147 } catch (final HttpException | RuntimeException | IOException ex) {
148 execRuntime.discardEndpoint();
149 throw ex;
150 } catch (final Error error) {
151 connectionManager.close(CloseMode.IMMEDIATE);
152 throw error;
153 }
154
155 }
156
157 }