You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
etesync-android/app/src/main/java/at/bitfire/davdroid/HttpClient.java

160 lines
5.9 KiB

/*
* Copyright © 2013 2015 Ricki Hirner (bitfire web engineering).
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/gpl.html
*/
package at.bitfire.davdroid;
import android.content.Context;
import android.os.Build;
import com.squareup.okhttp.Credentials;
import com.squareup.okhttp.Interceptor;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Response;
import com.squareup.okhttp.internal.tls.OkHostnameVerifier;
import com.squareup.okhttp.logging.HttpLoggingInterceptor;
import org.slf4j.Logger;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.net.CookieManager;
import java.text.SimpleDateFormat;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
import at.bitfire.dav4android.BasicDigestAuthenticator;
import de.duenndns.ssl.MemorizingTrustManager;
import lombok.RequiredArgsConstructor;
public class HttpClient extends OkHttpClient {
private final int MAX_LOG_LINE_LENGTH = 85;
final static UserAgentInterceptor userAgentInterceptor = new UserAgentInterceptor();
static final String userAgent;
static {
String date = new SimpleDateFormat("yyyy/MM/dd", Locale.US).format(BuildConfig.buildTime);
userAgent = "DAVdroid/" + BuildConfig.VERSION_NAME + " (" + date + "; dav4android) Android/" + Build.VERSION.RELEASE;
}
final Logger log;
final Context context;
protected String username, password;
protected HttpClient(Logger log, Context context) {
super();
this.log = (log != null) ? log : Constants.log;
this.context = context;
if (context != null) {
// use MemorizingTrustManager to manage self-signed certificates
MemorizingTrustManager mtm = new MemorizingTrustManager(context);
setSslSocketFactory(new SSLSocketFactoryCompat(mtm));
setHostnameVerifier(mtm.wrapHostnameVerifier(OkHostnameVerifier.INSTANCE));
}
// set timeouts
setConnectTimeout(30, TimeUnit.SECONDS);
setWriteTimeout(15, TimeUnit.SECONDS);
setReadTimeout(45, TimeUnit.SECONDS);
// add User-Agent to every request
networkInterceptors().add(userAgentInterceptor);
// add cookie store for non-persistent cookies (some services like Horde use cookies for session tracking)
CookieManager cookies = new CookieManager();
setCookieHandler(cookies);
// enable verbose logs, if requested
if (this.log.isTraceEnabled()) {
HttpLoggingInterceptor logger = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
@Override
public void log(String message) {
BufferedReader reader = new BufferedReader(new StringReader(message));
String line;
try {
while ((line = reader.readLine()) != null) {
int len = line.length();
for (int pos = 0; pos < len; pos += MAX_LOG_LINE_LENGTH)
if (pos < len - MAX_LOG_LINE_LENGTH)
HttpClient.this.log.trace(line.substring(pos, pos + MAX_LOG_LINE_LENGTH) + "\\");
else
HttpClient.this.log.trace(line.substring(pos));
}
} catch(IOException e) {
// for some reason, we couldn't split our message
HttpClient.this.log.trace(message);
}
}
});
logger.setLevel(HttpLoggingInterceptor.Level.BODY);
interceptors().add(logger);
}
}
public HttpClient(Logger log, Context context, String username, String password, boolean preemptive) {
this(log, context);
// authentication
this.username = username;
this.password = password;
if (preemptive)
networkInterceptors().add(new PreemptiveAuthenticationInterceptor(username, password));
else
setAuthenticator(new BasicDigestAuthenticator(null, username, password));
}
/**
* Creates a new HttpClient (based on another one) which can be used to download external resources:
* 1. it does not use preemptive authentication
* 2. it only authenticates against a given host
* @param client user name and password from this client will be used
* @param host authentication will be restricted to this host
*/
public HttpClient(Logger log, HttpClient client, String host) {
this(log, client.context);
username = client.username;
password = client.password;
setAuthenticator(new BasicDigestAuthenticator(host, username, password));
}
// for testing (mock server doesn't need auth)
public HttpClient() {
this(null, null, null, null, false);
}
static class UserAgentInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request().newBuilder()
.header("User-Agent", userAgent)
.build();
return chain.proceed(request);
}
}
@RequiredArgsConstructor
static class PreemptiveAuthenticationInterceptor implements Interceptor {
final String username, password;
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request().newBuilder()
.header("Authorization", Credentials.basic(username, password))
.build();
return chain.proceed(request);
}
}
}