The Google App Engine infrastructure, I'm
developing in my spare time, is meant to be used by an
Android client. To give our users at least a vague feeling of security, we decided to use a
Basic Authentication together with
HTTPS. Apparently, Android 1.5 is
shipping with Apache's HttpClient 4.0 Beta2, which has a pitfall, when it comes to
Basic Authentication.
When you search for
HttpClient and
Basic Authentication, Google will most definitely send you to the
official documentation of HttpClient 3.x, which shows you, how to do Basic Authentication in a preemptive way. That means, sending the client's credentials with every request, instead of waiting for a
401 Unauthorized response and only then sending the credentials. That's probably what you want to in the first place, because it saves your client a request.
HttpClient client = new HttpClient();
client.getParams().setAuthenticationPreemptive(true);
Credentials defaultcreds = new UsernamePasswordCredentials("username", "password");
client.getState().setCredentials(new AuthScope("myhost", 80, AuthScope.ANY_REALM), defaultcreds);
This sample code won't compile with HttpClient version 4. The method called
setAuthenticationPreemptive is missing. The problem is, if you omit this very method call, the code still works, but the authentication is not preemptive. We missed this little detail and only noticed after a while, that every request was preceded by a
401 Unauthorized request/response cycle. That doubled the amount of requests we served.
The
HttpClient 4 documentation shows how to do preemptive authentication with the new API. You need to implement a so called
HttpRequestInterceptor:
HttpRequestInterceptor preemptiveAuth = new HttpRequestInterceptor() {
public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
AuthState authState = (AuthState) context.getAttribute(ClientContext.TARGET_AUTH_STATE);
CredentialsProvider credsProvider = (CredentialsProvider) context.getAttribute(
ClientContext.CREDS_PROVIDER);
HttpHost targetHost = (HttpHost) context.getAttribute(ExecutionContext.HTTP_TARGET_HOST);
if (authState.getAuthScheme() == null) {
AuthScope authScope = new AuthScope(targetHost.getHostName(), targetHost.getPort());
Credentials creds = credsProvider.getCredentials(authScope);
if (creds != null) {
authState.setAuthScheme(new BasicScheme());
authState.setCredentials(creds);
}
}
}
};It basically sets the Basic Authentication headers, before each requests and thus avoids the 401 response. In order for the interceptor to work, you need to add it to the request chain:
DefaultHttpClient httpclient = new DefaultHttpClient();
httpclient.addRequestInterceptor(preemptiveAuth, 0);
You might also run into the problem of using the old HttpClient 3.x way of doing
Basic Authentication. It does work, but it's not preemptive, which you might not notice right away. Save yourself some time and checkout the
sample code provided by Apache.
Furthermore, I wasn't abel to find any official site, which states the version of HttpClient, used in Android 1.5. There are various sources, you might encounter, when searching Google, but I would like to see an official site, that states the version. A reason for Google not to reveal this information might be, that they adopted HttpClient and thus are not compatible anymore. However, to avoid mistakes and confusion, it would be nice to know, on which version the Android HttpClient is based on.