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