1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.fileupload;
18
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertTrue;
21 import static org.junit.Assert.fail;
22 import static org.junit.jupiter.api.Assertions.assertInstanceOf;
23 import static org.junit.jupiter.api.Assertions.assertThrows;
24
25 import java.io.ByteArrayInputStream;
26 import java.io.ByteArrayOutputStream;
27 import java.io.FilterInputStream;
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.io.OutputStreamWriter;
31 import java.util.Iterator;
32 import java.util.List;
33
34 import javax.servlet.http.HttpServletRequest;
35
36 import org.apache.commons.fileupload.FileUploadBase.IOFileUploadException;
37 import org.apache.commons.fileupload.disk.DiskFileItemFactory;
38 import org.apache.commons.fileupload.servlet.ServletFileUpload;
39 import org.apache.commons.fileupload.servlet.ServletRequestContext;
40 import org.junit.Test;
41
42
43
44
45 public class StreamingTest {
46
47 private String getFooter() {
48 return "-----1234--\r\n";
49 }
50
51 private String getHeader(final String fieldName) {
52 return "-----1234\r\n"
53 + "Content-Disposition: form-data; name=\"" + fieldName + "\"\r\n"
54 + "\r\n";
55
56 }
57
58 private InputStream newInputStream(final ByteArrayInputStream bais) {
59 return new InputStream() {
60
61 @Override
62 public int read() throws IOException {
63 return bais.read();
64 }
65
66 @Override
67 public int read(final byte[] b, final int off, final int len) throws IOException {
68 return bais.read(b, off, Math.min(len, 3));
69 }
70
71 };
72 }
73
74 private byte[] newRequest() throws IOException {
75 final ByteArrayOutputStream baos = new ByteArrayOutputStream();
76 try (OutputStreamWriter osw = new OutputStreamWriter(baos, "US-ASCII")) {
77 int add = 16;
78 int num = 0;
79 for (int i = 0; i < 16384; i += add) {
80 if (++add == 32) {
81 add = 16;
82 }
83 osw.write(getHeader("field" + num++));
84 osw.flush();
85 for (int j = 0; j < i; j++) {
86 baos.write((byte) j);
87 }
88 osw.write("\r\n");
89 }
90 osw.write(getFooter());
91 }
92 return baos.toByteArray();
93 }
94
95 private byte[] newShortRequest() throws IOException {
96 final ByteArrayOutputStream baos = new ByteArrayOutputStream();
97 try (OutputStreamWriter osw = new OutputStreamWriter(baos, "US-ASCII")) {
98 osw.write(getHeader("field"));
99 osw.write("123");
100 osw.write("\r\n");
101 osw.write(getFooter());
102 }
103 return baos.toByteArray();
104 }
105
106 private List<FileItem> parseUpload(final byte[] bytes) throws FileUploadException {
107 return parseUpload(new ByteArrayInputStream(bytes), bytes.length);
108 }
109
110 private List<FileItem> parseUpload(final InputStream in, final int length) throws FileUploadException {
111 final String contentType = "multipart/form-data; boundary=---1234";
112 final FileUploadBase upload = new ServletFileUpload();
113 upload.setFileItemFactory(new DiskFileItemFactory());
114 final HttpServletRequest request = new MockHttpServletRequest(in, length, contentType);
115 return upload.parseRequest(new ServletRequestContext(request));
116 }
117
118 private FileItemIterator parseUpload(final int length, final InputStream in) throws FileUploadException, IOException {
119 final String contentType = "multipart/form-data; boundary=---1234";
120 final FileUploadBase upload = new ServletFileUpload();
121 upload.setFileItemFactory(new DiskFileItemFactory());
122 final HttpServletRequest request = new MockHttpServletRequest(in, length, contentType);
123 return upload.getItemIterator(new ServletRequestContext(request));
124 }
125
126
127
128
129 @Test
130 public void testFileUpload() throws IOException, FileUploadException {
131 final byte[] request = newRequest();
132 final List<FileItem> fileItems = parseUpload(request);
133 final Iterator<FileItem> fileIter = fileItems.iterator();
134 int add = 16;
135 int num = 0;
136 for (int i = 0; i < 16384; i += add) {
137 if (++add == 32) {
138 add = 16;
139 }
140 final FileItem item = fileIter.next();
141 assertEquals("field" + num++, item.getFieldName());
142 final byte[] bytes = item.get();
143 assertEquals(i, bytes.length);
144 for (int j = 0; j < i; j++) {
145 assertEquals((byte) j, bytes[j]);
146 }
147 }
148 assertTrue(!fileIter.hasNext());
149 }
150
151
152
153
154 @Test
155 public void testFILEUPLOAD135() throws IOException, FileUploadException {
156 final byte[] request = newShortRequest();
157 final ByteArrayInputStream bais = new ByteArrayInputStream(request);
158 try (InputStream inputStream = newInputStream(bais)) {
159 final List<FileItem> fileItems = parseUpload(inputStream, request.length);
160 final Iterator<FileItem> fileIter = fileItems.iterator();
161 assertTrue(fileIter.hasNext());
162 final FileItem item = fileIter.next();
163 assertEquals("field", item.getFieldName());
164 final byte[] bytes = item.get();
165 assertEquals(3, bytes.length);
166 assertEquals((byte) '1', bytes[0]);
167 assertEquals((byte) '2', bytes[1]);
168 assertEquals((byte) '3', bytes[2]);
169 assertTrue(!fileIter.hasNext());
170 }
171 }
172
173
174
175
176 @Test
177 public void testFileUploadException() throws IOException, FileUploadException {
178 final byte[] request = newRequest();
179 final byte[] invalidRequest = new byte[request.length - 11];
180 System.arraycopy(request, 0, invalidRequest, 0, request.length - 11);
181 assertInstanceOf(MultipartStream.MalformedStreamException.class,
182 assertThrows(IOFileUploadException.class, () -> parseUpload(invalidRequest)).getCause());
183 }
184
185
186
187
188 @Test
189 public void testInvalidFileNameException() throws Exception {
190 final String fileName = "foo.exe\u0000.png";
191 final String request =
192 "-----1234\r\n" +
193 "Content-Disposition: form-data; name=\"file\"; filename=\"" + fileName + "\"\r\n" +
194 "Content-Type: text/whatever\r\n" +
195 "\r\n" +
196 "This is the content of the file\n" +
197 "\r\n" +
198 "-----1234\r\n" +
199 "Content-Disposition: form-data; name=\"field\"\r\n" +
200 "\r\n" +
201 "fieldValue\r\n" +
202 "-----1234\r\n" +
203 "Content-Disposition: form-data; name=\"multi\"\r\n" +
204 "\r\n" +
205 "value1\r\n" +
206 "-----1234\r\n" +
207 "Content-Disposition: form-data; name=\"multi\"\r\n" +
208 "\r\n" +
209 "value2\r\n" +
210 "-----1234--\r\n";
211 final byte[] reqBytes = request.getBytes("US-ASCII");
212
213 final FileItemIterator fileItemIter = parseUpload(reqBytes.length, new ByteArrayInputStream(reqBytes));
214 final FileItemStream fileItemStream = fileItemIter.next();
215 InvalidFileNameException e = assertThrows(InvalidFileNameException.class, fileItemStream::getName);
216 assertEquals(fileName, e.getName());
217 assertTrue(e.getMessage().indexOf(fileName) == -1);
218 assertTrue(e.getMessage().indexOf("foo.exe\\0.png") != -1);
219
220 final List<FileItem> fileItems = parseUpload(reqBytes);
221 final FileItem fileItem = fileItems.get(0);
222 e = assertThrows(InvalidFileNameException.class, fileItem::getName);
223 assertEquals(fileName, e.getName());
224 assertTrue(e.getMessage().indexOf(fileName) == -1);
225 assertTrue(e.getMessage().indexOf("foo.exe\\0.png") != -1);
226 }
227
228
229
230
231 @Test
232 public void testIOException() throws IOException {
233 final byte[] request = newRequest();
234 final InputStream stream = new FilterInputStream(new ByteArrayInputStream(request)) {
235
236 private int num;
237
238 @Override
239 public int read() throws IOException {
240 if (++num > 123) {
241 throw new IOException("123");
242 }
243 return super.read();
244 }
245
246 @Override
247 public int read(final byte[] buffer, final int offset, final int length) throws IOException {
248 for (int i = 0; i < length; i++) {
249 final int res = read();
250 if (res == -1) {
251 return i == 0 ? -1 : i;
252 }
253 buffer[offset + i] = (byte) res;
254 }
255 return length;
256 }
257 };
258 final FileUploadException e = assertThrows(FileUploadException.class, () -> parseUpload(stream, request.length));
259 assertTrue(e.getCause() instanceof IOException);
260 assertEquals("123", e.getCause().getMessage());
261 }
262 }