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 package org.apache.hc.client5.testing.sync;
28
29 import java.io.IOException;
30
31 import org.apache.hc.client5.http.UserTokenHandler;
32 import org.apache.hc.client5.http.classic.methods.HttpGet;
33 import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
34 import org.apache.hc.client5.http.protocol.HttpClientContext;
35 import org.apache.hc.client5.testing.sync.extension.TestClientResources;
36 import org.apache.hc.core5.http.ClassicHttpRequest;
37 import org.apache.hc.core5.http.ClassicHttpResponse;
38 import org.apache.hc.core5.http.EndpointDetails;
39 import org.apache.hc.core5.http.HttpException;
40 import org.apache.hc.core5.http.HttpHost;
41 import org.apache.hc.core5.http.HttpStatus;
42 import org.apache.hc.core5.http.URIScheme;
43 import org.apache.hc.core5.http.io.HttpRequestHandler;
44 import org.apache.hc.core5.http.io.entity.EntityUtils;
45 import org.apache.hc.core5.http.io.entity.StringEntity;
46 import org.apache.hc.core5.http.protocol.BasicHttpContext;
47 import org.apache.hc.core5.http.protocol.HttpContext;
48 import org.apache.hc.core5.testing.classic.ClassicTestServer;
49 import org.apache.hc.core5.util.Timeout;
50 import org.junit.jupiter.api.Assertions;
51 import org.junit.jupiter.api.Test;
52 import org.junit.jupiter.api.extension.RegisterExtension;
53
54
55
56
57 public class TestStatefulConnManagement {
58
59 public static final Timeout TIMEOUT = Timeout.ofMinutes(1);
60 public static final Timeout LONG_TIMEOUT = Timeout.ofMinutes(3);
61
62 @RegisterExtension
63 private TestClientResources testResources = new TestClientResources(URIScheme.HTTP, TIMEOUT);
64
65 private static class SimpleService implements HttpRequestHandler {
66
67 public SimpleService() {
68 super();
69 }
70
71 @Override
72 public void handle(
73 final ClassicHttpRequest request,
74 final ClassicHttpResponse response,
75 final HttpContext context) throws HttpException, IOException {
76 response.setCode(HttpStatus.SC_OK);
77 final StringEntity entity = new StringEntity("Whatever");
78 response.setEntity(entity);
79 }
80 }
81
82 @Test
83 public void testStatefulConnections() throws Exception {
84 final ClassicTestServer server = testResources.startServer(null, null, null);
85 server.registerHandler("*", new SimpleService());
86 final HttpHost target = testResources.targetHost();
87
88 final int workerCount = 5;
89 final int requestCount = 5;
90
91 final UserTokenHandler userTokenHandler = (route, context) -> {
92 final String id = (String) context.getAttribute("user");
93 return id;
94 };
95
96 final CloseableHttpClient client = testResources.startClient(
97 builder -> builder
98 .setMaxConnTotal(workerCount)
99 .setMaxConnPerRoute(workerCount),
100 builder -> builder
101 .setUserTokenHandler(userTokenHandler)
102 );
103
104 final HttpClientContext[] contexts = new HttpClientContext[workerCount];
105 final HttpWorker[] workers = new HttpWorker[workerCount];
106 for (int i = 0; i < contexts.length; i++) {
107 final HttpClientContext context = HttpClientContext.create();
108 contexts[i] = context;
109 workers[i] = new HttpWorker(
110 "user" + i,
111 context, requestCount, target, client);
112 }
113
114 for (final HttpWorker worker : workers) {
115 worker.start();
116 }
117 for (final HttpWorker worker : workers) {
118 worker.join(LONG_TIMEOUT.toMilliseconds());
119 }
120 for (final HttpWorker worker : workers) {
121 final Exception ex = worker.getException();
122 if (ex != null) {
123 throw ex;
124 }
125 Assertions.assertEquals(requestCount, worker.getCount());
126 }
127
128 for (final HttpContext context : contexts) {
129 final String state0 = (String) context.getAttribute("r0");
130 Assertions.assertNotNull(state0);
131 for (int r = 1; r < requestCount; r++) {
132 Assertions.assertEquals(state0, context.getAttribute("r" + r));
133 }
134 }
135
136 }
137
138 static class HttpWorker extends Thread {
139
140 private final String uid;
141 private final HttpClientContext context;
142 private final int requestCount;
143 private final HttpHost target;
144 private final CloseableHttpClient httpclient;
145
146 private volatile Exception exception;
147 private volatile int count;
148
149 public HttpWorker(
150 final String uid,
151 final HttpClientContext context,
152 final int requestCount,
153 final HttpHost target,
154 final CloseableHttpClient httpclient) {
155 super();
156 this.uid = uid;
157 this.context = context;
158 this.requestCount = requestCount;
159 this.target = target;
160 this.httpclient = httpclient;
161 this.count = 0;
162 }
163
164 public int getCount() {
165 return this.count;
166 }
167
168 public Exception getException() {
169 return this.exception;
170 }
171
172 @Override
173 public void run() {
174 try {
175 this.context.setAttribute("user", this.uid);
176 for (int r = 0; r < this.requestCount; r++) {
177 final HttpGet httpget = new HttpGet("/");
178 this.httpclient.execute(this.target, httpget, this.context, response -> {
179 EntityUtils.consume(response.getEntity());
180 return null;
181 });
182 this.count++;
183
184 final EndpointDetails endpointDetails = this.context.getEndpointDetails();
185 final String connuid = Integer.toHexString(System.identityHashCode(endpointDetails));
186 this.context.setAttribute("r" + r, connuid);
187 }
188
189 } catch (final Exception ex) {
190 this.exception = ex;
191 }
192 }
193
194 }
195
196 @Test
197 public void testRouteSpecificPoolRecylcing() throws Exception {
198 final ClassicTestServer server = testResources.startServer(null, null, null);
199 server.registerHandler("*", new SimpleService());
200 final HttpHost target = testResources.targetHost();
201
202
203
204
205
206 final int maxConn = 2;
207
208
209 final UserTokenHandler userTokenHandler = (route, context) -> context.getAttribute("user");
210
211 final CloseableHttpClient client = testResources.startClient(
212 builder -> builder
213 .setMaxConnTotal(maxConn)
214 .setMaxConnPerRoute(maxConn),
215 builder -> builder
216 .setUserTokenHandler(userTokenHandler)
217 );
218
219
220 final HttpContext context1 = new BasicHttpContext();
221 context1.setAttribute("user", "stuff");
222 client.execute(target, new HttpGet("/"), context1, response -> {
223 EntityUtils.consume(response.getEntity());
224 return null;
225 });
226
227
228
229
230
231 Thread.sleep(100);
232
233
234
235 final HttpContext context2 = new BasicHttpContext();
236 client.execute(new HttpHost("127.0.0.1", server.getPort()), new HttpGet("/"), context2, response -> {
237 EntityUtils.consume(response.getEntity());
238 return null;
239 });
240
241
242
243
244 Thread.sleep(100);
245
246
247
248
249
250
251 final HttpContext context3 = new BasicHttpContext();
252 client.execute(target, new HttpGet("/"), context3, response -> {
253 EntityUtils.consume(response.getEntity());
254 return null;
255 });
256
257
258
259
260
261 }
262
263 }