History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/java/net/CookieManager.java (1 of 18352 files). Show all

Unified · Split

java/pkg/java/net/CookieManager.java+446−0
@@ -0,0 +1,446 @@
1+/*
2+ * Copyright (c) 2005, 2008, Oracle and/or its affiliates. All rights reserved.
3+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+ *
5+ * This code is free software; you can redistribute it and/or modify it
6+ * under the terms of the GNU General Public License version 2 only, as
7+ * published by the Free Software Foundation. Oracle designates this
8+ * particular file as subject to the "Classpath" exception as provided
9+ * by Oracle in the LICENSE file that accompanied this code.
10+ *
11+ * This code is distributed in the hope that it will be useful, but WITHOUT
12+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+ * version 2 for more details (a copy is included in the LICENSE file that
15+ * accompanied this code).
16+ *
17+ * You should have received a copy of the GNU General Public License version
18+ * 2 along with this work; if not, write to the Free Software Foundation,
19+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+ *
21+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+ * or visit www.oracle.com if you need additional information or have any
23+ * questions.
24+ */
25+
26+package java.net;
27+
28+import java.util.Map;
29+import java.util.List;
30+import java.util.Collections;
31+import java.util.Comparator;
32+import java.io.IOException;
33+import sun.util.logging.PlatformLogger;
34+
35+/**
36+ * CookieManager provides a concrete implementation of {@link CookieHandler},
37+ * which separates the storage of cookies from the policy surrounding accepting
38+ * and rejecting cookies. A CookieManager is initialized with a {@link CookieStore}
39+ * which manages storage, and a {@link CookiePolicy} object, which makes
40+ * policy decisions on cookie acceptance/rejection.
41+ *
42+ * <p> The HTTP cookie management in java.net package looks like:
43+ * <blockquote>
44+ * <pre>
45+ * use
46+ * CookieHandler <------- HttpURLConnection
47+ * ^
48+ * | impl
49+ * | use
50+ * CookieManager -------> CookiePolicy
51+ * | use
52+ * |--------> HttpCookie
53+ * | ^
54+ * | | use
55+ * | use |
56+ * |--------> CookieStore
57+ * ^
58+ * | impl
59+ * |
60+ * Internal in-memory implementation
61+ * </pre>
62+ * <ul>
63+ * <li>
64+ * CookieHandler is at the core of cookie management. User can call
65+ * CookieHandler.setDefault to set a concrete CookieHanlder implementation
66+ * to be used.
67+ * </li>
68+ * <li>
69+ * CookiePolicy.shouldAccept will be called by CookieManager.put to see whether
70+ * or not one cookie should be accepted and put into cookie store. User can use
71+ * any of three pre-defined CookiePolicy, namely ACCEPT_ALL, ACCEPT_NONE and
72+ * ACCEPT_ORIGINAL_SERVER, or user can define his own CookiePolicy implementation
73+ * and tell CookieManager to use it.
74+ * </li>
75+ * <li>
76+ * CookieStore is the place where any accepted HTTP cookie is stored in.
77+ * If not specified when created, a CookieManager instance will use an internal
78+ * in-memory implementation. Or user can implements one and tell CookieManager
79+ * to use it.
80+ * </li>
81+ * <li>
82+ * Currently, only CookieStore.add(URI, HttpCookie) and CookieStore.get(URI)
83+ * are used by CookieManager. Others are for completeness and might be needed
84+ * by a more sophisticated CookieStore implementation, e.g. a NetscapeCookieSotre.
85+ * </li>
86+ * </ul>
87+ * </blockquote>
88+ *
89+ * <p>There're various ways user can hook up his own HTTP cookie management behavior, e.g.
90+ * <blockquote>
91+ * <ul>
92+ * <li>Use CookieHandler.setDefault to set a brand new {@link CookieHandler} implementation
93+ * <li>Let CookieManager be the default {@link CookieHandler} implementation,
94+ * but implement user's own {@link CookieStore} and {@link CookiePolicy}
95+ * and tell default CookieManager to use them:
96+ * <blockquote><pre>
97+ * // this should be done at the beginning of an HTTP session
98+ * CookieHandler.setDefault(new CookieManager(new MyCookieStore(), new MyCookiePolicy()));
99+ * </pre></blockquote>
100+ * <li>Let CookieManager be the default {@link CookieHandler} implementation, but
101+ * use customized {@link CookiePolicy}:
102+ * <blockquote><pre>
103+ * // this should be done at the beginning of an HTTP session
104+ * CookieHandler.setDefault(new CookieManager());
105+ * // this can be done at any point of an HTTP session
106+ * ((CookieManager)CookieHandler.getDefault()).setCookiePolicy(new MyCookiePolicy());
107+ * </pre></blockquote>
108+ * </ul>
109+ * </blockquote>
110+ *
111+ * <p>The implementation conforms to <a href="http://www.ietf.org/rfc/rfc2965.txt">RFC 2965</a>, section 3.3.
112+ *
113+ * @see CookiePolicy
114+ * @author Edward Wang
115+ * @since 1.6
116+ */
117+public class CookieManager extends CookieHandler
118+{
119+ /* ---------------- Fields -------------- */
120+
121+ private CookiePolicy policyCallback;
122+
123+
124+ private CookieStore cookieJar = null;
125+
126+
127+ /* ---------------- Ctors -------------- */
128+
129+ /**
130+ * Create a new cookie manager.
131+ *
132+ * <p>This constructor will create new cookie manager with default
133+ * cookie store and accept policy. The effect is same as
134+ * <tt>CookieManager(null, null)</tt>.
135+ */
136+ public CookieManager() {
137+ this(null, null);
138+ }
139+
140+
141+ /**
142+ * Create a new cookie manager with specified cookie store and cookie policy.
143+ *
144+ * @param store a <tt>CookieStore</tt> to be used by cookie manager.
145+ * if <tt>null</tt>, cookie manager will use a default one,
146+ * which is an in-memory CookieStore implmentation.
147+ * @param cookiePolicy a <tt>CookiePolicy</tt> instance
148+ * to be used by cookie manager as policy callback.
149+ * if <tt>null</tt>, ACCEPT_ORIGINAL_SERVER will
150+ * be used.
151+ */
152+ public CookieManager(CookieStore store,
153+ CookiePolicy cookiePolicy)
154+ {
155+ // use default cookie policy if not specify one
156+ policyCallback = (cookiePolicy == null) ? CookiePolicy.ACCEPT_ORIGINAL_SERVER
157+ : cookiePolicy;
158+
159+ // if not specify CookieStore to use, use default one
160+ if (store == null) {
161+ cookieJar = new InMemoryCookieStore();
162+ } else {
163+ cookieJar = store;
164+ }
165+ }
166+
167+
168+ /* ---------------- Public operations -------------- */
169+
170+ /**
171+ * To set the cookie policy of this cookie manager.
172+ *
173+ * <p> A instance of <tt>CookieManager</tt> will have
174+ * cookie policy ACCEPT_ORIGINAL_SERVER by default. Users always
175+ * can call this method to set another cookie policy.
176+ *
177+ * @param cookiePolicy the cookie policy. Can be <tt>null</tt>, which
178+ * has no effects on current cookie policy.
179+ */
180+ public void setCookiePolicy(CookiePolicy cookiePolicy) {
181+ if (cookiePolicy != null) policyCallback = cookiePolicy;
182+ }
183+
184+
185+ /**
186+ * To retrieve current cookie store.
187+ *
188+ * @return the cookie store currently used by cookie manager.
189+ */
190+ public CookieStore getCookieStore() {
191+ return cookieJar;
192+ }
193+
194+
195+ public Map<String, List<String>>
196+ get(URI uri, Map<String, List<String>> requestHeaders)
197+ throws IOException
198+ {
199+ // pre-condition check
200+ if (uri == null || requestHeaders == null) {
201+ throw new IllegalArgumentException("Argument is null");
202+ }
203+
204+ Map<String, List<String>> cookieMap =
205+ new java.util.HashMap<String, List<String>>();
206+ // if there's no default CookieStore, no way for us to get any cookie
207+ if (cookieJar == null)
208+ return Collections.unmodifiableMap(cookieMap);
209+
210+ boolean secureLink = "https".equalsIgnoreCase(uri.getScheme());
211+ List<HttpCookie> cookies = new java.util.ArrayList<HttpCookie>();
212+ String path = uri.getPath();
213+ if (path == null || path.isEmpty()) {
214+ path = "/";
215+ }
216+ for (HttpCookie cookie : cookieJar.get(uri)) {
217+ // apply path-matches rule (RFC 2965 sec. 3.3.4)
218+ // and check for the possible "secure" tag (i.e. don't send
219+ // 'secure' cookies over unsecure links)
220+ if (pathMatches(path, cookie.getPath()) &&
221+ (secureLink || !cookie.getSecure())) {
222+ // Enforce httponly attribute
223+ if (cookie.isHttpOnly()) {
224+ String s = uri.getScheme();
225+ if (!"http".equalsIgnoreCase(s) && !"https".equalsIgnoreCase(s)) {
226+ continue;
227+ }
228+ }
229+ // Let's check the authorize port list if it exists
230+ String ports = cookie.getPortlist();
231+ if (ports != null && !ports.isEmpty()) {
232+ int port = uri.getPort();
233+ if (port == -1) {
234+ port = "https".equals(uri.getScheme()) ? 443 : 80;
235+ }
236+ if (isInPortList(ports, port)) {
237+ cookies.add(cookie);
238+ }
239+ } else {
240+ cookies.add(cookie);
241+ }
242+ }
243+ }
244+
245+ // apply sort rule (RFC 2965 sec. 3.3.4)
246+ List<String> cookieHeader = sortByPath(cookies);
247+
248+ cookieMap.put("Cookie", cookieHeader);
249+ return Collections.unmodifiableMap(cookieMap);
250+ }
251+
252+
253+ public void
254+ put(URI uri, Map<String, List<String>> responseHeaders)
255+ throws IOException
256+ {
257+ // pre-condition check
258+ if (uri == null || responseHeaders == null) {
259+ throw new IllegalArgumentException("Argument is null");
260+ }
261+
262+
263+ // if there's no default CookieStore, no need to remember any cookie
264+ if (cookieJar == null)
265+ return;
266+
267+ PlatformLogger logger = PlatformLogger.getLogger("java.net.CookieManager");
268+ for (String headerKey : responseHeaders.keySet()) {
269+ // RFC 2965 3.2.2, key must be 'Set-Cookie2'
270+ // we also accept 'Set-Cookie' here for backward compatibility
271+ if (headerKey == null
272+ || !(headerKey.equalsIgnoreCase("Set-Cookie2")
273+ || headerKey.equalsIgnoreCase("Set-Cookie")
274+ )
275+ )
276+ {
277+ continue;
278+ }
279+
280+ for (String headerValue : responseHeaders.get(headerKey)) {
281+ try {
282+ List<HttpCookie> cookies;
283+ try {
284+ cookies = HttpCookie.parse(headerValue);
285+ } catch (IllegalArgumentException e) {
286+ // Bogus header, make an empty list and log the error
287+ cookies = java.util.Collections.EMPTY_LIST;
288+ if (logger.isLoggable(PlatformLogger.SEVERE)) {
289+ logger.severe("Invalid cookie for " + uri + ": " + headerValue);
290+ }
291+ }
292+ for (HttpCookie cookie : cookies) {
293+ if (cookie.getPath() == null) {
294+ // If no path is specified, then by default
295+ // the path is the directory of the page/doc
296+ String path = uri.getPath();
297+ if (!path.endsWith("/")) {
298+ int i = path.lastIndexOf("/");
299+ if (i > 0) {
300+ path = path.substring(0, i + 1);
301+ } else {
302+ path = "/";
303+ }
304+ }
305+ cookie.setPath(path);
306+ }
307+
308+ // As per RFC 2965, section 3.3.1:
309+ // Domain Defaults to the effective request-host. (Note that because
310+ // there is no dot at the beginning of effective request-host,
311+ // the default Domain can only domain-match itself.)
312+ if (cookie.getDomain() == null) {
313+ cookie.setDomain(uri.getHost());
314+ }
315+ String ports = cookie.getPortlist();
316+ if (ports != null) {
317+ int port = uri.getPort();
318+ if (port == -1) {
319+ port = "https".equals(uri.getScheme()) ? 443 : 80;
320+ }
321+ if (ports.isEmpty()) {
322+ // Empty port list means this should be restricted
323+ // to the incoming URI port
324+ cookie.setPortlist("" + port );
325+ if (shouldAcceptInternal(uri, cookie)) {
326+ cookieJar.add(uri, cookie);
327+ }
328+ } else {
329+ // Only store cookies with a port list
330+ // IF the URI port is in that list, as per
331+ // RFC 2965 section 3.3.2
332+ if (isInPortList(ports, port) &&
333+ shouldAcceptInternal(uri, cookie)) {
334+ cookieJar.add(uri, cookie);
335+ }
336+ }
337+ } else {
338+ if (shouldAcceptInternal(uri, cookie)) {
339+ cookieJar.add(uri, cookie);
340+ }
341+ }
342+ }
343+ } catch (IllegalArgumentException e) {
344+ // invalid set-cookie header string
345+ // no-op
346+ }
347+ }
348+ }
349+ }
350+
351+
352+ /* ---------------- Private operations -------------- */
353+
354+ // to determine whether or not accept this cookie
355+ private boolean shouldAcceptInternal(URI uri, HttpCookie cookie) {
356+ try {
357+ return policyCallback.shouldAccept(uri, cookie);
358+ } catch (Exception ignored) { // pretect against malicious callback
359+ return false;
360+ }
361+ }
362+
363+
364+ static private boolean isInPortList(String lst, int port) {
365+ int i = lst.indexOf(",");
366+ int val = -1;
367+ while (i > 0) {
368+ try {
369+ val = Integer.parseInt(lst.substring(0, i));
370+ if (val == port) {
371+ return true;
372+ }
373+ } catch (NumberFormatException numberFormatException) {
374+ }
375+ lst = lst.substring(i+1);
376+ i = lst.indexOf(",");
377+ }
378+ if (!lst.isEmpty()) {
379+ try {
380+ val = Integer.parseInt(lst);
381+ if (val == port) {
382+ return true;
383+ }
384+ } catch (NumberFormatException numberFormatException) {
385+ }
386+ }
387+ return false;
388+ }
389+
390+ /*
391+ * path-matches algorithm, as defined by RFC 2965
392+ */
393+ private boolean pathMatches(String path, String pathToMatchWith) {
394+ if (path == pathToMatchWith)
395+ return true;
396+ if (path == null || pathToMatchWith == null)
397+ return false;
398+ if (path.startsWith(pathToMatchWith))
399+ return true;
400+
401+ return false;
402+ }
403+
404+
405+ /*
406+ * sort cookies with respect to their path: those with more specific Path attributes
407+ * precede those with less specific, as defined in RFC 2965 sec. 3.3.4
408+ */
409+ private List<String> sortByPath(List<HttpCookie> cookies) {
410+ Collections.sort(cookies, new CookiePathComparator());
411+
412+ List<String> cookieHeader = new java.util.ArrayList<String>();
413+ for (HttpCookie cookie : cookies) {
414+ // Netscape cookie spec and RFC 2965 have different format of Cookie
415+ // header; RFC 2965 requires a leading $Version="1" string while Netscape
416+ // does not.
417+ // The workaround here is to add a $Version="1" string in advance
418+ if (cookies.indexOf(cookie) == 0 && cookie.getVersion() > 0) {
419+ cookieHeader.add("$Version=\"1\"");
420+ }
421+
422+ cookieHeader.add(cookie.toString());
423+ }
424+ return cookieHeader;
425+ }
426+
427+
428+ static class CookiePathComparator implements Comparator<HttpCookie> {
429+ public int compare(HttpCookie c1, HttpCookie c2) {
430+ if (c1 == c2) return 0;
431+ if (c1 == null) return -1;
432+ if (c2 == null) return 1;
433+
434+ // path rule only applies to the cookies with same name
435+ if (!c1.getName().equals(c2.getName())) return 0;
436+
437+ // those with more specific Path attributes precede those with less specific
438+ if (c1.getPath().startsWith(c2.getPath()))
439+ return -1;
440+ else if (c2.getPath().startsWith(c1.getPath()))
441+ return 1;
442+ else
443+ return 0;
444+ }
445+ }
446+}