/* Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved See License.txt in the project root for license information. */ package microsoft.aspnet.signalr.client.http.android; import android.annotation.SuppressLint; import android.net.http.AndroidHttpClient; import android.os.AsyncTask; import android.os.Build; import org.apache.http.Header; import org.apache.http.HttpHost; import org.apache.http.HttpRequest; import org.apache.http.HttpResponse; import org.apache.http.entity.StringEntity; import org.apache.http.message.BasicHttpEntityEnclosingRequest; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.SocketTimeoutException; import java.net.URI; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import microsoft.aspnet.signalr.client.*; import microsoft.aspnet.signalr.client.http.*; import microsoft.aspnet.signalr.client.http.HttpConnectionFuture.ResponseCallback; /** * Android HttpConnection implementation, based on AndroidHttpClient and * AsyncTask for async operations */ public class AndroidHttpConnection implements HttpConnection { private Logger mLogger; /** * Initializes the AndroidHttpConnection * * @param logger * logger to log activity */ public AndroidHttpConnection(Logger logger) { if (logger == null) { throw new IllegalArgumentException("logger"); } mLogger = logger; } @Override public HttpConnectionFuture execute(final Request request, final ResponseCallback responseCallback) { mLogger.log("Create new AsyncTask for HTTP Connection", LogLevel.Verbose); final HttpConnectionFuture future = new HttpConnectionFuture(); final RequestTask requestTask = new RequestTask() { AndroidHttpClient mClient; InputStream mResponseStream; @Override protected Void doInBackground(Void... voids) { if (request == null) { future.triggerError(new IllegalArgumentException("request")); } mClient = AndroidHttpClient.newInstance(Platform.getUserAgent()); mResponseStream = null; URI uri; try { mLogger.log("Create an Android-specific request", LogLevel.Verbose); request.log(mLogger); HttpRequest realRequest = createRealRequest(request); uri = new URI(request.getUrl()); HttpHost host = new HttpHost(uri.getHost(), uri.getPort(), uri.getScheme()); mLogger.log("Execute the HTTP Request", LogLevel.Verbose); HttpResponse response; try { response = mClient.execute(host, realRequest); } catch (SocketTimeoutException timeoutException) { closeStreamAndClient(); mLogger.log("Timeout executing request: " + timeoutException.getMessage(), LogLevel.Information); future.triggerTimeout(timeoutException); return null; } mLogger.log("Request executed", LogLevel.Verbose); mResponseStream = response.getEntity().getContent(); Header[] headers = response.getAllHeaders(); Map<String, List<String>> headersMap = new HashMap<String, List<String>>(); for (Header header : headers) { String headerName = header.getName(); if (headersMap.containsKey(headerName)) { headersMap.get(headerName).add(header.getValue()); } else { List<String> headerValues = new ArrayList<String>(); headerValues.add(header.getValue()); headersMap.put(headerName, headerValues); } } responseCallback.onResponse(new StreamResponse(mResponseStream, response.getStatusLine().getStatusCode(), headersMap)); future.setResult(null); closeStreamAndClient(); } catch (Exception e) { closeStreamAndClient(); mLogger.log("Error executing request: " + e.getMessage(), LogLevel.Critical); future.triggerError(e); } return null; } protected void closeStreamAndClient() { if (mResponseStream != null) { try { mResponseStream.close(); } catch (IOException e) { } } if (mClient != null) { mClient.close(); } } }; future.onCancelled(new Runnable() { @Override public void run() { AsyncTask<Void, Void, Void> cancelTask = new AsyncTask<Void, Void, Void>() { @Override protected Void doInBackground(Void... params) { requestTask.closeStreamAndClient(); return null; } }; executeTask(cancelTask); } }); executeTask(requestTask); return future; } @SuppressLint("NewApi") private void executeTask(AsyncTask<Void, Void, Void> task) { // If it's running with Honeycomb or greater, it must execute each // request in a different thread if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } else { task.execute(); } } /** * Internal class to represent an async operation that can close a stream */ private abstract class RequestTask extends AsyncTask<Void, Void, Void> { /** * Closes the internal stream and http client, if they exist */ abstract protected void closeStreamAndClient(); } /** * Creates a request that can be accepted by the AndroidHttpClient * * @param request * The request information * @throws java.io.UnsupportedEncodingException */ private static BasicHttpEntityEnclosingRequest createRealRequest(Request request) throws UnsupportedEncodingException { BasicHttpEntityEnclosingRequest realRequest = new BasicHttpEntityEnclosingRequest(request.getVerb(), request.getUrl()); if (request.getContent() != null) { realRequest.setEntity(new StringEntity(request.getContent())); } Map<String, String> headers = request.getHeaders(); for (String key : headers.keySet()) { realRequest.addHeader(key, headers.get(key)); } return realRequest; } }