View Javadoc
1   /*
2    * Copyright (c) 2002-2025 Gargoyle Software Inc.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * https://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software
10   * distributed under the License is distributed on an "AS IS" BASIS,
11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   * See the License for the specific language governing permissions and
13   * limitations under the License.
14   */
15  package org.htmlunit.html;
16  
17  import static java.nio.charset.StandardCharsets.UTF_8;
18  
19  import java.io.File;
20  import java.io.IOException;
21  import java.io.Writer;
22  import java.util.HashMap;
23  import java.util.Map;
24  
25  import javax.servlet.Servlet;
26  import javax.servlet.ServletException;
27  import javax.servlet.http.HttpServlet;
28  import javax.servlet.http.HttpServletRequest;
29  import javax.servlet.http.HttpServletResponse;
30  
31  import org.apache.commons.fileupload.FileItem;
32  import org.apache.commons.fileupload.FileUploadBase;
33  import org.apache.commons.fileupload.disk.DiskFileItemFactory;
34  import org.apache.commons.fileupload.servlet.ServletFileUpload;
35  import org.htmlunit.WebDriverTestCase;
36  import org.htmlunit.junit.BrowserRunner;
37  import org.htmlunit.junit.annotation.Alerts;
38  import org.htmlunit.util.MimeType;
39  import org.junit.BeforeClass;
40  import org.junit.Test;
41  import org.junit.runner.RunWith;
42  import org.openqa.selenium.By;
43  import org.openqa.selenium.WebDriver;
44  
45  /**
46   * Tests for {@link HtmlFileInput}.
47   *
48   * @author Marc Guillemot
49   * @author Ahmed Ashour
50   * @author Ronald Brill
51   * @author Frank Danek
52   */
53  @RunWith(BrowserRunner.class)
54  public class HtmlFileInput3Test extends WebDriverTestCase {
55  
56      /**
57       * @throws Exception if an error occurs
58       */
59      @Test
60      @Alerts({"CONTENT_TYPE:application/octet-stream", "charset"})
61      public void contentTypeUnknown() throws Exception {
62          contentType("unknown");
63      }
64  
65      /**
66       * @throws Exception if an error occurs
67       */
68      @Test
69      @Alerts({"CONTENT_TYPE:text/html", "charset"})
70      public void contentTypeHtm() throws Exception {
71          contentType("htm");
72      }
73  
74      /**
75       * @throws Exception if an error occurs
76       */
77      @Test
78      @Alerts({"CONTENT_TYPE:text/html", "charset"})
79      public void contentTypeHtml() throws Exception {
80          contentType("html");
81      }
82  
83      /**
84       * @throws Exception if an error occurs
85       */
86      @Test
87      @Alerts({"CONTENT_TYPE:text/css", "charset"})
88      public void contentTypeCss() throws Exception {
89          contentType("css");
90      }
91  
92      /**
93       * @throws Exception if an error occurs
94       */
95      @Test
96      @Alerts({"CONTENT_TYPE:text/xml", "charset"})
97      public void contentTypeXml() throws Exception {
98          contentType("xml");
99      }
100 
101     /**
102      * @throws Exception if an error occurs
103      */
104     @Test
105     @Alerts({"CONTENT_TYPE:image/gif", "charset"})
106     public void contentTypeGif() throws Exception {
107         contentType("gif");
108     }
109 
110     /**
111      * @throws Exception if an error occurs
112      */
113     @Test
114     @Alerts({"CONTENT_TYPE:image/jpeg", "charset"})
115     public void contentTypeJpeg() throws Exception {
116         contentType("jpeg");
117     }
118 
119     /**
120      * @throws Exception if an error occurs
121      */
122     @Test
123     @Alerts({"CONTENT_TYPE:image/jpeg", "charset"})
124     public void contentTypeJpg() throws Exception {
125         contentType("jpg");
126     }
127 
128     /**
129      * @throws Exception if an error occurs
130      */
131     @Test
132     @Alerts({"CONTENT_TYPE:image/png", "charset"})
133     public void contentTypePng() throws Exception {
134         contentType("png");
135     }
136 
137     /**
138      * @throws Exception if an error occurs
139      */
140     @Test
141     @Alerts({"CONTENT_TYPE:image/webp", "charset"})
142     public void contentTypeWebp() throws Exception {
143         contentType("webp");
144     }
145 
146     /**
147      * @throws Exception if an error occurs
148      */
149     @Test
150     @Alerts({"CONTENT_TYPE:video/mp4", "charset"})
151     public void contentTypeMp4() throws Exception {
152         contentType("mp4");
153     }
154 
155     /**
156      * @throws Exception if an error occurs
157      */
158     @Test
159     @Alerts({"CONTENT_TYPE:video/mp4", "charset"})
160     public void contentTypeM4v() throws Exception {
161         contentType("m4v");
162     }
163 
164     /**
165      * @throws Exception if an error occurs
166      */
167     @Test
168     @Alerts(DEFAULT = {"CONTENT_TYPE:audio/mp4", "charset"},
169             CHROME = {"CONTENT_TYPE:audio/x-m4a", "charset"},
170             EDGE = {"CONTENT_TYPE:audio/x-m4a", "charset"})
171     public void contentTypeM4a() throws Exception {
172         contentType("m4a");
173     }
174 
175     /**
176      * @throws Exception if an error occurs
177      */
178     @Test
179     @Alerts({"CONTENT_TYPE:audio/mpeg", "charset"})
180     public void contentTypeMp3() throws Exception {
181         contentType("mp3");
182     }
183 
184     /**
185      * @throws Exception if an error occurs
186      */
187     @Test
188     @Alerts({"CONTENT_TYPE:video/ogg", "charset"})
189     public void contentTypeOgv() throws Exception {
190         contentType("ogv");
191     }
192 
193     /**
194      * @throws Exception if an error occurs
195      */
196     @Test
197     @Alerts({"CONTENT_TYPE:video/ogg", "charset"})
198     public void contentTypeOgm() throws Exception {
199         contentType("ogm");
200     }
201 
202     /**
203      * @throws Exception if an error occurs
204      */
205     @Test
206     @Alerts(DEFAULT = {"CONTENT_TYPE:audio/ogg", "charset"},
207             FF = {"CONTENT_TYPE:application/ogg", "charset"},
208             FF_ESR = {"CONTENT_TYPE:video/ogg", "charset"})
209     public void contentTypeOgg() throws Exception {
210         contentType("ogg");
211     }
212 
213     /**
214      * @throws Exception if an error occurs
215      */
216     @Test
217     @Alerts({"CONTENT_TYPE:audio/ogg", "charset"})
218     public void contentTypeOga() throws Exception {
219         contentType("oga");
220     }
221 
222     /**
223      * @throws Exception if an error occurs
224      */
225     @Test
226     @Alerts({"CONTENT_TYPE:audio/ogg", "charset"})
227     public void contentTypeOpus() throws Exception {
228         contentType("opus");
229     }
230 
231     /**
232      * @throws Exception if an error occurs
233      */
234     @Test
235     @Alerts({"CONTENT_TYPE:video/webm", "charset"})
236     public void contentTypeWebm() throws Exception {
237         contentType("webm");
238     }
239 
240     /**
241      * @throws Exception if an error occurs
242      */
243     @Test
244     @Alerts({"CONTENT_TYPE:application/pdf", "charset"})
245     public void contentTypePdf() throws Exception {
246         contentType("pdf");
247     }
248 
249     /**
250      * @throws Exception if an error occurs
251      */
252     @Test
253     @Alerts({"CONTENT_TYPE:audio/wav", "charset"})
254     public void contentTypeWav() throws Exception {
255         contentType("wav");
256     }
257 
258     /**
259      * @throws Exception if an error occurs
260      */
261     @Test
262     @Alerts(DEFAULT = {"CONTENT_TYPE:audio/flac", "charset"},
263             FF = {"CONTENT_TYPE:audio/x-flac", "charset"},
264             FF_ESR = {"CONTENT_TYPE:audio/x-flac", "charset"})
265     public void contentTypeFlac() throws Exception {
266         contentType("flac");
267     }
268 
269     /**
270      * @throws Exception if an error occurs
271      */
272     @Test
273     @Alerts({"CONTENT_TYPE:application/xhtml+xml", "charset"})
274     public void contentTypeXhtml() throws Exception {
275         contentType("xhtml");
276     }
277 
278     /**
279      * @throws Exception if an error occurs
280      */
281     @Test
282     @Alerts({"CONTENT_TYPE:application/xhtml+xml", "charset"})
283     public void contentTypeXht() throws Exception {
284         contentType("xht");
285     }
286 
287     /**
288      * @throws Exception if an error occurs
289      */
290     @Test
291     @Alerts(DEFAULT = {"CONTENT_TYPE:application/octet-stream", "charset"},
292             CHROME = {"CONTENT_TYPE:application/xhtml+xml", "charset"},
293             EDGE = {"CONTENT_TYPE:application/xhtml+xml", "charset"})
294     public void contentTypeXhtm() throws Exception {
295         contentType("xhtm");
296     }
297 
298     /**
299      * @throws Exception if an error occurs
300      */
301     @Test
302     @Alerts({"CONTENT_TYPE:text/plain", "charset"})
303     public void contentTypeText() throws Exception {
304         contentType("text");
305     }
306 
307     /**
308      * @throws Exception if an error occurs
309      */
310     @Test
311     @Alerts({"CONTENT_TYPE:text/plain", "charset"})
312     public void contentTypeTxt() throws Exception {
313         contentType("txt");
314     }
315 
316     /**
317      * @throws Exception if an error occurs
318      */
319     @Test
320     @Alerts({"CONTENT_TYPE:text/html", "charset"})
321     public void contentTypeCaseInsensitive() throws Exception {
322         contentType("HtmL");
323     }
324 
325     /**
326      * Starts the web server.
327      */
328     @BeforeClass
329     public static void before() {
330         try {
331             final Map<String, Class<? extends Servlet>> servlets = new HashMap<>();
332             servlets.put("/upload1", Upload1Servlet.class);
333             servlets.put("/upload2", ContentTypeUpload2Servlet.class);
334             startWebServer("./", new String[0], servlets);
335         }
336         catch (final Exception e) {
337             throw new RuntimeException(e);
338         }
339     }
340 
341     private void contentType(final String extension) throws Exception {
342         final WebDriver driver = getWebDriver();
343         driver.get(URL_FIRST + "upload1");
344 
345         final File tmpFile = File.createTempFile("htmlunit-test", "." + extension);
346         try {
347             final String path = tmpFile.getAbsolutePath();
348             driver.findElement(By.name("myInput")).sendKeys(path);
349             driver.findElement(By.id("mySubmit")).submit();
350         }
351         finally {
352             assertTrue(tmpFile.delete());
353         }
354 
355         final long maxWait = System.currentTimeMillis() + DEFAULT_WAIT_TIME.toMillis();
356 
357         while (System.currentTimeMillis() < maxWait) {
358             try {
359                 final String pageSource = driver.getPageSource();
360                 assertNotNull(pageSource);
361                 assertTrue(pageSource, pageSource.contains(getExpectedAlerts()[0]));
362                 assertFalse(pageSource, pageSource.contains(getExpectedAlerts()[1]));
363                 return;
364             }
365             catch (final AssertionError e) {
366                 // ignore and wait
367             }
368         }
369 
370         final String pageSource = driver.getPageSource();
371         assertTrue("\"" + pageSource + "\" does not contain \""
372                 + getExpectedAlerts()[0] + "\"", pageSource.contains(getExpectedAlerts()[0]));
373         assertFalse("\"" + pageSource + "\" contains \""
374                 + getExpectedAlerts()[0] + "\" but should not", pageSource.contains(getExpectedAlerts()[1]));
375     }
376 
377     /**
378      * Servlet for '/upload1'.
379      */
380     public static class Upload1Servlet extends HttpServlet {
381 
382         /**
383          * {@inheritDoc}
384          */
385         @Override
386         protected void doGet(final HttpServletRequest request, final HttpServletResponse response)
387             throws ServletException, IOException {
388             response.setCharacterEncoding(UTF_8.name());
389             response.setContentType(MimeType.TEXT_HTML);
390             response.getWriter().write("<html>\n"
391                 + "<body>\n"
392                 + "<form action='upload2' method='post' enctype='multipart/form-data'>\n"
393                 + "Name: <input name='myInput' type='file'><br>\n"
394                 + "Name 2 (should stay empty): <input name='myInput2' type='file'><br>\n"
395                 + "<input type='submit' value='Upload' id='mySubmit'>\n"
396                 + "</form>\n"
397                 + "</body></html>\n");
398         }
399     }
400 
401     /**
402      * Servlet for '/upload2'.
403      */
404     public static class ContentTypeUpload2Servlet extends HttpServlet {
405 
406         /**
407          * {@inheritDoc}
408          */
409         @Override
410         protected void doPost(final HttpServletRequest request, final HttpServletResponse response)
411             throws ServletException, IOException {
412             request.setCharacterEncoding(UTF_8.name());
413             response.setContentType(MimeType.TEXT_HTML);
414             try (Writer writer = response.getWriter()) {
415                 if (ServletFileUpload.isMultipartContent(request)) {
416                     try {
417                         final ServletFileUpload upload = new ServletFileUpload(new DiskFileItemFactory());
418                         for (final FileItem item : upload.parseRequest(request)) {
419                             if ("myInput".equals(item.getFieldName())) {
420                                 writer.write("CONTENT_TYPE:" + item.getContentType());
421                             }
422                         }
423                     }
424                     catch (final FileUploadBase.SizeLimitExceededException e) {
425                         writer.write("SizeLimitExceeded");
426                     }
427                     catch (final Exception e) {
428                         writer.write("error");
429                     }
430                 }
431             }
432         }
433     }
434 }