/** * ===================================================================== * * @file AsyHttpClient.java * @Module Name com.benz.tools.http * @author benz * @OS version 1.0 * @Product type: JoySee * @date 2013-11-26 * @brief This file is the http **** implementation. * @This file is responsible by ANDROID TEAM. * @Comments: ===================================================================== Revision * History: * * Modification Tracking * * Author Date OS version Reason ---------- ------------ ------------- ----------- benz * 2013-11-26 1.0 Check for NULL, 0 h/w * ===================================================================== **/ // package com.letv.commonjar.http; import java.io.IOException; import java.io.InputStream; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.WeakHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ThreadPoolExecutor; import java.util.zip.GZIPInputStream; import org.apache.http.Header; import org.apache.http.HeaderElement; import org.apache.http.HttpEntity; import org.apache.http.HttpHost; import org.apache.http.HttpRequest; import org.apache.http.HttpRequestInterceptor; import org.apache.http.HttpResponse; import org.apache.http.HttpResponseInterceptor; import org.apache.http.HttpVersion; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.CookieStore; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpDelete; import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpHead; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPut; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.client.protocol.ClientContext; import org.apache.http.conn.params.ConnManagerParams; import org.apache.http.conn.params.ConnPerRouteBean; import org.apache.http.conn.params.ConnRoutePNames; import org.apache.http.conn.scheme.PlainSocketFactory; import org.apache.http.conn.scheme.Scheme; import org.apache.http.conn.scheme.SchemeRegistry; import org.apache.http.conn.ssl.SSLSocketFactory; import org.apache.http.entity.HttpEntityWrapper; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.client.DefaultRedirectHandler; import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; import org.apache.http.params.BasicHttpParams; import org.apache.http.params.HttpConnectionParams; import org.apache.http.params.HttpParams; import org.apache.http.params.HttpProtocolParams; import org.apache.http.protocol.BasicHttpContext; import org.apache.http.protocol.HttpContext; import org.apache.http.protocol.SyncBasicHttpContext; import com.letv.commonjar.CLog; import android.content.Context; class AsyncHttpClient { public static final String TAG = CLog.makeTag(AsyncHttpClient.class); public static final String VERSION = "1.0.0"; // 并行线程数 public static final int DEFAULT_MAX_CONNECTIONS = 10; // 超时时间 public static final int DEFAULT_SOCKET_TIMEOUT = 10 * 1000; // 重试次数 public static final int DEFAULT_MAX_RETRIES = 1; // 重试时间 public static final int DEFAULT_RETRY_SLEEP_TIME_MILLIS = 1500; public static final int DEFAULT_SOCKET_BUFFER_SIZE = 8192; public static final String HEADER_ACCEPT_ENCODING = "Accept-Encoding"; public static final String ENCODING_GZIP = "gzip"; private int maxConnections = DEFAULT_MAX_CONNECTIONS; private int timeout = DEFAULT_SOCKET_TIMEOUT; private boolean isUrlEncodingEnabled = true; private ExecutorService mThreadPool; private final HttpContext mHttpContext; private final DefaultHttpClient mHttpClient; private static AsyncHttpClient mAsyncHttpClient; private final Map<String, String> mHeaderClientMap; private final Map<String, WeakReference<Future<?>>> mUrlRequestMap; private final Map<Context, List<WeakReference<Future<?>>>> mContextRequestMap; public AsyncHttpClient() { this(false, 80, 443); } private AsyncHttpClient(boolean fixNoHttpResponseException, int httpPort, int httpsPort) { this(getDefaultSchemeRegistry(fixNoHttpResponseException, httpPort, httpsPort)); } public static AsyncHttpClient getInstance() { if (mAsyncHttpClient == null) { synchronized (AsyncHttpClient.class) { if (mAsyncHttpClient == null) { mAsyncHttpClient = new AsyncHttpClient(); } } } return mAsyncHttpClient; } /** * Returns default instance of SchemeRegistry */ private static SchemeRegistry getDefaultSchemeRegistry(boolean fixNoHttpResponseException, int httpPort, int httpsPort) { if (fixNoHttpResponseException) { CLog.d(TAG, "Beware! Using the fix is insecure, as it doesn't verify SSL certificates."); } if (httpPort < 1) { httpPort = 80; CLog.d(TAG, "Invalid HTTP port number specified, defaulting to 80"); } if (httpsPort < 1) { httpsPort = 443; CLog.d(TAG, "Invalid HTTPS port number specified, defaulting to 443"); } // Fix to SSL flaw in API < ICS // See https://code.google.com/p/android/issues/detail?id=13117 SSLSocketFactory sslSocketFactory; if (fixNoHttpResponseException) { sslSocketFactory = MySSLSocketFactory.getFixedSocketFactory(); } else { sslSocketFactory = SSLSocketFactory.getSocketFactory(); } SchemeRegistry schemeRegistry = new SchemeRegistry(); schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), httpPort)); schemeRegistry.register(new Scheme("https", sslSocketFactory, httpsPort)); return schemeRegistry; } public AsyncHttpClient(SchemeRegistry schemeRegistry) { BasicHttpParams httpParams = new BasicHttpParams(); ConnManagerParams.setTimeout(httpParams, timeout); ConnManagerParams.setMaxConnectionsPerRoute(httpParams, new ConnPerRouteBean(maxConnections)); ConnManagerParams.setMaxTotalConnections(httpParams, DEFAULT_MAX_CONNECTIONS); HttpConnectionParams.setSoTimeout(httpParams, timeout); HttpConnectionParams.setConnectionTimeout(httpParams, timeout); HttpConnectionParams.setTcpNoDelay(httpParams, true); HttpConnectionParams.setSocketBufferSize(httpParams, DEFAULT_SOCKET_BUFFER_SIZE); HttpProtocolParams.setVersion(httpParams, HttpVersion.HTTP_1_1); HttpProtocolParams.setUserAgent(httpParams, String.format("android-async-http/%s (http://loopj.com/android-async-http)", VERSION)); ThreadSafeClientConnManager cm = new ThreadSafeClientConnManager(httpParams, schemeRegistry); mThreadPool = Executors.newFixedThreadPool(DEFAULT_MAX_CONNECTIONS); mUrlRequestMap = new HashMap<String, WeakReference<Future<?>>>(); mContextRequestMap = new WeakHashMap<Context, List<WeakReference<Future<?>>>>(); mHeaderClientMap = new HashMap<String, String>(); mHttpContext = new SyncBasicHttpContext(new BasicHttpContext()); mHttpClient = new DefaultHttpClient(cm, httpParams); mHttpClient.addRequestInterceptor(new HttpRequestInterceptor() { @Override public void process(HttpRequest request, HttpContext context) { if (!request.containsHeader(HEADER_ACCEPT_ENCODING)) { request.addHeader(HEADER_ACCEPT_ENCODING, ENCODING_GZIP); } for (String header : mHeaderClientMap.keySet()) { request.addHeader(header, mHeaderClientMap.get(header)); } } }); mHttpClient.addResponseInterceptor(new HttpResponseInterceptor() { @Override public void process(HttpResponse response, HttpContext context) { final HttpEntity entity = response.getEntity(); if (entity == null) { return; } final Header encoding = entity.getContentEncoding(); if (encoding != null) { for (HeaderElement element : encoding.getElements()) { if (element.getName().equalsIgnoreCase(ENCODING_GZIP)) { response.setEntity(new InflatingEntity(entity)); break; } } } } }); } public HttpClient getHttpClient() { return this.mHttpClient; } public HttpContext getHttpContext() { return this.mHttpContext; } public void setCookieStore(CookieStore cookieStore) { mHttpContext.setAttribute(ClientContext.COOKIE_STORE, cookieStore); } public void setThreadPool(ThreadPoolExecutor threadPool) { this.mThreadPool = threadPool; } /** * 是否开启重定向 * * @param enableRedirects */ public void setEnableRedirects(final boolean enableRedirects) { mHttpClient.setRedirectHandler(new DefaultRedirectHandler() { @Override public boolean isRedirectRequested(HttpResponse response, HttpContext context) { return enableRedirects; } }); } public void setUserAgent(String userAgent) { HttpProtocolParams.setUserAgent(this.mHttpClient.getParams(), userAgent); } public int getMaxConnections() { return maxConnections; } public void setMaxConnections(int maxConnections) { if (maxConnections < 1) { maxConnections = DEFAULT_MAX_CONNECTIONS; } this.maxConnections = maxConnections; final HttpParams httpParams = this.mHttpClient.getParams(); ConnManagerParams.setMaxConnectionsPerRoute(httpParams, new ConnPerRouteBean(this.maxConnections)); } public int getTimeout() { return timeout; } public void setTimeout(int timeout) { if (timeout < 1000) { timeout = DEFAULT_SOCKET_TIMEOUT; } this.timeout = timeout; } public void setProxy(String hostname, int port) { final HttpHost proxy = new HttpHost(hostname, port); final HttpParams httpParams = this.mHttpClient.getParams(); httpParams.setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy); } public void setProxy(String hostname, int port, String username, String password) { AuthScope authScope = new AuthScope(hostname, port); UsernamePasswordCredentials uCredentials = new UsernamePasswordCredentials(username, password); mHttpClient.getCredentialsProvider().setCredentials(authScope, uCredentials); final HttpHost proxy = new HttpHost(hostname, port); final HttpParams httpParams = this.mHttpClient.getParams(); httpParams.setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy); } /** * Sets the SSLSocketFactory to user when making requests. By default, a new, default * SSLSocketFactory is used. */ public void setSSLSocketFactory(SSLSocketFactory sslSocketFactory) { this.mHttpClient.getConnectionManager().getSchemeRegistry().register(new Scheme("https", sslSocketFactory, 443)); } /** * 设置重试次数和超时时的重试时间 */ public void setMaxRetriesAndTimeout(int retries, int retrySleep) { this.mHttpClient.setHttpRequestRetryHandler(new RetryHandler(retries, retrySleep)); } /** * Sets headers that will be added to all requests this client makes (before sending). */ public void addHeader(String header, String value) { mHeaderClientMap.put(header, value); } /** * Remove header from all requests this client makes (before sending). */ public void removeHeader(String header) { mHeaderClientMap.remove(header); } /** * Sets basic authentication for the request. Uses AuthScope.ANY. This is the same as * setBasicAuth('username','password',AuthScope.ANY) */ public void setBasicAuth(String username, String password) { AuthScope scope = AuthScope.ANY; setBasicAuth(username, password, scope); } /** * Sets basic authentication for the request. You should pass in your AuthScope for security. It * should be like this setBasicAuth("username","password", new * AuthScope("host",port,AuthScope.ANY_REALM)) */ public void setBasicAuth(String username, String password, AuthScope scope) { UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(username, password); this.mHttpClient.getCredentialsProvider().setCredentials(scope, credentials); } /** * Removes set basic auth credentials */ public void clearBasicAuth() { this.mHttpClient.getCredentialsProvider().clear(); } /** * Cancels any pending (or potentially active) requests associated with the passed Context. This * will only affect requests which were created with a non-null android Context. This method is * intended to be used in the onDestroy method of your android activities to destroy all * requests which are no longer required */ public void cancelRequests(Context context, boolean mayInterruptIfRunning) { synchronized (mContextRequestMap) { List<WeakReference<Future<?>>> requestList = mContextRequestMap.get(context); if (requestList != null) { for (WeakReference<Future<?>> requestRef : requestList) { if (requestRef != null) { Future<?> request = requestRef.get(); if (request != null) { request.cancel(mayInterruptIfRunning); } mContextRequestMap.remove(context); } } } } } public void cancelRequest(String url, boolean mayInterruptIfRunning) { synchronized (mUrlRequestMap) { WeakReference<Future<?>> requestRef = mUrlRequestMap.get(url); if (requestRef != null) { Future<?> request = requestRef.get(); if (request != null) { request.cancel(mayInterruptIfRunning); } mUrlRequestMap.remove(url); } } } public void cancelRequest(ArrayList<String> urls, boolean mayInterruptIfRunning) { if (urls == null) { return; } synchronized (mUrlRequestMap) { int size = urls.size(); long begin = CLog.methodBegin(TAG); for (int i = 0; i < size; i++) { String url = urls.get(i); WeakReference<Future<?>> requestRef = mUrlRequestMap.get(url); if (requestRef != null) { Future<?> request = requestRef.get(); if (request != null) { request.cancel(mayInterruptIfRunning); } mUrlRequestMap.remove(url); } } CLog.methodEnd(TAG, begin, size + " count"); } } public void cancelAllRequest(boolean mayInterruptIfRunning) { synchronized (mUrlRequestMap) { Iterator<String> it = mUrlRequestMap.keySet().iterator(); while (it.hasNext()) { String key = (String) it.next(); WeakReference<Future<?>> requestRef = mUrlRequestMap.get(key); if (requestRef != null) { Future<?> request = requestRef.get(); if (request != null) { request.cancel(mayInterruptIfRunning); } } } mUrlRequestMap.clear(); } } /** * Perform a HTTP HEAD request, without any parameters. */ @SuppressWarnings("unused") private RequestReceipt head(String url, ResponseInterface responseInterface) { return head(null, url, null, responseInterface); } /** * Perform a HTTP HEAD request with parameters. */ @SuppressWarnings("unused") private RequestReceipt head(String url, RequestParams params, ResponseInterface responseHandler) { return head(null, url, params, responseHandler); } /** * Perform a HTTP HEAD request without any parameters and track the Android Context which * initiated the request. */ @SuppressWarnings("unused") private RequestReceipt head(Context context, String url, ResponseInterface responseHandler) { return head(context, url, null, responseHandler); } /** * Perform a HTTP HEAD request and track the Android Context which initiated the request. */ private RequestReceipt head(Context context, String url, RequestParams params, ResponseInterface responseInterface) { HttpHead head = new HttpHead(getUrlWithQueryString(isUrlEncodingEnabled, url, params)); return sendRequest(mHttpClient, mHttpContext, head, null, responseInterface, context); } /** * Perform a HTTP HEAD request and track the Android Context which initiated the request with * customized headers */ @SuppressWarnings("unused") private RequestReceipt head(Context context, String url, Header[] headers, RequestParams params, ResponseInterface responseHandler) { HttpUriRequest request = new HttpHead(getUrlWithQueryString(isUrlEncodingEnabled, url, params)); if (headers != null) { request.setHeaders(headers); } return sendRequest(mHttpClient, mHttpContext, request, null, responseHandler, context); } /** * @param context * @param url * @param params * @param responseHandler * @return */ public RequestReceipt get(Context context, String url, RequestParams params, ResponseInterface responseHandler) { HttpGet httpGet = new HttpGet(getUrlWithQueryString(isUrlEncodingEnabled, url, params)); return sendRequest(mHttpClient, mHttpContext, httpGet, null, responseHandler, context); } /** * @param context * @param url * @param headers * @param params * @param responseHandler * @return */ public RequestReceipt get(Context context, String url, Header[] headers, RequestParams params, ResponseInterface responseHandler) { HttpUriRequest request = new HttpGet(getUrlWithQueryString(isUrlEncodingEnabled, url, params)); if (headers != null) { request.setHeaders(headers); } return sendRequest(mHttpClient, mHttpContext, request, null, responseHandler, context); } /** * @param context * @param url * @param entity * @param contentType * @param responseHandler * @return */ public RequestReceipt post(Context context, String url, HttpEntity entity, String contentType, ResponseInterface responseHandler) { HttpPost httpPost = new HttpPost(url); return sendRequest(mHttpClient, mHttpContext, addEntityToRequestBase(httpPost, entity), contentType, responseHandler, context); } /** * @param context * @param url * @param headers * @param params * @param contentType * @param r * @return */ public RequestReceipt post(Context context, String url, Header[] headers, RequestParams params, String contentType, ResponseInterface r) { HttpEntityEnclosingRequestBase request = new HttpPost(url); if (params != null) { request.setEntity(paramsToEntity(params, r)); } if (headers != null) { request.setHeaders(headers); } return sendRequest(mHttpClient, mHttpContext, request, contentType, r, context); } /** * @param context * @param url * @param headers * @param entity * @param contentType * @param r * @return */ public RequestReceipt post(Context context, String url, Header[] headers, HttpEntity entity, String contentType, ResponseInterface r) { HttpEntityEnclosingRequestBase request = addEntityToRequestBase(new HttpPost(url), entity); if (headers != null) { request.setHeaders(headers); } return sendRequest(mHttpClient, mHttpContext, request, contentType, r, context); } /** * @param url * @param responseHandler * @return */ public RequestReceipt put(String url, ResponseInterface responseHandler) { return put(null, url, null, responseHandler); } /** * @param url * @param params * @param responseHandler * @return */ public RequestReceipt put(String url, RequestParams params, ResponseInterface responseHandler) { return put(null, url, params, responseHandler); } /** * @param context * @param url * @param params * @param responseHandler * @return */ public RequestReceipt put(Context context, String url, RequestParams params, ResponseInterface responseHandler) { return put(context, url, paramsToEntity(params, responseHandler), null, responseHandler); } /** * Perform a HTTP PUT request and track the Android Context which initiated the request. And set * one-time headers for the request */ public RequestReceipt put(Context context, String url, HttpEntity entity, String contentType, ResponseInterface responseHandler) { HttpPut httpPut = new HttpPut(url); return sendRequest(mHttpClient, mHttpContext, addEntityToRequestBase(httpPut, entity), contentType, responseHandler, context); } /** * Perform a HTTP PUT request and track the Android Context which initiated the request. And set * one-time headers for the request */ public RequestReceipt put(Context context, String url, Header[] headers, HttpEntity entity, String contentType, ResponseInterface r) { HttpEntityEnclosingRequestBase request = addEntityToRequestBase(new HttpPut(url), entity); if (headers != null) { request.setHeaders(headers); } return sendRequest(mHttpClient, mHttpContext, request, contentType, r, context); } /** * Perform a HTTP DELETE request. */ public RequestReceipt delete(String url, ResponseInterface responseHandler) { return delete(null, url, responseHandler); } /** * Perform a HTTP DELETE request. */ public RequestReceipt delete(Context context, String url, ResponseInterface responseHandler) { final HttpDelete delete = new HttpDelete(url); return sendRequest(mHttpClient, mHttpContext, delete, null, responseHandler, context); } /** * Perform a HTTP DELETE request. */ public RequestReceipt delete(Context context, String url, Header[] headers, ResponseInterface responseHandler) { final HttpDelete delete = new HttpDelete(url); if (headers != null) { delete.setHeaders(headers); } return sendRequest(mHttpClient, mHttpContext, delete, null, responseHandler, context); } /** * Perform a HTTP DELETE request. */ public RequestReceipt delete(Context context, String url, Header[] headers, RequestParams params, ResponseInterface r) { HttpDelete httpDelete = new HttpDelete(getUrlWithQueryString(isUrlEncodingEnabled, url, params)); if (headers != null) { httpDelete.setHeaders(headers); } return sendRequest(mHttpClient, mHttpContext, httpDelete, null, r, context); } /** * Create a request * * @param client * @param mHttpContext * @param uriRequest * @param contentType * @param responseInterface * @param context * @return */ protected RequestReceipt sendRequest(DefaultHttpClient client, HttpContext httpContext, HttpUriRequest uriRequest, // String contentType, ResponseInterface responseInterface, Context context) { if (contentType != null) { uriRequest.setHeader("Content-Type", contentType); } responseInterface.setRequestHeaders(uriRequest.getAllHeaders()); responseInterface.setRequestURI(uriRequest.getURI()); Future<?> request = mThreadPool.submit(new AsyncHttpRequest(client, httpContext, timeout, uriRequest, responseInterface)); // TODO: Remove dead Weakrefs from requestLists? if (context != null) { List<WeakReference<Future<?>>> requestList = mContextRequestMap.get(context); if (requestList == null) { requestList = new LinkedList<WeakReference<Future<?>>>(); mContextRequestMap.put(context, requestList); } requestList.add(new WeakReference<Future<?>>(request)); } String url = uriRequest.getURI().toASCIIString(); CLog.d(TAG, "sendRequest = " + url); if (url != null) { mUrlRequestMap.put(url, new WeakReference<Future<?>>(request)); } return new RequestReceipt(request); } /** * Sets state of URL encoding feature, see bug #227, this method allows you to turn off and on * this auto-magic feature on-demand. */ public void setURLEncodingEnabled(boolean enabled) { this.isUrlEncodingEnabled = enabled; } /** * Will encode url, if not disabled, and adds params on the end of it */ public static String getUrlWithQueryString(boolean shouldEncodeUrl, String url, RequestParams params) { if (shouldEncodeUrl) { url = url.replace(" ", "%20"); } if (params != null) { String paramString = params.getParamString(); if (!url.contains("?")) { url += "?" + paramString; } else { url += "&" + paramString; } } CLog.d(TAG, "add params to url=" + url); return url; } /** * Returns HttpEntity containing data from RequestParams included with request declaration. * Allows also passing progress from upload via provided ResponseHandler */ private HttpEntity paramsToEntity(RequestParams params, ResponseInterface responseHandler) { HttpEntity entity = null; try { if (params != null) { entity = params.getEntity(responseHandler); } } catch (Throwable t) { throw new IllegalArgumentException("JRequestParams Error=" + t.getMessage()); } return entity; } public boolean isUrlEncodingEnabled() { return isUrlEncodingEnabled; } /** * Applicable only to HttpRequest methods extending HttpEntityEnclosingRequestBase, which is for * example not DELETE */ private HttpEntityEnclosingRequestBase addEntityToRequestBase(HttpEntityEnclosingRequestBase requestBase, HttpEntity entity) { if (entity != null) { requestBase.setEntity(entity); } return requestBase; } /** * Enclosing entity to hold stream of gzip decoded data for accessing HttpEntity contents */ private static class InflatingEntity extends HttpEntityWrapper { public InflatingEntity(HttpEntity wrapped) { super(wrapped); } @Override public InputStream getContent() throws IOException { return new GZIPInputStream(wrappedEntity.getContent()); } @Override public long getContentLength() { return -1; } } }