org.refcodes.web.ResponseCookie Maven / Gradle / Ivy
package org.refcodes.web;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.time.DateTimeException;
import java.time.Instant;
import java.util.Date;
import java.util.Locale;
import java.util.StringTokenizer;
import org.refcodes.data.Delimiter;
import org.refcodes.data.Encoding;
import org.refcodes.mixin.DomainAccessor.DomainBuilder;
import org.refcodes.mixin.DomainAccessor.DomainProperty;
import org.refcodes.mixin.PathAccessor.PathBuilder;
import org.refcodes.mixin.PathAccessor.PathProperty;
import org.refcodes.struct.Property;
import org.refcodes.struct.PropertyImpl;
import org.refcodes.struct.PropertyImpl.PropertyBuilderImpl;
import org.refcodes.time.DateFormat;
import org.refcodes.time.DateFormats;
import org.refcodes.time.TimeUnit;
/**
* As of "HTTP cookies explained - NCZOnline": "... There is some confusion over
* encoding of a cookie value. The commonly held belief is that cookie values
* must be URL-encoded, but this is a fallacy even though it is the de facto
* implementation. The original specification indicates that only three types of
* characters must be encoded: semicolon, comma, and white space. The
* specification indicates that URL encoding may be used but stops short of
* requiring it. The RFC makes no mention of encoding whatsoever. Still, almost
* all implementations perform some sort of URL encoding on cookie values. In
* the case of name=value formats, the name and value are typically encoded
* separately while the equals sign is left as is. ..." Therefore we use URL
* encoding to make life easier and not fall into the trap of unescaped values.
*
* The {@link ResponseCookie} represents response cookies: We use URL encoding /
* decoding for the cookie value (regarding {@link #fromHttpCookie(String)} and
* {@link #toHttpCookie()}) to make life easier and not fall into the trap of
* unescaped values.
*
* @see "https://www.nczonline.net/blog/2009/05/05/http-cookies-explained"
*/
public class ResponseCookie extends PropertyBuilderImpl implements Cookie, PathProperty, PathBuilder, DomainProperty, DomainBuilder {
// /////////////////////////////////////////////////////////////////////////
// VARIABLES:
// /////////////////////////////////////////////////////////////////////////
private Date _expiresDate = null;
private int _maxAge = -1;
private String _domain = null;
private String _path = null;
private boolean _isSecure = false;
private boolean _isHttpOnly = false;
private String _version = null;
// /////////////////////////////////////////////////////////////////////////
// CONSTRUCTORS:
// /////////////////////////////////////////////////////////////////////////
/**
* Instantiates a new response cookie impl.
*/
public ResponseCookie() {}
/**
* Constructs a {@link ResponseCookie}.
*
* @param aCookieName The name of the cookie.
* @param aValue The value for the cookie.
*/
public ResponseCookie( String aCookieName, String aValue ) {
super( aCookieName, aValue );
}
/**
* Constructs a {@link ResponseCookie} from the given HTTP cookie.
*
* @param aHttpCookie The text as being found in the according HTTP header
* field.
*/
public ResponseCookie( String aHttpCookie ) {
fromHttpCookie( aHttpCookie );
}
/**
* Constructs a {@link ResponseCookie}.
*
* @param aCookie The {@link Cookie} from which to take the data.
*/
public ResponseCookie( Cookie aCookie ) {
super( aCookie.getKey(), aCookie.getValue() );
if ( aCookie instanceof ResponseCookie theResponseCookie ) {
setDomain( theResponseCookie.getDomain() );
setPath( theResponseCookie.getPath() );
setExpiresDate( theResponseCookie.getExpiresDate() );
setHttpOnly( theResponseCookie.isHttpOnly() );
setSecure( theResponseCookie.isSecure() );
setMaxAge( theResponseCookie.getMaxAge() );
setVersion( theResponseCookie.getVersion() );
}
}
/**
* Constructs a {@link ResponseCookie}.
*
* @param aCookieName The name for the cookie
* @param aValue The value of the cookie
* @param aExpiresDate The expiration date of the cookie
* @param aDomain The domain of the cookie
* @param aPath The path of the cookie
* @param isSecure The secure flag of the cookie
* @param isHttpOnly The HTTP-only flag of the cookie
* @param aVersion The version of the cookie.
*/
public ResponseCookie( String aCookieName, String aValue, Date aExpiresDate, String aDomain, String aPath, boolean isSecure, boolean isHttpOnly, String aVersion ) {
super( aCookieName, aValue );
_expiresDate = aExpiresDate;
_domain = aDomain;
_path = aPath;
_isSecure = isSecure;
_isHttpOnly = isHttpOnly;
_version = aVersion;
}
/**
* Constructs a {@link ResponseCookie}.
*
* @param aCookieName The name for the cookie
* @param aValue The value of the cookie
* @param aMaxAge Sets the Max-Age (seconds) of this cookie.
* @param aDomain The domain of the cookie
* @param aPath The path of the cookie
* @param isSecure The secure flag of the cookie
* @param isHttpOnly The HTTP-only flag of the cookie
* @param aVersion The version of the cookie.
*/
public ResponseCookie( String aCookieName, String aValue, int aMaxAge, String aDomain, String aPath, boolean isSecure, boolean isHttpOnly, String aVersion ) {
super( aCookieName, aValue );
_maxAge = aMaxAge;
_domain = aDomain;
_path = aPath;
_isSecure = isSecure;
_isHttpOnly = isHttpOnly;
_version = aVersion;
}
// /////////////////////////////////////////////////////////////////////////
// METHODS:
// /////////////////////////////////////////////////////////////////////////
/**
* Returns the Max-Age (seconds) for this cookie.
*
* @return The Max-Age.
*/
public int getMaxAge() {
return _maxAge;
}
/**
* Sets the Max-Age (seconds) of this cookie.
*
* @param aMaxAge Sets the Max-Age.
*/
public void setMaxAge( int aMaxAge ) {
_maxAge = aMaxAge;
}
/**
* Returns the version this cookie.
*
* @return The version.
*/
public String getVersion() {
return _version;
}
/**
* Sets the version of this cookie.
*
* @param aVersion Sets the version.
*/
public void setVersion( String aVersion ) {
_version = aVersion;
}
/**
* {@inheritDoc}
*/
@Override
public String getPath() {
return _path;
}
/**
* {@inheritDoc}
*/
@Override
public void setPath( String aPath ) {
_path = aPath;
}
/**
* Returns the expiration date of this cookie.
*
* @return The expiration date.
*/
public Date getExpiresDate() {
return _expiresDate;
}
/**
* Sets the expiration date of this cookie.
*
* @param aExpireDate The expiration date.
*/
public void setExpiresDate( Date aExpireDate ) {
_expiresDate = aExpireDate;
}
/**
* {@inheritDoc}
*/
@Override
public String getDomain() {
return _domain;
}
/**
* {@inheritDoc}
*/
@Override
public void setDomain( String aDomain ) {
_domain = aDomain;
}
/**
* Sets whether it be a secure HTTP cookie. Such a cookie only be
* transferred via HTTPS.
*
* @param isSecure True in case of being a secure only cookie.
*/
public void setSecure( boolean isSecure ) {
_isSecure = isSecure;
}
/**
* Returns true if we have a secure HTTP cookie. Such a cookie only be
* transferred via HTTPS.
*
* @return True in case of being a secure only cookie.
*/
public boolean isSecure() {
return _isSecure;
}
/**
* Returns true if we have an HTTP only cookie. Such a cookie cannot be
* accessed client-side (via JavaScript).
*
* @return True in case of being an HTTP only cookie.
*/
public boolean isHttpOnly() {
return _isHttpOnly;
}
/**
* Sets whether it be an HTTP only cookie. Such a cookie cannot be accessed
* client-side (via JavaScript).
*
* @param isHttpOnly True in case of being an HTTP only cookie.
*/
public void setHttpOnly( boolean isHttpOnly ) {
_isHttpOnly = isHttpOnly;
}
/**
* Sets the expires date to the current time plus the provided time. Use
* {@link #getExpiresDate()} to retrieve the resulting effective
* {@link Date}.
*
* @param aTimeUnit The {@link TimeUnit} of the provided time
* @param aTime The provided time after which to expire
*/
public void setExpiresAfter( TimeUnit aTimeUnit, long aTime ) {
final long theMilliseconds = TimeUnit.toMilliseconds( aTimeUnit, aTime );
Date theExpiresDate = new Date();
theExpiresDate = new Date( theExpiresDate.getTime() + theMilliseconds );
setExpiresDate( theExpiresDate );
}
/**
* Sets the expiration date of this cookie and returns this instance as of
* the Builder-Pattern.
*
* @param aExpiresDate the expires date
*
* @return This instance as of the Builder-Pattern.
*/
public ResponseCookie withExpiresDate( Date aExpiresDate ) {
setExpiresDate( aExpiresDate );
return this;
}
/**
* Sets the path of the cookie. {@inheritDoc}
*/
@Override
public ResponseCookie withPath( String aPath ) {
setPath( aPath );
return this;
}
/**
* Sets the domain for this cookie. {@inheritDoc}
*/
@Override
public ResponseCookie withDomain( String aDomain ) {
setDomain( aDomain );
return this;
}
/**
* Sets whether it be an HTTP only cookie. Such a cookie cannot be accessed
* client-side (via JavaScript). Returns this instance as of the builder
* pattern.
*
* @param isHttpOnly True in case of being an HTTP only cookie.
*
* @return Returns this instance as of the Builder-Pattern.
*/
public ResponseCookie withHttpOnly( boolean isHttpOnly ) {
setHttpOnly( isHttpOnly );
return this;
}
/**
* Builder method for the method {@link #setExpiresAfter(TimeUnit, long)}.
*
* @param aTimeUnit The {@link TimeUnit} of the provided time
* @param aTime The provided time after which to expire
*
* @return This cookie instance for further configuration.
*/
public ResponseCookie withExpiresAfter( TimeUnit aTimeUnit, long aTime ) {
setExpiresAfter( aTimeUnit, aTime );
return this;
}
/**
* Builder method for {@link #setVersion(String)}.
*
* @param aVersion Sets the version.
*
* @return This instance as of the Builder-Pattern.
*/
public ResponseCookie withVersion( String aVersion ) {
setVersion( aVersion );
return this;
}
/**
* Builder method for {@link #setMaxAge(int)}.
*
* @param aMaxAge Sets the Max-Age.
*
* @return This instance as of the Builder-Pattern.
*/
public ResponseCookie withMaxAge( int aMaxAge ) {
setMaxAge( aMaxAge );
return this;
}
/**
* Sets whether it be a secure HTTP cookie. Such a cookie only be
* transferred via HTTPS. Returns this instance as of the Builder-Pattern.
*
* @param isSecure True in case of being a secure only cookie.
*
* @return Returns this instance as of the Builder-Pattern.
*/
public ResponseCookie withSecure( boolean isSecure ) {
setSecure( isSecure );
return this;
}
/**
* {@inheritDoc}
*/
@Override
public ResponseCookie withHttpCookie( String aCookie ) {
fromHttpCookie( aCookie );
return this;
}
/**
* {@inheritDoc}
*/
@Override
public void fromHttpCookie( String aHttpCookie ) {
final StringTokenizer theTokenizer = new StringTokenizer( aHttpCookie, Delimiter.COOKIE_PROPERTIES.getChar() + "" );
String eTupel = theTokenizer.nextToken();
Property eProperty = new PropertyImpl( eTupel );
setKey( eProperty.getKey() );
// Cookie value URL decoding -->
try {
setValue( URLDecoder.decode( eProperty.getValue(), Encoding.UTF_8.getCode() ) );
}
catch ( UnsupportedEncodingException ignore ) {
setValue( eProperty.getValue() );
}
// <-- Cookie value URL decoding
CookieAttribute eCookieKey;
while ( theTokenizer.hasMoreTokens() ) {
eTupel = theTokenizer.nextToken();
if ( eTupel.equalsIgnoreCase( CookieAttribute.SECURE.getKey() ) ) {
eCookieKey = CookieAttribute.SECURE;
}
else if ( eTupel.equalsIgnoreCase( CookieAttribute.HTTPONLY.getKey() ) ) {
eCookieKey = CookieAttribute.HTTPONLY;
}
else {
eProperty = new PropertyImpl( eTupel );
eCookieKey = CookieAttribute.fromKey( eProperty.getKey() );
}
if ( eCookieKey != null ) { // Ignore "null" Cookie key:
switch ( eCookieKey ) {
case DOMAIN -> setDomain( eProperty.getValue() );
case EXPIRES -> {
try {
setExpiresDate( DateFormats.COOKIE_DATE_FORMATS.toDate( eProperty.getValue() ) );
}
catch ( DateTimeException e ) {
throw new IllegalArgumentException( "Encountered unparsable date <" + eProperty.getValue() + "> for key <" + eCookieKey.getKey() + ">.", e );
}
}
case HTTPONLY -> setHttpOnly( true );
case PATH -> setPath( eProperty.getValue() );
case SECURE -> setSecure( true );
case VERSION -> setVersion( eProperty.getValue() );
case MAX_AGE -> {
int theMaxAgeInSeconds = -1;
try {
theMaxAgeInSeconds = Integer.parseInt( eProperty.getValue() );
// setExpiresAfter( TimeUnit.SECOND, theMaxAgeInSeconds );
setMaxAge( theMaxAgeInSeconds );
}
catch ( NumberFormatException e ) {
throw new IllegalArgumentException( "Encountered unparsable max-age <" + eProperty.getValue() + "> for key <" + eCookieKey.getKey() + ">.", e );
}
}
default -> {
}
}
}
}
}
/**
* {@inheritDoc}
*/
@Override
public String toHttpCookie() {
// Cookie value URL encoding |-->
String theValue = null;
try {
theValue = URLEncoder.encode( getValue(), Encoding.UTF_8.getCode() );
}
catch ( UnsupportedEncodingException ignore ) {
theValue = getValue();
}
// Cookie value URL encoding <--|
String theHttpCookie = getKey() + Delimiter.COOKIE_TUPEL.getChar() + theValue;
if ( getExpiresDate() != null ) {
theHttpCookie += Delimiter.COOKIE_PROPERTIES.getChar() + CookieAttribute.EXPIRES.getKey() + Delimiter.COOKIE_TUPEL.getChar() + DateFormat.NETSCAPE_COOKIE_DATE_FORMAT.getFormatter().withLocale( Locale.ENGLISH ).format( Instant.ofEpochMilli( getExpiresDate().getTime() ) );
}
if ( getMaxAge() != -1 ) {
theHttpCookie += Delimiter.COOKIE_PROPERTIES.getChar() + CookieAttribute.MAX_AGE.getKey() + Delimiter.COOKIE_TUPEL.getChar() + getMaxAge();
}
if ( getDomain() != null ) {
theHttpCookie += Delimiter.COOKIE_PROPERTIES.getChar() + CookieAttribute.DOMAIN.getKey() + Delimiter.COOKIE_TUPEL.getChar() + getDomain();
}
if ( getPath() != null ) {
theHttpCookie += Delimiter.COOKIE_PROPERTIES.getChar() + CookieAttribute.PATH.getKey() + Delimiter.COOKIE_TUPEL.getChar() + getPath();
}
if ( isSecure() ) {
theHttpCookie += Delimiter.COOKIE_PROPERTIES.getChar() + CookieAttribute.SECURE.getKey();
}
if ( isHttpOnly() ) {
theHttpCookie += Delimiter.COOKIE_PROPERTIES.getChar() + CookieAttribute.HTTPONLY.getKey();
}
if ( getVersion() != null ) {
theHttpCookie += Delimiter.COOKIE_PROPERTIES.getChar() + CookieAttribute.VERSION.getKey() + Delimiter.COOKIE_TUPEL.getChar() + getVersion();
}
return theHttpCookie;
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return getClass().getName() + "@[key=" + _key + ", value=" + _value + ", expiresDate=" + _expiresDate + ", maxAge=" + _maxAge + ", domain=" + _domain + ", path=" + _path + ", isSecure=" + _isSecure + ", isHttpOnly=" + _isHttpOnly + ", version=" + _version + "]";
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy