    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.assertThrows;
    022import static org.junit.jupiter.api.Assertions.assertTrue;
    023import static org.junit.jupiter.api.Assertions.fail;
    024
    025import java.io.ByteArrayOutputStream;
    026import java.io.IOException;
    027
    028import org.apache.commons.io.IOUtils;
    029import org.junit.jupiter.api.Test;
    030
    031/**
    032 * Unit test for items with varying sizes.
    033 *
    034 * @param <AFU> The FileUpload type.
    035 * @param <R>   The FileUpload request type.
    036 * @param <I>   The FileItem type.
    037 * @param <F>   The FileItemFactory type.
    038 */
    039public abstract class AbstractSizesTest<AFU extends AbstractFileUpload<R, I, F>, R, I extends FileItem<I>, F extends FileItemFactory<I>>
    040        extends AbstractTest<AFU, R, I, F> {
    041
    042    /**
    043     * Checks, whether limiting the file size works.
    044     *
    045     * @throws IOException Test failure.
    046     */
    047    @Test
    048    public void testFileSizeLimit() throws IOException {
    049        final var content = "This is the content of the file\n";
    050        final var contentSize = content.getBytes().length;
    051
    052        // @formatter:off
    053        final var request =
    054            "-----1234\r\n" +
    055            "Content-Disposition: form-data; name=\"file\"; filename=\"foo.tab\"\r\n" +
    056            "Content-Type: text/whatever\r\n" +
    057            "\r\n" +
    058            content +
    059            "\r\n" +
    060            "-----1234--\r\n";
    061        // @formatter:on
    062
    063        var upload = newFileUpload();
    064        upload.setFileSizeMax(-1);
    065        var req = newMockHttpServletRequest(request, null, null);
    066        var fileItems = upload.parseRequest(req);
    067        assertEquals(1, fileItems.size());
    068        var item = fileItems.get(0);
    069        assertEquals(content, new String(item.get()));
    070
    071        upload = newFileUpload();
    072        upload.setFileSizeMax(40);
    073        req = newMockHttpServletRequest(request, null, null);
    074        fileItems = upload.parseRequest(req);
    075        assertEquals(1, fileItems.size());
    076        item = fileItems.get(0);
    077        assertEquals(content, new String(item.get()));
    078
    079        upload = newFileUpload();
    080        upload.setFileSizeMax(contentSize);
    081        req = newMockHttpServletRequest(request, null, null);
    082        fileItems = upload.parseRequest(req);
    083        assertEquals(1, fileItems.size());
    084        item = fileItems.get(0);
    085        assertEquals(content, new String(item.get()));
    086
    087        upload = newFileUpload();
    088        upload.setFileSizeMax(contentSize - 1);
    089        req = newMockHttpServletRequest(request, null, null);
    090        try {
    091            upload.parseRequest(req);
    092            fail("Expected exception.");
    093        } catch (final FileUploadByteCountLimitException e) {
    094            assertEquals(contentSize - 1, e.getPermitted());
    095        }
    096
    097        upload = newFileUpload();
    098        upload.setFileSizeMax(30);
    099        req = newMockHttpServletRequest(request, null, null);
    100        try {
    101            upload.parseRequest(req);
    102            fail("Expected exception.");
    103        } catch (final FileUploadByteCountLimitException e) {
    104            assertEquals(30, e.getPermitted());
    105        }
    106    }
    107
    108    /**
    109     * Checks, whether a faked Content-Length header is detected.
    110     *
    111     * @throws IOException Test failure.
    112     */
    113    @Test
    114    public void testFileSizeLimitWithFakedContentLength() throws IOException {
    115        // @formatter:off
    116        final var request =
    117            "-----1234\r\n" +
    118            "Content-Disposition: form-data; name=\"file\"; filename=\"foo.tab\"\r\n" +
    119            "Content-Type: text/whatever\r\n" +
    120            "Content-Length: 10\r\n" +
    121            "\r\n" +
    122            "This is the content of the file\n" +
    123            "\r\n" +
    124            "-----1234--\r\n";
    125        // @formatter:on
    126
    127        var upload = newFileUpload();
    128        upload.setFileSizeMax(-1);
    129        var req = newMockHttpServletRequest(request, null, null);
    130        var fileItems = upload.parseRequest(req);
    131        assertEquals(1, fileItems.size());
    132        var item = fileItems.get(0);
    133        assertEquals("This is the content of the file\n", new String(item.get()));
    134
    135        upload = newFileUpload();
    136        upload.setFileSizeMax(40);
    137        req = newMockHttpServletRequest(request, null, null);
    138        fileItems = upload.parseRequest(req);
    139        assertEquals(1, fileItems.size());
    140        item = fileItems.get(0);
    141        assertEquals("This is the content of the file\n", new String(item.get()));
    142
    143        // provided Content-Length is larger than the FileSizeMax -> handled by ctor
    144        upload = newFileUpload();
    145        upload.setFileSizeMax(5);
    146        req = newMockHttpServletRequest(request, null, null);
    147        try {
    148            upload.parseRequest(req);
    149            fail("Expected exception.");
    150        } catch (final FileUploadByteCountLimitException e) {
    151            assertEquals(5, e.getPermitted());
    152        }
    153
    154        // provided Content-Length is wrong, actual content is larger -> handled by LimitedInputStream
    155        upload = newFileUpload();
    156        upload.setFileSizeMax(15);
    157        req = newMockHttpServletRequest(request, null, null);
    158        try {
    159            upload.parseRequest(req);
    160            fail("Expected exception.");
    161        } catch (final FileUploadByteCountLimitException e) {
    162            assertEquals(15, e.getPermitted());
    163        }
    164    }
    165
    166    /**
    167     * Checks whether maxSize works.
    168     *
    169     * @throws IOException Test failure.
    170     */
    171    @Test
    172    public void testMaxSizeLimit() throws IOException {
    173        // @formatter:off
    174        final var request =
    175            "-----1234\r\n" +
    176            "Content-Disposition: form-data; name=\"file1\"; filename=\"foo1.tab\"\r\n" +
    177            "Content-Type: text/whatever\r\n" +
    178            "Content-Length: 10\r\n" +
    179            "\r\n" +
    180            "This is the content of the file\n" +
    181            "\r\n" +
    182            "-----1234\r\n" +
    183            "Content-Disposition: form-data; name=\"file2\"; filename=\"foo2.tab\"\r\n" +
    184            "Content-Type: text/whatever\r\n" +
    185            "\r\n" +
    186            "This is the content of the file\n" +
    187            "\r\n" +
    188            "-----1234--\r\n";
    189        // @formatter:on
    190
    191        final var upload = newFileUpload();
    192        upload.setFileSizeMax(-1);
    193        upload.setSizeMax(200);
    194
    195        final var req = newMockHttpServletRequest(request, null, null);
    196        try {
    197            upload.parseRequest(req);
    198            fail("Expected exception.");
    199        } catch (final FileUploadSizeException e) {
    200            assertEquals(200, e.getPermitted());
    201        }
    202    }
    203
    204    @Test
    205    public void testMaxSizeLimitUnknownContentLength() throws IOException {
    206        // @formatter:off
    207        final var request =
    208            "-----1234\r\n" +
    209            "Content-Disposition: form-data; name=\"file1\"; filename=\"foo1.tab\"\r\n" +
    210            "Content-Type: text/whatever\r\n" +
    211            "Content-Length: 10\r\n" +
    212            "\r\n" +
    213            "This is the content of the file\n" +
    214            "\r\n" +
    215            "-----1234\r\n" +
    216            "Content-Disposition: form-data; name=\"file2\"; filename=\"foo2.tab\"\r\n" +
    217            "Content-Type: text/whatever\r\n" +
    218            "\r\n" +
    219            "This is the content of the file\n" +
    220            "\r\n" +
    221            "-----1234--\r\n";
    222        // @formatter:on
    223
    224        final var upload = newFileUpload();
    225        upload.setFileSizeMax(-1);
    226        upload.setSizeMax(300);
    227
    228        // the first item should be within the max size limit
    229        // set the read limit to 10 to simulate a "real" stream
    230        // otherwise the buffer would be immediately filled
    231
    232        final var req = newMockHttpServletRequest(request, -1L, 10);
    233
    234        final var it = upload.getItemIterator(req);
    235        assertTrue(it.hasNext());
    236
    237        final var item = it.next();
    238        assertFalse(item.isFormField());
    239        assertEquals("file1", item.getFieldName());
    240        assertEquals("foo1.tab", item.getName());
    241
    242        {
    243            try (final var baos = new ByteArrayOutputStream();
    244                    final var stream = item.getInputStream()) {
    245                IOUtils.copy(stream, baos);
    246            }
    247
    248        }
    249
    250        // the second item is over the size max, thus we expect an error
    251        // the header is still within size max -> this shall still succeed
    252        assertTrue(it.hasNext());
    253
    254        assertThrows(FileUploadException.class, () -> {
    255            final var item2 = it.next();
    256            try (final var baos = new ByteArrayOutputStream();
    257                    final var stream = item2.getInputStream()) {
    258                IOUtils.copy(stream, baos);
    259            }
    260        });
    261    }
    262}