View Javadoc

    1   /*
    2    * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/cookie/CookieSpecBase.java,v 1.28 2004/11/06 19:15:42 mbecke Exp $
    3    * $Revision: 5562 $
    4    * $Date: 2007-11-16 00:53:10 +0000 (Fri, 16 Nov 2007) $
    5    *
    6    * ====================================================================
    7    *
    8    *  Copyright 2002-2004 The Apache Software Foundation
    9    *
    10   *  Licensed under the Apache License, Version 2.0 (the "License");
    11   *  you may not use this file except in compliance with the License.
    12   *  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 java.util.Collection;
    33  import java.util.Date;
    34  import java.util.Iterator; // <- IA/HERITRIX CHANGE
    35  import java.util.LinkedList;
    36  import java.util.List;
    37  import java.util.SortedMap; // <- IA/HERITRIX CHANGE
    38
    39  import org.apache.commons.httpclient.Cookie;
    40  import org.apache.commons.httpclient.Header;
    41  import org.apache.commons.httpclient.HeaderElement;
    42  import org.apache.commons.httpclient.NameValuePair;
    43  import org.apache.commons.httpclient.util.DateParseException;
    44  import org.apache.commons.httpclient.util.DateUtil;
    45  import org.apache.commons.logging.Log;
    46  import org.apache.commons.logging.LogFactory;
    47
    48  import com.sleepycat.collections.StoredIterator; // <- IA/HERITRIX CHANGE
    49
    50  /***
    51   *
    52   * Cookie management functions shared by all specification.
    53   *
    54   * @author  B.C. Holmes
    55   * @author <a href="mailto:jericho@thinkfree.com">Park, Sung-Gu</a>
    56   * @author <a href="mailto:dsale@us.britannica.com">Doug Sale</a>
    57   * @author Rod Waldhoff
    58   * @author dIon Gillard
    59   * @author Sean C. Sullivan
    60   * @author <a href="mailto:JEvans@Cyveillance.com">John Evans</a>
    61   * @author Marc A. Saegesser
    62   * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
    63   * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
    64   *
    65   * @since 2.0
    66   */
    67  @SuppressWarnings("unchecked") // <- IA/HERITRIX CHANGE
    68  public class CookieSpecBase implements CookieSpec {
    69
    70      /*** Log object */
    71      protected static final Log LOG = LogFactory.getLog(CookieSpec.class);
    72
    73      /*** Valid date patterns */
    74      private Collection datepatterns = null;
    75
    76      /*** Default constructor */
    77      public CookieSpecBase() {
    78          super();
    79      }
    80
    81
    82      /***
    83        * Parses the Set-Cookie value into an array of <tt>Cookie</tt>s.
    84        *
    85        * <P>The syntax for the Set-Cookie response header is:
    86        *
    87        * <PRE>
    88        * set-cookie      =    "Set-Cookie:" cookies
    89        * cookies         =    1#cookie
    90        * cookie          =    NAME "=" VALUE * (";" cookie-av)
    91        * NAME            =    attr
    92        * VALUE           =    value
    93        * cookie-av       =    "Comment" "=" value
    94        *                 |    "Domain" "=" value
    95        *                 |    "Max-Age" "=" value
    96        *                 |    "Path" "=" value
    97        *                 |    "Secure"
    98        *                 |    "Version" "=" 1*DIGIT
    99        * </PRE>
    100       *
    101       * @param host the host from which the <tt>Set-Cookie</tt> value was
    102       * received
    103       * @param port the port from which the <tt>Set-Cookie</tt> value was
    104       * received
    105       * @param path the path from which the <tt>Set-Cookie</tt> value was
    106       * received
    107       * @param secure <tt>true</tt> when the <tt>Set-Cookie</tt> value was
    108       * received over secure conection
    109       * @param header the <tt>Set-Cookie</tt> received from the server
    110       * @return an array of <tt>Cookie</tt>s parsed from the Set-Cookie value
    111       * @throws MalformedCookieException if an exception occurs during parsing
    112       */
    113     public Cookie[] parse(String host, int port, String path,
    114         boolean secure, final String header)
    115         throws MalformedCookieException {
    116
    117         LOG.trace("enter CookieSpecBase.parse("
    118             + "String, port, path, boolean, Header)");
    119
    120         if (host == null) {
    121             throw new IllegalArgumentException(
    122                 "Host of origin may not be null");
    123         }
    124         if (host.trim().equals("")) {
    125             throw new IllegalArgumentException(
    126                 "Host of origin may not be blank");
    127         }
    128         if (port < 0) {
    129             throw new IllegalArgumentException("Invalid port: " + port);
    130         }
    131         if (path == null) {
    132             throw new IllegalArgumentException(
    133                 "Path of origin may not be null.");
    134         }
    135         if (header == null) {
    136             throw new IllegalArgumentException("Header may not be null.");
    137         }
    138
    139         if (path.trim().equals("")) {
    140             path = PATH_DELIM;
    141         }
    142         host = host.toLowerCase();
    143
    144         String defaultPath = path;
    145         int lastSlashIndex = defaultPath.lastIndexOf(PATH_DELIM);
    146         if (lastSlashIndex >= 0) {
    147             if (lastSlashIndex == 0) {
    148                 //Do not remove the very first slash
    149                 lastSlashIndex = 1;
    150             }
    151             defaultPath = defaultPath.substring(0, lastSlashIndex);
    152         }
    153
    154         HeaderElement[] headerElements = null;
    155
    156         boolean isNetscapeCookie = false;
    157         int i1 = header.toLowerCase().indexOf("expires=");
    158         if (i1 != -1) {
    159             i1 += "expires=".length();
    160             int i2 = header.indexOf(";", i1);
    161             if (i2 == -1) {
    162                 i2 = header.length();
    163             }
    164             try {
    165                 DateUtil.parseDate(header.substring(i1, i2), this.datepatterns);
    166                 isNetscapeCookie = true;
    167             } catch (DateParseException e) {
    168                 // Does not look like a valid expiry date
    169             }
    170         }
    171         if (isNetscapeCookie) {
    172             headerElements = new HeaderElement[] {
    173                     new HeaderElement(header.toCharArray())
    174             };
    175         } else {
    176             headerElements = HeaderElement.parseElements(header.toCharArray());
    177         }
    178
    179         Cookie[] cookies = new Cookie[headerElements.length];
    180
    181         for (int i = 0; i < headerElements.length; i++) {
    182
    183             HeaderElement headerelement = headerElements[i];
    184             Cookie cookie = null;
    185             try {
    186                 cookie = new Cookie(host,
    187                                     headerelement.getName(),
    188                                     headerelement.getValue(),
    189                                     defaultPath,
    190                                     null,
    191                                     false);
    192             } catch (IllegalArgumentException e) {
    193                 throw new MalformedCookieException(e.getMessage());
    194             }
    195             // cycle through the parameters
    196             NameValuePair[] parameters = headerelement.getParameters();
    197             // could be null. In case only a header element and no parameters.
    198             if (parameters != null) {
    199
    200                 for (int j = 0; j < parameters.length; j++) {
    201                     parseAttribute(parameters[j], cookie);
    202                 }
    203             }
    204             cookies[i] = cookie;
    205         }
    206         return cookies;
    207     }
    208
    209
    210     /***
    211       * Parse the <tt>"Set-Cookie"</tt> {@link Header} into an array of {@link
    212       * Cookie}s.
    213       *
    214       * <P>The syntax for the Set-Cookie response header is:
    215       *
    216       * <PRE>
    217       * set-cookie      =    "Set-Cookie:" cookies
    218       * cookies         =    1#cookie
    219       * cookie          =    NAME "=" VALUE * (";" cookie-av)
    220       * NAME            =    attr
    221       * VALUE           =    value
    222       * cookie-av       =    "Comment" "=" value
    223       *                 |    "Domain" "=" value
    224       *                 |    "Max-Age" "=" value
    225       *                 |    "Path" "=" value
    226       *                 |    "Secure"
    227       *                 |    "Version" "=" 1*DIGIT
    228       * </PRE>
    229       *
    230       * @param host the host from which the <tt>Set-Cookie</tt> header was
    231       * received
    232       * @param port the port from which the <tt>Set-Cookie</tt> header was
    233       * received
    234       * @param path the path from which the <tt>Set-Cookie</tt> header was
    235       * received
    236       * @param secure <tt>true</tt> when the <tt>Set-Cookie</tt> header was
    237       * received over secure conection
    238       * @param header the <tt>Set-Cookie</tt> received from the server
    239       * @return an array of <tt>Cookie</tt>s parsed from the <tt>"Set-Cookie"
    240       * </tt> header
    241       * @throws MalformedCookieException if an exception occurs during parsing
    242       */
    243     public Cookie[] parse(
    244         String host, int port, String path, boolean secure, final Header header)
    245         throws MalformedCookieException {
    246
    247         LOG.trace("enter CookieSpecBase.parse("
    248             + "String, port, path, boolean, String)");
    249         if (header == null) {
    250             throw new IllegalArgumentException("Header may not be null.");
    251         }
    252         return parse(host, port, path, secure, header.getValue());
    253     }
    254
    255
    256     /***
    257       * Parse the cookie attribute and update the corresponsing {@link Cookie}
    258       * properties.
    259       *
    260       * @param attribute {@link HeaderElement} cookie attribute from the
    261       * <tt>Set- Cookie</tt>
    262       * @param cookie {@link Cookie} to be updated
    263       * @throws MalformedCookieException if an exception occurs during parsing
    264       */
    265
    266     public void parseAttribute(
    267         final NameValuePair attribute, final Cookie cookie)
    268         throws MalformedCookieException {
    269
    270         if (attribute == null) {
    271             throw new IllegalArgumentException("Attribute may not be null.");
    272         }
    273         if (cookie == null) {
    274             throw new IllegalArgumentException("Cookie may not be null.");
    275         }
    276         final String paramName = attribute.getName().toLowerCase();
    277         String paramValue = attribute.getValue();
    278
    279         if (paramName.equals("path")) {
    280
    281             if ((paramValue == null) || (paramValue.trim().equals(""))) {
    282                 paramValue = "/";
    283             }
    284             cookie.setPath(paramValue);
    285             cookie.setPathAttributeSpecified(true);
    286
    287         } else if (paramName.equals("domain")) {
    288
    289             if (paramValue == null) {
    290                 throw new MalformedCookieException(
    291                     "Missing value for domain attribute");
    292             }
    293             if (paramValue.trim().equals("")) {
    294                 throw new MalformedCookieException(
    295                     "Blank value for domain attribute");
    296             }
    297             cookie.setDomain(paramValue);
    298             cookie.setDomainAttributeSpecified(true);
    299
    300         } else if (paramName.equals("max-age")) {
    301
    302             if (paramValue == null) {
    303                 throw new MalformedCookieException(
    304                     "Missing value for max-age attribute");
    305             }
    306             int age;
    307             try {
    308                 age = Integer.parseInt(paramValue);
    309             } catch (NumberFormatException e) {
    310                 throw new MalformedCookieException ("Invalid max-age "
    311                     + "attribute: " + e.getMessage());
    312             }
    313             cookie.setExpiryDate(
    314                 new Date(System.currentTimeMillis() + age * 1000L));
    315
    316         } else if (paramName.equals("secure")) {
    317
    318             cookie.setSecure(true);
    319
    320         } else if (paramName.equals("comment")) {
    321
    322             cookie.setComment(paramValue);
    323
    324         } else if (paramName.equals("expires")) {
    325
    326             if (paramValue == null) {
    327                 throw new MalformedCookieException(
    328                     "Missing value for expires attribute");
    329             }
    330
    331             try {
    332                 cookie.setExpiryDate(DateUtil.parseDate(paramValue, this.datepatterns));
    333             } catch (DateParseException dpe) {
    334                 LOG.debug("Error parsing cookie date", dpe);
    335                 throw new MalformedCookieException(
    336                     "Unable to parse expiration date parameter: "
    337                     + paramValue);
    338             }
    339         } else {
    340             if (LOG.isDebugEnabled()) {
    341                 LOG.debug("Unrecognized cookie attribute: "
    342                     + attribute.toString());
    343             }
    344         }
    345     }
    346
    347
    348     public Collection getValidDateFormats() {
    349         return this.datepatterns;
    350     }
    351
    352     public void setValidDateFormats(final Collection datepatterns) {
    353         this.datepatterns = datepatterns;
    354     }
    355
    356     /***
    357       * Performs most common {@link Cookie} validation
    358       *
    359       * @param host the host from which the {@link Cookie} was received
    360       * @param port the port from which the {@link Cookie} was received
    361       * @param path the path from which the {@link Cookie} was received
    362       * @param secure <tt>true</tt> when the {@link Cookie} was received using a
    363       * secure connection
    364       * @param cookie The cookie to validate.
    365       * @throws MalformedCookieException if an exception occurs during
    366       * validation
    367       */
    368
    369     public void validate(String host, int port, String path,
    370         boolean secure, final Cookie cookie)
    371         throws MalformedCookieException {
    372
    373         LOG.trace("enter CookieSpecBase.validate("
    374             + "String, port, path, boolean, Cookie)");
    375         if (host == null) {
    376             throw new IllegalArgumentException(
    377                 "Host of origin may not be null");
    378         }
    379         if (host.trim().equals("")) {
    380             throw new IllegalArgumentException(
    381                 "Host of origin may not be blank");
    382         }
    383         if (port < 0) {
    384             throw new IllegalArgumentException("Invalid port: " + port);
    385         }
    386         if (path == null) {
    387             throw new IllegalArgumentException(
    388                 "Path of origin may not be null.");
    389         }
    390         if (path.trim().equals("")) {
    391             path = PATH_DELIM;
    392         }
    393         host = host.toLowerCase();
    394         // check version
    395         if (cookie.getVersion() < 0) {
    396             throw new MalformedCookieException ("Illegal version number "
    397                 + cookie.getValue());
    398         }
    399
    400         // security check... we musn't allow the server to give us an
    401         // invalid domain scope
    402
    403         // Validate the cookies domain attribute.  NOTE:  Domains without
    404         // any dots are allowed to support hosts on private LANs that don't
    405         // have DNS names.  Since they have no dots, to domain-match the
    406         // request-host and domain must be identical for the cookie to sent
    407         // back to the origin-server.
    408         if (host.indexOf(".") >= 0) {
    409             // Not required to have at least two dots.  RFC 2965.
    410             // A Set-Cookie2 with Domain=ajax.com will be accepted.
    411
    412             // domain must match host
    413             if (!host.endsWith(cookie.getDomain())) {
    414                 String s = cookie.getDomain();
    415                 if (s.startsWith(".")) {
    416                     s = s.substring(1, s.length());
    417                 }
    418                 if (!host.equals(s)) {
    419                     throw new MalformedCookieException(
    420                         "Illegal domain attribute \"" + cookie.getDomain()
    421                         + "\". Domain of origin: \"" + host + "\"");
    422                 }
    423             }
    424         } else {
    425             if (!host.equals(cookie.getDomain())) {
    426                 throw new MalformedCookieException(
    427                     "Illegal domain attribute \"" + cookie.getDomain()
    428                     + "\". Domain of origin: \"" + host + "\"");
    429             }
    430         }
    431
    432         // another security check... we musn't allow the server to give us a
    433         // cookie that doesn't match this path
    434
    435         if (!path.startsWith(cookie.getPath())) {
    436             throw new MalformedCookieException(
    437                 "Illegal path attribute \"" + cookie.getPath()
    438                 + "\". Path of origin: \"" + path + "\"");
    439         }
    440     }
    441
    442
    443     /***
    444      * Return <tt>true</tt> if the cookie should be submitted with a request
    445      * with given attributes, <tt>false</tt> otherwise.
    446      * @param host the host to which the request is being submitted
    447      * @param port the port to which the request is being submitted (ignored)
    448      * @param path the path to which the request is being submitted
    449      * @param secure <tt>true</tt> if the request is using a secure connection
    450      * @param cookie {@link Cookie} to be matched
    451      * @return true if the cookie matches the criterium
    452      */
    453
    454     public boolean match(String host, int port, String path,
    455         boolean secure, final Cookie cookie) {
    456
    457         LOG.trace("enter CookieSpecBase.match("
    458             + "String, int, String, boolean, Cookie");
    459
    460         if (host == null) {
    461             throw new IllegalArgumentException(
    462                 "Host of origin may not be null");
    463         }
    464         if (host.trim().equals("")) {
    465             throw new IllegalArgumentException(
    466                 "Host of origin may not be blank");
    467         }
    468         if (port < 0) {
    469             throw new IllegalArgumentException("Invalid port: " + port);
    470         }
    471         if (path == null) {
    472             throw new IllegalArgumentException(
    473                 "Path of origin may not be null.");
    474         }
    475         if (cookie == null) {
    476             throw new IllegalArgumentException("Cookie may not be null");
    477         }
    478         if (path.trim().equals("")) {
    479             path = PATH_DELIM;
    480         }
    481         host = host.toLowerCase();
    482         if (cookie.getDomain() == null) {
    483             LOG.warn("Invalid cookie state: domain not specified");
    484             return false;
    485         }
    486         if (cookie.getPath() == null) {
    487             LOG.warn("Invalid cookie state: path not specified");
    488             return false;
    489         }
    490
    491         return
    492             // only add the cookie if it hasn't yet expired
    493             (cookie.getExpiryDate() == null
    494                 || cookie.getExpiryDate().after(new Date()))
    495             // and the domain pattern matches
    496             && (domainMatch(host, cookie.getDomain()))
    497             // and the path is null or matching
    498             && (pathMatch(path, cookie.getPath()))
    499             // and if the secure flag is set, only if the request is
    500             // actually secure
    501             && (cookie.getSecure() ? secure : true);
    502     }
    503
    504     /***
    505      * Performs domain-match as implemented in common browsers.
    506      * @param host The target host.
    507      * @param domain The cookie domain attribute.
    508      * @return true if the specified host matches the given domain.
    509      */
    510     public boolean domainMatch(final String host, String domain) {
    511         if (host.equals(domain)) {
    512             return true;
    513         }
    514         if (!domain.startsWith(".")) {
    515             domain = "." + domain;
    516         }
    517         return host.endsWith(domain) || host.equals(domain.substring(1));
    518     }
    519
    520     /***
    521      * Performs path-match as implemented in common browsers.
    522      * @param path The target path.
    523      * @param topmostPath The cookie path attribute.
    524      * @return true if the paths match
    525      */
    526     public boolean pathMatch(final String path, final String topmostPath) {
    527         boolean match = path.startsWith (topmostPath);
    528         // if there is a match and these values are not exactly the same we have
    529         // to make sure we're not matcing "/foobar" and "/foo"
    530         if (match && path.length() != topmostPath.length()) {
    531             if (!topmostPath.endsWith(PATH_DELIM)) {
    532                 match = (path.charAt(topmostPath.length()) == PATH_DELIM_CHAR);
    533             }
    534         }
    535         return match;
    536     }
    537
    538     /***
    539      * Return an array of {@link Cookie}s that should be submitted with a
    540      * request with given attributes, <tt>false</tt> otherwise.
    541      * @param host the host to which the request is being submitted
    542      * @param port the port to which the request is being submitted (currently
    543      * ignored)
    544      * @param path the path to which the request is being submitted
    545      * @param secure <tt>true</tt> if the request is using a secure protocol
    546      * @param cookies an array of <tt>Cookie</tt>s to be matched
    547      * @return an array of <tt>Cookie</tt>s matching the criterium
    548      *
    549 // BEGIN IA/HERITRIX CHANGES
    550      * @deprecated use match(String, int, String, boolean, SortedMap)
    551 // END IA/HERITRIX CHANGES
    552      */
    553
    554     public Cookie[] match(String host, int port, String path,
    555         boolean secure, final Cookie cookies[]) {
    556
    557         LOG.trace("enter CookieSpecBase.match("
    558             + "String, int, String, boolean, Cookie[])");
    559
    560         if (cookies == null) {
    561             return null;
    562         }
    563         List matching = new LinkedList();
    564         for (int i = 0; i < cookies.length; i++) {
    565             if (match(host, port, path, secure, cookies[i])) {
    566                 addInPathOrder(matching, cookies[i]);
    567             }
    568         }
    569         return (Cookie[]) matching.toArray(new Cookie[matching.size()]);
    570     }
    571
    572 //  BEGIN IA/HERITRIX CHANGES
    573     /***
    574      * Return an array of {@link Cookie}s that should be submitted with a
    575      * request with given attributes, <tt>false</tt> otherwise.
    576      *
    577      * If the SortedMap comes from an HttpState and is not itself
    578      * thread-safe, it may be necessary to synchronize on the HttpState
    579      * instance to protect against concurrent modification.
    580      *
    581      * @param host the host to which the request is being submitted
    582      * @param port the port to which the request is being submitted (currently
    583      * ignored)
    584      * @param path the path to which the request is being submitted
    585      * @param secure <tt>true</tt> if the request is using a secure protocol
    586      * @param cookies SortedMap of <tt>Cookie</tt>s to be matched
    587      * @return an array of <tt>Cookie</tt>s matching the criterium
    588      */
    589
    590     public Cookie[] match(String host, int port, String path,
    591         boolean secure, final SortedMap cookies) {
    592
    593         LOG.trace("enter CookieSpecBase.match("
    594            + "String, int, String, boolean, SortedMap)");
    595
    596         // TODO: skip meaningless 'narrowing' when host is a numeric IP
    597         // (harmless in the meantime)
    598
    599         if (cookies == null) {
    600             return null;
    601         }
    602         List matching = new LinkedList();
    603         String narrowHost = host;
    604         do {
    605             Iterator iter = cookies.subMap(narrowHost,
    606                     narrowHost + Cookie.DOMAIN_OVERBOUNDS).values().iterator();
    607             while (iter.hasNext()) {
    608                 Cookie cookie = (Cookie) (iter.next());
    609                 if (match(host, port, path, secure, cookie)) {
    610                     addInPathOrder(matching, cookie);
    611                 }
    612             }
    613             StoredIterator.close(iter);
    614             int trimTo = narrowHost.indexOf('.', 1);
    615             narrowHost = (trimTo < 0) ? null : narrowHost.substring(trimTo+1);
    616         } while (narrowHost != null);
    617
    618         return (Cookie[]) matching.toArray(new Cookie[matching.size()]);
    619     }
    620 //  END IA/HERITRIX CHANGES
    621
    622     /***
    623      * Adds the given cookie into the given list in descending path order. That
    624      * is, more specific path to least specific paths.  This may not be the
    625      * fastest algorythm, but it'll work OK for the small number of cookies
    626      * we're generally dealing with.
    627      *
    628      * @param list - the list to add the cookie to
    629      * @param addCookie - the Cookie to add to list
    630      */
    631     private static void addInPathOrder(List list, Cookie addCookie) {
    632         int i = 0;
    633
    634         for (i = 0; i < list.size(); i++) {
    635             Cookie c = (Cookie) list.get(i);
    636             if (addCookie.compare(addCookie, c) > 0) {
    637                 break;
    638             }
    639         }
    640         list.add(i, addCookie);
    641     }
    642
    643     /***
    644      * Return a string suitable for sending in a <tt>"Cookie"</tt> header
    645      * @param cookie a {@link Cookie} to be formatted as string
    646      * @return a string suitable for sending in a <tt>"Cookie"</tt> header.
    647      */
    648     public String formatCookie(Cookie cookie) {
    649         LOG.trace("enter CookieSpecBase.formatCookie(Cookie)");
    650         if (cookie == null) {
    651             throw new IllegalArgumentException("Cookie may not be null");
    652         }
    653         StringBuffer buf = new StringBuffer();
    654         buf.append(cookie.getName());
    655         buf.append("=");
    656         String s = cookie.getValue();
    657         if (s != null) {
    658             buf.append(s);
    659         }
    660         return buf.toString();
    661     }
    662
    663     /***
    664      * Create a <tt>"Cookie"</tt> header value containing all {@link Cookie}s in
    665      * <i>cookies</i> suitable for sending in a <tt>"Cookie"</tt> header
    666      * @param cookies an array of {@link Cookie}s to be formatted
    667      * @return a string suitable for sending in a Cookie header.
    668      * @throws IllegalArgumentException if an input parameter is illegal
    669      */
    670
    671     public String formatCookies(Cookie[] cookies)
    672       throws IllegalArgumentException {
    673         LOG.trace("enter CookieSpecBase.formatCookies(Cookie[])");
    674         if (cookies == null) {
    675             throw new IllegalArgumentException("Cookie array may not be null");
    676         }
    677         if (cookies.length == 0) {
    678             throw new IllegalArgumentException("Cookie array may not be empty");
    679         }
    680
    681         StringBuffer buffer = new StringBuffer();
    682         for (int i = 0; i < cookies.length; i++) {
    683             if (i > 0) {
    684                 buffer.append("; ");
    685             }
    686             buffer.append(formatCookie(cookies[i]));
    687         }
    688         return buffer.toString();
    689     }
    690
    691
    692     /***
    693      * Create a <tt>"Cookie"</tt> {@link Header} containing all {@link Cookie}s
    694      * in <i>cookies</i>.
    695      * @param cookies an array of {@link Cookie}s to be formatted as a <tt>"
    696      * Cookie"</tt> header
    697      * @return a <tt>"Cookie"</tt> {@link Header}.
    698      */
    699     public Header formatCookieHeader(Cookie[] cookies) {
    700         LOG.trace("enter CookieSpecBase.formatCookieHeader(Cookie[])");
    701         return new Header("Cookie", formatCookies(cookies));
    702     }
    703
    704
    705     /***
    706      * Create a <tt>"Cookie"</tt> {@link Header} containing the {@link Cookie}.
    707      * @param cookie <tt>Cookie</tt>s to be formatted as a <tt>Cookie</tt>
    708      * header
    709      * @return a Cookie header.
    710      */
    711     public Header formatCookieHeader(Cookie cookie) {
    712         LOG.trace("enter CookieSpecBase.formatCookieHeader(Cookie)");
    713         return new Header("Cookie", formatCookie(cookie));
    714     }
    715
    716 }