Author: jawi
Date: Mon Nov 25 13:41:37 2013
New Revision: 1545277
URL: http://svn.apache.org/r1545277
Log:
ACE-437 - Ensure content-length header is not sent for DPs:
- simplify the ContentRangeResponseWrapper to no longer stream to an
intermediary file to determine the content length;
- ensure the agent is no longer depending on the content-length header
and does not make assumptions about the length;
- removed the reporting of total bytes as we no longer can provide this
information.
Modified:
ace/trunk/org.apache.ace.agent.itest/src/org/apache/ace/agent/itest/CustomAgentControllerTest.java
ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/DownloadHandle.java
ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/ContentRangeInputStream.java
ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/DefaultController.java
ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/DiscoveryHandlerImpl.java
ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/DownloadCallableImpl.java
ace/trunk/org.apache.ace.agent/test/org/apache/ace/agent/impl/ContentRangeInputStreamTest.java
ace/trunk/org.apache.ace.agent/test/org/apache/ace/agent/impl/DownloadHandleImplTest.java
ace/trunk/org.apache.ace.agent/test/org/apache/ace/agent/impl/DownloadHandlerTest.java
ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/servlet/ContentRangeResponseWrapper.java
ace/trunk/org.apache.ace.deployment/test/org/apache/ace/deployment/servlet/DeploymentServletTest.java
Modified:
ace/trunk/org.apache.ace.agent.itest/src/org/apache/ace/agent/itest/CustomAgentControllerTest.java
URL:
http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.agent.itest/src/org/apache/ace/agent/itest/CustomAgentControllerTest.java?rev=1545277&r1=1545276&r2=1545277&view=diff
==============================================================================
---
ace/trunk/org.apache.ace.agent.itest/src/org/apache/ace/agent/itest/CustomAgentControllerTest.java
(original)
+++
ace/trunk/org.apache.ace.agent.itest/src/org/apache/ace/agent/itest/CustomAgentControllerTest.java
Mon Nov 25 13:41:37 2013
@@ -200,8 +200,8 @@ public class CustomAgentControllerTest e
Future<DownloadResult> future = downloadHandle.start(new
DownloadProgressListener() {
@Override
- public void progress(long bytesRead, long totalBytes) {
- System.out.printf("Download progress: %d of %d
bytes read...%n", bytesRead, totalBytes);
+ public void progress(long bytesRead) {
+ System.out.printf("Download progress: %d bytes
read...%n", bytesRead);
}
});
// Block until the download is complete...
Modified:
ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/DownloadHandle.java
URL:
http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/DownloadHandle.java?rev=1545277&r1=1545276&r2=1545277&view=diff
==============================================================================
--- ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/DownloadHandle.java
(original)
+++ ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/DownloadHandle.java
Mon Nov 25 13:41:37 2013
@@ -42,10 +42,8 @@ public interface DownloadHandle {
*
* @param bytesRead
* The number of bytes that has been received so far;
- * @param totalBytes
- * The total length of the content or -1 if unknown.
*/
- void progress(long bytesRead, long totalBytes);
+ void progress(long bytesRead);
}
/**
Modified:
ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/ContentRangeInputStream.java
URL:
http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/ContentRangeInputStream.java?rev=1545277&r1=1545276&r2=1545277&view=diff
==============================================================================
---
ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/ContentRangeInputStream.java
(original)
+++
ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/ContentRangeInputStream.java
Mon Nov 25 13:41:37 2013
@@ -34,6 +34,15 @@ import org.apache.ace.agent.RetryAfterEx
/**
* Abstraction for {@link HttpURLConnection}s that might use content range
headers, or partial responses, to return the
* contents of an URL.
+ * <p>
+ * This implementation is capable of handling partial content requests, using
the HTTP 216 response code, and tries to
+ * follow the recommendations as specified in RFC 2616 as closely as possible.
This implementation deviates from the RFC
+ * by allowing the Content-Range header to be lacking both the last-byte-pos
and a resource-length, as used in
+ * {@link ContentRangeResponseWrapper}. This is an optimization that is used
for ACE as we do not want to, nor can, know
+ * the exact length of some of the streams we're sending.
+ * </p>
+ *
+ * @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.16
*/
class ContentRangeInputStream extends InputStream {
private static final String HDR_CONTENT_RANGE = "Content-Range";
@@ -143,22 +152,6 @@ class ContentRangeInputStream extends In
}
}
- /**
- * Returns the total content size, if available.
- *
- * @return the total length (in bytes) of the content, or <tt>-1</tt> if
no content size is known.
- * @throws IOException
- * in case of I/O errors, such as when this stream is already
closed.
- */
- public long getContentSize() throws IOException {
- assertOpen();
-
- if (m_contentInfo == null || m_contentInfo.length < 2) {
- return -1L;
- }
- return m_contentInfo[1];
- }
-
@Override
public int read() throws IOException {
assertOpen();
@@ -335,7 +328,7 @@ class ContentRangeInputStream extends In
if (contentRange != null) {
return parseContentRangeHeader(contentRange);
}
-
+
// fall through, we cannot handle this...
}
else if (rc == SC_SERVICE_UNAVAILABLE) {
@@ -385,10 +378,16 @@ class ContentRangeInputStream extends In
if (!"*".equals(parts[0])) {
String[] rangeDef = parts[0].split("-");
- long start = Long.parseLong(rangeDef[0]);
- long end = Long.parseLong(rangeDef[1]);
+ try {
+ long start = Long.parseLong(rangeDef[0]);
+ long end = Long.parseLong(rangeDef[1]);
- chunkSize = end - start;
+ chunkSize = end - start;
+ }
+ catch (Exception exception) {
+ // Ack; invalid range specified, cannot determine chunk size!
+ chunkSize = -1L;
+ }
}
long totalBytes;
Modified:
ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/DefaultController.java
URL:
http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/DefaultController.java?rev=1545277&r1=1545276&r2=1545277&view=diff
==============================================================================
---
ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/DefaultController.java
(original)
+++
ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/DefaultController.java
Mon Nov 25 13:41:37 2013
@@ -175,9 +175,9 @@ public class DefaultController extends C
}
@Override
- public void progress(long bytesRead, long totalBytes) {
+ public void progress(long bytesRead) {
if (m_updateInfo != null) {
- getController().logInfo("Progress of %s update download: %d of
%d bytes...", m_updateInfo.m_type, bytesRead, totalBytes);
+ getController().logInfo("%d bytes of %s update downloaded...",
bytesRead, m_updateInfo.m_type);
}
}
Modified:
ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/DiscoveryHandlerImpl.java
URL:
http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/DiscoveryHandlerImpl.java?rev=1545277&r1=1545276&r2=1545277&view=diff
==============================================================================
---
ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/DiscoveryHandlerImpl.java
(original)
+++
ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/DiscoveryHandlerImpl.java
Mon Nov 25 13:41:37 2013
@@ -78,7 +78,8 @@ public class DiscoveryHandlerImpl extend
}
}
- private static final String DEFAULT_SERVER_URL = "http://localhost:8080";
+ static final String DEFAULT_SERVER_URL = "http://localhost:8080";
+
private static final boolean DEFAULT_CHECK_SERVER_URLS = true;
private static final long DEFAULT_CACHE_MILLISECONDS = 30000;
private final Map<String, CheckedURL> m_checkedURLs = new HashMap<String,
CheckedURL>();
Modified:
ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/DownloadCallableImpl.java
URL:
http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/DownloadCallableImpl.java?rev=1545277&r1=1545276&r2=1545277&view=diff
==============================================================================
---
ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/DownloadCallableImpl.java
(original)
+++
ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/DownloadCallableImpl.java
Mon Nov 25 13:41:37 2013
@@ -64,15 +64,12 @@ final class DownloadCallableImpl impleme
byte buffer[] = new byte[READBUFFER_SIZE];
long bytesRead = targetLength;
- long totalBytes = -1L;
+ boolean downloadComplete = false;
int read = 0;
try {
while (!Thread.currentThread().isInterrupted() && (read >= 0))
{
read = is.read(buffer);
- // this can only fail when the IS is closed, but in that
case, the read() above should have failed
- // already...
- totalBytes = is.getContentSize();
if (read >= 0) {
os.write(buffer, 0, read);
@@ -80,11 +77,12 @@ final class DownloadCallableImpl impleme
bytesRead += read;
}
else {
+ downloadComplete = true;
break; // EOF...
}
if (m_listener != null) {
- m_listener.progress(bytesRead, totalBytes);
+ m_listener.progress(bytesRead);
}
}
}
@@ -97,16 +95,15 @@ final class DownloadCallableImpl impleme
os.flush();
}
- boolean downloadComplete = (bytesRead == totalBytes);
boolean stoppedEarly = Thread.currentThread().isInterrupted() ||
!downloadComplete;
if (stoppedEarly) {
- m_handle.logDebug("Download stopped early: %d of %d bytes
downloaded... (%d)", bytesRead, totalBytes, targetLength);
+ m_handle.logDebug("Download stopped early: %d bytes
downloaded...", bytesRead);
return new DownloadResultImpl(DownloadState.STOPPED);
}
- m_handle.logDebug("Download completed: %d bytes downloaded...",
totalBytes);
+ m_handle.logDebug("Download completed: %d bytes downloaded...",
bytesRead);
return new DownloadResultImpl(DownloadState.SUCCESSFUL, m_target);
}
Modified:
ace/trunk/org.apache.ace.agent/test/org/apache/ace/agent/impl/ContentRangeInputStreamTest.java
URL:
http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.agent/test/org/apache/ace/agent/impl/ContentRangeInputStreamTest.java?rev=1545277&r1=1545276&r2=1545277&view=diff
==============================================================================
---
ace/trunk/org.apache.ace.agent/test/org/apache/ace/agent/impl/ContentRangeInputStreamTest.java
(original)
+++
ace/trunk/org.apache.ace.agent/test/org/apache/ace/agent/impl/ContentRangeInputStreamTest.java
Mon Nov 25 13:41:37 2013
@@ -39,7 +39,7 @@ import org.testng.annotations.Test;
*/
public class ContentRangeInputStreamTest {
static enum Failure {
- CONTENT_NOT_FOUND, SERVER_UNAVAILABLE, PARTIAL_NO_CONTENT_RANGE,
PARTIAL_NON_BYTE_RANGE, PARTIAL_COMPLETE_BODY, PARTIAL_CHANGING_CONTENT_LENGTH;
+ CONTENT_NOT_FOUND, SERVER_UNAVAILABLE, PARTIAL_NO_CONTENT_RANGE,
PARTIAL_NON_BYTE_RANGE, PARTIAL_COMPLETE_BODY, PARTIAL_CHANGING_CONTENT_LENGTH,
PARTIAL_UNKNOWN_CHUNK_SIZE;
}
/**
@@ -111,6 +111,9 @@ public class ContentRangeInputStreamTest
int cl = (getRequestProperty("Range") != null) ? 1024 :
m_length;
return String.format("bytes %d-%d/%d", 48, 96, cl);
}
+ else if (m_failure == Failure.PARTIAL_UNKNOWN_CHUNK_SIZE) {
+ return String.format("bytes %d-/*", 48);
+ }
else if (m_failure != Failure.PARTIAL_NO_CONTENT_RANGE) {
return String.format("bytes %d-%d/%d", 48, 96, m_length);
}
@@ -121,14 +124,18 @@ public class ContentRangeInputStreamTest
@Override
public InputStream getInputStream() throws IOException {
if (m_stream == null) {
- m_stream = new ByteArrayInputStream(m_content.substring(48,
96).getBytes());
+ if (m_failure == Failure.PARTIAL_UNKNOWN_CHUNK_SIZE) {
+ m_stream = new
ByteArrayInputStream(m_content.substring(48).getBytes());
+ } else {
+ m_stream = new
ByteArrayInputStream(m_content.substring(48, 96).getBytes());
+ }
}
return m_stream;
}
@Override
public int getResponseCode() {
- if (m_failure == Failure.PARTIAL_NO_CONTENT_RANGE || m_failure ==
Failure.PARTIAL_NON_BYTE_RANGE || m_failure ==
Failure.PARTIAL_CHANGING_CONTENT_LENGTH) {
+ if (m_failure == Failure.PARTIAL_NO_CONTENT_RANGE || m_failure ==
Failure.PARTIAL_NON_BYTE_RANGE || m_failure ==
Failure.PARTIAL_CHANGING_CONTENT_LENGTH || m_failure ==
Failure.PARTIAL_UNKNOWN_CHUNK_SIZE) {
return 206;
}
else if (m_failure == Failure.PARTIAL_COMPLETE_BODY) {
@@ -531,6 +538,19 @@ public class ContentRangeInputStreamTest
}
/**
+ * Tests that we can read partial content and return the expected contents.
+ */
+ @Test
+ public void testReadPartialContentWithUnknownChunkSizeOk() throws
Exception {
+ String content = m_content;
+
+ ConnectionHandler handler = new TestConnectionHandler(new
FailingContentConnection(content, Failure.PARTIAL_UNKNOWN_CHUNK_SIZE));
+ ContentRangeInputStream is = new ContentRangeInputStream(handler,
m_testURL);
+
+ assertEquals(slurpAsStringWithBuffer(is), content.substring(48));
+ }
+
+ /**
* Tests that we cannot read partial content if the server is not
available.
*/
@Test(expectedExceptions = RetryAfterException.class)
Modified:
ace/trunk/org.apache.ace.agent/test/org/apache/ace/agent/impl/DownloadHandleImplTest.java
URL:
http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.agent/test/org/apache/ace/agent/impl/DownloadHandleImplTest.java?rev=1545277&r1=1545276&r2=1545277&view=diff
==============================================================================
---
ace/trunk/org.apache.ace.agent/test/org/apache/ace/agent/impl/DownloadHandleImplTest.java
(original)
+++
ace/trunk/org.apache.ace.agent/test/org/apache/ace/agent/impl/DownloadHandleImplTest.java
Mon Nov 25 13:41:37 2013
@@ -147,7 +147,7 @@ public class DownloadHandleImplTest exte
final DownloadHandle handle =
downloadHandler.getHandle(m_testContentURL);
future = handle.start(new DownloadProgressListener() {
@Override
- public void progress(long bytesRead, long totalBytes) {
+ public void progress(long bytesRead) {
handle.stop();
}
});
@@ -187,8 +187,8 @@ public class DownloadHandleImplTest exte
// Start the download, but interrupt it after reading the first chunk
of data...
future = handle.start(new DownloadProgressListener() {
@Override
- public void progress(long bytesRead, long totalBytes) {
- System.out.printf("Downloaded %d from %d bytes, interrupting
download...%n", bytesRead, totalBytes);
+ public void progress(long bytesRead) {
+ System.out.printf("Downloaded %d bytes, interrupting
download...%n", bytesRead);
Thread.currentThread().interrupt();
}
});
@@ -208,9 +208,9 @@ public class DownloadHandleImplTest exte
private int m_count = 5;
@Override
- public void progress(long bytesRead, long totalBytes) {
+ public void progress(long bytesRead) {
if (--m_count == 0) {
- System.out.printf("Downloaded %d from %d bytes, stopping
download...%n", bytesRead, totalBytes);
+ System.out.printf("Downloaded %d bytes, stopping
download...%n", bytesRead);
handle2.stop();
}
}
Modified:
ace/trunk/org.apache.ace.agent/test/org/apache/ace/agent/impl/DownloadHandlerTest.java
URL:
http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.agent/test/org/apache/ace/agent/impl/DownloadHandlerTest.java?rev=1545277&r1=1545276&r2=1545277&view=diff
==============================================================================
---
ace/trunk/org.apache.ace.agent/test/org/apache/ace/agent/impl/DownloadHandlerTest.java
(original)
+++
ace/trunk/org.apache.ace.agent/test/org/apache/ace/agent/impl/DownloadHandlerTest.java
Mon Nov 25 13:41:37 2013
@@ -180,7 +180,7 @@ public class DownloadHandlerTest extends
Future<DownloadResult> future = handle.start(new
DownloadProgressListener() {
@Override
- public void progress(long read, long total) {
+ public void progress(long read) {
Thread.currentThread().interrupt();
}
});
Modified:
ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/servlet/ContentRangeResponseWrapper.java
URL:
http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/servlet/ContentRangeResponseWrapper.java?rev=1545277&r1=1545276&r2=1545277&view=diff
==============================================================================
---
ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/servlet/ContentRangeResponseWrapper.java
(original)
+++
ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/servlet/ContentRangeResponseWrapper.java
Mon Nov 25 13:41:37 2013
@@ -18,12 +18,7 @@
*/
package org.apache.ace.deployment.servlet;
-import java.io.Closeable;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
import java.io.IOException;
-import java.io.InputStream;
import java.util.concurrent.atomic.AtomicLong;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -35,114 +30,20 @@ import javax.servlet.http.HttpServletRes
/**
* Wraps a HttpServletResponse to add byte range support allowing client to
request partial content.
+ * <p>
+ * Note: this implementation does <em>not</em> strictly follow the
recommendations made in RFC 2616! For example, it
+ * does not ever send the "Content-Length" header, nor provide a
"Resource-Length" value at any time. This is an
+ * "optimization" we've added for ACE, as we do not know the content/resource
length in advance, nor are willing to
+ * sacrifice performance to get knowledge about this.
+ * </p>
*
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35.1
*/
public class ContentRangeResponseWrapper extends HttpServletResponseWrapper {
-
- /**
- * Internal helper that Wraps a ServletOutputStream to add byte range
support.
- */
- private static class ContentRangeOutputStreamWrapper extends
ServletOutputStream {
- private static final int BUFFER_SIZE = 32 * 1024; // kB
-
- private final HttpServletResponse m_response;
- private final boolean m_streamAll;
- private final long m_requestFirstBytePos;
- private final long m_requestLastBytePos;
- private final FileOutputStream m_os;
- private final File m_file;
-
- private final AtomicLong m_instanceLen = new AtomicLong(0);
-
- public ContentRangeOutputStreamWrapper(HttpServletResponse response)
throws IOException {
- this(response, 0, Long.MAX_VALUE);
- }
-
- public ContentRangeOutputStreamWrapper(HttpServletResponse response,
long firstBytePos, long lastBytePos) throws IOException {
- this(response, firstBytePos, lastBytePos, (firstBytePos == 0 &&
lastBytePos == Long.MAX_VALUE));
- }
-
- private ContentRangeOutputStreamWrapper(HttpServletResponse response,
long firstBytePos, long lastBytePos, boolean streamAll) throws IOException {
- assert response != null;
- assert firstBytePos >= 0;
- assert lastBytePos > firstBytePos;
-
- m_response = response;
- m_requestFirstBytePos = firstBytePos;
- m_requestLastBytePos = lastBytePos;
- m_streamAll = streamAll;
-
- // We use a file to buffer because Deployment Packages can be big
and the current common ACE Agent case it a
- // range request for some start position up-to EOF.
- m_file = File.createTempFile("deploymentpackage", ".jar");
- m_os = new FileOutputStream(m_file);
- }
-
- @Override
- public void write(int b) throws IOException {
- // We only need to buffer the relevant bytes since we keep track
of the instance length in the counter.
- long value = m_instanceLen.getAndIncrement();
- if (value >= m_requestFirstBytePos && value <=
m_requestLastBytePos) {
- m_os.write(b);
- }
- }
-
- @Override
- public void close() throws IOException {
- closeQuietly(m_os);
-
- long instanceLength = m_instanceLen.get();
- long instanceLastBytePos = instanceLength - 1L;
- InputStream is = null;
- ServletOutputStream os = null;
-
- try {
- if (instanceLastBytePos < m_requestFirstBytePos) {
- m_response.setStatus(SC_REQUESTED_RANGE_NOT_SATISFIABLE);
- m_response.setHeader("Content-Range", String.format("bytes
*/%d", instanceLength));
- }
- else {
- long firstBytePos = m_requestFirstBytePos;
- long lastBytePos = instanceLastBytePos <
m_requestLastBytePos ? instanceLastBytePos : m_requestLastBytePos;
- long contentLength = lastBytePos - firstBytePos + 1;
-
- m_response.setStatus(m_streamAll ? SC_OK :
SC_PARTIAL_CONTENT);
- m_response.setHeader("Content-Length",
String.valueOf(contentLength));
- if (!m_streamAll) {
- m_response.setHeader("Content-Range",
String.format("bytes %d-%d/%d", firstBytePos, lastBytePos, instanceLength));
- }
-
- byte[] buffer = new byte[BUFFER_SIZE];
- is = new FileInputStream(m_file);
- os = m_response.getOutputStream();
-
- for (int bytesRead = is.read(buffer); bytesRead != -1;
bytesRead = is.read(buffer)) {
- os.write(buffer, 0, bytesRead);
- }
- }
- }
- finally {
- closeQuietly(is);
- closeQuietly(os);
- m_file.delete();
- }
- }
-
- private static void closeQuietly(Closeable resource) throws
IOException {
- if (resource != null) {
- try {
- resource.close();
- }
- catch (Exception e) {
- // ignore
- }
- }
- }
- }
+ private final long m_requestFirstBytePos;
+ private final long m_requestLastBytePos;
private final HttpServletResponse m_response;
- private final ServletOutputStream m_outputStream;
public ContentRangeResponseWrapper(HttpServletRequest request,
HttpServletResponse response) throws IOException {
super(response);
@@ -151,25 +52,62 @@ public class ContentRangeResponseWrapper
assert response != null;
m_response = response;
- m_response.setHeader("Accept-Ranges", "bytes");
- // If a valid Range request is present we install the
ContentRangeOutputStreamWrapper. Otherwise we do not touch
- // the response ServletOutputStream until we have to in
#getOutputStream().
- ContentRangeOutputStreamWrapper wrapper = null;
long[] requestRange = getRequestRange(request);
if (requestRange != null) {
- wrapper = new ContentRangeOutputStreamWrapper(response,
requestRange[0], requestRange[1]);
+ m_requestFirstBytePos = requestRange[0];
+ m_requestLastBytePos = requestRange[1];
+ }
+ else {
+ m_requestFirstBytePos = 0;
+ m_requestLastBytePos = Long.MAX_VALUE;
+ }
+
+ boolean streamAll = (m_requestFirstBytePos == 0) &&
(m_requestLastBytePos == Long.MAX_VALUE);
+
+ m_response.setHeader("Accept-Ranges", "bytes");
+ if (m_requestFirstBytePos < m_requestLastBytePos) {
+ if (streamAll) {
+ m_response.setStatus(SC_OK);
+ }
+ else {
+ m_response.setStatus(SC_PARTIAL_CONTENT);
+
+ StringBuilder cr = new StringBuilder("bytes
").append(m_requestFirstBytePos).append('-');
+ if (m_requestLastBytePos > 0 && m_requestLastBytePos <
Long.MAX_VALUE) {
+ cr.append(m_requestLastBytePos);
+ }
+ cr.append("/*"); // unknown instance length...
+ m_response.setHeader("Content-Range", cr.toString());
+ }
}
- if (wrapper == null) {
- // Assume a range of "bytes=0-", which simply streams everything.
This solves ACE-435...
- wrapper = new ContentRangeOutputStreamWrapper(response);
+ else {
+ m_response.setStatus(SC_REQUESTED_RANGE_NOT_SATISFIABLE);
}
- m_outputStream = wrapper;
}
@Override
public ServletOutputStream getOutputStream() throws IOException {
- return m_outputStream;
+ final ServletOutputStream delegate = m_response.getOutputStream();
+
+ return new ServletOutputStream() {
+ /** keeps the actual number of bytes written by our caller... */
+ private final AtomicLong m_written = new AtomicLong(0L);
+
+ @Override
+ public void write(int b) throws IOException {
+ // We only need to buffer the relevant bytes since we keep
track of the instance length in the counter.
+ long written = m_written.getAndIncrement();
+ if (written >= m_requestFirstBytePos && written <=
m_requestLastBytePos) {
+ delegate.write(b);
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ delegate.close();
+ }
+ };
}
/**
Modified:
ace/trunk/org.apache.ace.deployment/test/org/apache/ace/deployment/servlet/DeploymentServletTest.java
URL:
http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.deployment/test/org/apache/ace/deployment/servlet/DeploymentServletTest.java?rev=1545277&r1=1545276&r2=1545277&view=diff
==============================================================================
---
ace/trunk/org.apache.ace.deployment/test/org/apache/ace/deployment/servlet/DeploymentServletTest.java
(original)
+++
ace/trunk/org.apache.ace.deployment/test/org/apache/ace/deployment/servlet/DeploymentServletTest.java
Mon Nov 25 13:41:37 2013
@@ -96,7 +96,7 @@ public class DeploymentServletTest {
m_requestPathInfo = "/existing/versions/2.0.0";
m_servlet.doGet(m_request, m_response);
assertResponseCode(HttpServletResponse.SC_OK);
- assertResponseHeaderValue("Content-Length", "100");
+ assertResponseHeaderNotPresent("Content-Length");
assertResponseOutputSize(100);
assertGeneratorTargetId("existing");
assertGeneratorToVersion("2.0.0");
@@ -129,7 +129,7 @@ public class DeploymentServletTest {
m_requestRangeHeader = "bytes=a-1";
m_servlet.doGet(m_request, m_response);
assertResponseCode(HttpServletResponse.SC_OK);
- assertResponseHeaderValue("Content-Length", "100");
+ assertResponseHeaderNotPresent("Content-Length");
assertResponseOutputSize(100);
}
@@ -140,8 +140,8 @@ public class DeploymentServletTest {
m_requestRangeHeader = "bytes=0-10";
m_servlet.doGet(m_request, m_response);
assertResponseCode(HttpServletResponse.SC_PARTIAL_CONTENT);
- assertResponseHeaderValue("Content-Length", "11");
- assertResponseHeaderValue("Content-Range", "bytes 0-10/100");
+ assertResponseHeaderNotPresent("Content-Length");
+ assertResponseHeaderValue("Content-Range", "bytes 0-10/*");
}
@Test
@@ -154,8 +154,8 @@ public class DeploymentServletTest {
m_requestRangeHeader = "bytes=2-";
m_servlet.doGet(m_request, m_response);
assertResponseCode(HttpServletResponse.SC_PARTIAL_CONTENT);
- assertResponseHeaderValue("Content-Length", "98");
- assertResponseHeaderValue("Content-Range", "bytes 2-99/100");
+ assertResponseHeaderNotPresent("Content-Length");
+ assertResponseHeaderValue("Content-Range", "bytes 2-/*");
assertResponseOutputSize(98);
}
@@ -166,8 +166,8 @@ public class DeploymentServletTest {
m_requestRangeHeader = "bytes=2-50";
m_servlet.doGet(m_request, m_response);
assertResponseCode(HttpServletResponse.SC_PARTIAL_CONTENT);
- assertResponseHeaderValue("Content-Length", "49");
- assertResponseHeaderValue("Content-Range", "bytes 2-50/100");
+ assertResponseHeaderNotPresent("Content-Length");
+ assertResponseHeaderValue("Content-Range", "bytes 2-50/*");
}
@Test
@@ -180,8 +180,8 @@ public class DeploymentServletTest {
m_requestRangeHeader = "bytes=2-100";
m_servlet.doGet(m_request, m_response);
assertResponseCode(HttpServletResponse.SC_PARTIAL_CONTENT);
- assertResponseHeaderValue("Content-Length", "98");
- assertResponseHeaderValue("Content-Range", "bytes 2-99/100");
+ assertResponseHeaderNotPresent("Content-Length");
+ assertResponseHeaderValue("Content-Range", "bytes 2-100/*");
assertResponseOutputSize(98);
}
@@ -199,7 +199,7 @@ public class DeploymentServletTest {
m_requestRangeHeader = "bytes=2-1";
m_servlet.doGet(m_request, m_response);
assertResponseCode(HttpServletResponse.SC_OK);
- assertResponseHeaderValue("Content-Length", "100");
+ assertResponseHeaderNotPresent("Content-Length");
assertResponseOutputSize(100);
}
@@ -209,8 +209,8 @@ public class DeploymentServletTest {
m_requestPathInfo = "/existing/versions/2.0.0";
m_requestRangeHeader = "bytes=100-110";
m_servlet.doGet(m_request, m_response);
-
assertResponseCode(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
- assertResponseHeaderValue("Content-Range", "bytes */100");
+ assertResponseCode(HttpServletResponse.SC_PARTIAL_CONTENT);
+ assertResponseHeaderValue("Content-Range", "bytes 100-110/*");
assertResponseHeaderNotPresent("Content-Length");
assertResponseOutputSize(0);
}