jdk/src/share/classes/java/net/HttpCookie.java
changeset 11105 4dcb5baa61f3
parent 10352 edde66d3118f
child 11284 2750cfd2352c
equal deleted inserted replaced
11039:4ee27839f531 11105:4dcb5baa61f3
    29 import java.util.StringTokenizer;
    29 import java.util.StringTokenizer;
    30 import java.util.NoSuchElementException;
    30 import java.util.NoSuchElementException;
    31 import java.text.SimpleDateFormat;
    31 import java.text.SimpleDateFormat;
    32 import java.util.TimeZone;
    32 import java.util.TimeZone;
    33 import java.util.Date;
    33 import java.util.Date;
    34 
       
    35 import java.lang.NullPointerException;  // for javadoc
       
    36 import java.util.Locale;
    34 import java.util.Locale;
    37 import java.util.Objects;
    35 import java.util.Objects;
    38 
    36 
    39 /**
    37 /**
    40  * An HttpCookie object represents an http cookie, which carries state
    38  * An HttpCookie object represents an HTTP cookie, which carries state
    41  * information between server and user agent. Cookie is widely adopted
    39  * information between server and user agent. Cookie is widely adopted
    42  * to create stateful sessions.
    40  * to create stateful sessions.
    43  *
    41  *
    44  * <p>There are 3 http cookie specifications:
    42  * <p> There are 3 HTTP cookie specifications:
    45  * <blockquote>
    43  * <blockquote>
    46  *   Netscape draft<br>
    44  *   Netscape draft<br>
    47  *   RFC 2109 - <a href="http://www.ietf.org/rfc/rfc2109.txt">
    45  *   RFC 2109 - <a href="http://www.ietf.org/rfc/rfc2109.txt">
    48  * <i>http://www.ietf.org/rfc/rfc2109.txt</i></a><br>
    46  * <i>http://www.ietf.org/rfc/rfc2109.txt</i></a><br>
    49  *   RFC 2965 - <a href="http://www.ietf.org/rfc/rfc2965.txt">
    47  *   RFC 2965 - <a href="http://www.ietf.org/rfc/rfc2965.txt">
    50  * <i>http://www.ietf.org/rfc/rfc2965.txt</i></a>
    48  * <i>http://www.ietf.org/rfc/rfc2965.txt</i></a>
    51  * </blockquote>
    49  * </blockquote>
    52  *
    50  *
    53  * <p>HttpCookie class can accept all these 3 forms of syntax.
    51  * <p> HttpCookie class can accept all these 3 forms of syntax.
    54  *
    52  *
    55  * @author Edward Wang
    53  * @author Edward Wang
    56  * @since 1.6
    54  * @since 1.6
    57  */
    55  */
    58 public final class HttpCookie implements Cloneable {
    56 public final class HttpCookie implements Cloneable {
    59     /* ---------------- Fields -------------- */
    57     // ---------------- Fields --------------
    60 
    58 
    61     //
       
    62     // The value of the cookie itself.
    59     // The value of the cookie itself.
    63     //
    60     private final String name;  // NAME= ... "$Name" style is reserved
    64 
       
    65     private String name;        // NAME= ... "$Name" style is reserved
       
    66     private String value;       // value of NAME
    61     private String value;       // value of NAME
    67 
    62 
    68     //
       
    69     // Attributes encoded in the header's cookie fields.
    63     // Attributes encoded in the header's cookie fields.
    70     //
       
    71 
       
    72     private String comment;     // Comment=VALUE ... describes cookie's use
    64     private String comment;     // Comment=VALUE ... describes cookie's use
    73     private String commentURL;  // CommentURL="http URL" ... describes cookie's use
    65     private String commentURL;  // CommentURL="http URL" ... describes cookie's use
    74     private boolean toDiscard;  // Discard ... discard cookie unconditionally
    66     private boolean toDiscard;  // Discard ... discard cookie unconditionally
    75     private String domain;      // Domain=VALUE ... domain that sees cookie
    67     private String domain;      // Domain=VALUE ... domain that sees cookie
    76     private long maxAge = MAX_AGE_UNSPECIFIED;  // Max-Age=VALUE ... cookies auto-expire
    68     private long maxAge = MAX_AGE_UNSPECIFIED;  // Max-Age=VALUE ... cookies auto-expire
    78     private String portlist;    // Port[="portlist"] ... the port cookie may be returned to
    70     private String portlist;    // Port[="portlist"] ... the port cookie may be returned to
    79     private boolean secure;     // Secure ... e.g. use SSL
    71     private boolean secure;     // Secure ... e.g. use SSL
    80     private boolean httpOnly;   // HttpOnly ... i.e. not accessible to scripts
    72     private boolean httpOnly;   // HttpOnly ... i.e. not accessible to scripts
    81     private int version = 1;    // Version=1 ... RFC 2965 style
    73     private int version = 1;    // Version=1 ... RFC 2965 style
    82 
    74 
    83     //
       
    84     // Hold the creation time (in seconds) of the http cookie for later
    75     // Hold the creation time (in seconds) of the http cookie for later
    85     // expiration calculation
    76     // expiration calculation
    86     //
    77     private final long whenCreated;
    87     private long whenCreated = 0;
    78 
    88 
       
    89 
       
    90     //
       
    91     // Since the positive and zero max-age have their meanings,
    79     // Since the positive and zero max-age have their meanings,
    92     // this value serves as a hint as 'not specify max-age'
    80     // this value serves as a hint as 'not specify max-age'
    93     //
       
    94     private final static long MAX_AGE_UNSPECIFIED = -1;
    81     private final static long MAX_AGE_UNSPECIFIED = -1;
    95 
    82 
    96 
       
    97     //
       
    98     // date formats used by Netscape's cookie draft
    83     // date formats used by Netscape's cookie draft
    99     // as well as formats seen on various sites
    84     // as well as formats seen on various sites
   100     //
       
   101     private final static String[] COOKIE_DATE_FORMATS = {
    85     private final static String[] COOKIE_DATE_FORMATS = {
   102         "EEE',' dd-MMM-yyyy HH:mm:ss 'GMT'",
    86         "EEE',' dd-MMM-yyyy HH:mm:ss 'GMT'",
   103         "EEE',' dd MMM yyyy HH:mm:ss 'GMT'",
    87         "EEE',' dd MMM yyyy HH:mm:ss 'GMT'",
   104         "EEE MMM dd yyyy HH:mm:ss 'GMT'Z"
    88         "EEE MMM dd yyyy HH:mm:ss 'GMT'Z"
   105     };
    89     };
   106 
    90 
   107     //
       
   108     // constant strings represent set-cookie header token
    91     // constant strings represent set-cookie header token
   109     //
       
   110     private final static String SET_COOKIE = "set-cookie:";
    92     private final static String SET_COOKIE = "set-cookie:";
   111     private final static String SET_COOKIE2 = "set-cookie2:";
    93     private final static String SET_COOKIE2 = "set-cookie2:";
   112 
    94 
   113 
    95     // ---------------- Ctors --------------
   114     /* ---------------- Ctors -------------- */
       
   115 
    96 
   116     /**
    97     /**
   117      * Constructs a cookie with a specified name and value.
    98      * Constructs a cookie with a specified name and value.
   118      *
    99      *
   119      * <p>The name must conform to RFC 2965. That means it can contain
   100      * <p> The name must conform to RFC 2965. That means it can contain
   120      * only ASCII alphanumeric characters and cannot contain commas,
   101      * only ASCII alphanumeric characters and cannot contain commas,
   121      * semicolons, or white space or begin with a $ character. The cookie's
   102      * semicolons, or white space or begin with a $ character. The cookie's
   122      * name cannot be changed after creation.
   103      * name cannot be changed after creation.
   123      *
   104      *
   124      * <p>The value can be anything the server chooses to send. Its
   105      * <p> The value can be anything the server chooses to send. Its
   125      * value is probably of interest only to the server. The cookie's
   106      * value is probably of interest only to the server. The cookie's
   126      * value can be changed after creation with the
   107      * value can be changed after creation with the
   127      * <code>setValue</code> method.
   108      * {@code setValue} method.
   128      *
   109      *
   129      * <p>By default, cookies are created according to the RFC 2965
   110      * <p> By default, cookies are created according to the RFC 2965
   130      * cookie specification. The version can be changed with the
   111      * cookie specification. The version can be changed with the
   131      * <code>setVersion</code> method.
   112      * {@code setVersion} method.
   132      *
   113      *
   133      *
   114      *
   134      * @param name                      a <code>String</code> specifying the name of the cookie
   115      * @param  name
   135      *
   116      *         a {@code String} specifying the name of the cookie
   136      * @param value                     a <code>String</code> specifying the value of the cookie
   117      *
   137      *
   118      * @param  value
   138      * @throws IllegalArgumentException if the cookie name contains illegal characters
   119      *         a {@code String} specifying the value of the cookie
   139      *                                  or it is one of the tokens reserved for use
   120      *
   140      *                                  by the cookie protocol
   121      * @throws  IllegalArgumentException
   141      * @throws NullPointerException     if <tt>name</tt> is <tt>null</tt>
   122      *          if the cookie name contains illegal characters or it is one of
       
   123      *          the tokens reserved for use by the cookie protocol
       
   124      * @throws  NullPointerException
       
   125      *          if {@code name} is {@code null}
       
   126      *
   142      * @see #setValue
   127      * @see #setValue
   143      * @see #setVersion
   128      * @see #setVersion
   144      *
   129      */
   145      */
       
   146 
       
   147     public HttpCookie(String name, String value) {
   130     public HttpCookie(String name, String value) {
   148         name = name.trim();
   131         name = name.trim();
   149         if (name.length() == 0 || !isToken(name) || isReserved(name)) {
   132         if (name.length() == 0 || !isToken(name) || isReserved(name)) {
   150             throw new IllegalArgumentException("Illegal cookie name");
   133             throw new IllegalArgumentException("Illegal cookie name");
   151         }
   134         }
   157 
   140 
   158         whenCreated = System.currentTimeMillis();
   141         whenCreated = System.currentTimeMillis();
   159         portlist = null;
   142         portlist = null;
   160     }
   143     }
   161 
   144 
   162 
       
   163     /**
   145     /**
   164      * Constructs cookies from set-cookie or set-cookie2 header string.
   146      * Constructs cookies from set-cookie or set-cookie2 header string.
   165      * RFC 2965 section 3.2.2 set-cookie2 syntax indicates that one header line
   147      * RFC 2965 section 3.2.2 set-cookie2 syntax indicates that one header line
   166      * may contain more than one cookie definitions, so this is a static
   148      * may contain more than one cookie definitions, so this is a static
   167      * utility method instead of another constructor.
   149      * utility method instead of another constructor.
   168      *
   150      *
   169      * @param header    a <tt>String</tt> specifying the set-cookie header.
   151      * @param  header
   170      *                  The header should start with "set-cookie", or "set-cookie2"
   152      *         a {@code String} specifying the set-cookie header. The header
   171      *                  token; or it should have no leading token at all.
   153      *         should start with "set-cookie", or "set-cookie2" token; or it
   172      * @return          a List of cookie parsed from header line string
   154      *         should have no leading token at all.
   173      * @throws IllegalArgumentException if header string violates the cookie
   155      *
   174      *                                  specification's syntax, or the cookie
   156      * @return  a List of cookie parsed from header line string
   175      *                                  name contains llegal characters, or
   157      *
   176      *                                  the cookie name is one of the tokens
   158      * @throws  IllegalArgumentException
   177      *                                  reserved for use by the cookie protocol
   159      *          if header string violates the cookie specification's syntax, or
   178      * @throws NullPointerException     if the header string is <tt>null</tt>
   160      *          the cookie name contains illegal characters, or the cookie name
       
   161      *          is one of the tokens reserved for use by the cookie protocol
       
   162      * @throws  NullPointerException
       
   163      *          if the header string is {@code null}
   179      */
   164      */
   180     public static List<HttpCookie> parse(String header) {
   165     public static List<HttpCookie> parse(String header) {
   181         int version = guessCookieVersion(header);
   166         int version = guessCookieVersion(header);
   182 
   167 
   183         // if header start with set-cookie or set-cookie2, strip it off
   168         // if header start with set-cookie or set-cookie2, strip it off
   185             header = header.substring(SET_COOKIE2.length());
   170             header = header.substring(SET_COOKIE2.length());
   186         } else if (startsWithIgnoreCase(header, SET_COOKIE)) {
   171         } else if (startsWithIgnoreCase(header, SET_COOKIE)) {
   187             header = header.substring(SET_COOKIE.length());
   172             header = header.substring(SET_COOKIE.length());
   188         }
   173         }
   189 
   174 
   190 
   175         List<HttpCookie> cookies = new java.util.ArrayList<>();
   191         List<HttpCookie> cookies = new java.util.ArrayList<HttpCookie>();
   176         // The Netscape cookie may have a comma in its expires attribute, while
   192         // The Netscape cookie may have a comma in its expires attribute,
   177         // the comma is the delimiter in rfc 2965/2109 cookie header string.
   193         // while the comma is the delimiter in rfc 2965/2109 cookie header string.
       
   194         // so the parse logic is slightly different
   178         // so the parse logic is slightly different
   195         if (version == 0) {
   179         if (version == 0) {
   196             // Netscape draft cookie
   180             // Netscape draft cookie
   197             HttpCookie cookie = parseInternal(header);
   181             HttpCookie cookie = parseInternal(header);
   198             cookie.setVersion(0);
   182             cookie.setVersion(0);
   210         }
   194         }
   211 
   195 
   212         return cookies;
   196         return cookies;
   213     }
   197     }
   214 
   198 
   215 
   199     // ---------------- Public operations --------------
   216 
   200 
   217 
   201     /**
   218     /* ---------------- Public operations -------------- */
   202      * Reports whether this HTTP cookie has expired or not.
   219 
   203      *
   220 
   204      * @return  {@code true} to indicate this HTTP cookie has expired;
   221     /**
   205      *          otherwise, {@code false}
   222      * Reports whether this http cookie has expired or not.
       
   223      *
       
   224      * @return  <tt>true</tt> to indicate this http cookie has expired;
       
   225      *          otherwise, <tt>false</tt>
       
   226      */
   206      */
   227     public boolean hasExpired() {
   207     public boolean hasExpired() {
   228         if (maxAge == 0) return true;
   208         if (maxAge == 0) return true;
   229 
   209 
   230         // if not specify max-age, this cookie should be
   210         // if not specify max-age, this cookie should be
   238         else
   218         else
   239             return false;
   219             return false;
   240     }
   220     }
   241 
   221 
   242     /**
   222     /**
   243      *
       
   244      * Specifies a comment that describes a cookie's purpose.
   223      * Specifies a comment that describes a cookie's purpose.
   245      * The comment is useful if the browser presents the cookie
   224      * The comment is useful if the browser presents the cookie
   246      * to the user. Comments
   225      * to the user. Comments are not supported by Netscape Version 0 cookies.
   247      * are not supported by Netscape Version 0 cookies.
   226      *
   248      *
   227      * @param  purpose
   249      * @param purpose           a <code>String</code> specifying the comment
   228      *         a {@code String} specifying the comment to display to the user
   250      *                          to display to the user
   229      *
   251      *
   230      * @see  #getComment
   252      * @see #getComment
   231      */
   253      *
       
   254      */
       
   255 
       
   256     public void setComment(String purpose) {
   232     public void setComment(String purpose) {
   257         comment = purpose;
   233         comment = purpose;
   258     }
   234     }
   259 
   235 
   260 
       
   261 
       
   262 
       
   263     /**
   236     /**
   264      * Returns the comment describing the purpose of this cookie, or
   237      * Returns the comment describing the purpose of this cookie, or
   265      * <code>null</code> if the cookie has no comment.
   238      * {@code null} if the cookie has no comment.
   266      *
   239      *
   267      * @return                  a <code>String</code> containing the comment,
   240      * @return  a {@code String} containing the comment, or {@code null} if none
   268      *                          or <code>null</code> if none
   241      *
   269      *
   242      * @see  #setComment
   270      * @see #setComment
   243      */
   271      *
       
   272      */
       
   273 
       
   274     public String getComment() {
   244     public String getComment() {
   275         return comment;
   245         return comment;
   276     }
   246     }
   277 
   247 
   278 
   248     /**
   279     /**
   249      * Specifies a comment URL that describes a cookie's purpose.
   280      *
   250      * The comment URL is useful if the browser presents the cookie
   281      * Specifies a comment url that describes a cookie's purpose.
   251      * to the user. Comment URL is RFC 2965 only.
   282      * The comment url is useful if the browser presents the cookie
   252      *
   283      * to the user. Comment url is RFC 2965 only.
   253      * @param  purpose
   284      *
   254      *         a {@code String} specifying the comment URL to display to the user
   285      * @param purpose           a <code>String</code> specifying the comment url
   255      *
   286      *                          to display to the user
   256      * @see  #getCommentURL
   287      *
   257      */
   288      * @see #getCommentURL
       
   289      *
       
   290      */
       
   291 
       
   292     public void setCommentURL(String purpose) {
   258     public void setCommentURL(String purpose) {
   293         commentURL = purpose;
   259         commentURL = purpose;
   294     }
   260     }
   295 
   261 
   296 
   262     /**
   297 
   263      * Returns the comment URL describing the purpose of this cookie, or
   298 
   264      * {@code null} if the cookie has no comment URL.
   299     /**
   265      *
   300      * Returns the comment url describing the purpose of this cookie, or
   266      * @return  a {@code String} containing the comment URL, or {@code null}
   301      * <code>null</code> if the cookie has no comment url.
   267      *          if none
   302      *
   268      *
   303      * @return                  a <code>String</code> containing the comment url,
   269      * @see  #setCommentURL
   304      *                          or <code>null</code> if none
   270      */
   305      *
       
   306      * @see #setCommentURL
       
   307      *
       
   308      */
       
   309 
       
   310     public String getCommentURL() {
   271     public String getCommentURL() {
   311         return commentURL;
   272         return commentURL;
   312     }
   273     }
   313 
   274 
   314 
       
   315     /**
   275     /**
   316      * Specify whether user agent should discard the cookie unconditionally.
   276      * Specify whether user agent should discard the cookie unconditionally.
   317      * This is RFC 2965 only attribute.
   277      * This is RFC 2965 only attribute.
   318      *
   278      *
   319      * @param discard   <tt>true</tt> indicates to discard cookie unconditionally
   279      * @param  discard
   320      *
   280      *         {@code true} indicates to discard cookie unconditionally
   321      * @see #getDiscard
   281      *
   322      */
   282      * @see  #getDiscard
   323 
   283      */
   324     public void setDiscard(boolean discard) {
   284     public void setDiscard(boolean discard) {
   325         toDiscard = discard;
   285         toDiscard = discard;
   326     }
   286     }
   327 
   287 
   328 
   288     /**
   329 
   289      * Returns the discard attribute of the cookie
   330 
   290      *
   331     /**
   291      * @return  a {@code boolean} to represent this cookie's discard attribute
   332      * Return the discard attribute of the cookie
   292      *
   333      *
   293      * @see  #setDiscard
   334      * @return  a <tt>boolean</tt> to represent this cookie's discard attribute
   294      */
   335      *
       
   336      * @see #setDiscard
       
   337      */
       
   338 
       
   339     public boolean getDiscard() {
   295     public boolean getDiscard() {
   340         return toDiscard;
   296         return toDiscard;
   341     }
   297     }
   342 
   298 
   343 
       
   344     /**
   299     /**
   345      * Specify the portlist of the cookie, which restricts the port(s)
   300      * Specify the portlist of the cookie, which restricts the port(s)
   346      * to which a cookie may be sent back in a Cookie header.
   301      * to which a cookie may be sent back in a Cookie header.
   347      *
   302      *
   348      * @param ports     a <tt>String</tt> specify the port list, which is
   303      * @param  ports
   349      *                  comma seperated series of digits
   304      *         a {@code String} specify the port list, which is comma separated
   350      * @see #getPortlist
   305      *         series of digits
   351      */
   306      *
   352 
   307      * @see  #getPortlist
       
   308      */
   353     public void setPortlist(String ports) {
   309     public void setPortlist(String ports) {
   354         portlist = ports;
   310         portlist = ports;
   355     }
   311     }
   356 
   312 
   357 
   313     /**
   358 
   314      * Returns the port list attribute of the cookie
   359 
   315      *
   360     /**
   316      * @return  a {@code String} contains the port list or {@code null} if none
   361      * Return the port list attribute of the cookie
   317      *
   362      *
   318      * @see  #setPortlist
   363      * @return  a <tt>String</tt> contains the port list
   319      */
   364      *          or <tt>null</tt> if none
       
   365      * @see #setPortlist
       
   366      */
       
   367 
       
   368     public String getPortlist() {
   320     public String getPortlist() {
   369         return portlist;
   321         return portlist;
   370     }
   322     }
   371 
   323 
   372     /**
   324     /**
   373      *
       
   374      * Specifies the domain within which this cookie should be presented.
   325      * Specifies the domain within which this cookie should be presented.
   375      *
   326      *
   376      * <p>The form of the domain name is specified by RFC 2965. A domain
   327      * <p> The form of the domain name is specified by RFC 2965. A domain
   377      * name begins with a dot (<code>.foo.com</code>) and means that
   328      * name begins with a dot ({@code .foo.com}) and means that
   378      * the cookie is visible to servers in a specified Domain Name System
   329      * the cookie is visible to servers in a specified Domain Name System
   379      * (DNS) zone (for example, <code>www.foo.com</code>, but not
   330      * (DNS) zone (for example, {@code www.foo.com}, but not
   380      * <code>a.b.foo.com</code>). By default, cookies are only returned
   331      * {@code a.b.foo.com}). By default, cookies are only returned
   381      * to the server that sent them.
   332      * to the server that sent them.
   382      *
   333      *
   383      *
   334      * @param  pattern
   384      * @param pattern           a <code>String</code> containing the domain name
   335      *         a {@code String} containing the domain name within which this
   385      *                          within which this cookie is visible;
   336      *         cookie is visible; form is according to RFC 2965
   386      *                          form is according to RFC 2965
   337      *
   387      *
   338      * @see  #getDomain
   388      * @see #getDomain
   339      */
   389      *
       
   390      */
       
   391 
       
   392     public void setDomain(String pattern) {
   340     public void setDomain(String pattern) {
   393         if (pattern != null)
   341         if (pattern != null)
   394             domain = pattern.toLowerCase();
   342             domain = pattern.toLowerCase();
   395         else
   343         else
   396             domain = pattern;
   344             domain = pattern;
   397     }
   345     }
   398 
   346 
   399 
   347     /**
   400 
   348      * Returns the domain name set for this cookie. The form of the domain name
   401 
   349      * is set by RFC 2965.
   402 
   350      *
   403     /**
   351      * @return  a {@code String} containing the domain name
   404      * Returns the domain name set for this cookie. The form of
   352      *
   405      * the domain name is set by RFC 2965.
   353      * @see  #setDomain
   406      *
   354      */
   407      * @return                  a <code>String</code> containing the domain name
       
   408      *
       
   409      * @see #setDomain
       
   410      *
       
   411      */
       
   412 
       
   413     public String getDomain() {
   355     public String getDomain() {
   414         return domain;
   356         return domain;
   415     }
   357     }
   416 
   358 
   417 
       
   418     /**
   359     /**
   419      * Sets the maximum age of the cookie in seconds.
   360      * Sets the maximum age of the cookie in seconds.
   420      *
   361      *
   421      * <p>A positive value indicates that the cookie will expire
   362      * <p> A positive value indicates that the cookie will expire
   422      * after that many seconds have passed. Note that the value is
   363      * after that many seconds have passed. Note that the value is
   423      * the <i>maximum</i> age when the cookie will expire, not the cookie's
   364      * the <i>maximum</i> age when the cookie will expire, not the cookie's
   424      * current age.
   365      * current age.
   425      *
   366      *
   426      * <p>A negative value means
   367      * <p> A negative value means that the cookie is not stored persistently
   427      * that the cookie is not stored persistently and will be deleted
   368      * and will be deleted when the Web browser exits. A zero value causes the
   428      * when the Web browser exits. A zero value causes the cookie
   369      * cookie to be deleted.
   429      * to be deleted.
   370      *
   430      *
   371      * @param  expiry
   431      * @param expiry            an integer specifying the maximum age of the
   372      *         an integer specifying the maximum age of the cookie in seconds;
   432      *                          cookie in seconds; if zero, the cookie
   373      *         if zero, the cookie should be discarded immediately; otherwise,
   433      *                          should be discarded immediately;
   374      *         the cookie's max age is unspecified.
   434      *                          otherwise, the cookie's max age is unspecified.
   375      *
   435      *
   376      * @see  #getMaxAge
   436      * @see #getMaxAge
       
   437      *
       
   438      */
   377      */
   439     public void setMaxAge(long expiry) {
   378     public void setMaxAge(long expiry) {
   440         maxAge = expiry;
   379         maxAge = expiry;
   441     }
   380     }
   442 
   381 
   443 
   382     /**
   444 
   383      * Returns the maximum age of the cookie, specified in seconds. By default,
   445 
   384      * {@code -1} indicating the cookie will persist until browser shutdown.
   446     /**
   385      *
   447      * Returns the maximum age of the cookie, specified in seconds.
   386      * @return  an integer specifying the maximum age of the cookie in seconds
   448      * By default, <code>-1</code> indicating the cookie will persist
   387      *
   449      * until browser shutdown.
   388      * @see  #setMaxAge
   450      *
   389      */
   451      *
       
   452      * @return                  an integer specifying the maximum age of the
       
   453      *                          cookie in seconds
       
   454      *
       
   455      *
       
   456      * @see #setMaxAge
       
   457      *
       
   458      */
       
   459 
       
   460     public long getMaxAge() {
   390     public long getMaxAge() {
   461         return maxAge;
   391         return maxAge;
   462     }
   392     }
   463 
   393 
   464 
   394     /**
   465 
   395      * Specifies a path for the cookie to which the client should return
   466 
   396      * the cookie.
   467     /**
   397      *
   468      * Specifies a path for the cookie
   398      * <p> The cookie is visible to all the pages in the directory
   469      * to which the client should return the cookie.
       
   470      *
       
   471      * <p>The cookie is visible to all the pages in the directory
       
   472      * you specify, and all the pages in that directory's subdirectories.
   399      * you specify, and all the pages in that directory's subdirectories.
   473      * A cookie's path must include the servlet that set the cookie,
   400      * A cookie's path must include the servlet that set the cookie,
   474      * for example, <i>/catalog</i>, which makes the cookie
   401      * for example, <i>/catalog</i>, which makes the cookie
   475      * visible to all directories on the server under <i>/catalog</i>.
   402      * visible to all directories on the server under <i>/catalog</i>.
   476      *
   403      *
   477      * <p>Consult RFC 2965 (available on the Internet) for more
   404      * <p> Consult RFC 2965 (available on the Internet) for more
   478      * information on setting path names for cookies.
   405      * information on setting path names for cookies.
   479      *
   406      *
   480      *
   407      * @param  uri
   481      * @param uri               a <code>String</code> specifying a path
   408      *         a {@code String} specifying a path
   482      *
   409      *
   483      *
   410      * @see  #getPath
   484      * @see #getPath
   411      */
   485      *
       
   486      */
       
   487 
       
   488     public void setPath(String uri) {
   412     public void setPath(String uri) {
   489         path = uri;
   413         path = uri;
   490     }
   414     }
   491 
   415 
   492 
   416     /**
   493 
   417      * Returns the path on the server to which the browser returns this cookie.
   494 
   418      * The cookie is visible to all subpaths on the server.
   495     /**
   419      *
   496      * Returns the path on the server
   420      * @return  a {@code String} specifying a path that contains a servlet name,
   497      * to which the browser returns this cookie. The
   421      *          for example, <i>/catalog</i>
   498      * cookie is visible to all subpaths on the server.
   422      *
   499      *
   423      * @see  #setPath
   500      *
   424      */
   501      * @return          a <code>String</code> specifying a path that contains
       
   502      *                  a servlet name, for example, <i>/catalog</i>
       
   503      *
       
   504      * @see #setPath
       
   505      *
       
   506      */
       
   507 
       
   508     public String getPath() {
   425     public String getPath() {
   509         return path;
   426         return path;
   510     }
   427     }
   511 
   428 
   512 
       
   513 
       
   514 
       
   515 
       
   516     /**
   429     /**
   517      * Indicates whether the cookie should only be sent using a secure protocol,
   430      * Indicates whether the cookie should only be sent using a secure protocol,
   518      * such as HTTPS or SSL.
   431      * such as HTTPS or SSL.
   519      *
   432      *
   520      * <p>The default value is <code>false</code>.
   433      * <p> The default value is {@code false}.
   521      *
   434      *
   522      * @param flag      If <code>true</code>, the cookie can only be sent over
   435      * @param  flag
   523      *                  a secure protocol like https.
   436      *         If {@code true}, the cookie can only be sent over a secure
   524      *                  If <code>false</code>, it can be sent over any protocol.
   437      *         protocol like HTTPS. If {@code false}, it can be sent over
   525      *
   438      *         any protocol.
   526      * @see #getSecure
   439      *
   527      *
   440      * @see  #getSecure
   528      */
   441      */
   529 
       
   530     public void setSecure(boolean flag) {
   442     public void setSecure(boolean flag) {
   531         secure = flag;
   443         secure = flag;
   532     }
   444     }
   533 
   445 
   534 
   446     /**
   535 
   447      * Returns {@code true} if sending this cookie should be restricted to a
   536 
   448      * secure protocol, or {@code false} if the it can be sent using any
   537     /**
   449      * protocol.
   538      * Returns <code>true</code> if sending this cookie should be
   450      *
   539      * restricted to a secure protocol, or <code>false</code> if the
   451      * @return  {@code false} if the cookie can be sent over any standard
   540      * it can be sent using any protocol.
   452      *          protocol; otherwise, <code>true</code>
   541      *
   453      *
   542      * @return          <code>false</code> if the cookie can be sent over
   454      * @see  #setSecure
   543      *                  any standard protocol; otherwise, <code>true</code>
   455      */
   544      *
       
   545      * @see #setSecure
       
   546      *
       
   547      */
       
   548 
       
   549     public boolean getSecure() {
   456     public boolean getSecure() {
   550         return secure;
   457         return secure;
   551     }
   458     }
   552 
   459 
   553 
       
   554 
       
   555 
       
   556 
       
   557     /**
   460     /**
   558      * Returns the name of the cookie. The name cannot be changed after
   461      * Returns the name of the cookie. The name cannot be changed after
   559      * creation.
   462      * creation.
   560      *
   463      *
   561      * @return          a <code>String</code> specifying the cookie's name
   464      * @return  a {@code String} specifying the cookie's name
   562      *
   465      */
   563      */
       
   564 
       
   565     public String getName() {
   466     public String getName() {
   566         return name;
   467         return name;
   567     }
   468     }
   568 
   469 
   569 
   470     /**
   570 
       
   571 
       
   572 
       
   573     /**
       
   574      *
       
   575      * Assigns a new value to a cookie after the cookie is created.
   471      * Assigns a new value to a cookie after the cookie is created.
   576      * If you use a binary value, you may want to use BASE64 encoding.
   472      * If you use a binary value, you may want to use BASE64 encoding.
   577      *
   473      *
   578      * <p>With Version 0 cookies, values should not contain white
   474      * <p> With Version 0 cookies, values should not contain white space,
   579      * space, brackets, parentheses, equals signs, commas,
   475      * brackets, parentheses, equals signs, commas, double quotes, slashes,
   580      * double quotes, slashes, question marks, at signs, colons,
   476      * question marks, at signs, colons, and semicolons. Empty values may not
   581      * and semicolons. Empty values may not behave the same way
   477      * behave the same way on all browsers.
   582      * on all browsers.
   478      *
   583      *
   479      * @param  newValue
   584      * @param newValue          a <code>String</code> specifying the new value
   480      *         a {@code String} specifying the new value
   585      *
   481      *
   586      *
   482      * @see  #getValue
   587      * @see #getValue
   483      */
   588      *
       
   589      */
       
   590 
       
   591     public void setValue(String newValue) {
   484     public void setValue(String newValue) {
   592         value = newValue;
   485         value = newValue;
   593     }
   486     }
   594 
   487 
   595 
       
   596 
       
   597 
       
   598     /**
   488     /**
   599      * Returns the value of the cookie.
   489      * Returns the value of the cookie.
   600      *
   490      *
   601      * @return                  a <code>String</code> containing the cookie's
   491      * @return  a {@code String} containing the cookie's present value
   602      *                          present value
   492      *
   603      *
   493      * @see  #setValue
   604      * @see #setValue
   494      */
   605      *
       
   606      */
       
   607 
       
   608     public String getValue() {
   495     public String getValue() {
   609         return value;
   496         return value;
   610     }
   497     }
   611 
   498 
   612 
   499     /**
   613 
   500      * Returns the version of the protocol this cookie complies with. Version 1
   614 
   501      * complies with RFC 2965/2109, and version 0 complies with the original
   615     /**
   502      * cookie specification drafted by Netscape. Cookies provided by a browser
   616      * Returns the version of the protocol this cookie complies
   503      * use and identify the browser's cookie version.
   617      * with. Version 1 complies with RFC 2965/2109,
   504      *
   618      * and version 0 complies with the original
   505      * @return  0 if the cookie complies with the original Netscape
   619      * cookie specification drafted by Netscape. Cookies provided
   506      *          specification; 1 if the cookie complies with RFC 2965/2109
   620      * by a browser use and identify the browser's cookie version.
   507      *
   621      *
   508      * @see  #setVersion
   622      *
   509      */
   623      * @return                  0 if the cookie complies with the
       
   624      *                          original Netscape specification; 1
       
   625      *                          if the cookie complies with RFC 2965/2109
       
   626      *
       
   627      * @see #setVersion
       
   628      *
       
   629      */
       
   630 
       
   631     public int getVersion() {
   510     public int getVersion() {
   632         return version;
   511         return version;
   633     }
   512     }
   634 
       
   635 
       
   636 
       
   637 
   513 
   638     /**
   514     /**
   639      * Sets the version of the cookie protocol this cookie complies
   515      * Sets the version of the cookie protocol this cookie complies
   640      * with. Version 0 complies with the original Netscape cookie
   516      * with. Version 0 complies with the original Netscape cookie
   641      * specification. Version 1 complies with RFC 2965/2109.
   517      * specification. Version 1 complies with RFC 2965/2109.
   642      *
   518      *
   643      *
   519      * @param  v
   644      * @param v                 0 if the cookie should comply with
   520      *         0 if the cookie should comply with the original Netscape
   645      *                          the original Netscape specification;
   521      *         specification; 1 if the cookie should comply with RFC 2965/2109
   646      *                          1 if the cookie should comply with RFC 2965/2109
   522      *
   647      *
   523      * @throws  IllegalArgumentException
   648      * @throws IllegalArgumentException if <tt>v</tt> is neither 0 nor 1
   524      *          if {@code v} is neither 0 nor 1
   649      *
   525      *
   650      * @see #getVersion
   526      * @see  #getVersion
   651      *
   527      */
   652      */
       
   653 
       
   654     public void setVersion(int v) {
   528     public void setVersion(int v) {
   655         if (v != 0 && v != 1) {
   529         if (v != 0 && v != 1) {
   656             throw new IllegalArgumentException("cookie version should be 0 or 1");
   530             throw new IllegalArgumentException("cookie version should be 0 or 1");
   657         }
   531         }
   658 
   532 
   662     /**
   536     /**
   663      * Returns {@code true} if this cookie contains the <i>HttpOnly</i>
   537      * Returns {@code true} if this cookie contains the <i>HttpOnly</i>
   664      * attribute. This means that the cookie should not be accessible to
   538      * attribute. This means that the cookie should not be accessible to
   665      * scripting engines, like javascript.
   539      * scripting engines, like javascript.
   666      *
   540      *
   667      * @return {@code true} if this cookie should be considered http only.
   541      * @return  {@code true} if this cookie should be considered HTTPOnly
   668      * @see #setHttpOnly(boolean)
   542      *
   669      */
   543      * @see  #setHttpOnly(boolean)
   670     public boolean isHttpOnly()
   544      */
   671     {
   545     public boolean isHttpOnly() {
   672         return httpOnly;
   546         return httpOnly;
   673     }
   547     }
   674 
   548 
   675     /**
   549     /**
   676      * Indicates whether the cookie should be considered HTTP Only. If set to
   550      * Indicates whether the cookie should be considered HTTP Only. If set to
   677      * {@code true} it means the cookie should not be accessible to scripting
   551      * {@code true} it means the cookie should not be accessible to scripting
   678      * engines like javascript.
   552      * engines like javascript.
   679      *
   553      *
   680      * @param httpOnly if {@code true} make the cookie HTTP only, i.e.
   554      * @param  httpOnly
   681      *                 only visible as part of an HTTP request.
   555      *         if {@code true} make the cookie HTTP only, i.e. only visible as
   682      * @see #isHttpOnly()
   556      *         part of an HTTP request.
   683      */
   557      *
   684     public void setHttpOnly(boolean httpOnly)
   558      * @see  #isHttpOnly()
   685     {
   559      */
       
   560     public void setHttpOnly(boolean httpOnly) {
   686         this.httpOnly = httpOnly;
   561         this.httpOnly = httpOnly;
   687     }
   562     }
   688 
   563 
   689     /**
   564     /**
   690      * The utility method to check whether a host name is in a domain
   565      * The utility method to check whether a host name is in a domain or not.
   691      * or not.
   566      *
   692      *
   567      * <p> This concept is described in the cookie specification.
   693      * <p>This concept is described in the cookie specification.
       
   694      * To understand the concept, some terminologies need to be defined first:
   568      * To understand the concept, some terminologies need to be defined first:
   695      * <blockquote>
   569      * <blockquote>
   696      * effective host name = hostname if host name contains dot<br>
   570      * effective host name = hostname if host name contains dot<br>
   697      * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;or = hostname.local if not
   571      * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
       
   572      * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;or = hostname.local if not
   698      * </blockquote>
   573      * </blockquote>
   699      * <p>Host A's name domain-matches host B's if:
   574      * <p>Host A's name domain-matches host B's if:
   700      * <blockquote><ul>
   575      * <blockquote><ul>
   701      *   <li>their host name strings string-compare equal; or</li>
   576      *   <li>their host name strings string-compare equal; or</li>
   702      *   <li>A is a HDN string and has the form NB, where N is a non-empty
   577      *   <li>A is a HDN string and has the form NB, where N is a non-empty
   729      *   <li>A Set-Cookie2 from request-host example for Domain=.local will
   604      *   <li>A Set-Cookie2 from request-host example for Domain=.local will
   730      *   be accepted, because the effective host name for the request-
   605      *   be accepted, because the effective host name for the request-
   731      *   host is example.local, and example.local domain-matches .local.</li>
   606      *   host is example.local, and example.local domain-matches .local.</li>
   732      * </ul></blockquote>
   607      * </ul></blockquote>
   733      *
   608      *
   734      * @param domain    the domain name to check host name with
   609      * @param  domain
   735      * @param host      the host name in question
   610      *         the domain name to check host name with
   736      * @return          <tt>true</tt> if they domain-matches; <tt>false</tt> if not
   611      *
       
   612      * @param  host
       
   613      *         the host name in question
       
   614      *
       
   615      * @return  {@code true} if they domain-matches; {@code false} if not
   737      */
   616      */
   738     public static boolean domainMatches(String domain, String host) {
   617     public static boolean domainMatches(String domain, String host) {
   739         if (domain == null || host == null)
   618         if (domain == null || host == null)
   740             return false;
   619             return false;
   741 
   620 
   743         boolean isLocalDomain = ".local".equalsIgnoreCase(domain);
   622         boolean isLocalDomain = ".local".equalsIgnoreCase(domain);
   744         int embeddedDotInDomain = domain.indexOf('.');
   623         int embeddedDotInDomain = domain.indexOf('.');
   745         if (embeddedDotInDomain == 0)
   624         if (embeddedDotInDomain == 0)
   746             embeddedDotInDomain = domain.indexOf('.', 1);
   625             embeddedDotInDomain = domain.indexOf('.', 1);
   747         if (!isLocalDomain
   626         if (!isLocalDomain
   748             && (embeddedDotInDomain == -1 || embeddedDotInDomain == domain.length() - 1))
   627             && (embeddedDotInDomain == -1 ||
       
   628                 embeddedDotInDomain == domain.length() - 1))
   749             return false;
   629             return false;
   750 
   630 
   751         // if the host name contains no dot and the domain name
   631         // if the host name contains no dot and the domain name
   752         // is .local or host.local
   632         // is .local or host.local
   753         int firstDotInHost = host.indexOf('.');
   633         int firstDotInHost = host.indexOf('.');
   777         }
   657         }
   778 
   658 
   779         return false;
   659         return false;
   780     }
   660     }
   781 
   661 
   782 
       
   783     /**
   662     /**
   784      * Constructs a cookie header string representation of this cookie,
   663      * Constructs a cookie header string representation of this cookie,
   785      * which is in the format defined by corresponding cookie specification,
   664      * which is in the format defined by corresponding cookie specification,
   786      * but without the leading "Cookie:" token.
   665      * but without the leading "Cookie:" token.
   787      *
   666      *
   794         } else {
   673         } else {
   795             return toNetscapeHeaderString();
   674             return toNetscapeHeaderString();
   796         }
   675         }
   797     }
   676     }
   798 
   677 
   799 
   678     /**
   800     /**
   679      * Test the equality of two HTTP cookies.
   801      * Test the equality of two http cookies.
   680      *
   802      *
   681      * <p> The result is {@code true} only if two cookies come from same domain
   803      * <p> The result is <tt>true</tt> only if two cookies
   682      * (case-insensitive), have same name (case-insensitive), and have same path
   804      * come from same domain (case-insensitive),
   683      * (case-sensitive).
   805      * have same name (case-insensitive),
   684      *
   806      * and have same path (case-sensitive).
   685      * @return  {@code true} if two HTTP cookies equal to each other;
   807      *
   686      *          otherwise, {@code false}
   808      * @return          <tt>true</tt> if 2 http cookies equal to each other;
       
   809      *                  otherwise, <tt>false</tt>
       
   810      */
   687      */
   811     @Override
   688     @Override
   812     public boolean equals(Object obj) {
   689     public boolean equals(Object obj) {
   813         if (obj == this)
   690         if (obj == this)
   814             return true;
   691             return true;
   823         return equalsIgnoreCase(getName(), other.getName()) &&
   700         return equalsIgnoreCase(getName(), other.getName()) &&
   824                equalsIgnoreCase(getDomain(), other.getDomain()) &&
   701                equalsIgnoreCase(getDomain(), other.getDomain()) &&
   825                Objects.equals(getPath(), other.getPath());
   702                Objects.equals(getPath(), other.getPath());
   826     }
   703     }
   827 
   704 
   828 
   705     /**
   829     /**
   706      * Returns the hash code of this HTTP cookie. The result is the sum of
   830      * Return hash code of this http cookie. The result is the sum of
   707      * hash code value of three significant components of this cookie: name,
   831      * hash code value of three significant components of this cookie:
   708      * domain, and path. That is, the hash code is the value of the expression:
   832      * name, domain, and path.
       
   833      * That is, the hash code is the value of the expression:
       
   834      * <blockquote>
   709      * <blockquote>
   835      * getName().toLowerCase().hashCode()<br>
   710      * getName().toLowerCase().hashCode()<br>
   836      * + getDomain().toLowerCase().hashCode()<br>
   711      * + getDomain().toLowerCase().hashCode()<br>
   837      * + getPath().hashCode()
   712      * + getPath().hashCode()
   838      * </blockquote>
   713      * </blockquote>
   839      *
   714      *
   840      * @return          this http cookie's hash code
   715      * @return  this HTTP cookie's hash code
   841      */
   716      */
   842     @Override
   717     @Override
   843     public int hashCode() {
   718     public int hashCode() {
   844         int h1 = name.toLowerCase().hashCode();
   719         int h1 = name.toLowerCase().hashCode();
   845         int h2 = (domain!=null) ? domain.toLowerCase().hashCode() : 0;
   720         int h2 = (domain!=null) ? domain.toLowerCase().hashCode() : 0;
   849     }
   724     }
   850 
   725 
   851     /**
   726     /**
   852      * Create and return a copy of this object.
   727      * Create and return a copy of this object.
   853      *
   728      *
   854      * @return          a clone of this http cookie
   729      * @return  a clone of this HTTP cookie
   855      */
   730      */
   856     @Override
   731     @Override
   857     public Object clone() {
   732     public Object clone() {
   858         try {
   733         try {
   859             return super.clone();
   734             return super.clone();
   860         } catch (CloneNotSupportedException e) {
   735         } catch (CloneNotSupportedException e) {
   861             throw new RuntimeException(e.getMessage());
   736             throw new RuntimeException(e.getMessage());
   862         }
   737         }
   863     }
   738     }
   864 
   739 
   865 
   740     // ---------------- Private operations --------------
   866     /* ---------------- Private operations -------------- */
       
   867 
   741 
   868     // Note -- disabled for now to allow full Netscape compatibility
   742     // Note -- disabled for now to allow full Netscape compatibility
   869     // from RFC 2068, token special case characters
   743     // from RFC 2068, token special case characters
   870     //
   744     //
   871     // private static final String tspecials = "()<>@,;:\\\"/[]?={} \t";
   745     // private static final String tspecials = "()<>@,;:\\\"/[]?={} \t";
   872     private static final String tspecials = ",;";
   746     private static final String tspecials = ",;";
   873 
   747 
   874     /*
   748     /*
   875      * Tests a string and returns true if the string counts as a
   749      * Tests a string and returns true if the string counts as a token.
   876      * token.
   750      *
   877      *
   751      * @param  value
   878      * @param value             the <code>String</code> to be tested
   752      *         the {@code String} to be tested
   879      *
   753      *
   880      * @return                  <code>true</code> if the <code>String</code> is
   754      * @return  {@code true} if the {@code String} is a token;
   881      *                          a token; <code>false</code> if it is not
   755      *          {@code false} if it is not
   882      */
   756      */
   883 
       
   884     private static boolean isToken(String value) {
   757     private static boolean isToken(String value) {
   885         int len = value.length();
   758         int len = value.length();
   886 
   759 
   887         for (int i = 0; i < len; i++) {
   760         for (int i = 0; i < len; i++) {
   888             char c = value.charAt(i);
   761             char c = value.charAt(i);
   891                 return false;
   764                 return false;
   892         }
   765         }
   893         return true;
   766         return true;
   894     }
   767     }
   895 
   768 
   896 
       
   897     /*
   769     /*
   898      * @param name      the name to be tested
   770      * @param  name
   899      * @return          <tt>true</tt> if the name is reserved by cookie
   771      *         the name to be tested
   900      *                  specification, <tt>false</tt> if it is not
   772      *
       
   773      * @return  {@code true} if the name is reserved by cookie specification,
       
   774      *          {@code false} if it is not
   901      */
   775      */
   902     private static boolean isReserved(String name) {
   776     private static boolean isReserved(String name) {
   903         if (name.equalsIgnoreCase("Comment")
   777         if (name.equalsIgnoreCase("Comment")
   904             || name.equalsIgnoreCase("CommentURL")      // rfc2965 only
   778             || name.equalsIgnoreCase("CommentURL")      // rfc2965 only
   905             || name.equalsIgnoreCase("Discard")         // rfc2965 only
   779             || name.equalsIgnoreCase("Discard")         // rfc2965 only
   917         }
   791         }
   918 
   792 
   919         return false;
   793         return false;
   920     }
   794     }
   921 
   795 
   922 
       
   923     /*
   796     /*
   924      * Parse header string to cookie object.
   797      * Parse header string to cookie object.
   925      *
   798      *
   926      * @param header    header string; should contain only one NAME=VALUE pair
   799      * @param  header
   927      *
   800      *         header string; should contain only one NAME=VALUE pair
   928      * @return          an HttpCookie being extracted
   801      *
   929      *
   802      * @return  an HttpCookie being extracted
   930      * @throws IllegalArgumentException if header string violates the cookie
   803      *
   931      *                                  specification
   804      * @throws  IllegalArgumentException
       
   805      *          if header string violates the cookie specification
   932      */
   806      */
   933     private static HttpCookie parseInternal(String header)
   807     private static HttpCookie parseInternal(String header)
   934     {
   808     {
   935         HttpCookie cookie = null;
   809         HttpCookie cookie = null;
   936         String namevaluePair = null;
   810         String namevaluePair = null;
   972         }
   846         }
   973 
   847 
   974         return cookie;
   848         return cookie;
   975     }
   849     }
   976 
   850 
   977 
       
   978     /*
   851     /*
   979      * assign cookie attribute value to attribute name;
   852      * assign cookie attribute value to attribute name;
   980      * use a map to simulate method dispatch
   853      * use a map to simulate method dispatch
   981      */
   854      */
   982     static interface CookieAttributeAssignor {
   855     static interface CookieAttributeAssignor {
   983             public void assign(HttpCookie cookie, String attrName, String attrValue);
   856             public void assign(HttpCookie cookie,
   984     }
   857                                String attrName,
   985     static java.util.Map<String, CookieAttributeAssignor> assignors = null;
   858                                String attrValue);
       
   859     }
       
   860     static final java.util.Map<String, CookieAttributeAssignor> assignors =
       
   861             new java.util.HashMap<>();
   986     static {
   862     static {
   987         assignors = new java.util.HashMap<String, CookieAttributeAssignor>();
   863         assignors.put("comment", new CookieAttributeAssignor() {
   988         assignors.put("comment", new CookieAttributeAssignor(){
   864                 public void assign(HttpCookie cookie,
   989                 public void assign(HttpCookie cookie, String attrName, String attrValue) {
   865                                    String attrName,
   990                     if (cookie.getComment() == null) cookie.setComment(attrValue);
   866                                    String attrValue) {
       
   867                     if (cookie.getComment() == null)
       
   868                         cookie.setComment(attrValue);
   991                 }
   869                 }
   992             });
   870             });
   993         assignors.put("commenturl", new CookieAttributeAssignor(){
   871         assignors.put("commenturl", new CookieAttributeAssignor() {
   994                 public void assign(HttpCookie cookie, String attrName, String attrValue) {
   872                 public void assign(HttpCookie cookie,
   995                     if (cookie.getCommentURL() == null) cookie.setCommentURL(attrValue);
   873                                    String attrName,
       
   874                                    String attrValue) {
       
   875                     if (cookie.getCommentURL() == null)
       
   876                         cookie.setCommentURL(attrValue);
   996                 }
   877                 }
   997             });
   878             });
   998         assignors.put("discard", new CookieAttributeAssignor(){
   879         assignors.put("discard", new CookieAttributeAssignor() {
   999                 public void assign(HttpCookie cookie, String attrName, String attrValue) {
   880                 public void assign(HttpCookie cookie,
       
   881                                    String attrName,
       
   882                                    String attrValue) {
  1000                     cookie.setDiscard(true);
   883                     cookie.setDiscard(true);
  1001                 }
   884                 }
  1002             });
   885             });
  1003         assignors.put("domain", new CookieAttributeAssignor(){
   886         assignors.put("domain", new CookieAttributeAssignor(){
  1004                 public void assign(HttpCookie cookie, String attrName, String attrValue) {
   887                 public void assign(HttpCookie cookie,
  1005                     if (cookie.getDomain() == null) cookie.setDomain(attrValue);
   888                                    String attrName,
       
   889                                    String attrValue) {
       
   890                     if (cookie.getDomain() == null)
       
   891                         cookie.setDomain(attrValue);
  1006                 }
   892                 }
  1007             });
   893             });
  1008         assignors.put("max-age", new CookieAttributeAssignor(){
   894         assignors.put("max-age", new CookieAttributeAssignor(){
  1009                 public void assign(HttpCookie cookie, String attrName, String attrValue) {
   895                 public void assign(HttpCookie cookie,
       
   896                                    String attrName,
       
   897                                    String attrValue) {
  1010                     try {
   898                     try {
  1011                         long maxage = Long.parseLong(attrValue);
   899                         long maxage = Long.parseLong(attrValue);
  1012                         if (cookie.getMaxAge() == MAX_AGE_UNSPECIFIED) cookie.setMaxAge(maxage);
   900                         if (cookie.getMaxAge() == MAX_AGE_UNSPECIFIED)
       
   901                             cookie.setMaxAge(maxage);
  1013                     } catch (NumberFormatException ignored) {
   902                     } catch (NumberFormatException ignored) {
  1014                         throw new IllegalArgumentException("Illegal cookie max-age attribute");
   903                         throw new IllegalArgumentException(
       
   904                                 "Illegal cookie max-age attribute");
  1015                     }
   905                     }
  1016                 }
   906                 }
  1017             });
   907             });
  1018         assignors.put("path", new CookieAttributeAssignor(){
   908         assignors.put("path", new CookieAttributeAssignor(){
  1019                 public void assign(HttpCookie cookie, String attrName, String attrValue) {
   909                 public void assign(HttpCookie cookie,
  1020                     if (cookie.getPath() == null) cookie.setPath(attrValue);
   910                                    String attrName,
       
   911                                    String attrValue) {
       
   912                     if (cookie.getPath() == null)
       
   913                         cookie.setPath(attrValue);
  1021                 }
   914                 }
  1022             });
   915             });
  1023         assignors.put("port", new CookieAttributeAssignor(){
   916         assignors.put("port", new CookieAttributeAssignor(){
  1024                 public void assign(HttpCookie cookie, String attrName, String attrValue) {
   917                 public void assign(HttpCookie cookie,
  1025                     if (cookie.getPortlist() == null) cookie.setPortlist(attrValue == null ? "" : attrValue);
   918                                    String attrName,
       
   919                                    String attrValue) {
       
   920                     if (cookie.getPortlist() == null)
       
   921                         cookie.setPortlist(attrValue == null ? "" : attrValue);
  1026                 }
   922                 }
  1027             });
   923             });
  1028         assignors.put("secure", new CookieAttributeAssignor(){
   924         assignors.put("secure", new CookieAttributeAssignor(){
  1029                 public void assign(HttpCookie cookie, String attrName, String attrValue) {
   925                 public void assign(HttpCookie cookie,
       
   926                                    String attrName,
       
   927                                    String attrValue) {
  1030                     cookie.setSecure(true);
   928                     cookie.setSecure(true);
  1031                 }
   929                 }
  1032             });
   930             });
  1033         assignors.put("httponly", new CookieAttributeAssignor(){
   931         assignors.put("httponly", new CookieAttributeAssignor(){
  1034                 public void assign(HttpCookie cookie, String attrName, String attrValue) {
   932                 public void assign(HttpCookie cookie,
       
   933                                    String attrName,
       
   934                                    String attrValue) {
  1035                     cookie.setHttpOnly(true);
   935                     cookie.setHttpOnly(true);
  1036                 }
   936                 }
  1037             });
   937             });
  1038         assignors.put("version", new CookieAttributeAssignor(){
   938         assignors.put("version", new CookieAttributeAssignor(){
  1039                 public void assign(HttpCookie cookie, String attrName, String attrValue) {
   939                 public void assign(HttpCookie cookie,
       
   940                                    String attrName,
       
   941                                    String attrValue) {
  1040                     try {
   942                     try {
  1041                         int version = Integer.parseInt(attrValue);
   943                         int version = Integer.parseInt(attrValue);
  1042                         cookie.setVersion(version);
   944                         cookie.setVersion(version);
  1043                     } catch (NumberFormatException ignored) {
   945                     } catch (NumberFormatException ignored) {
  1044                         // Just ignore bogus version, it will default to 0 or 1
   946                         // Just ignore bogus version, it will default to 0 or 1
  1045                     }
   947                     }
  1046                 }
   948                 }
  1047             });
   949             });
  1048         assignors.put("expires", new CookieAttributeAssignor(){ // Netscape only
   950         assignors.put("expires", new CookieAttributeAssignor(){ // Netscape only
  1049                 public void assign(HttpCookie cookie, String attrName, String attrValue) {
   951                 public void assign(HttpCookie cookie,
       
   952                                    String attrName,
       
   953                                    String attrValue) {
  1050                     if (cookie.getMaxAge() == MAX_AGE_UNSPECIFIED) {
   954                     if (cookie.getMaxAge() == MAX_AGE_UNSPECIFIED) {
  1051                         cookie.setMaxAge(cookie.expiryDate2DeltaSeconds(attrValue));
   955                         cookie.setMaxAge(cookie.expiryDate2DeltaSeconds(attrValue));
  1052                     }
   956                     }
  1053                 }
   957                 }
  1054             });
   958             });
  1055     }
   959     }
  1056     private static void assignAttribute(HttpCookie cookie,
   960     private static void assignAttribute(HttpCookie cookie,
  1057                                        String attrName,
   961                                         String attrName,
  1058                                        String attrValue)
   962                                         String attrValue)
  1059     {
   963     {
  1060         // strip off the surrounding "-sign if there's any
   964         // strip off the surrounding "-sign if there's any
  1061         attrValue = stripOffSurroundingQuote(attrValue);
   965         attrValue = stripOffSurroundingQuote(attrValue);
  1062 
   966 
  1063         CookieAttributeAssignor assignor = assignors.get(attrName.toLowerCase());
   967         CookieAttributeAssignor assignor = assignors.get(attrName.toLowerCase());
  1071     /*
   975     /*
  1072      * Constructs a string representation of this cookie. The string format is
   976      * Constructs a string representation of this cookie. The string format is
  1073      * as Netscape spec, but without leading "Cookie:" token.
   977      * as Netscape spec, but without leading "Cookie:" token.
  1074      */
   978      */
  1075     private String toNetscapeHeaderString() {
   979     private String toNetscapeHeaderString() {
  1076         StringBuilder sb = new StringBuilder();
   980         return getName() + "=" + getValue();
  1077 
       
  1078         sb.append(getName() + "=" + getValue());
       
  1079 
       
  1080         return sb.toString();
       
  1081     }
   981     }
  1082 
   982 
  1083     /*
   983     /*
  1084      * Constructs a string representation of this cookie. The string format is
   984      * Constructs a string representation of this cookie. The string format is
  1085      * as RFC 2965/2109, but without leading "Cookie:" token.
   985      * as RFC 2965/2109, but without leading "Cookie:" token.
  1099     }
   999     }
  1100 
  1000 
  1101     static final TimeZone GMT = TimeZone.getTimeZone("GMT");
  1001     static final TimeZone GMT = TimeZone.getTimeZone("GMT");
  1102 
  1002 
  1103     /*
  1003     /*
  1104      * @param dateString        a date string in one of the formats
  1004      * @param  dateString
  1105      *                          defined in Netscape cookie spec
  1005      *         a date string in one of the formats defined in Netscape cookie spec
  1106      *
  1006      *
  1107      * @return                  delta seconds between this cookie's creation
  1007      * @return  delta seconds between this cookie's creation time and the time
  1108      *                          time and the time specified by dateString
  1008      *          specified by dateString
  1109      */
  1009      */
  1110     private long expiryDate2DeltaSeconds(String dateString) {
  1010     private long expiryDate2DeltaSeconds(String dateString) {
  1111         for (int i = 0; i < COOKIE_DATE_FORMATS.length; i++) {
  1011         for (int i = 0; i < COOKIE_DATE_FORMATS.length; i++) {
  1112             SimpleDateFormat df = new SimpleDateFormat(COOKIE_DATE_FORMATS[i], Locale.US);
  1012             SimpleDateFormat df = new SimpleDateFormat(COOKIE_DATE_FORMATS[i],
       
  1013                                                        Locale.US);
  1113             df.setTimeZone(GMT);
  1014             df.setTimeZone(GMT);
  1114             try {
  1015             try {
  1115                 Date date = df.parse(dateString);
  1016                 Date date = df.parse(dateString);
  1116                 return (date.getTime() - whenCreated) / 1000;
  1017                 return (date.getTime() - whenCreated) / 1000;
  1117             } catch (Exception e) {
  1018             } catch (Exception e) {
  1118                 // Ignore, try the next date format
  1019                 // Ignore, try the next date format
  1119             }
  1020             }
  1120         }
  1021         }
  1121         return 0;
  1022         return 0;
  1122     }
  1023     }
  1123 
       
  1124 
       
  1125 
  1024 
  1126     /*
  1025     /*
  1127      * try to guess the cookie version through set-cookie header string
  1026      * try to guess the cookie version through set-cookie header string
  1128      */
  1027      */
  1129     private static int guessCookieVersion(String header) {
  1028     private static int guessCookieVersion(String header) {
  1182      * Split cookie header string according to rfc 2965:
  1081      * Split cookie header string according to rfc 2965:
  1183      *   1) split where it is a comma;
  1082      *   1) split where it is a comma;
  1184      *   2) but not the comma surrounding by double-quotes, which is the comma
  1083      *   2) but not the comma surrounding by double-quotes, which is the comma
  1185      *      inside port list or embeded URIs.
  1084      *      inside port list or embeded URIs.
  1186      *
  1085      *
  1187      * @param header            the cookie header string to split
  1086      * @param  header
  1188      *
  1087      *         the cookie header string to split
  1189      * @return                  list of strings; never null
  1088      *
  1190      *
  1089      * @return  list of strings; never null
  1191      */
  1090      */
  1192     private static List<String> splitMultiCookies(String header) {
  1091     private static List<String> splitMultiCookies(String header) {
  1193         List<String> cookies = new java.util.ArrayList<String>();
  1092         List<String> cookies = new java.util.ArrayList<String>();
  1194         int quoteCount = 0;
  1093         int quoteCount = 0;
  1195         int p, q;
  1094         int p, q;
  1196 
  1095 
  1197         for (p = 0, q = 0; p < header.length(); p++) {
  1096         for (p = 0, q = 0; p < header.length(); p++) {
  1198             char c = header.charAt(p);
  1097             char c = header.charAt(p);
  1199             if (c == '"') quoteCount++;
  1098             if (c == '"') quoteCount++;
  1200             if (c == ',' && (quoteCount % 2 == 0)) {      // it is comma and not surrounding by double-quotes
  1099             if (c == ',' && (quoteCount % 2 == 0)) {
       
  1100                 // it is comma and not surrounding by double-quotes
  1201                 cookies.add(header.substring(q, p));
  1101                 cookies.add(header.substring(q, p));
  1202                 q = p + 1;
  1102                 q = p + 1;
  1203             }
  1103             }
  1204         }
  1104         }
  1205 
  1105