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