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      */
URL(String protocol, String host, int port, String file)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      */
URL(String protocol, String host, String file)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      */
URL(String protocol, String host, int port, String file, URLStreamHandler handler)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      */
URL(String spec)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      */
URL(URL context, String spec)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      */
URL(URL context, String spec, URLStreamHandler handler)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      */
isValidProtocol(String protocol)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      */
checkSpecifyHandler(SecurityManager sm)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      */
set(String protocol, String host, int port, String file, String ref)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      */
set(String protocol, String host, int port, String authority, String userInfo, String path, String query, String ref)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      */
getQuery()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      */
getPath()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      */
getUserInfo()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      */
getAuthority()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      */
getPort()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      */
getDefaultPort()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      */
getProtocol()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      */
getHost()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      */
getFile()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      */
getRef()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      */
equals(Object obj)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      */
hashCode()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      */
sameFile(URL other)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      */
toString()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      */
toExternalForm()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      */
toURI()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      */
openConnection()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      */
openConnection(Proxy proxy)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      */
openStream()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      */
getContent()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      */
getContent(Class[] classes)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      */
setURLStreamHandlerFactory(URLStreamHandlerFactory fac)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      */
getURLStreamHandler(String protocol)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      */
createBuiltinHandler(String protocol)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)}. */
createBuiltinHandlerClassNames()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      */
writeObject(java.io.ObjectOutputStream s)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      */
readObject(java.io.ObjectInputStream s)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 
readResolve()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 
setDeserializedFields(URLStreamHandler handler)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 
fabricateNewURL()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 
isBuiltinStreamHandler(String handlerClassName)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 
resetState()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 
setSerializedHashCode(int hc)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)
Parts(String file, String host)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