1   /*
2    * $HeadURL: https://svn.apache.org/repos/asf/httpcomponents/oac.hc3x/trunk/src/test/org/apache/commons/httpclient/cookie/TestCookieRFC2965Spec.java $
3    * $Revision: 1425331 $
4    * $Date: 2012-12-22 19:29:41 +0100 (Sat, 22 Dec 2012) $
5    * ====================================================================
6    *
7    *  Licensed to the Apache Software Foundation (ASF) under one or more
8    *  contributor license agreements.  See the NOTICE file distributed with
9    *  this work for additional information regarding copyright ownership.
10   *  The ASF licenses this file to You under the Apache License, Version 2.0
11   *  (the "License"); you may not use this file except in compliance with
12   *  the License.  You may obtain a copy of the License at
13   *
14   *      http://www.apache.org/licenses/LICENSE-2.0
15   *
16   *  Unless required by applicable law or agreed to in writing, software
17   *  distributed under the License is distributed on an "AS IS" BASIS,
18   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19   *  See the License for the specific language governing permissions and
20   *  limitations under the License.
21   * ====================================================================
22   *
23   * This software consists of voluntary contributions made by many
24   * individuals on behalf of the Apache Software Foundation.  For more
25   * information on the Apache Software Foundation, please see
26   * <http://www.apache.org/>.
27   *
28   */
29  
30  package org.apache.commons.httpclient.cookie;
31  
32  import junit.framework.Test;
33  import junit.framework.TestSuite;
34  
35  import org.apache.commons.httpclient.Cookie;
36  import org.apache.commons.httpclient.Header;
37  
38  import java.util.Date;
39  
40  /***
41   * Test cases for RFC2965 cookie spec
42   *
43   * @author jain.samit@gmail.com (Samit Jain)
44   */
45  public class TestCookieRFC2965Spec extends TestCookieBase {
46  
47      // ------------------------------------------------------------ Constructor
48  
49      public TestCookieRFC2965Spec(String name) {
50          super(name);
51      }
52  
53      // ------------------------------------------------------- TestCase Methods
54  
55      public static Test suite() {
56          return new TestSuite(TestCookieRFC2965Spec.class);
57      }
58  
59  
60      // ------------------------------------------------------- Test Cookie Parsing
61  
62      /***
63       * Test <tt>parse</tt> with invalid params.
64       */
65      public void testParseInvalidParams() throws Exception {
66          CookieSpec cookiespec = new RFC2965Spec();
67          try {
68              // invalid header
69              cookiespec.parse("www.domain.com", 80, "/", false, (Header) null /* header */);
70              fail("IllegalArgumentException must have been thrown");
71          } catch (IllegalArgumentException expected) {}
72  
73          Header header = new Header("Set-Cookie2", "name=value;Version=1");
74          try {
75              // invalid request host
76              cookiespec.parse(null /* host */, 80, "/", false, header);
77              fail("IllegalArgumentException must have been thrown");
78          } catch (IllegalArgumentException expected) {}
79          try {
80              // invalid request port
81              cookiespec.parse("www.domain.com", -32 /* port */, "/", false, header);
82              fail("IllegalArgumentException must have been thrown");
83          } catch (IllegalArgumentException expected) {}
84          try {
85              // invalid request path
86              cookiespec.parse("www.domain.com", 80, null /* path */, false, header);
87              fail("IllegalArgumentException must have been thrown");
88          } catch (IllegalArgumentException expected) {}
89      }
90  
91      /***
92       * Test parsing cookie <tt>"Path"</tt> attribute.
93       */
94      public void testParsePath() throws Exception {
95          CookieSpec cookiespec = new RFC2965Spec();
96          Header header = new Header("Set-Cookie2", "name=value;Path=/;Version=1;Path=");
97          Cookie[] parsed = cookiespec.parse("www.domain.com", 80, "/", false, header);
98          assertNotNull(parsed);
99          assertEquals(1, parsed.length);
100         // only the first occurence of path attribute is considered, others ignored
101         Cookie2 cookie = (Cookie2) parsed[0];
102         assertEquals("/", cookie.getPath());
103         assertTrue(cookie.isPathAttributeSpecified());
104     }
105 
106     public void testParsePathDefault() throws Exception {
107         CookieSpec cookiespec = new RFC2965Spec();
108         // Path is OPTIONAL, defaults to the request path
109         Header header = new Header("Set-Cookie2", "name=value;Version=1");
110         Cookie[] parsed = cookiespec.parse("www.domain.com", 80, "/path" /* request path */, false, header);
111         assertNotNull(parsed);
112         assertEquals(1, parsed.length);
113         Cookie2 cookie = (Cookie2) parsed[0];
114         assertEquals("/path", cookie.getPath());
115         assertFalse(cookie.isPathAttributeSpecified());
116     }
117 
118     public void testParseNullPath() throws Exception {
119         CookieSpec cookiespec = new RFC2965Spec();
120         Header header = new Header("Set-Cookie2", "name=value;Path=;Version=1");
121         try {
122             cookiespec.parse("www.domain.com", 80, "/", false, header);
123             fail("MalformedCookieException should have been thrown");
124         } catch (MalformedCookieException ex) {
125             // expected
126         }
127     }
128 
129     public void testParseBlankPath() throws Exception {
130         CookieSpec cookiespec = new RFC2965Spec();
131         Header header = new Header("Set-Cookie2", "name=value;Path=\"   \";Version=1");
132         try {
133             cookiespec.parse("www.domain.com", 80, "/", false, header);
134             fail("MalformedCookieException should have been thrown");
135         } catch (MalformedCookieException ex) {
136             // expected
137         }
138     }
139     /***
140      * Test parsing cookie <tt>"Domain"</tt> attribute.
141      */
142     public void testParseDomain() throws Exception {
143         CookieSpec cookiespec = new RFC2965Spec();
144         Header header = new Header("Set-Cookie2", "name=value;Domain=.domain.com;Version=1;Domain=");
145         Cookie[] parsed = cookiespec.parse("www.domain.com", 80, "/", false, header);
146         assertNotNull(parsed);
147         assertEquals(1, parsed.length);
148         // only the first occurence of domain attribute is considered, others ignored
149         Cookie2 cookie = (Cookie2) parsed[0];
150         assertEquals(".domain.com", cookie.getDomain());
151         assertTrue(cookie.isDomainAttributeSpecified());
152 
153         // should put a leading dot if there is no dot in front of domain
154         header = new Header("Set-Cookie2", "name=value;Domain=domain.com;Version=1");
155         parsed = cookiespec.parse("www.domain.com", 80, "/", false, header);
156         assertNotNull(parsed);
157         assertEquals(1, parsed.length);
158         cookie = (Cookie2) parsed[0];
159         assertEquals(".domain.com", cookie.getDomain());
160     }
161 
162     public void testParseDomainDefault() throws Exception {
163         CookieSpec cookiespec = new RFC2965Spec();
164         // Domain is OPTIONAL, defaults to the request host
165         Header header = new Header("Set-Cookie2", "name=value;Version=1");
166         Cookie[] parsed = cookiespec.parse("www.domain.com" /* request host */, 80, "/", false, header);
167         assertNotNull(parsed);
168         assertEquals(1, parsed.length);
169         Cookie2 cookie = (Cookie2) parsed[0];
170         assertEquals("www.domain.com", cookie.getDomain());
171         assertFalse(cookie.isDomainAttributeSpecified());
172     }
173 
174     public void testParseNullDomain() throws Exception {
175         CookieSpec cookiespec = new RFC2965Spec();
176         // domain cannot be null
177         Header header = new Header("Set-Cookie2", "name=value;Domain=;Version=1");
178         try {
179             cookiespec.parse("www.domain.com", 80, "/", false, header);
180             fail("MalformedCookieException should have been thrown");
181         } catch (MalformedCookieException ex) {
182             // expected
183         }
184     }
185 
186     public void testParseBlankDomain() throws Exception {
187         CookieSpec cookiespec = new RFC2965Spec();
188         Header header = new Header("Set-Cookie2", "name=value;Domain=\"   \";Version=1");
189         try {
190             cookiespec.parse("www.domain.com", 80, "/", false, header);
191             fail("MalformedCookieException should have been thrown");
192         } catch (MalformedCookieException ex) {
193             // expected
194         }
195     }
196 
197     /***
198      * Test parsing cookie <tt>"Port"</tt> attribute.
199      */
200     public void testParsePort() throws Exception {
201         CookieSpec cookiespec = new RFC2965Spec();
202         Header header = new Header("Set-Cookie2", "name=value;Port=\"80,800,8000\";Version=1;Port=nonsense");
203         Cookie[] parsed = cookiespec.parse("www.domain.com", 80, "/", false, header);
204         assertNotNull(parsed);
205         assertEquals(1, parsed.length);
206         // only the first occurence of port attribute is considered, others ignored
207         Cookie2 cookie = (Cookie2) parsed[0];
208         int[] ports = cookie.getPorts();
209         assertNotNull(ports);
210         assertEquals(3, ports.length);
211         assertEquals(80, ports[0]);
212         assertEquals(800, ports[1]);
213         assertEquals(8000, ports[2]);
214         assertTrue(cookie.isPortAttributeSpecified());
215     }
216 
217     public void testParsePortDefault() throws Exception {
218         CookieSpec cookiespec = new RFC2965Spec();
219         // Port is OPTIONAL, cookie can be accepted from any port
220         Header header = new Header("Set-Cookie2", "name=value;Version=1");
221         Cookie[] parsed = cookiespec.parse("www.domain.com", 80, "/", false, header);
222         assertNotNull(parsed);
223         assertEquals(1, parsed.length);
224         Cookie2 cookie = (Cookie2) parsed[0];
225         assertFalse(cookie.isPortAttributeSpecified());
226     }
227 
228     public void testParseNullPort() throws Exception {
229         CookieSpec cookiespec = new RFC2965Spec();
230         // null port defaults to request port
231         Header header = new Header("Set-Cookie2", "name=value;Port=;Version=1");
232         Cookie[] parsed = cookiespec.parse("www.domain.com", 80 /* request port */, "/", false, header);
233         assertNotNull(parsed);
234         assertEquals(1, parsed.length);
235         Cookie2 cookie = (Cookie2) parsed[0];
236         int[] ports = cookie.getPorts();
237         assertNotNull(ports);
238         assertEquals(1, ports.length);
239         assertEquals(80, ports[0]);
240         assertTrue(cookie.isPortAttributeSpecified() && cookie.isPortAttributeBlank());
241     }
242 
243     public void testParseBlankPort() throws Exception {
244         CookieSpec cookiespec = new RFC2965Spec();
245         // blank port defaults to request port
246         Header header = new Header("Set-Cookie2", "name=value;Port=\"  \";Version=1");
247         Cookie[] parsed = cookiespec.parse("www.domain.com", 80 /* request port */, "/", false, header);
248         assertNotNull(parsed);
249         assertEquals(1, parsed.length);
250         Cookie2 cookie = (Cookie2) parsed[0];
251         int[] ports = cookie.getPorts();
252         assertNotNull(ports);
253         assertEquals(1, ports.length);
254         assertEquals(80, ports[0]);
255         assertTrue(cookie.isPortAttributeSpecified() && cookie.isPortAttributeBlank());
256     }
257 
258     public void testParseInvalidPort() throws Exception {
259         CookieSpec cookiespec = new RFC2965Spec();
260         Header header = new Header("Set-Cookie2", "name=value;Port=nonsense;Version=1");
261         try {
262             cookiespec.parse("www.domain.com", 80, "/", false, header);
263             fail("MalformedCookieException should have been thrown");
264         } catch (MalformedCookieException ex) {
265             // expected
266         }
267     }
268 
269     public void testParseNegativePort() throws Exception {
270         CookieSpec cookiespec = new RFC2965Spec();
271         Header header = new Header("Set-Cookie2", "name=value;Port=\"80,-800,8000\";Version=1");
272         try {
273             cookiespec.parse("www.domain.com", 80, "/", false, header);
274             fail("MalformedCookieException should have been thrown");
275         } catch (MalformedCookieException ex) {
276             // expected
277         }
278     }
279 
280     /***
281      * test parsing cookie name/value.
282      */
283     public void testParseNameValue() throws Exception {
284         CookieSpec cookiespec = new RFC2965Spec();
285         Header header = new Header("Set-Cookie2", "name=value;Version=1;");
286         Cookie[] parsed = cookiespec.parse("www.domain.com", 80, "/", false, header);
287         assertNotNull(parsed);
288         assertEquals(1, parsed.length);
289         Cookie2 cookie = (Cookie2) parsed[0];
290         assertEquals("name", cookie.getName());
291         assertEquals("value", cookie.getValue());
292     }
293 
294     /***
295      * test parsing cookie <tt>"Version"</tt> attribute.
296      */
297     public void testParseVersion() throws Exception {
298         CookieSpec cookiespec = new RFC2965Spec();
299         Header header = new Header("Set-Cookie2", "name=value;Version=1;");
300         Cookie[] parsed = cookiespec.parse("www.domain.com", 80, "/", false, header);
301         assertNotNull(parsed);
302         assertEquals(1, parsed.length);
303         Cookie2 cookie = (Cookie2) parsed[0];
304         assertEquals(1, cookie.getVersion());
305         assertTrue(cookie.isVersionAttributeSpecified());
306     }
307 
308     public void testParseNullVersion() throws Exception {
309         CookieSpec cookiespec = new RFC2965Spec();
310         // version cannot ne null
311         Header header = new Header("Set-Cookie2", "name=value;Version=;");
312         try {
313             cookiespec.parse("www.domain.com", 80, "/", false, header);
314             fail("MalformedCookieException should have been thrown");
315         } catch (MalformedCookieException ex) {
316             // expected
317         }
318     }
319     
320     public void testParseNegativeVersion() throws Exception {
321         CookieSpec cookiespec = new RFC2965Spec();
322         Header header = new Header("Set-Cookie2", "name=value;Version=-1;");
323         try {
324             cookiespec.parse("www.domain.com", 80, "/", false, header);
325             fail("MalformedCookieException should have been thrown");
326         } catch (MalformedCookieException ex) {
327             // expected
328         }
329     }
330     /***
331      * test parsing cookie <tt>"Max-age"</tt> attribute.
332      */
333     public void testParseMaxage() throws Exception {
334         CookieSpec cookiespec = new RFC2965Spec();
335         Header header = new Header("Set-Cookie2", "name=value;Max-age=3600;Version=1;Max-age=nonsense");
336         Cookie[] parsed = cookiespec.parse("www.domain.com", 80, "/", false, header);
337         assertNotNull(parsed);
338         assertEquals(1, parsed.length);
339         // only the first occurence of max-age attribute is considered, others ignored
340         Cookie2 cookie = (Cookie2) parsed[0];
341         assertFalse(cookie.isExpired());
342     }
343 
344     public void testParseMaxageDefault() throws Exception {
345         CookieSpec cookiespec = new RFC2965Spec();
346         // Max-age is OPTIONAL, defaults to session cookie
347         Header header = new Header("Set-Cookie2", "name=value;Version=1");
348         Cookie[] parsed = cookiespec.parse("www.domain.com", 80, "/", false, header);
349         assertNotNull(parsed);
350         assertEquals(1, parsed.length);
351         Cookie2 cookie = (Cookie2) parsed[0];
352         assertFalse(cookie.isPersistent());
353     }
354 
355     public void testParseNullMaxage() throws Exception {
356         CookieSpec cookiespec = new RFC2965Spec();
357         Header header = new Header("Set-Cookie2", "name=value;Max-age=;Version=1");
358         try {
359             cookiespec.parse("www.domain.com", 80, "/", false, header);
360             fail("MalformedCookieException should have been thrown");
361         } catch (MalformedCookieException ex) {
362             // expected
363         }
364     }
365 
366     public void testParseNegativeMaxage() throws Exception {
367         CookieSpec cookiespec = new RFC2965Spec();
368         Header header = new Header("Set-Cookie2", "name=value;Max-age=-3600;Version=1;");
369         try {
370             cookiespec.parse("www.domain.com", 80, "/", false, header);
371             fail("MalformedCookieException should have been thrown");
372         } catch (MalformedCookieException ex) {
373             // expected
374         }
375     }
376 
377     /***
378      * test parsing <tt>"Secure"</tt> attribute.
379      */
380     public void testParseSecure() throws Exception {
381         CookieSpec cookiespec = new RFC2965Spec();
382         Header header = new Header("Set-Cookie2", "name=value;Secure;Version=1");
383         Cookie[] parsed = cookiespec.parse("www.domain.com", 80, "/", false, header);
384         assertNotNull(parsed);
385         assertEquals(1, parsed.length);
386         Cookie2 cookie = (Cookie2) parsed[0];
387         assertTrue(cookie.getSecure());
388     }
389 
390     /***
391      * test parsing <tt>"Discard"</tt> attribute.
392      */
393     public void testParseDiscard() throws Exception {
394         CookieSpec cookiespec = new RFC2965Spec();
395         Header header = new Header("Set-Cookie2", "name=value;Discard;Max-age=36000;Version=1");
396         Cookie[] parsed = cookiespec.parse("www.domain.com", 80, "/", false, header);
397         assertNotNull(parsed);
398         assertEquals(1, parsed.length);
399         Cookie2 cookie = (Cookie2) parsed[0];
400         // discard overrides max-age
401         assertFalse(cookie.isPersistent());
402 
403         // Discard is OPTIONAL, default behavior is dictated by max-age
404         header = new Header("Set-Cookie2", "name=value;Max-age=36000;Version=1");
405         parsed = cookiespec.parse("www.domain.com", 80, "/", false, header);
406         assertNotNull(parsed);
407         assertEquals(1, parsed.length);
408         cookie = (Cookie2) parsed[0];
409         assertTrue(cookie.isPersistent());
410     }
411 
412     /***
413      * test parsing <tt>"Comment"</tt>, <tt>"CommentURL"</tt> and
414      * <tt>"Secure"</tt> attributes.
415      */
416     public void testParseOtherAttributes() throws Exception {
417         CookieSpec cookiespec = new RFC2965Spec();
418         Header header = new Header("Set-Cookie2", "name=value;Comment=\"good cookie\";" +
419                 "CommentURL=\"www.domain.com/goodcookie/\";Secure;Version=1");
420         Cookie[] parsed = cookiespec.parse("www.domain.com", 80, "/", false, header);
421         assertNotNull(parsed);
422         assertEquals(1, parsed.length);
423         Cookie2 cookie = (Cookie2) parsed[0];
424         assertEquals("good cookie", cookie.getComment());
425         assertEquals("www.domain.com/goodcookie/", cookie.getCommentURL());
426         assertTrue(cookie.getSecure());
427 
428         // Comment, CommentURL, Secure are OPTIONAL
429         header = new Header("Set-Cookie2", "name=value;Version=1");
430         parsed = cookiespec.parse("www.domain.com", 80, "/", false, header);
431         assertNotNull(parsed);
432         assertEquals(1, parsed.length);
433         cookie = (Cookie2) parsed[0];
434         assertFalse(cookie.getSecure());
435     }
436 
437     /***
438      * Test parsing header with 2 cookies (separated by comma)
439      */
440     public void testCookiesWithComma() throws Exception {
441         CookieSpec cookiespec = new RFC2965Spec();
442         Header header = new Header("Set-Cookie2", "a=b,c");
443         Cookie[] parsed = cookiespec.parse("www.domain.com", 80, "/", false, header);
444         assertNotNull(parsed);
445         assertEquals(2, parsed.length);
446         assertEquals("a", parsed[0].getName());
447         assertEquals("b", parsed[0].getValue());
448         assertEquals("c", parsed[1].getName());
449         assertEquals(null, parsed[1].getValue());
450     }
451 
452     // ------------------------------------------------------- Test Cookie Validation
453 
454     /***
455      * Test <tt>Domain</tt> validation when domain is not specified
456      * in <tt>Set-Cookie2</tt> header.
457      */
458     public void testValidateNoDomain() throws Exception {
459         CookieSpec cookiespec = new RFC2965Spec();
460         Header header = new Header("Set-Cookie2", "name=value;Version=1");
461         Cookie[] parsed = cookieParse(cookiespec, "www.domain.com" /* request host */, 80, "/", false, header);
462         assertNotNull(parsed);
463         assertEquals(1, parsed.length);
464         Cookie2 cookie = (Cookie2) parsed[0];
465         // cookie domain must string match request host
466         assertEquals("www.domain.com", cookie.getDomain());
467     }
468 
469     /***
470      * Test <tt>Domain</tt> validation. Cookie domain attribute must have a
471      * leading dot.
472      */
473     public void testValidateDomainLeadingDot() throws Exception {
474         CookieSpec cookiespec = new RFC2965Spec();
475         Header header = new Header("Set-Cookie2", "name=value;Domain=domain.com;Version=1");
476         Cookie[] parsed = cookieParse(cookiespec, "www.domain.com", 80, "/", false, header);
477         assertNotNull(parsed);
478         assertEquals(1, parsed.length);
479         Cookie2 cookie = (Cookie2) parsed[0];
480         assertEquals(".domain.com", cookie.getDomain());
481     }
482 
483     /***
484      * Test <tt>Domain</tt> validation. Domain must have atleast one embedded dot.
485      */
486     public void testValidateDomainEmbeddedDot() throws Exception {
487         CookieSpec cookiespec = new RFC2965Spec();
488         Header header = new Header("Set-Cookie2", "name=value; domain=.com; version=1");
489         try {
490             cookieParse(cookiespec, "b.com", 80, "/", false, header);
491             fail("MalformedCookieException should have been thrown");
492         } catch (MalformedCookieException expected) {}
493 
494         header = new Header("Set-Cookie2", "name=value;Domain=domain.com;Version=1");
495         Cookie[] parsed = cookieParse(cookiespec, "www.domain.com", 80, "/", false, header);
496         assertNotNull(parsed);
497         assertEquals(1, parsed.length);
498     }
499 
500     /***
501      * Test local <tt>Domain</tt> validation. Simple host names
502      * (without any dots) are valid only when cookie domain is specified
503      * as ".local".
504      */
505     public void testValidateDomainLocal() throws Exception {
506         CookieSpec cookiespec = new RFC2965Spec();
507         // when domain is specified as .local, simple host names are valid
508         Header header = new Header("Set-Cookie2", "name=value; domain=.local; version=1");
509         Cookie[] parsed = cookieParse(cookiespec, "simplehost" /* request host */, 80, "/", false, header);
510         assertNotNull(parsed);
511         assertEquals(1, parsed.length);
512         Cookie2 cookie = (Cookie2) parsed[0];
513         assertEquals(".local", cookie.getDomain());
514 
515         // when domain is NOT specified as .local, simple host names are invalid
516         header = new Header("Set-Cookie2", "name=value; domain=domain.com; version=1");
517         try {
518             // since domain is not .local, this must fail
519             parsed = cookieParse(cookiespec, "simplehost" /* request host */, 80, "/", false, header);
520             fail("MalformedCookieException should have been thrown");
521         } catch (MalformedCookieException expected) {}
522     }
523 
524 
525     /***
526      * Test <tt>Domain</tt> validation. Effective host name
527      * must domain-match domain attribute.
528      */
529     public void testValidateDomainEffectiveHost() throws Exception {
530         CookieSpec cookiespec = new RFC2965Spec();
531 
532         // cookie domain does not domain-match request host
533         Header header = new Header("Set-Cookie2", "name=value; domain=.domain.com; version=1");
534         try {
535             cookieParse(cookiespec, "www.domain.org" /* request host */, 80, "/", false, header);
536             fail("MalformedCookieException should have been thrown");
537         } catch (MalformedCookieException expected) {}
538 
539         // cookie domain domain-matches request host
540         header = new Header("Set-Cookie2", "name=value; domain=.domain.com; version=1");
541         Cookie[] parsed = cookieParse(cookiespec, "www.domain.com" /* request host */, 80, "/", false, header);
542         assertNotNull(parsed);
543         assertEquals(1, parsed.length);
544     }
545 
546     /***
547      * Test local <tt>Domain</tt> validation.
548      * Effective host name minus domain must not contain any dots.
549      */
550     public void testValidateDomainIllegal() throws Exception {
551         CookieSpec cookiespec = new RFC2965Spec();
552         Header header = new Header("Set-Cookie2", "name=value; domain=.domain.com; version=1");
553         try {
554             cookieParse(cookiespec, "a.b.domain.com" /* request host */, 80, "/", false, header);
555             fail("MalformedCookieException should have been thrown");
556         } catch (MalformedCookieException expected) {}
557     }
558 
559     /***
560      * Test cookie <tt>Path</tt> validation. Cookie path attribute must path-match
561      * request path.
562      */
563     public void testValidatePath() throws Exception {
564         CookieSpec cookiespec = new RFC2965Spec();
565         Header header = new Header("Set-Cookie2", "name=value;path=/path;version=1");
566         try {
567             cookieParse(cookiespec, "www.domain.com", 80, "/" /* request path */, false, header);
568             fail("MalformedCookieException exception should have been thrown");
569         } catch (MalformedCookieException expected) {}
570 
571         // path-matching is case-sensitive
572         header = new Header("Set-Cookie2", "name=value;path=/Path;version=1");
573         try {
574             cookieParse(cookiespec, "www.domain.com", 80, "/path" /* request path */, false, header);
575             fail("MalformedCookieException exception should have been thrown");
576         } catch (MalformedCookieException expected) {}
577 
578         header = new Header("Set-Cookie2", "name=value;path=/path;version=1");
579         Cookie[] parsed = cookieParse(cookiespec, "www.domain.com",
580                                       80, "/path/path1" /* request path */, false, header);
581         assertNotNull(parsed);
582         assertEquals(1, parsed.length);
583         assertEquals("/path", parsed[0].getPath());
584     }
585 
586     /***
587      * Test cookie name validation.
588      */
589     public void testValidateCookieName() throws Exception {
590         CookieSpec cookiespec = new RFC2965Spec();
591         // cookie name must not contain blanks
592         Header header = new Header("Set-Cookie2", "invalid name=value; version=1");
593         try {
594             cookieParse(cookiespec, "127.0.0.1", 80, "/", false, header);
595             fail("MalformedCookieException exception should have been thrown");
596         } catch (MalformedCookieException expected) {}
597 
598         // cookie name must not start with '$'.
599         header = new Header("Set-Cookie2", "$invalid_name=value; version=1");
600         try {
601             cookieParse(cookiespec, "127.0.0.1", 80, "/", false, header);
602             fail("MalformedCookieException exception should have been thrown");
603         } catch (MalformedCookieException expected) {}
604 
605         // valid name
606         header = new Header("Set-Cookie2", "name=value; version=1");
607         Cookie[] parsed = cookieParse(cookiespec, "www.domain.com", 80, "/", false, header);
608         assertNotNull(parsed);
609         assertEquals(1, parsed.length);
610         Cookie2 cookie = (Cookie2) parsed[0];
611         assertEquals("name", cookie.getName());
612         assertEquals("value", cookie.getValue());
613     }
614 
615     /***
616      * Test cookie <tt>Port</tt> validation. Request port must be in the
617      * port attribute list.
618      */
619     public void testValidatePort() throws Exception {
620         Header header = new Header("Set-Cookie2", "name=value; Port=\"80,800\"; version=1");
621         CookieSpec cookiespec = new RFC2965Spec();
622         try {
623             cookieParse(cookiespec, "www.domain.com", 8000 /* request port */, "/", false, header);
624             fail("MalformedCookieException should have been thrown");
625         } catch (MalformedCookieException e) {}
626 
627         // valid port list
628         Cookie[] parsed = cookieParse(cookiespec, "www.domain.com", 80 /* request port */, "/", false, header);
629         assertNotNull(parsed);
630         assertEquals(1, parsed.length);
631         Cookie2 cookie = (Cookie2) parsed[0];
632         int[] ports = cookie.getPorts();
633         assertNotNull(ports);
634         assertEquals(2, ports.length);
635         assertEquals(80, ports[0]);
636         assertEquals(800, ports[1]);
637     }
638 
639     /***
640      * Test cookie <tt>Version</tt> validation.
641      */
642     public void testValidateVersion() throws Exception {
643         CookieSpec cookiespec = new RFC2965Spec();
644         // version attribute is REQUIRED
645         Header header = new Header("Set-Cookie2", "name=value");
646         try {
647             cookieParse(cookiespec, "www.domain.com", 8000, "/", false, header);
648             fail("MalformedCookieException should have been thrown");
649         } catch (MalformedCookieException e) {}
650     }
651 
652     // ------------------------------------------------------- Test Cookie Matching
653 
654     /***
655      * test cookie <tt>Path</tt> matching. Cookie path attribute must path-match
656      * path of the request URI.
657      */
658     public void testMatchPath() throws Exception {
659         Cookie2 cookie = new Cookie2(".domain.com", "name",
660                                      "value", "/path" /* path */, null, false, new int[] {80});
661         CookieSpec cookiespec = new RFC2965Spec();
662         assertFalse(cookiespec.match("www.domain.com", 80, "/" /* request path */, false, cookie));
663         assertTrue(cookiespec.match("www.domain.com", 80, "/path/path1" /* request path */, false, cookie));
664     }
665 
666     /***
667      * test cookie <tt>Domain</tt> matching.
668      */
669     public void testMatchDomain() throws Exception {
670         Cookie2 cookie = new Cookie2(".domain.com" /* domain */, "name",
671                                      "value", "/", null, false, new int[] {80});
672         CookieSpec cookiespec = new RFC2965Spec();
673         // effective host name minus domain must not contain any dots
674         assertFalse(cookiespec.match("a.b.domain.com" /* request host */, 80, "/", false, cookie));
675         // The effective host name MUST domain-match the Domain
676         // attribute of the cookie.
677         assertFalse(cookiespec.match("www.domain.org" /* request host */, 80, "/", false, cookie));
678         assertTrue(cookiespec.match("www.domain.com" /* request host */, 80, "/", false, cookie));
679     }
680 
681     /***
682      * test cookie local <tt>Domain</tt> matching.
683      */
684     public void testMatchDomainLocal() throws Exception {
685         Cookie2 cookie = new Cookie2(".local" /* domain */, "name",
686                                      "value", "/", null, false, new int[] {80});
687         CookieSpec cookiespec = new RFC2965Spec();
688         assertTrue(cookiespec.match("host" /* request host */, 80, "/", false, cookie));
689         assertFalse(cookiespec.match("host.com" /* request host */, 80, "/", false, cookie));
690     }
691 
692     /***
693      * test cookie <tt>Port</tt> matching.
694      */
695     public void testMatchPort() throws Exception {
696         // cookie can be sent to any port if port attribute not specified
697         Cookie2 cookie = new Cookie2(".domain.com", "name",
698                                      "value", "/", null, false, null /* ports */);
699         CookieSpec cookiespec = new RFC2965Spec();
700         cookie.setPortAttributeSpecified(false);
701         assertTrue(cookiespec.match("www.domain.com", 8080 /* request port */, "/", false, cookie));
702         assertTrue(cookiespec.match("www.domain.com", 323  /* request port */, "/", false, cookie));
703 
704         // otherwise, request port must be in cookie's port list
705         cookie = new Cookie2(".domain.com", "name",
706                              "value", "/", null, false, new int[] {80, 8080} /* ports */);
707         cookie.setPortAttributeSpecified(true);
708         assertFalse(cookiespec.match("www.domain.com", 434 /* request port */, "/", false, cookie));
709         assertTrue(cookiespec.match("www.domain.com", 8080 /* request port */, "/", false, cookie));
710     }
711 
712     /***
713      * test cookie expiration.
714      */
715     public void testCookieExpiration() throws Exception {
716         Date afterOneHour = new Date(System.currentTimeMillis() + 3600 * 1000L);
717         Cookie2 cookie = new Cookie2(".domain.com", "name",
718                                      "value", "/", afterOneHour /* expiry */, false, null);
719         CookieSpec cookiespec = new RFC2965Spec();
720         assertTrue(cookiespec.match("www.domain.com", 80, "/", false, cookie));
721 
722         Date beforeOneHour = new Date(System.currentTimeMillis() - 3600 * 1000L);
723         cookie = new Cookie2(".domain.com", "name",
724                              "value", "/", beforeOneHour /* expiry */, false, null);
725         assertFalse(cookiespec.match("www.domain.com", 80, "/", false, cookie));
726 
727         // discard attributes overrides cookie age, makes it a session cookie.
728         cookie.setDiscard(true);
729         assertFalse(cookie.isPersistent());
730         assertTrue(cookiespec.match("www.domain.com", 80, "/", false, cookie));
731     }
732 
733     /***
734      * test cookie <tt>Secure</tt> attribute.
735      */
736     public void testCookieSecure() throws Exception {
737         CookieSpec cookiespec = new RFC2965Spec();
738         // secure cookie can only be sent over a secure connection
739         Cookie2 cookie = new Cookie2(".domain.com", "name",
740                                      "value", "/", null, true /* secure */, null);
741         assertFalse(cookiespec.match("www.domain.com", 80, "/", false /* request secure */, cookie));
742         assertTrue(cookiespec.match("www.domain.com", 80, "/", true /* request secure */, cookie));
743     }
744 
745     // ------------------------------------------------------- Test Cookie Formatting
746 
747     public void testFormatInvalidCookie() throws Exception {
748         CookieSpec cookiespec = new RFC2965Spec();
749         try {
750             cookiespec.formatCookie(null);
751             fail("IllegalArgumentException nust have been thrown");
752         } catch (IllegalArgumentException expected) {}
753     }
754 
755     /***
756      * Tests RFC 2965 compliant cookie formatting.
757      */
758     public void testRFC2965CookieFormatting() throws Exception {
759         CookieSpec cookiespec = new RFC2965Spec();
760         Cookie2 cookie1 = new Cookie2(".domain.com", "name1",
761                                      "value", "/", null, false, new int[] {80,8080});
762         cookie1.setVersion(1);
763         // domain, path, port specified
764         cookie1.setDomainAttributeSpecified(true);
765         cookie1.setPathAttributeSpecified(true);
766         cookie1.setPortAttributeSpecified(true);
767         assertEquals("$Version=\"1\"; name1=\"value\"; $Domain=\".domain.com\"; $Path=\"/\"; $Port=\"80,8080\"",
768                      cookiespec.formatCookie(cookie1));
769 
770         Cookie2 cookie2 = new Cookie2(".domain.com", "name2",
771                 "value", "/a/", null, false, new int[] {80,8080});
772         cookie2.setVersion(2);
773         // domain, path specified  but port unspecified
774         cookie2.setDomainAttributeSpecified(true);
775         cookie2.setPathAttributeSpecified(true);
776         cookie2.setPortAttributeSpecified(false);
777         assertEquals("$Version=\"2\"; name2=\"value\"; $Domain=\".domain.com\"; $Path=\"/a/\"",
778                      cookiespec.formatCookie(cookie2));
779 
780         Cookie2 cookie3 = new Cookie2(".domain.com", "name3",
781                 "value", "/a/b/", null, false, new int[] {80,8080});
782         cookie3.setVersion(1);
783         // path specified, port specified but blank, domain unspecified
784         cookie3.setDomainAttributeSpecified(false);
785         cookie3.setPathAttributeSpecified(true);
786         cookie3.setPortAttributeSpecified(true);
787         cookie3.setPortAttributeBlank(true);
788         assertEquals("$Version=\"1\"; name3=\"value\"; $Path=\"/a/b/\"; $Port=\"\"",
789                      cookiespec.formatCookie(cookie3));
790 
791         assertEquals("$Version=\"2\"; " +
792                 "name3=\"value\"; $Path=\"/a/b/\"; $Port=\"\"; " +
793                 "name2=\"value\"; $Domain=\".domain.com\"; $Path=\"/a/\"; " +
794                 "name1=\"value\"; $Domain=\".domain.com\"; $Path=\"/\"; $Port=\"80,8080\"",
795                 cookiespec.formatCookies(new Cookie[] {cookie3, cookie2, cookie1}));
796     }
797 
798     /***
799      * Tests RFC 2965 compliant cookies formatting.
800      */
801     public void testRFC2965CookiesFormatting() throws Exception {
802         CookieSpec cookiespec = new RFC2965Spec();
803         Cookie2 cookie1 = new Cookie2(".domain.com", "name1",
804                                       "value1", "/", null, false, new int[] {80,8080});
805         cookie1.setVersion(1);
806         // domain, path, port specified
807         cookie1.setDomainAttributeSpecified(true);
808         cookie1.setPathAttributeSpecified(true);
809         cookie1.setPortAttributeSpecified(true);
810         Cookie2 cookie2 = new Cookie2(".domain.com", "name2",
811                                       null, "/", null, false, null);
812         cookie2.setVersion(1);
813         // value null, domain, path, port specified
814         cookie2.setDomainAttributeSpecified(true);
815         cookie2.setPathAttributeSpecified(true);
816         cookie2.setPortAttributeSpecified(false);
817         Cookie[] cookies = new Cookie[] {cookie1, cookie2};
818         assertEquals("$Version=\"1\"; name1=\"value1\"; $Domain=\".domain.com\"; $Path=\"/\"; $Port=\"80,8080\"; " +
819             "name2=\"\"; $Domain=\".domain.com\"; $Path=\"/\"", cookiespec.formatCookies(cookies));
820     }
821 
822     // ------------------------------------------------------- Backward compatibility tests
823 
824     /***
825      * Test backward compatibility with <tt>Set-Cookie</tt> header.
826      */
827     public void testCompatibilityWithSetCookie() throws Exception {
828         CookieSpec cookiespec = new RFC2965Spec();
829         Header header = new Header("Set-Cookie", "name=value; domain=.domain.com; version=1");
830         Cookie[] parsed = cookieParse(cookiespec, "www.domain.com", 80, "/", false, header);
831         assertNotNull(parsed);
832         assertEquals(1, parsed.length);
833         assertEquals("name", parsed[0].getName());
834         assertEquals("value", parsed[0].getValue());
835         assertEquals(".domain.com", parsed[0].getDomain());
836         assertEquals("/", parsed[0].getPath());
837     }
838 
839 }
840