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.assertNull;
    22  import static org.junit.Assert.assertTrue;
    23  import static org.junit.jupiter.api.Assertions.assertThrows;
    24
    25  import java.io.ByteArrayOutputStream;
    26  import java.io.IOException;
    27  import java.io.InputStream;
    28  import java.io.UnsupportedEncodingException;
    29  import java.nio.charset.StandardCharsets;
    30  import java.util.List;
    31
    32  import org.apache.commons.fileupload.disk.DiskFileItemFactory;
    33  import org.apache.commons.fileupload.portlet.PortletFileUploadTest;
    34  import org.apache.commons.fileupload.servlet.ServletFileUpload;
    35  import org.apache.commons.fileupload.servlet.ServletFileUploadTest;
    36  import org.apache.commons.fileupload.util.Streams;
    37  import org.junit.Test;
    38  import org.junit.runner.RunWith;
    39  import org.junit.runners.Parameterized;
    40  import org.junit.runners.Parameterized.Parameter;
    41  import org.junit.runners.Parameterized.Parameters;
    42
    43  /**
    44   * Common tests for implementations of {@link FileUpload}. This is a parameterized test.
    45   * Tests must be valid and common to all implementations of FileUpload added as parameter
    46   * in this class.
    47   *
    48   * @see ServletFileUploadTest
    49   * @see PortletFileUploadTest
    50   * @since 1.4
    51   */
    52  @RunWith(Parameterized.class)
    53  public class FileUploadTest {
    54
    55      /**
    56       * @return {@link FileUpload} classes under test.
    57       */
    58      @Parameters(name = "{0}")
    59      public static Iterable<? extends Object> data() {
    60          return Util.fileUploadImplementations();
    61      }
    62
    63      /**
    64       * Current parameterized FileUpload.
    65       */
    66      @Parameter
    67      public FileUpload upload;
    68
    69      private void assertHeaders(final String[] headerNames, final String[] headerValues, final FileItem item, final int index) {
    70          for (int i = 0; i < headerNames.length; i++) {
    71              final String value = item.getHeaders().getHeader(headerNames[i]);
    72              if (i == index) {
    73                  assertEquals(headerValues[i], value);
    74              } else {
    75                  assertNull(value);
    76              }
    77          }
    78      }
    79
    80      /**
    81       * Test for <a href="http://issues.apache.org/jira/browse/FILEUPLOAD-239">FILEUPLOAD-239</a>.
    82       */
    83      @Test
    84      public void testContentTypeAttachment()
    85              throws IOException, FileUploadException {
    86          final List<FileItem> fileItems = Util.parseUpload(upload,
    87                  "-----1234\r\n" +
    88                  "content-disposition: form-data; name=\"field1\"\r\n" +
    89                  "\r\n" +
    90                  "Joe Blow\r\n" +
    91                  "-----1234\r\n" +
    92                  "content-disposition: form-data; name=\"pics\"\r\n" +
    93                  "Content-type: multipart/mixed, boundary=---9876\r\n" +
    94                  "\r\n" +
    95                  "-----9876\r\n" +
    96                  "Content-disposition: attachment; filename=\"file1.txt\"\r\n" +
    97                  "Content-Type: text/plain\r\n" +
    98                  "\r\n" +
    99                  "... contents of file1.txt ...\r\n" +
    100                 "-----9876--\r\n" +
    101                 "-----1234--\r\n");
    102         assertEquals(2, fileItems.size());
    103
    104         final FileItem field = fileItems.get(0);
    105         assertEquals("field1", field.getFieldName());
    106         assertTrue(field.isFormField());
    107         assertEquals("Joe Blow", field.getString());
    108
    109         final FileItem file = fileItems.get(1);
    110         assertEquals("pics", file.getFieldName());
    111         assertFalse(file.isFormField());
    112         assertEquals("... contents of file1.txt ...", file.getString());
    113         assertEquals("text/plain", file.getContentType());
    114         assertEquals("file1.txt", file.getName());
    115     }
    116
    117     /**
    118      * This is what the browser does if you submit the form without choosing a file.
    119      */
    120     @Test
    121     public void testEmptyFile()
    122             throws UnsupportedEncodingException, FileUploadException {
    123         final List<FileItem> fileItems = Util.parseUpload (upload,
    124                                                 "-----1234\r\n" +
    125                                                 "Content-Disposition: form-data; name=\"file\"; filename=\"\"\r\n" +
    126                                                 "\r\n" +
    127                                                 "\r\n" +
    128                                                 "-----1234--\r\n");
    129         assertEquals(1, fileItems.size());
    130
    131         final FileItem file = fileItems.get(0);
    132         assertFalse(file.isFormField());
    133         assertEquals("", file.getString());
    134         assertEquals("", file.getName());
    135     }
    136
    137     @Test
    138     public void testFilenameCaseSensitivity()
    139             throws IOException, FileUploadException {
    140         final List<FileItem> fileItems = Util.parseUpload(upload,
    141                                                "-----1234\r\n" +
    142                                                "Content-Disposition: form-data; name=\"FiLe\"; filename=\"FOO.tab\"\r\n" +
    143                                                "Content-Type: text/whatever\r\n" +
    144                                                "\r\n" +
    145                                                "This is the content of the file\n" +
    146                                                "\r\n" +
    147                                                "-----1234--\r\n");
    148         assertEquals(1, fileItems.size());
    149
    150         final FileItem file = fileItems.get(0);
    151         assertEquals("FiLe", file.getFieldName());
    152         assertEquals("FOO.tab", file.getName());
    153     }
    154
    155     @Test
    156     public void testFileUpload()
    157             throws IOException, FileUploadException {
    158         final List<FileItem> fileItems = Util.parseUpload(upload,
    159                                                "-----1234\r\n" +
    160                                                "Content-Disposition: form-data; name=\"file\"; filename=\"foo.tab\"\r\n" +
    161                                                "Content-Type: text/whatever\r\n" +
    162                                                "\r\n" +
    163                                                "This is the content of the file\n" +
    164                                                "\r\n" +
    165                                                "-----1234\r\n" +
    166                                                "Content-Disposition: form-data; name=\"field\"\r\n" +
    167                                                "\r\n" +
    168                                                "fieldValue\r\n" +
    169                                                "-----1234\r\n" +
    170                                                "Content-Disposition: form-data; name=\"multi\"\r\n" +
    171                                                "\r\n" +
    172                                                "value1\r\n" +
    173                                                "-----1234\r\n" +
    174                                                "Content-Disposition: form-data; name=\"multi\"\r\n" +
    175                                                "\r\n" +
    176                                                "value2\r\n" +
    177                                                "-----1234--\r\n");
    178         assertEquals(4, fileItems.size());
    179
    180         final FileItem file = fileItems.get(0);
    181         assertEquals("file", file.getFieldName());
    182         assertFalse(file.isFormField());
    183         assertEquals("This is the content of the file\n", file.getString());
    184         assertEquals("text/whatever", file.getContentType());
    185         assertEquals("foo.tab", file.getName());
    186
    187         final FileItem field = fileItems.get(1);
    188         assertEquals("field", field.getFieldName());
    189         assertTrue(field.isFormField());
    190         assertEquals("fieldValue", field.getString());
    191
    192         final FileItem multi0 = fileItems.get(2);
    193         assertEquals("multi", multi0.getFieldName());
    194         assertTrue(multi0.isFormField());
    195         assertEquals("value1", multi0.getString());
    196
    197         final FileItem multi1 = fileItems.get(3);
    198         assertEquals("multi", multi1.getFieldName());
    199         assertTrue(multi1.isFormField());
    200         assertEquals("value2", multi1.getString());
    201     }
    202
    203     /**
    204      * Tests <a href="http://issues.apache.org/jira/browse/FILEUPLOAD-130">FILEUPLOAD-130</a>.
    205      */
    206     @Test
    207     public void testFileUpload130()
    208             throws Exception {
    209         final String[] headerNames = {
    210             "SomeHeader", "OtherHeader", "YetAnotherHeader", "WhatAHeader"
    211         };
    212         final String[] headerValues = {
    213             "present", "Is there", "Here", "Is That"
    214         };
    215         final List<FileItem> fileItems = Util.parseUpload(upload,
    216                                                "-----1234\r\n" +
    217                                                "Content-Disposition: form-data; name=\"file\"; filename=\"foo.tab\"\r\n" +
    218                                                "Content-Type: text/whatever\r\n" +
    219                                                headerNames[0] + ": " + headerValues[0] + "\r\n" +
    220                                                "\r\n" +
    221                                                "This is the content of the file\n" +
    222                                                "\r\n" +
    223                                                "-----1234\r\n" +
    224                                                "Content-Disposition: form-data; \r\n" +
    225                                                "\tname=\"field\"\r\n" +
    226                                                headerNames[1] + ": " + headerValues[1] + "\r\n" +
    227                                                "\r\n" +
    228                                                "fieldValue\r\n" +
    229                                                "-----1234\r\n" +
    230                                                "Content-Disposition: form-data;\r\n" +
    231                                                "     name=\"multi\"\r\n" +
    232                                                headerNames[2] + ": " + headerValues[2] + "\r\n" +
    233                                                "\r\n" +
    234                                                "value1\r\n" +
    235                                                "-----1234\r\n" +
    236                                                "Content-Disposition: form-data; name=\"multi\"\r\n" +
    237                                                headerNames[3] + ": " + headerValues[3] + "\r\n" +
    238                                                "\r\n" +
    239                                                "value2\r\n" +
    240                                                "-----1234--\r\n");
    241         assertEquals(4, fileItems.size());
    242
    243         final FileItem file = fileItems.get(0);
    244         assertHeaders(headerNames, headerValues, file, 0);
    245
    246         final FileItem field = fileItems.get(1);
    247         assertHeaders(headerNames, headerValues, field, 1);
    248
    249         final FileItem multi0 = fileItems.get(2);
    250         assertHeaders(headerNames, headerValues, multi0, 2);
    251
    252         final FileItem multi1 = fileItems.get(3);
    253         assertHeaders(headerNames, headerValues, multi1, 3);
    254     }
    255
    256     /**
    257      * Tests for <a href="http://issues.apache.org/jira/browse/FILEUPLOAD-62">FILEUPLOAD-62</a>.
    258      */
    259     @Test
    260     public void testFILEUPLOAD62() throws Exception {
    261         final String contentType = "multipart/form-data; boundary=AaB03x";
    262         final String request =
    263             "--AaB03x\r\n" +
    264             "content-disposition: form-data; name=\"field1\"\r\n" +
    265             "\r\n" +
    266             "Joe Blow\r\n" +
    267             "--AaB03x\r\n" +
    268             "content-disposition: form-data; name=\"pics\"\r\n" +
    269             "Content-type: multipart/mixed; boundary=BbC04y\r\n" +
    270             "\r\n" +
    271             "--BbC04y\r\n" +
    272             "Content-disposition: attachment; filename=\"file1.txt\"\r\n" +
    273             "Content-Type: text/plain\r\n" +
    274             "\r\n" +
    275             "... contents of file1.txt ...\r\n" +
    276             "--BbC04y\r\n" +
    277             "Content-disposition: attachment; filename=\"file2.gif\"\r\n" +
    278             "Content-type: image/gif\r\n" +
    279             "Content-Transfer-Encoding: binary\r\n" +
    280             "\r\n" +
    281             "...contents of file2.gif...\r\n" +
    282             "--BbC04y--\r\n" +
    283             "--AaB03x--";
    284         final List<FileItem> fileItems = Util.parseUpload(upload, request.getBytes("US-ASCII"), contentType);
    285         assertEquals(3, fileItems.size());
    286         final FileItem item0 = fileItems.get(0);
    287         assertEquals("field1", item0.getFieldName());
    288         assertNull(item0.getName());
    289         assertEquals("Joe Blow", new String(item0.get()));
    290         final FileItem item1 = fileItems.get(1);
    291         assertEquals("pics", item1.getFieldName());
    292         assertEquals("file1.txt", item1.getName());
    293         assertEquals("... contents of file1.txt ...", new String(item1.get()));
    294         final FileItem item2 = fileItems.get(2);
    295         assertEquals("pics", item2.getFieldName());
    296         assertEquals("file2.gif", item2.getName());
    297         assertEquals("...contents of file2.gif...", new String(item2.get()));
    298     }
    299
    300     /**
    301      * Test for <a href="http://issues.apache.org/jira/browse/FILEUPLOAD-111">FILEUPLOAD-111</a>.
    302      */
    303     @Test
    304     public void testFoldedHeaders()
    305             throws IOException, FileUploadException {
    306         final List<FileItem> fileItems = Util.parseUpload(upload, "-----1234\r\n" +
    307                 "Content-Disposition: form-data; name=\"file\"; filename=\"foo.tab\"\r\n" +
    308                 "Content-Type: text/whatever\r\n" +
    309                 "\r\n" +
    310                 "This is the content of the file\n" +
    311                 "\r\n" +
    312                 "-----1234\r\n" +
    313                 "Content-Disposition: form-data; \r\n" +
    314                 "\tname=\"field\"\r\n" +
    315                 "\r\n" +
    316                 "fieldValue\r\n" +
    317                 "-----1234\r\n" +
    318                 "Content-Disposition: form-data;\r\n" +
    319                 "     name=\"multi\"\r\n" +
    320                 "\r\n" +
    321                 "value1\r\n" +
    322                 "-----1234\r\n" +
    323                 "Content-Disposition: form-data; name=\"multi\"\r\n" +
    324                 "\r\n" +
    325                 "value2\r\n" +
    326                 "-----1234--\r\n");
    327         assertEquals(4, fileItems.size());
    328
    329         final FileItem file = fileItems.get(0);
    330         assertEquals("file", file.getFieldName());
    331         assertFalse(file.isFormField());
    332         assertEquals("This is the content of the file\n", file.getString());
    333         assertEquals("text/whatever", file.getContentType());
    334         assertEquals("foo.tab", file.getName());
    335
    336         final FileItem field = fileItems.get(1);
    337         assertEquals("field", field.getFieldName());
    338         assertTrue(field.isFormField());
    339         assertEquals("fieldValue", field.getString());
    340
    341         final FileItem multi0 = fileItems.get(2);
    342         assertEquals("multi", multi0.getFieldName());
    343         assertTrue(multi0.isFormField());
    344         assertEquals("value1", multi0.getString());
    345
    346         final FileItem multi1 = fileItems.get(3);
    347         assertEquals("multi", multi1.getFieldName());
    348         assertTrue(multi1.isFormField());
    349         assertEquals("value2", multi1.getString());
    350     }
    351
    352     /**
    353      * Internet Explorer 5 for the Mac has a bug where the carriage
    354      * return is missing on any boundary line immediately preceding
    355      * an input with type=image. (type=submit does not have the bug.)
    356      */
    357     @Test
    358     public void testIE5MacBug()
    359             throws UnsupportedEncodingException, FileUploadException {
    360         final List<FileItem> fileItems = Util.parseUpload(upload,
    361                                                "-----1234\r\n" +
    362                                                "Content-Disposition: form-data; name=\"field1\"\r\n" +
    363                                                "\r\n" +
    364                                                "fieldValue\r\n" +
    365                                                "-----1234\n" + // NOTE \r missing
    366                                                "Content-Disposition: form-data; name=\"submitName.x\"\r\n" +
    367                                                "\r\n" +
    368                                                "42\r\n" +
    369                                                "-----1234\n" + // NOTE \r missing
    370                                                "Content-Disposition: form-data; name=\"submitName.y\"\r\n" +
    371                                                "\r\n" +
    372                                                "21\r\n" +
    373                                                "-----1234\r\n" +
    374                                                "Content-Disposition: form-data; name=\"field2\"\r\n" +
    375                                                "\r\n" +
    376                                                "fieldValue2\r\n" +
    377                                                "-----1234--\r\n");
    378
    379         assertEquals(4, fileItems.size());
    380
    381         final FileItem field1 = fileItems.get(0);
    382         assertEquals("field1", field1.getFieldName());
    383         assertTrue(field1.isFormField());
    384         assertEquals("fieldValue", field1.getString());
    385
    386         final FileItem submitX = fileItems.get(1);
    387         assertEquals("submitName.x", submitX.getFieldName());
    388         assertTrue(submitX.isFormField());
    389         assertEquals("42", submitX.getString());
    390
    391         final FileItem submitY = fileItems.get(2);
    392         assertEquals("submitName.y", submitY.getFieldName());
    393         assertTrue(submitY.isFormField());
    394         assertEquals("21", submitY.getString());
    395
    396         final FileItem field2 = fileItems.get(3);
    397         assertEquals("field2", field2.getFieldName());
    398         assertTrue(field2.isFormField());
    399         assertEquals("fieldValue2", field2.getString());
    400     }
    401
    402     /**
    403      * Test for multipart/related without any content-disposition Header.
    404      * This kind of Content-Type is commonly used by SOAP-Requests with Attachments (MTOM)
    405      */
    406     @Test
    407     public void testMultipartRelated() throws FileUploadException {
    408         final String soapEnvelope =
    409                 "<soap:Envelope xmlns:soap=\"http://www.w3.org/2003/05/soap-envelope\">\r\n" +
    410                 "  <soap:Header></soap:Header>\r\n" +
    411                 "  <soap:Body>\r\n" +
    412                 "    <ns1:Test xmlns:ns1=\"http://www.test.org/some-test-namespace\">\r\n" +
    413                 "      <ns1:Attachment>\r\n" +
    414                 "        <xop:Include xmlns:xop=\"http://www.w3.org/2004/08/xop/include\"" +
    415                 " href=\"ref-to-attachment%40some.domain.org\"/>\r\n" +
    416                 "      </ns1:Attachment>\r\n" +
    417                 "    </ns1:Test>\r\n" +
    418                 "  </soap:Body>\r\n" +
    419                 "</soap:Envelope>";
    420
    421         final String content = "-----1234\r\n" +
    422                 "content-type: application/xop+xml; type=\"application/soap+xml\"\r\n" +
    423                 "\r\n" +
    424                 soapEnvelope + "\r\n" +
    425                 "-----1234\r\n" +
    426                 "Content-type: text/plain\r\n" +
    427                 "content-id: <ref-to-attachment@some.domain.org>\r\n" +
    428                 "\r\n" +
    429                 "some text/plain content\r\n" +
    430                 "-----1234--\r\n";
    431
    432         final List<FileItem> fileItems = Util.parseUpload(upload, content.getBytes(StandardCharsets.US_ASCII),
    433                 "multipart/related; boundary=---1234;" +
    434                     " type=\"application/xop+xml\"; start-info=\"application/soap+xml\"");
    435         assertEquals(2, fileItems.size());
    436
    437         final FileItem part1 = fileItems.get(0);
    438         assertNull(part1.getFieldName());
    439         assertFalse(part1.isFormField());
    440         assertEquals(soapEnvelope, part1.getString());
    441
    442         final FileItem part2 = fileItems.get(1);
    443         assertNull(part2.getFieldName());
    444         assertFalse(part2.isFormField());
    445         assertEquals("some text/plain content", part2.getString());
    446         assertEquals("text/plain", part2.getContentType());
    447         assertNull(part2.getName());
    448     }
    449
    450
    451     @Test
    452     public void testOpenStreamSecondCall() throws IOException, FileUploadException {
    453         final String request =
    454             "-----1234\r\n" +
    455             "Content-Disposition: form-data; name=\"file1\"; filename=\"foo1.tab\"\r\n" +
    456             "Content-Type: text/whatever\r\n" +
    457             "Content-Length: 10\r\n" +
    458             "\r\n" +
    459             "This is the content of the file\n" +
    460             "\r\n" +
    461             "-----1234--\r\n";
    462
    463         final ServletFileUpload upload = new ServletFileUpload(new DiskFileItemFactory());
    464         upload.setFileSizeMax(-1);
    465         upload.setSizeMax(300);
    466
    467         // the first item should be within the max size limit
    468         // set the read limit to 10 to simulate a "real" stream
    469         // otherwise the buffer would be immediately filled
    470
    471         final MockHttpServletRequest req = new MockHttpServletRequest(
    472                 request.getBytes("US-ASCII"), Constants.CONTENT_TYPE);
    473         req.setContentLength(-1);
    474         req.setReadLimit(10);
    475
    476         final FileItemIterator it = upload.getItemIterator(req);
    477         assertTrue(it.hasNext());
    478
    479         final FileItemStream item = it.next();
    480         assertFalse(item.isFormField());
    481         assertEquals("file1", item.getFieldName());
    482         assertEquals("foo1.tab", item.getName());
    483
    484         try (InputStream stream = item.openStream()) {
    485             final ByteArrayOutputStream baos = new ByteArrayOutputStream();
    486             Streams.copy(stream, baos, true);
    487         }
    488
    489         assertThrows(IOException.class, item::openStream, "Attempt to open a closed stream did not throw an exception");
    490         // Should only be one item
    491         assertFalse(it.hasNext());
    492     }
    493 }