    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 */
    017package org.apache.commons.fileupload2.core;
    018
    019import static org.junit.jupiter.api.Assertions.assertEquals;
    020import static org.junit.jupiter.api.Assertions.assertFalse;
    021import static org.junit.jupiter.api.Assertions.assertNull;
    022import static org.junit.jupiter.api.Assertions.assertTrue;
    023
    024import java.io.IOException;
    025import java.nio.charset.StandardCharsets;
    026
    027import org.junit.jupiter.api.Test;
    028
    029/**
    030 * Common tests for implementations of {@link AbstractFileUpload}. This is a parameterized test. Tests must be valid and common to all implementations of
    031 * FileUpload added as parameter in this class.
    032 *
    033 * @param <AFU> The {@link AbstractFileUpload} type.
    034 * @param <R>   The FileUpload request type.
    035 * @param <I>   The FileItem type.
    036 * @param <F>   The FileItemFactory type.
    037 */
    038public abstract class AbstractFileUploadTest<AFU extends AbstractFileUpload<R, I, F>, R, I extends FileItem<I>, F extends FileItemFactory<I>>
    039        extends AbstractFileUploadWrapper<AFU, R, I, F> {
    040
    041    protected AbstractFileUploadTest(final AFU fileUpload) {
    042        super(fileUpload);
    043    }
    044
    045    private void assertHeaders(final String[] headerNames, final String[] headerValues, final I fileItems, final int index) {
    046        for (var i = 0; i < headerNames.length; i++) {
    047            final var value = fileItems.getHeaders().getHeader(headerNames[i]);
    048            if (i == index) {
    049                assertEquals(headerValues[i], value);
    050            } else {
    051                assertNull(value);
    052            }
    053        }
    054    }
    055
    056    /**
    057     * Tests <a href="https://issues.apache.org/jira/browse/FILEUPLOAD-239">FILEUPLOAD-239</a>
    058     *
    059     * @throws IOException Test failure.
    060     */
    061    @Test
    062    public void testContentTypeAttachment() throws IOException {
    063        // @formatter:off
    064        final var fileItems = parseUpload(upload,
    065                "-----1234\r\n" +
    066                "content-disposition: form-data; name=\"field1\"\r\n" +
    067                "\r\n" +
    068                "Joe Blow\r\n" +
    069                "-----1234\r\n" +
    070                "content-disposition: form-data; name=\"pics\"\r\n" +
    071                "Content-type: multipart/mixed, boundary=---9876\r\n" +
    072                "\r\n" +
    073                "-----9876\r\n" +
    074                "Content-disposition: attachment; filename=\"file1.txt\"\r\n" +
    075                "Content-Type: text/plain\r\n" +
    076                "\r\n" +
    077                "... contents of file1.txt ...\r\n" +
    078                "-----9876--\r\n" +
    079                "-----1234--\r\n");
    080        // @formatter:on
    081        assertEquals(2, fileItems.size());
    082
    083        final var field = fileItems.get(0);
    084        assertEquals("field1", field.getFieldName());
    085        assertTrue(field.isFormField());
    086        assertEquals("Joe Blow", field.getString());
    087
    088        final var fileItem = fileItems.get(1);
    089        assertEquals("pics", fileItem.getFieldName());
    090        assertFalse(fileItem.isFormField());
    091        assertEquals("... contents of file1.txt ...", fileItem.getString());
    092        assertEquals("text/plain", fileItem.getContentType());
    093        assertEquals("file1.txt", fileItem.getName());
    094    }
    095
    096    /**
    097     * This is what the browser does if you submit the form without choosing a file.
    098     *
    099     * @throws FileUploadException Test failure.
    100     */
    101    @Test
    102    public void testEmptyFile() throws FileUploadException {
    103        // @formatter:off
    104        final var fileItems = parseUpload (upload,
    105                                                "-----1234\r\n" +
    106                                                "Content-Disposition: form-data; name=\"file\"; filename=\"\"\r\n" +
    107                                                "\r\n" +
    108                                                "\r\n" +
    109                                                "-----1234--\r\n");
    110        // @formatter:on
    111        assertEquals(1, fileItems.size());
    112
    113        final var file = fileItems.get(0);
    114        assertFalse(file.isFormField());
    115        assertEquals("", file.getString());
    116        assertEquals("", file.getName());
    117    }
    118
    119    @Test
    120    public void testFileNameCaseSensitivity() throws IOException {
    121        // @formatter:off
    122        final var fileItems = parseUpload(upload,
    123                                               "-----1234\r\n" +
    124                                               "Content-Disposition: form-data; "
    125                                             + "name=\"FiLe\"; filename=\"FOO.tab\"\r\n" +
    126                                               "Content-Type: text/whatever\r\n" +
    127                                               "\r\n" +
    128                                               "This is the content of the file\n" +
    129                                               "\r\n" +
    130                                               "-----1234--\r\n");
    131        // @formatter:on
    132        assertEquals(1, fileItems.size());
    133
    134        final var file = fileItems.get(0);
    135        assertEquals("FiLe", file.getFieldName());
    136        assertEquals("FOO.tab", file.getName());
    137    }
    138
    139    @Test
    140    public void testFileUpload() throws IOException {
    141        // @formatter:off
    142        final var fileItems = parseUpload(upload,
    143                                               "-----1234\r\n" +
    144                                               "Content-Disposition: "
    145                                               + "form-data; name=\"file\"; filename=\"foo.tab\"\r\n" +
    146                                               "Content-Type: text/whatever\r\n" +
    147                                               "\r\n" +
    148                                               "This is the content of the file\n" +
    149                                               "\r\n" +
    150                                               "-----1234\r\n" +
    151                                               "Content-Disposition: form-data; name=\"field\"\r\n" +
    152                                               "\r\n" +
    153                                               "fieldValue\r\n" +
    154                                               "-----1234\r\n" +
    155                                               "Content-Disposition: form-data; name=\"multi\"\r\n" +
    156                                               "\r\n" +
    157                                               "value1\r\n" +
    158                                               "-----1234\r\n" +
    159                                               "Content-Disposition: form-data; name=\"multi\"\r\n" +
    160                                               "\r\n" +
    161                                               "value2\r\n" +
    162                                               "-----1234--\r\n");
    163        // @formatter:on
    164        assertEquals(4, fileItems.size());
    165
    166        final var file = fileItems.get(0);
    167        assertEquals("file", file.getFieldName());
    168        assertFalse(file.isFormField());
    169        assertEquals("This is the content of the file\n", file.getString());
    170        assertEquals("text/whatever", file.getContentType());
    171        assertEquals("foo.tab", file.getName());
    172
    173        final var field = fileItems.get(1);
    174        assertEquals("field", field.getFieldName());
    175        assertTrue(field.isFormField());
    176        assertEquals("fieldValue", field.getString());
    177
    178        final var multi0 = fileItems.get(2);
    179        assertEquals("multi", multi0.getFieldName());
    180        assertTrue(multi0.isFormField());
    181        assertEquals("value1", multi0.getString());
    182
    183        final var multi1 = fileItems.get(3);
    184        assertEquals("multi", multi1.getFieldName());
    185        assertTrue(multi1.isFormField());
    186        assertEquals("value2", multi1.getString());
    187    }
    188
    189    /**
    190     * Test case for <a href="https://issues.apache.org/jira/browse/FILEUPLOAD-130">FILEUPLOAD-130</a>.
    191     *
    192     * @throws IOException Test failure.
    193     */
    194    @Test
    195    public void testFileUpload130() throws IOException {
    196        final String[] headerNames = { "SomeHeader", "OtherHeader", "YetAnotherHeader", "WhatAHeader" };
    197        final String[] headerValues = { "present", "Is there", "Here", "Is That" };
    198        // @formatter:off
    199        final var fileItems = parseUpload(upload,
    200                                               "-----1234\r\n" +
    201                                               "Content-Disposition: form-data; name=\"file\"; "
    202                                             + "filename=\"foo.tab\"\r\n" +
    203                                               "Content-Type: text/whatever\r\n" +
    204                                               headerNames[0] + ": " + headerValues[0] + "\r\n" +
    205                                               "\r\n" +
    206                                               "This is the content of the file\n" +
    207                                               "\r\n" +
    208                                               "-----1234\r\n" +
    209                                               "Content-Disposition: form-data; \r\n" +
    210                                               "\tname=\"field\"\r\n" +
    211                                               headerNames[1] + ": " + headerValues[1] + "\r\n" +
    212                                               "\r\n" +
    213                                               "fieldValue\r\n" +
    214                                               "-----1234\r\n" +
    215                                               "Content-Disposition: form-data;\r\n" +
    216                                               "     name=\"multi\"\r\n" +
    217                                               headerNames[2] + ": " + headerValues[2] + "\r\n" +
    218                                               "\r\n" +
    219                                               "value1\r\n" +
    220                                               "-----1234\r\n" +
    221                                               "Content-Disposition: form-data; name=\"multi\"\r\n" +
    222                                               headerNames[3] + ": " + headerValues[3] + "\r\n" +
    223                                               "\r\n" +
    224                                               "value2\r\n" +
    225                                               "-----1234--\r\n");
    226        // @formatter:on
    227        assertEquals(4, fileItems.size());
    228
    229        final var file = fileItems.get(0);
    230        assertHeaders(headerNames, headerValues, file, 0);
    231
    232        final var field = fileItems.get(1);
    233        assertHeaders(headerNames, headerValues, field, 1);
    234
    235        final var multi0 = fileItems.get(2);
    236        assertHeaders(headerNames, headerValues, multi0, 2);
    237
    238        final var multi1 = fileItems.get(3);
    239        assertHeaders(headerNames, headerValues, multi1, 3);
    240    }
    241
    242    /**
    243     * Test for <a href="https://issues.apache.org/jira/browse/FILEUPLOAD-62">FILEUPLOAD-62</a>
    244     *
    245     * @throws IOException Test failure.
    246     */
    247    @Test
    248    public void testFILEUPLOAD62() throws IOException {
    249        // @formatter:off
    250        final var contentType = "multipart/form-data; boundary=AaB03x";
    251        final var request =
    252            "--AaB03x\r\n" +
    253            "content-disposition: form-data; name=\"field1\"\r\n" +
    254            "\r\n" +
    255            "Joe Blow\r\n" +
    256            "--AaB03x\r\n" +
    257            "content-disposition: form-data; name=\"pics\"\r\n" +
    258            "Content-type: multipart/mixed; boundary=BbC04y\r\n" +
    259            "\r\n" +
    260            "--BbC04y\r\n" +
    261            "Content-disposition: attachment; filename=\"file1.txt\"\r\n" +
    262            "Content-Type: text/plain\r\n" +
    263            "\r\n" +
    264            "... contents of file1.txt ...\r\n" +
    265            "--BbC04y\r\n" +
    266            "Content-disposition: attachment; filename=\"file2.gif\"\r\n" +
    267            "Content-type: image/gif\r\n" +
    268            "Content-Transfer-Encoding: binary\r\n" +
    269            "\r\n" +
    270            "...contents of file2.gif...\r\n" +
    271            "--BbC04y--\r\n" +
    272            "--AaB03x--";
    273        // @formatter:on
    274        final var fileItems = parseUpload(upload, request.getBytes(StandardCharsets.US_ASCII), contentType);
    275        assertEquals(3, fileItems.size());
    276        final var item0 = fileItems.get(0);
    277        assertEquals("field1", item0.getFieldName());
    278        assertNull(item0.getName());
    279        assertEquals("Joe Blow", new String(item0.get()));
    280        final var item1 = fileItems.get(1);
    281        assertEquals("pics", item1.getFieldName());
    282        assertEquals("file1.txt", item1.getName());
    283        assertEquals("... contents of file1.txt ...", new String(item1.get()));
    284        final var item2 = fileItems.get(2);
    285        assertEquals("pics", item2.getFieldName());
    286        assertEquals("file2.gif", item2.getName());
    287        assertEquals("...contents of file2.gif...", new String(item2.get()));
    288    }
    289
    290    /**
    291     * Test for <a href="https://issues.apache.org/jira/browse/FILEUPLOAD-111">FILEUPLOAD-111</a>
    292     *
    293     * @throws IOException Test failure.
    294     */
    295    @Test
    296    public void testFoldedHeaders() throws IOException {
    297        // @formatter:off
    298        final var fileItems = parseUpload(upload, "-----1234\r\n" +
    299                "Content-Disposition: form-data; name=\"file\"; filename=\"foo.tab\"\r\n" +
    300                "Content-Type: text/whatever\r\n" +
    301                "\r\n" +
    302                "This is the content of the file\n" +
    303                "\r\n" +
    304                "-----1234\r\n" +
    305                "Content-Disposition: form-data; \r\n" +
    306                "\tname=\"field\"\r\n" +
    307                "\r\n" +
    308                "fieldValue\r\n" +
    309                "-----1234\r\n" +
    310                "Content-Disposition: form-data;\r\n" +
    311                "     name=\"multi\"\r\n" +
    312                "\r\n" +
    313                "value1\r\n" +
    314                "-----1234\r\n" +
    315                "Content-Disposition: form-data; name=\"multi\"\r\n" +
    316                "\r\n" +
    317                "value2\r\n" +
    318                "-----1234--\r\n");
    319        // @formatter:on
    320        assertEquals(4, fileItems.size());
    321
    322        final var file = fileItems.get(0);
    323        assertEquals("file", file.getFieldName());
    324        assertFalse(file.isFormField());
    325        assertEquals("This is the content of the file\n", file.getString());
    326        assertEquals("text/whatever", file.getContentType());
    327        assertEquals("foo.tab", file.getName());
    328
    329        final var field = fileItems.get(1);
    330        assertEquals("field", field.getFieldName());
    331        assertTrue(field.isFormField());
    332        assertEquals("fieldValue", field.getString());
    333
    334        final var multi0 = fileItems.get(2);
    335        assertEquals("multi", multi0.getFieldName());
    336        assertTrue(multi0.isFormField());
    337        assertEquals("value1", multi0.getString());
    338
    339        final var multi1 = fileItems.get(3);
    340        assertEquals("multi", multi1.getFieldName());
    341        assertTrue(multi1.isFormField());
    342        assertEquals("value2", multi1.getString());
    343    }
    344
    345    /**
    346     * Internet Explorer 5 for the Mac has a bug where the carriage return is missing on any boundary line immediately preceding an input with type=image.
    347     * (type=submit does not have the bug.)
    348     *
    349     * @throws FileUploadException Test failure.
    350     */
    351    @Test
    352    public void testIE5MacBug() throws FileUploadException {
    353        final var fileItems = parseUpload(upload,
    354        // @formatter:off
    355                "-----1234\r\n" +
    356                "Content-Disposition: form-data; name=\"field1\"\r\n" +
    357                "\r\n" +
    358                "fieldValue\r\n" +
    359                "-----1234\n" + // NOTE \r missing
    360                "Content-Disposition: form-data; name=\"submitName.x\"\r\n" +
    361                "\r\n" +
    362                "42\r\n" +
    363                "-----1234\n" + // NOTE \r missing
    364                "Content-Disposition: form-data; name=\"submitName.y\"\r\n" +
    365                "\r\n" +
    366                "21\r\n" +
    367                "-----1234\r\n" +
    368                "Content-Disposition: form-data; name=\"field2\"\r\n" +
    369                "\r\n" +
    370                "fieldValue2\r\n" +
    371                "-----1234--\r\n");
    372        //@formatter:on
    373
    374        assertEquals(4, fileItems.size());
    375
    376        final var field1 = fileItems.get(0);
    377        assertEquals("field1", field1.getFieldName());
    378        assertTrue(field1.isFormField());
    379        assertEquals("fieldValue", field1.getString());
    380
    381        final var submitX = fileItems.get(1);
    382        assertEquals("submitName.x", submitX.getFieldName());
    383        assertTrue(submitX.isFormField());
    384        assertEquals("42", submitX.getString());
    385
    386        final var submitY = fileItems.get(2);
    387        assertEquals("submitName.y", submitY.getFieldName());
    388        assertTrue(submitY.isFormField());
    389        assertEquals("21", submitY.getString());
    390
    391        final var field2 = fileItems.get(3);
    392        assertEquals("field2", field2.getFieldName());
    393        assertTrue(field2.isFormField());
    394        assertEquals("fieldValue2", field2.getString());
    395    }
    396}