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
26package java.net;
27
28import java.util.Map;
29import java.util.List;
30import java.util.Collections;
31import java.util.Comparator;
32import java.io.IOException;
33import 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 */
117public 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}