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