diff --git a/src/zutil/io/BufferedBoundaryInputStream.java b/src/zutil/io/BufferedBoundaryInputStream.java index bc9b042..9a3be75 100755 --- a/src/zutil/io/BufferedBoundaryInputStream.java +++ b/src/zutil/io/BufferedBoundaryInputStream.java @@ -39,7 +39,7 @@ import java.io.InputStream; */ public class BufferedBoundaryInputStream extends FilterInputStream{ /** The size of the buffer in bytes */ - private static final int DEFAULT_BUF_SIZE = 8192; + protected static final int DEFAULT_BUF_SIZE = 8192; /** The raw buffer */ private byte buffer[]; @@ -76,43 +76,16 @@ public class BufferedBoundaryInputStream extends FilterInputStream{ buffer = new byte[buf_size]; } - /** - * Moves the remaining data to the beginning of the - * buffer and then fills the buffer with data from - * the source stream to the buffer - * - * @return the size of the buffer - */ - protected int fillBuffer() throws IOException { - int leftover = buf_end - buf_pos; - // is there any data available - if(leftover < 0 && super.available() <= 0) - return -1; // EOF - - // Move the end of the buffer to the start to not miss any split boundary - System.arraycopy(buffer, buf_pos, buffer, 0, buf_end); - // Copy in new data from the stream - int n = super.read(buffer, leftover, buffer.length ); - // Reset positions - if(n+leftover >= 0) { - buf_end = leftover + n; - buf_pos = 0; - } - searchNextBoundary(); - return n+leftover; - } /** * @return the next byte in the buffer */ public final int read() throws IOException{ - if(buf_pos >= buf_end-boundary.length) { - if(fillBuffer() <= 0) - return -1; // EOF - } - - if(buf_bound_pos == buf_pos) + if (fillBuffer() < 0) + return -1; + + if(isOnBoundary()) return -1; // boundary return buffer[buf_pos++]; } @@ -136,15 +109,13 @@ public class BufferedBoundaryInputStream extends FilterInputStream{ * @return the amount of bytes read or -1 if EOF */ public int read(byte b[], int off, int len) throws IOException { - if(buf_pos >= buf_end-boundary.length) { - if(fillBuffer() <= 0) - return -1; // EOF - } - if(buf_bound_pos == buf_pos) + if (fillBuffer() < 0) + return -1; // EOF + if(isOnBoundary()) return -1; // boundary // The request is larger then the buffer size - int leftover = buf_end - buf_pos; + int leftover = available(); if(len > leftover){ len = leftover; } @@ -160,41 +131,44 @@ public class BufferedBoundaryInputStream extends FilterInputStream{ /** - * @return if there is more data to read + * @return if the current position in the buffer is a boundary */ - public boolean isOnBoundary(){ + private boolean isOnBoundary(){ return buf_bound_pos == buf_pos; } + + /** - * Skips over the boundary at the current position in the buffer - */ - public boolean next(){ - if(buf_bound_pos == buf_pos){ + * Checks if there is more data available after the boundary. + * Note if the data between boundaries are longer than the buffer + * size then this method will return true until the next boundary + * is available in the buffer or the end of the stream. + * + * @return true if there is more data available after the closest boundary. + */ + public boolean hasNext() throws IOException { + if (buf_bound_pos < 0 && fillBuffer() >= 0) + return true; // the boundary not in the buffer? + if (buf_bound_pos >= 0 && buf_bound_pos + boundary.length < buf_end) + return true; // there is more data beyond boundary in the buffer + return false; + } + /** + * Skips over the closest boundary + */ + public void next() throws IOException { + if (buf_bound_pos >= 0){ // is boundary in buffer? buf_pos += boundary.length; searchNextBoundary(); - return buf_end >= buf_pos; } - return true; - } - - /** - * Searches for the nearest boundary from the current position - */ - protected void searchNextBoundary(){ - for(int i=buf_pos; i= 0 || fillBuffer() < 0); + } } + /** * Skips a specific amounts of bytes in the buffer. * Note that his method does not check for boundaries. @@ -203,7 +177,7 @@ public class BufferedBoundaryInputStream extends FilterInputStream{ * @return the actual number of bytes skipped. */ public long skip(long n) throws IOException { - int leftover = buf_end - buf_pos; + int leftover = available(); if(n > leftover){ buf_pos = buf_end; return leftover; @@ -243,4 +217,55 @@ public class BufferedBoundaryInputStream extends FilterInputStream{ public boolean markSupported(){ return false; } + + + + + /** + * Checks if the buffer needs to be appended with data. + * If so it moves the remaining data to the beginning of the + * buffer and then fills the buffer with data from + * the source stream + * + * @return the number of new bytes read from the source stream + */ + private int fillBuffer() throws IOException { + int leftover = available(); + // Do we need to fill the buffer + if(buf_pos < buf_end-boundary.length) + return 0; + // is there any data available + if(leftover <= 0 && super.available() <= 0) + return -1; // EOF + + // Move the end of the buffer to the start to not miss any split boundary + if (leftover > 0) + System.arraycopy(buffer, buf_pos, buffer, 0, leftover); + // Copy in new data from the stream + int n = super.read(buffer, leftover, buffer.length-leftover ); + // Reset positions + if(n+leftover >= 0) { + buf_end = leftover + n; + buf_pos = 0; + } + searchNextBoundary(); + return n; + } + + /** + * Searches for the nearest boundary from the current buffer position + */ + private void searchNextBoundary(){ + for(int i=buf_pos; i 0){ - int ret = Character.getNumericValue( buffer.charAt( 0 )); - buffer.deleteCharAt( 0 ); - return ret; - } - return -1; + if(buffer.length() == 0) + return -1; + + int ret = Character.getNumericValue( buffer.charAt( 0 )); + buffer.deleteCharAt( 0 ); + return ret; } /** @@ -84,12 +84,13 @@ public class StringInputStream extends InputStream{ * into an array of bytes. */ public int read(byte[] b, int off, int len){ + if(buffer.length() == 0) + return -1; + if( buffer.length() < len ){ len = buffer.length(); } - char[] ctmp = new char[len]; - buffer.getChars(0, len, ctmp, 0); - byte[] btmp = new String( ctmp ).getBytes(); + byte[] btmp = buffer.substring(0, len).getBytes(); System.arraycopy(btmp, 0, b, off, len); buffer.delete(0, len); return len; diff --git a/src/zutil/net/http/multipart/MultipartParser.java b/src/zutil/net/http/multipart/MultipartParser.java index 11aff89..2f1c800 100755 --- a/src/zutil/net/http/multipart/MultipartParser.java +++ b/src/zutil/net/http/multipart/MultipartParser.java @@ -119,9 +119,8 @@ public class MultipartParser implements Iterable{ @Override public boolean hasNext() { try { - IOUtil.copyStream(buffIn, new NullWriter()); - return boundaryIn.isOnBoundary(); - } catch (IOException e){ + return boundaryIn.hasNext(); + } catch (IOException e) { logger.log(Level.SEVERE, null, e); } return false; diff --git a/test/zutil/io/BufferedBoundaryInputStreamTest.java b/test/zutil/io/BufferedBoundaryInputStreamTest.java index de887ed..f672778 100755 --- a/test/zutil/io/BufferedBoundaryInputStreamTest.java +++ b/test/zutil/io/BufferedBoundaryInputStreamTest.java @@ -42,21 +42,22 @@ public class BufferedBoundaryInputStreamTest { BufferedBoundaryInputStream in = new BufferedBoundaryInputStream(inin); in.setBoundary("#"); + assertTrue(in.hasNext()); assertEquals('a', in.read()); assertEquals('a', in.read()); assertEquals('a', in.read()); assertEquals(-1, in.read()); - assertTrue(in.isOnBoundary()); + assertTrue(in.hasNext()); in.next(); assertEquals('a', in.read()); assertEquals(-1, in.read()); - assertTrue(in.isOnBoundary()); + assertTrue(in.hasNext()); in.next(); assertEquals(-1, in.read()); - assertTrue(in.isOnBoundary()); + assertTrue(in.hasNext()); in.next(); assertEquals('a', in.read()); assertEquals('a', in.read()); @@ -66,11 +67,11 @@ public class BufferedBoundaryInputStreamTest { assertEquals('a', in.read()); assertEquals('a', in.read()); assertEquals(-1, in.read()); - assertTrue(in.isOnBoundary()); + assertFalse(in.hasNext()); in.next(); assertEquals(-1, in.read()); - assertFalse(in.isOnBoundary()); + assertFalse(in.hasNext()); } @Test public void readArr_normal() throws IOException { @@ -82,14 +83,17 @@ public class BufferedBoundaryInputStreamTest { int n = in.read(buff); assertEquals(3, n); + assertTrue(in.hasNext()); in.next(); n = in.read(buff); assertEquals(16, n); + assertTrue(in.hasNext()); in.next(); n = in.read(buff); assertEquals(15, n); + assertFalse(in.hasNext()); in.next(); n = in.read(buff); assertEquals(-1, n); @@ -104,11 +108,11 @@ public class BufferedBoundaryInputStreamTest { byte[] buff = new byte[100]; assertEquals(3, in.read(buff)); assertEquals(-1, in.read()); - assertTrue(in.isOnBoundary()); + assertFalse(in.hasNext()); in.next(); assertEquals(-1, in.read(buff)); - assertFalse(in.isOnBoundary()); + assertFalse(in.hasNext()); } @Test public void readArr_multiCharBoundary() throws IOException { @@ -120,11 +124,11 @@ public class BufferedBoundaryInputStreamTest { assertEquals('a', in.read()); assertEquals('a', in.read()); assertEquals(-1, in.read()); - assertTrue(in.isOnBoundary()); + assertFalse(in.hasNext()); in.next(); assertEquals(-1, in.read()); - assertFalse(in.isOnBoundary()); + assertFalse(in.hasNext()); } @Test @@ -151,12 +155,10 @@ public class BufferedBoundaryInputStreamTest { in.setBoundary("a"); int n; - for(n=1; true; n++){ + for(n=1; in.hasNext(); n++){ assertEquals(-1, in.read()); assertEquals(-1, in.read()); in.next(); - if(!in.isOnBoundary()) - break; } assertEquals(35, n); } @@ -168,12 +170,10 @@ public class BufferedBoundaryInputStreamTest { byte[] buff = new byte[100]; int n; - for(n=1; true; n++){ + for(n=1; in.hasNext(); n++){ assertEquals(-1, in.read(buff)); assertEquals(-1, in.read(buff)); in.next(); - if(!in.isOnBoundary()) - break; } assertEquals(35, n); } @@ -203,4 +203,54 @@ public class BufferedBoundaryInputStreamTest { assertEquals(data.length(), in.read(buff)); assertEquals(data, new String(buff, 0, data.length())); } + + + @Test + public void read_largeData() throws IOException { + String data = "aaaaaaaaaaaa#aa#aaaaaaaaaaaaaaa#"; + StringInputStream inin = new StringInputStream(data); + BufferedBoundaryInputStream in = new BufferedBoundaryInputStream(inin, 10); + in.setBoundary("#"); + + assertTrue(in.hasNext()); + for (int i=0; i<12; ++i) + assertEquals('a', in.read()); + assertEquals(-1, in.read()); + + assertTrue(in.hasNext()); + in.next(); + assertEquals('a', in.read()); + assertEquals('a', in.read()); + + assertTrue(in.hasNext()); + in.next(); + for (int i=0; i<15; ++i) + assertEquals('a', in.read()); + assertEquals(-1, in.read()); + assertFalse(in.hasNext()); + } + @Test + public void readArr_largeData() throws IOException { + String data = "aaaaaaaaaaaa#aa#aaaaaaaaaaaaaaa#"; + StringInputStream inin = new StringInputStream(data); + BufferedBoundaryInputStream in = new BufferedBoundaryInputStream(inin, 10); + in.setBoundary("#"); + + byte[] buff = new byte[100]; + assertTrue(in.hasNext()); + assertEquals(10, in.read(buff)); + assertEquals(2, in.read(buff)); + assertEquals(-1, in.read()); + + assertTrue(in.hasNext()); + in.next(); + assertEquals(2, in.read(buff)); + + assertTrue(in.hasNext()); + in.next(); + assertEquals(10, in.read(buff)); + assertEquals(5, in.read(buff)); + assertEquals(-1, in.read()); + assertFalse(in.hasNext()); + } }