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