package com.andrewshu.android.reddit.common;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
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.HttpVersion;
import org.apache.http.client.CookieStore;
import org.apache.http.client.HttpClient;
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.scheme.SocketFactory;
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.params.CoreProtocolPNames;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.HttpContext;
import android.content.Context;
import android.content.pm.PackageManager.NameNotFoundException;
import android.util.Log;
import com.andrewshu.android.reddit.R;
import com.andrewshu.android.reddit.RedditIsFunApplication;
public class RedditIsFunHttpClientFactory {
private static final String TAG = "RedditIsFunHttpClientFactory";
private static final DefaultHttpClient mGzipHttpClient = createGzipHttpClient();
private static final CookieStore mCookieStore = mGzipHttpClient.getCookieStore();
// Default connection and socket timeout of 60 seconds. Tweak to taste.
private static final int SOCKET_OPERATION_TIMEOUT = 60 * 1000;
static DefaultHttpClient createGzipHttpClient() {
HttpParams params = new BasicHttpParams();
params.setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1);
DefaultHttpClient httpclient = new DefaultHttpClient(params) {
@Override
protected ClientConnectionManager createClientConnectionManager() {
SchemeRegistry registry = new SchemeRegistry();
registry.register(
new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
registry.register(
new Scheme("https", getHttpsSocketFactory(), 443));
HttpParams params = getParams();
HttpConnectionParams.setConnectionTimeout(params, SOCKET_OPERATION_TIMEOUT);
HttpConnectionParams.setSoTimeout(params, SOCKET_OPERATION_TIMEOUT);
HttpConnectionParams.setSocketBufferSize(params, 8192);
return new ThreadSafeClientConnManager(params, registry);
}
/** Gets an HTTPS socket factory with SSL Session Caching if such support is available, otherwise falls back to a non-caching factory
* @return
*/
protected SocketFactory getHttpsSocketFactory(){
try {
Class<?> sslSessionCacheClass = Class.forName("android.net.SSLSessionCache");
Object sslSessionCache = sslSessionCacheClass.getConstructor(Context.class).newInstance(RedditIsFunApplication.getApplication());
Method getHttpSocketFactory = Class.forName("android.net.SSLCertificateSocketFactory").getMethod("getHttpSocketFactory", new Class<?>[]{int.class, sslSessionCacheClass});
return (SocketFactory) getHttpSocketFactory.invoke(null, SOCKET_OPERATION_TIMEOUT, sslSessionCache);
}catch(Exception e){
return SSLSocketFactory.getSocketFactory();
}
}
};
httpclient.addRequestInterceptor(new HttpRequestInterceptor() {
public void process(
final HttpRequest request,
final HttpContext context
) throws HttpException, IOException {
RedditIsFunApplication app = RedditIsFunApplication.getApplication();
String version;
try {
version = app.getPackageManager().getPackageInfo(app.getPackageName(), 0).versionName;
} catch (NameNotFoundException e) {
Log.e(TAG, "Package name not found.", e);
version = "1";
}
String userAgent = app.getString(R.string.user_agent, version);
request.setHeader("User-Agent", userAgent);
if (!request.containsHeader("Accept-Encoding"))
request.addHeader("Accept-Encoding", "gzip");
}
});
httpclient.addResponseInterceptor(new HttpResponseInterceptor() {
public void process(
final HttpResponse response,
final HttpContext context) throws HttpException, IOException {
HttpEntity entity = response.getEntity();
Header ceheader = entity.getContentEncoding();
if (ceheader != null) {
HeaderElement[] codecs = ceheader.getElements();
for (int i = 0; i < codecs.length; i++) {
if (codecs[i].getName().equalsIgnoreCase("gzip")) {
response.setEntity(
new GzipDecompressingEntity(response.getEntity()));
return;
}
}
}
}
});
return httpclient;
}
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;
}
}
/**
* http://hc.apache.org/httpcomponents-client/examples.html
* @return a Gzip-enabled DefaultHttpClient
*/
public static HttpClient getGzipHttpClient() {
return mGzipHttpClient;
}
public static CookieStore getCookieStore() {
return mCookieStore;
}
}