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