Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android: Scheme 'http' not registered on ICS 4.0.4 w/ proxy

I'm using HttpClient for HTTPS requests, which has worked fine up until now. After upgrading to ICS, some users are reporting problems connecting on 3G connections.

EDIT: Most of them seem to be using a proxy, and I can reproduce this locally with a T-Mobile SIM using their proxy.

The logs have this stack trace:

java.lang.IllegalStateException: Scheme 'http' not registered.
org.apache.http.conn.scheme.SchemeRegistry.getScheme(SchemeRegistry.java:80)
org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:126)
org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:164)
org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:119)
org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:360)
org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:555)
org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:487)
org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:465)

Our endpoint is HTTPS only, so we do not register a HTTP endpoint in our SchemeRegistry on purpose. There isn't anywhere (AFAIK) where we redirect to HTTP.

Here is the code that sets up the HttpClient for the HTTPS client:

    DefaultHttpClient ret = null;

    // sets up parameters
    HttpParams params = new BasicHttpParams();
    HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
    HttpProtocolParams.setContentCharset(params, "utf-8");
    params.setBooleanParameter("http.protocol.expect-continue", false);

    HttpConnectionParams.setConnectionTimeout(params, DEFAULT_CONN_TIMEOUT_MSEC);
    HttpConnectionParams.setSoTimeout(params, timeoutMsec);
    HttpConnectionParams.setStaleCheckingEnabled(params, true);

    SchemeRegistry registry = new SchemeRegistry();
    final SocketFactory sslSocketFactory = getPreferredSSLSocketFactory();
    registry.register(new Scheme("https", sslSocketFactory, 443));

    ThreadSafeClientConnManager manager = new ThreadSafeClientConnManager(params, registry);
    ret = new DefaultHttpClient(manager, params);
    // for preemptive authentication
    // http://dlinsin.blogspot.com/2009/08/http-basic-authentication-with-android.html
    ret.addRequestInterceptor(preemptiveAuth, 0);
    ret.setCookieStore(communalCookieJar);

    SimpleCredentialsProvider credProvider = new SimpleCredentialsProvider(getAccountPreferences());
    ret.setCredentialsProvider(credProvider);

    return ret;

Note: We share this HttpClient instance among multiple threads.

like image 624
sehugg Avatar asked Apr 27 '12 14:04

sehugg


3 Answers

From your stacktrace, i would suggest that you register both ( http, https ) and see if that does not work.

You should be able to debug it by including the apache source jars - drill down on the trace @SchemeRegistry.getScheme().

This thread may help.

Below is tested OK on ICS... Sample SSL ConnectionMgr on androidhttpclient libs:

static X509TrustManager tm = new X509TrustManager() {

        public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException {
        }

        public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException {
        }

        public X509Certificate[] getAcceptedIssuers() {
        return null;
        }
};

MyConnectionManager(SchemeRegistry scheme){
    super(scheme);
}

public static MyConnectionManager getInstance() {
    if (instance == null){

        SSLContext ctx=null;
        try {
            ctx = SSLContext.getInstance("TLS");
            ctx.init(null, new TrustManager[]{tm}, null);
        } catch (NoSuchAlgorithmException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (KeyManagementException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }                   
        SchemeRegistry schemeRegistry = new SchemeRegistry();
        schemeRegistry.register( new Scheme("http", 80,PlainSocketFactory.getSocketFactory()));
        schemeRegistry.register(new Scheme("https", 443, SSLSocketFactory.getSocketFactory()));
        instance = new MyConnectionManager(schemeRegistry);
         // Increase max total connection to 200
         instance.setMaxTotal(15);
         // Increase default max connection per route to 20
         instance.setDefaultMaxPerRoute(15);
         // Increase max connections for localhost:80 to 50
         HttpHost localhost = new HttpHost("picasaweb.google.com", 443);
         instance.setMaxForRoute(new HttpRoute(localhost), 10);
    }
    return instance;
}
like image 169
Robert Rowntree Avatar answered Oct 24 '22 01:10

Robert Rowntree


The issue seemed to be that some carriers were pushing invalid proxy definitions with the 4.0.4 update. This broke HTTPS but HTTP worked properly (Google Play didn't work, for instance).

One possible fix (besides fixing the invalid proxy entry) is to catch IllegalStateException when performing HttpClient requests and set a flag. This code will bypass any proxies:

    hc = new DefaultHttpClient(manager, params);
    if (BYPASS_PROXY)
        hc.setRoutePlanner(new DefaultHttpRoutePlanner(registry));
like image 35
sehugg Avatar answered Oct 24 '22 00:10

sehugg


I would not create a new SchemeRegistry. I would take the default one from the ThreadSafeClientConnManager.getSchemeRegistry(). This way, it probably contains all sort of already supported schemes.

The http part can come from your carrier's proxy.

like image 2
njzk2 Avatar answered Oct 24 '22 01:10

njzk2