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