| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package java.net; |
| |
| import java.io.IOException; |
| |
| import org.apache.harmony.luni.util.Msg; |
| import org.apache.harmony.luni.util.URLUtil; |
| |
| /** |
| * The abstract class {@code URLStreamHandler} is the base for all classes which |
| * can handle the communication with a URL object over a particular protocol |
| * type. |
| */ |
| public abstract class URLStreamHandler { |
| /** |
| * Establishes a new connection to the resource specified by the URL {@code |
| * u}. Since different protocols also have unique ways of connecting, it |
| * must be overwritten by the subclass. |
| * |
| * @param u |
| * the URL to the resource where a connection has to be opened. |
| * @return the opened URLConnection to the specified resource. |
| * @throws IOException |
| * if an I/O error occurs during opening the connection. |
| */ |
| protected abstract URLConnection openConnection(URL u) throws IOException; |
| |
| /** |
| * Establishes a new connection to the resource specified by the URL {@code |
| * u} using the given {@code proxy}. Since different protocols also have |
| * unique ways of connecting, it must be overwritten by the subclass. |
| * |
| * @param u |
| * the URL to the resource where a connection has to be opened. |
| * @param proxy |
| * the proxy that is used to make the connection. |
| * @return the opened URLConnection to the specified resource. |
| * @throws IOException |
| * if an I/O error occurs during opening the connection. |
| * @throws IllegalArgumentException |
| * if any argument is {@code null} or the type of proxy is |
| * wrong. |
| * @throws UnsupportedOperationException |
| * if the protocol handler doesn't support this method. |
| */ |
| protected URLConnection openConnection(URL u, Proxy proxy) |
| throws IOException { |
| throw new UnsupportedOperationException(Msg.getString("K034d")); //$NON-NLS-1$ |
| } |
| |
| /** |
| * Parses the clear text URL in {@code str} into a URL object. URL strings |
| * generally have the following format: |
| * <p> |
| * http://www.company.com/java/file1.java#reference |
| * <p> |
| * The string is parsed in HTTP format. If the protocol has a different URL |
| * format this method must be overridden. |
| * |
| * @param u |
| * the URL to fill in the parsed clear text URL parts. |
| * @param str |
| * the URL string that is to be parsed. |
| * @param start |
| * the string position from where to begin parsing. |
| * @param end |
| * the string position to stop parsing. |
| * @see #toExternalForm |
| * @see URL |
| */ |
| protected void parseURL(URL u, String str, int start, int end) { |
| // For compatibility, refer to Harmony-2941 |
| if (str.startsWith("//", start) //$NON-NLS-1$ |
| && str.indexOf('/', start + 2) == -1 |
| && end <= Integer.MIN_VALUE + 1) { |
| throw new StringIndexOutOfBoundsException(end - 2 - start); |
| } |
| if (end < start) { |
| if (this != u.strmHandler) { |
| throw new SecurityException(); |
| } |
| return; |
| } |
| String parseString = ""; //$NON-NLS-1$ |
| if (start < end) { |
| parseString = str.substring(start, end); |
| } |
| end -= start; |
| int fileIdx = 0; |
| |
| // Default is to use info from context |
| String host = u.getHost(); |
| int port = u.getPort(); |
| String ref = u.getRef(); |
| String file = u.getPath(); |
| String query = u.getQuery(); |
| String authority = u.getAuthority(); |
| String userInfo = u.getUserInfo(); |
| |
| int refIdx = parseString.indexOf('#', 0); |
| if (parseString.startsWith("//")) { //$NON-NLS-1$ |
| int hostIdx = 2, portIdx = -1; |
| port = -1; |
| fileIdx = parseString.indexOf('/', hostIdx); |
| int questionMarkIndex = parseString.indexOf('?', hostIdx); |
| if ((questionMarkIndex != -1) |
| && ((fileIdx == -1) || (fileIdx > questionMarkIndex))) { |
| fileIdx = questionMarkIndex; |
| } |
| if (fileIdx == -1) { |
| fileIdx = end; |
| // Use default |
| file = ""; //$NON-NLS-1$ |
| } |
| int hostEnd = fileIdx; |
| if (refIdx != -1 && refIdx < fileIdx) { |
| hostEnd = refIdx; |
| } |
| int userIdx = parseString.lastIndexOf('@', hostEnd); |
| authority = parseString.substring(hostIdx, hostEnd); |
| if (userIdx > -1) { |
| userInfo = parseString.substring(hostIdx, userIdx); |
| hostIdx = userIdx + 1; |
| } |
| |
| portIdx = parseString.indexOf(':', userIdx == -1 ? hostIdx |
| : userIdx); |
| int endOfIPv6Addr = parseString.indexOf(']'); |
| // if there are square braces, ie. IPv6 address, use last ':' |
| if (endOfIPv6Addr != -1) { |
| try { |
| if (parseString.length() > endOfIPv6Addr + 1) { |
| char c = parseString.charAt(endOfIPv6Addr + 1); |
| if (c == ':') { |
| portIdx = endOfIPv6Addr + 1; |
| } else { |
| portIdx = -1; |
| } |
| } else { |
| portIdx = -1; |
| } |
| } catch (Exception e) { |
| // Ignored |
| } |
| } |
| |
| if (portIdx == -1 || portIdx > fileIdx) { |
| host = parseString.substring(hostIdx, hostEnd); |
| } else { |
| host = parseString.substring(hostIdx, portIdx); |
| String portString = parseString.substring(portIdx + 1, hostEnd); |
| if (portString.length() == 0) { |
| port = -1; |
| } else { |
| port = Integer.parseInt(portString); |
| } |
| } |
| } |
| |
| if (refIdx > -1) { |
| ref = parseString.substring(refIdx + 1, end); |
| } |
| int fileEnd = (refIdx == -1 ? end : refIdx); |
| |
| int queryIdx = parseString.lastIndexOf('?', fileEnd); |
| boolean canonicalize = false; |
| if (queryIdx > -1) { |
| query = parseString.substring(queryIdx + 1, fileEnd); |
| if (queryIdx == 0 && file != null) { |
| if (file.equals("")) { //$NON-NLS-1$ |
| file = "/"; //$NON-NLS-1$ |
| } else if (file.startsWith("/")) { //$NON-NLS-1$ |
| canonicalize = true; |
| } |
| int last = file.lastIndexOf('/') + 1; |
| file = file.substring(0, last); |
| } |
| fileEnd = queryIdx; |
| } else |
| // Don't inherit query unless only the ref is changed |
| if (refIdx != 0) { |
| query = null; |
| } |
| |
| if (fileIdx > -1) { |
| if (fileIdx < end && parseString.charAt(fileIdx) == '/') { |
| file = parseString.substring(fileIdx, fileEnd); |
| } else if (fileEnd > fileIdx) { |
| if (file == null) { |
| file = ""; //$NON-NLS-1$ |
| } else if (file.equals("")) { //$NON-NLS-1$ |
| file = "/"; //$NON-NLS-1$ |
| } else if (file.startsWith("/")) { //$NON-NLS-1$ |
| canonicalize = true; |
| } |
| int last = file.lastIndexOf('/') + 1; |
| if (last == 0) { |
| file = parseString.substring(fileIdx, fileEnd); |
| } else { |
| file = file.substring(0, last) |
| + parseString.substring(fileIdx, fileEnd); |
| } |
| } |
| } |
| if (file == null) { |
| file = ""; //$NON-NLS-1$ |
| } |
| |
| if (host == null) { |
| host = ""; //$NON-NLS-1$ |
| } |
| |
| if (canonicalize) { |
| // modify file if there's any relative referencing |
| file = URLUtil.canonicalizePath(file); |
| } |
| |
| setURL(u, u.getProtocol(), host, port, authority, userInfo, file, |
| query, ref); |
| } |
| |
| /** |
| * Sets the fields of the URL {@code u} to the values of the supplied |
| * arguments. |
| * |
| * @param u |
| * the non-null URL object to be set. |
| * @param protocol |
| * the protocol. |
| * @param host |
| * the host name. |
| * @param port |
| * the port number. |
| * @param file |
| * the file component. |
| * @param ref |
| * the reference. |
| * @deprecated use setURL(URL, String String, int, String, String, String, |
| * String, String) instead. |
| */ |
| @Deprecated |
| protected void setURL(URL u, String protocol, String host, int port, |
| String file, String ref) { |
| if (this != u.strmHandler) { |
| throw new SecurityException(); |
| } |
| u.set(protocol, host, port, file, ref); |
| } |
| |
| /** |
| * Sets the fields of the URL {@code u} to the values of the supplied |
| * arguments. |
| * |
| * @param u |
| * the non-null URL object to be set. |
| * @param protocol |
| * the protocol. |
| * @param host |
| * the host name. |
| * @param port |
| * the port number. |
| * @param authority |
| * the authority. |
| * @param userInfo |
| * the user info. |
| * @param file |
| * the file component. |
| * @param query |
| * the query. |
| * @param ref |
| * the reference. |
| */ |
| protected void setURL(URL u, String protocol, String host, int port, |
| String authority, String userInfo, String file, String query, |
| String ref) { |
| if (this != u.strmHandler) { |
| throw new SecurityException(); |
| } |
| u.set(protocol, host, port, authority, userInfo, file, query, ref); |
| } |
| |
| /** |
| * Returns the clear text representation of a given URL using HTTP format. |
| * |
| * @param url |
| * the URL object to be converted. |
| * @return the clear text representation of the specified URL. |
| * @see #parseURL |
| * @see URL#toExternalForm() |
| */ |
| protected String toExternalForm(URL url) { |
| StringBuilder answer = new StringBuilder(); |
| answer.append(url.getProtocol()); |
| answer.append(':'); |
| String authority = url.getAuthority(); |
| if (authority != null && authority.length() > 0) { |
| answer.append("//"); //$NON-NLS-1$ |
| answer.append(url.getAuthority()); |
| } |
| |
| String file = url.getFile(); |
| String ref = url.getRef(); |
| if (file != null) { |
| answer.append(file); |
| } |
| if (ref != null) { |
| answer.append('#'); |
| answer.append(ref); |
| } |
| return answer.toString(); |
| } |
| |
| /** |
| * Compares two URL objects whether they represent the same URL. Two URLs |
| * are equal if they have the same file, host, port, protocol, query, and |
| * reference components. |
| * |
| * @param url1 |
| * the first URL to compare. |
| * @param url2 |
| * the second URL to compare. |
| * @return {@code true} if the URLs are the same, {@code false} otherwise. |
| * @see #hashCode |
| */ |
| protected boolean equals(URL url1, URL url2) { |
| if (!sameFile(url1, url2)) { |
| return false; |
| } |
| String s1 = url1.getRef(), s2 = url2.getRef(); |
| if (s1 != s2 && (s1 == null || !s1.equals(s2))) { |
| return false; |
| } |
| s1 = url1.getQuery(); |
| s2 = url2.getQuery(); |
| return s1 == s2 || (s1 != null && s1.equals(s2)); |
| } |
| |
| /** |
| * Returns the default port of the protocol used by the handled URL. The |
| * current implementation returns always {@code -1}. |
| * |
| * @return the appropriate default port number of the protocol. |
| */ |
| protected int getDefaultPort() { |
| return -1; |
| } |
| |
| /** |
| * Returns the host address of the given URL. |
| * |
| * @param url |
| * the URL object where to read the host address from. |
| * @return the host address of the specified URL. |
| */ |
| protected InetAddress getHostAddress(URL url) { |
| try { |
| String host = url.getHost(); |
| if (host == null || host.length() == 0) { |
| return null; |
| } |
| return InetAddress.getByName(host); |
| } catch (UnknownHostException e) { |
| return null; |
| } |
| } |
| |
| /** |
| * Returns the hashcode value for the given URL object. |
| * |
| * @param url |
| * the URL to determine the hashcode. |
| * @return the hashcode of the given URL. |
| */ |
| protected int hashCode(URL url) { |
| return toExternalForm(url).hashCode(); |
| } |
| |
| /** |
| * Compares two URL objects whether they refer to the same host. |
| * |
| * @param url1 |
| * the first URL to be compared. |
| * @param url2 |
| * the second URL to be compared. |
| * @return {@code true} if both URLs refer to the same host, {@code false} |
| * otherwise. |
| */ |
| protected boolean hostsEqual(URL url1, URL url2) { |
| String host1 = getHost(url1), host2 = getHost(url2); |
| if (host1 == host2 || (host1 != null && host1.equalsIgnoreCase(host2))) { |
| return true; |
| } |
| // Compare host address if the host name is not equal. |
| InetAddress address1 = getHostAddress(url1); |
| InetAddress address2 = getHostAddress(url2); |
| if (address1 != null && address1.equals(address2)) { |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Compares two URL objects whether they refer to the same file. In the |
| * comparison included are the URL components protocol, host, port and file. |
| * |
| * @param url1 |
| * the first URL to be compared. |
| * @param url2 |
| * the second URL to be compared. |
| * @return {@code true} if both URLs refer to the same file, {@code false} |
| * otherwise. |
| */ |
| protected boolean sameFile(URL url1, URL url2) { |
| String s1 = url1.getProtocol(); |
| String s2 = url2.getProtocol(); |
| if (s1 != s2 && (s1 == null || !s1.equals(s2))) { |
| return false; |
| } |
| |
| s1 = url1.getFile(); |
| s2 = url2.getFile(); |
| if (s1 != s2 && (s1 == null || !s1.equals(s2))) { |
| return false; |
| } |
| if (!hostsEqual(url1, url2)) { |
| return false; |
| } |
| int p1 = url1.getPort(); |
| if (p1 == -1) { |
| p1 = getDefaultPort(); |
| } |
| int p2 = url2.getPort(); |
| if (p2 == -1) { |
| p2 = getDefaultPort(); |
| } |
| return p1 == p2; |
| } |
| |
| /* |
| * If the URL host is empty while protocal is file, the host is regarded as |
| * localhost. |
| */ |
| private static String getHost(URL url) { |
| String host = url.getHost(); |
| if ("file".equals(url.getProtocol()) //$NON-NLS-1$ |
| && "".equals(host)) { //$NON-NLS-1$ |
| host = "localhost"; //$NON-NLS-1$ |
| } |
| return host; |
| } |
| } |