Home | History | Annotate | Download | only in net
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  * Copyright (c) 1995, 2015, Oracle and/or its affiliates. All rights reserved.
      4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      5  *
      6  * This code is free software; you can redistribute it and/or modify it
      7  * under the terms of the GNU General Public License version 2 only, as
      8  * published by the Free Software Foundation.  Oracle designates this
      9  * particular file as subject to the "Classpath" exception as provided
     10  * by Oracle in the LICENSE file that accompanied this code.
     11  *
     12  * This code is distributed in the hope that it will be useful, but WITHOUT
     13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     14  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
     15  * version 2 for more details (a copy is included in the LICENSE file that
     16  * accompanied this code).
     17  *
     18  * You should have received a copy of the GNU General Public License version
     19  * 2 along with this work; if not, write to the Free Software Foundation,
     20  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
     21  *
     22  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
     23  * or visit www.oracle.com if you need additional information or have any
     24  * questions.
     25  */
     26 
     27 package java.net;
     28 
     29 import java.io.IOException;
     30 import java.io.InputStream;
     31 import java.io.InvalidObjectException;
     32 import java.io.ObjectInputStream.GetField;
     33 import java.io.ObjectStreamException;
     34 import java.io.ObjectStreamField;
     35 import java.util.Collections;
     36 import java.util.HashSet;
     37 import java.util.Hashtable;
     38 import java.util.Set;
     39 import java.util.StringTokenizer;
     40 import sun.security.util.SecurityConstants;
     41 
     42 /**
     43  * Class {@code URL} represents a Uniform Resource
     44  * Locator, a pointer to a "resource" on the World
     45  * Wide Web. A resource can be something as simple as a file or a
     46  * directory, or it can be a reference to a more complicated object,
     47  * such as a query to a database or to a search engine. More
     48  * information on the types of URLs and their formats can be found at:
     49  * <a href=
     50  * "http://web.archive.org/web/20051219043731/http://archive.ncsa.uiuc.edu/SDG/Software/Mosaic/Demo/url-primer.html">
     51  * <i>Types of URL</i></a>
     52  * <p>
     53  * In general, a URL can be broken into several parts. Consider the
     54  * following example:
     55  * <blockquote><pre>
     56  *     http://www.example.com/docs/resource1.html
     57  * </pre></blockquote>
     58  * <p>
     59  * The URL above indicates that the protocol to use is
     60  * {@code http} (HyperText Transfer Protocol) and that the
     61  * information resides on a host machine named
     62  * {@code www.example.com}. The information on that host
     63  * machine is named {@code /docs/resource1.html}. The exact
     64  * meaning of this name on the host machine is both protocol
     65  * dependent and host dependent. The information normally resides in
     66  * a file, but it could be generated on the fly. This component of
     67  * the URL is called the <i>path</i> component.
     68  * <p>
     69  * A URL can optionally specify a "port", which is the
     70  * port number to which the TCP connection is made on the remote host
     71  * machine. If the port is not specified, the default port for
     72  * the protocol is used instead. For example, the default port for
     73  * {@code http} is {@code 80}. An alternative port could be
     74  * specified as:
     75  * <blockquote><pre>
     76  *     http://www.example.com:1080/docs/resource1.html
     77  * </pre></blockquote>
     78  * <p>
     79  * The syntax of {@code URL} is defined by  <a
     80  * href="http://www.ietf.org/rfc/rfc2396.txt"><i>RFC&nbsp;2396: Uniform
     81  * Resource Identifiers (URI): Generic Syntax</i></a>, amended by <a
     82  * href="http://www.ietf.org/rfc/rfc2732.txt"><i>RFC&nbsp;2732: Format for
     83  * Literal IPv6 Addresses in URLs</i></a>. The Literal IPv6 address format
     84  * also supports scope_ids. The syntax and usage of scope_ids is described
     85  * <a href="Inet6Address.html#scoped">here</a>.
     86  * <p>
     87  * A URL may have appended to it a "fragment", also known
     88  * as a "ref" or a "reference". The fragment is indicated by the sharp
     89  * sign character "#" followed by more characters. For example,
     90  * <blockquote><pre>
     91  *     http://java.sun.com/index.html#chapter1
     92  * </pre></blockquote>
     93  * <p>
     94  * This fragment is not technically part of the URL. Rather, it
     95  * indicates that after the specified resource is retrieved, the
     96  * application is specifically interested in that part of the
     97  * document that has the tag {@code chapter1} attached to it. The
     98  * meaning of a tag is resource specific.
     99  * <p>
    100  * An application can also specify a "relative URL",
    101  * which contains only enough information to reach the resource
    102  * relative to another URL. Relative URLs are frequently used within
    103  * HTML pages. For example, if the contents of the URL:
    104  * <blockquote><pre>
    105  *     http://java.sun.com/index.html
    106  * </pre></blockquote>
    107  * contained within it the relative URL:
    108  * <blockquote><pre>
    109  *     FAQ.html
    110  * </pre></blockquote>
    111  * it would be a shorthand for:
    112  * <blockquote><pre>
    113  *     http://java.sun.com/FAQ.html
    114  * </pre></blockquote>
    115  * <p>
    116  * The relative URL need not specify all the components of a URL. If
    117  * the protocol, host name, or port number is missing, the value is
    118  * inherited from the fully specified URL. The file component must be
    119  * specified. The optional fragment is not inherited.
    120  * <p>
    121  * The URL class does not itself encode or decode any URL components
    122  * according to the escaping mechanism defined in RFC2396. It is the
    123  * responsibility of the caller to encode any fields, which need to be
    124  * escaped prior to calling URL, and also to decode any escaped fields,
    125  * that are returned from URL. Furthermore, because URL has no knowledge
    126  * of URL escaping, it does not recognise equivalence between the encoded
    127  * or decoded form of the same URL. For example, the two URLs:<br>
    128  * <pre>    http://foo.com/hello world/ and http://foo.com/hello%20world</pre>
    129  * would be considered not equal to each other.
    130  * <p>
    131  * Note, the {@link java.net.URI} class does perform escaping of its
    132  * component fields in certain circumstances. The recommended way
    133  * to manage the encoding and decoding of URLs is to use {@link java.net.URI},
    134  * and to convert between these two classes using {@link #toURI()} and
    135  * {@link URI#toURL()}.
    136  * <p>
    137  * The {@link URLEncoder} and {@link URLDecoder} classes can also be
    138  * used, but only for HTML form encoding, which is not the same
    139  * as the encoding scheme defined in RFC2396.
    140  *
    141  * @author  James Gosling
    142  * @since JDK1.0
    143  */
    144 public final class URL implements java.io.Serializable {
    145 
    146     // Android-changed: Custom built-in URLStreamHandlers for http, https.
    147     // static final String BUILTIN_HANDLERS_PREFIX = "sun.net.www.protocol";
    148     private static final Set<String> BUILTIN_HANDLER_CLASS_NAMES = createBuiltinHandlerClassNames();
    149     static final long serialVersionUID = -7627629688361524110L;
    150 
    151     /**
    152      * The property which specifies the package prefix list to be scanned
    153      * for protocol handlers.  The value of this property (if any) should
    154      * be a vertical bar delimited list of package names to search through
    155      * for a protocol handler to load.  The policy of this class is that
    156      * all protocol handlers will be in a class called <protocolname>.Handler,
    157      * and each package in the list is examined in turn for a matching
    158      * handler.  If none are found (or the property is not specified), the
    159      * default package prefix, sun.net.www.protocol, is used.  The search
    160      * proceeds from the first package in the list to the last and stops
    161      * when a match is found.
    162      */
    163     private static final String protocolPathProp = "java.protocol.handler.pkgs";
    164 
    165     /**
    166      * The protocol to use (ftp, http, nntp, ... etc.) .
    167      * @serial
    168      */
    169     private String protocol;
    170 
    171     /**
    172      * The host name to connect to.
    173      * @serial
    174      */
    175     private String host;
    176 
    177     /**
    178      * The protocol port to connect to.
    179      * @serial
    180      */
    181     private int port = -1;
    182 
    183     /**
    184      * The specified file name on that host. {@code file} is
    185      * defined as {@code path[?query]}
    186      * @serial
    187      */
    188     private String file;
    189 
    190     /**
    191      * The query part of this URL.
    192      */
    193     private transient String query;
    194 
    195     /**
    196      * The authority part of this URL.
    197      * @serial
    198      */
    199     private String authority;
    200 
    201     /**
    202      * The path part of this URL.
    203      */
    204     private transient String path;
    205 
    206     /**
    207      * The userinfo part of this URL.
    208      */
    209     private transient String userInfo;
    210 
    211     /**
    212      * # reference.
    213      * @serial
    214      */
    215     private String ref;
    216 
    217     /**
    218      * The host's IP address, used in equals and hashCode.
    219      * Computed on demand. An uninitialized or unknown hostAddress is null.
    220      */
    221     transient InetAddress hostAddress;
    222 
    223     /**
    224      * The URLStreamHandler for this URL.
    225      */
    226     transient URLStreamHandler handler;
    227 
    228     /* Our hash code.
    229      * @serial
    230      */
    231     private int hashCode = -1;
    232 
    233     private transient UrlDeserializedState tempState;
    234 
    235     /**
    236      * Creates a {@code URL} object from the specified
    237      * {@code protocol}, {@code host}, {@code port}
    238      * number, and {@code file}.<p>
    239      *
    240      * {@code host} can be expressed as a host name or a literal
    241      * IP address. If IPv6 literal address is used, it should be
    242      * enclosed in square brackets ({@code '['} and {@code ']'}), as
    243      * specified by <a
    244      * href="http://www.ietf.org/rfc/rfc2732.txt">RFC&nbsp;2732</a>;
    245      * However, the literal IPv6 address format defined in <a
    246      * href="http://www.ietf.org/rfc/rfc2373.txt"><i>RFC&nbsp;2373: IP
    247      * Version 6 Addressing Architecture</i></a> is also accepted.<p>
    248      *
    249      * Specifying a {@code port} number of {@code -1}
    250      * indicates that the URL should use the default port for the
    251      * protocol.<p>
    252      *
    253      * If this is the first URL object being created with the specified
    254      * protocol, a <i>stream protocol handler</i> object, an instance of
    255      * class {@code URLStreamHandler}, is created for that protocol:
    256      * <ol>
    257      * <li>If the application has previously set up an instance of
    258      *     {@code URLStreamHandlerFactory} as the stream handler factory,
    259      *     then the {@code createURLStreamHandler} method of that instance
    260      *     is called with the protocol string as an argument to create the
    261      *     stream protocol handler.
    262      * <li>If no {@code URLStreamHandlerFactory} has yet been set up,
    263      *     or if the factory's {@code createURLStreamHandler} method
    264      *     returns {@code null}, then the constructor finds the
    265      *     value of the system property:
    266      *     <blockquote><pre>
    267      *         java.protocol.handler.pkgs
    268      *     </pre></blockquote>
    269      *     If the value of that system property is not {@code null},
    270      *     it is interpreted as a list of packages separated by a vertical
    271      *     slash character '{@code |}'. The constructor tries to load
    272      *     the class named:
    273      *     <blockquote><pre>
    274      *         &lt;<i>package</i>&gt;.&lt;<i>protocol</i>&gt;.Handler
    275      *     </pre></blockquote>
    276      *     where &lt;<i>package</i>&gt; is replaced by the name of the package
    277      *     and &lt;<i>protocol</i>&gt; is replaced by the name of the protocol.
    278      *     If this class does not exist, or if the class exists but it is not
    279      *     a subclass of {@code URLStreamHandler}, then the next package
    280      *     in the list is tried.
    281      * <li>If the previous step fails to find a protocol handler, then the
    282      *     constructor tries to load from a system default package.
    283      *     <blockquote><pre>
    284      *         &lt;<i>system default package</i>&gt;.&lt;<i>protocol</i>&gt;.Handler
    285      *     </pre></blockquote>
    286      *     If this class does not exist, or if the class exists but it is not a
    287      *     subclass of {@code URLStreamHandler}, then a
    288      *     {@code MalformedURLException} is thrown.
    289      * </ol>
    290      *
    291      * <p>Protocol handlers for the following protocols are guaranteed
    292      * to exist on the search path :-
    293      * <blockquote><pre>
    294      *     http, https, file, and jar
    295      * </pre></blockquote>
    296      * Protocol handlers for additional protocols may also be
    297      * available.
    298      *
    299      * <p>No validation of the inputs is performed by this constructor.
    300      *
    301      * @param      protocol   the name of the protocol to use.
    302      * @param      host       the name of the host.
    303      * @param      port       the port number on the host.
    304      * @param      file       the file on the host
    305      * @exception  MalformedURLException  if an unknown protocol is specified.
    306      * @see        java.lang.System#getProperty(java.lang.String)
    307      * @see        java.net.URL#setURLStreamHandlerFactory(
    308      *                  java.net.URLStreamHandlerFactory)
    309      * @see        java.net.URLStreamHandler
    310      * @see        java.net.URLStreamHandlerFactory#createURLStreamHandler(
    311      *                  java.lang.String)
    312      */
    313     public URL(String protocol, String host, int port, String file)
    314         throws MalformedURLException
    315     {
    316         this(protocol, host, port, file, null);
    317     }
    318 
    319     /**
    320      * Creates a URL from the specified {@code protocol}
    321      * name, {@code host} name, and {@code file} name. The
    322      * default port for the specified protocol is used.
    323      * <p>
    324      * This method is equivalent to calling the four-argument
    325      * constructor with the arguments being {@code protocol},
    326      * {@code host}, {@code -1}, and {@code file}.
    327      *
    328      * No validation of the inputs is performed by this constructor.
    329      *
    330      * @param      protocol   the name of the protocol to use.
    331      * @param      host       the name of the host.
    332      * @param      file       the file on the host.
    333      * @exception  MalformedURLException  if an unknown protocol is specified.
    334      * @see        java.net.URL#URL(java.lang.String, java.lang.String,
    335      *                  int, java.lang.String)
    336      */
    337     public URL(String protocol, String host, String file)
    338             throws MalformedURLException {
    339         this(protocol, host, -1, file);
    340     }
    341 
    342     /**
    343      * Creates a {@code URL} object from the specified
    344      * {@code protocol}, {@code host}, {@code port}
    345      * number, {@code file}, and {@code handler}. Specifying
    346      * a {@code port} number of {@code -1} indicates that
    347      * the URL should use the default port for the protocol. Specifying
    348      * a {@code handler} of {@code null} indicates that the URL
    349      * should use a default stream handler for the protocol, as outlined
    350      * for:
    351      *     java.net.URL#URL(java.lang.String, java.lang.String, int,
    352      *                      java.lang.String)
    353      *
    354      * <p>If the handler is not null and there is a security manager,
    355      * the security manager's {@code checkPermission}
    356      * method is called with a
    357      * {@code NetPermission("specifyStreamHandler")} permission.
    358      * This may result in a SecurityException.
    359      *
    360      * No validation of the inputs is performed by this constructor.
    361      *
    362      * @param      protocol   the name of the protocol to use.
    363      * @param      host       the name of the host.
    364      * @param      port       the port number on the host.
    365      * @param      file       the file on the host
    366      * @param      handler    the stream handler for the URL.
    367      * @exception  MalformedURLException  if an unknown protocol is specified.
    368      * @exception  SecurityException
    369      *        if a security manager exists and its
    370      *        {@code checkPermission} method doesn't allow
    371      *        specifying a stream handler explicitly.
    372      * @see        java.lang.System#getProperty(java.lang.String)
    373      * @see        java.net.URL#setURLStreamHandlerFactory(
    374      *                  java.net.URLStreamHandlerFactory)
    375      * @see        java.net.URLStreamHandler
    376      * @see        java.net.URLStreamHandlerFactory#createURLStreamHandler(
    377      *                  java.lang.String)
    378      * @see        SecurityManager#checkPermission
    379      * @see        java.net.NetPermission
    380      */
    381     public URL(String protocol, String host, int port, String file,
    382                URLStreamHandler handler) throws MalformedURLException {
    383         if (handler != null) {
    384             SecurityManager sm = System.getSecurityManager();
    385             if (sm != null) {
    386                 // check for permission to specify a handler
    387                 checkSpecifyHandler(sm);
    388             }
    389         }
    390 
    391         protocol = protocol.toLowerCase();
    392         this.protocol = protocol;
    393         if (host != null) {
    394 
    395             /**
    396              * if host is a literal IPv6 address,
    397              * we will make it conform to RFC 2732
    398              */
    399             if (host.indexOf(':') >= 0 && !host.startsWith("[")) {
    400                 host = "["+host+"]";
    401             }
    402             this.host = host;
    403 
    404             if (port < -1) {
    405                 throw new MalformedURLException("Invalid port number :" +
    406                                                     port);
    407             }
    408             this.port = port;
    409             authority = (port == -1) ? host : host + ":" + port;
    410         }
    411 
    412         // Android-changed: App compat. Prepend '/' if host is null / empty
    413         // Parts parts = new Parts(file);
    414         Parts parts = new Parts(file, host);
    415         path = parts.getPath();
    416         query = parts.getQuery();
    417 
    418         if (query != null) {
    419             this.file = path + "?" + query;
    420         } else {
    421             this.file = path;
    422         }
    423         ref = parts.getRef();
    424 
    425         // Note: we don't do validation of the URL here. Too risky to change
    426         // right now, but worth considering for future reference. -br
    427         if (handler == null &&
    428             (handler = getURLStreamHandler(protocol)) == null) {
    429             throw new MalformedURLException("unknown protocol: " + protocol);
    430         }
    431         this.handler = handler;
    432     }
    433 
    434     /**
    435      * Creates a {@code URL} object from the {@code String}
    436      * representation.
    437      * <p>
    438      * This constructor is equivalent to a call to the two-argument
    439      * constructor with a {@code null} first argument.
    440      *
    441      * @param      spec   the {@code String} to parse as a URL.
    442      * @exception  MalformedURLException  if no protocol is specified, or an
    443      *               unknown protocol is found, or {@code spec} is {@code null}.
    444      * @see        java.net.URL#URL(java.net.URL, java.lang.String)
    445      */
    446     public URL(String spec) throws MalformedURLException {
    447         this(null, spec);
    448     }
    449 
    450     /**
    451      * Creates a URL by parsing the given spec within a specified context.
    452      *
    453      * The new URL is created from the given context URL and the spec
    454      * argument as described in
    455      * RFC2396 &quot;Uniform Resource Identifiers : Generic * Syntax&quot; :
    456      * <blockquote><pre>
    457      *          &lt;scheme&gt;://&lt;authority&gt;&lt;path&gt;?&lt;query&gt;#&lt;fragment&gt;
    458      * </pre></blockquote>
    459      * The reference is parsed into the scheme, authority, path, query and
    460      * fragment parts. If the path component is empty and the scheme,
    461      * authority, and query components are undefined, then the new URL is a
    462      * reference to the current document. Otherwise, the fragment and query
    463      * parts present in the spec are used in the new URL.
    464      * <p>
    465      * If the scheme component is defined in the given spec and does not match
    466      * the scheme of the context, then the new URL is created as an absolute
    467      * URL based on the spec alone. Otherwise the scheme component is inherited
    468      * from the context URL.
    469      * <p>
    470      * If the authority component is present in the spec then the spec is
    471      * treated as absolute and the spec authority and path will replace the
    472      * context authority and path. If the authority component is absent in the
    473      * spec then the authority of the new URL will be inherited from the
    474      * context.
    475      * <p>
    476      * If the spec's path component begins with a slash character
    477      * &quot;/&quot; then the
    478      * path is treated as absolute and the spec path replaces the context path.
    479      * <p>
    480      * Otherwise, the path is treated as a relative path and is appended to the
    481      * context path, as described in RFC2396. Also, in this case,
    482      * the path is canonicalized through the removal of directory
    483      * changes made by occurrences of &quot;..&quot; and &quot;.&quot;.
    484      * <p>
    485      * For a more detailed description of URL parsing, refer to RFC2396.
    486      *
    487      * @param      context   the context in which to parse the specification.
    488      * @param      spec      the {@code String} to parse as a URL.
    489      * @exception  MalformedURLException  if no protocol is specified, or an
    490      *               unknown protocol is found, or {@code spec} is {@code null}.
    491      * @see        java.net.URL#URL(java.lang.String, java.lang.String,
    492      *                  int, java.lang.String)
    493      * @see        java.net.URLStreamHandler
    494      * @see        java.net.URLStreamHandler#parseURL(java.net.URL,
    495      *                  java.lang.String, int, int)
    496      */
    497     public URL(URL context, String spec) throws MalformedURLException {
    498         this(context, spec, null);
    499     }
    500 
    501     /**
    502      * Creates a URL by parsing the given spec with the specified handler
    503      * within a specified context. If the handler is null, the parsing
    504      * occurs as with the two argument constructor.
    505      *
    506      * @param      context   the context in which to parse the specification.
    507      * @param      spec      the {@code String} to parse as a URL.
    508      * @param      handler   the stream handler for the URL.
    509      * @exception  MalformedURLException  if no protocol is specified, or an
    510      *               unknown protocol is found, or {@code spec} is {@code null}.
    511      * @exception  SecurityException
    512      *        if a security manager exists and its
    513      *        {@code checkPermission} method doesn't allow
    514      *        specifying a stream handler.
    515      * @see        java.net.URL#URL(java.lang.String, java.lang.String,
    516      *                  int, java.lang.String)
    517      * @see        java.net.URLStreamHandler
    518      * @see        java.net.URLStreamHandler#parseURL(java.net.URL,
    519      *                  java.lang.String, int, int)
    520      */
    521     public URL(URL context, String spec, URLStreamHandler handler)
    522         throws MalformedURLException
    523     {
    524         String original = spec;
    525         int i, limit, c;
    526         int start = 0;
    527         String newProtocol = null;
    528         boolean aRef=false;
    529         boolean isRelative = false;
    530 
    531         // Check for permission to specify a handler
    532         if (handler != null) {
    533             SecurityManager sm = System.getSecurityManager();
    534             if (sm != null) {
    535                 checkSpecifyHandler(sm);
    536             }
    537         }
    538 
    539         try {
    540             limit = spec.length();
    541             while ((limit > 0) && (spec.charAt(limit - 1) <= ' ')) {
    542                 limit--;        //eliminate trailing whitespace
    543             }
    544             while ((start < limit) && (spec.charAt(start) <= ' ')) {
    545                 start++;        // eliminate leading whitespace
    546             }
    547 
    548             if (spec.regionMatches(true, start, "url:", 0, 4)) {
    549                 start += 4;
    550             }
    551             if (start < spec.length() && spec.charAt(start) == '#') {
    552                 /* we're assuming this is a ref relative to the context URL.
    553                  * This means protocols cannot start w/ '#', but we must parse
    554                  * ref URL's like: "hello:there" w/ a ':' in them.
    555                  */
    556                 aRef=true;
    557             }
    558             for (i = start ; !aRef && (i < limit) &&
    559                      ((c = spec.charAt(i)) != '/') ; i++) {
    560                 if (c == ':') {
    561 
    562                     String s = spec.substring(start, i).toLowerCase();
    563                     if (isValidProtocol(s)) {
    564                         newProtocol = s;
    565                         start = i + 1;
    566                     }
    567                     break;
    568                 }
    569             }
    570 
    571             // Only use our context if the protocols match.
    572             protocol = newProtocol;
    573             if ((context != null) && ((newProtocol == null) ||
    574                             newProtocol.equalsIgnoreCase(context.protocol))) {
    575                 // inherit the protocol handler from the context
    576                 // if not specified to the constructor
    577                 if (handler == null) {
    578                     handler = context.handler;
    579                 }
    580 
    581                 // If the context is a hierarchical URL scheme and the spec
    582                 // contains a matching scheme then maintain backwards
    583                 // compatibility and treat it as if the spec didn't contain
    584                 // the scheme; see 5.2.3 of RFC2396
    585                 if (context.path != null && context.path.startsWith("/"))
    586                     newProtocol = null;
    587 
    588                 if (newProtocol == null) {
    589                     protocol = context.protocol;
    590                     authority = context.authority;
    591                     userInfo = context.userInfo;
    592                     host = context.host;
    593                     port = context.port;
    594                     file = context.file;
    595                     path = context.path;
    596                     isRelative = true;
    597                 }
    598             }
    599 
    600             if (protocol == null) {
    601                 throw new MalformedURLException("no protocol: "+original);
    602             }
    603 
    604             // Get the protocol handler if not specified or the protocol
    605             // of the context could not be used
    606             if (handler == null &&
    607                 (handler = getURLStreamHandler(protocol)) == null) {
    608                 throw new MalformedURLException("unknown protocol: "+protocol);
    609             }
    610 
    611             this.handler = handler;
    612 
    613             i = spec.indexOf('#', start);
    614             if (i >= 0) {
    615                 ref = spec.substring(i + 1, limit);
    616                 limit = i;
    617             }
    618 
    619             /*
    620              * Handle special case inheritance of query and fragment
    621              * implied by RFC2396 section 5.2.2.
    622              */
    623             if (isRelative && start == limit) {
    624                 query = context.query;
    625                 if (ref == null) {
    626                     ref = context.ref;
    627                 }
    628             }
    629 
    630             handler.parseURL(this, spec, start, limit);
    631 
    632         } catch(MalformedURLException e) {
    633             throw e;
    634         } catch(Exception e) {
    635             MalformedURLException exception = new MalformedURLException(e.getMessage());
    636             exception.initCause(e);
    637             throw exception;
    638         }
    639     }
    640 
    641     /*
    642      * Returns true if specified string is a valid protocol name.
    643      */
    644     private boolean isValidProtocol(String protocol) {
    645         int len = protocol.length();
    646         if (len < 1)
    647             return false;
    648         char c = protocol.charAt(0);
    649         if (!Character.isLetter(c))
    650             return false;
    651         for (int i = 1; i < len; i++) {
    652             c = protocol.charAt(i);
    653             if (!Character.isLetterOrDigit(c) && c != '.' && c != '+' &&
    654                 c != '-') {
    655                 return false;
    656             }
    657         }
    658         return true;
    659     }
    660 
    661     /*
    662      * Checks for permission to specify a stream handler.
    663      */
    664     private void checkSpecifyHandler(SecurityManager sm) {
    665         sm.checkPermission(SecurityConstants.SPECIFY_HANDLER_PERMISSION);
    666     }
    667 
    668     /**
    669      * Sets the fields of the URL. This is not a public method so that
    670      * only URLStreamHandlers can modify URL fields. URLs are
    671      * otherwise constant.
    672      *
    673      * @param protocol the name of the protocol to use
    674      * @param host the name of the host
    675        @param port the port number on the host
    676      * @param file the file on the host
    677      * @param ref the internal reference in the URL
    678      */
    679     void set(String protocol, String host, int port,
    680              String file, String ref) {
    681         synchronized (this) {
    682             this.protocol = protocol;
    683             this.host = host;
    684             authority = port == -1 ? host : host + ":" + port;
    685             this.port = port;
    686             this.file = file;
    687             this.ref = ref;
    688             /* This is very important. We must recompute this after the
    689              * URL has been changed. */
    690             hashCode = -1;
    691             hostAddress = null;
    692             int q = file.lastIndexOf('?');
    693             if (q != -1) {
    694                 query = file.substring(q+1);
    695                 path = file.substring(0, q);
    696             } else
    697                 path = file;
    698         }
    699     }
    700 
    701     /**
    702      * Sets the specified 8 fields of the URL. This is not a public method so
    703      * that only URLStreamHandlers can modify URL fields. URLs are otherwise
    704      * constant.
    705      *
    706      * @param protocol the name of the protocol to use
    707      * @param host the name of the host
    708      * @param port the port number on the host
    709      * @param authority the authority part for the url
    710      * @param userInfo the username and password
    711      * @param path the file on the host
    712      * @param ref the internal reference in the URL
    713      * @param query the query part of this URL
    714      * @since 1.3
    715      */
    716     void set(String protocol, String host, int port,
    717              String authority, String userInfo, String path,
    718              String query, String ref) {
    719         synchronized (this) {
    720             this.protocol = protocol;
    721             this.host = host;
    722             this.port = port;
    723             // Android-changed: App compat. Only include query part if it's nonempty.
    724             // this.file = query == null ? path : path + "?" + query;
    725             this.file = (query == null || query.isEmpty()) ? path : path + "?" + query;
    726             this.userInfo = userInfo;
    727             this.path = path;
    728             this.ref = ref;
    729             /* This is very important. We must recompute this after the
    730              * URL has been changed. */
    731             hashCode = -1;
    732             hostAddress = null;
    733             this.query = query;
    734             this.authority = authority;
    735         }
    736     }
    737 
    738     /**
    739      * Gets the query part of this {@code URL}.
    740      *
    741      * @return  the query part of this {@code URL},
    742      * or <CODE>null</CODE> if one does not exist
    743      * @since 1.3
    744      */
    745     public String getQuery() {
    746         return query;
    747     }
    748 
    749     /**
    750      * Gets the path part of this {@code URL}.
    751      *
    752      * @return  the path part of this {@code URL}, or an
    753      * empty string if one does not exist
    754      * @since 1.3
    755      */
    756     public String getPath() {
    757         return path;
    758     }
    759 
    760     /**
    761      * Gets the userInfo part of this {@code URL}.
    762      *
    763      * @return  the userInfo part of this {@code URL}, or
    764      * <CODE>null</CODE> if one does not exist
    765      * @since 1.3
    766      */
    767     public String getUserInfo() {
    768         return userInfo;
    769     }
    770 
    771     /**
    772      * Gets the authority part of this {@code URL}.
    773      *
    774      * @return  the authority part of this {@code URL}
    775      * @since 1.3
    776      */
    777     public String getAuthority() {
    778         return authority;
    779     }
    780 
    781     /**
    782      * Gets the port number of this {@code URL}.
    783      *
    784      * @return  the port number, or -1 if the port is not set
    785      */
    786     public int getPort() {
    787         return port;
    788     }
    789 
    790     /**
    791      * Gets the default port number of the protocol associated
    792      * with this {@code URL}. If the URL scheme or the URLStreamHandler
    793      * for the URL do not define a default port number,
    794      * then -1 is returned.
    795      *
    796      * @return  the port number
    797      * @since 1.4
    798      */
    799     public int getDefaultPort() {
    800         return handler.getDefaultPort();
    801     }
    802 
    803     /**
    804      * Gets the protocol name of this {@code URL}.
    805      *
    806      * @return  the protocol of this {@code URL}.
    807      */
    808     public String getProtocol() {
    809         return protocol;
    810     }
    811 
    812     /**
    813      * Gets the host name of this {@code URL}, if applicable.
    814      * The format of the host conforms to RFC 2732, i.e. for a
    815      * literal IPv6 address, this method will return the IPv6 address
    816      * enclosed in square brackets ({@code '['} and {@code ']'}).
    817      *
    818      * @return  the host name of this {@code URL}.
    819      */
    820     public String getHost() {
    821         return host;
    822     }
    823 
    824     /**
    825      * Gets the file name of this {@code URL}.
    826      * The returned file portion will be
    827      * the same as <CODE>getPath()</CODE>, plus the concatenation of
    828      * the value of <CODE>getQuery()</CODE>, if any. If there is
    829      * no query portion, this method and <CODE>getPath()</CODE> will
    830      * return identical results.
    831      *
    832      * @return  the file name of this {@code URL},
    833      * or an empty string if one does not exist
    834      */
    835     public String getFile() {
    836         return file;
    837     }
    838 
    839     /**
    840      * Gets the anchor (also known as the "reference") of this
    841      * {@code URL}.
    842      *
    843      * @return  the anchor (also known as the "reference") of this
    844      *          {@code URL}, or <CODE>null</CODE> if one does not exist
    845      */
    846     public String getRef() {
    847         return ref;
    848     }
    849 
    850     // Android-changed: Don't let URL.equals() attempt to resolve host names.
    851     /**
    852      * Compares this URL for equality with another object.<p>
    853      *
    854      * If the given object is not a URL then this method immediately returns
    855      * {@code false}.<p>
    856      *
    857      * Two URL objects are equal if they have the same protocol, reference
    858      * equivalent hosts, have the same port number on the host, and the same
    859      * file and fragment of the file.<p>
    860      *
    861      * Returns true if this URL equals {@code o}. URLs are equal if they have
    862      * the same protocol, host, port, file, and reference.
    863      *
    864      * <h3>Network I/O Warning</h3>
    865      * <p>Some implementations of URL.equals() resolve host names over the
    866      * network. This is problematic:
    867      * <ul>
    868      * <li><strong>The network may be slow.</strong> Many classes, including
    869      * core collections like {@link java.util.Map Map} and {@link java.util.Set
    870      * Set} expect that {@code equals} and {@code hashCode} will return quickly.
    871      * By violating this assumption, this method posed potential performance
    872      * problems.
    873      * <li><strong>Equal IP addresses do not imply equal content.</strong>
    874      * Virtual hosting permits unrelated sites to share an IP address. This
    875      * method could report two otherwise unrelated URLs to be equal because
    876      * they're hosted on the same server.</li>
    877      * <li><strong>The network may not be available.</strong> Two URLs could be
    878      * equal when a network is available and unequal otherwise.</li>
    879      * <li><strong>The network may change.</strong> The IP address for a given
    880      * host name varies by network and over time. This is problematic for mobile
    881      * devices. Two URLs could be equal on some networks and unequal on
    882      * others.</li>
    883      * </ul>
    884      * <p>This problem is fixed in Android 4.0 (Ice Cream Sandwich). In that
    885      * release, URLs are only equal if their host names are equal (ignoring
    886      * case).
    887      *
    888      * @param   obj   the URL to compare against.
    889      * @return  {@code true} if the objects are the same;
    890      *          {@code false} otherwise.
    891      */
    892     public boolean equals(Object obj) {
    893         if (!(obj instanceof URL))
    894             return false;
    895         URL u2 = (URL)obj;
    896 
    897         return handler.equals(this, u2);
    898     }
    899 
    900     /**
    901      * Creates an integer suitable for hash table indexing.<p>
    902      *
    903      * The hash code is based upon all the URL components relevant for URL
    904      * comparison. As such, this operation is a blocking operation.<p>
    905      *
    906      * @return  a hash code for this {@code URL}.
    907      */
    908     public synchronized int hashCode() {
    909         if (hashCode != -1)
    910             return hashCode;
    911 
    912         hashCode = handler.hashCode(this);
    913         return hashCode;
    914     }
    915 
    916     /**
    917      * Compares two URLs, excluding the fragment component.<p>
    918      *
    919      * Returns {@code true} if this {@code URL} and the
    920      * {@code other} argument are equal without taking the
    921      * fragment component into consideration.
    922      *
    923      * @param   other   the {@code URL} to compare against.
    924      * @return  {@code true} if they reference the same remote object;
    925      *          {@code false} otherwise.
    926      */
    927     public boolean sameFile(URL other) {
    928         return handler.sameFile(this, other);
    929     }
    930 
    931     /**
    932      * Constructs a string representation of this {@code URL}. The
    933      * string is created by calling the {@code toExternalForm}
    934      * method of the stream protocol handler for this object.
    935      *
    936      * @return  a string representation of this object.
    937      * @see     java.net.URL#URL(java.lang.String, java.lang.String, int,
    938      *                  java.lang.String)
    939      * @see     java.net.URLStreamHandler#toExternalForm(java.net.URL)
    940      */
    941     public String toString() {
    942         return toExternalForm();
    943     }
    944 
    945     /**
    946      * Constructs a string representation of this {@code URL}. The
    947      * string is created by calling the {@code toExternalForm}
    948      * method of the stream protocol handler for this object.
    949      *
    950      * @return  a string representation of this object.
    951      * @see     java.net.URL#URL(java.lang.String, java.lang.String,
    952      *                  int, java.lang.String)
    953      * @see     java.net.URLStreamHandler#toExternalForm(java.net.URL)
    954      */
    955     public String toExternalForm() {
    956         return handler.toExternalForm(this);
    957     }
    958 
    959     /**
    960      * Returns a {@link java.net.URI} equivalent to this URL.
    961      * This method functions in the same way as {@code new URI (this.toString())}.
    962      * <p>Note, any URL instance that complies with RFC 2396 can be converted
    963      * to a URI. However, some URLs that are not strictly in compliance
    964      * can not be converted to a URI.
    965      *
    966      * @exception URISyntaxException if this URL is not formatted strictly according to
    967      *            to RFC2396 and cannot be converted to a URI.
    968      *
    969      * @return    a URI instance equivalent to this URL.
    970      * @since 1.5
    971      */
    972     public URI toURI() throws URISyntaxException {
    973         return new URI (toString());
    974     }
    975 
    976     /**
    977      * Returns a {@link java.net.URLConnection URLConnection} instance that
    978      * represents a connection to the remote object referred to by the
    979      * {@code URL}.
    980      *
    981      * <P>A new instance of {@linkplain java.net.URLConnection URLConnection} is
    982      * created every time when invoking the
    983      * {@linkplain java.net.URLStreamHandler#openConnection(URL)
    984      * URLStreamHandler.openConnection(URL)} method of the protocol handler for
    985      * this URL.</P>
    986      *
    987      * <P>It should be noted that a URLConnection instance does not establish
    988      * the actual network connection on creation. This will happen only when
    989      * calling {@linkplain java.net.URLConnection#connect() URLConnection.connect()}.</P>
    990      *
    991      * <P>If for the URL's protocol (such as HTTP or JAR), there
    992      * exists a public, specialized URLConnection subclass belonging
    993      * to one of the following packages or one of their subpackages:
    994      * java.lang, java.io, java.util, java.net, the connection
    995      * returned will be of that subclass. For example, for HTTP an
    996      * HttpURLConnection will be returned, and for JAR a
    997      * JarURLConnection will be returned.</P>
    998      *
    999      * @return     a {@link java.net.URLConnection URLConnection} linking
   1000      *             to the URL.
   1001      * @exception  IOException  if an I/O exception occurs.
   1002      * @see        java.net.URL#URL(java.lang.String, java.lang.String,
   1003      *             int, java.lang.String)
   1004      */
   1005     public URLConnection openConnection() throws java.io.IOException {
   1006         return handler.openConnection(this);
   1007     }
   1008 
   1009     /**
   1010      * Same as {@link #openConnection()}, except that the connection will be
   1011      * made through the specified proxy; Protocol handlers that do not
   1012      * support proxing will ignore the proxy parameter and make a
   1013      * normal connection.
   1014      *
   1015      * Invoking this method preempts the system's default ProxySelector
   1016      * settings.
   1017      *
   1018      * @param      proxy the Proxy through which this connection
   1019      *             will be made. If direct connection is desired,
   1020      *             Proxy.NO_PROXY should be specified.
   1021      * @return     a {@code URLConnection} to the URL.
   1022      * @exception  IOException  if an I/O exception occurs.
   1023      * @exception  SecurityException if a security manager is present
   1024      *             and the caller doesn't have permission to connect
   1025      *             to the proxy.
   1026      * @exception  IllegalArgumentException will be thrown if proxy is null,
   1027      *             or proxy has the wrong type
   1028      * @exception  UnsupportedOperationException if the subclass that
   1029      *             implements the protocol handler doesn't support
   1030      *             this method.
   1031      * @see        java.net.URL#URL(java.lang.String, java.lang.String,
   1032      *             int, java.lang.String)
   1033      * @see        java.net.URLConnection
   1034      * @see        java.net.URLStreamHandler#openConnection(java.net.URL,
   1035      *             java.net.Proxy)
   1036      * @since      1.5
   1037      */
   1038     public URLConnection openConnection(Proxy proxy)
   1039         throws java.io.IOException {
   1040         if (proxy == null) {
   1041             throw new IllegalArgumentException("proxy can not be null");
   1042         }
   1043 
   1044         // Create a copy of Proxy as a security measure
   1045         Proxy p = proxy == Proxy.NO_PROXY ? Proxy.NO_PROXY : sun.net.ApplicationProxy.create(proxy);
   1046         SecurityManager sm = System.getSecurityManager();
   1047         if (p.type() != Proxy.Type.DIRECT && sm != null) {
   1048             InetSocketAddress epoint = (InetSocketAddress) p.address();
   1049             if (epoint.isUnresolved())
   1050                 sm.checkConnect(epoint.getHostName(), epoint.getPort());
   1051             else
   1052                 sm.checkConnect(epoint.getAddress().getHostAddress(),
   1053                                 epoint.getPort());
   1054         }
   1055         return handler.openConnection(this, p);
   1056     }
   1057 
   1058     /**
   1059      * Opens a connection to this {@code URL} and returns an
   1060      * {@code InputStream} for reading from that connection. This
   1061      * method is a shorthand for:
   1062      * <blockquote><pre>
   1063      *     openConnection().getInputStream()
   1064      * </pre></blockquote>
   1065      *
   1066      * @return     an input stream for reading from the URL connection.
   1067      * @exception  IOException  if an I/O exception occurs.
   1068      * @see        java.net.URL#openConnection()
   1069      * @see        java.net.URLConnection#getInputStream()
   1070      */
   1071     public final InputStream openStream() throws java.io.IOException {
   1072         return openConnection().getInputStream();
   1073     }
   1074 
   1075     /**
   1076      * Gets the contents of this URL. This method is a shorthand for:
   1077      * <blockquote><pre>
   1078      *     openConnection().getContent()
   1079      * </pre></blockquote>
   1080      *
   1081      * @return     the contents of this URL.
   1082      * @exception  IOException  if an I/O exception occurs.
   1083      * @see        java.net.URLConnection#getContent()
   1084      */
   1085     public final Object getContent() throws java.io.IOException {
   1086         return openConnection().getContent();
   1087     }
   1088 
   1089     /**
   1090      * Gets the contents of this URL. This method is a shorthand for:
   1091      * <blockquote><pre>
   1092      *     openConnection().getContent(Class[])
   1093      * </pre></blockquote>
   1094      *
   1095      * @param classes an array of Java types
   1096      * @return     the content object of this URL that is the first match of
   1097      *               the types specified in the classes array.
   1098      *               null if none of the requested types are supported.
   1099      * @exception  IOException  if an I/O exception occurs.
   1100      * @see        java.net.URLConnection#getContent(Class[])
   1101      * @since 1.3
   1102      */
   1103     public final Object getContent(Class[] classes)
   1104     throws java.io.IOException {
   1105         return openConnection().getContent(classes);
   1106     }
   1107 
   1108     /**
   1109      * The URLStreamHandler factory.
   1110      */
   1111     static URLStreamHandlerFactory factory;
   1112 
   1113     /**
   1114      * Sets an application's {@code URLStreamHandlerFactory}.
   1115      * This method can be called at most once in a given Java Virtual
   1116      * Machine.
   1117      *
   1118      *<p> The {@code URLStreamHandlerFactory} instance is used to
   1119      *construct a stream protocol handler from a protocol name.
   1120      *
   1121      * <p> If there is a security manager, this method first calls
   1122      * the security manager's {@code checkSetFactory} method
   1123      * to ensure the operation is allowed.
   1124      * This could result in a SecurityException.
   1125      *
   1126      * @param      fac   the desired factory.
   1127      * @exception  Error  if the application has already set a factory.
   1128      * @exception  SecurityException  if a security manager exists and its
   1129      *             {@code checkSetFactory} method doesn't allow
   1130      *             the operation.
   1131      * @see        java.net.URL#URL(java.lang.String, java.lang.String,
   1132      *             int, java.lang.String)
   1133      * @see        java.net.URLStreamHandlerFactory
   1134      * @see        SecurityManager#checkSetFactory
   1135      */
   1136     public static void setURLStreamHandlerFactory(URLStreamHandlerFactory fac) {
   1137         synchronized (streamHandlerLock) {
   1138             if (factory != null) {
   1139                 throw new Error("factory already defined");
   1140             }
   1141             SecurityManager security = System.getSecurityManager();
   1142             if (security != null) {
   1143                 security.checkSetFactory();
   1144             }
   1145             handlers.clear();
   1146             factory = fac;
   1147         }
   1148     }
   1149 
   1150     /**
   1151      * A table of protocol handlers.
   1152      */
   1153     static Hashtable<String,URLStreamHandler> handlers = new Hashtable<>();
   1154     private static Object streamHandlerLock = new Object();
   1155 
   1156     /**
   1157      * Returns the Stream Handler.
   1158      * @param protocol the protocol to use
   1159      */
   1160     static URLStreamHandler getURLStreamHandler(String protocol) {
   1161 
   1162         URLStreamHandler handler = handlers.get(protocol);
   1163         if (handler == null) {
   1164 
   1165             boolean checkedWithFactory = false;
   1166 
   1167             // Use the factory (if any)
   1168             if (factory != null) {
   1169                 handler = factory.createURLStreamHandler(protocol);
   1170                 checkedWithFactory = true;
   1171             }
   1172 
   1173             // Try java protocol handler
   1174             if (handler == null) {
   1175                 // Android-changed: Android doesn't need AccessController.
   1176                 // Remove unnecessary use of reflection for sun classes
   1177                 /*
   1178                 packagePrefixList
   1179                     = java.security.AccessController.doPrivileged(
   1180                     new sun.security.action.GetPropertyAction(
   1181                         protocolPathProp,""));
   1182                 if (packagePrefixList != "") {
   1183                     packagePrefixList += "|";
   1184                 }
   1185 
   1186                 // REMIND: decide whether to allow the "null" class prefix
   1187                 // or not.
   1188                 packagePrefixList += "sun.net.www.protocol";
   1189                  */
   1190                 final String packagePrefixList = System.getProperty(protocolPathProp,"");
   1191 
   1192                 StringTokenizer packagePrefixIter =
   1193                     new StringTokenizer(packagePrefixList, "|");
   1194 
   1195                 while (handler == null &&
   1196                        packagePrefixIter.hasMoreTokens()) {
   1197 
   1198                     String packagePrefix =
   1199                       packagePrefixIter.nextToken().trim();
   1200                     try {
   1201                         String clsName = packagePrefix + "." + protocol +
   1202                           ".Handler";
   1203                         Class<?> cls = null;
   1204                         try {
   1205                             ClassLoader cl = ClassLoader.getSystemClassLoader();
   1206                             // BEGIN Android-changed: Fall back to thread's contextClassLoader.
   1207                             // http://b/25897689
   1208                             cls = Class.forName(clsName, true, cl);
   1209                         } catch (ClassNotFoundException e) {
   1210                             ClassLoader contextLoader = Thread.currentThread().getContextClassLoader();
   1211                             if (contextLoader != null) {
   1212                                 cls = Class.forName(clsName, true, contextLoader);
   1213                             }
   1214                             // END Android-changed: Fall back to thread's contextClassLoader.
   1215                         }
   1216                         if (cls != null) {
   1217                             handler  =
   1218                               (URLStreamHandler)cls.newInstance();
   1219                         }
   1220                     } catch (ReflectiveOperationException ignored) {
   1221                     }
   1222                 }
   1223             }
   1224 
   1225             // BEGIN Android-added: Custom built-in URLStreamHandlers for http, https.
   1226             // Fallback to built-in stream handler.
   1227             if (handler == null) {
   1228                 try {
   1229                     handler = createBuiltinHandler(protocol);
   1230                 } catch (Exception e) {
   1231                     throw new AssertionError(e);
   1232                 }
   1233             }
   1234             // END Android-added: Custom built-in URLStreamHandlers for http, https.
   1235 
   1236             synchronized (streamHandlerLock) {
   1237 
   1238                 URLStreamHandler handler2 = null;
   1239 
   1240                 // Check again with hashtable just in case another
   1241                 // thread created a handler since we last checked
   1242                 handler2 = handlers.get(protocol);
   1243 
   1244                 if (handler2 != null) {
   1245                     return handler2;
   1246                 }
   1247 
   1248                 // Check with factory if another thread set a
   1249                 // factory since our last check
   1250                 if (!checkedWithFactory && factory != null) {
   1251                     handler2 = factory.createURLStreamHandler(protocol);
   1252                 }
   1253 
   1254                 if (handler2 != null) {
   1255                     // The handler from the factory must be given more
   1256                     // importance. Discard the default handler that
   1257                     // this thread created.
   1258                     handler = handler2;
   1259                 }
   1260 
   1261                 // Insert this handler into the hashtable
   1262                 if (handler != null) {
   1263                     handlers.put(protocol, handler);
   1264                 }
   1265 
   1266             }
   1267         }
   1268 
   1269         return handler;
   1270 
   1271     }
   1272 
   1273     // BEGIN Android-added: Custom built-in URLStreamHandlers for http, https.
   1274     /**
   1275      * Returns an instance of the built-in handler for the given protocol, or null if none exists.
   1276      */
   1277     private static URLStreamHandler createBuiltinHandler(String protocol)
   1278             throws ClassNotFoundException, InstantiationException, IllegalAccessException {
   1279         URLStreamHandler handler = null;
   1280         if (protocol.equals("file")) {
   1281             handler = new sun.net.www.protocol.file.Handler();
   1282         } else if (protocol.equals("ftp")) {
   1283             handler = new sun.net.www.protocol.ftp.Handler();
   1284         } else if (protocol.equals("jar")) {
   1285             handler = new sun.net.www.protocol.jar.Handler();
   1286         } else if (protocol.equals("http")) {
   1287             handler = (URLStreamHandler)Class.
   1288                     forName("com.android.okhttp.HttpHandler").newInstance();
   1289         } else if (protocol.equals("https")) {
   1290             handler = (URLStreamHandler)Class.
   1291                     forName("com.android.okhttp.HttpsHandler").newInstance();
   1292         }
   1293         return handler;
   1294     }
   1295 
   1296     /** Names of implementation classes returned by {@link #createBuiltinHandler(String)}. */
   1297     private static Set<String> createBuiltinHandlerClassNames() {
   1298         Set<String> result = new HashSet<>();
   1299         // Refer to class names rather than classes to avoid needlessly triggering <clinit>.
   1300         result.add("sun.net.www.protocol.file.Handler");
   1301         result.add("sun.net.www.protocol.ftp.Handler");
   1302         result.add("sun.net.www.protocol.jar.Handler");
   1303         result.add("com.android.okhttp.HttpHandler");
   1304         result.add("com.android.okhttp.HttpsHandler");
   1305         return Collections.unmodifiableSet(result);
   1306     }
   1307     // END Android-added: Custom built-in URLStreamHandlers for http, https.
   1308 
   1309     /**
   1310      * @serialField    protocol String
   1311      *
   1312      * @serialField    host String
   1313      *
   1314      * @serialField    port int
   1315      *
   1316      * @serialField    authority String
   1317      *
   1318      * @serialField    file String
   1319      *
   1320      * @serialField    ref String
   1321      *
   1322      * @serialField    hashCode int
   1323      *
   1324      */
   1325     private static final ObjectStreamField[] serialPersistentFields = {
   1326         new ObjectStreamField("protocol", String.class),
   1327         new ObjectStreamField("host", String.class),
   1328         new ObjectStreamField("port", int.class),
   1329         new ObjectStreamField("authority", String.class),
   1330         new ObjectStreamField("file", String.class),
   1331         new ObjectStreamField("ref", String.class),
   1332     // Android-changed: App compat: hashCode should not be serialized.
   1333     //    new ObjectStreamField("hashCode", int.class), };
   1334     };
   1335 
   1336     /**
   1337      * WriteObject is called to save the state of the URL to an
   1338      * ObjectOutputStream. The handler is not saved since it is
   1339      * specific to this system.
   1340      *
   1341      * @serialData the default write object value. When read back in,
   1342      * the reader must ensure that calling getURLStreamHandler with
   1343      * the protocol variable returns a valid URLStreamHandler and
   1344      * throw an IOException if it does not.
   1345      */
   1346     private synchronized void writeObject(java.io.ObjectOutputStream s)
   1347         throws IOException
   1348     {
   1349         s.defaultWriteObject(); // write the fields
   1350     }
   1351 
   1352     /**
   1353      * readObject is called to restore the state of the URL from the
   1354      * stream.  It reads the components of the URL and finds the local
   1355      * stream handler.
   1356      */
   1357     private synchronized void readObject(java.io.ObjectInputStream s)
   1358             throws IOException, ClassNotFoundException {
   1359         GetField gf = s.readFields();
   1360         String protocol = (String)gf.get("protocol", null);
   1361         if (getURLStreamHandler(protocol) == null) {
   1362             throw new IOException("unknown protocol: " + protocol);
   1363         }
   1364         String host = (String)gf.get("host", null);
   1365         int port = gf.get("port", -1);
   1366         String authority = (String)gf.get("authority", null);
   1367         String file = (String)gf.get("file", null);
   1368         String ref = (String)gf.get("ref", null);
   1369         // Android-changed: App compat: hashCode should not be serialized.
   1370         // int hashCode = gf.get("hashCode", -1);
   1371         final int hashCode = -1;
   1372         if (authority == null
   1373                 && ((host != null && host.length() > 0) || port != -1)) {
   1374             if (host == null)
   1375                 host = "";
   1376             authority = (port == -1) ? host : host + ":" + port;
   1377         }
   1378         tempState = new UrlDeserializedState(protocol, host, port, authority,
   1379                file, ref, hashCode);
   1380     }
   1381 
   1382     /**
   1383      * Replaces the de-serialized object with an URL object.
   1384      *
   1385      * @return a newly created object from the deserialzed state.
   1386      *
   1387      * @throws ObjectStreamException if a new object replacing this
   1388      * object could not be created
   1389      */
   1390 
   1391    private Object readResolve() throws ObjectStreamException {
   1392 
   1393         URLStreamHandler handler = null;
   1394         // already been checked in readObject
   1395         handler = getURLStreamHandler(tempState.getProtocol());
   1396 
   1397         URL replacementURL = null;
   1398         if (isBuiltinStreamHandler(handler.getClass().getName())) {
   1399             replacementURL = fabricateNewURL();
   1400         } else {
   1401             replacementURL = setDeserializedFields(handler);
   1402         }
   1403         return replacementURL;
   1404     }
   1405 
   1406     private URL setDeserializedFields(URLStreamHandler handler) {
   1407         URL replacementURL;
   1408         String userInfo = null;
   1409         String protocol = tempState.getProtocol();
   1410         String host = tempState.getHost();
   1411         int port = tempState.getPort();
   1412         String authority = tempState.getAuthority();
   1413         String file = tempState.getFile();
   1414         String ref = tempState.getRef();
   1415         int hashCode = tempState.getHashCode();
   1416 
   1417 
   1418         // Construct authority part
   1419         if (authority == null
   1420             && ((host != null && host.length() > 0) || port != -1)) {
   1421             if (host == null)
   1422                 host = "";
   1423             authority = (port == -1) ? host : host + ":" + port;
   1424 
   1425             // Handle hosts with userInfo in them
   1426             int at = host.lastIndexOf('@');
   1427             if (at != -1) {
   1428                 userInfo = host.substring(0, at);
   1429                 host = host.substring(at+1);
   1430             }
   1431         } else if (authority != null) {
   1432             // Construct user info part
   1433             int ind = authority.indexOf('@');
   1434             if (ind != -1)
   1435                 userInfo = authority.substring(0, ind);
   1436         }
   1437 
   1438         // Construct path and query part
   1439         String path = null;
   1440         String query = null;
   1441         if (file != null) {
   1442             // Fix: only do this if hierarchical?
   1443             int q = file.lastIndexOf('?');
   1444             if (q != -1) {
   1445                 query = file.substring(q+1);
   1446                 path = file.substring(0, q);
   1447             } else
   1448                 path = file;
   1449         }
   1450 
   1451         // Set the object fields.
   1452         this.protocol = protocol;
   1453         this.host = host;
   1454         this.port = port;
   1455         this.file = file;
   1456         this.authority = authority;
   1457         this.ref = ref;
   1458         this.hashCode = hashCode;
   1459         this.handler = handler;
   1460         this.query = query;
   1461         this.path = path;
   1462         this.userInfo = userInfo;
   1463         replacementURL = this;
   1464         return replacementURL;
   1465     }
   1466 
   1467     private URL fabricateNewURL()
   1468                 throws InvalidObjectException {
   1469         // create URL string from deserialized object
   1470         URL replacementURL = null;
   1471         String urlString = tempState.reconstituteUrlString();
   1472 
   1473         try {
   1474             replacementURL = new URL(urlString);
   1475         } catch (MalformedURLException mEx) {
   1476             resetState();
   1477             InvalidObjectException invoEx = new InvalidObjectException(
   1478                     "Malformed URL: " + urlString);
   1479             invoEx.initCause(mEx);
   1480             throw invoEx;
   1481         }
   1482         replacementURL.setSerializedHashCode(tempState.getHashCode());
   1483         resetState();
   1484         return replacementURL;
   1485     }
   1486 
   1487     private boolean isBuiltinStreamHandler(String handlerClassName) {
   1488         // Android-changed: Some built-in handlers (eg. HttpHandler) are not in sun.net.www.protocol.
   1489         // return (handlerClassName.startsWith(BUILTIN_HANDLERS_PREFIX));
   1490         return BUILTIN_HANDLER_CLASS_NAMES.contains(handlerClassName);
   1491     }
   1492 
   1493     private void resetState() {
   1494         this.protocol = null;
   1495         this.host = null;
   1496         this.port = -1;
   1497         this.file = null;
   1498         this.authority = null;
   1499         this.ref = null;
   1500         this.hashCode = -1;
   1501         this.handler = null;
   1502         this.query = null;
   1503         this.path = null;
   1504         this.userInfo = null;
   1505         this.tempState = null;
   1506     }
   1507 
   1508     private void setSerializedHashCode(int hc) {
   1509         this.hashCode = hc;
   1510     }
   1511 }
   1512 
   1513 class Parts {
   1514     String path, query, ref;
   1515 
   1516     // Android-changed: App compat. Prepend '/' if host is null / empty.
   1517     // Parts(String file)
   1518     Parts(String file, String host) {
   1519         int ind = file.indexOf('#');
   1520         ref = ind < 0 ? null: file.substring(ind + 1);
   1521         file = ind < 0 ? file: file.substring(0, ind);
   1522         int q = file.lastIndexOf('?');
   1523         if (q != -1) {
   1524             query = file.substring(q+1);
   1525             path = file.substring(0, q);
   1526         } else {
   1527             path = file;
   1528         }
   1529         // BEGIN Android-changed: App compat. Prepend '/' if host is null / empty.
   1530         if (path != null && path.length() > 0 && path.charAt(0) != '/' &&
   1531             host != null && !host.isEmpty()) {
   1532             path = '/' + path;
   1533         }
   1534         // END Android-changed: App compat. Prepend '/' if host is null / empty.
   1535     }
   1536 
   1537     String getPath() {
   1538         return path;
   1539     }
   1540 
   1541     String getQuery() {
   1542         return query;
   1543     }
   1544 
   1545     String getRef() {
   1546         return ref;
   1547     }
   1548 }
   1549 
   1550 final class UrlDeserializedState {
   1551     private final String protocol;
   1552     private final String host;
   1553     private final int port;
   1554     private final String authority;
   1555     private final String file;
   1556     private final String ref;
   1557     private final int hashCode;
   1558 
   1559     public UrlDeserializedState(String protocol,
   1560                                 String host, int port,
   1561                                 String authority, String file,
   1562                                 String ref, int hashCode) {
   1563         this.protocol = protocol;
   1564         this.host = host;
   1565         this.port = port;
   1566         this.authority = authority;
   1567         this.file = file;
   1568         this.ref = ref;
   1569         this.hashCode = hashCode;
   1570     }
   1571 
   1572     String getProtocol() {
   1573         return protocol;
   1574     }
   1575 
   1576     String getHost() {
   1577         return host;
   1578     }
   1579 
   1580     String getAuthority () {
   1581         return authority;
   1582     }
   1583 
   1584     int getPort() {
   1585         return port;
   1586     }
   1587 
   1588     String getFile () {
   1589         return file;
   1590     }
   1591 
   1592     String getRef () {
   1593         return ref;
   1594     }
   1595 
   1596     int getHashCode () {
   1597         return hashCode;
   1598     }
   1599 
   1600     String reconstituteUrlString() {
   1601 
   1602         // pre-compute length of StringBuilder
   1603         int len = protocol.length() + 1;
   1604         if (authority != null && authority.length() > 0)
   1605             len += 2 + authority.length();
   1606         if (file != null) {
   1607             len += file.length();
   1608         }
   1609         if (ref != null)
   1610             len += 1 + ref.length();
   1611         StringBuilder result = new StringBuilder(len);
   1612         result.append(protocol);
   1613         result.append(":");
   1614         if (authority != null && authority.length() > 0) {
   1615             result.append("//");
   1616             result.append(authority);
   1617         }
   1618         if (file != null) {
   1619             result.append(file);
   1620         }
   1621         if (ref != null) {
   1622             result.append("#");
   1623             result.append(ref);
   1624         }
   1625         return result.toString();
   1626     }
   1627 }
   1628