View Javadoc
1   /*
2    * Copyright (c) 2002-2026 Gargoyle Software Inc.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * https://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software
10   * distributed under the License is distributed on an "AS IS" BASIS,
11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   * See the License for the specific language governing permissions and
13   * limitations under the License.
14   */
15  package org.htmlunit.http;
16  
17  import java.io.Serializable;
18  import java.util.Date;
19  import java.util.Locale;
20  import java.util.Objects;
21  
22  import org.apache.commons.lang3.builder.EqualsBuilder;
23  
24  /**
25   * A cookie. This class is immutable.
26   *
27   * @author Daniel Gredler
28   * @author Nicolas Belisle
29   * @author Ahmed Ashour
30   * @author Ronald Brill
31   */
32  public class Cookie implements Serializable {
33  
34      private final String domain_;
35      private final String name_;
36      private final String value_;
37      private final String path_;
38      private final Date expiryDate_;
39      private final boolean isSecure_;
40      private final boolean isHttpOnly_;
41      private final String samesite_;
42  
43      /**
44       * Creates a new cookie with the specified name and value which applies to the specified domain.
45       * The new cookie applies to all paths, never expires and is not secure.
46       * @param domain the domain to which this cookie applies
47       * @param name the cookie name
48       * @param value the cookie name
49       */
50      public Cookie(final String domain, final String name, final String value) {
51          this(domain, name, value, null, null, false);
52      }
53  
54      /**
55       * Creates a new cookie with the specified name and value which applies to the specified domain,
56       * the specified path, and expires on the specified date.
57       * @param domain the domain to which this cookie applies
58       * @param name the cookie name
59       * @param value the cookie name
60       * @param path the path to which this cookie applies
61       * @param expires the date on which this cookie expires
62       * @param secure whether or not this cookie is secure (i.e. HTTPS vs HTTP)
63       */
64      public Cookie(final String domain, final String name, final String value, final String path, final Date expires,
65          final boolean secure) {
66          this(domain, name, value, path, expires, secure, false, null);
67      }
68  
69      /**
70       * Creates a new cookie with the specified name and value which applies to the specified domain,
71       * the specified path, and expires on the specified date.
72       * @param domain the domain to which this cookie applies
73       * @param name the cookie name
74       * @param value the cookie name
75       * @param path the path to which this cookie applies
76       * @param expires the date on which this cookie expires
77       * @param secure whether or not this cookie is secure (i.e. HTTPS vs HTTP)
78       * @param httpOnly whether or not this cookie should be only used for HTTP(S) headers
79       */
80      public Cookie(final String domain, final String name, final String value, final String path, final Date expires,
81          final boolean secure, final boolean httpOnly) {
82          this(domain, name, value, path, expires, secure, httpOnly, null);
83      }
84  
85      /**
86       * Creates a new cookie with the specified name and value which applies to the specified domain,
87       * the specified path, and expires on the specified date.
88       * @param domain the domain to which this cookie applies
89       * @param name the cookie name
90       * @param value the cookie name
91       * @param path the path to which this cookie applies
92       * @param expires the date on which this cookie expires
93       * @param secure whether or not this cookie is secure (i.e. HTTPS vs HTTP)
94       * @param httpOnly whether or not this cookie should be only used for HTTP(S) headers
95       * @param sameSite the sameSite attribute
96       */
97      public Cookie(final String domain, final String name, final String value, final String path, final Date expires,
98          final boolean secure, final boolean httpOnly, final String sameSite) {
99          if (domain == null) {
100             throw new IllegalArgumentException("Cookie domain must be specified");
101         }
102 
103         domain_ = domain.toLowerCase(Locale.ROOT);
104         name_ = name;
105         if (value == null) {
106             value_ = "";
107         }
108         else {
109             value_ = value;
110         }
111         path_ = path;
112         expiryDate_ = expires;
113 
114         isSecure_ = secure;
115         isHttpOnly_ = httpOnly;
116 
117         samesite_ = sameSite;
118     }
119 
120     /**
121      * Creates a new cookie with the specified name and value which applies to the specified domain,
122      * the specified path, and expires after the specified amount of time.
123      * @param domain the domain to which this cookie applies
124      * @param name the cookie name
125      * @param value the cookie name
126      * @param path the path to which this cookie applies
127      * @param maxAge the number of seconds for which this cookie is valid; <code>-1</code> indicates that the
128      *        cookie should never expire; other negative numbers are not allowed
129      * @param secure whether or not this cookie is secure (i.e. HTTPS vs HTTP)
130      */
131     public Cookie(final String domain, final String name, final String value, final String path, final int maxAge,
132         final boolean secure) {
133         this(domain, name, value, path, convertToExpiryDate(maxAge), secure);
134     }
135 
136     private static Date convertToExpiryDate(final int maxAge) {
137         if (maxAge < -1) {
138             throw new IllegalArgumentException("invalid max age:  " + maxAge);
139         }
140 
141         if (maxAge >= 0) {
142             return new Date(System.currentTimeMillis() + (maxAge * 1000L));
143         }
144 
145         return null;
146     }
147 
148     /**
149      * Returns the cookie name.
150      * @return the cookie name
151      */
152     public String getName() {
153         return name_;
154     }
155 
156     /**
157      * Returns the cookie value.
158      * @return the cookie value
159      */
160     public String getValue() {
161         return value_;
162     }
163 
164     /**
165      * Returns the domain to which this cookie applies ({@code null} for all domains).
166      * @return the domain to which this cookie applies ({@code null} for all domains)
167      */
168     public String getDomain() {
169         return domain_;
170     }
171 
172     /**
173      * Returns the path to which this cookie applies ({@code null} for all paths).
174      * @return the path to which this cookie applies ({@code null} for all paths)
175      */
176     public String getPath() {
177         return path_;
178     }
179 
180     /**
181      * Returns the date on which this cookie expires ({@code null} if it never expires).
182      * @return the date on which this cookie expires ({@code null} if it never expires)
183      */
184     public Date getExpires() {
185         return expiryDate_;
186     }
187 
188     /**
189      * Returns whether or not this cookie is secure (i.e. HTTPS vs HTTP).
190      * @return whether or not this cookie is secure (i.e. HTTPS vs HTTP)
191      */
192     public boolean isSecure() {
193         return isSecure_;
194     }
195 
196     /**
197      * Returns whether or not this cookie is HttpOnly (i.e. not available in JS).
198      * @see <a href="http://en.wikipedia.org/wiki/HTTP_cookie#Secure_and_HttpOnly">Wikipedia</a>
199      * @return whether or not this cookie is HttpOnly (i.e. not available in JS).
200      */
201     public boolean isHttpOnly() {
202         return isHttpOnly_;
203     }
204 
205     /**
206      * @return the SameSite value or {@code null} if not set.
207      */
208     public String getSameSite() {
209         return samesite_;
210     }
211 
212     /**
213      * {@inheritDoc}
214      */
215     @Override
216     public String toString() {
217         return getName() + "=" + getValue()
218             + (getDomain() == null ? "" : ";domain=" + getDomain())
219             + (getPath() == null ? "" : ";path=" + getPath())
220             + (getExpires() == null ? "" : ";expires=" + getExpires())
221             + (isSecure() ? ";secure" : "")
222             + (isHttpOnly() ? ";httpOnly" : "")
223             + (getSameSite() == null ? "" : ";sameSite=" + getSameSite());
224     }
225 
226     /**
227      * {@inheritDoc}
228      */
229     @Override
230     public boolean equals(final Object o) {
231         if (!(o instanceof Cookie other)) {
232             return false;
233         }
234         final String path = getPath() == null ? "/" : getPath();
235         final String otherPath = other.getPath() == null ? "/" : other.getPath();
236         return new EqualsBuilder()
237                     .append(getName(), other.getName())
238                     .append(getDomain(), other.getDomain())
239                     .append(path, otherPath)
240                     .isEquals();
241     }
242 
243     /**
244      * {@inheritDoc}
245      */
246     @Override
247     public int hashCode() {
248         final String path = getPath() == null ? "/" : getPath();
249         return Objects.hash(getName(), getDomain(), path);
250     }
251 }