1. /*
  2. * @(#)URLStreamHandler.java 1.68 04/05/18
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package java.net;
  8. import java.io.IOException;
  9. import java.io.InputStream;
  10. import java.io.File;
  11. import java.io.OutputStream;
  12. import java.util.Hashtable;
  13. import sun.net.util.IPAddressUtil;
  14. import sun.net.www.ParseUtil;
  15. /**
  16. * The abstract class <code>URLStreamHandler</code> is the common
  17. * superclass for all stream protocol handlers. A stream protocol
  18. * handler knows how to make a connection for a particular protocol
  19. * type, such as <code>http</code>, <code>ftp</code>, or
  20. * <code>gopher</code>.
  21. * <p>
  22. * In most cases, an instance of a <code>URLStreamHandler</code>
  23. * subclass is not created directly by an application. Rather, the
  24. * first time a protocol name is encountered when constructing a
  25. * <code>URL</code>, the appropriate stream protocol handler is
  26. * automatically loaded.
  27. *
  28. * @author James Gosling
  29. * @version 1.68, 05/18/04
  30. * @see java.net.URL#URL(java.lang.String, java.lang.String, int, java.lang.String)
  31. * @since JDK1.0
  32. */
  33. public abstract class URLStreamHandler {
  34. /**
  35. * Opens a connection to the object referenced by the
  36. * <code>URL</code> argument.
  37. * This method should be overridden by a subclass.
  38. *
  39. * <p>If for the handler's protocol (such as HTTP or JAR), there
  40. * exists a public, specialized URLConnection subclass belonging
  41. * to one of the following packages or one of their subpackages:
  42. * java.lang, java.io, java.util, java.net, the connection
  43. * returned will be of that subclass. For example, for HTTP an
  44. * HttpURLConnection will be returned, and for JAR a
  45. * JarURLConnection will be returned.
  46. *
  47. * @param u the URL that this connects to.
  48. * @return a <code>URLConnection</code> object for the <code>URL</code>.
  49. * @exception IOException if an I/O error occurs while opening the
  50. * connection.
  51. */
  52. abstract protected URLConnection openConnection(URL u) throws IOException;
  53. /**
  54. * Same as openConnection(URL), except that the connection will be
  55. * made through the specified proxy; Protocol handlers that do not
  56. * support proxing will ignore the proxy parameter and make a
  57. * normal connection.
  58. *
  59. * Calling this method preempts the system's default ProxySelector
  60. * settings.
  61. *
  62. * @param u the URL that this connects to.
  63. * @param p the proxy through which the connection will be made.
  64. * If direct connection is desired, Proxy.NO_PROXY
  65. * should be specified.
  66. * @return a <code>URLConnection</code> object for the <code>URL</code>.
  67. * @exception IOException if an I/O error occurs while opening the
  68. * connection.
  69. * @exception IllegalArgumentException if either u or p is null,
  70. * or p has the wrong type.
  71. * @exception UnsupportedOperationException if the subclass that
  72. * implements the protocol doesn't support this method.
  73. * @since 1.5
  74. */
  75. protected URLConnection openConnection(URL u, Proxy p) throws IOException {
  76. throw new UnsupportedOperationException("Method not implemented.");
  77. }
  78. /**
  79. * Parses the string representation of a <code>URL</code> into a
  80. * <code>URL</code> object.
  81. * <p>
  82. * If there is any inherited context, then it has already been
  83. * copied into the <code>URL</code> argument.
  84. * <p>
  85. * The <code>parseURL</code> method of <code>URLStreamHandler</code>
  86. * parses the string representation as if it were an
  87. * <code>http</code> specification. Most URL protocol families have a
  88. * similar parsing. A stream protocol handler for a protocol that has
  89. * a different syntax must override this routine.
  90. *
  91. * @param u the <code>URL</code> to receive the result of parsing
  92. * the spec.
  93. * @param spec the <code>String</code> representing the URL that
  94. * must be parsed.
  95. * @param start the character index at which to begin parsing. This is
  96. * just past the '<code>:</code>' (if there is one) that
  97. * specifies the determination of the protocol name.
  98. * @param limit the character position to stop parsing at. This is the
  99. * end of the string or the position of the
  100. * "<code>#</code>" character, if present. All information
  101. * after the sharp sign indicates an anchor.
  102. */
  103. protected void parseURL(URL u, String spec, int start, int limit) {
  104. // These fields may receive context content if this was relative URL
  105. String protocol = u.getProtocol();
  106. String authority = u.getAuthority();
  107. String userInfo = u.getUserInfo();
  108. String host = u.getHost();
  109. int port = u.getPort();
  110. String path = u.getPath();
  111. String query = u.getQuery();
  112. // This field has already been parsed
  113. String ref = u.getRef();
  114. boolean isRelPath = false;
  115. boolean queryOnly = false;
  116. // FIX: should not assume query if opaque
  117. // Strip off the query part
  118. if (start < limit) {
  119. int queryStart = spec.indexOf('?');
  120. queryOnly = queryStart == start;
  121. if ((queryStart != -1) && (queryStart < limit)) {
  122. query = spec.substring(queryStart+1, limit);
  123. if (limit > queryStart)
  124. limit = queryStart;
  125. spec = spec.substring(0, queryStart);
  126. }
  127. }
  128. int i = 0;
  129. // Parse the authority part if any
  130. if ((start <= limit - 2) && (spec.charAt(start) == '/') &&
  131. (spec.charAt(start + 1) == '/')) {
  132. start += 2;
  133. i = spec.indexOf('/', start);
  134. if (i < 0) {
  135. i = spec.indexOf('?', start);
  136. if (i < 0)
  137. i = limit;
  138. }
  139. host = authority = spec.substring(start, i);
  140. int ind = authority.indexOf('@');
  141. if (ind != -1) {
  142. userInfo = authority.substring(0, ind);
  143. host = authority.substring(ind+1);
  144. } else {
  145. userInfo = null;
  146. }
  147. if (host != null) {
  148. // If the host is surrounded by [ and ] then its an IPv6
  149. // literal address as specified in RFC2732
  150. if (host.length()>0 && (host.charAt(0) == '[')) {
  151. if ((ind = host.indexOf(']')) > 2) {
  152. String nhost = host ;
  153. host = nhost.substring(0,ind+1);
  154. if (!IPAddressUtil.
  155. isIPv6LiteralAddress(host.substring(1, ind))) {
  156. throw new IllegalArgumentException(
  157. "Invalid host: "+ host);
  158. }
  159. port = -1 ;
  160. if (nhost.length() > ind+1) {
  161. if (nhost.charAt(ind+1) == ':') {
  162. ++ind ;
  163. // port can be null according to RFC2396
  164. if (nhost.length() > (ind + 1)) {
  165. port = Integer.parseInt(nhost.substring(ind+1));
  166. }
  167. } else {
  168. throw new IllegalArgumentException(
  169. "Invalid authority field: " + authority);
  170. }
  171. }
  172. } else {
  173. throw new IllegalArgumentException(
  174. "Invalid authority field: " + authority);
  175. }
  176. } else {
  177. ind = host.indexOf(':');
  178. port = -1;
  179. if (ind >= 0) {
  180. // port can be null according to RFC2396
  181. if (host.length() > (ind + 1)) {
  182. port = Integer.parseInt(host.substring(ind + 1));
  183. }
  184. host = host.substring(0, ind);
  185. }
  186. }
  187. } else {
  188. host = "";
  189. }
  190. if (port < -1)
  191. throw new IllegalArgumentException("Invalid port number :" +
  192. port);
  193. start = i;
  194. // If the authority is defined then the path is defined by the
  195. // spec only; See RFC 2396 Section 5.2.4.
  196. if (authority != null && authority.length() > 0)
  197. path = "";
  198. }
  199. if (host == null) {
  200. host = "";
  201. }
  202. // Parse the file path if any
  203. if (start < limit) {
  204. if (spec.charAt(start) == '/') {
  205. path = spec.substring(start, limit);
  206. } else if (path != null && path.length() > 0) {
  207. isRelPath = true;
  208. int ind = path.lastIndexOf('/');
  209. String seperator = "";
  210. if (ind == -1 && authority != null)
  211. seperator = "/";
  212. path = path.substring(0, ind + 1) + seperator +
  213. spec.substring(start, limit);
  214. } else {
  215. String seperator = (authority != null) ? "/" : "";
  216. path = seperator + spec.substring(start, limit);
  217. }
  218. } else if (queryOnly && path != null) {
  219. int ind = path.lastIndexOf('/');
  220. if (ind < 0)
  221. ind = 0;
  222. path = path.substring(0, ind) + "/";
  223. }
  224. if (path == null)
  225. path = "";
  226. if (isRelPath) {
  227. // Remove embedded /./
  228. while ((i = path.indexOf("/./")) >= 0) {
  229. path = path.substring(0, i) + path.substring(i + 2);
  230. }
  231. // Remove embedded /../ if possible
  232. i = 0;
  233. while ((i = path.indexOf("/../", i)) >= 0) {
  234. /*
  235. * A "/../" will cancel the previous segment and itself,
  236. * unless that segment is a "/../" itself
  237. * i.e. "/a/b/../c" becomes "/a/c"
  238. * but "/../../a" should stay unchanged
  239. */
  240. if (i > 0 && (limit = path.lastIndexOf('/', i - 1)) >= 0 &&
  241. (path.indexOf("/../", limit) != 0)) {
  242. path = path.substring(0, limit) + path.substring(i + 3);
  243. i = 0;
  244. } else {
  245. i = i + 3;
  246. }
  247. }
  248. // Remove trailing .. if possible
  249. while (path.endsWith("/..")) {
  250. i = path.indexOf("/..");
  251. if ((limit = path.lastIndexOf('/', i - 1)) >= 0) {
  252. path = path.substring(0, limit+1);
  253. } else {
  254. break;
  255. }
  256. }
  257. // Remove starting .
  258. if (path.startsWith("./") && path.length() > 2)
  259. path = path.substring(2);
  260. // Remove trailing .
  261. if (path.endsWith("/."))
  262. path = path.substring(0, path.length() -1);
  263. }
  264. setURL(u, protocol, host, port, authority, userInfo, path, query, ref);
  265. }
  266. /**
  267. * Returns the default port for a URL parsed by this handler. This method
  268. * is meant to be overidden by handlers with default port numbers.
  269. * @return the default port for a <code>URL</code> parsed by this handler.
  270. */
  271. protected int getDefaultPort() {
  272. return -1;
  273. }
  274. /**
  275. * Provides the default equals calculation. May be overidden by handlers
  276. * for other protocols that have different requirements for equals().
  277. * This method requires that none of its arguments is null. This is
  278. * guaranteed by the fact that it is only called by java.net.URL class.
  279. * @param u1 a URL object
  280. * @param u2 a URL object
  281. * @return <tt>true</tt> if the two urls are
  282. * considered equal, ie. they refer to the same
  283. * fragment in the same file.
  284. */
  285. protected boolean equals(URL u1, URL u2) {
  286. String ref1 = u1.getRef();
  287. String ref2 = u2.getRef();
  288. return sameFile(u1, u2) &&
  289. (ref1 == ref2 ||
  290. (ref1 != null && ref1.equals(ref2)));
  291. }
  292. /**
  293. * Provides the default hash calculation. May be overidden by handlers for
  294. * other protocols that have different requirements for hashCode
  295. * calculation.
  296. * @param u a URL object
  297. * @return an <tt>int</tt> suitable for hash table indexing
  298. */
  299. protected int hashCode(URL u) {
  300. int h = 0;
  301. // Generate the protocol part.
  302. String protocol = u.getProtocol();
  303. if (protocol != null)
  304. h += protocol.hashCode();
  305. // Generate the host part.
  306. InetAddress addr = getHostAddress(u);
  307. if (addr != null) {
  308. h += addr.hashCode();
  309. } else {
  310. String host = u.getHost();
  311. if (host != null)
  312. h += host.toLowerCase().hashCode();
  313. }
  314. // Generate the file part.
  315. String file = u.getFile();
  316. if (file != null)
  317. h += file.hashCode();
  318. // Generate the port part.
  319. if (u.getPort() == -1)
  320. h += getDefaultPort();
  321. else
  322. h += u.getPort();
  323. // Generate the ref part.
  324. String ref = u.getRef();
  325. if (ref != null)
  326. h += ref.hashCode();
  327. return h;
  328. }
  329. /**
  330. * Compare two urls to see whether they refer to the same file,
  331. * i.e., having the same protocol, host, port, and path.
  332. * This method requires that none of its arguments is null. This is
  333. * guaranteed by the fact that it is only called indirectly
  334. * by java.net.URL class.
  335. * @param u1 a URL object
  336. * @param u2 a URL object
  337. * @return true if u1 and u2 refer to the same file
  338. */
  339. protected boolean sameFile(URL u1, URL u2) {
  340. // Compare the protocols.
  341. if (!((u1.getProtocol() == u2.getProtocol()) ||
  342. (u1.getProtocol() != null &&
  343. u1.getProtocol().equalsIgnoreCase(u2.getProtocol()))))
  344. return false;
  345. // Compare the hosts.
  346. if (!hostsEqual(u1, u2))
  347. return false;
  348. // Compare the files.
  349. if (!(u1.getFile() == u2.getFile() ||
  350. (u1.getFile() != null && u1.getFile().equals(u2.getFile()))))
  351. return false;
  352. // Compare the ports.
  353. int port1, port2;
  354. port1 = (u1.getPort() != -1) ? u1.getPort() : u1.handler.getDefaultPort();
  355. port2 = (u2.getPort() != -1) ? u2.getPort() : u2.handler.getDefaultPort();
  356. if (port1 != port2)
  357. return false;
  358. return true;
  359. }
  360. /**
  361. * Get the IP address of our host. An empty host field or a DNS failure
  362. * will result in a null return.
  363. *
  364. * @param u a URL object
  365. * @return an <code>InetAddress</code> representing the host
  366. * IP address.
  367. */
  368. protected synchronized InetAddress getHostAddress(URL u) {
  369. if (u.hostAddress != null)
  370. return u.hostAddress;
  371. String host = u.getHost();
  372. if (host == null || host.equals("")) {
  373. return null;
  374. } else {
  375. try {
  376. u.hostAddress = InetAddress.getByName(host);
  377. } catch (UnknownHostException ex) {
  378. return null;
  379. } catch (SecurityException se) {
  380. return null;
  381. }
  382. }
  383. return u.hostAddress;
  384. }
  385. /**
  386. * Compares the host components of two URLs.
  387. * @param u1 the URL of the first host to compare
  388. * @param u2 the URL of the second host to compare
  389. * @return <tt>true</tt> if and only if they
  390. * are equal, <tt>false</tt> otherwise.
  391. */
  392. protected boolean hostsEqual(URL u1, URL u2) {
  393. InetAddress a1 = getHostAddress(u1);
  394. InetAddress a2 = getHostAddress(u2);
  395. // if we have internet address for both, compare them
  396. if (a1 != null && a2 != null) {
  397. return a1.equals(a2);
  398. // else, if both have host names, compare them
  399. } else if (u1.getHost() != null && u2.getHost() != null)
  400. return u1.getHost().equalsIgnoreCase(u2.getHost());
  401. else
  402. return u1.getHost() == null && u2.getHost() == null;
  403. }
  404. /**
  405. * Converts a <code>URL</code> of a specific protocol to a
  406. * <code>String</code>.
  407. *
  408. * @param u the URL.
  409. * @return a string representation of the <code>URL</code> argument.
  410. */
  411. protected String toExternalForm(URL u) {
  412. // pre-compute length of StringBuffer
  413. int len = u.getProtocol().length() + 1;
  414. if (u.getAuthority() != null && u.getAuthority().length() > 0)
  415. len += 2 + u.getAuthority().length();
  416. if (u.getPath() != null) {
  417. len += u.getPath().length();
  418. }
  419. if (u.getQuery() != null) {
  420. len += 1 + u.getQuery().length();
  421. }
  422. if (u.getRef() != null)
  423. len += 1 + u.getRef().length();
  424. StringBuffer result = new StringBuffer(len);
  425. result.append(u.getProtocol());
  426. result.append(":");
  427. if (u.getAuthority() != null && u.getAuthority().length() > 0) {
  428. result.append("//");
  429. result.append(u.getAuthority());
  430. }
  431. if (u.getPath() != null) {
  432. result.append(u.getPath());
  433. }
  434. if (u.getQuery() != null) {
  435. result.append('?');
  436. result.append(u.getQuery());
  437. }
  438. if (u.getRef() != null) {
  439. result.append("#");
  440. result.append(u.getRef());
  441. }
  442. return result.toString();
  443. }
  444. /**
  445. * Sets the fields of the <code>URL</code> argument to the indicated values.
  446. * Only classes derived from URLStreamHandler are supposed to be able
  447. * to call the set method on a URL.
  448. *
  449. * @param u the URL to modify.
  450. * @param protocol the protocol name.
  451. * @param host the remote host value for the URL.
  452. * @param port the port on the remote machine.
  453. * @param authority the authority part for the URL.
  454. * @param userInfo the userInfo part of the URL.
  455. * @param path the path component of the URL.
  456. * @param query the query part for the URL.
  457. * @param ref the reference.
  458. * @exception SecurityException if the protocol handler of the URL is
  459. * different from this one
  460. * @see java.net.URL#set(java.lang.String, java.lang.String, int, java.lang.String, java.lang.String)
  461. */
  462. protected void setURL(URL u, String protocol, String host, int port,
  463. String authority, String userInfo, String path,
  464. String query, String ref) {
  465. if (this != u.handler) {
  466. throw new SecurityException("handler for url different from " +
  467. "this handler");
  468. }
  469. // ensure that no one can reset the protocol on a given URL.
  470. u.set(u.getProtocol(), host, port, authority, userInfo, path, query, ref);
  471. }
  472. /**
  473. * Sets the fields of the <code>URL</code> argument to the indicated values.
  474. * Only classes derived from URLStreamHandler are supposed to be able
  475. * to call the set method on a URL.
  476. *
  477. * @param u the URL to modify.
  478. * @param protocol the protocol name. This value is ignored since 1.2.
  479. * @param host the remote host value for the URL.
  480. * @param port the port on the remote machine.
  481. * @param file the file.
  482. * @param ref the reference.
  483. * @exception SecurityException if the protocol handler of the URL is
  484. * different from this one
  485. * @deprecated Use setURL(URL, String, String, int, String, String, String,
  486. * String);
  487. */
  488. @Deprecated
  489. protected void setURL(URL u, String protocol, String host, int port,
  490. String file, String ref) {
  491. /*
  492. * Only old URL handlers call this, so assume that the host
  493. * field might contain "user:passwd@host". Fix as necessary.
  494. */
  495. String authority = null;
  496. String userInfo = null;
  497. if (host != null && host.length() != 0) {
  498. authority = (port == -1) ? host : host + ":" + port;
  499. int at = host.lastIndexOf('@');
  500. if (at != -1) {
  501. userInfo = host.substring(0, at);
  502. host = host.substring(at+1);
  503. }
  504. }
  505. /*
  506. * Assume file might contain query part. Fix as necessary.
  507. */
  508. String path = null;
  509. String query = null;
  510. if (file != null) {
  511. int q = file.lastIndexOf('?');
  512. if (q != -1) {
  513. query = file.substring(q+1);
  514. path = file.substring(0, q);
  515. } else
  516. path = file;
  517. }
  518. setURL(u, protocol, host, port, authority, userInfo, path, query, ref);
  519. }
  520. }