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