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