View Javadoc

1   /*
2    * $Header: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpState.java,v 1.33 2004/05/13 04:03:25 mbecke Exp $
3    * $Revision: 1.33 $
4    * $Date: 2004/05/13 04:03:25 $
5    *
6    * ====================================================================
7    *
8    *  Copyright 1999-2004 The Apache Software Foundation
9    *
10   *  Licensed under the Apache License, Version 2.0 (the "License");
11   *  you may not use this file except in compliance with the License.
12   *  You may obtain a copy of the License at
13   *
14   *      http://www.apache.org/licenses/LICENSE-2.0
15   *
16   *  Unless required by applicable law or agreed to in writing, software
17   *  distributed under the License is distributed on an "AS IS" BASIS,
18   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19   *  See the License for the specific language governing permissions and
20   *  limitations under the License.
21   * ====================================================================
22   *
23   * This software consists of voluntary contributions made by many
24   * individuals on behalf of the Apache Software Foundation.  For more
25   * information on the Apache Software Foundation, please see
26   * <http://www.apache.org/>.
27   *
28   */
29  
30  package org.apache.commons.httpclient;
31  
32  import java.util.ArrayList;
33  import java.util.Date;
34  import java.util.HashMap;
35  import java.util.Map;
36  import java.util.List;
37  import java.util.Iterator;
38  import org.apache.commons.httpclient.cookie.CookieSpec;
39  import org.apache.commons.httpclient.cookie.CookiePolicy;
40  import org.apache.commons.httpclient.auth.HttpAuthRealm; 
41  import org.apache.commons.logging.Log;
42  import org.apache.commons.logging.LogFactory;
43  
44  /***
45   * <p>
46   * A container for HTTP attributes that may persist from request
47   * to request, such as {@link Cookie cookies} and authentication
48   * {@link Credentials credentials}.
49   * </p>
50   * <p>
51   * Preemptive authentication can be turned on by using the property value of
52   * #PREEMPTIVE_PROPERTY.  If left unspecified, it has the default value of
53   * #PREEMPTIVE_DEFAULT.  This configurable behaviour conforms to rcf2617:
54   * </p>
55   * 
56   * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
57   * @author Rodney Waldhoff
58   * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
59   * @author Sean C. Sullivan
60   * @author <a href="mailto:becke@u.washington.edu">Michael Becke</a>
61   * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
62   * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
63   * @author <a href="mailto:adrian@intencha.com">Adrian Sutton</a>
64   * 
65   * @version $Revision: 1.33 $ $Date: 2004/05/13 04:03:25 $
66   * 
67   */
68  public class HttpState {
69  
70      // ----------------------------------------------------- Instance Variables
71  
72      /***
73       * Map of {@link Credentials credentials} by realm that this 
74       * HTTP state contains.
75       */
76      private HashMap credMap = new HashMap();
77  
78      /***
79       * Map of {@link Credentials proxy credentials} by realm that this
80       * HTTP state contains
81       */
82      private HashMap proxyCred = new HashMap();
83  
84      /***
85       * Array of {@link Cookie cookies} that this HTTP state contains.
86       */
87      private ArrayList cookies = new ArrayList();
88  
89      private boolean preemptive = false;
90  
91      private int cookiePolicy = 0;
92          // -------------------------------------------------------- Class Variables
93  
94      /*** Log object for this class. */
95      private static final Log LOG = LogFactory.getLog(HttpState.class);
96  
97      /***
98       * Default constructor.
99       */
100     public HttpState() {
101         super();
102     }
103 
104     // ------------------------------------------------------------- Properties
105 
106     /***
107      * Adds an {@link Cookie HTTP cookie}, replacing any existing equivalent cookies.
108      * If the given cookie has already expired it will not be added, but existing 
109      * values will still be removed.
110      * 
111      * @param cookie the {@link Cookie cookie} to be added
112      * 
113      * @see #addCookies(Cookie[])
114      * 
115      */
116     public synchronized void addCookie(Cookie cookie) {
117         LOG.trace("enter HttpState.addCookie(Cookie)");
118 
119         if (cookie != null) {
120             // first remove any old cookie that is equivalent
121             for (Iterator it = cookies.iterator(); it.hasNext();) {
122                 Cookie tmp = (Cookie) it.next();
123                 if (cookie.equals(tmp)) {
124                     it.remove();
125                     break;
126                 }
127             }
128             if (!cookie.isExpired()) {
129                 cookies.add(cookie);
130             }
131         }
132     }
133 
134     /***
135      * Adds an array of {@link Cookie HTTP cookies}. Cookies are added individually and 
136      * in the given array order. If any of the given cookies has already expired it will 
137      * not be added, but existing values will still be removed.
138      * 
139      * @param cookies the {@link Cookie cookies} to be added
140      * 
141      * @see #addCookie(Cookie)
142      * 
143      * 
144      */
145     public synchronized void addCookies(Cookie[] cookies) {
146         LOG.trace("enter HttpState.addCookies(Cookie[])");
147 
148         if (cookies != null) {
149             for (int i = 0; i < cookies.length; i++) {
150                 this.addCookie(cookies[i]);
151             }
152         }
153     }
154 
155     /***
156      * Returns an array of {@link Cookie cookies} that this HTTP
157      * state currently contains.
158      * 
159      * @return an array of {@link Cookie cookies}.
160      * 
161      * @see #getCookies(String, int, String, boolean)
162      * 
163      */
164     public synchronized Cookie[] getCookies() {
165         LOG.trace("enter HttpState.getCookies()");
166         return (Cookie[]) (cookies.toArray(new Cookie[cookies.size()]));
167     }
168 
169     /***
170      * Returns an array of {@link Cookie cookies} in this HTTP 
171      * state that match the given request parameters.
172      * 
173      * @param domain the request domain
174      * @param port the request port
175      * @param path the request path
176      * @param secure <code>true</code> when using HTTPS
177      * 
178      * @return an array of {@link Cookie cookies}.
179      * 
180      * @see #getCookies()
181      * 
182      * @deprecated use CookieSpec#match(String, int, String, boolean, Cookie)
183      */
184     public synchronized Cookie[] getCookies(
185         String domain, 
186         int port, 
187         String path, 
188         boolean secure
189     ) {
190         LOG.trace("enter HttpState.getCookies(String, int, String, boolean)");
191 
192         CookieSpec matcher = CookiePolicy.getDefaultSpec();
193         ArrayList list = new ArrayList(cookies.size());
194         for (int i = 0, m = cookies.size(); i < m; i++) {
195             Cookie cookie = (Cookie) (cookies.get(i));
196             if (matcher.match(domain, port, path, secure, cookie)) {
197                 list.add(cookie);
198             }
199         }
200         return (Cookie[]) (list.toArray(new Cookie[list.size()]));
201     }
202 
203     /***
204      * Removes all of {@link Cookie cookies} in this HTTP state
205      * that have expired according to the current system time.
206      * 
207      * @see #purgeExpiredCookies(java.util.Date)
208      * 
209      */
210     public synchronized boolean purgeExpiredCookies() {
211         LOG.trace("enter HttpState.purgeExpiredCookies()");
212         return purgeExpiredCookies(new Date());
213     }
214 
215     /***
216      * Removes all of {@link Cookie cookies} in this HTTP state
217      * that have expired by the specified {@link java.util.Date date}. 
218      * 
219      * @param date The {@link java.util.Date date} to compare against.
220      * 
221      * @return true if any cookies were purged.
222      * 
223      * @see Cookie#isExpired(java.util.Date)
224      * 
225      * @see #purgeExpiredCookies()
226      */
227     public synchronized boolean purgeExpiredCookies(Date date) {
228         LOG.trace("enter HttpState.purgeExpiredCookies(Date)");
229         boolean removed = false;
230         Iterator it = cookies.iterator();
231         while (it.hasNext()) {
232             if (((Cookie) (it.next())).isExpired(date)) {
233                 it.remove();
234                 removed = true;
235             }
236         }
237         return removed;
238     }
239 
240 
241     /***
242      * Returns the current {@link CookiePolicy cookie policy} for this
243      * HTTP state.
244      * 
245      * @return The {@link CookiePolicy cookie policy}.
246      * 
247      * @deprecated Use 
248      *  {@link org.apache.commons.httpclient.params.HttpMethodParams#getCookiePolicy()},
249      *  {@link HttpMethod#getParams()}.     
250      */
251     
252     public int getCookiePolicy() {
253         return CookiePolicy.getDefaultPolicy();
254     }
255     
256 
257     /***
258      * Defines whether preemptive authentication should be 
259      * attempted.
260      * 
261      * @param value <tt>true</tt> if preemptive authentication should be 
262      * attempted, <tt>false</tt> otherwise. 
263      * 
264      * @deprecated Use 
265      * {@link org.apache.commons.httpclient.params.HttpClientParams#setAuthenticationPreemptive(boolean)}, 
266      * {@link HttpClient#getParams()}.
267      */
268     
269     public void setAuthenticationPreemptive(boolean value) {
270         this.preemptive = value;
271     }
272 
273 
274     /***
275      * Returns <tt>true</tt> if preemptive authentication should be 
276      * attempted, <tt>false</tt> otherwise.
277      * 
278      * @return boolean flag.
279      * 
280      * @deprecated Use 
281      * {@link org.apache.commons.httpclient.params.HttpClientParams#isAuthenticationPreemptive()}, 
282      * {@link HttpClient#getParams()}.
283      */
284     
285     public boolean isAuthenticationPreemptive() {
286         return this.preemptive;
287     }
288     
289 
290     /***
291      * Sets the current {@link CookiePolicy cookie policy} for this HTTP
292      * state to one of the following supported policies: 
293      * {@link CookiePolicy#COMPATIBILITY}, 
294      * {@link CookiePolicy#NETSCAPE_DRAFT} or
295      * {@link CookiePolicy#RFC2109}.
296      * 
297      * @param policy new {@link CookiePolicy cookie policy}
298      * 
299      * @deprecated 
300      *  Use {@link org.apache.commons.httpclient.params.HttpMethodParams#setCookiePolicy(String)},
301      *  {@link HttpMethod#getParams()}.     
302      */
303     
304     public void setCookiePolicy(int policy) {
305         this.cookiePolicy = policy;
306     }
307 
308     /*** 
309      * Sets the {@link Credentials credentials} for the given authentication 
310      * realm on the given host. The <code>null</code> realm signifies default 
311      * credentials for the given host, which should be used when no 
312      * {@link Credentials credentials} have been explictly supplied for the 
313      * challenging realm. The <code>null</code> host signifies default 
314      * credentials, which should be used when no {@link Credentials credentials} 
315      * have been explictly supplied for the challenging host. Any previous 
316      * credentials for the given realm on the given host will be overwritten.
317      * 
318      * @param realm the authentication realm
319      * @param host the host the realm belongs to
320      * @param credentials the authentication {@link Credentials credentials} 
321      * for the given realm.
322      * 
323      * @see #getCredentials(String, String)
324      * @see #setProxyCredentials(String, String, Credentials) 
325      */
326     
327     public synchronized void setCredentials(String realm, String host, Credentials credentials) {
328         LOG.trace("enter HttpState.setCredentials(String, String, Credentials)");
329         credMap.put(new HttpAuthRealm(host, realm), credentials);
330     }
331 
332     /*** 
333      * Sets the {@link Credentials credentials} for the given authentication 
334      * realm. Any previous credentials for the given realm will be overwritten.
335      * 
336      * @param realm the {@link HttpAuthRealm authentication realm}
337      * @param credentials the authentication {@link Credentials credentials} 
338      * for the given realm.
339      * 
340      * @see #getCredentials(HttpAuthRealm)
341      * @see #setProxyCredentials(HttpAuthRealm, Credentials) 
342      * 
343      * @since 3.0
344      */
345     public synchronized void setCredentials(final HttpAuthRealm realm, Credentials credentials) {
346         if (realm == null) {
347             throw new IllegalArgumentException("Authentication realm token may not be null");
348         }
349         LOG.trace("enter HttpState.setCredentials(HttpAuthRealm, Credentials)");
350         credMap.put(realm, credentials);
351     }
352 
353     /***
354      * Find matching {@link Credentials credentials} for the given authentication realm.
355      *
356      * @param map the credentials hash map
357      * @param token the {@link HttpAuthRealm authentication realm token}
358      * @return the credentials 
359      * 
360      */
361     private static Credentials matchCredentials(HashMap map, HttpAuthRealm token) {
362         HttpAuthRealm key = token;
363         Credentials creds = (Credentials) map.get(key);
364         if (creds == null && token.getScheme() != null) {
365             key = new HttpAuthRealm(token.getHost(), token.getPort(), token.getRealm());
366             creds = (Credentials) map.get(key);
367         }
368         if (creds == null && token.getRealm() != null) {
369             key = new HttpAuthRealm(token.getHost(), token.getPort());
370             creds = (Credentials) map.get(key);
371         }
372         if (creds == null && token.getPort() >= 0) {
373             key = new HttpAuthRealm(token.getHost(), -1);
374             creds = (Credentials) map.get(key);
375         }
376         if (creds == null && token.getHost() != null) {
377             key = new HttpAuthRealm();
378             creds = (Credentials) map.get(key);
379         }
380         return creds;
381     }
382     
383     /***
384      * Get the {@link Credentials credentials} for the given authentication realm on the 
385      * given host.
386      *
387      * If the <i>realm</i> exists on <i>host</i>, return the coresponding credentials.
388      * If the <i>host</i> exists with a <tt>null</tt> <i>realm</i>, return the corresponding
389      * credentials.
390      * If the <i>realm</i> exists with a <tt>null</tt> <i>host</i>, return the
391      * corresponding credentials.  If the <i>realm</i> does not exist, return
392      * the default Credentials.  If there are no default credentials, return
393      * <code>null</code>.
394      *
395      * @param realm the authentication realm
396      * @param host the host the realm is on
397      * @return the credentials 
398      * 
399      * @see #setCredentials(String, String, Credentials)
400      */
401     
402     public synchronized Credentials getCredentials(String realm, String host) {
403         LOG.trace("enter HttpState.getCredentials(String, String");
404         return matchCredentials(this.credMap, new HttpAuthRealm(host, realm));
405     }
406 
407     /***
408      * Get the {@link Credentials credentials} for the given authentication realm.
409      *
410      * @param realm the {@link HttpAuthRealm authentication realm}
411      * @return the credentials 
412      * 
413      * @see #setCredentials(HttpAuthRealm, Credentials)
414      * 
415      * @since 3.0
416      */
417     public synchronized Credentials getCredentials(HttpAuthRealm realm) {
418         if (realm == null) {
419             throw new IllegalArgumentException("Authentication realm token may not be null");
420         }
421         LOG.trace("enter HttpState.getCredentials(HttpAuthRealm)");
422         return matchCredentials(this.credMap, realm);
423     }
424 
425     /***
426      * Sets the {@link Credentials credentials} for the given proxy authentication 
427      * realm on the given proxy host. The <code>null</code> proxy realm signifies 
428      * default credentials for the given proxy host, which should be used when no 
429      * {@link Credentials credentials} have been explictly supplied for the 
430      * challenging proxy realm. The <code>null</code> proxy host signifies default 
431      * credentials, which should be used when no {@link Credentials credentials} 
432      * have been explictly supplied for the challenging proxy host. Any previous 
433      * credentials for the given proxy realm on the given proxy host will be 
434      * overwritten.
435      *
436      * @param realm the authentication realm
437      * @param proxyHost the proxy host
438      * @param credentials the authentication credentials for the given realm
439      * 
440      * @see #getProxyCredentials(String,String)
441      * @see #setCredentials(String, String, Credentials)
442      */
443     public synchronized void setProxyCredentials(
444         String realm, 
445         String proxyHost, 
446         Credentials credentials
447     ) {
448         LOG.trace("enter HttpState.setProxyCredentials(String, String, Credentials");
449         proxyCred.put(new HttpAuthRealm(proxyHost, realm), credentials);
450     }
451 
452     /*** 
453      * Sets the {@link Credentials credentials} for the given proxy authentication 
454      * realm. Any previous credentials for the given realm will be overwritten.
455      * 
456      * @param realm the {@link HttpAuthRealm authentication realm}
457      * @param credentials the authentication {@link Credentials credentials} 
458      * for the given realm.
459      * 
460      * @see #getProxyCredentials(HttpAuthRealm)
461      * @see #setCredentials(HttpAuthRealm, Credentials) 
462      * 
463      * @since 3.0
464      */
465     public synchronized void setProxyCredentials(final HttpAuthRealm realm, 
466         Credentials credentials)
467     {
468         if (realm == null) {
469             throw new IllegalArgumentException("Authentication realm token may not be null");
470         }
471         LOG.trace("enter HttpState.setProxyCredentials(HttpAuthRealm, Credentials)");
472         proxyCred.put(realm, credentials);
473     }
474 
475     /***
476      * Get the {@link Credentials credentials} for the proxy host with the given 
477      * authentication realm.
478      *
479      * If the <i>realm</i> exists on <i>host</i>, return the coresponding credentials.
480      * If the <i>host</i> exists with a <tt>null</tt> <i>realm</i>, return the corresponding
481      * credentials.
482      * If the <i>realm</i> exists with a <tt>null</tt> <i>host</i>, return the
483      * corresponding credentials.  If the <i>realm</i> does not exist, return
484      * the default Credentials.  If there are no default credentials, return
485      * <code>null</code>.
486      * 
487      * @param realm the authentication realm
488      * @param proxyHost the proxy host the realm is on
489      * @return the credentials 
490      * @see #setProxyCredentials(String, String, Credentials)
491      */
492     public synchronized Credentials getProxyCredentials(String realm, String proxyHost) {
493        LOG.trace("enter HttpState.getCredentials(String, String");
494         return matchCredentials(this.proxyCred, new HttpAuthRealm(proxyHost, realm));
495     }
496     
497     /***
498      * Get the {@link Credentials credentials} for the given proxy authentication realm.
499      *
500      * @param realm the {@link HttpAuthRealm authentication realm}
501      * @return the credentials 
502      * 
503      * @see #setProxyCredentials(HttpAuthRealm, Credentials)
504      * 
505      * @since 3.0
506      */
507     public synchronized Credentials getProxyCredentials(HttpAuthRealm realm) {
508         if (realm == null) {
509             throw new IllegalArgumentException("Authentication realm token may not be null");
510         }
511         LOG.trace("enter HttpState.getProxyCredentials(HttpAuthRealm)");
512         return matchCredentials(this.proxyCred, realm);
513     }
514 
515     /***
516      * Returns a string representation of this HTTP state.
517      * 
518      * @return The string representation of the HTTP state.
519      * 
520      * @see java.lang.Object#toString()
521      */
522     public synchronized String toString() {
523         StringBuffer sbResult = new StringBuffer();
524 
525         sbResult.append("[");
526         sbResult.append(getCredentialsStringRepresentation(proxyCred));
527         sbResult.append(" | ");
528         sbResult.append(getCredentialsStringRepresentation(credMap));
529         sbResult.append(" | ");
530         sbResult.append(getCookiesStringRepresentation(cookies));
531         sbResult.append("]");
532 
533         String strResult = sbResult.toString();
534 
535         return strResult;
536     }
537     
538     /***
539      * Returns a string representation of the credentials.
540      * @param credMap The credentials.
541      * @return The string representation.
542      */
543     private static String getCredentialsStringRepresentation(final Map credMap) {
544         StringBuffer sbResult = new StringBuffer();
545         Iterator iter = credMap.keySet().iterator();
546         while (iter.hasNext()) {
547             Object key = iter.next();
548             Credentials cred = (Credentials) credMap.get(key);
549             if (sbResult.length() > 0) {
550                 sbResult.append(", ");
551             }
552             sbResult.append(key);
553             sbResult.append("#");
554             sbResult.append(cred.toString());
555         }
556         return sbResult.toString();
557     }
558     
559     /***
560      * Returns a string representation of the cookies.
561      * @param cookies The cookies
562      * @return The string representation.
563      */
564     private static String getCookiesStringRepresentation(final List cookies) {
565         StringBuffer sbResult = new StringBuffer();
566         Iterator iter = cookies.iterator();
567         while (iter.hasNext()) {
568             Cookie ck = (Cookie) iter.next();
569             if (sbResult.length() > 0) {
570                 sbResult.append("#");
571             }
572             sbResult.append(ck.toExternalForm());
573         }
574         return sbResult.toString();
575     }
576 }