HTTPCLIENT-962: Fixed handling of Authorization headers in shared cache mode
Contributed by Jonathan Moore <jonathan_moore at comcast.com> git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk@960338 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
c85eaa094b
commit
7b21535a49
|
@ -1,6 +1,9 @@
|
|||
Changes since 4.1 ALPHA2
|
||||
-------------------
|
||||
|
||||
* [HTTPCLIENT-962] Fixed handling of Authorization headers in shared cache mode.
|
||||
Contributed by Jonathan Moore <jonathan_moore at comcast.com>
|
||||
|
||||
* [HTTPCLIENT-961] Not all applicable URIs are invalidated on PUT/POST/DELETEs
|
||||
that pass through client cache.
|
||||
Contributed by Jonathan Moore <jonathan_moore at comcast.com>
|
||||
|
|
|
@ -612,4 +612,8 @@ public class CachingHttpClient implements HttpClient {
|
|||
return SUPPORTS_RANGE_AND_CONTENT_RANGE_HEADERS;
|
||||
}
|
||||
|
||||
public boolean isSharedCache() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -153,23 +153,29 @@ public class ResponseCachingPolicy {
|
|||
return false;
|
||||
}
|
||||
|
||||
protected boolean isExplicitlyCacheable(HttpResponse response) {
|
||||
if (response.getFirstHeader(HeaderConstants.EXPIRES) != null)
|
||||
return true;
|
||||
protected boolean hasCacheControlParameterFrom(HttpResponse response, String[] params) {
|
||||
Header[] cacheControlHeaders = response.getHeaders(HeaderConstants.CACHE_CONTROL);
|
||||
for (Header header : cacheControlHeaders) {
|
||||
for (HeaderElement elem : header.getElements()) {
|
||||
if ("max-age".equals(elem.getName()) || "s-maxage".equals(elem.getName())
|
||||
|| "must-revalidate".equals(elem.getName())
|
||||
|| "proxy-revalidate".equals(elem.getName())
|
||||
|| "public".equals(elem.getName())) {
|
||||
return true;
|
||||
for (String param : params) {
|
||||
if (param.equals(elem.getName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean isExplicitlyCacheable(HttpResponse response) {
|
||||
if (response.getFirstHeader(HeaderConstants.EXPIRES) != null)
|
||||
return true;
|
||||
String[] cacheableParams = { "max-age", "s-maxage",
|
||||
"must-revalidate", "proxy-revalidate", "public"
|
||||
};
|
||||
return hasCacheControlParameterFrom(response, cacheableParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the {@link HttpResponse} gotten from the origin is a
|
||||
* cacheable response.
|
||||
|
@ -189,6 +195,14 @@ public class ResponseCachingPolicy {
|
|||
return false;
|
||||
}
|
||||
|
||||
Header[] authNHeaders = request.getHeaders("Authorization");
|
||||
if (authNHeaders != null && authNHeaders.length > 0) {
|
||||
String[] authCacheableParams = {
|
||||
"s-maxage", "must-revalidate", "public"
|
||||
};
|
||||
return hasCacheControlParameterFrom(response, authCacheableParams);
|
||||
}
|
||||
|
||||
String method = request.getRequestLine().getMethod();
|
||||
return isResponseCacheable(method, response);
|
||||
}
|
||||
|
|
|
@ -1054,6 +1054,11 @@ public class TestCachingHttpClient {
|
|||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testIsSharedCache() throws Exception {
|
||||
Assert.assertTrue(impl.isSharedCache());
|
||||
}
|
||||
|
||||
private byte[] readResponse(HttpResponse response) {
|
||||
try {
|
||||
ByteArrayOutputStream s1 = new ByteArrayOutputStream();
|
||||
|
|
|
@ -57,6 +57,7 @@ import org.easymock.IExpectationSetters;
|
|||
import org.easymock.classextension.EasyMock;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
|
@ -2156,9 +2157,8 @@ public class TestProtocolRequirements {
|
|||
originResponse = new BasicHttpResponse(HTTP_1_1, 405, "Method Not Allowed");
|
||||
originResponse.setHeader("Allow", "GET, HEAD");
|
||||
|
||||
EasyMock.expect(
|
||||
mockBackend.execute(EasyMock.isA(HttpHost.class), EasyMock.isA(HttpRequest.class),
|
||||
(HttpContext) EasyMock.isNull())).andReturn(originResponse);
|
||||
backendExpectsAnyRequest().andReturn(originResponse);
|
||||
|
||||
replayMocks();
|
||||
|
||||
HttpResponse result = impl.execute(host, request);
|
||||
|
@ -2589,7 +2589,6 @@ public class TestProtocolRequirements {
|
|||
Date nineSecondsAgo = new Date(now.getTime() - 9 * 1000L);
|
||||
Date eightSecondsAgo = new Date(now.getTime() - 8 * 1000L);
|
||||
|
||||
|
||||
FakeHeaderGroup headerGroup = new FakeHeaderGroup();
|
||||
|
||||
headerGroup.setHeader("Date", DateUtils.formatDate(nineSecondsAgo));
|
||||
|
@ -4506,6 +4505,279 @@ public class TestProtocolRequirements {
|
|||
testUnsafeMethodDoesNotInvalidateCacheForUriInLocationHeadersFromOtherHosts(req);
|
||||
}
|
||||
|
||||
/* "All methods that might be expected to cause modifications to the origin
|
||||
* server's resources MUST be written through to the origin server. This
|
||||
* currently includes all methods except for GET and HEAD. A cache MUST NOT
|
||||
* reply to such a request from a client before having transmitted the
|
||||
* request to the inbound server, and having received a corresponding
|
||||
* response from the inbound server."
|
||||
*
|
||||
* http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.11
|
||||
*/
|
||||
private void testRequestIsWrittenThroughToOrigin(HttpRequest req)
|
||||
throws Exception {
|
||||
HttpResponse resp = new BasicHttpResponse(HTTP_1_1, HttpStatus.SC_NO_CONTENT, "No Content");
|
||||
EasyMock.expect(mockBackend.execute(EasyMock.eq(host),
|
||||
eqRequest(req),
|
||||
(HttpContext)EasyMock.isNull()))
|
||||
.andReturn(resp);
|
||||
|
||||
replayMocks();
|
||||
impl.execute(host, req);
|
||||
verifyMocks();
|
||||
}
|
||||
|
||||
@Test @Ignore
|
||||
public void testOPTIONSRequestsAreWrittenThroughToOrigin()
|
||||
throws Exception {
|
||||
HttpRequest req = new BasicHttpRequest("OPTIONS","*",HTTP_1_1);
|
||||
testRequestIsWrittenThroughToOrigin(req);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPOSTRequestsAreWrittenThroughToOrigin()
|
||||
throws Exception {
|
||||
HttpEntityEnclosingRequest req = new BasicHttpEntityEnclosingRequest("POST","/",HTTP_1_1);
|
||||
req.setEntity(makeBody(128));
|
||||
req.setHeader("Content-Length","128");
|
||||
testRequestIsWrittenThroughToOrigin(req);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPUTRequestsAreWrittenThroughToOrigin()
|
||||
throws Exception {
|
||||
HttpEntityEnclosingRequest req = new BasicHttpEntityEnclosingRequest("PUT","/",HTTP_1_1);
|
||||
req.setEntity(makeBody(128));
|
||||
req.setHeader("Content-Length","128");
|
||||
testRequestIsWrittenThroughToOrigin(req);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDELETERequestsAreWrittenThroughToOrigin()
|
||||
throws Exception {
|
||||
HttpRequest req = new BasicHttpRequest("DELETE","/",HTTP_1_1);
|
||||
testRequestIsWrittenThroughToOrigin(req);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTRACERequestsAreWrittenThroughToOrigin()
|
||||
throws Exception {
|
||||
HttpRequest req = new BasicHttpRequest("TRACE","/",HTTP_1_1);
|
||||
testRequestIsWrittenThroughToOrigin(req);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCONNECTRequestsAreWrittenThroughToOrigin()
|
||||
throws Exception {
|
||||
HttpRequest req = new BasicHttpRequest("CONNECT","/",HTTP_1_1);
|
||||
testRequestIsWrittenThroughToOrigin(req);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnknownMethodRequestsAreWrittenThroughToOrigin()
|
||||
throws Exception {
|
||||
HttpRequest req = new BasicHttpRequest("UNKNOWN","/",HTTP_1_1);
|
||||
testRequestIsWrittenThroughToOrigin(req);
|
||||
}
|
||||
|
||||
/* "If a cache receives a value larger than the largest positive
|
||||
* integer it can represent, or if any of its age calculations
|
||||
* overflows, it MUST transmit an Age header with a value of
|
||||
* 2147483648 (2^31)."
|
||||
*
|
||||
* http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.6
|
||||
*/
|
||||
@Test
|
||||
public void testTransmitsAgeHeaderIfIncomingAgeHeaderTooBig()
|
||||
throws Exception {
|
||||
String reallyOldAge = "1" + Long.MAX_VALUE;
|
||||
originResponse.setHeader("Age",reallyOldAge);
|
||||
|
||||
backendExpectsAnyRequest().andReturn(originResponse);
|
||||
|
||||
replayMocks();
|
||||
HttpResponse result = impl.execute(host,request);
|
||||
verifyMocks();
|
||||
|
||||
Assert.assertEquals("2147483648",
|
||||
result.getFirstHeader("Age").getValue());
|
||||
}
|
||||
|
||||
/* "A proxy MUST NOT modify the Allow header field even if it does not
|
||||
* understand all the methods specified, since the user agent might
|
||||
* have other means of communicating with the origin server.
|
||||
*
|
||||
* http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.7
|
||||
*/
|
||||
@Test
|
||||
public void testDoesNotModifyAllowHeaderWithUnknownMethods()
|
||||
throws Exception {
|
||||
String allowHeaderValue = "GET, HEAD, FOOBAR";
|
||||
originResponse.setHeader("Allow",allowHeaderValue);
|
||||
backendExpectsAnyRequest().andReturn(originResponse);
|
||||
replayMocks();
|
||||
HttpResponse result = impl.execute(host,request);
|
||||
verifyMocks();
|
||||
Assert.assertEquals(HttpTestUtils.getCanonicalHeaderValue(originResponse,"Allow"),
|
||||
HttpTestUtils.getCanonicalHeaderValue(result, "Allow"));
|
||||
}
|
||||
|
||||
/* "When a shared cache (see section 13.7) receives a request
|
||||
* containing an Authorization field, it MUST NOT return the
|
||||
* corresponding response as a reply to any other request, unless one
|
||||
* of the following specific exceptions holds:
|
||||
*
|
||||
* 1. If the response includes the "s-maxage" cache-control
|
||||
* directive, the cache MAY use that response in replying to a
|
||||
* subsequent request. But (if the specified maximum age has
|
||||
* passed) a proxy cache MUST first revalidate it with the origin
|
||||
* server, using the request-headers from the new request to allow
|
||||
* the origin server to authenticate the new request. (This is the
|
||||
* defined behavior for s-maxage.) If the response includes "s-
|
||||
* maxage=0", the proxy MUST always revalidate it before re-using
|
||||
* it.
|
||||
*
|
||||
* 2. If the response includes the "must-revalidate" cache-control
|
||||
* directive, the cache MAY use that response in replying to a
|
||||
* subsequent request. But if the response is stale, all caches
|
||||
* MUST first revalidate it with the origin server, using the
|
||||
* request-headers from the new request to allow the origin server
|
||||
* to authenticate the new request.
|
||||
*
|
||||
* 3. If the response includes the "public" cache-control directive,
|
||||
* it MAY be returned in reply to any subsequent request.
|
||||
*/
|
||||
protected void testSharedCacheRevalidatesAuthorizedResponse(
|
||||
HttpResponse authorizedResponse, int minTimes, int maxTimes) throws Exception,
|
||||
IOException {
|
||||
if (impl.isSharedCache()) {
|
||||
String authorization = "Basic dXNlcjpwYXNzd2Q=";
|
||||
HttpRequest req1 = new BasicHttpRequest("GET","/",HTTP_1_1);
|
||||
req1.setHeader("Authorization",authorization);
|
||||
|
||||
backendExpectsAnyRequest().andReturn(authorizedResponse);
|
||||
|
||||
HttpRequest req2 = new BasicHttpRequest("GET","/",HTTP_1_1);
|
||||
HttpResponse resp2 = make200Response();
|
||||
resp2.setHeader("Cache-Control","max-age=3600");
|
||||
|
||||
if (maxTimes > 0) {
|
||||
// this request MUST happen
|
||||
backendExpectsAnyRequest().andReturn(resp2)
|
||||
.times(minTimes,maxTimes);
|
||||
}
|
||||
|
||||
replayMocks();
|
||||
impl.execute(host, req1);
|
||||
impl.execute(host, req2);
|
||||
verifyMocks();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSharedCacheMustNotNormallyCacheAuthorizedResponses()
|
||||
throws Exception {
|
||||
HttpResponse resp = make200Response();
|
||||
resp.setHeader("Cache-Control","max-age=3600");
|
||||
resp.setHeader("ETag","\"etag\"");
|
||||
testSharedCacheRevalidatesAuthorizedResponse(resp, 1, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSharedCacheMayCacheAuthorizedResponsesWithSMaxAgeHeader()
|
||||
throws Exception {
|
||||
HttpResponse resp = make200Response();
|
||||
resp.setHeader("Cache-Control","s-maxage=3600");
|
||||
resp.setHeader("ETag","\"etag\"");
|
||||
testSharedCacheRevalidatesAuthorizedResponse(resp, 0, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSharedCacheMustRevalidateAuthorizedResponsesWhenSMaxAgeIsZero()
|
||||
throws Exception {
|
||||
HttpResponse resp = make200Response();
|
||||
resp.setHeader("Cache-Control","s-maxage=0");
|
||||
resp.setHeader("ETag","\"etag\"");
|
||||
testSharedCacheRevalidatesAuthorizedResponse(resp, 1, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSharedCacheMayCacheAuthorizedResponsesWithMustRevalidate()
|
||||
throws Exception {
|
||||
HttpResponse resp = make200Response();
|
||||
resp.setHeader("Cache-Control","must-revalidate");
|
||||
resp.setHeader("ETag","\"etag\"");
|
||||
testSharedCacheRevalidatesAuthorizedResponse(resp, 0, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSharedCacheMayCacheAuthorizedResponsesWithCacheControlPublic()
|
||||
throws Exception {
|
||||
HttpResponse resp = make200Response();
|
||||
resp.setHeader("Cache-Control","public");
|
||||
testSharedCacheRevalidatesAuthorizedResponse(resp, 0, 1);
|
||||
}
|
||||
|
||||
protected void testSharedCacheMustUseNewRequestHeadersWhenRevalidatingAuthorizedResponse(
|
||||
HttpResponse authorizedResponse) throws Exception, IOException,
|
||||
ClientProtocolException {
|
||||
if (impl.isSharedCache()) {
|
||||
String authorization1 = "Basic dXNlcjpwYXNzd2Q=";
|
||||
String authorization2 = "Basic dXNlcjpwYXNzd2Qy";
|
||||
|
||||
HttpRequest req1 = new BasicHttpRequest("GET","/",HTTP_1_1);
|
||||
req1.setHeader("Authorization",authorization1);
|
||||
|
||||
backendExpectsAnyRequest().andReturn(authorizedResponse);
|
||||
|
||||
HttpRequest req2 = new BasicHttpRequest("GET","/",HTTP_1_1);
|
||||
req2.setHeader("Authorization",authorization2);
|
||||
|
||||
HttpResponse resp2 = make200Response();
|
||||
|
||||
Capture<HttpRequest> cap = new Capture<HttpRequest>();
|
||||
EasyMock.expect(mockBackend.execute(EasyMock.eq(host),
|
||||
EasyMock.capture(cap),
|
||||
(HttpContext)EasyMock.isNull()))
|
||||
.andReturn(resp2);
|
||||
|
||||
replayMocks();
|
||||
impl.execute(host,req1);
|
||||
impl.execute(host,req2);
|
||||
verifyMocks();
|
||||
|
||||
HttpRequest captured = cap.getValue();
|
||||
Assert.assertEquals(HttpTestUtils.getCanonicalHeaderValue(req2, "Authorization"),
|
||||
HttpTestUtils.getCanonicalHeaderValue(captured, "Authorization"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSharedCacheMustUseNewRequestHeadersWhenRevalidatingAuthorizedResponsesWithSMaxAge()
|
||||
throws Exception {
|
||||
Date now = new Date();
|
||||
Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
|
||||
HttpResponse resp1 = make200Response();
|
||||
resp1.setHeader("Date",DateUtils.formatDate(tenSecondsAgo));
|
||||
resp1.setHeader("ETag","\"etag\"");
|
||||
resp1.setHeader("Cache-Control","s-maxage=5");
|
||||
|
||||
testSharedCacheMustUseNewRequestHeadersWhenRevalidatingAuthorizedResponse(resp1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSharedCacheMustUseNewRequestHeadersWhenRevalidatingAuthorizedResponsesWithMustRevalidate()
|
||||
throws Exception {
|
||||
Date now = new Date();
|
||||
Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
|
||||
HttpResponse resp1 = make200Response();
|
||||
resp1.setHeader("Date",DateUtils.formatDate(tenSecondsAgo));
|
||||
resp1.setHeader("ETag","\"etag\"");
|
||||
resp1.setHeader("Cache-Control","maxage=5, must-revalidate");
|
||||
|
||||
testSharedCacheMustUseNewRequestHeadersWhenRevalidatingAuthorizedResponse(resp1);
|
||||
}
|
||||
|
||||
private class FakeHeaderGroup extends HeaderGroup{
|
||||
|
||||
public void addHeader(String name, String value){
|
||||
|
|
|
@ -43,7 +43,7 @@ import org.junit.Test;
|
|||
|
||||
public class TestResponseCachingPolicy {
|
||||
|
||||
private static final ProtocolVersion PROTOCOL_VERSION = new ProtocolVersion("HTTP", 1, 1);
|
||||
private static final ProtocolVersion HTTP_1_1 = new ProtocolVersion("HTTP", 1, 1);
|
||||
private ResponseCachingPolicy policy;
|
||||
private HttpResponse response;
|
||||
private HttpRequest request;
|
||||
|
@ -55,7 +55,7 @@ public class TestResponseCachingPolicy {
|
|||
public void setUp() throws Exception {
|
||||
policy = new ResponseCachingPolicy(0);
|
||||
response = new BasicHttpResponse(
|
||||
new BasicStatusLine(PROTOCOL_VERSION, HttpStatus.SC_OK, ""));
|
||||
new BasicStatusLine(HTTP_1_1, HttpStatus.SC_OK, ""));
|
||||
response.setHeader("Date", DateUtils.formatDate(new Date()));
|
||||
response.setHeader("Content-Length", "0");
|
||||
}
|
||||
|
@ -65,6 +65,45 @@ public class TestResponseCachingPolicy {
|
|||
Assert.assertTrue(policy.isResponseCacheable("GET", response));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResponsesToRequestsWithAuthorizationHeadersAreNotCacheable() {
|
||||
request = new BasicHttpRequest("GET","/",HTTP_1_1);
|
||||
request.setHeader("Authorization","Basic dXNlcjpwYXNzd2Q=");
|
||||
Assert.assertFalse(policy.isResponseCacheable(request,response));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAuthorizedResponsesWithSMaxAgeAreCacheable() {
|
||||
request = new BasicHttpRequest("GET","/",HTTP_1_1);
|
||||
request.setHeader("Authorization","Basic dXNlcjpwYXNzd2Q=");
|
||||
response.setHeader("Cache-Control","s-maxage=3600");
|
||||
Assert.assertTrue(policy.isResponseCacheable(request,response));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAuthorizedResponsesWithMustRevalidateAreCacheable() {
|
||||
request = new BasicHttpRequest("GET","/",HTTP_1_1);
|
||||
request.setHeader("Authorization","Basic dXNlcjpwYXNzd2Q=");
|
||||
response.setHeader("Cache-Control","must-revalidate");
|
||||
Assert.assertTrue(policy.isResponseCacheable(request,response));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAuthorizedResponsesWithCacheControlPublicAreCacheable() {
|
||||
request = new BasicHttpRequest("GET","/",HTTP_1_1);
|
||||
request.setHeader("Authorization","Basic dXNlcjpwYXNzd2Q=");
|
||||
response.setHeader("Cache-Control","public");
|
||||
Assert.assertTrue(policy.isResponseCacheable(request,response));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAuthorizedResponsesWithCacheControlMaxAgeAreNotCacheable() {
|
||||
request = new BasicHttpRequest("GET","/",HTTP_1_1);
|
||||
request.setHeader("Authorization","Basic dXNlcjpwYXNzd2Q=");
|
||||
response.setHeader("Cache-Control","max-age=3600");
|
||||
Assert.assertFalse(policy.isResponseCacheable(request,response));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test203ResponseCodeIsCacheable() {
|
||||
response.setStatusCode(HttpStatus.SC_NON_AUTHORITATIVE_INFORMATION);
|
||||
|
@ -219,7 +258,7 @@ public class TestResponseCachingPolicy {
|
|||
Assert.assertTrue(policy.isResponseCacheable("GET", response));
|
||||
|
||||
response = new BasicHttpResponse(
|
||||
new BasicStatusLine(PROTOCOL_VERSION, HttpStatus.SC_OK, ""));
|
||||
new BasicStatusLine(HTTP_1_1, HttpStatus.SC_OK, ""));
|
||||
response.setHeader("Date", DateUtils.formatDate(new Date()));
|
||||
response.addHeader("Cache-Control", "no-transform");
|
||||
response.setHeader("Content-Length", "0");
|
||||
|
@ -229,12 +268,12 @@ public class TestResponseCachingPolicy {
|
|||
|
||||
@Test
|
||||
public void testIsGetWithout200Cacheable() {
|
||||
HttpResponse response = new BasicHttpResponse(new BasicStatusLine(PROTOCOL_VERSION,
|
||||
HttpResponse response = new BasicHttpResponse(new BasicStatusLine(HTTP_1_1,
|
||||
HttpStatus.SC_NOT_FOUND, ""));
|
||||
|
||||
Assert.assertFalse(policy.isResponseCacheable("GET", response));
|
||||
|
||||
response = new BasicHttpResponse(new BasicStatusLine(PROTOCOL_VERSION,
|
||||
response = new BasicHttpResponse(new BasicStatusLine(HTTP_1_1,
|
||||
HttpStatus.SC_GATEWAY_TIMEOUT, ""));
|
||||
|
||||
Assert.assertFalse(policy.isResponseCacheable("GET", response));
|
||||
|
|
Loading…
Reference in New Issue