diff --git a/src/main/java/com/squareup/okhttp/internal/net/http/HttpDate.java b/src/main/java/com/squareup/okhttp/internal/net/http/HttpDate.java index d8f218b2b..60f1f3559 100644 --- a/src/main/java/com/squareup/okhttp/internal/net/http/HttpDate.java +++ b/src/main/java/com/squareup/okhttp/internal/net/http/HttpDate.java @@ -26,7 +26,7 @@ import java.util.TimeZone; /** * Best-effort parser for HTTP dates. */ -public final class HttpDate { +final class HttpDate { /** * Most websites serve cookies in the blessed format. Eagerly create the parser to ensure such diff --git a/src/main/java/com/squareup/okhttp/internal/net/http/RequestHeaders.java b/src/main/java/com/squareup/okhttp/internal/net/http/RequestHeaders.java index ca872c2e1..b53fc8106 100644 --- a/src/main/java/com/squareup/okhttp/internal/net/http/RequestHeaders.java +++ b/src/main/java/com/squareup/okhttp/internal/net/http/RequestHeaders.java @@ -24,7 +24,7 @@ import java.util.Map; /** * Parsed HTTP request headers. */ -public final class RequestHeaders { +final class RequestHeaders { private final URI uri; private final RawHeaders headers; diff --git a/src/main/java/com/squareup/okhttp/internal/net/http/ResponseHeaders.java b/src/main/java/com/squareup/okhttp/internal/net/http/ResponseHeaders.java index 2061960d7..5e6962d10 100644 --- a/src/main/java/com/squareup/okhttp/internal/net/http/ResponseHeaders.java +++ b/src/main/java/com/squareup/okhttp/internal/net/http/ResponseHeaders.java @@ -32,7 +32,7 @@ import java.util.concurrent.TimeUnit; /** * Parsed HTTP response headers. */ -public final class ResponseHeaders { +final class ResponseHeaders { /** HTTP header name for the local time when the request was sent. */ private static final String SENT_MILLIS = "X-Android-Sent-Millis"; diff --git a/src/main/java/com/squareup/okhttp/internal/net/http/RouteSelector.java b/src/main/java/com/squareup/okhttp/internal/net/http/RouteSelector.java index 1d6571c5c..81ddf96fd 100644 --- a/src/main/java/com/squareup/okhttp/internal/net/http/RouteSelector.java +++ b/src/main/java/com/squareup/okhttp/internal/net/http/RouteSelector.java @@ -37,7 +37,7 @@ import java.util.NoSuchElementException; * choice of proxy server, IP address, and TLS mode. Connections may also be * recycled. */ -public final class RouteSelector { +final class RouteSelector { /** * A TLS connection with useful extensions enabled. This mode supports more * features, but is less likely to be compatible with older HTTP servers. diff --git a/src/test/java/com/squareup/okhttp/internal/net/http/NewURLConnectionTest.java b/src/test/java/com/squareup/okhttp/internal/net/http/NewURLConnectionTest.java index 183d15d56..6a85448c9 100644 --- a/src/test/java/com/squareup/okhttp/internal/net/http/NewURLConnectionTest.java +++ b/src/test/java/com/squareup/okhttp/internal/net/http/NewURLConnectionTest.java @@ -37,4 +37,8 @@ public final class NewURLConnectionTest extends TestCase { // TODO: read the outgoing status line and incoming status line? + // TODO: test that request bodies are retransmitted on IP address failures + + // TODO: pooled proxy failures are not reported to the proxy selector + } diff --git a/src/test/java/com/squareup/okhttp/internal/net/http/URLConnectionTest.java b/src/test/java/com/squareup/okhttp/internal/net/http/URLConnectionTest.java index 52beed0c7..f721700ca 100644 --- a/src/test/java/com/squareup/okhttp/internal/net/http/URLConnectionTest.java +++ b/src/test/java/com/squareup/okhttp/internal/net/http/URLConnectionTest.java @@ -46,7 +46,6 @@ import java.net.ResponseCache; import java.net.SocketAddress; import java.net.SocketTimeoutException; import java.net.URI; -import java.net.URISyntaxException; import java.net.URL; import java.net.URLConnection; import java.net.UnknownHostException; @@ -63,12 +62,12 @@ import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLException; +import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; @@ -120,9 +119,6 @@ public final class URLConnectionTest extends TestCase { super.tearDown(); } - // TODO: test that request bodies are retransmitted on IP address failures - // TODO: pooled proxy failures are not reported to the proxy selector - public void testRequestHeaders() throws IOException, InterruptedException { server.enqueue(new MockResponse()); server.play(); @@ -397,7 +393,7 @@ public final class URLConnectionTest extends TestCase { testServerClosesOutput(SHUTDOWN_INPUT_AT_END); } - public void SUPPRESSED_testServerShutdownOutput() throws Exception { + public void testServerShutdownOutput() throws Exception { testServerClosesOutput(SHUTDOWN_OUTPUT_AT_END); } @@ -405,15 +401,27 @@ public final class URLConnectionTest extends TestCase { server.enqueue(new MockResponse() .setBody("This connection won't pool properly") .setSocketPolicy(socketPolicy)); - server.enqueue(new MockResponse() - .setBody("This comes after a busted connection")); + MockResponse responseAfter = new MockResponse() + .setBody("This comes after a busted connection"); + server.enqueue(responseAfter); + server.enqueue(responseAfter); // Enqueue 2x because the broken connection may be reused. server.play(); - assertContent("This connection won't pool properly", client.open(server.getUrl("/a"))); + HttpURLConnection connection1 = client.open(server.getUrl("/a")); + connection1.setReadTimeout(100); + assertContent("This connection won't pool properly", connection1); assertEquals(0, server.takeRequest().getSequenceNumber()); - assertContent("This comes after a busted connection", client.open(server.getUrl("/b"))); + HttpURLConnection connection2 = client.open(server.getUrl("/b")); + connection2.setReadTimeout(100); + assertContent("This comes after a busted connection", connection2); + + // Check that a fresh connection was created, either immediately or after attempting reuse. + RecordedRequest requestAfter = server.takeRequest(); + if (server.getRequestCount() == 3) { + requestAfter = server.takeRequest(); // The failure consumed a response. + } // sequence number 0 means the HTTP socket connection was not reused - assertEquals(0, server.takeRequest().getSequenceNumber()); + assertEquals(0, requestAfter.getSequenceNumber()); } enum WriteKind { BYTE_BY_BYTE, SMALL_BUFFERS, LARGE_BUFFERS } @@ -430,7 +438,7 @@ public final class URLConnectionTest extends TestCase { doUpload(TransferKind.CHUNKED, WriteKind.LARGE_BUFFERS); } - public void SUPPRESSED_test_fixedLengthUpload_byteByByte() throws Exception { + public void test_fixedLengthUpload_byteByByte() throws Exception { doUpload(TransferKind.FIXED_LENGTH, WriteKind.BYTE_BY_BYTE); } @@ -576,24 +584,20 @@ public final class URLConnectionTest extends TestCase { * * http://code.google.com/p/android/issues/detail?id=13178 */ -// public void testConnectViaHttpsToUntrustedServer() throws IOException, InterruptedException { -// TestSSLContext testSSLContext = TestSSLContext.create(TestKeyStore.getClientCA2(), -// TestKeyStore.getServer()); -// -// server.useHttps(testSSLContext.serverContext.getSocketFactory(), false); -// server.enqueue(new MockResponse()); // unused -// server.play(); -// -// HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/foo").openConnection(); -// connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory()); -// try { -// connection.getInputStream(); -// fail(); -// } catch (SSLHandshakeException expected) { -// assertTrue(expected.getCause() instanceof CertificateException); -// } -// assertEquals(0, server.getRequestCount()); -// } + public void testConnectViaHttpsToUntrustedServer() throws IOException, InterruptedException { + server.useHttps(sslContext.getSocketFactory(), false); + server.enqueue(new MockResponse()); // unused + server.play(); + + HttpURLConnection connection = client.open(server.getUrl("/foo")); + try { + connection.getInputStream(); + fail(); + } catch (SSLHandshakeException expected) { + assertTrue(expected.getCause() instanceof CertificateException); + } + assertEquals(0, server.getRequestCount()); + } public void testConnectViaProxyUsingProxyArg() throws Exception { testConnectViaProxy(ProxyConfig.CREATE_ARG); @@ -961,7 +965,7 @@ public final class URLConnectionTest extends TestCase { * code 401. This causes a new HTTP request to be issued for every call into * the URLConnection. */ - public void SUPPRESSED_testUnauthorizedResponseHandling() throws IOException { + public void testUnauthorizedResponseHandling() throws IOException { MockResponse response = new MockResponse() .addHeader("WWW-Authenticate: challenge") .setResponseCode(401) // UNAUTHORIZED @@ -1578,7 +1582,7 @@ public final class URLConnectionTest extends TestCase { readAscii(connection.getInputStream(), Integer.MAX_VALUE)); } - public void SUPPRESSED_testRedirectToAnotherOriginServer() throws Exception { + public void testRedirectToAnotherOriginServer() throws Exception { MockWebServer server2 = new MockWebServer(); server2.enqueue(new MockResponse().setBody("This is the 2nd server!")); server2.play(); @@ -1597,7 +1601,7 @@ public final class URLConnectionTest extends TestCase { // make sure the first server was careful to recycle the connection assertEquals("This is the first server again!", - readAscii(server.getUrl("/").openStream(), Integer.MAX_VALUE)); + readAscii(client.open(server.getUrl("/")).getInputStream(), Integer.MAX_VALUE)); RecordedRequest first = server.takeRequest(); assertContains(first.getHeaders(), "Host: " + hostName + ":" + server.getPort()); @@ -1891,137 +1895,27 @@ public final class URLConnectionTest extends TestCase { } } - public void SUPPRESSED_testGetKeepAlive() throws Exception { + public void testGetKeepAlive() throws Exception { MockWebServer server = new MockWebServer(); server.enqueue(new MockResponse().setBody("ABC")); server.play(); // The request should work once and then fail - URLConnection connection = client.open(server.getUrl("")); - InputStream input = connection.getInputStream(); + URLConnection connection1 = client.open(server.getUrl("")); + connection1.setReadTimeout(100); + InputStream input = connection1.getInputStream(); assertEquals("ABC", readAscii(input, Integer.MAX_VALUE)); input.close(); + server.shutdown(); try { - client.open(server.getUrl("")).getInputStream(); + HttpURLConnection connection2 = client.open(server.getUrl("")); + connection2.setReadTimeout(100); + connection2.getInputStream(); fail(); } catch (ConnectException expected) { } } - /** - * This test goes through the exhaustive set of interesting ASCII characters - * because most of those characters are interesting in some way according to - * RFC 2396 and RFC 2732. http://b/1158780 - */ - public void SUPPRESSED_testLenientUrlToUri() throws Exception { - // alphanum - testUrlToUriMapping("abzABZ09", "abzABZ09", "abzABZ09", "abzABZ09", "abzABZ09"); - - // control characters - testUrlToUriMapping("\u0001", "%01", "%01", "%01", "%01"); - testUrlToUriMapping("\u001f", "%1F", "%1F", "%1F", "%1F"); - - // ascii characters - testUrlToUriMapping("%20", "%20", "%20", "%20", "%20"); - testUrlToUriMapping("%20", "%20", "%20", "%20", "%20"); - testUrlToUriMapping(" ", "%20", "%20", "%20", "%20"); - testUrlToUriMapping("!", "!", "!", "!", "!"); - testUrlToUriMapping("\"", "%22", "%22", "%22", "%22"); - testUrlToUriMapping("#", null, null, null, "%23"); - testUrlToUriMapping("$", "$", "$", "$", "$"); - testUrlToUriMapping("&", "&", "&", "&", "&"); - testUrlToUriMapping("'", "'", "'", "'", "'"); - testUrlToUriMapping("(", "(", "(", "(", "("); - testUrlToUriMapping(")", ")", ")", ")", ")"); - testUrlToUriMapping("*", "*", "*", "*", "*"); - testUrlToUriMapping("+", "+", "+", "+", "+"); - testUrlToUriMapping(",", ",", ",", ",", ","); - testUrlToUriMapping("-", "-", "-", "-", "-"); - testUrlToUriMapping(".", ".", ".", ".", "."); - testUrlToUriMapping("/", null, "/", "/", "/"); - testUrlToUriMapping(":", null, ":", ":", ":"); - testUrlToUriMapping(";", ";", ";", ";", ";"); - testUrlToUriMapping("<", "%3C", "%3C", "%3C", "%3C"); - testUrlToUriMapping("=", "=", "=", "=", "="); - testUrlToUriMapping(">", "%3E", "%3E", "%3E", "%3E"); - testUrlToUriMapping("?", null, null, "?", "?"); - testUrlToUriMapping("@", "@", "@", "@", "@"); - testUrlToUriMapping("[", null, "%5B", null, "%5B"); - testUrlToUriMapping("\\", "%5C", "%5C", "%5C", "%5C"); - testUrlToUriMapping("]", null, "%5D", null, "%5D"); - testUrlToUriMapping("^", "%5E", "%5E", "%5E", "%5E"); - testUrlToUriMapping("_", "_", "_", "_", "_"); - testUrlToUriMapping("`", "%60", "%60", "%60", "%60"); - testUrlToUriMapping("{", "%7B", "%7B", "%7B", "%7B"); - testUrlToUriMapping("|", "%7C", "%7C", "%7C", "%7C"); - testUrlToUriMapping("}", "%7D", "%7D", "%7D", "%7D"); - testUrlToUriMapping("~", "~", "~", "~", "~"); - testUrlToUriMapping("~", "~", "~", "~", "~"); - testUrlToUriMapping("\u007f", "%7F", "%7F", "%7F", "%7F"); - - // beyond ascii - testUrlToUriMapping("\u0080", "%C2%80", "%C2%80", "%C2%80", "%C2%80"); - testUrlToUriMapping("\u20ac", "\u20ac", "\u20ac", "\u20ac", "\u20ac"); - testUrlToUriMapping("\ud842\udf9f", - "\ud842\udf9f", "\ud842\udf9f", "\ud842\udf9f", "\ud842\udf9f"); - } - - public void SUPPRESSED_testLenientUrlToUriNul() throws Exception { - testUrlToUriMapping("\u0000", "%00", "%00", "%00", "%00"); // RI fails this - } - - private void testUrlToUriMapping(String string, String asAuthority, String asFile, - String asQuery, String asFragment) throws Exception { - if (asAuthority != null) { - assertEquals("http://host" + asAuthority + ".tld/", - backdoorUrlToUri(new URL("http://host" + string + ".tld/")).toString()); - } - if (asFile != null) { - assertEquals("http://host.tld/file" + asFile + "/", - backdoorUrlToUri(new URL("http://host.tld/file" + string + "/")).toString()); - } - if (asQuery != null) { - assertEquals("http://host.tld/file?q" + asQuery + "=x", - backdoorUrlToUri(new URL("http://host.tld/file?q" + string + "=x")).toString()); - } - assertEquals("http://host.tld/file#" + asFragment + "-x", - backdoorUrlToUri(new URL("http://host.tld/file#" + asFragment + "-x")).toString()); - } - - /** - * Exercises HttpURLConnection to convert URL to a URI. Unlike URL#toURI, - * HttpURLConnection recovers from URLs with unescaped but unsupported URI - * characters like '{' and '|' by escaping these characters. - */ - private URI backdoorUrlToUri(URL url) throws Exception { - final AtomicReference uriReference = new AtomicReference(); - - client.setResponseCache(new ResponseCache() { - @Override - public CacheRequest put(URI uri, URLConnection connection) throws IOException { - return null; - } - - @Override - public CacheResponse get(URI uri, String requestMethod, - Map> requestHeaders) throws IOException { - uriReference.set(uri); - throw new UnsupportedOperationException(); - } - }); - - try { - HttpURLConnection connection = client.open(url); - connection.getResponseCode(); - } catch (Exception expected) { - if (expected.getCause() instanceof URISyntaxException) { - expected.printStackTrace(); - } - } - - return uriReference.get(); - } - /** * Don't explode if the cache returns a null body. http://b/3373699 */