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;
|
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) {
|
for (final RequestProtocolError error : fatalError) {
|
||||||
setResponseStatus(context, CacheResponseStatus.CACHE_MODULE_RESPONSE);
|
setResponseStatus(context, CacheResponseStatus.CACHE_MODULE_RESPONSE);
|
||||||
fatalErrorResponse = requestCompliance.getErrorForRequest(error);
|
fatalErrorResponse = responseGenerator.getErrorForRequest(error);
|
||||||
}
|
}
|
||||||
return fatalErrorResponse;
|
return fatalErrorResponse;
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,25 +31,14 @@ import java.util.Arrays;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.hc.client5.http.ClientProtocolException;
|
|
||||||
import org.apache.hc.client5.http.cache.HeaderConstants;
|
import org.apache.hc.client5.http.cache.HeaderConstants;
|
||||||
import org.apache.hc.core5.annotation.Contract;
|
import org.apache.hc.core5.annotation.Contract;
|
||||||
import org.apache.hc.core5.annotation.ThreadingBehavior;
|
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.Header;
|
||||||
import org.apache.hc.core5.http.HeaderElement;
|
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.HttpRequest;
|
||||||
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.ProtocolVersion;
|
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;
|
import org.apache.hc.core5.http.message.MessageSupport;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -107,17 +96,8 @@ class RequestProtocolCompliance {
|
||||||
* fix the request here.
|
* fix the request here.
|
||||||
*
|
*
|
||||||
* @param request the request to check for compliance
|
* @param request the request to check for compliance
|
||||||
* @throws ClientProtocolException when we have trouble making the request compliant
|
|
||||||
*/
|
*/
|
||||||
public void makeRequestCompliant(final ClassicHttpRequest request)
|
public void makeRequestCompliant(final HttpRequest request) {
|
||||||
throws ClientProtocolException {
|
|
||||||
|
|
||||||
if (requestMustNotHaveEntity(request)) {
|
|
||||||
request.setEntity(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
verifyRequestWithExpectContinueFlagHas100continueHeader(request);
|
|
||||||
verifyOPTIONSRequestWithBodyHasContentType(request);
|
|
||||||
decrementOPTIONSMaxForwardsIfGreaterThen0(request);
|
decrementOPTIONSMaxForwardsIfGreaterThen0(request);
|
||||||
stripOtherFreshnessDirectivesWithNoCache(request);
|
stripOtherFreshnessDirectivesWithNoCache(request);
|
||||||
|
|
||||||
|
@ -160,10 +140,6 @@ class RequestProtocolCompliance {
|
||||||
return newHdr.toString();
|
return newHdr.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean requestMustNotHaveEntity(final HttpRequest request) {
|
|
||||||
return HeaderConstants.TRACE_METHOD.equals(request.getMethod());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void decrementOPTIONSMaxForwardsIfGreaterThen0(final HttpRequest request) {
|
private void decrementOPTIONSMaxForwardsIfGreaterThen0(final HttpRequest request) {
|
||||||
if (!HeaderConstants.OPTIONS_METHOD.equals(request.getMethod())) {
|
if (!HeaderConstants.OPTIONS_METHOD.equals(request.getMethod())) {
|
||||||
return;
|
return;
|
||||||
|
@ -180,81 +156,6 @@ class RequestProtocolCompliance {
|
||||||
request.setHeader(HeaderConstants.MAX_FORWARDS, Integer.toString(currentMaxForwards - 1));
|
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) {
|
protected boolean requestMinorVersionIsTooHighMajorVersionsMatch(final HttpRequest request) {
|
||||||
final ProtocolVersion requestProtocol = request.getVersion();
|
final ProtocolVersion requestProtocol = request.getVersion();
|
||||||
if (requestProtocol == null) {
|
if (requestProtocol == null) {
|
||||||
|
@ -276,37 +177,6 @@ class RequestProtocolCompliance {
|
||||||
return requestProtocol != null && requestProtocol.compareToVersion(HttpVersion.HTTP_1_1) < 0;
|
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) {
|
private RequestProtocolError requestHasWeakETagAndRange(final HttpRequest request) {
|
||||||
// TODO: Should these be looking at all the headers marked as Range?
|
// TODO: Should these be looking at all the headers marked as Range?
|
||||||
final String method = request.getMethod();
|
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.client5.http.utils.DateUtils;
|
||||||
import org.apache.hc.core5.annotation.Contract;
|
import org.apache.hc.core5.annotation.Contract;
|
||||||
import org.apache.hc.core5.annotation.ThreadingBehavior;
|
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.Header;
|
||||||
import org.apache.hc.core5.http.HeaderElement;
|
import org.apache.hc.core5.http.HeaderElement;
|
||||||
import org.apache.hc.core5.http.HeaderElements;
|
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.HttpHeaders;
|
||||||
import org.apache.hc.core5.http.HttpRequest;
|
import org.apache.hc.core5.http.HttpRequest;
|
||||||
import org.apache.hc.core5.http.HttpResponse;
|
import org.apache.hc.core5.http.HttpResponse;
|
||||||
|
@ -72,14 +69,9 @@ class ResponseProtocolCompliance {
|
||||||
* @throws IOException Bad things happened
|
* @throws IOException Bad things happened
|
||||||
*/
|
*/
|
||||||
public void ensureProtocolCompliance(
|
public void ensureProtocolCompliance(
|
||||||
final ClassicHttpRequest originalRequest,
|
final HttpRequest originalRequest,
|
||||||
final ClassicHttpRequest request,
|
final HttpRequest request,
|
||||||
final ClassicHttpResponse response) throws IOException {
|
final HttpResponse response) throws IOException {
|
||||||
if (backendResponseMustNotHaveBody(request, response)) {
|
|
||||||
consumeBody(response);
|
|
||||||
response.setEntity(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
requestDidNotExpect100ContinueButResponseIsOne(originalRequest, response);
|
requestDidNotExpect100ContinueButResponseIsOne(originalRequest, response);
|
||||||
|
|
||||||
transferEncodingIsNotReturnedTo1_0Client(originalRequest, response);
|
transferEncodingIsNotReturnedTo1_0Client(originalRequest, response);
|
||||||
|
@ -97,13 +89,6 @@ class ResponseProtocolCompliance {
|
||||||
warningsWithNonMatchingWarnDatesAreRemoved(response);
|
warningsWithNonMatchingWarnDatesAreRemoved(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void consumeBody(final ClassicHttpResponse response) throws IOException {
|
|
||||||
final HttpEntity body = response.getEntity();
|
|
||||||
if (body != null) {
|
|
||||||
IOUtils.consume(body);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void warningsWithNonMatchingWarnDatesAreRemoved(
|
private void warningsWithNonMatchingWarnDatesAreRemoved(
|
||||||
final HttpResponse response) {
|
final HttpResponse response) {
|
||||||
final Date responseDate = DateUtils.parseDate(response.getFirstHeader(HttpHeaders.DATE).getValue());
|
final Date responseDate = DateUtils.parseDate(response.getFirstHeader(HttpHeaders.DATE).getValue());
|
||||||
|
@ -180,13 +165,11 @@ class ResponseProtocolCompliance {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ensurePartialContentIsNotSentToAClientThatDidNotRequestIt(final HttpRequest request,
|
private void ensurePartialContentIsNotSentToAClientThatDidNotRequestIt(final HttpRequest request,
|
||||||
final ClassicHttpResponse response) throws IOException {
|
final HttpResponse response) throws IOException {
|
||||||
if (request.getFirstHeader(HeaderConstants.RANGE) != null
|
if (request.getFirstHeader(HeaderConstants.RANGE) != null
|
||||||
|| response.getCode() != HttpStatus.SC_PARTIAL_CONTENT) {
|
|| response.getCode() != HttpStatus.SC_PARTIAL_CONTENT) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
consumeBody(response);
|
|
||||||
throw new ClientProtocolException(UNEXPECTED_PARTIAL_CONTENT);
|
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(
|
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) {
|
if (response.getCode() != HttpStatus.SC_CONTINUE) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -234,12 +210,11 @@ class ResponseProtocolCompliance {
|
||||||
if (header != null && header.getValue().equalsIgnoreCase(HeaderElements.CONTINUE)) {
|
if (header != null && header.getValue().equalsIgnoreCase(HeaderElements.CONTINUE)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
consumeBody(response);
|
|
||||||
throw new ClientProtocolException(UNEXPECTED_100_CONTINUE);
|
throw new ClientProtocolException(UNEXPECTED_100_CONTINUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void transferEncodingIsNotReturnedTo1_0Client(
|
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;
|
final ProtocolVersion version = originalRequest.getVersion() != null ? originalRequest.getVersion() : HttpVersion.DEFAULT;
|
||||||
if (version.compareToVersion(HttpVersion.HTTP_1_1) >= 0) {
|
if (version.compareToVersion(HttpVersion.HTTP_1_1) >= 0) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -49,7 +49,6 @@ import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.hc.client5.http.ClientProtocolException;
|
|
||||||
import org.apache.hc.client5.http.HttpRoute;
|
import org.apache.hc.client5.http.HttpRoute;
|
||||||
import org.apache.hc.client5.http.cache.CacheResponseStatus;
|
import org.apache.hc.client5.http.cache.CacheResponseStatus;
|
||||||
import org.apache.hc.client5.http.cache.HttpCacheContext;
|
import org.apache.hc.client5.http.cache.HttpCacheContext;
|
||||||
|
@ -359,27 +358,6 @@ public abstract class TestCachingExecChain {
|
||||||
verifyMocks();
|
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
|
@Test
|
||||||
public void testSetsModuleGeneratedResponseContextForCacheOptionsResponse() throws Exception {
|
public void testSetsModuleGeneratedResponseContextForCacheOptionsResponse() throws Exception {
|
||||||
impl = createCachingExecChain(new BasicHttpCache(), CacheConfig.DEFAULT);
|
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
|
* "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
|
||||||
|
|
|
@ -641,39 +641,6 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
|
||||||
Assert.assertFalse(foundExpect);
|
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
|
* "If a proxy receives a request that includes an Expect request- header
|
||||||
* field with the '100-continue' expectation, and the proxy either knows
|
* field with the '100-continue' expectation, and the proxy either knows
|
||||||
|
@ -1030,32 +997,6 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
|
||||||
verifyMocks();
|
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."
|
* "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);
|
final ClassicHttpResponse result = execute(request);
|
||||||
|
|
||||||
verifyMocks();
|
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);
|
final ClassicHttpResponse result = execute(request);
|
||||||
|
|
||||||
verifyMocks();
|
verifyMocks();
|
||||||
|
|
||||||
Assert.assertTrue(result.getEntity() == null || result.getEntity().getContentLength() == 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -27,33 +27,30 @@
|
||||||
package org.apache.hc.client5.http.impl.cache;
|
package org.apache.hc.client5.http.impl.cache;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertNull;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
import org.apache.hc.client5.http.classic.methods.HttpPut;
|
import org.apache.hc.client5.http.impl.RequestCopier;
|
||||||
import org.apache.hc.client5.http.impl.classic.ClassicRequestCopier;
|
import org.apache.hc.core5.http.HttpRequest;
|
||||||
import org.apache.hc.core5.http.ClassicHttpRequest;
|
|
||||||
import org.apache.hc.core5.http.HttpVersion;
|
import org.apache.hc.core5.http.HttpVersion;
|
||||||
import org.apache.hc.core5.http.ProtocolVersion;
|
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.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
public class TestRequestProtocolCompliance {
|
public class TestRequestProtocolCompliance {
|
||||||
|
|
||||||
private RequestProtocolCompliance impl;
|
private RequestProtocolCompliance impl;
|
||||||
private ClassicHttpRequest req;
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
req = HttpTestUtils.makeDefaultRequest();
|
|
||||||
impl = new RequestProtocolCompliance(false);
|
impl = new RequestProtocolCompliance(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRequestWithWeakETagAndRange() throws Exception {
|
public void testRequestWithWeakETagAndRange() throws Exception {
|
||||||
|
final HttpRequest req = new BasicHttpRequest("GET", "/");
|
||||||
req.setHeader("Range", "bytes=0-499");
|
req.setHeader("Range", "bytes=0-499");
|
||||||
req.setHeader("If-Range", "W/\"weak\"");
|
req.setHeader("If-Range", "W/\"weak\"");
|
||||||
assertEquals(1, impl.requestIsFatallyNonCompliant(req).size());
|
assertEquals(1, impl.requestIsFatallyNonCompliant(req).size());
|
||||||
|
@ -61,14 +58,14 @@ public class TestRequestProtocolCompliance {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRequestWithWeekETagForPUTOrDELETEIfMatch() throws Exception {
|
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\"");
|
req.setHeader("If-Match", "W/\"weak\"");
|
||||||
assertEquals(1, impl.requestIsFatallyNonCompliant(req).size());
|
assertEquals(1, impl.requestIsFatallyNonCompliant(req).size());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRequestWithWeekETagForPUTOrDELETEIfMatchAllowed() throws Exception {
|
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\"");
|
req.setHeader("If-Match", "W/\"weak\"");
|
||||||
impl = new RequestProtocolCompliance(true);
|
impl = new RequestProtocolCompliance(true);
|
||||||
assertEquals(Arrays.asList(), impl.requestIsFatallyNonCompliant(req));
|
assertEquals(Arrays.asList(), impl.requestIsFatallyNonCompliant(req));
|
||||||
|
@ -76,41 +73,33 @@ public class TestRequestProtocolCompliance {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRequestContainsNoCacheDirectiveWithFieldName() throws Exception {
|
public void testRequestContainsNoCacheDirectiveWithFieldName() throws Exception {
|
||||||
|
final HttpRequest req = new BasicHttpRequest("GET", "/");
|
||||||
req.setHeader("Cache-Control", "no-cache=false");
|
req.setHeader("Cache-Control", "no-cache=false");
|
||||||
assertEquals(1, impl.requestIsFatallyNonCompliant(req).size());
|
assertEquals(1, impl.requestIsFatallyNonCompliant(req).size());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void doesNotModifyACompliantRequest() throws Exception {
|
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);
|
impl.makeRequestCompliant(wrapper);
|
||||||
assertTrue(HttpTestUtils.equivalent(req, 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
|
@Test
|
||||||
public void upgrades1_0RequestTo1_1() throws Exception {
|
public void upgrades1_0RequestTo1_1() throws Exception {
|
||||||
req = new BasicClassicHttpRequest("GET", "/");
|
final HttpRequest req = new BasicHttpRequest("GET", "/");
|
||||||
req.setVersion(HttpVersion.HTTP_1_0);
|
req.setVersion(HttpVersion.HTTP_1_0);
|
||||||
final ClassicHttpRequest wrapper = ClassicRequestCopier.INSTANCE.copy(req);
|
final HttpRequest wrapper = RequestCopier.INSTANCE.copy(req);
|
||||||
impl.makeRequestCompliant(wrapper);
|
impl.makeRequestCompliant(wrapper);
|
||||||
assertEquals(HttpVersion.HTTP_1_1, wrapper.getVersion());
|
assertEquals(HttpVersion.HTTP_1_1, wrapper.getVersion());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void downgrades1_2RequestTo1_1() throws Exception {
|
public void downgrades1_2RequestTo1_1() throws Exception {
|
||||||
req = new BasicClassicHttpRequest("GET", "/");
|
final HttpRequest req = new BasicHttpRequest("GET", "/");
|
||||||
req.setVersion(new ProtocolVersion("HTTP", 1, 2));
|
req.setVersion(new ProtocolVersion("HTTP", 1, 2));
|
||||||
final ClassicHttpRequest wrapper = ClassicRequestCopier.INSTANCE.copy(req);
|
final HttpRequest wrapper = RequestCopier.INSTANCE.copy(req);
|
||||||
impl.makeRequestCompliant(wrapper);
|
impl.makeRequestCompliant(wrapper);
|
||||||
assertEquals(HttpVersion.HTTP_1_1, wrapper.getVersion());
|
assertEquals(HttpVersion.HTTP_1_1, wrapper.getVersion());
|
||||||
}
|
}
|
||||||
|
@ -118,8 +107,9 @@ public class TestRequestProtocolCompliance {
|
||||||
@Test
|
@Test
|
||||||
public void stripsMinFreshFromRequestIfNoCachePresent()
|
public void stripsMinFreshFromRequestIfNoCachePresent()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
final HttpRequest req = new BasicHttpRequest("GET", "/");
|
||||||
req.setHeader("Cache-Control", "no-cache, min-fresh=10");
|
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);
|
impl.makeRequestCompliant(wrapper);
|
||||||
assertEquals("no-cache",
|
assertEquals("no-cache",
|
||||||
wrapper.getFirstHeader("Cache-Control").getValue());
|
wrapper.getFirstHeader("Cache-Control").getValue());
|
||||||
|
@ -128,8 +118,9 @@ public class TestRequestProtocolCompliance {
|
||||||
@Test
|
@Test
|
||||||
public void stripsMaxFreshFromRequestIfNoCachePresent()
|
public void stripsMaxFreshFromRequestIfNoCachePresent()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
final HttpRequest req = new BasicHttpRequest("GET", "/");
|
||||||
req.setHeader("Cache-Control", "no-cache, max-stale=10");
|
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);
|
impl.makeRequestCompliant(wrapper);
|
||||||
assertEquals("no-cache",
|
assertEquals("no-cache",
|
||||||
wrapper.getFirstHeader("Cache-Control").getValue());
|
wrapper.getFirstHeader("Cache-Control").getValue());
|
||||||
|
@ -138,8 +129,9 @@ public class TestRequestProtocolCompliance {
|
||||||
@Test
|
@Test
|
||||||
public void stripsMaxAgeFromRequestIfNoCachePresent()
|
public void stripsMaxAgeFromRequestIfNoCachePresent()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
final HttpRequest req = new BasicHttpRequest("GET", "/");
|
||||||
req.setHeader("Cache-Control", "no-cache, max-age=10");
|
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);
|
impl.makeRequestCompliant(wrapper);
|
||||||
assertEquals("no-cache",
|
assertEquals("no-cache",
|
||||||
wrapper.getFirstHeader("Cache-Control").getValue());
|
wrapper.getFirstHeader("Cache-Control").getValue());
|
||||||
|
@ -148,8 +140,9 @@ public class TestRequestProtocolCompliance {
|
||||||
@Test
|
@Test
|
||||||
public void doesNotStripMinFreshFromRequestWithoutNoCache()
|
public void doesNotStripMinFreshFromRequestWithoutNoCache()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
final HttpRequest req = new BasicHttpRequest("GET", "/");
|
||||||
req.setHeader("Cache-Control", "min-fresh=10");
|
req.setHeader("Cache-Control", "min-fresh=10");
|
||||||
final ClassicHttpRequest wrapper = ClassicRequestCopier.INSTANCE.copy(req);
|
final HttpRequest wrapper = RequestCopier.INSTANCE.copy(req);
|
||||||
impl.makeRequestCompliant(wrapper);
|
impl.makeRequestCompliant(wrapper);
|
||||||
assertEquals("min-fresh=10",
|
assertEquals("min-fresh=10",
|
||||||
wrapper.getFirstHeader("Cache-Control").getValue());
|
wrapper.getFirstHeader("Cache-Control").getValue());
|
||||||
|
@ -158,8 +151,9 @@ public class TestRequestProtocolCompliance {
|
||||||
@Test
|
@Test
|
||||||
public void correctlyStripsMinFreshFromMiddleIfNoCache()
|
public void correctlyStripsMinFreshFromMiddleIfNoCache()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
final HttpRequest req = new BasicHttpRequest("GET", "/");
|
||||||
req.setHeader("Cache-Control", "no-cache,min-fresh=10,no-store");
|
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);
|
impl.makeRequestCompliant(wrapper);
|
||||||
assertEquals("no-cache,no-store",
|
assertEquals("no-cache,no-store",
|
||||||
wrapper.getFirstHeader("Cache-Control").getValue());
|
wrapper.getFirstHeader("Cache-Control").getValue());
|
||||||
|
|
|
@ -26,136 +26,49 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.hc.client5.http.impl.cache;
|
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 java.util.Date;
|
||||||
|
|
||||||
import org.apache.hc.client5.http.ClientProtocolException;
|
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.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.impl.classic.ClassicRequestCopier;
|
||||||
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.HttpRequest;
|
||||||
import org.apache.hc.core5.http.ClassicHttpResponse;
|
import org.apache.hc.core5.http.HttpResponse;
|
||||||
import org.apache.hc.core5.http.HttpEntity;
|
|
||||||
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.ByteArrayEntity;
|
import org.apache.hc.core5.http.message.BasicHttpResponse;
|
||||||
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.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
public class TestResponseProtocolCompliance {
|
public class TestResponseProtocolCompliance {
|
||||||
|
|
||||||
private HttpRoute route;
|
|
||||||
private ResponseProtocolCompliance impl;
|
private ResponseProtocolCompliance impl;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
route = new HttpRoute(new HttpHost("foo.example.com", 80));
|
|
||||||
impl = new ResponseProtocolCompliance();
|
impl = new ResponseProtocolCompliance();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class Flag {
|
private void setMinimalResponseHeaders(final HttpResponse resp) {
|
||||||
public boolean set;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setMinimalResponseHeaders(final ClassicHttpResponse resp) {
|
|
||||||
resp.setHeader("Date", DateUtils.formatDate(new Date()));
|
resp.setHeader("Date", DateUtils.formatDate(new Date()));
|
||||||
resp.setHeader("Server", "MyServer/1.0");
|
resp.setHeader("Server", "MyServer/1.0");
|
||||||
}
|
}
|
||||||
|
|
||||||
private ByteArrayInputStream makeTrackableBody(final int nbytes, final Flag closed) {
|
private HttpResponse makePartialResponse(final int nbytes) {
|
||||||
final byte[] buf = HttpTestUtils.getRandomBytes(nbytes);
|
final HttpResponse resp = new BasicHttpResponse(HttpStatus.SC_PARTIAL_CONTENT, "Partial Content");
|
||||||
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");
|
|
||||||
setMinimalResponseHeaders(resp);
|
setMinimalResponseHeaders(resp);
|
||||||
resp.setHeader("Content-Length","" + nbytes);
|
resp.setHeader("Content-Length","" + nbytes);
|
||||||
resp.setHeader("Content-Range","0-127/256");
|
resp.setHeader("Content-Range","0-127/256");
|
||||||
return resp;
|
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)
|
@Test(expected=ClientProtocolException.class)
|
||||||
public void throwsExceptionIfOriginReturnsPartialResponseWhenNotRequested() throws Exception {
|
public void throwsExceptionIfOriginReturnsPartialResponseWhenNotRequested() throws Exception {
|
||||||
final HttpGet req = new HttpGet("http://foo.example.com/");
|
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 int nbytes = 128;
|
||||||
final ClassicHttpResponse resp = makePartialResponse(nbytes);
|
final HttpResponse resp = makePartialResponse(nbytes);
|
||||||
resp.setEntity(HttpTestUtils.makeBody(nbytes));
|
|
||||||
|
|
||||||
impl.ensureProtocolCompliance(wrapper, req, resp);
|
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