| // Copyright 2014 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| package org.chromium.net; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| import static com.google.common.truth.Truth.assertWithMessage; |
| import static com.google.common.truth.TruthJUnit.assume; |
| |
| import static org.junit.Assert.assertThrows; |
| import static org.junit.Assert.fail; |
| |
| import static org.chromium.net.truth.UrlResponseInfoSubject.assertThat; |
| |
| import android.net.Network; |
| import android.os.Build; |
| import android.os.ConditionVariable; |
| import android.os.Process; |
| import android.os.StrictMode; |
| |
| import androidx.test.ext.junit.runners.AndroidJUnit4; |
| import androidx.test.filters.SmallTest; |
| |
| import org.jni_zero.NativeMethods; |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Rule; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| |
| import org.chromium.base.Log; |
| import org.chromium.base.test.util.DoNotBatch; |
| import org.chromium.net.CronetTestRule.CronetImplementation; |
| import org.chromium.net.CronetTestRule.IgnoreFor; |
| import org.chromium.net.CronetTestRule.RequiresMinAndroidApi; |
| import org.chromium.net.CronetTestRule.RequiresMinApi; |
| import org.chromium.net.NetworkChangeNotifierAutoDetect.ConnectivityManagerDelegate; |
| import org.chromium.net.TestUrlRequestCallback.FailureType; |
| import org.chromium.net.TestUrlRequestCallback.ResponseStep; |
| import org.chromium.net.apihelpers.UploadDataProviders; |
| import org.chromium.net.impl.CronetExceptionImpl; |
| import org.chromium.net.impl.CronetUrlRequest; |
| import org.chromium.net.impl.NetworkExceptionImpl; |
| import org.chromium.net.impl.UrlResponseInfoImpl; |
| import org.chromium.net.test.FailurePhase; |
| |
| import java.io.IOException; |
| import java.net.ConnectException; |
| import java.nio.ByteBuffer; |
| import java.util.AbstractMap; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.concurrent.Executor; |
| import java.util.concurrent.ExecutorService; |
| import java.util.concurrent.Executors; |
| import java.util.concurrent.atomic.AtomicBoolean; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| /** Test functionality of CronetUrlRequest. */ |
| @DoNotBatch(reason = "crbug/1459563") |
| @RunWith(AndroidJUnit4.class) |
| public class CronetUrlRequestTest { |
| private static final String TAG = CronetUrlRequestTest.class.getSimpleName(); |
| |
| // URL used for base tests. |
| private static final String TEST_URL = "http://127.0.0.1:8000"; |
| |
| @Rule public final CronetTestRule mTestRule = CronetTestRule.withAutomaticEngineStartup(); |
| |
| private MockUrlRequestJobFactory mMockUrlRequestJobFactory; |
| |
| @Before |
| public void setUp() throws Exception { |
| assertThat( |
| NativeTestServer.startNativeTestServer( |
| mTestRule.getTestFramework().getContext())) |
| .isTrue(); |
| } |
| |
| @After |
| public void tearDown() throws Exception { |
| if (mMockUrlRequestJobFactory != null) { |
| mMockUrlRequestJobFactory.shutdown(); |
| } |
| NativeTestServer.shutdownNativeTestServer(); |
| } |
| |
| private TestUrlRequestCallback startAndWaitForComplete(String url) throws Exception { |
| TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| // Create request. |
| UrlRequest.Builder builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder(url, callback, callback.getExecutor()); |
| UrlRequest urlRequest = builder.build(); |
| urlRequest.start(); |
| callback.blockForDone(); |
| // Wait for all posted tasks to be executed to ensure there is no unhandled exception. |
| callback.shutdownExecutorAndWait(); |
| assertThat(urlRequest.isDone()).isTrue(); |
| return callback; |
| } |
| |
| private void checkResponseInfo( |
| UrlResponseInfo responseInfo, |
| String expectedUrl, |
| int expectedHttpStatusCode, |
| String expectedHttpStatusText) { |
| assertThat(responseInfo).hasUrlThat().isEqualTo(expectedUrl); |
| assertThat(responseInfo.getUrlChain().get(responseInfo.getUrlChain().size() - 1)) |
| .isEqualTo(expectedUrl); |
| assertThat(responseInfo).hasHttpStatusCodeThat().isEqualTo(expectedHttpStatusCode); |
| assertThat(responseInfo).hasHttpStatusTextThat().isEqualTo(expectedHttpStatusText); |
| assertThat(responseInfo).wasNotCached(); |
| assertThat(responseInfo.toString()).isNotEmpty(); |
| } |
| |
| private void checkResponseInfoHeader( |
| UrlResponseInfo responseInfo, String headerName, String headerValue) { |
| Map<String, List<String>> responseHeaders = responseInfo.getAllHeaders(); |
| List<String> header = responseHeaders.get(headerName); |
| assertThat(header).contains(headerValue); |
| } |
| |
| @Test |
| @SmallTest |
| public void testBuilderChecks() throws Exception { |
| TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| |
| NullPointerException e = |
| assertThrows( |
| NullPointerException.class, |
| () -> |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder( |
| null, callback, callback.getExecutor())); |
| assertThat(e).hasMessageThat().isEqualTo("URL is required."); |
| |
| e = |
| assertThrows( |
| NullPointerException.class, |
| () -> |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder( |
| NativeTestServer.getRedirectURL(), |
| null, |
| callback.getExecutor())); |
| assertThat(e).hasMessageThat().isEqualTo("Callback is required."); |
| |
| e = |
| assertThrows( |
| NullPointerException.class, |
| () -> |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder( |
| NativeTestServer.getRedirectURL(), callback, null)); |
| assertThat(e).hasMessageThat().isEqualTo("Executor is required."); |
| |
| // Verify successful creation doesn't throw. |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder( |
| NativeTestServer.getRedirectURL(), callback, callback.getExecutor()); |
| } |
| |
| @Test |
| @SmallTest |
| public void testSimpleGet() throws Exception { |
| String url = NativeTestServer.getEchoMethodURL(); |
| TestUrlRequestCallback callback = startAndWaitForComplete(url); |
| assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); |
| // Default method is 'GET'. |
| assertThat(callback.mResponseAsString).isEqualTo("GET"); |
| assertThat(callback.mRedirectCount).isEqualTo(0); |
| assertThat(ResponseStep.ON_SUCCEEDED).isEqualTo(callback.mResponseStep); |
| UrlResponseInfo urlResponseInfo = |
| createUrlResponseInfo( |
| new String[] {url}, |
| "OK", |
| 200, |
| 86, |
| "Connection", |
| "close", |
| "Content-Length", |
| "3", |
| "Content-Type", |
| "text/plain"); |
| mTestRule.assertResponseEquals(urlResponseInfo, callback.getResponseInfoWithChecks()); |
| checkResponseInfo( |
| callback.getResponseInfoWithChecks(), |
| NativeTestServer.getEchoMethodURL(), |
| 200, |
| "OK"); |
| } |
| |
| UrlResponseInfo createUrlResponseInfo( |
| String[] urls, String message, int statusCode, int receivedBytes, String... headers) { |
| ArrayList<Map.Entry<String, String>> headersList = new ArrayList<>(); |
| for (int i = 0; i < headers.length; i += 2) { |
| headersList.add( |
| new AbstractMap.SimpleImmutableEntry<String, String>( |
| headers[i], headers[i + 1])); |
| } |
| UrlResponseInfoImpl unknown = |
| new UrlResponseInfoImpl( |
| Arrays.asList(urls), |
| statusCode, |
| message, |
| headersList, |
| false, |
| "unknown", |
| ":0", |
| receivedBytes); |
| return unknown; |
| } |
| |
| void runConnectionMigrationTest(boolean disableConnectionMigration) { |
| // URLRequest load flags at net/base/load_flags_list.h. |
| int connectionMigrationLoadFlag = |
| CronetUrlRequestTestJni.get().getConnectionMigrationDisableLoadFlag(); |
| TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| callback.setAutoAdvance(false); |
| // Create builder, start a request, and check if default load_flags are set correctly. |
| ExperimentalUrlRequest.Builder builder = |
| (ExperimentalUrlRequest.Builder) |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder( |
| NativeTestServer.getFileURL("/success.txt"), |
| callback, |
| callback.getExecutor()); |
| // Disable connection migration. |
| if (disableConnectionMigration) builder.disableConnectionMigration(); |
| UrlRequest urlRequest = builder.build(); |
| urlRequest.start(); |
| callback.waitForNextStep(); |
| int loadFlags = CronetTestUtil.getLoadFlags(urlRequest); |
| if (disableConnectionMigration) { |
| assertThat(loadFlags & connectionMigrationLoadFlag) |
| .isEqualTo(connectionMigrationLoadFlag); |
| } else { |
| assertThat(loadFlags & connectionMigrationLoadFlag).isEqualTo(0); |
| } |
| callback.setAutoAdvance(true); |
| callback.startNextRead(urlRequest); |
| callback.blockForDone(); |
| } |
| |
| /** Tests that disabling connection migration sets the URLRequest load flag correctly. */ |
| @Test |
| @SmallTest |
| @IgnoreFor( |
| implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, |
| reason = "bug.com/1494845: tests native implementation internals") |
| public void testLoadFlagsWithConnectionMigration() throws Exception { |
| runConnectionMigrationTest(/* disableConnectionMigration= */ false); |
| runConnectionMigrationTest(/* disableConnectionMigration= */ true); |
| } |
| |
| /** |
| * Tests a redirect by running it step-by-step. Also tests that delaying a request works as |
| * expected. To make sure there are no unexpected pending messages, does a GET between |
| * UrlRequest.Callback callbacks. |
| */ |
| @Test |
| @SmallTest |
| public void testRedirectAsync() throws Exception { |
| // Start the request and wait to see the redirect. |
| TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| callback.setAutoAdvance(false); |
| UrlRequest.Builder builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder( |
| NativeTestServer.getRedirectURL(), |
| callback, |
| callback.getExecutor()); |
| UrlRequest urlRequest = builder.build(); |
| urlRequest.start(); |
| callback.waitForNextStep(); |
| |
| // Check the redirect. |
| assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_RECEIVED_REDIRECT); |
| assertThat(callback.mRedirectResponseInfoList).hasSize(1); |
| checkResponseInfo( |
| callback.mRedirectResponseInfoList.get(0), |
| NativeTestServer.getRedirectURL(), |
| 302, |
| "Found"); |
| assertThat(callback.mRedirectResponseInfoList.get(0).getUrlChain()).hasSize(1); |
| assertThat(callback.mRedirectUrlList.get(0)).isEqualTo(NativeTestServer.getSuccessURL()); |
| checkResponseInfoHeader( |
| callback.mRedirectResponseInfoList.get(0), "redirect-header", "header-value"); |
| |
| UrlResponseInfo expected = |
| createUrlResponseInfo( |
| new String[] {NativeTestServer.getRedirectURL()}, |
| "Found", |
| 302, |
| 73, |
| "Location", |
| "/success.txt", |
| "redirect-header", |
| "header-value"); |
| mTestRule.assertResponseEquals(expected, callback.mRedirectResponseInfoList.get(0)); |
| |
| // Wait for an unrelated request to finish. The request should not |
| // advance until followRedirect is invoked. |
| testSimpleGet(); |
| assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_RECEIVED_REDIRECT); |
| assertThat(callback.mRedirectResponseInfoList).hasSize(1); |
| |
| // Follow the redirect and wait for the next set of headers. |
| urlRequest.followRedirect(); |
| callback.waitForNextStep(); |
| |
| assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_RESPONSE_STARTED); |
| assertThat(callback.mRedirectResponseInfoList).hasSize(1); |
| assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); |
| checkResponseInfo( |
| callback.getResponseInfoWithChecks(), NativeTestServer.getSuccessURL(), 200, "OK"); |
| assertThat(callback.getResponseInfoWithChecks()) |
| .hasUrlChainThat() |
| .containsExactly( |
| NativeTestServer.getRedirectURL(), NativeTestServer.getSuccessURL()) |
| .inOrder(); |
| |
| // Wait for an unrelated request to finish. The request should not |
| // advance until read is invoked. |
| testSimpleGet(); |
| assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_RESPONSE_STARTED); |
| |
| // One read should get all the characters, but best not to depend on |
| // how much is actually read from the socket at once. |
| while (!callback.isDone()) { |
| callback.startNextRead(urlRequest); |
| callback.waitForNextStep(); |
| String response = callback.mResponseAsString; |
| ResponseStep step = callback.mResponseStep; |
| if (!callback.isDone()) { |
| assertThat(step).isEqualTo(ResponseStep.ON_READ_COMPLETED); |
| } |
| // Should not receive any messages while waiting for another get, |
| // as the next read has not been started. |
| testSimpleGet(); |
| assertThat(callback.mResponseAsString).isEqualTo(response); |
| assertThat(callback.mResponseStep).isEqualTo(step); |
| } |
| assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_SUCCEEDED); |
| assertThat(callback.mResponseAsString).isEqualTo(NativeTestServer.SUCCESS_BODY); |
| |
| UrlResponseInfo urlResponseInfo = |
| createUrlResponseInfo( |
| new String[] { |
| NativeTestServer.getRedirectURL(), NativeTestServer.getSuccessURL() |
| }, |
| "OK", |
| 200, |
| 258, |
| "Content-Type", |
| "text/plain", |
| "Access-Control-Allow-Origin", |
| "*", |
| "header-name", |
| "header-value", |
| "multi-header-name", |
| "header-value1", |
| "multi-header-name", |
| "header-value2"); |
| |
| mTestRule.assertResponseEquals(urlResponseInfo, callback.getResponseInfoWithChecks()); |
| // Make sure there are no other pending messages, which would trigger |
| // asserts in TestUrlRequestCallback. |
| testSimpleGet(); |
| } |
| |
| /** Tests redirect without location header doesn't cause a crash. */ |
| @Test |
| @SmallTest |
| public void testRedirectWithNullLocationHeader() throws Exception { |
| String url = NativeTestServer.getFileURL("/redirect_broken_header.html"); |
| TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| |
| UrlRequest.Builder builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder(url, callback, callback.getExecutor()); |
| final UrlRequest urlRequest = builder.build(); |
| urlRequest.start(); |
| callback.blockForDone(); |
| assertThat(callback.mResponseAsString) |
| .isEqualTo( |
| "<!DOCTYPE html>\n<html>\n<head>\n<title>Redirect</title>\n" |
| + "<p>Redirecting...</p>\n</head>\n</html>\n"); |
| assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_SUCCEEDED); |
| assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(302); |
| assertThat(callback.mError).isNull(); |
| assertThat(callback.mOnErrorCalled).isFalse(); |
| } |
| |
| /** Tests onRedirectReceived after cancel doesn't cause a crash. */ |
| @Test |
| @SmallTest |
| public void testOnRedirectReceivedAfterCancel() throws Exception { |
| final AtomicBoolean failedExpectation = new AtomicBoolean(); |
| TestUrlRequestCallback callback = |
| new TestUrlRequestCallback() { |
| @Override |
| public void onRedirectReceived( |
| UrlRequest request, UrlResponseInfo info, String newLocationUrl) { |
| assertThat(mRedirectCount).isEqualTo(0); |
| failedExpectation.compareAndSet(false, 0 != mRedirectCount); |
| super.onRedirectReceived(request, info, newLocationUrl); |
| // Cancel the request, so the second redirect will not be received. |
| request.cancel(); |
| } |
| |
| @Override |
| public void onResponseStarted(UrlRequest request, UrlResponseInfo info) { |
| failedExpectation.set(true); |
| fail(); |
| } |
| |
| @Override |
| public void onReadCompleted( |
| UrlRequest request, UrlResponseInfo info, ByteBuffer byteBuffer) { |
| failedExpectation.set(true); |
| fail(); |
| } |
| |
| @Override |
| public void onSucceeded(UrlRequest request, UrlResponseInfo info) { |
| failedExpectation.set(true); |
| fail(); |
| } |
| |
| @Override |
| public void onFailed( |
| UrlRequest request, UrlResponseInfo info, CronetException error) { |
| failedExpectation.set(true); |
| fail(); |
| } |
| |
| @Override |
| public void onCanceled(UrlRequest request, UrlResponseInfo info) { |
| assertThat(mRedirectCount).isEqualTo(1); |
| failedExpectation.compareAndSet(false, 1 != mRedirectCount); |
| super.onCanceled(request, info); |
| } |
| }; |
| |
| UrlRequest.Builder builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder( |
| NativeTestServer.getMultiRedirectURL(), |
| callback, |
| callback.getExecutor()); |
| |
| final UrlRequest urlRequest = builder.build(); |
| urlRequest.start(); |
| callback.blockForDone(); |
| assertThat(failedExpectation.get()).isFalse(); |
| // Check that only one redirect is received. |
| assertThat(callback.mRedirectCount).isEqualTo(1); |
| // Check that onCanceled is called. |
| assertThat(callback.mOnCanceledCalled).isTrue(); |
| } |
| |
| @Test |
| @SmallTest |
| public void testNotFound() throws Exception { |
| String url = NativeTestServer.getFileURL("/notfound.html"); |
| TestUrlRequestCallback callback = startAndWaitForComplete(url); |
| checkResponseInfo(callback.getResponseInfoWithChecks(), url, 404, "Not Found"); |
| assertThat(callback.mResponseAsString) |
| .isEqualTo( |
| "<!DOCTYPE html>\n<html>\n<head>\n<title>Not found</title>\n" |
| + "<p>Test page loaded.</p>\n</head>\n</html>\n"); |
| assertThat(callback.mRedirectCount).isEqualTo(0); |
| assertThat(ResponseStep.ON_SUCCEEDED).isEqualTo(callback.mResponseStep); |
| } |
| |
| // Checks that UrlRequest.Callback.onFailed is only called once in the case |
| // of ERR_CONTENT_LENGTH_MISMATCH, which has an unusual failure path. |
| // See http://crbug.com/468803. |
| @Test |
| @SmallTest |
| @IgnoreFor( |
| implementations = {CronetImplementation.FALLBACK}, |
| reason = "No canonical exception to assert on") |
| public void testContentLengthMismatchFailsOnce() throws Exception { |
| String url = NativeTestServer.getFileURL("/content_length_mismatch.html"); |
| TestUrlRequestCallback callback = startAndWaitForComplete(url); |
| assertThat(callback.getResponseInfo()).hasHttpStatusCodeThat().isEqualTo(200); |
| // The entire response body will be read before the error is returned. |
| // This is because the network stack returns data as it's read from the |
| // socket, and the socket close message which triggers the error will |
| // only be passed along after all data has been read. |
| assertThat(callback.mResponseAsString) |
| .isEqualTo("Response that lies about content length."); |
| assertThat(callback.mError) |
| .hasMessageThat() |
| .contains("Exception in CronetUrlRequest: net::ERR_CONTENT_LENGTH_MISMATCH"); |
| // Wait for a couple round trips to make sure there are no pending |
| // onFailed messages. This test relies on checks in |
| // TestUrlRequestCallback catching a second onFailed call. |
| testSimpleGet(); |
| } |
| |
| @Test |
| @SmallTest |
| public void testSetHttpMethod() throws Exception { |
| TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| String methodName = "HEAD"; |
| UrlRequest.Builder builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder( |
| NativeTestServer.getEchoMethodURL(), |
| callback, |
| callback.getExecutor()); |
| // Try to set 'null' method. |
| NullPointerException e = |
| assertThrows(NullPointerException.class, () -> builder.setHttpMethod(null)); |
| assertThat(e).hasMessageThat().isEqualTo("Method is required."); |
| |
| builder.setHttpMethod(methodName); |
| builder.build().start(); |
| callback.blockForDone(); |
| assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); |
| assertThat(callback.mHttpResponseDataLength).isEqualTo(0); |
| } |
| |
| @Test |
| @SmallTest |
| public void testBadMethod() throws Exception { |
| TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| UrlRequest.Builder builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder(TEST_URL, callback, callback.getExecutor()); |
| builder.setHttpMethod("bad:method!"); |
| IllegalArgumentException e = |
| assertThrows(IllegalArgumentException.class, () -> builder.build().start()); |
| assertThat(e).hasMessageThat().isEqualTo("Invalid http method bad:method!"); |
| } |
| |
| @Test |
| @SmallTest |
| public void testBadHeaderName() throws Exception { |
| TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| UrlRequest.Builder builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder(TEST_URL, callback, callback.getExecutor()); |
| builder.addHeader("header:name", "headervalue"); |
| IllegalArgumentException e = |
| assertThrows(IllegalArgumentException.class, () -> builder.build().start()); |
| if (mTestRule.implementationUnderTest() == CronetImplementation.AOSP_PLATFORM && |
| !mTestRule.isRunningInAOSP()) { |
| // TODO(b/307234565): Remove check once chromium emulator has latest changes. |
| assertThat(e).hasMessageThat().isEqualTo("Invalid header header:name=headervalue"); |
| } else { |
| assertThat(e).hasMessageThat().isEqualTo("Invalid header with headername: header:name"); |
| } |
| } |
| |
| @Test |
| @SmallTest |
| public void testAcceptEncodingIgnored() throws Exception { |
| TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| UrlRequest.Builder builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder( |
| NativeTestServer.getEchoAllHeadersURL(), |
| callback, |
| callback.getExecutor()); |
| // This line should eventually throw an exception, once callers have migrated |
| builder.addHeader("accept-encoding", "foozip"); |
| builder.build().start(); |
| callback.blockForDone(); |
| assertThat(callback.mResponseAsString).doesNotContain("foozip"); |
| } |
| |
| @Test |
| @SmallTest |
| public void testBadHeaderValue() throws Exception { |
| TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| UrlRequest.Builder builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder(TEST_URL, callback, callback.getExecutor()); |
| builder.addHeader("headername", "bad header\r\nvalue"); |
| IllegalArgumentException e = |
| assertThrows(IllegalArgumentException.class, () -> builder.build().start()); |
| if (mTestRule.implementationUnderTest() == CronetImplementation.AOSP_PLATFORM && |
| !mTestRule.isRunningInAOSP()) { |
| // TODO(b/307234565): Remove check once chromium emulator has latest changes. |
| assertThat(e) |
| .hasMessageThat() |
| .isEqualTo("Invalid header headername=bad header\r\nvalue"); |
| } else { |
| assertThat(e).hasMessageThat().isEqualTo("Invalid header with headername: headername"); |
| } |
| } |
| |
| @Test |
| @SmallTest |
| public void testAddHeader() throws Exception { |
| TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| String headerName = "header-name"; |
| String headerValue = "header-value"; |
| UrlRequest.Builder builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder( |
| NativeTestServer.getEchoHeaderURL(headerName), |
| callback, |
| callback.getExecutor()); |
| |
| builder.addHeader(headerName, headerValue); |
| builder.build().start(); |
| callback.blockForDone(); |
| assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); |
| assertThat(callback.mResponseAsString).isEqualTo(headerValue); |
| } |
| |
| @Test |
| @SmallTest |
| public void testMultiRequestHeaders() throws Exception { |
| TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| String headerName = "header-name"; |
| String headerValue1 = "header-value1"; |
| String headerValue2 = "header-value2"; |
| UrlRequest.Builder builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder( |
| NativeTestServer.getEchoAllHeadersURL(), |
| callback, |
| callback.getExecutor()); |
| builder.addHeader(headerName, headerValue1); |
| builder.addHeader(headerName, headerValue2); |
| builder.build().start(); |
| callback.blockForDone(); |
| assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); |
| String headers = callback.mResponseAsString; |
| Pattern pattern = Pattern.compile(headerName + ":\\s(.*)\\r\\n"); |
| Matcher matcher = pattern.matcher(headers); |
| List<String> actualValues = new ArrayList<String>(); |
| while (matcher.find()) { |
| actualValues.add(matcher.group(1)); |
| } |
| assertThat(actualValues).containsExactly("header-value2"); |
| } |
| |
| @Test |
| @SmallTest |
| public void testCustomReferer_verbatim() throws Exception { |
| TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| String refererName = "Referer"; |
| String refererValue = "http://example.com/"; |
| UrlRequest.Builder builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder( |
| NativeTestServer.getEchoHeaderURL(refererName), |
| callback, |
| callback.getExecutor()); |
| builder.addHeader(refererName, refererValue); |
| builder.build().start(); |
| callback.blockForDone(); |
| assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); |
| assertThat(callback.mResponseAsString).isEqualTo(refererValue); |
| } |
| |
| @Test |
| @SmallTest |
| @IgnoreFor( |
| implementations = {CronetImplementation.FALLBACK}, |
| reason = "This is not the case for the fallback implementation") |
| public void testCustomReferer_changeToCanonical() throws Exception { |
| TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| String refererName = "Referer"; |
| String refererValueNoTrailingSlash = "http://example.com"; |
| UrlRequest.Builder builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder( |
| NativeTestServer.getEchoHeaderURL(refererName), |
| callback, |
| callback.getExecutor()); |
| builder.addHeader(refererName, refererValueNoTrailingSlash); |
| builder.build().start(); |
| callback.blockForDone(); |
| assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); |
| assertThat(callback.mResponseAsString).isEqualTo(refererValueNoTrailingSlash + "/"); |
| } |
| |
| @Test |
| @SmallTest |
| @IgnoreFor( |
| implementations = {CronetImplementation.FALLBACK}, |
| reason = "This is not the case for the fallback implementation") |
| public void testCustomReferer_discardInvalid() throws Exception { |
| TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| String refererName = "Referer"; |
| String invalidRefererValue = "foobar"; |
| UrlRequest.Builder builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder( |
| NativeTestServer.getEchoHeaderURL(refererName), |
| callback, |
| callback.getExecutor()); |
| builder.addHeader(refererName, invalidRefererValue); |
| builder.build().start(); |
| callback.blockForDone(); |
| assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); |
| assertThat(callback.mResponseAsString).isEqualTo("Header not found. :("); |
| } |
| |
| @Test |
| @SmallTest |
| public void testCustomUserAgent() throws Exception { |
| TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| String userAgentName = "User-Agent"; |
| String userAgentValue = "User-Agent-Value"; |
| UrlRequest.Builder builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder( |
| NativeTestServer.getEchoHeaderURL(userAgentName), |
| callback, |
| callback.getExecutor()); |
| builder.addHeader(userAgentName, userAgentValue); |
| builder.build().start(); |
| callback.blockForDone(); |
| assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); |
| assertThat(callback.mResponseAsString).isEqualTo(userAgentValue); |
| } |
| |
| @Test |
| @SmallTest |
| public void testDefaultUserAgent() throws Exception { |
| TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| String headerName = "User-Agent"; |
| UrlRequest.Builder builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder( |
| NativeTestServer.getEchoHeaderURL(headerName), |
| callback, |
| callback.getExecutor()); |
| builder.build().start(); |
| callback.blockForDone(); |
| assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); |
| assertWithMessage( |
| "Default User-Agent should contain Cronet/n.n.n.n but is " |
| + callback.mResponseAsString) |
| .that(callback.mResponseAsString) |
| .matches(Pattern.compile(".+Cronet/\\d+\\.\\d+\\.\\d+\\.\\d+.+")); |
| } |
| |
| @Test |
| @SmallTest |
| @IgnoreFor( |
| implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, |
| reason = "crbug.com/1495309: Enable once we drop MockUrlRequestJobFactory") |
| public void testMockSuccess() throws Exception { |
| mMockUrlRequestJobFactory = |
| new MockUrlRequestJobFactory(mTestRule.getTestFramework().getEngine()); |
| TestUrlRequestCallback callback = startAndWaitForComplete(NativeTestServer.getSuccessURL()); |
| assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); |
| assertThat(callback.mRedirectResponseInfoList).isEmpty(); |
| assertThat(callback.mHttpResponseDataLength).isNotEqualTo(0); |
| assertThat(ResponseStep.ON_SUCCEEDED).isEqualTo(callback.mResponseStep); |
| Map<String, List<String>> responseHeaders = |
| callback.getResponseInfoWithChecks().getAllHeaders(); |
| assertThat(responseHeaders).containsEntry("header-name", Arrays.asList("header-value")); |
| assertThat(responseHeaders) |
| .containsEntry( |
| "multi-header-name", Arrays.asList("header-value1", "header-value2")); |
| } |
| |
| @Test |
| @SmallTest |
| public void testResponseHeadersList() throws Exception { |
| TestUrlRequestCallback callback = startAndWaitForComplete(NativeTestServer.getSuccessURL()); |
| assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); |
| List<Map.Entry<String, String>> responseHeaders = |
| callback.getResponseInfoWithChecks().getAllHeadersAsList(); |
| |
| assertThat(new AbstractMap.SimpleEntry<>("Content-Type", "text/plain")) |
| .isEqualTo(responseHeaders.get(0)); |
| assertThat(new AbstractMap.SimpleEntry<>("Access-Control-Allow-Origin", "*")) |
| .isEqualTo(responseHeaders.get(1)); |
| assertThat(new AbstractMap.SimpleEntry<>("header-name", "header-value")) |
| .isEqualTo(responseHeaders.get(2)); |
| assertThat(new AbstractMap.SimpleEntry<>("multi-header-name", "header-value1")) |
| .isEqualTo(responseHeaders.get(3)); |
| assertThat(new AbstractMap.SimpleEntry<>("multi-header-name", "header-value2")) |
| .isEqualTo(responseHeaders.get(4)); |
| } |
| |
| @Test |
| @SmallTest |
| @IgnoreFor( |
| implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, |
| reason = "crbug.com/1495309: Enable once we drop MockUrlRequestJobFactory") |
| public void testMockMultiRedirect() throws Exception { |
| mMockUrlRequestJobFactory = |
| new MockUrlRequestJobFactory(mTestRule.getTestFramework().getEngine()); |
| TestUrlRequestCallback callback = |
| startAndWaitForComplete(NativeTestServer.getMultiRedirectURL()); |
| UrlResponseInfo mResponseInfo = callback.getResponseInfoWithChecks(); |
| assertThat(callback.mRedirectCount).isEqualTo(2); |
| assertThat(mResponseInfo).hasHttpStatusCodeThat().isEqualTo(200); |
| assertThat(callback.mRedirectResponseInfoList).hasSize(2); |
| |
| // Check first redirect (multiredirect.html -> redirect.html) |
| UrlResponseInfo firstExpectedResponseInfo = |
| createUrlResponseInfo( |
| new String[] {NativeTestServer.getMultiRedirectURL()}, |
| "Found", |
| 302, |
| 76, |
| "Location", |
| "/redirect.html", |
| "redirect-header0", |
| "header-value"); |
| UrlResponseInfo firstRedirectResponseInfo = callback.mRedirectResponseInfoList.get(0); |
| mTestRule.assertResponseEquals(firstExpectedResponseInfo, firstRedirectResponseInfo); |
| |
| // Check second redirect (redirect.html -> success.txt) |
| UrlResponseInfo secondExpectedResponseInfo = |
| createUrlResponseInfo( |
| new String[] { |
| NativeTestServer.getMultiRedirectURL(), |
| NativeTestServer.getRedirectURL(), |
| NativeTestServer.getSuccessURL() |
| }, |
| "OK", |
| 200, |
| 334, |
| "Content-Type", |
| "text/plain", |
| "Access-Control-Allow-Origin", |
| "*", |
| "header-name", |
| "header-value", |
| "multi-header-name", |
| "header-value1", |
| "multi-header-name", |
| "header-value2"); |
| |
| mTestRule.assertResponseEquals(secondExpectedResponseInfo, mResponseInfo); |
| assertThat(callback.mHttpResponseDataLength).isNotEqualTo(0); |
| assertThat(callback.mRedirectCount).isEqualTo(2); |
| assertThat(ResponseStep.ON_SUCCEEDED).isEqualTo(callback.mResponseStep); |
| } |
| |
| @Test |
| @SmallTest |
| @IgnoreFor( |
| implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, |
| reason = "crbug.com/1495309: Enable once we drop MockUrlRequestJobFactory") |
| public void testMockNotFound() throws Exception { |
| mMockUrlRequestJobFactory = |
| new MockUrlRequestJobFactory(mTestRule.getTestFramework().getEngine()); |
| TestUrlRequestCallback callback = |
| startAndWaitForComplete(NativeTestServer.getNotFoundURL()); |
| UrlResponseInfo expected = |
| createUrlResponseInfo( |
| new String[] {NativeTestServer.getNotFoundURL()}, "Not Found", 404, 120); |
| mTestRule.assertResponseEquals(expected, callback.getResponseInfoWithChecks()); |
| assertThat(callback.mHttpResponseDataLength).isNotEqualTo(0); |
| assertThat(callback.mRedirectCount).isEqualTo(0); |
| assertThat(callback.mOnErrorCalled).isFalse(); |
| assertThat(ResponseStep.ON_SUCCEEDED).isEqualTo(callback.mResponseStep); |
| } |
| |
| @Test |
| @SmallTest |
| @IgnoreFor( |
| implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, |
| reason = "crbug.com/1495309: Enable once we drop MockUrlRequestJobFactory") |
| public void testMockStartAsyncError() throws Exception { |
| mMockUrlRequestJobFactory = |
| new MockUrlRequestJobFactory(mTestRule.getTestFramework().getEngine()); |
| final int arbitraryNetError = -3; |
| TestUrlRequestCallback callback = |
| startAndWaitForComplete( |
| MockUrlRequestJobFactory.getMockUrlWithFailure( |
| FailurePhase.START, arbitraryNetError)); |
| assertThat(callback.getResponseInfo()).isNull(); |
| assertThat(callback.mError).isNotNull(); |
| assertThat(((NetworkException) callback.mError).getCronetInternalErrorCode()) |
| .isEqualTo(arbitraryNetError); |
| assertThat(callback.mRedirectCount).isEqualTo(0); |
| assertThat(callback.mOnErrorCalled).isTrue(); |
| assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_FAILED); |
| } |
| |
| @Test |
| @SmallTest |
| @IgnoreFor( |
| implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, |
| reason = "crbug.com/1495309: Enable once we drop MockUrlRequestJobFactory") |
| public void testMockReadDataSyncError() throws Exception { |
| mMockUrlRequestJobFactory = |
| new MockUrlRequestJobFactory(mTestRule.getTestFramework().getEngine()); |
| final int arbitraryNetError = -4; |
| TestUrlRequestCallback callback = |
| startAndWaitForComplete( |
| MockUrlRequestJobFactory.getMockUrlWithFailure( |
| FailurePhase.READ_SYNC, arbitraryNetError)); |
| assertThat(callback.getResponseInfo()).hasHttpStatusCodeThat().isEqualTo(200); |
| assertThat(callback.getResponseInfo()).hasReceivedByteCountThat().isEqualTo(15); |
| assertThat(callback.mError).isNotNull(); |
| assertThat(((NetworkException) callback.mError).getCronetInternalErrorCode()) |
| .isEqualTo(arbitraryNetError); |
| assertThat(callback.mRedirectCount).isEqualTo(0); |
| assertThat(callback.mOnErrorCalled).isTrue(); |
| assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_FAILED); |
| } |
| |
| @Test |
| @SmallTest |
| @IgnoreFor( |
| implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, |
| reason = "crbug.com/1495309: Enable once we drop MockUrlRequestJobFactory") |
| public void testMockReadDataAsyncError() throws Exception { |
| mMockUrlRequestJobFactory = |
| new MockUrlRequestJobFactory(mTestRule.getTestFramework().getEngine()); |
| final int arbitraryNetError = -5; |
| TestUrlRequestCallback callback = |
| startAndWaitForComplete( |
| MockUrlRequestJobFactory.getMockUrlWithFailure( |
| FailurePhase.READ_ASYNC, arbitraryNetError)); |
| assertThat(callback.getResponseInfo()).hasHttpStatusCodeThat().isEqualTo(200); |
| assertThat(callback.getResponseInfo()).hasReceivedByteCountThat().isEqualTo(15); |
| assertThat(callback.mError).isNotNull(); |
| assertThat(((NetworkException) callback.mError).getCronetInternalErrorCode()) |
| .isEqualTo(arbitraryNetError); |
| assertThat(callback.mRedirectCount).isEqualTo(0); |
| assertThat(callback.mOnErrorCalled).isTrue(); |
| assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_FAILED); |
| } |
| |
| /** Tests that request continues when client certificate is requested. */ |
| @Test |
| @SmallTest |
| @IgnoreFor( |
| implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, |
| reason = "crbug.com/1495309: Enable once we drop MockUrlRequestJobFactory") |
| public void testMockClientCertificateRequested() throws Exception { |
| mMockUrlRequestJobFactory = |
| new MockUrlRequestJobFactory(mTestRule.getTestFramework().getEngine()); |
| TestUrlRequestCallback callback = |
| startAndWaitForComplete( |
| MockUrlRequestJobFactory.getMockUrlForClientCertificateRequest()); |
| assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); |
| assertThat(callback.mResponseAsString).isEqualTo("data"); |
| assertThat(callback.mRedirectCount).isEqualTo(0); |
| assertThat(callback.mError).isNull(); |
| assertThat(callback.mOnErrorCalled).isFalse(); |
| } |
| |
| /** Tests that an SSL cert error will be reported via {@link UrlRequest.Callback#onFailed}. */ |
| @Test |
| @SmallTest |
| @IgnoreFor( |
| implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, |
| reason = "crbug.com/1495309: Enable once we drop MockUrlRequestJobFactory") |
| public void testMockSSLCertificateError() throws Exception { |
| mMockUrlRequestJobFactory = |
| new MockUrlRequestJobFactory(mTestRule.getTestFramework().getEngine()); |
| TestUrlRequestCallback callback = |
| startAndWaitForComplete( |
| MockUrlRequestJobFactory.getMockUrlForSSLCertificateError()); |
| assertThat(callback.getResponseInfo()).isNull(); |
| assertThat(callback.mOnErrorCalled).isTrue(); |
| assertThat(callback.mError) |
| .hasMessageThat() |
| .contains("Exception in CronetUrlRequest: net::ERR_CERT_DATE_INVALID"); |
| assertThat(((NetworkException) callback.mError).getCronetInternalErrorCode()) |
| .isEqualTo(-201); |
| assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_FAILED); |
| } |
| |
| /** Checks that the buffer is updated correctly, when starting at an offset. */ |
| @Test |
| @SmallTest |
| @IgnoreFor( |
| implementations = {CronetImplementation.FALLBACK}, |
| reason = "No canonical exception to assert on") |
| public void testSimpleGetBufferUpdates() throws Exception { |
| TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| callback.setAutoAdvance(false); |
| // Since the default method is "GET", the expected response body is also |
| // "GET". |
| UrlRequest.Builder builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder( |
| NativeTestServer.getEchoMethodURL(), |
| callback, |
| callback.getExecutor()); |
| UrlRequest urlRequest = builder.build(); |
| urlRequest.start(); |
| callback.waitForNextStep(); |
| |
| ByteBuffer readBuffer = ByteBuffer.allocateDirect(5); |
| readBuffer.put("FOR".getBytes()); |
| assertThat(readBuffer.position()).isEqualTo(3); |
| |
| // Read first two characters of the response ("GE"). It's theoretically |
| // possible to need one read per character, though in practice, |
| // shouldn't happen. |
| while (callback.mResponseAsString.length() < 2) { |
| assertThat(callback.isDone()).isFalse(); |
| callback.startNextRead(urlRequest, readBuffer); |
| callback.waitForNextStep(); |
| } |
| |
| // Make sure the two characters were read. |
| assertThat(callback.mResponseAsString).isEqualTo("GE"); |
| |
| // Check the contents of the entire buffer. The first 3 characters |
| // should not have been changed, and the last two should be the first |
| // two characters from the response. |
| assertThat(bufferContentsToString(readBuffer, 0, 5)).isEqualTo("FORGE"); |
| // The limit and position should be 5. |
| assertThat(readBuffer.limit()).isEqualTo(5); |
| assertThat(readBuffer.position()).isEqualTo(5); |
| |
| assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_READ_COMPLETED); |
| |
| // Start reading from position 3. Since the only remaining character |
| // from the response is a "T", when the read completes, the buffer |
| // should contain "FORTE", with a position() of 4 and a limit() of 5. |
| readBuffer.position(3); |
| callback.startNextRead(urlRequest, readBuffer); |
| callback.waitForNextStep(); |
| |
| // Make sure all three characters of the response have now been read. |
| assertThat(callback.mResponseAsString).isEqualTo("GET"); |
| |
| // Check the entire contents of the buffer. Only the third character |
| // should have been modified. |
| assertThat(bufferContentsToString(readBuffer, 0, 5)).isEqualTo("FORTE"); |
| |
| // Make sure position and limit were updated correctly. |
| assertThat(readBuffer.position()).isEqualTo(4); |
| assertThat(readBuffer.limit()).isEqualTo(5); |
| |
| assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_READ_COMPLETED); |
| |
| // One more read attempt. The request should complete. |
| readBuffer.position(1); |
| readBuffer.limit(5); |
| callback.startNextRead(urlRequest, readBuffer); |
| callback.waitForNextStep(); |
| |
| assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); |
| assertThat(callback.mResponseAsString).isEqualTo("GET"); |
| checkResponseInfo( |
| callback.getResponseInfoWithChecks(), |
| NativeTestServer.getEchoMethodURL(), |
| 200, |
| "OK"); |
| |
| // Check that buffer contents were not modified. |
| assertThat(bufferContentsToString(readBuffer, 0, 5)).isEqualTo("FORTE"); |
| |
| // Position should not have been modified, since nothing was read. |
| assertThat(readBuffer.position()).isEqualTo(1); |
| // Limit should be unchanged as always. |
| assertThat(readBuffer.limit()).isEqualTo(5); |
| |
| assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_SUCCEEDED); |
| |
| // Make sure there are no other pending messages, which would trigger |
| // asserts in TestUrlRequestCallback. |
| testSimpleGet(); |
| } |
| |
| @Test |
| @SmallTest |
| public void testBadBuffers() throws Exception { |
| TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| callback.setAutoAdvance(false); |
| UrlRequest.Builder builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder( |
| NativeTestServer.getEchoMethodURL(), |
| callback, |
| callback.getExecutor()); |
| UrlRequest urlRequest = builder.build(); |
| urlRequest.start(); |
| callback.waitForNextStep(); |
| |
| // Try to read using a full buffer. |
| ByteBuffer readBuffer = ByteBuffer.allocateDirect(4); |
| readBuffer.put("full".getBytes()); |
| IllegalArgumentException e = |
| assertThrows(IllegalArgumentException.class, () -> urlRequest.read(readBuffer)); |
| assertThat(e).hasMessageThat().isEqualTo("ByteBuffer is already full."); |
| |
| // Try to read using a non-direct buffer. |
| ByteBuffer readBuffer1 = ByteBuffer.allocate(5); |
| e = assertThrows(IllegalArgumentException.class, () -> urlRequest.read(readBuffer1)); |
| assertThat(e).hasMessageThat().isEqualTo("byteBuffer must be a direct ByteBuffer."); |
| |
| // Finish the request with a direct ByteBuffer. |
| callback.setAutoAdvance(true); |
| ByteBuffer readBuffer2 = ByteBuffer.allocateDirect(5); |
| urlRequest.read(readBuffer2); |
| callback.blockForDone(); |
| assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); |
| assertThat(callback.mResponseAsString).isEqualTo("GET"); |
| } |
| |
| @Test |
| @SmallTest |
| public void testNoIoInCancel() throws Exception { |
| final TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| callback.setAutoAdvance(false); |
| final UrlRequest urlRequest = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder( |
| NativeTestServer.getEchoHeaderURL("blah-header"), |
| callback, |
| callback.getExecutor()) |
| .addHeader("blah-header", "blahblahblah") |
| .build(); |
| urlRequest.start(); |
| callback.waitForNextStep(); |
| callback.startNextRead(urlRequest, ByteBuffer.allocateDirect(4)); |
| callback.waitForNextStep(); |
| StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy(); |
| StrictMode.setThreadPolicy( |
| new StrictMode.ThreadPolicy.Builder() |
| .detectAll() |
| .penaltyDeath() |
| .penaltyLog() |
| .build()); |
| try { |
| urlRequest.cancel(); |
| } finally { |
| StrictMode.setThreadPolicy(oldPolicy); |
| } |
| callback.blockForDone(); |
| assertThat(callback.mOnCanceledCalled).isEqualTo(true); |
| } |
| |
| @Test |
| @SmallTest |
| public void testUnexpectedReads() throws Exception { |
| final TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| callback.setAutoAdvance(false); |
| final UrlRequest urlRequest = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder( |
| NativeTestServer.getRedirectURL(), callback, callback.getExecutor()) |
| .build(); |
| |
| // Try to read before starting request. |
| assertThrows(IllegalStateException.class, () -> callback.startNextRead(urlRequest)); |
| |
| // Verify reading right after start throws an assertion. Both must be |
| // invoked on the Executor thread, to prevent receiving data until after |
| // startNextRead has been invoked. |
| Runnable startAndRead = |
| new Runnable() { |
| @Override |
| public void run() { |
| urlRequest.start(); |
| assertThrows( |
| IllegalStateException.class, |
| () -> callback.startNextRead(urlRequest)); |
| } |
| }; |
| callback.getExecutor().submit(startAndRead).get(); |
| callback.waitForNextStep(); |
| |
| assertThat(ResponseStep.ON_RECEIVED_REDIRECT).isEqualTo(callback.mResponseStep); |
| // Try to read after the redirect. |
| assertThrows(IllegalStateException.class, () -> callback.startNextRead(urlRequest)); |
| urlRequest.followRedirect(); |
| callback.waitForNextStep(); |
| |
| assertThat(ResponseStep.ON_RESPONSE_STARTED).isEqualTo(callback.mResponseStep); |
| assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); |
| |
| while (!callback.isDone()) { |
| Runnable readTwice = |
| new Runnable() { |
| @Override |
| public void run() { |
| callback.startNextRead(urlRequest); |
| // Try to read again before the last read completes. |
| assertThrows( |
| IllegalStateException.class, |
| () -> callback.startNextRead(urlRequest)); |
| } |
| }; |
| callback.getExecutor().submit(readTwice).get(); |
| callback.waitForNextStep(); |
| } |
| |
| assertThat(ResponseStep.ON_SUCCEEDED).isEqualTo(callback.mResponseStep); |
| assertThat(callback.mResponseAsString).isEqualTo(NativeTestServer.SUCCESS_BODY); |
| |
| // Try to read after request is complete. |
| assertThrows(IllegalStateException.class, () -> callback.startNextRead(urlRequest)); |
| } |
| |
| @Test |
| @SmallTest |
| public void testUnexpectedFollowRedirects() throws Exception { |
| final TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| callback.setAutoAdvance(false); |
| final UrlRequest urlRequest = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder( |
| NativeTestServer.getRedirectURL(), callback, callback.getExecutor()) |
| .build(); |
| |
| // Try to follow a redirect before starting the request. |
| assertThrows(IllegalStateException.class, urlRequest::followRedirect); |
| |
| // Try to follow a redirect just after starting the request. Has to be |
| // done on the executor thread to avoid a race. |
| Runnable startAndRead = |
| new Runnable() { |
| @Override |
| public void run() { |
| urlRequest.start(); |
| assertThrows(IllegalStateException.class, urlRequest::followRedirect); |
| } |
| }; |
| callback.getExecutor().execute(startAndRead); |
| callback.waitForNextStep(); |
| |
| assertThat(ResponseStep.ON_RECEIVED_REDIRECT).isEqualTo(callback.mResponseStep); |
| // Try to follow the redirect twice. Second attempt should fail. |
| Runnable followRedirectTwice = |
| new Runnable() { |
| @Override |
| public void run() { |
| urlRequest.followRedirect(); |
| assertThrows(IllegalStateException.class, urlRequest::followRedirect); |
| } |
| }; |
| callback.getExecutor().execute(followRedirectTwice); |
| callback.waitForNextStep(); |
| |
| assertThat(ResponseStep.ON_RESPONSE_STARTED).isEqualTo(callback.mResponseStep); |
| assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); |
| |
| while (!callback.isDone()) { |
| assertThrows(IllegalStateException.class, urlRequest::followRedirect); |
| callback.startNextRead(urlRequest); |
| callback.waitForNextStep(); |
| } |
| |
| assertThat(ResponseStep.ON_SUCCEEDED).isEqualTo(callback.mResponseStep); |
| assertThat(callback.mResponseAsString).isEqualTo(NativeTestServer.SUCCESS_BODY); |
| |
| // Try to follow redirect after request is complete. |
| assertThrows(IllegalStateException.class, urlRequest::followRedirect); |
| } |
| |
| @Test |
| @SmallTest |
| public void testUploadSetDataProvider() throws Exception { |
| TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| UrlRequest.Builder builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder( |
| NativeTestServer.getEchoBodyURL(), |
| callback, |
| callback.getExecutor()); |
| |
| NullPointerException e = |
| assertThrows( |
| NullPointerException.class, |
| () -> builder.setUploadDataProvider(null, callback.getExecutor())); |
| assertThat(e).hasMessageThat().isEqualTo("Invalid UploadDataProvider."); |
| |
| TestUploadDataProvider dataProvider = |
| new TestUploadDataProvider( |
| TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor()); |
| builder.setUploadDataProvider(dataProvider, callback.getExecutor()); |
| assertThrows(IllegalArgumentException.class, () -> builder.build().start()); |
| } |
| |
| @Test |
| @SmallTest |
| public void testUploadEmptyBodySync() throws Exception { |
| TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| UrlRequest.Builder builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder( |
| NativeTestServer.getEchoBodyURL(), |
| callback, |
| callback.getExecutor()); |
| |
| TestUploadDataProvider dataProvider = |
| new TestUploadDataProvider( |
| TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor()); |
| builder.setUploadDataProvider(dataProvider, callback.getExecutor()); |
| builder.addHeader("Content-Type", "useless/string"); |
| builder.build().start(); |
| callback.blockForDone(); |
| |
| assertThat(dataProvider.getUploadedLength()).isEqualTo(0); |
| assertThat(dataProvider.getNumReadCalls()).isEqualTo(0); |
| assertThat(dataProvider.getNumRewindCalls()).isEqualTo(0); |
| |
| assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); |
| assertThat(callback.mResponseAsString).isEmpty(); |
| dataProvider.assertClosed(); |
| } |
| |
| @Test |
| @SmallTest |
| public void testUploadSync() throws Exception { |
| TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| UrlRequest.Builder builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder( |
| NativeTestServer.getEchoBodyURL(), |
| callback, |
| callback.getExecutor()); |
| |
| TestUploadDataProvider dataProvider = |
| new TestUploadDataProvider( |
| TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor()); |
| dataProvider.addRead("test".getBytes()); |
| builder.setUploadDataProvider(dataProvider, callback.getExecutor()); |
| builder.addHeader("Content-Type", "useless/string"); |
| builder.build().start(); |
| callback.blockForDone(); |
| dataProvider.assertClosed(); |
| |
| assertThat(dataProvider.getUploadedLength()).isEqualTo(4); |
| assertThat(dataProvider.getNumReadCalls()).isEqualTo(1); |
| assertThat(dataProvider.getNumRewindCalls()).isEqualTo(0); |
| |
| assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); |
| assertThat(callback.mResponseAsString).isEqualTo("test"); |
| } |
| |
| @Test |
| @SmallTest |
| public void testUploadMultiplePiecesSync() throws Exception { |
| TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| UrlRequest.Builder builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder( |
| NativeTestServer.getEchoBodyURL(), |
| callback, |
| callback.getExecutor()); |
| |
| TestUploadDataProvider dataProvider = |
| new TestUploadDataProvider( |
| TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor()); |
| dataProvider.addRead("Y".getBytes()); |
| dataProvider.addRead("et ".getBytes()); |
| dataProvider.addRead("another ".getBytes()); |
| dataProvider.addRead("test".getBytes()); |
| |
| builder.setUploadDataProvider(dataProvider, callback.getExecutor()); |
| builder.addHeader("Content-Type", "useless/string"); |
| builder.build().start(); |
| callback.blockForDone(); |
| dataProvider.assertClosed(); |
| |
| assertThat(dataProvider.getUploadedLength()).isEqualTo(16); |
| assertThat(dataProvider.getNumReadCalls()).isEqualTo(4); |
| assertThat(dataProvider.getNumRewindCalls()).isEqualTo(0); |
| |
| assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); |
| assertThat(callback.mResponseAsString).isEqualTo("Yet another test"); |
| } |
| |
| @Test |
| @SmallTest |
| public void testUploadMultiplePiecesAsync() throws Exception { |
| TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| UrlRequest.Builder builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder( |
| NativeTestServer.getEchoBodyURL(), |
| callback, |
| callback.getExecutor()); |
| |
| TestUploadDataProvider dataProvider = |
| new TestUploadDataProvider( |
| TestUploadDataProvider.SuccessCallbackMode.ASYNC, callback.getExecutor()); |
| dataProvider.addRead("Y".getBytes()); |
| dataProvider.addRead("et ".getBytes()); |
| dataProvider.addRead("another ".getBytes()); |
| dataProvider.addRead("test".getBytes()); |
| |
| builder.setUploadDataProvider(dataProvider, callback.getExecutor()); |
| builder.addHeader("Content-Type", "useless/string"); |
| builder.build().start(); |
| callback.blockForDone(); |
| dataProvider.assertClosed(); |
| |
| assertThat(dataProvider.getUploadedLength()).isEqualTo(16); |
| assertThat(dataProvider.getNumReadCalls()).isEqualTo(4); |
| assertThat(dataProvider.getNumRewindCalls()).isEqualTo(0); |
| |
| assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); |
| assertThat(callback.mResponseAsString).isEqualTo("Yet another test"); |
| } |
| |
| @Test |
| @SmallTest |
| public void testUploadChangesDefaultMethod() throws Exception { |
| TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| UrlRequest.Builder builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder( |
| NativeTestServer.getEchoMethodURL(), |
| callback, |
| callback.getExecutor()); |
| |
| TestUploadDataProvider dataProvider = |
| new TestUploadDataProvider( |
| TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor()); |
| dataProvider.addRead("test".getBytes()); |
| builder.setUploadDataProvider(dataProvider, callback.getExecutor()); |
| builder.addHeader("Content-Type", "useless/string"); |
| builder.build().start(); |
| callback.blockForDone(); |
| dataProvider.assertClosed(); |
| |
| assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); |
| assertThat(callback.mResponseAsString).isEqualTo("POST"); |
| } |
| |
| @Test |
| @SmallTest |
| public void testUploadWithSetMethod() throws Exception { |
| TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| UrlRequest.Builder builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder( |
| NativeTestServer.getEchoMethodURL(), |
| callback, |
| callback.getExecutor()); |
| |
| final String method = "PUT"; |
| builder.setHttpMethod(method); |
| |
| TestUploadDataProvider dataProvider = |
| new TestUploadDataProvider( |
| TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor()); |
| dataProvider.addRead("test".getBytes()); |
| builder.setUploadDataProvider(dataProvider, callback.getExecutor()); |
| builder.addHeader("Content-Type", "useless/string"); |
| builder.build().start(); |
| callback.blockForDone(); |
| dataProvider.assertClosed(); |
| |
| assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); |
| assertThat(callback.mResponseAsString).isEqualTo("PUT"); |
| } |
| |
| @Test |
| @SmallTest |
| public void testUploadRedirectSync() throws Exception { |
| TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| UrlRequest.Builder builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder( |
| NativeTestServer.getRedirectToEchoBody(), |
| callback, |
| callback.getExecutor()); |
| |
| TestUploadDataProvider dataProvider = |
| new TestUploadDataProvider( |
| TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor()); |
| dataProvider.addRead("test".getBytes()); |
| builder.setUploadDataProvider(dataProvider, callback.getExecutor()); |
| builder.addHeader("Content-Type", "useless/string"); |
| builder.build().start(); |
| callback.blockForDone(); |
| dataProvider.assertClosed(); |
| |
| // 1 read call before the rewind, 1 after. |
| assertThat(dataProvider.getNumReadCalls()).isEqualTo(2); |
| assertThat(dataProvider.getNumRewindCalls()).isEqualTo(1); |
| |
| assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); |
| assertThat(callback.mResponseAsString).isEqualTo("test"); |
| } |
| |
| @Test |
| @SmallTest |
| public void testUploadRedirectAsync() throws Exception { |
| TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| UrlRequest.Builder builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder( |
| NativeTestServer.getRedirectToEchoBody(), |
| callback, |
| callback.getExecutor()); |
| |
| TestUploadDataProvider dataProvider = |
| new TestUploadDataProvider( |
| TestUploadDataProvider.SuccessCallbackMode.ASYNC, callback.getExecutor()); |
| dataProvider.addRead("test".getBytes()); |
| builder.setUploadDataProvider(dataProvider, callback.getExecutor()); |
| builder.addHeader("Content-Type", "useless/string"); |
| builder.build().start(); |
| dataProvider.assertClosed(); |
| callback.blockForDone(); |
| |
| // 1 read call before the rewind, 1 after. |
| assertThat(dataProvider.getNumReadCalls()).isEqualTo(2); |
| assertThat(dataProvider.getNumRewindCalls()).isEqualTo(1); |
| |
| assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); |
| assertThat(callback.mResponseAsString).isEqualTo("test"); |
| } |
| |
| @Test |
| @SmallTest |
| public void testUploadWithBadLength() throws Exception { |
| TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| UrlRequest.Builder builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder( |
| NativeTestServer.getEchoBodyURL(), |
| callback, |
| callback.getExecutor()); |
| |
| TestUploadDataProvider dataProvider = |
| new TestUploadDataProvider( |
| TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor()) { |
| @Override |
| public long getLength() throws IOException { |
| return 1; |
| } |
| |
| @Override |
| public void read(UploadDataSink uploadDataSink, ByteBuffer byteBuffer) |
| throws IOException { |
| byteBuffer.put("12".getBytes()); |
| uploadDataSink.onReadSucceeded(false); |
| } |
| }; |
| builder.setUploadDataProvider(dataProvider, callback.getExecutor()); |
| builder.addHeader("Content-Type", "useless/string"); |
| builder.build().start(); |
| callback.blockForDone(); |
| dataProvider.assertClosed(); |
| |
| assertThat(callback.mError) |
| .hasMessageThat() |
| .contains("Exception received from UploadDataProvider"); |
| assertThat(callback.mError) |
| .hasCauseThat() |
| .hasMessageThat() |
| .contains("Read upload data length 2 exceeds expected length 1"); |
| assertThat(callback.getResponseInfo()).isNull(); |
| } |
| |
| @Test |
| @SmallTest |
| public void testUploadWithBadLengthBufferAligned() throws Exception { |
| TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| UrlRequest.Builder builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder( |
| NativeTestServer.getEchoBodyURL(), |
| callback, |
| callback.getExecutor()); |
| |
| TestUploadDataProvider dataProvider = |
| new TestUploadDataProvider( |
| TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor()) { |
| @Override |
| public long getLength() throws IOException { |
| return 8191; |
| } |
| |
| @Override |
| public void read(UploadDataSink uploadDataSink, ByteBuffer byteBuffer) |
| throws IOException { |
| byteBuffer.put("0123456789abcdef".getBytes()); |
| uploadDataSink.onReadSucceeded(false); |
| } |
| }; |
| builder.setUploadDataProvider(dataProvider, callback.getExecutor()); |
| builder.addHeader("Content-Type", "useless/string"); |
| builder.build().start(); |
| callback.blockForDone(); |
| dataProvider.assertClosed(); |
| assertThat(callback.mError) |
| .hasMessageThat() |
| .contains("Exception received from UploadDataProvider"); |
| assertThat(callback.mError) |
| .hasCauseThat() |
| .hasMessageThat() |
| .contains("Read upload data length 8192 exceeds expected length 8191"); |
| assertThat(callback.getResponseInfo()).isNull(); |
| } |
| |
| @Test |
| @SmallTest |
| public void testUploadReadFailSync() throws Exception { |
| TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| UrlRequest.Builder builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder( |
| NativeTestServer.getEchoBodyURL(), |
| callback, |
| callback.getExecutor()); |
| |
| TestUploadDataProvider dataProvider = |
| new TestUploadDataProvider( |
| TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor()); |
| dataProvider.setReadFailure(0, TestUploadDataProvider.FailMode.CALLBACK_SYNC); |
| // This will never be read, but if the length is 0, read may never be |
| // called. |
| dataProvider.addRead("test".getBytes()); |
| builder.setUploadDataProvider(dataProvider, callback.getExecutor()); |
| builder.addHeader("Content-Type", "useless/string"); |
| builder.build().start(); |
| callback.blockForDone(); |
| dataProvider.assertClosed(); |
| |
| assertThat(dataProvider.getNumReadCalls()).isEqualTo(1); |
| assertThat(dataProvider.getNumRewindCalls()).isEqualTo(0); |
| |
| assertThat(callback.mError) |
| .hasMessageThat() |
| .contains("Exception received from UploadDataProvider"); |
| assertThat(callback.mError).hasCauseThat().hasMessageThat().contains("Sync read failure"); |
| assertThat(callback.getResponseInfo()).isNull(); |
| } |
| |
| @Test |
| @SmallTest |
| public void testUploadLengthFailSync() throws Exception { |
| TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| UrlRequest.Builder builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder( |
| NativeTestServer.getEchoBodyURL(), |
| callback, |
| callback.getExecutor()); |
| |
| TestUploadDataProvider dataProvider = |
| new TestUploadDataProvider( |
| TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor()); |
| dataProvider.setLengthFailure(); |
| // This will never be read, but if the length is 0, read may never be |
| // called. |
| dataProvider.addRead("test".getBytes()); |
| builder.setUploadDataProvider(dataProvider, callback.getExecutor()); |
| builder.addHeader("Content-Type", "useless/string"); |
| builder.build().start(); |
| callback.blockForDone(); |
| dataProvider.assertClosed(); |
| |
| assertThat(dataProvider.getNumReadCalls()).isEqualTo(0); |
| assertThat(dataProvider.getNumRewindCalls()).isEqualTo(0); |
| |
| assertThat(callback.mError) |
| .hasMessageThat() |
| .contains("Exception received from UploadDataProvider"); |
| assertThat(callback.mError).hasCauseThat().hasMessageThat().contains("Sync length failure"); |
| assertThat(callback.getResponseInfo()).isNull(); |
| } |
| |
| @Test |
| @SmallTest |
| public void testUploadReadFailAsync() throws Exception { |
| TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| UrlRequest.Builder builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder( |
| NativeTestServer.getEchoBodyURL(), |
| callback, |
| callback.getExecutor()); |
| |
| TestUploadDataProvider dataProvider = |
| new TestUploadDataProvider( |
| TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor()); |
| dataProvider.setReadFailure(0, TestUploadDataProvider.FailMode.CALLBACK_ASYNC); |
| // This will never be read, but if the length is 0, read may never be |
| // called. |
| dataProvider.addRead("test".getBytes()); |
| builder.setUploadDataProvider(dataProvider, callback.getExecutor()); |
| builder.addHeader("Content-Type", "useless/string"); |
| builder.build().start(); |
| callback.blockForDone(); |
| dataProvider.assertClosed(); |
| |
| assertThat(dataProvider.getNumReadCalls()).isEqualTo(1); |
| assertThat(dataProvider.getNumRewindCalls()).isEqualTo(0); |
| |
| assertThat(callback.mError) |
| .hasMessageThat() |
| .contains("Exception received from UploadDataProvider"); |
| assertThat(callback.mError).hasCauseThat().hasMessageThat().contains("Async read failure"); |
| assertThat(callback.getResponseInfo()).isNull(); |
| } |
| |
| /** This test uses a direct executor for upload, and non direct for callbacks */ |
| @Test |
| @SmallTest |
| public void testDirectExecutorUploadProhibitedByDefault() throws Exception { |
| TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| Executor myExecutor = |
| new Executor() { |
| @Override |
| public void execute(Runnable command) { |
| command.run(); |
| } |
| }; |
| UrlRequest.Builder builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder( |
| NativeTestServer.getEchoBodyURL(), |
| callback, |
| callback.getExecutor()); |
| |
| TestUploadDataProvider dataProvider = |
| new TestUploadDataProvider( |
| TestUploadDataProvider.SuccessCallbackMode.SYNC, myExecutor); |
| // This will never be read, but if the length is 0, read may never be |
| // called. |
| dataProvider.addRead("test".getBytes()); |
| builder.setUploadDataProvider(dataProvider, myExecutor); |
| builder.addHeader("Content-Type", "useless/string"); |
| builder.build().start(); |
| callback.blockForDone(); |
| |
| assertThat(dataProvider.getNumReadCalls()).isEqualTo(0); |
| assertThat(dataProvider.getNumRewindCalls()).isEqualTo(0); |
| |
| assertThat(callback.mError) |
| .hasMessageThat() |
| .contains("Exception received from UploadDataProvider"); |
| assertThat(callback.mError) |
| .hasCauseThat() |
| .hasMessageThat() |
| .contains("Inline execution is prohibited for this request"); |
| assertThat(callback.getResponseInfo()).isNull(); |
| } |
| |
| /** This test uses a direct executor for callbacks, and non direct for upload */ |
| @Test |
| @SmallTest |
| @IgnoreFor( |
| implementations = {CronetImplementation.AOSP_PLATFORM}, |
| reason = "b/311163531: Re-enable once HttpEngine propagates UploadDataProvider#close") |
| public void testDirectExecutorProhibitedByDefault() throws Exception { |
| TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| Executor myExecutor = |
| new Executor() { |
| @Override |
| public void execute(Runnable command) { |
| command.run(); |
| } |
| }; |
| UrlRequest.Builder builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder( |
| NativeTestServer.getEchoBodyURL(), callback, myExecutor); |
| |
| TestUploadDataProvider dataProvider = |
| new TestUploadDataProvider( |
| TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor()); |
| // This will never be read, but if the length is 0, read may never be |
| // called. |
| dataProvider.addRead("test".getBytes()); |
| builder.setUploadDataProvider(dataProvider, callback.getExecutor()); |
| builder.addHeader("Content-Type", "useless/string"); |
| builder.build().start(); |
| callback.blockForDone(); |
| |
| assertThat(dataProvider.getNumReadCalls()).isEqualTo(1); |
| assertThat(dataProvider.getNumRewindCalls()).isEqualTo(0); |
| |
| assertThat(callback.mError).hasMessageThat().contains("Exception posting task to executor"); |
| assertThat(callback.mError) |
| .hasCauseThat() |
| .hasMessageThat() |
| .contains("Inline execution is prohibited for this request"); |
| assertThat(callback.getResponseInfo()).isNull(); |
| dataProvider.assertClosed(); |
| } |
| |
| @Test |
| @SmallTest |
| public void testDirectExecutorAllowed() throws Exception { |
| TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| callback.setAllowDirectExecutor(true); |
| Executor myExecutor = |
| new Executor() { |
| @Override |
| public void execute(Runnable command) { |
| command.run(); |
| } |
| }; |
| UrlRequest.Builder builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder( |
| NativeTestServer.getEchoBodyURL(), callback, myExecutor); |
| UploadDataProvider dataProvider = UploadDataProviders.create("test".getBytes()); |
| builder.setUploadDataProvider(dataProvider, myExecutor); |
| builder.addHeader("Content-Type", "useless/string"); |
| builder.allowDirectExecutor(); |
| builder.build().start(); |
| callback.blockForDone(); |
| |
| if (callback.mOnErrorCalled) { |
| throw callback.mError; |
| } |
| |
| assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); |
| assertThat(callback.mResponseAsString).isEqualTo("test"); |
| } |
| |
| @Test |
| @SmallTest |
| public void testUploadReadFailThrown() throws Exception { |
| TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| UrlRequest.Builder builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder( |
| NativeTestServer.getEchoBodyURL(), |
| callback, |
| callback.getExecutor()); |
| |
| TestUploadDataProvider dataProvider = |
| new TestUploadDataProvider( |
| TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor()); |
| dataProvider.setReadFailure(0, TestUploadDataProvider.FailMode.THROWN); |
| // This will never be read, but if the length is 0, read may never be |
| // called. |
| dataProvider.addRead("test".getBytes()); |
| builder.setUploadDataProvider(dataProvider, callback.getExecutor()); |
| builder.addHeader("Content-Type", "useless/string"); |
| builder.build().start(); |
| callback.blockForDone(); |
| dataProvider.assertClosed(); |
| |
| assertThat(dataProvider.getNumReadCalls()).isEqualTo(1); |
| assertThat(dataProvider.getNumRewindCalls()).isEqualTo(0); |
| |
| assertThat(callback.mError) |
| .hasMessageThat() |
| .contains("Exception received from UploadDataProvider"); |
| assertThat(callback.mError).hasCauseThat().hasMessageThat().contains("Thrown read failure"); |
| assertThat(callback.getResponseInfo()).isNull(); |
| } |
| |
| @Test |
| @SmallTest |
| public void testUploadRewindFailSync() throws Exception { |
| TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| UrlRequest.Builder builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder( |
| NativeTestServer.getRedirectToEchoBody(), |
| callback, |
| callback.getExecutor()); |
| |
| TestUploadDataProvider dataProvider = |
| new TestUploadDataProvider( |
| TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor()); |
| dataProvider.setRewindFailure(TestUploadDataProvider.FailMode.CALLBACK_SYNC); |
| dataProvider.addRead("test".getBytes()); |
| builder.setUploadDataProvider(dataProvider, callback.getExecutor()); |
| builder.addHeader("Content-Type", "useless/string"); |
| builder.build().start(); |
| callback.blockForDone(); |
| dataProvider.assertClosed(); |
| |
| assertThat(dataProvider.getNumReadCalls()).isEqualTo(1); |
| assertThat(dataProvider.getNumRewindCalls()).isEqualTo(1); |
| |
| assertThat(callback.mError) |
| .hasMessageThat() |
| .contains("Exception received from UploadDataProvider"); |
| assertThat(callback.mError).hasCauseThat().hasMessageThat().contains("Sync rewind failure"); |
| assertThat(callback.getResponseInfo()).isNull(); |
| } |
| |
| @Test |
| @SmallTest |
| public void testUploadRewindFailAsync() throws Exception { |
| TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| UrlRequest.Builder builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder( |
| NativeTestServer.getRedirectToEchoBody(), |
| callback, |
| callback.getExecutor()); |
| |
| TestUploadDataProvider dataProvider = |
| new TestUploadDataProvider( |
| TestUploadDataProvider.SuccessCallbackMode.ASYNC, callback.getExecutor()); |
| dataProvider.setRewindFailure(TestUploadDataProvider.FailMode.CALLBACK_ASYNC); |
| dataProvider.addRead("test".getBytes()); |
| builder.setUploadDataProvider(dataProvider, callback.getExecutor()); |
| builder.addHeader("Content-Type", "useless/string"); |
| builder.build().start(); |
| callback.blockForDone(); |
| dataProvider.assertClosed(); |
| |
| assertThat(dataProvider.getNumReadCalls()).isEqualTo(1); |
| assertThat(dataProvider.getNumRewindCalls()).isEqualTo(1); |
| |
| assertThat(callback.mError) |
| .hasMessageThat() |
| .contains("Exception received from UploadDataProvider"); |
| assertThat(callback.mError) |
| .hasCauseThat() |
| .hasMessageThat() |
| .contains("Async rewind failure"); |
| assertThat(callback.getResponseInfo()).isNull(); |
| } |
| |
| @Test |
| @SmallTest |
| public void testUploadRewindFailThrown() throws Exception { |
| TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| UrlRequest.Builder builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder( |
| NativeTestServer.getRedirectToEchoBody(), |
| callback, |
| callback.getExecutor()); |
| |
| TestUploadDataProvider dataProvider = |
| new TestUploadDataProvider( |
| TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor()); |
| dataProvider.setRewindFailure(TestUploadDataProvider.FailMode.THROWN); |
| dataProvider.addRead("test".getBytes()); |
| builder.setUploadDataProvider(dataProvider, callback.getExecutor()); |
| builder.addHeader("Content-Type", "useless/string"); |
| builder.build().start(); |
| callback.blockForDone(); |
| dataProvider.assertClosed(); |
| |
| assertThat(dataProvider.getNumReadCalls()).isEqualTo(1); |
| assertThat(dataProvider.getNumRewindCalls()).isEqualTo(1); |
| |
| assertThat(callback.mError) |
| .hasMessageThat() |
| .contains("Exception received from UploadDataProvider"); |
| assertThat(callback.mError) |
| .hasCauseThat() |
| .hasMessageThat() |
| .contains("Thrown rewind failure"); |
| assertThat(callback.getResponseInfo()).isNull(); |
| } |
| |
| @Test |
| @SmallTest |
| public void testUploadChunked() throws Exception { |
| TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| UrlRequest.Builder builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder( |
| NativeTestServer.getEchoBodyURL(), |
| callback, |
| callback.getExecutor()); |
| |
| TestUploadDataProvider dataProvider = |
| new TestUploadDataProvider( |
| TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor()); |
| dataProvider.addRead("test hello".getBytes()); |
| dataProvider.setChunked(true); |
| builder.setUploadDataProvider(dataProvider, callback.getExecutor()); |
| builder.addHeader("Content-Type", "useless/string"); |
| |
| assertThat(dataProvider.getUploadedLength()).isEqualTo(-1); |
| |
| builder.build().start(); |
| callback.blockForDone(); |
| dataProvider.assertClosed(); |
| |
| // 1 read call for one data chunk. |
| assertThat(dataProvider.getNumReadCalls()).isEqualTo(1); |
| assertThat(callback.mResponseAsString).isEqualTo("test hello"); |
| } |
| |
| @Test |
| @SmallTest |
| public void testUploadChunkedLastReadZeroLengthBody() throws Exception { |
| TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| UrlRequest.Builder builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder( |
| NativeTestServer.getEchoBodyURL(), |
| callback, |
| callback.getExecutor()); |
| |
| TestUploadDataProvider dataProvider = |
| new TestUploadDataProvider( |
| TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor()); |
| // Add 3 reads. The last read has a 0-length body. |
| dataProvider.addRead("hello there".getBytes()); |
| dataProvider.addRead("!".getBytes()); |
| dataProvider.addRead("".getBytes()); |
| dataProvider.setChunked(true); |
| builder.setUploadDataProvider(dataProvider, callback.getExecutor()); |
| builder.addHeader("Content-Type", "useless/string"); |
| |
| assertThat(dataProvider.getUploadedLength()).isEqualTo(-1); |
| |
| builder.build().start(); |
| callback.blockForDone(); |
| dataProvider.assertClosed(); |
| |
| // 2 read call for the first two data chunks, and 1 for final chunk. |
| assertThat(dataProvider.getNumReadCalls()).isEqualTo(3); |
| assertThat(callback.mResponseAsString).isEqualTo("hello there!"); |
| } |
| |
| // Test where an upload fails without ever initializing the |
| // UploadDataStream, because it can't connect to the server. |
| @Test |
| @SmallTest |
| public void testUploadFailsWithoutInitializingStream() throws Exception { |
| TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| // The port for PTP will always refuse a TCP connection |
| UrlRequest.Builder builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder( |
| "http://127.0.0.1:319", callback, callback.getExecutor()); |
| |
| TestUploadDataProvider dataProvider = |
| new TestUploadDataProvider( |
| TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor()); |
| dataProvider.addRead("test".getBytes()); |
| builder.setUploadDataProvider(dataProvider, callback.getExecutor()); |
| builder.addHeader("Content-Type", "useless/string"); |
| builder.build().start(); |
| callback.blockForDone(); |
| dataProvider.assertClosed(); |
| |
| assertThat(callback.getResponseInfo()).isNull(); |
| if (mTestRule.testingJavaImpl()) { |
| assertThat(callback.mError).hasCauseThat().isInstanceOf(ConnectException.class); |
| } else { |
| assertThat(callback.mError) |
| .hasMessageThat() |
| .contains("Exception in CronetUrlRequest: net::ERR_CONNECTION_REFUSED"); |
| } |
| } |
| |
| private void throwOrCancel( |
| FailureType failureType, |
| ResponseStep failureStep, |
| boolean expectResponseInfo, |
| boolean expectError) { |
| if (Log.isLoggable("TESTING", Log.VERBOSE)) { |
| Log.v("TESTING", "Testing " + failureType + " during " + failureStep); |
| } |
| TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| callback.setFailure(failureType, failureStep); |
| UrlRequest.Builder builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder( |
| NativeTestServer.getRedirectURL(), |
| callback, |
| callback.getExecutor()); |
| UrlRequest urlRequest = builder.build(); |
| urlRequest.start(); |
| callback.blockForDone(); |
| // Wait for all posted tasks to be executed to ensure there is no unhandled exception. |
| callback.shutdownExecutorAndWait(); |
| assertThat(callback.mRedirectCount).isEqualTo(1); |
| if (failureType == FailureType.CANCEL_SYNC || failureType == FailureType.CANCEL_ASYNC) { |
| assertResponseStepCanceled(callback); |
| } else if (failureType == FailureType.THROW_SYNC) { |
| assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_FAILED); |
| } |
| assertThat(urlRequest.isDone()).isTrue(); |
| assertThat(callback.getResponseInfo() != null).isEqualTo(expectResponseInfo); |
| assertThat(callback.mError != null).isEqualTo(expectError); |
| assertThat(callback.mOnErrorCalled).isEqualTo(expectError); |
| // When failureType is FailureType.CANCEL_ASYNC_WITHOUT_PAUSE and failureStep is |
| // ResponseStep.ON_READ_COMPLETED, there might be an onSucceeded() task already posted. If |
| // that's the case, onCanceled() will not be invoked. See crbug.com/657415. |
| if (!(failureType == FailureType.CANCEL_ASYNC_WITHOUT_PAUSE |
| && failureStep == ResponseStep.ON_READ_COMPLETED)) { |
| assertThat(callback.mOnCanceledCalled) |
| .isEqualTo( |
| failureType == FailureType.CANCEL_SYNC |
| || failureType == FailureType.CANCEL_ASYNC |
| || failureType == FailureType.CANCEL_ASYNC_WITHOUT_PAUSE); |
| } |
| } |
| |
| @Test |
| @SmallTest |
| public void testFailures() throws Exception { |
| throwOrCancel(FailureType.CANCEL_SYNC, ResponseStep.ON_RECEIVED_REDIRECT, false, false); |
| throwOrCancel(FailureType.CANCEL_ASYNC, ResponseStep.ON_RECEIVED_REDIRECT, false, false); |
| throwOrCancel( |
| FailureType.CANCEL_ASYNC_WITHOUT_PAUSE, |
| ResponseStep.ON_RECEIVED_REDIRECT, |
| false, |
| false); |
| throwOrCancel(FailureType.THROW_SYNC, ResponseStep.ON_RECEIVED_REDIRECT, false, true); |
| |
| throwOrCancel(FailureType.CANCEL_SYNC, ResponseStep.ON_RESPONSE_STARTED, true, false); |
| throwOrCancel(FailureType.CANCEL_ASYNC, ResponseStep.ON_RESPONSE_STARTED, true, false); |
| throwOrCancel( |
| FailureType.CANCEL_ASYNC_WITHOUT_PAUSE, |
| ResponseStep.ON_RESPONSE_STARTED, |
| true, |
| false); |
| throwOrCancel(FailureType.THROW_SYNC, ResponseStep.ON_RESPONSE_STARTED, true, true); |
| |
| throwOrCancel(FailureType.CANCEL_SYNC, ResponseStep.ON_READ_COMPLETED, true, false); |
| throwOrCancel(FailureType.CANCEL_ASYNC, ResponseStep.ON_READ_COMPLETED, true, false); |
| throwOrCancel( |
| FailureType.CANCEL_ASYNC_WITHOUT_PAUSE, |
| ResponseStep.ON_READ_COMPLETED, |
| true, |
| false); |
| throwOrCancel(FailureType.THROW_SYNC, ResponseStep.ON_READ_COMPLETED, true, true); |
| } |
| |
| @Test |
| @SmallTest |
| public void testThrowOrCancelInOnSucceeded() { |
| FailureType[] testTypes = |
| new FailureType[] { |
| FailureType.THROW_SYNC, FailureType.CANCEL_SYNC, FailureType.CANCEL_ASYNC |
| }; |
| for (FailureType type : testTypes) { |
| TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| callback.setFailure(type, ResponseStep.ON_SUCCEEDED); |
| UrlRequest.Builder builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder( |
| NativeTestServer.getEchoMethodURL(), |
| callback, |
| callback.getExecutor()); |
| UrlRequest urlRequest = builder.build(); |
| urlRequest.start(); |
| callback.blockForDone(); |
| // Wait for all posted tasks to be executed to ensure there is no unhandled exception. |
| callback.shutdownExecutorAndWait(); |
| assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_SUCCEEDED); |
| assertThat(urlRequest.isDone()).isTrue(); |
| assertThat(callback.getResponseInfoWithChecks()).isNotNull(); |
| assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); |
| assertThat(callback.mResponseAsString).isEqualTo("GET"); |
| } |
| } |
| |
| @Test |
| @SmallTest |
| public void testThrowOrCancelInOnFailed() { |
| FailureType[] testTypes = |
| new FailureType[] { |
| FailureType.THROW_SYNC, FailureType.CANCEL_SYNC, FailureType.CANCEL_ASYNC |
| }; |
| for (FailureType type : testTypes) { |
| String url = NativeTestServer.getEchoBodyURL(); |
| // Shut down NativeTestServer so request will fail. |
| NativeTestServer.shutdownNativeTestServer(); |
| TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| callback.setFailure(type, ResponseStep.ON_FAILED); |
| UrlRequest.Builder builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder(url, callback, callback.getExecutor()); |
| UrlRequest urlRequest = builder.build(); |
| urlRequest.start(); |
| callback.blockForDone(); |
| // Wait for all posted tasks to be executed to ensure there is no unhandled exception. |
| callback.shutdownExecutorAndWait(); |
| assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_FAILED); |
| assertThat(callback.mOnErrorCalled).isTrue(); |
| assertThat(callback.mError).isNotNull(); |
| assertThat(urlRequest.isDone()).isTrue(); |
| // Start NativeTestServer again to run the test for a second time. |
| assertThat( |
| NativeTestServer.startNativeTestServer( |
| mTestRule.getTestFramework().getContext())) |
| .isTrue(); |
| } |
| } |
| |
| @Test |
| @SmallTest |
| public void testThrowOrCancelInOnCanceled() { |
| FailureType[] testTypes = |
| new FailureType[] { |
| FailureType.THROW_SYNC, FailureType.CANCEL_SYNC, FailureType.CANCEL_ASYNC |
| }; |
| for (FailureType type : testTypes) { |
| TestUrlRequestCallback callback = |
| new TestUrlRequestCallback() { |
| @Override |
| public void onResponseStarted(UrlRequest request, UrlResponseInfo info) { |
| super.onResponseStarted(request, info); |
| request.cancel(); |
| } |
| }; |
| callback.setFailure(type, ResponseStep.ON_CANCELED); |
| UrlRequest.Builder builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder( |
| NativeTestServer.getEchoBodyURL(), |
| callback, |
| callback.getExecutor()); |
| UrlRequest urlRequest = builder.build(); |
| urlRequest.start(); |
| callback.blockForDone(); |
| // Wait for all posted tasks to be executed to ensure there is no unhandled exception. |
| callback.shutdownExecutorAndWait(); |
| assertResponseStepCanceled(callback); |
| assertThat(urlRequest.isDone()).isTrue(); |
| assertThat(callback.getResponseInfoWithChecks()).isNotNull(); |
| assertThat(callback.mOnCanceledCalled).isTrue(); |
| } |
| } |
| |
| @Test |
| @SmallTest |
| @IgnoreFor( |
| implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, |
| reason = "crbug.com/1494846: tests native-specific internals") |
| public void testExecutorShutdown() { |
| TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| |
| callback.setAutoAdvance(false); |
| UrlRequest.Builder builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder( |
| NativeTestServer.getEchoBodyURL(), |
| callback, |
| callback.getExecutor()); |
| CronetUrlRequest urlRequest = (CronetUrlRequest) builder.build(); |
| urlRequest.start(); |
| callback.waitForNextStep(); |
| assertThat(callback.isDone()).isFalse(); |
| assertThat(urlRequest.isDone()).isFalse(); |
| |
| final ConditionVariable requestDestroyed = new ConditionVariable(false); |
| urlRequest.setOnDestroyedCallbackForTesting( |
| new Runnable() { |
| @Override |
| public void run() { |
| requestDestroyed.open(); |
| } |
| }); |
| |
| // Shutdown the executor, so posting the task will throw an exception. |
| callback.shutdownExecutor(); |
| ByteBuffer readBuffer = ByteBuffer.allocateDirect(5); |
| urlRequest.read(readBuffer); |
| // Callback will never be called again because executor is shutdown, |
| // but request will be destroyed from network thread. |
| requestDestroyed.block(); |
| |
| assertThat(callback.isDone()).isFalse(); |
| assertThat(urlRequest.isDone()).isTrue(); |
| } |
| |
| @Test |
| @SmallTest |
| public void testUploadExecutorShutdown() throws Exception { |
| class HangingUploadDataProvider extends UploadDataProvider { |
| UploadDataSink mUploadDataSink; |
| ByteBuffer mByteBuffer; |
| ConditionVariable mReadCalled = new ConditionVariable(false); |
| |
| @Override |
| public long getLength() { |
| return 69; |
| } |
| |
| @Override |
| public void read(final UploadDataSink uploadDataSink, final ByteBuffer byteBuffer) { |
| mUploadDataSink = uploadDataSink; |
| mByteBuffer = byteBuffer; |
| mReadCalled.open(); |
| } |
| |
| @Override |
| public void rewind(final UploadDataSink uploadDataSink) {} |
| } |
| |
| TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| UrlRequest.Builder builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder( |
| NativeTestServer.getEchoBodyURL(), |
| callback, |
| callback.getExecutor()); |
| |
| ExecutorService uploadExecutor = Executors.newSingleThreadExecutor(); |
| HangingUploadDataProvider dataProvider = new HangingUploadDataProvider(); |
| builder.setUploadDataProvider(dataProvider, uploadExecutor); |
| builder.addHeader("Content-Type", "useless/string"); |
| UrlRequest urlRequest = builder.build(); |
| urlRequest.start(); |
| // Wait for read to be called on executor. |
| dataProvider.mReadCalled.block(); |
| // Shutdown the executor, so posting next task will throw an exception. |
| uploadExecutor.shutdown(); |
| // Continue the upload. |
| dataProvider.mByteBuffer.putInt(42); |
| dataProvider.mUploadDataSink.onReadSucceeded(false); |
| // Callback.onFailed will be called on request executor even though upload |
| // executor is shutdown. |
| callback.blockForDone(); |
| assertThat(callback.isDone()).isTrue(); |
| assertThat(callback.mOnErrorCalled).isTrue(); |
| assertThat(callback.mError) |
| .hasMessageThat() |
| .contains("Exception received from UploadDataProvider"); |
| assertThat(urlRequest.isDone()).isTrue(); |
| } |
| |
| /** A TestUrlRequestCallback that shuts down executor upon receiving onSucceeded callback. */ |
| private static class QuitOnSuccessCallback extends TestUrlRequestCallback { |
| @Override |
| public void onSucceeded(UrlRequest request, UrlResponseInfo info) { |
| // Stop accepting new tasks. |
| shutdownExecutor(); |
| super.onSucceeded(request, info); |
| } |
| } |
| |
| @Test |
| @SmallTest |
| @IgnoreFor( |
| implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, |
| reason = "crbug.com/1494846: tests native-specific internals") |
| // Regression test for crbug.com/564946. |
| public void testDestroyUploadDataStreamAdapterOnSucceededCallback() throws Exception { |
| TestUrlRequestCallback callback = new QuitOnSuccessCallback(); |
| UrlRequest.Builder builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder( |
| NativeTestServer.getEchoBodyURL(), |
| callback, |
| callback.getExecutor()); |
| |
| TestUploadDataProvider dataProvider = |
| new TestUploadDataProvider( |
| TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor()); |
| builder.setUploadDataProvider(dataProvider, callback.getExecutor()); |
| builder.addHeader("Content-Type", "useless/string"); |
| CronetUrlRequest request = (CronetUrlRequest) builder.build(); |
| final ConditionVariable uploadDataStreamAdapterDestroyed = new ConditionVariable(); |
| request.setOnDestroyedUploadCallbackForTesting( |
| new Runnable() { |
| @Override |
| public void run() { |
| uploadDataStreamAdapterDestroyed.open(); |
| } |
| }); |
| |
| request.start(); |
| uploadDataStreamAdapterDestroyed.block(); |
| callback.blockForDone(); |
| |
| assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); |
| assertThat(callback.mResponseAsString).isEmpty(); |
| } |
| |
| /* |
| * Verifies error codes are passed through correctly. |
| */ |
| @Test |
| @SmallTest |
| @IgnoreFor( |
| implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, |
| reason = "crbug.com/1494846: tests native-specific internals") |
| public void testErrorCodes() throws Exception { |
| mMockUrlRequestJobFactory = |
| new MockUrlRequestJobFactory(mTestRule.getTestFramework().getEngine()); |
| checkSpecificErrorCode( |
| -105, NetworkException.ERROR_HOSTNAME_NOT_RESOLVED, "NAME_NOT_RESOLVED", false); |
| checkSpecificErrorCode( |
| -106, NetworkException.ERROR_INTERNET_DISCONNECTED, "INTERNET_DISCONNECTED", false); |
| checkSpecificErrorCode( |
| -21, NetworkException.ERROR_NETWORK_CHANGED, "NETWORK_CHANGED", true); |
| checkSpecificErrorCode( |
| -100, NetworkException.ERROR_CONNECTION_CLOSED, "CONNECTION_CLOSED", true); |
| checkSpecificErrorCode( |
| -102, NetworkException.ERROR_CONNECTION_REFUSED, "CONNECTION_REFUSED", false); |
| checkSpecificErrorCode( |
| -101, NetworkException.ERROR_CONNECTION_RESET, "CONNECTION_RESET", true); |
| checkSpecificErrorCode( |
| -118, NetworkException.ERROR_CONNECTION_TIMED_OUT, "CONNECTION_TIMED_OUT", true); |
| checkSpecificErrorCode(-7, NetworkException.ERROR_TIMED_OUT, "TIMED_OUT", true); |
| checkSpecificErrorCode( |
| -109, NetworkException.ERROR_ADDRESS_UNREACHABLE, "ADDRESS_UNREACHABLE", false); |
| checkSpecificErrorCode(-2, NetworkException.ERROR_OTHER, "FAILED", false); |
| } |
| |
| /* |
| * Verifies no cookies are saved or sent by default. |
| */ |
| @Test |
| @SmallTest |
| public void testCookiesArentSavedOrSent() throws Exception { |
| // Make a request to a url that sets the cookie |
| String url = NativeTestServer.getFileURL("/set_cookie.html"); |
| TestUrlRequestCallback callback = startAndWaitForComplete(url); |
| assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); |
| assertThat(callback.getResponseInfoWithChecks()) |
| .hasHeadersThat() |
| .containsEntry("Set-Cookie", Arrays.asList("A=B")); |
| |
| // Make a request that check that cookie header isn't sent. |
| String headerName = "Cookie"; |
| String url2 = NativeTestServer.getEchoHeaderURL(headerName); |
| TestUrlRequestCallback callback2 = startAndWaitForComplete(url2); |
| assertThat(callback2.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); |
| assertThat(callback2.mResponseAsString).isEqualTo("Header not found. :("); |
| } |
| |
| @Test |
| @SmallTest |
| @IgnoreFor( |
| implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, |
| reason = "crbug.com/1494846: tests native-specific internals") |
| public void testQuicErrorCode() throws Exception { |
| mMockUrlRequestJobFactory = |
| new MockUrlRequestJobFactory(mTestRule.getTestFramework().getEngine()); |
| TestUrlRequestCallback callback = |
| startAndWaitForComplete( |
| MockUrlRequestJobFactory.getMockUrlWithFailure( |
| FailurePhase.START, NetError.ERR_QUIC_PROTOCOL_ERROR)); |
| assertThat(callback.getResponseInfo()).isNull(); |
| assertThat(callback.mError).isInstanceOf(QuicException.class); |
| QuicException quicException = (QuicException) callback.mError; |
| // 1 is QUIC_INTERNAL_ERROR |
| assertThat(quicException.getQuicDetailedErrorCode()).isEqualTo(1); |
| assertThat(quicException.getErrorCode()) |
| .isEqualTo(NetworkException.ERROR_QUIC_PROTOCOL_FAILED); |
| } |
| |
| @Test |
| @SmallTest |
| @IgnoreFor( |
| implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, |
| reason = "crbug.com/1494846: tests native-specific internals") |
| public void testQuicErrorCodeForNetworkChanged() throws Exception { |
| mMockUrlRequestJobFactory = |
| new MockUrlRequestJobFactory(mTestRule.getTestFramework().getEngine()); |
| TestUrlRequestCallback callback = |
| startAndWaitForComplete( |
| MockUrlRequestJobFactory.getMockUrlWithFailure( |
| FailurePhase.START, NetError.ERR_NETWORK_CHANGED)); |
| assertThat(callback.getResponseInfo()).isNull(); |
| assertThat(callback.mError).isInstanceOf(QuicException.class); |
| QuicException quicException = (QuicException) callback.mError; |
| // QUIC_CONNECTION_MIGRATION_NO_NEW_NETWORK(83) is set in |
| // URLRequestFailedJob::PopulateNetErrorDetails for this test. |
| final int quicErrorCode = 83; |
| assertThat(quicException.getQuicDetailedErrorCode()).isEqualTo(quicErrorCode); |
| assertThat(quicException.getErrorCode()).isEqualTo(NetworkException.ERROR_NETWORK_CHANGED); |
| } |
| |
| /** |
| * Tests that legacy onFailed callback is invoked with UrlRequestException if there is no |
| * onFailed callback implementation that takes CronetException. |
| */ |
| @Test |
| @SmallTest |
| @IgnoreFor( |
| implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, |
| reason = "crbug.com/1494846: tests native-specific internals") |
| public void testLegacyOnFailedCallback() throws Exception { |
| mMockUrlRequestJobFactory = |
| new MockUrlRequestJobFactory(mTestRule.getTestFramework().getEngine()); |
| final int netError = -123; |
| final AtomicBoolean failedExpectation = new AtomicBoolean(); |
| final ConditionVariable done = new ConditionVariable(); |
| UrlRequest.Callback callback = |
| new UrlRequest.Callback() { |
| @Override |
| public void onRedirectReceived( |
| UrlRequest request, UrlResponseInfo info, String newLocationUrl) { |
| failedExpectation.set(true); |
| fail(); |
| } |
| |
| @Override |
| public void onResponseStarted(UrlRequest request, UrlResponseInfo info) { |
| failedExpectation.set(true); |
| fail(); |
| } |
| |
| @Override |
| public void onReadCompleted( |
| UrlRequest request, UrlResponseInfo info, ByteBuffer byteBuffer) { |
| failedExpectation.set(true); |
| fail(); |
| } |
| |
| @Override |
| public void onSucceeded(UrlRequest request, UrlResponseInfo info) { |
| failedExpectation.set(true); |
| fail(); |
| } |
| |
| @Override |
| public void onFailed( |
| UrlRequest request, UrlResponseInfo info, CronetException error) { |
| assertThat(error).isInstanceOf(NetworkException.class); |
| assertThat(((NetworkException) error).getCronetInternalErrorCode()) |
| .isEqualTo(netError); |
| failedExpectation.set( |
| ((NetworkException) error).getCronetInternalErrorCode() |
| != netError); |
| done.open(); |
| } |
| |
| @Override |
| public void onCanceled(UrlRequest request, UrlResponseInfo info) { |
| failedExpectation.set(true); |
| fail(); |
| } |
| }; |
| |
| UrlRequest.Builder builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder( |
| MockUrlRequestJobFactory.getMockUrlWithFailure( |
| FailurePhase.START, netError), |
| callback, |
| Executors.newSingleThreadExecutor()); |
| final UrlRequest urlRequest = builder.build(); |
| urlRequest.start(); |
| done.block(); |
| // Check that onFailed is called. |
| assertThat(failedExpectation.get()).isFalse(); |
| } |
| |
| private void checkSpecificErrorCode( |
| int netError, int errorCode, String name, boolean immediatelyRetryable) |
| throws Exception { |
| TestUrlRequestCallback callback = |
| startAndWaitForComplete( |
| MockUrlRequestJobFactory.getMockUrlWithFailure( |
| FailurePhase.START, netError)); |
| assertThat(callback.getResponseInfo()).isNull(); |
| assertThat(callback.mError).isNotNull(); |
| assertThat(((NetworkException) callback.mError).getCronetInternalErrorCode()) |
| .isEqualTo(netError); |
| assertThat(((NetworkException) callback.mError).getErrorCode()).isEqualTo(errorCode); |
| assertThat(((NetworkException) callback.mError).immediatelyRetryable()) |
| .isEqualTo(immediatelyRetryable); |
| assertThat(callback.mError) |
| .hasMessageThat() |
| .contains("Exception in CronetUrlRequest: net::ERR_" + name); |
| assertThat(callback.mRedirectCount).isEqualTo(0); |
| assertThat(callback.mOnErrorCalled).isTrue(); |
| assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_FAILED); |
| } |
| |
| // Returns the contents of byteBuffer, from its position() to its limit(), |
| // as a String. Does not modify byteBuffer's position(). |
| private String bufferContentsToString(ByteBuffer byteBuffer, int start, int end) { |
| // Use a duplicate to avoid modifying byteBuffer. |
| ByteBuffer duplicate = byteBuffer.duplicate(); |
| duplicate.position(start); |
| duplicate.limit(end); |
| byte[] contents = new byte[duplicate.remaining()]; |
| duplicate.get(contents); |
| return new String(contents); |
| } |
| |
| private void assertResponseStepCanceled(TestUrlRequestCallback callback) { |
| if (callback.mResponseStep == ResponseStep.ON_FAILED && callback.mError != null) { |
| throw new Error( |
| "Unexpected response state: " + ResponseStep.ON_FAILED, callback.mError); |
| } |
| assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_CANCELED); |
| } |
| |
| @Test |
| @SmallTest |
| @RequiresMinAndroidApi(Build.VERSION_CODES.N) |
| // Used for Android's NetworkSecurityPolicy added in Nougat |
| public void testCleartextTrafficBlocked() throws Exception { |
| final int cleartextNotPermitted = -29; |
| // This hostname needs to match the one in network_security_config.xml and the one used |
| // by QuicTestServer. |
| // https requests to it are tested in QuicTest, so this checks that we're only blocking |
| // cleartext. |
| final String url = "http://example.com/simple.txt"; |
| TestUrlRequestCallback callback = startAndWaitForComplete(url); |
| assertThat(callback.getResponseInfo()).isNull(); |
| assertThat(callback.mError).isNotNull(); |
| // NetworkException#getCronetInternalErrorCode is exposed only by the native |
| // implementation. |
| if (mTestRule.implementationUnderTest() == CronetImplementation.STATICALLY_LINKED) { |
| assertThat(((NetworkException) callback.mError).getCronetInternalErrorCode()) |
| .isEqualTo(cleartextNotPermitted); |
| } |
| } |
| |
| @Test |
| @SmallTest |
| /** |
| * Open many connections and cancel them right away. This test verifies all internal sockets and |
| * other Closeables are properly closed. See crbug.com/726193. |
| */ |
| public void testGzipCancel() throws Exception { |
| String url = NativeTestServer.getFileURL("/gzipped.html"); |
| for (int i = 0; i < 100; i++) { |
| TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| callback.setAutoAdvance(false); |
| UrlRequest urlRequest = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder(url, callback, callback.getExecutor()) |
| .build(); |
| urlRequest.start(); |
| urlRequest.cancel(); |
| // If the test blocks until each UrlRequest finishes before starting the next UrlRequest |
| // then it never catches the leak. If it starts all UrlRequests and then blocks until |
| // all UrlRequests finish, it only catches the leak ~10% of the time. In its current |
| // form it appears to catch the leak ~70% of the time. |
| // Catching the leak may require a lot of busy threads so that the cancel() happens |
| // before the UrlRequest has made much progress (and set mCurrentUrlConnection and |
| // mResponseChannel). This may be why blocking until each UrlRequest finishes doesn't |
| // catch the leak. |
| // The other quirk of this is that from teardown(), JavaCronetEngine.shutdown() is |
| // called which calls ExecutorService.shutdown() which doesn't wait for the thread to |
| // finish running tasks, and then teardown() calls GC looking for leaks. One possible |
| // modification would be to expose the ExecutorService and then have tests call |
| // awaitTermination() but this would complicate things, and adding a 1s sleep() to |
| // allow the ExecutorService to terminate did not increase the chances of catching the |
| // leak. |
| } |
| } |
| |
| @Test |
| @SmallTest |
| @RequiresMinApi(8) // JavaUrlRequest fixed in API level 8: crrev.com/499303 |
| /** Do a HEAD request and get back a 404. */ |
| public void test404Head() throws Exception { |
| TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| UrlRequest.Builder builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder( |
| NativeTestServer.getFileURL("/notfound.html"), |
| callback, |
| callback.getExecutor()); |
| builder.setHttpMethod("HEAD").build().start(); |
| callback.blockForDone(); |
| } |
| |
| @Test |
| @SmallTest |
| @RequiresMinApi(9) // Tagging support added in API level 9: crrev.com/c/chromium/src/+/930086 |
| @RequiresMinAndroidApi(Build.VERSION_CODES.M) // crbug/1301957 |
| public void testTagging() throws Exception { |
| if (!CronetTestUtil.nativeCanGetTaggedBytes()) { |
| Log.i(TAG, "Skipping test - GetTaggedBytes unsupported."); |
| return; |
| } |
| String url = NativeTestServer.getEchoMethodURL(); |
| |
| // Test untagged requests are given tag 0. |
| int tag = 0; |
| long priorBytes = CronetTestUtil.nativeGetTaggedBytes(tag); |
| TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| ExperimentalUrlRequest.Builder builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder(url, callback, callback.getExecutor()); |
| builder.build().start(); |
| callback.blockForDone(); |
| assertThat(CronetTestUtil.nativeGetTaggedBytes(tag)).isGreaterThan(priorBytes); |
| |
| // Test explicit tagging. |
| tag = 0x12345678; |
| priorBytes = CronetTestUtil.nativeGetTaggedBytes(tag); |
| callback = new TestUrlRequestCallback(); |
| builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder(url, callback, callback.getExecutor()); |
| assertThat(builder).isEqualTo(builder.setTrafficStatsTag(tag)); |
| builder.build().start(); |
| callback.blockForDone(); |
| assertThat(CronetTestUtil.nativeGetTaggedBytes(tag)).isGreaterThan(priorBytes); |
| |
| // Test a different tag value to make sure reused connections are retagged. |
| tag = 0x87654321; |
| priorBytes = CronetTestUtil.nativeGetTaggedBytes(tag); |
| callback = new TestUrlRequestCallback(); |
| builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder(url, callback, callback.getExecutor()); |
| assertThat(builder).isEqualTo(builder.setTrafficStatsTag(tag)); |
| builder.build().start(); |
| callback.blockForDone(); |
| assertThat(CronetTestUtil.nativeGetTaggedBytes(tag)).isGreaterThan(priorBytes); |
| |
| // Test tagging with our UID. |
| tag = 0; |
| priorBytes = CronetTestUtil.nativeGetTaggedBytes(tag); |
| callback = new TestUrlRequestCallback(); |
| builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder(url, callback, callback.getExecutor()); |
| assertThat(builder).isEqualTo(builder.setTrafficStatsUid(Process.myUid())); |
| builder.build().start(); |
| callback.blockForDone(); |
| assertThat(CronetTestUtil.nativeGetTaggedBytes(tag)).isGreaterThan(priorBytes); |
| } |
| |
| @Test |
| @SmallTest |
| /** |
| * Initiate many requests concurrently to make sure neither Cronet implementation crashes. |
| * Regression test for https://crbug.com/844031. |
| */ |
| public void testManyRequests() throws Exception { |
| String url = NativeTestServer.getMultiRedirectURL(); |
| final int numRequests = 2000; |
| TestUrlRequestCallback[] callbacks = new TestUrlRequestCallback[numRequests]; |
| UrlRequest[] requests = new UrlRequest[numRequests]; |
| for (int i = 0; i < numRequests; i++) { |
| // Share the first callback's executor to avoid creating too many single-threaded |
| // executors and hence too many threads. |
| if (i == 0) { |
| callbacks[i] = new TestUrlRequestCallback(); |
| } else { |
| callbacks[i] = new TestUrlRequestCallback(callbacks[0].getExecutor()); |
| } |
| UrlRequest.Builder builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder(url, callbacks[i], callbacks[i].getExecutor()); |
| requests[i] = builder.build(); |
| } |
| for (UrlRequest request : requests) { |
| request.start(); |
| } |
| for (UrlRequest request : requests) { |
| request.cancel(); |
| } |
| for (TestUrlRequestCallback callback : callbacks) { |
| callback.blockForDone(); |
| } |
| } |
| |
| @Test |
| @SmallTest |
| public void testSetIdempotency() throws Exception { |
| TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| ExperimentalUrlRequest.Builder builder = |
| mTestRule |
| .getTestFramework() |
| .getEngine() |
| .newUrlRequestBuilder( |
| NativeTestServer.getEchoMethodURL(), |
| callback, |
| callback.getExecutor()); |
| assertThat(builder) |
| .isEqualTo(builder.setIdempotency(ExperimentalUrlRequest.Builder.IDEMPOTENT)); |
| |
| TestUploadDataProvider dataProvider = |
| new TestUploadDataProvider( |
| TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor()); |
| dataProvider.addRead("test".getBytes()); |
| builder.setUploadDataProvider(dataProvider, callback.getExecutor()); |
| builder.addHeader("Content-Type", "useless/string"); |
| builder.build().start(); |
| callback.blockForDone(); |
| dataProvider.assertClosed(); |
| |
| assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); |
| assertThat(callback.mResponseAsString).isEqualTo("POST"); |
| } |
| |
| @Test |
| @RequiresMinAndroidApi(Build.VERSION_CODES.M) |
| public void testBindToInvalidNetworkFails() { |
| String url = NativeTestServer.getEchoMethodURL(); |
| ExperimentalCronetEngine cronetEngine = mTestRule.getTestFramework().getEngine(); |
| TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| ExperimentalUrlRequest.Builder builder = |
| cronetEngine.newUrlRequestBuilder(url, callback, callback.getExecutor()); |
| |
| if (mTestRule.implementationUnderTest() == CronetImplementation.AOSP_PLATFORM) { |
| // android.net.http.UrlRequestBuilder#bindToNetwork requires an android.net.Network |
| // object. So, in this case, it will be the wrapper layer that will fail to translate |
| // that to a Network, not something in net's code. Hence, the failure will manifest |
| // itself at bind time, not at request execution time. |
| // Note: this will never happen in prod, as translation failure can only happen if we're |
| // given a fake networkHandle. |
| assertThrows( |
| IllegalArgumentException.class, |
| () -> builder.bindToNetwork(-150 /* invalid network handle */)); |
| return; |
| } |
| |
| builder.bindToNetwork(-150 /* invalid network handle */); |
| builder.build().start(); |
| callback.blockForDone(); |
| assertThat(callback.mError).isNotNull(); |
| if (mTestRule.implementationUnderTest() == CronetImplementation.FALLBACK) { |
| assertThat(callback.mError).isInstanceOf(CronetExceptionImpl.class); |
| assertThat(callback.mError).hasCauseThat().isInstanceOf(NetworkExceptionImpl.class); |
| } else { |
| assertThat(callback.mError).isInstanceOf(NetworkExceptionImpl.class); |
| } |
| } |
| |
| @Test |
| @RequiresMinAndroidApi(Build.VERSION_CODES.M) |
| public void testBindToDefaultNetworkSucceeds() { |
| String url = NativeTestServer.getEchoMethodURL(); |
| ConnectivityManagerDelegate delegate = |
| new ConnectivityManagerDelegate(mTestRule.getTestFramework().getContext()); |
| Network defaultNetwork = delegate.getDefaultNetwork(); |
| assume().that(defaultNetwork).isNotNull(); |
| |
| ExperimentalCronetEngine cronetEngine = mTestRule.getTestFramework().getEngine(); |
| TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| ExperimentalUrlRequest.Builder builder = |
| cronetEngine.newUrlRequestBuilder(url, callback, callback.getExecutor()); |
| builder.bindToNetwork(defaultNetwork.getNetworkHandle()); |
| builder.build().start(); |
| callback.blockForDone(); |
| assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); |
| } |
| |
| @NativeMethods("cronet_tests") |
| interface Natives { |
| // Return connection migration disable load flag value. |
| int getConnectionMigrationDisableLoadFlag(); |
| } |
| } |