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;
import java.io.IOException;
import java.io.InputStream;
/**
* Generates {@link Resource} instances.
@ -35,7 +36,7 @@ import java.io.IOException;
*/
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;

View File

@ -602,8 +602,7 @@ public class CachingHttpClient implements HttpClient {
variants);
}
HttpResponse correctIncompleteResponse(HttpResponse resp,
byte[] bodyBytes) {
HttpResponse correctIncompleteResponse(HttpResponse resp, Resource resource) {
int status = resp.getStatusLine().getStatusCode();
if (status != HttpStatus.SC_OK
&& status != HttpStatus.SC_PARTIAL_CONTENT) {
@ -617,13 +616,14 @@ public class CachingHttpClient implements HttpClient {
} catch (NumberFormatException nfe) {
return resp;
}
if (bodyBytes.length >= contentLength) return resp;
if (resource.length() >= contentLength) return resp;
HttpResponse error =
new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_BAD_GATEWAY, "Bad Gateway");
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();
error.setHeader("Content-Length", String.format("%d",msgBytes.length));
error.setHeader("Content-Length", Integer.toString(msgBytes.length));
error.setEntity(new ByteArrayEntity(msgBytes));
return error;
}
@ -643,19 +643,17 @@ public class CachingHttpClient implements HttpClient {
HttpResponse corrected = backendResponse;
if (cacheable) {
SizeLimitedResponseReader responseReader = getResponseReader(backendResponse);
SizeLimitedResponseReader responseReader = getResponseReader(request, backendResponse);
responseReader.readResponse();
if (responseReader.isResponseTooLarge()) {
if (responseReader.isLimitReached()) {
return responseReader.getReconstructedResponse();
}
byte[] responseBytes = responseReader.getResponseBytes();
corrected = correctIncompleteResponse(backendResponse,
responseBytes);
Resource resource = responseReader.getResource();
corrected = correctIncompleteResponse(backendResponse, resource);
int correctedStatus = corrected.getStatusLine().getStatusCode();
if (HttpStatus.SC_BAD_GATEWAY != correctedStatus) {
Resource resource = resourceFactory.generate(
request.getRequestLine().getUri(), responseBytes);
HttpCacheEntry entry = new HttpCacheEntry(
requestDate,
responseDate,
@ -673,8 +671,9 @@ public class CachingHttpClient implements HttpClient {
return corrected;
}
SizeLimitedResponseReader getResponseReader(HttpResponse backEndResponse) {
return new SizeLimitedResponseReader(maxObjectSizeBytes, backEndResponse);
SizeLimitedResponseReader getResponseReader(HttpRequest request, HttpResponse 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.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
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.ResourceFactory;
@ -67,18 +69,33 @@ public class FileResourceFactory implements ResourceFactory {
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);
FileOutputStream outstream = new FileOutputStream(file);
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 {
outstream.close();
}
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);
if (resource instanceof FileResource) {

View File

@ -28,8 +28,10 @@ package org.apache.http.impl.client.cache;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
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.ResourceFactory;
@ -41,11 +43,28 @@ import org.apache.http.client.cache.ResourceFactory;
@Immutable
public class HeapResourceFactory implements ResourceFactory {
public Resource generate(final String requestId, final byte[] body) throws IOException {
return new HeapResource(body);
public Resource generate(
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;
if (resource instanceof HeapResource) {
body = ((HeapResource) resource).getByteArray();

View File

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

View File

@ -239,10 +239,9 @@ public class TestCachingHttpClient {
responseProtocolValidationIsCalled();
getMockResponseReader();
responseIsTooLarge(false);
byte[] buf = responseReaderReturnsBufferOfSize(100);
generateResource(buf);
responseRead();
responseLimitReached(false);
responseGetResource();
storeInCacheWasCalled();
responseIsGeneratedFromCache();
responseStatusLineIsInspectable();
@ -553,8 +552,9 @@ public class TestCachingHttpClient {
getCurrentDateReturns(responseDate);
responsePolicyAllowsCaching(true);
getMockResponseReader();
responseIsTooLarge(true);
readerReturnsReconstructedResponse();
responseRead();
responseLimitReached(true);
responseGetReconstructed();
replayMocks();
@ -575,9 +575,9 @@ public class TestCachingHttpClient {
getCurrentDateReturns(responseDate);
responsePolicyAllowsCaching(true);
getMockResponseReader();
responseIsTooLarge(false);
byte[] buf = responseReaderReturnsBufferOfSize(100);
generateResource(buf);
responseRead();
responseLimitReached(false);
responseGetResource();
storeInCacheWasCalled();
responseIsGeneratedFromCache();
responseStatusLineIsInspectable();
@ -961,7 +961,7 @@ public class TestCachingHttpClient {
resp.setEntity(new ByteArrayEntity(bytes));
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));
}
@ -974,7 +974,7 @@ public class TestCachingHttpClient {
resp.setHeader("Content-Length","128");
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));
}
@ -986,7 +986,7 @@ public class TestCachingHttpClient {
resp.setEntity(new ByteArrayEntity(bytes));
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());
}
@ -998,7 +998,7 @@ public class TestCachingHttpClient {
resp.setEntity(new ByteArrayEntity(bytes));
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));
}
@ -1009,7 +1009,7 @@ public class TestCachingHttpClient {
byte[] bytes = HttpTestUtils.getRandomBytes(128);
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));
}
@ -1021,7 +1021,7 @@ public class TestCachingHttpClient {
resp.setHeader("Content-Length","foo");
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));
}
@ -1033,7 +1033,7 @@ public class TestCachingHttpClient {
resp.setEntity(new ByteArrayEntity(bytes));
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");
Assert.assertEquals("text/plain;charset=UTF-8", ctype.getValue());
}
@ -1046,7 +1046,7 @@ public class TestCachingHttpClient {
resp.setEntity(new ByteArrayEntity(bytes));
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());
Assert.assertTrue(clen > 0);
HttpEntity body = result.getEntity();
@ -1130,6 +1130,7 @@ public class TestCachingHttpClient {
private void getMockResponseReader() {
EasyMock.expect(impl.getResponseReader(
EasyMock.<HttpRequest>anyObject(),
EasyMock.<HttpResponse>anyObject())).andReturn(mockResponseReader);
}
@ -1147,19 +1148,20 @@ public class TestCachingHttpClient {
.andReturn(null).anyTimes();
}
private byte[] responseReaderReturnsBufferOfSize(int bufferSize) {
byte[] buffer = new byte[bufferSize];
EasyMock.expect(mockResponseReader.getResponseBytes()).andReturn(buffer);
return buffer;
private void responseRead() throws Exception {
mockResponseReader.readResponse();
}
private void readerReturnsReconstructedResponse() {
EasyMock.expect(mockResponseReader.getReconstructedResponse()).andReturn(
mockReconstructedResponse);
private void responseLimitReached(boolean limitReached) throws Exception {
EasyMock.expect(mockResponseReader.isLimitReached()).andReturn(limitReached);
}
private void responseIsTooLarge(boolean tooLarge) throws Exception {
EasyMock.expect(mockResponseReader.isResponseTooLarge()).andReturn(tooLarge);
private void responseGetResource() throws Exception {
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 {
@ -1245,13 +1247,6 @@ public class TestCachingHttpClient {
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 {
EasyMock.expect(
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.HttpEntity;
import org.apache.http.HttpRequest;
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.junit.Assert;
import org.junit.Before;
@ -41,211 +44,134 @@ import org.junit.Test;
public class TestSizeLimitedResponseReader {
private static final int MAX_SIZE = 4;
private static final long MAX_SIZE = 4;
private SizeLimitedResponseReader impl;
private HttpRequest mockRequest;
private HttpResponse mockResponse;
private HttpEntity mockEntity;
private InputStream mockInputStream;
private ProtocolVersion mockVersion;
private boolean mockedImpl;
@Before
public void setUp() {
mockRequest = EasyMock.createMock(HttpRequest.class);
mockResponse = EasyMock.createMock(HttpResponse.class);
mockEntity = EasyMock.createMock(HttpEntity.class);
mockInputStream = EasyMock.createMock(InputStream.class);
mockVersion = EasyMock.createMock(ProtocolVersion.class);
}
@Test
public void testLargeResponseIsTooLarge() throws Exception {
responseHasValidEntity();
entityHasValidContentStream();
inputStreamReturnsValidBytes(5);
getReader();
byte[] buf = new byte[] { 1, 2, 3, 4, 5};
requestReturnsRequestLine();
responseReturnsProtocolVersion();
responseReturnsHeaders();
responseReturnsContent(new ByteArrayInputStream(buf));
initReader();
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();
Assert.assertTrue(tooLarge);
Assert.assertArrayEquals(new byte[] { 1, 1, 1, 1, 1 }, result);
Assert.assertArrayEquals(buf, result);
}
@Test
public void testExactSizeResponseIsNotTooLarge() throws Exception {
responseHasValidEntity();
entityHasValidContentStream();
inputStreamReturnsValidBytes(4);
inputStreamReturnsEndOfStream();
getReader();
byte[] buf = new byte[] { 1, 2, 3, 4 };
requestReturnsRequestLine();
responseReturnsProtocolVersion();
responseReturnsHeaders();
responseReturnsContent(new ByteArrayInputStream(buf));
initReader();
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();
Assert.assertFalse(tooLarge);
Assert.assertArrayEquals(new byte[] { 1, 1, 1, 1 }, result);
Assert.assertArrayEquals(buf, result);
}
@Test
public void testSmallResponseIsNotTooLarge() throws Exception {
responseHasValidEntity();
entityHasValidContentStream();
org.easymock.EasyMock.expect(mockInputStream.read()).andReturn(1).times(3);
org.easymock.EasyMock.expect(mockInputStream.read()).andReturn(-1).times(2);
getReader();
byte[] buf = new byte[] { 1, 2, 3 };
requestReturnsRequestLine();
responseReturnsProtocolVersion();
responseReturnsHeaders();
responseReturnsContent(new ByteArrayInputStream(buf));
initReader();
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();
Assert.assertFalse(tooLarge);
Assert.assertArrayEquals(new byte[] { 1, 1, 1 }, result);
Assert.assertArrayEquals(buf, result);
}
@Test
public void testResponseWithNoEntityIsNotTooLarge() throws Exception {
responseHasNullEntity();
getReader();
initReader();
replayMocks();
boolean tooLarge = impl.isResponseTooLarge();
impl.readResponse();
boolean tooLarge = impl.isLimitReached();
verifyMocks();
Assert.assertFalse(tooLarge);
}
@Test
public void testReconstructedSmallResponseHasCorrectLength() throws Exception {
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 responseReturnsContent(InputStream buffer) throws IOException {
EasyMock.expect(mockResponse.getEntity()).andReturn(mockEntity);
EasyMock.expect(mockEntity.getContent()).andReturn(buffer);
}
private void getContentInputStreamReturns(InputStream inputStream) {
org.easymock.EasyMock.expect(impl.getContentInputStream()).andReturn(inputStream);
}
@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 requestReturnsRequestLine() {
EasyMock.expect(mockRequest.getRequestLine()).andReturn(
new BasicRequestLine("GET", "/", HttpVersion.HTTP_1_1));
}
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 {
org.easymock.EasyMock.expect(mockInputStream.read()).andReturn(1).times(times);
private void responseReturnsHeaders() {
EasyMock.expect(mockResponse.getAllHeaders()).andReturn(new Header[] {});
}
private void responseHasNullEntity() {
org.easymock.EasyMock.expect(mockResponse.getEntity()).andReturn(null);
EasyMock.expect(mockResponse.getEntity()).andReturn(null);
}
private void verifyMocks() {
EasyMock.verify(mockResponse, mockEntity, mockInputStream, mockVersion);
EasyMock.verify(mockRequest, mockResponse, mockEntity);
if (mockedImpl) {
EasyMock.verify(impl);
}
}
private void replayMocks() {
EasyMock.replay(mockResponse, mockEntity, mockInputStream, mockVersion);
EasyMock.replay(mockRequest, mockResponse, mockEntity);
if (mockedImpl) {
EasyMock.replay(impl);
}
}
private void getReader() {
impl = new SizeLimitedResponseReader(MAX_SIZE, mockResponse);
}
private void mockImplMethods(String... methods) {
mockedImpl = true;
impl = EasyMock.createMockBuilder(SizeLimitedResponseReader.class).withConstructor(
MAX_SIZE, mockResponse).addMockedMethods(methods).createMock();
private void initReader() {
impl = new SizeLimitedResponseReader(
new HeapResourceFactory(), MAX_SIZE, mockRequest, mockResponse);
}
}