Removed dependency on classic (blocking) I/O APIs from RequestProtocolCompliance and ResponseProtocolCompliance; removed compliance checks unrelated to HTTP caching enforced by protocol handlers
This commit is contained in:
parent
d75c5c2be2
commit
0226eaff6d
|
@ -168,4 +168,35 @@ class CachedHttpResponseGenerator {
|
|||
return request.getMethod().equals(HeaderConstants.GET_METHOD) && cacheEntry.getResource() != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract error information about the {@link HttpRequest} telling the 'caller'
|
||||
* that a problem occured.
|
||||
*
|
||||
* @param errorCheck What type of error should I get
|
||||
* @return The {@link ClassicHttpResponse} that is the error generated
|
||||
*/
|
||||
public ClassicHttpResponse getErrorForRequest(final RequestProtocolError errorCheck) {
|
||||
switch (errorCheck) {
|
||||
case BODY_BUT_NO_LENGTH_ERROR:
|
||||
return new BasicClassicHttpResponse(HttpStatus.SC_LENGTH_REQUIRED, "");
|
||||
|
||||
case WEAK_ETAG_AND_RANGE_ERROR:
|
||||
return new BasicClassicHttpResponse(HttpStatus.SC_BAD_REQUEST,
|
||||
"Weak eTag not compatible with byte range");
|
||||
|
||||
case WEAK_ETAG_ON_PUTDELETE_METHOD_ERROR:
|
||||
return new BasicClassicHttpResponse(HttpStatus.SC_BAD_REQUEST,
|
||||
"Weak eTag not compatible with PUT or DELETE requests");
|
||||
|
||||
case NO_CACHE_DIRECTIVE_WITH_FIELD_NAME:
|
||||
return new BasicClassicHttpResponse(HttpStatus.SC_BAD_REQUEST,
|
||||
"No-Cache directive MUST NOT include a field name");
|
||||
|
||||
default:
|
||||
throw new IllegalStateException(
|
||||
"The request was compliant, therefore no error can be generated for it.");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -360,7 +360,7 @@ public class CachingExec implements ExecChainHandler {
|
|||
|
||||
for (final RequestProtocolError error : fatalError) {
|
||||
setResponseStatus(context, CacheResponseStatus.CACHE_MODULE_RESPONSE);
|
||||
fatalErrorResponse = requestCompliance.getErrorForRequest(error);
|
||||
fatalErrorResponse = responseGenerator.getErrorForRequest(error);
|
||||
}
|
||||
return fatalErrorResponse;
|
||||
}
|
||||
|
|
|
@ -31,25 +31,14 @@ import java.util.Arrays;
|
|||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.hc.client5.http.ClientProtocolException;
|
||||
import org.apache.hc.client5.http.cache.HeaderConstants;
|
||||
import org.apache.hc.core5.annotation.Contract;
|
||||
import org.apache.hc.core5.annotation.ThreadingBehavior;
|
||||
import org.apache.hc.core5.http.ClassicHttpRequest;
|
||||
import org.apache.hc.core5.http.ClassicHttpResponse;
|
||||
import org.apache.hc.core5.http.ContentType;
|
||||
import org.apache.hc.core5.http.Header;
|
||||
import org.apache.hc.core5.http.HeaderElement;
|
||||
import org.apache.hc.core5.http.HeaderElements;
|
||||
import org.apache.hc.core5.http.HttpEntity;
|
||||
import org.apache.hc.core5.http.HttpHeaders;
|
||||
import org.apache.hc.core5.http.HttpRequest;
|
||||
import org.apache.hc.core5.http.HttpStatus;
|
||||
import org.apache.hc.core5.http.HttpVersion;
|
||||
import org.apache.hc.core5.http.ProtocolVersion;
|
||||
import org.apache.hc.core5.http.io.entity.HttpEntityWrapper;
|
||||
import org.apache.hc.core5.http.message.BasicClassicHttpResponse;
|
||||
import org.apache.hc.core5.http.message.BasicHeader;
|
||||
import org.apache.hc.core5.http.message.MessageSupport;
|
||||
|
||||
/**
|
||||
|
@ -107,17 +96,8 @@ class RequestProtocolCompliance {
|
|||
* fix the request here.
|
||||
*
|
||||
* @param request the request to check for compliance
|
||||
* @throws ClientProtocolException when we have trouble making the request compliant
|
||||
*/
|
||||
public void makeRequestCompliant(final ClassicHttpRequest request)
|
||||
throws ClientProtocolException {
|
||||
|
||||
if (requestMustNotHaveEntity(request)) {
|
||||
request.setEntity(null);
|
||||
}
|
||||
|
||||
verifyRequestWithExpectContinueFlagHas100continueHeader(request);
|
||||
verifyOPTIONSRequestWithBodyHasContentType(request);
|
||||
public void makeRequestCompliant(final HttpRequest request) {
|
||||
decrementOPTIONSMaxForwardsIfGreaterThen0(request);
|
||||
stripOtherFreshnessDirectivesWithNoCache(request);
|
||||
|
||||
|
@ -160,10 +140,6 @@ class RequestProtocolCompliance {
|
|||
return newHdr.toString();
|
||||
}
|
||||
|
||||
private boolean requestMustNotHaveEntity(final HttpRequest request) {
|
||||
return HeaderConstants.TRACE_METHOD.equals(request.getMethod());
|
||||
}
|
||||
|
||||
private void decrementOPTIONSMaxForwardsIfGreaterThen0(final HttpRequest request) {
|
||||
if (!HeaderConstants.OPTIONS_METHOD.equals(request.getMethod())) {
|
||||
return;
|
||||
|
@ -180,81 +156,6 @@ class RequestProtocolCompliance {
|
|||
request.setHeader(HeaderConstants.MAX_FORWARDS, Integer.toString(currentMaxForwards - 1));
|
||||
}
|
||||
|
||||
private void verifyOPTIONSRequestWithBodyHasContentType(final ClassicHttpRequest request) {
|
||||
if (!HeaderConstants.OPTIONS_METHOD.equals(request.getMethod())) {
|
||||
return;
|
||||
}
|
||||
|
||||
addContentTypeHeaderIfMissing(request);
|
||||
}
|
||||
|
||||
private void addContentTypeHeaderIfMissing(final ClassicHttpRequest request) {
|
||||
final HttpEntity entity = request.getEntity();
|
||||
if (entity != null && entity.getContentType() == null) {
|
||||
final HttpEntityWrapper entityWrapper = new HttpEntityWrapper(entity) {
|
||||
|
||||
@Override
|
||||
public String getContentType() {
|
||||
return ContentType.APPLICATION_OCTET_STREAM.getMimeType();
|
||||
}
|
||||
|
||||
};
|
||||
request.setEntity(entityWrapper);
|
||||
}
|
||||
}
|
||||
|
||||
private void verifyRequestWithExpectContinueFlagHas100continueHeader(final ClassicHttpRequest request) {
|
||||
if (request.containsHeader(HttpHeaders.EXPECT) && request.getEntity() != null) {
|
||||
add100ContinueHeaderIfMissing(request);
|
||||
} else {
|
||||
remove100ContinueHeaderIfExists(request);
|
||||
}
|
||||
}
|
||||
|
||||
private void remove100ContinueHeaderIfExists(final HttpRequest request) {
|
||||
boolean hasHeader = false;
|
||||
|
||||
final Header[] expectHeaders = request.getHeaders(HttpHeaders.EXPECT);
|
||||
List<HeaderElement> expectElementsThatAreNot100Continue = new ArrayList<>();
|
||||
|
||||
for (final Header h : expectHeaders) {
|
||||
for (final HeaderElement elt : MessageSupport.parse(h)) {
|
||||
if (!(HeaderElements.CONTINUE.equalsIgnoreCase(elt.getName()))) {
|
||||
expectElementsThatAreNot100Continue.add(elt);
|
||||
} else {
|
||||
hasHeader = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasHeader) {
|
||||
request.removeHeader(h);
|
||||
for (final HeaderElement elt : expectElementsThatAreNot100Continue) {
|
||||
final BasicHeader newHeader = new BasicHeader(HeaderElements.CONTINUE, elt.getName());
|
||||
request.addHeader(newHeader);
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
expectElementsThatAreNot100Continue = new ArrayList<>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void add100ContinueHeaderIfMissing(final HttpRequest request) {
|
||||
boolean hasHeader = false;
|
||||
|
||||
final Iterator<HeaderElement> it = MessageSupport.iterate(request, HttpHeaders.EXPECT);
|
||||
while (it.hasNext()) {
|
||||
final HeaderElement elt = it.next();
|
||||
if (HeaderElements.CONTINUE.equalsIgnoreCase(elt.getName())) {
|
||||
hasHeader = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasHeader) {
|
||||
request.addHeader(HttpHeaders.EXPECT, HeaderElements.CONTINUE);
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean requestMinorVersionIsTooHighMajorVersionsMatch(final HttpRequest request) {
|
||||
final ProtocolVersion requestProtocol = request.getVersion();
|
||||
if (requestProtocol == null) {
|
||||
|
@ -276,37 +177,6 @@ class RequestProtocolCompliance {
|
|||
return requestProtocol != null && requestProtocol.compareToVersion(HttpVersion.HTTP_1_1) < 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract error information about the {@link HttpRequest} telling the 'caller'
|
||||
* that a problem occured.
|
||||
*
|
||||
* @param errorCheck What type of error should I get
|
||||
* @return The {@link ClassicHttpResponse} that is the error generated
|
||||
*/
|
||||
public ClassicHttpResponse getErrorForRequest(final RequestProtocolError errorCheck) {
|
||||
switch (errorCheck) {
|
||||
case BODY_BUT_NO_LENGTH_ERROR:
|
||||
return new BasicClassicHttpResponse(HttpStatus.SC_LENGTH_REQUIRED, "");
|
||||
|
||||
case WEAK_ETAG_AND_RANGE_ERROR:
|
||||
return new BasicClassicHttpResponse(HttpStatus.SC_BAD_REQUEST,
|
||||
"Weak eTag not compatible with byte range");
|
||||
|
||||
case WEAK_ETAG_ON_PUTDELETE_METHOD_ERROR:
|
||||
return new BasicClassicHttpResponse(HttpStatus.SC_BAD_REQUEST,
|
||||
"Weak eTag not compatible with PUT or DELETE requests");
|
||||
|
||||
case NO_CACHE_DIRECTIVE_WITH_FIELD_NAME:
|
||||
return new BasicClassicHttpResponse(HttpStatus.SC_BAD_REQUEST,
|
||||
"No-Cache directive MUST NOT include a field name");
|
||||
|
||||
default:
|
||||
throw new IllegalStateException(
|
||||
"The request was compliant, therefore no error can be generated for it.");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private RequestProtocolError requestHasWeakETagAndRange(final HttpRequest request) {
|
||||
// TODO: Should these be looking at all the headers marked as Range?
|
||||
final String method = request.getMethod();
|
||||
|
|
|
@ -36,12 +36,9 @@ import org.apache.hc.client5.http.cache.HeaderConstants;
|
|||
import org.apache.hc.client5.http.utils.DateUtils;
|
||||
import org.apache.hc.core5.annotation.Contract;
|
||||
import org.apache.hc.core5.annotation.ThreadingBehavior;
|
||||
import org.apache.hc.core5.http.ClassicHttpRequest;
|
||||
import org.apache.hc.core5.http.ClassicHttpResponse;
|
||||
import org.apache.hc.core5.http.Header;
|
||||
import org.apache.hc.core5.http.HeaderElement;
|
||||
import org.apache.hc.core5.http.HeaderElements;
|
||||
import org.apache.hc.core5.http.HttpEntity;
|
||||
import org.apache.hc.core5.http.HttpHeaders;
|
||||
import org.apache.hc.core5.http.HttpRequest;
|
||||
import org.apache.hc.core5.http.HttpResponse;
|
||||
|
@ -72,14 +69,9 @@ class ResponseProtocolCompliance {
|
|||
* @throws IOException Bad things happened
|
||||
*/
|
||||
public void ensureProtocolCompliance(
|
||||
final ClassicHttpRequest originalRequest,
|
||||
final ClassicHttpRequest request,
|
||||
final ClassicHttpResponse response) throws IOException {
|
||||
if (backendResponseMustNotHaveBody(request, response)) {
|
||||
consumeBody(response);
|
||||
response.setEntity(null);
|
||||
}
|
||||
|
||||
final HttpRequest originalRequest,
|
||||
final HttpRequest request,
|
||||
final HttpResponse response) throws IOException {
|
||||
requestDidNotExpect100ContinueButResponseIsOne(originalRequest, response);
|
||||
|
||||
transferEncodingIsNotReturnedTo1_0Client(originalRequest, response);
|
||||
|
@ -97,13 +89,6 @@ class ResponseProtocolCompliance {
|
|||
warningsWithNonMatchingWarnDatesAreRemoved(response);
|
||||
}
|
||||
|
||||
private void consumeBody(final ClassicHttpResponse response) throws IOException {
|
||||
final HttpEntity body = response.getEntity();
|
||||
if (body != null) {
|
||||
IOUtils.consume(body);
|
||||
}
|
||||
}
|
||||
|
||||
private void warningsWithNonMatchingWarnDatesAreRemoved(
|
||||
final HttpResponse response) {
|
||||
final Date responseDate = DateUtils.parseDate(response.getFirstHeader(HttpHeaders.DATE).getValue());
|
||||
|
@ -180,13 +165,11 @@ class ResponseProtocolCompliance {
|
|||
}
|
||||
|
||||
private void ensurePartialContentIsNotSentToAClientThatDidNotRequestIt(final HttpRequest request,
|
||||
final ClassicHttpResponse response) throws IOException {
|
||||
final HttpResponse response) throws IOException {
|
||||
if (request.getFirstHeader(HeaderConstants.RANGE) != null
|
||||
|| response.getCode() != HttpStatus.SC_PARTIAL_CONTENT) {
|
||||
return;
|
||||
}
|
||||
|
||||
consumeBody(response);
|
||||
throw new ClientProtocolException(UNEXPECTED_PARTIAL_CONTENT);
|
||||
}
|
||||
|
||||
|
@ -217,15 +200,8 @@ class ResponseProtocolCompliance {
|
|||
}
|
||||
}
|
||||
|
||||
private boolean backendResponseMustNotHaveBody(final HttpRequest request, final HttpResponse backendResponse) {
|
||||
return HeaderConstants.HEAD_METHOD.equals(request.getMethod())
|
||||
|| backendResponse.getCode() == HttpStatus.SC_NO_CONTENT
|
||||
|| backendResponse.getCode() == HttpStatus.SC_RESET_CONTENT
|
||||
|| backendResponse.getCode() == HttpStatus.SC_NOT_MODIFIED;
|
||||
}
|
||||
|
||||
private void requestDidNotExpect100ContinueButResponseIsOne(
|
||||
final ClassicHttpRequest originalRequest, final ClassicHttpResponse response) throws IOException {
|
||||
final HttpRequest originalRequest, final HttpResponse response) throws IOException {
|
||||
if (response.getCode() != HttpStatus.SC_CONTINUE) {
|
||||
return;
|
||||
}
|
||||
|
@ -234,12 +210,11 @@ class ResponseProtocolCompliance {
|
|||
if (header != null && header.getValue().equalsIgnoreCase(HeaderElements.CONTINUE)) {
|
||||
return;
|
||||
}
|
||||
consumeBody(response);
|
||||
throw new ClientProtocolException(UNEXPECTED_100_CONTINUE);
|
||||
}
|
||||
|
||||
private void transferEncodingIsNotReturnedTo1_0Client(
|
||||
final ClassicHttpRequest originalRequest, final HttpResponse response) {
|
||||
final HttpRequest originalRequest, final HttpResponse response) {
|
||||
final ProtocolVersion version = originalRequest.getVersion() != null ? originalRequest.getVersion() : HttpVersion.DEFAULT;
|
||||
if (version.compareToVersion(HttpVersion.HTTP_1_1) >= 0) {
|
||||
return;
|
||||
|
|
|
@ -49,7 +49,6 @@ import java.util.ArrayList;
|
|||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.hc.client5.http.ClientProtocolException;
|
||||
import org.apache.hc.client5.http.HttpRoute;
|
||||
import org.apache.hc.client5.http.cache.CacheResponseStatus;
|
||||
import org.apache.hc.client5.http.cache.HttpCacheContext;
|
||||
|
@ -359,27 +358,6 @@ public abstract class TestCachingExecChain {
|
|||
verifyMocks();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNonCompliantRequestWrapsAndReThrowsProtocolException() throws Exception {
|
||||
|
||||
final ClientProtocolException expected = new ClientProtocolException("ouch");
|
||||
|
||||
requestIsFatallyNonCompliant(null);
|
||||
mockRequestProtocolCompliance.makeRequestCompliant((ClassicHttpRequest) anyObject());
|
||||
expectLastCall().andThrow(expected);
|
||||
|
||||
boolean gotException = false;
|
||||
replayMocks();
|
||||
try {
|
||||
execute(request);
|
||||
} catch (final ClientProtocolException ex) {
|
||||
Assert.assertSame(expected, ex);
|
||||
gotException = true;
|
||||
}
|
||||
verifyMocks();
|
||||
Assert.assertTrue(gotException);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetsModuleGeneratedResponseContextForCacheOptionsResponse() throws Exception {
|
||||
impl = createCachingExecChain(new BasicHttpCache(), CacheConfig.DEFAULT);
|
||||
|
|
|
@ -254,36 +254,6 @@ public class TestProtocolDeviations {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* "If the OPTIONS request includes an entity-body (as indicated by the
|
||||
* presence of Content-Length or Transfer-Encoding), then the media type
|
||||
* MUST be indicated by a Content-Type field."
|
||||
*
|
||||
* http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.2
|
||||
*/
|
||||
@Test
|
||||
public void testOPTIONSRequestsWithBodiesAndNoContentTypeHaveOneSupplied() throws Exception {
|
||||
final ClassicHttpRequest options = new BasicClassicHttpRequest("OPTIONS", "/");
|
||||
options.setEntity(body);
|
||||
options.setHeader("Content-Length", "1");
|
||||
|
||||
final Capture<ClassicHttpRequest> reqCap = new Capture<>();
|
||||
EasyMock.expect(
|
||||
mockExecChain.proceed(
|
||||
EasyMock.capture(reqCap),
|
||||
EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
|
||||
replayMocks();
|
||||
|
||||
execute(options);
|
||||
|
||||
verifyMocks();
|
||||
|
||||
final ClassicHttpRequest reqWithBody = reqCap.getValue();
|
||||
final HttpEntity reqBody = reqWithBody.getEntity();
|
||||
Assert.assertNotNull(reqBody);
|
||||
Assert.assertNotNull(reqBody.getContentType());
|
||||
}
|
||||
|
||||
/*
|
||||
* "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
|
||||
|
|
|
@ -641,39 +641,6 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
|
|||
Assert.assertFalse(foundExpect);
|
||||
}
|
||||
|
||||
/*
|
||||
* "A client MUST NOT send an Expect request-header field (section 14.20)
|
||||
* with the '100-continue' expectation if it does not intend to send a
|
||||
* request body."
|
||||
*
|
||||
* http://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html#sec8.2.3
|
||||
*/
|
||||
@Test
|
||||
public void testExpect100ContinueIsNotSentIfThereIsNoRequestBody() throws Exception {
|
||||
request.addHeader("Expect", "100-continue");
|
||||
final Capture<ClassicHttpRequest> reqCap = new Capture<>();
|
||||
EasyMock.expect(
|
||||
mockExecChain.proceed(
|
||||
EasyMock.capture(reqCap),
|
||||
EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
|
||||
|
||||
replayMocks();
|
||||
execute(request);
|
||||
verifyMocks();
|
||||
final ClassicHttpRequest forwarded = reqCap.getValue();
|
||||
boolean foundExpectContinue = false;
|
||||
|
||||
final Iterator<HeaderElement> it = MessageSupport.iterate(forwarded, HttpHeaders.EXPECT);
|
||||
while (it.hasNext()) {
|
||||
final HeaderElement elt = it.next();
|
||||
if ("100-continue".equalsIgnoreCase(elt.getName())) {
|
||||
foundExpectContinue = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Assert.assertFalse(foundExpectContinue);
|
||||
}
|
||||
|
||||
/*
|
||||
* "If a proxy receives a request that includes an Expect request- header
|
||||
* field with the '100-continue' expectation, and the proxy either knows
|
||||
|
@ -1030,32 +997,6 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
|
|||
verifyMocks();
|
||||
}
|
||||
|
||||
/*
|
||||
* "A TRACE request MUST NOT include an entity."
|
||||
*
|
||||
* http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.8
|
||||
*/
|
||||
@Test
|
||||
public void testForwardedTRACERequestsDoNotIncludeAnEntity() throws Exception {
|
||||
final BasicClassicHttpRequest trace = new BasicClassicHttpRequest("TRACE", "/");
|
||||
trace.setEntity(HttpTestUtils.makeBody(entityLength));
|
||||
trace.setHeader("Content-Length", Integer.toString(entityLength));
|
||||
|
||||
final Capture<ClassicHttpRequest> reqCap = new Capture<>();
|
||||
|
||||
EasyMock.expect(
|
||||
mockExecChain.proceed(
|
||||
EasyMock.capture(reqCap),
|
||||
EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
|
||||
|
||||
replayMocks();
|
||||
execute(trace);
|
||||
verifyMocks();
|
||||
|
||||
final ClassicHttpRequest bodyReq = reqCap.getValue();
|
||||
Assert.assertTrue(bodyReq.getEntity() == null || bodyReq.getEntity().getContentLength() == 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* "9.8 TRACE ... Responses to this method MUST NOT be cached."
|
||||
*
|
||||
|
@ -1101,32 +1042,6 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
|
|||
final ClassicHttpResponse result = execute(request);
|
||||
|
||||
verifyMocks();
|
||||
|
||||
Assert.assertTrue(result.getEntity() == null || result.getEntity().getContentLength() == 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* "10.2.6 205 Reset Content ... The response MUST NOT include an entity."
|
||||
*
|
||||
* http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.6
|
||||
*/
|
||||
@Test
|
||||
public void test205ResponsesDoNotContainMessageBodies() throws Exception {
|
||||
originResponse = new BasicClassicHttpResponse(HttpStatus.SC_RESET_CONTENT, "Reset Content");
|
||||
originResponse.setEntity(HttpTestUtils.makeBody(entityLength));
|
||||
|
||||
EasyMock.expect(
|
||||
mockExecChain.proceed(
|
||||
EasyMock.isA(ClassicHttpRequest.class),
|
||||
EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse);
|
||||
|
||||
replayMocks();
|
||||
|
||||
final ClassicHttpResponse result = execute(request);
|
||||
|
||||
verifyMocks();
|
||||
|
||||
Assert.assertTrue(result.getEntity() == null || result.getEntity().getContentLength() == 0);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1704,8 +1619,6 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
|
|||
final ClassicHttpResponse result = execute(request);
|
||||
|
||||
verifyMocks();
|
||||
|
||||
Assert.assertTrue(result.getEntity() == null || result.getEntity().getContentLength() == 0);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -27,33 +27,30 @@
|
|||
package org.apache.hc.client5.http.impl.cache;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.apache.hc.client5.http.classic.methods.HttpPut;
|
||||
import org.apache.hc.client5.http.impl.classic.ClassicRequestCopier;
|
||||
import org.apache.hc.core5.http.ClassicHttpRequest;
|
||||
import org.apache.hc.client5.http.impl.RequestCopier;
|
||||
import org.apache.hc.core5.http.HttpRequest;
|
||||
import org.apache.hc.core5.http.HttpVersion;
|
||||
import org.apache.hc.core5.http.ProtocolVersion;
|
||||
import org.apache.hc.core5.http.message.BasicClassicHttpRequest;
|
||||
import org.apache.hc.core5.http.message.BasicHttpRequest;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class TestRequestProtocolCompliance {
|
||||
|
||||
private RequestProtocolCompliance impl;
|
||||
private ClassicHttpRequest req;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
req = HttpTestUtils.makeDefaultRequest();
|
||||
impl = new RequestProtocolCompliance(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequestWithWeakETagAndRange() throws Exception {
|
||||
final HttpRequest req = new BasicHttpRequest("GET", "/");
|
||||
req.setHeader("Range", "bytes=0-499");
|
||||
req.setHeader("If-Range", "W/\"weak\"");
|
||||
assertEquals(1, impl.requestIsFatallyNonCompliant(req).size());
|
||||
|
@ -61,14 +58,14 @@ public class TestRequestProtocolCompliance {
|
|||
|
||||
@Test
|
||||
public void testRequestWithWeekETagForPUTOrDELETEIfMatch() throws Exception {
|
||||
req = new HttpPut("http://example.com/");
|
||||
final HttpRequest req = new BasicHttpRequest("PUT", "http://example.com/");
|
||||
req.setHeader("If-Match", "W/\"weak\"");
|
||||
assertEquals(1, impl.requestIsFatallyNonCompliant(req).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequestWithWeekETagForPUTOrDELETEIfMatchAllowed() throws Exception {
|
||||
req = new HttpPut("http://example.com/");
|
||||
final HttpRequest req = new BasicHttpRequest("PUT", "http://example.com/");
|
||||
req.setHeader("If-Match", "W/\"weak\"");
|
||||
impl = new RequestProtocolCompliance(true);
|
||||
assertEquals(Arrays.asList(), impl.requestIsFatallyNonCompliant(req));
|
||||
|
@ -76,41 +73,33 @@ public class TestRequestProtocolCompliance {
|
|||
|
||||
@Test
|
||||
public void testRequestContainsNoCacheDirectiveWithFieldName() throws Exception {
|
||||
final HttpRequest req = new BasicHttpRequest("GET", "/");
|
||||
req.setHeader("Cache-Control", "no-cache=false");
|
||||
assertEquals(1, impl.requestIsFatallyNonCompliant(req).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doesNotModifyACompliantRequest() throws Exception {
|
||||
final ClassicHttpRequest wrapper = ClassicRequestCopier.INSTANCE.copy(req);
|
||||
final HttpRequest req = new BasicHttpRequest("GET", "/");
|
||||
final HttpRequest wrapper = RequestCopier.INSTANCE.copy(req);
|
||||
impl.makeRequestCompliant(wrapper);
|
||||
assertTrue(HttpTestUtils.equivalent(req, wrapper));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removesEntityFromTRACERequest() throws Exception {
|
||||
final ClassicHttpRequest request = new BasicClassicHttpRequest("TRACE", "/");
|
||||
request.setVersion(HttpVersion.HTTP_1_1);
|
||||
request.setEntity(HttpTestUtils.makeBody(50));
|
||||
final ClassicHttpRequest wrapper = ClassicRequestCopier.INSTANCE.copy(request);
|
||||
impl.makeRequestCompliant(wrapper);
|
||||
assertNull(wrapper.getEntity());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void upgrades1_0RequestTo1_1() throws Exception {
|
||||
req = new BasicClassicHttpRequest("GET", "/");
|
||||
final HttpRequest req = new BasicHttpRequest("GET", "/");
|
||||
req.setVersion(HttpVersion.HTTP_1_0);
|
||||
final ClassicHttpRequest wrapper = ClassicRequestCopier.INSTANCE.copy(req);
|
||||
final HttpRequest wrapper = RequestCopier.INSTANCE.copy(req);
|
||||
impl.makeRequestCompliant(wrapper);
|
||||
assertEquals(HttpVersion.HTTP_1_1, wrapper.getVersion());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void downgrades1_2RequestTo1_1() throws Exception {
|
||||
req = new BasicClassicHttpRequest("GET", "/");
|
||||
final HttpRequest req = new BasicHttpRequest("GET", "/");
|
||||
req.setVersion(new ProtocolVersion("HTTP", 1, 2));
|
||||
final ClassicHttpRequest wrapper = ClassicRequestCopier.INSTANCE.copy(req);
|
||||
final HttpRequest wrapper = RequestCopier.INSTANCE.copy(req);
|
||||
impl.makeRequestCompliant(wrapper);
|
||||
assertEquals(HttpVersion.HTTP_1_1, wrapper.getVersion());
|
||||
}
|
||||
|
@ -118,8 +107,9 @@ public class TestRequestProtocolCompliance {
|
|||
@Test
|
||||
public void stripsMinFreshFromRequestIfNoCachePresent()
|
||||
throws Exception {
|
||||
final HttpRequest req = new BasicHttpRequest("GET", "/");
|
||||
req.setHeader("Cache-Control", "no-cache, min-fresh=10");
|
||||
final ClassicHttpRequest wrapper = ClassicRequestCopier.INSTANCE.copy(req);
|
||||
final HttpRequest wrapper = RequestCopier.INSTANCE.copy(req);
|
||||
impl.makeRequestCompliant(wrapper);
|
||||
assertEquals("no-cache",
|
||||
wrapper.getFirstHeader("Cache-Control").getValue());
|
||||
|
@ -128,8 +118,9 @@ public class TestRequestProtocolCompliance {
|
|||
@Test
|
||||
public void stripsMaxFreshFromRequestIfNoCachePresent()
|
||||
throws Exception {
|
||||
final HttpRequest req = new BasicHttpRequest("GET", "/");
|
||||
req.setHeader("Cache-Control", "no-cache, max-stale=10");
|
||||
final ClassicHttpRequest wrapper = ClassicRequestCopier.INSTANCE.copy(req);
|
||||
final HttpRequest wrapper = RequestCopier.INSTANCE.copy(req);
|
||||
impl.makeRequestCompliant(wrapper);
|
||||
assertEquals("no-cache",
|
||||
wrapper.getFirstHeader("Cache-Control").getValue());
|
||||
|
@ -138,8 +129,9 @@ public class TestRequestProtocolCompliance {
|
|||
@Test
|
||||
public void stripsMaxAgeFromRequestIfNoCachePresent()
|
||||
throws Exception {
|
||||
final HttpRequest req = new BasicHttpRequest("GET", "/");
|
||||
req.setHeader("Cache-Control", "no-cache, max-age=10");
|
||||
final ClassicHttpRequest wrapper = ClassicRequestCopier.INSTANCE.copy(req);
|
||||
final HttpRequest wrapper = RequestCopier.INSTANCE.copy(req);
|
||||
impl.makeRequestCompliant(wrapper);
|
||||
assertEquals("no-cache",
|
||||
wrapper.getFirstHeader("Cache-Control").getValue());
|
||||
|
@ -148,8 +140,9 @@ public class TestRequestProtocolCompliance {
|
|||
@Test
|
||||
public void doesNotStripMinFreshFromRequestWithoutNoCache()
|
||||
throws Exception {
|
||||
final HttpRequest req = new BasicHttpRequest("GET", "/");
|
||||
req.setHeader("Cache-Control", "min-fresh=10");
|
||||
final ClassicHttpRequest wrapper = ClassicRequestCopier.INSTANCE.copy(req);
|
||||
final HttpRequest wrapper = RequestCopier.INSTANCE.copy(req);
|
||||
impl.makeRequestCompliant(wrapper);
|
||||
assertEquals("min-fresh=10",
|
||||
wrapper.getFirstHeader("Cache-Control").getValue());
|
||||
|
@ -158,8 +151,9 @@ public class TestRequestProtocolCompliance {
|
|||
@Test
|
||||
public void correctlyStripsMinFreshFromMiddleIfNoCache()
|
||||
throws Exception {
|
||||
final HttpRequest req = new BasicHttpRequest("GET", "/");
|
||||
req.setHeader("Cache-Control", "no-cache,min-fresh=10,no-store");
|
||||
final ClassicHttpRequest wrapper = ClassicRequestCopier.INSTANCE.copy(req);
|
||||
final HttpRequest wrapper = RequestCopier.INSTANCE.copy(req);
|
||||
impl.makeRequestCompliant(wrapper);
|
||||
assertEquals("no-cache,no-store",
|
||||
wrapper.getFirstHeader("Cache-Control").getValue());
|
||||
|
|
|
@ -26,136 +26,49 @@
|
|||
*/
|
||||
package org.apache.hc.client5.http.impl.cache;
|
||||
|
||||
import static junit.framework.TestCase.assertNull;
|
||||
import static junit.framework.TestCase.assertTrue;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.util.Date;
|
||||
|
||||
import org.apache.hc.client5.http.ClientProtocolException;
|
||||
import org.apache.hc.client5.http.HttpRoute;
|
||||
import org.apache.hc.client5.http.classic.methods.HttpGet;
|
||||
import org.apache.hc.client5.http.classic.methods.HttpHead;
|
||||
import org.apache.hc.client5.http.impl.classic.ClassicRequestCopier;
|
||||
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.HttpEntity;
|
||||
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.io.entity.ByteArrayEntity;
|
||||
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.apache.hc.core5.http.message.BasicHttpResponse;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class TestResponseProtocolCompliance {
|
||||
|
||||
private HttpRoute route;
|
||||
private ResponseProtocolCompliance impl;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
route = new HttpRoute(new HttpHost("foo.example.com", 80));
|
||||
impl = new ResponseProtocolCompliance();
|
||||
}
|
||||
|
||||
private static class Flag {
|
||||
public boolean set;
|
||||
}
|
||||
|
||||
private void setMinimalResponseHeaders(final ClassicHttpResponse resp) {
|
||||
private void setMinimalResponseHeaders(final HttpResponse resp) {
|
||||
resp.setHeader("Date", DateUtils.formatDate(new Date()));
|
||||
resp.setHeader("Server", "MyServer/1.0");
|
||||
}
|
||||
|
||||
private ByteArrayInputStream makeTrackableBody(final int nbytes, final Flag closed) {
|
||||
final byte[] buf = HttpTestUtils.getRandomBytes(nbytes);
|
||||
final ByteArrayInputStream bais = new ByteArrayInputStream(buf) {
|
||||
@Override
|
||||
public void close() {
|
||||
closed.set = true;
|
||||
}
|
||||
};
|
||||
return bais;
|
||||
}
|
||||
|
||||
private ClassicHttpResponse makePartialResponse(final int nbytes) {
|
||||
final ClassicHttpResponse resp = new BasicClassicHttpResponse(HttpStatus.SC_PARTIAL_CONTENT, "Partial Content");
|
||||
private HttpResponse makePartialResponse(final int nbytes) {
|
||||
final HttpResponse resp = new BasicHttpResponse(HttpStatus.SC_PARTIAL_CONTENT, "Partial Content");
|
||||
setMinimalResponseHeaders(resp);
|
||||
resp.setHeader("Content-Length","" + nbytes);
|
||||
resp.setHeader("Content-Range","0-127/256");
|
||||
return resp;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void consumesBodyIfOriginSendsOneInResponseToHEAD() throws Exception {
|
||||
final HttpHead req = new HttpHead("http://foo.example.com/");
|
||||
final ClassicHttpRequest wrapper = ClassicRequestCopier.INSTANCE.copy(req);
|
||||
final int nbytes = 128;
|
||||
final ClassicHttpResponse resp = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
|
||||
setMinimalResponseHeaders(resp);
|
||||
resp.setHeader("Content-Length","" + nbytes);
|
||||
|
||||
final Flag closed = new Flag();
|
||||
final ByteArrayInputStream bais = makeTrackableBody(nbytes, closed);
|
||||
resp.setEntity(new InputStreamEntity(bais, -1));
|
||||
|
||||
impl.ensureProtocolCompliance(wrapper, req, resp);
|
||||
assertNull(resp.getEntity());
|
||||
assertTrue(closed.set || bais.read() == -1);
|
||||
}
|
||||
|
||||
@Test(expected=ClientProtocolException.class)
|
||||
public void throwsExceptionIfOriginReturnsPartialResponseWhenNotRequested() throws Exception {
|
||||
final HttpGet req = new HttpGet("http://foo.example.com/");
|
||||
final ClassicHttpRequest wrapper = ClassicRequestCopier.INSTANCE.copy(req);
|
||||
final HttpRequest wrapper = ClassicRequestCopier.INSTANCE.copy(req);
|
||||
final int nbytes = 128;
|
||||
final ClassicHttpResponse resp = makePartialResponse(nbytes);
|
||||
resp.setEntity(HttpTestUtils.makeBody(nbytes));
|
||||
final HttpResponse resp = makePartialResponse(nbytes);
|
||||
|
||||
impl.ensureProtocolCompliance(wrapper, req, resp);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void consumesPartialContentFromOriginEvenIfNotRequested() throws Exception {
|
||||
final HttpGet req = new HttpGet("http://foo.example.com/");
|
||||
final ClassicHttpRequest wrapper = ClassicRequestCopier.INSTANCE.copy(req);
|
||||
final int nbytes = 128;
|
||||
final ClassicHttpResponse resp = makePartialResponse(nbytes);
|
||||
|
||||
final Flag closed = new Flag();
|
||||
final ByteArrayInputStream bais = makeTrackableBody(nbytes, closed);
|
||||
resp.setEntity(new InputStreamEntity(bais, -1));
|
||||
|
||||
try {
|
||||
impl.ensureProtocolCompliance(wrapper, req, resp);
|
||||
} catch (final ClientProtocolException expected) {
|
||||
}
|
||||
assertTrue(closed.set || bais.read() == -1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void consumesBodyOf100ContinueResponseIfItArrives() throws Exception {
|
||||
final ClassicHttpRequest req = new BasicClassicHttpRequest("POST", "/");
|
||||
final int nbytes = 128;
|
||||
req.setHeader("Content-Length","" + nbytes);
|
||||
req.setHeader("Content-Type", "application/octet-stream");
|
||||
final HttpEntity postBody = new ByteArrayEntity(HttpTestUtils.getRandomBytes(nbytes));
|
||||
req.setEntity(postBody);
|
||||
final ClassicHttpRequest wrapper = ClassicRequestCopier.INSTANCE.copy(req);
|
||||
|
||||
final ClassicHttpResponse resp = new BasicClassicHttpResponse(HttpStatus.SC_CONTINUE, "Continue");
|
||||
final Flag closed = new Flag();
|
||||
final ByteArrayInputStream bais = makeTrackableBody(nbytes, closed);
|
||||
resp.setEntity(new InputStreamEntity(bais, -1));
|
||||
|
||||
try {
|
||||
impl.ensureProtocolCompliance(wrapper, req, resp);
|
||||
} catch (final ClientProtocolException expected) {
|
||||
}
|
||||
assertTrue(closed.set || bais.read() == -1);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue