WICKET-5819 Support for HTML 5 media tags (audio / video) Simplify PartWriterCallback by using BoundedInputStream from commons-io. Add an additional test case with bigger range sizes
Project: http://git-wip-us.apache.org/repos/asf/wicket/repo Commit: http://git-wip-us.apache.org/repos/asf/wicket/commit/a1d0df29 Tree: http://git-wip-us.apache.org/repos/asf/wicket/tree/a1d0df29 Diff: http://git-wip-us.apache.org/repos/asf/wicket/diff/a1d0df29 Branch: refs/heads/master Commit: a1d0df298447247fa901cdee26caf6ae4a727c2d Parents: 4a36aa7 Author: Martin Tzvetanov Grigorov <mgrigo...@apache.org> Authored: Fri Mar 27 21:25:17 2015 +0200 Committer: Martin Tzvetanov Grigorov <mgrigo...@apache.org> Committed: Mon Mar 30 21:59:15 2015 +0300 ---------------------------------------------------------------------- .../request/resource/PartWriterCallback.java | 79 +++++++++++++------- .../resource/PackageResourceReferenceTest.java | 56 ++++++++++++++ .../request/resource/resource_gt_4096.txt | 1 + 3 files changed, 108 insertions(+), 28 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/wicket/blob/a1d0df29/wicket-core/src/main/java/org/apache/wicket/request/resource/PartWriterCallback.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/request/resource/PartWriterCallback.java b/wicket-core/src/main/java/org/apache/wicket/request/resource/PartWriterCallback.java index c0c7a63..5fe8185 100644 --- a/wicket-core/src/main/java/org/apache/wicket/request/resource/PartWriterCallback.java +++ b/wicket-core/src/main/java/org/apache/wicket/request/resource/PartWriterCallback.java @@ -20,28 +20,47 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import org.apache.commons.io.input.BoundedInputStream; import org.apache.wicket.protocol.http.servlet.ResponseIOException; import org.apache.wicket.request.resource.AbstractResource.WriteCallback; import org.apache.wicket.request.resource.IResource.Attributes; +import org.apache.wicket.util.io.IOUtils; import org.apache.wicket.util.io.Streams; +import org.apache.wicket.util.lang.Args; /** * Used to read a part of an input stream and writes it to the output stream of the response taken - * from attributes of the writeData method. + * from attributes in {@link #writeData(org.apache.wicket.request.resource.IResource.Attributes)} method. * * @author Tobias Soloschenko - * */ public class PartWriterCallback extends WriteCallback { + /** + * The input stream to read from + */ private final InputStream inputStream; + /** + * The total length to read if {@link #endbyte} is not specified + */ private final Long contentLength; - private final Long startbyte; + /** + * The byte to start reading from. If omitted then the input stream will be read + * from its beginning + */ + private Long startbyte; + /** + * The end byte to read from the {@link #inputStream}. + * If omitted then the input stream will be read till its end + */ private Long endbyte; + /** + * The size of the buffer that is used for the copying of the data + */ private int bufferSize; @@ -51,12 +70,12 @@ public class PartWriterCallback extends WriteCallback * Reads a part of the given input stream. If the startbyte parameter is not null the number of * bytes are skipped till the stream is read. If the endbyte is not null the stream is read till * endbyte, else to the end of the whole stream. If startbyte and endbyte is null the whole - * stream is read. + * stream is copied. * * @param inputStream - * the input stream to be read - * @param the - * content length + * the input stream to read from + * @param contentLength + * content length of the input stream. Ignored if <em>endByte</em> is specified * @param startbyte * the start position to read from (if not null the number of bytes are skipped till * the stream is read) @@ -68,7 +87,7 @@ public class PartWriterCallback extends WriteCallback Long endbyte) { this.inputStream = inputStream; - this.contentLength = contentLength; + this.contentLength = Args.notNull(contentLength, "contentLength"); this.startbyte = startbyte; this.endbyte = endbyte; } @@ -97,6 +116,12 @@ public class PartWriterCallback extends WriteCallback { inputStream.skip(startbyte); } + else + { + // If no start byte has been given set it to 0 + // which means no bytes has been skipped + startbyte = 0L; + } // If there are no end bytes given read the whole stream till the end if (endbyte == null || Long.valueOf(-1).equals(endbyte)) @@ -104,36 +129,34 @@ public class PartWriterCallback extends WriteCallback endbyte = contentLength; } - // The read bytes in the current buffer - int readBytes; + BoundedInputStream boundedInputStream = null; + try + { + // Stream is going to be read from the starting point next to the skipped bytes + // till the end byte computed by the range between startbyte / endbyte + boundedInputStream = new BoundedInputStream(inputStream, endbyte - startbyte); - // The total bytes read - long totalBytes = 0; + // The original input stream is going to be closed by the end of the request + // so set propagate close to false + boundedInputStream.setPropagateClose(false); - while ((readBytes = inputStream.read(buffer)) != -1) - { - totalBytes += readBytes; + // The read bytes in the current buffer + int readBytes; - // Check if the end byte is reached - if (endbyte - totalBytes < 0) + while ((readBytes = boundedInputStream.read(buffer)) != -1) { - // calculate the bytes left to be read in the current buffer - // can be casted to int, because the the previous chunks are - // subtracted - so it can't exceed buffer size - int leftBytesToBeRead = (int)(totalBytes - startbyte) - - (int)(totalBytes - endbyte); - outputStream.write(buffer, 0, leftBytesToBeRead); - break; - } - else - { - // If the end byte is not reached read the full buffer outputStream.write(buffer, 0, readBytes); } } + finally + { + IOUtils.closeQuietly(boundedInputStream); + } } else { + // No range has been given so copy the content + // from input stream to the output stream Streams.copy(inputStream, outputStream, getBufferSize()); } } http://git-wip-us.apache.org/repos/asf/wicket/blob/a1d0df29/wicket-core/src/test/java/org/apache/wicket/request/resource/PackageResourceReferenceTest.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/test/java/org/apache/wicket/request/resource/PackageResourceReferenceTest.java b/wicket-core/src/test/java/org/apache/wicket/request/resource/PackageResourceReferenceTest.java index a58a2c9..4057674 100644 --- a/wicket-core/src/test/java/org/apache/wicket/request/resource/PackageResourceReferenceTest.java +++ b/wicket-core/src/test/java/org/apache/wicket/request/resource/PackageResourceReferenceTest.java @@ -16,6 +16,8 @@ */ package org.apache.wicket.request.resource; +import java.io.IOException; +import java.io.InputStream; import java.util.Locale; import org.apache.wicket.Application; @@ -28,6 +30,7 @@ import org.apache.wicket.request.resource.AbstractResource.ContentRangeType; import org.apache.wicket.request.resource.IResource.Attributes; import org.apache.wicket.request.resource.ResourceReference.UrlAttributes; import org.apache.wicket.response.ByteArrayResponse; +import org.apache.wicket.util.io.IOUtils; import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -275,6 +278,59 @@ public class PackageResourceReferenceTest extends WicketTestCase /** * See WICKET-5819 - Media tags + * + * @throws IOException + */ + @Test + public void testContentRangeLarge() throws IOException + { + InputStream resourceAsStream = null; + try + { + resourceAsStream = PackageResourceReference.class.getResourceAsStream("resource_gt_4096.txt"); + String content = new String(IOUtils.toByteArray(resourceAsStream)); + + // Check buffer comprehensive range request + String bytes4094_4106 = makeRangeRequestToBigResource("bytes=4094-4106"); + assertEquals(12, bytes4094_4106.length()); + assertEquals("River Roller", bytes4094_4106); + + // Check buffer exceeding range request + String bytes1000_5000 = makeRangeRequestToBigResource("bytes=1000-5000"); + assertEquals(4000, bytes1000_5000.length()); + assertEquals(content.substring(1000, 5000), bytes1000_5000); + + // Check buffer exceeding range request until end of content + String bytes1000_end = makeRangeRequestToBigResource("bytes=1000-"); + assertEquals(4529, bytes1000_end.length()); + assertEquals(content.substring(1000, content.length()), bytes1000_end); + + // Check complete range request + assertEquals(content.length(), makeRangeRequestToBigResource("bytes=-").length()); + } + finally + { + IOUtils.closeQuietly(resourceAsStream); + } + } + + private String makeRangeRequestToBigResource(String range) + { + ResourceReference reference = new PackageResourceReference(scope, "resource_gt_4096.txt", + null, null, null); + + ByteArrayResponse byteResponse = new ByteArrayResponse(); + + Request request = tester.getRequestCycle().getRequest(); + MockHttpServletRequest mockHttpServletRequest = (MockHttpServletRequest)request.getContainerRequest(); + mockHttpServletRequest.setHeader("range", range); + Attributes mockAttributes = new Attributes(request, byteResponse); + reference.getResource().respond(mockAttributes); + return new String(byteResponse.getBytes()); + } + + /** + * See WICKET-5819 - Media tags */ @Test public void testContentRangeHeaders() http://git-wip-us.apache.org/repos/asf/wicket/blob/a1d0df29/wicket-core/src/test/java/org/apache/wicket/request/resource/resource_gt_4096.txt ---------------------------------------------------------------------- diff --git a/wicket-core/src/test/java/org/apache/wicket/request/resource/resource_gt_4096.txt b/wicket-core/src/test/java/org/apache/wicket/request/resource/resource_gt_4096.txt new file mode 100644 index 0000000..2b0c235 --- /dev/null +++ b/wicket-core/src/test/java/org/apache/wicket/request/resource/resource_gt_4096.txt @@ -0,0 +1 @@ +HTTP Server Abdera Accumulo ACE ActiveMQ Airavata Allura Ambari Ant Any23 APR Archiva Aries Avro Axis Bigtop Bloodhound Buildr BVal Camel Cassandra Cayenne Chemistry Chukwa Clerezza CloudStack Cocoon Commons Continuum Cordova CouchDB Creadur Crunch cTAKES Curator CXF DB Deltacloud DeltaSpike DirectMemory Directory Empire-db Etch Falcon Felix Flex Flume Forrest Geronimo Giraph Gora Gump Hadoop Hama HBase Helix Hive HttpComponents Isis Jackrabbit James jclouds Jena JMeter JSPWiki jUDDI Kafka Karaf Knox Lenya Libcloud Logging Lucene Lucene.Net Lucy Mahout ManifoldCF Marmotta Maven Mesos MINA MRUnit MyFaces Nutch ODE OFBiz Olingo Oltu Onami OODT Oozie Open Climate Workbench OpenJPA OpenMeetings OpenNLP OpenOffice OpenWebBeans PDFBox Perl Pig Pivot POI Portals Qpid Rave River Roller Santuario ServiceMix Shindig Shiro SIS Sling SpamAssassin Spark Sqoop Stanbol STeVe Storm Struts Subversion Synapse Syncope Tajo Tapestry Tcl Tez Thrift Tika Tiles Tomcat TomEE Traffic Server Turbine Tuscany UIMA VCL Velocity VXQuery Web Services Whirr Wicket Wink Wookie Xalan Xerces XMLBeans XML Graphics ZooKeeper HTTP Server Abdera Accumulo ACE ActiveMQ Airavata Allura Ambari Ant Any23 APR Archiva Aries Avro Axis Bigtop Bloodhound Buildr BVal Camel Cassandra Cayenne Chemistry Chukwa Clerezza CloudStack Cocoon Commons Continuum Cordova CouchDB Creadur Crunch cTAKES Curator CXF DB Deltacloud DeltaSpike DirectMemory Directory Empire-db Etch Falcon Felix Flex Flume Forrest Geronimo Giraph Gora Gump Hadoop Hama HBase Helix Hive HttpComponents Isis Jackrabbit James jclouds Jena JMeter JSPWiki jUDDI Kafka Karaf Knox Lenya Libcloud Logging Lucene Lucene.Net Lucy Mahout ManifoldCF Marmotta Maven Mesos MINA MRUnit MyFaces Nutch ODE OFBiz Olingo Oltu Onami OODT Oozie Open Climate Workbench OpenJPA OpenMeetings OpenNLP OpenOffice OpenWebBeans PDFBox Perl Pig Pivot POI Portals Qpid Rave River Roller Santuario ServiceMix Shindig Shiro SIS Sling SpamAssassin Spark Sqoop Stanbol STeVe Storm Struts Su bversion Synapse Syncope Tajo Tapestry Tcl Tez Thrift Tika Tiles Tomcat TomEE Traffic Server Turbine Tuscany UIMA VCL Velocity VXQuery Web Services Whirr Wicket Wink Wookie Xalan Xerces XMLBeans XML Graphics ZooKeeper HTTP Server Abdera Accumulo ACE ActiveMQ Airavata Allura Ambari Ant Any23 APR Archiva Aries Avro Axis Bigtop Bloodhound Buildr BVal Camel Cassandra Cayenne Chemistry Chukwa Clerezza CloudStack Cocoon Commons Continuum Cordova CouchDB Creadur Crunch cTAKES Curator CXF DB Deltacloud DeltaSpike DirectMemory Directory Empire-db Etch Falcon Felix Flex Flume Forrest Geronimo Giraph Gora Gump Hadoop Hama HBase Helix Hive HttpComponents Isis Jackrabbit James jclouds Jena JMeter JSPWiki jUDDI Kafka Karaf Knox Lenya Libcloud Logging Lucene Lucene.Net Lucy Mahout ManifoldCF Marmotta Maven Mesos MINA MRUnit MyFaces Nutch ODE OFBiz Olingo Oltu Onami OODT Oozie Open Climate Workbench OpenJPA OpenMeetings OpenNLP OpenOffice OpenWebBeans PDFBox Perl Pig Pivot POI Portals Qpid Rave Riv er Roller Santuario ServiceMix Shindig Shiro SIS Sling SpamAssassin Spark Sqoop Stanbol STeVe Storm Struts Subversion Synapse Syncope Tajo Tapestry Tcl Tez Thrift Tika Tiles Tomcat TomEE Traffic Server Turbine Tuscany UIMA VCL Velocity VXQuery Web Services Whirr Wicket Wink Wookie Xalan Xerces XMLBeans XML Graphics ZooKeeper HTTP Server Abdera Accumulo ACE ActiveMQ Airavata Allura Ambari Ant Any23 APR Archiva Aries Avro Axis Bigtop Bloodhound Buildr BVal Camel Cassandra Cayenne Chemistry Chukwa Clerezza CloudStack Cocoon Commons Continuum Cordova CouchDB Creadur Crunch cTAKES Curator CXF DB Deltacloud DeltaSpike DirectMemory Directory Empire-db Etch Falcon Felix Flex Flume Forrest Geronimo Giraph Gora Gump Hadoop Hama HBase Helix Hive HttpComponents Isis Jackrabbit James jclouds Jena JMeter JSPWiki jUDDI Kafka Karaf Knox Lenya Libcloud Logging Lucene Lucene.Net Lucy Mahout ManifoldCF Marmotta Maven Mesos MINA MRUnit MyFaces Nutch ODE OFBiz Olingo Oltu Onami OODT Oozie Open Climate W orkbench OpenJPA OpenMeetings OpenNLP OpenOffice OpenWebBeans PDFBox Perl Pig Pivot POI Portals Qpid Rave River Roller Santuario ServiceMix Shindig Shiro SIS Sling SpamAssassin Spark Sqoop Stanbol STeVe Storm Struts Subversion Synapse Syncope Tajo Tapestry Tcl Tez Thrift Tika Tiles Tomcat TomEE Traffic Server Turbine Tuscany UIMA VCL Velocity VXQuery Web Services Whirr Wicket Wink Wookie Xalan Xerces XMLBeans XML Graphics ZooKeeper HTTP Server Abdera Accumulo ACE ActiveMQ Airavata Allura Ambari Ant Any23 APR Archiva Aries Avro Axis Bigtop Bloodhound Buildr BVal Camel Cassandra Cayenne Chemistry Chukwa Clerezza CloudStack Cocoon Commons Continuum Cordova CouchDB Creadur Crunch cTAKES Curator CXF DB Deltacloud DeltaSpike DirectMemory Directory Empire-db Etch Falcon Felix Flex Flume Forrest Geronimo Giraph Gora Gump Hadoop Hama HBase Helix Hive HttpComponents Isis Jackrabbit James jclouds Jena JMeter JSPWiki jUDDI Kafka Karaf Knox Lenya Libcloud Logging Lucene Lucene.Net Lucy Mahout Ma nifoldCF Marmotta Maven Mesos MINA MRUnit MyFaces Nutch ODE OFBiz Olingo Oltu Onami OODT Oozie Open Climate Workbench OpenJPA OpenMeetings OpenNLP OpenOffice OpenWebBeans PDFBox Perl Pig Pivot POI Portals Qpid Rave River Roller Santuario ServiceMix Shindig Shiro SIS Sling SpamAssassin Spark Sqoop Stanbol STeVe Storm Struts Subversion Synapse Syncope Tajo Tapestry Tcl Tez Thrift Tika Tiles Tomcat TomEE Traffic Server Turbine Tuscany UIMA VCL Velocity VXQuery Web Services Whirr Wicket Wink Wookie Xalan Xerces XMLBeans XML Graphics ZooKeeper \ No newline at end of file