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