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.http.impl.client.cache;
28
29 import static org.junit.Assert.assertEquals;
30 import static org.junit.Assert.assertNull;
31
32 import java.io.IOException;
33 import java.util.Locale;
34
35 import org.apache.http.Consts;
36 import org.apache.http.Header;
37 import org.apache.http.HttpEntity;
38 import org.apache.http.HttpException;
39 import org.apache.http.HttpRequest;
40 import org.apache.http.HttpResponse;
41 import org.apache.http.MethodNotSupportedException;
42 import org.apache.http.client.ClientProtocolException;
43 import org.apache.http.client.HttpClient;
44 import org.apache.http.client.cache.CacheResponseStatus;
45 import org.apache.http.client.cache.HttpCacheContext;
46 import org.apache.http.client.config.RequestConfig;
47 import org.apache.http.client.methods.HttpGet;
48 import org.apache.http.config.SocketConfig;
49 import org.apache.http.entity.ByteArrayEntity;
50 import org.apache.http.impl.bootstrap.HttpServer;
51 import org.apache.http.impl.bootstrap.ServerBootstrap;
52 import org.apache.http.impl.client.CloseableHttpClient;
53 import org.apache.http.impl.client.HttpClientBuilder;
54 import org.apache.http.protocol.BasicHttpContext;
55 import org.apache.http.protocol.HttpContext;
56 import org.apache.http.protocol.HttpRequestHandler;
57 import org.junit.After;
58 import org.junit.Before;
59 import org.junit.Test;
60
61
62
63
64
65
66 public class TestStaleWhileRevalidationReleasesConnection {
67
68 private static final EchoViaHeaderHandler cacheHandler = new EchoViaHeaderHandler();
69
70 protected HttpServer localServer;
71 private int port;
72 private CloseableHttpClient client;
73 private final String url = "/static/dom";
74 private final String url2 = "2";
75
76
77 @Before
78 public void start() throws Exception {
79 this.localServer = ServerBootstrap.bootstrap()
80 .setSocketConfig(SocketConfig.custom()
81 .setSoTimeout(5000)
82 .build())
83 .registerHandler(url + "*", new EchoViaHeaderHandler())
84 .create();
85 this.localServer.start();
86
87 port = this.localServer.getLocalPort();
88
89 final CacheConfig cacheConfig = CacheConfig.custom()
90 .setMaxCacheEntries(100)
91 .setMaxObjectSize(15)
92 .setAsynchronousWorkerIdleLifetimeSecs(60)
93 .setAsynchronousWorkersMax(1)
94 .setAsynchronousWorkersCore(1)
95 .setRevalidationQueueSize(100)
96 .setSharedCache(true)
97 .build();
98
99 final HttpClientBuilder clientBuilder = CachingHttpClientBuilder.create().setCacheConfig(cacheConfig);
100 clientBuilder.setMaxConnTotal(1);
101 clientBuilder.setMaxConnPerRoute(1);
102
103 final RequestConfig config = RequestConfig.custom()
104 .setSocketTimeout(10000)
105 .setConnectTimeout(10000)
106 .setConnectionRequestTimeout(1000)
107 .build();
108
109 clientBuilder.setDefaultRequestConfig(config);
110
111
112 client = clientBuilder.build();
113 }
114
115 @After
116 public void stop() {
117 if (this.localServer != null) {
118 try {
119 this.localServer.stop();
120 } catch(final Exception e) {
121 e.printStackTrace();
122 }
123 }
124
125 try {
126 client.close();
127 } catch(final IOException e) {
128 e.printStackTrace();
129 }
130 }
131
132 @Test
133 public void testStaleWhileRevalidate() {
134 final String urlToCall = "http://localhost:"+port + url;
135 final HttpContext localContext = new BasicHttpContext();
136 Exception requestException = null;
137
138
139 requestException = sendRequest(client, localContext,urlToCall,null);
140 assertNull(requestException);
141
142 CacheResponseStatus responseStatus = (CacheResponseStatus) localContext.getAttribute(HttpCacheContext.CACHE_RESPONSE_STATUS);
143 assertEquals(CacheResponseStatus.CACHE_MISS,responseStatus);
144
145 try {
146 Thread.sleep(1000);
147 } catch (final Exception e) {
148
149 }
150
151 requestException = sendRequest(client, localContext,urlToCall,null);
152 assertNull(requestException);
153
154 responseStatus = (CacheResponseStatus) localContext.getAttribute(HttpCacheContext.CACHE_RESPONSE_STATUS);
155 assertEquals(CacheResponseStatus.CACHE_HIT,responseStatus);
156
157 requestException = sendRequest(client, localContext,urlToCall,null);
158 assertNull(requestException);
159
160 responseStatus = (CacheResponseStatus) localContext.getAttribute(HttpCacheContext.CACHE_RESPONSE_STATUS);
161 assertEquals(CacheResponseStatus.CACHE_HIT,responseStatus);
162
163
164 try {
165 Thread.sleep(4000);
166 } catch (final Exception e) {
167
168 }
169
170
171 requestException = sendRequest(client, localContext,urlToCall,"This is new content that is bigger than cache limit");
172 assertNull(requestException);
173
174 responseStatus = (CacheResponseStatus) localContext.getAttribute(HttpCacheContext.CACHE_RESPONSE_STATUS);
175 assertEquals(CacheResponseStatus.CACHE_HIT,responseStatus);
176
177 try {
178 Thread.sleep(1000);
179 } catch (final Exception e) {
180
181 }
182
183
184 requestException = sendRequest(client, localContext,urlToCall+url2,null);
185 if(requestException!=null) {
186 requestException.printStackTrace();
187 }
188 assertNull(requestException);
189
190
191 }
192
193 static Exception sendRequest(final HttpClient cachingClient, final HttpContext localContext , final String url, final String content) {
194 final HttpGet httpget = new HttpGet(url);
195 if(content!=null) {
196 httpget.setHeader(cacheHandler.getUserContentHeader(),content);
197 }
198
199 HttpResponse response = null;
200 try {
201 response = cachingClient.execute(httpget, localContext);
202 return null;
203 } catch (final ClientProtocolException e1) {
204 return e1;
205 } catch (final IOException e1) {
206 return e1;
207 } finally {
208 if(response!=null) {
209 final HttpEntity entity = response.getEntity();
210 try {
211 IOUtils.consume(entity);
212 } catch (final IOException e) {
213 e.printStackTrace();
214 }
215 }
216 }
217 }
218
219 public static class EchoViaHeaderHandler
220 implements HttpRequestHandler {
221
222 private final String CACHE_CONTROL_HEADER = "Cache-Control";
223
224 private final byte[] DEFAULT_CONTENT;
225 private final String DEFAULT_CLIENT_CONTROLLED_CONTENT_HEADER;
226 private final String DEFAULT_RESPONSE_CACHE_HEADER;
227
228
229 public EchoViaHeaderHandler() {
230 this("ECHO-CONTENT","abc".getBytes(), "public, max-age=3, stale-while-revalidate=5");
231 }
232
233 public EchoViaHeaderHandler(final String contentHeader,final byte[] content,
234 final String defaultResponseCacheHeader) {
235 DEFAULT_CLIENT_CONTROLLED_CONTENT_HEADER = contentHeader;
236 DEFAULT_CONTENT = content;
237 DEFAULT_RESPONSE_CACHE_HEADER = defaultResponseCacheHeader;
238 }
239
240
241
242
243
244 public String getUserContentHeader() {
245 return DEFAULT_CLIENT_CONTROLLED_CONTENT_HEADER;
246 }
247
248
249
250
251
252
253
254
255
256
257
258
259 @Override
260 public void handle(final HttpRequest request,
261 final HttpResponse response,
262 final HttpContext context)
263 throws HttpException, IOException {
264
265 final String method = request.getRequestLine().getMethod().toUpperCase(Locale.ROOT);
266 if (!"GET".equals(method) &&
267 !"POST".equals(method) &&
268 !"PUT".equals(method)
269 ) {
270 throw new MethodNotSupportedException
271 (method + " not supported by " + getClass().getName());
272 }
273
274 response.setStatusCode(org.apache.http.HttpStatus.SC_OK);
275 response.addHeader("Cache-Control",getCacheContent(request));
276 final byte[] content = getHeaderContent(request);
277 final ByteArrayEntity bae = new ByteArrayEntity(content);
278 response.setHeader("Connection","keep-alive");
279
280 response.setEntity(bae);
281
282 }
283
284
285 public byte[] getHeaderContent(final HttpRequest request) {
286 final Header contentHeader = request.getFirstHeader(DEFAULT_CLIENT_CONTROLLED_CONTENT_HEADER);
287 if(contentHeader!=null) {
288 return contentHeader.getValue().getBytes(Consts.UTF_8);
289 } else {
290 return DEFAULT_CONTENT;
291 }
292 }
293
294 public String getCacheContent(final HttpRequest request) {
295 final Header contentHeader = request.getFirstHeader(CACHE_CONTROL_HEADER);
296 if(contentHeader!=null) {
297 return contentHeader.getValue();
298 } else {
299 return DEFAULT_RESPONSE_CACHE_HEADER;
300 }
301 }
302
303 }
304
305 }
306