Using buffered boundary stream in multipart parser. But currently not working

This commit is contained in:
Ziver Koc 2016-07-08 16:23:29 +02:00
parent 53dec4603c
commit 077963ae28
11 changed files with 198 additions and 152 deletions

View file

@ -216,9 +216,9 @@ public class BufferedBoundaryInputStream extends FilterInputStream{
} }
/** /**
* @return if current position in the buffer is a boundary * @return if there is more data to read
*/ */
public boolean isBoundary(){ public boolean hasNext(){
return bound_pos == buf_pos; return bound_pos == buf_pos;
} }

View file

@ -89,11 +89,23 @@ public class IOUtil {
} }
/** /**
* Copies all data from the input stream to the output stream. * Copies all data from one InputStream to another OutputStream.
* The input stream will not be closed after method has returned. * The streams will not be closed after method has returned.
*/ */
public static void copyStream(InputStream in, OutputStream out) throws IOException { public static void copyStream(InputStream in, OutputStream out) throws IOException {
byte[] buff = new byte[256]; byte[] buff = new byte[8192]; // This is the default BufferedInputStream buffer size
int len;
while((len = in.read(buff)) > 0){
out.write(buff, 0, len);
}
}
/**
* Copies all data from one Reader to another Writer.
* The streams will not be closed after method has returned.
*/
public static void copyStream(Reader in, Writer out) throws IOException {
char[] buff = new char[8192]; // This is the default BufferedReader buffer size
int len; int len;
while((len = in.read(buff)) > 0){ while((len = in.read(buff)) > 0){
out.write(buff, 0, len); out.write(buff, 0, len);

View file

@ -0,0 +1,20 @@
package zutil.io;
import java.io.OutputStream;
/**
* An OutputStream that does nothing but discard data. Similar to /dev/null
*
* Created by Ziver on 2016-07-08.
*/
public class NullOutputStream extends OutputStream {
@Override
public void write(int b) { }
@Override
public void write(byte b[]) { }
@Override
public void write(byte b[], int off, int len) { }
}

41
src/zutil/io/NullWriter.java Executable file
View file

@ -0,0 +1,41 @@
package zutil.io;
import java.io.IOException;
import java.io.Writer;
/**
* An Writer that does nothing but discard data. Similar to /dev/null
*
* Created by Ziver on 2016-07-08.
*/
public class NullWriter extends Writer{
@Override
public void write(int c) { }
@Override
public void write(char cbuf[]) { }
@Override
public void write(char cbuf[], int off, int len) { }
@Override
public void write(String str) throws IOException { }
@Override
public void write(String str, int off, int len) { }
@Override
public Writer append(CharSequence csq) {
return this;
}
@Override
public Writer append(CharSequence csq, int start, int end) {
return this;
}
@Override
public Writer append(char c) {
return this;
}
@Override
public void flush() { }
@Override
public void close() { };
}

View file

@ -24,8 +24,11 @@
package zutil.net.http.multipart; package zutil.net.http.multipart;
import java.io.File; import zutil.io.IOUtil;
import java.io.IOException;
import java.io.*;
import java.util.HashMap;
import java.util.Map;
/** /**
@ -34,14 +37,14 @@ import java.io.IOException;
* @author Ziver * @author Ziver
*/ */
public class MultipartFileField implements MultipartField{ public class MultipartFileField implements MultipartField{
/** This is the temporary directory for the received files */ private String fieldname;
private File tempDir; private String filename;
protected String filename; private InputStream in;
protected File file;
protected MultipartFileField() throws IOException { protected MultipartFileField(Map<String,String> header, BufferedReader in) throws IOException {
this.file = createTempFile(); this.fieldname = header.get("fieldname");
this.filename = header.get("filename");
} }
/** /**
@ -52,51 +55,28 @@ public class MultipartFileField implements MultipartField{
} }
/** /**
* @return the specific file name for this field. * @return the field name
*/ */
public String getName(){ public String getName(){
return filename; return fieldname;
}
public String getFilename(){
return filename;
}
public InputStream getInputStream(){
return in;
} }
/** /**
* @return the File class that points to the received file * Reads in all data and save it into the specified file
*/
public File getFile(){
return file;
}
/**
* Moves the received file.
* *
* @param newFile is the new location to move the file to * @param file is the new file where the data will be stored
* @return if the move was successful
*/ */
public boolean moveFile(File newFile){ public void saveToFile(File file) throws IOException {
boolean success = file.renameTo(newFile); BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file));
if(success) IOUtil.copyStream(in, out);
file = newFile; out.close();
return success;
}
/**
* Creates a temporary file in either the system
* temporary folder or by the setTempDir() function
*
* @return the temporary file
*/
protected File createTempFile() throws IOException {
if(tempDir != null)
return File.createTempFile("upload", ".part", tempDir.getAbsoluteFile());
else
return File.createTempFile("upload", ".part");
}
public void setTempDir(File dir){
if(!dir.isDirectory())
throw new RuntimeException("\""+dir.getAbsolutePath()+"\" is not a directory!");
if(!dir.canWrite())
throw new RuntimeException("\""+dir.getAbsolutePath()+"\" is not writable!");
tempDir = dir;
} }
} }

View file

@ -24,17 +24,17 @@
package zutil.net.http.multipart; package zutil.net.http.multipart;
import zutil.ProgressListener; import zutil.io.BufferedBoundaryInputStream;
import zutil.io.IOUtil;
import zutil.io.NullWriter;
import zutil.log.LogUtil; import zutil.log.LogUtil;
import zutil.net.http.HttpHeader; import zutil.net.http.HttpHeader;
import zutil.net.http.HttpHeaderParser; import zutil.net.http.HttpHeaderParser;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import java.io.*; import java.io.*;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.List;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -57,23 +57,23 @@ public class MultipartParser implements Iterable<MultipartField>{
/** The length of the HTTP Body */ /** The length of the HTTP Body */
private long contentLength; private long contentLength;
/** This is the input stream */ /** This is the input stream */
private BufferedReader in; private InputStream in;
private MultiPartIterator iterator; private MultiPartIterator iterator;
public MultipartParser(BufferedReader in, String delimiter, long length){ public MultipartParser(InputStream in, String delimiter, long length){
this.in = in; this.in = in;
this.delimiter = delimiter; this.delimiter = delimiter;
this.contentLength = length; this.contentLength = length;
} }
public MultipartParser(BufferedReader in, HttpHeader header){ public MultipartParser(InputStream in, HttpHeader header){
this(in, this(in,
parseDelimiter(header.getHeader("Content-type")), parseDelimiter(header.getHeader("Content-type")),
Long.parseLong( header.getHeader("Content-Length"))); Long.parseLong( header.getHeader("Content-Length")));
} }
public MultipartParser(HttpServletRequest req) throws IOException { public MultipartParser(HttpServletRequest req) throws IOException {
this(req.getReader(), this(req.getInputStream(),
parseDelimiter(req.getHeader("Content-type")), parseDelimiter(req.getHeader("Content-type")),
req.getContentLength()); req.getContentLength());
} }
@ -101,56 +101,39 @@ public class MultipartParser implements Iterable<MultipartField>{
protected class MultiPartIterator implements Iterator<MultipartField>{ protected class MultiPartIterator implements Iterator<MultipartField>{
private String currentLine; private BufferedBoundaryInputStream boundaryIn;
private boolean endOfStream; private BufferedReader buffIn;
private HttpHeaderParser parser; private HttpHeaderParser parser;
protected MultiPartIterator(){ protected MultiPartIterator(){
this.endOfStream = false; this.boundaryIn = new BufferedBoundaryInputStream(in);
this.parser = new HttpHeaderParser(in); this.buffIn = new BufferedReader(new InputStreamReader(boundaryIn));
this.parser = new HttpHeaderParser(buffIn);
this.parser.setReadStatusLine(false); this.parser.setReadStatusLine(false);
this.boundaryIn.setBoundary("--"+delimiter);
} }
@Override @Override
public boolean hasNext() { public boolean hasNext() {
try{ try {
findDelimiter(); IOUtil.copyStream(buffIn, new NullWriter());
if (endOfStream) return boundaryIn.hasNext();
return false;
return true;
} catch (IOException e){ } catch (IOException e){
logger.log(Level.SEVERE, null, e); logger.log(Level.SEVERE, null, e);
endOfStream = true;
} }
return false; return false;
} }
private void findDelimiter() throws IOException {
if (endOfStream || matchesDelimiter(currentLine))
return;
while ((currentLine=in.readLine()) != null) {
if (matchesDelimiter(currentLine))
break;
}
}
private boolean matchesDelimiter(String match){
if (match == null)
return false;
else if (match.equals("--" + delimiter + "--")) {
endOfStream = true;
return true;
}
else if (match.equals("--" + delimiter))
return true;
return false;
}
@Override @Override
public MultipartField next() { public MultipartField next() {
if (!hasNext()) if (!hasNext())
return null; return null;
try { try {
boundaryIn.next(); // Skip current boundary
HttpHeader header = parser.read(); HttpHeader header = parser.read();
String disposition = header.getHeader(HEADER_CONTENT_DISPOSITION); String disposition = header.getHeader(HEADER_CONTENT_DISPOSITION);
if (disposition != null){ if (disposition != null){
@ -158,16 +141,16 @@ public class MultipartParser implements Iterable<MultipartField>{
HttpHeaderParser.parseHeaderValue(map, disposition); HttpHeaderParser.parseHeaderValue(map, disposition);
if (map.containsKey("form-data")){ if (map.containsKey("form-data")){
if (map.containsKey("filename")){ if (map.containsKey("filename")){
MultipartFileField field = new MultipartFileField(); MultipartFileField field = new MultipartFileField(map, buffIn);
return field; return field;
} }
else{ else{
MultipartStringField field = new MultipartStringField(map.get("name")); MultipartStringField field = new MultipartStringField(map, buffIn);
return field; return field;
} }
} }
else { else {
logger.warning("Only form-data is supported"); logger.warning("Only multipart form-data is supported");
return this.next(); // find next field return this.next(); // find next field
} }
} }

View file

@ -1,7 +1,12 @@
package zutil.net.http.multipart; package zutil.net.http.multipart;
import java.util.HashMap; import zutil.io.IOUtil;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.Map;
/** /**
* Created by Ziver on 2016-07-06. * Created by Ziver on 2016-07-06.
@ -10,8 +15,9 @@ public class MultipartStringField implements MultipartField {
private String name; private String name;
private String value; private String value;
protected MultipartStringField(String name){ protected MultipartStringField(Map<String,String> header, BufferedReader in) throws IOException {
this.name = name; this.name = header.get("name");
value = IOUtil.getContentAsString(in);
} }
@Override @Override

View file

@ -31,6 +31,7 @@ import java.util.NoSuchElementException;
/** /**
* This class is a first in first out circular buffer with a fixed size. * This class is a first in first out circular buffer with a fixed size.
* If the size is exceed then the oldest item will be removed. * If the size is exceed then the oldest item will be removed.
*
* Created by Ziver on 2015-09-22. * Created by Ziver on 2015-09-22.
*/ */
public class CircularBuffer<T> implements Iterable<T>{ public class CircularBuffer<T> implements Iterable<T>{

View file

@ -29,13 +29,14 @@ import org.junit.Test;
import java.io.IOException; import java.io.IOException;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@SuppressWarnings("resource") @SuppressWarnings("resource")
public class BufferedBoundaryInputStreamTest { public class BufferedBoundaryInputStreamTest {
@Test @Test
public void testReadB1() throws IOException { public void normal() throws IOException {
StringInputStream inin = new StringInputStream("aaa#aaaaaaaaaaaaaaaa#aaaaaaaaaaaaaaa#"); StringInputStream inin = new StringInputStream("aaa#aaaaaaaaaaaaaaaa#aaaaaaaaaaaaaaa#");
BufferedBoundaryInputStream in = new BufferedBoundaryInputStream(inin); BufferedBoundaryInputStream in = new BufferedBoundaryInputStream(inin);
@ -61,7 +62,16 @@ public class BufferedBoundaryInputStreamTest {
} }
@Test @Test
public void testOnlyBoundaries() throws IOException { public void startWithBound() throws IOException {
StringInputStream inin = new StringInputStream("#aaa");
BufferedBoundaryInputStream in = new BufferedBoundaryInputStream(inin);
in.setBoundary("#");
assertEquals(-1, in.read());
}
@Test
public void onlyBoundaries() throws IOException {
StringInputStream inin = new StringInputStream("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); StringInputStream inin = new StringInputStream("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
BufferedBoundaryInputStream in = new BufferedBoundaryInputStream(inin); BufferedBoundaryInputStream in = new BufferedBoundaryInputStream(inin);
@ -72,14 +82,14 @@ public class BufferedBoundaryInputStreamTest {
assertEquals(-1, in.read()); assertEquals(-1, in.read());
assertEquals(-1, in.read()); assertEquals(-1, in.read());
in.next(); in.next();
if(!in.isBoundary()) if(!in.hasNext())
break; break;
} }
assertEquals(35, n); assertEquals(35, n);
} }
@Test @Test
public void testNoBounds() throws IOException { public void noBounds() throws IOException {
String data = "1234567891011121314151617181920"; String data = "1234567891011121314151617181920";
StringInputStream inin = new StringInputStream(data); StringInputStream inin = new StringInputStream(data);
BufferedBoundaryInputStream in = new BufferedBoundaryInputStream(inin); BufferedBoundaryInputStream in = new BufferedBoundaryInputStream(inin);

View file

@ -1,48 +1,47 @@
package zutil.net.http.multipart; package zutil.net.http.multipart;
import org.junit.Test; import org.junit.Test;
import zutil.io.StringInputStream;
import java.io.BufferedReader; import java.util.Iterator;
import java.io.StringReader;
import static junit.framework.TestCase.assertFalse;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail; import static org.junit.Assert.assertTrue;
/** /**
* JUnit test for MultipartParser class * JUnit test for MultipartParser class
* * <p>
* Created by Ziver on 2016-07-06. * Created by Ziver on 2016-07-06.
*/ */
public class MultipartParserTest { public class MultipartParserTest {
@Test @Test
public void singleFormDataField(){ public void singleFormDataField() {
String input = String input =
"------------------------------83ff53821b7c\n" + "------------------------------83ff53821b7c\n" +
"Content-Disposition: form-data; name=\"foo\"\n" + "Content-Disposition: form-data; name=\"foo\"\n" +
"\n" + "\n" +
"bar\n" + "bar\n" +
"------------------------------83ff53821b7c--"; "------------------------------83ff53821b7c--";
MultipartParser parser = new MultipartParser( MultipartParser parser = new MultipartParser(
new BufferedReader(new StringReader(input)), new StringInputStream(input),
"----------------------------83ff53821b7c", "----------------------------83ff53821b7c",
input.length()); input.length());
int count = 0; // Assertions
for(MultipartField field : parser){ Iterator<MultipartField> it = parser.iterator();
assertEquals(0, count); assertTrue(it.hasNext());
if (field instanceof MultipartStringField) { MultipartField field = it.next();
MultipartStringField stringField = (MultipartStringField)field; assertTrue(field instanceof MultipartStringField);
assertEquals("foo", stringField.getName()); MultipartStringField stringField = (MultipartStringField) field;
assertEquals("bar", stringField.getValue()); assertEquals("foo", stringField.getName());
} assertEquals("bar", stringField.getValue());
else fail("Field is not an instance of "+MultipartStringField.class); assertFalse(it.hasNext());
++count;
}
} }
//@Test //@Test
public void singleFileUpload(){ public void singleFileUpload() {
String input = String input =
"------------------------------83ff53821b7c\n" + "------------------------------83ff53821b7c\n" +
"Content-Disposition: form-data; name=\"img\"; filename=\"a.png\"\n" + "Content-Disposition: form-data; name=\"img\"; filename=\"a.png\"\n" +
@ -57,7 +56,7 @@ public class MultipartParserTest {
" IDAc????????-IEND?B`?\n" + " IDAc????????-IEND?B`?\n" +
"------------------------------83ff53821b7c--"; "------------------------------83ff53821b7c--";
MultipartParser parser = new MultipartParser( MultipartParser parser = new MultipartParser(
new BufferedReader(new StringReader(input)), new StringInputStream(input),
"----------------------------83ff53821b7c", "----------------------------83ff53821b7c",
input.length()); input.length());
@ -65,31 +64,31 @@ public class MultipartParserTest {
} }
//Test //Test
public void multiFileUpload(){ public void multiFileUpload() {
String input = String input =
" --AaB03x\n" + "--AaB03x\n" +
" Content-Disposition: form-data; name=\"submit-name\"\n" + "Content-Disposition: form-data; name=\"submit-name\"\n" +
"\n" + "\n" +
" Larry\n" + "Larry\n" +
" --AaB03x\n" + "--AaB03x\n" +
" Content-Disposition: form-data; name=\"files\"\n" + "Content-Disposition: form-data; name=\"files\"\n" +
" Content-Type: multipart/mixed; boundary=BbC04y\n" + "Content-Type: multipart/mixed; boundary=BbC04y\n" +
"\n" + "\n" +
" --BbC04y\n" + "--BbC04y\n" +
" Content-Disposition: file; filename=\"file1.txt\"\n" + "Content-Disposition: file; filename=\"file1.txt\"\n" +
" Content-Type: text/plain\n" + "Content-Type: text/plain\n" +
"\n" + "\n" +
" ... contents of file1.txt ...\n" + "... contents of file1.txt ...\n" +
" --BbC04y\n" + "--BbC04y\n" +
" Content-Disposition: file; filename=\"file2.gif\"\n" + "Content-Disposition: file; filename=\"file2.gif\"\n" +
" Content-Type: image/gif\n" + "Content-Type: image/gif\n" +
" Content-Transfer-Encoding: binary\n" + "Content-Transfer-Encoding: binary\n" +
"\n" + "\n" +
" ...contents of file2.gif...\n" + "...contents of file2.gif...\n" +
" --BbC04y--\n" + "--BbC04y--\n" +
" --AaB03x--"; "--AaB03x--";
MultipartParser parser = new MultipartParser( MultipartParser parser = new MultipartParser(
new BufferedReader(new StringReader(input)), new StringInputStream(input),
"AaB03x", "AaB03x",
input.length()); input.length());
} }

View file

@ -83,19 +83,16 @@ public class CircularBufferTest {
} catch (IndexOutOfBoundsException e) {} } catch (IndexOutOfBoundsException e) {}
} }
@Test @Test(expected = NoSuchElementException.class)
public void iteratorEmpty() { public void iteratorEmpty() {
CircularBuffer<Integer> buff = new CircularBuffer<Integer>(10); CircularBuffer<Integer> buff = new CircularBuffer<Integer>(10);
Iterator<Integer> it = buff.iterator(); Iterator<Integer> it = buff.iterator();
assert (!it.hasNext()); assert (!it.hasNext());
try { it.next(); // Exception
it.next();
fail("NoSuchElementException was not thrown");
} catch (NoSuchElementException e) {}
} }
@Test @Test(expected = NoSuchElementException.class)
public void iteratorThreeElements() { public void iteratorThreeElements() {
CircularBuffer<Integer> buff = new CircularBuffer<Integer>(10); CircularBuffer<Integer> buff = new CircularBuffer<Integer>(10);
buff.add(10); buff.add(10);
@ -111,9 +108,6 @@ public class CircularBufferTest {
assertEquals((Integer) 10, it.next()); assertEquals((Integer) 10, it.next());
assert (!it.hasNext()); assert (!it.hasNext());
try { it.next(); // Exception
it.next();
fail("NoSuchElementException was not thrown");
} catch (NoSuchElementException e) {}
} }
} }