org.eclipse.jetty.http.HttpCookie Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aem-sdk-api Show documentation
Show all versions of aem-sdk-api Show documentation
The Adobe Experience Manager SDK
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.http;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
import javax.servlet.ServletContext;
import org.eclipse.jetty.util.QuotedStringTokenizer;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
// TODO consider replacing this with java.net.HttpCookie (once it supports RFC6265)@deprecated The Eclipse Jetty and Apache Felix Http Jetty packages are no longer supported.
@Deprecated(since = "2021-05-27")
public class HttpCookie {
private static final Logger LOG = Log.getLogger(HttpCookie.class);
private static final String __COOKIE_DELIM = "\",;\\ \t";
private static final String __01Jan1970_COOKIE = DateGenerator.formatCookieDate(0).trim();
/**
* If this string is found within the comment parsed with {@link #isHttpOnlyInComment(String)} the check will return true
*/
public static final String HTTP_ONLY_COMMENT = "__HTTP_ONLY__";
/**
* These strings are used by {@link #getSameSiteFromComment(String)} to check for a SameSite specifier in the comment
*/
private static final String SAME_SITE_COMMENT = "__SAME_SITE_";
public static final String SAME_SITE_NONE_COMMENT = SAME_SITE_COMMENT + "NONE__";
public static final String SAME_SITE_LAX_COMMENT = SAME_SITE_COMMENT + "LAX__";
public static final String SAME_SITE_STRICT_COMMENT = SAME_SITE_COMMENT + "STRICT__";
/**
* Name of context attribute with default SameSite cookie value
*/
public static final String SAME_SITE_DEFAULT_ATTRIBUTE = "org.eclipse.jetty.cookie.sameSiteDefault";
public enum SameSite {
NONE("None"), STRICT("Strict"), LAX("Lax");
private String attributeValue;
SameSite(String attributeValue) {
this.attributeValue = attributeValue;
}
public String getAttributeValue() {
return this.attributeValue;
}
}
private final String _name;
private final String _value;
private final String _comment;
private final String _domain;
private final long _maxAge;
private final String _path;
private final boolean _secure;
private final int _version;
private final boolean _httpOnly;
private final long _expiration;
private final SameSite _sameSite;
public HttpCookie(String name, String value) {
this(name, value, -1);
}
public HttpCookie(String name, String value, String domain, String path) {
this(name, value, domain, path, -1, false, false);
}
public HttpCookie(String name, String value, long maxAge) {
this(name, value, null, null, maxAge, false, false);
}
public HttpCookie(String name, String value, String domain, String path, long maxAge, boolean httpOnly, boolean secure) {
this(name, value, domain, path, maxAge, httpOnly, secure, null, 0);
}
public HttpCookie(String name, String value, String domain, String path, long maxAge, boolean httpOnly, boolean secure, String comment, int version) {
this(name, value, domain, path, maxAge, httpOnly, secure, comment, version, null);
}
public HttpCookie(String name, String value, String domain, String path, long maxAge, boolean httpOnly, boolean secure, String comment, int version, SameSite sameSite) {
_name = name;
_value = value;
_domain = domain;
_path = path;
_maxAge = maxAge;
_httpOnly = httpOnly;
_secure = secure;
_comment = comment;
_version = version;
_expiration = maxAge < 0 ? -1 : System.nanoTime() + TimeUnit.SECONDS.toNanos(maxAge);
_sameSite = sameSite;
}
public HttpCookie(String setCookie) {
List cookies = java.net.HttpCookie.parse(setCookie);
if (cookies.size() != 1)
throw new IllegalStateException();
java.net.HttpCookie cookie = cookies.get(0);
_name = cookie.getName();
_value = cookie.getValue();
_domain = cookie.getDomain();
_path = cookie.getPath();
_maxAge = cookie.getMaxAge();
_httpOnly = cookie.isHttpOnly();
_secure = cookie.getSecure();
_comment = cookie.getComment();
_version = cookie.getVersion();
_expiration = _maxAge < 0 ? -1 : System.nanoTime() + TimeUnit.SECONDS.toNanos(_maxAge);
// support for SameSite values has not yet been added to java.net.HttpCookie
_sameSite = getSameSiteFromComment(cookie.getComment());
}
/**
* @return the cookie name
*/
public String getName() {
return _name;
}
/**
* @return the cookie value
*/
public String getValue() {
return _value;
}
/**
* @return the cookie comment
*/
public String getComment() {
return _comment;
}
/**
* @return the cookie domain
*/
public String getDomain() {
return _domain;
}
/**
* @return the cookie max age in seconds
*/
public long getMaxAge() {
return _maxAge;
}
/**
* @return the cookie path
*/
public String getPath() {
return _path;
}
/**
* @return whether the cookie is valid for secure domains
*/
public boolean isSecure() {
return _secure;
}
/**
* @return the cookie version
*/
public int getVersion() {
return _version;
}
/**
* @return the cookie SameSite enum attribute
*/
public SameSite getSameSite() {
return _sameSite;
}
/**
* @return whether the cookie is valid for the http protocol only
*/
public boolean isHttpOnly() {
return _httpOnly;
}
/**
* @param timeNanos the time to check for cookie expiration, in nanoseconds
* @return whether the cookie is expired by the given time
*/
public boolean isExpired(long timeNanos) {
return _expiration >= 0 && timeNanos >= _expiration;
}
/**
* @return a string representation of this cookie
*/
public String asString() {
StringBuilder builder = new StringBuilder();
builder.append(getName()).append("=").append(getValue());
if (getDomain() != null)
builder.append(";$Domain=").append(getDomain());
if (getPath() != null)
builder.append(";$Path=").append(getPath());
return builder.toString();
}
private static void quoteOnlyOrAppend(StringBuilder buf, String s, boolean quote) {
if (quote)
QuotedStringTokenizer.quoteOnly(buf, s);
else
buf.append(s);
}
/**
* Does a cookie value need to be quoted?
*
* @param s value string
* @return true if quoted;
* @throws IllegalArgumentException If there a control characters in the string
*/
private static boolean isQuoteNeededForCookie(String s) {
if (s == null || s.length() == 0)
return true;
if (QuotedStringTokenizer.isQuoted(s))
return false;
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (__COOKIE_DELIM.indexOf(c) >= 0)
return true;
if (c < 0x20 || c >= 0x7f)
throw new IllegalArgumentException("Illegal character in cookie value");
}
return false;
}
public String getSetCookie(CookieCompliance compliance) {
switch(compliance) {
case RFC2965:
return getRFC2965SetCookie();
case RFC6265:
return getRFC6265SetCookie();
default:
throw new IllegalStateException();
}
}
public String getRFC2965SetCookie() {
// Check arguments
if (_name == null || _name.length() == 0)
throw new IllegalArgumentException("Bad cookie name");
// Format value and params
StringBuilder buf = new StringBuilder();
// Name is checked for legality by servlet spec, but can also be passed directly so check again for quoting
boolean quoteName = isQuoteNeededForCookie(_name);
quoteOnlyOrAppend(buf, _name, quoteName);
buf.append('=');
// Append the value
boolean quoteValue = isQuoteNeededForCookie(_value);
quoteOnlyOrAppend(buf, _value, quoteValue);
// Look for domain and path fields and check if they need to be quoted
boolean hasDomain = _domain != null && _domain.length() > 0;
boolean quoteDomain = hasDomain && isQuoteNeededForCookie(_domain);
boolean hasPath = _path != null && _path.length() > 0;
boolean quotePath = hasPath && isQuoteNeededForCookie(_path);
// Upgrade the version if we have a comment or we need to quote value/path/domain or if they were already quoted
int version = _version;
if (version == 0 && (_comment != null || quoteName || quoteValue || quoteDomain || quotePath || QuotedStringTokenizer.isQuoted(_name) || QuotedStringTokenizer.isQuoted(_value) || QuotedStringTokenizer.isQuoted(_path) || QuotedStringTokenizer.isQuoted(_domain)))
version = 1;
// Append version
if (version == 1)
buf.append(";Version=1");
else if (version > 1)
buf.append(";Version=").append(version);
// Append path
if (hasPath) {
buf.append(";Path=");
quoteOnlyOrAppend(buf, _path, quotePath);
}
// Append domain
if (hasDomain) {
buf.append(";Domain=");
quoteOnlyOrAppend(buf, _domain, quoteDomain);
}
// Handle max-age and/or expires
if (_maxAge >= 0) {
// Always use expires
// This is required as some browser (M$ this means you!) don't handle max-age even with v1 cookies
buf.append(";Expires=");
if (_maxAge == 0)
buf.append(__01Jan1970_COOKIE);
else
DateGenerator.formatCookieDate(buf, System.currentTimeMillis() + 1000L * _maxAge);
// for v1 cookies, also send max-age
if (version >= 1) {
buf.append(";Max-Age=");
buf.append(_maxAge);
}
}
// add the other fields
if (_secure)
buf.append(";Secure");
if (_httpOnly)
buf.append(";HttpOnly");
if (_comment != null) {
buf.append(";Comment=");
quoteOnlyOrAppend(buf, _comment, isQuoteNeededForCookie(_comment));
}
return buf.toString();
}
public String getRFC6265SetCookie() {
// Check arguments
if (_name == null || _name.length() == 0)
throw new IllegalArgumentException("Bad cookie name");
// Name is checked for legality by servlet spec, but can also be passed directly so check again for quoting
// Per RFC6265, Cookie.name follows RFC2616 Section 2.2 token rules
Syntax.requireValidRFC2616Token(_name, "RFC6265 Cookie name");
// Ensure that Per RFC6265, Cookie.value follows syntax rules
Syntax.requireValidRFC6265CookieValue(_value);
// Format value and params
StringBuilder buf = new StringBuilder();
buf.append(_name).append('=').append(_value == null ? "" : _value);
// Append path
if (_path != null && _path.length() > 0)
buf.append("; Path=").append(_path);
// Append domain
if (_domain != null && _domain.length() > 0)
buf.append("; Domain=").append(_domain);
// Handle max-age and/or expires
if (_maxAge >= 0) {
// Always use expires
// This is required as some browser (M$ this means you!) don't handle max-age even with v1 cookies
buf.append("; Expires=");
if (_maxAge == 0)
buf.append(__01Jan1970_COOKIE);
else
DateGenerator.formatCookieDate(buf, System.currentTimeMillis() + 1000L * _maxAge);
buf.append("; Max-Age=");
buf.append(_maxAge);
}
// add the other fields
if (_secure)
buf.append("; Secure");
if (_httpOnly)
buf.append("; HttpOnly");
if (_sameSite != null) {
buf.append("; SameSite=");
buf.append(_sameSite.getAttributeValue());
}
return buf.toString();
}
public static boolean isHttpOnlyInComment(String comment) {
return comment != null && comment.contains(HTTP_ONLY_COMMENT);
}
public static SameSite getSameSiteFromComment(String comment) {
if (comment != null) {
if (comment.contains(SAME_SITE_STRICT_COMMENT)) {
return SameSite.STRICT;
}
if (comment.contains(SAME_SITE_LAX_COMMENT)) {
return SameSite.LAX;
}
if (comment.contains(SAME_SITE_NONE_COMMENT)) {
return SameSite.NONE;
}
}
return null;
}
/**
* Get the default value for SameSite cookie attribute, if one
* has been set for the given context.
*
* @param context the context to check for default SameSite value
* @return the default SameSite value or null if one does not exist
* @throws IllegalStateException if the default value is not a permitted value
*/
public static SameSite getSameSiteDefault(ServletContext context) {
if (context == null)
return null;
Object o = context.getAttribute(SAME_SITE_DEFAULT_ATTRIBUTE);
if (o == null) {
if (LOG.isDebugEnabled())
LOG.debug("No default value for SameSite");
return null;
}
if (o instanceof SameSite)
return (SameSite) o;
try {
SameSite samesite = Enum.valueOf(SameSite.class, o.toString().trim().toUpperCase(Locale.ENGLISH));
context.setAttribute(SAME_SITE_DEFAULT_ATTRIBUTE, samesite);
return samesite;
} catch (Exception e) {
LOG.warn("Bad default value {} for SameSite", o);
throw new IllegalStateException(e);
}
}
public static String getCommentWithoutAttributes(String comment) {
if (comment == null) {
return null;
}
String strippedComment = comment.trim();
strippedComment = StringUtil.strip(strippedComment, HTTP_ONLY_COMMENT);
strippedComment = StringUtil.strip(strippedComment, SAME_SITE_NONE_COMMENT);
strippedComment = StringUtil.strip(strippedComment, SAME_SITE_LAX_COMMENT);
strippedComment = StringUtil.strip(strippedComment, SAME_SITE_STRICT_COMMENT);
return strippedComment.length() == 0 ? null : strippedComment;
}
public static String getCommentWithAttributes(String comment, boolean httpOnly, SameSite sameSite) {
if (comment == null && sameSite == null)
return null;
StringBuilder builder = new StringBuilder();
if (StringUtil.isNotBlank(comment)) {
comment = getCommentWithoutAttributes(comment);
if (StringUtil.isNotBlank(comment))
builder.append(comment);
}
if (httpOnly)
builder.append(HTTP_ONLY_COMMENT);
if (sameSite != null) {
switch(sameSite) {
case NONE:
builder.append(SAME_SITE_NONE_COMMENT);
break;
case STRICT:
builder.append(SAME_SITE_STRICT_COMMENT);
break;
case LAX:
builder.append(SAME_SITE_LAX_COMMENT);
break;
default:
throw new IllegalArgumentException(sameSite.toString());
}
}
if (builder.length() == 0)
return null;
return builder.toString();
}
// @deprecated The Eclipse Jetty and Apache Felix Http Jetty packages are no longer supported.
@Deprecated(since = "2021-05-27")
public static class SetCookieHttpField extends HttpField {
final HttpCookie _cookie;
public SetCookieHttpField(HttpCookie cookie, CookieCompliance compliance) {
super(HttpHeader.SET_COOKIE, cookie.getSetCookie(compliance));
this._cookie = cookie;
}
public HttpCookie getHttpCookie() {
return _cookie;
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy