im.delight.android.webrequest.WebRequest.java Source code

Java tutorial

Introduction

Here is the source code for im.delight.android.webrequest.WebRequest.java

Source

package im.delight.android.webrequest;

/**
 * Copyright 2014 www.delight.im <info@delight.im>
 *
 * Licensed 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.
 */

import org.apache.http.util.CharArrayBuffer;
import java.io.Reader;
import java.io.InputStreamReader;
import org.apache.http.HttpEntity;
import java.util.zip.GZIPInputStream;
import java.io.InputStream;
import org.apache.http.Header;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.util.EntityUtils;
import android.util.Base64;

/** Fluent interface for easy HTTP(S) GET/POST/PUT/DELETE requests to web servers */
public class WebRequest {

    protected static final int METHOD_GET = 1;
    protected static final int METHOD_POST = 2;
    protected static final int METHOD_PUT = 3;
    protected static final int METHOD_DELETE = 4;
    protected static final String DEFAULT_USER_AGENT = "Android";
    protected static final int DEFAULT_CONNECTION_TIMEOUT = 6000;
    protected static final int DEFAULT_SOCKET_TIMEOUT = 8000;
    protected static final String DEFAULT_CHARSET = "utf-8";
    protected int mRequestMethod;
    protected String mUrl;
    protected String mUserAgent;
    protected int mConnectionTimeout;
    protected int mSocketTimeout;
    protected String mCharset;
    protected List<NameValuePair> mParams;
    protected String mUsername;
    protected String mPassword;
    protected boolean mGzip;
    protected DefaultHttpClient mClient;
    protected HttpRequestBase mHttpRequest;

    public interface Callback {
        public void onSuccess(String responseText);

        public void onError();
    }

    /** Creates a new WebRequest instance which you can then call get(), post(), put() or delete() on */
    public WebRequest() {
        super();
        mUserAgent = DEFAULT_USER_AGENT;
        mConnectionTimeout = DEFAULT_CONNECTION_TIMEOUT;
        mSocketTimeout = DEFAULT_SOCKET_TIMEOUT;
        mCharset = DEFAULT_CHARSET;
        mParams = new ArrayList<NameValuePair>();
        mUsername = null;
        mPassword = null;
        mGzip = false;
    }

    /**
     * Make this request a GET request so that you can call to(...)
     * @return this instance for chaining
     */
    public WebRequest get() {
        mRequestMethod = METHOD_GET;
        return this;
    }

    /**
     * Make this request a POST request so that you can call to(...)
     * @return this instance for chaining
     */
    public WebRequest post() {
        mRequestMethod = METHOD_POST;
        return this;
    }

    /**
     * Make this request a PUT request so that you can call to(...)
     * @return this instance for chaining
     */
    public WebRequest put() {
        mRequestMethod = METHOD_PUT;
        return this;
    }

    /**
     * Make this request a DELETE request so that you can call to(...)
     * @return this instance for chaining
     */
    public WebRequest delete() {
        mRequestMethod = METHOD_DELETE;
        return this;
    }

    /**
     * Sets the URL for this web request
     * @param url the URL to send the request to
     * @return this instance for chaining
     */
    public WebRequest to(String url) {
        if (url == null || url.length() < 1) {
            throw new RuntimeException("You must provide a valid target URL");
        } else {
            mUrl = url;
            return this;
        }
    }

    /**
     * Sets the username and password for HTTP Basic Auth
     * @param username the username to authenticate as
     * @param password the password to authenticate with
     * @return this instance for chaining
     */
    public WebRequest auth(String username, String password) {
        mUsername = username;
        mPassword = password;
        return this;
    }

    /**
     * Sets the User-Agent header for the current request
     * @param userAgent User-Agent header to send
     * @return this instance for chaining
     */
    public WebRequest asUserAgent(String userAgent) {
        if (userAgent == null) {
            throw new RuntimeException("You must pass a valid User-Agent header");
        } else {
            mUserAgent = userAgent;
            return this;
        }
    }

    /**
     * Sets the connection timeout and socket timeout for the current request in milliseconds
     * @param connectionTimeout timeout in milliseconds
     * @param socketTimeout timeout in milliseconds
     * @return this instance for chaining
     */
    public WebRequest withTimeout(int connectionTimeout, int socketTimeout) {
        mConnectionTimeout = connectionTimeout;
        mSocketTimeout = socketTimeout;
        return this;
    }

    /**
     * Sets the charset for the current request
     * @param charset the character encoding to use for the current request
     * @return this instance for chaining
     */
    public WebRequest withCharset(String charset) {
        if (charset == null) {
            throw new RuntimeException("You must pass a valid charset name");
        } else {
            mCharset = charset;
            return this;
        }
    }

    /**
     * Whether to ask for GZIP response compression or not
     * @param gzip whether to ask for GZIP or not
     * @return this instance for chaining
     */
    public WebRequest askForGzip(boolean gzip) {
        mGzip = gzip;
        return this;
    }

    /**
     * Adds a new key-value pair to the parameters of this request
     * @param key the key of this pair
     * @param value the corresponding value for this pair
     * @return this instance for chaining
     */
    public WebRequest addParam(String key, String value) {
        if (key == null) {
            throw new RuntimeException("You must pass a valid key when adding a param");
        } else {
            mParams.add(new BasicNameValuePair(key, value));
            return this;
        }
    }

    /**
     * Adds a new key-value pair to the parameters of this request
     * @param key the key of this pair
     * @param value the corresponding value for this pair
     * @return this instance for chaining
     */
    public WebRequest addParam(String key, int value) {
        if (key == null) {
            throw new RuntimeException("You must pass a valid key when adding a param");
        } else {
            mParams.add(new BasicNameValuePair(key, String.valueOf(value)));
            return this;
        }
    }

    /**
     * Adds a new key-value pair to the parameters of this request
     * @param key the key of this pair
     * @param value the corresponding value for this pair
     * @return this instance for chaining
     */
    public WebRequest addParam(String key, long value) {
        if (key == null) {
            throw new RuntimeException("You must pass a valid key when adding a param");
        } else {
            mParams.add(new BasicNameValuePair(key, String.valueOf(value)));
            return this;
        }
    }

    /**
     * Adds a new key-value pair to the parameters of this request
     * @param key the key of this pair
     * @param value the corresponding value for this pair
     * @return this instance for chaining
     */
    public WebRequest addParam(String key, float value) {
        if (key == null) {
            throw new RuntimeException("You must pass a valid key when adding a param");
        } else {
            mParams.add(new BasicNameValuePair(key, String.valueOf(value)));
            return this;
        }
    }

    /**
     * Adds a new key-value pair to the parameters of this request
     * @param key the key of this pair
     * @param value the corresponding value for this pair
     * @return this instance for chaining
     */
    public WebRequest addParam(String key, double value) {
        if (key == null) {
            throw new RuntimeException("You must pass a valid key when adding a param");
        } else {
            mParams.add(new BasicNameValuePair(key, String.valueOf(value)));
            return this;
        }
    }

    /**
     * Method to be overwritten in subclasses if you want to add custom HTTP headers
     *
     * @param request the request object to add the HTTP headers to
     */
    @SuppressWarnings("unused")
    protected void addCustomHTTPHeaders(HttpRequestBase request) {
    }

    protected void prepare() {
        if (mRequestMethod == 0) {
            throw new RuntimeException(
                    "You must call one of get(), post(), put() or delete() before executing the request");
        } else if (mUrl == null || mUrl.length() < 1) {
            throw new RuntimeException("You must provide a valid target URL before executing the request");
        }

        final BasicHttpParams httpParameters = new BasicHttpParams();
        // connections break all the time anyway so disable stale checking and get slightly improved performance
        HttpConnectionParams.setStaleCheckingEnabled(httpParameters, false);
        // use the consumer-supplied connection timeout (or the default)
        HttpConnectionParams.setConnectionTimeout(httpParameters, mConnectionTimeout);
        // use the consumer-supplied socket timeout (or the default)
        HttpConnectionParams.setSoTimeout(httpParameters, mSocketTimeout);

        mClient = new DefaultHttpClient(httpParameters);
        if (mRequestMethod == METHOD_GET) {
            if (mParams.size() > 0) {
                mUrl = mUrl + "?" + httpBuildQuery(mParams, mCharset);
            }
            mHttpRequest = new HttpGet(mUrl);
        } else if (mRequestMethod == METHOD_POST) {
            mHttpRequest = new HttpPost(mUrl);
            if (mParams.size() > 0) {
                try {
                    ((HttpPost) mHttpRequest).setEntity(new UrlEncodedFormEntity(mParams, mCharset));
                } catch (UnsupportedEncodingException e) {
                    throw new RuntimeException(e);
                }
            }
        } else {
            throw new RuntimeException("Unknown request method: " + mRequestMethod);
        }

        addCustomHTTPHeaders(mHttpRequest);
        mHttpRequest.setHeader("User-Agent", mUserAgent);
        if (mUsername != null && mPassword != null) {
            mHttpRequest.setHeader("Authorization", getAuthDigest());
        }
        if (mGzip) {
            mHttpRequest.addHeader("Accept-Encoding", "gzip");
        }
    }

    /**
     * Runs the current request asynchronously and executes the given callback afterwards
     *
     * @param callback the callback to execute when the request succeeds or fails
     */
    public void executeAsync(final Callback callback) {
        prepare();

        new Thread() {

            @Override
            public void run() {
                String responseStr;
                try {
                    final HttpResponse responseData = mClient.execute(mHttpRequest);
                    responseStr = parseResponse(responseData);
                    if (responseStr == null) {
                        if (callback != null) {
                            callback.onError();
                        }
                    } else {
                        if (callback != null) {
                            callback.onSuccess(responseStr);
                        }
                    }
                } catch (Exception e) {
                    if (callback != null) {
                        callback.onError();
                    }
                    return;
                }
            }

        }.start();
    }

    /**
     * Runs the current request synchronously and returns the response text
     *
     * @return the response text as a string
     */
    public String executeSync() {
        prepare();

        String responseStr;
        try {
            final HttpResponse responseData = mClient.execute(mHttpRequest);
            responseStr = parseResponse(responseData);
            if (responseStr == null) {
                return null;
            } else {
                return responseStr;
            }
        } catch (Exception e) {
            return null;
        }
    }

    protected String parseResponse(HttpResponse response) throws Exception {
        final Header contentEncoding = response.getFirstHeader("Content-Encoding");
        // if we have a compressed response (GZIP)
        if (contentEncoding != null && contentEncoding.getValue() != null
                && contentEncoding.getValue().equalsIgnoreCase("gzip")) {
            // get the entity and the content length (if any) from the response
            final HttpEntity entity = response.getEntity();
            long contentLength = entity.getContentLength();

            // handle too large or undefined content lengths
            if (contentLength > Integer.MAX_VALUE) {
                throw new Exception("Response too large");
            } else if (contentLength < 0) {
                // use an arbitrary buffer size
                contentLength = 4096;
            }

            // construct a GZIP input stream from the response
            InputStream responseStream = entity.getContent();
            if (responseStream == null) {
                return null;
            }
            responseStream = new GZIPInputStream(responseStream);

            // read from the stream
            Reader reader = new InputStreamReader(responseStream, mCharset);
            CharArrayBuffer buffer = new CharArrayBuffer((int) contentLength);
            try {
                char[] tmp = new char[1024];
                int l;
                while ((l = reader.read(tmp)) != -1) {
                    buffer.append(tmp, 0, l);
                }
            } finally {
                reader.close();
            }

            // return the decompressed response text as a string
            return buffer.toString();
        }
        // if we have an uncompressed response
        else {
            // return the response text as a string
            return EntityUtils.toString(response.getEntity(), mCharset);
        }
    }

    /** Builds the HTTP query string from the list of parameters (analogous to PHP's http_build_query(...) function) */
    protected static String httpBuildQuery(List<? extends NameValuePair> parameters, String encoding) {
        return URLEncodedUtils.format(parameters, encoding).replace("*", "%2A");
    }

    protected String getAuthDigest() {
        if (mUsername != null && mPassword != null) {
            return "Basic " + Base64.encodeToString((mUsername + ":" + mPassword).getBytes(), Base64.NO_WRAP);
        } else {
            return "";
        }
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append("WebRequest [mUrl=");
        builder.append(mUrl);
        builder.append(", mRequestMethod=");
        builder.append(mRequestMethod);
        builder.append(", mUserAgent=");
        builder.append(mUserAgent);
        builder.append(", mConnectionTimeout=");
        builder.append(mConnectionTimeout);
        builder.append(", mSocketTimeout=");
        builder.append(mSocketTimeout);
        builder.append(", mCharset=");
        builder.append(mCharset);
        builder.append(", mParams=");
        builder.append(mParams);
        builder.append(", mUsername=");
        builder.append(mUsername);
        builder.append(", mPassword=");
        builder.append(mPassword);
        builder.append(", mGzip=");
        builder.append(mGzip);
        builder.append("]");
        return builder.toString();
    }

}