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