diff --git a/pom.xml b/pom.xml index 88794b4a5372..336c964ea68b 100644 --- a/pom.xml +++ b/pom.xml @@ -38,7 +38,7 @@ 1.6 8.1.2.v20120308 - 20120401 + 20120731 1.47 diff --git a/src/main/java/com/squareup/okhttp/OkHttpConnection.java b/src/main/java/com/squareup/okhttp/OkHttpConnection.java index c937d219b132..5df657a92b3c 100644 --- a/src/main/java/com/squareup/okhttp/OkHttpConnection.java +++ b/src/main/java/com/squareup/okhttp/OkHttpConnection.java @@ -514,12 +514,33 @@ public abstract class OkHttpConnection extends URLConnection { */ public static final int HTTP_VERSION = 505; + /** + * Returns a new OkHttpConnection or OkHttpsConnection to {@code url}. + */ public static OkHttpConnection open(URL url) { - return new libcore.net.http.HttpURLConnectionImpl(url, 443); + String protocol = url.getProtocol(); + if (protocol.equals("http")) { + return new libcore.net.http.HttpURLConnectionImpl(url, 80); + } else if (protocol.equals("https")) { + return new libcore.net.http.HttpsURLConnectionImpl(url, 443); + } else { + throw new IllegalArgumentException(); + } } + /** + * Returns a new OkHttpConnection or OkHttpsConnection to {@code url} that + * connects via {@code proxy}. + */ public static OkHttpConnection open(URL url, Proxy proxy) { - return new libcore.net.http.HttpURLConnectionImpl(url, 443, proxy); + String protocol = url.getProtocol(); + if (protocol.equals("http")) { + return new libcore.net.http.HttpURLConnectionImpl(url, 80, proxy); + } else if (protocol.equals("https")) { + return new libcore.net.http.HttpsURLConnectionImpl(url, 443, proxy); + } else { + throw new IllegalArgumentException(); + } } /** diff --git a/src/main/java/com/squareup/okhttp/OkHttpsConnection.java b/src/main/java/com/squareup/okhttp/OkHttpsConnection.java index 58f0b8ae0c3c..669a26e93184 100644 --- a/src/main/java/com/squareup/okhttp/OkHttpsConnection.java +++ b/src/main/java/com/squareup/okhttp/OkHttpsConnection.java @@ -17,7 +17,6 @@ package com.squareup.okhttp; -import java.net.Proxy; import java.net.URL; import java.security.Principal; import java.security.cert.Certificate; @@ -113,14 +112,6 @@ public abstract class OkHttpsConnection extends OkHttpConnection { private static SSLSocketFactory defaultSSLSocketFactory = (SSLSocketFactory) SSLSocketFactory .getDefault(); - public static OkHttpsConnection open(URL url) { - return new libcore.net.http.HttpsURLConnectionImpl(url, 443); - } - - public static OkHttpsConnection open(URL url, Proxy proxy) { - return new libcore.net.http.HttpsURLConnectionImpl(url, 443, proxy); - } - /** * Sets the default hostname verifier to be used by new instances. * diff --git a/src/test/java/libcore/net/http/ExternalSpdyExample.java b/src/test/java/libcore/net/http/ExternalSpdyExample.java index 29aa72da19fd..dc8aa1ed3551 100644 --- a/src/test/java/libcore/net/http/ExternalSpdyExample.java +++ b/src/test/java/libcore/net/http/ExternalSpdyExample.java @@ -16,6 +16,7 @@ package libcore.net.http; +import com.squareup.okhttp.OkHttpConnection; import com.squareup.okhttp.OkHttpsConnection; import java.io.BufferedReader; import java.io.InputStreamReader; @@ -26,7 +27,7 @@ public final class ExternalSpdyExample { public static void main(String[] args) throws Exception { URL url = new URL("https://www.google.ca/"); - OkHttpsConnection connection = OkHttpsConnection.open(url); + OkHttpsConnection connection = (OkHttpsConnection) OkHttpConnection.open(url); connection.setHostnameVerifier(new HostnameVerifier() { @Override public boolean verify(String s, SSLSession sslSession) { diff --git a/src/test/java/libcore/net/http/URLConnectionTest.java b/src/test/java/libcore/net/http/URLConnectionTest.java index 4c5398623800..993135c20f9d 100644 --- a/src/test/java/libcore/net/http/URLConnectionTest.java +++ b/src/test/java/libcore/net/http/URLConnectionTest.java @@ -31,6 +31,7 @@ import java.net.CacheResponse; import java.net.ConnectException; import java.net.HttpRetryException; +import java.net.HttpURLConnection; import java.net.InetAddress; import java.net.PasswordAuthentication; import java.net.ProtocolException; @@ -41,10 +42,13 @@ import java.net.URISyntaxException; import java.net.URL; import java.net.URLConnection; +import java.net.UnknownHostException; +import java.security.GeneralSecurityException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -56,7 +60,9 @@ import java.util.zip.GZIPOutputStream; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLException; import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.X509TrustManager; import junit.framework.TestCase; import libcore.net.ssl.SslContextBuilder; @@ -83,6 +89,19 @@ protected PasswordAuthentication getPasswordAuthentication() { private MockWebServer server = new MockWebServer(); private String hostName; + 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(); hostName = server.getHostName(); @@ -105,6 +124,10 @@ private static OkHttpConnection openConnection(URL url) { return OkHttpConnection.open(url); } + private static OkHttpConnection openConnection(URL url, Proxy proxy) { + return OkHttpConnection.open(url, proxy); + } + public void testRequestHeaders() throws IOException, InterruptedException { server.enqueue(new MockResponse()); server.play(); @@ -386,87 +409,85 @@ public void testGetResponseCodeNoResponseBody() throws Exception { } public void testConnectViaHttps() throws Exception { - SSLContext sslContext = new SslContextBuilder(InetAddress.getLocalHost().getHostName()) - .build(); + server.useHttps(sslContext.getSocketFactory(), false); + server.enqueue(new MockResponse().setBody("this response comes via HTTPS")); + server.play(); + + OkHttpsConnection connection = (OkHttpsConnection) openConnection(server.getUrl("/foo")); + connection.setSSLSocketFactory(sslContext.getSocketFactory()); + connection.setHostnameVerifier(new RecordingHostnameVerifier()); + assertContent("this response comes via HTTPS", connection); + + RecordedRequest request = server.takeRequest(); + assertEquals("GET /foo HTTP/1.1", request.getRequestLine()); + } + + public void testConnectViaHttpsReusingConnections() throws IOException, InterruptedException { server.useHttps(sslContext.getSocketFactory(), false); server.enqueue(new MockResponse().setBody("this response comes via HTTPS")); + server.enqueue(new MockResponse().setBody("another response via HTTPS")); server.play(); - OkHttpsConnection connection = OkHttpsConnection.open(server.getUrl("/foo")); + // The pool will only reuse sockets if the SSL socket factories are the same. + SSLSocketFactory clientSocketFactory = sslContext.getSocketFactory(); + + OkHttpsConnection connection = (OkHttpsConnection) openConnection(server.getUrl("/")); + connection.setSSLSocketFactory(clientSocketFactory); connection.setHostnameVerifier(new RecordingHostnameVerifier()); - connection.setSSLSocketFactory(sslContext.getSocketFactory()); + assertContent("this response comes via HTTPS", connection); + + connection = (OkHttpsConnection) openConnection(server.getUrl("/")); + connection.setSSLSocketFactory(clientSocketFactory); + connection.setHostnameVerifier(new RecordingHostnameVerifier()); + assertContent("another response via HTTPS", connection); + + assertEquals(0, server.takeRequest().getSequenceNumber()); + assertEquals(1, server.takeRequest().getSequenceNumber()); + } + + public void testConnectViaHttpsReusingConnectionsDifferentFactories() + throws IOException, InterruptedException { + server.useHttps(sslContext.getSocketFactory(), false); + server.enqueue(new MockResponse().setBody("this response comes via HTTPS")); + server.enqueue(new MockResponse().setBody("another response via HTTPS")); + server.play(); + // install a custom SSL socket factory so the server can be authorized + OkHttpsConnection connection = (OkHttpsConnection) openConnection(server.getUrl("/")); + connection.setSSLSocketFactory(sslContext.getSocketFactory()); + connection.setHostnameVerifier(new RecordingHostnameVerifier()); assertContent("this response comes via HTTPS", connection); + connection = (OkHttpsConnection) openConnection(server.getUrl("/")); + try { + readAscii(connection.getInputStream(), Integer.MAX_VALUE); + fail("without an SSL socket factory, the connection should fail"); + } catch (SSLException expected) { + } + } + + public void testConnectViaHttpsWithSSLFallback() throws IOException, InterruptedException { + server.useHttps(sslContext.getSocketFactory(), false); + server.enqueue(new MockResponse().setSocketPolicy(DISCONNECT_AT_START)); + server.enqueue(new MockResponse().setBody("this response comes via SSL")); + server.play(); + + OkHttpsConnection connection = (OkHttpsConnection) openConnection(server.getUrl("/foo")); + connection.setSSLSocketFactory(sslContext.getSocketFactory()); + connection.setHostnameVerifier(new RecordingHostnameVerifier()); + + assertContent("this response comes via SSL", connection); + RecordedRequest request = server.takeRequest(); assertEquals("GET /foo HTTP/1.1", request.getRequestLine()); } -// public void testConnectViaHttpsReusingConnections() throws IOException, InterruptedException { -// TestSSLContext testSSLContext = TestSSLContext.create(); -// -// server.useHttps(testSSLContext.serverContext.getSocketFactory(), false); -// server.enqueue(new MockResponse().setBody("this response comes via HTTPS")); -// server.enqueue(new MockResponse().setBody("another response via HTTPS")); -// server.play(); -// -// HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/").openConnection(); -// connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory()); -// assertContent("this response comes via HTTPS", connection); -// -// connection = (HttpsURLConnection) server.getUrl("/").openConnection(); -// connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory()); -// assertContent("another response via HTTPS", connection); -// -// assertEquals(0, server.takeRequest().getSequenceNumber()); -// assertEquals(1, server.takeRequest().getSequenceNumber()); -// } -// -// public void testConnectViaHttpsReusingConnectionsDifferentFactories() -// throws IOException, InterruptedException { -// TestSSLContext testSSLContext = TestSSLContext.create(); -// -// server.useHttps(testSSLContext.serverContext.getSocketFactory(), false); -// server.enqueue(new MockResponse().setBody("this response comes via HTTPS")); -// server.enqueue(new MockResponse().setBody("another response via HTTPS")); -// server.play(); -// -// // install a custom SSL socket factory so the server can be authorized -// HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/").openConnection(); -// connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory()); -// assertContent("this response comes via HTTPS", connection); -// -// connection = (HttpsURLConnection) server.getUrl("/").openConnection(); -// try { -// readAscii(connection.getInputStream(), Integer.MAX_VALUE); -// fail("without an SSL socket factory, the connection should fail"); -// } catch (SSLException expected) { -// } -// } -// -// public void testConnectViaHttpsWithSSLFallback() throws IOException, InterruptedException { -// TestSSLContext testSSLContext = TestSSLContext.create(); -// -// server.useHttps(testSSLContext.serverContext.getSocketFactory(), false); -// server.enqueue(new MockResponse().setSocketPolicy(DISCONNECT_AT_START)); -// server.enqueue(new MockResponse().setBody("this response comes via SSL")); -// server.play(); -// -// HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/foo").openConnection(); -// connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory()); -// -// assertContent("this response comes via SSL", connection); -// -// RecordedRequest request = server.takeRequest(); -// assertEquals("GET /foo HTTP/1.1", request.getRequestLine()); -// } -// -// /** -// * Verify that we don't retry connections on certificate verification errors. -// * -// * http://code.google.com/p/android/issues/detail?id=13178 -// */ + /** + * Verify that we don't retry connections on certificate verification errors. + * + * http://code.google.com/p/android/issues/detail?id=13178 + */ // public void testConnectViaHttpsToUntrustedServer() throws IOException, InterruptedException { // TestSSLContext testSSLContext = TestSSLContext.create(TestKeyStore.getClientCA2(), // TestKeyStore.getServer()); @@ -538,150 +559,146 @@ public void testContentDisagreesWithChunkedHeader() throws IOException { assertContent("abc", openConnection(server.getUrl("/"))); } -// public void testConnectViaHttpProxyToHttpsUsingProxyArgWithNoProxy() throws Exception { -// testConnectViaDirectProxyToHttps(ProxyConfig.NO_PROXY); -// } -// -// public void testConnectViaHttpProxyToHttpsUsingHttpProxySystemProperty() throws Exception { -// // https should not use http proxy -// testConnectViaDirectProxyToHttps(ProxyConfig.HTTP_PROXY_SYSTEM_PROPERTY); -// } -// -// private void testConnectViaDirectProxyToHttps(ProxyConfig proxyConfig) throws Exception { -// TestSSLContext testSSLContext = TestSSLContext.create(); -// -// server.useHttps(testSSLContext.serverContext.getSocketFactory(), false); -// server.enqueue(new MockResponse().setBody("this response comes via HTTPS")); -// server.play(); -// -// URL url = server.getUrl("/foo"); -// HttpsURLConnection connection = (HttpsURLConnection) proxyConfig.connect(server, url); -// connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory()); -// -// assertContent("this response comes via HTTPS", connection); -// -// RecordedRequest request = server.takeRequest(); -// assertEquals("GET /foo HTTP/1.1", request.getRequestLine()); -// } -// -// public void testConnectViaHttpProxyToHttpsUsingProxyArg() throws Exception { -// testConnectViaHttpProxyToHttps(ProxyConfig.CREATE_ARG); -// } -// -// /** -// * We weren't honoring all of the appropriate proxy system properties when -// * connecting via HTTPS. http://b/3097518 -// */ -// public void testConnectViaHttpProxyToHttpsUsingProxySystemProperty() throws Exception { -// testConnectViaHttpProxyToHttps(ProxyConfig.PROXY_SYSTEM_PROPERTY); -// } -// -// public void testConnectViaHttpProxyToHttpsUsingHttpsProxySystemProperty() throws Exception { -// testConnectViaHttpProxyToHttps(ProxyConfig.HTTPS_PROXY_SYSTEM_PROPERTY); -// } -// -// /** -// * We were verifying the wrong hostname when connecting to an HTTPS site -// * through a proxy. http://b/3097277 -// */ -// private void testConnectViaHttpProxyToHttps(ProxyConfig proxyConfig) throws Exception { -// TestSSLContext testSSLContext = TestSSLContext.create(); -// RecordingHostnameVerifier hostnameVerifier = new RecordingHostnameVerifier(); -// -// server.useHttps(testSSLContext.serverContext.getSocketFactory(), true); -// server.enqueue(new MockResponse() -// .setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END) -// .clearHeaders()); -// server.enqueue(new MockResponse().setBody("this response comes via a secure proxy")); -// server.play(); -// -// URL url = new URL("https://android.com/foo"); -// HttpsURLConnection connection = (HttpsURLConnection) proxyConfig.connect(server, url); -// connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory()); -// connection.setHostnameVerifier(hostnameVerifier); -// -// assertContent("this response comes via a secure proxy", connection); -// -// RecordedRequest connect = server.takeRequest(); -// assertEquals("Connect line failure on proxy", -// "CONNECT android.com:443 HTTP/1.1", connect.getRequestLine()); -// assertContains(connect.getHeaders(), "Host: android.com"); -// -// RecordedRequest get = server.takeRequest(); -// assertEquals("GET /foo HTTP/1.1", get.getRequestLine()); -// assertContains(get.getHeaders(), "Host: android.com"); -// assertEquals(Arrays.asList("verify android.com"), hostnameVerifier.calls); -// } -// -// /** -// * Test which headers are sent unencrypted to the HTTP proxy. -// */ -// public void testProxyConnectIncludesProxyHeadersOnly() -// throws IOException, InterruptedException { -// RecordingHostnameVerifier hostnameVerifier = new RecordingHostnameVerifier(); -// TestSSLContext testSSLContext = TestSSLContext.create(); -// -// server.useHttps(testSSLContext.serverContext.getSocketFactory(), true); -// server.enqueue(new MockResponse() -// .setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END) -// .clearHeaders()); -// server.enqueue(new MockResponse().setBody("encrypted response from the origin server")); -// server.play(); -// -// URL url = new URL("https://android.com/foo"); -// HttpsURLConnection connection = (HttpsURLConnection) url.openConnection( -// server.toProxyAddress()); -// connection.addRequestProperty("Private", "Secret"); -// connection.addRequestProperty("Proxy-Authorization", "bar"); -// connection.addRequestProperty("User-Agent", "baz"); -// connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory()); -// connection.setHostnameVerifier(hostnameVerifier); -// assertContent("encrypted response from the origin server", connection); -// -// RecordedRequest connect = server.takeRequest(); -// assertContainsNoneMatching(connect.getHeaders(), "Private.*"); -// assertContains(connect.getHeaders(), "Proxy-Authorization: bar"); -// assertContains(connect.getHeaders(), "User-Agent: baz"); -// assertContains(connect.getHeaders(), "Host: android.com"); -// assertContains(connect.getHeaders(), "Proxy-Connection: Keep-Alive"); -// -// RecordedRequest get = server.takeRequest(); -// assertContains(get.getHeaders(), "Private: Secret"); -// assertEquals(Arrays.asList("verify android.com"), hostnameVerifier.calls); -// } -// -// public void testProxyAuthenticateOnConnect() throws Exception { -// Authenticator.setDefault(SIMPLE_AUTHENTICATOR); -// TestSSLContext testSSLContext = TestSSLContext.create(); -// server.useHttps(testSSLContext.serverContext.getSocketFactory(), true); -// server.enqueue(new MockResponse() -// .setResponseCode(407) -// .addHeader("Proxy-Authenticate: Basic realm=\"localhost\"")); -// server.enqueue(new MockResponse() -// .setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END) -// .clearHeaders()); -// server.enqueue(new MockResponse().setBody("A")); -// server.play(); -// -// URL url = new URL("https://android.com/foo"); -// HttpsURLConnection connection = (HttpsURLConnection) url.openConnection( -// server.toProxyAddress()); -// connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory()); -// connection.setHostnameVerifier(new RecordingHostnameVerifier()); -// assertContent("A", connection); -// -// RecordedRequest connect1 = server.takeRequest(); -// assertEquals("CONNECT android.com:443 HTTP/1.1", connect1.getRequestLine()); -// assertContainsNoneMatching(connect1.getHeaders(), "Proxy\\-Authorization.*"); -// -// RecordedRequest connect2 = server.takeRequest(); -// assertEquals("CONNECT android.com:443 HTTP/1.1", connect2.getRequestLine()); -// assertContains(connect2.getHeaders(), "Proxy-Authorization: Basic " + BASE_64_CREDENTIALS); -// -// RecordedRequest get = server.takeRequest(); -// assertEquals("GET /foo HTTP/1.1", get.getRequestLine()); -// assertContainsNoneMatching(get.getHeaders(), "Proxy\\-Authorization.*"); -// } + public void testConnectViaHttpProxyToHttpsUsingProxyArgWithNoProxy() throws Exception { + testConnectViaDirectProxyToHttps(ProxyConfig.NO_PROXY); + } + + public void testConnectViaHttpProxyToHttpsUsingHttpProxySystemProperty() throws Exception { + // https should not use http proxy + testConnectViaDirectProxyToHttps(ProxyConfig.HTTP_PROXY_SYSTEM_PROPERTY); + } + + private void testConnectViaDirectProxyToHttps(ProxyConfig proxyConfig) throws Exception { + server.useHttps(sslContext.getSocketFactory(), false); + server.enqueue(new MockResponse().setBody("this response comes via HTTPS")); + server.play(); + + URL url = server.getUrl("/foo"); + OkHttpsConnection connection = (OkHttpsConnection) proxyConfig.connect(server, url); + connection.setSSLSocketFactory(sslContext.getSocketFactory()); + connection.setHostnameVerifier(new RecordingHostnameVerifier()); + + assertContent("this response comes via HTTPS", connection); + + RecordedRequest request = server.takeRequest(); + assertEquals("GET /foo HTTP/1.1", request.getRequestLine()); + } + + public void testConnectViaHttpProxyToHttpsUsingProxyArg() throws Exception { + testConnectViaHttpProxyToHttps(ProxyConfig.CREATE_ARG); + } + + /** + * We weren't honoring all of the appropriate proxy system properties when + * connecting via HTTPS. http://b/3097518 + */ + public void testConnectViaHttpProxyToHttpsUsingProxySystemProperty() throws Exception { + testConnectViaHttpProxyToHttps(ProxyConfig.PROXY_SYSTEM_PROPERTY); + } + + public void testConnectViaHttpProxyToHttpsUsingHttpsProxySystemProperty() throws Exception { + testConnectViaHttpProxyToHttps(ProxyConfig.HTTPS_PROXY_SYSTEM_PROPERTY); + } + + /** + * We were verifying the wrong hostname when connecting to an HTTPS site + * through a proxy. http://b/3097277 + */ + private void testConnectViaHttpProxyToHttps(ProxyConfig proxyConfig) throws Exception { + RecordingHostnameVerifier hostnameVerifier = new RecordingHostnameVerifier(); + + server.useHttps(sslContext.getSocketFactory(), true); + server.enqueue(new MockResponse() + .setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END) + .clearHeaders()); + server.enqueue(new MockResponse().setBody("this response comes via a secure proxy")); + server.play(); + + URL url = new URL("https://android.com/foo"); + OkHttpsConnection connection = (OkHttpsConnection) proxyConfig.connect(server, url); + connection.setSSLSocketFactory(sslContext.getSocketFactory()); + connection.setHostnameVerifier(hostnameVerifier); + + assertContent("this response comes via a secure proxy", connection); + + RecordedRequest connect = server.takeRequest(); + assertEquals("Connect line failure on proxy", + "CONNECT android.com:443 HTTP/1.1", connect.getRequestLine()); + assertContains(connect.getHeaders(), "Host: android.com"); + + RecordedRequest get = server.takeRequest(); + assertEquals("GET /foo HTTP/1.1", get.getRequestLine()); + assertContains(get.getHeaders(), "Host: android.com"); + assertEquals(Arrays.asList("verify android.com"), hostnameVerifier.calls); + } + + /** + * Test which headers are sent unencrypted to the HTTP proxy. + */ + public void testProxyConnectIncludesProxyHeadersOnly() + throws IOException, InterruptedException { + RecordingHostnameVerifier hostnameVerifier = new RecordingHostnameVerifier(); + + server.useHttps(sslContext.getSocketFactory(), true); + server.enqueue(new MockResponse() + .setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END) + .clearHeaders()); + server.enqueue(new MockResponse().setBody("encrypted response from the origin server")); + server.play(); + + URL url = new URL("https://android.com/foo"); + OkHttpsConnection connection = (OkHttpsConnection) openConnection( + url, server.toProxyAddress()); + connection.addRequestProperty("Private", "Secret"); + connection.addRequestProperty("Proxy-Authorization", "bar"); + connection.addRequestProperty("User-Agent", "baz"); + connection.setSSLSocketFactory(sslContext.getSocketFactory()); + connection.setHostnameVerifier(hostnameVerifier); + assertContent("encrypted response from the origin server", connection); + + RecordedRequest connect = server.takeRequest(); + assertContainsNoneMatching(connect.getHeaders(), "Private.*"); + assertContains(connect.getHeaders(), "Proxy-Authorization: bar"); + assertContains(connect.getHeaders(), "User-Agent: baz"); + assertContains(connect.getHeaders(), "Host: android.com"); + assertContains(connect.getHeaders(), "Proxy-Connection: Keep-Alive"); + + RecordedRequest get = server.takeRequest(); + assertContains(get.getHeaders(), "Private: Secret"); + assertEquals(Arrays.asList("verify android.com"), hostnameVerifier.calls); + } + + public void testProxyAuthenticateOnConnect() throws Exception { + Authenticator.setDefault(SIMPLE_AUTHENTICATOR); + server.useHttps(sslContext.getSocketFactory(), true); + server.enqueue(new MockResponse() + .setResponseCode(407) + .addHeader("Proxy-Authenticate: Basic realm=\"localhost\"")); + server.enqueue(new MockResponse() + .setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END) + .clearHeaders()); + server.enqueue(new MockResponse().setBody("A")); + server.play(); + + URL url = new URL("https://android.com/foo"); + OkHttpsConnection connection = (OkHttpsConnection) openConnection( + url, server.toProxyAddress()); + connection.setSSLSocketFactory(sslContext.getSocketFactory()); + connection.setHostnameVerifier(new RecordingHostnameVerifier()); + assertContent("A", connection); + + RecordedRequest connect1 = server.takeRequest(); + assertEquals("CONNECT android.com:443 HTTP/1.1", connect1.getRequestLine()); + assertContainsNoneMatching(connect1.getHeaders(), "Proxy\\-Authorization.*"); + + RecordedRequest connect2 = server.takeRequest(); + assertEquals("CONNECT android.com:443 HTTP/1.1", connect2.getRequestLine()); + assertContains(connect2.getHeaders(), "Proxy-Authorization: Basic " + BASE_64_CREDENTIALS); + + RecordedRequest get = server.takeRequest(); + assertEquals("GET /foo HTTP/1.1", get.getRequestLine()); + assertContainsNoneMatching(get.getHeaders(), "Proxy\\-Authorization.*"); + } public void testDisconnectedConnection() throws IOException { server.enqueue(new MockResponse().setBody("ABCDEFGHIJKLMNOPQR")); @@ -1062,47 +1079,47 @@ public void testCannotSetChunkedStreamingModeAfterFixedLengthStreamingMode() thr } } -// public void testSecureFixedLengthStreaming() throws Exception { -// testSecureStreamingPost(StreamingMode.FIXED_LENGTH); -// } -// -// public void testSecureChunkedStreaming() throws Exception { -// testSecureStreamingPost(StreamingMode.CHUNKED); -// } + public void testSecureFixedLengthStreaming() throws Exception { + testSecureStreamingPost(StreamingMode.FIXED_LENGTH); + } + + public void testSecureChunkedStreaming() throws Exception { + testSecureStreamingPost(StreamingMode.CHUNKED); + } /** * Users have reported problems using HTTPS with streaming request bodies. * http://code.google.com/p/android/issues/detail?id=12860 */ -// private void testSecureStreamingPost(StreamingMode streamingMode) throws Exception { -// TestSSLContext testSSLContext = TestSSLContext.create(); -// server.useHttps(testSSLContext.serverContext.getSocketFactory(), false); -// server.enqueue(new MockResponse().setBody("Success!")); -// server.play(); -// -// HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/").openConnection(); -// connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory()); -// connection.setDoOutput(true); -// byte[] requestBody = { 'A', 'B', 'C', 'D' }; -// if (streamingMode == StreamingMode.FIXED_LENGTH) { -// connection.setFixedLengthStreamingMode(requestBody.length); -// } else if (streamingMode == StreamingMode.CHUNKED) { -// connection.setChunkedStreamingMode(0); -// } -// OutputStream outputStream = connection.getOutputStream(); -// outputStream.write(requestBody); -// outputStream.close(); -// assertEquals("Success!", readAscii(connection.getInputStream(), Integer.MAX_VALUE)); -// -// RecordedRequest request = server.takeRequest(); -// assertEquals("POST / HTTP/1.1", request.getRequestLine()); -// if (streamingMode == StreamingMode.FIXED_LENGTH) { -// assertEquals(Collections.emptyList(), request.getChunkSizes()); -// } else if (streamingMode == StreamingMode.CHUNKED) { -// assertEquals(Arrays.asList(4), request.getChunkSizes()); -// } -// assertEquals(Arrays.toString(requestBody), Arrays.toString(request.getBody())); -// } + private void testSecureStreamingPost(StreamingMode streamingMode) throws Exception { + server.useHttps(sslContext.getSocketFactory(), false); + server.enqueue(new MockResponse().setBody("Success!")); + server.play(); + + OkHttpsConnection connection = (OkHttpsConnection) openConnection(server.getUrl("/")); + connection.setSSLSocketFactory(sslContext.getSocketFactory()); + connection.setHostnameVerifier(new RecordingHostnameVerifier()); + connection.setDoOutput(true); + byte[] requestBody = { 'A', 'B', 'C', 'D' }; + if (streamingMode == StreamingMode.FIXED_LENGTH) { + connection.setFixedLengthStreamingMode(requestBody.length); + } else if (streamingMode == StreamingMode.CHUNKED) { + connection.setChunkedStreamingMode(0); + } + OutputStream outputStream = connection.getOutputStream(); + outputStream.write(requestBody); + outputStream.close(); + assertEquals("Success!", readAscii(connection.getInputStream(), Integer.MAX_VALUE)); + + RecordedRequest request = server.takeRequest(); + assertEquals("POST / HTTP/1.1", request.getRequestLine()); + if (streamingMode == StreamingMode.FIXED_LENGTH) { + assertEquals(Collections.emptyList(), request.getChunkSizes()); + } else if (streamingMode == StreamingMode.CHUNKED) { + assertEquals(Arrays.asList(4), request.getChunkSizes()); + } + assertEquals(Arrays.toString(requestBody), Arrays.toString(request.getBody())); + } enum StreamingMode { FIXED_LENGTH, CHUNKED @@ -1206,42 +1223,42 @@ private void testRedirected(TransferKind transferKind, boolean reuse) throws Exc } } -// public void testRedirectedOnHttps() throws IOException, InterruptedException { -// TestSSLContext testSSLContext = TestSSLContext.create(); -// server.useHttps(testSSLContext.serverContext.getSocketFactory(), false); -// server.enqueue(new MockResponse() -// .setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP) -// .addHeader("Location: /foo") -// .setBody("This page has moved!")); -// server.enqueue(new MockResponse().setBody("This is the new location!")); -// server.play(); -// -// HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/").openConnection(); -// connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory()); -// assertEquals("This is the new location!", -// readAscii(connection.getInputStream(), Integer.MAX_VALUE)); -// -// RecordedRequest first = server.takeRequest(); -// assertEquals("GET / HTTP/1.1", first.getRequestLine()); -// RecordedRequest retry = server.takeRequest(); -// assertEquals("GET /foo HTTP/1.1", retry.getRequestLine()); -// assertEquals("Expected connection reuse", 1, retry.getSequenceNumber()); -// } -// -// public void testNotRedirectedFromHttpsToHttp() throws IOException, InterruptedException { -// TestSSLContext testSSLContext = TestSSLContext.create(); -// server.useHttps(testSSLContext.serverContext.getSocketFactory(), false); -// server.enqueue(new MockResponse() -// .setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP) -// .addHeader("Location: http://anyhost/foo") -// .setBody("This page has moved!")); -// server.play(); -// -// HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/").openConnection(); -// connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory()); -// assertEquals("This page has moved!", -// readAscii(connection.getInputStream(), Integer.MAX_VALUE)); -// } + public void testRedirectedOnHttps() throws IOException, InterruptedException { + server.useHttps(sslContext.getSocketFactory(), false); + server.enqueue(new MockResponse() + .setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP) + .addHeader("Location: /foo") + .setBody("This page has moved!")); + server.enqueue(new MockResponse().setBody("This is the new location!")); + server.play(); + + OkHttpsConnection connection = (OkHttpsConnection) openConnection(server.getUrl("/")); + connection.setSSLSocketFactory(sslContext.getSocketFactory()); + connection.setHostnameVerifier(new RecordingHostnameVerifier()); + assertEquals("This is the new location!", + readAscii(connection.getInputStream(), Integer.MAX_VALUE)); + + RecordedRequest first = server.takeRequest(); + assertEquals("GET / HTTP/1.1", first.getRequestLine()); + RecordedRequest retry = server.takeRequest(); + assertEquals("GET /foo HTTP/1.1", retry.getRequestLine()); + assertEquals("Expected connection reuse", 1, retry.getSequenceNumber()); + } + + public void testNotRedirectedFromHttpsToHttp() throws IOException, InterruptedException { + server.useHttps(sslContext.getSocketFactory(), false); + server.enqueue(new MockResponse() + .setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP) + .addHeader("Location: http://anyhost/foo") + .setBody("This page has moved!")); + server.play(); + + OkHttpsConnection connection = (OkHttpsConnection) openConnection(server.getUrl("/")); + connection.setSSLSocketFactory(sslContext.getSocketFactory()); + connection.setHostnameVerifier(new RecordingHostnameVerifier()); + assertEquals("This page has moved!", + readAscii(connection.getInputStream(), Integer.MAX_VALUE)); + } public void testNotRedirectedFromHttpToHttps() throws IOException, InterruptedException { server.enqueue(new MockResponse() @@ -1985,14 +2002,14 @@ enum ProxyConfig { NO_PROXY() { @Override public OkHttpConnection connect(MockWebServer server, URL url) throws IOException { - return OkHttpConnection.open(url, Proxy.NO_PROXY); + return openConnection(url, Proxy.NO_PROXY); } }, CREATE_ARG() { @Override public OkHttpConnection connect(MockWebServer server, URL url) throws IOException { - return OkHttpConnection.open(url, server.toProxyAddress()); + return openConnection(url, server.toProxyAddress()); } }, @@ -2001,7 +2018,7 @@ enum ProxyConfig { throws IOException { System.setProperty("proxyHost", "localhost"); System.setProperty("proxyPort", Integer.toString(server.getPort())); - return OkHttpConnection.open(url); + return openConnection(url); } },