1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 package org.apache.commons.httpclient;
31
32 import java.util.ArrayList;
33 import java.util.Collection;
34 import java.util.Date;
35 import java.util.HashMap;
36 import java.util.Map;
37 import java.util.List;
38 import java.util.Iterator;
39 import java.util.SortedMap;
40 import java.util.TreeMap;
41
42 import org.apache.commons.httpclient.cookie.CookieSpec;
43 import org.apache.commons.httpclient.cookie.CookiePolicy;
44 import org.apache.commons.httpclient.auth.AuthScope;
45 import org.apache.commons.logging.Log;
46 import org.apache.commons.logging.LogFactory;
47
48 import com.sleepycat.collections.StoredIterator;
49
50 /***
51 * <p>
52 * A container for HTTP attributes that may persist from request
53 * to request, such as {@link Cookie cookies} and authentication
54 * {@link Credentials credentials}.
55 * </p>
56 *
57 * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
58 * @author Rodney Waldhoff
59 * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
60 * @author Sean C. Sullivan
61 * @author <a href="mailto:becke@u.washington.edu">Michael Becke</a>
62 * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
63 * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
64 * @author <a href="mailto:adrian@intencha.com">Adrian Sutton</a>
65 *
66 * @version $Revision: 5562 $ $Date: 2007-11-16 00:53:10 +0000 (Fri, 16 Nov 2007) $
67 *
68 */
69 @SuppressWarnings("unchecked")
70 public class HttpState {
71
72
73
74 /***
75 * Map of {@link Credentials credentials} by realm that this
76 * HTTP state contains.
77 */
78 private HashMap credMap = new HashMap();
79
80 /***
81 * Map of {@link Credentials proxy credentials} by realm that this
82 * HTTP state contains
83 */
84 private HashMap proxyCred = new HashMap();
85
86
87 /***
88 // * Array of {@link Cookie cookies} that this HTTP state contains.
89 // */
90
91 /***
92 * SortedMap of {@link Cookie cookies} that this HTTP state contains.
93 */
94 private SortedMap cookiesMap = new TreeMap();
95
96
97 private boolean preemptive = false;
98
99 private int cookiePolicy = -1;
100
101
102 /***
103 * The boolean system property name to turn on preemptive authentication.
104 * @deprecated This field and feature will be removed following HttpClient 3.0.
105 */
106 public static final String PREEMPTIVE_PROPERTY = "httpclient.authentication.preemptive";
107
108 /***
109 * The default value for {@link #PREEMPTIVE_PROPERTY}.
110 * @deprecated This field and feature will be removed following HttpClient 3.0.
111 */
112 public static final String PREEMPTIVE_DEFAULT = "false";
113
114 /*** Log object for this class. */
115 private static final Log LOG = LogFactory.getLog(HttpState.class);
116
117 /***
118 * Default constructor.
119 */
120 public HttpState() {
121 super();
122 }
123
124
125
126 /***
127 * Adds an {@link Cookie HTTP cookie}, replacing any existing equivalent cookies.
128 * If the given cookie has already expired it will not be added, but existing
129 * values will still be removed.
130 *
131 * @param cookie the {@link Cookie cookie} to be added
132 *
133 * @see #addCookies(Cookie[])
134 *
135 */
136 public synchronized void addCookie(Cookie cookie) {
137 LOG.trace("enter HttpState.addCookie(Cookie)");
138
139
140
141
142
143 if (cookie != null) {
144
145
146
147
148
149
150
151
152
153 if (!cookie.isExpired()) {
154
155 cookiesMap.put(cookie.getSortKey(),cookie);
156 } else {
157 cookiesMap.remove(cookie.getSortKey());
158 }
159 }
160
161
162
163
164 }
165
166 /***
167 * Adds an array of {@link Cookie HTTP cookies}. Cookies are added individually and
168 * in the given array order. If any of the given cookies has already expired it will
169 * not be added, but existing values will still be removed.
170 *
171 * @param cookies the {@link Cookie cookies} to be added
172 *
173 * @see #addCookie(Cookie)
174 *
175 *
176 */
177 public synchronized void addCookies(Cookie[] cookies) {
178 LOG.trace("enter HttpState.addCookies(Cookie[])");
179
180 if (cookies != null) {
181 for (int i = 0; i < cookies.length; i++) {
182 this.addCookie(cookies[i]);
183 }
184 }
185 }
186
187 /***
188 * Returns an array of {@link Cookie cookies} that this HTTP
189 * state currently contains.
190 *
191 * @return an array of {@link Cookie cookies}.
192 *
193 * @see #getCookies(String, int, String, boolean)
194 *
195 * @deprecated use getCookiesMap() // <- IA/HERITRIX CHANGE
196 */
197 public synchronized Cookie[] getCookies() {
198 LOG.trace("enter HttpState.getCookies()");
199
200
201
202 ArrayList arrayableCookies = new ArrayList();
203 Iterator iter = cookiesMap.values().iterator();
204 while(iter.hasNext()) {
205 arrayableCookies.add(iter.next());
206 }
207 StoredIterator.close(iter);
208 Cookie[] mapAnswer =
209 (Cookie[]) arrayableCookies.toArray(new Cookie[arrayableCookies.size()]);
210
211
212
213
214 return mapAnswer;
215
216 }
217
218
219 /***
220 * Returns a sorted map of {@link Cookie cookies} that this HTTP
221 * state currently contains.
222 *
223 * Any operations on this map should be synchronized with respect
224 * to this HttpState instance.
225 *
226 * @return sorter map of {@link Cookie cookies}
227 */
228 public synchronized SortedMap getCookiesMap() {
229 return cookiesMap;
230 }
231
232 /***
233 * Replace the standard sorted map with an external implemenations
234 * (such as one backed by persistent store, like BDB's StoredSortedMap.)
235 *
236 * @param map alternate sorted map to use to store cookies
237 */
238 public synchronized void setCookiesMap(SortedMap map) {
239 this.cookiesMap = map;
240 }
241
242
243 /***
244 * Returns an array of {@link Cookie cookies} in this HTTP
245 * state that match the given request parameters.
246 *
247 * @param domain the request domain
248 * @param port the request port
249 * @param path the request path
250 * @param secure <code>true</code> when using HTTPS
251 *
252 * @return an array of {@link Cookie cookies}.
253 *
254 * @see #getCookies()
255 *
256 * @deprecated use CookieSpec#match(String, int, String, boolean, Cookie)
257 */
258 public synchronized Cookie[] getCookies(
259 String domain,
260 int port,
261 String path,
262 boolean secure
263 ) {
264 LOG.trace("enter HttpState.getCookies(String, int, String, boolean)");
265
266 CookieSpec matcher = CookiePolicy.getDefaultSpec();
267
268
269
270
271
272
273
274
275
276
277 Cookie[] mapAnswer = matcher.match(domain,port,path,secure,cookiesMap);
278
279
280
281
282 return mapAnswer;
283
284 }
285
286 /***
287 * Removes all of {@link Cookie cookies} in this HTTP state
288 * that have expired according to the current system time.
289 *
290 * @see #purgeExpiredCookies(java.util.Date)
291 *
292 */
293 public synchronized boolean purgeExpiredCookies() {
294 LOG.trace("enter HttpState.purgeExpiredCookies()");
295 return purgeExpiredCookies(new Date());
296 }
297
298 /***
299 * Removes all of {@link Cookie cookies} in this HTTP state
300 * that have expired by the specified {@link java.util.Date date}.
301 *
302 * @param date The {@link java.util.Date date} to compare against.
303 *
304 * @return true if any cookies were purged.
305 *
306 * @see Cookie#isExpired(java.util.Date)
307 *
308 * @see #purgeExpiredCookies()
309 */
310 public synchronized boolean purgeExpiredCookies(Date date) {
311 LOG.trace("enter HttpState.purgeExpiredCookies(Date)");
312
313
314
315
316
317
318
319
320
321
322 boolean removed = false;
323 Iterator it = cookiesMap.values().iterator();
324 while (it.hasNext()) {
325 if (((Cookie) (it.next())).isExpired(date)) {
326 it.remove();
327 removed = true;
328 }
329 }
330 StoredIterator.close(it);
331
332
333 return removed;
334 }
335
336
337 /***
338 * Returns the current {@link CookiePolicy cookie policy} for this
339 * HTTP state.
340 *
341 * @return The {@link CookiePolicy cookie policy}.
342 *
343 * @deprecated Use
344 * {@link org.apache.commons.httpclient.params.HttpMethodParams#getCookiePolicy()},
345 * {@link HttpMethod#getParams()}.
346 */
347
348 public int getCookiePolicy() {
349 return this.cookiePolicy;
350 }
351
352
353 /***
354 * Defines whether preemptive authentication should be
355 * attempted.
356 *
357 * @param value <tt>true</tt> if preemptive authentication should be
358 * attempted, <tt>false</tt> otherwise.
359 *
360 * @deprecated Use
361 * {@link org.apache.commons.httpclient.params.HttpClientParams#setAuthenticationPreemptive(boolean)},
362 * {@link HttpClient#getParams()}.
363 */
364
365 public void setAuthenticationPreemptive(boolean value) {
366 this.preemptive = value;
367 }
368
369
370 /***
371 * Returns <tt>true</tt> if preemptive authentication should be
372 * attempted, <tt>false</tt> otherwise.
373 *
374 * @return boolean flag.
375 *
376 * @deprecated Use
377 * {@link org.apache.commons.httpclient.params.HttpClientParams#isAuthenticationPreemptive()},
378 * {@link HttpClient#getParams()}.
379 */
380
381 public boolean isAuthenticationPreemptive() {
382 return this.preemptive;
383 }
384
385
386 /***
387 * Sets the current {@link CookiePolicy cookie policy} for this HTTP
388 * state to one of the following supported policies:
389 * {@link CookiePolicy#COMPATIBILITY},
390 * {@link CookiePolicy#NETSCAPE_DRAFT} or
391 * {@link CookiePolicy#RFC2109}.
392 *
393 * @param policy new {@link CookiePolicy cookie policy}
394 *
395 * @deprecated
396 * Use {@link org.apache.commons.httpclient.params.HttpMethodParams#setCookiePolicy(String)},
397 * {@link HttpMethod#getParams()}.
398 */
399
400 public void setCookiePolicy(int policy) {
401 this.cookiePolicy = policy;
402 }
403
404 /***
405 * Sets the {@link Credentials credentials} for the given authentication
406 * realm on the given host. The <code>null</code> realm signifies default
407 * credentials for the given host, which should be used when no
408 * {@link Credentials credentials} have been explictly supplied for the
409 * challenging realm. The <code>null</code> host signifies default
410 * credentials, which should be used when no {@link Credentials credentials}
411 * have been explictly supplied for the challenging host. Any previous
412 * credentials for the given realm on the given host will be overwritten.
413 *
414 * @param realm the authentication realm
415 * @param host the host the realm belongs to
416 * @param credentials the authentication {@link Credentials credentials}
417 * for the given realm.
418 *
419 * @see #getCredentials(String, String)
420 * @see #setProxyCredentials(String, String, Credentials)
421 *
422 * @deprecated use #setCredentials(AuthScope, Credentials)
423 */
424
425 public synchronized void setCredentials(String realm, String host, Credentials credentials) {
426 LOG.trace("enter HttpState.setCredentials(String, String, Credentials)");
427 credMap.put(new AuthScope(host, AuthScope.ANY_PORT, realm, AuthScope.ANY_SCHEME), credentials);
428 }
429
430 /***
431 * Sets the {@link Credentials credentials} for the given authentication
432 * scope. Any previous credentials for the given scope will be overwritten.
433 *
434 * @param authscope the {@link AuthScope authentication scope}
435 * @param credentials the authentication {@link Credentials credentials}
436 * for the given scope.
437 *
438 * @see #getCredentials(AuthScope)
439 * @see #setProxyCredentials(AuthScope, Credentials)
440 *
441 * @since 3.0
442 */
443 public synchronized void setCredentials(final AuthScope authscope, final Credentials credentials) {
444 if (authscope == null) {
445 throw new IllegalArgumentException("Authentication scope may not be null");
446 }
447 LOG.trace("enter HttpState.setCredentials(AuthScope, Credentials)");
448 credMap.put(authscope, credentials);
449 }
450
451 /***
452 * Find matching {@link Credentials credentials} for the given authentication scope.
453 *
454 * @param map the credentials hash map
455 * @param token the {@link AuthScope authentication scope}
456 * @return the credentials
457 *
458 */
459 private static Credentials matchCredentials(final HashMap map, final AuthScope authscope) {
460
461 Credentials creds = (Credentials)map.get(authscope);
462 if (creds == null) {
463
464
465 int bestMatchFactor = -1;
466 AuthScope bestMatch = null;
467 Iterator items = map.keySet().iterator();
468 while (items.hasNext()) {
469 AuthScope current = (AuthScope)items.next();
470 int factor = authscope.match(current);
471 if (factor > bestMatchFactor) {
472 bestMatchFactor = factor;
473 bestMatch = current;
474 }
475 }
476 if (bestMatch != null) {
477 creds = (Credentials)map.get(bestMatch);
478 }
479 }
480 return creds;
481 }
482
483 /***
484 * Get the {@link Credentials credentials} for the given authentication scope on the
485 * given host.
486 *
487 * If the <i>realm</i> exists on <i>host</i>, return the coresponding credentials.
488 * If the <i>host</i> exists with a <tt>null</tt> <i>realm</i>, return the corresponding
489 * credentials.
490 * If the <i>realm</i> exists with a <tt>null</tt> <i>host</i>, return the
491 * corresponding credentials. If the <i>realm</i> does not exist, return
492 * the default Credentials. If there are no default credentials, return
493 * <code>null</code>.
494 *
495 * @param realm the authentication realm
496 * @param host the host the realm is on
497 * @return the credentials
498 *
499 * @see #setCredentials(String, String, Credentials)
500 *
501 * @deprecated use #getCredentials(AuthScope)
502 */
503
504 public synchronized Credentials getCredentials(String realm, String host) {
505 LOG.trace("enter HttpState.getCredentials(String, String");
506 return matchCredentials(this.credMap,
507 new AuthScope(host, AuthScope.ANY_PORT, realm, AuthScope.ANY_SCHEME));
508 }
509
510 /***
511 * Get the {@link Credentials credentials} for the given authentication scope.
512 *
513 * @param authscope the {@link AuthScope authentication scope}
514 * @return the credentials
515 *
516 * @see #setCredentials(AuthScope, Credentials)
517 *
518 * @since 3.0
519 */
520 public synchronized Credentials getCredentials(final AuthScope authscope) {
521 if (authscope == null) {
522 throw new IllegalArgumentException("Authentication scope may not be null");
523 }
524 LOG.trace("enter HttpState.getCredentials(AuthScope)");
525 return matchCredentials(this.credMap, authscope);
526 }
527
528 /***
529 * Sets the {@link Credentials credentials} for the given proxy authentication
530 * realm on the given proxy host. The <code>null</code> proxy realm signifies
531 * default credentials for the given proxy host, which should be used when no
532 * {@link Credentials credentials} have been explictly supplied for the
533 * challenging proxy realm. The <code>null</code> proxy host signifies default
534 * credentials, which should be used when no {@link Credentials credentials}
535 * have been explictly supplied for the challenging proxy host. Any previous
536 * credentials for the given proxy realm on the given proxy host will be
537 * overwritten.
538 *
539 * @param realm the authentication realm
540 * @param proxyHost the proxy host
541 * @param credentials the authentication credentials for the given realm
542 *
543 * @see #getProxyCredentials(AuthScope)
544 * @see #setCredentials(AuthScope, Credentials)
545 *
546 * @deprecated use #setProxyCredentials(AuthScope, Credentials)
547 */
548 public synchronized void setProxyCredentials(
549 String realm,
550 String proxyHost,
551 Credentials credentials
552 ) {
553 LOG.trace("enter HttpState.setProxyCredentials(String, String, Credentials");
554 proxyCred.put(new AuthScope(proxyHost, AuthScope.ANY_PORT, realm, AuthScope.ANY_SCHEME), credentials);
555 }
556
557 /***
558 * Sets the {@link Credentials proxy credentials} for the given authentication
559 * realm. Any previous credentials for the given realm will be overwritten.
560 *
561 * @param authscope the {@link AuthScope authentication scope}
562 * @param credentials the authentication {@link Credentials credentials}
563 * for the given realm.
564 *
565 * @see #getProxyCredentials(AuthScope)
566 * @see #setCredentials(AuthScope, Credentials)
567 *
568 * @since 3.0
569 */
570 public synchronized void setProxyCredentials(final AuthScope authscope,
571 final Credentials credentials)
572 {
573 if (authscope == null) {
574 throw new IllegalArgumentException("Authentication scope may not be null");
575 }
576 LOG.trace("enter HttpState.setProxyCredentials(AuthScope, Credentials)");
577 proxyCred.put(authscope, credentials);
578 }
579
580 /***
581 * Get the {@link Credentials credentials} for the proxy host with the given
582 * authentication scope.
583 *
584 * If the <i>realm</i> exists on <i>host</i>, return the coresponding credentials.
585 * If the <i>host</i> exists with a <tt>null</tt> <i>realm</i>, return the corresponding
586 * credentials.
587 * If the <i>realm</i> exists with a <tt>null</tt> <i>host</i>, return the
588 * corresponding credentials. If the <i>realm</i> does not exist, return
589 * the default Credentials. If there are no default credentials, return
590 * <code>null</code>.
591 *
592 * @param realm the authentication realm
593 * @param proxyHost the proxy host the realm is on
594 * @return the credentials
595 * @see #setProxyCredentials(String, String, Credentials)
596 *
597 * @deprecated use #getProxyCredentials(AuthScope)
598 */
599 public synchronized Credentials getProxyCredentials(String realm, String proxyHost) {
600 LOG.trace("enter HttpState.getCredentials(String, String");
601 return matchCredentials(this.proxyCred,
602 new AuthScope(proxyHost, AuthScope.ANY_PORT, realm, AuthScope.ANY_SCHEME));
603 }
604
605 /***
606 * Get the {@link Credentials proxy credentials} for the given authentication scope.
607 *
608 * @param authscope the {@link AuthScope authentication scope}
609 * @return the credentials
610 *
611 * @see #setProxyCredentials(AuthScope, Credentials)
612 *
613 * @since 3.0
614 */
615 public synchronized Credentials getProxyCredentials(final AuthScope authscope) {
616 if (authscope == null) {
617 throw new IllegalArgumentException("Authentication scope may not be null");
618 }
619 LOG.trace("enter HttpState.getProxyCredentials(AuthScope)");
620 return matchCredentials(this.proxyCred, authscope);
621 }
622
623 /***
624 * Returns a string representation of this HTTP state.
625 *
626 * @return The string representation of the HTTP state.
627 *
628 * @see java.lang.Object#toString()
629 */
630 public synchronized String toString() {
631 StringBuffer sbResult = new StringBuffer();
632
633 sbResult.append("[");
634 sbResult.append(getCredentialsStringRepresentation(proxyCred));
635 sbResult.append(" | ");
636 sbResult.append(getCredentialsStringRepresentation(credMap));
637 sbResult.append(" | ");
638 sbResult.append(getCookiesStringRepresentation(cookiesMap.values()));
639 sbResult.append("]");
640
641 String strResult = sbResult.toString();
642
643 return strResult;
644 }
645
646 /***
647 * Returns a string representation of the credentials.
648 * @param credMap The credentials.
649 * @return The string representation.
650 */
651 private static String getCredentialsStringRepresentation(final Map credMap) {
652 StringBuffer sbResult = new StringBuffer();
653 Iterator iter = credMap.keySet().iterator();
654 while (iter.hasNext()) {
655 Object key = iter.next();
656 Credentials cred = (Credentials) credMap.get(key);
657 if (sbResult.length() > 0) {
658 sbResult.append(", ");
659 }
660 sbResult.append(key);
661 sbResult.append("#");
662 sbResult.append(cred.toString());
663 }
664 return sbResult.toString();
665 }
666
667 /***
668 * Returns a string representation of the cookies.
669 * @param cookies The cookies
670 * @return The string representation.
671 */
672 private static String getCookiesStringRepresentation(final Collection cookies) {
673 StringBuffer sbResult = new StringBuffer();
674 Iterator iter = cookies.iterator();
675 while (iter.hasNext()) {
676 Cookie ck = (Cookie) iter.next();
677 if (sbResult.length() > 0) {
678 sbResult.append("#");
679 }
680 sbResult.append(ck.toExternalForm());
681 }
682 return sbResult.toString();
683 }
684
685 /***
686 * Clears all credentials.
687 */
688 public void clearCredentials() {
689 this.credMap.clear();
690 }
691
692 /***
693 * Clears all proxy credentials.
694 */
695 public void clearProxyCredentials() {
696 this.proxyCred.clear();
697 }
698
699 /***
700 * Clears all cookies.
701 */
702 public void clearCookies() {
703
704
705 this.cookiesMap.clear();
706
707 }
708
709 /***
710 * Clears the state information (all cookies, credentials and proxy credentials).
711 */
712 public void clear() {
713 clearCookies();
714 clearCredentials();
715 clearProxyCredentials();
716 }
717 }