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.http.impl.cache;
28
29 import java.time.Instant;
30 import java.util.HashMap;
31 import java.util.Iterator;
32 import java.util.Map;
33
34 import org.apache.hc.client5.http.cache.HeaderConstants;
35 import org.apache.hc.client5.http.cache.HttpCacheEntry;
36 import org.apache.hc.client5.http.utils.DateUtils;
37 import org.apache.hc.core5.http.Header;
38 import org.apache.hc.core5.http.HeaderElement;
39 import org.apache.hc.core5.http.HttpRequest;
40 import org.apache.hc.core5.http.message.BasicHeader;
41 import org.apache.hc.core5.http.message.BasicHttpRequest;
42 import org.apache.hc.core5.http.message.MessageSupport;
43 import org.apache.hc.core5.http.support.BasicRequestBuilder;
44 import org.junit.jupiter.api.Assertions;
45 import org.junit.jupiter.api.BeforeEach;
46 import org.junit.jupiter.api.Test;
47
48 public class TestConditionalRequestBuilder {
49
50 private ConditionalRequestBuilder<HttpRequest> impl;
51 private HttpRequest request;
52
53 @BeforeEach
54 public void setUp() throws Exception {
55 impl = new ConditionalRequestBuilder<>(request -> BasicRequestBuilder.copy(request).build());
56 request = new BasicHttpRequest("GET", "/");
57 }
58
59 @Test
60 public void testBuildConditionalRequestWithLastModified() {
61 final String theMethod = "GET";
62 final String theUri = "/theuri";
63 final String lastModified = "this is my last modified date";
64
65 final HttpRequest basicRequest = new BasicHttpRequest(theMethod, theUri);
66 basicRequest.addHeader("Accept-Encoding", "gzip");
67
68 final Header[] headers = new Header[] {
69 new BasicHeader("Date", DateUtils.formatStandardDate(Instant.now())),
70 new BasicHeader("Last-Modified", lastModified) };
71
72 final HttpCacheEntry cacheEntry = HttpTestUtils.makeCacheEntry(headers);
73 final HttpRequest newRequest = impl.buildConditionalRequest(basicRequest, cacheEntry);
74
75 Assertions.assertEquals(theMethod, newRequest.getMethod());
76 Assertions.assertEquals(theUri, newRequest.getRequestUri());
77 Assertions.assertEquals(2, newRequest.getHeaders().length);
78
79 Assertions.assertEquals("Accept-Encoding", newRequest.getHeaders()[0].getName());
80 Assertions.assertEquals("gzip", newRequest.getHeaders()[0].getValue());
81
82 Assertions.assertEquals("If-Modified-Since", newRequest.getHeaders()[1].getName());
83 Assertions.assertEquals(lastModified, newRequest.getHeaders()[1].getValue());
84 }
85
86 @Test
87 public void testConditionalRequestForEntryWithLastModifiedAndEtagIncludesBothAsValidators()
88 throws Exception {
89 final Instant now = Instant.now();
90 final Instant tenSecondsAgo = now.minusSeconds(10);
91 final Instant twentySecondsAgo = now.plusSeconds(20);
92 final String lmDate = DateUtils.formatStandardDate(twentySecondsAgo);
93 final String etag = "\"etag\"";
94 final Header[] headers = {
95 new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)),
96 new BasicHeader("Last-Modified", lmDate),
97 new BasicHeader("ETag", etag)
98 };
99 final HttpRequest basicRequest = new BasicHttpRequest("GET", "/");
100 final HttpCacheEntry cacheEntry = HttpTestUtils.makeCacheEntry(headers);
101 final HttpRequest result = impl.buildConditionalRequest(basicRequest, cacheEntry);
102 Assertions.assertEquals(lmDate,
103 result.getFirstHeader("If-Modified-Since").getValue());
104 Assertions.assertEquals(etag,
105 result.getFirstHeader("If-None-Match").getValue());
106 }
107
108 @Test
109 public void testBuildConditionalRequestWithETag() {
110 final String theMethod = "GET";
111 final String theUri = "/theuri";
112 final String theETag = "this is my eTag";
113
114 final HttpRequest basicRequest = new BasicHttpRequest(theMethod, theUri);
115 basicRequest.addHeader("Accept-Encoding", "gzip");
116
117 final Header[] headers = new Header[] {
118 new BasicHeader("Date", DateUtils.formatStandardDate(Instant.now())),
119 new BasicHeader("Last-Modified", DateUtils.formatStandardDate(Instant.now())),
120 new BasicHeader("ETag", theETag) };
121
122 final HttpCacheEntry cacheEntry = HttpTestUtils.makeCacheEntry(headers);
123
124 final HttpRequest newRequest = impl.buildConditionalRequest(basicRequest, cacheEntry);
125
126 Assertions.assertEquals(theMethod, newRequest.getMethod());
127 Assertions.assertEquals(theUri, newRequest.getRequestUri());
128
129 Assertions.assertEquals(3, newRequest.getHeaders().length);
130
131 Assertions.assertEquals("Accept-Encoding", newRequest.getHeaders()[0].getName());
132 Assertions.assertEquals("gzip", newRequest.getHeaders()[0].getValue());
133
134 Assertions.assertEquals("If-None-Match", newRequest.getHeaders()[1].getName());
135 Assertions.assertEquals(theETag, newRequest.getHeaders()[1].getValue());
136 }
137
138 @Test
139 public void testCacheEntryWithMustRevalidateDoesEndToEndRevalidation() throws Exception {
140 final HttpRequest basicRequest = new BasicHttpRequest("GET","/");
141 final Instant now = Instant.now();
142 final Instant elevenSecondsAgo = now.minusSeconds(11);
143 final Instant tenSecondsAgo = now.minusSeconds(10);
144 final Instant nineSecondsAgo = now.plusSeconds(9);
145
146 final Header[] cacheEntryHeaders = new Header[] {
147 new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)),
148 new BasicHeader("ETag", "\"etag\""),
149 new BasicHeader("Cache-Control","max-age=5, must-revalidate") };
150 final HttpCacheEntry cacheEntry = HttpTestUtils.makeCacheEntry(elevenSecondsAgo, nineSecondsAgo, cacheEntryHeaders);
151
152 final HttpRequest result = impl.buildConditionalRequest(basicRequest, cacheEntry);
153
154 boolean foundMaxAge0 = false;
155
156 final Iterator<HeaderElement> it = MessageSupport.iterate(result, HeaderConstants.CACHE_CONTROL);
157 while (it.hasNext()) {
158 final HeaderElement elt = it.next();
159 if ("max-age".equalsIgnoreCase(elt.getName()) && "0".equals(elt.getValue())) {
160 foundMaxAge0 = true;
161 }
162 }
163 Assertions.assertTrue(foundMaxAge0);
164 }
165
166 @Test
167 public void testCacheEntryWithProxyRevalidateDoesEndToEndRevalidation() throws Exception {
168 final HttpRequest basicRequest = new BasicHttpRequest("GET", "/");
169 final Instant now = Instant.now();
170 final Instant elevenSecondsAgo = now.minusSeconds(11);
171 final Instant tenSecondsAgo = now.minusSeconds(10);
172 final Instant nineSecondsAgo = now.plusSeconds(9);
173
174 final Header[] cacheEntryHeaders = new Header[] {
175 new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)),
176 new BasicHeader("ETag", "\"etag\""),
177 new BasicHeader("Cache-Control","max-age=5, proxy-revalidate") };
178 final HttpCacheEntry cacheEntry = HttpTestUtils.makeCacheEntry(elevenSecondsAgo, nineSecondsAgo, cacheEntryHeaders);
179
180 final HttpRequest result = impl.buildConditionalRequest(basicRequest, cacheEntry);
181
182 boolean foundMaxAge0 = false;
183 final Iterator<HeaderElement> it = MessageSupport.iterate(result, HeaderConstants.CACHE_CONTROL);
184 while (it.hasNext()) {
185 final HeaderElement elt = it.next();
186 if ("max-age".equalsIgnoreCase(elt.getName()) && "0".equals(elt.getValue())) {
187 foundMaxAge0 = true;
188 }
189 }
190 Assertions.assertTrue(foundMaxAge0);
191 }
192
193 @Test
194 public void testBuildUnconditionalRequestUsesGETMethod()
195 throws Exception {
196 final HttpRequest result = impl.buildUnconditionalRequest(request);
197 Assertions.assertEquals("GET", result.getMethod());
198 }
199
200 @Test
201 public void testBuildUnconditionalRequestUsesRequestUri()
202 throws Exception {
203 final String uri = "/theURI";
204 request = new BasicHttpRequest("GET", uri);
205 final HttpRequest result = impl.buildUnconditionalRequest(request);
206 Assertions.assertEquals(uri, result.getRequestUri());
207 }
208
209 @Test
210 public void testBuildUnconditionalRequestAddsCacheControlNoCache()
211 throws Exception {
212 final HttpRequest result = impl.buildUnconditionalRequest(request);
213 boolean ccNoCacheFound = false;
214 final Iterator<HeaderElement> it = MessageSupport.iterate(result, HeaderConstants.CACHE_CONTROL);
215 while (it.hasNext()) {
216 final HeaderElement elt = it.next();
217 if ("no-cache".equals(elt.getName())) {
218 ccNoCacheFound = true;
219 }
220 }
221 Assertions.assertTrue(ccNoCacheFound);
222 }
223
224 @Test
225 public void testBuildUnconditionalRequestAddsPragmaNoCache()
226 throws Exception {
227 final HttpRequest result = impl.buildUnconditionalRequest(request);
228 boolean ccNoCacheFound = false;
229 final Iterator<HeaderElement> it = MessageSupport.iterate(result, HeaderConstants.PRAGMA);
230 while (it.hasNext()) {
231 final HeaderElement elt = it.next();
232 if ("no-cache".equals(elt.getName())) {
233 ccNoCacheFound = true;
234 }
235 }
236 Assertions.assertTrue(ccNoCacheFound);
237 }
238
239 @Test
240 public void testBuildUnconditionalRequestDoesNotUseIfRange()
241 throws Exception {
242 request.addHeader("If-Range","\"etag\"");
243 final HttpRequest result = impl.buildUnconditionalRequest(request);
244 Assertions.assertNull(result.getFirstHeader("If-Range"));
245 }
246
247 @Test
248 public void testBuildUnconditionalRequestDoesNotUseIfMatch()
249 throws Exception {
250 request.addHeader("If-Match","\"etag\"");
251 final HttpRequest result = impl.buildUnconditionalRequest(request);
252 Assertions.assertNull(result.getFirstHeader("If-Match"));
253 }
254
255 @Test
256 public void testBuildUnconditionalRequestDoesNotUseIfNoneMatch()
257 throws Exception {
258 request.addHeader("If-None-Match","\"etag\"");
259 final HttpRequest result = impl.buildUnconditionalRequest(request);
260 Assertions.assertNull(result.getFirstHeader("If-None-Match"));
261 }
262
263 @Test
264 public void testBuildUnconditionalRequestDoesNotUseIfUnmodifiedSince()
265 throws Exception {
266 request.addHeader("If-Unmodified-Since", DateUtils.formatStandardDate(Instant.now()));
267 final HttpRequest result = impl.buildUnconditionalRequest(request);
268 Assertions.assertNull(result.getFirstHeader("If-Unmodified-Since"));
269 }
270
271 @Test
272 public void testBuildUnconditionalRequestDoesNotUseIfModifiedSince()
273 throws Exception {
274 request.addHeader("If-Modified-Since", DateUtils.formatStandardDate(Instant.now()));
275 final HttpRequest result = impl.buildUnconditionalRequest(request);
276 Assertions.assertNull(result.getFirstHeader("If-Modified-Since"));
277 }
278
279 @Test
280 public void testBuildUnconditionalRequestCarriesOtherRequestHeaders()
281 throws Exception {
282 request.addHeader("User-Agent","MyBrowser/1.0");
283 final HttpRequest result = impl.buildUnconditionalRequest(request);
284 Assertions.assertEquals("MyBrowser/1.0",
285 result.getFirstHeader("User-Agent").getValue());
286 }
287
288 @Test
289 public void testBuildConditionalRequestFromVariants() throws Exception {
290 final String etag1 = "\"123\"";
291 final String etag2 = "\"456\"";
292 final String etag3 = "\"789\"";
293
294 final Map<String,Variant> variantEntries = new HashMap<>();
295 variantEntries.put(etag1, new Variant("A", HttpTestUtils.makeCacheEntry(new Header[] { new BasicHeader("ETag", etag1) })));
296 variantEntries.put(etag2, new Variant("B", HttpTestUtils.makeCacheEntry(new Header[] { new BasicHeader("ETag", etag2) })));
297 variantEntries.put(etag3, new Variant("C", HttpTestUtils.makeCacheEntry(new Header[] { new BasicHeader("ETag", etag3) })));
298
299 final HttpRequest conditional = impl.buildConditionalRequestFromVariants(request, variantEntries);
300
301
302 String ifNoneMatch = conditional.getFirstHeader(HeaderConstants.IF_NONE_MATCH).getValue();
303 Assertions.assertTrue(ifNoneMatch.contains(etag1));
304 Assertions.assertTrue(ifNoneMatch.contains(etag2));
305 Assertions.assertTrue(ifNoneMatch.contains(etag3));
306 ifNoneMatch = ifNoneMatch.replace(etag1, "");
307 ifNoneMatch = ifNoneMatch.replace(etag2, "");
308 ifNoneMatch = ifNoneMatch.replace(etag3, "");
309 ifNoneMatch = ifNoneMatch.replace(",","");
310 ifNoneMatch = ifNoneMatch.replace(" ", "");
311 Assertions.assertEquals(ifNoneMatch, "");
312 }
313
314 }