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