1/* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18package java.net; 19 20import java.io.IOException; 21import java.io.InputStream; 22import java.io.OutputStream; 23import java.util.Collections; 24import java.util.Date; 25import java.util.Hashtable; 26import java.util.List; 27import java.util.Locale; 28import java.util.Map; 29 30/** 31 * A connection to a URL for reading or writing. For HTTP connections, see 32 * {@link HttpURLConnection} for documentation of HTTP-specific features. 33 * 34 * <p>For example, to retrieve {@code 35 * ftp://mirror.csclub.uwaterloo.ca/index.html}: <pre> {@code 36 * URL url = new URL("ftp://mirror.csclub.uwaterloo.ca/index.html"); 37 * URLConnection urlConnection = url.openConnection(); 38 * InputStream in = new BufferedInputStream(urlConnection.getInputStream()); 39 * try { 40 * readStream(in); 41 * } finally { 42 * in.close(); 43 * } 44 * }</pre> 45 * 46 * <p>{@code URLConnection} must be configured before it has connected to the 47 * remote resource. Instances of {@code URLConnection} are not reusable: you 48 * must use a different instance for each connection to a resource. 49 * 50 * <h3>Timeouts</h3> 51 * {@code URLConnection} supports two timeouts: a {@link #setConnectTimeout 52 * connect timeout} and a {@link #setReadTimeout read timeout}. By default, 53 * operations never time out. 54 * 55 * <h3>Built-in Protocols</h3> 56 * <ul> 57 * <li><strong>File</strong><br> 58 * Resources from the local file system can be loaded using {@code file:} 59 * URIs. File connections can only be used for input. 60 * <li><strong>FTP</strong><br> 61 * File Transfer Protocol (<a href="http://www.ietf.org/rfc/rfc959.txt">RFC 959</a>) 62 * is supported, but with no public subclass. FTP connections can 63 * be used for input or output but not both. 64 * <p>By default, FTP connections will be made using {@code anonymous} as 65 * the username and the empty string as the password. Specify alternate 66 * usernames and passwords in the URL: {@code 67 * ftp://username:password@host/path}. 68 * <li><strong>HTTP and HTTPS</strong><br> 69 * Refer to the {@link HttpURLConnection} and {@link 70 * javax.net.ssl.HttpsURLConnection HttpsURLConnection} subclasses. 71 * <li><strong>Jar</strong><br> 72 * Refer to the {@link JarURLConnection} subclass. 73 * </ul> 74 * 75 * <h3>Registering Additional Protocols</h3> 76 * Use {@link URL#setURLStreamHandlerFactory} to register handlers for other 77 * protocol types. 78 */ 79public abstract class URLConnection { 80 81 /** 82 * The URL which represents the remote target of this {@code URLConnection}. 83 */ 84 protected URL url; 85 86 private String contentType; 87 88 private static boolean defaultAllowUserInteraction; 89 90 private static boolean defaultUseCaches = true; 91 92 ContentHandler defaultHandler = new DefaultContentHandler(); 93 94 private long lastModified = -1; 95 96 /** 97 * The data must be modified more recently than this time in milliseconds 98 * since January 1, 1970, GMT to be transmitted. 99 */ 100 protected long ifModifiedSince; 101 102 /** 103 * Specifies whether the using of caches is enabled or the data has to be 104 * recent for every request. 105 */ 106 protected boolean useCaches = defaultUseCaches; 107 108 /** 109 * Specifies whether this {@code URLConnection} is already connected to the 110 * remote resource. If this field is set to {@code true} the flags for 111 * setting up the connection are not changeable anymore. 112 */ 113 protected boolean connected; 114 115 /** 116 * Specifies whether this {@code URLConnection} allows sending data. 117 */ 118 protected boolean doOutput; 119 120 /** 121 * Specifies whether this {@code URLConnection} allows receiving data. 122 */ 123 protected boolean doInput = true; 124 125 /** 126 * Unused by Android. This field can be accessed via {@link #getAllowUserInteraction} 127 * and {@link #setAllowUserInteraction}. 128 */ 129 protected boolean allowUserInteraction = defaultAllowUserInteraction; 130 131 private static ContentHandlerFactory contentHandlerFactory; 132 133 private int readTimeout = 0; 134 135 private int connectTimeout = 0; 136 137 /** 138 * Cache for storing content handler 139 */ 140 static Hashtable<String, Object> contentHandlers = new Hashtable<String, Object>(); 141 142 /** 143 * A hashtable that maps the filename extension (key) to a MIME-type 144 * (element) 145 */ 146 private static FileNameMap fileNameMap; 147 148 /** 149 * Creates a new {@code URLConnection} instance pointing to the resource 150 * specified by the given URL. 151 * 152 * @param url 153 * the URL which represents the resource this {@code 154 * URLConnection} will point to. 155 */ 156 protected URLConnection(URL url) { 157 this.url = url; 158 } 159 160 /** 161 * Opens a connection to the resource. This method will <strong>not</strong> 162 * reconnect to a resource after the initial connection has been closed. 163 * 164 * @throws IOException 165 * if an error occurs while connecting to the resource. 166 */ 167 public abstract void connect() throws IOException; 168 169 /** 170 * Returns {@code allowUserInteraction}. Unused by Android. 171 */ 172 public boolean getAllowUserInteraction() { 173 return allowUserInteraction; 174 } 175 176 /** 177 * Returns an object representing the content of the resource this {@code 178 * URLConnection} is connected to. First, it attempts to get the content 179 * type from the method {@code getContentType()} which looks at the response 180 * header field "Content-Type". If none is found it will guess the content 181 * type from the filename extension. If that fails the stream itself will be 182 * used to guess the content type. 183 * 184 * @return the content representing object. 185 * @throws IOException 186 * if an error occurs obtaining the content. 187 */ 188 public Object getContent() throws java.io.IOException { 189 if (!connected) { 190 connect(); 191 } 192 193 if ((contentType = getContentType()) == null) { 194 if ((contentType = guessContentTypeFromName(url.getFile())) == null) { 195 contentType = guessContentTypeFromStream(getInputStream()); 196 } 197 } 198 if (contentType != null) { 199 return getContentHandler(contentType).getContent(this); 200 } 201 return null; 202 } 203 204 /** 205 * Returns an object representing the content of the resource this {@code 206 * URLConnection} is connected to. First, it attempts to get the content 207 * type from the method {@code getContentType()} which looks at the response 208 * header field "Content-Type". If none is found it will guess the content 209 * type from the filename extension. If that fails the stream itself will be 210 * used to guess the content type. The content type must match with one of 211 * the list {@code types}. 212 * 213 * @param types 214 * the list of acceptable content types. 215 * @return the content representing object or {@code null} if the content 216 * type does not match with one of the specified types. 217 * @throws IOException 218 * if an error occurs obtaining the content. 219 */ 220 // Param is not generic in spec 221 @SuppressWarnings("unchecked") 222 public Object getContent(Class[] types) throws IOException { 223 if (!connected) { 224 connect(); 225 } 226 227 if ((contentType = getContentType()) == null) { 228 if ((contentType = guessContentTypeFromName(url.getFile())) == null) { 229 contentType = guessContentTypeFromStream(getInputStream()); 230 } 231 } 232 if (contentType != null) { 233 return getContentHandler(contentType).getContent(this, types); 234 } 235 return null; 236 } 237 238 /** 239 * Returns the content encoding type specified by the response header field 240 * {@code content-encoding} or {@code null} if this field is not set. 241 * 242 * @return the value of the response header field {@code content-encoding}. 243 */ 244 public String getContentEncoding() { 245 return getHeaderField("Content-Encoding"); 246 } 247 248 /** 249 * Returns the specific ContentHandler that will handle the type {@code 250 * contentType}. 251 * 252 * @param type 253 * The type that needs to be handled 254 * @return An instance of the Content Handler 255 */ 256 private ContentHandler getContentHandler(String type) throws IOException { 257 // Replace all non-alphanumeric character by '_' 258 final String typeString = parseTypeString(type.replace('/', '.')); 259 260 // if there's a cached content handler, use it 261 Object cHandler = contentHandlers.get(type); 262 if (cHandler != null) { 263 return (ContentHandler) cHandler; 264 } 265 266 if (contentHandlerFactory != null) { 267 cHandler = contentHandlerFactory.createContentHandler(type); 268 contentHandlers.put(type, cHandler); 269 return (ContentHandler) cHandler; 270 } 271 272 // search through the package list for the right class for the Content Type 273 String packageList = System.getProperty("java.content.handler.pkgs"); 274 if (packageList != null) { 275 for (String packageName : packageList.split("\\|")) { 276 String className = packageName + "." + typeString; 277 try { 278 Class<?> klass = Class.forName(className, true, ClassLoader.getSystemClassLoader()); 279 cHandler = klass.newInstance(); 280 } catch (ClassNotFoundException e) { 281 } catch (IllegalAccessException e) { 282 } catch (InstantiationException e) { 283 } 284 } 285 } 286 287 if (cHandler == null) { 288 try { 289 // Try looking up AWT image content handlers 290 String className = "org.apache.harmony.awt.www.content." + typeString; 291 cHandler = Class.forName(className).newInstance(); 292 } catch (ClassNotFoundException e) { 293 } catch (IllegalAccessException e) { 294 } catch (InstantiationException e) { 295 } 296 } 297 if (cHandler != null) { 298 if (!(cHandler instanceof ContentHandler)) { 299 throw new UnknownServiceException(); 300 } 301 contentHandlers.put(type, cHandler); // if we got the handler, 302 // cache it for next time 303 return (ContentHandler) cHandler; 304 } 305 306 return defaultHandler; 307 } 308 309 /** 310 * Returns the content length in bytes specified by the response header field 311 * {@code content-length} or {@code -1} if this field is not set or cannot be represented as an 312 * {@code int}. 313 */ 314 public int getContentLength() { 315 return getHeaderFieldInt("Content-Length", -1); 316 } 317 318 /** 319 * Returns the MIME-type of the content specified by the response header field 320 * {@code content-type} or {@code null} if type is unknown. 321 * 322 * @return the value of the response header field {@code content-type}. 323 */ 324 public String getContentType() { 325 return getHeaderField("Content-Type"); 326 } 327 328 /** 329 * Returns the timestamp when this response has been sent as a date in 330 * milliseconds since January 1, 1970 GMT or {@code 0} if this timestamp is 331 * unknown. 332 * 333 * @return the sending timestamp of the current response. 334 */ 335 public long getDate() { 336 return getHeaderFieldDate("Date", 0); 337 } 338 339 /** 340 * Returns the default value of {@code allowUserInteraction}. Unused by Android. 341 */ 342 public static boolean getDefaultAllowUserInteraction() { 343 return defaultAllowUserInteraction; 344 } 345 346 /** 347 * Returns null. 348 * 349 * @deprecated Use {@link #getRequestProperty} instead. 350 */ 351 @Deprecated 352 public static String getDefaultRequestProperty(String field) { 353 return null; 354 } 355 356 /** 357 * Returns the default setting whether this connection allows using caches. 358 * 359 * @return the value of the default setting {@code defaultUseCaches}. 360 * @see #useCaches 361 */ 362 public boolean getDefaultUseCaches() { 363 return defaultUseCaches; 364 } 365 366 /** 367 * Returns the value of the option {@code doInput} which specifies whether this 368 * connection allows to receive data. 369 * 370 * @return {@code true} if this connection allows input, {@code false} 371 * otherwise. 372 * @see #doInput 373 */ 374 public boolean getDoInput() { 375 return doInput; 376 } 377 378 /** 379 * Returns the value of the option {@code doOutput} which specifies whether 380 * this connection allows to send data. 381 * 382 * @return {@code true} if this connection allows output, {@code false} 383 * otherwise. 384 * @see #doOutput 385 */ 386 public boolean getDoOutput() { 387 return doOutput; 388 } 389 390 /** 391 * Returns the timestamp when this response will be expired in milliseconds 392 * since January 1, 1970 GMT or {@code 0} if this timestamp is unknown. 393 * 394 * @return the value of the response header field {@code expires}. 395 */ 396 public long getExpiration() { 397 return getHeaderFieldDate("Expires", 0); 398 } 399 400 /** 401 * Returns the table which is used by all {@code URLConnection} instances to 402 * determine the MIME-type according to a file extension. 403 * 404 * @return the file name map to determine the MIME-type. 405 */ 406 public static FileNameMap getFileNameMap() { 407 synchronized (URLConnection.class) { 408 if (fileNameMap == null) { 409 fileNameMap = new DefaultFileNameMap(); 410 } 411 return fileNameMap; 412 } 413 } 414 415 /** 416 * Returns the header value at the field position {@code pos} or {@code null} 417 * if the header has fewer than {@code pos} fields. The base 418 * implementation of this method returns always {@code null}. 419 * 420 * <p>Some implementations (notably {@code HttpURLConnection}) include a mapping 421 * for the null key; in HTTP's case, this maps to the HTTP status line and is 422 * treated as being at position 0 when indexing into the header fields. 423 * 424 * @param pos 425 * the field position of the response header. 426 * @return the value of the field at position {@code pos}. 427 */ 428 public String getHeaderField(int pos) { 429 return null; 430 } 431 432 /** 433 * Returns an unmodifiable map of the response-header fields and values. The 434 * response-header field names are the key values of the map. The map values 435 * are lists of header field values associated with a particular key name. 436 * 437 * <p>Some implementations (notably {@code HttpURLConnection}) include a mapping 438 * for the null key; in HTTP's case, this maps to the HTTP status line and is 439 * treated as being at position 0 when indexing into the header fields. 440 * 441 * @return the response-header representing generic map. 442 * @since 1.4 443 */ 444 public Map<String, List<String>> getHeaderFields() { 445 return Collections.emptyMap(); 446 } 447 448 /** 449 * Returns an unmodifiable map of general request properties used by this 450 * connection. The request property names are the key values of the map. The 451 * map values are lists of property values of the corresponding key name. 452 * 453 * @return the request-property representing generic map. 454 * @since 1.4 455 */ 456 public Map<String, List<String>> getRequestProperties() { 457 checkNotConnected(); 458 return Collections.emptyMap(); 459 } 460 461 private void checkNotConnected() { 462 if (connected) { 463 throw new IllegalStateException("Already connected"); 464 } 465 } 466 467 /** 468 * Adds the given property to the request header. Existing properties with 469 * the same name will not be overwritten by this method. 470 * 471 * @param field 472 * the request property field name to add. 473 * @param newValue 474 * the value of the property which is to add. 475 * @throws IllegalStateException 476 * if the connection has been already established. 477 * @throws NullPointerException 478 * if the property name is {@code null}. 479 * @since 1.4 480 */ 481 public void addRequestProperty(String field, String newValue) { 482 checkNotConnected(); 483 if (field == null) { 484 throw new NullPointerException("field == null"); 485 } 486 } 487 488 /** 489 * Returns the value of the header field specified by {@code key} or {@code 490 * null} if there is no field with this name. The base implementation of 491 * this method returns always {@code null}. 492 * 493 * <p>Some implementations (notably {@code HttpURLConnection}) include a mapping 494 * for the null key; in HTTP's case, this maps to the HTTP status line and is 495 * treated as being at position 0 when indexing into the header fields. 496 * 497 * @param key 498 * the name of the header field. 499 * @return the value of the header field. 500 */ 501 public String getHeaderField(String key) { 502 return null; 503 } 504 505 /** 506 * Returns the specified header value as a date in milliseconds since January 507 * 1, 1970 GMT. Returns the {@code defaultValue} if no such header field 508 * could be found. 509 * 510 * @param field 511 * the header field name whose value is needed. 512 * @param defaultValue 513 * the default value if no field has been found. 514 * @return the value of the specified header field as a date in 515 * milliseconds. 516 */ 517 @SuppressWarnings("deprecation") 518 public long getHeaderFieldDate(String field, long defaultValue) { 519 String date = getHeaderField(field); 520 if (date == null) { 521 return defaultValue; 522 } 523 try { 524 return Date.parse(date); // TODO: use HttpDate.parse() 525 } catch (Exception e) { 526 return defaultValue; 527 } 528 } 529 530 /** 531 * Returns the specified header value as a number. Returns the {@code 532 * defaultValue} if no such header field could be found or the value could 533 * not be parsed as an {@code int}. 534 * 535 * @param field 536 * the header field name whose value is needed. 537 * @param defaultValue 538 * the default value if no field has been found. 539 * @return the value of the specified header field as a number. 540 */ 541 public int getHeaderFieldInt(String field, int defaultValue) { 542 try { 543 return Integer.parseInt(getHeaderField(field)); 544 } catch (NumberFormatException e) { 545 return defaultValue; 546 } 547 } 548 549 /** 550 * Returns the name of the header field at the given position {@code posn} or 551 * {@code null} if there are fewer than {@code posn} fields. The base 552 * implementation of this method returns always {@code null}. 553 * 554 * <p>Some implementations (notably {@code HttpURLConnection}) include a mapping 555 * for the null key; in HTTP's case, this maps to the HTTP status line and is 556 * treated as being at position 0 when indexing into the header fields. 557 * 558 * @param posn 559 * the position of the header field which has to be returned. 560 * @return the header field name at the given position. 561 */ 562 public String getHeaderFieldKey(int posn) { 563 return null; 564 } 565 566 /** 567 * Returns the point of time since when the data must be modified to be 568 * transmitted. Some protocols transmit data only if it has been modified 569 * more recently than a particular time. 570 * 571 * @return the time in milliseconds since January 1, 1970 GMT. 572 * @see #ifModifiedSince 573 */ 574 public long getIfModifiedSince() { 575 return ifModifiedSince; 576 } 577 578 /** 579 * Returns an {@code InputStream} for reading data from the resource pointed by 580 * this {@code URLConnection}. It throws an UnknownServiceException by 581 * default. This method must be overridden by its subclasses. 582 * 583 * @return the InputStream to read data from. 584 * @throws IOException 585 * if no InputStream could be created. 586 */ 587 public InputStream getInputStream() throws IOException { 588 throw new UnknownServiceException("Does not support writing to the input stream"); 589 } 590 591 /** 592 * Returns the value of the response header field {@code last-modified} or 593 * {@code 0} if this value is not set. 594 * 595 * @return the value of the {@code last-modified} header field. 596 */ 597 public long getLastModified() { 598 if (lastModified != -1) { 599 return lastModified; 600 } 601 return lastModified = getHeaderFieldDate("Last-Modified", 0); 602 } 603 604 /** 605 * Returns an {@code OutputStream} for writing data to this {@code 606 * URLConnection}. It throws an {@code UnknownServiceException} by default. 607 * This method must be overridden by its subclasses. 608 * 609 * @return the OutputStream to write data. 610 * @throws IOException 611 * if no OutputStream could be created. 612 */ 613 public OutputStream getOutputStream() throws IOException { 614 throw new UnknownServiceException("Does not support writing to the output stream"); 615 } 616 617 /** 618 * Returns a {@code Permission} object representing all needed permissions to 619 * open this connection. The returned permission object depends on the state 620 * of the connection and will be {@code null} if no permissions are 621 * necessary. By default, this method returns {@code AllPermission}. 622 * Subclasses should overwrite this method to return an appropriate 623 * permission object. 624 * 625 * @return the permission object representing the needed permissions to open 626 * this connection. 627 * @throws IOException 628 * if an I/O error occurs while creating the permission object. 629 */ 630 public java.security.Permission getPermission() throws IOException { 631 return new java.security.AllPermission(); 632 } 633 634 /** 635 * Returns the value of the request header property specified by {code field} 636 * or {@code null} if there is no field with this name. The base 637 * implementation of this method returns always {@code null}. 638 * 639 * @param field 640 * the name of the request header property. 641 * @return the value of the property. 642 * @throws IllegalStateException 643 * if the connection has been already established. 644 */ 645 public String getRequestProperty(String field) { 646 checkNotConnected(); 647 return null; 648 } 649 650 /** 651 * Returns the URL represented by this {@code URLConnection}. 652 * 653 * @return the URL of this connection. 654 */ 655 public URL getURL() { 656 return url; 657 } 658 659 /** 660 * Returns the value of the flag which specifies whether this {@code 661 * URLConnection} allows to use caches. 662 * 663 * @return {@code true} if using caches is allowed, {@code false} otherwise. 664 */ 665 public boolean getUseCaches() { 666 return useCaches; 667 } 668 669 /** 670 * Determines the MIME-type of the given resource {@code url} by resolving 671 * the filename extension with the internal FileNameMap. Any fragment 672 * identifier is removed before processing. 673 * 674 * @param url 675 * the URL with the filename to get the MIME type. 676 * @return the guessed content type or {@code null} if the type could not be 677 * determined. 678 */ 679 public static String guessContentTypeFromName(String url) { 680 return getFileNameMap().getContentTypeFor(url); 681 } 682 683 /** 684 * Determines the MIME-type of the resource represented by the input stream 685 * {@code is} by reading its first few characters. 686 * 687 * @param is 688 * the resource representing input stream to determine the 689 * content type. 690 * @return the guessed content type or {@code null} if the type could not be 691 * determined. 692 * @throws IOException 693 * if an I/O error occurs while reading from the input stream. 694 */ 695 public static String guessContentTypeFromStream(InputStream is) throws IOException { 696 if (!is.markSupported()) { 697 return null; 698 } 699 // Look ahead up to 64 bytes for the longest encoded header 700 is.mark(64); 701 byte[] bytes = new byte[64]; 702 int length = is.read(bytes); 703 is.reset(); 704 705 // If there is no data from the input stream, we can't determine content type. 706 if (length == -1) { 707 return null; 708 } 709 710 // Check for Unicode BOM encoding indicators 711 String encoding = "US-ASCII"; 712 int start = 0; 713 if (length > 1) { 714 if ((bytes[0] == (byte) 0xFF) && (bytes[1] == (byte) 0xFE)) { 715 encoding = "UTF-16LE"; 716 start = 2; 717 length -= length & 1; 718 } 719 if ((bytes[0] == (byte) 0xFE) && (bytes[1] == (byte) 0xFF)) { 720 encoding = "UTF-16BE"; 721 start = 2; 722 length -= length & 1; 723 } 724 if (length > 2) { 725 if ((bytes[0] == (byte) 0xEF) && (bytes[1] == (byte) 0xBB) 726 && (bytes[2] == (byte) 0xBF)) { 727 encoding = "UTF-8"; 728 start = 3; 729 } 730 if (length > 3) { 731 if ((bytes[0] == (byte) 0x00) && (bytes[1] == (byte) 0x00) 732 && (bytes[2] == (byte) 0xFE) 733 && (bytes[3] == (byte) 0xFF)) { 734 encoding = "UTF-32BE"; 735 start = 4; 736 length -= length & 3; 737 } 738 if ((bytes[0] == (byte) 0xFF) && (bytes[1] == (byte) 0xFE) 739 && (bytes[2] == (byte) 0x00) 740 && (bytes[3] == (byte) 0x00)) { 741 encoding = "UTF-32LE"; 742 start = 4; 743 length -= length & 3; 744 } 745 } 746 } 747 } 748 749 String header = new String(bytes, start, length - start, encoding); 750 751 // Check binary types 752 if (header.startsWith("PK")) { 753 return "application/zip"; 754 } 755 if (header.startsWith("GI")) { 756 return "image/gif"; 757 } 758 759 // Check text types 760 String textHeader = header.trim().toUpperCase(Locale.US); 761 if (textHeader.startsWith("<!DOCTYPE HTML") || 762 textHeader.startsWith("<HTML") || 763 textHeader.startsWith("<HEAD") || 764 textHeader.startsWith("<BODY") || 765 textHeader.startsWith("<HEAD")) { 766 return "text/html"; 767 } 768 769 if (textHeader.startsWith("<?XML")) { 770 return "application/xml"; 771 } 772 773 // Give up 774 return null; 775 } 776 777 /** 778 * Performs any necessary string parsing on the input string such as 779 * converting non-alphanumeric character into underscore. 780 * 781 * @param typeString 782 * the parsed string 783 * @return the string to be parsed 784 */ 785 private String parseTypeString(String typeString) { 786 StringBuilder result = new StringBuilder(typeString); 787 for (int i = 0; i < result.length(); i++) { 788 // if non-alphanumeric, replace it with '_' 789 char c = result.charAt(i); 790 if (!(Character.isLetter(c) || Character.isDigit(c) || c == '.')) { 791 result.setCharAt(i, '_'); 792 } 793 } 794 return result.toString(); 795 } 796 797 /** 798 * Sets {@code allowUserInteraction}. Unused by Android. 799 */ 800 public void setAllowUserInteraction(boolean newValue) { 801 checkNotConnected(); 802 this.allowUserInteraction = newValue; 803 } 804 805 /** 806 * Sets the internally used content handler factory. The content factory can 807 * only be set once during the lifetime of the application. 808 * 809 * @param contentFactory 810 * the content factory to be set. 811 * @throws Error 812 * if the factory has been already set. 813 */ 814 public static synchronized void setContentHandlerFactory(ContentHandlerFactory contentFactory) { 815 if (contentHandlerFactory != null) { 816 throw new Error("Factory already set"); 817 } 818 contentHandlerFactory = contentFactory; 819 } 820 821 /** 822 * Sets the default value for {@code allowUserInteraction}. Unused by Android. 823 */ 824 public static void setDefaultAllowUserInteraction(boolean allows) { 825 defaultAllowUserInteraction = allows; 826 } 827 828 /** 829 * Does nothing. 830 * 831 * @deprecated Use {@link URLConnection#setRequestProperty(String, String)} instead. 832 */ 833 @Deprecated 834 public static void setDefaultRequestProperty(String field, String value) { 835 } 836 837 /** 838 * Sets the default value for the flag indicating whether this connection 839 * allows to use caches. Existing {@code URLConnection}s are unaffected. 840 * 841 * @param newValue 842 * the default value of the flag to be used for new connections. 843 * @see #useCaches 844 */ 845 public void setDefaultUseCaches(boolean newValue) { 846 defaultUseCaches = newValue; 847 } 848 849 /** 850 * Sets the flag indicating whether this {@code URLConnection} allows input. 851 * It cannot be set after the connection is established. 852 * 853 * @param newValue 854 * the new value for the flag to be set. 855 * @throws IllegalAccessError 856 * if this method attempts to change the value after the 857 * connection has been already established. 858 * @see #doInput 859 */ 860 public void setDoInput(boolean newValue) { 861 checkNotConnected(); 862 this.doInput = newValue; 863 } 864 865 /** 866 * Sets the flag indicating whether this {@code URLConnection} allows 867 * output. It cannot be set after the connection is established. 868 * 869 * @param newValue 870 * the new value for the flag to be set. 871 * @throws IllegalAccessError 872 * if this method attempts to change the value after the 873 * connection has been already established. 874 * @see #doOutput 875 */ 876 public void setDoOutput(boolean newValue) { 877 checkNotConnected(); 878 this.doOutput = newValue; 879 } 880 881 /** 882 * Sets the internal map which is used by all {@code URLConnection} 883 * instances to determine the MIME-type according to a filename extension. 884 * 885 * @param map 886 * the MIME table to be set. 887 */ 888 public static void setFileNameMap(FileNameMap map) { 889 synchronized (URLConnection.class) { 890 fileNameMap = map; 891 } 892 } 893 894 /** 895 * Sets the point of time since when the data must be modified to be 896 * transmitted. Some protocols transmit data only if it has been modified 897 * more recently than a particular time. The data will be transmitted 898 * regardless of its timestamp if this option is set to {@code 0}. 899 * 900 * @param newValue 901 * the time in milliseconds since January 1, 1970 GMT. 902 * @throws IllegalStateException 903 * if this {@code URLConnection} has already been connected. 904 * @see #ifModifiedSince 905 */ 906 public void setIfModifiedSince(long newValue) { 907 checkNotConnected(); 908 this.ifModifiedSince = newValue; 909 } 910 911 /** 912 * Sets the value of the specified request header field. The value will only 913 * be used by the current {@code URLConnection} instance. This method can 914 * only be called before the connection is established. 915 * 916 * @param field 917 * the request header field to be set. 918 * @param newValue 919 * the new value of the specified property. 920 * @throws IllegalStateException 921 * if the connection has been already established. 922 * @throws NullPointerException 923 * if the parameter {@code field} is {@code null}. 924 */ 925 public void setRequestProperty(String field, String newValue) { 926 checkNotConnected(); 927 if (field == null) { 928 throw new NullPointerException("field == null"); 929 } 930 } 931 932 /** 933 * Sets the flag indicating whether this connection allows to use caches or 934 * not. This method can only be called prior to the connection 935 * establishment. 936 * 937 * @param newValue 938 * the value of the flag to be set. 939 * @throws IllegalStateException 940 * if this method attempts to change the flag after the 941 * connection has been established. 942 * @see #useCaches 943 */ 944 public void setUseCaches(boolean newValue) { 945 checkNotConnected(); 946 this.useCaches = newValue; 947 } 948 949 /** 950 * Sets the maximum time in milliseconds to wait while connecting. 951 * Connecting to a server will fail with a {@link SocketTimeoutException} if 952 * the timeout elapses before a connection is established. The default value 953 * of {@code 0} causes us to do a blocking connect. This does not mean we 954 * will never time out, but it probably means you'll get a TCP timeout 955 * after several minutes. 956 * 957 * <p><strong>Warning:</strong> if the hostname resolves to multiple IP 958 * addresses, this client will try each in <a 959 * href="http://www.ietf.org/rfc/rfc3484.txt">RFC 3484</a> order. If 960 * connecting to each of these addresses fails, multiple timeouts will 961 * elapse before the connect attempt throws an exception. Host names that 962 * support both IPv6 and IPv4 always have at least 2 IP addresses. 963 * 964 * @throws IllegalArgumentException if {@code timeoutMillis < 0}. 965 */ 966 public void setConnectTimeout(int timeoutMillis) { 967 if (timeoutMillis < 0) { 968 throw new IllegalArgumentException("timeoutMillis < 0"); 969 } 970 this.connectTimeout = timeoutMillis; 971 } 972 973 /** 974 * Returns the connect timeout in milliseconds. (See {#setConnectTimeout}.) 975 */ 976 public int getConnectTimeout() { 977 return connectTimeout; 978 } 979 980 /** 981 * Sets the maximum time to wait for an input stream read to complete before 982 * giving up. Reading will fail with a {@link SocketTimeoutException} if the 983 * timeout elapses before data becomes available. The default value of 984 * {@code 0} disables read timeouts; read attempts will block indefinitely. 985 * 986 * @param timeoutMillis the read timeout in milliseconds. Non-negative. 987 */ 988 public void setReadTimeout(int timeoutMillis) { 989 if (timeoutMillis < 0) { 990 throw new IllegalArgumentException("timeoutMillis < 0"); 991 } 992 this.readTimeout = timeoutMillis; 993 } 994 995 /** 996 * Returns the read timeout in milliseconds, or {@code 0} if reads never 997 * timeout. 998 */ 999 public int getReadTimeout() { 1000 return readTimeout; 1001 } 1002 1003 /** 1004 * Returns the string representation containing the name of this class and 1005 * the URL. 1006 * 1007 * @return the string representation of this {@code URLConnection} instance. 1008 */ 1009 @Override 1010 public String toString() { 1011 return getClass().getName() + ":" + url.toString(); 1012 } 1013 1014 static class DefaultContentHandler extends java.net.ContentHandler { 1015 @Override 1016 public Object getContent(URLConnection u) throws IOException { 1017 return u.getInputStream(); 1018 } 1019 } 1020} 1021