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