| // Licensed to the Software Freedom Conservancy (SFC) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The SFC licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| |
| package org.openqa.selenium; |
| |
| import java.io.Serializable; |
| import java.text.SimpleDateFormat; |
| import java.util.Date; |
| import java.util.Map; |
| import java.util.Objects; |
| import java.util.TimeZone; |
| import java.util.TreeMap; |
| import org.jspecify.annotations.Nullable; |
| import org.openqa.selenium.internal.Require; |
| |
| public class Cookie implements Serializable { |
| private static final long serialVersionUID = 4115876353625612383L; |
| |
| private final String name; |
| private final String value; |
| private final String path; |
| private final @Nullable String domain; |
| private final @Nullable Date expiry; |
| private final boolean isSecure; |
| private final boolean isHttpOnly; |
| private final @Nullable String sameSite; |
| |
| /** |
| * Creates an insecure non-httpOnly cookie with no domain specified. |
| * |
| * @param name The name of the cookie; may not be null or an empty string. |
| * @param value The cookie value; may not be null. |
| * @param path The path the cookie is visible to. If left blank or set to null, will be set to |
| * "/". |
| * @param expiry The cookie's expiration date; may be null. |
| * @see #Cookie(String, String, String, String, Date) |
| */ |
| public Cookie(String name, String value, @Nullable String path, @Nullable Date expiry) { |
| this(name, value, null, path, expiry); |
| } |
| |
| /** |
| * Creates an insecure non-httpOnly cookie. |
| * |
| * @param name The name of the cookie; may not be null or an empty string. |
| * @param value The cookie value; may not be null. |
| * @param domain The domain the cookie is visible to. |
| * @param path The path the cookie is visible to. If left blank or set to null, will be set to |
| * "/". |
| * @param expiry The cookie's expiration date; may be null. |
| * @see #Cookie(String, String, String, String, Date, boolean) |
| */ |
| public Cookie( |
| String name, |
| String value, |
| @Nullable String domain, |
| @Nullable String path, |
| @Nullable Date expiry) { |
| this(name, value, domain, path, expiry, false); |
| } |
| |
| /** |
| * Creates a non-httpOnly cookie. |
| * |
| * @param name The name of the cookie; may not be null or an empty string. |
| * @param value The cookie value; may not be null. |
| * @param domain The domain the cookie is visible to. |
| * @param path The path the cookie is visible to. If left blank or set to null, will be set to |
| * "/". |
| * @param expiry The cookie's expiration date; may be null. |
| * @param isSecure Whether this cookie requires a secure connection. |
| */ |
| public Cookie( |
| String name, |
| String value, |
| @Nullable String domain, |
| @Nullable String path, |
| @Nullable Date expiry, |
| boolean isSecure) { |
| this(name, value, domain, path, expiry, isSecure, false); |
| } |
| |
| /** |
| * Creates a cookie. |
| * |
| * @param name The name of the cookie; may not be null or an empty string. |
| * @param value The cookie value; may not be null. |
| * @param domain The domain the cookie is visible to. |
| * @param path The path the cookie is visible to. If left blank or set to null, will be set to |
| * "/". |
| * @param expiry The cookie's expiration date; may be null. |
| * @param isSecure Whether this cookie requires a secure connection. |
| * @param isHttpOnly Whether this cookie is a httpOnly cooke. |
| */ |
| public Cookie( |
| String name, |
| String value, |
| @Nullable String domain, |
| @Nullable String path, |
| @Nullable Date expiry, |
| boolean isSecure, |
| boolean isHttpOnly) { |
| this(name, value, domain, path, expiry, isSecure, isHttpOnly, null); |
| } |
| |
| /** |
| * Creates a cookie. |
| * |
| * @param name The name of the cookie; may not be null or an empty string. |
| * @param value The cookie value; may not be null. |
| * @param domain The domain the cookie is visible to. |
| * @param path The path the cookie is visible to. If left blank or set to null, will be set to |
| * "/". |
| * @param expiry The cookie's expiration date; may be null. |
| * @param isSecure Whether this cookie requires a secure connection. |
| * @param isHttpOnly Whether this cookie is a httpOnly cookie. |
| * @param sameSite The samesite attribute of this cookie; e.g. None, Lax, Strict. |
| */ |
| public Cookie( |
| String name, |
| String value, |
| @Nullable String domain, |
| @Nullable String path, |
| @Nullable Date expiry, |
| boolean isSecure, |
| boolean isHttpOnly, |
| @Nullable String sameSite) { |
| this.name = name; |
| this.value = value; |
| this.path = path == null || path.isEmpty() ? "/" : path; |
| |
| this.domain = stripPort(domain); |
| this.isSecure = isSecure; |
| this.isHttpOnly = isHttpOnly; |
| |
| if (expiry != null) { |
| // Expiration date is specified in seconds since (UTC) epoch time, so truncate the date. |
| this.expiry = new Date(expiry.getTime() / 1000 * 1000); |
| } else { |
| this.expiry = null; |
| } |
| |
| this.sameSite = sameSite; |
| } |
| |
| /** |
| * Create a cookie for the default path with the given name and value with no expiry set. |
| * |
| * @param name The cookie's name |
| * @param value The cookie's value |
| */ |
| public Cookie(String name, String value) { |
| this(name, value, "/", null); |
| } |
| |
| /** |
| * Create a cookie. |
| * |
| * @param name The cookie's name |
| * @param value The cookie's value |
| * @param path The path the cookie is for |
| */ |
| public Cookie(String name, String value, String path) { |
| this(name, value, path, null); |
| } |
| |
| public String getName() { |
| return name; |
| } |
| |
| public String getValue() { |
| return value; |
| } |
| |
| public @Nullable String getDomain() { |
| return domain; |
| } |
| |
| public String getPath() { |
| return path; |
| } |
| |
| public boolean isSecure() { |
| return isSecure; |
| } |
| |
| public boolean isHttpOnly() { |
| return isHttpOnly; |
| } |
| |
| public @Nullable Date getExpiry() { |
| return expiry == null ? null : new Date(expiry.getTime()); |
| } |
| |
| public @Nullable String getSameSite() { |
| return sameSite; |
| } |
| |
| private static @Nullable String stripPort(@Nullable String domain) { |
| return (domain == null) ? null : domain.split(":")[0]; |
| } |
| |
| public void validate() { |
| Require.nonEmpty("Name", name); |
| Require.nonNull("Value", value); |
| Require.nonNull("Path", path); |
| |
| if (name.indexOf(';') != -1) { |
| throw new IllegalArgumentException("Cookie names cannot contain a ';': " + name); |
| } |
| |
| if (domain != null && domain.contains(":")) { |
| throw new IllegalArgumentException("Domain should not contain a port: " + domain); |
| } |
| } |
| |
| /** |
| * JSON object keys are defined in |
| * https://w3c.github.io/webdriver/#dfn-table-for-cookie-conversion. |
| */ |
| public Map<String, Object> toJson() { |
| Map<String, Object> toReturn = new TreeMap<>(); |
| |
| if (getDomain() != null) { |
| toReturn.put("domain", getDomain()); |
| } |
| |
| if (getExpiry() != null) { |
| toReturn.put("expiry", getExpiry()); |
| } |
| |
| if (getName() != null) { |
| toReturn.put("name", getName()); |
| } |
| |
| if (getPath() != null) { |
| toReturn.put("path", getPath()); |
| } |
| |
| if (getValue() != null) { |
| toReturn.put("value", getValue()); |
| } |
| |
| toReturn.put("secure", isSecure()); |
| toReturn.put("httpOnly", isHttpOnly()); |
| |
| if (getSameSite() != null) { |
| toReturn.put("sameSite", getSameSite()); |
| } |
| |
| return toReturn; |
| } |
| |
| @Override |
| public String toString() { |
| SimpleDateFormat sdf = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z"); |
| sdf.setTimeZone(TimeZone.getTimeZone("GMT")); |
| return name |
| + "=" |
| + value |
| + (expiry == null ? "" : "; expires=" + sdf.format(expiry)) |
| + ("".equals(path) ? "" : "; path=" + path) |
| + (domain == null ? "" : "; domain=" + domain) |
| + (isSecure ? ";secure;" : "") |
| + (sameSite == null ? "" : "; sameSite=" + sameSite); |
| } |
| |
| /** Two cookies are equal if the name and value match */ |
| @Override |
| public boolean equals(@Nullable Object o) { |
| if (this == o) { |
| return true; |
| } |
| if (!(o instanceof Cookie)) { |
| return false; |
| } |
| |
| Cookie cookie = (Cookie) o; |
| |
| if (!name.equals(cookie.name)) { |
| return false; |
| } |
| return Objects.equals(value, cookie.value); |
| } |
| |
| @Override |
| public int hashCode() { |
| return name.hashCode(); |
| } |
| |
| public static class Builder { |
| |
| private final String name; |
| private final String value; |
| private @Nullable String path; |
| private @Nullable String domain; |
| private @Nullable Date expiry; |
| private boolean secure; |
| private boolean httpOnly; |
| private @Nullable String sameSite; |
| |
| public Builder(String name, String value) { |
| this.name = name; |
| this.value = value; |
| } |
| |
| public Builder domain(@Nullable String host) { |
| this.domain = stripPort(host); |
| return this; |
| } |
| |
| public Builder path(@Nullable String path) { |
| this.path = path; |
| return this; |
| } |
| |
| public Builder expiresOn(@Nullable Date expiry) { |
| this.expiry = expiry == null ? null : new Date(expiry.getTime()); |
| return this; |
| } |
| |
| public Builder isSecure(boolean secure) { |
| this.secure = secure; |
| return this; |
| } |
| |
| public Builder isHttpOnly(boolean httpOnly) { |
| this.httpOnly = httpOnly; |
| return this; |
| } |
| |
| public Builder sameSite(@Nullable String sameSite) { |
| this.sameSite = sameSite; |
| return this; |
| } |
| |
| public Cookie build() { |
| return new Cookie(name, value, domain, path, expiry, secure, httpOnly, sameSite); |
| } |
| } |
| } |