emul/mini/src/main/java/java/net/URLStreamHandler.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Wed, 23 Jan 2013 20:39:23 +0100
branchemul
changeset 554 05224402145d
parent 339 emul/src/main/java/java/net/URLStreamHandler.java@7579a0ee92fb
permissions -rw-r--r--
First attempt to separate 'mini' profile from the rest of JDK APIs
     1 /*
     2  * Copyright (c) 1995, 2006, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Oracle designates this
     8  * particular file as subject to the "Classpath" exception as provided
     9  * by Oracle in the LICENSE file that accompanied this code.
    10  *
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    14  * version 2 for more details (a copy is included in the LICENSE file that
    15  * accompanied this code).
    16  *
    17  * You should have received a copy of the GNU General Public License version
    18  * 2 along with this work; if not, write to the Free Software Foundation,
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    20  *
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    22  * or visit www.oracle.com if you need additional information or have any
    23  * questions.
    24  */
    25 
    26 package java.net;
    27 
    28 
    29 /**
    30  * The abstract class <code>URLStreamHandler</code> is the common
    31  * superclass for all stream protocol handlers. A stream protocol
    32  * handler knows how to make a connection for a particular protocol
    33  * type, such as <code>http</code>, <code>ftp</code>, or
    34  * <code>gopher</code>.
    35  * <p>
    36  * In most cases, an instance of a <code>URLStreamHandler</code>
    37  * subclass is not created directly by an application. Rather, the
    38  * first time a protocol name is encountered when constructing a
    39  * <code>URL</code>, the appropriate stream protocol handler is
    40  * automatically loaded.
    41  *
    42  * @author  James Gosling
    43  * @see     java.net.URL#URL(java.lang.String, java.lang.String, int, java.lang.String)
    44  * @since   JDK1.0
    45  */
    46 public abstract class URLStreamHandler {
    47     /**
    48      * Opens a connection to the object referenced by the
    49      * <code>URL</code> argument.
    50      * This method should be overridden by a subclass.
    51      *
    52      * <p>If for the handler's protocol (such as HTTP or JAR), there
    53      * exists a public, specialized URLConnection subclass belonging
    54      * to one of the following packages or one of their subpackages:
    55      * java.lang, java.io, java.util, java.net, the connection
    56      * returned will be of that subclass. For example, for HTTP an
    57      * HttpURLConnection will be returned, and for JAR a
    58      * JarURLConnection will be returned.
    59      *
    60      * @param      u   the URL that this connects to.
    61      * @return     a <code>URLConnection</code> object for the <code>URL</code>.
    62      * @exception  IOException  if an I/O error occurs while opening the
    63      *               connection.
    64      */
    65 //    abstract protected URLConnection openConnection(URL u) throws IOException;
    66 
    67     /**
    68      * Same as openConnection(URL), except that the connection will be
    69      * made through the specified proxy; Protocol handlers that do not
    70      * support proxying will ignore the proxy parameter and make a
    71      * normal connection.
    72      *
    73      * Calling this method preempts the system's default ProxySelector
    74      * settings.
    75      *
    76      * @param      u   the URL that this connects to.
    77      * @param      p   the proxy through which the connection will be made.
    78      *                 If direct connection is desired, Proxy.NO_PROXY
    79      *                 should be specified.
    80      * @return     a <code>URLConnection</code> object for the <code>URL</code>.
    81      * @exception  IOException  if an I/O error occurs while opening the
    82      *               connection.
    83      * @exception  IllegalArgumentException if either u or p is null,
    84      *               or p has the wrong type.
    85      * @exception  UnsupportedOperationException if the subclass that
    86      *               implements the protocol doesn't support this method.
    87      * @since      1.5
    88      */
    89 //    protected URLConnection openConnection(URL u, Proxy p) throws IOException {
    90 //        throw new UnsupportedOperationException("Method not implemented.");
    91 //    }
    92 
    93     /**
    94      * Parses the string representation of a <code>URL</code> into a
    95      * <code>URL</code> object.
    96      * <p>
    97      * If there is any inherited context, then it has already been
    98      * copied into the <code>URL</code> argument.
    99      * <p>
   100      * The <code>parseURL</code> method of <code>URLStreamHandler</code>
   101      * parses the string representation as if it were an
   102      * <code>http</code> specification. Most URL protocol families have a
   103      * similar parsing. A stream protocol handler for a protocol that has
   104      * a different syntax must override this routine.
   105      *
   106      * @param   u       the <code>URL</code> to receive the result of parsing
   107      *                  the spec.
   108      * @param   spec    the <code>String</code> representing the URL that
   109      *                  must be parsed.
   110      * @param   start   the character index at which to begin parsing. This is
   111      *                  just past the '<code>:</code>' (if there is one) that
   112      *                  specifies the determination of the protocol name.
   113      * @param   limit   the character position to stop parsing at. This is the
   114      *                  end of the string or the position of the
   115      *                  "<code>#</code>" character, if present. All information
   116      *                  after the sharp sign indicates an anchor.
   117      */
   118     protected void parseURL(URL u, String spec, int start, int limit) {
   119         // These fields may receive context content if this was relative URL
   120         String protocol = u.getProtocol();
   121         String authority = u.getAuthority();
   122         String userInfo = u.getUserInfo();
   123         String host = u.getHost();
   124         int port = u.getPort();
   125         String path = u.getPath();
   126         String query = u.getQuery();
   127 
   128         // This field has already been parsed
   129         String ref = u.getRef();
   130 
   131         boolean isRelPath = false;
   132         boolean queryOnly = false;
   133 
   134 // FIX: should not assume query if opaque
   135         // Strip off the query part
   136         if (start < limit) {
   137             int queryStart = spec.indexOf('?');
   138             queryOnly = queryStart == start;
   139             if ((queryStart != -1) && (queryStart < limit)) {
   140                 query = spec.substring(queryStart+1, limit);
   141                 if (limit > queryStart)
   142                     limit = queryStart;
   143                 spec = spec.substring(0, queryStart);
   144             }
   145         }
   146 
   147         int i = 0;
   148         // Parse the authority part if any
   149         boolean isUNCName = (start <= limit - 4) &&
   150                         (spec.charAt(start) == '/') &&
   151                         (spec.charAt(start + 1) == '/') &&
   152                         (spec.charAt(start + 2) == '/') &&
   153                         (spec.charAt(start + 3) == '/');
   154         if (!isUNCName && (start <= limit - 2) && (spec.charAt(start) == '/') &&
   155             (spec.charAt(start + 1) == '/')) {
   156             start += 2;
   157             i = spec.indexOf('/', start);
   158             if (i < 0) {
   159                 i = spec.indexOf('?', start);
   160                 if (i < 0)
   161                     i = limit;
   162             }
   163 
   164             host = authority = spec.substring(start, i);
   165 
   166             int ind = authority.indexOf('@');
   167             if (ind != -1) {
   168                 userInfo = authority.substring(0, ind);
   169                 host = authority.substring(ind+1);
   170             } else {
   171                 userInfo = null;
   172             }
   173             if (host != null) {
   174                 // If the host is surrounded by [ and ] then its an IPv6
   175                 // literal address as specified in RFC2732
   176                 if (host.length()>0 && (host.charAt(0) == '[')) {
   177                     if ((ind = host.indexOf(']')) > 2) {
   178 
   179                         String nhost = host ;
   180                         host = nhost.substring(0,ind+1);
   181 //                        if (!IPAddressUtil.
   182 //                            isIPv6LiteralAddress(host.substring(1, ind))) {
   183 //                            throw new IllegalArgumentException(
   184 //                                "Invalid host: "+ host);
   185 //                        }
   186 
   187                         port = -1 ;
   188                         if (nhost.length() > ind+1) {
   189                             if (nhost.charAt(ind+1) == ':') {
   190                                 ++ind ;
   191                                 // port can be null according to RFC2396
   192                                 if (nhost.length() > (ind + 1)) {
   193                                     port = Integer.parseInt(nhost.substring(ind+1));
   194                                 }
   195                             } else {
   196                                 throw new IllegalArgumentException(
   197                                     "Invalid authority field: " + authority);
   198                             }
   199                         }
   200                     } else {
   201                         throw new IllegalArgumentException(
   202                             "Invalid authority field: " + authority);
   203                     }
   204                 } else {
   205                     ind = host.indexOf(':');
   206                     port = -1;
   207                     if (ind >= 0) {
   208                         // port can be null according to RFC2396
   209                         if (host.length() > (ind + 1)) {
   210                             port = Integer.parseInt(host.substring(ind + 1));
   211                         }
   212                         host = host.substring(0, ind);
   213                     }
   214                 }
   215             } else {
   216                 host = "";
   217             }
   218             if (port < -1)
   219                 throw new IllegalArgumentException("Invalid port number :" +
   220                                                    port);
   221             start = i;
   222             // If the authority is defined then the path is defined by the
   223             // spec only; See RFC 2396 Section 5.2.4.
   224             if (authority != null && authority.length() > 0)
   225                 path = "";
   226         }
   227 
   228         if (host == null) {
   229             host = "";
   230         }
   231 
   232         // Parse the file path if any
   233         if (start < limit) {
   234             if (spec.charAt(start) == '/') {
   235                 path = spec.substring(start, limit);
   236             } else if (path != null && path.length() > 0) {
   237                 isRelPath = true;
   238                 int ind = path.lastIndexOf('/');
   239                 String seperator = "";
   240                 if (ind == -1 && authority != null)
   241                     seperator = "/";
   242                 path = path.substring(0, ind + 1) + seperator +
   243                          spec.substring(start, limit);
   244 
   245             } else {
   246                 String seperator = (authority != null) ? "/" : "";
   247                 path = seperator + spec.substring(start, limit);
   248             }
   249         } else if (queryOnly && path != null) {
   250             int ind = path.lastIndexOf('/');
   251             if (ind < 0)
   252                 ind = 0;
   253             path = path.substring(0, ind) + "/";
   254         }
   255         if (path == null)
   256             path = "";
   257 
   258         if (isRelPath) {
   259             // Remove embedded /./
   260             while ((i = path.indexOf("/./")) >= 0) {
   261                 path = path.substring(0, i) + path.substring(i + 2);
   262             }
   263             // Remove embedded /../ if possible
   264             i = 0;
   265             while ((i = path.indexOf("/../", i)) >= 0) {
   266                 /*
   267                  * A "/../" will cancel the previous segment and itself,
   268                  * unless that segment is a "/../" itself
   269                  * i.e. "/a/b/../c" becomes "/a/c"
   270                  * but "/../../a" should stay unchanged
   271                  */
   272                 if (i > 0 && (limit = path.lastIndexOf('/', i - 1)) >= 0 &&
   273                     (path.indexOf("/../", limit) != 0)) {
   274                     path = path.substring(0, limit) + path.substring(i + 3);
   275                     i = 0;
   276                 } else {
   277                     i = i + 3;
   278                 }
   279             }
   280             // Remove trailing .. if possible
   281             while (path.endsWith("/..")) {
   282                 i = path.indexOf("/..");
   283                 if ((limit = path.lastIndexOf('/', i - 1)) >= 0) {
   284                     path = path.substring(0, limit+1);
   285                 } else {
   286                     break;
   287                 }
   288             }
   289             // Remove starting .
   290             if (path.startsWith("./") && path.length() > 2)
   291                 path = path.substring(2);
   292 
   293             // Remove trailing .
   294             if (path.endsWith("/."))
   295                 path = path.substring(0, path.length() -1);
   296         }
   297 
   298         setURL(u, protocol, host, port, authority, userInfo, path, query, ref);
   299     }
   300 
   301     /**
   302      * Returns the default port for a URL parsed by this handler. This method
   303      * is meant to be overidden by handlers with default port numbers.
   304      * @return the default port for a <code>URL</code> parsed by this handler.
   305      * @since 1.3
   306      */
   307     protected int getDefaultPort() {
   308         return -1;
   309     }
   310 
   311     /**
   312      * Provides the default equals calculation. May be overidden by handlers
   313      * for other protocols that have different requirements for equals().
   314      * This method requires that none of its arguments is null. This is
   315      * guaranteed by the fact that it is only called by java.net.URL class.
   316      * @param u1 a URL object
   317      * @param u2 a URL object
   318      * @return <tt>true</tt> if the two urls are
   319      * considered equal, ie. they refer to the same
   320      * fragment in the same file.
   321      * @since 1.3
   322      */
   323     protected boolean equals(URL u1, URL u2) {
   324         String ref1 = u1.getRef();
   325         String ref2 = u2.getRef();
   326         return (ref1 == ref2 || (ref1 != null && ref1.equals(ref2))) &&
   327                sameFile(u1, u2);
   328     }
   329 
   330     /**
   331      * Provides the default hash calculation. May be overidden by handlers for
   332      * other protocols that have different requirements for hashCode
   333      * calculation.
   334      * @param u a URL object
   335      * @return an <tt>int</tt> suitable for hash table indexing
   336      * @since 1.3
   337      */
   338     protected int hashCode(URL u) {
   339         int h = 0;
   340 
   341         // Generate the protocol part.
   342         String protocol = u.getProtocol();
   343         if (protocol != null)
   344             h += protocol.hashCode();
   345 
   346         // Generate the host part.
   347         Object addr = getHostAddress(u);
   348         if (addr != null) {
   349             h += addr.hashCode();
   350         } else {
   351             String host = u.getHost();
   352             if (host != null)
   353                 h += host.toLowerCase().hashCode();
   354         }
   355 
   356         // Generate the file part.
   357         String file = u.getFile();
   358         if (file != null)
   359             h += file.hashCode();
   360 
   361         // Generate the port part.
   362         if (u.getPort() == -1)
   363             h += getDefaultPort();
   364         else
   365             h += u.getPort();
   366 
   367         // Generate the ref part.
   368         String ref = u.getRef();
   369         if (ref != null)
   370             h += ref.hashCode();
   371 
   372         return h;
   373     }
   374 
   375     /**
   376      * Compare two urls to see whether they refer to the same file,
   377      * i.e., having the same protocol, host, port, and path.
   378      * This method requires that none of its arguments is null. This is
   379      * guaranteed by the fact that it is only called indirectly
   380      * by java.net.URL class.
   381      * @param u1 a URL object
   382      * @param u2 a URL object
   383      * @return true if u1 and u2 refer to the same file
   384      * @since 1.3
   385      */
   386     protected boolean sameFile(URL u1, URL u2) {
   387         // Compare the protocols.
   388         if (!((u1.getProtocol() == u2.getProtocol()) ||
   389               (u1.getProtocol() != null &&
   390                u1.getProtocol().equalsIgnoreCase(u2.getProtocol()))))
   391             return false;
   392 
   393         // Compare the files.
   394         if (!(u1.getFile() == u2.getFile() ||
   395               (u1.getFile() != null && u1.getFile().equals(u2.getFile()))))
   396             return false;
   397 
   398         // Compare the ports.
   399         int port1, port2;
   400         port1 = (u1.getPort() != -1) ? u1.getPort() : u1.handler.getDefaultPort();
   401         port2 = (u2.getPort() != -1) ? u2.getPort() : u2.handler.getDefaultPort();
   402         if (port1 != port2)
   403             return false;
   404 
   405         // Compare the hosts.
   406         if (!hostsEqual(u1, u2))
   407             return false;
   408 
   409         return true;
   410     }
   411 
   412     /**
   413      * Get the IP address of our host. An empty host field or a DNS failure
   414      * will result in a null return.
   415      *
   416      * @param u a URL object
   417      * @return an <code>InetAddress</code> representing the host
   418      * IP address.
   419      * @since 1.3
   420      */
   421     private synchronized Object getHostAddress(URL u) {
   422         return u.hostAddress;
   423     }
   424 
   425     /**
   426      * Compares the host components of two URLs.
   427      * @param u1 the URL of the first host to compare
   428      * @param u2 the URL of the second host to compare
   429      * @return  <tt>true</tt> if and only if they
   430      * are equal, <tt>false</tt> otherwise.
   431      * @since 1.3
   432      */
   433     protected boolean hostsEqual(URL u1, URL u2) {
   434         Object a1 = getHostAddress(u1);
   435         Object a2 = getHostAddress(u2);
   436         // if we have internet address for both, compare them
   437         if (a1 != null && a2 != null) {
   438             return a1.equals(a2);
   439         // else, if both have host names, compare them
   440         } else if (u1.getHost() != null && u2.getHost() != null)
   441             return u1.getHost().equalsIgnoreCase(u2.getHost());
   442          else
   443             return u1.getHost() == null && u2.getHost() == null;
   444     }
   445 
   446     /**
   447      * Converts a <code>URL</code> of a specific protocol to a
   448      * <code>String</code>.
   449      *
   450      * @param   u   the URL.
   451      * @return  a string representation of the <code>URL</code> argument.
   452      */
   453     protected String toExternalForm(URL u) {
   454 
   455         // pre-compute length of StringBuffer
   456         int len = u.getProtocol().length() + 1;
   457         if (u.getAuthority() != null && u.getAuthority().length() > 0)
   458             len += 2 + u.getAuthority().length();
   459         if (u.getPath() != null) {
   460             len += u.getPath().length();
   461         }
   462         if (u.getQuery() != null) {
   463             len += 1 + u.getQuery().length();
   464         }
   465         if (u.getRef() != null)
   466             len += 1 + u.getRef().length();
   467 
   468         StringBuffer result = new StringBuffer(len);
   469         result.append(u.getProtocol());
   470         result.append(":");
   471         if (u.getAuthority() != null && u.getAuthority().length() > 0) {
   472             result.append("//");
   473             result.append(u.getAuthority());
   474         }
   475         if (u.getPath() != null) {
   476             result.append(u.getPath());
   477         }
   478         if (u.getQuery() != null) {
   479             result.append('?');
   480             result.append(u.getQuery());
   481         }
   482         if (u.getRef() != null) {
   483             result.append("#");
   484             result.append(u.getRef());
   485         }
   486         return result.toString();
   487     }
   488 
   489     /**
   490      * Sets the fields of the <code>URL</code> argument to the indicated values.
   491      * Only classes derived from URLStreamHandler are supposed to be able
   492      * to call the set method on a URL.
   493      *
   494      * @param   u         the URL to modify.
   495      * @param   protocol  the protocol name.
   496      * @param   host      the remote host value for the URL.
   497      * @param   port      the port on the remote machine.
   498      * @param   authority the authority part for the URL.
   499      * @param   userInfo the userInfo part of the URL.
   500      * @param   path      the path component of the URL.
   501      * @param   query     the query part for the URL.
   502      * @param   ref       the reference.
   503      * @exception       SecurityException       if the protocol handler of the URL is
   504      *                                  different from this one
   505      * @see     java.net.URL#set(java.lang.String, java.lang.String, int, java.lang.String, java.lang.String)
   506      * @since 1.3
   507      */
   508        protected void setURL(URL u, String protocol, String host, int port,
   509                              String authority, String userInfo, String path,
   510                              String query, String ref) {
   511         if (this != u.handler) {
   512             throw new SecurityException("handler for url different from " +
   513                                         "this handler");
   514         }
   515         // ensure that no one can reset the protocol on a given URL.
   516         u.set(u.getProtocol(), host, port, authority, userInfo, path, query, ref);
   517     }
   518 
   519     /**
   520      * Sets the fields of the <code>URL</code> argument to the indicated values.
   521      * Only classes derived from URLStreamHandler are supposed to be able
   522      * to call the set method on a URL.
   523      *
   524      * @param   u         the URL to modify.
   525      * @param   protocol  the protocol name. This value is ignored since 1.2.
   526      * @param   host      the remote host value for the URL.
   527      * @param   port      the port on the remote machine.
   528      * @param   file      the file.
   529      * @param   ref       the reference.
   530      * @exception       SecurityException       if the protocol handler of the URL is
   531      *                                  different from this one
   532      * @deprecated Use setURL(URL, String, String, int, String, String, String,
   533      *             String);
   534      */
   535     @Deprecated
   536     protected void setURL(URL u, String protocol, String host, int port,
   537                           String file, String ref) {
   538         /*
   539          * Only old URL handlers call this, so assume that the host
   540          * field might contain "user:passwd@host". Fix as necessary.
   541          */
   542         String authority = null;
   543         String userInfo = null;
   544         if (host != null && host.length() != 0) {
   545             authority = (port == -1) ? host : host + ":" + port;
   546             int at = host.lastIndexOf('@');
   547             if (at != -1) {
   548                 userInfo = host.substring(0, at);
   549                 host = host.substring(at+1);
   550             }
   551         }
   552 
   553         /*
   554          * Assume file might contain query part. Fix as necessary.
   555          */
   556         String path = null;
   557         String query = null;
   558         if (file != null) {
   559             int q = file.lastIndexOf('?');
   560             if (q != -1) {
   561                 query = file.substring(q+1);
   562                 path = file.substring(0, q);
   563             } else
   564                 path = file;
   565         }
   566         setURL(u, protocol, host, port, authority, userInfo, path, query, ref);
   567     }
   568 }