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