001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package java.net;
019:
020: import java.io.IOException;
021:
022: import org.apache.harmony.luni.util.Msg;
023: import org.apache.harmony.luni.util.URLUtil;
024:
025: /**
026: * The abstract superclass of all classes that implement Protocol Handler.
027: */
028: public abstract class URLStreamHandler {
029: /**
030: * Establishes a connection to the resource specified by <code>URL</code>.
031: * Since different protocols may have unique ways of connecting, it must be
032: * overwritten by the subclass.
033: *
034: * @return java.net.URLConnection
035: * @param u
036: * java.net.URL
037: *
038: * @exception IOException
039: * thrown if an IO error occurs during connection
040: * establishment
041: */
042: protected abstract URLConnection openConnection(URL u)
043: throws IOException;
044:
045: /**
046: * The method is the same as <code>openConnection(URL u)</code> except
047: * that it uses the <code>proxy</code> to establish a connection to the
048: * <code>URL</code>. Since different protocols may have different ways of
049: * connecting, it must be overwritten by the subclass.
050: *
051: * @return java.net.URLConnection
052: * @param u
053: * java.net.URL
054: * @param proxy
055: * the proxy which is used to make the connection
056: *
057: * @throws IOException
058: * thrown if an IO error occurs during connection establishment
059: * @throws IllegalArgumentException
060: * if any argument is null or the type of proxy is wrong.
061: * @throws UnsupportedOperationException
062: * if the protocol handler doesn't support this method.
063: */
064: protected URLConnection openConnection(URL u, Proxy proxy)
065: throws IOException {
066: throw new UnsupportedOperationException(Msg.getString("K034d")); //$NON-NLS-1$
067: }
068:
069: /**
070: * Parse the <code>string</code>str into <code>URL</code> using u's
071: * context. URL strings generally have the following format:
072: * <code><center>//www.company.com/java/file1.java#reference </center></code>
073: * The string is parsed in HTTP format. If the protocol has a different URL
074: * format this method must be overridden.
075: *
076: * @param u
077: * java.net.URL The URL to receive parsed values.
078: * @param str
079: * java.lang.String The string URL spec from which u is derived
080: * @param start
081: * int The index in the string from which to begin parsing
082: * @param end
083: * int The index to stop parsing
084: *
085: * @see #toExternalForm
086: * @see URL
087: */
088: protected void parseURL(URL u, String str, int start, int end) {
089: // For compatibility, refer to Harmony-2941
090: if (str.startsWith("//", start) //$NON-NLS-1$
091: && str.indexOf('/', start + 2) == -1
092: && end <= Integer.MIN_VALUE + 1) {
093: throw new StringIndexOutOfBoundsException(end - 2 - start);
094: }
095: if (end < start) {
096: if (this != u.strmHandler) {
097: throw new SecurityException();
098: }
099: return;
100: }
101: String parseString = ""; //$NON-NLS-1$
102: if (start < end) {
103: parseString = str.substring(start, end);
104: }
105: end -= start;
106: int fileIdx = 0;
107:
108: // Default is to use info from context
109: String host = u.getHost();
110: int port = u.getPort();
111: String ref = u.getRef();
112: String file = u.getPath();
113: String query = u.getQuery();
114: String authority = u.getAuthority();
115: String userInfo = u.getUserInfo();
116:
117: int refIdx = parseString.indexOf('#', 0);
118: if (parseString.startsWith("//")) { //$NON-NLS-1$
119: int hostIdx = 2, portIdx = -1;
120: port = -1;
121: fileIdx = parseString.indexOf('/', hostIdx);
122: int questionMarkIndex = parseString.indexOf('?', hostIdx);
123: if ((questionMarkIndex != -1)
124: && ((fileIdx == -1) || (fileIdx > questionMarkIndex))) {
125: fileIdx = questionMarkIndex;
126: }
127: if (fileIdx == -1) {
128: fileIdx = end;
129: // Use default
130: file = ""; //$NON-NLS-1$
131: }
132: int hostEnd = fileIdx;
133: if (refIdx != -1 && refIdx < fileIdx) {
134: hostEnd = refIdx;
135: }
136: int userIdx = parseString.lastIndexOf('@', hostEnd);
137: authority = parseString.substring(hostIdx, hostEnd);
138: if (userIdx > -1) {
139: userInfo = parseString.substring(hostIdx, userIdx);
140: hostIdx = userIdx + 1;
141: }
142:
143: portIdx = parseString.indexOf(':', userIdx == -1 ? hostIdx
144: : userIdx);
145: int endOfIPv6Addr = parseString.indexOf(']');
146: // if there are square braces, ie. IPv6 address, use last ':'
147: if (endOfIPv6Addr != -1) {
148: try {
149: if (parseString.length() > endOfIPv6Addr + 1) {
150: char c = parseString.charAt(endOfIPv6Addr + 1);
151: if (c == ':') {
152: portIdx = endOfIPv6Addr + 1;
153: } else {
154: portIdx = -1;
155: }
156: } else {
157: portIdx = -1;
158: }
159: } catch (Exception e) {
160: // Ignored
161: }
162: }
163:
164: if (portIdx == -1 || portIdx > fileIdx) {
165: host = parseString.substring(hostIdx, hostEnd);
166: } else {
167: host = parseString.substring(hostIdx, portIdx);
168: String portString = parseString.substring(portIdx + 1,
169: hostEnd);
170: if (portString.length() == 0) {
171: port = -1;
172: } else {
173: port = Integer.parseInt(portString);
174: }
175: }
176: }
177:
178: if (refIdx > -1) {
179: ref = parseString.substring(refIdx + 1, end);
180: }
181: int fileEnd = (refIdx == -1 ? end : refIdx);
182:
183: int queryIdx = parseString.lastIndexOf('?', fileEnd);
184: boolean canonicalize = false;
185: if (queryIdx > -1) {
186: query = parseString.substring(queryIdx + 1, fileEnd);
187: if (queryIdx == 0 && file != null) {
188: if (file.equals("")) { //$NON-NLS-1$
189: file = "/"; //$NON-NLS-1$
190: } else if (file.startsWith("/")) { //$NON-NLS-1$
191: canonicalize = true;
192: }
193: int last = file.lastIndexOf('/') + 1;
194: file = file.substring(0, last);
195: }
196: fileEnd = queryIdx;
197: } else
198: // Don't inherit query unless only the ref is changed
199: if (refIdx != 0) {
200: query = null;
201: }
202:
203: if (fileIdx > -1) {
204: if (fileIdx < end && parseString.charAt(fileIdx) == '/') {
205: file = parseString.substring(fileIdx, fileEnd);
206: } else if (fileEnd > fileIdx) {
207: if (file == null) {
208: file = ""; //$NON-NLS-1$
209: } else if (file.equals("")) { //$NON-NLS-1$
210: file = "/"; //$NON-NLS-1$
211: } else if (file.startsWith("/")) { //$NON-NLS-1$
212: canonicalize = true;
213: }
214: int last = file.lastIndexOf('/') + 1;
215: if (last == 0) {
216: file = parseString.substring(fileIdx, fileEnd);
217: } else {
218: file = file.substring(0, last)
219: + parseString.substring(fileIdx, fileEnd);
220: }
221: }
222: }
223: if (file == null) {
224: file = ""; //$NON-NLS-1$
225: }
226:
227: if (host == null) {
228: host = ""; //$NON-NLS-1$
229: }
230:
231: if (canonicalize) {
232: // modify file if there's any relative referencing
233: file = URLUtil.canonicalizePath(file);
234: }
235:
236: setURL(u, u.getProtocol(), host, port, authority, userInfo,
237: file, query, ref);
238: }
239:
240: /**
241: * Sets the fields of the <code>URL</code> with the supplied arguments
242: *
243: * @param u
244: * java.net.URL The non-null URL to be set
245: * @param protocol
246: * java.lang.String The protocol
247: * @param host
248: * java.lang.String The host name
249: * @param port
250: * int The port number
251: * @param file
252: * java.lang.String The file component
253: * @param ref
254: * java.lang.String The reference
255: *
256: * @see java.util.Set
257: *
258: * @deprecated use setURL(URL, String String, int, String, String, String,
259: * String, String)
260: */
261: @Deprecated
262: protected void setURL(URL u, String protocol, String host,
263: int port, String file, String ref) {
264: if (this != u.strmHandler) {
265: throw new SecurityException();
266: }
267: u.set(protocol, host, port, file, ref);
268: }
269:
270: /**
271: * Sets the fields of the <code>URL</code> with the supplied arguments
272: *
273: * @param u
274: * java.net.URL The non-null URL to be set
275: * @param protocol
276: * java.lang.String The protocol
277: * @param host
278: * java.lang.String The host name
279: * @param port
280: * int The port number
281: * @param authority
282: * java.lang.String The authority
283: * @param userInfo
284: * java.lang.String The user info
285: * @param file
286: * java.lang.String The file component
287: * @param query
288: * java.lang.String The query
289: * @param ref
290: * java.lang.String The reference
291: *
292: * @see java.util.Set
293: */
294: protected void setURL(URL u, String protocol, String host,
295: int port, String authority, String userInfo, String file,
296: String query, String ref) {
297: if (this != u.strmHandler) {
298: throw new SecurityException();
299: }
300: u.set(protocol, host, port, authority, userInfo, file, query,
301: ref);
302: }
303:
304: /**
305: * Answers the string equivalent of an URL using HTTP parsinf format.
306: *
307: * @return java.lang.String the string representation of this URL
308: * @param url
309: * java.net.URL the url object to be processed
310: *
311: * @see #parseURL
312: * @see URL#toExternalForm()
313: */
314: protected String toExternalForm(URL url) {
315: StringBuffer answer = new StringBuffer(url.getProtocol()
316: .length()
317: + url.getFile().length() + 16);
318: answer.append(url.getProtocol());
319: answer.append(':');
320: String authority = url.getAuthority();
321: if (authority != null && authority.length() > 0) {
322: answer.append("//"); //$NON-NLS-1$
323: answer.append(url.getAuthority());
324: }
325:
326: String file = url.getFile();
327: String ref = url.getRef();
328: // file is never null
329: answer.append(file);
330: if (ref != null) {
331: answer.append('#');
332: answer.append(ref);
333: }
334: return answer.toString();
335: }
336:
337: /**
338: * Compares the two urls, and answers true if they represent the same URL.
339: * Two URLs are equal if they have the same file, host, port, protocol,
340: * query, and ref components.
341: *
342: * @param url1
343: * URL the first URL to compare
344: * @param url2
345: * URL the second URL to compare
346: * @return <code>true</code> if the URLs are the same <code>false</code>
347: * if the URLs are different
348: *
349: * @see #hashCode
350: */
351: protected boolean equals(URL url1, URL url2) {
352: if (!sameFile(url1, url2)) {
353: return false;
354: }
355: String s1 = url1.getRef(), s2 = url2.getRef();
356: if (s1 != s2 && (s1 == null || !s1.equals(s2))) {
357: return false;
358: }
359: s1 = url1.getQuery();
360: s2 = url2.getQuery();
361: return s1 == s2 || (s1 != null && s1.equals(s2));
362: }
363:
364: /**
365: * Return the default port.
366: */
367: protected int getDefaultPort() {
368: return -1;
369: }
370:
371: /**
372: * Return the InetAddress for the host of the URL, or null.
373: */
374: protected InetAddress getHostAddress(URL url) {
375: try {
376: String host = url.getHost();
377: if (host == null || host.length() == 0) {
378: return null;
379: }
380: return InetAddress.getByName(host);
381: } catch (UnknownHostException e) {
382: return null;
383: }
384: }
385:
386: /**
387: * Answers a hash code for the URL object.
388: *
389: * @return int the hashcode for hashtable indexing
390: */
391: protected int hashCode(URL url) {
392: return toExternalForm(url).hashCode();
393: }
394:
395: /**
396: * Compares the two urls, and answers true if they have the same host
397: * components.
398: *
399: * @return <code>true</code> if the hosts of the URLs are the same
400: * <code>false</code> if the hosts are different
401: */
402: protected boolean hostsEqual(URL url1, URL url2) {
403: String host1 = getHost(url1), host2 = getHost(url2);
404: if (host1 == host2
405: || (host1 != null && host1.equalsIgnoreCase(host2))) {
406: return true;
407: }
408: // Compare host address if the host name is not equal.
409: InetAddress address1 = getHostAddress(url1);
410: InetAddress address2 = getHostAddress(url2);
411: if (address1 != null && address1.equals(address2)) {
412: return true;
413: }
414: return false;
415: }
416:
417: /**
418: * Answers true if the urls refer to the same file. Compares the protocol,
419: * host, port and file components.
420: *
421: * @return boolean true if the same resource, false otherwise
422: */
423: protected boolean sameFile(URL url1, URL url2) {
424: String s1 = url1.getProtocol();
425: String s2 = url2.getProtocol();
426: if (s1 != s2 && (s1 == null || !s1.equals(s2))) {
427: return false;
428: }
429:
430: s1 = url1.getFile();
431: s2 = url2.getFile();
432: if (s1 != s2 && (s1 == null || !s1.equals(s2))) {
433: return false;
434: }
435: if (!hostsEqual(url1, url2)) {
436: return false;
437: }
438: int p1 = url1.getPort();
439: if (p1 == -1) {
440: p1 = getDefaultPort();
441: }
442: int p2 = url2.getPort();
443: if (p2 == -1) {
444: p2 = getDefaultPort();
445: }
446: return p1 == p2;
447: }
448:
449: /*
450: * If the URL host is empty while protocal is file, the host is regarded as
451: * localhost.
452: */
453: private static String getHost(URL url) {
454: String host = url.getHost();
455: if ("file".equals(url.getProtocol()) //$NON-NLS-1$
456: && "".equals(host)) { //$NON-NLS-1$
457: host = "localhost"; //$NON-NLS-1$
458: }
459: return host;
460: }
461: }
|