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