1
0
mirror of https://github.com/square/okhttp.git synced 2026-01-27 04:22:07 +03:00

Merge pull request #64 from square/jwilson/tls_caching_tests

Restore tests for response caching + TLS.
This commit is contained in:
Jake Wharton
2012-12-22 11:14:50 -08:00
4 changed files with 151 additions and 118 deletions

View File

@@ -18,7 +18,6 @@
package com.squareup.okhttp.internal.net.http;
import com.squareup.okhttp.internal.util.Libcore;
import java.net.Socket;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.HashMap;
@@ -73,8 +72,7 @@ final class HttpConnectionPool {
}
if (connection.isEligibleForRecycling()) {
// Since Socket is recycled, re-tag before using
Socket socket = connection.getSocket();
Libcore.tagSocket(socket);
Libcore.tagSocket(connection.getSocket());
return connection;
}
}
@@ -91,9 +89,8 @@ final class HttpConnectionPool {
throw new IllegalArgumentException(); // TODO: just 'return' here?
}
Socket socket = connection.getSocket();
try {
Libcore.untagSocket(socket);
Libcore.untagSocket(connection.getSocket());
} catch (SocketException e) {
// When unable to remove tagging, skip recycling and close
Libcore.logW("Unable to untagSocket(): " + e);

View File

@@ -287,6 +287,7 @@ public class HttpEngine {
connection.connect(policy.getConnectTimeout(), policy.getReadTimeout(),
getTunnelConfig());
}
connected(connection);
Proxy proxy = connection.getProxy();
if (proxy != null) {
policy.setProxy(proxy);
@@ -295,6 +296,13 @@ public class HttpEngine {
}
}
/**
* Called after a socket connection has been created or retrieved from the
* pool. Subclasses use this hook to get a reference to the TLS data.
*/
protected void connected(HttpConnection connection) {
}
/**
* @param body the response body, or null if it doesn't exist or isn't
* available.

View File

@@ -395,14 +395,9 @@ public final class HttpsURLConnectionImpl extends HttpsURLConnection {
}
private static final class HttpsEngine extends HttpEngine {
/**
* Local stash of HttpsEngine.connection.sslSocket for answering
* queries such as getCipherSuite even after
* httpsEngine.Connection has been recycled. It's presence is also
* used to tell if the HttpsURLConnection is considered connected,
* as opposed to the connected field of URLConnection or the a
* non-null connect in HttpURLConnectionImpl
* Stash of HttpsEngine.connection.socket to implement requests like
* {@link #getCipherSuite} even after the connection has been recycled.
*/
private SSLSocket sslSocket;
@@ -420,6 +415,10 @@ public final class HttpsURLConnectionImpl extends HttpsURLConnection {
this.enclosing = enclosing;
}
@Override protected void connected(HttpConnection connection) {
this.sslSocket = (SSLSocket) connection.getSocket();
}
@Override protected boolean acceptCacheResponseType(CacheResponse cacheResponse) {
return cacheResponse instanceof SecureCacheResponse;
}

View File

@@ -21,6 +21,7 @@ import com.google.mockwebserver.MockWebServer;
import com.google.mockwebserver.RecordedRequest;
import static com.google.mockwebserver.SocketPolicy.DISCONNECT_AT_END;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.internal.net.ssl.SslContextBuilder;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
@@ -35,12 +36,17 @@ import java.net.CookieHandler;
import java.net.CookieManager;
import java.net.HttpCookie;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.ResponseCache;
import java.net.SecureCacheResponse;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.net.UnknownHostException;
import java.security.GeneralSecurityException;
import java.security.Principal;
import java.security.cert.Certificate;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
@@ -57,30 +63,47 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.zip.GZIPOutputStream;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import junit.framework.TestCase;
/**
* Android's HttpResponseCacheTest.
*/
public final class HttpResponseCacheTest extends TestCase {
private static final HostnameVerifier NULL_HOSTNAME_VERIFIER = new HostnameVerifier() {
@Override public boolean verify(String s, SSLSession sslSession) {
return true;
}
};
private final OkHttpClient client = new OkHttpClient();
private MockWebServer server = new MockWebServer();
private HttpResponseCache cache;
// private final MockOs mockOs = new MockOs();
private final CookieManager cookieManager = new CookieManager();
private static final SSLContext sslContext;
static {
try {
sslContext = new SslContextBuilder(InetAddress.getLocalHost().getHostName()).build();
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
} catch (UnknownHostException e) {
throw new RuntimeException(e);
}
}
@Override protected void setUp() throws Exception {
super.setUp();
String tmp = System.getProperty("java.io.tmpdir");
File cacheDir = new File(tmp, "HttpCache-" + UUID.randomUUID());
cache = new HttpResponseCache(cacheDir, Integer.MAX_VALUE);
ResponseCache.setDefault(cache);
// mockOs.install();
CookieHandler.setDefault(cookieManager);
}
@Override protected void tearDown() throws Exception {
// mockOs.uninstall();
server.shutdown();
ResponseCache.setDefault(null);
cache.getCache().delete();
@@ -88,8 +111,8 @@ public final class HttpResponseCacheTest extends TestCase {
super.tearDown();
}
private static HttpURLConnection openConnection(URL url) {
return new OkHttpClient().open(url);
private HttpURLConnection openConnection(URL url) {
return client.open(url);
}
/**
@@ -294,58 +317,61 @@ public final class HttpResponseCacheTest extends TestCase {
assertEquals(1, cache.getHitCount());
}
// public void testSecureResponseCaching() throws IOException {
// TestSSLContext testSSLContext = TestSSLContext.create();
// server.useHttps(testSSLContext.serverContext.getSocketFactory(), false);
// server.enqueue(new MockResponse()
// .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
// .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
// .setBody("ABC"));
// server.play();
//
// HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/").openConnection();
// connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
// assertEquals("ABC", readAscii(connection));
//
// // OpenJDK 6 fails on this line, complaining that the connection isn't open yet
// String suite = connection.getCipherSuite();
// List<Certificate> localCerts = toListOrNull(connection.getLocalCertificates());
// List<Certificate> serverCerts = toListOrNull(connection.getServerCertificates());
// Principal peerPrincipal = connection.getPeerPrincipal();
// Principal localPrincipal = connection.getLocalPrincipal();
//
// connection = (HttpsURLConnection) server.getUrl("/").openConnection(); // cached!
// connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
// assertEquals("ABC", readAscii(connection));
//
// assertEquals(2, cache.getRequestCount());
// assertEquals(1, cache.getNetworkCount());
// assertEquals(1, cache.getHitCount());
//
// assertEquals(suite, connection.getCipherSuite());
// assertEquals(localCerts, toListOrNull(connection.getLocalCertificates()));
// assertEquals(serverCerts, toListOrNull(connection.getServerCertificates()));
// assertEquals(peerPrincipal, connection.getPeerPrincipal());
// assertEquals(localPrincipal, connection.getLocalPrincipal());
// }
//
// public void testCacheReturnsInsecureResponseForSecureRequest() throws IOException {
// TestSSLContext testSSLContext = TestSSLContext.create();
// server.useHttps(testSSLContext.serverContext.getSocketFactory(), false);
// server.enqueue(new MockResponse().setBody("ABC"));
// server.enqueue(new MockResponse().setBody("DEF"));
// server.play();
//
// ResponseCache.setDefault(new InsecureResponseCache());
//
// HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/").openConnection();
// connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
// assertEquals("ABC", readAscii(connection));
//
// connection = (HttpsURLConnection) server.getUrl("/").openConnection(); // not cached!
// connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
// assertEquals("DEF", readAscii(connection));
// }
public void testSecureResponseCaching() throws IOException {
server.useHttps(sslContext.getSocketFactory(), false);
server.enqueue(new MockResponse()
.addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
.addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
.setBody("ABC"));
server.play();
HttpsURLConnection connection = (HttpsURLConnection) client.open(server.getUrl("/"));
connection.setSSLSocketFactory(sslContext.getSocketFactory());
connection.setHostnameVerifier(NULL_HOSTNAME_VERIFIER);
assertEquals("ABC", readAscii(connection));
// OpenJDK 6 fails on this line, complaining that the connection isn't open yet
String suite = connection.getCipherSuite();
List<Certificate> localCerts = toListOrNull(connection.getLocalCertificates());
List<Certificate> serverCerts = toListOrNull(connection.getServerCertificates());
Principal peerPrincipal = connection.getPeerPrincipal();
Principal localPrincipal = connection.getLocalPrincipal();
connection = (HttpsURLConnection) client.open(server.getUrl("/")); // cached!
connection.setSSLSocketFactory(sslContext.getSocketFactory());
connection.setHostnameVerifier(NULL_HOSTNAME_VERIFIER);
assertEquals("ABC", readAscii(connection));
assertEquals(2, cache.getRequestCount());
assertEquals(1, cache.getNetworkCount());
assertEquals(1, cache.getHitCount());
assertEquals(suite, connection.getCipherSuite());
assertEquals(localCerts, toListOrNull(connection.getLocalCertificates()));
assertEquals(serverCerts, toListOrNull(connection.getServerCertificates()));
assertEquals(peerPrincipal, connection.getPeerPrincipal());
assertEquals(localPrincipal, connection.getLocalPrincipal());
}
public void testCacheReturnsInsecureResponseForSecureRequest() throws IOException {
server.useHttps(sslContext.getSocketFactory(), false);
server.enqueue(new MockResponse().setBody("ABC"));
server.enqueue(new MockResponse().setBody("DEF"));
server.play();
ResponseCache.setDefault(new InsecureResponseCache());
HttpsURLConnection connection1 = (HttpsURLConnection) client.open(server.getUrl("/"));
connection1.setSSLSocketFactory(sslContext.getSocketFactory());
connection1.setHostnameVerifier(NULL_HOSTNAME_VERIFIER);
assertEquals("ABC", readAscii(connection1));
// Not cached!
HttpsURLConnection connection2 = (HttpsURLConnection) client.open(server.getUrl("/"));
connection2.setSSLSocketFactory(sslContext.getSocketFactory());
connection2.setHostnameVerifier(NULL_HOSTNAME_VERIFIER);
assertEquals("DEF", readAscii(connection2));
}
public void testResponseCachingAndRedirects() throws Exception {
server.enqueue(new MockResponse()
@@ -398,32 +424,34 @@ public final class HttpResponseCacheTest extends TestCase {
assertEquals(2, request3.getSequenceNumber());
}
// public void testSecureResponseCachingAndRedirects() throws IOException {
// TestSSLContext testSSLContext = TestSSLContext.create();
// server.useHttps(testSSLContext.serverContext.getSocketFactory(), false);
// server.enqueue(new MockResponse()
// .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
// .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
// .setResponseCode(HttpURLConnection.HTTP_MOVED_PERM)
// .addHeader("Location: /foo"));
// server.enqueue(new MockResponse()
// .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
// .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
// .setBody("ABC"));
// server.enqueue(new MockResponse().setBody("DEF"));
// server.play();
//
// HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/").openConnection();
// connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
// assertEquals("ABC", readAscii(connection));
//
// connection = (HttpsURLConnection) server.getUrl("/").openConnection(); // cached!
// connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
// assertEquals("ABC", readAscii(connection));
//
// assertEquals(4, cache.getRequestCount()); // 2 direct + 2 redirect = 4
// assertEquals(2, cache.getHitCount());
// }
public void testSecureResponseCachingAndRedirects() throws IOException {
server.useHttps(sslContext.getSocketFactory(), false);
server.enqueue(new MockResponse()
.addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
.addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
.setResponseCode(HttpURLConnection.HTTP_MOVED_PERM)
.addHeader("Location: /foo"));
server.enqueue(new MockResponse()
.addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
.addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
.setBody("ABC"));
server.enqueue(new MockResponse().setBody("DEF"));
server.play();
HttpsURLConnection connection1 = (HttpsURLConnection) client.open(server.getUrl("/"));
connection1.setSSLSocketFactory(sslContext.getSocketFactory());
connection1.setHostnameVerifier(NULL_HOSTNAME_VERIFIER);
assertEquals("ABC", readAscii(connection1));
// Cached!
HttpsURLConnection connection2 = (HttpsURLConnection) client.open(server.getUrl("/"));
connection1.setSSLSocketFactory(sslContext.getSocketFactory());
connection1.setHostnameVerifier(NULL_HOSTNAME_VERIFIER);
assertEquals("ABC", readAscii(connection2));
assertEquals(4, cache.getRequestCount()); // 2 direct + 2 redirect = 4
assertEquals(2, cache.getHitCount());
}
public void testResponseCacheRequestHeaders() throws IOException, URISyntaxException {
server.enqueue(new MockResponse().setBody("ABC"));
@@ -1459,28 +1487,29 @@ public final class HttpResponseCacheTest extends TestCase {
assertEquals("B", readAscii(openConnection(server.getUrl("/"))));
}
// public void testVaryAndHttps() throws Exception {
// TestSSLContext testSSLContext = TestSSLContext.create();
// server.useHttps(testSSLContext.serverContext.getSocketFactory(), false);
// server.enqueue(new MockResponse()
// .addHeader("Cache-Control: max-age=60")
// .addHeader("Vary: Accept-Language")
// .setBody("A"));
// server.enqueue(new MockResponse().setBody("B"));
// server.play();
//
// URL url = server.getUrl("/");
// HttpsURLConnection connection1 = (HttpsURLConnection) url.openConnection();
// connection1.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
// connection1.addRequestProperty("Accept-Language", "en-US");
// assertEquals("A", readAscii(connection1));
//
// HttpsURLConnection connection2 = (HttpsURLConnection) url.openConnection();
// connection2.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
// connection2.addRequestProperty("Accept-Language", "en-US");
// assertEquals("A", readAscii(connection2));
// }
//
public void testVaryAndHttps() throws Exception {
server.useHttps(sslContext.getSocketFactory(), false);
server.enqueue(new MockResponse()
.addHeader("Cache-Control: max-age=60")
.addHeader("Vary: Accept-Language")
.setBody("A"));
server.enqueue(new MockResponse().setBody("B"));
server.play();
URL url = server.getUrl("/");
HttpsURLConnection connection1 = (HttpsURLConnection) client.open(url);
connection1.setSSLSocketFactory(sslContext.getSocketFactory());
connection1.setHostnameVerifier(NULL_HOSTNAME_VERIFIER);
connection1.addRequestProperty("Accept-Language", "en-US");
assertEquals("A", readAscii(connection1));
HttpsURLConnection connection2 = (HttpsURLConnection) client.open(url);
connection2.setSSLSocketFactory(sslContext.getSocketFactory());
connection2.setHostnameVerifier(NULL_HOSTNAME_VERIFIER);
connection2.addRequestProperty("Accept-Language", "en-US");
assertEquals("A", readAscii(connection2));
}
// public void testDiskWriteFailureCacheDegradation() throws Exception {
// Deque<InvocationHandler> writeHandlers = mockOs.getHandlers("write");
// int i = 0;