1. /*
  2. * $Header: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpURL.java,v 1.17 2004/05/02 15:19:15 mbecke Exp $
  3. * $Revision: 1.17 $
  4. * $Date: 2004/05/02 15:19:15 $
  5. *
  6. * ====================================================================
  7. *
  8. * Copyright 2002-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 org.apache.commons.httpclient.util.URIUtil;
  31. /**
  32. * The HTTP URL.
  33. *
  34. * @author <a href="mailto:jericho at apache.org">Sung-Gu</a>
  35. * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
  36. */
  37. public class HttpURL extends URI {
  38. // ----------------------------------------------------------- Constructors
  39. /** Create an instance as an internal use. */
  40. protected HttpURL() {
  41. }
  42. /**
  43. * Construct a HTTP URL as an escaped form of a character array with the
  44. * given charset to do escape encoding.
  45. *
  46. * @param escaped the HTTP URL character sequence
  47. * @param charset the charset string to do escape encoding
  48. * @throws URIException If {@link #checkValid()} fails
  49. * @throws NullPointerException if <code>escaped</code> is <code>null</code>
  50. * @see #getProtocolCharset
  51. */
  52. public HttpURL(char[] escaped, String charset)
  53. throws URIException, NullPointerException {
  54. protocolCharset = charset;
  55. parseUriReference(new String(escaped), true);
  56. checkValid();
  57. }
  58. /**
  59. * Construct a HTTP URL as an escaped form of a character array.
  60. *
  61. * @param escaped the HTTP URL character sequence
  62. * @throws URIException If {@link #checkValid()} fails
  63. * @throws NullPointerException if <code>escaped</code> is <code>null</code>
  64. * @see #getDefaultProtocolCharset
  65. */
  66. public HttpURL(char[] escaped) throws URIException, NullPointerException {
  67. parseUriReference(new String(escaped), true);
  68. checkValid();
  69. }
  70. /**
  71. * Construct a HTTP URL from a given string with the given charset to do
  72. * escape encoding.
  73. *
  74. * @param original the HTTP URL string
  75. * @param charset the charset string to do escape encoding
  76. * @throws URIException If {@link #checkValid()} fails
  77. * @see #getProtocolCharset
  78. */
  79. public HttpURL(String original, String charset) throws URIException {
  80. protocolCharset = charset;
  81. parseUriReference(original, false);
  82. checkValid();
  83. }
  84. /**
  85. * Construct a HTTP URL from a given string.
  86. *
  87. * @param original the HTTP URL string
  88. * @throws URIException If {@link #checkValid()} fails
  89. * @see #getDefaultProtocolCharset
  90. */
  91. public HttpURL(String original) throws URIException {
  92. parseUriReference(original, false);
  93. checkValid();
  94. }
  95. /**
  96. * Construct a HTTP URL from given components.
  97. *
  98. * @param host the host string
  99. * @param port the port number
  100. * @param path the path string
  101. * @throws URIException If {@link #checkValid()} fails
  102. * @see #getDefaultProtocolCharset
  103. */
  104. public HttpURL(String host, int port, String path) throws URIException {
  105. this(null, host, port, path, null, null);
  106. checkValid();
  107. }
  108. /**
  109. * Construct a HTTP URL from given components.
  110. *
  111. * @param host the host string
  112. * @param port the port number
  113. * @param path the path string
  114. * @param query the query string
  115. * @throws URIException If {@link #checkValid()} fails
  116. * @see #getDefaultProtocolCharset
  117. */
  118. public HttpURL(String host, int port, String path, String query)
  119. throws URIException {
  120. this(null, host, port, path, query, null);
  121. checkValid();
  122. }
  123. /**
  124. * Construct a HTTP URL from given components.
  125. *
  126. * @param user the user name
  127. * @param password his or her password
  128. * @param host the host string
  129. * @throws URIException If {@link #checkValid()} fails
  130. * @see #getDefaultProtocolCharset
  131. */
  132. public HttpURL(String user, String password, String host)
  133. throws URIException {
  134. this((user == null) ? null : user
  135. + ((password == null) ? "" : ':' + password),
  136. host, -1, null, null, null);
  137. checkValid();
  138. }
  139. /**
  140. * Construct a HTTP URL from given components.
  141. *
  142. * @param user the user name
  143. * @param password his or her password
  144. * @param host the host string
  145. * @param port the port number
  146. * @throws URIException If {@link #checkValid()} fails
  147. * @see #getDefaultProtocolCharset
  148. */
  149. public HttpURL(String user, String password, String host, int port)
  150. throws URIException {
  151. this((user == null) ? null : user
  152. + ((password == null) ? "" : ':' + password),
  153. host, port, null, null, null);
  154. checkValid();
  155. }
  156. /**
  157. * Construct a HTTP URL from given components.
  158. *
  159. * @param user the user name
  160. * @param password his or her password
  161. * @param host the host string
  162. * @param port the port number
  163. * @param path the path string
  164. * @throws URIException If {@link #checkValid()} fails
  165. * @see #getDefaultProtocolCharset
  166. */
  167. public HttpURL(String user, String password, String host, int port,
  168. String path) throws URIException {
  169. this((user == null) ? null : user
  170. + ((password == null) ? "" : ':' + password),
  171. host, port, path, null, null);
  172. checkValid();
  173. }
  174. /**
  175. * Construct a HTTP URL from given components.
  176. *
  177. * @param user the user name
  178. * @param password his or her password
  179. * @param host the host string
  180. * @param port the port number
  181. * @param path the path string
  182. * @param query The query string.
  183. * @throws URIException If {@link #checkValid()} fails
  184. * @see #getDefaultProtocolCharset
  185. */
  186. public HttpURL(String user, String password, String host, int port,
  187. String path, String query) throws URIException {
  188. this((user == null) ? null : user
  189. + ((password == null) ? "" : ':' + password),
  190. host, port, path, query, null);
  191. checkValid();
  192. }
  193. /**
  194. * Construct a HTTP URL from given components.
  195. *
  196. * @param host the host string
  197. * @param path the path string
  198. * @param query the query string
  199. * @param fragment the fragment string
  200. * @throws URIException If {@link #checkValid()} fails
  201. * @see #getDefaultProtocolCharset
  202. */
  203. public HttpURL(String host, String path, String query, String fragment)
  204. throws URIException {
  205. this(null, host, -1, path, query, fragment);
  206. checkValid();
  207. }
  208. /**
  209. * Construct a HTTP URL from given components.
  210. *
  211. * @param userinfo the userinfo string
  212. * @param host the host string
  213. * @param path the path string
  214. * @param query the query string
  215. * @param fragment the fragment string
  216. * @throws URIException If {@link #checkValid()} fails
  217. * @see #getDefaultProtocolCharset
  218. */
  219. public HttpURL(String userinfo, String host, String path, String query,
  220. String fragment) throws URIException {
  221. this(userinfo, host, -1, path, query, fragment);
  222. checkValid();
  223. }
  224. /**
  225. * Construct a HTTP URL from given components.
  226. *
  227. * @param userinfo the userinfo string
  228. * @param host the host string
  229. * @param port the port number
  230. * @param path the path string
  231. * @throws URIException If {@link #checkValid()} fails
  232. * @see #getDefaultProtocolCharset
  233. */
  234. public HttpURL(String userinfo, String host, int port, String path)
  235. throws URIException {
  236. this(userinfo, host, port, path, null, null);
  237. checkValid();
  238. }
  239. /**
  240. * Construct a HTTP URL from given components.
  241. *
  242. * @param userinfo the userinfo string
  243. * @param host the host string
  244. * @param port the port number
  245. * @param path the path string
  246. * @param query the query string
  247. * @throws URIException If {@link #checkValid()} fails
  248. * @see #getDefaultProtocolCharset
  249. */
  250. public HttpURL(String userinfo, String host, int port, String path,
  251. String query) throws URIException {
  252. this(userinfo, host, port, path, query, null);
  253. checkValid();
  254. }
  255. /**
  256. * Construct a HTTP URL from given components.
  257. *
  258. * @param userinfo the userinfo string
  259. * @param host the host string
  260. * @param port the port number
  261. * @param path the path string
  262. * @param query the query string
  263. * @param fragment the fragment string
  264. * @throws URIException If {@link #checkValid()} fails
  265. * @see #getDefaultProtocolCharset
  266. */
  267. public HttpURL(String userinfo, String host, int port, String path,
  268. String query, String fragment) throws URIException {
  269. // validate and contruct the URI character sequence
  270. StringBuffer buff = new StringBuffer();
  271. if (userinfo != null || host != null || port != -1) {
  272. _scheme = DEFAULT_SCHEME; // in order to verify the own protocol
  273. buff.append(_default_scheme);
  274. buff.append("://");
  275. if (userinfo != null) {
  276. buff.append(URIUtil.encode(userinfo, URI.allowed_userinfo));
  277. buff.append('@');
  278. }
  279. if (host != null) {
  280. buff.append(URIUtil.encode(host, URI.allowed_host));
  281. if (port != -1 || port != DEFAULT_PORT) {
  282. buff.append(':');
  283. buff.append(port);
  284. }
  285. }
  286. }
  287. if (path != null) { // accept empty path
  288. if (scheme != null && !path.startsWith("/")) {
  289. throw new URIException(URIException.PARSING,
  290. "abs_path requested");
  291. }
  292. buff.append(URIUtil.encode(path, URI.allowed_abs_path));
  293. }
  294. if (query != null) {
  295. buff.append('?');
  296. buff.append(URIUtil.encode(query, URI.allowed_query));
  297. }
  298. if (fragment != null) {
  299. buff.append('#');
  300. buff.append(URIUtil.encode(fragment, URI.allowed_fragment));
  301. }
  302. parseUriReference(buff.toString(), true);
  303. checkValid();
  304. }
  305. /**
  306. * Construct a HTTP URL with a given relative URL string.
  307. *
  308. * @param base the base HttpURL
  309. * @param relative the relative HTTP URL string
  310. * @throws URIException If {@link #checkValid()} fails
  311. */
  312. public HttpURL(HttpURL base, String relative) throws URIException {
  313. this(base, new HttpURL(relative));
  314. }
  315. /**
  316. * Construct a HTTP URL with a given relative URL.
  317. *
  318. * @param base the base HttpURL
  319. * @param relative the relative HttpURL
  320. * @throws URIException If {@link #checkValid()} fails
  321. */
  322. public HttpURL(HttpURL base, HttpURL relative) throws URIException {
  323. super(base, relative);
  324. checkValid();
  325. }
  326. // -------------------------------------------------------------- Constants
  327. /**
  328. * Default scheme for HTTP URL.
  329. */
  330. public static final char[] DEFAULT_SCHEME = { 'h', 't', 't', 'p' };
  331. /**
  332. * Default scheme for HTTP URL.
  333. * @deprecated Use {@link #DEFAULT_SCHEME} instead. This one doesn't
  334. * conform to the project naming conventions.
  335. */
  336. public static final char[] _default_scheme = DEFAULT_SCHEME;
  337. /**
  338. * Default port for HTTP URL.
  339. */
  340. public static final int DEFAULT_PORT = 80;
  341. /**
  342. * Default port for HTTP URL.
  343. * @deprecated Use {@link #DEFAULT_PORT} instead. This one doesn't conform
  344. * to the project naming conventions.
  345. */
  346. public static final int _default_port = DEFAULT_PORT;
  347. /**
  348. * The serialVersionUID.
  349. */
  350. static final long serialVersionUID = -7158031098595039459L;
  351. // ------------------------------------------------------------- The scheme
  352. /**
  353. * Get the scheme. You can get the scheme explicitly.
  354. *
  355. * @return the scheme
  356. */
  357. public char[] getRawScheme() {
  358. return (_scheme == null) ? null : HttpURL.DEFAULT_SCHEME;
  359. }
  360. /**
  361. * Get the scheme. You can get the scheme explicitly.
  362. *
  363. * @return the scheme null if empty or undefined
  364. */
  365. public String getScheme() {
  366. return (_scheme == null) ? null : new String(HttpURL.DEFAULT_SCHEME);
  367. }
  368. // --------------------------------------------------------------- The port
  369. /**
  370. * Get the port number.
  371. * @return the port number
  372. */
  373. public int getPort() {
  374. return (_port == -1) ? HttpURL.DEFAULT_PORT : _port;
  375. }
  376. // ----------------------------------------------------------- The userinfo
  377. /**
  378. * Set the raw-escaped user and password.
  379. *
  380. * @param escapedUser the raw-escaped user
  381. * @param escapedPassword the raw-escaped password; could be null
  382. * @throws URIException escaped user not valid or user required; escaped
  383. * password not valid or username missed
  384. */
  385. public void setRawUserinfo(char[] escapedUser, char[] escapedPassword)
  386. throws URIException {
  387. if (escapedUser == null || escapedUser.length == 0) {
  388. throw new URIException(URIException.PARSING, "user required");
  389. }
  390. if (!validate(escapedUser, within_userinfo)
  391. || ((escapedPassword != null)
  392. && !validate(escapedPassword, within_userinfo))) {
  393. throw new URIException(URIException.ESCAPING,
  394. "escaped userinfo not valid");
  395. }
  396. String username = new String(escapedUser);
  397. String password = (escapedPassword == null)
  398. ? null : new String(escapedPassword);
  399. String userinfo = username + ((password == null) ? "" : ":" + password);
  400. String hostname = new String(getRawHost());
  401. String hostport = (_port == -1) ? hostname : hostname + ":" + _port;
  402. String authority = userinfo + "@" + hostport;
  403. _userinfo = userinfo.toCharArray();
  404. _authority = authority.toCharArray();
  405. setURI();
  406. }
  407. /**
  408. * Set the raw-escaped user and password.
  409. *
  410. * @param escapedUser the escaped user
  411. * @param escapedPassword the escaped password; could be null
  412. * @throws URIException escaped user not valid or user required; escaped
  413. * password not valid or username missed
  414. * @throws NullPointerException null user
  415. */
  416. public void setEscapedUserinfo(String escapedUser, String escapedPassword)
  417. throws URIException, NullPointerException {
  418. setRawUserinfo(escapedUser.toCharArray(), (escapedPassword == null)
  419. ? null : escapedPassword.toCharArray());
  420. }
  421. /**
  422. * Set the user and password.
  423. *
  424. * @param user the user
  425. * @param password the password; could be null
  426. * @throws URIException encoding error or username missed
  427. * @throws NullPointerException null user
  428. */
  429. public void setUserinfo(String user, String password)
  430. throws URIException, NullPointerException {
  431. // set the charset to do escape encoding
  432. String charset = getProtocolCharset();
  433. setRawUserinfo(encode(user, within_userinfo, charset),
  434. (password == null)
  435. ? null
  436. : encode(password, within_userinfo, charset));
  437. }
  438. /**
  439. * Set the raw-escaped user.
  440. *
  441. * @param escapedUser the raw-escaped user
  442. * @throws URIException escaped user not valid or user required
  443. */
  444. public void setRawUser(char[] escapedUser) throws URIException {
  445. if (escapedUser == null || escapedUser.length == 0) {
  446. throw new URIException(URIException.PARSING, "user required");
  447. }
  448. if (!validate(escapedUser, within_userinfo)) {
  449. throw new URIException(URIException.ESCAPING,
  450. "escaped user not valid");
  451. }
  452. String username = new String(escapedUser);
  453. String password = new String(getRawPassword());
  454. String userinfo = username + ((password == null) ? "" : ":" + password);
  455. String hostname = new String(getRawHost());
  456. String hostport = (_port == -1) ? hostname : hostname + ":" + _port;
  457. String authority = userinfo + "@" + hostport;
  458. _userinfo = userinfo.toCharArray();
  459. _authority = authority.toCharArray();
  460. setURI();
  461. }
  462. /**
  463. * Set the escaped user string.
  464. *
  465. * @param escapedUser the escaped user string
  466. * @throws URIException escaped user not valid
  467. * @throws NullPointerException null user
  468. */
  469. public void setEscapedUser(String escapedUser)
  470. throws URIException, NullPointerException {
  471. setRawUser(escapedUser.toCharArray());
  472. }
  473. /**
  474. * Set the user string.
  475. *
  476. * @param user the user string
  477. * @throws URIException user encoding error
  478. * @throws NullPointerException null user
  479. */
  480. public void setUser(String user) throws URIException, NullPointerException {
  481. setRawUser(encode(user, allowed_within_userinfo, getProtocolCharset()));
  482. }
  483. /**
  484. * Get the raw-escaped user.
  485. *
  486. * @return the raw-escaped user
  487. */
  488. public char[] getRawUser() {
  489. if (_userinfo == null || _userinfo.length == 0) {
  490. return null;
  491. }
  492. int to = indexFirstOf(_userinfo, ':');
  493. // String.indexOf(':', 0, _userinfo.length, _userinfo, 0, 1, 0);
  494. if (to == -1) {
  495. return _userinfo; // only user.
  496. }
  497. char[] result = new char[to];
  498. System.arraycopy(_userinfo, 0, result, 0, to);
  499. return result;
  500. }
  501. /**
  502. * Get the escaped user
  503. *
  504. * @return the escaped user
  505. */
  506. public String getEscapedUser() {
  507. char[] user = getRawUser();
  508. return (user == null) ? null : new String(user);
  509. }
  510. /**
  511. * Get the user.
  512. *
  513. * @return the user name
  514. * @throws URIException If {@link #decode} fails
  515. */
  516. public String getUser() throws URIException {
  517. char[] user = getRawUser();
  518. return (user == null) ? null : decode(user, getProtocolCharset());
  519. }
  520. /**
  521. * Set the raw-escaped password.
  522. *
  523. * @param escapedPassword the raw-escaped password; could be null
  524. * @throws URIException escaped password not valid or username missed
  525. */
  526. public void setRawPassword(char[] escapedPassword) throws URIException {
  527. if (escapedPassword != null
  528. && !validate(escapedPassword, within_userinfo)) {
  529. throw new URIException(URIException.ESCAPING,
  530. "escaped password not valid");
  531. }
  532. if (getRawUser() == null || getRawUser().length == 0) {
  533. throw new URIException(URIException.PARSING, "username required");
  534. }
  535. String username = new String(getRawUser());
  536. String password = new String(escapedPassword);
  537. // an emtpy string is allowed as a password
  538. String userinfo = username + ((password == null) ? "" : ":" + password);
  539. String hostname = new String(getRawHost());
  540. String hostport = (_port == -1) ? hostname : hostname + ":" + _port;
  541. String authority = userinfo + "@" + hostport;
  542. _userinfo = userinfo.toCharArray();
  543. _authority = authority.toCharArray();
  544. setURI();
  545. }
  546. /**
  547. * Set the escaped password string.
  548. *
  549. * @param escapedPassword the escaped password string; could be null
  550. * @throws URIException escaped password not valid or username missed
  551. */
  552. public void setEscapedPassword(String escapedPassword) throws URIException {
  553. setRawPassword((escapedPassword == null) ? null
  554. : escapedPassword.toCharArray());
  555. }
  556. /**
  557. * Set the password string.
  558. *
  559. * @param password the password string; could be null
  560. * @throws URIException encoding error or username missed
  561. */
  562. public void setPassword(String password) throws URIException {
  563. setRawPassword((password == null) ? null : encode(password,
  564. allowed_within_userinfo, getProtocolCharset()));
  565. }
  566. /**
  567. * Get the raw-escaped password.
  568. *
  569. * @return the raw-escaped password
  570. */
  571. public char[] getRawPassword() {
  572. int from = indexFirstOf(_userinfo, ':');
  573. if (from == -1) {
  574. return null; // null or only user.
  575. }
  576. int len = _userinfo.length - from - 1;
  577. char[] result = new char[len];
  578. System.arraycopy(_userinfo, from + 1, result, 0, len);
  579. return result;
  580. }
  581. /**
  582. * Get the escaped password.
  583. *
  584. * @return the escaped password
  585. */
  586. public String getEscapedPassword() {
  587. char[] password = getRawPassword();
  588. return (password == null) ? null : new String(password);
  589. }
  590. /**
  591. * Get the password.
  592. *
  593. * @return the password
  594. * @throws URIException If {@link #decode(char[],String)} fails.
  595. */
  596. public String getPassword() throws URIException {
  597. char[] password = getRawPassword();
  598. return (password == null) ? null : decode(password,
  599. getProtocolCharset());
  600. }
  601. // --------------------------------------------------------------- The path
  602. /**
  603. * Get the raw-escaped current hierarchy level.
  604. *
  605. * @return the raw-escaped current hierarchy level
  606. * @throws URIException If {@link #getRawCurrentHierPath(char[])} fails.
  607. */
  608. public char[] getRawCurrentHierPath() throws URIException {
  609. return (_path == null || _path.length == 0) ? rootPath
  610. : super.getRawCurrentHierPath(_path);
  611. }
  612. /**
  613. * Get the level above the this hierarchy level.
  614. *
  615. * @return the raw above hierarchy level
  616. * @throws URIException If {@link #getRawCurrentHierPath(char[])} fails.
  617. */
  618. public char[] getRawAboveHierPath() throws URIException {
  619. char[] path = getRawCurrentHierPath();
  620. return (path == null || path.length == 0) ? rootPath : getRawCurrentHierPath(path);
  621. }
  622. /**
  623. * Get the raw escaped path.
  624. *
  625. * @return the path '/' if empty or undefined
  626. */
  627. public char[] getRawPath() {
  628. char[] path = super.getRawPath();
  629. return (path == null || path.length == 0) ? rootPath : path;
  630. }
  631. // -------------------------------------------------------------- The query
  632. /**
  633. * Set the query as the name and value pair.
  634. *
  635. * @param queryName the query string.
  636. * @param queryValue the query string.
  637. * @throws URIException incomplete trailing escape pattern
  638. * Or unsupported character encoding
  639. * @throws NullPointerException null query
  640. * @see #encode
  641. */
  642. public void setQuery(String queryName, String queryValue)
  643. throws URIException, NullPointerException {
  644. StringBuffer buff = new StringBuffer();
  645. // set the charset to do escape encoding
  646. String charset = getProtocolCharset();
  647. buff.append(encode(queryName, allowed_within_query, charset));
  648. buff.append('=');
  649. buff.append(encode(queryValue, allowed_within_query, charset));
  650. _query = buff.toString().toCharArray();
  651. setURI();
  652. }
  653. /**
  654. * Set the query as the name and value pairs.
  655. *
  656. * @param queryName the array of the query string.
  657. * @param queryValue the array of the query string.
  658. * @throws URIException incomplete trailing escape pattern,
  659. * unsupported character encoding or wrong array size
  660. * @throws NullPointerException null query
  661. * @see #encode
  662. */
  663. public void setQuery(String[] queryName, String[] queryValue)
  664. throws URIException, NullPointerException {
  665. int length = queryName.length;
  666. if (length != queryValue.length) {
  667. throw new URIException("wrong array size of query");
  668. }
  669. StringBuffer buff = new StringBuffer();
  670. // set the charset to do escape encoding
  671. String charset = getProtocolCharset();
  672. for (int i = 0; i < length; i++) {
  673. buff.append(encode(queryName[i], allowed_within_query, charset));
  674. buff.append('=');
  675. buff.append(encode(queryValue[i], allowed_within_query, charset));
  676. if (i + 1 < length) {
  677. buff.append('&');
  678. }
  679. }
  680. _query = buff.toString().toCharArray();
  681. setURI();
  682. }
  683. // ---------------------------------------------------------------- Utility
  684. /**
  685. * Verify the valid class use for construction.
  686. *
  687. * @throws URIException the wrong scheme use
  688. */
  689. protected void checkValid() throws URIException {
  690. // could be explicit protocol or undefined.
  691. if (!(equals(_scheme, DEFAULT_SCHEME) || _scheme == null)) {
  692. throw new URIException(URIException.PARSING, "wrong class use");
  693. }
  694. }
  695. }