View Javadoc

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