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:
parent
e0a296f6b6
commit
a840e65989
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
105
httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CombinedEntity.java
vendored
Normal file
105
httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CombinedEntity.java
vendored
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(
|
||||||
|
|
59
httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCombinedEntity.java
vendored
Normal file
59
httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCombinedEntity.java
vendored
Normal 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue