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;
16  
17  import java.net.URL;
18  import java.nio.charset.Charset;
19  
20  import org.htmlunit.junit.annotation.Alerts;
21  import org.htmlunit.junit.annotation.BuggyWebDriver;
22  import org.htmlunit.junit.annotation.HtmlUnitNYI;
23  import org.junit.jupiter.api.Test;
24  import org.openqa.selenium.By;
25  import org.openqa.selenium.WebDriver;
26  import org.openqa.selenium.WebDriverException;
27  
28  /**
29   * Tests using the {@link PrimitiveWebServer}.
30   *
31   * @author Ahmed Ashour
32   * @author Ronald Brill
33   */
34  public class WebClient7Test extends WebDriverTestCase {
35  
36      /**
37       * Test that the path and query string are encoded to be valid.
38       * @throws Exception if something goes wrong
39       */
40      @Test
41      @Alerts("/test.html?a=b%20c&d=%C3%A9%C3%A8")
42      public void loadPage_EncodeRequest() throws Exception {
43          // with query string not encoded
44          testRequestUrlEncoding("test.html?a=b c&d=\u00E9\u00E8");
45      }
46  
47      /**
48       * Test that the path and query string are encoded to be valid.
49       * @throws Exception if something goes wrong
50       */
51      @Test
52      @Alerts("/test.html?a=b%20c&d=%C3%A9%C3%A8")
53      public void loadPage_EncodeRequest2() throws Exception {
54          // with query string already encoded
55          testRequestUrlEncoding("test.html?a=b%20c&d=%C3%A9%C3%A8");
56      }
57  
58      /**
59       * Test that the path and query string are encoded to be valid.
60       * @throws Exception if something goes wrong
61       */
62      @Test
63      @Alerts("/test.html?a=b%20c&d=e%20f")
64      public void loadPage_EncodeRequest3() throws Exception {
65          // with query string partially encoded
66          testRequestUrlEncoding("test.html?a=b%20c&d=e f");
67      }
68  
69      /**
70       * Test that the path and query string are encoded to be valid.
71       * @throws Exception if something goes wrong
72       */
73      @Test
74      @Alerts("/test.html?a=b%20c")
75      public void loadPage_EncodeRequest4() throws Exception {
76          // with anchor
77          testRequestUrlEncoding("test.html?a=b c#myAnchor");
78      }
79  
80      /**
81       * Test that the path and query string are encoded to be valid.
82       * @throws Exception if something goes wrong
83       */
84      @Test
85      @Alerts("/test.html?a=%26%3D%20%2C%24")
86      public void loadPage_EncodeRequest5() throws Exception {
87          // with query string containing encoded "&", "=", "+", ",", and "$"
88          testRequestUrlEncoding("test.html?a=%26%3D%20%2C%24");
89      }
90  
91      /**
92       * Test that the path and query string are encoded to be valid.
93       * @throws Exception if something goes wrong
94       */
95      @Test
96      @Alerts("/page%201.html")
97      public void loadPage_EncodeRequest6() throws Exception {
98          // with character to encode in path
99          testRequestUrlEncoding("page 1.html");
100     }
101 
102     /**
103      * Test that the path and query string are encoded to be valid.
104      * @throws Exception if something goes wrong
105      */
106     @Test
107     @Alerts("/test.html?param=%C2%A9%C2%A3")
108     public void loadPage_EncodeRequest7() throws Exception {
109         // unicode
110         testRequestUrlEncoding("test.html?param=\u00A9\u00A3");
111     }
112 
113     private void testRequestUrlEncoding(final String url) throws Exception {
114         final String html = DOCTYPE_HTML
115                 + "<html>"
116                 + "<head><title>foo</title></head>"
117                 + "<body></body></html>";
118 
119         final String response = "HTTP/1.1 200 OK\r\n"
120                 + "Content-Length: " + html.length() + "\r\n"
121                 + "Content-Type: text/html\r\n"
122                 + "Connection: close\r\n"
123                 + "\r\n"
124                 + html;
125 
126         shutDownAll();
127         try (PrimitiveWebServer primitiveWebServer = new PrimitiveWebServer(null, response, null)) {
128             final URL baseUrl = new URL("http://localhost:" + primitiveWebServer.getPort() + "/");
129             final WebDriver driver = getWebDriver();
130 
131             driver.get(new URL(baseUrl, url).toString());
132             String reqUrl = primitiveWebServer.getRequests().get(0);
133             reqUrl = reqUrl.substring(4, reqUrl.indexOf("HTTP/1.1") - 1);
134 
135             assertEquals(getExpectedAlerts()[0], reqUrl);
136         }
137     }
138 
139     private void contentEncoding(final boolean header,
140             final String charset,
141             final String responseEncodingCharset,
142             final String addHeader,
143             final String addHtml) throws Exception {
144         String html = DOCTYPE_HTML
145                 + "<html>\n"
146                 + "<head><title>foo</title>\n";
147         if (!header) {
148             html += "  <meta http-equiv='Content-Type' content='text/html; charset=" + charset + "'>\n";
149         }
150         if (addHeader != null) {
151             html += addHeader + "\n";
152         }
153 
154         html += "</head>\n"
155                 + "<body>\n"
156                 + addHtml + "\n"
157                 + "</body></html>";
158 
159         String firstResponse = "HTTP/1.1 200 OK\r\n"
160                 + "Content-Length: " + html.length() + "\r\n"
161                 + "Content-Type: text/html";
162         if (header) {
163             firstResponse += "; charset=" + charset;
164         }
165         firstResponse += "\r\n"
166                 + "Connection: close\r\n"
167                 + "\r\n" + html;
168 
169         final String secondResponse = "HTTP/1.1 404 Not Found\r\n"
170                 + "Content-Length: 0\r\n"
171                 + "Connection: close\r\n"
172                 + "\r\n";
173 
174         shutDownAll();
175         try (PrimitiveWebServer primitiveWebServer =
176                 new PrimitiveWebServer(Charset.forName(responseEncodingCharset), firstResponse, secondResponse)) {
177             final String url = "http://localhost:" + primitiveWebServer.getPort() + "/";
178             final WebDriver driver = getWebDriver();
179 
180             driver.get(url);
181             final String content = driver.findElement(By.tagName("body")).getText();
182             assertEquals(getExpectedAlerts()[0], content);
183         }
184     }
185 
186     /**
187      * @throws Exception if the test fails
188      */
189     @Test
190     @Alerts("/test.html?k%C3%B6nig")
191     public void anchorUrlEncodingUTF8Header() throws Exception {
192         anchorUrlEncoding(true, "UTF-8");
193     }
194 
195     /**
196      * @throws Exception if the test fails
197      */
198     @Test
199     @Alerts("/test.html?k%C3%B6nig")
200     public void anchorUrlEncodingUTF8Meta() throws Exception {
201         anchorUrlEncoding(false, "UTF-8");
202     }
203 
204     /**
205      * @throws Exception if the test fails
206      */
207     @Test
208     @Alerts("/test.html?k%F6nig")
209     public void anchorUrlEncodingISO8859_1Header() throws Exception {
210         anchorUrlEncoding(true, "ISO-8859-1");
211     }
212 
213     /**
214      * @throws Exception if the test fails
215      */
216     @Test
217     @Alerts("/test.html?k%F6nig")
218     public void anchorUrlEncodingISO8859_1Meta() throws Exception {
219         anchorUrlEncoding(false, "ISO-8859-1");
220     }
221 
222     /**
223      * @throws Exception if the test fails
224      */
225     @Test
226     @Alerts("/test.html?k?nig")
227     public void anchorUrlEncodingWindows_1251Header() throws Exception {
228         anchorUrlEncoding(true, "Windows-1251");
229     }
230 
231     /**
232      * @throws Exception if the test fails
233      */
234     @Test
235     @Alerts("/test.html?k?nig")
236     public void anchorUrlEncodingWindows_1251Meta() throws Exception {
237         anchorUrlEncoding(false, "Windows-1251");
238     }
239 
240     /**
241      * @throws Exception if the test fails
242      */
243     @Test
244     @Alerts("/area.html?k%C3%B6nig")
245     @BuggyWebDriver(FF = "WebDriverException",
246             FF_ESR = "WebDriverException")
247     public void areaUrlEncodingUTF8Header() throws Exception {
248         areaUrlEncoding(true, "UTF-8");
249     }
250 
251     /**
252      * @throws Exception if the test fails
253      */
254     @Test
255     @Alerts("/area.html?k%C3%B6nig")
256     @BuggyWebDriver(FF = "WebDriverException",
257             FF_ESR = "WebDriverException")
258     public void areaUrlEncodingUTF8Meta() throws Exception {
259         areaUrlEncoding(false, "UTF-8");
260     }
261 
262     /**
263      * @throws Exception if the test fails
264      */
265     @Test
266     @Alerts("/area.html?k%F6nig")
267     @BuggyWebDriver(FF = "WebDriverException",
268             FF_ESR = "WebDriverException")
269     public void areaUrlEncodingISO8859_1Header() throws Exception {
270         areaUrlEncoding(true, "ISO-8859-1");
271     }
272 
273     /**
274      * @throws Exception if the test fails
275      */
276     @Test
277     @Alerts("/area.html?k%F6nig")
278     @BuggyWebDriver(FF = "WebDriverException",
279             FF_ESR = "WebDriverException")
280     public void areaUrlEncodingISO8859_1Meta() throws Exception {
281         areaUrlEncoding(false, "ISO-8859-1");
282     }
283 
284     /**
285      * @throws Exception if the test fails
286      */
287     @Test
288     @Alerts("/test.gif?k%C3%B6nig")
289     public void imageUrlEncodingUTF8Header() throws Exception {
290         imageUrlEncoding(true, "UTF-8");
291     }
292 
293     /**
294      * @throws Exception if the test fails
295      */
296     @Test
297     @Alerts("/test.gif?k%C3%B6nig")
298     public void imageUrlEncodingUTF8Meta() throws Exception {
299         imageUrlEncoding(false, "UTF-8");
300     }
301 
302     /**
303      * @throws Exception if the test fails
304      */
305     @Test
306     @Alerts(DEFAULT = "/test.gif?k%F6nig",
307             FF = "/test.gif?k%EF%BF%BDnig",
308             FF_ESR = "/test.gif?k%EF%BF%BDnig")
309     @HtmlUnitNYI(FF = "/test.gif?k%F6nig",
310             FF_ESR = "/test.gif?k%F6nig")
311     public void imageUrlEncodingISO8859_1Header() throws Exception {
312         imageUrlEncoding(true, "ISO_8859_1");
313     }
314 
315     /**
316      * @throws Exception if the test fails
317      */
318     @Test
319     @Alerts(DEFAULT = "/test.gif?k%F6nig",
320             FF = "/test.gif?k%EF%BF%BDnig",
321             FF_ESR = "/test.gif?k%EF%BF%BDnig")
322     @HtmlUnitNYI(FF = "/test.gif?k%F6nig",
323             FF_ESR = "/test.gif?k%F6nig")
324     public void imageUrlEncodingISO8859_1Meta() throws Exception {
325         imageUrlEncoding(false, "ISO_8859_1");
326     }
327 
328     /**
329      * @throws Exception if the test fails
330      */
331     @Test
332     @Alerts("/test.css?k%C3%B6nig")
333     public void linkUrlEncodingUTF8Header() throws Exception {
334         linkUrlEncoding(true, "UTF-8");
335     }
336 
337     /**
338      * @throws Exception if the test fails
339      */
340     @Test
341     @Alerts("/test.css?k%C3%B6nig")
342     public void linkUrlEncodingUTF8Meta() throws Exception {
343         linkUrlEncoding(false, "UTF-8");
344     }
345 
346     /**
347      * @throws Exception if the test fails
348      */
349     @Test
350     @Alerts(DEFAULT = "/test.css?k%F6nig",
351             FF = "/test.css?k%EF%BF%BDnig",
352             FF_ESR = "/test.css?k%EF%BF%BDnig")
353     @HtmlUnitNYI(FF = "/test.css?k%F6nig",
354             FF_ESR = "/test.css?k%F6nig")
355     public void linkUrlEncodingISO8859_1Header() throws Exception {
356         linkUrlEncoding(true, "ISO_8859_1");
357     }
358 
359     /**
360      * @throws Exception if the test fails
361      */
362     @Test
363     @Alerts(DEFAULT = "/test.css?k%F6nig",
364             FF = "/test.css?k%EF%BF%BDnig",
365             FF_ESR = "/test.css?k%EF%BF%BDnig")
366     @HtmlUnitNYI(FF = "/test.css?k%F6nig",
367             FF_ESR = "/test.css?k%F6nig")
368     public void linkUrlEncodingISO8859_1Meta() throws Exception {
369         linkUrlEncoding(false, "ISO_8859_1");
370     }
371 
372     /**
373      * @throws Exception if the test fails
374      */
375     @Test
376     @Alerts("/test.html?k%C3%B6nig")
377     public void iframeUrlEncodingUTF8Header() throws Exception {
378         iframeUrlEncoding(true, "UTF-8");
379     }
380 
381     /**
382      * @throws Exception if the test fails
383      */
384     @Test
385     @Alerts("/test.html?k%C3%B6nig")
386     public void iframeUrlEncodingUTF8Meta() throws Exception {
387         iframeUrlEncoding(false, "UTF-8");
388     }
389 
390     /**
391      * @throws Exception if the test fails
392      */
393     @Test
394     @Alerts("/test.html?k%F6nig")
395     public void iframeUrlEncodingISO8859_1Header() throws Exception {
396         framesetUrlEncoding("ISO_8859_1");
397     }
398 
399     /**
400      * @throws Exception if the test fails
401      */
402     @Test
403     @Alerts("!abcd\u20AC\u00F6\u00FF")
404     public void contentEncodingAscii() throws Exception {
405         contentEncoding(true, "ascii", "windows-1252", null, "!abcd\u20AC\u00F6\u00FF");
406     }
407 
408     /**
409      * @throws Exception if the test fails
410      */
411     @Test
412     @Alerts("!abcd???")
413     public void contentEncodingAsciiAscii() throws Exception {
414         contentEncoding(true, "ascii", "ascii", null, "!abcd\u20AC\u00F6\u00FF");
415     }
416 
417     /**
418      * @throws Exception if the test fails
419      */
420     @Test
421     @Alerts("!abcd\u20AC\u00F6\u00FF")
422     public void contentEncodingXUserDefined() throws Exception {
423         contentEncoding(false, "x-user-defined", "windows-1252", null, "!abcd\u20AC\u00F6\u00FF");
424     }
425 
426     private void anchorUrlEncoding(final boolean header, final String charset) throws Exception {
427         urlEncoding(header, charset,
428                 null,
429                 "  <a id='myLink' href='test.html?k\u00F6nig'>Click me</a>",
430                 true);
431     }
432 
433     private void areaUrlEncoding(final boolean header, final String charset) throws Exception {
434         urlEncoding(header, charset,
435                 null,
436                 "  <img id='myImg' usemap='#dot' width='100' height='100'"
437                         + " src='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAA"
438                         + "HElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='>\n"
439                 + "  <map name='dot'>\n"
440                 + "    <area id='myLink' shape='rect' coords='0,0,42,42' href='area.html?k\u00F6nig'/>\n"
441                 + "  <map>\n",
442                 true);
443     }
444 
445     private void imageUrlEncoding(final boolean header, final String charset) throws Exception {
446         urlEncoding(header, charset,
447                 null,
448                 "  <img id='myImg' src='test.gif?k\u00F6nig'>"
449                 + "  <button id='myLink' onClick='document.getElementById(\"myImg\").width'></button>",
450                 true);
451     }
452 
453     private void linkUrlEncoding(final boolean header, final String charset) throws Exception {
454         urlEncoding(header, charset,
455                 "  <link rel='stylesheet' type='text/css' href='test.css?k\u00F6nig'>",
456                 "",
457                 false);
458     }
459 
460     private void iframeUrlEncoding(final boolean header, final String charset) throws Exception {
461         urlEncoding(header, charset,
462                 "  <iframe src='test.html?k\u00F6nig'></iframe> ",
463                 "",
464                 false);
465     }
466 
467     private void urlEncoding(final boolean header, final String charset,
468             final String addHeader,
469             final String addHtml,
470             final boolean click) throws Exception {
471         String html = DOCTYPE_HTML
472                 + "<html>\n"
473                 + "<head><title>foo</title>\n";
474         if (!header) {
475             html += "  <meta http-equiv='Content-Type' content='text/html; charset=" + charset + "'>\n";
476         }
477         if (addHeader != null) {
478             html += addHeader + "\n";
479         }
480 
481         html += "</head>\n"
482                 + "<body>\n"
483                 + addHtml + "\n"
484                 + "</body></html>";
485 
486         String firstResponse = "HTTP/1.1 200 OK\r\n"
487                 + "Content-Length: " + html.length() + "\r\n"
488                 + "Content-Type: text/html";
489         if (header) {
490             firstResponse += "; charset=" + charset;
491         }
492         firstResponse += "\r\n"
493                 + "Connection: close\r\n"
494                 + "\r\n" + html;
495 
496         final String secondResponse = "HTTP/1.1 404 Not Found\r\n"
497                 + "Content-Length: 0\r\n"
498                 + "Connection: close\r\n"
499                 + "\r\n";
500 
501         shutDownAll();
502         try (PrimitiveWebServer primitiveWebServer =
503                 new PrimitiveWebServer(Charset.forName(charset), firstResponse, secondResponse)) {
504             final String url = "http://localhost:" + primitiveWebServer.getPort() + "/";
505             final WebDriver driver = getWebDriver();
506 
507             driver.get(url);
508             try {
509                 if (click) {
510                     driver.findElement(By.id("myLink")).click();
511                 }
512 
513                 String reqUrl = primitiveWebServer.getRequests().get(1);
514                 reqUrl = reqUrl.substring(4, reqUrl.indexOf("HTTP/1.1") - 1);
515 
516                 assertEquals(getExpectedAlerts()[0], reqUrl);
517             }
518             catch (final WebDriverException e) {
519                 assertEquals(getExpectedAlerts()[0], "WebDriverException");
520             }
521         }
522     }
523 
524     /**
525      * @throws Exception if the test fails
526      */
527     @Test
528     @Alerts("/test.html?k%C3%B6nig")
529     public void framesetUrlEncodingUTF8() throws Exception {
530         framesetUrlEncoding("UTF-8");
531     }
532 
533     /**
534      * @throws Exception if the test fails
535      */
536     @Test
537     @Alerts("/test.html?k%F6nig")
538     public void framesetUrlEncodingISO8859_1() throws Exception {
539         framesetUrlEncoding("ISO_8859_1");
540     }
541 
542     private void framesetUrlEncoding(final String charset) throws Exception {
543         final String html = DOCTYPE_HTML
544                 + "<html>\n"
545                 + "<frameset><frame src='test.html?k\u00F6nig'></frameset>\n"
546                 + "</html>";
547 
548         final String firstResponse = "HTTP/1.1 200 OK\r\n"
549                 + "Content-Length: " + html.length() + "\r\n"
550                 + "Content-Type: text/html; charset=" + charset + "\r\n"
551                 + "Connection: close\r\n"
552                 + "\r\n" + html;
553 
554         final String secondResponse = "HTTP/1.1 404 Not Found\r\n"
555                 + "Content-Length: 0\r\n"
556                 + "Connection: close\r\n"
557                 + "\r\n";
558 
559         shutDownAll();
560         try (PrimitiveWebServer primitiveWebServer =
561                 new PrimitiveWebServer(Charset.forName(charset), firstResponse, secondResponse)) {
562             final String url = "http://localhost:" + primitiveWebServer.getPort() + "/";
563             final WebDriver driver = getWebDriver();
564 
565             driver.get(url);
566 
567             String reqUrl = primitiveWebServer.getRequests().get(1);
568             reqUrl = reqUrl.substring(4, reqUrl.indexOf("HTTP/1.1") - 1);
569 
570             assertEquals(getExpectedAlerts()[0], reqUrl);
571         }
572     }
573 }