FileDocCategorySizeDatePackage
HttpURLConnection.javaAPI DocAndroid 1.5 API18331Wed May 06 22:41:04 BST 2009java.net

HttpURLConnection.java

/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package java.net;

import java.io.IOException;

import org.apache.harmony.luni.util.Msg;

/**
 * This abstract subclass of {@code URLConnection} defines methods for managing
 * HTTP connection according to the description given by RFC 2068.
 * 
 * @see ContentHandler
 * @see URL
 * @see URLConnection
 * @see URLStreamHandler
 * @since Android 1.0
 */
public abstract class HttpURLConnection extends URLConnection {
    @SuppressWarnings("nls")
    private String methodTokens[] = { "GET", "DELETE", "HEAD", "OPTIONS",
            "POST", "PUT", "TRACE" };

   /**
     * The HTTP request method of this {@code HttpURLConnection}. The default
     * value is {@code "GET"}.
     * 
     * @since Android 1.0
     */
    protected String method = "GET"; //$NON-NLS-1$

    /**
     * The status code of the response obtained from the HTTP request. The
     * default value is {@code -1}.
     * <p>
     * <li>1xx: Informational</li>
     * <li>2xx: Success</li>
     * <li>3xx: Relocation/Redirection</li>
     * <li>4xx: Client Error</li>
     * <li>5xx: Server Error</li>
     * </p>
     * 
     * @since Android 1.0
     */
    protected int responseCode = -1;

    /**
     * The HTTP response message which corresponds to the response code.
     * 
     * @since Android 1.0
     */
    protected String responseMessage;

    /**
     * Flag to define whether the protocol will automatically follow redirects
     * or not. The default value is {@code true}.
     * 
     * @since Android 1.0
     */
    protected boolean instanceFollowRedirects = followRedirects;

    private static boolean followRedirects = true;

    /**
     * If the HTTP chunked encoding is enabled this parameter defines the
     * chunk-length. Default value is {@code -1} that means the chunked encoding
     * mode is disabled.
     * 
     * @since Android 1.0
     */
    protected int chunkLength = -1;

    /**
     * If using HTTP fixed-length streaming mode this parameter defines the
     * fixed length of content. Default value is {@code -1} that means the
     * fixed-length streaming mode is disabled.
     * 
     * @since Android 1.0
     */
    protected int fixedContentLength = -1;

    private final static int DEFAULT_CHUNK_LENGTH = 1024;

    // 2XX: generally "OK"
    // 3XX: relocation/redirect
    // 4XX: client error
    // 5XX: server error
    /**
     * Numeric status code, 202: Accepted
     * 
     * @since Android 1.0
     */
    public final static int HTTP_ACCEPTED = 202;

    /**
     * Numeric status code, 502: Bad Gateway
     * 
     * @since Android 1.0
     */
    public final static int HTTP_BAD_GATEWAY = 502;

    /**
     * Numeric status code, 405: Bad Method
     * 
     * @since Android 1.0
     */
    public final static int HTTP_BAD_METHOD = 405;

    /**
     * Numeric status code, 400: Bad Request
     * 
     * @since Android 1.0
     */
    public final static int HTTP_BAD_REQUEST = 400;

    /**
     * Numeric status code, 408: Client Timeout
     * 
     * @since Android 1.0
     */
    public final static int HTTP_CLIENT_TIMEOUT = 408;

    /**
     * Numeric status code, 409: Conflict
     * 
     * @since Android 1.0
     */
    public final static int HTTP_CONFLICT = 409;

    /**
     * Numeric status code, 201: Created
     * 
     * @since Android 1.0
     */
    public final static int HTTP_CREATED = 201;

    /**
     * Numeric status code, 413: Entity too large
     * 
     * @since Android 1.0
     */
    public final static int HTTP_ENTITY_TOO_LARGE = 413;

    /**
     * Numeric status code, 403: Forbidden
     * 
     * @since Android 1.0
     */
    public final static int HTTP_FORBIDDEN = 403;

    /**
     * Numeric status code, 504: Gateway timeout
     * 
     * @since Android 1.0
     */
    public final static int HTTP_GATEWAY_TIMEOUT = 504;

    /**
     * Numeric status code, 410: Gone
     * 
     * @since Android 1.0
     */
    public final static int HTTP_GONE = 410;

    /**
     * Numeric status code, 500: Internal error
     * 
     * @since Android 1.0
     */
    public final static int HTTP_INTERNAL_ERROR = 500;

    /**
     * Numeric status code, 411: Length required
     * 
     * @since Android 1.0
     */
    public final static int HTTP_LENGTH_REQUIRED = 411;

    /**
     * Numeric status code, 301 Moved permanently
     * 
     * @since Android 1.0
     */
    public final static int HTTP_MOVED_PERM = 301;

    /**
     * Numeric status code, 302: Moved temporarily
     * 
     * @since Android 1.0
     */
    public final static int HTTP_MOVED_TEMP = 302;

    /**
     * Numeric status code, 300: Multiple choices
     * 
     * @since Android 1.0
     */
    public final static int HTTP_MULT_CHOICE = 300;

    /**
     * Numeric status code, 204: No content
     * 
     * @since Android 1.0
     */
    public final static int HTTP_NO_CONTENT = 204;

    /**
     * Numeric status code, 406: Not acceptable
     * 
     * @since Android 1.0
     */
    public final static int HTTP_NOT_ACCEPTABLE = 406;

    /**
     * Numeric status code, 203: Not authoritative
     * 
     * @since Android 1.0
     */
    public final static int HTTP_NOT_AUTHORITATIVE = 203;

    /**
     * Numeric status code, 404: Not found
     * 
     * @since Android 1.0
     */
    public final static int HTTP_NOT_FOUND = 404;

    /**
     * Numeric status code, 501: Not implemented
     * 
     * @since Android 1.0
     */
    public final static int HTTP_NOT_IMPLEMENTED = 501;

    /**
     * Numeric status code, 304: Not modified
     * 
     * @since Android 1.0
     */
    public final static int HTTP_NOT_MODIFIED = 304;

    /**
     * Numeric status code, 200: OK
     * 
     * @since Android 1.0
     */
    public final static int HTTP_OK = 200;

    /**
     * Numeric status code, 206: Partial
     * 
     * @since Android 1.0
     */
    public final static int HTTP_PARTIAL = 206;

    /**
     * Numeric status code, 402: Payment required
     * 
     * @since Android 1.0
     */
    public final static int HTTP_PAYMENT_REQUIRED = 402;

    /**
     * Numeric status code, 412: Precondition failed
     * 
     * @since Android 1.0
     */
    public final static int HTTP_PRECON_FAILED = 412;

    /**
     * Numeric status code, 407: Proxy authentication required
     * 
     * @since Android 1.0
     */
    public final static int HTTP_PROXY_AUTH = 407;

    /**
     * Numeric status code, 414: Request too long
     * 
     * @since Android 1.0
     */
    public final static int HTTP_REQ_TOO_LONG = 414;

    /**
     * Numeric status code, 205: Reset
     * 
     * @since Android 1.0
     */
    public final static int HTTP_RESET = 205;

    /**
     * Numeric status code, 303: See other
     * 
     * @since Android 1.0
     */
    public final static int HTTP_SEE_OTHER = 303;

    /**
     * Numeric status code, 500: Internal error
     * 
     * @deprecated Use {@link #HTTP_INTERNAL_ERROR}
     * @since Android 1.0
     */
    @Deprecated
    public final static int HTTP_SERVER_ERROR = 500;

    /**
     * Numeric status code, 305: Use proxy
     * 
     * @since Android 1.0
     */
    public final static int HTTP_USE_PROXY = 305;

    /**
     * Numeric status code, 401: Unauthorized
     * 
     * @since Android 1.0
     */
    public final static int HTTP_UNAUTHORIZED = 401;

    /**
     * Numeric status code, 415: Unsupported type
     * 
     * @since Android 1.0
     */
    public final static int HTTP_UNSUPPORTED_TYPE = 415;

    /**
     * Numeric status code, 503: Unavailable
     * 
     * @since Android 1.0
     */
    public final static int HTTP_UNAVAILABLE = 503;

    /**
     * Numeric status code, 505: Version not supported
     * 
     * @since Android 1.0
     */
    public final static int HTTP_VERSION = 505;

    /**
     * Constructs a new {@code HttpURLConnection} instance pointing to the
     * resource specified by the {@code url}.
     * 
     * @param url
     *            the URL of this connection.
     * @see URL
     * @see URLConnection
     * @since Android 1.0
     */
    protected HttpURLConnection(URL url) {
        super(url);
    }

    /**
     * Closes the connection to the HTTP server.
     * 
     * @see URLConnection#connect()
     * @see URLConnection#connected
     * @since Android 1.0
     */
    public abstract void disconnect();

    /**
     * Returns an input stream from the server in the case of an error such as
     * the requested file has not been found on the remote server. This stream
     * can be used to read the data the server will send back.
     * 
     * @return the error input stream returned by the server.
     * @since Android 1.0
     */
    public java.io.InputStream getErrorStream() {
        return null;
    }

    /**
     * Returns the value of {@code followRedirects} which indicates if this
     * connection follows a different URL redirected by the server. It is
     * enabled by default.
     * 
     * @return the value of the flag.
     * @see #setFollowRedirects
     * @since Android 1.0
     */
    public static boolean getFollowRedirects() {
        return followRedirects;
    }

    /**
     * Returns the permission object (in this case {@code SocketPermission})
     * with the host and the port number as the target name and {@code
     * "resolve, connect"} as the action list. If the port number of this URL
     * instance is lower than {@code 0} the port will be set to {@code 80}.
     * 
     * @return the permission object required for this connection.
     * @throws IOException
     *             if an IO exception occurs during the creation of the
     *             permission object.
     * @since Android 1.0
     */
    @Override
    public java.security.Permission getPermission() throws IOException {
        int port = url.getPort();
        if (port < 0) {
            port = 80;
        }
        return new SocketPermission(url.getHost() + ":" + port, //$NON-NLS-1$
                "connect, resolve"); //$NON-NLS-1$
    }

    /**
     * Returns the request method which will be used to make the request to the
     * remote HTTP server. All possible methods of this HTTP implementation is
     * listed in the class definition.
     * 
     * @return the request method string.
     * @see #method
     * @see #setRequestMethod
     * @since Android 1.0
     */
    public String getRequestMethod() {
        return method;
    }

    /**
     * Returns the response code returned by the remote HTTP server.
     * 
     * @return the response code, -1 if no valid response code.
     * @throws IOException
     *             if there is an IO error during the retrieval.
     * @see #getResponseMessage
     * @since Android 1.0
     */
    public int getResponseCode() throws IOException {
        // Call getInputStream() first since getHeaderField() doesn't return
        // exceptions
        getInputStream();
        String response = getHeaderField(0);
        if (response == null) {
            return -1;
        }
        response = response.trim();
        int mark = response.indexOf(" ") + 1; //$NON-NLS-1$
        if (mark == 0) {
            return -1;
        }
        int last = mark + 3;
        if (last > response.length()) {
            last = response.length();
        }
        responseCode = Integer.parseInt(response.substring(mark, last));
        if (last + 1 <= response.length()) {
            responseMessage = response.substring(last + 1);
        }
        return responseCode;
    }

    /**
     * Returns the response message returned by the remote HTTP server.
     * 
     * @return the response message. {@code null} if no such response exists.
     * @throws IOException
     *             if there is an error during the retrieval.
     * @see #getResponseCode()
     * @since Android 1.0
     */
    public String getResponseMessage() throws IOException {
        if (responseMessage != null) {
            return responseMessage;
        }
        getResponseCode();
        return responseMessage;
    }

    /**
     * Sets the flag of whether this connection will follow redirects returned
     * by the remote server. This method can only be called with the permission
     * from the security manager.
     * 
     * @param auto
     *            the value to enable or disable this option.
     * @see SecurityManager#checkSetFactory()
     * @since Android 1.0
     */
    public static void setFollowRedirects(boolean auto) {
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkSetFactory();
        }
        followRedirects = auto;
    }

    /**
     * Sets the request command which will be sent to the remote HTTP server.
     * This method can only be called before the connection is made.
     * 
     * @param method
     *            the string representing the method to be used.
     * @throws ProtocolException
     *             if this is called after connected, or the method is not
     *             supported by this HTTP implementation.
     * @see #getRequestMethod()
     * @see #method
     * @since Android 1.0
     */
    public void setRequestMethod(String method) throws ProtocolException {
        if (connected) {
            throw new ProtocolException(Msg.getString("K0037")); //$NON-NLS-1$
        }
        for (int i = 0; i < methodTokens.length; i++) {
            if (methodTokens[i].equals(method)) {
                // if there is a supported method that matches the desired
                // method, then set the current method and return
                this.method = methodTokens[i];
                return;
            }
        }
        // if none matches, then throw ProtocolException
        throw new ProtocolException();
    }

    /**
     * Returns whether this connection uses a proxy server or not.
     * 
     * @return {@code true} if this connection passes a proxy server, false
     *         otherwise.
     * @since Android 1.0
     */
    public abstract boolean usingProxy();

    /**
     * Returns whether this connection follows redirects.
     * 
     * @return {@code true} if this connection follows redirects, false
     *         otherwise.
     * @since Android 1.0
     */
    public boolean getInstanceFollowRedirects() {
        return instanceFollowRedirects;
    }

    /**
     * Sets whether this connection follows redirects.
     * 
     * @param followRedirects
     *            {@code true} if this connection will follows redirects, false
     *            otherwise.
     * @since Android 1.0
     */
    public void setInstanceFollowRedirects(boolean followRedirects) {
        instanceFollowRedirects = followRedirects;
    }

    /**
     * Returns the date value in milliseconds since {@code 01.01.1970, 00:00h}
     * corresponding to the header field {@code field}. The {@code defaultValue}
     * will be returned if no such field can be found in the response header.
     * 
     * @param field
     *            the header field name.
     * @param defaultValue
     *            the default value to use if the specified header field wont be
     *            found.
     * @return the header field represented in milliseconds since January 1,
     *         1970 GMT.
     * @since Android 1.0
     */
    @Override
    public long getHeaderFieldDate(String field, long defaultValue) {
        return super.getHeaderFieldDate(field, defaultValue);
    }

    /**
     * If the length of a HTTP request body is known ahead, sets fixed length to
     * enable streaming without buffering. Sets after connection will cause an
     * exception.
     * 
     * @see #setChunkedStreamingMode
     * @param contentLength
     *            the fixed length of the HTTP request body.
     * @throws IllegalStateException
     *             if already connected or an other mode already set.
     * @throws IllegalArgumentException
     *             if {@code contentLength} is less than zero.
     * @since Android 1.0
     */
    public void setFixedLengthStreamingMode(int contentLength) {
        if (super.connected) {
            throw new IllegalStateException(Msg.getString("K0079")); //$NON-NLS-1$
        }
        if (0 < chunkLength) {
            throw new IllegalStateException(Msg.getString("KA003")); //$NON-NLS-1$
        }
        if (0 > contentLength) {
            throw new IllegalArgumentException(Msg.getString("K0051")); //$NON-NLS-1$
        }
        this.fixedContentLength = contentLength;
    }

    /**
     * If the length of a HTTP request body is NOT known ahead, enable chunked
     * transfer encoding to enable streaming with buffering. Notice that not all
     * http servers support this mode. Sets after connection will cause an
     * exception.
     * 
     * @see #setFixedLengthStreamingMode
     * @param chunklen
     *            the length of a chunk.
     * @throws IllegalStateException
     *             if already connected or an other mode already set.
     * @since Android 1.0
     */
    public void setChunkedStreamingMode(int chunklen) {
        if (super.connected) {
            throw new IllegalStateException(Msg.getString("K0079")); //$NON-NLS-1$
        }
        if (0 <= fixedContentLength) {
            throw new IllegalStateException(Msg.getString("KA003")); //$NON-NLS-1$
        }
        if (0 >= chunklen) {
            chunkLength = DEFAULT_CHUNK_LENGTH;
        } else {
            chunkLength = chunklen;
        }
    }
}