URLStreamHandler.java 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476
  1. /* URLStreamHandler.java -- Abstract superclass for all protocol handlers
  2. Copyright (C) 1998, 1999, 2002, 2003 Free Software Foundation, Inc.
  3. This file is part of GNU Classpath.
  4. GNU Classpath is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2, or (at your option)
  7. any later version.
  8. GNU Classpath is distributed in the hope that it will be useful, but
  9. WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with GNU Classpath; see the file COPYING. If not, write to the
  14. Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
  15. 02111-1307 USA.
  16. Linking this library statically or dynamically with other modules is
  17. making a combined work based on this library. Thus, the terms and
  18. conditions of the GNU General Public License cover the whole
  19. combination.
  20. As a special exception, the copyright holders of this library give you
  21. permission to link this library with independent modules to produce an
  22. executable, regardless of the license terms of these independent
  23. modules, and to copy and distribute the resulting executable under
  24. terms of your choice, provided that you also meet, for each linked
  25. independent module, the terms and conditions of the license of that
  26. module. An independent module is a module which is not derived from
  27. or based on this library. If you modify this library, you may extend
  28. this exception to your version of the library, but you are not
  29. obligated to do so. If you do not wish to do so, delete this
  30. exception statement from your version. */
  31. package java.net;
  32. import java.io.IOException;
  33. import java.io.File;
  34. /*
  35. * Written using on-line Java Platform 1.2 API Specification, as well
  36. * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998).
  37. * Status: Believed complete and correct.
  38. */
  39. /**
  40. * This class is the superclass of all URL protocol handlers. The URL
  41. * class loads the appropriate protocol handler to establish a connection
  42. * to a (possibly) remote service (eg, "http", "ftp") and to do protocol
  43. * specific parsing of URL's. Refer to the URL class documentation for
  44. * details on how that class locates and loads protocol handlers.
  45. * <p>
  46. * A protocol handler implementation should override the openConnection()
  47. * method, and optionally override the parseURL() and toExternalForm()
  48. * methods if necessary. (The default implementations will parse/write all
  49. * URL's in the same form as http URL's). A protocol specific subclass
  50. * of URLConnection will most likely need to be created as well.
  51. * <p>
  52. * Note that the instance methods in this class are called as if they
  53. * were static methods. That is, a URL object to act on is passed with
  54. * every call rather than the caller assuming the URL is stored in an
  55. * instance variable of the "this" object.
  56. * <p>
  57. * The methods in this class are protected and accessible only to subclasses.
  58. * URLStreamConnection objects are intended for use by the URL class only,
  59. * not by other classes (unless those classes are implementing protocols).
  60. *
  61. * @author Aaron M. Renn (arenn@urbanophile.com)
  62. * @author Warren Levy (warrenl@cygnus.com)
  63. *
  64. * @see URL
  65. */
  66. public abstract class URLStreamHandler
  67. {
  68. /**
  69. * Creates a URLStreamHander
  70. */
  71. public URLStreamHandler ()
  72. {
  73. }
  74. /**
  75. * Returns a URLConnection for the passed in URL. Note that this should
  76. * not actually create the connection to the (possibly) remote host, but
  77. * rather simply return a URLConnection object. The connect() method of
  78. * URL connection is used to establish the actual connection, possibly
  79. * after the caller sets up various connection options.
  80. *
  81. * @param url The URL to get a connection object for
  82. *
  83. * @return A URLConnection object for the given URL
  84. *
  85. * @exception IOException If an error occurs
  86. */
  87. protected abstract URLConnection openConnection(URL u)
  88. throws IOException;
  89. /**
  90. * This method parses the string passed in as a URL and set's the
  91. * instance data fields in the URL object passed in to the various values
  92. * parsed out of the string. The start parameter is the position to start
  93. * scanning the string. This is usually the position after the ":" which
  94. * terminates the protocol name. The end parameter is the position to
  95. * stop scanning. This will be either the end of the String, or the
  96. * position of the "#" character, which separates the "file" portion of
  97. * the URL from the "anchor" portion.
  98. * <p>
  99. * This method assumes URL's are formatted like http protocol URL's, so
  100. * subclasses that implement protocols with URL's the follow a different
  101. * syntax should override this method. The lone exception is that if
  102. * the protocol name set in the URL is "file", this method will accept
  103. * an empty hostname (i.e., "file:///"), which is legal for that protocol
  104. *
  105. * @param url The URL object in which to store the results
  106. * @param spec The String-ized URL to parse
  107. * @param start The position in the string to start scanning from
  108. * @param end The position in the string to stop scanning
  109. */
  110. protected void parseURL(URL url, String spec, int start, int end)
  111. {
  112. String host = url.getHost();
  113. int port = url.getPort();
  114. String file = url.getFile();
  115. String ref = url.getRef();
  116. if (spec.regionMatches (start, "//", 0, 2))
  117. {
  118. int hostEnd;
  119. int colon;
  120. start += 2;
  121. int slash = spec.indexOf('/', start);
  122. if (slash >= 0)
  123. hostEnd = slash;
  124. else
  125. hostEnd = end;
  126. host = spec.substring (start, hostEnd);
  127. // Look for optional port number. It is valid for the non-port
  128. // part of the host name to be null (e.g. a URL "http://:80").
  129. // TBD: JDK 1.2 in this case sets host to null rather than "";
  130. // this is undocumented and likely an unintended side effect in 1.2
  131. // so we'll be simple here and stick with "". Note that
  132. // "http://" or "http:///" produce a "" host in JDK 1.2.
  133. if ((colon = host.indexOf(':')) >= 0)
  134. {
  135. try
  136. {
  137. port = Integer.parseInt(host.substring(colon + 1));
  138. }
  139. catch (NumberFormatException e)
  140. {
  141. ; // Ignore invalid port values; port is already set to u's
  142. // port.
  143. }
  144. host = host.substring(0, colon);
  145. }
  146. file = null;
  147. start = hostEnd;
  148. }
  149. else if (host == null)
  150. host = "";
  151. if (file == null || file.length() == 0
  152. || (start < end && spec.charAt(start) == '/'))
  153. {
  154. // No file context available; just spec for file.
  155. // Or this is an absolute path name; ignore any file context.
  156. file = spec.substring(start, end);
  157. ref = null;
  158. }
  159. else if (start < end)
  160. {
  161. // Context is available, but only override it if there is a new file.
  162. char sepChar = '/';
  163. int lastSlash = file.lastIndexOf (sepChar);
  164. if (lastSlash < 0 && File.separatorChar != sepChar
  165. && url.getProtocol ().equals ("file"))
  166. {
  167. // On Windows, even '\' is allowed in a "file" URL.
  168. sepChar = File.separatorChar;
  169. lastSlash = file.lastIndexOf (sepChar);
  170. }
  171. file = file.substring(0, lastSlash)
  172. + sepChar + spec.substring (start, end);
  173. if (url.getProtocol ().equals ("file"))
  174. {
  175. // For "file" URLs constructed relative to a context, we
  176. // need to canonicalise the file path.
  177. try
  178. {
  179. file = new File (file).getCanonicalPath ();
  180. }
  181. catch (IOException e)
  182. {
  183. }
  184. }
  185. ref = null;
  186. }
  187. if (ref == null)
  188. {
  189. // Normally there should be no '#' in the file part,
  190. // but we are nice.
  191. int hash = file.indexOf('#');
  192. if (hash != -1)
  193. {
  194. ref = file.substring(hash + 1, file.length());
  195. file = file.substring(0, hash);
  196. }
  197. }
  198. // XXX - Classpath used to call PlatformHelper.toCanonicalForm() on
  199. // the file part. It seems like overhead, but supposedly there is some
  200. // benefit in windows based systems (it also lowercased the string).
  201. setURL(url, url.getProtocol(), host, port, file, ref);
  202. }
  203. private static String canonicalizeFilename(String file)
  204. {
  205. // XXX - GNU Classpath has an implementation that might be more appropriate
  206. // for Windows based systems (gnu.java.io.PlatformHelper.toCanonicalForm)
  207. int index;
  208. // Replace "/./" with "/". This probably isn't very efficient in
  209. // the general case, but it's probably not bad most of the time.
  210. while ((index = file.indexOf("/./")) >= 0)
  211. file = file.substring(0, index) + file.substring(index + 2);
  212. // Process "/../" correctly. This probably isn't very efficient in
  213. // the general case, but it's probably not bad most of the time.
  214. while ((index = file.indexOf("/../")) >= 0)
  215. {
  216. // Strip of the previous directory - if it exists.
  217. int previous = file.lastIndexOf('/', index - 1);
  218. if (previous >= 0)
  219. file = file.substring(0, previous) + file.substring(index + 3);
  220. else
  221. break;
  222. }
  223. return file;
  224. }
  225. /**
  226. * Compares two URLs, excluding the fragment component
  227. *
  228. * @param url1 The first url
  229. * @param url2 The second url to compare with the first
  230. *
  231. * @specnote Now protected
  232. */
  233. protected boolean sameFile(URL url1, URL url2)
  234. {
  235. if (url1 == url2)
  236. return true;
  237. // This comparison is very conservative. It assumes that any
  238. // field can be null.
  239. if (url1 == null || url2 == null || url1.getPort() != url2.getPort())
  240. return false;
  241. String s1, s2;
  242. s1 = url1.getProtocol();
  243. s2 = url2.getProtocol();
  244. if (s1 != s2 && (s1 == null || ! s1.equals(s2)))
  245. return false;
  246. s1 = url1.getHost();
  247. s2 = url2.getHost();
  248. if (s1 != s2 && (s1 == null || ! s1.equals(s2)))
  249. return false;
  250. s1 = canonicalizeFilename(url1.getFile());
  251. s2 = canonicalizeFilename(url2.getFile());
  252. if (s1 != s2 && (s1 == null || ! s1.equals(s2)))
  253. return false;
  254. return true;
  255. }
  256. /**
  257. * This methods sets the instance variables representing the various fields
  258. * of the URL to the values passed in.
  259. *
  260. * @param u The URL to modify
  261. * @param protocol The protocol to set
  262. * @param host The host name to et
  263. * @param port The port number to set
  264. * @param file The filename to set
  265. * @param ref The reference
  266. *
  267. * @exception SecurityException If the protocol handler of the URL is
  268. * different from this one
  269. *
  270. * @deprecated 1.2 Please use
  271. * #setURL(URL,String,String,int,String,String,String,String);
  272. */
  273. protected void setURL(URL u, String protocol, String host, int port,
  274. String file, String ref)
  275. {
  276. u.set(protocol, host, port, file, ref);
  277. }
  278. /**
  279. * Sets the fields of the URL argument to the indicated values
  280. *
  281. * @param u The URL to modify
  282. * @param protocol The protocol to set
  283. * @param host The host name to set
  284. * @param port The port number to set
  285. * @param authority The authority to set
  286. * @param userInfo The user information to set
  287. * @param path The path/filename to set
  288. * @param query The query part to set
  289. * @param ref The reference
  290. *
  291. * @exception SecurityException If the protocol handler of the URL is
  292. * different from this one
  293. */
  294. protected void setURL(URL u, String protocol, String host, int port,
  295. String authority, String userInfo, String path,
  296. String query, String ref)
  297. {
  298. u.set(protocol, host, port, authority, userInfo, path, query, ref);
  299. }
  300. /**
  301. * Provides the default equals calculation. May be overidden by handlers for
  302. * other protocols that have different requirements for equals(). This method
  303. * requires that none of its arguments is null. This is guaranteed by the
  304. * fact that it is only called by java.net.URL class.
  305. *
  306. * @param url1 An URL object
  307. * @param url2 An URL object
  308. */
  309. protected boolean equals (URL url1, URL url2)
  310. {
  311. // This comparison is very conservative. It assumes that any
  312. // field can be null.
  313. return (url1.getPort () == url2.getPort ()
  314. && ((url1.getProtocol () == null && url2.getProtocol () == null)
  315. || (url1.getProtocol () != null
  316. && url1.getProtocol ().equals (url2.getProtocol ())))
  317. && ((url1.getUserInfo () == null && url2.getUserInfo () == null)
  318. || (url1.getUserInfo () != null
  319. && url1.getUserInfo ().equals(url2.getUserInfo ())))
  320. && ((url1.getAuthority () == null && url2.getAuthority () == null)
  321. || (url1.getAuthority () != null
  322. && url1.getAuthority ().equals(url2.getAuthority ())))
  323. && ((url1.getHost () == null && url2.getHost () == null)
  324. || (url1.getHost () != null
  325. && url1.getHost ().equals(url2.getHost ())))
  326. && ((url1.getPath () == null && url2.getPath () == null)
  327. || (url1.getPath () != null
  328. && url1.getPath ().equals (url2.getPath ())))
  329. && ((url1.getQuery () == null && url2.getQuery () == null)
  330. || (url1.getQuery () != null
  331. && url1.getQuery ().equals(url2.getQuery ())))
  332. && ((url1.getRef () == null && url2.getRef () == null)
  333. || (url1.getRef () != null
  334. && url1.getRef ().equals(url2.getRef ()))));
  335. }
  336. /**
  337. * Compares the host components of two URLs.
  338. *
  339. * @exception UnknownHostException If an unknown host is found
  340. */
  341. protected boolean hostsEqual (URL url1, URL url2)
  342. throws UnknownHostException
  343. {
  344. InetAddress addr1 = InetAddress.getByName (url1.getHost ());
  345. InetAddress addr2 = InetAddress.getByName (url2.getHost ());
  346. return addr1.equals (addr2);
  347. }
  348. /**
  349. * Get the IP address of our host. An empty host field or a DNS failure will
  350. * result in a null return.
  351. */
  352. protected InetAddress getHostAddress (URL url)
  353. {
  354. String hostname = url.getHost ();
  355. if (hostname == "")
  356. return null;
  357. try
  358. {
  359. return InetAddress.getByName (hostname);
  360. }
  361. catch (UnknownHostException e)
  362. {
  363. return null;
  364. }
  365. }
  366. /**
  367. * Returns the default port for a URL parsed by this handler. This method is
  368. * meant to be overidden by handlers with default port numbers.
  369. */
  370. protected int getDefaultPort ()
  371. {
  372. return -1;
  373. }
  374. /**
  375. * Provides the default hash calculation. May be overidden by handlers for
  376. * other protocols that have different requirements for hashCode calculation.
  377. */
  378. protected int hashCode (URL url)
  379. {
  380. return url.getProtocol ().hashCode () +
  381. ((url.getHost () == null) ? 0 : url.getHost ().hashCode ()) +
  382. url.getFile ().hashCode() +
  383. url.getPort ();
  384. }
  385. /**
  386. * This method converts a URL object into a String. This method creates
  387. * Strings in the mold of http URL's, so protocol handlers which use URL's
  388. * that have a different syntax should override this method
  389. *
  390. * @param url The URL object to convert
  391. */
  392. protected String toExternalForm(URL u)
  393. {
  394. String protocol, host, file, ref;
  395. int port;
  396. protocol = u.getProtocol();
  397. // JDK 1.2 online doc infers that host could be null because it
  398. // explicitly states that file cannot be null, but is silent on host.
  399. host = u.getHost();
  400. if (host == null)
  401. host = "";
  402. port = u.getPort();
  403. file = u.getFile();
  404. ref = u.getRef();
  405. // Guess a reasonable size for the string buffer so we have to resize
  406. // at most once.
  407. int size = protocol.length() + host.length() + file.length() + 24;
  408. StringBuffer sb = new StringBuffer(size);
  409. sb.append(protocol);
  410. sb.append(':');
  411. if (host.length() != 0)
  412. sb.append("//").append(host);
  413. // Note that this produces different results from JDK 1.2 as JDK 1.2
  414. // ignores a non-default port if host is null or "". That is inconsistent
  415. // with the spec since the result of this method is spec'ed so it can be
  416. // used to construct a new URL that is equivalent to the original.
  417. boolean port_needed = port > 0 && port != getDefaultPort();
  418. if (port_needed)
  419. sb.append(':').append(port);
  420. sb.append(file);
  421. if (ref != null)
  422. sb.append('#').append(ref);
  423. return sb.toString();
  424. }
  425. }