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