| // |
| // ======================================================================== |
| // Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd. |
| // ------------------------------------------------------------------------ |
| // All rights reserved. This program and the accompanying materials |
| // are made available under the terms of the Eclipse Public License v1.0 |
| // and Apache License v2.0 which accompanies this distribution. |
| // |
| // The Eclipse Public License is available at |
| // http://www.eclipse.org/legal/epl-v10.html |
| // |
| // The Apache License v2.0 is available at |
| // http://www.opensource.org/licenses/apache2.0.php |
| // |
| // You may elect to redistribute this code under either of these licenses. |
| // ======================================================================== |
| // |
| |
| package org.eclipse.jetty.util; |
| |
| import static org.hamcrest.Matchers.is; |
| import static org.hamcrest.Matchers.not; |
| import static org.hamcrest.Matchers.notNullValue; |
| import static org.hamcrest.Matchers.nullValue; |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertNotNull; |
| import static org.junit.Assert.assertNull; |
| import static org.junit.Assert.assertThat; |
| import static org.junit.Assert.assertTrue; |
| import static org.junit.Assert.fail; |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.ByteArrayOutputStream; |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.util.Collection; |
| |
| import javax.servlet.MultipartConfigElement; |
| import javax.servlet.ServletException; |
| import javax.servlet.http.Part; |
| |
| import org.eclipse.jetty.util.MultiPartInputStreamParser.MultiPart; |
| import org.junit.Test; |
| |
| /** |
| * MultiPartInputStreamTest |
| * |
| * |
| */ |
| public class MultiPartInputStreamTest |
| { |
| private static final String FILENAME = "stuff.txt"; |
| protected String _contentType = "multipart/form-data, boundary=AaB03x"; |
| protected String _multi = createMultipartRequestString(FILENAME); |
| protected String _dirname = System.getProperty("java.io.tmpdir")+File.separator+"myfiles-"+System.currentTimeMillis(); |
| protected File _tmpDir = new File(_dirname); |
| |
| public MultiPartInputStreamTest () |
| { |
| _tmpDir.deleteOnExit(); |
| } |
| |
| public void testBadMultiPartRequest() |
| throws Exception |
| { |
| String boundary = "X0Y0"; |
| String str = "--" + boundary + "\r\n"+ |
| "Content-Disposition: form-data; name=\"fileup\"; filename=\"test.upload\"\r\n"+ |
| "Content-Type: application/octet-stream\r\n\r\n"+ |
| "How now brown cow."+ |
| "\r\n--" + boundary + "-\r\n\r\n"; |
| |
| MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50); |
| MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(str.getBytes()), |
| "multipart/form-data, boundary="+boundary, |
| config, |
| _tmpDir); |
| mpis.setDeleteOnExit(true); |
| try |
| { |
| mpis.getParts(); |
| fail ("Multipart incomplete"); |
| } |
| catch (IOException e) |
| { |
| assertTrue(e.getMessage().startsWith("Incomplete")); |
| } |
| } |
| |
| |
| public void testNoBoundaryRequest() |
| throws Exception |
| { |
| String str = "--\r\n"+ |
| "Content-Disposition: form-data; name=\"fileName\"\r\n"+ |
| "Content-Type: text/plain; charset=US-ASCII\r\n"+ |
| "Content-Transfer-Encoding: 8bit\r\n"+ |
| "\r\n"+ |
| "abc\r\n"+ |
| "--\r\n"+ |
| "Content-Disposition: form-data; name=\"desc\"\r\n"+ |
| "Content-Type: text/plain; charset=US-ASCII\r\n"+ |
| "Content-Transfer-Encoding: 8bit\r\n"+ |
| "\r\n"+ |
| "123\r\n"+ |
| "--\r\n"+ |
| "Content-Disposition: form-data; name=\"title\"\r\n"+ |
| "Content-Type: text/plain; charset=US-ASCII\r\n"+ |
| "Content-Transfer-Encoding: 8bit\r\n"+ |
| "\r\n"+ |
| "ttt\r\n"+ |
| "--\r\n"+ |
| "Content-Disposition: form-data; name=\"datafile5239138112980980385.txt\"; filename=\"datafile5239138112980980385.txt\"\r\n"+ |
| "Content-Type: application/octet-stream; charset=ISO-8859-1\r\n"+ |
| "Content-Transfer-Encoding: binary\r\n"+ |
| "\r\n"+ |
| "000\r\n"+ |
| "----\r\n"; |
| |
| MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50); |
| MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(str.getBytes()), |
| "multipart/form-data", |
| config, |
| _tmpDir); |
| mpis.setDeleteOnExit(true); |
| Collection<Part> parts = mpis.getParts(); |
| assertThat(parts.size(), is(4)); |
| |
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| Part fileName = mpis.getPart("fileName"); |
| assertThat(fileName, notNullValue()); |
| assertThat(fileName.getSize(), is(3L)); |
| IO.copy(fileName.getInputStream(), baos); |
| assertThat(baos.toString("US-ASCII"), is("abc")); |
| |
| baos = new ByteArrayOutputStream(); |
| Part desc = mpis.getPart("desc"); |
| assertThat(desc, notNullValue()); |
| assertThat(desc.getSize(), is(3L)); |
| IO.copy(desc.getInputStream(), baos); |
| assertThat(baos.toString("US-ASCII"), is("123")); |
| |
| baos = new ByteArrayOutputStream(); |
| Part title = mpis.getPart("title"); |
| assertThat(title, notNullValue()); |
| assertThat(title.getSize(), is(3L)); |
| IO.copy(title.getInputStream(), baos); |
| assertThat(baos.toString("US-ASCII"), is("ttt")); |
| } |
| |
| @Test |
| public void testNonMultiPartRequest() |
| throws Exception |
| { |
| MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50); |
| MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(_multi.getBytes()), |
| "Content-type: text/plain", |
| config, |
| _tmpDir); |
| mpis.setDeleteOnExit(true); |
| assertTrue(mpis.getParts().isEmpty()); |
| } |
| |
| @Test |
| public void testNoBody() |
| throws Exception |
| { |
| String body = ""; |
| |
| MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50); |
| MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(body.getBytes()), |
| _contentType, |
| config, |
| _tmpDir); |
| mpis.setDeleteOnExit(true); |
| try |
| { |
| mpis.getParts(); |
| fail ("Multipart missing body"); |
| } |
| catch (IOException e) |
| { |
| assertTrue(e.getMessage().startsWith("Missing content")); |
| } |
| } |
| |
| @Test |
| public void testWhitespaceBodyWithCRLF() |
| throws Exception |
| { |
| String whitespace = " \n\n\n\r\n\r\n\r\n\r\n"; |
| |
| |
| MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50); |
| MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(whitespace.getBytes()), |
| _contentType, |
| config, |
| _tmpDir); |
| mpis.setDeleteOnExit(true); |
| try |
| { |
| mpis.getParts(); |
| fail ("Multipart missing body"); |
| } |
| catch (IOException e) |
| { |
| assertTrue(e.getMessage().startsWith("Missing initial")); |
| } |
| } |
| |
| @Test |
| public void testWhitespaceBody() |
| throws Exception |
| { |
| String whitespace = " "; |
| |
| MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50); |
| MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(whitespace.getBytes()), |
| _contentType, |
| config, |
| _tmpDir); |
| mpis.setDeleteOnExit(true); |
| try |
| { |
| mpis.getParts(); |
| fail ("Multipart missing body"); |
| } |
| catch (IOException e) |
| { |
| assertTrue(e.getMessage().startsWith("Missing initial")); |
| } |
| } |
| |
| @Test |
| public void testLeadingWhitespaceBodyWithCRLF() |
| throws Exception |
| { |
| String body = " \n\n\n\r\n\r\n\r\n\r\n"+ |
| "--AaB03x\r\n"+ |
| "content-disposition: form-data; name=\"field1\"\r\n"+ |
| "\r\n"+ |
| "Joe Blow\r\n"+ |
| "--AaB03x\r\n"+ |
| "content-disposition: form-data; name=\"stuff\"; filename=\"" + "foo.txt" + "\"\r\n"+ |
| "Content-Type: text/plain\r\n"+ |
| "\r\n"+"aaaa"+ |
| "bbbbb"+"\r\n" + |
| "--AaB03x--\r\n"; |
| |
| |
| MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50); |
| MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(body.getBytes()), |
| _contentType, |
| config, |
| _tmpDir); |
| mpis.setDeleteOnExit(true); |
| |
| Collection<Part> parts = mpis.getParts(); |
| assertThat(parts, notNullValue()); |
| assertThat(parts.size(), is(2)); |
| Part field1 = mpis.getPart("field1"); |
| assertThat(field1, notNullValue()); |
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| IO.copy(field1.getInputStream(), baos); |
| assertThat(baos.toString("US-ASCII"), is("Joe Blow")); |
| |
| Part stuff = mpis.getPart("stuff"); |
| assertThat(stuff, notNullValue()); |
| baos = new ByteArrayOutputStream(); |
| IO.copy(stuff.getInputStream(), baos); |
| assertTrue(baos.toString("US-ASCII").contains("aaaa")); |
| } |
| |
| |
| |
| @Test |
| public void testLeadingWhitespaceBodyWithoutCRLF() |
| throws Exception |
| { |
| String body = " "+ |
| "--AaB03x\r\n"+ |
| "content-disposition: form-data; name=\"field1\"\r\n"+ |
| "\r\n"+ |
| "Joe Blow\r\n"+ |
| "--AaB03x\r\n"+ |
| "content-disposition: form-data; name=\"stuff\"; filename=\"" + "foo.txt" + "\"\r\n"+ |
| "Content-Type: text/plain\r\n"+ |
| "\r\n"+"aaaa"+ |
| "bbbbb"+"\r\n" + |
| "--AaB03x--\r\n"; |
| |
| MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50); |
| MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(body.getBytes()), |
| _contentType, |
| config, |
| _tmpDir); |
| mpis.setDeleteOnExit(true); |
| |
| Collection<Part> parts = mpis.getParts(); |
| assertThat(parts, notNullValue()); |
| assertThat(parts.size(), is(2)); |
| Part field1 = mpis.getPart("field1"); |
| assertThat(field1, notNullValue()); |
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| IO.copy(field1.getInputStream(), baos); |
| assertThat(baos.toString("US-ASCII"), is("Joe Blow")); |
| |
| Part stuff = mpis.getPart("stuff"); |
| assertThat(stuff, notNullValue()); |
| baos = new ByteArrayOutputStream(); |
| IO.copy(stuff.getInputStream(), baos); |
| assertTrue(baos.toString("US-ASCII").contains("bbbbb")); |
| } |
| |
| |
| |
| |
| |
| |
| @Test |
| public void testNoLimits() |
| throws Exception |
| { |
| MultipartConfigElement config = new MultipartConfigElement(_dirname); |
| MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(_multi.getBytes()), |
| _contentType, |
| config, |
| _tmpDir); |
| mpis.setDeleteOnExit(true); |
| Collection<Part> parts = mpis.getParts(); |
| assertFalse(parts.isEmpty()); |
| } |
| |
| @Test |
| public void testRequestTooBig () |
| throws Exception |
| { |
| MultipartConfigElement config = new MultipartConfigElement(_dirname, 60, 100, 50); |
| MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(_multi.getBytes()), |
| _contentType, |
| config, |
| _tmpDir); |
| mpis.setDeleteOnExit(true); |
| Collection<Part> parts = null; |
| try |
| { |
| parts = mpis.getParts(); |
| fail("Request should have exceeded maxRequestSize"); |
| } |
| catch (IllegalStateException e) |
| { |
| assertTrue(e.getMessage().startsWith("Request exceeds maxRequestSize")); |
| } |
| } |
| |
| @Test |
| public void testFileTooBig() |
| throws Exception |
| { |
| MultipartConfigElement config = new MultipartConfigElement(_dirname, 40, 1024, 30); |
| MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(_multi.getBytes()), |
| _contentType, |
| config, |
| _tmpDir); |
| mpis.setDeleteOnExit(true); |
| Collection<Part> parts = null; |
| try |
| { |
| parts = mpis.getParts(); |
| fail("stuff.txt should have been larger than maxFileSize"); |
| } |
| catch (IllegalStateException e) |
| { |
| assertTrue(e.getMessage().startsWith("Multipart Mime part")); |
| } |
| } |
| |
| @Test |
| public void testPartFileNotDeleted () throws Exception |
| { |
| MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50); |
| MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(createMultipartRequestString("tptfd").getBytes()), |
| _contentType, |
| config, |
| _tmpDir); |
| mpis.setDeleteOnExit(true); |
| Collection<Part> parts = mpis.getParts(); |
| |
| MultiPart part = (MultiPart)mpis.getPart("stuff"); |
| File stuff = ((MultiPartInputStreamParser.MultiPart)part).getFile(); |
| assertThat(stuff,notNullValue()); // longer than 100 bytes, should already be a tmp file |
| part.write("tptfd.txt"); |
| File tptfd = new File (_dirname+File.separator+"tptfd.txt"); |
| assertThat(tptfd.exists(), is(true)); |
| assertThat(stuff.exists(), is(false)); //got renamed |
| part.cleanUp(); |
| assertThat(tptfd.exists(), is(true)); //explicitly written file did not get removed after cleanup |
| tptfd.deleteOnExit(); //clean up test |
| } |
| |
| @Test |
| public void testPartTmpFileDeletion () throws Exception |
| { |
| MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50); |
| MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(createMultipartRequestString("tptfd").getBytes()), |
| _contentType, |
| config, |
| _tmpDir); |
| mpis.setDeleteOnExit(true); |
| Collection<Part> parts = mpis.getParts(); |
| |
| MultiPart part = (MultiPart)mpis.getPart("stuff"); |
| File stuff = ((MultiPartInputStreamParser.MultiPart)part).getFile(); |
| assertThat(stuff,notNullValue()); // longer than 100 bytes, should already be a tmp file |
| assertThat (stuff.exists(), is(true)); |
| part.cleanUp(); |
| assertThat(stuff.exists(), is(false)); //tmp file was removed after cleanup |
| } |
| |
| @Test |
| public void testLFOnlyRequest() |
| throws Exception |
| { |
| String str = "--AaB03x\n"+ |
| "content-disposition: form-data; name=\"field1\"\n"+ |
| "\n"+ |
| "Joe Blow\n"+ |
| "--AaB03x\n"+ |
| "content-disposition: form-data; name=\"field2\"\n"+ |
| "\n"+ |
| "Other\n"+ |
| "--AaB03x--\n"; |
| |
| MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50); |
| MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(str.getBytes()), |
| _contentType, |
| config, |
| _tmpDir); |
| mpis.setDeleteOnExit(true); |
| Collection<Part> parts = mpis.getParts(); |
| assertThat(parts.size(), is(2)); |
| Part p1 = mpis.getPart("field1"); |
| assertThat(p1, notNullValue()); |
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| IO.copy(p1.getInputStream(), baos); |
| assertThat(baos.toString("UTF-8"), is("Joe Blow")); |
| |
| Part p2 = mpis.getPart("field2"); |
| assertThat(p2, notNullValue()); |
| baos = new ByteArrayOutputStream(); |
| IO.copy(p2.getInputStream(), baos); |
| assertThat(baos.toString("UTF-8"), is("Other")); |
| } |
| |
| @Test |
| public void testCROnlyRequest() |
| throws Exception |
| { |
| String str = "--AaB03x\r"+ |
| "content-disposition: form-data; name=\"field1\"\r"+ |
| "\r"+ |
| "Joe Blow\r"+ |
| "--AaB03x\r"+ |
| "content-disposition: form-data; name=\"field2\"\r"+ |
| "\r"+ |
| "Other\r"+ |
| "--AaB03x--\r"; |
| |
| MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50); |
| MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(str.getBytes()), |
| _contentType, |
| config, |
| _tmpDir); |
| mpis.setDeleteOnExit(true); |
| Collection<Part> parts = mpis.getParts(); |
| assertThat(parts.size(), is(2)); |
| |
| assertThat(parts.size(), is(2)); |
| Part p1 = mpis.getPart("field1"); |
| assertThat(p1, notNullValue()); |
| |
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| IO.copy(p1.getInputStream(), baos); |
| assertThat(baos.toString("UTF-8"), is("Joe Blow")); |
| |
| Part p2 = mpis.getPart("field2"); |
| assertThat(p2, notNullValue()); |
| baos = new ByteArrayOutputStream(); |
| IO.copy(p2.getInputStream(), baos); |
| assertThat(baos.toString("UTF-8"), is("Other")); |
| } |
| |
| @Test |
| public void testCRandLFMixRequest() |
| throws Exception |
| { |
| String str = "--AaB03x\r"+ |
| "content-disposition: form-data; name=\"field1\"\r"+ |
| "\r"+ |
| "\nJoe Blow\n"+ |
| "\r"+ |
| "--AaB03x\r"+ |
| "content-disposition: form-data; name=\"field2\"\r"+ |
| "\r"+ |
| "Other\r"+ |
| "--AaB03x--\r"; |
| |
| MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50); |
| MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(str.getBytes()), |
| _contentType, |
| config, |
| _tmpDir); |
| mpis.setDeleteOnExit(true); |
| Collection<Part> parts = mpis.getParts(); |
| assertThat(parts.size(), is(2)); |
| |
| Part p1 = mpis.getPart("field1"); |
| assertThat(p1, notNullValue()); |
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| IO.copy(p1.getInputStream(), baos); |
| assertThat(baos.toString("UTF-8"), is("\nJoe Blow\n")); |
| |
| Part p2 = mpis.getPart("field2"); |
| assertThat(p2, notNullValue()); |
| baos = new ByteArrayOutputStream(); |
| IO.copy(p2.getInputStream(), baos); |
| assertThat(baos.toString("UTF-8"), is("Other")); |
| } |
| |
| @Test |
| public void testBufferOverflowNoCRLF () throws Exception |
| { |
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| baos.write("--AaB03x".getBytes()); |
| for (int i=0; i< 8500; i++) //create content that will overrun default buffer size of BufferedInputStream |
| { |
| baos.write('a'); |
| } |
| |
| MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50); |
| MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(baos.toByteArray()), |
| _contentType, |
| config, |
| _tmpDir); |
| mpis.setDeleteOnExit(true); |
| try |
| { |
| mpis.getParts(); |
| fail ("Multipart buffer overrun"); |
| } |
| catch (IOException e) |
| { |
| assertTrue(e.getMessage().startsWith("Buffer size exceeded")); |
| } |
| |
| } |
| |
| |
| public void testCharsetEncoding () throws Exception |
| { |
| String contentType = "multipart/form-data; boundary=TheBoundary; charset=ISO-8859-1"; |
| String str = "--TheBoundary\r"+ |
| "content-disposition: form-data; name=\"field1\"\r"+ |
| "\r"+ |
| "\nJoe Blow\n"+ |
| "\r"+ |
| "--TheBoundary--\r"; |
| |
| MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50); |
| MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(str.getBytes()), |
| contentType, |
| config, |
| _tmpDir); |
| mpis.setDeleteOnExit(true); |
| Collection<Part> parts = mpis.getParts(); |
| assertThat(parts.size(), is(1)); |
| } |
| |
| |
| @Test |
| public void testBadlyEncodedFilename() throws Exception |
| { |
| |
| String contents = "--AaB03x\r\n"+ |
| "content-disposition: form-data; name=\"stuff\"; filename=\"" +"Taken on Aug 22 \\ 2012.jpg" + "\"\r\n"+ |
| "Content-Type: text/plain\r\n"+ |
| "\r\n"+"stuff"+ |
| "aaa"+"\r\n" + |
| "--AaB03x--\r\n"; |
| |
| MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50); |
| MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(contents.getBytes()), |
| _contentType, |
| config, |
| _tmpDir); |
| mpis.setDeleteOnExit(true); |
| Collection<Part> parts = mpis.getParts(); |
| assertThat(parts.size(), is(1)); |
| assertThat(((MultiPartInputStreamParser.MultiPart)parts.iterator().next()).getSubmittedFileName(), is("Taken on Aug 22 \\ 2012.jpg")); |
| } |
| |
| @Test |
| public void testBadlyEncodedMSFilename() throws Exception |
| { |
| |
| String contents = "--AaB03x\r\n"+ |
| "content-disposition: form-data; name=\"stuff\"; filename=\"" +"c:\\this\\really\\is\\some\\path\\to\\a\\file.txt" + "\"\r\n"+ |
| "Content-Type: text/plain\r\n"+ |
| "\r\n"+"stuff"+ |
| "aaa"+"\r\n" + |
| "--AaB03x--\r\n"; |
| |
| MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50); |
| MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(contents.getBytes()), |
| _contentType, |
| config, |
| _tmpDir); |
| mpis.setDeleteOnExit(true); |
| Collection<Part> parts = mpis.getParts(); |
| assertThat(parts.size(), is(1)); |
| assertThat(((MultiPartInputStreamParser.MultiPart)parts.iterator().next()).getSubmittedFileName(), is("c:\\this\\really\\is\\some\\path\\to\\a\\file.txt")); |
| } |
| |
| @Test |
| public void testCorrectlyEncodedMSFilename() throws Exception |
| { |
| String contents = "--AaB03x\r\n"+ |
| "content-disposition: form-data; name=\"stuff\"; filename=\"" +"c:\\\\this\\\\really\\\\is\\\\some\\\\path\\\\to\\\\a\\\\file.txt" + "\"\r\n"+ |
| "Content-Type: text/plain\r\n"+ |
| "\r\n"+"stuff"+ |
| "aaa"+"\r\n" + |
| "--AaB03x--\r\n"; |
| |
| MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50); |
| MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(contents.getBytes()), |
| _contentType, |
| config, |
| _tmpDir); |
| mpis.setDeleteOnExit(true); |
| Collection<Part> parts = mpis.getParts(); |
| assertThat(parts.size(), is(1)); |
| assertThat(((MultiPartInputStreamParser.MultiPart)parts.iterator().next()).getSubmittedFileName(), is("c:\\this\\really\\is\\some\\path\\to\\a\\file.txt")); |
| } |
| |
| public void testMulti () |
| throws Exception |
| { |
| testMulti(FILENAME); |
| } |
| |
| @Test |
| public void testMultiWithSpaceInFilename() throws Exception |
| { |
| testMulti("stuff with spaces.txt"); |
| } |
| |
| |
| |
| |
| private void testMulti(String filename) throws IOException, ServletException, InterruptedException |
| { |
| MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50); |
| MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(createMultipartRequestString(filename).getBytes()), |
| _contentType, |
| config, |
| _tmpDir); |
| mpis.setDeleteOnExit(true); |
| Collection<Part> parts = mpis.getParts(); |
| assertThat(parts.size(), is(2)); |
| Part field1 = mpis.getPart("field1"); //field 1 too small to go into tmp file, should be in internal buffer |
| assertThat(field1,notNullValue()); |
| assertThat(field1.getName(),is("field1")); |
| InputStream is = field1.getInputStream(); |
| ByteArrayOutputStream os = new ByteArrayOutputStream(); |
| IO.copy(is, os); |
| assertEquals("Joe Blow", new String(os.toByteArray())); |
| assertEquals(8, field1.getSize()); |
| |
| assertNotNull(((MultiPartInputStreamParser.MultiPart)field1).getBytes());//in internal buffer |
| field1.write("field1.txt"); |
| assertNull(((MultiPartInputStreamParser.MultiPart)field1).getBytes());//no longer in internal buffer |
| File f = new File (_dirname+File.separator+"field1.txt"); |
| assertTrue(f.exists()); |
| field1.write("another_field1.txt"); //write after having already written |
| File f2 = new File(_dirname+File.separator+"another_field1.txt"); |
| assertTrue(f2.exists()); |
| assertFalse(f.exists()); //should have been renamed |
| field1.delete(); //file should be deleted |
| assertFalse(f.exists()); //original file was renamed |
| assertFalse(f2.exists()); //2nd written file was explicitly deleted |
| |
| MultiPart stuff = (MultiPart)mpis.getPart("stuff"); |
| assertThat(stuff.getSubmittedFileName(), is(filename)); |
| assertThat(stuff.getContentType(),is("text/plain")); |
| assertThat(stuff.getHeader("Content-Type"),is("text/plain")); |
| assertThat(stuff.getHeaders("content-type").size(),is(1)); |
| assertThat(stuff.getHeader("content-disposition"),is("form-data; name=\"stuff\"; filename=\"" + filename + "\"")); |
| assertThat(stuff.getHeaderNames().size(),is(2)); |
| assertThat(stuff.getSize(),is(51L)); |
| |
| File tmpfile = ((MultiPartInputStreamParser.MultiPart)stuff).getFile(); |
| assertThat(tmpfile,notNullValue()); // longer than 100 bytes, should already be a tmp file |
| assertThat(((MultiPartInputStreamParser.MultiPart)stuff).getBytes(),nullValue()); //not in an internal buffer |
| assertThat(tmpfile.exists(),is(true)); |
| assertThat(tmpfile.getName(),is(not("stuff with space.txt"))); |
| stuff.write(filename); |
| f = new File(_dirname+File.separator+filename); |
| assertThat(f.exists(),is(true)); |
| assertThat(tmpfile.exists(), is(false)); |
| try |
| { |
| stuff.getInputStream(); |
| } |
| catch (Exception e) |
| { |
| fail("Part.getInputStream() after file rename operation"); |
| } |
| f.deleteOnExit(); //clean up after test |
| } |
| |
| @Test |
| public void testMultiSameNames () |
| throws Exception |
| { |
| String sameNames = "--AaB03x\r\n"+ |
| "content-disposition: form-data; name=\"stuff\"; filename=\"stuff1.txt\"\r\n"+ |
| "Content-Type: text/plain\r\n"+ |
| "\r\n"+ |
| "00000\r\n"+ |
| "--AaB03x\r\n"+ |
| "content-disposition: form-data; name=\"stuff\"; filename=\"stuff2.txt\"\r\n"+ |
| "Content-Type: text/plain\r\n"+ |
| "\r\n"+ |
| "110000000000000000000000000000000000000000000000000\r\n"+ |
| "--AaB03x--\r\n"; |
| |
| MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50); |
| MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(sameNames.getBytes()), |
| _contentType, |
| config, |
| _tmpDir); |
| mpis.setDeleteOnExit(true); |
| Collection<Part> parts = mpis.getParts(); |
| assertEquals(2, parts.size()); |
| for (Part p:parts) |
| assertEquals("stuff", p.getName()); |
| |
| //if they all have the name name, then only retrieve the first one |
| Part p = mpis.getPart("stuff"); |
| assertNotNull(p); |
| assertEquals(5, p.getSize()); |
| } |
| |
| @Test |
| public void testBase64EncodedContent () throws Exception |
| { |
| String contentWithEncodedPart = |
| "--AaB03x\r\n"+ |
| "Content-disposition: form-data; name=\"other\"\r\n"+ |
| "Content-Type: text/plain\r\n"+ |
| "\r\n"+ |
| "other" + "\r\n"+ |
| "--AaB03x\r\n"+ |
| "Content-disposition: form-data; name=\"stuff\"; filename=\"stuff.txt\"\r\n"+ |
| "Content-Transfer-Encoding: base64\r\n"+ |
| "Content-Type: application/octet-stream\r\n"+ |
| "\r\n"+ |
| B64Code.encode("hello jetty") + "\r\n"+ |
| "--AaB03x\r\n"+ |
| "Content-disposition: form-data; name=\"final\"\r\n"+ |
| "Content-Type: text/plain\r\n"+ |
| "\r\n"+ |
| "the end" + "\r\n"+ |
| "--AaB03x--\r\n"; |
| |
| MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50); |
| MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(contentWithEncodedPart.getBytes()), |
| _contentType, |
| config, |
| _tmpDir); |
| mpis.setDeleteOnExit(true); |
| Collection<Part> parts = mpis.getParts(); |
| assertEquals(3, parts.size()); |
| |
| Part p1 = mpis.getPart("other"); |
| assertNotNull(p1); |
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| IO.copy(p1.getInputStream(), baos); |
| assertEquals("other", baos.toString("US-ASCII")); |
| |
| Part p2 = mpis.getPart("stuff"); |
| assertNotNull(p2); |
| baos = new ByteArrayOutputStream(); |
| IO.copy(p2.getInputStream(), baos); |
| assertEquals("hello jetty", baos.toString("US-ASCII")); |
| |
| Part p3 = mpis.getPart("final"); |
| assertNotNull(p3); |
| baos = new ByteArrayOutputStream(); |
| IO.copy(p3.getInputStream(), baos); |
| assertEquals("the end", baos.toString("US-ASCII")); |
| } |
| |
| @Test |
| public void testQuotedPrintableEncoding () throws Exception |
| { |
| String contentWithEncodedPart = |
| "--AaB03x\r\n"+ |
| "Content-disposition: form-data; name=\"other\"\r\n"+ |
| "Content-Type: text/plain\r\n"+ |
| "\r\n"+ |
| "other" + "\r\n"+ |
| "--AaB03x\r\n"+ |
| "Content-disposition: form-data; name=\"stuff\"; filename=\"stuff.txt\"\r\n"+ |
| "Content-Transfer-Encoding: quoted-printable\r\n"+ |
| "Content-Type: text/plain\r\n"+ |
| "\r\n"+ |
| "truth=3Dbeauty" + "\r\n"+ |
| "--AaB03x--\r\n"; |
| MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50); |
| MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(contentWithEncodedPart.getBytes()), |
| _contentType, |
| config, |
| _tmpDir); |
| mpis.setDeleteOnExit(true); |
| Collection<Part> parts = mpis.getParts(); |
| assertEquals(2, parts.size()); |
| |
| Part p1 = mpis.getPart("other"); |
| assertNotNull(p1); |
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| IO.copy(p1.getInputStream(), baos); |
| assertEquals("other", baos.toString("US-ASCII")); |
| |
| Part p2 = mpis.getPart("stuff"); |
| assertNotNull(p2); |
| baos = new ByteArrayOutputStream(); |
| IO.copy(p2.getInputStream(), baos); |
| assertEquals("truth=beauty", baos.toString("US-ASCII")); |
| } |
| |
| |
| |
| |
| |
| private String createMultipartRequestString(String filename) |
| { |
| int length = filename.length(); |
| String name = filename; |
| if (length > 10) |
| name = filename.substring(0,10); |
| StringBuffer filler = new StringBuffer(); |
| int i = name.length(); |
| while (i < 51) |
| { |
| filler.append("0"); |
| i++; |
| } |
| |
| return "--AaB03x\r\n"+ |
| "content-disposition: form-data; name=\"field1\"; filename=\"frooble.txt\"\r\n"+ |
| "\r\n"+ |
| "Joe Blow\r\n"+ |
| "--AaB03x\r\n"+ |
| "content-disposition: form-data; name=\"stuff\"; filename=\"" + filename + "\"\r\n"+ |
| "Content-Type: text/plain\r\n"+ |
| "\r\n"+name+ |
| filler.toString()+"\r\n" + |
| "--AaB03x--\r\n"; |
| } |
| } |