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