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