package com.openfeint.internal.request; import java.io.IOException; import java.io.InputStream; import java.util.concurrent.ExecutorService; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.zip.GZIPInputStream; import org.apache.http.Header; import org.apache.http.HeaderElement; import org.apache.http.HttpEntity; import org.apache.http.HttpException; import org.apache.http.HttpRequest; import org.apache.http.HttpRequestInterceptor; import org.apache.http.HttpResponse; import org.apache.http.HttpResponseInterceptor; import org.apache.http.conn.ClientConnectionManager; 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.conn.tsccm.ThreadSafeClientConnManager; import org.apache.http.params.BasicHttpParams; import org.apache.http.protocol.HttpContext; import android.os.Bundle; import android.os.Handler; import com.openfeint.internal.CookieStore; import com.openfeint.internal.OpenFeintInternal; import com.openfeint.internal.SyncedStore; public class Client extends DefaultHttpClient { private static final int MIN_THREADS = 2; private static final int MAX_THREADS = 4; private static final int EXCESS_THREAD_LIFETIME = 30; private Signer mSigner; private Handler mMainThreadHandler; private CookieStore mCookieStore; final ExecutorService mExecutor; public void saveInstanceState(Bundle outState) { mCookieStore.saveInstanceState(outState); } public void restoreInstanceState(Bundle inState) { mCookieStore.restoreInstanceState(inState); } private static class GzipDecompressingEntity extends HttpEntityWrapper { public GzipDecompressingEntity(final HttpEntity entity) { super(entity); } @Override public InputStream getContent() throws IOException, IllegalStateException { // the wrapped entity's getContent() decides about repeatability InputStream wrappedin = wrappedEntity.getContent(); return new GZIPInputStream(wrappedin); } @Override public long getContentLength() { // length of ungzipped content is not known return -1; } } static final ClientConnectionManager makeCCM() { SchemeRegistry sr = new SchemeRegistry(); sr.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80)); sr.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443)); ClientConnectionManager ccm = new ThreadSafeClientConnManager(new BasicHttpParams(), sr); return ccm; } private final class Executor extends ThreadPoolExecutor { Executor() { super(MIN_THREADS, MAX_THREADS, EXCESS_THREAD_LIFETIME, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), new RejectedExecutionHandler() { public void rejectedExecution(Runnable arg0, ThreadPoolExecutor arg1) { OpenFeintInternal.log("HTTPClient", "Can't submit runnable " + arg0.toString()); } }); } } public Client(String key, String secret, SyncedStore prefs) { super(makeCCM(), new BasicHttpParams()); mExecutor = new Executor(); mSigner = new Signer(key, secret); mMainThreadHandler = new Handler(); mCookieStore = new CookieStore(prefs); setCookieStore(mCookieStore); // gzip stream support addRequestInterceptor(new HttpRequestInterceptor() { public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException { if (!request.containsHeader("Accept-Encoding")) { request.addHeader("Accept-Encoding", "gzip"); } } }); addResponseInterceptor(new HttpResponseInterceptor() { public void process(final HttpResponse response, final HttpContext context) throws HttpException, IOException { HttpEntity entity = response.getEntity(); if (entity != null) { Header ceheader = entity.getContentEncoding(); if (ceheader != null) { HeaderElement[] codecs = ceheader.getElements(); if (codecs != null) { for (int i = 0; i < codecs.length; i++) { if (codecs[i].getName().equalsIgnoreCase("gzip")) { response.setEntity(new GzipDecompressingEntity(response.getEntity())); return; } } } } } } }); } public final void makeRequest(final BaseRequest req) { makeRequest(req, req.timeout()); } public final void makeRequest(final BaseRequest req, final long timeoutMillis) { final Runnable onResponse = new Runnable() { public void run() { req.onResponse(); } }; final Runnable onTimeout = new Runnable() { public void run() { // If it's finished its reponse, let it continue with its response handling. if (req.getResponse() == null) { boolean victory = req.getFuture().cancel(true); if (victory) { req.postTimeoutCleanup(); mMainThreadHandler.post(onResponse); } } } }; final Runnable requestRunnable = new Runnable() { public void run() { // Timeout starts NOW. mMainThreadHandler.postDelayed(onTimeout, timeoutMillis); req.sign(mSigner); req.exec(); mMainThreadHandler.removeCallbacks(onTimeout); mMainThreadHandler.post(onResponse); } }; req.setFuture(mExecutor.submit(requestRunnable)); } }