DefaultCookieManager.java
/*
* Licensed 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.esigate.cookie;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import org.apache.http.cookie.Cookie;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.cookie.BasicClientCookie;
import org.apache.http.impl.cookie.BasicClientCookie2;
import org.esigate.ConfigurationException;
import org.esigate.Driver;
import org.esigate.Parameters;
import org.esigate.UserContext;
import org.esigate.http.cookie.CookieUtil;
import org.esigate.impl.DriverRequest;
import org.esigate.util.UriUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This cookie manager supports rules for forwarding cookies to the user browser, ignore and discard cookies, or store
* cookies in the user session.
*
* <p>
* When cookies are not stored in the session or discarded, they are forwarded to the client browser. If no cookie is
* stored to the session (default) EsiGate is completely stateless. For public deployment is is recommended to use
* cookie forwarding and discarding to prevent session creation.
*
* @author Francois-Xavier Bonnet
* @author Nicolas Richeton
*
*/
public class DefaultCookieManager implements CookieManager {
private static final Logger LOG = LoggerFactory.getLogger(CookieManager.class);
private static final String COOKIES_LIST_SESSION_KEY = CookieManager.class.getName() + "#cookies";
private Collection<String> discardCookies;
private Collection<String> storeCookiesInSession;
protected Collection<String> getStoredCookies() {
return storeCookiesInSession;
}
/**
* Init cookie manager. Reads parameters <b>discardCookies</b> and <b>storeCookiesInSession</b>.
*/
@Override
public void init(Driver d, Properties properties) {
// Cookies to store to session
this.storeCookiesInSession = Parameters.STORE_COOKIES_IN_SESSION.getValue(properties);
// Cookies to discard
this.discardCookies = Parameters.DISCARD_COOKIES.getValue(properties);
// Verify configuration
if (this.storeCookiesInSession.contains("*") && this.storeCookiesInSession.size() > 1) {
throw new ConfigurationException("storeCookiesInSession must be a list of cookie names OR *");
}
if (this.discardCookies.contains("*") && this.discardCookies.size() > 1) {
throw new ConfigurationException("discardCookies must be a list of cookie names OR *");
}
if (this.storeCookiesInSession.contains("*") && this.discardCookies.contains("*")) {
throw new ConfigurationException(
"cannot use * for storeCookiesInSession AND discardCookies at the same time");
}
}
@Override
public void addCookie(Cookie cookie, DriverRequest originalRequest) {
String name = cookie.getName();
if (discardCookies.contains(name) || (discardCookies.contains("*") && !storeCookiesInSession.contains(name))) {
if (LOG.isInfoEnabled()) {
LOG.info("Cookie " + toString(cookie) + " -> discarding");
}
// Ignore cookie
} else if (storeCookiesInSession.contains(name) || storeCookiesInSession.contains("*")) {
if (LOG.isInfoEnabled()) {
LOG.info("Cookie " + toString(cookie) + " -> storing to session");
}
// Store cookie in session
UserContext userContext = originalRequest.getUserContext();
BasicCookieStore cookies = (BasicCookieStore) userContext.getAttribute(COOKIES_LIST_SESSION_KEY);
if (cookies == null) {
cookies = new BasicCookieStore();
}
cookies.addCookie(cookie);
userContext.setAttribute(COOKIES_LIST_SESSION_KEY, cookies);
} else {
if (LOG.isInfoEnabled()) {
LOG.info("Cookie " + toString(cookie) + " -> forwarding");
}
// Forward cookie to response.
originalRequest.getOriginalRequest().addNewCookie(rewriteForBrowser(cookie, originalRequest));
}
}
@Override
public List<Cookie> getCookies(DriverRequest originalRequest) {
BasicCookieStore cookies = new BasicCookieStore();
UserContext userContext = originalRequest.getUserContext();
// Read cookies from session
BasicCookieStore sessionCookies = (BasicCookieStore) userContext.getAttribute(COOKIES_LIST_SESSION_KEY);
if (sessionCookies != null) {
for (Cookie c : sessionCookies.getCookies()) {
cookies.addCookie(c);
}
}
// Read cookie from request
Cookie[] requestCookies = originalRequest.getOriginalRequest().getCookies();
if (requestCookies != null) {
for (Cookie cookie : requestCookies) {
String name = cookie.getName();
if (!storeCookiesInSession.contains(name) && !storeCookiesInSession.contains("*")
&& !discardCookies.contains(name) && !discardCookies.contains("*")) {
cookies.addCookie(rewriteForServer(cookie, originalRequest));
}
}
}
return cookies.getCookies();
}
private static Cookie rewriteForServer(Cookie cookie, DriverRequest request) {
String name = cookie.getName();
if ("_JSESSIONID".equalsIgnoreCase(name)) {
name = name.substring(1);
}
BasicClientCookie2 httpClientCookie = new BasicClientCookie2(name, cookie.getValue());
httpClientCookie.setSecure(false);
String domain;
if (request.getDriver().getConfiguration().isPreserveHost()) {
domain = UriUtils.extractHostName(request.getOriginalRequest().getRequestLine().getUri());
} else {
domain = request.getBaseUrl().getHost();
}
httpClientCookie.setDomain(domain);
httpClientCookie.setPath("/");
httpClientCookie.setComment(cookie.getComment());
httpClientCookie.setVersion(cookie.getVersion());
return httpClientCookie;
}
protected static String rewriteDomain(String originalDomain, String providerHostName, String requestHostName) {
String domain = null;
if (!providerHostName.equals(originalDomain)) {
// if original domain starts with ".", remove it.
if (originalDomain.startsWith(".")) {
originalDomain = originalDomain.substring(1);
}
String[] originalDomainParts = originalDomain.split("\\.");
String[] requestHostNameParts = requestHostName.split("\\.");
int targetLength = Math.min(originalDomainParts.length, requestHostNameParts.length);
if (targetLength == requestHostNameParts.length) {
// The bigger domain we can use is request host name, it is like
// returning null as domain name!
return null;
}
domain = "";
for (int i = requestHostNameParts.length; i > requestHostNameParts.length - targetLength; i--) {
domain = "." + requestHostNameParts[i - 1] + domain;
}
}
return domain;
}
protected static Cookie rewriteForBrowser(Cookie cookie, DriverRequest request) {
String name = cookie.getName();
// Rewrite name if JSESSIONID because it will interfere with current
// server session
if ("JSESSIONID".equalsIgnoreCase(name)) {
name = "_" + name;
}
// Rewrite domain
String domain =
rewriteDomain(cookie.getDomain(), request.getBaseUrl().getHost(),
UriUtils.extractHostName(request.getOriginalRequest().getRequestLine().getUri()));
// Rewrite path
String originalPath = cookie.getPath();
String requestPath = UriUtils.getPath(request.getOriginalRequest().getRequestLine().getUri());
String path = originalPath;
if (requestPath == null || !requestPath.startsWith(originalPath)) {
path = "/";
}
// Rewrite secure
boolean secure =
(cookie.isSecure() && request.getOriginalRequest().getRequestLine().getUri().startsWith("https"));
BasicClientCookie cookieToForward = new BasicClientCookie(name, cookie.getValue());
if (domain != null) {
cookieToForward.setDomain(domain);
}
cookieToForward.setPath(path);
cookieToForward.setSecure(secure);
cookieToForward.setComment(cookie.getComment());
cookieToForward.setVersion(cookie.getVersion());
cookieToForward.setExpiryDate(cookie.getExpiryDate());
if (((BasicClientCookie) cookie).containsAttribute(CookieUtil.HTTP_ONLY_ATTR)) {
cookieToForward.setAttribute(CookieUtil.HTTP_ONLY_ATTR, "");
}
if (LOG.isDebugEnabled()) {
// Ensure .toString is only called if debug enabled.
LOG.debug("Forwarding cookie {} -> {}", cookie.toString(), cookieToForward.toString());
}
return cookieToForward;
}
private String toString(Cookie cookie) {
StringBuilder result = new StringBuilder(Parameters.SMALL_BUFFER_SIZE);
result.append(cookie.getName());
result.append("=");
result.append(cookie.getValue());
if (cookie.getDomain() != null) {
result.append(";domain=");
result.append(cookie.getDomain());
}
if (cookie.getPath() != null) {
result.append(";path=");
result.append(cookie.getPath());
}
if (cookie.getExpiryDate() != null) {
result.append(";expires=");
result.append(cookie.getExpiryDate());
}
if (cookie.getCommentURL() != null) {
result.append(";comment=");
result.append(cookie.getComment());
}
if (cookie.getCommentURL() != null) {
result.append(";comment=");
result.append(cookie.getCommentURL());
}
return result.toString();
}
@Override
public boolean clearExpired(Date date, DriverRequest request) {
UserContext userContext = request.getUserContext();
BasicCookieStore cookies = (BasicCookieStore) userContext.getAttribute(COOKIES_LIST_SESSION_KEY);
return cookies != null && cookies.clearExpired(date);
}
@Override
public void clear(DriverRequest request) {
UserContext userContext = request.getUserContext();
BasicCookieStore cookies = (BasicCookieStore) userContext.getAttribute(COOKIES_LIST_SESSION_KEY);
if (cookies != null) {
cookies.clear();
userContext.setAttribute(COOKIES_LIST_SESSION_KEY, cookies);
}
}
}