Search code examples
javahttpcookieshttpshttpcookie

CookieManager for multiple threads


I am trying to make multiple connections via threads.

But every connection seems to override the other's cookies, resulting in the connections using the wrong cookies.

inside the threaded class's constructor:

    manager = new CookieManager();
    manager.setCookiePolicy(CookiePolicy.ACCEPT_ALL);
    CookieHandler.setDefault(manager);

Any way to manage the cookies per thread or per class?

New failed try:

Now every thread is using it's own index, yet they still seem to override each other cookie-wise. Any ideas?

public class threadedCookieStore implements CookieStore, Runnable {
    CookieStore[] store = new CookieStore[1000];
    int index;

    public threadedCookieStore(int new_index) {
        index = new_index;
        // get the default in memory cookie store
        store[index] = new CookieManager().getCookieStore();


        // todo: read in cookies from persistant storage
        // and add them store

        // add a shutdown hook to write out the in memory cookies
        Runtime.getRuntime().addShutdownHook(new Thread(this)); 
    }

    public void run() {
        // todo: write cookies in store to persistent storage
    }

    public void add(URI uri, HttpCookie cookie) {
        store[index].add(uri, cookie);
    }

    public List<HttpCookie> get(URI uri) {
        return store[index].get(uri);
    }

    public List<HttpCookie> getCookies() {
        return store[index].getCookies();
    }

    public List<URI> getURIs() {
        return store[index].getURIs();
    }

    public boolean remove(URI uri, HttpCookie cookie) {
        return store[index].remove(uri, cookie);
    }

     public boolean removeAll()  {
         return store[index].removeAll();
    }
}

Within the class:

threadedCookieStore cookiestore = new threadedCookieStore(index);

manager = new CookieManager(cookiestore,CookiePolicy.ACCEPT_ALL);
manager.setCookiePolicy(CookiePolicy.ACCEPT_ALL);
CookieHandler.setDefault(manager);

Solution

  • Thanks everyone.

    I upvoted all the answers, yet none had a complete solution.

    Since google'ing this problem leads to this page here, I'll post the complete solution and accept my own answer:

    HowTo:

    1 Extend CookieHandler to SessionCookieManager

    this is based on How to use different cookies for each connection using HttpURLConnection and the CookieManager in Java , nivs describes it correctly, doesn't provide a full solution tho. So most/all credit goes to him, I'm just making the complete HowTo. The SessionCookieManager is based on Java 's source code http://docs.oracle.com/javase/7/docs/api/java/net/CookieManager.html

    import java.io.IOException;
    import java.net.CookieHandler;
    import java.net.CookiePolicy;
    import java.net.CookieStore;
    import java.net.HttpCookie;
    import java.net.URI;
    import java.util.Collections;
    import java.util.Comparator;
    import java.util.List;
    import java.util.Map;
    
    public class SessionCookieManager extends CookieHandler
    {
        private CookiePolicy policyCallback;
    
    
        public SessionCookieManager() {
            this(null, null);
        }
    
        private final static SessionCookieManager ms_instance = new SessionCookieManager();
    
        public static SessionCookieManager getInstance()
        {
            return ms_instance;
        }
    
        private final static ThreadLocal<CookieStore> ms_cookieJars = new ThreadLocal<CookieStore>() {
            @Override
            protected synchronized CookieStore initialValue() { return new InMemoryCookieStore(); }
        };
    
        public void clear()
        {
            getCookieStore().removeAll();
        }
        public SessionCookieManager(CookieStore store,
                             CookiePolicy cookiePolicy)
        {
            // use default cookie policy if not specify one
            policyCallback = (cookiePolicy == null) ? CookiePolicy.ACCEPT_ALL //note that I changed it to ACCEPT_ALL
                                                    : cookiePolicy;
    
            // if not specify CookieStore to use, use default one
    
        }
        public void setCookiePolicy(CookiePolicy cookiePolicy) {
            if (cookiePolicy != null) policyCallback = cookiePolicy;
        }
    
        public CookieStore getCookieStore() {
            return ms_cookieJars.get();
        }
    
        public Map<String, List<String>>
            get(URI uri, Map<String, List<String>> requestHeaders)
            throws IOException
        {
            // pre-condition check
            if (uri == null || requestHeaders == null) {
                throw new IllegalArgumentException("Argument is null");
            }
    
            Map<String, List<String>> cookieMap =
                            new java.util.HashMap<String, List<String>>();
            // if there's no default CookieStore, no way for us to get any cookie
            if (getCookieStore() == null)
                return Collections.unmodifiableMap(cookieMap);
    
            List<HttpCookie> cookies = new java.util.ArrayList<HttpCookie>();
            for (HttpCookie cookie : getCookieStore().get(uri)) {
                // apply path-matches rule (RFC 2965 sec. 3.3.4)
                if (pathMatches(uri.getPath(), cookie.getPath())) {
                    cookies.add(cookie);
                }
            }
    
            // apply sort rule (RFC 2965 sec. 3.3.4)
            List<String> cookieHeader = sortByPath(cookies);
    
            cookieMap.put("Cookie", cookieHeader);
            return Collections.unmodifiableMap(cookieMap);
        }
    
    
        public void
            put(URI uri, Map<String, List<String>> responseHeaders)
            throws IOException
        {
            // pre-condition check
            if (uri == null || responseHeaders == null) {
                throw new IllegalArgumentException("Argument is null");
            }
    
    
            // if there's no default CookieStore, no need to remember any cookie
            if (getCookieStore() == null)
                return;
    
            for (String headerKey : responseHeaders.keySet()) {
                // RFC 2965 3.2.2, key must be 'Set-Cookie2'
                // we also accept 'Set-Cookie' here for backward compatibility
                if (headerKey == null
                    || !(headerKey.equalsIgnoreCase("Set-Cookie2")
                         || headerKey.equalsIgnoreCase("Set-Cookie")
                        )
                    )
                {
                    continue;
                }
    
                for (String headerValue : responseHeaders.get(headerKey)) {
                    try {
                        List<HttpCookie> cookies = HttpCookie.parse(headerValue);
                        for (HttpCookie cookie : cookies) {
                            if (shouldAcceptInternal(uri, cookie)) {
                                getCookieStore().add(uri, cookie);
                            }
                        }
                    } catch (IllegalArgumentException e) {
                        // invalid set-cookie header string
                        // no-op
                    }
                }
            }
        }
    
    
        /* ---------------- Private operations -------------- */
    
        // to determine whether or not accept this cookie
        private boolean shouldAcceptInternal(URI uri, HttpCookie cookie) {
            try {
                return policyCallback.shouldAccept(uri, cookie);
            } catch (Exception ignored) { // pretect against malicious callback
                return false;
            }
        }
    
    
        /*
         * path-matches algorithm, as defined by RFC 2965
         */
        private boolean pathMatches(String path, String pathToMatchWith) {
            if (path == pathToMatchWith)
                return true;
            if (path == null || pathToMatchWith == null)
                return false;
            if (path.startsWith(pathToMatchWith))
                return true;
    
            return false;
        }
    
    
        /*
         * sort cookies with respect to their path: those with more specific Path attributes
         * precede those with less specific, as defined in RFC 2965 sec. 3.3.4
         */
        private List<String> sortByPath(List<HttpCookie> cookies) {
            Collections.sort(cookies, new CookiePathComparator());
    
            List<String> cookieHeader = new java.util.ArrayList<String>();
            for (HttpCookie cookie : cookies) {
                // Netscape cookie spec and RFC 2965 have different format of Cookie
                // header; RFC 2965 requires a leading $Version="1" string while Netscape
                // does not.
                // The workaround here is to add a $Version="1" string in advance
                if (cookies.indexOf(cookie) == 0 && cookie.getVersion() > 0) {
                    cookieHeader.add("$Version=\"1\"");
                }
    
                cookieHeader.add(cookie.toString());
            }
            return cookieHeader;
        }
    
    
        static class CookiePathComparator implements Comparator<HttpCookie> {
            public int compare(HttpCookie c1, HttpCookie c2) {
                if (c1 == c2) return 0;
                if (c1 == null) return -1;
                if (c2 == null) return 1;
    
                // path rule only applies to the cookies with same name
                if (!c1.getName().equals(c2.getName())) return 0;
    
                // those with more specific Path attributes precede those with less specific
                if (c1.getPath().startsWith(c2.getPath()))
                    return -1;
                else if (c2.getPath().startsWith(c1.getPath()))
                    return 1;
                else
                    return 0;
            }
        }
    }
    

    Note that in my case I changed the default value of CookiePolicy to ACCEPT_ALL

    2 In global scope, before running any threads, call:

    CookieHandler.setDefault(SessionCookieManager.getInstance());
    

    3 When your thread is finished, call inside of it:

    SessionCookieManager.getInstance().clear();
    

    again: not my idea, just putting it together. All credit goes to Java and https://stackoverflow.com/users/1442259/nivs