001: /*
002: * @(#)URLStreamHandler.java 1.55 06/10/10
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: *
026: */
027:
028: package java.net;
029:
030: import java.io.IOException;
031: import java.io.InputStream;
032: import java.io.File;
033: import java.io.OutputStream;
034: import java.util.Hashtable;
035: import sun.net.www.ParseUtil;
036:
037: /**
038: * The abstract class <code>URLStreamHandler</code> is the common
039: * superclass for all stream protocol handlers. A stream protocol
040: * handler knows how to make a connection for a particular protocol
041: * type, such as <code>http</code>.
042: * <p>
043: * In most cases, an instance of a <code>URLStreamHandler</code>
044: * subclass is not created directly by an application. Rather, the
045: * first time a protocol name is encountered when constructing a
046: * <code>URL</code>, the appropriate stream protocol handler is
047: * automatically loaded.
048: *
049: * @author James Gosling
050: * @version 1.48 10/27/00
051: * @see java.net.URL#URL(java.lang.String, java.lang.String, int, java.lang.String)
052: * @since JDK1.0
053: */
054: public abstract class URLStreamHandler {
055: /**
056: * Opens a connection to the object referenced by the
057: * <code>URL</code> argument.
058: * This method should be overridden by a subclass.
059: *
060: * <p>If for the handler's protocol (such as HTTP or JAR), there
061: * exists a public, specialized URLConnection subclass belonging
062: * to one of the following packages or one of their subpackages:
063: * java.lang, java.io, java.util, java.net, the connection
064: * returned will be of that subclass. For example, for HTTP an
065: * HttpURLConnection will be returned, and for JAR a
066: * JarURLConnection will be returned.
067: * NOTE: <B>java.net.HttpURLConnection</B> is found in J2ME CDC profiles
068: * such as J2ME Foundation Profile.
069: *
070: * @param u the URL that this connects to.
071: * @return a <code>URLConnection</code> object for the <code>URL</code>.
072: * @exception IOException if an I/O error occurs while opening the
073: * connection.
074: */
075: abstract protected URLConnection openConnection(URL u)
076: throws IOException;
077:
078: /**
079: * Parses the string representation of a <code>URL</code> into a
080: * <code>URL</code> object.
081: * <p>
082: * If there is any inherited context, then it has already been
083: * copied into the <code>URL</code> argument.
084: * <p>
085: * The <code>parseURL</code> method of <code>URLStreamHandler</code>
086: * parses the string representation as if it were an
087: * <code>http</code> specification. Most URL protocol families have a
088: * similar parsing. A stream protocol handler for a protocol that has
089: * a different syntax must override this routine.
090: *
091: * @param u the <code>URL</code> to receive the result of parsing
092: * the spec.
093: * @param spec the <code>String</code> representing the URL that
094: * must be parsed.
095: * @param start the character index at which to begin parsing. This is
096: * just past the '<code>:</code>' (if there is one) that
097: * specifies the determination of the protocol name.
098: * @param limit the character position to stop parsing at. This is the
099: * 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:
113: // This field has already been parsed
114: String ref = u.getRef();
115:
116: boolean isRelPath = false;
117: boolean queryOnly = false;
118:
119: // FIX: should not assume query if opaque
120: // Strip off the query part
121: if (start < limit) {
122: int queryStart = spec.indexOf('?');
123: queryOnly = queryStart == start;
124: if ((queryStart != -1) && (queryStart < limit)) {
125: query = spec.substring(queryStart + 1, limit);
126: if (limit > queryStart)
127: limit = queryStart;
128: spec = spec.substring(0, queryStart);
129: }
130: }
131:
132: int i = 0;
133: // Parse the authority part if any
134: if ((start <= limit - 2) && (spec.charAt(start) == '/')
135: && (spec.charAt(start + 1) == '/')) {
136: start += 2;
137: i = spec.indexOf('/', start);
138: if (i < 0) {
139: i = spec.indexOf('?', start);
140: if (i < 0)
141: i = limit;
142: }
143:
144: host = authority = spec.substring(start, i);
145:
146: int ind = authority.indexOf('@');
147: if (ind != -1) {
148: userInfo = authority.substring(0, ind);
149: host = authority.substring(ind + 1);
150: } else {
151: userInfo = null;
152: }
153: if (host != null) {
154: // If the host is surrounded by [ and ] then its an IPv6
155: // literal address as specified in RFC2732
156: if (host.length() > 0 && (host.charAt(0) == '[')) {
157: if ((ind = host.indexOf(']')) > 2) {
158:
159: String nhost = host;
160: host = nhost.substring(0, ind + 1);
161: if (Inet6Address.textToNumericFormat(host
162: .substring(1, ind)) == null) {
163: throw new IllegalArgumentException(
164: "Invalid host: " + host);
165: }
166:
167: port = -1;
168: if (nhost.length() > ind + 1) {
169: if (nhost.charAt(ind + 1) == ':') {
170: ++ind;
171: // port can be null according to RFC2396
172: if (nhost.length() > (ind + 1)) {
173: port = Integer.parseInt(nhost
174: .substring(ind + 1));
175: }
176: } else {
177: throw new IllegalArgumentException(
178: "Invalid authority field: "
179: + authority);
180: }
181: }
182: } else {
183: throw new IllegalArgumentException(
184: "Invalid authority field: " + authority);
185: }
186: } else {
187: ind = host.indexOf(':');
188: port = -1;
189: if (ind >= 0) {
190: // port can be null according to RFC2396
191: if (host.length() > (ind + 1)) {
192: port = Integer.parseInt(host
193: .substring(ind + 1));
194: }
195: host = host.substring(0, ind);
196: }
197: }
198: } else {
199: host = "";
200: }
201: if (port < -1)
202: throw new IllegalArgumentException(
203: "Invalid port number :" + port);
204: start = i;
205: // If the authority is defined then the path is defined by the
206: // spec only; See RFC 2396 Section 5.2.4.
207: if (authority != null && authority.length() > 0)
208: path = "";
209: }
210:
211: if (host == null) {
212: host = "";
213: }
214:
215: // Parse the file path if any
216: if (start < limit) {
217: if (spec.charAt(start) == '/') {
218: path = spec.substring(start, limit);
219: } else if (path != null && path.length() > 0) {
220: isRelPath = true;
221: int ind = path.lastIndexOf('/');
222: String seperator = "";
223: if (ind == -1 && authority != null)
224: seperator = "/";
225: path = path.substring(0, ind + 1) + seperator
226: + spec.substring(start, limit);
227:
228: } else {
229: String seperator = (authority != null) ? "/" : "";
230: path = seperator + spec.substring(start, limit);
231: }
232: } else if (queryOnly && path != null) {
233: int ind = path.lastIndexOf('/');
234: if (ind < 0)
235: ind = 0;
236: path = path.substring(0, ind) + "/";
237: }
238: if (path == null)
239: path = "";
240:
241: if (isRelPath) {
242: // Remove embedded /./
243: while ((i = path.indexOf("/./")) >= 0) {
244: path = path.substring(0, i) + path.substring(i + 2);
245: }
246: // Remove embedded /../ if possible
247: i = 0;
248: while ((i = path.indexOf("/../", i)) > 0) {
249: if ((limit = path.lastIndexOf('/', i - 1)) >= 0) {
250: path = path.substring(0, limit)
251: + path.substring(i + 3);
252: i = 0;
253: } else {
254: i = i + 3;
255: }
256: }
257: // Remove trailing .. if possible
258: while (path.endsWith("/..")) {
259: i = path.indexOf("/..");
260: if ((limit = path.lastIndexOf('/', i - 1)) >= 0) {
261: path = path.substring(0, limit + 1);
262: } else {
263: break;
264: }
265: }
266: // Remove starting .
267: if (path.startsWith("./") && path.length() > 2)
268: path = path.substring(2);
269:
270: // Remove trailing .
271: if (path.endsWith("/."))
272: path = path.substring(0, path.length() - 1);
273: }
274:
275: setURL(u, protocol, host, port, authority, userInfo, path,
276: query, ref);
277: }
278:
279: /**
280: * Returns the default port for a URL parsed by this handler. This method
281: * is meant to be overidden by handlers with default port numbers.
282: * @return the default port for a <code>URL</code> parsed by this handler.
283: */
284: protected int getDefaultPort() {
285: return -1;
286: }
287:
288: /**
289: * Provides the default equals calculation. May be overidden by handlers
290: * for other protocols that have different requirements for equals().
291: * This method requires that none of its arguments is null. This is
292: * guaranteed by the fact that it is only called by java.net.URL class.
293: * @param u1 a URL object
294: * @param u2 a URL object
295: * @return <tt>true</tt> if the two urls are
296: * considered equal, ie. they refer to the same
297: * fragment in the same file.
298: */
299: protected boolean equals(URL u1, URL u2) {
300: String ref1 = u1.getRef();
301: String ref2 = u2.getRef();
302: return sameFile(u1, u2)
303: && (ref1 == ref2 || (ref1 != null && ref1.equals(ref2)));
304: }
305:
306: /**
307: * Provides the default hash calculation. May be overidden by handlers for
308: * other protocols that have different requirements for hashCode
309: * calculation.
310: * @param u a URL object
311: * @return an <tt>int</tt> suitable for hash table indexing
312: */
313: protected int hashCode(URL u) {
314: int h = 0;
315:
316: // Generate the protocol part.
317: String protocol = u.getProtocol();
318: if (protocol != null)
319: h += protocol.hashCode();
320:
321: // Generate the host part.
322: InetAddress addr = getHostAddress(u);
323: if (addr != null) {
324: h += addr.hashCode();
325: } else {
326: String host = u.getHost();
327: if (host != null)
328: h += host.toLowerCase().hashCode();
329: }
330:
331: // Generate the file part.
332: String file = u.getFile();
333: if (file != null)
334: h += file.hashCode();
335:
336: // Generate the port part.
337: if (u.getPort() == -1)
338: h += getDefaultPort();
339: else
340: h += u.getPort();
341:
342: // Generate the ref part.
343: String ref = u.getRef();
344: if (ref != null)
345: h += ref.hashCode();
346:
347: return h;
348: }
349:
350: /**
351: * Compare two urls to see whether they refer to the same file,
352: * i.e., having the same protocol, host, port, and path.
353: * This method requires that none of its arguments is null. This is
354: * guaranteed by the fact that it is only called indirectly
355: * by java.net.URL class.
356: * @param u1 a URL object
357: * @param u2 a URL object
358: * @return true if u1 and u2 refer to the same file
359: */
360: protected boolean sameFile(URL u1, URL u2) {
361: // Compare the protocols.
362: if (!((u1.getProtocol() == u2.getProtocol()) || (u1
363: .getProtocol() != null && u1.getProtocol()
364: .equalsIgnoreCase(u2.getProtocol()))))
365: return false;
366:
367: // Compare the hosts.
368: if (!hostsEqual(u1, u2))
369: return false;
370:
371: // Compare the files.
372: if (!(u1.getFile() == u2.getFile() || (u1.getFile() != null && u1
373: .getFile().equals(u2.getFile()))))
374: return false;
375:
376: // Compare the ports.
377: int port1, port2;
378: port1 = (u1.getPort() != -1) ? u1.getPort() : u1.handler
379: .getDefaultPort();
380: port2 = (u2.getPort() != -1) ? u2.getPort() : u2.handler
381: .getDefaultPort();
382: if (port1 != port2)
383: return false;
384:
385: return true;
386: }
387:
388: /**
389: * Get the IP address of our host. An empty host field or a DNS failure
390: * will result in a null return.
391: *
392: * @param u a URL object
393: * @return an <code>InetAddress</code> representing the host
394: * IP address.
395: */
396: protected synchronized InetAddress getHostAddress(URL u) {
397: if (u.hostAddress != null)
398: return u.hostAddress;
399:
400: String host = u.getHost();
401: if (host == null || host.equals("")) {
402: return null;
403: } else {
404: try {
405: u.hostAddress = InetAddress.getByName(host);
406: } catch (UnknownHostException ex) {
407: return null;
408: } catch (SecurityException se) {
409: return null;
410: }
411: }
412: return u.hostAddress;
413: }
414:
415: /**
416: * Compares the host components of two URLs.
417: * @param u1 the URL of the first host to compare
418: * @param u2 the URL of the second host to compare
419: * @return <tt>true</tt> if and only if they
420: * are equal, <tt>false</tt> otherwise.
421: */
422: protected boolean hostsEqual(URL u1, URL u2) {
423: InetAddress a1 = getHostAddress(u1);
424: InetAddress a2 = getHostAddress(u2);
425: // if we have internet address for both, compare them
426: if (a1 != null && a2 != null) {
427: return a1.equals(a2);
428: // else, if both have host names, compare them
429: } else if (u1.getHost() != null && u2.getHost() != null)
430: return u1.getHost().equalsIgnoreCase(u2.getHost());
431: else
432: return u1.getHost() == null && u2.getHost() == null;
433: }
434:
435: /**
436: * Converts a <code>URL</code> of a specific protocol to a
437: * <code>String</code>.
438: *
439: * @param u the URL.
440: * @return a string representation of the <code>URL</code> argument.
441: */
442: protected String toExternalForm(URL u) {
443:
444: // pre-compute length of StringBuffer
445: int len = u.getProtocol().length() + 1;
446: if (u.getAuthority() != null && u.getAuthority().length() > 0)
447: len += 2 + u.getAuthority().length();
448: if (u.getPath() != null) {
449: len += u.getPath().length();
450: }
451: if (u.getQuery() != null) {
452: len += 1 + u.getQuery().length();
453: }
454: if (u.getRef() != null)
455: len += 1 + u.getRef().length();
456:
457: StringBuffer result = new StringBuffer(len);
458: result.append(u.getProtocol());
459: result.append(":");
460: if (u.getAuthority() != null && u.getAuthority().length() > 0) {
461: result.append("//");
462: result.append(u.getAuthority());
463: }
464: if (u.getPath() != null) {
465: result.append(u.getPath());
466: }
467: if (u.getQuery() != null) {
468: result.append('?');
469: result.append(u.getQuery());
470: }
471: if (u.getRef() != null) {
472: result.append("#");
473: result.append(u.getRef());
474: }
475: return result.toString();
476: }
477:
478: /**
479: * Sets the fields of the <code>URL</code> argument to the indicated values.
480: * Only classes derived from URLStreamHandler are supposed to be able
481: * to call the set method on a URL.
482: *
483: * @param u the URL to modify.
484: * @param protocol the protocol name.
485: * @param host the remote host value for the URL.
486: * @param port the port on the remote machine.
487: * @param authority the authority part for the URL.
488: * @param userInfo the userInfo part of the URL.
489: * @param path the path component of the URL.
490: * @param query the query part for the URL.
491: * @param ref the reference.
492: * @exception SecurityException if the protocol handler of the URL is
493: * different from this one
494: * @see java.net.URL#set(java.lang.String, java.lang.String, int, java.lang.String, java.lang.String)
495: */
496: protected void setURL(URL u, String protocol, String host,
497: int port, String authority, String userInfo, String path,
498: String query, String ref) {
499: if (this != u.handler) {
500: throw new SecurityException(
501: "handler for url different from " + "this handler");
502: }
503: // ensure that no one can reset the protocol on a given URL.
504: u.set(u.getProtocol(), host, port, authority, userInfo, path,
505: query, ref);
506: }
507:
508: /**
509: * Sets the fields of the <code>URL</code> argument to the indicated values.
510: * Only classes derived from URLStreamHandler are supposed to be able
511: * to call the set method on a URL.
512: *
513: * param u the URL to modify.
514: * param protocol the protocol name. This value is ignored since 1.2.
515: * param host the remote host value for the URL.
516: * param port the port on the remote machine.
517: * param file the file.
518: * param ref the reference.
519: * exception SecurityException if the protocol handler of the URL is
520: * different from this one
521: * deprecated Use setURL(URL, String, String, int, String, String, String,
522: * String);
523: *
524: protected void setURL(URL u, String protocol, String host, int port,
525: String file, String ref) {
526: /*
527: * Only old URL handlers call this, so assume that the host
528: * field might contain "user:passwd@host". Fix as necessary.
529: *
530: String authority = null;
531: String userInfo = null;
532: if (host != null && host.length() != 0) {
533: authority = (port == -1) ? host : host + ":" + port;
534: int at = host.lastIndexOf('@');
535: if (at != -1) {
536: userInfo = host.substring(0, at);
537: host = host.substring(at+1);
538: }
539: }
540:
541: /*
542: * Assume file might contain query part. Fix as necessary.
543: *
544: String path = null;
545: String query = null;
546: if (file != null) {
547: int q = file.lastIndexOf('?');
548: if (q != -1) {
549: query = file.substring(q+1);
550: path = file.substring(0, q);
551: } else
552: path = file;
553: }
554: setURL(u, protocol, host, port, authority, userInfo, path, query, ref);
555: }
556: */
557: }
|