HTTPCLIENT-1244: Replaced EasyMock with Mockito in the HTTP cache unit tests
This commit is contained in:
parent
d77112f608
commit
760795b6df
|
@ -27,14 +27,10 @@ addons:
|
||||||
- maven
|
- maven
|
||||||
|
|
||||||
jdk:
|
jdk:
|
||||||
- oraclejdk8
|
|
||||||
- openjdk12
|
- openjdk12
|
||||||
- oraclejdk12
|
- oraclejdk16
|
||||||
- openjdk-ea
|
- openjdk-ea
|
||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
allow_failures:
|
allow_failures:
|
||||||
- jdk: openjdk-ea
|
- jdk: openjdk-ea
|
||||||
|
|
||||||
after_success:
|
|
||||||
- mvn clean cobertura:cobertura coveralls:report
|
|
||||||
|
|
|
@ -79,11 +79,6 @@
|
||||||
<artifactId>mockito-core</artifactId>
|
<artifactId>mockito-core</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.easymock</groupId>
|
|
||||||
<artifactId>easymock</artifactId>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.httpcomponents.client5</groupId>
|
<groupId>org.apache.httpcomponents.client5</groupId>
|
||||||
<artifactId>httpclient5</artifactId>
|
<artifactId>httpclient5</artifactId>
|
||||||
|
|
|
@ -1,186 +0,0 @@
|
||||||
/*
|
|
||||||
* ====================================================================
|
|
||||||
* 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.io.IOException;
|
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
import org.apache.hc.client5.http.HttpRoute;
|
|
||||||
import org.apache.hc.client5.http.classic.ExecChain;
|
|
||||||
import org.apache.hc.client5.http.classic.ExecChainHandler;
|
|
||||||
import org.apache.hc.client5.http.classic.ExecRuntime;
|
|
||||||
import org.apache.hc.client5.http.protocol.HttpClientContext;
|
|
||||||
import org.apache.hc.core5.http.ClassicHttpRequest;
|
|
||||||
import org.apache.hc.core5.http.ClassicHttpResponse;
|
|
||||||
import org.apache.hc.core5.http.HttpEntity;
|
|
||||||
import org.apache.hc.core5.http.HttpException;
|
|
||||||
import org.apache.hc.core5.http.HttpHost;
|
|
||||||
import org.apache.hc.core5.http.HttpRequest;
|
|
||||||
import org.apache.hc.core5.http.HttpResponse;
|
|
||||||
import org.apache.hc.core5.http.io.support.ClassicRequestBuilder;
|
|
||||||
import org.apache.hc.core5.http.message.BasicClassicHttpRequest;
|
|
||||||
import org.easymock.EasyMock;
|
|
||||||
import org.easymock.IExpectationSetters;
|
|
||||||
import org.junit.Before;
|
|
||||||
|
|
||||||
public abstract class AbstractProtocolTest {
|
|
||||||
|
|
||||||
protected static final int MAX_BYTES = 1024;
|
|
||||||
protected static final int MAX_ENTRIES = 100;
|
|
||||||
protected final int entityLength = 128;
|
|
||||||
protected HttpHost host;
|
|
||||||
protected HttpRoute route;
|
|
||||||
protected HttpEntity body;
|
|
||||||
protected HttpClientContext context;
|
|
||||||
protected ExecChain mockExecChain;
|
|
||||||
protected ExecRuntime mockExecRuntime;
|
|
||||||
protected HttpCache mockCache;
|
|
||||||
protected ClassicHttpRequest request;
|
|
||||||
protected ClassicHttpResponse originResponse;
|
|
||||||
protected CacheConfig config;
|
|
||||||
protected ExecChainHandler impl;
|
|
||||||
protected HttpCache cache;
|
|
||||||
|
|
||||||
public static ClassicHttpRequest eqRequest(final ClassicHttpRequest in) {
|
|
||||||
EasyMock.reportMatcher(new RequestEquivalent(in));
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static HttpResponse eqResponse(final HttpResponse in) {
|
|
||||||
EasyMock.reportMatcher(new ResponseEquivalent(in));
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ClassicHttpResponse eqCloseableResponse(final ClassicHttpResponse in) {
|
|
||||||
EasyMock.reportMatcher(new ResponseEquivalent(in));
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
host = new HttpHost("foo.example.com", 80);
|
|
||||||
|
|
||||||
route = new HttpRoute(host);
|
|
||||||
|
|
||||||
body = HttpTestUtils.makeBody(entityLength);
|
|
||||||
|
|
||||||
request = new BasicClassicHttpRequest("GET", "/foo");
|
|
||||||
|
|
||||||
context = HttpClientContext.create();
|
|
||||||
|
|
||||||
originResponse = HttpTestUtils.make200Response();
|
|
||||||
|
|
||||||
config = CacheConfig.custom()
|
|
||||||
.setMaxCacheEntries(MAX_ENTRIES)
|
|
||||||
.setMaxObjectSize(MAX_BYTES)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
cache = new BasicHttpCache(config);
|
|
||||||
mockExecChain = EasyMock.createNiceMock(ExecChain.class);
|
|
||||||
mockExecRuntime = EasyMock.createNiceMock(ExecRuntime.class);
|
|
||||||
mockCache = EasyMock.createNiceMock(HttpCache.class);
|
|
||||||
impl = createCachingExecChain(cache, config);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ClassicHttpResponse execute(final ClassicHttpRequest request) throws IOException, HttpException {
|
|
||||||
return impl.execute(
|
|
||||||
ClassicRequestBuilder.copy(request).build(),
|
|
||||||
new ExecChain.Scope("test", route, request, mockExecRuntime, context),
|
|
||||||
mockExecChain);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ExecChainHandler createCachingExecChain(final HttpCache cache, final CacheConfig config) {
|
|
||||||
return new CachingExec(cache, null, config);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean supportsRangeAndContentRangeHeaders(final ExecChainHandler impl) {
|
|
||||||
return impl instanceof CachingExec && ((CachingExec) impl).supportsRangeAndContentRangeHeaders();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void replayMocks() {
|
|
||||||
EasyMock.replay(mockExecChain);
|
|
||||||
EasyMock.replay(mockCache);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void verifyMocks() {
|
|
||||||
EasyMock.verify(mockExecChain);
|
|
||||||
EasyMock.verify(mockCache);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected IExpectationSetters<ClassicHttpResponse> backendExpectsAnyRequest() throws Exception {
|
|
||||||
final ClassicHttpResponse resp = mockExecChain.proceed(
|
|
||||||
EasyMock.isA(ClassicHttpRequest.class),
|
|
||||||
EasyMock.isA(ExecChain.Scope.class));
|
|
||||||
return EasyMock.expect(resp);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected IExpectationSetters<ClassicHttpResponse> backendExpectsAnyRequestAndReturn(
|
|
||||||
final ClassicHttpResponse response) throws Exception {
|
|
||||||
final ClassicHttpResponse resp = mockExecChain.proceed(
|
|
||||||
EasyMock.isA(ClassicHttpRequest.class),
|
|
||||||
EasyMock.isA(ExecChain.Scope.class));
|
|
||||||
return EasyMock.expect(resp).andReturn(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void emptyMockCacheExpectsNoPuts() throws Exception {
|
|
||||||
mockExecChain = EasyMock.createNiceMock(ExecChain.class);
|
|
||||||
mockCache = EasyMock.createNiceMock(HttpCache.class);
|
|
||||||
|
|
||||||
impl = new CachingExec(mockCache, null, config);
|
|
||||||
|
|
||||||
EasyMock.expect(mockCache.getCacheEntry(EasyMock.isA(HttpHost.class), EasyMock.isA(HttpRequest.class)))
|
|
||||||
.andReturn(null).anyTimes();
|
|
||||||
EasyMock.expect(mockCache.getVariantCacheEntriesWithEtags(EasyMock.isA(HttpHost.class), EasyMock.isA(HttpRequest.class)))
|
|
||||||
.andReturn(new HashMap<>()).anyTimes();
|
|
||||||
|
|
||||||
mockCache.flushCacheEntriesFor(EasyMock.isA(HttpHost.class), EasyMock.isA(HttpRequest.class));
|
|
||||||
EasyMock.expectLastCall().anyTimes();
|
|
||||||
|
|
||||||
mockCache.flushCacheEntriesFor(EasyMock.isA(HttpHost.class), EasyMock.isA(HttpRequest.class));
|
|
||||||
EasyMock.expectLastCall().anyTimes();
|
|
||||||
|
|
||||||
mockCache.flushCacheEntriesInvalidatedByRequest(EasyMock.isA(HttpHost.class), EasyMock.isA(HttpRequest.class));
|
|
||||||
EasyMock.expectLastCall().anyTimes();
|
|
||||||
|
|
||||||
mockCache.flushCacheEntriesInvalidatedByExchange(EasyMock.isA(HttpHost.class), EasyMock.isA(HttpRequest.class), EasyMock.isA(HttpResponse.class));
|
|
||||||
EasyMock.expectLastCall().anyTimes();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void behaveAsNonSharedCache() {
|
|
||||||
config = CacheConfig.custom()
|
|
||||||
.setMaxCacheEntries(MAX_ENTRIES)
|
|
||||||
.setMaxObjectSize(MAX_BYTES)
|
|
||||||
.setSharedCache(false)
|
|
||||||
.build();
|
|
||||||
impl = new CachingExec(cache, null, config);
|
|
||||||
}
|
|
||||||
|
|
||||||
public AbstractProtocolTest() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,65 +0,0 @@
|
||||||
/*
|
|
||||||
* ====================================================================
|
|
||||||
* 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.io.IOException;
|
|
||||||
|
|
||||||
import org.apache.hc.client5.http.classic.ExecChain;
|
|
||||||
import org.apache.hc.core5.http.ClassicHttpRequest;
|
|
||||||
import org.apache.hc.core5.http.ClassicHttpResponse;
|
|
||||||
import org.apache.hc.core5.http.HttpException;
|
|
||||||
import org.apache.hc.core5.http.HttpRequest;
|
|
||||||
import org.apache.hc.core5.http.HttpStatus;
|
|
||||||
import org.apache.hc.core5.http.message.BasicClassicHttpResponse;
|
|
||||||
|
|
||||||
public class DummyBackend implements ExecChain {
|
|
||||||
|
|
||||||
private ClassicHttpRequest request;
|
|
||||||
private ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
|
|
||||||
private int executions;
|
|
||||||
|
|
||||||
public void setResponse(final ClassicHttpResponse resp) {
|
|
||||||
response = resp;
|
|
||||||
}
|
|
||||||
|
|
||||||
public HttpRequest getCapturedRequest() {
|
|
||||||
return request;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ClassicHttpResponse proceed(
|
|
||||||
final ClassicHttpRequest request,
|
|
||||||
final Scope scope) throws IOException, HttpException {
|
|
||||||
this.request = request;
|
|
||||||
executions++;
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getExecutions() {
|
|
||||||
return executions;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -251,7 +251,7 @@ public class HttpTestUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HttpCacheEntry makeCacheEntry(final Date requestDate,
|
public static HttpCacheEntry makeCacheEntry(final Date requestDate,
|
||||||
final Date responseDate, final Header[] headers) {
|
final Date responseDate, final Header... headers) {
|
||||||
final byte[] bytes = getRandomBytes(128);
|
final byte[] bytes = getRandomBytes(128);
|
||||||
return makeCacheEntry(requestDate, responseDate, headers, bytes);
|
return makeCacheEntry(requestDate, responseDate, headers, bytes);
|
||||||
}
|
}
|
||||||
|
@ -283,7 +283,7 @@ public class HttpTestUtils {
|
||||||
return makeCacheEntry(getStockHeaders(new Date()), bytes);
|
return makeCacheEntry(getStockHeaders(new Date()), bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HttpCacheEntry makeCacheEntry(final Header[] headers) {
|
public static HttpCacheEntry makeCacheEntry(final Header... headers) {
|
||||||
return makeCacheEntry(headers, getRandomBytes(128));
|
return makeCacheEntry(headers, getRandomBytes(128));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -292,27 +292,27 @@ public class HttpTestUtils {
|
||||||
return makeCacheEntry(now, now);
|
return makeCacheEntry(now, now);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HttpCacheEntry makeCacheEntryWithNoRequestMethodOrEntity(final Header[] headers) {
|
public static HttpCacheEntry makeCacheEntryWithNoRequestMethodOrEntity(final Header... headers) {
|
||||||
final Date now = new Date();
|
final Date now = new Date();
|
||||||
return new HttpCacheEntry(now, now, HttpStatus.SC_OK, headers, null, null);
|
return new HttpCacheEntry(now, now, HttpStatus.SC_OK, headers, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HttpCacheEntry makeCacheEntryWithNoRequestMethod(final Header[] headers) {
|
public static HttpCacheEntry makeCacheEntryWithNoRequestMethod(final Header... headers) {
|
||||||
final Date now = new Date();
|
final Date now = new Date();
|
||||||
return new HttpCacheEntry(now, now, HttpStatus.SC_OK, headers, new HeapResource(getRandomBytes(128)), null);
|
return new HttpCacheEntry(now, now, HttpStatus.SC_OK, headers, new HeapResource(getRandomBytes(128)), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HttpCacheEntry make204CacheEntryWithNoRequestMethod(final Header[] headers) {
|
public static HttpCacheEntry make204CacheEntryWithNoRequestMethod(final Header... headers) {
|
||||||
final Date now = new Date();
|
final Date now = new Date();
|
||||||
return new HttpCacheEntry(now, now, HttpStatus.SC_NO_CONTENT, headers, null, null);
|
return new HttpCacheEntry(now, now, HttpStatus.SC_NO_CONTENT, headers, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HttpCacheEntry makeHeadCacheEntry(final Header[] headers) {
|
public static HttpCacheEntry makeHeadCacheEntry(final Header... headers) {
|
||||||
final Date now = new Date();
|
final Date now = new Date();
|
||||||
return new HttpCacheEntry(now, now, HttpStatus.SC_OK, headers, null, null);
|
return new HttpCacheEntry(now, now, HttpStatus.SC_OK, headers, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HttpCacheEntry makeHeadCacheEntryWithNoRequestMethod(final Header[] headers) {
|
public static HttpCacheEntry makeHeadCacheEntryWithNoRequestMethod(final Header... headers) {
|
||||||
final Date now = new Date();
|
final Date now = new Date();
|
||||||
return new HttpCacheEntry(now, now, HttpStatus.SC_OK, headers, null, null);
|
return new HttpCacheEntry(now, now, HttpStatus.SC_OK, headers, null, null);
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,30 +27,27 @@
|
||||||
package org.apache.hc.client5.http.impl.cache;
|
package org.apache.hc.client5.http.impl.cache;
|
||||||
|
|
||||||
import org.apache.hc.core5.http.HttpRequest;
|
import org.apache.hc.core5.http.HttpRequest;
|
||||||
import org.easymock.IArgumentMatcher;
|
import org.mockito.ArgumentMatcher;
|
||||||
|
import org.mockito.ArgumentMatchers;
|
||||||
|
|
||||||
public class RequestEquivalent implements IArgumentMatcher {
|
public class RequestEquivalent<T extends HttpRequest> implements ArgumentMatcher<T> {
|
||||||
|
|
||||||
private final HttpRequest expected;
|
private final T expected;
|
||||||
|
|
||||||
public RequestEquivalent(final HttpRequest expected) {
|
public RequestEquivalent(final T expected) {
|
||||||
this.expected = expected;
|
this.expected = expected;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean matches(final Object actual) {
|
public boolean matches(final T argument) {
|
||||||
if (!(actual instanceof HttpRequest)) {
|
if (argument == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
final HttpRequest other = (HttpRequest) actual;
|
return HttpTestUtils.equivalent(expected, argument);
|
||||||
return HttpTestUtils.equivalent(expected, other);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public static <T extends HttpRequest> T eq(final T request) {
|
||||||
public void appendTo(final StringBuffer buf) {
|
return ArgumentMatchers.argThat(new RequestEquivalent<>(request));
|
||||||
buf.append("eqRequest(");
|
|
||||||
buf.append(expected);
|
|
||||||
buf.append(")");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,30 +27,27 @@
|
||||||
package org.apache.hc.client5.http.impl.cache;
|
package org.apache.hc.client5.http.impl.cache;
|
||||||
|
|
||||||
import org.apache.hc.core5.http.HttpResponse;
|
import org.apache.hc.core5.http.HttpResponse;
|
||||||
import org.easymock.IArgumentMatcher;
|
import org.mockito.ArgumentMatcher;
|
||||||
|
import org.mockito.ArgumentMatchers;
|
||||||
|
|
||||||
public class ResponseEquivalent implements IArgumentMatcher {
|
public class ResponseEquivalent<T extends HttpResponse> implements ArgumentMatcher<T> {
|
||||||
|
|
||||||
private final HttpResponse expected;
|
private final T expected;
|
||||||
|
|
||||||
public ResponseEquivalent(final HttpResponse expected) {
|
public ResponseEquivalent(final T expected) {
|
||||||
this.expected = expected;
|
this.expected = expected;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean matches(final Object actual) {
|
public boolean matches(final T argument) {
|
||||||
if (!(actual instanceof HttpResponse)) {
|
if (argument == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
final HttpResponse other = (HttpResponse) actual;
|
return HttpTestUtils.equivalent(expected, argument);
|
||||||
return HttpTestUtils.equivalent(expected, other);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public static <T extends HttpResponse> T eq(final T response) {
|
||||||
public void appendTo(final StringBuffer buf) {
|
return ArgumentMatchers.argThat(new ResponseEquivalent<>(response));
|
||||||
buf.append("eqRequest(");
|
|
||||||
buf.append(expected);
|
|
||||||
buf.append(")");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,458 +0,0 @@
|
||||||
/*
|
|
||||||
* ====================================================================
|
|
||||||
* 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 static org.easymock.EasyMock.anyObject;
|
|
||||||
import static org.easymock.EasyMock.eq;
|
|
||||||
import static org.easymock.EasyMock.expect;
|
|
||||||
import static org.easymock.EasyMock.isA;
|
|
||||||
import static org.easymock.EasyMock.same;
|
|
||||||
import static org.easymock.EasyMock.createMockBuilder;
|
|
||||||
import static org.easymock.EasyMock.createNiceMock;
|
|
||||||
import static org.easymock.EasyMock.replay;
|
|
||||||
import static org.easymock.EasyMock.verify;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
|
|
||||||
import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
|
|
||||||
import org.apache.hc.client5.http.cache.HttpCacheEntry;
|
|
||||||
import org.apache.hc.client5.http.classic.ExecChain;
|
|
||||||
import org.apache.hc.client5.http.classic.methods.HttpGet;
|
|
||||||
import org.apache.hc.client5.http.utils.DateUtils;
|
|
||||||
import org.apache.hc.core5.http.ClassicHttpRequest;
|
|
||||||
import org.apache.hc.core5.http.ClassicHttpResponse;
|
|
||||||
import org.apache.hc.core5.http.HeaderElements;
|
|
||||||
import org.apache.hc.core5.http.HttpHeaders;
|
|
||||||
import org.apache.hc.core5.http.HttpHost;
|
|
||||||
import org.apache.hc.core5.http.HttpRequest;
|
|
||||||
import org.apache.hc.core5.http.HttpResponse;
|
|
||||||
import org.apache.hc.core5.http.HttpStatus;
|
|
||||||
import org.apache.hc.core5.http.HttpVersion;
|
|
||||||
import org.apache.hc.core5.http.io.entity.InputStreamEntity;
|
|
||||||
import org.apache.hc.core5.http.message.BasicClassicHttpRequest;
|
|
||||||
import org.apache.hc.core5.http.message.BasicClassicHttpResponse;
|
|
||||||
import org.easymock.EasyMock;
|
|
||||||
import org.easymock.IExpectationSetters;
|
|
||||||
import org.junit.Assert;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
@SuppressWarnings("boxing") // test code
|
|
||||||
public class TestCachingExec extends TestCachingExecChain {
|
|
||||||
|
|
||||||
private static final String GET_CURRENT_DATE = "getCurrentDate";
|
|
||||||
|
|
||||||
private static final String HANDLE_BACKEND_RESPONSE = "handleBackendResponse";
|
|
||||||
|
|
||||||
private static final String CALL_BACKEND = "callBackend";
|
|
||||||
|
|
||||||
private static final String REVALIDATE_CACHE_ENTRY = "revalidateCacheEntry";
|
|
||||||
|
|
||||||
private CachingExec impl;
|
|
||||||
private boolean mockedImpl;
|
|
||||||
|
|
||||||
private ExecChain.Scope scope;
|
|
||||||
private ClassicHttpResponse mockBackendResponse;
|
|
||||||
|
|
||||||
private Date requestDate;
|
|
||||||
private Date responseDate;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
super.setUp();
|
|
||||||
|
|
||||||
scope = new ExecChain.Scope("test", route, request, mockEndpoint, context);
|
|
||||||
mockBackendResponse = createNiceMock(ClassicHttpResponse.class);
|
|
||||||
|
|
||||||
requestDate = new Date(System.currentTimeMillis() - 1000);
|
|
||||||
responseDate = new Date();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CachingExec createCachingExecChain(
|
|
||||||
final HttpCache mockCache, final CacheValidityPolicy mockValidityPolicy,
|
|
||||||
final ResponseCachingPolicy mockResponsePolicy,
|
|
||||||
final CachedHttpResponseGenerator mockResponseGenerator,
|
|
||||||
final CacheableRequestPolicy mockRequestPolicy,
|
|
||||||
final CachedResponseSuitabilityChecker mockSuitabilityChecker,
|
|
||||||
final ResponseProtocolCompliance mockResponseProtocolCompliance,
|
|
||||||
final RequestProtocolCompliance mockRequestProtocolCompliance,
|
|
||||||
final DefaultCacheRevalidator mockCacheRevalidator,
|
|
||||||
final ConditionalRequestBuilder<ClassicHttpRequest> mockConditionalRequestBuilder,
|
|
||||||
final CacheConfig config) {
|
|
||||||
return impl = new CachingExec(
|
|
||||||
mockCache,
|
|
||||||
mockValidityPolicy,
|
|
||||||
mockResponsePolicy,
|
|
||||||
mockResponseGenerator,
|
|
||||||
mockRequestPolicy,
|
|
||||||
mockSuitabilityChecker,
|
|
||||||
mockResponseProtocolCompliance,
|
|
||||||
mockRequestProtocolCompliance,
|
|
||||||
mockCacheRevalidator,
|
|
||||||
mockConditionalRequestBuilder,
|
|
||||||
config);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CachingExec createCachingExecChain(final HttpCache cache, final CacheConfig config) {
|
|
||||||
return impl = new CachingExec(cache, null, config);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void replayMocks() {
|
|
||||||
super.replayMocks();
|
|
||||||
replay(mockBackendResponse);
|
|
||||||
if (mockedImpl) {
|
|
||||||
replay(impl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void verifyMocks() {
|
|
||||||
super.verifyMocks();
|
|
||||||
verify(mockBackendResponse);
|
|
||||||
if (mockedImpl) {
|
|
||||||
verify(impl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testRequestThatCannotBeServedFromCacheCausesBackendRequest() throws Exception {
|
|
||||||
cacheInvalidatorWasCalled();
|
|
||||||
requestPolicyAllowsCaching(false);
|
|
||||||
mockImplMethods(CALL_BACKEND);
|
|
||||||
|
|
||||||
implExpectsAnyRequestAndReturn(mockBackendResponse);
|
|
||||||
requestIsFatallyNonCompliant(null);
|
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
final HttpResponse result = impl.execute(request, scope, mockExecChain);
|
|
||||||
verifyMocks();
|
|
||||||
|
|
||||||
Assert.assertSame(mockBackendResponse, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCacheMissCausesBackendRequest() throws Exception {
|
|
||||||
mockImplMethods(CALL_BACKEND);
|
|
||||||
requestPolicyAllowsCaching(true);
|
|
||||||
getCacheEntryReturns(null);
|
|
||||||
getVariantCacheEntriesReturns(new HashMap<>());
|
|
||||||
|
|
||||||
requestIsFatallyNonCompliant(null);
|
|
||||||
|
|
||||||
implExpectsAnyRequestAndReturn(mockBackendResponse);
|
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
final HttpResponse result = impl.execute(request, scope, mockExecChain);
|
|
||||||
verifyMocks();
|
|
||||||
|
|
||||||
Assert.assertSame(mockBackendResponse, result);
|
|
||||||
Assert.assertEquals(1, impl.getCacheMisses());
|
|
||||||
Assert.assertEquals(0, impl.getCacheHits());
|
|
||||||
Assert.assertEquals(0, impl.getCacheUpdates());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testUnsuitableUnvalidatableCacheEntryCausesBackendRequest() throws Exception {
|
|
||||||
mockImplMethods(CALL_BACKEND);
|
|
||||||
requestPolicyAllowsCaching(true);
|
|
||||||
requestIsFatallyNonCompliant(null);
|
|
||||||
|
|
||||||
getCacheEntryReturns(mockCacheEntry);
|
|
||||||
cacheEntrySuitable(false);
|
|
||||||
cacheEntryValidatable(false);
|
|
||||||
expect(mockConditionalRequestBuilder.buildConditionalRequest(request, mockCacheEntry))
|
|
||||||
.andReturn(request);
|
|
||||||
backendExpectsRequestAndReturn(request, mockBackendResponse);
|
|
||||||
expect(mockBackendResponse.getVersion()).andReturn(HttpVersion.HTTP_1_1).anyTimes();
|
|
||||||
expect(mockBackendResponse.getCode()).andReturn(200);
|
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
final HttpResponse result = impl.execute(request, scope, mockExecChain);
|
|
||||||
verifyMocks();
|
|
||||||
|
|
||||||
Assert.assertSame(mockBackendResponse, result);
|
|
||||||
Assert.assertEquals(0, impl.getCacheMisses());
|
|
||||||
Assert.assertEquals(1, impl.getCacheHits());
|
|
||||||
Assert.assertEquals(1, impl.getCacheUpdates());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testUnsuitableValidatableCacheEntryCausesRevalidation() throws Exception {
|
|
||||||
mockImplMethods(REVALIDATE_CACHE_ENTRY);
|
|
||||||
requestPolicyAllowsCaching(true);
|
|
||||||
requestIsFatallyNonCompliant(null);
|
|
||||||
|
|
||||||
getCacheEntryReturns(mockCacheEntry);
|
|
||||||
cacheEntrySuitable(false);
|
|
||||||
cacheEntryValidatable(true);
|
|
||||||
cacheEntryMustRevalidate(false);
|
|
||||||
cacheEntryProxyRevalidate(false);
|
|
||||||
mayReturnStaleWhileRevalidating(false);
|
|
||||||
|
|
||||||
expect(impl.revalidateCacheEntry(
|
|
||||||
isA(HttpHost.class),
|
|
||||||
isA(ClassicHttpRequest.class),
|
|
||||||
isA(ExecChain.Scope.class),
|
|
||||||
isA(ExecChain.class),
|
|
||||||
isA(HttpCacheEntry.class))).andReturn(mockBackendResponse);
|
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
final HttpResponse result = impl.execute(request, scope, mockExecChain);
|
|
||||||
verifyMocks();
|
|
||||||
|
|
||||||
Assert.assertSame(mockBackendResponse, result);
|
|
||||||
Assert.assertEquals(0, impl.getCacheMisses());
|
|
||||||
Assert.assertEquals(1, impl.getCacheHits());
|
|
||||||
Assert.assertEquals(0, impl.getCacheUpdates());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testRevalidationCallsHandleBackEndResponseWhenNot200Or304() throws Exception {
|
|
||||||
mockImplMethods(GET_CURRENT_DATE, HANDLE_BACKEND_RESPONSE);
|
|
||||||
|
|
||||||
final ClassicHttpRequest validate = new BasicClassicHttpRequest("GET", "/");
|
|
||||||
final ClassicHttpResponse originResponse = new BasicClassicHttpResponse(HttpStatus.SC_NOT_FOUND, "Not Found");
|
|
||||||
final ClassicHttpResponse finalResponse = HttpTestUtils.make200Response();
|
|
||||||
|
|
||||||
conditionalRequestBuilderReturns(validate);
|
|
||||||
getCurrentDateReturns(requestDate);
|
|
||||||
backendExpectsRequestAndReturn(validate, originResponse);
|
|
||||||
getCurrentDateReturns(responseDate);
|
|
||||||
expect(impl.handleBackendResponse(
|
|
||||||
same(host),
|
|
||||||
same(validate),
|
|
||||||
same(scope),
|
|
||||||
eq(requestDate),
|
|
||||||
eq(responseDate),
|
|
||||||
same(originResponse))).andReturn(finalResponse);
|
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
final HttpResponse result =
|
|
||||||
impl.revalidateCacheEntry(host, request, scope, mockExecChain, entry);
|
|
||||||
verifyMocks();
|
|
||||||
|
|
||||||
Assert.assertSame(finalResponse, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testRevalidationUpdatesCacheEntryAndPutsItToCacheWhen304ReturningCachedResponse()
|
|
||||||
throws Exception {
|
|
||||||
|
|
||||||
mockImplMethods(GET_CURRENT_DATE);
|
|
||||||
|
|
||||||
final ClassicHttpRequest validate = new BasicClassicHttpRequest("GET", "/");
|
|
||||||
final ClassicHttpResponse originResponse = HttpTestUtils.make304Response();
|
|
||||||
final HttpCacheEntry updatedEntry = HttpTestUtils.makeCacheEntry();
|
|
||||||
|
|
||||||
conditionalRequestBuilderReturns(validate);
|
|
||||||
getCurrentDateReturns(requestDate);
|
|
||||||
backendExpectsRequestAndReturn(validate, originResponse);
|
|
||||||
getCurrentDateReturns(responseDate);
|
|
||||||
expect(mockCache.updateCacheEntry(
|
|
||||||
eq(host),
|
|
||||||
same(request),
|
|
||||||
same(entry),
|
|
||||||
same(originResponse),
|
|
||||||
eq(requestDate),
|
|
||||||
eq(responseDate)))
|
|
||||||
.andReturn(updatedEntry);
|
|
||||||
expect(mockSuitabilityChecker.isConditional(request)).andReturn(false);
|
|
||||||
responseIsGeneratedFromCache(SimpleHttpResponse.create(HttpStatus.SC_OK));
|
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
impl.revalidateCacheEntry(host, request, scope, mockExecChain, entry);
|
|
||||||
verifyMocks();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testRevalidationRewritesAbsoluteUri() throws Exception {
|
|
||||||
|
|
||||||
mockImplMethods(GET_CURRENT_DATE);
|
|
||||||
|
|
||||||
// Fail on an unexpected request, rather than causing a later NPE
|
|
||||||
EasyMock.resetToStrict(mockExecChain);
|
|
||||||
|
|
||||||
final ClassicHttpRequest validate = new HttpGet("http://foo.example.com/resource");
|
|
||||||
final ClassicHttpRequest relativeValidate = new BasicClassicHttpRequest("GET", "/resource");
|
|
||||||
final ClassicHttpResponse originResponse = new BasicClassicHttpResponse(HttpStatus.SC_OK, "Okay");
|
|
||||||
|
|
||||||
conditionalRequestBuilderReturns(validate);
|
|
||||||
getCurrentDateReturns(requestDate);
|
|
||||||
|
|
||||||
final ClassicHttpResponse resp = mockExecChain.proceed(
|
|
||||||
eqRequest(relativeValidate), isA(ExecChain.Scope.class));
|
|
||||||
expect(resp).andReturn(originResponse);
|
|
||||||
|
|
||||||
getCurrentDateReturns(responseDate);
|
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
impl.revalidateCacheEntry(host, request, scope, mockExecChain, entry);
|
|
||||||
verifyMocks();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testEndlessResponsesArePassedThrough() throws Exception {
|
|
||||||
impl = createCachingExecChain(new BasicHttpCache(), CacheConfig.DEFAULT);
|
|
||||||
|
|
||||||
final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
|
|
||||||
resp1.setHeader("Date", DateUtils.formatDate(new Date()));
|
|
||||||
resp1.setHeader("Server", "MockOrigin/1.0");
|
|
||||||
resp1.setHeader(HttpHeaders.TRANSFER_ENCODING, HeaderElements.CHUNKED_ENCODING);
|
|
||||||
|
|
||||||
final AtomicInteger size = new AtomicInteger();
|
|
||||||
final AtomicInteger maxlength = new AtomicInteger(Integer.MAX_VALUE);
|
|
||||||
resp1.setEntity(new InputStreamEntity(new InputStream() {
|
|
||||||
private Throwable closed;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() throws IOException {
|
|
||||||
closed = new Throwable();
|
|
||||||
super.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int read() throws IOException {
|
|
||||||
Thread.yield();
|
|
||||||
if (closed != null) {
|
|
||||||
throw new IOException("Response has been closed");
|
|
||||||
|
|
||||||
}
|
|
||||||
if (size.incrementAndGet() > maxlength.get()) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return 'y';
|
|
||||||
}
|
|
||||||
}, -1, null));
|
|
||||||
|
|
||||||
final ClassicHttpResponse resp = mockExecChain.proceed(
|
|
||||||
isA(ClassicHttpRequest.class), isA(ExecChain.Scope.class));
|
|
||||||
EasyMock.expect(resp).andReturn(resp1);
|
|
||||||
|
|
||||||
final ClassicHttpRequest req1 = HttpTestUtils.makeDefaultRequest();
|
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
final ClassicHttpResponse resp2 = impl.execute(req1, scope, mockExecChain);
|
|
||||||
maxlength.set(size.get() * 2);
|
|
||||||
verifyMocks();
|
|
||||||
assertTrue(HttpTestUtils.semanticallyTransparent(resp1, resp2));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCallBackendMakesBackEndRequestAndHandlesResponse() throws Exception {
|
|
||||||
mockImplMethods(GET_CURRENT_DATE, HANDLE_BACKEND_RESPONSE);
|
|
||||||
final ClassicHttpResponse resp = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
|
|
||||||
getCurrentDateReturns(requestDate);
|
|
||||||
backendExpectsRequestAndReturn(request, resp);
|
|
||||||
getCurrentDateReturns(responseDate);
|
|
||||||
handleBackendResponseReturnsResponse(request, resp);
|
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
|
|
||||||
impl.callBackend(host, request, scope, mockExecChain);
|
|
||||||
|
|
||||||
verifyMocks();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDoesNotFlushCachesOnCacheHit() throws Exception {
|
|
||||||
requestPolicyAllowsCaching(true);
|
|
||||||
requestIsFatallyNonCompliant(null);
|
|
||||||
|
|
||||||
getCacheEntryReturns(mockCacheEntry);
|
|
||||||
doesNotFlushCache();
|
|
||||||
cacheEntrySuitable(true);
|
|
||||||
cacheEntryValidatable(true);
|
|
||||||
|
|
||||||
responseIsGeneratedFromCache(SimpleHttpResponse.create(HttpStatus.SC_OK));
|
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
impl.execute(request, scope, mockExecChain);
|
|
||||||
verifyMocks();
|
|
||||||
}
|
|
||||||
|
|
||||||
private IExpectationSetters<ClassicHttpResponse> implExpectsAnyRequestAndReturn(
|
|
||||||
final ClassicHttpResponse response) throws Exception {
|
|
||||||
final ClassicHttpResponse resp = impl.callBackend(
|
|
||||||
same(host),
|
|
||||||
isA(ClassicHttpRequest.class),
|
|
||||||
isA(ExecChain.Scope.class),
|
|
||||||
isA(ExecChain.class));
|
|
||||||
return EasyMock.expect(resp).andReturn(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void getVariantCacheEntriesReturns(final Map<String,Variant> result) {
|
|
||||||
expect(mockCache.getVariantCacheEntriesWithEtags(host, request)).andReturn(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void cacheInvalidatorWasCalled() {
|
|
||||||
mockCache.flushCacheEntriesInvalidatedByRequest((HttpHost)anyObject(), (HttpRequest)anyObject());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void getCurrentDateReturns(final Date date) {
|
|
||||||
expect(impl.getCurrentDate()).andReturn(date);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleBackendResponseReturnsResponse(final ClassicHttpRequest request, final ClassicHttpResponse response)
|
|
||||||
throws IOException {
|
|
||||||
expect(
|
|
||||||
impl.handleBackendResponse(
|
|
||||||
same(host),
|
|
||||||
same(request),
|
|
||||||
same(scope),
|
|
||||||
isA(Date.class),
|
|
||||||
isA(Date.class),
|
|
||||||
isA(ClassicHttpResponse.class))).andReturn(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void mockImplMethods(final String... methods) {
|
|
||||||
mockedImpl = true;
|
|
||||||
impl = createMockBuilder(CachingExec.class).withConstructor(
|
|
||||||
mockCache,
|
|
||||||
mockValidityPolicy,
|
|
||||||
mockResponsePolicy,
|
|
||||||
mockResponseGenerator,
|
|
||||||
mockRequestPolicy,
|
|
||||||
mockSuitabilityChecker,
|
|
||||||
mockResponseProtocolCompliance,
|
|
||||||
mockRequestProtocolCompliance,
|
|
||||||
mockCacheRevalidator,
|
|
||||||
mockConditionalRequestBuilder,
|
|
||||||
config).addMockedMethods(methods).createNiceMock();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
|
@ -26,26 +26,90 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.hc.client5.http.impl.cache;
|
package org.apache.hc.client5.http.impl.cache;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.net.SocketTimeoutException;
|
import java.net.SocketTimeoutException;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
|
import org.apache.hc.client5.http.HttpRoute;
|
||||||
|
import org.apache.hc.client5.http.classic.ExecChain;
|
||||||
|
import org.apache.hc.client5.http.classic.ExecRuntime;
|
||||||
|
import org.apache.hc.client5.http.protocol.HttpClientContext;
|
||||||
import org.apache.hc.client5.http.utils.DateUtils;
|
import org.apache.hc.client5.http.utils.DateUtils;
|
||||||
import org.apache.hc.core5.http.ClassicHttpRequest;
|
import org.apache.hc.core5.http.ClassicHttpRequest;
|
||||||
|
import org.apache.hc.core5.http.ClassicHttpResponse;
|
||||||
|
import org.apache.hc.core5.http.HttpException;
|
||||||
|
import org.apache.hc.core5.http.HttpHost;
|
||||||
import org.apache.hc.core5.http.HttpResponse;
|
import org.apache.hc.core5.http.HttpResponse;
|
||||||
import org.apache.hc.core5.http.HttpStatus;
|
import org.apache.hc.core5.http.HttpStatus;
|
||||||
|
import org.apache.hc.core5.http.io.support.ClassicRequestBuilder;
|
||||||
import org.apache.hc.core5.http.message.BasicClassicHttpRequest;
|
import org.apache.hc.core5.http.message.BasicClassicHttpRequest;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class tests behavior that is allowed (MAY) by the HTTP/1.1 protocol
|
* This class tests behavior that is allowed (MAY) by the HTTP/1.1 protocol
|
||||||
* specification and for which we have implemented the behavior in HTTP cache.
|
* specification and for which we have implemented the behavior in HTTP cache.
|
||||||
*/
|
*/
|
||||||
public class TestProtocolAllowedBehavior extends AbstractProtocolTest {
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
|
public class TestProtocolAllowedBehavior {
|
||||||
|
|
||||||
|
static final int MAX_BYTES = 1024;
|
||||||
|
static final int MAX_ENTRIES = 100;
|
||||||
|
static final int ENTITY_LENGTH = 128;
|
||||||
|
|
||||||
|
HttpHost host;
|
||||||
|
HttpRoute route;
|
||||||
|
HttpClientContext context;
|
||||||
|
@Mock
|
||||||
|
ExecChain mockExecChain;
|
||||||
|
@Mock
|
||||||
|
ExecRuntime mockExecRuntime;
|
||||||
|
@Mock
|
||||||
|
HttpCache mockCache;
|
||||||
|
ClassicHttpRequest request;
|
||||||
|
ClassicHttpResponse originResponse;
|
||||||
|
CacheConfig config;
|
||||||
|
CachingExec impl;
|
||||||
|
HttpCache cache;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
host = new HttpHost("foo.example.com", 80);
|
||||||
|
|
||||||
|
route = new HttpRoute(host);
|
||||||
|
|
||||||
|
request = new BasicClassicHttpRequest("GET", "/foo");
|
||||||
|
|
||||||
|
context = HttpClientContext.create();
|
||||||
|
|
||||||
|
originResponse = HttpTestUtils.make200Response();
|
||||||
|
|
||||||
|
config = CacheConfig.custom()
|
||||||
|
.setMaxCacheEntries(MAX_ENTRIES)
|
||||||
|
.setMaxObjectSize(MAX_BYTES)
|
||||||
|
.setSharedCache(false)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
cache = new BasicHttpCache(config);
|
||||||
|
impl = new CachingExec(cache, null, config);
|
||||||
|
|
||||||
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClassicHttpResponse execute(final ClassicHttpRequest request) throws IOException, HttpException {
|
||||||
|
return impl.execute(
|
||||||
|
ClassicRequestBuilder.copy(request).build(),
|
||||||
|
new ExecChain.Scope("test", route, request, mockExecRuntime, context),
|
||||||
|
mockExecChain);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNonSharedCacheReturnsStaleResponseWhenRevalidationFailsForProxyRevalidate()
|
public void testNonSharedCacheReturnsStaleResponseWhenRevalidationFailsForProxyRevalidate() throws Exception {
|
||||||
throws Exception {
|
|
||||||
final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET","/");
|
final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET","/");
|
||||||
final Date now = new Date();
|
final Date now = new Date();
|
||||||
final Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
|
final Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
|
||||||
|
@ -53,37 +117,33 @@ public class TestProtocolAllowedBehavior extends AbstractProtocolTest {
|
||||||
originResponse.setHeader("Cache-Control","max-age=5,proxy-revalidate");
|
originResponse.setHeader("Cache-Control","max-age=5,proxy-revalidate");
|
||||||
originResponse.setHeader("Etag","\"etag\"");
|
originResponse.setHeader("Etag","\"etag\"");
|
||||||
|
|
||||||
backendExpectsAnyRequest().andReturn(originResponse);
|
|
||||||
|
|
||||||
final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET","/");
|
final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET","/");
|
||||||
|
|
||||||
backendExpectsAnyRequest().andThrow(new SocketTimeoutException());
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
behaveAsNonSharedCache();
|
|
||||||
execute(req1);
|
execute(req1);
|
||||||
final HttpResponse result = execute(req2);
|
|
||||||
verifyMocks();
|
|
||||||
|
|
||||||
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenThrow(new SocketTimeoutException());
|
||||||
|
|
||||||
|
final HttpResponse result = execute(req2);
|
||||||
Assert.assertEquals(HttpStatus.SC_OK, result.getCode());
|
Assert.assertEquals(HttpStatus.SC_OK, result.getCode());
|
||||||
|
|
||||||
|
Mockito.verifyNoInteractions(mockCache);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNonSharedCacheMayCacheResponsesWithCacheControlPrivate()
|
public void testNonSharedCacheMayCacheResponsesWithCacheControlPrivate() throws Exception {
|
||||||
throws Exception {
|
|
||||||
final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET","/");
|
final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET","/");
|
||||||
originResponse.setHeader("Cache-Control","private,max-age=3600");
|
originResponse.setHeader("Cache-Control","private,max-age=3600");
|
||||||
|
|
||||||
backendExpectsAnyRequest().andReturn(originResponse);
|
|
||||||
|
|
||||||
final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET","/");
|
final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET","/");
|
||||||
|
|
||||||
replayMocks();
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
|
||||||
behaveAsNonSharedCache();
|
|
||||||
execute(req1);
|
execute(req1);
|
||||||
final HttpResponse result = execute(req2);
|
final HttpResponse result = execute(req2);
|
||||||
verifyMocks();
|
|
||||||
|
|
||||||
Assert.assertEquals(HttpStatus.SC_OK, result.getCode());
|
Assert.assertEquals(HttpStatus.SC_OK, result.getCode());
|
||||||
|
|
||||||
|
Mockito.verifyNoInteractions(mockCache);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,18 +42,18 @@ import org.apache.hc.core5.http.ClassicHttpResponse;
|
||||||
import org.apache.hc.core5.http.HttpEntity;
|
import org.apache.hc.core5.http.HttpEntity;
|
||||||
import org.apache.hc.core5.http.HttpException;
|
import org.apache.hc.core5.http.HttpException;
|
||||||
import org.apache.hc.core5.http.HttpHost;
|
import org.apache.hc.core5.http.HttpHost;
|
||||||
import org.apache.hc.core5.http.HttpRequest;
|
|
||||||
import org.apache.hc.core5.http.HttpResponse;
|
import org.apache.hc.core5.http.HttpResponse;
|
||||||
import org.apache.hc.core5.http.HttpStatus;
|
import org.apache.hc.core5.http.HttpStatus;
|
||||||
import org.apache.hc.core5.http.io.entity.ByteArrayEntity;
|
import org.apache.hc.core5.http.io.entity.ByteArrayEntity;
|
||||||
import org.apache.hc.core5.http.message.BasicClassicHttpRequest;
|
import org.apache.hc.core5.http.message.BasicClassicHttpRequest;
|
||||||
import org.apache.hc.core5.http.message.BasicClassicHttpResponse;
|
import org.apache.hc.core5.http.message.BasicClassicHttpResponse;
|
||||||
import org.easymock.Capture;
|
|
||||||
import org.easymock.EasyMock;
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Ignore;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* We are a conditionally-compliant HTTP/1.1 client with a cache. However, a lot
|
* We are a conditionally-compliant HTTP/1.1 client with a cache. However, a lot
|
||||||
|
@ -70,22 +70,27 @@ import org.junit.Test;
|
||||||
* document the places where we differ from the HTTP RFC.
|
* document the places where we differ from the HTTP RFC.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("boxing") // test code
|
@SuppressWarnings("boxing") // test code
|
||||||
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
public class TestProtocolDeviations {
|
public class TestProtocolDeviations {
|
||||||
|
|
||||||
private static final int MAX_BYTES = 1024;
|
private static final int MAX_BYTES = 1024;
|
||||||
private static final int MAX_ENTRIES = 100;
|
private static final int MAX_ENTRIES = 100;
|
||||||
|
|
||||||
private HttpHost host;
|
HttpHost host;
|
||||||
private HttpRoute route;
|
HttpRoute route;
|
||||||
private HttpEntity mockEntity;
|
@Mock
|
||||||
private ExecRuntime mockEndpoint;
|
HttpEntity mockEntity;
|
||||||
private ExecChain mockExecChain;
|
@Mock
|
||||||
private HttpCache mockCache;
|
ExecRuntime mockEndpoint;
|
||||||
private ClassicHttpRequest request;
|
@Mock
|
||||||
private HttpCacheContext context;
|
ExecChain mockExecChain;
|
||||||
private ClassicHttpResponse originResponse;
|
@Mock
|
||||||
|
HttpCache mockCache;
|
||||||
|
ClassicHttpRequest request;
|
||||||
|
HttpCacheContext context;
|
||||||
|
ClassicHttpResponse originResponse;
|
||||||
|
|
||||||
private ExecChainHandler impl;
|
ExecChainHandler impl;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
|
@ -105,11 +110,6 @@ public class TestProtocolDeviations {
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
final HttpCache cache = new BasicHttpCache(config);
|
final HttpCache cache = new BasicHttpCache(config);
|
||||||
mockEndpoint = EasyMock.createNiceMock(ExecRuntime.class);
|
|
||||||
mockExecChain = EasyMock.createNiceMock(ExecChain.class);
|
|
||||||
mockEntity = EasyMock.createNiceMock(HttpEntity.class);
|
|
||||||
mockCache = EasyMock.createNiceMock(HttpCache.class);
|
|
||||||
|
|
||||||
impl = createCachingExecChain(cache, config);
|
impl = createCachingExecChain(cache, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,122 +131,12 @@ public class TestProtocolDeviations {
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void replayMocks() {
|
|
||||||
EasyMock.replay(mockExecChain);
|
|
||||||
EasyMock.replay(mockCache);
|
|
||||||
EasyMock.replay(mockEntity);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void verifyMocks() {
|
|
||||||
EasyMock.verify(mockExecChain);
|
|
||||||
EasyMock.verify(mockCache);
|
|
||||||
EasyMock.verify(mockEntity);
|
|
||||||
}
|
|
||||||
|
|
||||||
private HttpEntity makeBody(final int nbytes) {
|
private HttpEntity makeBody(final int nbytes) {
|
||||||
final byte[] bytes = new byte[nbytes];
|
final byte[] bytes = new byte[nbytes];
|
||||||
new Random().nextBytes(bytes);
|
new Random().nextBytes(bytes);
|
||||||
return new ByteArrayEntity(bytes, null);
|
return new ByteArrayEntity(bytes, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HttpRequest eqRequest(final HttpRequest in) {
|
|
||||||
org.easymock.EasyMock.reportMatcher(new RequestEquivalent(in));
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* "For compatibility with HTTP/1.0 applications, HTTP/1.1 requests
|
|
||||||
* containing a message-body MUST include a valid Content-Length header
|
|
||||||
* field unless the server is known to be HTTP/1.1 compliant. If a request
|
|
||||||
* contains a message-body and a Content-Length is not given, the server
|
|
||||||
* SHOULD respond with 400 (bad request) if it cannot determine the length
|
|
||||||
* of the message, or with 411 (length required) if it wishes to insist on
|
|
||||||
* receiving a valid Content-Length."
|
|
||||||
*
|
|
||||||
* http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.4
|
|
||||||
*
|
|
||||||
* 8/23/2010 JRC - This test has been moved to Ignore. The caching client
|
|
||||||
* was set to return status code 411 on a missing content-length header when
|
|
||||||
* a request had a body. It seems that somewhere deeper in the client stack
|
|
||||||
* this header is added automatically for us - so the caching client shouldn't
|
|
||||||
* specifically be worried about this requirement.
|
|
||||||
*/
|
|
||||||
@Ignore
|
|
||||||
public void testHTTP1_1RequestsWithBodiesOfKnownLengthMustHaveContentLength() throws Exception {
|
|
||||||
final ClassicHttpRequest post = new BasicClassicHttpRequest("POST", "/");
|
|
||||||
post.setEntity(mockEntity);
|
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
|
|
||||||
final HttpResponse response = execute(post);
|
|
||||||
|
|
||||||
verifyMocks();
|
|
||||||
|
|
||||||
Assert.assertEquals(HttpStatus.SC_LENGTH_REQUIRED, response.getCode());
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Discussion: if an incoming request has a body, but the HttpEntity
|
|
||||||
* attached has an unknown length (meaning entity.getContentLength() is
|
|
||||||
* negative), we have two choices if we want to be conditionally compliant.
|
|
||||||
* (1) we can slurp the whole body into a bytearray and compute its length
|
|
||||||
* before sending; or (2) we can push responsibility for (1) back onto the
|
|
||||||
* client by just generating a 411 response
|
|
||||||
*
|
|
||||||
* There is a third option, which is that we delegate responsibility for (1)
|
|
||||||
* onto the backend HttpClient, but because that is an injected dependency,
|
|
||||||
* we can't rely on it necessarily being conditionally compliant with
|
|
||||||
* HTTP/1.1. Currently, option (2) seems like the safest bet, as this
|
|
||||||
* exposes to the client application that the slurping required for (1)
|
|
||||||
* needs to happen in order to compute the content length.
|
|
||||||
*
|
|
||||||
* In any event, this test just captures the behavior required.
|
|
||||||
*
|
|
||||||
* 8/23/2010 JRC - This test has been moved to Ignore. The caching client
|
|
||||||
* was set to return status code 411 on a missing content-length header when
|
|
||||||
* a request had a body. It seems that somewhere deeper in the client stack
|
|
||||||
* this header is added automatically for us - so the caching client shouldn't
|
|
||||||
* specifically be worried about this requirement.
|
|
||||||
*/
|
|
||||||
@Ignore
|
|
||||||
public void testHTTP1_1RequestsWithUnknownBodyLengthAreRejectedOrHaveContentLengthAdded()
|
|
||||||
throws Exception {
|
|
||||||
final ClassicHttpRequest post = new BasicClassicHttpRequest("POST", "/");
|
|
||||||
|
|
||||||
final byte[] bytes = new byte[128];
|
|
||||||
new Random().nextBytes(bytes);
|
|
||||||
|
|
||||||
final HttpEntity mockBody = EasyMock.createMockBuilder(ByteArrayEntity.class).withConstructor(
|
|
||||||
new Object[] { bytes }).addMockedMethods("getContentLength").createNiceMock();
|
|
||||||
org.easymock.EasyMock.expect(mockBody.getContentLength()).andReturn(-1L).anyTimes();
|
|
||||||
post.setEntity(mockBody);
|
|
||||||
|
|
||||||
final Capture<ClassicHttpRequest> reqCap = EasyMock.newCapture();
|
|
||||||
EasyMock.expect(
|
|
||||||
mockExecChain.proceed(
|
|
||||||
EasyMock.capture(reqCap),
|
|
||||||
EasyMock.isA(ExecChain.Scope.class))).andReturn(
|
|
||||||
originResponse).times(0, 1);
|
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
EasyMock.replay(mockBody);
|
|
||||||
|
|
||||||
final HttpResponse result = execute(post);
|
|
||||||
|
|
||||||
verifyMocks();
|
|
||||||
EasyMock.verify(mockBody);
|
|
||||||
|
|
||||||
if (reqCap.hasCaptured()) {
|
|
||||||
// backend request was made
|
|
||||||
final HttpRequest forwarded = reqCap.getValue();
|
|
||||||
Assert.assertNotNull(forwarded.getFirstHeader("Content-Length"));
|
|
||||||
} else {
|
|
||||||
final int status = result.getCode();
|
|
||||||
Assert.assertTrue(HttpStatus.SC_LENGTH_REQUIRED == status
|
|
||||||
|| HttpStatus.SC_BAD_REQUEST == status);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* "10.2.7 206 Partial Content ... The request MUST have included a Range
|
* "10.2.7 206 Partial Content ... The request MUST have included a Range
|
||||||
* header field (section 14.35) indicating the desired range, and MAY have
|
* header field (section 14.35) indicating the desired range, and MAY have
|
||||||
|
@ -266,12 +156,8 @@ public class TestProtocolDeviations {
|
||||||
originResponse.setHeader("Content-Range", "bytes 0-499/1234");
|
originResponse.setHeader("Content-Range", "bytes 0-499/1234");
|
||||||
originResponse.setEntity(makeBody(500));
|
originResponse.setEntity(makeBody(500));
|
||||||
|
|
||||||
EasyMock.expect(
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
|
||||||
mockExecChain.proceed(
|
|
||||||
EasyMock.isA(ClassicHttpRequest.class),
|
|
||||||
EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
|
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
try {
|
try {
|
||||||
final HttpResponse result = execute(request);
|
final HttpResponse result = execute(request);
|
||||||
Assert.assertTrue(HttpStatus.SC_PARTIAL_CONTENT != result.getCode());
|
Assert.assertTrue(HttpStatus.SC_PARTIAL_CONTENT != result.getCode());
|
||||||
|
@ -292,13 +178,9 @@ public class TestProtocolDeviations {
|
||||||
|
|
||||||
originResponse = new BasicClassicHttpResponse(401, "Unauthorized");
|
originResponse = new BasicClassicHttpResponse(401, "Unauthorized");
|
||||||
|
|
||||||
EasyMock.expect(
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
|
||||||
mockExecChain.proceed(
|
|
||||||
EasyMock.isA(ClassicHttpRequest.class),
|
|
||||||
EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
|
|
||||||
replayMocks();
|
|
||||||
final HttpResponse result = execute(request);
|
final HttpResponse result = execute(request);
|
||||||
verifyMocks();
|
|
||||||
Assert.assertSame(originResponse, result);
|
Assert.assertSame(originResponse, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -312,13 +194,9 @@ public class TestProtocolDeviations {
|
||||||
public void testPassesOnOrigin405WithoutAllowHeader() throws Exception {
|
public void testPassesOnOrigin405WithoutAllowHeader() throws Exception {
|
||||||
originResponse = new BasicClassicHttpResponse(405, "Method Not Allowed");
|
originResponse = new BasicClassicHttpResponse(405, "Method Not Allowed");
|
||||||
|
|
||||||
EasyMock.expect(
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
|
||||||
mockExecChain.proceed(
|
|
||||||
EasyMock.isA(ClassicHttpRequest.class),
|
|
||||||
EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
|
|
||||||
replayMocks();
|
|
||||||
final HttpResponse result = execute(request);
|
final HttpResponse result = execute(request);
|
||||||
verifyMocks();
|
|
||||||
Assert.assertSame(originResponse, result);
|
Assert.assertSame(originResponse, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -333,13 +211,9 @@ public class TestProtocolDeviations {
|
||||||
public void testPassesOnOrigin407WithoutAProxyAuthenticateHeader() throws Exception {
|
public void testPassesOnOrigin407WithoutAProxyAuthenticateHeader() throws Exception {
|
||||||
originResponse = new BasicClassicHttpResponse(407, "Proxy Authentication Required");
|
originResponse = new BasicClassicHttpResponse(407, "Proxy Authentication Required");
|
||||||
|
|
||||||
EasyMock.expect(
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
|
||||||
mockExecChain.proceed(
|
|
||||||
EasyMock.isA(ClassicHttpRequest.class),
|
|
||||||
EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
|
|
||||||
replayMocks();
|
|
||||||
final HttpResponse result = execute(request);
|
final HttpResponse result = execute(request);
|
||||||
verifyMocks();
|
|
||||||
Assert.assertSame(originResponse, result);
|
Assert.assertSame(originResponse, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,44 +39,101 @@ import java.util.Date;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.hc.client5.http.HttpRoute;
|
||||||
import org.apache.hc.client5.http.auth.StandardAuthScheme;
|
import org.apache.hc.client5.http.auth.StandardAuthScheme;
|
||||||
import org.apache.hc.client5.http.classic.ExecChain;
|
import org.apache.hc.client5.http.classic.ExecChain;
|
||||||
|
import org.apache.hc.client5.http.classic.ExecRuntime;
|
||||||
import org.apache.hc.client5.http.classic.methods.HttpGet;
|
import org.apache.hc.client5.http.classic.methods.HttpGet;
|
||||||
import org.apache.hc.client5.http.classic.methods.HttpPost;
|
import org.apache.hc.client5.http.classic.methods.HttpPost;
|
||||||
|
import org.apache.hc.client5.http.protocol.HttpClientContext;
|
||||||
import org.apache.hc.client5.http.utils.DateUtils;
|
import org.apache.hc.client5.http.utils.DateUtils;
|
||||||
import org.apache.hc.core5.http.ClassicHttpRequest;
|
import org.apache.hc.core5.http.ClassicHttpRequest;
|
||||||
import org.apache.hc.core5.http.ClassicHttpResponse;
|
import org.apache.hc.core5.http.ClassicHttpResponse;
|
||||||
import org.apache.hc.core5.http.Header;
|
import org.apache.hc.core5.http.Header;
|
||||||
import org.apache.hc.core5.http.HeaderElement;
|
import org.apache.hc.core5.http.HeaderElement;
|
||||||
|
import org.apache.hc.core5.http.HttpEntity;
|
||||||
|
import org.apache.hc.core5.http.HttpException;
|
||||||
import org.apache.hc.core5.http.HttpHeaders;
|
import org.apache.hc.core5.http.HttpHeaders;
|
||||||
|
import org.apache.hc.core5.http.HttpHost;
|
||||||
import org.apache.hc.core5.http.HttpStatus;
|
import org.apache.hc.core5.http.HttpStatus;
|
||||||
import org.apache.hc.core5.http.HttpVersion;
|
import org.apache.hc.core5.http.HttpVersion;
|
||||||
|
import org.apache.hc.core5.http.io.support.ClassicRequestBuilder;
|
||||||
import org.apache.hc.core5.http.message.BasicClassicHttpRequest;
|
import org.apache.hc.core5.http.message.BasicClassicHttpRequest;
|
||||||
import org.apache.hc.core5.http.message.BasicClassicHttpResponse;
|
import org.apache.hc.core5.http.message.BasicClassicHttpResponse;
|
||||||
import org.apache.hc.core5.http.message.MessageSupport;
|
import org.apache.hc.core5.http.message.MessageSupport;
|
||||||
import org.easymock.Capture;
|
|
||||||
import org.easymock.EasyMock;
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This test class captures functionality required to achieve unconditional
|
* This test class captures functionality required to achieve unconditional
|
||||||
* compliance with the HTTP/1.1 spec, i.e. all the SHOULD, SHOULD NOT,
|
* compliance with the HTTP/1.1 spec, i.e. all the SHOULD, SHOULD NOT,
|
||||||
* RECOMMENDED, and NOT RECOMMENDED behaviors.
|
* RECOMMENDED, and NOT RECOMMENDED behaviors.
|
||||||
*/
|
*/
|
||||||
public class TestProtocolRecommendations extends AbstractProtocolTest {
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
|
public class TestProtocolRecommendations {
|
||||||
|
|
||||||
private Date now;
|
static final int MAX_BYTES = 1024;
|
||||||
private Date tenSecondsAgo;
|
static final int MAX_ENTRIES = 100;
|
||||||
private Date twoMinutesAgo;
|
static final int ENTITY_LENGTH = 128;
|
||||||
|
|
||||||
|
HttpHost host;
|
||||||
|
HttpRoute route;
|
||||||
|
HttpEntity body;
|
||||||
|
HttpClientContext context;
|
||||||
|
@Mock
|
||||||
|
ExecChain mockExecChain;
|
||||||
|
@Mock
|
||||||
|
ExecRuntime mockExecRuntime;
|
||||||
|
@Mock
|
||||||
|
HttpCache mockCache;
|
||||||
|
ClassicHttpRequest request;
|
||||||
|
ClassicHttpResponse originResponse;
|
||||||
|
CacheConfig config;
|
||||||
|
CachingExec impl;
|
||||||
|
HttpCache cache;
|
||||||
|
Date now;
|
||||||
|
Date tenSecondsAgo;
|
||||||
|
Date twoMinutesAgo;
|
||||||
|
|
||||||
@Override
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() throws Exception {
|
||||||
super.setUp();
|
host = new HttpHost("foo.example.com", 80);
|
||||||
|
|
||||||
|
route = new HttpRoute(host);
|
||||||
|
|
||||||
|
body = HttpTestUtils.makeBody(ENTITY_LENGTH);
|
||||||
|
|
||||||
|
request = new BasicClassicHttpRequest("GET", "/foo");
|
||||||
|
|
||||||
|
context = HttpClientContext.create();
|
||||||
|
|
||||||
|
originResponse = HttpTestUtils.make200Response();
|
||||||
|
|
||||||
|
config = CacheConfig.custom()
|
||||||
|
.setMaxCacheEntries(MAX_ENTRIES)
|
||||||
|
.setMaxObjectSize(MAX_BYTES)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
cache = new BasicHttpCache(config);
|
||||||
|
impl = new CachingExec(cache, null, config);
|
||||||
|
|
||||||
now = new Date();
|
now = new Date();
|
||||||
tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
|
tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
|
||||||
twoMinutesAgo = new Date(now.getTime() - 2 * 60 * 1000L);
|
twoMinutesAgo = new Date(now.getTime() - 2 * 60 * 1000L);
|
||||||
|
|
||||||
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClassicHttpResponse execute(final ClassicHttpRequest request) throws IOException, HttpException {
|
||||||
|
return impl.execute(
|
||||||
|
ClassicRequestBuilder.copy(request).build(),
|
||||||
|
new ExecChain.Scope("test", route, request, mockExecRuntime, context),
|
||||||
|
mockExecChain);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* "identity: The default (identity) encoding; the use of no
|
/* "identity: The default (identity) encoding; the use of no
|
||||||
|
@ -89,10 +146,10 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
|
||||||
@Test
|
@Test
|
||||||
public void testIdentityCodingIsNotUsedInContentEncodingHeader() throws Exception {
|
public void testIdentityCodingIsNotUsedInContentEncodingHeader() throws Exception {
|
||||||
originResponse.setHeader("Content-Encoding", "identity");
|
originResponse.setHeader("Content-Encoding", "identity");
|
||||||
backendExpectsAnyRequest().andReturn(originResponse);
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
|
||||||
replayMocks();
|
|
||||||
final ClassicHttpResponse result = execute(request);
|
final ClassicHttpResponse result = execute(request);
|
||||||
verifyMocks();
|
|
||||||
boolean foundIdentity = false;
|
boolean foundIdentity = false;
|
||||||
final Iterator<HeaderElement> it = MessageSupport.iterate(result, HttpHeaders.CONTENT_ENCODING);
|
final Iterator<HeaderElement> it = MessageSupport.iterate(result, HttpHeaders.CONTENT_ENCODING);
|
||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
|
@ -120,15 +177,14 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
|
||||||
resp1.setHeader(validatorHeader, validator);
|
resp1.setHeader(validatorHeader, validator);
|
||||||
resp1.setHeader(headerName, headerValue);
|
resp1.setHeader(headerName, headerValue);
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp1);
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
|
||||||
|
|
||||||
final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
|
final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
|
||||||
req2.setHeader(conditionalHeader, validator);
|
req2.setHeader(conditionalHeader, validator);
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
execute(req1);
|
execute(req1);
|
||||||
final ClassicHttpResponse result = execute(req2);
|
final ClassicHttpResponse result = execute(req2);
|
||||||
verifyMocks();
|
|
||||||
|
|
||||||
if (HttpStatus.SC_NOT_MODIFIED == result.getCode()) {
|
if (HttpStatus.SC_NOT_MODIFIED == result.getCode()) {
|
||||||
assertNull(result.getFirstHeader(headerName));
|
assertNull(result.getFirstHeader(headerName));
|
||||||
|
@ -217,7 +273,7 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
|
||||||
resp1.setHeader(validatorHeader, validator);
|
resp1.setHeader(validatorHeader, validator);
|
||||||
resp1.setHeader("Content-Range", "bytes 0-127/256");
|
resp1.setHeader("Content-Range", "bytes 0-127/256");
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp1);
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
|
||||||
|
|
||||||
final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
|
final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
|
||||||
req2.setHeader("If-Range", validator);
|
req2.setHeader("If-Range", validator);
|
||||||
|
@ -230,19 +286,15 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
|
||||||
|
|
||||||
// cache module does not currently deal with byte ranges, but we want
|
// cache module does not currently deal with byte ranges, but we want
|
||||||
// this test to work even if it does some day
|
// this test to work even if it does some day
|
||||||
final Capture<ClassicHttpRequest> cap = EasyMock.newCapture();
|
|
||||||
EasyMock.expect(
|
|
||||||
mockExecChain.proceed(
|
|
||||||
EasyMock.capture(cap),
|
|
||||||
EasyMock.isA(ExecChain.Scope.class))).andReturn(resp2).times(0,1);
|
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
execute(req1);
|
execute(req1);
|
||||||
final ClassicHttpResponse result = execute(req2);
|
final ClassicHttpResponse result = execute(req2);
|
||||||
verifyMocks();
|
|
||||||
|
|
||||||
if (!cap.hasCaptured()
|
final ArgumentCaptor<ClassicHttpRequest> reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class);
|
||||||
&& HttpStatus.SC_NOT_MODIFIED == result.getCode()) {
|
Mockito.verify(mockExecChain, Mockito.atMost(2)).proceed(reqCapture.capture(), Mockito.any());
|
||||||
|
|
||||||
|
final List<ClassicHttpRequest> allRequests = reqCapture.getAllValues();
|
||||||
|
if (allRequests.isEmpty() && HttpStatus.SC_NOT_MODIFIED == result.getCode()) {
|
||||||
// cache generated a 304
|
// cache generated a 304
|
||||||
assertNull(result.getFirstHeader("Content-Range"));
|
assertNull(result.getFirstHeader("Content-Range"));
|
||||||
}
|
}
|
||||||
|
@ -294,11 +346,9 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
|
||||||
resp.setHeader("Etag", "\"etag\"");
|
resp.setHeader("Etag", "\"etag\"");
|
||||||
resp.setHeader(entityHeader, entityHeaderValue);
|
resp.setHeader(entityHeader, entityHeaderValue);
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp);
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp);
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
final ClassicHttpResponse result = execute(req);
|
final ClassicHttpResponse result = execute(req);
|
||||||
verifyMocks();
|
|
||||||
|
|
||||||
assertNull(result.getFirstHeader(entityHeader));
|
assertNull(result.getFirstHeader(entityHeader));
|
||||||
}
|
}
|
||||||
|
@ -350,11 +400,9 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
|
||||||
resp.setHeader("ETag", "\"etag\"");
|
resp.setHeader("ETag", "\"etag\"");
|
||||||
resp.setHeader("Content-Range", "bytes 0-127/256");
|
resp.setHeader("Content-Range", "bytes 0-127/256");
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp);
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp);
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
final ClassicHttpResponse result = execute(req);
|
final ClassicHttpResponse result = execute(req);
|
||||||
verifyMocks();
|
|
||||||
|
|
||||||
assertNull(result.getFirstHeader("Content-Range"));
|
assertNull(result.getFirstHeader("Content-Range"));
|
||||||
}
|
}
|
||||||
|
@ -377,23 +425,22 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
|
||||||
resp1.setHeader("Cache-Control","public,max-age=5");
|
resp1.setHeader("Cache-Control","public,max-age=5");
|
||||||
resp1.setHeader("Etag","\"etag\"");
|
resp1.setHeader("Etag","\"etag\"");
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp1);
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
|
||||||
return req1;
|
return req1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testDoesNotReturnStaleResponseOnError(final ClassicHttpRequest req2) throws Exception {
|
private void testDoesNotReturnStaleResponseOnError(final ClassicHttpRequest req2) throws Exception {
|
||||||
final ClassicHttpRequest req1 = requestToPopulateStaleCacheEntry();
|
final ClassicHttpRequest req1 = requestToPopulateStaleCacheEntry();
|
||||||
|
|
||||||
backendExpectsAnyRequest().andThrow(new IOException());
|
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
execute(req1);
|
execute(req1);
|
||||||
|
|
||||||
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenThrow(new IOException());
|
||||||
|
|
||||||
ClassicHttpResponse result = null;
|
ClassicHttpResponse result = null;
|
||||||
try {
|
try {
|
||||||
result = execute(req2);
|
result = execute(req2);
|
||||||
} catch (final IOException acceptable) {
|
} catch (final IOException acceptable) {
|
||||||
}
|
}
|
||||||
verifyMocks();
|
|
||||||
|
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
assertNotEquals(HttpStatus.SC_OK, result.getCode());
|
assertNotEquals(HttpStatus.SC_OK, result.getCode());
|
||||||
|
@ -451,15 +498,14 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
|
||||||
final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
|
final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
|
||||||
req2.setHeader("Cache-Control","max-stale=20");
|
req2.setHeader("Cache-Control","max-stale=20");
|
||||||
|
|
||||||
backendExpectsAnyRequest().andThrow(new IOException()).times(0,1);
|
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
execute(req1);
|
execute(req1);
|
||||||
|
|
||||||
final ClassicHttpResponse result = execute(req2);
|
final ClassicHttpResponse result = execute(req2);
|
||||||
verifyMocks();
|
|
||||||
|
|
||||||
assertEquals(HttpStatus.SC_OK, result.getCode());
|
assertEquals(HttpStatus.SC_OK, result.getCode());
|
||||||
assertNotNull(result.getFirstHeader("Warning"));
|
assertNotNull(result.getFirstHeader("Warning"));
|
||||||
|
|
||||||
|
Mockito.verify(mockExecChain, Mockito.atMost(1)).proceed(Mockito.any(), Mockito.any());
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -503,16 +549,17 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
|
||||||
resp1.setHeader("ETag","\"etag\"");
|
resp1.setHeader("ETag","\"etag\"");
|
||||||
resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
|
resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp1);
|
|
||||||
|
|
||||||
final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
|
final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
|
||||||
|
|
||||||
backendExpectsAnyRequest().andThrow(new IOException()).anyTimes();
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
execute(req1);
|
execute(req1);
|
||||||
|
|
||||||
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenThrow(new IOException());
|
||||||
|
|
||||||
final ClassicHttpResponse result = execute(req2);
|
final ClassicHttpResponse result = execute(req2);
|
||||||
verifyMocks();
|
|
||||||
|
Mockito.verify(mockExecChain, Mockito.times(2)).proceed(Mockito.any(), Mockito.any());
|
||||||
|
|
||||||
assertEquals(HttpStatus.SC_OK, result.getCode());
|
assertEquals(HttpStatus.SC_OK, result.getCode());
|
||||||
boolean warning111Found = false;
|
boolean warning111Found = false;
|
||||||
|
@ -545,11 +592,9 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
|
||||||
originResponse.setHeader("Cache-Control","public, max-age=5");
|
originResponse.setHeader("Cache-Control","public, max-age=5");
|
||||||
originResponse.setHeader("ETag","\"etag\"");
|
originResponse.setHeader("ETag","\"etag\"");
|
||||||
|
|
||||||
backendExpectsAnyRequest().andReturn(originResponse);
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
final ClassicHttpResponse result = execute(request);
|
final ClassicHttpResponse result = execute(request);
|
||||||
verifyMocks();
|
|
||||||
|
|
||||||
assertNull(result.getFirstHeader("Warning"));
|
assertNull(result.getFirstHeader("Warning"));
|
||||||
}
|
}
|
||||||
|
@ -563,11 +608,9 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
|
||||||
final String warning = "110 fred \"Response is stale\"";
|
final String warning = "110 fred \"Response is stale\"";
|
||||||
originResponse.addHeader("Warning",warning);
|
originResponse.addHeader("Warning",warning);
|
||||||
|
|
||||||
backendExpectsAnyRequest().andReturn(originResponse);
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
final ClassicHttpResponse result = execute(request);
|
final ClassicHttpResponse result = execute(request);
|
||||||
verifyMocks();
|
|
||||||
|
|
||||||
assertEquals(warning, result.getFirstHeader("Warning").getValue());
|
assertEquals(warning, result.getFirstHeader("Warning").getValue());
|
||||||
}
|
}
|
||||||
|
@ -581,27 +624,23 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
|
||||||
private void testDoesNotModifyHeaderOnResponses(final String headerName) throws Exception {
|
private void testDoesNotModifyHeaderOnResponses(final String headerName) throws Exception {
|
||||||
final String headerValue = HttpTestUtils
|
final String headerValue = HttpTestUtils
|
||||||
.getCanonicalHeaderValue(originResponse, headerName);
|
.getCanonicalHeaderValue(originResponse, headerName);
|
||||||
backendExpectsAnyRequest().andReturn(originResponse);
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
|
||||||
replayMocks();
|
|
||||||
final ClassicHttpResponse result = execute(request);
|
final ClassicHttpResponse result = execute(request);
|
||||||
verifyMocks();
|
|
||||||
assertEquals(headerValue,
|
assertEquals(headerValue, result.getFirstHeader(headerName).getValue());
|
||||||
result.getFirstHeader(headerName).getValue());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testDoesNotModifyHeaderOnRequests(final String headerName) throws Exception {
|
private void testDoesNotModifyHeaderOnRequests(final String headerName) throws Exception {
|
||||||
final String headerValue = HttpTestUtils.getCanonicalHeaderValue(request, headerName);
|
final String headerValue = HttpTestUtils.getCanonicalHeaderValue(request, headerName);
|
||||||
final Capture<ClassicHttpRequest> cap = EasyMock.newCapture();
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
|
||||||
EasyMock.expect(
|
|
||||||
mockExecChain.proceed(
|
|
||||||
EasyMock.capture(cap),
|
|
||||||
EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
|
|
||||||
replayMocks();
|
|
||||||
execute(request);
|
execute(request);
|
||||||
verifyMocks();
|
|
||||||
assertEquals(headerValue,
|
final ArgumentCaptor<ClassicHttpRequest> reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class);
|
||||||
HttpTestUtils.getCanonicalHeaderValue(cap.getValue(),
|
Mockito.verify(mockExecChain).proceed(reqCapture.capture(), Mockito.any());
|
||||||
headerName));
|
|
||||||
|
assertEquals(headerValue, HttpTestUtils.getCanonicalHeaderValue(reqCapture.getValue(), headerName));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -834,25 +873,22 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
|
||||||
resp1.setHeader("Last-Modified", lmDate);
|
resp1.setHeader("Last-Modified", lmDate);
|
||||||
resp1.setHeader("Cache-Control","max-age=5");
|
resp1.setHeader("Cache-Control","max-age=5");
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp1);
|
|
||||||
|
|
||||||
final Capture<ClassicHttpRequest> cap = EasyMock.newCapture();
|
|
||||||
final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
|
final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
|
||||||
final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
|
final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
|
||||||
|
|
||||||
EasyMock.expect(
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
|
||||||
mockExecChain.proceed(
|
|
||||||
EasyMock.capture(cap),
|
|
||||||
EasyMock.isA(ExecChain.Scope.class))).andReturn(resp2);
|
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
execute(req1);
|
execute(req1);
|
||||||
execute(req2);
|
|
||||||
verifyMocks();
|
|
||||||
|
|
||||||
final ClassicHttpRequest captured = cap.getValue();
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
|
||||||
final Header ifModifiedSince =
|
|
||||||
captured.getFirstHeader("If-Modified-Since");
|
execute(req2);
|
||||||
|
|
||||||
|
final ArgumentCaptor<ClassicHttpRequest> reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class);
|
||||||
|
Mockito.verify(mockExecChain, Mockito.times(2)).proceed(reqCapture.capture(), Mockito.any());
|
||||||
|
|
||||||
|
final ClassicHttpRequest captured = reqCapture.getValue();
|
||||||
|
final Header ifModifiedSince = captured.getFirstHeader("If-Modified-Since");
|
||||||
assertEquals(lmDate, ifModifiedSince.getValue());
|
assertEquals(lmDate, ifModifiedSince.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -877,28 +913,24 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
|
||||||
resp1.setHeader("Cache-Control","max-age=5");
|
resp1.setHeader("Cache-Control","max-age=5");
|
||||||
resp1.setHeader("ETag", etag);
|
resp1.setHeader("ETag", etag);
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp1);
|
|
||||||
|
|
||||||
final Capture<ClassicHttpRequest> cap = EasyMock.newCapture();
|
|
||||||
final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
|
final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
|
||||||
final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
|
final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
|
||||||
|
|
||||||
EasyMock.expect(
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
|
||||||
mockExecChain.proceed(
|
|
||||||
EasyMock.capture(cap),
|
|
||||||
EasyMock.isA(ExecChain.Scope.class))).andReturn(resp2);
|
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
execute(req1);
|
execute(req1);
|
||||||
execute(req2);
|
|
||||||
verifyMocks();
|
|
||||||
|
|
||||||
final ClassicHttpRequest captured = cap.getValue();
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
|
||||||
final Header ifModifiedSince =
|
|
||||||
captured.getFirstHeader("If-Modified-Since");
|
execute(req2);
|
||||||
|
|
||||||
|
final ArgumentCaptor<ClassicHttpRequest> reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class);
|
||||||
|
Mockito.verify(mockExecChain, Mockito.times(2)).proceed(reqCapture.capture(), Mockito.any());
|
||||||
|
|
||||||
|
final ClassicHttpRequest captured = reqCapture.getValue();
|
||||||
|
final Header ifModifiedSince = captured.getFirstHeader("If-Modified-Since");
|
||||||
assertEquals(lmDate, ifModifiedSince.getValue());
|
assertEquals(lmDate, ifModifiedSince.getValue());
|
||||||
final Header ifNoneMatch =
|
final Header ifNoneMatch = captured.getFirstHeader("If-None-Match");
|
||||||
captured.getFirstHeader("If-None-Match");
|
|
||||||
assertEquals(etag, ifNoneMatch.getValue());
|
assertEquals(etag, ifNoneMatch.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -922,7 +954,7 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
|
||||||
resp1.setHeader("Expires",DateUtils.formatDate(oneSecondAgo));
|
resp1.setHeader("Expires",DateUtils.formatDate(oneSecondAgo));
|
||||||
resp1.setHeader("Cache-Control", "public");
|
resp1.setHeader("Cache-Control", "public");
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp1);
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
|
||||||
|
|
||||||
final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
|
final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
|
||||||
final ClassicHttpRequest revalidate = new BasicClassicHttpRequest("GET", "/");
|
final ClassicHttpRequest revalidate = new BasicClassicHttpRequest("GET", "/");
|
||||||
|
@ -933,18 +965,12 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
|
||||||
resp2.setHeader("Expires", DateUtils.formatDate(oneSecondFromNow));
|
resp2.setHeader("Expires", DateUtils.formatDate(oneSecondFromNow));
|
||||||
resp2.setHeader("ETag","\"etag\"");
|
resp2.setHeader("ETag","\"etag\"");
|
||||||
|
|
||||||
EasyMock.expect(
|
Mockito.when(mockExecChain.proceed(RequestEquivalent.eq(revalidate), Mockito.any())).thenReturn(resp2);
|
||||||
mockExecChain.proceed(
|
|
||||||
eqRequest(revalidate),
|
|
||||||
EasyMock.isA(ExecChain.Scope.class))).andReturn(resp2);
|
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
execute(req1);
|
execute(req1);
|
||||||
final ClassicHttpResponse result = execute(req2);
|
final ClassicHttpResponse result = execute(req2);
|
||||||
verifyMocks();
|
|
||||||
|
|
||||||
assertEquals(HttpStatus.SC_OK,
|
assertEquals(HttpStatus.SC_OK, result.getCode());
|
||||||
result.getCode());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* "When a client tries to revalidate a cache entry, and the response
|
/* "When a client tries to revalidate a cache entry, and the response
|
||||||
|
@ -969,32 +995,23 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
|
||||||
resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
|
resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
|
||||||
resp1.setHeader("Cache-Control","max-age=5");
|
resp1.setHeader("Cache-Control","max-age=5");
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp1);
|
|
||||||
|
|
||||||
final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
|
final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
|
||||||
final ClassicHttpResponse resp2 = HttpTestUtils.make304Response();
|
final ClassicHttpResponse resp2 = HttpTestUtils.make304Response();
|
||||||
resp2.setHeader("ETag","\"etag\"");
|
resp2.setHeader("ETag","\"etag\"");
|
||||||
resp2.setHeader("Date", DateUtils.formatDate(elevenSecondsAgo));
|
resp2.setHeader("Date", DateUtils.formatDate(elevenSecondsAgo));
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp2);
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
|
||||||
|
|
||||||
final Capture<ClassicHttpRequest> cap = EasyMock.newCapture();
|
|
||||||
final ClassicHttpResponse resp3 = HttpTestUtils.make200Response();
|
|
||||||
resp3.setHeader("ETag","\"etag2\"");
|
|
||||||
resp3.setHeader("Date", DateUtils.formatDate(now));
|
|
||||||
resp3.setHeader("Cache-Control","max-age=5");
|
|
||||||
|
|
||||||
EasyMock.expect(
|
|
||||||
mockExecChain.proceed(
|
|
||||||
EasyMock.capture(cap),
|
|
||||||
EasyMock.isA(ExecChain.Scope.class))).andReturn(resp3);
|
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
execute(req1);
|
execute(req1);
|
||||||
execute(req2);
|
|
||||||
verifyMocks();
|
|
||||||
|
|
||||||
final ClassicHttpRequest captured = cap.getValue();
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
|
||||||
|
|
||||||
|
execute(req2);
|
||||||
|
|
||||||
|
final ArgumentCaptor<ClassicHttpRequest> reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class);
|
||||||
|
Mockito.verify(mockExecChain, Mockito.times(3)).proceed(reqCapture.capture(), Mockito.any());
|
||||||
|
|
||||||
|
final ClassicHttpRequest captured = reqCapture.getValue();
|
||||||
boolean hasMaxAge0 = false;
|
boolean hasMaxAge0 = false;
|
||||||
boolean hasNoCache = false;
|
boolean hasNoCache = false;
|
||||||
final Iterator<HeaderElement> it = MessageSupport.iterate(captured, HttpHeaders.CACHE_CONTROL);
|
final Iterator<HeaderElement> it = MessageSupport.iterate(captured, HttpHeaders.CACHE_CONTROL);
|
||||||
|
@ -1037,8 +1054,6 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
|
||||||
resp1.setHeader("Vary","User-Agent");
|
resp1.setHeader("Vary","User-Agent");
|
||||||
resp1.setHeader("Etag","\"etag1\"");
|
resp1.setHeader("Etag","\"etag1\"");
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp1);
|
|
||||||
|
|
||||||
final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET","/");
|
final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET","/");
|
||||||
req2.setHeader("User-Agent","agent2");
|
req2.setHeader("User-Agent","agent2");
|
||||||
final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
|
final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
|
||||||
|
@ -1046,25 +1061,26 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
|
||||||
resp2.setHeader("Vary","User-Agent");
|
resp2.setHeader("Vary","User-Agent");
|
||||||
resp2.setHeader("Etag","\"etag2\"");
|
resp2.setHeader("Etag","\"etag2\"");
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp2);
|
|
||||||
|
|
||||||
final Capture<ClassicHttpRequest> cap = EasyMock.newCapture();
|
|
||||||
final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET","/");
|
final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET","/");
|
||||||
req3.setHeader("User-Agent","agent3");
|
req3.setHeader("User-Agent","agent3");
|
||||||
final ClassicHttpResponse resp3 = HttpTestUtils.make200Response();
|
final ClassicHttpResponse resp3 = HttpTestUtils.make200Response();
|
||||||
|
|
||||||
EasyMock.expect(
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
|
||||||
mockExecChain.proceed(
|
|
||||||
EasyMock.capture(cap),
|
|
||||||
EasyMock.isA(ExecChain.Scope.class))).andReturn(resp3);
|
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
execute(req1);
|
execute(req1);
|
||||||
execute(req2);
|
|
||||||
execute(req3);
|
|
||||||
verifyMocks();
|
|
||||||
|
|
||||||
final ClassicHttpRequest captured = cap.getValue();
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
|
||||||
|
|
||||||
|
execute(req2);
|
||||||
|
|
||||||
|
Mockito.when(mockExecChain.proceed(Mockito.any(),Mockito.any())).thenReturn(resp3);
|
||||||
|
|
||||||
|
execute(req3);
|
||||||
|
|
||||||
|
final ArgumentCaptor<ClassicHttpRequest> reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class);
|
||||||
|
Mockito.verify(mockExecChain, Mockito.times(3)).proceed(reqCapture.capture(), Mockito.any());
|
||||||
|
|
||||||
|
final ClassicHttpRequest captured = reqCapture.getValue();
|
||||||
boolean foundEtag1 = false;
|
boolean foundEtag1 = false;
|
||||||
boolean foundEtag2 = false;
|
boolean foundEtag2 = false;
|
||||||
for(final Header h : captured.getHeaders("If-None-Match")) {
|
for(final Header h : captured.getHeaders("If-None-Match")) {
|
||||||
|
@ -1099,9 +1115,6 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
|
||||||
resp1.setHeader("Cache-Control", "max-age=3600");
|
resp1.setHeader("Cache-Control", "max-age=3600");
|
||||||
resp1.setHeader("ETag", "\"etag1\"");
|
resp1.setHeader("ETag", "\"etag1\"");
|
||||||
|
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp1);
|
|
||||||
|
|
||||||
final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
|
final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
|
||||||
req2.setHeader("User-Agent", "agent2");
|
req2.setHeader("User-Agent", "agent2");
|
||||||
|
|
||||||
|
@ -1111,8 +1124,6 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
|
||||||
resp2.setHeader("Cache-Control", "max-age=3600");
|
resp2.setHeader("Cache-Control", "max-age=3600");
|
||||||
resp2.setHeader("ETag", "\"etag2\"");
|
resp2.setHeader("ETag", "\"etag2\"");
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp2);
|
|
||||||
|
|
||||||
final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/");
|
final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/");
|
||||||
req3.setHeader("User-Agent", "agent3");
|
req3.setHeader("User-Agent", "agent3");
|
||||||
|
|
||||||
|
@ -1120,17 +1131,21 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
|
||||||
resp3.setHeader("Date", DateUtils.formatDate(now));
|
resp3.setHeader("Date", DateUtils.formatDate(now));
|
||||||
resp3.setHeader("ETag", "\"etag1\"");
|
resp3.setHeader("ETag", "\"etag1\"");
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp3);
|
|
||||||
|
|
||||||
final ClassicHttpRequest req4 = new BasicClassicHttpRequest("GET", "/");
|
final ClassicHttpRequest req4 = new BasicClassicHttpRequest("GET", "/");
|
||||||
req4.setHeader("User-Agent", "agent1");
|
req4.setHeader("User-Agent", "agent1");
|
||||||
|
|
||||||
replayMocks();
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
|
||||||
|
|
||||||
execute(req1);
|
execute(req1);
|
||||||
|
|
||||||
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
|
||||||
|
|
||||||
execute(req2);
|
execute(req2);
|
||||||
|
|
||||||
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp3);
|
||||||
|
|
||||||
final ClassicHttpResponse result1 = execute(req3);
|
final ClassicHttpResponse result1 = execute(req3);
|
||||||
final ClassicHttpResponse result2 = execute(req4);
|
final ClassicHttpResponse result2 = execute(req4);
|
||||||
verifyMocks();
|
|
||||||
|
|
||||||
assertEquals(HttpStatus.SC_OK, result1.getCode());
|
assertEquals(HttpStatus.SC_OK, result1.getCode());
|
||||||
assertEquals("\"etag1\"", result1.getFirstHeader("ETag").getValue());
|
assertEquals("\"etag1\"", result1.getFirstHeader("ETag").getValue());
|
||||||
|
@ -1150,7 +1165,7 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
|
||||||
resp1.setHeader("Cache-Control", "max-age=3600");
|
resp1.setHeader("Cache-Control", "max-age=3600");
|
||||||
resp1.setHeader("ETag", "\"etag1\"");
|
resp1.setHeader("ETag", "\"etag1\"");
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp1);
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
|
||||||
|
|
||||||
final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
|
final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
|
||||||
req2.setHeader("User-Agent", "agent2");
|
req2.setHeader("User-Agent", "agent2");
|
||||||
|
@ -1159,16 +1174,14 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
|
||||||
resp2.setHeader("Date", DateUtils.formatDate(now));
|
resp2.setHeader("Date", DateUtils.formatDate(now));
|
||||||
resp2.setHeader("ETag", "\"etag1\"");
|
resp2.setHeader("ETag", "\"etag1\"");
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp2);
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
|
||||||
|
|
||||||
final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/");
|
final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/");
|
||||||
req3.setHeader("User-Agent", "agent2");
|
req3.setHeader("User-Agent", "agent2");
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
execute(req1);
|
execute(req1);
|
||||||
execute(req2);
|
execute(req2);
|
||||||
execute(req3);
|
execute(req3);
|
||||||
verifyMocks();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* "If any of the existing cache entries contains only partial content
|
/* "If any of the existing cache entries contains only partial content
|
||||||
|
@ -1187,8 +1200,6 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
|
||||||
resp1.setHeader("Vary", "User-Agent");
|
resp1.setHeader("Vary", "User-Agent");
|
||||||
resp1.setHeader("ETag", "\"etag1\"");
|
resp1.setHeader("ETag", "\"etag1\"");
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp1);
|
|
||||||
|
|
||||||
final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
|
final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
|
||||||
req2.setHeader("User-Agent", "agent2");
|
req2.setHeader("User-Agent", "agent2");
|
||||||
req2.setHeader("Range", "bytes=0-49");
|
req2.setHeader("Range", "bytes=0-49");
|
||||||
|
@ -1201,8 +1212,6 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
|
||||||
resp2.setHeader("Cache-Control","max-age=3600");
|
resp2.setHeader("Cache-Control","max-age=3600");
|
||||||
resp2.setHeader("Date", DateUtils.formatDate(new Date()));
|
resp2.setHeader("Date", DateUtils.formatDate(new Date()));
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp2);
|
|
||||||
|
|
||||||
final ClassicHttpRequest req3 = HttpTestUtils.makeDefaultRequest();
|
final ClassicHttpRequest req3 = HttpTestUtils.makeDefaultRequest();
|
||||||
req3.setHeader("User-Agent", "agent3");
|
req3.setHeader("User-Agent", "agent3");
|
||||||
|
|
||||||
|
@ -1211,19 +1220,22 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
|
||||||
resp1.setHeader("Vary", "User-Agent");
|
resp1.setHeader("Vary", "User-Agent");
|
||||||
resp1.setHeader("ETag", "\"etag3\"");
|
resp1.setHeader("ETag", "\"etag3\"");
|
||||||
|
|
||||||
final Capture<ClassicHttpRequest> cap = EasyMock.newCapture();
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
|
||||||
EasyMock.expect(
|
|
||||||
mockExecChain.proceed(
|
|
||||||
EasyMock.capture(cap),
|
|
||||||
EasyMock.isA(ExecChain.Scope.class))).andReturn(resp3);
|
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
execute(req1);
|
execute(req1);
|
||||||
execute(req2);
|
|
||||||
execute(req3);
|
|
||||||
verifyMocks();
|
|
||||||
|
|
||||||
final ClassicHttpRequest captured = cap.getValue();
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
|
||||||
|
|
||||||
|
execute(req2);
|
||||||
|
|
||||||
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp3);
|
||||||
|
|
||||||
|
execute(req3);
|
||||||
|
|
||||||
|
final ArgumentCaptor<ClassicHttpRequest> reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class);
|
||||||
|
Mockito.verify(mockExecChain, Mockito.times(3)).proceed(reqCapture.capture(), Mockito.any());
|
||||||
|
|
||||||
|
final ClassicHttpRequest captured = reqCapture.getValue();
|
||||||
final Iterator<HeaderElement> it = MessageSupport.iterate(captured, HttpHeaders.IF_NONE_MATCH);
|
final Iterator<HeaderElement> it = MessageSupport.iterate(captured, HttpHeaders.IF_NONE_MATCH);
|
||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
final HeaderElement elt = it.next();
|
final HeaderElement elt = it.next();
|
||||||
|
@ -1248,7 +1260,7 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
|
||||||
resp1.setHeader("ETag", "\"old-etag\"");
|
resp1.setHeader("ETag", "\"old-etag\"");
|
||||||
resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
|
resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp1);
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
|
||||||
|
|
||||||
final ClassicHttpRequest req2 = new HttpPost("http://foo.example.com/bar");
|
final ClassicHttpRequest req2 = new HttpPost("http://foo.example.com/bar");
|
||||||
final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
|
final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
|
||||||
|
@ -1256,18 +1268,16 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
|
||||||
resp2.setHeader("Date", DateUtils.formatDate(now));
|
resp2.setHeader("Date", DateUtils.formatDate(now));
|
||||||
resp2.setHeader("Content-Location", "http://foo.example.com/");
|
resp2.setHeader("Content-Location", "http://foo.example.com/");
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp2);
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
|
||||||
|
|
||||||
final ClassicHttpRequest req3 = new HttpGet("http://foo.example.com");
|
final ClassicHttpRequest req3 = new HttpGet("http://foo.example.com");
|
||||||
final ClassicHttpResponse resp3 = HttpTestUtils.make200Response();
|
final ClassicHttpResponse resp3 = HttpTestUtils.make200Response();
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp3);
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp3);
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
execute(req1);
|
execute(req1);
|
||||||
execute(req2);
|
execute(req2);
|
||||||
execute(req3);
|
execute(req3);
|
||||||
verifyMocks();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1286,11 +1296,9 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
|
||||||
resp2.setHeader("Content-Length","200");
|
resp2.setHeader("Content-Length","200");
|
||||||
resp2.setHeader("Date", DateUtils.formatDate(now));
|
resp2.setHeader("Date", DateUtils.formatDate(now));
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp2);
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
execute(req2);
|
execute(req2);
|
||||||
verifyMocks();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -1303,11 +1311,9 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
|
||||||
resp2.setHeader("Date", DateUtils.formatDate(now));
|
resp2.setHeader("Date", DateUtils.formatDate(now));
|
||||||
resp2.setHeader("Via","1.0 someproxy");
|
resp2.setHeader("Via","1.0 someproxy");
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp2);
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
execute(req2);
|
execute(req2);
|
||||||
verifyMocks();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1323,25 +1329,23 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
|
||||||
final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
|
final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
|
||||||
resp1.setHeader("Cache-Control","max-age=3600");
|
resp1.setHeader("Cache-Control","max-age=3600");
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp1);
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
|
||||||
|
|
||||||
final ClassicHttpRequest req2 = new BasicClassicHttpRequest("FROB", "/");
|
final ClassicHttpRequest req2 = new BasicClassicHttpRequest("FROB", "/");
|
||||||
final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
|
final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
|
||||||
resp2.setHeader("Cache-Control","max-age=3600");
|
resp2.setHeader("Cache-Control","max-age=3600");
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp2);
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
|
||||||
|
|
||||||
final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/");
|
final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/");
|
||||||
final ClassicHttpResponse resp3 = HttpTestUtils.make200Response();
|
final ClassicHttpResponse resp3 = HttpTestUtils.make200Response();
|
||||||
resp3.setHeader("ETag", "\"etag\"");
|
resp3.setHeader("ETag", "\"etag\"");
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp3);
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp3);
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
execute(req1);
|
execute(req1);
|
||||||
execute(req2);
|
execute(req2);
|
||||||
final ClassicHttpResponse result = execute(req3);
|
final ClassicHttpResponse result = execute(req3);
|
||||||
verifyMocks();
|
|
||||||
|
|
||||||
assertTrue(HttpTestUtils.semanticallyTransparent(resp3, result));
|
assertTrue(HttpTestUtils.semanticallyTransparent(resp3, result));
|
||||||
}
|
}
|
||||||
|
@ -1354,44 +1358,46 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
|
||||||
resp1.setHeader("Cache-Control","max-age=3600");
|
resp1.setHeader("Cache-Control","max-age=3600");
|
||||||
resp1.setHeader("Vary", "User-Agent");
|
resp1.setHeader("Vary", "User-Agent");
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp1);
|
|
||||||
|
|
||||||
final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
|
final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
|
||||||
req2.setHeader("User-Agent", "agent2");
|
req2.setHeader("User-Agent", "agent2");
|
||||||
final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
|
final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
|
||||||
resp2.setHeader("Cache-Control","max-age=3600");
|
resp2.setHeader("Cache-Control","max-age=3600");
|
||||||
resp2.setHeader("Vary", "User-Agent");
|
resp2.setHeader("Vary", "User-Agent");
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp2);
|
|
||||||
|
|
||||||
final ClassicHttpRequest req3 = new BasicClassicHttpRequest("FROB", "/");
|
final ClassicHttpRequest req3 = new BasicClassicHttpRequest("FROB", "/");
|
||||||
req3.setHeader("User-Agent", "agent3");
|
req3.setHeader("User-Agent", "agent3");
|
||||||
final ClassicHttpResponse resp3 = HttpTestUtils.make200Response();
|
final ClassicHttpResponse resp3 = HttpTestUtils.make200Response();
|
||||||
resp3.setHeader("Cache-Control","max-age=3600");
|
resp3.setHeader("Cache-Control","max-age=3600");
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp3);
|
|
||||||
|
|
||||||
final ClassicHttpRequest req4 = new BasicClassicHttpRequest("GET", "/");
|
final ClassicHttpRequest req4 = new BasicClassicHttpRequest("GET", "/");
|
||||||
req4.setHeader("User-Agent", "agent1");
|
req4.setHeader("User-Agent", "agent1");
|
||||||
final ClassicHttpResponse resp4 = HttpTestUtils.make200Response();
|
final ClassicHttpResponse resp4 = HttpTestUtils.make200Response();
|
||||||
resp4.setHeader("ETag", "\"etag1\"");
|
resp4.setHeader("ETag", "\"etag1\"");
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp4);
|
|
||||||
|
|
||||||
final ClassicHttpRequest req5 = new BasicClassicHttpRequest("GET", "/");
|
final ClassicHttpRequest req5 = new BasicClassicHttpRequest("GET", "/");
|
||||||
req5.setHeader("User-Agent", "agent2");
|
req5.setHeader("User-Agent", "agent2");
|
||||||
final ClassicHttpResponse resp5 = HttpTestUtils.make200Response();
|
final ClassicHttpResponse resp5 = HttpTestUtils.make200Response();
|
||||||
resp5.setHeader("ETag", "\"etag2\"");
|
resp5.setHeader("ETag", "\"etag2\"");
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp5);
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
execute(req1);
|
execute(req1);
|
||||||
|
|
||||||
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
|
||||||
|
|
||||||
execute(req2);
|
execute(req2);
|
||||||
|
|
||||||
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp3);
|
||||||
|
|
||||||
execute(req3);
|
execute(req3);
|
||||||
|
|
||||||
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp4);
|
||||||
|
|
||||||
final ClassicHttpResponse result4 = execute(req4);
|
final ClassicHttpResponse result4 = execute(req4);
|
||||||
|
|
||||||
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp5);
|
||||||
|
|
||||||
final ClassicHttpResponse result5 = execute(req5);
|
final ClassicHttpResponse result5 = execute(req5);
|
||||||
verifyMocks();
|
|
||||||
|
|
||||||
assertTrue(HttpTestUtils.semanticallyTransparent(resp4, result4));
|
assertTrue(HttpTestUtils.semanticallyTransparent(resp4, result4));
|
||||||
assertTrue(HttpTestUtils.semanticallyTransparent(resp5, result5));
|
assertTrue(HttpTestUtils.semanticallyTransparent(resp5, result5));
|
||||||
|
@ -1412,7 +1418,7 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
|
||||||
resp1.setHeader("Cache-Control", "max-age=3600");
|
resp1.setHeader("Cache-Control", "max-age=3600");
|
||||||
resp1.setHeader("ETag", "\"etag1\"");
|
resp1.setHeader("ETag", "\"etag1\"");
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp1);
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
|
||||||
|
|
||||||
final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
|
final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
|
||||||
req2.setHeader("Cache-Control", "max-age=0");
|
req2.setHeader("Cache-Control", "max-age=0");
|
||||||
|
@ -1421,15 +1427,13 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
|
||||||
resp2.setHeader("Cache-Control", "max-age=3600");
|
resp2.setHeader("Cache-Control", "max-age=3600");
|
||||||
resp2.setHeader("ETag", "\"etag2\"");
|
resp2.setHeader("ETag", "\"etag2\"");
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp2);
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
|
||||||
|
|
||||||
final ClassicHttpRequest req3 = HttpTestUtils.makeDefaultRequest();
|
final ClassicHttpRequest req3 = HttpTestUtils.makeDefaultRequest();
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
execute(req1);
|
execute(req1);
|
||||||
execute(req2);
|
execute(req2);
|
||||||
final ClassicHttpResponse result = execute(req3);
|
final ClassicHttpResponse result = execute(req3);
|
||||||
verifyMocks();
|
|
||||||
|
|
||||||
assertTrue(HttpTestUtils.semanticallyTransparent(resp2, result));
|
assertTrue(HttpTestUtils.semanticallyTransparent(resp2, result));
|
||||||
}
|
}
|
||||||
|
@ -1453,19 +1457,17 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
|
||||||
resp1.setHeader("Expires", DateUtils.formatDate(now));
|
resp1.setHeader("Expires", DateUtils.formatDate(now));
|
||||||
resp1.removeHeaders("Cache-Control");
|
resp1.removeHeaders("Cache-Control");
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp1);
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
|
||||||
|
|
||||||
final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
|
final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
|
||||||
req2.setHeader("Cache-Control", "max-stale=1000");
|
req2.setHeader("Cache-Control", "max-stale=1000");
|
||||||
final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
|
final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
|
||||||
resp2.setHeader("ETag", "\"etag2\"");
|
resp2.setHeader("ETag", "\"etag2\"");
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp2);
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
execute(req1);
|
execute(req1);
|
||||||
final ClassicHttpResponse result = execute(req2);
|
final ClassicHttpResponse result = execute(req2);
|
||||||
verifyMocks();
|
|
||||||
|
|
||||||
assertTrue(HttpTestUtils.semanticallyTransparent(resp2, result));
|
assertTrue(HttpTestUtils.semanticallyTransparent(resp2, result));
|
||||||
}
|
}
|
||||||
|
@ -1478,19 +1480,17 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
|
||||||
resp1.setHeader("Expires", DateUtils.formatDate(tenSecondsAgo));
|
resp1.setHeader("Expires", DateUtils.formatDate(tenSecondsAgo));
|
||||||
resp1.removeHeaders("Cache-Control");
|
resp1.removeHeaders("Cache-Control");
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp1);
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
|
||||||
|
|
||||||
final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
|
final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
|
||||||
req2.setHeader("Cache-Control", "max-stale=1000");
|
req2.setHeader("Cache-Control", "max-stale=1000");
|
||||||
final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
|
final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
|
||||||
resp2.setHeader("ETag", "\"etag2\"");
|
resp2.setHeader("ETag", "\"etag2\"");
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp2);
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
execute(req1);
|
execute(req1);
|
||||||
final ClassicHttpResponse result = execute(req2);
|
final ClassicHttpResponse result = execute(req2);
|
||||||
verifyMocks();
|
|
||||||
|
|
||||||
assertTrue(HttpTestUtils.semanticallyTransparent(resp2, result));
|
assertTrue(HttpTestUtils.semanticallyTransparent(resp2, result));
|
||||||
}
|
}
|
||||||
|
@ -1507,18 +1507,12 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
|
||||||
req1.setHeader("Cache-Control", "min-fresh=10, no-cache");
|
req1.setHeader("Cache-Control", "min-fresh=10, no-cache");
|
||||||
req1.addHeader("Cache-Control", "max-stale=0, max-age=0");
|
req1.addHeader("Cache-Control", "max-stale=0, max-age=0");
|
||||||
|
|
||||||
|
|
||||||
final Capture<ClassicHttpRequest> cap = EasyMock.newCapture();
|
|
||||||
EasyMock.expect(
|
|
||||||
mockExecChain.proceed(
|
|
||||||
EasyMock.capture(cap),
|
|
||||||
EasyMock.isA(ExecChain.Scope.class))).andReturn(HttpTestUtils.make200Response());
|
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
execute(req1);
|
execute(req1);
|
||||||
verifyMocks();
|
|
||||||
|
|
||||||
final ClassicHttpRequest captured = cap.getValue();
|
final ArgumentCaptor<ClassicHttpRequest> reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class);
|
||||||
|
Mockito.verify(mockExecChain).proceed(reqCapture.capture(), Mockito.any());
|
||||||
|
|
||||||
|
final ClassicHttpRequest captured = reqCapture.getValue();
|
||||||
boolean foundNoCache = false;
|
boolean foundNoCache = false;
|
||||||
boolean foundDisallowedDirective = false;
|
boolean foundDisallowedDirective = false;
|
||||||
final List<String> disallowed =
|
final List<String> disallowed =
|
||||||
|
@ -1551,12 +1545,9 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
|
||||||
final ClassicHttpRequest req = HttpTestUtils.makeDefaultRequest();
|
final ClassicHttpRequest req = HttpTestUtils.makeDefaultRequest();
|
||||||
req.setHeader("Cache-Control", "only-if-cached");
|
req.setHeader("Cache-Control", "only-if-cached");
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
final ClassicHttpResponse result = execute(req);
|
final ClassicHttpResponse result = execute(req);
|
||||||
verifyMocks();
|
|
||||||
|
|
||||||
assertEquals(HttpStatus.SC_GATEWAY_TIMEOUT,
|
assertEquals(HttpStatus.SC_GATEWAY_TIMEOUT, result.getCode());
|
||||||
result.getCode());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -1565,15 +1556,13 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
|
||||||
final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
|
final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
|
||||||
resp1.setHeader("Cache-Control","max-age=3600");
|
resp1.setHeader("Cache-Control","max-age=3600");
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp1);
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
|
||||||
|
|
||||||
final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
|
final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
|
||||||
req2.setHeader("Cache-Control", "only-if-cached");
|
req2.setHeader("Cache-Control", "only-if-cached");
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
execute(req1);
|
execute(req1);
|
||||||
final ClassicHttpResponse result = execute(req2);
|
final ClassicHttpResponse result = execute(req2);
|
||||||
verifyMocks();
|
|
||||||
|
|
||||||
assertTrue(HttpTestUtils.semanticallyTransparent(resp1, result));
|
assertTrue(HttpTestUtils.semanticallyTransparent(resp1, result));
|
||||||
}
|
}
|
||||||
|
@ -1585,18 +1574,15 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
|
||||||
resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
|
resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
|
||||||
resp1.setHeader("Cache-Control","max-age=5");
|
resp1.setHeader("Cache-Control","max-age=5");
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp1);
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
|
||||||
|
|
||||||
final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
|
final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
|
||||||
req2.setHeader("Cache-Control", "only-if-cached");
|
req2.setHeader("Cache-Control", "only-if-cached");
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
execute(req1);
|
execute(req1);
|
||||||
final ClassicHttpResponse result = execute(req2);
|
final ClassicHttpResponse result = execute(req2);
|
||||||
verifyMocks();
|
|
||||||
|
|
||||||
assertEquals(HttpStatus.SC_GATEWAY_TIMEOUT,
|
assertEquals(HttpStatus.SC_GATEWAY_TIMEOUT, result.getCode());
|
||||||
result.getCode());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -1607,15 +1593,13 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
|
||||||
resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
|
resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
|
||||||
resp1.setHeader("Cache-Control","max-age=5");
|
resp1.setHeader("Cache-Control","max-age=5");
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp1);
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
|
||||||
|
|
||||||
final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
|
final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
|
||||||
req2.setHeader("Cache-Control", "max-stale=20, only-if-cached");
|
req2.setHeader("Cache-Control", "max-stale=20, only-if-cached");
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
execute(req1);
|
execute(req1);
|
||||||
final ClassicHttpResponse result = execute(req2);
|
final ClassicHttpResponse result = execute(req2);
|
||||||
verifyMocks();
|
|
||||||
|
|
||||||
assertTrue(HttpTestUtils.semanticallyTransparent(resp1, result));
|
assertTrue(HttpTestUtils.semanticallyTransparent(resp1, result));
|
||||||
}
|
}
|
||||||
|
@ -1628,18 +1612,15 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
|
||||||
resp1.setHeader("Cache-Control", "max-age=300");
|
resp1.setHeader("Cache-Control", "max-age=300");
|
||||||
resp1.setHeader("ETag","W/\"weak-sauce\"");
|
resp1.setHeader("ETag","W/\"weak-sauce\"");
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp1);
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
|
||||||
|
|
||||||
final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
|
final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
|
||||||
req2.setHeader("If-None-Match","W/\"weak-sauce\"");
|
req2.setHeader("If-None-Match","W/\"weak-sauce\"");
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
execute(req1);
|
execute(req1);
|
||||||
final ClassicHttpResponse result = execute(req2);
|
final ClassicHttpResponse result = execute(req2);
|
||||||
verifyMocks();
|
|
||||||
|
|
||||||
assertEquals(HttpStatus.SC_NOT_MODIFIED, result.getCode());
|
assertEquals(HttpStatus.SC_NOT_MODIFIED, result.getCode());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -31,37 +31,90 @@ import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||||
|
|
||||||
|
import org.apache.hc.client5.http.HttpRoute;
|
||||||
|
import org.apache.hc.client5.http.classic.ExecChain;
|
||||||
|
import org.apache.hc.client5.http.classic.ExecRuntime;
|
||||||
import org.apache.hc.client5.http.impl.schedule.ImmediateSchedulingStrategy;
|
import org.apache.hc.client5.http.impl.schedule.ImmediateSchedulingStrategy;
|
||||||
|
import org.apache.hc.client5.http.protocol.HttpClientContext;
|
||||||
import org.apache.hc.client5.http.utils.DateUtils;
|
import org.apache.hc.client5.http.utils.DateUtils;
|
||||||
import org.apache.hc.core5.http.ClassicHttpRequest;
|
import org.apache.hc.core5.http.ClassicHttpRequest;
|
||||||
import org.apache.hc.core5.http.ClassicHttpResponse;
|
import org.apache.hc.core5.http.ClassicHttpResponse;
|
||||||
import org.apache.hc.core5.http.Header;
|
import org.apache.hc.core5.http.Header;
|
||||||
import org.apache.hc.core5.http.HttpEntity;
|
import org.apache.hc.core5.http.HttpEntity;
|
||||||
|
import org.apache.hc.core5.http.HttpException;
|
||||||
|
import org.apache.hc.core5.http.HttpHost;
|
||||||
import org.apache.hc.core5.http.HttpStatus;
|
import org.apache.hc.core5.http.HttpStatus;
|
||||||
import org.apache.hc.core5.http.io.entity.InputStreamEntity;
|
import org.apache.hc.core5.http.io.entity.InputStreamEntity;
|
||||||
|
import org.apache.hc.core5.http.io.support.ClassicRequestBuilder;
|
||||||
import org.apache.hc.core5.http.message.BasicClassicHttpRequest;
|
import org.apache.hc.core5.http.message.BasicClassicHttpRequest;
|
||||||
import org.easymock.EasyMock;
|
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A suite of acceptance tests for compliance with RFC5861, which
|
* A suite of acceptance tests for compliance with RFC5861, which
|
||||||
* describes the stale-if-error and stale-while-revalidate
|
* describes the stale-if-error and stale-while-revalidate
|
||||||
* Cache-Control extensions.
|
* Cache-Control extensions.
|
||||||
*/
|
*/
|
||||||
public class TestRFC5861Compliance extends AbstractProtocolTest {
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
|
public class TestRFC5861Compliance {
|
||||||
|
|
||||||
private ScheduledExecutorService executorService;
|
static final int MAX_BYTES = 1024;
|
||||||
|
static final int MAX_ENTRIES = 100;
|
||||||
|
static final int ENTITY_LENGTH = 128;
|
||||||
|
|
||||||
|
HttpHost host;
|
||||||
|
HttpRoute route;
|
||||||
|
HttpEntity body;
|
||||||
|
HttpClientContext context;
|
||||||
|
@Mock
|
||||||
|
ExecChain mockExecChain;
|
||||||
|
@Mock
|
||||||
|
ExecRuntime mockExecRuntime;
|
||||||
|
@Mock
|
||||||
|
HttpCache mockCache;
|
||||||
|
ClassicHttpRequest request;
|
||||||
|
ClassicHttpResponse originResponse;
|
||||||
|
CacheConfig config;
|
||||||
|
CachingExec impl;
|
||||||
|
HttpCache cache;
|
||||||
|
ScheduledExecutorService executorService;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setup() {
|
public void setUp() throws Exception {
|
||||||
|
host = new HttpHost("foo.example.com", 80);
|
||||||
|
|
||||||
|
route = new HttpRoute(host);
|
||||||
|
|
||||||
|
body = HttpTestUtils.makeBody(ENTITY_LENGTH);
|
||||||
|
|
||||||
|
request = new BasicClassicHttpRequest("GET", "/foo");
|
||||||
|
|
||||||
|
context = HttpClientContext.create();
|
||||||
|
|
||||||
|
originResponse = HttpTestUtils.make200Response();
|
||||||
|
|
||||||
|
config = CacheConfig.custom()
|
||||||
|
.setMaxCacheEntries(MAX_ENTRIES)
|
||||||
|
.setMaxObjectSize(MAX_BYTES)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
cache = new BasicHttpCache(config);
|
||||||
|
impl = new CachingExec(cache, null, config);
|
||||||
|
|
||||||
executorService = new ScheduledThreadPoolExecutor(1);
|
executorService = new ScheduledThreadPoolExecutor(1);
|
||||||
EasyMock.expect(mockExecRuntime.fork(null)).andReturn(mockExecRuntime).anyTimes();
|
|
||||||
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
|
||||||
|
Mockito.when(mockExecRuntime.fork(null)).thenReturn(mockExecRuntime);
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
|
@ -69,16 +122,11 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
|
||||||
executorService.shutdownNow();
|
executorService.shutdownNow();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public ClassicHttpResponse execute(final ClassicHttpRequest request) throws IOException, HttpException {
|
||||||
protected void replayMocks() {
|
return impl.execute(
|
||||||
super.replayMocks();
|
ClassicRequestBuilder.copy(request).build(),
|
||||||
EasyMock.replay(mockExecRuntime);
|
new ExecChain.Scope("test", route, request, mockExecRuntime, context),
|
||||||
}
|
mockExecChain);
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void verifyMocks() {
|
|
||||||
super.verifyMocks();
|
|
||||||
EasyMock.verify(mockExecRuntime);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -104,17 +152,16 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
|
||||||
final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(tenSecondsAgo,
|
final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(tenSecondsAgo,
|
||||||
"public, max-age=5, stale-if-error=60");
|
"public, max-age=5, stale-if-error=60");
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp1);
|
|
||||||
|
|
||||||
final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
|
final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
|
||||||
final ClassicHttpResponse resp2 = HttpTestUtils.make500Response();
|
final ClassicHttpResponse resp2 = HttpTestUtils.make500Response();
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp2);
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
execute(req1);
|
execute(req1);
|
||||||
|
|
||||||
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
|
||||||
|
|
||||||
final ClassicHttpResponse result = execute(req2);
|
final ClassicHttpResponse result = execute(req2);
|
||||||
verifyMocks();
|
|
||||||
|
|
||||||
HttpTestUtils.assert110WarningFound(result);
|
HttpTestUtils.assert110WarningFound(result);
|
||||||
}
|
}
|
||||||
|
@ -127,8 +174,6 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
|
||||||
final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(tenSecondsAgo,
|
final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(tenSecondsAgo,
|
||||||
"public, max-age=5, stale-if-error=60");
|
"public, max-age=5, stale-if-error=60");
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp1);
|
|
||||||
|
|
||||||
final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
|
final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
|
||||||
final ClassicHttpResponse resp2 = HttpTestUtils.make500Response();
|
final ClassicHttpResponse resp2 = HttpTestUtils.make500Response();
|
||||||
final byte[] body101 = HttpTestUtils.getRandomBytes(101);
|
final byte[] body101 = HttpTestUtils.getRandomBytes(101);
|
||||||
|
@ -137,12 +182,13 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
|
||||||
final HttpEntity entity = new InputStreamEntity(cis, 101, null);
|
final HttpEntity entity = new InputStreamEntity(cis, 101, null);
|
||||||
resp2.setEntity(entity);
|
resp2.setEntity(entity);
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp2);
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
execute(req1);
|
execute(req1);
|
||||||
|
|
||||||
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
|
||||||
|
|
||||||
execute(req2);
|
execute(req2);
|
||||||
verifyMocks();
|
|
||||||
|
|
||||||
assertTrue(cis.wasClosed());
|
assertTrue(cis.wasClosed());
|
||||||
}
|
}
|
||||||
|
@ -155,17 +201,16 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
|
||||||
final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(tenSecondsAgo,
|
final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(tenSecondsAgo,
|
||||||
"public, max-age=5, stale-if-error=60, must-revalidate");
|
"public, max-age=5, stale-if-error=60, must-revalidate");
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp1);
|
|
||||||
|
|
||||||
final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
|
final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
|
||||||
final ClassicHttpResponse resp2 = HttpTestUtils.make500Response();
|
final ClassicHttpResponse resp2 = HttpTestUtils.make500Response();
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp2);
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
execute(req1);
|
execute(req1);
|
||||||
|
|
||||||
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
|
||||||
|
|
||||||
final ClassicHttpResponse result = execute(req2);
|
final ClassicHttpResponse result = execute(req2);
|
||||||
verifyMocks();
|
|
||||||
|
|
||||||
assertTrue(HttpStatus.SC_OK != result.getCode());
|
assertTrue(HttpStatus.SC_OK != result.getCode());
|
||||||
}
|
}
|
||||||
|
@ -179,17 +224,16 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
|
||||||
final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(tenSecondsAgo,
|
final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(tenSecondsAgo,
|
||||||
"public, max-age=5, stale-if-error=60, proxy-revalidate");
|
"public, max-age=5, stale-if-error=60, proxy-revalidate");
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp1);
|
|
||||||
|
|
||||||
final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
|
final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
|
||||||
final ClassicHttpResponse resp2 = HttpTestUtils.make500Response();
|
final ClassicHttpResponse resp2 = HttpTestUtils.make500Response();
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp2);
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
execute(req1);
|
execute(req1);
|
||||||
|
|
||||||
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
|
||||||
|
|
||||||
final ClassicHttpResponse result = execute(req2);
|
final ClassicHttpResponse result = execute(req2);
|
||||||
verifyMocks();
|
|
||||||
|
|
||||||
assertTrue(HttpStatus.SC_OK != result.getCode());
|
assertTrue(HttpStatus.SC_OK != result.getCode());
|
||||||
}
|
}
|
||||||
|
@ -206,17 +250,16 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
|
||||||
final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(tenSecondsAgo,
|
final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(tenSecondsAgo,
|
||||||
"public, max-age=5, stale-if-error=60, proxy-revalidate");
|
"public, max-age=5, stale-if-error=60, proxy-revalidate");
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp1);
|
|
||||||
|
|
||||||
final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
|
final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
|
||||||
final ClassicHttpResponse resp2 = HttpTestUtils.make500Response();
|
final ClassicHttpResponse resp2 = HttpTestUtils.make500Response();
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp2);
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
execute(req1);
|
execute(req1);
|
||||||
|
|
||||||
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
|
||||||
|
|
||||||
final ClassicHttpResponse result = execute(req2);
|
final ClassicHttpResponse result = execute(req2);
|
||||||
verifyMocks();
|
|
||||||
|
|
||||||
HttpTestUtils.assert110WarningFound(result);
|
HttpTestUtils.assert110WarningFound(result);
|
||||||
}
|
}
|
||||||
|
@ -229,18 +272,17 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
|
||||||
final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(tenSecondsAgo,
|
final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(tenSecondsAgo,
|
||||||
"public, max-age=5, stale-if-error=60");
|
"public, max-age=5, stale-if-error=60");
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp1);
|
|
||||||
|
|
||||||
final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
|
final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
|
||||||
req2.setHeader("Cache-Control","min-fresh=2");
|
req2.setHeader("Cache-Control","min-fresh=2");
|
||||||
final ClassicHttpResponse resp2 = HttpTestUtils.make500Response();
|
final ClassicHttpResponse resp2 = HttpTestUtils.make500Response();
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp2);
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
execute(req1);
|
execute(req1);
|
||||||
|
|
||||||
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
|
||||||
|
|
||||||
final ClassicHttpResponse result = execute(req2);
|
final ClassicHttpResponse result = execute(req2);
|
||||||
verifyMocks();
|
|
||||||
|
|
||||||
assertTrue(HttpStatus.SC_OK != result.getCode());
|
assertTrue(HttpStatus.SC_OK != result.getCode());
|
||||||
}
|
}
|
||||||
|
@ -253,18 +295,17 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
|
||||||
final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(tenSecondsAgo,
|
final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(tenSecondsAgo,
|
||||||
"public, max-age=5");
|
"public, max-age=5");
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp1);
|
|
||||||
|
|
||||||
final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
|
final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
|
||||||
req2.setHeader("Cache-Control","public, stale-if-error=60");
|
req2.setHeader("Cache-Control","public, stale-if-error=60");
|
||||||
final ClassicHttpResponse resp2 = HttpTestUtils.make500Response();
|
final ClassicHttpResponse resp2 = HttpTestUtils.make500Response();
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp2);
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
execute(req1);
|
execute(req1);
|
||||||
|
|
||||||
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
|
||||||
|
|
||||||
final ClassicHttpResponse result = execute(req2);
|
final ClassicHttpResponse result = execute(req2);
|
||||||
verifyMocks();
|
|
||||||
|
|
||||||
HttpTestUtils.assert110WarningFound(result);
|
HttpTestUtils.assert110WarningFound(result);
|
||||||
}
|
}
|
||||||
|
@ -278,18 +319,17 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
|
||||||
resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
|
resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
|
||||||
resp1.setHeader("Cache-Control", "public, max-age=5");
|
resp1.setHeader("Cache-Control", "public, max-age=5");
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp1);
|
|
||||||
|
|
||||||
final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
|
final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
|
||||||
req2.setHeader("Cache-Control", "public, stale-if-error=60");
|
req2.setHeader("Cache-Control", "public, stale-if-error=60");
|
||||||
final ClassicHttpResponse resp2 = HttpTestUtils.make500Response();
|
final ClassicHttpResponse resp2 = HttpTestUtils.make500Response();
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp2);
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
execute(req1);
|
execute(req1);
|
||||||
|
|
||||||
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
|
||||||
|
|
||||||
final ClassicHttpResponse result = execute(req2);
|
final ClassicHttpResponse result = execute(req2);
|
||||||
verifyMocks();
|
|
||||||
|
|
||||||
HttpTestUtils.assert110WarningFound(result);
|
HttpTestUtils.assert110WarningFound(result);
|
||||||
}
|
}
|
||||||
|
@ -303,17 +343,16 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
|
||||||
final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(tenSecondsAgo,
|
final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(tenSecondsAgo,
|
||||||
"public, max-age=5, stale-if-error=2");
|
"public, max-age=5, stale-if-error=2");
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp1);
|
|
||||||
|
|
||||||
final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
|
final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
|
||||||
final ClassicHttpResponse resp2 = HttpTestUtils.make500Response();
|
final ClassicHttpResponse resp2 = HttpTestUtils.make500Response();
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp2);
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
execute(req1);
|
execute(req1);
|
||||||
|
|
||||||
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
|
||||||
|
|
||||||
final ClassicHttpResponse result = execute(req2);
|
final ClassicHttpResponse result = execute(req2);
|
||||||
verifyMocks();
|
|
||||||
|
|
||||||
assertEquals(HttpStatus.SC_INTERNAL_SERVER_ERROR,
|
assertEquals(HttpStatus.SC_INTERNAL_SERVER_ERROR,
|
||||||
result.getCode());
|
result.getCode());
|
||||||
|
@ -328,21 +367,19 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
|
||||||
final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(tenSecondsAgo,
|
final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(tenSecondsAgo,
|
||||||
"public, max-age=5");
|
"public, max-age=5");
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp1);
|
|
||||||
|
|
||||||
final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
|
final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest();
|
||||||
req2.setHeader("Cache-Control","stale-if-error=2");
|
req2.setHeader("Cache-Control","stale-if-error=2");
|
||||||
final ClassicHttpResponse resp2 = HttpTestUtils.make500Response();
|
final ClassicHttpResponse resp2 = HttpTestUtils.make500Response();
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp2);
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
execute(req1);
|
execute(req1);
|
||||||
final ClassicHttpResponse result = execute(req2);
|
|
||||||
verifyMocks();
|
|
||||||
|
|
||||||
assertEquals(HttpStatus.SC_INTERNAL_SERVER_ERROR,
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
|
||||||
result.getCode());
|
|
||||||
|
final ClassicHttpResponse result = execute(req2);
|
||||||
|
|
||||||
|
assertEquals(HttpStatus.SC_INTERNAL_SERVER_ERROR, result.getCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -372,14 +409,12 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
|
||||||
resp1.setHeader("ETag","\"etag\"");
|
resp1.setHeader("ETag","\"etag\"");
|
||||||
resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
|
resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp1).times(1,2);
|
|
||||||
|
|
||||||
final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
|
final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
|
||||||
|
|
||||||
replayMocks();
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
|
||||||
|
|
||||||
execute(req1);
|
execute(req1);
|
||||||
final ClassicHttpResponse result = execute(req2);
|
final ClassicHttpResponse result = execute(req2);
|
||||||
verifyMocks();
|
|
||||||
|
|
||||||
assertEquals(HttpStatus.SC_OK, result.getCode());
|
assertEquals(HttpStatus.SC_OK, result.getCode());
|
||||||
boolean warning110Found = false;
|
boolean warning110Found = false;
|
||||||
|
@ -392,6 +427,9 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assertTrue(warning110Found);
|
assertTrue(warning110Found);
|
||||||
|
|
||||||
|
Mockito.verify(mockExecChain, Mockito.atLeastOnce()).proceed(Mockito.any(), Mockito.any());
|
||||||
|
Mockito.verify(mockExecChain, Mockito.atMost(2)).proceed(Mockito.any(), Mockito.any());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -409,14 +447,12 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
|
||||||
resp1.setHeader("Cache-Control", "public, max-age=5, stale-while-revalidate=15");
|
resp1.setHeader("Cache-Control", "public, max-age=5, stale-while-revalidate=15");
|
||||||
resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
|
resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp1).times(1, 2);
|
|
||||||
|
|
||||||
final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
|
final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
|
||||||
|
|
||||||
replayMocks();
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
|
||||||
|
|
||||||
execute(req1);
|
execute(req1);
|
||||||
final ClassicHttpResponse result = execute(req2);
|
final ClassicHttpResponse result = execute(req2);
|
||||||
verifyMocks();
|
|
||||||
|
|
||||||
assertEquals(HttpStatus.SC_OK, result.getCode());
|
assertEquals(HttpStatus.SC_OK, result.getCode());
|
||||||
boolean warning110Found = false;
|
boolean warning110Found = false;
|
||||||
|
@ -429,6 +465,9 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assertTrue(warning110Found);
|
assertTrue(warning110Found);
|
||||||
|
|
||||||
|
Mockito.verify(mockExecChain, Mockito.atLeastOnce()).proceed(Mockito.any(), Mockito.any());
|
||||||
|
Mockito.verify(mockExecChain, Mockito.atMost(2)).proceed(Mockito.any(), Mockito.any());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -451,15 +490,13 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
|
||||||
resp1.setHeader("ETag","\"etag\"");
|
resp1.setHeader("ETag","\"etag\"");
|
||||||
resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
|
resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp1).times(1,2);
|
|
||||||
|
|
||||||
final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
|
final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
|
||||||
req2.setHeader("If-None-Match","\"etag\"");
|
req2.setHeader("If-None-Match","\"etag\"");
|
||||||
|
|
||||||
replayMocks();
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
|
||||||
|
|
||||||
execute(req1);
|
execute(req1);
|
||||||
final ClassicHttpResponse result = execute(req2);
|
final ClassicHttpResponse result = execute(req2);
|
||||||
verifyMocks();
|
|
||||||
|
|
||||||
assertEquals(HttpStatus.SC_NOT_MODIFIED, result.getCode());
|
assertEquals(HttpStatus.SC_NOT_MODIFIED, result.getCode());
|
||||||
boolean warning110Found = false;
|
boolean warning110Found = false;
|
||||||
|
@ -472,8 +509,10 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assertTrue(warning110Found);
|
assertTrue(warning110Found);
|
||||||
}
|
|
||||||
|
|
||||||
|
Mockito.verify(mockExecChain, Mockito.atLeastOnce()).proceed(Mockito.any(), Mockito.any());
|
||||||
|
Mockito.verify(mockExecChain, Mockito.atMost(2)).proceed(Mockito.any(), Mockito.any());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testStaleWhileRevalidateYieldsToMustRevalidate()
|
public void testStaleWhileRevalidateYieldsToMustRevalidate()
|
||||||
|
@ -495,20 +534,19 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
|
||||||
resp1.setHeader("ETag","\"etag\"");
|
resp1.setHeader("ETag","\"etag\"");
|
||||||
resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
|
resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp1);
|
|
||||||
|
|
||||||
final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
|
final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
|
||||||
final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
|
final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
|
||||||
resp2.setHeader("Cache-Control", "public, max-age=5, stale-while-revalidate=15, must-revalidate");
|
resp2.setHeader("Cache-Control", "public, max-age=5, stale-while-revalidate=15, must-revalidate");
|
||||||
resp2.setHeader("ETag","\"etag\"");
|
resp2.setHeader("ETag","\"etag\"");
|
||||||
resp2.setHeader("Date", DateUtils.formatDate(now));
|
resp2.setHeader("Date", DateUtils.formatDate(now));
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp2);
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
execute(req1);
|
execute(req1);
|
||||||
|
|
||||||
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
|
||||||
|
|
||||||
final ClassicHttpResponse result = execute(req2);
|
final ClassicHttpResponse result = execute(req2);
|
||||||
verifyMocks();
|
|
||||||
|
|
||||||
assertEquals(HttpStatus.SC_OK, result.getCode());
|
assertEquals(HttpStatus.SC_OK, result.getCode());
|
||||||
boolean warning110Found = false;
|
boolean warning110Found = false;
|
||||||
|
@ -544,20 +582,19 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
|
||||||
resp1.setHeader("ETag","\"etag\"");
|
resp1.setHeader("ETag","\"etag\"");
|
||||||
resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
|
resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp1);
|
|
||||||
|
|
||||||
final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
|
final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
|
||||||
final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
|
final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
|
||||||
resp2.setHeader("Cache-Control", "public, max-age=5, stale-while-revalidate=15, proxy-revalidate");
|
resp2.setHeader("Cache-Control", "public, max-age=5, stale-while-revalidate=15, proxy-revalidate");
|
||||||
resp2.setHeader("ETag","\"etag\"");
|
resp2.setHeader("ETag","\"etag\"");
|
||||||
resp2.setHeader("Date", DateUtils.formatDate(now));
|
resp2.setHeader("Date", DateUtils.formatDate(now));
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp2);
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
execute(req1);
|
execute(req1);
|
||||||
|
|
||||||
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
|
||||||
|
|
||||||
final ClassicHttpResponse result = execute(req2);
|
final ClassicHttpResponse result = execute(req2);
|
||||||
verifyMocks();
|
|
||||||
|
|
||||||
assertEquals(HttpStatus.SC_OK, result.getCode());
|
assertEquals(HttpStatus.SC_OK, result.getCode());
|
||||||
boolean warning110Found = false;
|
boolean warning110Found = false;
|
||||||
|
@ -593,8 +630,6 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
|
||||||
resp1.setHeader("ETag","\"etag\"");
|
resp1.setHeader("ETag","\"etag\"");
|
||||||
resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
|
resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp1);
|
|
||||||
|
|
||||||
final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
|
final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
|
||||||
req2.setHeader("Cache-Control","min-fresh=2");
|
req2.setHeader("Cache-Control","min-fresh=2");
|
||||||
final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
|
final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
|
||||||
|
@ -602,12 +637,13 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
|
||||||
resp2.setHeader("ETag","\"etag\"");
|
resp2.setHeader("ETag","\"etag\"");
|
||||||
resp2.setHeader("Date", DateUtils.formatDate(now));
|
resp2.setHeader("Date", DateUtils.formatDate(now));
|
||||||
|
|
||||||
backendExpectsAnyRequestAndReturn(resp2);
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
|
||||||
|
|
||||||
replayMocks();
|
|
||||||
execute(req1);
|
execute(req1);
|
||||||
|
|
||||||
|
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
|
||||||
|
|
||||||
final ClassicHttpResponse result = execute(req2);
|
final ClassicHttpResponse result = execute(req2);
|
||||||
verifyMocks();
|
|
||||||
|
|
||||||
assertEquals(HttpStatus.SC_OK, result.getCode());
|
assertEquals(HttpStatus.SC_OK, result.getCode());
|
||||||
boolean warning110Found = false;
|
boolean warning110Found = false;
|
||||||
|
|
7
pom.xml
7
pom.xml
|
@ -70,7 +70,6 @@
|
||||||
<memcached.version>2.12.3</memcached.version>
|
<memcached.version>2.12.3</memcached.version>
|
||||||
<slf4j.version>1.7.25</slf4j.version>
|
<slf4j.version>1.7.25</slf4j.version>
|
||||||
<junit.version>4.13</junit.version>
|
<junit.version>4.13</junit.version>
|
||||||
<easymock.version>3.6</easymock.version>
|
|
||||||
<mockito.version>3.10.0</mockito.version>
|
<mockito.version>3.10.0</mockito.version>
|
||||||
<jna.version>5.2.0</jna.version>
|
<jna.version>5.2.0</jna.version>
|
||||||
<hc.stylecheck.version>1</hc.stylecheck.version>
|
<hc.stylecheck.version>1</hc.stylecheck.version>
|
||||||
|
@ -189,12 +188,6 @@
|
||||||
<version>${mockito.version}</version>
|
<version>${mockito.version}</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.easymock</groupId>
|
|
||||||
<artifactId>easymock</artifactId>
|
|
||||||
<version>${easymock.version}</version>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</dependencyManagement>
|
</dependencyManagement>
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue