SizeLimitedResponseReader can now generate arbitrary type of cache resources using resource factory

git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk@984949 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Oleg Kalnichevski 2010-08-12 20:32:54 +00:00
parent e0a296f6b6
commit a840e65989
12 changed files with 424 additions and 472 deletions

View File

@ -0,0 +1,55 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.http.client.cache;
/**
* @since 4.1
*/
public class InputLimit {
private final long value;
private boolean reached;
public InputLimit(long value) {
super();
this.value = value;
this.reached = false;
}
public long getValue() {
return this.value;
}
public void reached() {
this.reached = true;
}
public boolean isReached() {
return this.reached;
}
}

View File

@ -27,6 +27,7 @@
package org.apache.http.client.cache; package org.apache.http.client.cache;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
/** /**
* Generates {@link Resource} instances. * Generates {@link Resource} instances.
@ -35,7 +36,7 @@ import java.io.IOException;
*/ */
public interface ResourceFactory { public interface ResourceFactory {
Resource generate(String requestId, byte[] body) throws IOException; Resource generate(String requestId, InputStream instream, InputLimit limit) throws IOException;
Resource copy(String requestId, Resource resource) throws IOException; Resource copy(String requestId, Resource resource) throws IOException;

View File

@ -602,8 +602,7 @@ public class CachingHttpClient implements HttpClient {
variants); variants);
} }
HttpResponse correctIncompleteResponse(HttpResponse resp, HttpResponse correctIncompleteResponse(HttpResponse resp, Resource resource) {
byte[] bodyBytes) {
int status = resp.getStatusLine().getStatusCode(); int status = resp.getStatusLine().getStatusCode();
if (status != HttpStatus.SC_OK if (status != HttpStatus.SC_OK
&& status != HttpStatus.SC_PARTIAL_CONTENT) { && status != HttpStatus.SC_PARTIAL_CONTENT) {
@ -617,13 +616,14 @@ public class CachingHttpClient implements HttpClient {
} catch (NumberFormatException nfe) { } catch (NumberFormatException nfe) {
return resp; return resp;
} }
if (bodyBytes.length >= contentLength) return resp; if (resource.length() >= contentLength) return resp;
HttpResponse error = HttpResponse error =
new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_BAD_GATEWAY, "Bad Gateway"); new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_BAD_GATEWAY, "Bad Gateway");
error.setHeader("Content-Type","text/plain;charset=UTF-8"); error.setHeader("Content-Type","text/plain;charset=UTF-8");
String msg = String.format("Received incomplete response with Content-Length %d but actual body length %d", contentLength, bodyBytes.length); String msg = String.format("Received incomplete response " +
"with Content-Length %d but actual body length %d", contentLength, resource.length());
byte[] msgBytes = msg.getBytes(); byte[] msgBytes = msg.getBytes();
error.setHeader("Content-Length", String.format("%d",msgBytes.length)); error.setHeader("Content-Length", Integer.toString(msgBytes.length));
error.setEntity(new ByteArrayEntity(msgBytes)); error.setEntity(new ByteArrayEntity(msgBytes));
return error; return error;
} }
@ -643,19 +643,17 @@ public class CachingHttpClient implements HttpClient {
HttpResponse corrected = backendResponse; HttpResponse corrected = backendResponse;
if (cacheable) { if (cacheable) {
SizeLimitedResponseReader responseReader = getResponseReader(backendResponse); SizeLimitedResponseReader responseReader = getResponseReader(request, backendResponse);
responseReader.readResponse();
if (responseReader.isResponseTooLarge()) { if (responseReader.isLimitReached()) {
return responseReader.getReconstructedResponse(); return responseReader.getReconstructedResponse();
} }
byte[] responseBytes = responseReader.getResponseBytes(); Resource resource = responseReader.getResource();
corrected = correctIncompleteResponse(backendResponse, corrected = correctIncompleteResponse(backendResponse, resource);
responseBytes);
int correctedStatus = corrected.getStatusLine().getStatusCode(); int correctedStatus = corrected.getStatusLine().getStatusCode();
if (HttpStatus.SC_BAD_GATEWAY != correctedStatus) { if (HttpStatus.SC_BAD_GATEWAY != correctedStatus) {
Resource resource = resourceFactory.generate(
request.getRequestLine().getUri(), responseBytes);
HttpCacheEntry entry = new HttpCacheEntry( HttpCacheEntry entry = new HttpCacheEntry(
requestDate, requestDate,
responseDate, responseDate,
@ -673,8 +671,9 @@ public class CachingHttpClient implements HttpClient {
return corrected; return corrected;
} }
SizeLimitedResponseReader getResponseReader(HttpResponse backEndResponse) { SizeLimitedResponseReader getResponseReader(HttpRequest request, HttpResponse backEndResponse) {
return new SizeLimitedResponseReader(maxObjectSizeBytes, backEndResponse); return new SizeLimitedResponseReader(
resourceFactory, maxObjectSizeBytes, request, backEndResponse);
} }
} }

View File

@ -0,0 +1,105 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.http.impl.client.cache;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.SequenceInputStream;
import org.apache.http.annotation.NotThreadSafe;
import org.apache.http.client.cache.Resource;
import org.apache.http.entity.AbstractHttpEntity;
@NotThreadSafe
class CombinedEntity extends AbstractHttpEntity {
private final Resource resource;
private final InputStream combinedStream;
CombinedEntity(final Resource resource, final InputStream instream) throws IOException {
super();
this.resource = resource;
this.combinedStream = new SequenceInputStream(
new ResourceStream(resource.getInputStream()), instream);
}
public long getContentLength() {
return -1;
}
public boolean isRepeatable() {
return false;
}
public boolean isStreaming() {
return true;
}
public InputStream getContent() throws IOException, IllegalStateException {
return this.combinedStream;
}
public void writeTo(final OutputStream outstream) throws IOException {
if (outstream == null) {
throw new IllegalArgumentException("Output stream may not be null");
}
InputStream instream = getContent();
try {
int l;
byte[] tmp = new byte[2048];
while ((l = instream.read(tmp)) != -1) {
outstream.write(tmp, 0, l);
}
} finally {
instream.close();
}
}
private void dispose() {
this.resource.dispose();
}
class ResourceStream extends FilterInputStream {
protected ResourceStream(final InputStream in) {
super(in);
}
@Override
public void close() throws IOException {
try {
super.close();
} finally {
dispose();
}
}
}
}

View File

@ -1,75 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.http.impl.client.cache;
import java.io.IOException;
import java.io.InputStream;
/**
* A class that presents two inputstreams as a single stream
*
* @since 4.1
*/
class CombinedInputStream extends InputStream {
private final InputStream inputStream1;
private final InputStream inputStream2;
/**
* Take two inputstreams and produce an object that makes them appear as if they
* are actually a 'single' input stream.
*
* @param inputStream1
* First stream to read
* @param inputStream2
* Second stream to read
*/
public CombinedInputStream(InputStream inputStream1, InputStream inputStream2) {
if (inputStream1 == null)
throw new IllegalArgumentException("inputStream1 cannot be null");
if (inputStream2 == null)
throw new IllegalArgumentException("inputStream2 cannot be null");
this.inputStream1 = inputStream1;
this.inputStream2 = inputStream2;
}
@Override
public int available() throws IOException {
return inputStream1.available() + inputStream2.available();
}
@Override
public int read() throws IOException {
int result = inputStream1.read();
if (result == -1)
result = inputStream2.read();
return result;
}
}

View File

@ -29,8 +29,10 @@ package org.apache.http.impl.client.cache;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import org.apache.http.annotation.Immutable; import org.apache.http.annotation.Immutable;
import org.apache.http.client.cache.InputLimit;
import org.apache.http.client.cache.Resource; import org.apache.http.client.cache.Resource;
import org.apache.http.client.cache.ResourceFactory; import org.apache.http.client.cache.ResourceFactory;
@ -67,18 +69,33 @@ public class FileResourceFactory implements ResourceFactory {
return new File(this.cacheDir, buffer.toString()); return new File(this.cacheDir, buffer.toString());
} }
public Resource generate(final String requestId, final byte[] body) throws IOException { public Resource generate(
final String requestId,
final InputStream instream,
final InputLimit limit) throws IOException {
File file = generateUniqueCacheFile(requestId); File file = generateUniqueCacheFile(requestId);
FileOutputStream outstream = new FileOutputStream(file); FileOutputStream outstream = new FileOutputStream(file);
try { try {
outstream.write(body); byte[] buf = new byte[2048];
long total = 0;
int l;
while ((l = instream.read(buf)) != -1) {
outstream.write(buf, 0, l);
total += l;
if (limit != null && total > limit.getValue()) {
limit.reached();
break;
}
}
} finally { } finally {
outstream.close(); outstream.close();
} }
return new FileResource(file); return new FileResource(file);
} }
public Resource copy(final String requestId, final Resource resource) throws IOException { public Resource copy(
final String requestId,
final Resource resource) throws IOException {
File file = generateUniqueCacheFile(requestId); File file = generateUniqueCacheFile(requestId);
if (resource instanceof FileResource) { if (resource instanceof FileResource) {

View File

@ -28,8 +28,10 @@ package org.apache.http.impl.client.cache;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import org.apache.http.annotation.Immutable; import org.apache.http.annotation.Immutable;
import org.apache.http.client.cache.InputLimit;
import org.apache.http.client.cache.Resource; import org.apache.http.client.cache.Resource;
import org.apache.http.client.cache.ResourceFactory; import org.apache.http.client.cache.ResourceFactory;
@ -41,11 +43,28 @@ import org.apache.http.client.cache.ResourceFactory;
@Immutable @Immutable
public class HeapResourceFactory implements ResourceFactory { public class HeapResourceFactory implements ResourceFactory {
public Resource generate(final String requestId, final byte[] body) throws IOException { public Resource generate(
return new HeapResource(body); final String requestId,
final InputStream instream,
final InputLimit limit) throws IOException {
ByteArrayOutputStream outstream = new ByteArrayOutputStream();
byte[] buf = new byte[2048];
long total = 0;
int l;
while ((l = instream.read(buf)) != -1) {
outstream.write(buf, 0, l);
total += l;
if (limit != null && total > limit.getValue()) {
limit.reached();
break;
}
}
return new HeapResource(outstream.toByteArray());
} }
public Resource copy(final String requestId, final Resource resource) throws IOException { public Resource copy(
final String requestId,
final Resource resource) throws IOException {
byte[] body; byte[] body;
if (resource instanceof HeapResource) { if (resource instanceof HeapResource) {
body = ((HeapResource) resource).getByteArray(); body = ((HeapResource) resource).getByteArray();

View File

@ -26,132 +26,101 @@
*/ */
package org.apache.http.impl.client.cache; package org.apache.http.impl.client.cache;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import org.apache.http.HttpEntity; import org.apache.http.HttpEntity;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse; import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus; import org.apache.http.HttpStatus;
import org.apache.http.entity.InputStreamEntity; import org.apache.http.annotation.NotThreadSafe;
import org.apache.http.client.cache.InputLimit;
import org.apache.http.client.cache.Resource;
import org.apache.http.client.cache.ResourceFactory;
import org.apache.http.message.BasicHttpResponse; import org.apache.http.message.BasicHttpResponse;
/** /**
* @since 4.1 * @since 4.1
*/ */
@NotThreadSafe
class SizeLimitedResponseReader { class SizeLimitedResponseReader {
private final int maxResponseSizeBytes; private final ResourceFactory resourceFactory;
private final long maxResponseSizeBytes;
private final HttpRequest request;
private final HttpResponse response; private final HttpResponse response;
private ByteArrayOutputStream outputStream; private InputStream instream;
private InputStream contentInputStream; private InputLimit limit;
private boolean isTooLarge; private Resource resource;
private boolean responseIsConsumed; private boolean consumed;
private byte[] sizeLimitedContent;
private boolean outputStreamConsumed;
/** /**
* Create an {@link HttpResponse} that is limited in size, this allows for checking * Create an {@link HttpResponse} that is limited in size, this allows for checking
* the size of objects that will be stored in the cache. * the size of objects that will be stored in the cache.
*
* @param maxResponseSizeBytes
* Maximum size that a response can be to be eligible for cache inclusion
*
* @param response
* The {@link HttpResponse}
*/ */
public SizeLimitedResponseReader(int maxResponseSizeBytes, HttpResponse response) { public SizeLimitedResponseReader(
ResourceFactory resourceFactory,
long maxResponseSizeBytes,
HttpRequest request,
HttpResponse response) {
super();
this.resourceFactory = resourceFactory;
this.maxResponseSizeBytes = maxResponseSizeBytes; this.maxResponseSizeBytes = maxResponseSizeBytes;
this.request = request;
this.response = response; this.response = response;
} }
protected boolean isResponseTooLarge() throws IOException { protected void readResponse() throws IOException {
if (!responseIsConsumed) if (!consumed) {
isTooLarge = consumeResponse(); doConsume();
}
return isTooLarge;
} }
private boolean consumeResponse() throws IOException { private void ensureNotConsumed() {
if (consumed) {
throw new IllegalStateException("Response has already been consumed");
}
}
if (responseIsConsumed) private void ensureConsumed() {
throw new IllegalStateException( if (!consumed) {
"You cannot call this method more than once, because it consumes an underlying stream"); throw new IllegalStateException("Response has not been consumed");
}
}
responseIsConsumed = true; private void doConsume() throws IOException {
ensureNotConsumed();
consumed = true;
limit = new InputLimit(maxResponseSizeBytes);
HttpEntity entity = response.getEntity(); HttpEntity entity = response.getEntity();
if (entity == null) if (entity == null) {
return false; return;
contentInputStream = entity.getContent();
int bytes = 0;
outputStream = new ByteArrayOutputStream();
int current;
while (bytes < maxResponseSizeBytes && (current = contentInputStream.read()) != -1) {
outputStream.write(current);
bytes++;
} }
String uri = request.getRequestLine().getUri();
if ((current = contentInputStream.read()) != -1) { instream = entity.getContent();
outputStream.write(current); resource = resourceFactory.generate(uri, instream, limit);
return true;
}
return false;
} }
private void consumeOutputStream() { boolean isLimitReached() {
if (outputStreamConsumed) ensureConsumed();
throw new IllegalStateException( return limit.isReached();
"underlying output stream has already been written to byte[]");
if (!responseIsConsumed)
throw new IllegalStateException("Must call consumeResponse first.");
sizeLimitedContent = outputStream.toByteArray();
outputStreamConsumed = true;
} }
protected byte[] getResponseBytes() { Resource getResource() {
if (!outputStreamConsumed) ensureConsumed();
consumeOutputStream(); return resource;
return sizeLimitedContent;
} }
protected HttpResponse getReconstructedResponse() { HttpResponse getReconstructedResponse() throws IOException {
ensureConsumed();
InputStream combinedStream = getCombinedInputStream(); HttpResponse reconstructed = new BasicHttpResponse(response.getProtocolVersion(),
return constructResponse(response, combinedStream);
}
protected InputStream getCombinedInputStream() {
InputStream input1 = new ByteArrayInputStream(getResponseBytes());
InputStream input2 = getContentInputStream();
return new CombinedInputStream(input1, input2);
}
protected InputStream getContentInputStream() {
return contentInputStream;
}
protected HttpResponse constructResponse(HttpResponse originalResponse,
InputStream combinedStream) {
HttpResponse response = new BasicHttpResponse(originalResponse.getProtocolVersion(),
HttpStatus.SC_OK, "Success"); HttpStatus.SC_OK, "Success");
reconstructed.setHeaders(response.getAllHeaders());
HttpEntity entity = new InputStreamEntity(combinedStream, -1); reconstructed.setEntity(new CombinedEntity(resource, instream));
response.setEntity(entity); return reconstructed;
response.setHeaders(originalResponse.getAllHeaders());
return response;
} }
} }

View File

@ -239,10 +239,9 @@ public class TestCachingHttpClient {
responseProtocolValidationIsCalled(); responseProtocolValidationIsCalled();
getMockResponseReader(); getMockResponseReader();
responseIsTooLarge(false); responseRead();
byte[] buf = responseReaderReturnsBufferOfSize(100); responseLimitReached(false);
responseGetResource();
generateResource(buf);
storeInCacheWasCalled(); storeInCacheWasCalled();
responseIsGeneratedFromCache(); responseIsGeneratedFromCache();
responseStatusLineIsInspectable(); responseStatusLineIsInspectable();
@ -553,8 +552,9 @@ public class TestCachingHttpClient {
getCurrentDateReturns(responseDate); getCurrentDateReturns(responseDate);
responsePolicyAllowsCaching(true); responsePolicyAllowsCaching(true);
getMockResponseReader(); getMockResponseReader();
responseIsTooLarge(true); responseRead();
readerReturnsReconstructedResponse(); responseLimitReached(true);
responseGetReconstructed();
replayMocks(); replayMocks();
@ -575,9 +575,9 @@ public class TestCachingHttpClient {
getCurrentDateReturns(responseDate); getCurrentDateReturns(responseDate);
responsePolicyAllowsCaching(true); responsePolicyAllowsCaching(true);
getMockResponseReader(); getMockResponseReader();
responseIsTooLarge(false); responseRead();
byte[] buf = responseReaderReturnsBufferOfSize(100); responseLimitReached(false);
generateResource(buf); responseGetResource();
storeInCacheWasCalled(); storeInCacheWasCalled();
responseIsGeneratedFromCache(); responseIsGeneratedFromCache();
responseStatusLineIsInspectable(); responseStatusLineIsInspectable();
@ -961,7 +961,7 @@ public class TestCachingHttpClient {
resp.setEntity(new ByteArrayEntity(bytes)); resp.setEntity(new ByteArrayEntity(bytes));
resp.setHeader("Content-Length","128"); resp.setHeader("Content-Length","128");
HttpResponse result = impl.correctIncompleteResponse(resp, bytes); HttpResponse result = impl.correctIncompleteResponse(resp, new HeapResource(bytes));
Assert.assertTrue(HttpTestUtils.semanticallyTransparent(resp, result)); Assert.assertTrue(HttpTestUtils.semanticallyTransparent(resp, result));
} }
@ -974,7 +974,7 @@ public class TestCachingHttpClient {
resp.setHeader("Content-Length","128"); resp.setHeader("Content-Length","128");
resp.setHeader("Content-Range","bytes 0-127/255"); resp.setHeader("Content-Range","bytes 0-127/255");
HttpResponse result = impl.correctIncompleteResponse(resp, bytes); HttpResponse result = impl.correctIncompleteResponse(resp, new HeapResource(bytes));
Assert.assertTrue(HttpTestUtils.semanticallyTransparent(resp, result)); Assert.assertTrue(HttpTestUtils.semanticallyTransparent(resp, result));
} }
@ -986,7 +986,7 @@ public class TestCachingHttpClient {
resp.setEntity(new ByteArrayEntity(bytes)); resp.setEntity(new ByteArrayEntity(bytes));
resp.setHeader("Content-Length","256"); resp.setHeader("Content-Length","256");
HttpResponse result = impl.correctIncompleteResponse(resp, bytes); HttpResponse result = impl.correctIncompleteResponse(resp, new HeapResource(bytes));
Assert.assertTrue(HttpStatus.SC_BAD_GATEWAY == result.getStatusLine().getStatusCode()); Assert.assertTrue(HttpStatus.SC_BAD_GATEWAY == result.getStatusLine().getStatusCode());
} }
@ -998,7 +998,7 @@ public class TestCachingHttpClient {
resp.setEntity(new ByteArrayEntity(bytes)); resp.setEntity(new ByteArrayEntity(bytes));
resp.setHeader("Content-Length","256"); resp.setHeader("Content-Length","256");
HttpResponse result = impl.correctIncompleteResponse(resp, bytes); HttpResponse result = impl.correctIncompleteResponse(resp, new HeapResource(bytes));
Assert.assertTrue(HttpTestUtils.semanticallyTransparent(resp, result)); Assert.assertTrue(HttpTestUtils.semanticallyTransparent(resp, result));
} }
@ -1009,7 +1009,7 @@ public class TestCachingHttpClient {
byte[] bytes = HttpTestUtils.getRandomBytes(128); byte[] bytes = HttpTestUtils.getRandomBytes(128);
resp.setEntity(new ByteArrayEntity(bytes)); resp.setEntity(new ByteArrayEntity(bytes));
HttpResponse result = impl.correctIncompleteResponse(resp, bytes); HttpResponse result = impl.correctIncompleteResponse(resp, new HeapResource(bytes));
Assert.assertTrue(HttpTestUtils.semanticallyTransparent(resp, result)); Assert.assertTrue(HttpTestUtils.semanticallyTransparent(resp, result));
} }
@ -1021,7 +1021,7 @@ public class TestCachingHttpClient {
resp.setHeader("Content-Length","foo"); resp.setHeader("Content-Length","foo");
resp.setEntity(new ByteArrayEntity(bytes)); resp.setEntity(new ByteArrayEntity(bytes));
HttpResponse result = impl.correctIncompleteResponse(resp, bytes); HttpResponse result = impl.correctIncompleteResponse(resp, new HeapResource(bytes));
Assert.assertTrue(HttpTestUtils.semanticallyTransparent(resp, result)); Assert.assertTrue(HttpTestUtils.semanticallyTransparent(resp, result));
} }
@ -1033,7 +1033,7 @@ public class TestCachingHttpClient {
resp.setEntity(new ByteArrayEntity(bytes)); resp.setEntity(new ByteArrayEntity(bytes));
resp.setHeader("Content-Length","256"); resp.setHeader("Content-Length","256");
HttpResponse result = impl.correctIncompleteResponse(resp, bytes); HttpResponse result = impl.correctIncompleteResponse(resp, new HeapResource(bytes));
Header ctype = result.getFirstHeader("Content-Type"); Header ctype = result.getFirstHeader("Content-Type");
Assert.assertEquals("text/plain;charset=UTF-8", ctype.getValue()); Assert.assertEquals("text/plain;charset=UTF-8", ctype.getValue());
} }
@ -1046,7 +1046,7 @@ public class TestCachingHttpClient {
resp.setEntity(new ByteArrayEntity(bytes)); resp.setEntity(new ByteArrayEntity(bytes));
resp.setHeader("Content-Length","256"); resp.setHeader("Content-Length","256");
HttpResponse result = impl.correctIncompleteResponse(resp, bytes); HttpResponse result = impl.correctIncompleteResponse(resp, new HeapResource(bytes));
int clen = Integer.parseInt(result.getFirstHeader("Content-Length").getValue()); int clen = Integer.parseInt(result.getFirstHeader("Content-Length").getValue());
Assert.assertTrue(clen > 0); Assert.assertTrue(clen > 0);
HttpEntity body = result.getEntity(); HttpEntity body = result.getEntity();
@ -1130,6 +1130,7 @@ public class TestCachingHttpClient {
private void getMockResponseReader() { private void getMockResponseReader() {
EasyMock.expect(impl.getResponseReader( EasyMock.expect(impl.getResponseReader(
EasyMock.<HttpRequest>anyObject(),
EasyMock.<HttpResponse>anyObject())).andReturn(mockResponseReader); EasyMock.<HttpResponse>anyObject())).andReturn(mockResponseReader);
} }
@ -1147,19 +1148,20 @@ public class TestCachingHttpClient {
.andReturn(null).anyTimes(); .andReturn(null).anyTimes();
} }
private byte[] responseReaderReturnsBufferOfSize(int bufferSize) { private void responseRead() throws Exception {
byte[] buffer = new byte[bufferSize]; mockResponseReader.readResponse();
EasyMock.expect(mockResponseReader.getResponseBytes()).andReturn(buffer);
return buffer;
} }
private void readerReturnsReconstructedResponse() { private void responseLimitReached(boolean limitReached) throws Exception {
EasyMock.expect(mockResponseReader.getReconstructedResponse()).andReturn( EasyMock.expect(mockResponseReader.isLimitReached()).andReturn(limitReached);
mockReconstructedResponse);
} }
private void responseIsTooLarge(boolean tooLarge) throws Exception { private void responseGetResource() throws Exception {
EasyMock.expect(mockResponseReader.isResponseTooLarge()).andReturn(tooLarge); EasyMock.expect(mockResponseReader.getResource()).andReturn(new HeapResource(new byte[] {} ));
}
private void responseGetReconstructed() throws Exception {
EasyMock.expect(mockResponseReader.getReconstructedResponse()).andReturn(mockReconstructedResponse);
} }
private void backendCallWasMadeWithRequest(HttpRequest request) throws IOException { private void backendCallWasMadeWithRequest(HttpRequest request) throws IOException {
@ -1245,13 +1247,6 @@ public class TestCachingHttpClient {
mockCache.putEntry(theURI, entry); mockCache.putEntry(theURI, entry);
} }
private void generateResource(byte [] b) throws IOException {
EasyMock.expect(
mockResourceFactory.generate(
EasyMock.<String>anyObject(),
EasyMock.same(b))).andReturn(new HeapResource(b));
}
private void copyResource() throws IOException { private void copyResource() throws IOException {
EasyMock.expect( EasyMock.expect(
mockResourceFactory.copy( mockResourceFactory.copy(

View File

@ -0,0 +1,59 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.http.impl.client.cache;
import java.io.ByteArrayInputStream;
import org.apache.http.client.cache.Resource;
import org.apache.http.util.EntityUtils;
import org.easymock.classextension.EasyMock;
import org.junit.Assert;
import org.junit.Test;
public class TestCombinedEntity {
@Test
public void testCombinedEntityBasics() throws Exception {
Resource resource = EasyMock.createMock(Resource.class);
EasyMock.expect(resource.getInputStream()).andReturn(
new ByteArrayInputStream(new byte[] { 1, 2, 3, 4, 5 }));
resource.dispose();
EasyMock.replay(resource);
ByteArrayInputStream instream = new ByteArrayInputStream(new byte[] { 6, 7, 8, 9, 10 });
CombinedEntity entity = new CombinedEntity(resource, instream);
Assert.assertEquals(-1, entity.getContentLength());
Assert.assertFalse(entity.isRepeatable());
Assert.assertTrue(entity.isStreaming());
byte[] result = EntityUtils.toByteArray(entity);
Assert.assertArrayEquals(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, result);
EasyMock.verify(resource);
}
}

View File

@ -1,118 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.http.impl.client.cache;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import org.easymock.classextension.EasyMock;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
/**
*/
public class TestCombinedInputStream {
private InputStream mockInputStream1;
private InputStream mockInputStream2;
private CombinedInputStream impl;
@Before
public void setUp() {
mockInputStream1 = EasyMock.createMock(InputStream.class);
mockInputStream2 = EasyMock.createMock(InputStream.class);
impl = new CombinedInputStream(mockInputStream1, mockInputStream2);
}
@Test
public void testCreatingInputStreamWithNullInputFails() {
boolean gotex1 = false;
boolean gotex2 = false;
try {
impl = new CombinedInputStream(null, mockInputStream2);
} catch (Exception ex) {
gotex1 = true;
}
try {
impl = new CombinedInputStream(mockInputStream1, null);
} catch (Exception ex) {
gotex2 = true;
}
Assert.assertTrue(gotex1);
Assert.assertTrue(gotex2);
}
@Test
public void testAvailableReturnsCorrectSize() throws Exception {
ByteArrayInputStream s1 = new ByteArrayInputStream(new byte[] { 1, 1, 1, 1, 1 });
ByteArrayInputStream s2 = new ByteArrayInputStream(new byte[] { 1, 1, 1, 1, 1 });
impl = new CombinedInputStream(s1, s2);
int avail = impl.available();
Assert.assertEquals(10, avail);
}
@Test
public void testFirstEmptyStreamReadsFromOtherStream() throws Exception {
org.easymock.EasyMock.expect(mockInputStream1.read()).andReturn(-1);
org.easymock.EasyMock.expect(mockInputStream2.read()).andReturn(500);
replayMocks();
int result = impl.read();
verifyMocks();
Assert.assertEquals(500, result);
}
@Test
public void testThatWeReadTheFirstInputStream() throws Exception {
org.easymock.EasyMock.expect(mockInputStream1.read()).andReturn(500);
replayMocks();
int result = impl.read();
verifyMocks();
Assert.assertEquals(500, result);
}
private void verifyMocks() {
EasyMock.verify(mockInputStream1, mockInputStream2);
}
private void replayMocks() {
EasyMock.replay(mockInputStream1, mockInputStream2);
}
}

View File

@ -32,8 +32,11 @@ import java.io.InputStream;
import org.apache.http.Header; import org.apache.http.Header;
import org.apache.http.HttpEntity; import org.apache.http.HttpEntity;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse; import org.apache.http.HttpResponse;
import org.apache.http.ProtocolVersion; import org.apache.http.HttpVersion;
import org.apache.http.message.BasicRequestLine;
import org.apache.http.util.EntityUtils;
import org.easymock.classextension.EasyMock; import org.easymock.classextension.EasyMock;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
@ -41,211 +44,134 @@ import org.junit.Test;
public class TestSizeLimitedResponseReader { public class TestSizeLimitedResponseReader {
private static final int MAX_SIZE = 4; private static final long MAX_SIZE = 4;
private SizeLimitedResponseReader impl; private SizeLimitedResponseReader impl;
private HttpRequest mockRequest;
private HttpResponse mockResponse; private HttpResponse mockResponse;
private HttpEntity mockEntity; private HttpEntity mockEntity;
private InputStream mockInputStream;
private ProtocolVersion mockVersion;
private boolean mockedImpl; private boolean mockedImpl;
@Before @Before
public void setUp() { public void setUp() {
mockRequest = EasyMock.createMock(HttpRequest.class);
mockResponse = EasyMock.createMock(HttpResponse.class); mockResponse = EasyMock.createMock(HttpResponse.class);
mockEntity = EasyMock.createMock(HttpEntity.class); mockEntity = EasyMock.createMock(HttpEntity.class);
mockInputStream = EasyMock.createMock(InputStream.class);
mockVersion = EasyMock.createMock(ProtocolVersion.class);
} }
@Test @Test
public void testLargeResponseIsTooLarge() throws Exception { public void testLargeResponseIsTooLarge() throws Exception {
byte[] buf = new byte[] { 1, 2, 3, 4, 5};
responseHasValidEntity(); requestReturnsRequestLine();
entityHasValidContentStream(); responseReturnsProtocolVersion();
inputStreamReturnsValidBytes(5); responseReturnsHeaders();
responseReturnsContent(new ByteArrayInputStream(buf));
getReader(); initReader();
replayMocks(); replayMocks();
boolean tooLarge = impl.isResponseTooLarge();
byte[] result = impl.getResponseBytes(); impl.readResponse();
boolean tooLarge = impl.isLimitReached();
HttpResponse response = impl.getReconstructedResponse();
byte[] result = EntityUtils.toByteArray(response.getEntity());
verifyMocks(); verifyMocks();
Assert.assertTrue(tooLarge); Assert.assertTrue(tooLarge);
Assert.assertArrayEquals(buf, result);
Assert.assertArrayEquals(new byte[] { 1, 1, 1, 1, 1 }, result);
} }
@Test @Test
public void testExactSizeResponseIsNotTooLarge() throws Exception { public void testExactSizeResponseIsNotTooLarge() throws Exception {
responseHasValidEntity(); byte[] buf = new byte[] { 1, 2, 3, 4 };
entityHasValidContentStream(); requestReturnsRequestLine();
inputStreamReturnsValidBytes(4); responseReturnsProtocolVersion();
inputStreamReturnsEndOfStream(); responseReturnsHeaders();
responseReturnsContent(new ByteArrayInputStream(buf));
getReader(); initReader();
replayMocks(); replayMocks();
boolean tooLarge = impl.isResponseTooLarge();
byte[] result = impl.getResponseBytes(); impl.readResponse();
boolean tooLarge = impl.isLimitReached();
HttpResponse response = impl.getReconstructedResponse();
byte[] result = EntityUtils.toByteArray(response.getEntity());
verifyMocks(); verifyMocks();
Assert.assertFalse(tooLarge); Assert.assertFalse(tooLarge);
Assert.assertArrayEquals(buf, result);
Assert.assertArrayEquals(new byte[] { 1, 1, 1, 1 }, result);
} }
@Test @Test
public void testSmallResponseIsNotTooLarge() throws Exception { public void testSmallResponseIsNotTooLarge() throws Exception {
responseHasValidEntity(); byte[] buf = new byte[] { 1, 2, 3 };
entityHasValidContentStream(); requestReturnsRequestLine();
responseReturnsProtocolVersion();
org.easymock.EasyMock.expect(mockInputStream.read()).andReturn(1).times(3); responseReturnsHeaders();
responseReturnsContent(new ByteArrayInputStream(buf));
org.easymock.EasyMock.expect(mockInputStream.read()).andReturn(-1).times(2); initReader();
getReader();
replayMocks(); replayMocks();
boolean tooLarge = impl.isResponseTooLarge();
byte[] result = impl.getResponseBytes(); impl.readResponse();
boolean tooLarge = impl.isLimitReached();
HttpResponse response = impl.getReconstructedResponse();
byte[] result = EntityUtils.toByteArray(response.getEntity());
verifyMocks(); verifyMocks();
Assert.assertFalse(tooLarge); Assert.assertFalse(tooLarge);
Assert.assertArrayEquals(buf, result);
Assert.assertArrayEquals(new byte[] { 1, 1, 1 }, result);
} }
@Test @Test
public void testResponseWithNoEntityIsNotTooLarge() throws Exception { public void testResponseWithNoEntityIsNotTooLarge() throws Exception {
responseHasNullEntity(); responseHasNullEntity();
getReader(); initReader();
replayMocks(); replayMocks();
boolean tooLarge = impl.isResponseTooLarge(); impl.readResponse();
boolean tooLarge = impl.isLimitReached();
verifyMocks(); verifyMocks();
Assert.assertFalse(tooLarge); Assert.assertFalse(tooLarge);
} }
@Test private void responseReturnsContent(InputStream buffer) throws IOException {
public void testReconstructedSmallResponseHasCorrectLength() throws Exception { EasyMock.expect(mockResponse.getEntity()).andReturn(mockEntity);
EasyMock.expect(mockEntity.getContent()).andReturn(buffer);
byte[] expectedArray = new byte[] { 1, 1, 1, 1 };
InputStream stream = new ByteArrayInputStream(new byte[] {});
responseReturnsHeaders();
responseReturnsProtocolVersion();
getReader();
mockImplMethods("getResponseBytes", "getContentInputStream");
getContentInputStreamReturns(stream);
getResponseBytesReturns(expectedArray);
replayMocks();
HttpResponse response = impl.getReconstructedResponse();
verifyMocks();
Assert.assertNotNull("Response should not be null", response);
InputStream resultStream = response.getEntity().getContent();
byte[] buffer = new byte[expectedArray.length];
resultStream.read(buffer);
Assert.assertArrayEquals(expectedArray, buffer);
} }
private void getContentInputStreamReturns(InputStream inputStream) { private void requestReturnsRequestLine() {
org.easymock.EasyMock.expect(impl.getContentInputStream()).andReturn(inputStream); EasyMock.expect(mockRequest.getRequestLine()).andReturn(
} new BasicRequestLine("GET", "/", HttpVersion.HTTP_1_1));
@Test
public void testReconstructedLargeResponseHasCorrectLength() throws Exception {
byte[] expectedArray = new byte[] { 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1 };
byte[] arrayAfterConsumedBytes = new byte[] { 1, 1, 1, 1, 1, 1, 1 };
byte[] smallArray = new byte[] { 2, 2, 2, 2, };
InputStream is = new ByteArrayInputStream(arrayAfterConsumedBytes);
responseReturnsHeaders();
responseReturnsProtocolVersion();
getReader();
mockImplMethods("getResponseBytes", "getContentInputStream");
getResponseBytesReturns(smallArray);
getContentInputStreamReturns(is);
replayMocks();
HttpResponse response = impl.getReconstructedResponse();
verifyMocks();
InputStream resultStream = response.getEntity().getContent();
byte[] buffer = new byte[expectedArray.length];
resultStream.read(buffer);
Assert.assertArrayEquals(expectedArray, buffer);
}
private void getResponseBytesReturns(byte[] expectedArray) {
org.easymock.EasyMock.expect(impl.getResponseBytes()).andReturn(expectedArray);
}
private void responseReturnsHeaders() {
org.easymock.EasyMock.expect(mockResponse.getAllHeaders()).andReturn(new Header[] {});
}
private void entityHasValidContentStream() throws IOException {
org.easymock.EasyMock.expect(mockEntity.getContent()).andReturn(mockInputStream);
}
private void inputStreamReturnsEndOfStream() throws IOException {
org.easymock.EasyMock.expect(mockInputStream.read()).andReturn(-1);
}
private void responseHasValidEntity() {
org.easymock.EasyMock.expect(mockResponse.getEntity()).andReturn(mockEntity);
} }
private void responseReturnsProtocolVersion() { private void responseReturnsProtocolVersion() {
org.easymock.EasyMock.expect(mockResponse.getProtocolVersion()).andReturn(mockVersion); EasyMock.expect(mockResponse.getProtocolVersion()).andReturn(HttpVersion.HTTP_1_1);
} }
private void inputStreamReturnsValidBytes(int times) throws IOException { private void responseReturnsHeaders() {
org.easymock.EasyMock.expect(mockInputStream.read()).andReturn(1).times(times); EasyMock.expect(mockResponse.getAllHeaders()).andReturn(new Header[] {});
} }
private void responseHasNullEntity() { private void responseHasNullEntity() {
org.easymock.EasyMock.expect(mockResponse.getEntity()).andReturn(null); EasyMock.expect(mockResponse.getEntity()).andReturn(null);
} }
private void verifyMocks() { private void verifyMocks() {
EasyMock.verify(mockResponse, mockEntity, mockInputStream, mockVersion); EasyMock.verify(mockRequest, mockResponse, mockEntity);
if (mockedImpl) { if (mockedImpl) {
EasyMock.verify(impl); EasyMock.verify(impl);
} }
} }
private void replayMocks() { private void replayMocks() {
EasyMock.replay(mockResponse, mockEntity, mockInputStream, mockVersion); EasyMock.replay(mockRequest, mockResponse, mockEntity);
if (mockedImpl) { if (mockedImpl) {
EasyMock.replay(impl); EasyMock.replay(impl);
} }
} }
private void getReader() { private void initReader() {
impl = new SizeLimitedResponseReader(MAX_SIZE, mockResponse); impl = new SizeLimitedResponseReader(
} new HeapResourceFactory(), MAX_SIZE, mockRequest, mockResponse);
private void mockImplMethods(String... methods) {
mockedImpl = true;
impl = EasyMock.createMockBuilder(SizeLimitedResponseReader.class).withConstructor(
MAX_SIZE, mockResponse).addMockedMethods(methods).createMock();
} }
} }