View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.fileupload;
18  
19  import static org.junit.Assert.assertEquals;
20  import static org.junit.Assert.assertFalse;
21  import static org.junit.Assert.assertTrue;
22  import static org.junit.Assert.fail;
23  import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
24  import static org.junit.jupiter.api.Assertions.assertThrows;
25  
26  import java.io.ByteArrayOutputStream;
27  import java.io.IOException;
28  import java.io.InputStream;
29  import java.util.Iterator;
30  import java.util.List;
31  
32  import javax.servlet.http.HttpServletRequest;
33  
34  import org.apache.commons.fileupload.FileUploadBase.FileUploadIOException;
35  import org.apache.commons.fileupload.MultipartStream.MalformedStreamException;
36  import org.apache.commons.fileupload.disk.DiskFileItemFactory;
37  import org.apache.commons.fileupload.servlet.ServletFileUpload;
38  import org.apache.commons.fileupload.util.Streams;
39  import org.junit.Test;
40  
41  /**
42   * Unit test for items with varying sizes.
43   */
44  public class SizesTest {
45  
46      /** Checks, whether limiting the file size works.
47       */
48      @Test
49      public void testFileSizeLimit()
50              throws IOException, FileUploadException {
51          final String request =
52              "-----1234\r\n" +
53              "Content-Disposition: form-data; name=\"file\"; filename=\"foo.tab\"\r\n" +
54              "Content-Type: text/whatever\r\n" +
55              "\r\n" +
56              "This is the content of the file\n" +
57              "\r\n" +
58              "-----1234--\r\n";
59  
60          ServletFileUpload upload = new ServletFileUpload(new DiskFileItemFactory());
61          upload.setFileSizeMax(-1);
62          HttpServletRequest req = new MockHttpServletRequest(
63                  request.getBytes("US-ASCII"), Constants.CONTENT_TYPE);
64          List<FileItem> fileItems = upload.parseRequest(req);
65          assertEquals(1, fileItems.size());
66          FileItem item = fileItems.get(0);
67          assertEquals("This is the content of the file\n", new String(item.get()));
68  
69          upload = new ServletFileUpload(new DiskFileItemFactory());
70          upload.setFileSizeMax(40);
71          req = new MockHttpServletRequest(request.getBytes("US-ASCII"), Constants.CONTENT_TYPE);
72          fileItems = upload.parseRequest(req);
73          assertEquals(1, fileItems.size());
74          item = fileItems.get(0);
75          assertEquals("This is the content of the file\n", new String(item.get()));
76  
77          upload = new ServletFileUpload(new DiskFileItemFactory());
78          upload.setFileSizeMax(30);
79          req = new MockHttpServletRequest(request.getBytes("US-ASCII"), Constants.CONTENT_TYPE);
80          try {
81              upload.parseRequest(req);
82              fail("Expected exception.");
83          } catch (final FileUploadBase.FileSizeLimitExceededException e) {
84              assertEquals(30, e.getPermittedSize());
85          }
86      }
87  
88      /** Checks, whether a faked Content-Length header is detected.
89       */
90      @Test
91      public void testFileSizeLimitWithFakedContentLength()
92              throws IOException, FileUploadException {
93          final String request =
94              "-----1234\r\n" +
95              "Content-Disposition: form-data; name=\"file\"; filename=\"foo.tab\"\r\n" +
96              "Content-Type: text/whatever\r\n" +
97              "Content-Length: 10\r\n" +
98              "\r\n" +
99              "This is the content of the file\n" +
100             "\r\n" +
101             "-----1234--\r\n";
102 
103         ServletFileUpload upload = new ServletFileUpload(new DiskFileItemFactory());
104         upload.setFileSizeMax(-1);
105         HttpServletRequest req = new MockHttpServletRequest(
106                 request.getBytes("US-ASCII"), Constants.CONTENT_TYPE);
107         List<FileItem> fileItems = upload.parseRequest(req);
108         assertEquals(1, fileItems.size());
109         FileItem item = fileItems.get(0);
110         assertEquals("This is the content of the file\n", new String(item.get()));
111 
112         upload = new ServletFileUpload(new DiskFileItemFactory());
113         upload.setFileSizeMax(40);
114         req = new MockHttpServletRequest(request.getBytes("US-ASCII"), Constants.CONTENT_TYPE);
115         fileItems = upload.parseRequest(req);
116         assertEquals(1, fileItems.size());
117         item = fileItems.get(0);
118         assertEquals("This is the content of the file\n", new String(item.get()));
119 
120         // provided Content-Length is larger than the FileSizeMax -> handled by ctor
121         upload = new ServletFileUpload(new DiskFileItemFactory());
122         upload.setFileSizeMax(5);
123         req = new MockHttpServletRequest(request.getBytes("US-ASCII"), Constants.CONTENT_TYPE);
124         try {
125             upload.parseRequest(req);
126             fail("Expected exception.");
127         } catch (final FileUploadBase.FileSizeLimitExceededException e) {
128             assertEquals(5, e.getPermittedSize());
129         }
130 
131         // provided Content-Length is wrong, actual content is larger -> handled by LimitedInputStream
132         upload = new ServletFileUpload(new DiskFileItemFactory());
133         upload.setFileSizeMax(15);
134         req = new MockHttpServletRequest(request.getBytes("US-ASCII"), Constants.CONTENT_TYPE);
135         try {
136             upload.parseRequest(req);
137             fail("Expected exception.");
138         } catch (final FileUploadBase.FileSizeLimitExceededException e) {
139             assertEquals(15, e.getPermittedSize());
140         }
141     }
142 
143     /**
144      * Runs a test with varying file sizes.
145      */
146     @Test
147     public void testFileUpload()
148             throws IOException, FileUploadException {
149         final ByteArrayOutputStream baos = new ByteArrayOutputStream();
150         int add = 16;
151         int num = 0;
152         for (int i = 0;  i < 16384;  i += add) {
153             if (++add == 32) {
154                 add = 16;
155             }
156             final String header = "-----1234\r\n"
157                 + "Content-Disposition: form-data; name=\"field" + num++ + "\"\r\n"
158                 + "\r\n";
159             baos.write(header.getBytes("US-ASCII"));
160             for (int j = 0;  j < i;  j++) {
161                 baos.write((byte) j);
162             }
163             baos.write("\r\n".getBytes("US-ASCII"));
164         }
165         baos.write("-----1234--\r\n".getBytes("US-ASCII"));
166 
167         final List<FileItem> fileItems =
168                 Util.parseUpload(new ServletFileUpload(new DiskFileItemFactory()), baos.toByteArray());
169         final Iterator<FileItem> fileIter = fileItems.iterator();
170         add = 16;
171         num = 0;
172         for (int i = 0;  i < 16384;  i += add) {
173             if (++add == 32) {
174                 add = 16;
175             }
176             final FileItem item = fileIter.next();
177             assertEquals("field" + num++, item.getFieldName());
178             final byte[] bytes = item.get();
179             assertEquals(i, bytes.length);
180             for (int j = 0;  j < i;  j++) {
181                 assertEquals((byte) j, bytes[j]);
182             }
183         }
184         assertTrue(!fileIter.hasNext());
185     }
186 
187     /** Checks, whether the maxSize works.
188      */
189     @Test
190     public void testMaxSizeLimit()
191             throws IOException, FileUploadException {
192         final String request =
193             "-----1234\r\n" +
194             "Content-Disposition: form-data; name=\"file1\"; filename=\"foo1.tab\"\r\n" +
195             "Content-Type: text/whatever\r\n" +
196             "Content-Length: 10\r\n" +
197             "\r\n" +
198             "This is the content of the file\n" +
199             "\r\n" +
200             "-----1234\r\n" +
201             "Content-Disposition: form-data; name=\"file2\"; filename=\"foo2.tab\"\r\n" +
202             "Content-Type: text/whatever\r\n" +
203             "\r\n" +
204             "This is the content of the file\n" +
205             "\r\n" +
206             "-----1234--\r\n";
207 
208         final ServletFileUpload upload = new ServletFileUpload(new DiskFileItemFactory());
209         upload.setFileSizeMax(-1);
210         upload.setSizeMax(200);
211 
212         final MockHttpServletRequest req = new MockHttpServletRequest(request.getBytes("US-ASCII"), Constants.CONTENT_TYPE);
213         assertEquals(200, assertThrows(FileUploadBase.SizeLimitExceededException.class, () -> upload.parseRequest(req)).getPermittedSize());
214     }
215 
216     @Test
217     public void testMaxSizeLimitUnknownContentLength()
218             throws IOException, FileUploadException {
219         final String request =
220             "-----1234\r\n" +
221             "Content-Disposition: form-data; name=\"file1\"; filename=\"foo1.tab\"\r\n" +
222             "Content-Type: text/whatever\r\n" +
223             "Content-Length: 10\r\n" +
224             "\r\n" +
225             "This is the content of the file\n" +
226             "\r\n" +
227             "-----1234\r\n" +
228             "Content-Disposition: form-data; name=\"file2\"; filename=\"foo2.tab\"\r\n" +
229             "Content-Type: text/whatever\r\n" +
230             "\r\n" +
231             "This is the content of the file\n" +
232             "\r\n" +
233             "-----1234--\r\n";
234 
235         final ServletFileUpload upload = new ServletFileUpload(new DiskFileItemFactory());
236         upload.setFileSizeMax(-1);
237         upload.setSizeMax(300);
238 
239         // the first item should be within the max size limit
240         // set the read limit to 10 to simulate a "real" stream
241         // otherwise the buffer would be immediately filled
242 
243         final MockHttpServletRequest req = new MockHttpServletRequest(
244                 request.getBytes("US-ASCII"), Constants.CONTENT_TYPE);
245         req.setContentLength(-1);
246         req.setReadLimit(10);
247 
248         final FileItemIterator it = upload.getItemIterator(req);
249         assertTrue(it.hasNext());
250 
251         FileItemStream item = it.next();
252         assertFalse(item.isFormField());
253         assertEquals("file1", item.getFieldName());
254         assertEquals("foo1.tab", item.getName());
255 
256         try (InputStream stream = item.openStream()) {
257             final ByteArrayOutputStream baos = new ByteArrayOutputStream();
258             Streams.copy(stream, baos, true);
259         }
260 
261         // the second item is over the size max, thus we expect an error
262         // the header is still within size max -> this shall still succeed
263         assertDoesNotThrow(it::hasNext);
264 
265         item = it.next();
266 
267         try (InputStream stream = item.openStream()) {
268             final ByteArrayOutputStream baos = new ByteArrayOutputStream();
269             assertThrows(FileUploadIOException.class, () -> Streams.copy(stream, baos, true));
270             assertThrows(MalformedStreamException.class, stream::close);
271         } catch (final MalformedStreamException e) {
272             // expected as close() throws again.
273         }
274     }
275 
276     /** Checks, whether the maxSize works.
277      */
278     @Test
279     public void testPartHeaderSizeMaxLimit()
280             throws IOException, FileUploadException {
281         final String request =
282             "-----1234\r\n" +
283             "Content-Disposition: form-data; name=\"file1\"; filename=\"foo1.tab\"\r\n" +
284             "Content-Type: text/whatever\r\n" +
285             "Content-Length: 10\r\n" +
286             "\r\n" +
287             "This is the content of the file\n" +
288             "\r\n" +
289             "-----1234\r\n" +
290             "Content-Disposition: form-data; name=\"file2\"; filename=\"foo2.tab\"\r\n" +
291             "Content-Type: text/whatever\r\n" +
292             "\r\n" +
293             "This is the content of the file\n" +
294             "\r\n" +
295             "-----1234--\r\n";
296 
297         final ServletFileUpload upload = new ServletFileUpload(new DiskFileItemFactory());
298         upload.setFileSizeMax(-1);
299         upload.setSizeMax(-1);
300         upload.setPartHeaderSizeMax(100);
301 
302         final MockHttpServletRequest req = new MockHttpServletRequest(request.getBytes("US-ASCII"), Constants.CONTENT_TYPE);
303         assertEquals(100, assertThrows(FileUploadBase.SizeLimitExceededException.class, () -> upload.parseRequest(req)).getPermittedSize());
304     }
305 }