All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.mike10004.seleniumhelp.CookieUtility Maven / Gradle / Ivy

There is a newer version: 0.58
Show newest version
package com.github.mike10004.seleniumhelp;

import com.google.common.base.CharMatcher;
import com.google.common.base.MoreObjects;
import com.google.common.net.HttpHeaders;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.ImmutableTriple;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.tuple.Triple;
import org.apache.commons.text.StringEscapeUtils;
import org.apache.http.Header;
import org.apache.http.cookie.ClientCookie;
import org.apache.http.cookie.Cookie;
import org.apache.http.cookie.CookieOrigin;
import org.apache.http.cookie.CookieSpec;
import org.apache.http.cookie.MalformedCookieException;
import org.apache.http.message.BasicHeader;

import javax.annotation.Nullable;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.util.Date;
import java.util.TimeZone;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream;

import static com.google.common.base.Preconditions.checkNotNull;

/**
 * Cookie utility. Methods copied from HTMLUnit CookieManager. See that class for license.
 */
public class CookieUtility {

    private CookieUtility() {

    }

    private static final CookieUtility instance = new CookieUtility();

    public static CookieUtility getInstance() {
        return instance;
    }

    public static class CookieHash extends Triple {

        private final ImmutableTriple inner;

        public CookieHash(String domain, String name, String path) {
            this(ImmutableTriple.of(domain, name, path));
        }

        private CookieHash(ImmutableTriple inner) {
            this.inner = inner;
        }

        @Override
        public String getLeft() {
            return inner.getLeft();
        }

        @Override
        public String getMiddle() {
            return inner.getMiddle();
        }

        @Override
        public String getRight() {
            return inner.getRight();
        }

        @Override
        public int compareTo(Triple other) {
            return inner.compareTo(other);
        }

        @SuppressWarnings("EqualsWhichDoesntCheckParameterClass") // because the delegate evaluates this
        @Override
        public boolean equals(Object obj) {
            return inner.equals(obj);
        }

        @Override
        public int hashCode() {
            return inner.hashCode();
        }

        @Override
        public String toString() {
            return inner.toString();
        }
    }

    public static CookieHash getIdentifier(Cookie apacheCookie) {
        return new CookieHash(checkNotNull(apacheCookie.getDomain(), "domain"),
                checkNotNull(apacheCookie.getName(), "name"),
                MoreObjects.firstNonNull(apacheCookie.getPath(), "/"));
    }

//    public Set filterApacheCookiesBySeleniumMatches(List apacheCookies, Iterable seleniumCookies) {
//        List matchingCookieList = new ArrayList<>();
//        for (org.openqa.selenium.Cookie seleniumCookie : seleniumCookies) {
//            Iterator matches = apacheCookies.stream().filter(apacheMatcher(seleniumCookie)).iterator();
//            if (matches.hasNext()) {
//                Cookie m = matches.next();
//                if (matches.hasNext()) {
//                    throw new IllegalArgumentException("more than one apache cookie matches " + seleniumCookie + "; all matching: " + matches);
//                }
//                matchingCookieList.add(m);
//            }
//        }
//        Map mapWithLatest = new HashMap<>(matchingCookieList.size());
//        for (Cookie cookie : matchingCookieList) {
//            mapWithLatest.put(getIdentifier(cookie), cookie);
//        }
//        Set cookieSet = ImmutableSet.copyOf(mapWithLatest.values());
//        return cookieSet;
//    }

//    public static Predicate apacheMatcher(final org.openqa.selenium.Cookie requiredParameters) {
//        return apacheMatcher(requiredParameters.getName(), requiredParameters.getValue(), requiredParameters.getDomain(), requiredParameters.getPath());
//    }

//    private static boolean isSameDomain(String domain, String apacheCookieDomainValue) {
//        return Objects.equals(domain, apacheCookieDomainValue)
//                || (!domain.isEmpty() && domain.charAt(0) == '.'
//                && domain.regionMatches(true, 1, apacheCookieDomainValue, 0, apacheCookieDomainValue.length()));
//    }

//    public static Predicate
//
//    public static Predicate apacheMatcher(final String name, final String value, final String domain, final @Nullable String path) {
//        checkNotNull(name);
//        checkNotNull(value);
//        checkNotNull(domain);
//        return c -> c != null && name.equalsIgnoreCase(c.getName())
//                && value.equals(c.getValue())
//                && isSameDomain(domain, c.getDomain())
//                && Objects.equals(path, c.getPath());
//    }

    public static Function> headerToCookiesFunction(final CookieOrigin cookieOrigin, final CookieSpec cookieSpec) {
        return input -> {
            Header header = new BasicHeader(HttpHeaders.SET_COOKIE, input);
            try {
                return cookieSpec.parse(header, cookieOrigin).stream();
            } catch (MalformedCookieException e) {
                throw new IllegalArgumentException(e);
            }
        };
    }

    public static class ParsedCookieOrigin extends Pair {

        public final CookieOrigin origin;
        public final URL normalizedUrl;
        private final ImmutablePair delegate;

        public ParsedCookieOrigin(CookieOrigin origin, URL normalizedUrl) {
            this.origin = checkNotNull(origin);
            this.normalizedUrl = checkNotNull(normalizedUrl);
            this.delegate = ImmutablePair.of(origin, normalizedUrl);
        }

        @Override
        public CookieOrigin getLeft() {
            return delegate.getLeft();
        }

        @Override
        public URL getRight() {
            return delegate.getRight();
        }

        @Override
        public URL setValue(URL value) {
            return delegate.setValue(value);
        }

        @Override
        public URL getValue() {
            return delegate.getValue();
        }

        @Override
        public int compareTo(Pair other) {
            return delegate.compareTo(other);
        }

        @SuppressWarnings("EqualsWhichDoesntCheckParameterClass") // because we delegate
        @Override
        public boolean equals(Object obj) {
            return delegate.equals(obj);
        }

        @Override
        public int hashCode() {
            return delegate.hashCode();
        }

        @Override
        public String toString() {
            return delegate.toString();
        }
    }

    /**
     * Helper that builds a CookieOrigin.
     * @param url the url to be used
     * @return the new CookieOrigin and the normalized URL
     */
    public ParsedCookieOrigin buildCookieOrigin(final URL url) {
        final URL normalizedUrl = replaceForCookieIfNecessary(url);
        CookieOrigin origin = new CookieOrigin(
                normalizedUrl.getHost(),
                getPort(normalizedUrl),
                normalizedUrl.getPath(),
                "https".equals(normalizedUrl.getProtocol()));
        return new ParsedCookieOrigin(origin, normalizedUrl);
    }

    /**
     * Creates a predicate that filters expired cookies.
     * @param referenceDate the date to use for comparison (usually the current time) to the cookie's expiry date
     * @return a predicate that returns true for a cookie that is not expired by the given date
     */
    public synchronized Predicate notExpiredOn(final Date referenceDate) {
        checkNotNull(referenceDate, "date");
        return cookie -> {
            checkNotNull(cookie, "cookie");
            Date expiry = cookie.getExpiryDate();
            if (expiry == null) {
                return true;
            }
            boolean stillValid = expiry.after(referenceDate);
            return stillValid;
        };
    }

    /**
     * Gets the port of the URL.
     * This functionality is implemented here as protected method to allow subclass to change it
     * as workaround to 
     * Google App Engine bug 4784.
     * @param url the URL
     * @return the port use to connect the server
     */
    protected int getPort(final URL url) {
        if (url.getPort() != -1) {
            return url.getPort();
        }
        return url.getDefaultPort();
    }

    /**
     * {@link CookieOrigin} doesn't like empty hosts and negative ports,
     * but these things happen if we're dealing with a local file.
     * This method allows us to work around this limitation in HttpClient by feeding it a bogus host and port.
     *
     * @param url the URL to replace if necessary
     * @return the replacement URL, or the original URL if no replacement was necessary
     */
    private URL replaceForCookieIfNecessary(URL url) {
        final String protocol = url.getProtocol();
        final boolean file = "file".equals(protocol);
        if (file) {
            try {
                url = getUrlWithNewHostAndPort(url, LOCAL_FILESYSTEM_DOMAIN, 0);
            }
            catch (final MalformedURLException e) {
                throw new RuntimeException(e);
            }
        }
        return url;
    }
    /** Workaround for domain of local files. */
    private static final String LOCAL_FILESYSTEM_DOMAIN = "LOCAL_FILESYSTEM";

    /**
     * Creates and returns a new URL identical to the specified URL, except using the specified host.
     * 

See {@code com.gargoylesoftware.htmlunit.util.UrlUtils}. * @param u the URL on which to base the returned URL * @param newHost the new host to use in the returned URL * @param newPort the new port to use in the returned URL * @return a new URL identical to the specified URL, except using the specified host * @throws MalformedURLException if there is a problem creating the new URL */ @SuppressWarnings("SameParameterValue") private static URL getUrlWithNewHostAndPort(final URL u, final String newHost, final int newPort) throws MalformedURLException { return createNewUrl(u.getProtocol(), u.getUserInfo(), newHost, newPort, u.getPath(), u.getRef(), u.getQuery()); } /** * Creates a new URL based on the specified fragments. *

See {@code com.gargoylesoftware.htmlunit.util.UrlUtils}. * @param protocol the protocol to use (may not be {@code null}) * @param userInfo the user info to use (may be {@code null}) * @param host the host to use (may not be {@code null}) * @param port the port to use (may be -1 if no port is specified) * @param path the path to use (may be {@code null} and may omit the initial '/') * @param ref the reference to use (may be {@code null} and must not include the '#') * @param query the query to use (may be {@code null} and must not include the '?') * @return a new URL based on the specified fragments * @throws MalformedURLException if there is a problem creating the new URL */ private static URL createNewUrl(final String protocol, final String userInfo, final String host, final int port, final String path, final String ref, final String query) throws MalformedURLException { final StringBuilder s = new StringBuilder(); s.append(protocol); s.append("://"); if (userInfo != null) { s.append(userInfo).append("@"); } s.append(host); if (port != -1) { s.append(":").append(port); } if (path != null && !path.isEmpty()) { if (!('/' == path.charAt(0))) { s.append("/"); } s.append(path); } if (query != null) { s.append("?").append(query); } if (ref != null) { if (ref.isEmpty() || ref.charAt(0) != '#') { s.append("#"); } s.append(ref); } final URL url = new URL(s.toString()); return url; } private static String checkOnlyContains(String value, CharMatcher permitted) { if (!permitted.matchesAllOf(value)) { throw new IllegalArgumentException(String.format("value \"%s\" does not match %s", StringEscapeUtils.escapeJava(value), permitted)); } return value; } private static CharMatcher legalNameChars = CharMatcher.noneOf("="); private static CharMatcher legalOtherChars = CharMatcher.noneOf(";"); private static final SimpleDateFormat dateFormat; static { // Thu, 29-Nov-2018 19:22:24 GMT dateFormat = new SimpleDateFormat("EEE, dd-MMM-yyyy HH:mm:ss zzz"); dateFormat.setTimeZone(TimeZone.getTimeZone("GMT")); } protected static String formatDateForHeader(Instant instant) { return formatDateForHeader(Date.from(instant)); } protected static String formatDateForHeader(Date date) { checkNotNull(date, "date"); return dateFormat.format(date); } protected static @Nullable String getDomain(org.apache.http.cookie.Cookie c) { if (c instanceof ClientCookie) { ClientCookie d = (ClientCookie) c; @Nullable String dAttr = d.getAttribute("domain"); if (dAttr != null) { return dAttr; } } return c.getDomain(); } public String formatSetCookieHeader(DeserializableCookie c) { StringBuilder b = new StringBuilder(512); b.append(checkOnlyContains(c.getName(), legalNameChars)); b.append('='); b.append(checkOnlyContains(c.getValue(), legalOtherChars)); if (c.getExpiryInstant() != null) { b.append("; Expires=").append(formatDateForHeader(c.getExpiryInstant())); } @Nullable String domain = getDomain(c); if (domain != null) { b.append("; Domain=").append(domain); } if (c.getPath() != null) { b.append("; Path=").append(c.getPath()); } if (c.isSecure()) { b.append("; Secure"); } if (c.isHttpOnly()) { b.append("; HttpOnly"); } return b.toString(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy