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