001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.commons.fileupload;
018    
019    import java.io.ByteArrayInputStream;
020    import java.io.ByteArrayOutputStream;
021    import java.io.FilterInputStream;
022    import java.io.IOException;
023    import java.io.InputStream;
024    import java.io.OutputStreamWriter;
025    import java.util.Iterator;
026    import java.util.List;
027    import javax.servlet.http.HttpServletRequest;
028    
029    import org.apache.commons.fileupload.FileUploadBase.IOFileUploadException;
030    import org.apache.commons.fileupload.disk.DiskFileItemFactory;
031    import org.apache.commons.fileupload.servlet.ServletFileUpload;
032    import org.apache.commons.fileupload.servlet.ServletRequestContext;
033    
034    import junit.framework.TestCase;
035    
036    
037    /**
038     * Unit test for items with varying sizes.
039     */
040    public class StreamingTest extends TestCase
041    {
042        /**
043         * Tests a file upload with varying file sizes.
044         */
045        public void testFileUpload()
046                throws IOException, FileUploadException
047        {
048            byte[] request = newRequest();
049            List fileItems = parseUpload(request);
050            Iterator fileIter = fileItems.iterator();
051            int add = 16;
052            int num = 0;
053            for (int i = 0;  i < 16384;  i += add) {
054                if (++add == 32) {
055                    add = 16;
056                }
057                FileItem item = (FileItem) fileIter.next();
058                assertEquals("field" + (num++), item.getFieldName());
059                byte[] bytes = item.get();
060                assertEquals(i, bytes.length);
061                for (int j = 0;  j < i;  j++) {
062                    assertEquals((byte) j, bytes[j]);
063                }
064            }
065            assertTrue(!fileIter.hasNext());
066        }
067    
068    
069        /**
070         * Tests, whether an invalid request throws a proper
071         * exception.
072         */
073        public void testFileUploadException()
074                    throws IOException, FileUploadException {
075            byte[] request = newRequest();
076            byte[] invalidRequest = new byte[request.length-11];
077            System.arraycopy(request, 0, invalidRequest, 0, request.length-11);
078            try {
079                    parseUpload(invalidRequest);
080                    fail("Expected EndOfStreamException");
081            } catch (IOFileUploadException e) {
082                    assertTrue(e.getCause() instanceof MultipartStream.MalformedStreamException);
083            }
084        }
085    
086        /**
087         * Tests, whether an IOException is properly delegated.
088         */
089        public void testIOException()
090                    throws IOException {
091            byte[] request = newRequest();
092            InputStream stream = new FilterInputStream(new ByteArrayInputStream(request)){
093                    private int num;
094                    public int read() throws IOException {
095                            if (++num > 123) {
096                                    throw new IOException("123");
097                            }
098                            return super.read();
099                    }
100                            public int read(byte[] pB, int pOff, int pLen)
101                                            throws IOException {
102                                    for (int i = 0;  i < pLen;  i++) {
103                                            int res = read();
104                                            if (res == -1) {
105                                                    return i == 0 ? -1 : i;
106                                            }
107                                            pB[pOff+i] = (byte) res;
108                                    }
109                                    return pLen;
110                            }
111            };
112            try {
113                    parseUpload(stream, request.length);
114                    fail("Expected IOException");
115            } catch (FileUploadException e) {
116                    assertTrue(e.getCause() instanceof IOException);
117                    assertEquals("123", e.getCause().getMessage());
118            }     
119        }         
120    
121        /**
122         * Test for FILEUPLOAD-135
123         */
124        public void testFILEUPLOAD135()
125                throws IOException, FileUploadException
126        {
127            byte[] request = newShortRequest();
128            final ByteArrayInputStream bais = new ByteArrayInputStream(request);
129            List fileItems = parseUpload(new InputStream() {
130                public int read()
131                throws IOException
132                {
133                    return bais.read();
134                }
135                public int read(byte b[], int off, int len) throws IOException 
136                {
137                    return bais.read(b, off, Math.min(len, 3));
138                }
139    
140            }, request.length);
141            Iterator fileIter = fileItems.iterator();
142            assertTrue(fileIter.hasNext());
143            FileItem item = (FileItem) fileIter.next();
144            assertEquals("field", item.getFieldName());
145            byte[] bytes = item.get();
146            assertEquals(3, bytes.length);
147            assertEquals((byte)'1', bytes[0]);
148            assertEquals((byte)'2', bytes[1]);
149            assertEquals((byte)'3', bytes[2]);
150            assertTrue(!fileIter.hasNext());
151        }
152    
153        private List parseUpload(byte[] bytes) throws FileUploadException {
154            return parseUpload(new ByteArrayInputStream(bytes), bytes.length);
155        }
156    
157        private FileItemIterator parseUpload(int pLength, InputStream pStream)
158                throws FileUploadException, IOException {
159            String contentType = "multipart/form-data; boundary=---1234";
160    
161            FileUploadBase upload = new ServletFileUpload();
162            upload.setFileItemFactory(new DiskFileItemFactory());
163            HttpServletRequest request = new MockHttpServletRequest(pStream,
164                    pLength, contentType);
165    
166            return upload.getItemIterator(new ServletRequestContext(request));
167        }
168    
169        private List parseUpload(InputStream pStream, int pLength)
170                    throws FileUploadException {
171            String contentType = "multipart/form-data; boundary=---1234";
172    
173            FileUploadBase upload = new ServletFileUpload();
174            upload.setFileItemFactory(new DiskFileItemFactory());
175            HttpServletRequest request = new MockHttpServletRequest(pStream,
176                            pLength, contentType);
177    
178            List fileItems = upload.parseRequest(new ServletRequestContext(request));
179            return fileItems;
180        }
181    
182        private String getHeader(String pField) {
183            return "-----1234\r\n"
184                + "Content-Disposition: form-data; name=\"" + pField + "\"\r\n"
185                + "\r\n";
186    
187        }
188    
189        private String getFooter() {
190            return "-----1234--\r\n";
191        }
192    
193        private byte[] newShortRequest() throws IOException {
194            final ByteArrayOutputStream baos = new ByteArrayOutputStream();
195            final OutputStreamWriter osw = new OutputStreamWriter(baos, "US-ASCII");
196            osw.write(getHeader("field"));
197            osw.write("123");
198            osw.write("\r\n");
199            osw.write(getFooter());
200            osw.close();
201            return baos.toByteArray();
202        }
203    
204        private byte[] newRequest() throws IOException {
205            final ByteArrayOutputStream baos = new ByteArrayOutputStream();
206            final OutputStreamWriter osw = new OutputStreamWriter(baos, "US-ASCII");
207            int add = 16;
208            int num = 0;
209            for (int i = 0;  i < 16384;  i += add) {
210                if (++add == 32) {
211                    add = 16;
212                }
213                osw.write(getHeader("field" + (num++)));
214                osw.flush();
215                for (int j = 0;  j < i;  j++) {
216                    baos.write((byte) j);
217                }
218                osw.write("\r\n");
219            }
220            osw.write(getFooter());
221            osw.close();
222            return baos.toByteArray();
223        }
224    
225        /**
226         * Tests, whether an {@link InvalidFileNameException} is thrown. 
227         */
228        public void testInvalidFileNameException() throws Exception {
229            final String fileName = "foo.exe\u0000.png";
230            final String request =
231                "-----1234\r\n" +
232                "Content-Disposition: form-data; name=\"file\"; filename=\"" + fileName + "\"\r\n" +
233                "Content-Type: text/whatever\r\n" +
234                "\r\n" +
235                "This is the content of the file\n" +
236                "\r\n" +
237                "-----1234\r\n" +
238                "Content-Disposition: form-data; name=\"field\"\r\n" +
239                "\r\n" +
240                "fieldValue\r\n" +
241                "-----1234\r\n" +
242                "Content-Disposition: form-data; name=\"multi\"\r\n" +
243                "\r\n" +
244                "value1\r\n" +
245                "-----1234\r\n" +
246                "Content-Disposition: form-data; name=\"multi\"\r\n" +
247                "\r\n" +
248                "value2\r\n" +
249                "-----1234--\r\n";
250            final byte[] reqBytes = request.getBytes("US-ASCII");
251            
252            FileItemIterator fileItemIter = parseUpload(reqBytes.length, new ByteArrayInputStream(reqBytes));
253            final FileItemStream fileItemStream = fileItemIter.next();
254            try {
255                fileItemStream.getName();
256                fail("Expected exception");
257            } catch (InvalidFileNameException e) {
258                assertEquals(fileName, e.getName());
259                assertTrue(e.getMessage().indexOf(fileName) == -1);
260                assertTrue(e.getMessage().indexOf("foo.exe\\0.png") != -1);
261            }
262    
263            List fileItems = parseUpload(reqBytes);
264            final FileItem fileItem = (FileItem) fileItems.get(0);
265            try {
266                fileItem.getName();
267                fail("Expected exception");
268            } catch (InvalidFileNameException e) {
269                assertEquals(fileName, e.getName());
270                assertTrue(e.getMessage().indexOf(fileName) == -1);
271                assertTrue(e.getMessage().indexOf("foo.exe\\0.png") != -1);
272            }
273        }
274    }