View Javadoc
1   /*
2    * Copyright (c) 2002-2025 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.util;
16  
17  import java.io.Serializable;
18  import java.util.Date;
19  
20  import org.apache.commons.lang3.builder.EqualsBuilder;
21  import org.apache.commons.lang3.builder.HashCodeBuilder;
22  import org.apache.http.cookie.ClientCookie;
23  import org.apache.http.impl.cookie.BasicClientCookie;
24  
25  /**
26   * A cookie. This class is immutable.
27   *
28   * @author Daniel Gredler
29   * @author Nicolas Belisle
30   * @author Ahmed Ashour
31   * @author Ronald Brill
32   */
33  public class Cookie implements Serializable {
34  
35      private final ClientCookie httpClientCookie_;
36  
37      /**
38       * Creates a new cookie with the specified name and value which applies to the specified domain.
39       * The new cookie applies to all paths, never expires and is not secure.
40       * @param domain the domain to which this cookie applies
41       * @param name the cookie name
42       * @param value the cookie name
43       */
44      public Cookie(final String domain, final String name, final String value) {
45          this(domain, name, value, null, null, false);
46      }
47  
48      /**
49       * Creates a new cookie with the specified name and value which applies to the specified domain,
50       * the specified path, and expires on the specified date.
51       * @param domain the domain to which this cookie applies
52       * @param name the cookie name
53       * @param value the cookie name
54       * @param path the path to which this cookie applies
55       * @param expires the date on which this cookie expires
56       * @param secure whether or not this cookie is secure (i.e. HTTPS vs HTTP)
57       */
58      public Cookie(final String domain, final String name, final String value, final String path, final Date expires,
59          final boolean secure) {
60          this(domain, name, value, path, expires, secure, false, null);
61      }
62  
63      /**
64       * Creates a new cookie with the specified name and value which applies to the specified domain,
65       * the specified path, and expires on the specified date.
66       * @param domain the domain to which this cookie applies
67       * @param name the cookie name
68       * @param value the cookie name
69       * @param path the path to which this cookie applies
70       * @param expires the date on which this cookie expires
71       * @param secure whether or not this cookie is secure (i.e. HTTPS vs HTTP)
72       * @param httpOnly whether or not this cookie should be only used for HTTP(S) headers
73       */
74      public Cookie(final String domain, final String name, final String value, final String path, final Date expires,
75          final boolean secure, final boolean httpOnly) {
76          this(domain, name, value, path, expires, secure, httpOnly, null);
77      }
78  
79      /**
80       * Creates a new cookie with the specified name and value which applies to the specified domain,
81       * the specified path, and expires on the specified date.
82       * @param domain the domain to which this cookie applies
83       * @param name the cookie name
84       * @param value the cookie name
85       * @param path the path to which this cookie applies
86       * @param expires the date on which this cookie expires
87       * @param secure whether or not this cookie is secure (i.e. HTTPS vs HTTP)
88       * @param httpOnly whether or not this cookie should be only used for HTTP(S) headers
89       * @param sameSite the sameSite attribute
90       */
91      public Cookie(final String domain, final String name, final String value, final String path, final Date expires,
92          final boolean secure, final boolean httpOnly, final String sameSite) {
93          if (domain == null) {
94              throw new IllegalArgumentException("Cookie domain must be specified");
95          }
96  
97          final BasicClientCookie cookie = new BasicClientCookie(name, value == null ? "" : value);
98  
99          cookie.setDomain(domain);
100         // BasicDomainHandler.match(Cookie, CookieOrigin) checks the attib also (see #333)
101         cookie.setAttribute(ClientCookie.DOMAIN_ATTR, domain);
102 
103         cookie.setPath(path);
104         if (expires != null) {
105             cookie.setExpiryDate(expires);
106         }
107         cookie.setSecure(secure);
108         if (httpOnly) {
109             cookie.setAttribute("httponly", "true");
110         }
111 
112         if (sameSite != null) {
113             cookie.setAttribute("samesite", sameSite);
114         }
115 
116         httpClientCookie_ = cookie;
117     }
118 
119     /**
120      * Creates a new HtmlUnit cookie from the HttpClient cookie provided.
121      * @param clientCookie the HttpClient cookie
122      */
123     public Cookie(final ClientCookie clientCookie) {
124         httpClientCookie_ = clientCookie;
125     }
126 
127     /**
128      * Creates a new cookie with the specified name and value which applies to the specified domain,
129      * the specified path, and expires after the specified amount of time.
130      * @param domain the domain to which this cookie applies
131      * @param name the cookie name
132      * @param value the cookie name
133      * @param path the path to which this cookie applies
134      * @param maxAge the number of seconds for which this cookie is valid; <code>-1</code> indicates that the
135      *        cookie should never expire; other negative numbers are not allowed
136      * @param secure whether or not this cookie is secure (i.e. HTTPS vs HTTP)
137      */
138     public Cookie(final String domain, final String name, final String value, final String path, final int maxAge,
139         final boolean secure) {
140         this(domain, name, value, path, convertToExpiryDate(maxAge), secure);
141     }
142 
143     private static Date convertToExpiryDate(final int maxAge) {
144         if (maxAge < -1) {
145             throw new IllegalArgumentException("invalid max age:  " + maxAge);
146         }
147 
148         if (maxAge >= 0) {
149             return new Date(System.currentTimeMillis() + (maxAge * 1000L));
150         }
151 
152         return null;
153     }
154 
155     /**
156      * Returns the cookie name.
157      * @return the cookie name
158      */
159     public String getName() {
160         return httpClientCookie_.getName();
161     }
162 
163     /**
164      * Returns the cookie value.
165      * @return the cookie value
166      */
167     public String getValue() {
168         return httpClientCookie_.getValue();
169     }
170 
171     /**
172      * Returns the domain to which this cookie applies ({@code null} for all domains).
173      * @return the domain to which this cookie applies ({@code null} for all domains)
174      */
175     public String getDomain() {
176         return httpClientCookie_.getDomain();
177     }
178 
179     /**
180      * Returns the path to which this cookie applies ({@code null} for all paths).
181      * @return the path to which this cookie applies ({@code null} for all paths)
182      */
183     public String getPath() {
184         return httpClientCookie_.getPath();
185     }
186 
187     /**
188      * Returns the date on which this cookie expires ({@code null} if it never expires).
189      * @return the date on which this cookie expires ({@code null} if it never expires)
190      */
191     public Date getExpires() {
192         return httpClientCookie_.getExpiryDate();
193     }
194 
195     /**
196      * Returns whether or not this cookie is secure (i.e. HTTPS vs HTTP).
197      * @return whether or not this cookie is secure (i.e. HTTPS vs HTTP)
198      */
199     public boolean isSecure() {
200         return httpClientCookie_.isSecure();
201     }
202 
203     /**
204      * Returns whether or not this cookie is HttpOnly (i.e. not available in JS).
205      * @see <a href="http://en.wikipedia.org/wiki/HTTP_cookie#Secure_and_HttpOnly">Wikipedia</a>
206      * @return whether or not this cookie is HttpOnly (i.e. not available in JS).
207      */
208     public boolean isHttpOnly() {
209         return httpClientCookie_.getAttribute("httponly") != null;
210     }
211 
212     /**
213      * @return the SameSite value or {@code null} if not set.
214      */
215     public String getSameSite() {
216         return httpClientCookie_.getAttribute("samesite");
217     }
218 
219     /**
220      * {@inheritDoc}
221      */
222     @Override
223     public String toString() {
224         return getName() + "=" + getValue()
225             + (getDomain() == null ? "" : ";domain=" + getDomain())
226             + (getPath() == null ? "" : ";path=" + getPath())
227             + (getExpires() == null ? "" : ";expires=" + getExpires())
228             + (isSecure() ? ";secure" : "")
229             + (isHttpOnly() ? ";httpOnly" : "")
230             + (getSameSite() == null ? "" : ";sameSite=" + getSameSite());
231     }
232 
233     /**
234      * {@inheritDoc}
235      */
236     @Override
237     public boolean equals(final Object o) {
238         if (!(o instanceof Cookie)) {
239             return false;
240         }
241         final Cookie other = (Cookie) o;
242         final String path = getPath() == null ? "/" : getPath();
243         final String otherPath = other.getPath() == null ? "/" : other.getPath();
244         return new EqualsBuilder()
245                     .append(getName(), other.getName())
246                     .append(getDomain(), other.getDomain())
247                     .append(path, otherPath)
248                     .isEquals();
249     }
250 
251     /**
252      * {@inheritDoc}
253      */
254     @Override
255     public int hashCode() {
256         final String path = getPath() == null ? "/" : getPath();
257         return new HashCodeBuilder()
258                     .append(getName())
259                     .append(getDomain())
260                     .append(path)
261                     .toHashCode();
262     }
263 
264     /**
265      * Converts this cookie to an HttpClient cookie.
266      * @return an HttpClient version of this cookie
267      */
268     public org.apache.http.cookie.Cookie toHttpClient() {
269         return httpClientCookie_;
270     }
271 }