Upgrade to okhttp3

pull/2/head
Ricki Hirner 9 years ago
parent ba0350c83d
commit 89050d88c6

@ -68,7 +68,7 @@ dependencies {
compile project(':MemorizingTrustManager')
androidTestCompile 'com.squareup.okhttp:mockwebserver:2.7.2'
androidTestCompile 'com.squareup.okhttp3:mockwebserver:3.0.1+'
compile 'dnsjava:dnsjava:2.1.7'
compile 'org.apache.commons:commons-lang3:3.4'
compile 'org.slf4j:slf4j-android:1.7.13'

@ -10,13 +10,14 @@ package at.bitfire.davdroid;
import android.test.InstrumentationTestCase;
import com.squareup.okhttp.ConnectionSpec;
import com.squareup.okhttp.HttpUrl;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Response;
import com.squareup.okhttp.TlsVersion;
import com.squareup.okhttp.mockwebserver.MockResponse;
import com.squareup.okhttp.mockwebserver.MockWebServer;
import okhttp3.ConnectionSpec;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.TlsVersion;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import java.io.IOException;
import java.net.URISyntaxException;
@ -25,11 +26,11 @@ import java.util.Collections;
public class HttpClientTest extends InstrumentationTestCase {
MockWebServer server;
HttpClient httpClient;
OkHttpClient httpClient;
@Override
public void setUp() throws IOException {
httpClient = new HttpClient(null, getInstrumentation().getTargetContext().getApplicationContext());
httpClient = HttpClient.create(getInstrumentation().getTargetContext().getApplicationContext());
server = new MockWebServer();
server.start();
@ -64,20 +65,4 @@ public class HttpClientTest extends InstrumentationTestCase {
// doesn't work for URLs with ports, see https://code.google.com/p/android/issues/detail?id=193475
}
public void testTLSVersion() throws IOException {
server.useHttps(new SSLSocketFactoryCompat(null), false);
assertEquals("https", server.url("/").scheme());
httpClient.setConnectionSpecs(Collections.singletonList(new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_2)
.build()));
// FIXME
/*server.enqueue(new MockResponse().setResponseCode(204));
Response response = httpClient.newCall(new Request.Builder()
.get().url(server.url("/"))
.build()).execute();
assertTrue(response.isSuccessful());*/
}
}

@ -10,7 +10,7 @@ package at.bitfire.davdroid;
import android.os.Build;
import com.squareup.okhttp.mockwebserver.MockWebServer;
import okhttp3.mockwebserver.MockWebServer;
import junit.framework.TestCase;

@ -2,9 +2,9 @@ package at.bitfire.davdroid.resource;
import android.test.InstrumentationTestCase;
import com.squareup.okhttp.HttpUrl;
import com.squareup.okhttp.mockwebserver.MockResponse;
import com.squareup.okhttp.mockwebserver.MockWebServer;
import okhttp3.HttpUrl;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import junit.framework.TestCase;

@ -11,13 +11,15 @@ 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 lombok.NonNull;
import okhttp3.CookieJar;
import okhttp3.Credentials;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.internal.tls.OkHostnameVerifier;
import okhttp3.logging.HttpLoggingInterceptor;
import org.slf4j.Logger;
@ -34,103 +36,110 @@ 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;
public class HttpClient {
private static final int MAX_LOG_LINE_LENGTH = 85;
final static UserAgentInterceptor userAgentInterceptor = new UserAgentInterceptor();
private static final OkHttpClient client = new OkHttpClient();
private static final UserAgentInterceptor userAgentInterceptor = new UserAgentInterceptor();
static final String userAgent;
private static final String userAgent;
static {
String date = new SimpleDateFormat("yyyy/MM/dd", Locale.US).format(new Date(BuildConfig.buildTime));
userAgent = "DAVdroid/" + BuildConfig.VERSION_NAME + " (" + date + "; dav4android) Android/" + Build.VERSION.RELEASE;
userAgent = "DAVdroid/" + BuildConfig.VERSION_NAME + " (" + date + "; dav4android; okhttp3) Android/" + Build.VERSION.RELEASE;
}
final Logger log;
final Context context;
protected String username, password;
private HttpClient() {
}
protected HttpClient(Logger log, Context context) {
super();
this.log = (log != null) ? log : Constants.log;
this.context = context;
public static OkHttpClient create(Context context) {
OkHttpClient.Builder builder = client.newBuilder();
if (context != null) {
// use MemorizingTrustManager to manage self-signed certificates
MemorizingTrustManager mtm = new MemorizingTrustManager(context);
setSslSocketFactory(new SSLSocketFactoryCompat(mtm));
setHostnameVerifier(mtm.wrapHostnameVerifier(OkHostnameVerifier.INSTANCE));
builder.sslSocketFactory(new SSLSocketFactoryCompat(mtm));
builder.hostnameVerifier(mtm.wrapHostnameVerifier(OkHostnameVerifier.INSTANCE));
}
// set timeouts
setConnectTimeout(30, TimeUnit.SECONDS);
setWriteTimeout(30, TimeUnit.SECONDS);
setReadTimeout(120, TimeUnit.SECONDS);
builder.connectTimeout(30, TimeUnit.SECONDS);
builder.writeTimeout(30, TimeUnit.SECONDS);
builder.readTimeout(120, TimeUnit.SECONDS);
// don't allow redirects, because it would break PROPFIND handling
builder.followRedirects(false);
// add User-Agent to every request
networkInterceptors().add(userAgentInterceptor);
builder.addNetworkInterceptor(userAgentInterceptor);
// add cookie store for non-persistent cookies (some services like Horde use cookies for session tracking)
CookieManager cookies = new CookieManager();
setCookieHandler(cookies);
builder.cookieJar(MemoryCookieStore.INSTANCE);
// 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);
}
return builder.build();
}
public HttpClient(Logger log, Context context, String username, String password, boolean preemptive) {
this(log, context);
// authentication
this.username = username;
this.password = password;
public static OkHttpClient addAuthentication(@NonNull OkHttpClient httpClient, @NonNull String username, @NonNull String password, boolean preemptive) {
OkHttpClient.Builder builder = httpClient.newBuilder();
if (preemptive)
networkInterceptors().add(new PreemptiveAuthenticationInterceptor(username, password));
builder.addNetworkInterceptor(new PreemptiveAuthenticationInterceptor(username, password));
else
setAuthenticator(new BasicDigestAuthenticator(null, username, password));
builder.authenticator(new BasicDigestAuthenticator(null, username, password));
return builder.build();
}
public static OkHttpClient addAuthentication(@NonNull OkHttpClient httpClient, @NonNull String host, @NonNull String username, @NonNull String password, boolean preemptive) {
return httpClient.newBuilder()
.authenticator(new BasicDigestAuthenticator(host, username, password))
.build();
}
//@NonNull final Logger log
/**
* 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 httpClient 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) {
/*public HttpClient(Logger log, HttpClient client, String host) {
this(log, client.context);
username = client.username;
password = client.password;
setAuthenticator(new BasicDigestAuthenticator(host, username, password));
}
}*/
public static OkHttpClient addLogger(@NonNull OkHttpClient httpClient, @NonNull final Logger logger) {
// enable verbose logs, if requested
if (logger.isTraceEnabled()) {
HttpLoggingInterceptor loggingInterceptor = 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)
logger.trace(line.substring(pos, pos + MAX_LOG_LINE_LENGTH) + "\\");
else
logger.trace(line.substring(pos));
}
} catch(IOException e) {
// for some reason, we couldn't split our message
logger.trace(message);
}
}
});
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
// for testing (mock server doesn't need auth)
public HttpClient() {
this(null, null, null, null, false);
return httpClient.newBuilder()
.addInterceptor(loggingInterceptor)
.build();
} else
return httpClient;
}

@ -0,0 +1,41 @@
/*
* Copyright © 2013 2016 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 java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import okhttp3.Cookie;
import okhttp3.CookieJar;
import okhttp3.HttpUrl;
/**
* Primitive cookie store that stores cookies in a (volatile) hash map.
* Will be sufficient for session cookies.
*/
public class MemoryCookieStore implements CookieJar {
public static final MemoryCookieStore INSTANCE = new MemoryCookieStore();
protected final Map<HttpUrl, List<Cookie>> store = new ConcurrentHashMap<>();
@Override
public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
store.put(url, cookies);
}
@Override
public List<Cookie> loadForRequest(HttpUrl url) {
return store.get(url);
}
}

@ -10,7 +10,7 @@ package at.bitfire.davdroid.resource;
import android.content.Context;
import android.text.TextUtils;
import com.squareup.okhttp.HttpUrl;
import okhttp3.HttpUrl;
import org.slf4j.Logger;
import org.xbill.DNS.Lookup;
@ -48,6 +48,7 @@ import at.bitfire.davdroid.ui.setup.LoginCredentialsFragment;
import lombok.Data;
import lombok.NonNull;
import lombok.ToString;
import okhttp3.OkHttpClient;
public class DavResourceFinder {
protected enum Service {
@ -59,11 +60,12 @@ public class DavResourceFinder {
@Override public String toString() { return name; }
};
protected final Logger log;
protected final Context context;
protected final HttpClient httpClient;
protected final Context context;
protected final LoginCredentialsFragment.LoginCredentials credentials;
protected final Logger log = new StringLogger("DavResourceFinder", true);
protected OkHttpClient httpClient;
protected HttpUrl carddavPrincipal, caldavPrincipal;
protected Map<HttpUrl, ServerConfiguration.Collection>
addressBooks = new HashMap<>(),
@ -74,8 +76,9 @@ public class DavResourceFinder {
this.context = context;
this.credentials = credentials;
log = new StringLogger("DavResourceFinder", true);
httpClient = new HttpClient(log, context, credentials.getUserName(), credentials.getPassword(), credentials.isAuthPreemptive());
httpClient = HttpClient.create(context);
httpClient = HttpClient.addLogger(httpClient, log);
httpClient = HttpClient.addAuthentication(httpClient, credentials.getUserName(), credentials.getPassword(), credentials.isAuthPreemptive());
}

@ -16,10 +16,10 @@ import android.os.Bundle;
import android.provider.CalendarContract.Calendars;
import android.text.TextUtils;
import com.squareup.okhttp.HttpUrl;
import com.squareup.okhttp.MediaType;
import com.squareup.okhttp.RequestBody;
import com.squareup.okhttp.ResponseBody;
import okhttp3.HttpUrl;
import okhttp3.MediaType;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import org.apache.commons.codec.Charsets;
import org.apache.commons.lang3.StringUtils;

@ -14,12 +14,13 @@ import android.content.Context;
import android.content.SyncResult;
import android.os.Bundle;
import com.squareup.okhttp.HttpUrl;
import com.squareup.okhttp.MediaType;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.RequestBody;
import com.squareup.okhttp.Response;
import com.squareup.okhttp.ResponseBody;
import okhttp3.HttpUrl;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import org.apache.commons.codec.Charsets;
import org.apache.commons.lang3.StringUtils;
@ -144,7 +145,7 @@ public class ContactsSyncManager extends SyncManager {
log.info("Downloading " + toDownload.size() + " contacts (" + MAX_MULTIGET + " at once)");
// prepare downloader which may be used to download external resource like contact photos
Contact.Downloader downloader = new ResourceDownloader(httpClient, collectionURL);
Contact.Downloader downloader = new ResourceDownloader(collectionURL);
// download new/updated VCards from server
for (DavResource[] bunch : ArrayUtils.partition(toDownload.toArray(new DavResource[toDownload.size()]), MAX_MULTIGET)) {
@ -258,7 +259,6 @@ public class ContactsSyncManager extends SyncManager {
@RequiredArgsConstructor
private class ResourceDownloader implements Contact.Downloader {
final HttpClient httpClient;
final HttpUrl baseUrl;
@Override
@ -276,7 +276,16 @@ public class ContactsSyncManager extends SyncManager {
return null;
}
HttpClient resourceClient = new HttpClient(log, httpClient, host);
OkHttpClient resourceClient = HttpClient.create(context);
// authenticate only against a certain host, and only upon request
resourceClient = HttpClient.addAuthentication(resourceClient, baseUrl.host(), settings.username(), settings.password(), false);
// allow redirects
resourceClient = resourceClient.newBuilder()
.followRedirects(true)
.build();
try {
Response response = resourceClient.newCall(new Request.Builder()
.get()

@ -20,8 +20,9 @@ package at.bitfire.davdroid.syncadapter;
import android.os.Bundle;
import android.text.TextUtils;
import com.squareup.okhttp.HttpUrl;
import com.squareup.okhttp.RequestBody;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.RequestBody;
import org.slf4j.Logger;
@ -81,7 +82,7 @@ abstract public class SyncManager {
protected Logger log;
protected final HttpClient httpClient;
protected OkHttpClient httpClient;
protected HttpUrl collectionURL;
protected DavResource davCollection;
@ -122,7 +123,9 @@ abstract public class SyncManager {
log = Constants.log;
// create HttpClient with given logger
httpClient = new HttpClient(log, context, settings.username(), settings.password(), settings.preemptiveAuth());
httpClient = HttpClient.create(context);
httpClient = HttpClient.addLogger(httpClient, log);
httpClient = HttpClient.addAuthentication(httpClient, settings.username(), settings.password(), settings.preemptiveAuth());
// dismiss previous error notifications
notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);

@ -15,10 +15,10 @@ import android.content.SyncResult;
import android.os.Bundle;
import android.text.TextUtils;
import com.squareup.okhttp.HttpUrl;
import com.squareup.okhttp.MediaType;
import com.squareup.okhttp.RequestBody;
import com.squareup.okhttp.ResponseBody;
import okhttp3.HttpUrl;
import okhttp3.MediaType;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import org.apache.commons.codec.Charsets;
import org.apache.commons.lang3.StringUtils;

@ -1 +1 @@
Subproject commit c67d40c47e792af812866289b6809dc8ccf49fd8
Subproject commit 94a5ee4aec98a29fe1c56046f9a6c71131db8b51
Loading…
Cancel
Save