/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF 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.apache.jmeter.protocol.http.control; import java.net.URL; import java.util.ArrayList; import java.util.Date; import java.util.List; import org.apache.http.Header; import org.apache.http.client.config.CookieSpecs; import org.apache.http.client.protocol.HttpClientContext; import org.apache.http.config.Registry; import org.apache.http.config.RegistryBuilder; import org.apache.http.conn.util.PublicSuffixMatcher; import org.apache.http.conn.util.PublicSuffixMatcherLoader; import org.apache.http.cookie.ClientCookie; import org.apache.http.cookie.CookieOrigin; import org.apache.http.cookie.CookieSpec; import org.apache.http.cookie.CookieSpecProvider; import org.apache.http.cookie.MalformedCookieException; import org.apache.http.impl.cookie.BasicClientCookie; import org.apache.http.impl.cookie.DefaultCookieSpecProvider; import org.apache.http.impl.cookie.IgnoreSpecProvider; import org.apache.http.impl.cookie.NetscapeDraftSpecProvider; import org.apache.http.impl.cookie.RFC2109SpecProvider; import org.apache.http.impl.cookie.RFC2965SpecProvider; import org.apache.http.impl.cookie.RFC6265CookieSpecProvider; import org.apache.http.message.BasicHeader; import org.apache.jmeter.protocol.http.sampler.HTTPSamplerBase; import org.apache.jmeter.protocol.http.util.HTTPConstants; import org.apache.jmeter.testelement.property.CollectionProperty; import org.apache.jmeter.testelement.property.JMeterProperty; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class HC4CookieHandler implements CookieHandler { private static final Logger log = LoggerFactory.getLogger(HC4CookieHandler.class); // Needed by CookiePanel public static final String DEFAULT_POLICY_NAME = CookieSpecs.STANDARD; // NOSONAR private static final String[] AVAILABLE_POLICIES = new String[]{ DEFAULT_POLICY_NAME, CookieSpecs.STANDARD_STRICT, CookieSpecs.IGNORE_COOKIES, CookieSpecs.NETSCAPE, CookieSpecs.DEFAULT, "rfc2109", "rfc2965", CookieSpecs.BEST_MATCH, CookieSpecs.BROWSER_COMPATIBILITY }; private final transient CookieSpec cookieSpec; private static PublicSuffixMatcher publicSuffixMatcher = PublicSuffixMatcherLoader.getDefault(); private static Registry<CookieSpecProvider> registry = RegistryBuilder.<CookieSpecProvider>create() // case is ignored bug registry as it converts to lowerCase(Locale.US) .register(CookieSpecs.BEST_MATCH, new DefaultCookieSpecProvider(publicSuffixMatcher)) .register(CookieSpecs.BROWSER_COMPATIBILITY, new DefaultCookieSpecProvider(publicSuffixMatcher)) .register(CookieSpecs.STANDARD, new RFC6265CookieSpecProvider()) .register("rfc2109", new RFC2109SpecProvider(publicSuffixMatcher, true)) //$NON-NLS-1$ .register("rfc2965", new RFC2965SpecProvider(publicSuffixMatcher, true)) //$NON-NLS-1$ .register(CookieSpecs.STANDARD_STRICT, new RFC6265CookieSpecProvider( org.apache.http.impl.cookie.RFC6265CookieSpecProvider.CompatibilityLevel.STRICT, null)) .register(CookieSpecs.DEFAULT, new DefaultCookieSpecProvider(publicSuffixMatcher)) .register(CookieSpecs.IGNORE_COOKIES, new IgnoreSpecProvider()) .register(CookieSpecs.NETSCAPE, new NetscapeDraftSpecProvider()) .build(); /** * Default constructor that uses {@link HC4CookieHandler#DEFAULT_POLICY_NAME} */ public HC4CookieHandler() { this(DEFAULT_POLICY_NAME); } public HC4CookieHandler(String policy) { super(); if (policy.equalsIgnoreCase("default")) { // tweak diff HC3 vs HC4 policy = CookieSpecs.DEFAULT; } HttpClientContext context = HttpClientContext.create(); this.cookieSpec = registry.lookup(policy).create(context); } @Override public void addCookieFromHeader(CookieManager cookieManager, boolean checkCookies, String cookieHeader, URL url) { boolean debugEnabled = log.isDebugEnabled(); if (debugEnabled) { log.debug("Received Cookie: " + cookieHeader + " From: " + url.toExternalForm()); } String protocol = url.getProtocol(); String host = url.getHost(); int port= HTTPSamplerBase.getDefaultPort(protocol,url.getPort()); String path = url.getPath(); boolean isSecure=HTTPSamplerBase.isSecure(protocol); List<org.apache.http.cookie.Cookie> cookies = null; CookieOrigin cookieOrigin = new CookieOrigin(host, port, path, isSecure); BasicHeader basicHeader = new BasicHeader(HTTPConstants.HEADER_SET_COOKIE, cookieHeader); try { cookies = cookieSpec.parse(basicHeader, cookieOrigin); } catch (MalformedCookieException e) { log.error("Unable to add the cookie", e); } if (cookies == null) { return; } for (org.apache.http.cookie.Cookie cookie : cookies) { try { if (checkCookies) { cookieSpec.validate(cookie, cookieOrigin); } Date expiryDate = cookie.getExpiryDate(); long exp = 0; if (expiryDate!= null) { exp=expiryDate.getTime(); } Cookie newCookie = new Cookie( cookie.getName(), cookie.getValue(), cookie.getDomain(), cookie.getPath(), cookie.isSecure(), exp / 1000, ((BasicClientCookie)cookie).containsAttribute(ClientCookie.PATH_ATTR), ((BasicClientCookie)cookie).containsAttribute(ClientCookie.DOMAIN_ATTR), cookie.getVersion()); // Store session cookies as well as unexpired ones if (exp == 0 || exp >= System.currentTimeMillis()) { cookieManager.add(newCookie); // Has its own debug log; removes matching cookies } else { cookieManager.removeMatchingCookies(newCookie); if (debugEnabled){ log.info("Dropping expired Cookie: "+newCookie.toString()); } } } catch (MalformedCookieException e) { // This means the cookie was wrong for the URL log.warn("Not storing invalid cookie: <"+cookieHeader+"> for URL "+url+" ("+e.getLocalizedMessage()+")"); } catch (IllegalArgumentException e) { log.warn(cookieHeader+e.getLocalizedMessage()); } } } @Override public String getCookieHeaderForURL(CollectionProperty cookiesCP, URL url, boolean allowVariableCookie) { List<org.apache.http.cookie.Cookie> c = getCookiesForUrl(cookiesCP, url, allowVariableCookie); boolean debugEnabled = log.isDebugEnabled(); if (debugEnabled){ log.debug("Found "+c.size()+" cookies for "+url.toExternalForm()); } if (c.isEmpty()) { return null; } List<Header> lstHdr = cookieSpec.formatCookies(c); StringBuilder sbHdr = new StringBuilder(); for (Header header : lstHdr) { sbHdr.append(header.getValue()); } return sbHdr.toString(); } /** * Get array of valid HttpClient cookies for the URL * * @param cookiesCP property with all available cookies * @param url the target URL * @param allowVariableCookie flag whether cookies may contain jmeter variables * @return array of HttpClient cookies * */ List<org.apache.http.cookie.Cookie> getCookiesForUrl( CollectionProperty cookiesCP, URL url, boolean allowVariableCookie) { List<org.apache.http.cookie.Cookie> cookies = new ArrayList<>(); for (JMeterProperty jMeterProperty : cookiesCP) { Cookie jmcookie = (Cookie) jMeterProperty.getObjectValue(); // Set to running version, to allow function evaluation for the cookie values (bug 28715) if (allowVariableCookie) { jmcookie.setRunningVersion(true); } cookies.add(makeCookie(jmcookie)); if (allowVariableCookie) { jmcookie.setRunningVersion(false); } } String host = url.getHost(); String protocol = url.getProtocol(); int port = HTTPSamplerBase.getDefaultPort(protocol, url.getPort()); String path = url.getPath(); boolean secure = HTTPSamplerBase.isSecure(protocol); CookieOrigin cookieOrigin = new CookieOrigin(host, port, path, secure); List<org.apache.http.cookie.Cookie> cookiesValid = new ArrayList<>(); for (org.apache.http.cookie.Cookie cookie : cookies) { if (cookieSpec.match(cookie, cookieOrigin)) { cookiesValid.add(cookie); } } return cookiesValid; } /** * Create an HttpClient cookie from a JMeter cookie */ private org.apache.http.cookie.Cookie makeCookie(Cookie jmc) { long exp = jmc.getExpiresMillis(); BasicClientCookie ret = new BasicClientCookie(jmc.getName(), jmc.getValue()); ret.setDomain(jmc.getDomain()); ret.setPath(jmc.getPath()); ret.setExpiryDate(exp > 0 ? new Date(exp) : null); // use null for no expiry ret.setSecure(jmc.getSecure()); ret.setVersion(jmc.getVersion()); if(jmc.isDomainSpecified()) { ret.setAttribute(ClientCookie.DOMAIN_ATTR, jmc.getDomain()); } if(jmc.isPathSpecified()) { ret.setAttribute(ClientCookie.PATH_ATTR, jmc.getPath()); } return ret; } @Override public String getDefaultPolicy() { return DEFAULT_POLICY_NAME; } @Override public String[] getPolicies() { return AVAILABLE_POLICIES; } }