View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  
28  package org.apache.hc.client5.http.impl.cookie;
29  
30  import java.util.Arrays;
31  import java.util.Collections;
32  import java.util.List;
33  
34  import org.apache.hc.client5.http.cookie.CommonCookieAttributeHandler;
35  import org.apache.hc.client5.http.cookie.Cookie;
36  import org.apache.hc.client5.http.cookie.CookieOrigin;
37  import org.apache.hc.client5.http.cookie.MalformedCookieException;
38  import org.apache.hc.core5.http.Header;
39  import org.apache.hc.core5.http.message.BasicHeader;
40  import org.junit.jupiter.api.Assertions;
41  import org.junit.jupiter.api.Test;
42  import org.mockito.ArgumentMatchers;
43  import org.mockito.Mockito;
44  
45  class TestRFC6265CookieSpec {
46  
47      @Test
48      void testParseCookieBasics() throws Exception {
49          final CommonCookieAttributeHandler h1 = Mockito.mock(CommonCookieAttributeHandler.class);
50          Mockito.when(h1.getAttributeName()).thenReturn("this");
51          final CommonCookieAttributeHandler h2 = Mockito.mock(CommonCookieAttributeHandler.class);
52          Mockito.when(h2.getAttributeName()).thenReturn("that");
53  
54          final RFC6265CookieSpec cookiespec = new RFC6265CookieSpec(h1, h2);
55  
56          final Header header = new BasicHeader("Set-Cookie", "name = value ; this = stuff;");
57          final CookieOrigin origin = new CookieOrigin("host", 80, "/path/", true);
58          final List<Cookie> cookies = cookiespec.parse(header, origin);
59  
60          Assertions.assertEquals(1, cookies.size());
61          final Cookie cookie = cookies.get(0);
62          Assertions.assertEquals("name", cookie.getName());
63          Assertions.assertEquals("value", cookie.getValue());
64          Assertions.assertEquals("/path", cookie.getPath());
65          Assertions.assertEquals("host", cookie.getDomain());
66          Assertions.assertEquals("stuff", cookie.getAttribute("this"));
67          Assertions.assertNull(cookie.getAttribute("that"));
68  
69          Mockito.verify(h1).parse(ArgumentMatchers.any(), ArgumentMatchers.eq("stuff"));
70          Mockito.verify(h2, Mockito.never()).parse(ArgumentMatchers.any(), ArgumentMatchers.anyString());
71      }
72  
73      @Test
74      void testParseCookieQuotedValue() throws Exception {
75          final RFC6265CookieSpec cookiespec = new RFC6265CookieSpec();
76  
77          final Header header = new BasicHeader("Set-Cookie", "name = \" one, two, three; four \" ; this = stuff;");
78          final CookieOrigin origin = new CookieOrigin("host", 80, "/path/", true);
79          final List<Cookie> cookies = cookiespec.parse(header, origin);
80  
81          Assertions.assertEquals(1, cookies.size());
82          final Cookie cookie = cookies.get(0);
83          Assertions.assertEquals("name", cookie.getName());
84          Assertions.assertEquals(" one, two, three; four ", cookie.getValue());
85          Assertions.assertEquals("stuff", cookie.getAttribute("this"));
86      }
87  
88      @Test
89      void testParseCookieWrongHeader() {
90          final RFC6265CookieSpec cookiespec = new RFC6265CookieSpec();
91  
92          final Header header = new BasicHeader("Set-Cookie2", "blah");
93          final CookieOrigin origin = new CookieOrigin("host", 80, "/path/", true);
94          Assertions.assertThrows(MalformedCookieException.class, () ->
95                  cookiespec.parse(header, origin));
96      }
97  
98      @Test
99      void testParseCookieMissingName() throws Exception {
100         final RFC6265CookieSpec cookiespec = new RFC6265CookieSpec();
101 
102         final Header header = new BasicHeader("Set-Cookie", "=blah ; this = stuff;");
103         final CookieOrigin origin = new CookieOrigin("host", 80, "/path/", true);
104         final List<Cookie> cookies = cookiespec.parse(header, origin);
105         Assertions.assertEquals(0, cookies.size());
106     }
107 
108     @Test
109     void testParseCookieMissingValue1() throws Exception {
110         final RFC6265CookieSpec cookiespec = new RFC6265CookieSpec();
111 
112         final Header header = new BasicHeader("Set-Cookie", "blah");
113         final CookieOrigin origin = new CookieOrigin("host", 80, "/path/", true);
114         final List<Cookie> cookies = cookiespec.parse(header, origin);
115         Assertions.assertEquals(0, cookies.size());
116     }
117 
118     @Test
119     void testParseCookieMissingValue2() {
120         final RFC6265CookieSpec cookiespec = new RFC6265CookieSpec();
121 
122         final Header header = new BasicHeader("Set-Cookie", "blah;");
123         final CookieOrigin origin = new CookieOrigin("host", 80, "/path/", true);
124         Assertions.assertThrows(MalformedCookieException.class, () ->
125                 cookiespec.parse(header, origin));
126     }
127 
128     @Test
129     void testParseCookieEmptyValue() throws Exception {
130         final RFC6265CookieSpec cookiespec = new RFC6265CookieSpec();
131 
132         final Header header = new BasicHeader("Set-Cookie", "blah=;");
133         final CookieOrigin origin = new CookieOrigin("host", 80, "/path/", true);
134         final List<Cookie> cookies = cookiespec.parse(header, origin);
135         Assertions.assertEquals(1, cookies.size());
136         final Cookie cookie = cookies.get(0);
137         Assertions.assertEquals("blah", cookie.getName());
138         Assertions.assertEquals("", cookie.getValue());
139     }
140 
141     @Test
142     void testParseCookieWithAttributes() throws Exception {
143         final CommonCookieAttributeHandler h1 = Mockito.mock(CommonCookieAttributeHandler.class);
144         Mockito.when(h1.getAttributeName()).thenReturn("this");
145         final CommonCookieAttributeHandler h2 = Mockito.mock(CommonCookieAttributeHandler.class);
146         Mockito.when(h2.getAttributeName()).thenReturn("that");
147 
148         final RFC6265CookieSpec cookiespec = new RFC6265CookieSpec(h1, h2);
149 
150         final Header header = new BasicHeader("Set-Cookie", "name = value ; p1 = v ; p2 = v,0; p3 ; p4");
151         final CookieOrigin origin = new CookieOrigin("host", 80, "/path/", true);
152         final List<Cookie> cookies = cookiespec.parse(header, origin);
153 
154         Assertions.assertEquals(1, cookies.size());
155         final Cookie cookie = cookies.get(0);
156         Assertions.assertEquals("name", cookie.getName());
157         Assertions.assertEquals("value", cookie.getValue());
158         Assertions.assertEquals("v", cookie.getAttribute("p1"));
159         Assertions.assertEquals("v,0", cookie.getAttribute("p2"));
160         Assertions.assertTrue(cookie.containsAttribute("p3"));
161         Assertions.assertTrue(cookie.containsAttribute("p4"));
162         Assertions.assertFalse(cookie.containsAttribute("p5"));
163     }
164 
165     @Test
166     void testParseCookieWithAttributes2() throws Exception {
167         final CommonCookieAttributeHandler h1 = Mockito.mock(CommonCookieAttributeHandler.class);
168         Mockito.when(h1.getAttributeName()).thenReturn("this");
169         final CommonCookieAttributeHandler h2 = Mockito.mock(CommonCookieAttributeHandler.class);
170         Mockito.when(h2.getAttributeName()).thenReturn("that");
171 
172         final RFC6265CookieSpec cookiespec = new RFC6265CookieSpec(h1, h2);
173 
174         final Header header = new BasicHeader("Set-Cookie", "name = value ; p1 = v");
175         final CookieOrigin origin = new CookieOrigin("host", 80, "/path/", true);
176         final List<Cookie> cookies = cookiespec.parse(header, origin);
177 
178         Assertions.assertEquals(1, cookies.size());
179         final Cookie cookie = cookies.get(0);
180         Assertions.assertEquals("name", cookie.getName());
181         Assertions.assertEquals("value", cookie.getValue());
182         Assertions.assertEquals("v", cookie.getAttribute("p1"));
183     }
184 
185     @Test
186     void testParseCookieWithAttributes3() throws Exception {
187         final CommonCookieAttributeHandler h1 = Mockito.mock(CommonCookieAttributeHandler.class);
188         Mockito.when(h1.getAttributeName()).thenReturn("this");
189         final CommonCookieAttributeHandler h2 = Mockito.mock(CommonCookieAttributeHandler.class);
190         Mockito.when(h2.getAttributeName()).thenReturn("that");
191 
192         final RFC6265CookieSpec cookiespec = new RFC6265CookieSpec(h1, h2);
193 
194         final Header header = new BasicHeader("Set-Cookie", "name = value ; p1 =");
195         final CookieOrigin origin = new CookieOrigin("host", 80, "/path/", true);
196         final List<Cookie> cookies = cookiespec.parse(header, origin);
197 
198         Assertions.assertEquals(1, cookies.size());
199         final Cookie cookie = cookies.get(0);
200         Assertions.assertEquals("name", cookie.getName());
201         Assertions.assertEquals("value", cookie.getValue());
202         Assertions.assertEquals("", cookie.getAttribute("p1"));
203     }
204 
205     @Test
206     void testParseCookieWithHttpOnly() throws Exception {
207         final RFC6265CookieSpec cookiespec = new RFC6265CookieSpec();
208 
209         final Header header = new BasicHeader("Set-Cookie", "name = value ; HttpOnly");
210         final CookieOrigin origin = new CookieOrigin("host", 80, "/path/", true);
211         final List<Cookie> cookies = cookiespec.parse(header, origin);
212 
213         Assertions.assertEquals(1, cookies.size());
214         final Cookie cookie = cookies.get(0);
215         Assertions.assertTrue(cookie.containsAttribute(Cookie.HTTP_ONLY_ATTR));
216     }
217 
218     @Test
219     void testValidateCookieBasics() throws Exception {
220         final CommonCookieAttributeHandler h1 = Mockito.mock(CommonCookieAttributeHandler.class);
221         Mockito.when(h1.getAttributeName()).thenReturn("this");
222         final CommonCookieAttributeHandler h2 = Mockito.mock(CommonCookieAttributeHandler.class);
223         Mockito.when(h2.getAttributeName()).thenReturn("that");
224 
225         final RFC6265CookieSpec cookiespec = new RFC6265CookieSpec(h1, h2);
226 
227         final CookieOrigin origin = new CookieOrigin("host", 80, "/path/", true);
228         final BasicClientCookie cookie = new BasicClientCookie("name", "value");
229         cookiespec.validate(cookie, origin);
230 
231         Mockito.verify(h1).validate(cookie, origin);
232         Mockito.verify(h2).validate(cookie, origin);
233     }
234 
235     @Test
236     void testMatchCookie() {
237         final CommonCookieAttributeHandler h1 = Mockito.mock(CommonCookieAttributeHandler.class);
238         Mockito.when(h1.getAttributeName()).thenReturn("this");
239         final CommonCookieAttributeHandler h2 = Mockito.mock(CommonCookieAttributeHandler.class);
240         Mockito.when(h2.getAttributeName()).thenReturn("that");
241 
242         final RFC6265CookieSpec cookiespec = new RFC6265CookieSpec(h1, h2);
243 
244         final CookieOrigin origin = new CookieOrigin("host", 80, "/path/", true);
245         final BasicClientCookie cookie = new BasicClientCookie("name", "value");
246 
247         Mockito.when(h1.match(cookie, origin)).thenReturn(true);
248         Mockito.when(h2.match(cookie, origin)).thenReturn(true);
249 
250         Assertions.assertTrue(cookiespec.match(cookie, origin));
251 
252         Mockito.verify(h1).match(cookie, origin);
253         Mockito.verify(h2).match(cookie, origin);
254     }
255 
256     @Test
257     void testMatchCookieNoMatch() {
258         final CommonCookieAttributeHandler h1 = Mockito.mock(CommonCookieAttributeHandler.class);
259         Mockito.when(h1.getAttributeName()).thenReturn("this");
260         final CommonCookieAttributeHandler h2 = Mockito.mock(CommonCookieAttributeHandler.class);
261         Mockito.when(h2.getAttributeName()).thenReturn("that");
262 
263         final RFC6265CookieSpec cookiespec = new RFC6265CookieSpec(h1, h2);
264 
265         final CookieOrigin origin = new CookieOrigin("host", 80, "/path/", true);
266         final BasicClientCookie cookie = new BasicClientCookie("name", "value");
267 
268         Mockito.when(h1.match(cookie, origin)).thenReturn(false);
269         Mockito.when(h2.match(cookie, origin)).thenReturn(false);
270 
271         Assertions.assertFalse(cookiespec.match(cookie, origin));
272 
273         Mockito.verify(h1).match(cookie, origin);
274         Mockito.verify(h2, Mockito.never()).match(cookie, origin);
275     }
276 
277     @Test
278     void testFormatCookiesBasics() {
279         final Cookie cookie1 = new BasicClientCookie("name1", "value");
280 
281         final RFC6265CookieSpec cookiespec = new RFC6265CookieSpec();
282         final List<Header> headers = cookiespec.formatCookies(Collections.singletonList(cookie1));
283         Assertions.assertNotNull(headers);
284         Assertions.assertEquals(1, headers.size());
285         final Header header = headers.get(0);
286         Assertions.assertEquals("Cookie", header.getName());
287         Assertions.assertEquals("name1=value", header.getValue());
288     }
289 
290     @Test
291     void testFormatCookiesIllegalCharsInValue() {
292         final Cookie cookie1 = new BasicClientCookie("name1", "value");
293         final Cookie cookie2 = new BasicClientCookie("name2", "some value");
294         final Cookie cookie3 = new BasicClientCookie("name3", "\"\\\"");
295         final RFC6265CookieSpec cookiespec = new RFC6265CookieSpec();
296         final List<Header> headers = cookiespec.formatCookies(Arrays.asList(cookie1, cookie2, cookie3));
297         Assertions.assertNotNull(headers);
298         Assertions.assertEquals(1, headers.size());
299         final Header header = headers.get(0);
300         Assertions.assertEquals("Cookie", header.getName());
301         Assertions.assertEquals("name1=value; name2=\"some value\"; name3=\"\\\"\\\\\\\"\"", header.getValue());
302     }
303 
304     @Test
305     void testParseCookieMultipleAttributes() throws Exception {
306         final CommonCookieAttributeHandler h1 = Mockito.mock(CommonCookieAttributeHandler.class);
307         Mockito.when(h1.getAttributeName()).thenReturn("this");
308 
309         final RFC6265CookieSpec cookiespec = new RFC6265CookieSpec(h1);
310 
311         final Header header = new BasicHeader("Set-Cookie", "name = value ; this = stuff; this = morestuff;");
312         final CookieOrigin origin = new CookieOrigin("host", 80, "/path/", true);
313         cookiespec.parse(header, origin);
314 
315         Mockito.verify(h1).parse(ArgumentMatchers.any(), ArgumentMatchers.eq("morestuff"));
316         Mockito.verify(h1, Mockito.times(1)).parse(ArgumentMatchers.any(), ArgumentMatchers.anyString());
317     }
318 
319     @Test
320     void testParseCookieMaxAgeOverExpires() throws Exception {
321         final CommonCookieAttributeHandler h1 = Mockito.mock(CommonCookieAttributeHandler.class);
322         Mockito.when(h1.getAttributeName()).thenReturn("Expires");
323         final CommonCookieAttributeHandler h2 = Mockito.mock(CommonCookieAttributeHandler.class);
324         Mockito.when(h2.getAttributeName()).thenReturn("Max-Age");
325 
326         final RFC6265CookieSpec cookiespec = new RFC6265CookieSpec(h1, h2);
327 
328         final Header header = new BasicHeader("Set-Cookie", "name = value ; expires = stuff; max-age = otherstuff;");
329         final CookieOrigin origin = new CookieOrigin("host", 80, "/path/", true);
330         cookiespec.parse(header, origin);
331 
332         Mockito.verify(h1, Mockito.never()).parse(ArgumentMatchers.any(), ArgumentMatchers.anyString());
333         Mockito.verify(h2).parse(ArgumentMatchers.any(), ArgumentMatchers.eq("otherstuff"));
334     }
335 
336 }