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.http;
16  
17  import java.nio.charset.StandardCharsets;
18  import java.time.Instant;
19  import java.time.LocalDate;
20  import java.time.Month;
21  import java.time.ZoneId;
22  import java.util.ArrayList;
23  import java.util.Date;
24  import java.util.List;
25  
26  import org.htmlunit.WebTestCase;
27  import org.htmlunit.util.NameValuePair;
28  import org.junit.jupiter.api.Assertions;
29  import org.junit.jupiter.api.Test;
30  
31  /**
32   * Tests for {@link HttpUtils}.
33   *
34   * @author Ronald Brill
35   */
36  public class HttpUtilsTest extends WebTestCase {
37  
38      /**
39       * @throws Exception if the test fails
40       */
41      @Test
42      public void testBasicDateParse() {
43          final Date expected = createDate(2004, Month.NOVEMBER, 13);
44          Assertions.assertEquals(HttpUtils.parseDate("Sat, 13 Nov 2004 00:00:00 GMT"), expected);
45          Assertions.assertEquals(HttpUtils.parseDate("Saturday, 13 Nov 2004 00:00:00 GMT"), expected);
46          Assertions.assertEquals(HttpUtils.parseDate("Sat, 13-Nov-2004 00:00:00 GMT"), expected);
47          Assertions.assertEquals(HttpUtils.parseDate("Saturday, 13-Nov-2004 00:00:00 GMT"), expected);
48          Assertions.assertEquals(HttpUtils.parseDate("Sat, 13 Nov 2004 00:00:00 GMT"), expected);
49      }
50  
51      /**
52       * @throws Exception if the test fails
53       */
54      @Test
55      public void testMalformedDate() {
56          Assertions.assertNull(HttpUtils.parseDate("Sat, 13 Nov 2004 00:00:00 GMD"));
57          Assertions.assertNull(HttpUtils.parseDate("Die Feb 22 17:20:18 2024"));
58          Assertions.assertNull(HttpUtils.parseDate("Thu Feb 22 17:20;18 2024"));
59      }
60  
61      /**
62       * @throws Exception if the test fails
63       */
64      @Test
65      public void testParseInvalid() {
66          Assertions.assertNull(HttpUtils.parseDate("Friday, 13-Nov-04 00:00:00 GMT"));
67      }
68  
69      /**
70       * @throws Exception if the test fails
71       */
72      @Test
73      public void testParseNull() {
74          Assertions.assertNull(HttpUtils.parseDate(null));
75      }
76  
77      /**
78       * @throws Exception if the test fails
79       */
80      @Test
81      public void testTwoDigitYearDateParse() {
82          Assertions.assertEquals(createDate(2004, Month.NOVEMBER, 13),
83                  HttpUtils.parseDate("Saturday, 13-Nov-04 00:00:00 GMT"));
84  
85          Assertions.assertEquals(createDate(2074, Month.NOVEMBER, 13),
86                  HttpUtils.parseDate("Tuesday, 13-Nov-74 00:00:00 GMT"));
87      }
88  
89      /**
90       * @throws Exception if the test fails
91       */
92      @Test
93      public void testParseQuotedDate() {
94          Assertions.assertEquals(createDate(2004, Month.NOVEMBER, 13),
95                  HttpUtils.parseDate("'Sat, 13 Nov 2004 00:00:00 GMT'"));
96      }
97  
98      /**
99       * @throws Exception if the test fails
100      */
101     @Test
102     public void testFormatDate() {
103         Assertions.assertEquals("Sat, 13 Nov 2004 00:00:00 GMT",
104                 HttpUtils.formatDate(createDate(2004, Month.NOVEMBER, 13)));
105     }
106 
107     private static Date createDate(final int year, final Month month, final int day) {
108         final Instant instant = LocalDate.of(year, month, day).atStartOfDay(ZoneId.of("GMT")).toInstant();
109         return new Date(instant.toEpochMilli());
110     }
111 
112     /**
113      * @throws Exception if the test fails
114      */
115     @Test
116     public void testParseURLCodedContent() throws Exception {
117         List<NameValuePair> result;
118 
119         result = HttpUtils.parseUrlQuery("", StandardCharsets.UTF_8);
120         Assertions.assertTrue(result.isEmpty());
121 
122         result = HttpUtils.parseUrlQuery("Name0", StandardCharsets.UTF_8);
123         Assertions.assertEquals(1, result.size());
124         assertNameValuePair(result.get(0), "Name0", null);
125 
126         result = HttpUtils.parseUrlQuery("Name1=Value1", StandardCharsets.UTF_8);
127         Assertions.assertEquals(1, result.size());
128         assertNameValuePair(result.get(0), "Name1", "Value1");
129 
130         result = HttpUtils.parseUrlQuery("Name2=", StandardCharsets.UTF_8);
131         Assertions.assertEquals(1, result.size());
132         assertNameValuePair(result.get(0), "Name2", "");
133 
134         result = HttpUtils.parseUrlQuery("Name3", StandardCharsets.UTF_8);
135         Assertions.assertEquals(1, result.size());
136         assertNameValuePair(result.get(0), "Name3", null);
137 
138         result = HttpUtils.parseUrlQuery("Name4=Value%204%21", StandardCharsets.UTF_8);
139         Assertions.assertEquals(1, result.size());
140         assertNameValuePair(result.get(0), "Name4", "Value 4!");
141 
142         result = HttpUtils.parseUrlQuery("Name4=Value%2B4%21", StandardCharsets.UTF_8);
143         Assertions.assertEquals(1, result.size());
144         assertNameValuePair(result.get(0), "Name4", "Value+4!");
145 
146         result = HttpUtils.parseUrlQuery("Name4=Value%204%21%20%214", StandardCharsets.UTF_8);
147         Assertions.assertEquals(1, result.size());
148         assertNameValuePair(result.get(0), "Name4", "Value 4! !4");
149 
150         result = HttpUtils.parseUrlQuery("Name5=aaa&Name6=bbb", StandardCharsets.UTF_8);
151         Assertions.assertEquals(2, result.size());
152         assertNameValuePair(result.get(0), "Name5", "aaa");
153         assertNameValuePair(result.get(1), "Name6", "bbb");
154 
155         result = HttpUtils.parseUrlQuery("Name7=aaa&Name7=b%2Cb&Name7=ccc", StandardCharsets.UTF_8);
156         Assertions.assertEquals(3, result.size());
157         assertNameValuePair(result.get(0), "Name7", "aaa");
158         assertNameValuePair(result.get(1), "Name7", "b,b");
159         assertNameValuePair(result.get(2), "Name7", "ccc");
160 
161         result = HttpUtils.parseUrlQuery("Name8=xx%2C%20%20yy%20%20%2Czz", StandardCharsets.UTF_8);
162         Assertions.assertEquals(1, result.size());
163         assertNameValuePair(result.get(0), "Name8", "xx,  yy  ,zz");
164 
165         result = HttpUtils.parseUrlQuery("price=10%20%E2%82%AC", StandardCharsets.UTF_8);
166         Assertions.assertEquals(1, result.size());
167         assertNameValuePair(result.get(0), "price", "10 \u20AC");
168 
169         result = HttpUtils.parseUrlQuery("a=b\"c&d=e", StandardCharsets.UTF_8);
170         Assertions.assertEquals(2, result.size());
171         assertNameValuePair(result.get(0), "a", "b\"c");
172         assertNameValuePair(result.get(1), "d", "e");
173     }
174 
175     /**
176      * @throws Exception if the test fails
177      */
178     @Test
179     public void testParseInvalidURLCodedContent() throws Exception {
180         List<NameValuePair> result;
181 
182         result = HttpUtils.parseUrlQuery("name=%", StandardCharsets.UTF_8);
183         Assertions.assertEquals(1, result.size());
184         assertNameValuePair(result.get(0), "name", "%");
185 
186         result = HttpUtils.parseUrlQuery("name=%a", StandardCharsets.UTF_8);
187         Assertions.assertEquals(1, result.size());
188         assertNameValuePair(result.get(0), "name", "%a");
189 
190         result = HttpUtils.parseUrlQuery("name=%wa%20", StandardCharsets.UTF_8);
191         Assertions.assertEquals(1, result.size());
192         assertNameValuePair(result.get(0), "name", "%wa ");
193     }
194 
195     private static void assertNameValuePair(
196             final NameValuePair parameter,
197             final String expectedName,
198             final String expectedValue) {
199         Assertions.assertEquals(parameter.getName(), expectedName);
200         Assertions.assertEquals(parameter.getValue(), expectedValue);
201     }
202 
203     /**
204      * @throws Exception if the test fails
205      */
206     @Test
207     public void toQueryFormFields() {
208         final List<NameValuePair> params = new ArrayList<>();
209         Assertions.assertEquals("",
210                 HttpUtils.toQueryFormFields(params, StandardCharsets.US_ASCII));
211 
212         params.clear();
213         params.add(new NameValuePair("Name0", null));
214         Assertions.assertEquals("Name0",
215                 HttpUtils.toQueryFormFields(params, StandardCharsets.US_ASCII));
216 
217         params.clear();
218         params.add(new NameValuePair("Name1", "Value1"));
219         Assertions.assertEquals("Name1=Value1",
220                 HttpUtils.toQueryFormFields(params, StandardCharsets.US_ASCII));
221 
222         params.clear();
223         params.add(new NameValuePair("Name2", ""));
224         Assertions.assertEquals("Name2=",
225                 HttpUtils.toQueryFormFields(params, StandardCharsets.US_ASCII));
226 
227         params.clear();
228         params.add(new NameValuePair("Name4", "Value 4&"));
229         Assertions.assertEquals("Name4=Value+4%26",
230                 HttpUtils.toQueryFormFields(params, StandardCharsets.US_ASCII));
231 
232         params.clear();
233         params.add(new NameValuePair("Name4", "Value+4&"));
234         Assertions.assertEquals("Name4=Value%2B4%26",
235                 HttpUtils.toQueryFormFields(params, StandardCharsets.US_ASCII));
236 
237         params.clear();
238         params.add(new NameValuePair("Name4", "Value 4& =4"));
239         Assertions.assertEquals("Name4=Value+4%26+%3D4",
240                 HttpUtils.toQueryFormFields(params, StandardCharsets.US_ASCII));
241 
242         params.clear();
243         params.add(new NameValuePair("Name5", "aaa"));
244         params.add(new NameValuePair("Name6", "bbb"));
245         Assertions.assertEquals("Name5=aaa&Name6=bbb",
246                 HttpUtils.toQueryFormFields(params, StandardCharsets.US_ASCII));
247 
248         params.clear();
249         params.add(new NameValuePair("Name7", "aaa"));
250         params.add(new NameValuePair("Name7", "b,b"));
251         params.add(new NameValuePair("Name7", "ccc"));
252         Assertions.assertEquals("Name7=aaa&Name7=b%2Cb&Name7=ccc",
253                 HttpUtils.toQueryFormFields(params, StandardCharsets.US_ASCII));
254 
255         params.clear();
256         params.add(new NameValuePair("Name8", "xx,  yy  ,zz"));
257         Assertions.assertEquals("Name8=xx%2C++yy++%2Czz",
258                 HttpUtils.toQueryFormFields(params, StandardCharsets.US_ASCII));
259     }
260 }