diff --git a/okhttp-android-support/src/test/java/okhttp3/internal/huc/CacheAdapterTest.java b/okhttp-android-support/src/test/java/okhttp3/internal/huc/CacheAdapterTest.java index ac50cef32..81aeaf2b9 100644 --- a/okhttp-android-support/src/test/java/okhttp3/internal/huc/CacheAdapterTest.java +++ b/okhttp-android-support/src/test/java/okhttp3/internal/huc/CacheAdapterTest.java @@ -38,7 +38,7 @@ import okhttp3.internal.InternalCache; import okhttp3.internal.SslContextBuilder; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; -import okhttp3.testing.RecordingHostnameVerifier; +import okhttp3.RecordingHostnameVerifier; import okio.Buffer; import org.junit.After; import org.junit.Before; diff --git a/okhttp-android-support/src/test/java/okhttp3/internal/huc/ResponseCacheTest.java b/okhttp-android-support/src/test/java/okhttp3/internal/huc/ResponseCacheTest.java index 0048212f8..2ac16c618 100644 --- a/okhttp-android-support/src/test/java/okhttp3/internal/huc/ResponseCacheTest.java +++ b/okhttp-android-support/src/test/java/okhttp3/internal/huc/ResponseCacheTest.java @@ -68,7 +68,7 @@ import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; import okhttp3.mockwebserver.RecordedRequest; import okhttp3.mockwebserver.SocketPolicy; -import okhttp3.testing.RecordingHostnameVerifier; +import okhttp3.RecordingHostnameVerifier; import okio.Buffer; import okio.BufferedSink; import okio.GzipSink; diff --git a/okhttp-tests/src/test/java/okhttp3/internal/http/FakeDns.java b/okhttp-testing-support/src/main/java/okhttp3/FakeDns.java similarity index 97% rename from okhttp-tests/src/test/java/okhttp3/internal/http/FakeDns.java rename to okhttp-testing-support/src/main/java/okhttp3/FakeDns.java index d013bea2d..f2edc75d6 100644 --- a/okhttp-tests/src/test/java/okhttp3/internal/http/FakeDns.java +++ b/okhttp-testing-support/src/main/java/okhttp3/FakeDns.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package okhttp3.internal.http; +package okhttp3; import java.net.InetAddress; import java.net.UnknownHostException; @@ -21,7 +21,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; -import okhttp3.Dns; import static org.junit.Assert.assertEquals; diff --git a/okhttp-testing-support/src/main/java/okhttp3/FakeProxySelector.java b/okhttp-testing-support/src/main/java/okhttp3/FakeProxySelector.java new file mode 100644 index 000000000..c7d37f57e --- /dev/null +++ b/okhttp-testing-support/src/main/java/okhttp3/FakeProxySelector.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package okhttp3; + +import java.io.IOException; +import java.net.Proxy; +import java.net.ProxySelector; +import java.net.SocketAddress; +import java.net.URI; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public final class FakeProxySelector extends ProxySelector { + public final List proxies = new ArrayList<>(); + + public FakeProxySelector addProxy(Proxy proxy) { + proxies.add(proxy); + return this; + } + + @Override public List select(URI uri) { + // Don't handle 'socket' schemes, which the RI's Socket class may request (for SOCKS). + return uri.getScheme().equals("http") || uri.getScheme().equals("https") ? proxies + : Collections.singletonList(Proxy.NO_PROXY); + } + + @Override public void connectFailed(URI uri, SocketAddress sa, IOException ioe) { + } +} diff --git a/okhttp-tests/src/test/java/okhttp3/internal/tls/FakeSSLSession.java b/okhttp-testing-support/src/main/java/okhttp3/FakeSSLSession.java similarity index 97% rename from okhttp-tests/src/test/java/okhttp3/internal/tls/FakeSSLSession.java rename to okhttp-testing-support/src/main/java/okhttp3/FakeSSLSession.java index ead8c1b87..ab67a9f06 100644 --- a/okhttp-tests/src/test/java/okhttp3/internal/tls/FakeSSLSession.java +++ b/okhttp-testing-support/src/main/java/okhttp3/FakeSSLSession.java @@ -14,7 +14,7 @@ * License for the specific language governing permissions and limitations under * the License. */ -package okhttp3.internal.tls; +package okhttp3; import java.security.Principal; import java.security.cert.Certificate; @@ -23,7 +23,7 @@ import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSessionContext; import javax.security.cert.X509Certificate; -final class FakeSSLSession implements SSLSession { +public final class FakeSSLSession implements SSLSession { private final Certificate[] certificates; public FakeSSLSession(Certificate... certificates) throws Exception { diff --git a/okhttp-testing-support/src/main/java/okhttp3/testing/RecordingHostnameVerifier.java b/okhttp-testing-support/src/main/java/okhttp3/RecordingHostnameVerifier.java similarity index 97% rename from okhttp-testing-support/src/main/java/okhttp3/testing/RecordingHostnameVerifier.java rename to okhttp-testing-support/src/main/java/okhttp3/RecordingHostnameVerifier.java index 7b682e56d..568b7c518 100644 --- a/okhttp-testing-support/src/main/java/okhttp3/testing/RecordingHostnameVerifier.java +++ b/okhttp-testing-support/src/main/java/okhttp3/RecordingHostnameVerifier.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package okhttp3.testing; +package okhttp3; import java.util.ArrayList; import java.util.List; diff --git a/okhttp-tests/src/test/java/okhttp3/CallTest.java b/okhttp-tests/src/test/java/okhttp3/CallTest.java index 12bf5db21..3ccecfa65 100644 --- a/okhttp-tests/src/test/java/okhttp3/CallTest.java +++ b/okhttp-tests/src/test/java/okhttp3/CallTest.java @@ -24,7 +24,6 @@ import java.net.HttpCookie; import java.net.HttpURLConnection; import java.net.InetAddress; import java.net.InetSocketAddress; -import java.net.ProtocolException; import java.net.Proxy; import java.net.ServerSocket; import java.net.UnknownHostException; @@ -56,7 +55,6 @@ import okhttp3.internal.SingleInetAddressDns; import okhttp3.internal.SslContextBuilder; import okhttp3.internal.Util; import okhttp3.internal.Version; -import okhttp3.internal.http.FakeDns; import okhttp3.internal.http.RecordingProxySelector; import okhttp3.internal.io.InMemoryFileSystem; import okhttp3.mockwebserver.Dispatcher; @@ -64,7 +62,6 @@ import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; import okhttp3.mockwebserver.RecordedRequest; import okhttp3.mockwebserver.SocketPolicy; -import okhttp3.testing.RecordingHostnameVerifier; import okio.Buffer; import okio.BufferedSink; import okio.BufferedSource; @@ -72,6 +69,7 @@ import okio.GzipSink; import okio.Okio; import org.junit.After; import org.junit.Before; +import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestRule; @@ -113,12 +111,7 @@ public final class CallTest { @Test public void get() throws Exception { server.enqueue(new MockResponse().setBody("abc").addHeader("Content-Type: text/plain")); - Request request = new Request.Builder() - .url(server.url("/")) - .header("User-Agent", "SyncApiTest") - .build(); - - executeSynchronously(request) + executeSynchronously("/", "User-Agent", "SyncApiTest") .assertCode(200) .assertSuccessful() .assertHeader("Content-Type", "text/plain") @@ -133,14 +126,7 @@ public final class CallTest { @Test public void buildRequestUsingHttpUrl() throws Exception { server.enqueue(new MockResponse()); - - HttpUrl httpUrl = server.url("/"); - Request request = new Request.Builder() - .url(httpUrl) - .build(); - assertEquals(httpUrl, request.url()); - - executeSynchronously(request).assertSuccessful(); + executeSynchronously("/").assertSuccessful(); } @Test public void invalidScheme() throws Exception { @@ -165,12 +151,7 @@ public final class CallTest { @Test public void getReturns500() throws Exception { server.enqueue(new MockResponse().setResponseCode(500)); - - Request request = new Request.Builder() - .url(server.url("/")) - .build(); - - executeSynchronously(request) + executeSynchronously("/") .assertCode(500) .assertNotSuccessful(); } @@ -195,13 +176,7 @@ public final class CallTest { .addHeader("B", "123") .addHeader("B", "234")); - Request request = new Request.Builder() - .url(server.url("/")) - .addHeader("A", "345") - .addHeader("A", "456") - .build(); - - executeSynchronously(request) + executeSynchronously("/", "A", "345", "A", "456") .assertCode(200) .assertHeader("B", "123", "234"); @@ -410,8 +385,7 @@ public final class CallTest { .authenticator(new RecordingOkAuthenticator(credential)) .build(); - Request request = new Request.Builder().url(server.url("/")).build(); - executeSynchronously(request) + executeSynchronously("/") .assertCode(200) .assertBody("Success!"); } @@ -675,14 +649,9 @@ public final class CallTest { server.enqueue(new MockResponse().setBody("def")); server.enqueue(new MockResponse().setBody("ghi")); - executeSynchronously(new Request.Builder().url(server.url("/a")).build()) - .assertBody("abc"); - - executeSynchronously(new Request.Builder().url(server.url("/b")).build()) - .assertBody("def"); - - executeSynchronously(new Request.Builder().url(server.url("/c")).build()) - .assertBody("ghi"); + executeSynchronously("/a").assertBody("abc"); + executeSynchronously("/b").assertBody("def"); + executeSynchronously("/c").assertBody("ghi"); assertEquals(0, server.takeRequest().getSequenceNumber()); assertEquals(1, server.takeRequest().getSequenceNumber()); @@ -742,7 +711,7 @@ public final class CallTest { client = client.newBuilder() .readTimeout(1000, TimeUnit.MILLISECONDS) .build(); - executeSynchronously(new Request.Builder().url(server.url("/a")).build()).assertBody("abc"); + executeSynchronously("/a").assertBody("abc"); // Second request: time out after 250ms. client = client.newBuilder() @@ -903,8 +872,7 @@ public final class CallTest { .setBody("abc") .addHeader("Content-Type: text/plain")); - executeSynchronously(new Request.Builder().url(server.url("/")).build()) - .assertHandshake(); + executeSynchronously("/").assertHandshake(); } @Test public void tls_Async() throws Exception { @@ -931,9 +899,8 @@ public final class CallTest { .build(); assertTrue(client.retryOnConnectionFailure()); - Request request = new Request.Builder().url(server.url("/")).build(); - executeSynchronously(request).assertBody("seed connection pool"); - executeSynchronously(request).assertBody("retry success"); + executeSynchronously("/").assertBody("seed connection pool"); + executeSynchronously("/").assertBody("retry success"); } @Test public void noRecoverWhenRetryOnConnectionFailureIsFalse() throws Exception { @@ -946,14 +913,10 @@ public final class CallTest { .retryOnConnectionFailure(false) .build(); - Request request = new Request.Builder().url(server.url("/")).build(); - executeSynchronously(request).assertBody("seed connection pool"); - try { - // If this succeeds, too many requests were made. - client.newCall(request).execute(); - fail(); - } catch (IOException expected) { - } + executeSynchronously("/").assertBody("seed connection pool"); + + // If this succeeds, too many requests were made. + executeSynchronously("/").assertFailure(IOException.class); } @Test public void recoverFromTlsHandshakeFailure() throws Exception { @@ -967,8 +930,7 @@ public final class CallTest { .sslSocketFactory(suppressTlsFallbackClientSocketFactory()) .build(); - executeSynchronously(new Request.Builder().url(server.url("/")).build()) - .assertBody("abc"); + executeSynchronously("/").assertBody("abc"); } @Test public void recoverFromTlsHandshakeFailure_tlsFallbackScsvEnabled() throws Exception { @@ -1184,29 +1146,20 @@ public final class CallTest { // Store a response in the cache. HttpUrl url = server.url("/"); - Request cacheStoreRequest = new Request.Builder() - .url(url) - .addHeader("Accept-Language", "fr-CA") - .addHeader("Accept-Charset", "UTF-8") - .build(); - executeSynchronously(cacheStoreRequest) + executeSynchronously("/", "Accept-Language", "fr-CA", "Accept-Charset", "UTF-8") .assertCode(200) .assertBody("A"); assertNull(server.takeRequest().getHeader("If-None-Match")); - // Hit that stored response. - Request cacheHitRequest = new Request.Builder() - .url(url) - .addHeader("Accept-Language", "en-US") // Different, but Vary says it doesn't matter. - .addHeader("Accept-Charset", "UTF-8") - .build(); - RecordedResponse cacheHit = executeSynchronously(cacheHitRequest); + // Hit that stored response. It's different, but Vary says it doesn't matter. + RecordedResponse cacheHit = executeSynchronously( + "/", "Accept-Language", "en-US", "Accept-Charset", "UTF-8"); // Check the merged response. The request is the application's original request. cacheHit.assertCode(200) .assertBody("A") .assertHeader("ETag", "v1") - .assertRequestUrl(cacheStoreRequest.url()) + .assertRequestUrl(url) .assertRequestHeader("Accept-Language", "en-US") .assertRequestHeader("Accept-Charset", "UTF-8"); @@ -1215,7 +1168,7 @@ public final class CallTest { .assertCode(200) .assertHeader("ETag", "v1") .assertRequestMethod("GET") - .assertRequestUrl(cacheStoreRequest.url()) + .assertRequestUrl(url) .assertRequestHeader("Accept-Language") .assertRequestHeader("Accept-Charset", "UTF-8"); @@ -1237,32 +1190,22 @@ public final class CallTest { .build(); // Store a response in the cache. - HttpUrl url = server.url("/"); - Request cacheStoreRequest = new Request.Builder() - .url(url) - .addHeader("Accept-Language", "fr-CA") - .addHeader("Accept-Charset", "UTF-8") - .build(); - executeSynchronously(cacheStoreRequest) + executeSynchronously("/", "Accept-Language", "fr-CA", "Accept-Charset", "UTF-8") .assertCode(200) .assertHeader("Donut", "a") .assertBody("A"); assertNull(server.takeRequest().getHeader("If-None-Match")); - // Hit that stored response. - Request cacheHitRequest = new Request.Builder() - .url(url) - .addHeader("Accept-Language", "en-US") // Different, but Vary says it doesn't matter. - .addHeader("Accept-Charset", "UTF-8") - .build(); - RecordedResponse cacheHit = executeSynchronously(cacheHitRequest); + // Hit that stored response. It's different, but Vary says it doesn't matter. + RecordedResponse cacheHit = executeSynchronously( + "/", "Accept-Language", "en-US", "Accept-Charset", "UTF-8"); assertEquals("v1", server.takeRequest().getHeader("If-None-Match")); // Check the merged response. The request is the application's original request. cacheHit.assertCode(200) .assertBody("A") .assertHeader("Donut", "b") - .assertRequestUrl(cacheStoreRequest.url()) + .assertRequestUrl(server.url("/")) .assertRequestHeader("Accept-Language", "en-US") .assertRequestHeader("Accept-Charset", "UTF-8") .assertRequestHeader("If-None-Match"); // No If-None-Match on the user's request. @@ -1272,7 +1215,7 @@ public final class CallTest { .assertCode(200) .assertHeader("Donut", "a") .assertHeader("ETag", "v1") - .assertRequestUrl(cacheStoreRequest.url()) + .assertRequestUrl(server.url("/")) .assertRequestHeader("Accept-Language") // No Vary on Accept-Language. .assertRequestHeader("Accept-Charset", "UTF-8") // Because of Vary on Accept-Charset. .assertRequestHeader("If-None-Match"); // This wasn't present in the original request. @@ -1325,43 +1268,35 @@ public final class CallTest { .cache(cache) .build(); - Request cacheStoreRequest = new Request.Builder() - .url(server.url("/")) - .addHeader("Accept-Language", "fr-CA") - .addHeader("Accept-Charset", "UTF-8") - .build(); - executeSynchronously(cacheStoreRequest) + executeSynchronously("/", "Accept-Language", "fr-CA", "Accept-Charset", "UTF-8") .assertCode(200) .assertBody("A"); assertNull(server.takeRequest().getHeader("If-None-Match")); - Request cacheMissRequest = new Request.Builder() - .url(server.url("/")) - .addHeader("Accept-Language", "en-US") // Different, but Vary says it doesn't matter. - .addHeader("Accept-Charset", "UTF-8") - .build(); - RecordedResponse cacheHit = executeSynchronously(cacheMissRequest); + // Different request, but Vary says it doesn't matter. + RecordedResponse cacheHit = executeSynchronously( + "/", "Accept-Language", "en-US", "Accept-Charset", "UTF-8"); assertEquals("v1", server.takeRequest().getHeader("If-None-Match")); // Check the user response. It has the application's original request. cacheHit.assertCode(200) .assertBody("B") .assertHeader("Donut", "b") - .assertRequestUrl(cacheStoreRequest.url()); + .assertRequestUrl(server.url("/")); // Check the cache response. Even though it's a miss, we used the cache. cacheHit.cacheResponse() .assertCode(200) .assertHeader("Donut", "a") .assertHeader("ETag", "v1") - .assertRequestUrl(cacheStoreRequest.url()); + .assertRequestUrl(server.url("/")); // Check the network response. It has the network request, plus caching headers. cacheHit.networkResponse() .assertCode(200) .assertHeader("Donut", "b") .assertRequestHeader("If-None-Match", "v1") // If-None-Match in the validation request. - .assertRequestUrl(cacheStoreRequest.url()); + .assertRequestUrl(server.url("/")); } @Test public void conditionalCacheMiss_Async() throws Exception { @@ -1388,12 +1323,7 @@ public final class CallTest { } @Test public void onlyIfCachedReturns504WhenNotCached() throws Exception { - Request request = new Request.Builder() - .url(server.url("/")) - .header("Cache-Control", "only-if-cached") - .build(); - - executeSynchronously(request) + executeSynchronously("/", "Cache-Control", "only-if-cached") .assertCode(504) .assertBody("") .assertNoNetworkResponse() @@ -1409,10 +1339,7 @@ public final class CallTest { server.enqueue(new MockResponse() .addHeader("ETag: v1") .setBody("A")); - Request cacheStoreRequest = new Request.Builder() - .url(server.url("/")) - .build(); - executeSynchronously(cacheStoreRequest) + executeSynchronously("/") .assertCode(200) .assertBody("A"); @@ -1421,14 +1348,7 @@ public final class CallTest { client = client.newBuilder() .dns(new FakeDns().unknownHost()) .build(); - Request conditionalGetRequest = new Request.Builder() - .url(server.url("/")) - .build(); - try { - executeSynchronously(conditionalGetRequest); - fail(); - } catch (UnknownHostException expected) { - } + executeSynchronously("/").assertFailure(UnknownHostException.class); } @Test public void redirect() throws Exception { @@ -1444,7 +1364,7 @@ public final class CallTest { .setBody("/b has moved!")); server.enqueue(new MockResponse().setBody("C")); - executeSynchronously(new Request.Builder().url(server.url("/a")).build()) + executeSynchronously("/a") .assertCode(200) .assertBody("C") .priorResponse() @@ -1511,11 +1431,7 @@ public final class CallTest { .cookieJar(cookieJar) .build(); - Request request = new Request.Builder() - .url(server.url("/")) - .build(); - executeSynchronously(request) - .assertCode(200); + executeSynchronously("/").assertCode(200); List responseCookies = cookieJar.takeResponseCookies(); assertEquals(2, responseCookies.size()); @@ -1537,11 +1453,7 @@ public final class CallTest { .cookieJar(cookieJar) .build(); - Request request = new Request.Builder() - .url(server.url("/")) - .build(); - executeSynchronously(request) - .assertCode(200); + executeSynchronously("/").assertCode(200); RecordedRequest recordedRequest = server.takeRequest(); assertEquals("a=b; c=d", recordedRequest.getHeader("Cookie")); @@ -1637,7 +1549,7 @@ public final class CallTest { } server.enqueue(new MockResponse().setBody("Success!")); - executeSynchronously(new Request.Builder().url(server.url("/0")).build()) + executeSynchronously("/0") .assertCode(200) .assertBody("Success!"); } @@ -1692,12 +1604,8 @@ public final class CallTest { .setResponseCode(204) .setBody("I'm not even supposed to be here today.")); - try { - executeSynchronously(new Request.Builder().url(server.url("/")).build()); - fail(); - } catch (ProtocolException e) { - assertEquals("HTTP 204 had non-zero Content-Length: 39", e.getMessage()); - } + executeSynchronously("/") + .assertFailure("HTTP 204 had non-zero Content-Length: 39"); } @Test public void http205WithBodyDisallowed() throws IOException { @@ -1705,12 +1613,8 @@ public final class CallTest { .setResponseCode(205) .setBody("I'm not even supposed to be here today.")); - try { - executeSynchronously(new Request.Builder().url(server.url("/")).build()); - fail(); - } catch (ProtocolException e) { - assertEquals("HTTP 205 had non-zero Content-Length: 39", e.getMessage()); - } + executeSynchronously("/") + .assertFailure("HTTP 205 had non-zero Content-Length: 39"); } @Test public void canceledBeforeExecute() throws Exception { @@ -1998,13 +1902,9 @@ public final class CallTest { .setBody(gzippedBody) .addHeader("Content-Encoding: gzip")); - Request request = new Request.Builder() - .url(server.url("/")) - .build(); - // Confirm that the user request doesn't have Accept-Encoding, and the user // response doesn't have a Content-Encoding or Content-Length. - RecordedResponse userResponse = executeSynchronously(request); + RecordedResponse userResponse = executeSynchronously("/"); userResponse.assertCode(200) .assertRequestHeader("Accept-Encoding") .assertHeader("Content-Encoding") @@ -2029,11 +1929,7 @@ public final class CallTest { .authenticator(new RecordingOkAuthenticator("password")) .build(); - Request request = new Request.Builder() - .url(server.url("/")) - .build(); - executeSynchronously(request) - .assertBody("abcabcabc"); + executeSynchronously("/").assertBody("abcabcabc"); } @Test public void asyncResponseCanBeConsumedLater() throws Exception { @@ -2065,7 +1961,7 @@ public final class CallTest { assertEquals("abc", response.body().string()); // Make another request just to confirm that that connection can be reused... - executeSynchronously(new Request.Builder().url(server.url("/")).build()).assertBody("def"); + executeSynchronously("/").assertBody("def"); assertEquals(0, server.takeRequest().getSequenceNumber()); // New connection. assertEquals(1, server.takeRequest().getSequenceNumber()); // Connection reused. @@ -2076,7 +1972,7 @@ public final class CallTest { @Test public void userAgentIsIncludedByDefault() throws Exception { server.enqueue(new MockResponse()); - executeSynchronously(new Request.Builder().url(server.url("/")).build()); + executeSynchronously("/"); RecordedRequest recordedRequest = server.takeRequest(); assertTrue(recordedRequest.getHeader("User-Agent") @@ -2093,10 +1989,7 @@ public final class CallTest { client = client.newBuilder() .followRedirects(false) .build(); - RecordedResponse recordedResponse = executeSynchronously( - new Request.Builder().url(server.url("/a")).build()); - - recordedResponse + executeSynchronously("/a") .assertBody("A") .assertCode(302); } @@ -2141,11 +2034,7 @@ public final class CallTest { .build(); server.enqueue(new MockResponse().setHeaders(headers)); - Request request = new Request.Builder() - .url(server.url("/")) - .build(); - - executeSynchronously(request) + executeSynchronously("/") .assertHeader("a\tb", "c\u007fd") .assertHeader("\ud83c\udf69", "\u2615\ufe0f") .assertHeader("", "ef"); @@ -2322,10 +2211,143 @@ public final class CallTest { // Make an HTTP/2 request and confirm that the protocol matches. server.enqueue(new MockResponse()); + executeSynchronously("/"); + assertEquals(Protocol.HTTP_2, protocolRef.get()); + } + + @Test public void serverSendsInvalidResponseHeaders() throws Exception { + server.enqueue(new MockResponse() + .setStatus("HTP/1.1 200 OK")); + + executeSynchronously("/") + .assertFailure("Unexpected status line: HTP/1.1 200 OK"); + } + + @Test public void serverSendsInvalidCodeTooLarge() throws Exception { + server.enqueue(new MockResponse() + .setStatus("HTTP/1.1 2147483648 OK")); + + executeSynchronously("/") + .assertFailure("Unexpected status line: HTTP/1.1 2147483648 OK"); + } + + @Test public void serverSendsInvalidCodeNotANumber() throws Exception { + server.enqueue(new MockResponse() + .setStatus("HTTP/1.1 00a OK")); + + executeSynchronously("/") + .assertFailure("Unexpected status line: HTTP/1.1 00a OK"); + } + + @Test public void serverSendsUnnecessaryWhitespace() throws Exception { + server.enqueue(new MockResponse() + .setStatus(" HTTP/1.1 200 OK")); + + executeSynchronously("/") + .assertFailure("Unexpected status line: HTTP/1.1 200 OK"); + } + + @Test public void connectFails() throws Exception { + server.shutdown(); + + executeSynchronously("/") + .assertFailure(IOException.class); + } + + @Test public void requestBodySurvivesRetries() throws Exception { + server.enqueue(new MockResponse()); + + // Enable a misconfigured proxy selector to guarantee that the request is retried. + client = client.newBuilder() + .proxySelector(new FakeProxySelector() + .addProxy(server2.toProxyAddress())) + .build(); + server2.shutdown(); + + Request request = new Request.Builder() + .url(server.url("/")) + .post(RequestBody.create(MediaType.parse("text/plain"), "abc")) + .build(); + + executeSynchronously(request); + assertEquals("abc", server.takeRequest().getBody().readUtf8()); + } + + @Ignore // This may fail in DNS lookup, which we don't have timeouts for. + @Test public void invalidHost() throws Exception { + Request request = new Request.Builder() + .url(HttpUrl.parse("http://1234.1.1.1/")) + .build(); + + executeSynchronously(request) + .assertFailure(UnknownHostException.class); + } + + @Test public void uploadBodySmallChunkedEncoding() throws Exception { + upload(true, 1048576, 256); + RecordedRequest recordedRequest = server.takeRequest(); + assertEquals(1048576, recordedRequest.getBodySize()); + assertFalse(recordedRequest.getChunkSizes().isEmpty()); + } + + @Test public void uploadBodyLargeChunkedEncoding() throws Exception { + upload(true, 1048576, 65536); + RecordedRequest recordedRequest = server.takeRequest(); + assertEquals(1048576, recordedRequest.getBodySize()); + assertFalse(recordedRequest.getChunkSizes().isEmpty()); + } + + @Test public void uploadBodySmallFixedLength() throws Exception { + upload(false, 1048576, 256); + RecordedRequest recordedRequest = server.takeRequest(); + assertEquals(1048576, recordedRequest.getBodySize()); + assertTrue(recordedRequest.getChunkSizes().isEmpty()); + } + + @Test public void uploadBodyLargeFixedLength() throws Exception { + upload(false, 1048576, 65536); + RecordedRequest recordedRequest = server.takeRequest(); + assertEquals(1048576, recordedRequest.getBodySize()); + assertTrue(recordedRequest.getChunkSizes().isEmpty()); + } + + private void upload( + final boolean chunked, final int size, final int writeSize) throws Exception { + server.enqueue(new MockResponse()); executeSynchronously(new Request.Builder() .url(server.url("/")) + .post(requestBody(chunked, size, writeSize)) .build()); - assertEquals(Protocol.HTTP_2, protocolRef.get()); + } + + private RequestBody requestBody(final boolean chunked, final long size, final int writeSize) { + final byte[] buffer = new byte[writeSize]; + Arrays.fill(buffer, (byte) 'x'); + + return new RequestBody() { + @Override public MediaType contentType() { + return MediaType.parse("text/plain; charset=utf-8"); + } + + @Override public long contentLength() throws IOException { + return chunked ? -1L : size; + } + + @Override public void writeTo(BufferedSink sink) throws IOException { + for (int count = 0; count < size; count += writeSize) { + sink.write(buffer, 0, (int) Math.min(size - count, writeSize)); + } + } + }; + } + + @Test public void emptyResponseBody() throws Exception { + server.enqueue(new MockResponse() + .addHeader("abc", "def")); + executeSynchronously("/") + .assertCode(200) + .assertHeader("abc", "def") + .assertBody(""); } private void makeFailingCall() { @@ -2357,9 +2379,24 @@ public final class CallTest { } } + private RecordedResponse executeSynchronously(String path, String... headers) throws IOException { + Request.Builder builder = new Request.Builder(); + builder.url(server.url(path)); + for (int i = 0, size = headers.length; i < size; i += 2) { + builder.addHeader(headers[i], headers[i + 1]); + } + return executeSynchronously(builder.build()); + } + private RecordedResponse executeSynchronously(Request request) throws IOException { - Response response = client.newCall(request).execute(); - return new RecordedResponse(request, response, null, response.body().string(), null); + Call call = client.newCall(request); + try { + Response response = call.execute(); + String bodyString = response.body().string(); + return new RecordedResponse(request, response, null, bodyString, null); + } catch (IOException e) { + return new RecordedResponse(request, null, null, null, e); + } } /** diff --git a/okhttp-tests/src/test/java/okhttp3/ConnectionReuseTest.java b/okhttp-tests/src/test/java/okhttp3/ConnectionReuseTest.java index 7423f7bfe..a25d8debe 100644 --- a/okhttp-tests/src/test/java/okhttp3/ConnectionReuseTest.java +++ b/okhttp-tests/src/test/java/okhttp3/ConnectionReuseTest.java @@ -18,11 +18,12 @@ package okhttp3; import java.util.Arrays; import java.util.concurrent.TimeUnit; import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLSocketFactory; import okhttp3.internal.SslContextBuilder; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; import okhttp3.mockwebserver.SocketPolicy; -import okhttp3.testing.RecordingHostnameVerifier; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestRule; @@ -30,6 +31,7 @@ import org.junit.rules.Timeout; import static okhttp3.TestUtil.defaultClient; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; public final class ConnectionReuseTest { @Rule public final TestRule timeout = new Timeout(30_000); @@ -233,11 +235,71 @@ public final class ConnectionReuseTest { assertEquals(0, server.takeRequest().getSequenceNumber()); } + @Test public void connectionsAreNotReusedIfSslSocketFactoryChanges() throws Exception { + enableHttps(); + server.enqueue(new MockResponse()); + server.enqueue(new MockResponse()); + + Request request = new Request.Builder() + .url(server.url("/")) + .build(); + + Response response = client.newCall(request).execute(); + response.body().close(); + + // This client shares a connection pool but has a different SSL socket factory. + SSLContext sslContext2 = SSLContext.getInstance("TLS"); + sslContext2.init(null, null, null); + SSLSocketFactory sslSocketFactory2 = sslContext2.getSocketFactory(); + OkHttpClient anotherClient = client.newBuilder() + .sslSocketFactory(sslSocketFactory2) + .build(); + + // This client fails to connect because the new SSL socket factory refuses. + try { + anotherClient.newCall(request).execute(); + fail(); + } catch (SSLException expected) { + } + } + + @Test public void connectionsAreNotReusedIfHostnameVerifierChanges() throws Exception { + enableHttps(); + server.enqueue(new MockResponse()); + server.enqueue(new MockResponse()); + + Request request = new Request.Builder() + .url(server.url("/")) + .build(); + + Response response1 = client.newCall(request).execute(); + response1.body().close(); + + // This client shares a connection pool but has a different SSL socket factory. + OkHttpClient anotherClient = client.newBuilder() + .hostnameVerifier(new RecordingHostnameVerifier()) + .build(); + + Response response2 = anotherClient.newCall(request).execute(); + response2.body().close(); + + assertEquals(0, server.takeRequest().getSequenceNumber()); + assertEquals(0, server.takeRequest().getSequenceNumber()); + } + + private void enableHttps() { + enableHttpsAndAlpn(Protocol.HTTP_1_1); + } + private void enableHttp2() { + enableHttpsAndAlpn(Protocol.HTTP_2, Protocol.HTTP_1_1); + } + + private void enableHttpsAndAlpn(Protocol... protocols) { client = client.newBuilder() .sslSocketFactory(sslContext.getSocketFactory()) .hostnameVerifier(new RecordingHostnameVerifier()) - .protocols(Arrays.asList(Protocol.HTTP_2, Protocol.HTTP_1_1)) + .protocols(Arrays.asList(protocols)) .build(); server.useHttps(sslContext.getSocketFactory(), false); server.setProtocols(client.protocols()); diff --git a/okhttp-tests/src/test/java/okhttp3/RecordedResponse.java b/okhttp-tests/src/test/java/okhttp3/RecordedResponse.java index 0c9d1169f..6e1f0728b 100644 --- a/okhttp-tests/src/test/java/okhttp3/RecordedResponse.java +++ b/okhttp-tests/src/test/java/okhttp3/RecordedResponse.java @@ -137,8 +137,14 @@ public final class RecordedResponse { return new RecordedResponse(cacheResponse.request(), cacheResponse, null, null, null); } - public void assertFailure(String... messages) { + public RecordedResponse assertFailure(Class exceptionClass) { + assertTrue(exceptionClass.isInstance(failure)); + return this; + } + + public RecordedResponse assertFailure(String... messages) { assertNotNull(failure); assertTrue(failure.getMessage(), Arrays.asList(messages).contains(failure.getMessage())); + return this; } } diff --git a/okhttp-tests/src/test/java/okhttp3/URLConnectionTest.java b/okhttp-tests/src/test/java/okhttp3/URLConnectionTest.java index 8a642735e..b86438336 100644 --- a/okhttp-tests/src/test/java/okhttp3/URLConnectionTest.java +++ b/okhttp-tests/src/test/java/okhttp3/URLConnectionTest.java @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package okhttp3; import java.io.IOException; @@ -69,7 +68,6 @@ import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; import okhttp3.mockwebserver.RecordedRequest; import okhttp3.mockwebserver.SocketPolicy; -import okhttp3.testing.RecordingHostnameVerifier; import okio.Buffer; import okio.BufferedSink; import okio.GzipSink; @@ -306,10 +304,9 @@ public final class URLConnectionTest { server.enqueue(new MockResponse().setBody("abc")); // Use a misconfigured proxy to guarantee that the request is retried. - FakeProxySelector proxySelector = new FakeProxySelector(); - proxySelector.proxies.add(server2.toProxyAddress()); urlFactory.setClient(urlFactory.client().newBuilder() - .proxySelector(proxySelector) + .proxySelector(new FakeProxySelector() + .addProxy(server2.toProxyAddress())) .build()); server2.shutdown(); @@ -617,6 +614,8 @@ public final class URLConnectionTest { } } + // TODO(jwilson): tests below this marker need to be migrated to OkHttp's request/response API. + @Test public void connectViaHttpsWithSSLFallback() throws Exception { server.useHttps(sslContext.getSocketFactory(), false); server.enqueue(new MockResponse().setSocketPolicy(FAIL_HANDSHAKE)); @@ -3560,19 +3559,6 @@ public final class URLConnectionTest { } } - private static class FakeProxySelector extends ProxySelector { - List proxies = new ArrayList<>(); - - @Override public List select(URI uri) { - // Don't handle 'socket' schemes, which the RI's Socket class may request (for SOCKS). - return uri.getScheme().equals("http") || uri.getScheme().equals("https") ? proxies - : Collections.singletonList(Proxy.NO_PROXY); - } - - @Override public void connectFailed(URI uri, SocketAddress sa, IOException ioe) { - } - } - /** * Tests that use this will fail unless boot classpath is set. Ex. {@code * -Xbootclasspath/p:/tmp/alpn-boot-8.0.0.v20140317} diff --git a/okhttp-tests/src/test/java/okhttp3/internal/framed/HttpOverSpdyTest.java b/okhttp-tests/src/test/java/okhttp3/internal/framed/HttpOverSpdyTest.java index 9716ef393..7c27cd29d 100644 --- a/okhttp-tests/src/test/java/okhttp3/internal/framed/HttpOverSpdyTest.java +++ b/okhttp-tests/src/test/java/okhttp3/internal/framed/HttpOverSpdyTest.java @@ -44,7 +44,7 @@ import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; import okhttp3.mockwebserver.RecordedRequest; import okhttp3.mockwebserver.SocketPolicy; -import okhttp3.testing.RecordingHostnameVerifier; +import okhttp3.RecordingHostnameVerifier; import okio.Buffer; import okio.BufferedSink; import okio.GzipSink; diff --git a/okhttp-tests/src/test/java/okhttp3/internal/http/RouteSelectorTest.java b/okhttp-tests/src/test/java/okhttp3/internal/http/RouteSelectorTest.java index 427d4af13..7f0b47037 100644 --- a/okhttp-tests/src/test/java/okhttp3/internal/http/RouteSelectorTest.java +++ b/okhttp-tests/src/test/java/okhttp3/internal/http/RouteSelectorTest.java @@ -35,6 +35,7 @@ import javax.net.ssl.SSLSocketFactory; import okhttp3.Address; import okhttp3.Authenticator; import okhttp3.ConnectionSpec; +import okhttp3.FakeDns; import okhttp3.Protocol; import okhttp3.Route; import okhttp3.internal.RouteDatabase; diff --git a/okhttp-tests/src/test/java/okhttp3/internal/tls/HostnameVerifierTest.java b/okhttp-tests/src/test/java/okhttp3/internal/tls/HostnameVerifierTest.java index 6d9ee3bf3..f03f197d9 100644 --- a/okhttp-tests/src/test/java/okhttp3/internal/tls/HostnameVerifierTest.java +++ b/okhttp-tests/src/test/java/okhttp3/internal/tls/HostnameVerifierTest.java @@ -23,6 +23,7 @@ import java.security.cert.X509Certificate; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLSession; import javax.security.auth.x500.X500Principal; +import okhttp3.FakeSSLSession; import okhttp3.internal.Util; import org.junit.Ignore; import org.junit.Test; diff --git a/okhttp-ws-tests/src/test/java/okhttp3/ws/WebSocketCallTest.java b/okhttp-ws-tests/src/test/java/okhttp3/ws/WebSocketCallTest.java index 3665e241c..931515209 100644 --- a/okhttp-ws-tests/src/test/java/okhttp3/ws/WebSocketCallTest.java +++ b/okhttp-ws-tests/src/test/java/okhttp3/ws/WebSocketCallTest.java @@ -30,7 +30,7 @@ import okhttp3.ResponseBody; import okhttp3.internal.SslContextBuilder; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; -import okhttp3.testing.RecordingHostnameVerifier; +import okhttp3.RecordingHostnameVerifier; import okio.Buffer; import org.junit.After; import org.junit.Rule;