    1   /*
    2    * $Header: $
    3    * $Revision: 480424 $
    4    * $Date: 2006-11-29 06:56:49 +0100 (Wed, 29 Nov 2006) $
    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