| /* 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 java.net; |
| |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.Map; |
| |
| /** |
| * This class provides a concrete implementation of CookieHandler. It separates |
| * the storage of cookies from the policy which decides to accept or deny |
| * cookies. The constructor can have two arguments: a CookieStore and a |
| * CookiePolicy. The former is in charge of cookie storage and the latter makes |
| * decision on acceptance/rejection. |
| * |
| * CookieHandler is in the center of cookie management. User can make use of |
| * CookieHandler.setDefault to set a CookieManager as the default one used. |
| * |
| * CookieManager.put uses CookiePolicy.shouldAccept to decide whether to put |
| * some cookies into a cookie store. Three built-in CookiePolicy is defined: |
| * ACCEPT_ALL, ACCEPT_NONE and ACCEPT_ORIGINAL_SERVER. Users can also customize |
| * the policy by implementing CookiePolicy. Any accepted HTTP cookie is stored |
| * in CookieStore and users can also have their own implementation. Up to now, |
| * Only add(URI, HttpCookie) and get(URI) are used by CookieManager. Other |
| * methods in this class may probably be used in a more complicated |
| * implementation. |
| * |
| * There are many ways to customize user's own HTTP cookie management: |
| * |
| * First, call CookieHandler.setDefault to set a new CookieHandler |
| * implementation. Second, call CookieHandler.getDefault to use CookieManager. |
| * The CookiePolicy and CookieStore used are customized. Third, use the |
| * customized CookiePolicy and the CookieStore. |
| * |
| * This implementation conforms to RFC 2965, section 3.3. |
| * |
| * @since 1.6 |
| */ |
| public class CookieManager extends CookieHandler { |
| private CookieStore store; |
| |
| private CookiePolicy policy; |
| |
| private static final String VERSION_ZERO_HEADER = "Set-cookie"; |
| |
| private static final String VERSION_ONE_HEADER = "Set-cookie2"; |
| |
| /** |
| * Constructs a new cookie manager. |
| * |
| * The invocation of this constructor is the same as the invocation of |
| * CookieManager(null, null). |
| * |
| */ |
| public CookieManager() { |
| this(null, null); |
| } |
| |
| /** |
| * Constructs a new cookie manager using a specified cookie store and a |
| * cookie policy. |
| * |
| * @param store |
| * a CookieStore to be used by cookie manager. The manager will |
| * use a default one if the arg is null. |
| * @param cookiePolicy |
| * a CookiePolicy to be used by cookie manager |
| * ACCEPT_ORIGINAL_SERVER will be used if the arg is null. |
| */ |
| public CookieManager(CookieStore store, CookiePolicy cookiePolicy) { |
| this.store = store == null ? new CookieStoreImpl() : store; |
| policy = cookiePolicy == null ? CookiePolicy.ACCEPT_ORIGINAL_SERVER |
| : cookiePolicy; |
| } |
| |
| /** |
| * Searches and gets all cookies in the cache by the specified uri in the |
| * request header. |
| * |
| * @param uri |
| * the specified uri to search for |
| * @param requestHeaders |
| * a list of request headers |
| * @return a map that record all such cookies, the map is unchangeable |
| * @throws IOException |
| * if some error of I/O operation occurs |
| */ |
| @Override |
| public Map<String, List<String>> get(URI uri, |
| Map<String, List<String>> requestHeaders) throws IOException { |
| if (uri == null || requestHeaders == null) { |
| throw new IllegalArgumentException(); |
| } |
| |
| List<HttpCookie> result = new ArrayList<HttpCookie>(); |
| for (HttpCookie cookie : store.get(uri)) { |
| if (HttpCookie.pathMatches(cookie, uri) |
| && HttpCookie.secureMatches(cookie, uri) |
| && HttpCookie.portMatches(cookie, uri)) { |
| result.add(cookie); |
| } |
| } |
| |
| return cookiesToHeaders(result); |
| } |
| |
| private static Map<String, List<String>> cookiesToHeaders(List<HttpCookie> cookies) { |
| if (cookies.isEmpty()) { |
| return Collections.emptyMap(); |
| } |
| |
| StringBuilder result = new StringBuilder(); |
| |
| // If all cookies are version 1, add a version 1 header. No header for version 0 cookies. |
| int minVersion = 1; |
| for (HttpCookie cookie : cookies) { |
| minVersion = Math.min(minVersion, cookie.getVersion()); |
| } |
| if (minVersion == 1) { |
| result.append("$Version=\"1\"; "); |
| } |
| |
| result.append(cookies.get(0).toString()); |
| for (int i = 1; i < cookies.size(); i++) { |
| result.append("; ").append(cookies.get(i).toString()); |
| } |
| |
| return Collections.singletonMap("Cookie", Collections.singletonList(result.toString())); |
| } |
| |
| /** |
| * Sets cookies according to uri and responseHeaders |
| * |
| * @param uri |
| * the specified uri |
| * @param responseHeaders |
| * a list of request headers |
| * @throws IOException |
| * if some error of I/O operation occurs |
| */ |
| @Override |
| public void put(URI uri, Map<String, List<String>> responseHeaders) throws IOException { |
| if (uri == null || responseHeaders == null) { |
| throw new IllegalArgumentException(); |
| } |
| |
| // parse and construct cookies according to the map |
| List<HttpCookie> cookies = parseCookie(responseHeaders); |
| for (HttpCookie cookie : cookies) { |
| |
| // if the cookie doesn't have a domain, set one. The policy will do validation. |
| if (cookie.getDomain() == null) { |
| cookie.setDomain(uri.getHost()); |
| } |
| |
| // if the cookie doesn't have a path, set one. If it does, validate it. |
| if (cookie.getPath() == null) { |
| cookie.setPath(pathToCookiePath(uri.getPath())); |
| } else if (!HttpCookie.pathMatches(cookie, uri)) { |
| continue; |
| } |
| |
| // if the cookie has the placeholder port list "", set the port. Otherwise validate it. |
| if ("".equals(cookie.getPortlist())) { |
| cookie.setPortlist(Integer.toString(uri.getEffectivePort())); |
| } else if (cookie.getPortlist() != null && !HttpCookie.portMatches(cookie, uri)) { |
| continue; |
| } |
| |
| // if the cookie conforms to the policy, add it into the store |
| if (policy.shouldAccept(uri, cookie)) { |
| store.add(uri, cookie); |
| } |
| } |
| } |
| |
| /** |
| * Returns a cookie-safe path by truncating everything after the last "/". |
| * When request path like "/foo/bar.html" yields a cookie, that cookie's |
| * default path is "/foo/". |
| */ |
| static String pathToCookiePath(String path) { |
| if (path == null) { |
| return "/"; |
| } |
| int lastSlash = path.lastIndexOf('/'); // -1 yields the empty string |
| return path.substring(0, lastSlash + 1); |
| } |
| |
| private static List<HttpCookie> parseCookie(Map<String, List<String>> responseHeaders) { |
| List<HttpCookie> cookies = new ArrayList<HttpCookie>(); |
| for (Map.Entry<String, List<String>> entry : responseHeaders.entrySet()) { |
| String key = entry.getKey(); |
| // Only "Set-cookie" and "Set-cookie2" pair will be parsed |
| if (key != null && (key.equalsIgnoreCase(VERSION_ZERO_HEADER) |
| || key.equalsIgnoreCase(VERSION_ONE_HEADER))) { |
| // parse list elements one by one |
| for (String cookieStr : entry.getValue()) { |
| try { |
| for (HttpCookie cookie : HttpCookie.parse(cookieStr)) { |
| cookies.add(cookie); |
| } |
| } catch (IllegalArgumentException ignored) { |
| // this string is invalid, jump to the next one. |
| } |
| } |
| } |
| } |
| return cookies; |
| } |
| |
| /** |
| * Sets the cookie policy of this cookie manager. |
| * |
| * ACCEPT_ORIGINAL_SERVER is the default policy for CookieManager. |
| * |
| * @param cookiePolicy |
| * the cookie policy. if null, the original policy will not be |
| * changed. |
| */ |
| public void setCookiePolicy(CookiePolicy cookiePolicy) { |
| if (cookiePolicy != null) { |
| policy = cookiePolicy; |
| } |
| } |
| |
| /** |
| * Gets current cookie store. |
| * |
| * @return the cookie store currently used by cookie manager. |
| */ |
| public CookieStore getCookieStore() { |
| return store; |
| } |
| } |