blob: 3daf75c5128541d7d6dce6264a0db6bc5a6d1031 [file] [log] [blame]
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.hc.client5.http.impl.cache;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import org.apache.hc.client5.http.HeadersMatcher;
import org.apache.hc.client5.http.cache.HttpCacheEntry;
import org.apache.hc.client5.http.cache.RequestCacheControl;
import org.apache.hc.client5.http.cache.ResponseCacheControl;
import org.apache.hc.client5.http.utils.DateUtils;
import org.apache.hc.client5.http.validator.ETag;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HttpHeaders;
import org.apache.hc.core5.http.HttpRequest;
import org.apache.hc.core5.http.message.BasicHeader;
import org.apache.hc.core5.http.message.BasicHttpRequest;
import org.apache.hc.core5.http.message.MessageSupport;
import org.apache.hc.core5.http.support.BasicRequestBuilder;
import org.hamcrest.MatcherAssert;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public class TestConditionalRequestBuilder {
private ConditionalRequestBuilder<HttpRequest> impl;
private HttpRequest request;
@BeforeEach
public void setUp() throws Exception {
impl = new ConditionalRequestBuilder<>(request -> BasicRequestBuilder.copy(request).build());
request = new BasicHttpRequest("GET", "/");
}
@Test
public void testBuildConditionalRequestWithLastModified() {
final String theMethod = "GET";
final String theUri = "/theuri";
final String lastModified = "this is my last modified date";
final HttpRequest basicRequest = new BasicHttpRequest(theMethod, theUri);
basicRequest.addHeader("Accept-Encoding", "gzip");
final Header[] headers = new Header[] {
new BasicHeader("Date", DateUtils.formatStandardDate(Instant.now())),
new BasicHeader("Last-Modified", lastModified) };
final HttpCacheEntry cacheEntry = HttpTestUtils.makeCacheEntry(headers);
final ResponseCacheControl cacheControl = ResponseCacheControl.builder().build();
final HttpRequest newRequest = impl.buildConditionalRequest(cacheControl, basicRequest, cacheEntry);
Assertions.assertEquals(theMethod, newRequest.getMethod());
Assertions.assertEquals(theUri, newRequest.getRequestUri());
Assertions.assertEquals(2, newRequest.getHeaders().length);
MatcherAssert.assertThat(
newRequest.getHeaders(),
HeadersMatcher.same(
new BasicHeader("Accept-Encoding", "gzip"),
new BasicHeader("If-Modified-Since", lastModified)));
}
@Test
public void testConditionalRequestForEntryWithLastModifiedAndEtagIncludesBothAsValidators()
throws Exception {
final Instant now = Instant.now();
final Instant tenSecondsAgo = now.minusSeconds(10);
final Instant twentySecondsAgo = now.plusSeconds(20);
final String lmDate = DateUtils.formatStandardDate(twentySecondsAgo);
final String etag = "\"etag\"";
final Header[] headers = {
new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)),
new BasicHeader("Last-Modified", lmDate),
new BasicHeader("ETag", etag)
};
final HttpRequest basicRequest = new BasicHttpRequest("GET", "/");
final HttpCacheEntry cacheEntry = HttpTestUtils.makeCacheEntry(headers);
final ResponseCacheControl cacheControl = ResponseCacheControl.builder().build();
final HttpRequest result = impl.buildConditionalRequest(cacheControl, basicRequest, cacheEntry);
Assertions.assertEquals(lmDate,
result.getFirstHeader("If-Modified-Since").getValue());
Assertions.assertEquals(etag,
result.getFirstHeader("If-None-Match").getValue());
}
@Test
public void testBuildConditionalRequestWithETag() {
final String theMethod = "GET";
final String theUri = "/theuri";
final String theETag = "\"this is my eTag\"";
final HttpRequest basicRequest = new BasicHttpRequest(theMethod, theUri);
basicRequest.addHeader("Accept-Encoding", "gzip");
final Instant now = Instant.now();
final Header[] headers = new Header[] {
new BasicHeader("Date", DateUtils.formatStandardDate(now)),
new BasicHeader("Last-Modified", DateUtils.formatStandardDate(now)),
new BasicHeader("ETag", theETag) };
final HttpCacheEntry cacheEntry = HttpTestUtils.makeCacheEntry(headers);
final ResponseCacheControl cacheControl = ResponseCacheControl.builder().build();
final HttpRequest newRequest = impl.buildConditionalRequest(cacheControl, basicRequest, cacheEntry);
Assertions.assertEquals(theMethod, newRequest.getMethod());
Assertions.assertEquals(theUri, newRequest.getRequestUri());
MatcherAssert.assertThat(
newRequest.getHeaders(),
HeadersMatcher.same(
new BasicHeader("Accept-Encoding", "gzip"),
new BasicHeader("If-None-Match", theETag),
new BasicHeader("If-Modified-Since", DateUtils.formatStandardDate(now))));
}
@Test
public void testCacheEntryWithMustRevalidateDoesEndToEndRevalidation() throws Exception {
final HttpRequest basicRequest = new BasicHttpRequest("GET","/");
final Instant now = Instant.now();
final Instant elevenSecondsAgo = now.minusSeconds(11);
final Instant tenSecondsAgo = now.minusSeconds(10);
final Instant nineSecondsAgo = now.plusSeconds(9);
final Header[] cacheEntryHeaders = new Header[] {
new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)),
new BasicHeader("ETag", "\"etag\"")
};
final HttpCacheEntry cacheEntry = HttpTestUtils.makeCacheEntry(elevenSecondsAgo, nineSecondsAgo, cacheEntryHeaders);
final ResponseCacheControl responseCacheControl = ResponseCacheControl.builder()
.setMaxAge(5)
.setMustRevalidate(true)
.build();
final HttpRequest result = impl.buildConditionalRequest(responseCacheControl, basicRequest, cacheEntry);
final RequestCacheControl requestCacheControl = CacheControlHeaderParser.INSTANCE.parse(result);
Assertions.assertEquals(0, requestCacheControl.getMaxAge());
}
@Test
public void testCacheEntryWithProxyRevalidateDoesEndToEndRevalidation() throws Exception {
final HttpRequest basicRequest = new BasicHttpRequest("GET", "/");
final Instant now = Instant.now();
final Instant elevenSecondsAgo = now.minusSeconds(11);
final Instant tenSecondsAgo = now.minusSeconds(10);
final Instant nineSecondsAgo = now.plusSeconds(9);
final Header[] cacheEntryHeaders = new Header[] {
new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)),
new BasicHeader("ETag", "\"etag\"")
};
final HttpCacheEntry cacheEntry = HttpTestUtils.makeCacheEntry(elevenSecondsAgo, nineSecondsAgo, cacheEntryHeaders);
final ResponseCacheControl responseCacheControl = ResponseCacheControl.builder()
.setMaxAge(5)
.setProxyRevalidate(true)
.build();
final HttpRequest result = impl.buildConditionalRequest(responseCacheControl, basicRequest, cacheEntry);
final RequestCacheControl requestCacheControl = CacheControlHeaderParser.INSTANCE.parse(result);
Assertions.assertEquals(0, requestCacheControl.getMaxAge());
}
@Test
public void testBuildUnconditionalRequestUsesGETMethod()
throws Exception {
final HttpRequest result = impl.buildUnconditionalRequest(request);
Assertions.assertEquals("GET", result.getMethod());
}
@Test
public void testBuildUnconditionalRequestUsesRequestUri()
throws Exception {
final String uri = "/theURI";
request = new BasicHttpRequest("GET", uri);
final HttpRequest result = impl.buildUnconditionalRequest(request);
Assertions.assertEquals(uri, result.getRequestUri());
}
@Test
public void testBuildUnconditionalRequestAddsCacheControlNoCache()
throws Exception {
final HttpRequest result = impl.buildUnconditionalRequest(request);
final RequestCacheControl requestCacheControl = CacheControlHeaderParser.INSTANCE.parse(result);
Assertions.assertTrue(requestCacheControl.isNoCache());
}
@Test
public void testBuildUnconditionalRequestDoesNotUseIfRange()
throws Exception {
request.addHeader("If-Range","\"etag\"");
final HttpRequest result = impl.buildUnconditionalRequest(request);
Assertions.assertNull(result.getFirstHeader("If-Range"));
}
@Test
public void testBuildUnconditionalRequestDoesNotUseIfMatch()
throws Exception {
request.addHeader("If-Match","\"etag\"");
final HttpRequest result = impl.buildUnconditionalRequest(request);
Assertions.assertNull(result.getFirstHeader("If-Match"));
}
@Test
public void testBuildUnconditionalRequestDoesNotUseIfNoneMatch()
throws Exception {
request.addHeader("If-None-Match","\"etag\"");
final HttpRequest result = impl.buildUnconditionalRequest(request);
Assertions.assertNull(result.getFirstHeader("If-None-Match"));
}
@Test
public void testBuildUnconditionalRequestDoesNotUseIfUnmodifiedSince()
throws Exception {
request.addHeader("If-Unmodified-Since", DateUtils.formatStandardDate(Instant.now()));
final HttpRequest result = impl.buildUnconditionalRequest(request);
Assertions.assertNull(result.getFirstHeader("If-Unmodified-Since"));
}
@Test
public void testBuildUnconditionalRequestDoesNotUseIfModifiedSince()
throws Exception {
request.addHeader("If-Modified-Since", DateUtils.formatStandardDate(Instant.now()));
final HttpRequest result = impl.buildUnconditionalRequest(request);
Assertions.assertNull(result.getFirstHeader("If-Modified-Since"));
}
@Test
public void testBuildUnconditionalRequestCarriesOtherRequestHeaders()
throws Exception {
request.addHeader("User-Agent","MyBrowser/1.0");
final HttpRequest result = impl.buildUnconditionalRequest(request);
Assertions.assertEquals("MyBrowser/1.0",
result.getFirstHeader("User-Agent").getValue());
}
@Test
public void testBuildConditionalRequestFromVariants() throws Exception {
final ETag etag1 = new ETag("123");
final ETag etag2 = new ETag("456");
final ETag etag3 = new ETag("789");
final List<ETag> variantEntries = Arrays.asList(etag1, etag2, etag3);
final HttpRequest conditional = impl.buildConditionalRequestFromVariants(request, variantEntries);
final Iterator<String> it = MessageSupport.iterateTokens(conditional, HttpHeaders.IF_NONE_MATCH);
final List<ETag> etags = new ArrayList<>();
while (it.hasNext()) {
etags.add(ETag.parse(it.next()));
}
MatcherAssert.assertThat(etags, Matchers.containsInAnyOrder(etag1, etag2, etag3));
}
}