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 class TestConditionalRequestBuilder {
55
56 private ConditionalRequestBuilder<HttpRequest> impl;
57 private HttpRequest request;
58
59 @BeforeEach
60 void setUp() {
61 impl = new ConditionalRequestBuilder<>(request -> BasicRequestBuilder.copy(request).build());
62 request = new BasicHttpRequest("GET", "/");
63 }
64
65 @Test
66 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 void testConditionalRequestForEntryWithLastModifiedAndEtagIncludesBothAsValidators() {
95 final Instant now = Instant.now();
96 final Instant tenSecondsAgo = now.minusSeconds(10);
97 final Instant twentySecondsAgo = now.plusSeconds(20);
98 final String lmDate = DateUtils.formatStandardDate(twentySecondsAgo);
99 final String etag = "\"etag\"";
100 final Header[] headers = {
101 new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)),
102 new BasicHeader("Last-Modified", lmDate),
103 new BasicHeader("ETag", etag)
104 };
105 final HttpRequest basicRequest = new BasicHttpRequest("GET", "/");
106 final HttpCacheEntry cacheEntry = HttpTestUtils.makeCacheEntry(headers);
107 final ResponseCacheControl cacheControl = ResponseCacheControl.builder().build();
108 final HttpRequest result = impl.buildConditionalRequest(cacheControl, basicRequest, cacheEntry);
109 Assertions.assertEquals(lmDate,
110 result.getFirstHeader("If-Modified-Since").getValue());
111 Assertions.assertEquals(etag,
112 result.getFirstHeader("If-None-Match").getValue());
113 }
114
115 @Test
116 void testBuildConditionalRequestWithETag() {
117 final String theMethod = "GET";
118 final String theUri = "/theuri";
119 final String theETag = "\"this is my eTag\"";
120
121 final HttpRequest basicRequest = new BasicHttpRequest(theMethod, theUri);
122 basicRequest.addHeader("Accept-Encoding", "gzip");
123
124 final Instant now = Instant.now();
125
126 final Header[] headers = new Header[] {
127 new BasicHeader("Date", DateUtils.formatStandardDate(now)),
128 new BasicHeader("Last-Modified", DateUtils.formatStandardDate(now)),
129 new BasicHeader("ETag", theETag) };
130
131 final HttpCacheEntry cacheEntry = HttpTestUtils.makeCacheEntry(headers);
132
133 final ResponseCacheControl cacheControl = ResponseCacheControl.builder().build();
134 final HttpRequest newRequest = impl.buildConditionalRequest(cacheControl, basicRequest, cacheEntry);
135
136 Assertions.assertEquals(theMethod, newRequest.getMethod());
137 Assertions.assertEquals(theUri, newRequest.getRequestUri());
138
139 MatcherAssert.assertThat(
140 newRequest.getHeaders(),
141 HeadersMatcher.same(
142 new BasicHeader("Accept-Encoding", "gzip"),
143 new BasicHeader("If-None-Match", theETag),
144 new BasicHeader("If-Modified-Since", DateUtils.formatStandardDate(now))));
145 }
146
147 @Test
148 void testCacheEntryWithMustRevalidateDoesEndToEndRevalidation() {
149 final HttpRequest basicRequest = new BasicHttpRequest("GET","/");
150 final Instant now = Instant.now();
151 final Instant elevenSecondsAgo = now.minusSeconds(11);
152 final Instant tenSecondsAgo = now.minusSeconds(10);
153 final Instant nineSecondsAgo = now.plusSeconds(9);
154
155 final Header[] cacheEntryHeaders = new Header[] {
156 new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)),
157 new BasicHeader("ETag", "\"etag\"")
158 };
159 final HttpCacheEntry cacheEntry = HttpTestUtils.makeCacheEntry(elevenSecondsAgo, nineSecondsAgo, cacheEntryHeaders);
160
161 final ResponseCacheControl responseCacheControl = ResponseCacheControl.builder()
162 .setMaxAge(5)
163 .setMustRevalidate(true)
164 .build();
165 final HttpRequest result = impl.buildConditionalRequest(responseCacheControl, basicRequest, cacheEntry);
166
167 final RequestCacheControl requestCacheControl = CacheControlHeaderParser.INSTANCE.parse(result);
168 Assertions.assertEquals(0, requestCacheControl.getMaxAge());
169 }
170
171 @Test
172 void testCacheEntryWithProxyRevalidateDoesEndToEndRevalidation() {
173 final HttpRequest basicRequest = new BasicHttpRequest("GET", "/");
174 final Instant now = Instant.now();
175 final Instant elevenSecondsAgo = now.minusSeconds(11);
176 final Instant tenSecondsAgo = now.minusSeconds(10);
177 final Instant nineSecondsAgo = now.plusSeconds(9);
178
179 final Header[] cacheEntryHeaders = new Header[] {
180 new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)),
181 new BasicHeader("ETag", "\"etag\"")
182 };
183 final HttpCacheEntry cacheEntry = HttpTestUtils.makeCacheEntry(elevenSecondsAgo, nineSecondsAgo, cacheEntryHeaders);
184
185 final ResponseCacheControl responseCacheControl = ResponseCacheControl.builder()
186 .setMaxAge(5)
187 .setProxyRevalidate(true)
188 .build();
189 final HttpRequest result = impl.buildConditionalRequest(responseCacheControl, basicRequest, cacheEntry);
190
191 final RequestCacheControl requestCacheControl = CacheControlHeaderParser.INSTANCE.parse(result);
192 Assertions.assertEquals(0, requestCacheControl.getMaxAge());
193 }
194
195 @Test
196 void testBuildUnconditionalRequestUsesGETMethod() {
197 final HttpRequest result = impl.buildUnconditionalRequest(request);
198 Assertions.assertEquals("GET", result.getMethod());
199 }
200
201 @Test
202 void testBuildUnconditionalRequestUsesRequestUri() {
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 void testBuildUnconditionalRequestAddsCacheControlNoCache() {
211 final HttpRequest result = impl.buildUnconditionalRequest(request);
212 final RequestCacheControl requestCacheControl = CacheControlHeaderParser.INSTANCE.parse(result);
213 Assertions.assertTrue(requestCacheControl.isNoCache());
214 }
215
216 @Test
217 void testBuildUnconditionalRequestDoesNotUseIfRange() {
218 request.addHeader("If-Range","\"etag\"");
219 final HttpRequest result = impl.buildUnconditionalRequest(request);
220 Assertions.assertNull(result.getFirstHeader("If-Range"));
221 }
222
223 @Test
224 void testBuildUnconditionalRequestDoesNotUseIfMatch() {
225 request.addHeader("If-Match","\"etag\"");
226 final HttpRequest result = impl.buildUnconditionalRequest(request);
227 Assertions.assertNull(result.getFirstHeader("If-Match"));
228 }
229
230 @Test
231 void testBuildUnconditionalRequestDoesNotUseIfNoneMatch() {
232 request.addHeader("If-None-Match","\"etag\"");
233 final HttpRequest result = impl.buildUnconditionalRequest(request);
234 Assertions.assertNull(result.getFirstHeader("If-None-Match"));
235 }
236
237 @Test
238 void testBuildUnconditionalRequestDoesNotUseIfUnmodifiedSince() {
239 request.addHeader("If-Unmodified-Since", DateUtils.formatStandardDate(Instant.now()));
240 final HttpRequest result = impl.buildUnconditionalRequest(request);
241 Assertions.assertNull(result.getFirstHeader("If-Unmodified-Since"));
242 }
243
244 @Test
245 void testBuildUnconditionalRequestDoesNotUseIfModifiedSince() {
246 request.addHeader("If-Modified-Since", DateUtils.formatStandardDate(Instant.now()));
247 final HttpRequest result = impl.buildUnconditionalRequest(request);
248 Assertions.assertNull(result.getFirstHeader("If-Modified-Since"));
249 }
250
251 @Test
252 void testBuildUnconditionalRequestCarriesOtherRequestHeaders() {
253 request.addHeader("User-Agent","MyBrowser/1.0");
254 final HttpRequest result = impl.buildUnconditionalRequest(request);
255 Assertions.assertEquals("MyBrowser/1.0",
256 result.getFirstHeader("User-Agent").getValue());
257 }
258
259 @Test
260 void testBuildConditionalRequestFromVariants() {
261 final ETag etag1 = new ETag("123");
262 final ETag etag2 = new ETag("456");
263 final ETag etag3 = new ETag("789");
264
265 final List<ETag> variantEntries = Arrays.asList(etag1, etag2, etag3);
266
267 final HttpRequest conditional = impl.buildConditionalRequestFromVariants(request, variantEntries);
268
269
270 final Iterator<String> it = MessageSupport.iterateTokens(conditional, HttpHeaders.IF_NONE_MATCH);
271 final List<ETag> etags = new ArrayList<>();
272 while (it.hasNext()) {
273 etags.add(ETag.parse(it.next()));
274 }
275 MatcherAssert.assertThat(etags, Matchers.containsInAnyOrder(etag1, etag2, etag3));
276 }
277
278 }