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.testing.sync;
29
30 import java.io.IOException;
31 import java.net.URI;
32
33 import org.apache.hc.client5.http.classic.methods.HttpGet;
34 import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
35 import org.apache.hc.core5.http.ClassicHttpResponse;
36 import org.apache.hc.core5.http.EntityDetails;
37 import org.apache.hc.core5.http.Header;
38 import org.apache.hc.core5.http.HeaderElements;
39 import org.apache.hc.core5.http.HttpException;
40 import org.apache.hc.core5.http.HttpHeaders;
41 import org.apache.hc.core5.http.HttpHost;
42 import org.apache.hc.core5.http.HttpResponse;
43 import org.apache.hc.core5.http.HttpResponseInterceptor;
44 import org.apache.hc.core5.http.impl.HttpProcessors;
45 import org.apache.hc.core5.http.io.entity.EntityUtils;
46 import org.apache.hc.core5.http.protocol.HttpContext;
47 import org.apache.hc.core5.http.protocol.HttpProcessor;
48 import org.junit.Assert;
49 import org.junit.Test;
50
51 public class TestConnectionReuse extends LocalServerTestBase {
52
53 @Test
54 public void testReuseOfPersistentConnections() throws Exception {
55 this.connManager.setMaxTotal(5);
56 this.connManager.setDefaultMaxPerRoute(5);
57
58 final HttpHost target = start();
59
60 final WorkerThread[] workers = new WorkerThread[10];
61 for (int i = 0; i < workers.length; i++) {
62 workers[i] = new WorkerThread(
63 this.httpclient,
64 target,
65 new URI("/random/2000"),
66 10, false);
67 }
68
69 for (final WorkerThread worker : workers) {
70 worker.start();
71 }
72 for (final WorkerThread worker : workers) {
73 worker.join(10000);
74 final Exception ex = worker.getException();
75 if (ex != null) {
76 throw ex;
77 }
78 }
79
80
81 Assert.assertTrue(this.connManager.getTotalStats().getAvailable() > 0);
82 }
83
84 private static class AlwaysCloseConn implements HttpResponseInterceptor {
85
86 @Override
87 public void process(
88 final HttpResponse response,
89 final EntityDetails entityDetails,
90 final HttpContext context) throws HttpException, IOException {
91 response.setHeader(HttpHeaders.CONNECTION, HeaderElements.CLOSE);
92 }
93
94 }
95
96 @Test
97 public void testReuseOfClosedConnections() throws Exception {
98 this.connManager.setMaxTotal(5);
99 this.connManager.setDefaultMaxPerRoute(5);
100
101 final HttpProcessor httpproc = HttpProcessors.customServer(null)
102 .add(new AlwaysCloseConn())
103 .build();
104 final HttpHost target = start(httpproc, null);
105
106 final WorkerThread[] workers = new WorkerThread[10];
107 for (int i = 0; i < workers.length; i++) {
108 workers[i] = new WorkerThread(
109 this.httpclient,
110 target,
111 new URI("/random/2000"),
112 10, false);
113 }
114
115 for (final WorkerThread worker : workers) {
116 worker.start();
117 }
118 for (final WorkerThread worker : workers) {
119 worker.join(10000);
120 final Exception ex = worker.getException();
121 if (ex != null) {
122 throw ex;
123 }
124 }
125
126
127 Assert.assertEquals(0, this.connManager.getTotalStats().getAvailable());
128 }
129
130 @Test
131 public void testReuseOfAbortedConnections() throws Exception {
132 this.connManager.setMaxTotal(5);
133 this.connManager.setDefaultMaxPerRoute(5);
134
135 final HttpHost target = start();
136
137 final WorkerThread[] workers = new WorkerThread[10];
138 for (int i = 0; i < workers.length; i++) {
139 workers[i] = new WorkerThread(
140 this.httpclient,
141 target,
142 new URI("/random/2000"),
143 10, true);
144 }
145
146 for (final WorkerThread worker : workers) {
147 worker.start();
148 }
149 for (final WorkerThread worker : workers) {
150 worker.join(10000);
151 final Exception ex = worker.getException();
152 if (ex != null) {
153 throw ex;
154 }
155 }
156
157
158 Assert.assertEquals(0, this.connManager.getTotalStats().getAvailable());
159 }
160
161 @Test
162 public void testKeepAliveHeaderRespected() throws Exception {
163 this.connManager.setMaxTotal(1);
164 this.connManager.setDefaultMaxPerRoute(1);
165
166 final HttpProcessor httpproc = HttpProcessors.customServer(null)
167 .add(new ResponseKeepAlive())
168 .build();
169 final HttpHost target = start(httpproc, null);
170
171 ClassicHttpResponse response = this.httpclient.execute(target, new HttpGet("/random/2000"));
172 EntityUtils.consume(response.getEntity());
173
174 Assert.assertEquals(1, this.connManager.getTotalStats().getAvailable());
175
176 response = this.httpclient.execute(target, new HttpGet("/random/2000"));
177 EntityUtils.consume(response.getEntity());
178
179 Assert.assertEquals(1, this.connManager.getTotalStats().getAvailable());
180
181
182 Thread.sleep(1100);
183 response = this.httpclient.execute(target, new HttpGet("/random/2000"));
184 EntityUtils.consume(response.getEntity());
185
186 Assert.assertEquals(1, this.connManager.getTotalStats().getAvailable());
187
188
189
190 Thread.sleep(500);
191 response = this.httpclient.execute(target, new HttpGet("/random/2000"));
192 EntityUtils.consume(response.getEntity());
193
194 Assert.assertEquals(1, this.connManager.getTotalStats().getAvailable());
195 }
196
197 private static class WorkerThread extends Thread {
198
199 private final URI requestURI;
200 private final HttpHost target;
201 private final CloseableHttpClient httpclient;
202 private final int repetitions;
203 private final boolean forceClose;
204
205 private volatile Exception exception;
206
207 public WorkerThread(
208 final CloseableHttpClient httpclient,
209 final HttpHost target,
210 final URI requestURI,
211 final int repetitions,
212 final boolean forceClose) {
213 super();
214 this.httpclient = httpclient;
215 this.requestURI = requestURI;
216 this.target = target;
217 this.repetitions = repetitions;
218 this.forceClose = forceClose;
219 }
220
221 @Override
222 public void run() {
223 try {
224 for (int i = 0; i < this.repetitions; i++) {
225 final HttpGet httpget = new HttpGet(this.requestURI);
226 final ClassicHttpResponse response = this.httpclient.execute(
227 this.target,
228 httpget);
229 if (this.forceClose) {
230 httpget.cancel();
231 } else {
232 EntityUtils.consume(response.getEntity());
233 }
234 }
235 } catch (final Exception ex) {
236 this.exception = ex;
237 }
238 }
239
240 public Exception getException() {
241 return exception;
242 }
243
244 }
245
246
247
248 private static class ResponseKeepAlive implements HttpResponseInterceptor {
249 @Override
250 public void process(
251 final HttpResponse response,
252 final EntityDetails entityDetails,
253 final HttpContext context) throws HttpException, IOException {
254 final Header connection = response.getFirstHeader(HttpHeaders.CONNECTION);
255 if(connection != null) {
256 if(!connection.getValue().equalsIgnoreCase("Close")) {
257 response.addHeader(HeaderElements.KEEP_ALIVE, "timeout=1");
258 }
259 }
260 }
261 }
262
263 }