1 // Copyright 2014 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package org.chromium.net; 6 7 import static com.google.common.truth.Truth.assertThat; 8 import static com.google.common.truth.Truth.assertWithMessage; 9 import static com.google.common.truth.TruthJUnit.assume; 10 11 import static org.junit.Assert.assertThrows; 12 import static org.junit.Assert.fail; 13 14 import static org.chromium.net.truth.UrlResponseInfoSubject.assertThat; 15 16 import android.net.Network; 17 import android.os.Build; 18 import android.os.ConditionVariable; 19 import android.os.Process; 20 import android.os.StrictMode; 21 22 import androidx.test.ext.junit.runners.AndroidJUnit4; 23 import androidx.test.filters.SmallTest; 24 25 import org.jni_zero.NativeMethods; 26 import org.junit.After; 27 import org.junit.Before; 28 import org.junit.Rule; 29 import org.junit.Test; 30 import org.junit.runner.RunWith; 31 32 import org.chromium.base.Log; 33 import org.chromium.base.test.util.DoNotBatch; 34 import org.chromium.net.CronetTestRule.CronetImplementation; 35 import org.chromium.net.CronetTestRule.IgnoreFor; 36 import org.chromium.net.CronetTestRule.RequiresMinAndroidApi; 37 import org.chromium.net.CronetTestRule.RequiresMinApi; 38 import org.chromium.net.NetworkChangeNotifierAutoDetect.ConnectivityManagerDelegate; 39 import org.chromium.net.TestUrlRequestCallback.FailureType; 40 import org.chromium.net.TestUrlRequestCallback.ResponseStep; 41 import org.chromium.net.apihelpers.UploadDataProviders; 42 import org.chromium.net.impl.CronetExceptionImpl; 43 import org.chromium.net.impl.CronetUrlRequest; 44 import org.chromium.net.impl.NetworkExceptionImpl; 45 import org.chromium.net.impl.UrlResponseInfoImpl; 46 import org.chromium.net.test.FailurePhase; 47 48 import java.io.IOException; 49 import java.net.ConnectException; 50 import java.nio.ByteBuffer; 51 import java.util.AbstractMap; 52 import java.util.ArrayList; 53 import java.util.Arrays; 54 import java.util.List; 55 import java.util.Map; 56 import java.util.concurrent.Executor; 57 import java.util.concurrent.ExecutorService; 58 import java.util.concurrent.Executors; 59 import java.util.concurrent.atomic.AtomicBoolean; 60 import java.util.regex.Matcher; 61 import java.util.regex.Pattern; 62 63 /** Test functionality of CronetUrlRequest. */ 64 @DoNotBatch(reason = "crbug/1459563") 65 @RunWith(AndroidJUnit4.class) 66 public class CronetUrlRequestTest { 67 private static final String TAG = CronetUrlRequestTest.class.getSimpleName(); 68 69 // URL used for base tests. 70 private static final String TEST_URL = "http://127.0.0.1:8000"; 71 72 @Rule public final CronetTestRule mTestRule = CronetTestRule.withAutomaticEngineStartup(); 73 74 private MockUrlRequestJobFactory mMockUrlRequestJobFactory; 75 76 @Before setUp()77 public void setUp() throws Exception { 78 assertThat( 79 NativeTestServer.startNativeTestServer( 80 mTestRule.getTestFramework().getContext())) 81 .isTrue(); 82 } 83 84 @After tearDown()85 public void tearDown() throws Exception { 86 if (mMockUrlRequestJobFactory != null) { 87 mMockUrlRequestJobFactory.shutdown(); 88 } 89 NativeTestServer.shutdownNativeTestServer(); 90 } 91 startAndWaitForComplete(String url)92 private TestUrlRequestCallback startAndWaitForComplete(String url) throws Exception { 93 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 94 // Create request. 95 UrlRequest.Builder builder = 96 mTestRule 97 .getTestFramework() 98 .getEngine() 99 .newUrlRequestBuilder(url, callback, callback.getExecutor()); 100 UrlRequest urlRequest = builder.build(); 101 urlRequest.start(); 102 callback.blockForDone(); 103 // Wait for all posted tasks to be executed to ensure there is no unhandled exception. 104 callback.shutdownExecutorAndWait(); 105 assertThat(urlRequest.isDone()).isTrue(); 106 return callback; 107 } 108 checkResponseInfo( UrlResponseInfo responseInfo, String expectedUrl, int expectedHttpStatusCode, String expectedHttpStatusText)109 private void checkResponseInfo( 110 UrlResponseInfo responseInfo, 111 String expectedUrl, 112 int expectedHttpStatusCode, 113 String expectedHttpStatusText) { 114 assertThat(responseInfo).hasUrlThat().isEqualTo(expectedUrl); 115 assertThat(responseInfo.getUrlChain().get(responseInfo.getUrlChain().size() - 1)) 116 .isEqualTo(expectedUrl); 117 assertThat(responseInfo).hasHttpStatusCodeThat().isEqualTo(expectedHttpStatusCode); 118 assertThat(responseInfo).hasHttpStatusTextThat().isEqualTo(expectedHttpStatusText); 119 assertThat(responseInfo).wasNotCached(); 120 assertThat(responseInfo.toString()).isNotEmpty(); 121 } 122 checkResponseInfoHeader( UrlResponseInfo responseInfo, String headerName, String headerValue)123 private void checkResponseInfoHeader( 124 UrlResponseInfo responseInfo, String headerName, String headerValue) { 125 Map<String, List<String>> responseHeaders = responseInfo.getAllHeaders(); 126 List<String> header = responseHeaders.get(headerName); 127 assertThat(header).contains(headerValue); 128 } 129 130 @Test 131 @SmallTest testBuilderChecks()132 public void testBuilderChecks() throws Exception { 133 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 134 135 NullPointerException e = 136 assertThrows( 137 NullPointerException.class, 138 () -> 139 mTestRule 140 .getTestFramework() 141 .getEngine() 142 .newUrlRequestBuilder( 143 null, callback, callback.getExecutor())); 144 assertThat(e).hasMessageThat().isEqualTo("URL is required."); 145 146 e = 147 assertThrows( 148 NullPointerException.class, 149 () -> 150 mTestRule 151 .getTestFramework() 152 .getEngine() 153 .newUrlRequestBuilder( 154 NativeTestServer.getRedirectURL(), 155 null, 156 callback.getExecutor())); 157 assertThat(e).hasMessageThat().isEqualTo("Callback is required."); 158 159 e = 160 assertThrows( 161 NullPointerException.class, 162 () -> 163 mTestRule 164 .getTestFramework() 165 .getEngine() 166 .newUrlRequestBuilder( 167 NativeTestServer.getRedirectURL(), callback, null)); 168 assertThat(e).hasMessageThat().isEqualTo("Executor is required."); 169 170 // Verify successful creation doesn't throw. 171 mTestRule 172 .getTestFramework() 173 .getEngine() 174 .newUrlRequestBuilder( 175 NativeTestServer.getRedirectURL(), callback, callback.getExecutor()); 176 } 177 178 @Test 179 @SmallTest testSimpleGet()180 public void testSimpleGet() throws Exception { 181 String url = NativeTestServer.getEchoMethodURL(); 182 TestUrlRequestCallback callback = startAndWaitForComplete(url); 183 assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); 184 // Default method is 'GET'. 185 assertThat(callback.mResponseAsString).isEqualTo("GET"); 186 assertThat(callback.mRedirectCount).isEqualTo(0); 187 assertThat(ResponseStep.ON_SUCCEEDED).isEqualTo(callback.mResponseStep); 188 UrlResponseInfo urlResponseInfo = 189 createUrlResponseInfo( 190 new String[] {url}, 191 "OK", 192 200, 193 86, 194 "Connection", 195 "close", 196 "Content-Length", 197 "3", 198 "Content-Type", 199 "text/plain"); 200 mTestRule.assertResponseEquals(urlResponseInfo, callback.getResponseInfoWithChecks()); 201 checkResponseInfo( 202 callback.getResponseInfoWithChecks(), 203 NativeTestServer.getEchoMethodURL(), 204 200, 205 "OK"); 206 } 207 createUrlResponseInfo( String[] urls, String message, int statusCode, int receivedBytes, String... headers)208 UrlResponseInfo createUrlResponseInfo( 209 String[] urls, String message, int statusCode, int receivedBytes, String... headers) { 210 ArrayList<Map.Entry<String, String>> headersList = new ArrayList<>(); 211 for (int i = 0; i < headers.length; i += 2) { 212 headersList.add( 213 new AbstractMap.SimpleImmutableEntry<String, String>( 214 headers[i], headers[i + 1])); 215 } 216 UrlResponseInfoImpl unknown = 217 new UrlResponseInfoImpl( 218 Arrays.asList(urls), 219 statusCode, 220 message, 221 headersList, 222 false, 223 "unknown", 224 ":0", 225 receivedBytes); 226 return unknown; 227 } 228 runConnectionMigrationTest(boolean disableConnectionMigration)229 void runConnectionMigrationTest(boolean disableConnectionMigration) { 230 // URLRequest load flags at net/base/load_flags_list.h. 231 int connectionMigrationLoadFlag = 232 CronetUrlRequestTestJni.get().getConnectionMigrationDisableLoadFlag(); 233 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 234 callback.setAutoAdvance(false); 235 // Create builder, start a request, and check if default load_flags are set correctly. 236 ExperimentalUrlRequest.Builder builder = 237 (ExperimentalUrlRequest.Builder) 238 mTestRule 239 .getTestFramework() 240 .getEngine() 241 .newUrlRequestBuilder( 242 NativeTestServer.getFileURL("/success.txt"), 243 callback, 244 callback.getExecutor()); 245 // Disable connection migration. 246 if (disableConnectionMigration) builder.disableConnectionMigration(); 247 UrlRequest urlRequest = builder.build(); 248 urlRequest.start(); 249 callback.waitForNextStep(); 250 int loadFlags = CronetTestUtil.getLoadFlags(urlRequest); 251 if (disableConnectionMigration) { 252 assertThat(loadFlags & connectionMigrationLoadFlag) 253 .isEqualTo(connectionMigrationLoadFlag); 254 } else { 255 assertThat(loadFlags & connectionMigrationLoadFlag).isEqualTo(0); 256 } 257 callback.setAutoAdvance(true); 258 callback.startNextRead(urlRequest); 259 callback.blockForDone(); 260 } 261 262 /** Tests that disabling connection migration sets the URLRequest load flag correctly. */ 263 @Test 264 @SmallTest 265 @IgnoreFor( 266 implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, 267 reason = "bug.com/1494845: tests native implementation internals") testLoadFlagsWithConnectionMigration()268 public void testLoadFlagsWithConnectionMigration() throws Exception { 269 runConnectionMigrationTest(/* disableConnectionMigration= */ false); 270 runConnectionMigrationTest(/* disableConnectionMigration= */ true); 271 } 272 273 /** 274 * Tests a redirect by running it step-by-step. Also tests that delaying a request works as 275 * expected. To make sure there are no unexpected pending messages, does a GET between 276 * UrlRequest.Callback callbacks. 277 */ 278 @Test 279 @SmallTest testRedirectAsync()280 public void testRedirectAsync() throws Exception { 281 // Start the request and wait to see the redirect. 282 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 283 callback.setAutoAdvance(false); 284 UrlRequest.Builder builder = 285 mTestRule 286 .getTestFramework() 287 .getEngine() 288 .newUrlRequestBuilder( 289 NativeTestServer.getRedirectURL(), 290 callback, 291 callback.getExecutor()); 292 UrlRequest urlRequest = builder.build(); 293 urlRequest.start(); 294 callback.waitForNextStep(); 295 296 // Check the redirect. 297 assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_RECEIVED_REDIRECT); 298 assertThat(callback.mRedirectResponseInfoList).hasSize(1); 299 checkResponseInfo( 300 callback.mRedirectResponseInfoList.get(0), 301 NativeTestServer.getRedirectURL(), 302 302, 303 "Found"); 304 assertThat(callback.mRedirectResponseInfoList.get(0).getUrlChain()).hasSize(1); 305 assertThat(callback.mRedirectUrlList.get(0)).isEqualTo(NativeTestServer.getSuccessURL()); 306 checkResponseInfoHeader( 307 callback.mRedirectResponseInfoList.get(0), "redirect-header", "header-value"); 308 309 UrlResponseInfo expected = 310 createUrlResponseInfo( 311 new String[] {NativeTestServer.getRedirectURL()}, 312 "Found", 313 302, 314 73, 315 "Location", 316 "/success.txt", 317 "redirect-header", 318 "header-value"); 319 mTestRule.assertResponseEquals(expected, callback.mRedirectResponseInfoList.get(0)); 320 321 // Wait for an unrelated request to finish. The request should not 322 // advance until followRedirect is invoked. 323 testSimpleGet(); 324 assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_RECEIVED_REDIRECT); 325 assertThat(callback.mRedirectResponseInfoList).hasSize(1); 326 327 // Follow the redirect and wait for the next set of headers. 328 urlRequest.followRedirect(); 329 callback.waitForNextStep(); 330 331 assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_RESPONSE_STARTED); 332 assertThat(callback.mRedirectResponseInfoList).hasSize(1); 333 assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); 334 checkResponseInfo( 335 callback.getResponseInfoWithChecks(), NativeTestServer.getSuccessURL(), 200, "OK"); 336 assertThat(callback.getResponseInfoWithChecks()) 337 .hasUrlChainThat() 338 .containsExactly( 339 NativeTestServer.getRedirectURL(), NativeTestServer.getSuccessURL()) 340 .inOrder(); 341 342 // Wait for an unrelated request to finish. The request should not 343 // advance until read is invoked. 344 testSimpleGet(); 345 assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_RESPONSE_STARTED); 346 347 // One read should get all the characters, but best not to depend on 348 // how much is actually read from the socket at once. 349 while (!callback.isDone()) { 350 callback.startNextRead(urlRequest); 351 callback.waitForNextStep(); 352 String response = callback.mResponseAsString; 353 ResponseStep step = callback.mResponseStep; 354 if (!callback.isDone()) { 355 assertThat(step).isEqualTo(ResponseStep.ON_READ_COMPLETED); 356 } 357 // Should not receive any messages while waiting for another get, 358 // as the next read has not been started. 359 testSimpleGet(); 360 assertThat(callback.mResponseAsString).isEqualTo(response); 361 assertThat(callback.mResponseStep).isEqualTo(step); 362 } 363 assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_SUCCEEDED); 364 assertThat(callback.mResponseAsString).isEqualTo(NativeTestServer.SUCCESS_BODY); 365 366 UrlResponseInfo urlResponseInfo = 367 createUrlResponseInfo( 368 new String[] { 369 NativeTestServer.getRedirectURL(), NativeTestServer.getSuccessURL() 370 }, 371 "OK", 372 200, 373 258, 374 "Content-Type", 375 "text/plain", 376 "Access-Control-Allow-Origin", 377 "*", 378 "header-name", 379 "header-value", 380 "multi-header-name", 381 "header-value1", 382 "multi-header-name", 383 "header-value2"); 384 385 mTestRule.assertResponseEquals(urlResponseInfo, callback.getResponseInfoWithChecks()); 386 // Make sure there are no other pending messages, which would trigger 387 // asserts in TestUrlRequestCallback. 388 testSimpleGet(); 389 } 390 391 /** Tests redirect without location header doesn't cause a crash. */ 392 @Test 393 @SmallTest testRedirectWithNullLocationHeader()394 public void testRedirectWithNullLocationHeader() throws Exception { 395 String url = NativeTestServer.getFileURL("/redirect_broken_header.html"); 396 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 397 398 UrlRequest.Builder builder = 399 mTestRule 400 .getTestFramework() 401 .getEngine() 402 .newUrlRequestBuilder(url, callback, callback.getExecutor()); 403 final UrlRequest urlRequest = builder.build(); 404 urlRequest.start(); 405 callback.blockForDone(); 406 assertThat(callback.mResponseAsString) 407 .isEqualTo( 408 "<!DOCTYPE html>\n<html>\n<head>\n<title>Redirect</title>\n" 409 + "<p>Redirecting...</p>\n</head>\n</html>\n"); 410 assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_SUCCEEDED); 411 assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(302); 412 assertThat(callback.mError).isNull(); 413 assertThat(callback.mOnErrorCalled).isFalse(); 414 } 415 416 /** Tests onRedirectReceived after cancel doesn't cause a crash. */ 417 @Test 418 @SmallTest testOnRedirectReceivedAfterCancel()419 public void testOnRedirectReceivedAfterCancel() throws Exception { 420 final AtomicBoolean failedExpectation = new AtomicBoolean(); 421 TestUrlRequestCallback callback = 422 new TestUrlRequestCallback() { 423 @Override 424 public void onRedirectReceived( 425 UrlRequest request, UrlResponseInfo info, String newLocationUrl) { 426 assertThat(mRedirectCount).isEqualTo(0); 427 failedExpectation.compareAndSet(false, 0 != mRedirectCount); 428 super.onRedirectReceived(request, info, newLocationUrl); 429 // Cancel the request, so the second redirect will not be received. 430 request.cancel(); 431 } 432 433 @Override 434 public void onResponseStarted(UrlRequest request, UrlResponseInfo info) { 435 failedExpectation.set(true); 436 fail(); 437 } 438 439 @Override 440 public void onReadCompleted( 441 UrlRequest request, UrlResponseInfo info, ByteBuffer byteBuffer) { 442 failedExpectation.set(true); 443 fail(); 444 } 445 446 @Override 447 public void onSucceeded(UrlRequest request, UrlResponseInfo info) { 448 failedExpectation.set(true); 449 fail(); 450 } 451 452 @Override 453 public void onFailed( 454 UrlRequest request, UrlResponseInfo info, CronetException error) { 455 failedExpectation.set(true); 456 fail(); 457 } 458 459 @Override 460 public void onCanceled(UrlRequest request, UrlResponseInfo info) { 461 assertThat(mRedirectCount).isEqualTo(1); 462 failedExpectation.compareAndSet(false, 1 != mRedirectCount); 463 super.onCanceled(request, info); 464 } 465 }; 466 467 UrlRequest.Builder builder = 468 mTestRule 469 .getTestFramework() 470 .getEngine() 471 .newUrlRequestBuilder( 472 NativeTestServer.getMultiRedirectURL(), 473 callback, 474 callback.getExecutor()); 475 476 final UrlRequest urlRequest = builder.build(); 477 urlRequest.start(); 478 callback.blockForDone(); 479 assertThat(failedExpectation.get()).isFalse(); 480 // Check that only one redirect is received. 481 assertThat(callback.mRedirectCount).isEqualTo(1); 482 // Check that onCanceled is called. 483 assertThat(callback.mOnCanceledCalled).isTrue(); 484 } 485 486 @Test 487 @SmallTest testNotFound()488 public void testNotFound() throws Exception { 489 String url = NativeTestServer.getFileURL("/notfound.html"); 490 TestUrlRequestCallback callback = startAndWaitForComplete(url); 491 checkResponseInfo(callback.getResponseInfoWithChecks(), url, 404, "Not Found"); 492 assertThat(callback.mResponseAsString) 493 .isEqualTo( 494 "<!DOCTYPE html>\n<html>\n<head>\n<title>Not found</title>\n" 495 + "<p>Test page loaded.</p>\n</head>\n</html>\n"); 496 assertThat(callback.mRedirectCount).isEqualTo(0); 497 assertThat(ResponseStep.ON_SUCCEEDED).isEqualTo(callback.mResponseStep); 498 } 499 500 // Checks that UrlRequest.Callback.onFailed is only called once in the case 501 // of ERR_CONTENT_LENGTH_MISMATCH, which has an unusual failure path. 502 // See http://crbug.com/468803. 503 @Test 504 @SmallTest 505 @IgnoreFor( 506 implementations = {CronetImplementation.FALLBACK}, 507 reason = "No canonical exception to assert on") testContentLengthMismatchFailsOnce()508 public void testContentLengthMismatchFailsOnce() throws Exception { 509 String url = NativeTestServer.getFileURL("/content_length_mismatch.html"); 510 TestUrlRequestCallback callback = startAndWaitForComplete(url); 511 assertThat(callback.getResponseInfo()).hasHttpStatusCodeThat().isEqualTo(200); 512 // The entire response body will be read before the error is returned. 513 // This is because the network stack returns data as it's read from the 514 // socket, and the socket close message which triggers the error will 515 // only be passed along after all data has been read. 516 assertThat(callback.mResponseAsString) 517 .isEqualTo("Response that lies about content length."); 518 assertThat(callback.mError) 519 .hasMessageThat() 520 .contains("Exception in CronetUrlRequest: net::ERR_CONTENT_LENGTH_MISMATCH"); 521 // Wait for a couple round trips to make sure there are no pending 522 // onFailed messages. This test relies on checks in 523 // TestUrlRequestCallback catching a second onFailed call. 524 testSimpleGet(); 525 } 526 527 @Test 528 @SmallTest testSetHttpMethod()529 public void testSetHttpMethod() throws Exception { 530 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 531 String methodName = "HEAD"; 532 UrlRequest.Builder builder = 533 mTestRule 534 .getTestFramework() 535 .getEngine() 536 .newUrlRequestBuilder( 537 NativeTestServer.getEchoMethodURL(), 538 callback, 539 callback.getExecutor()); 540 // Try to set 'null' method. 541 NullPointerException e = 542 assertThrows(NullPointerException.class, () -> builder.setHttpMethod(null)); 543 assertThat(e).hasMessageThat().isEqualTo("Method is required."); 544 545 builder.setHttpMethod(methodName); 546 builder.build().start(); 547 callback.blockForDone(); 548 assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); 549 assertThat(callback.mHttpResponseDataLength).isEqualTo(0); 550 } 551 552 @Test 553 @SmallTest testBadMethod()554 public void testBadMethod() throws Exception { 555 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 556 UrlRequest.Builder builder = 557 mTestRule 558 .getTestFramework() 559 .getEngine() 560 .newUrlRequestBuilder(TEST_URL, callback, callback.getExecutor()); 561 builder.setHttpMethod("bad:method!"); 562 IllegalArgumentException e = 563 assertThrows(IllegalArgumentException.class, () -> builder.build().start()); 564 assertThat(e).hasMessageThat().isEqualTo("Invalid http method bad:method!"); 565 } 566 567 @Test 568 @SmallTest testBadHeaderName()569 public void testBadHeaderName() throws Exception { 570 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 571 UrlRequest.Builder builder = 572 mTestRule 573 .getTestFramework() 574 .getEngine() 575 .newUrlRequestBuilder(TEST_URL, callback, callback.getExecutor()); 576 builder.addHeader("header:name", "headervalue"); 577 IllegalArgumentException e = 578 assertThrows(IllegalArgumentException.class, () -> builder.build().start()); 579 if (mTestRule.implementationUnderTest() == CronetImplementation.AOSP_PLATFORM && 580 !mTestRule.isRunningInAOSP()) { 581 // TODO(b/307234565): Remove check once chromium emulator has latest changes. 582 assertThat(e).hasMessageThat().isEqualTo("Invalid header header:name=headervalue"); 583 } else { 584 assertThat(e).hasMessageThat().isEqualTo("Invalid header with headername: header:name"); 585 } 586 } 587 588 @Test 589 @SmallTest testAcceptEncodingIgnored()590 public void testAcceptEncodingIgnored() throws Exception { 591 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 592 UrlRequest.Builder builder = 593 mTestRule 594 .getTestFramework() 595 .getEngine() 596 .newUrlRequestBuilder( 597 NativeTestServer.getEchoAllHeadersURL(), 598 callback, 599 callback.getExecutor()); 600 // This line should eventually throw an exception, once callers have migrated 601 builder.addHeader("accept-encoding", "foozip"); 602 builder.build().start(); 603 callback.blockForDone(); 604 assertThat(callback.mResponseAsString).doesNotContain("foozip"); 605 } 606 607 @Test 608 @SmallTest testBadHeaderValue()609 public void testBadHeaderValue() throws Exception { 610 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 611 UrlRequest.Builder builder = 612 mTestRule 613 .getTestFramework() 614 .getEngine() 615 .newUrlRequestBuilder(TEST_URL, callback, callback.getExecutor()); 616 builder.addHeader("headername", "bad header\r\nvalue"); 617 IllegalArgumentException e = 618 assertThrows(IllegalArgumentException.class, () -> builder.build().start()); 619 if (mTestRule.implementationUnderTest() == CronetImplementation.AOSP_PLATFORM && 620 !mTestRule.isRunningInAOSP()) { 621 // TODO(b/307234565): Remove check once chromium emulator has latest changes. 622 assertThat(e) 623 .hasMessageThat() 624 .isEqualTo("Invalid header headername=bad header\r\nvalue"); 625 } else { 626 assertThat(e).hasMessageThat().isEqualTo("Invalid header with headername: headername"); 627 } 628 } 629 630 @Test 631 @SmallTest testAddHeader()632 public void testAddHeader() throws Exception { 633 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 634 String headerName = "header-name"; 635 String headerValue = "header-value"; 636 UrlRequest.Builder builder = 637 mTestRule 638 .getTestFramework() 639 .getEngine() 640 .newUrlRequestBuilder( 641 NativeTestServer.getEchoHeaderURL(headerName), 642 callback, 643 callback.getExecutor()); 644 645 builder.addHeader(headerName, headerValue); 646 builder.build().start(); 647 callback.blockForDone(); 648 assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); 649 assertThat(callback.mResponseAsString).isEqualTo(headerValue); 650 } 651 652 @Test 653 @SmallTest testMultiRequestHeaders()654 public void testMultiRequestHeaders() throws Exception { 655 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 656 String headerName = "header-name"; 657 String headerValue1 = "header-value1"; 658 String headerValue2 = "header-value2"; 659 UrlRequest.Builder builder = 660 mTestRule 661 .getTestFramework() 662 .getEngine() 663 .newUrlRequestBuilder( 664 NativeTestServer.getEchoAllHeadersURL(), 665 callback, 666 callback.getExecutor()); 667 builder.addHeader(headerName, headerValue1); 668 builder.addHeader(headerName, headerValue2); 669 builder.build().start(); 670 callback.blockForDone(); 671 assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); 672 String headers = callback.mResponseAsString; 673 Pattern pattern = Pattern.compile(headerName + ":\\s(.*)\\r\\n"); 674 Matcher matcher = pattern.matcher(headers); 675 List<String> actualValues = new ArrayList<String>(); 676 while (matcher.find()) { 677 actualValues.add(matcher.group(1)); 678 } 679 assertThat(actualValues).containsExactly("header-value2"); 680 } 681 682 @Test 683 @SmallTest testCustomReferer_verbatim()684 public void testCustomReferer_verbatim() throws Exception { 685 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 686 String refererName = "Referer"; 687 String refererValue = "http://example.com/"; 688 UrlRequest.Builder builder = 689 mTestRule 690 .getTestFramework() 691 .getEngine() 692 .newUrlRequestBuilder( 693 NativeTestServer.getEchoHeaderURL(refererName), 694 callback, 695 callback.getExecutor()); 696 builder.addHeader(refererName, refererValue); 697 builder.build().start(); 698 callback.blockForDone(); 699 assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); 700 assertThat(callback.mResponseAsString).isEqualTo(refererValue); 701 } 702 703 @Test 704 @SmallTest 705 @IgnoreFor( 706 implementations = {CronetImplementation.FALLBACK}, 707 reason = "This is not the case for the fallback implementation") testCustomReferer_changeToCanonical()708 public void testCustomReferer_changeToCanonical() throws Exception { 709 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 710 String refererName = "Referer"; 711 String refererValueNoTrailingSlash = "http://example.com"; 712 UrlRequest.Builder builder = 713 mTestRule 714 .getTestFramework() 715 .getEngine() 716 .newUrlRequestBuilder( 717 NativeTestServer.getEchoHeaderURL(refererName), 718 callback, 719 callback.getExecutor()); 720 builder.addHeader(refererName, refererValueNoTrailingSlash); 721 builder.build().start(); 722 callback.blockForDone(); 723 assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); 724 assertThat(callback.mResponseAsString).isEqualTo(refererValueNoTrailingSlash + "/"); 725 } 726 727 @Test 728 @SmallTest 729 @IgnoreFor( 730 implementations = {CronetImplementation.FALLBACK}, 731 reason = "This is not the case for the fallback implementation") testCustomReferer_discardInvalid()732 public void testCustomReferer_discardInvalid() throws Exception { 733 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 734 String refererName = "Referer"; 735 String invalidRefererValue = "foobar"; 736 UrlRequest.Builder builder = 737 mTestRule 738 .getTestFramework() 739 .getEngine() 740 .newUrlRequestBuilder( 741 NativeTestServer.getEchoHeaderURL(refererName), 742 callback, 743 callback.getExecutor()); 744 builder.addHeader(refererName, invalidRefererValue); 745 builder.build().start(); 746 callback.blockForDone(); 747 assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); 748 assertThat(callback.mResponseAsString).isEqualTo("Header not found. :("); 749 } 750 751 @Test 752 @SmallTest testCustomUserAgent()753 public void testCustomUserAgent() throws Exception { 754 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 755 String userAgentName = "User-Agent"; 756 String userAgentValue = "User-Agent-Value"; 757 UrlRequest.Builder builder = 758 mTestRule 759 .getTestFramework() 760 .getEngine() 761 .newUrlRequestBuilder( 762 NativeTestServer.getEchoHeaderURL(userAgentName), 763 callback, 764 callback.getExecutor()); 765 builder.addHeader(userAgentName, userAgentValue); 766 builder.build().start(); 767 callback.blockForDone(); 768 assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); 769 assertThat(callback.mResponseAsString).isEqualTo(userAgentValue); 770 } 771 772 @Test 773 @SmallTest testDefaultUserAgent()774 public void testDefaultUserAgent() throws Exception { 775 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 776 String headerName = "User-Agent"; 777 UrlRequest.Builder builder = 778 mTestRule 779 .getTestFramework() 780 .getEngine() 781 .newUrlRequestBuilder( 782 NativeTestServer.getEchoHeaderURL(headerName), 783 callback, 784 callback.getExecutor()); 785 builder.build().start(); 786 callback.blockForDone(); 787 assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); 788 assertWithMessage( 789 "Default User-Agent should contain Cronet/n.n.n.n but is " 790 + callback.mResponseAsString) 791 .that(callback.mResponseAsString) 792 .matches(Pattern.compile(".+Cronet/\\d+\\.\\d+\\.\\d+\\.\\d+.+")); 793 } 794 795 @Test 796 @SmallTest 797 @IgnoreFor( 798 implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, 799 reason = "crbug.com/1495309: Enable once we drop MockUrlRequestJobFactory") testMockSuccess()800 public void testMockSuccess() throws Exception { 801 mMockUrlRequestJobFactory = 802 new MockUrlRequestJobFactory(mTestRule.getTestFramework().getEngine()); 803 TestUrlRequestCallback callback = startAndWaitForComplete(NativeTestServer.getSuccessURL()); 804 assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); 805 assertThat(callback.mRedirectResponseInfoList).isEmpty(); 806 assertThat(callback.mHttpResponseDataLength).isNotEqualTo(0); 807 assertThat(ResponseStep.ON_SUCCEEDED).isEqualTo(callback.mResponseStep); 808 Map<String, List<String>> responseHeaders = 809 callback.getResponseInfoWithChecks().getAllHeaders(); 810 assertThat(responseHeaders).containsEntry("header-name", Arrays.asList("header-value")); 811 assertThat(responseHeaders) 812 .containsEntry( 813 "multi-header-name", Arrays.asList("header-value1", "header-value2")); 814 } 815 816 @Test 817 @SmallTest testResponseHeadersList()818 public void testResponseHeadersList() throws Exception { 819 TestUrlRequestCallback callback = startAndWaitForComplete(NativeTestServer.getSuccessURL()); 820 assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); 821 List<Map.Entry<String, String>> responseHeaders = 822 callback.getResponseInfoWithChecks().getAllHeadersAsList(); 823 824 assertThat(new AbstractMap.SimpleEntry<>("Content-Type", "text/plain")) 825 .isEqualTo(responseHeaders.get(0)); 826 assertThat(new AbstractMap.SimpleEntry<>("Access-Control-Allow-Origin", "*")) 827 .isEqualTo(responseHeaders.get(1)); 828 assertThat(new AbstractMap.SimpleEntry<>("header-name", "header-value")) 829 .isEqualTo(responseHeaders.get(2)); 830 assertThat(new AbstractMap.SimpleEntry<>("multi-header-name", "header-value1")) 831 .isEqualTo(responseHeaders.get(3)); 832 assertThat(new AbstractMap.SimpleEntry<>("multi-header-name", "header-value2")) 833 .isEqualTo(responseHeaders.get(4)); 834 } 835 836 @Test 837 @SmallTest 838 @IgnoreFor( 839 implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, 840 reason = "crbug.com/1495309: Enable once we drop MockUrlRequestJobFactory") testMockMultiRedirect()841 public void testMockMultiRedirect() throws Exception { 842 mMockUrlRequestJobFactory = 843 new MockUrlRequestJobFactory(mTestRule.getTestFramework().getEngine()); 844 TestUrlRequestCallback callback = 845 startAndWaitForComplete(NativeTestServer.getMultiRedirectURL()); 846 UrlResponseInfo mResponseInfo = callback.getResponseInfoWithChecks(); 847 assertThat(callback.mRedirectCount).isEqualTo(2); 848 assertThat(mResponseInfo).hasHttpStatusCodeThat().isEqualTo(200); 849 assertThat(callback.mRedirectResponseInfoList).hasSize(2); 850 851 // Check first redirect (multiredirect.html -> redirect.html) 852 UrlResponseInfo firstExpectedResponseInfo = 853 createUrlResponseInfo( 854 new String[] {NativeTestServer.getMultiRedirectURL()}, 855 "Found", 856 302, 857 76, 858 "Location", 859 "/redirect.html", 860 "redirect-header0", 861 "header-value"); 862 UrlResponseInfo firstRedirectResponseInfo = callback.mRedirectResponseInfoList.get(0); 863 mTestRule.assertResponseEquals(firstExpectedResponseInfo, firstRedirectResponseInfo); 864 865 // Check second redirect (redirect.html -> success.txt) 866 UrlResponseInfo secondExpectedResponseInfo = 867 createUrlResponseInfo( 868 new String[] { 869 NativeTestServer.getMultiRedirectURL(), 870 NativeTestServer.getRedirectURL(), 871 NativeTestServer.getSuccessURL() 872 }, 873 "OK", 874 200, 875 334, 876 "Content-Type", 877 "text/plain", 878 "Access-Control-Allow-Origin", 879 "*", 880 "header-name", 881 "header-value", 882 "multi-header-name", 883 "header-value1", 884 "multi-header-name", 885 "header-value2"); 886 887 mTestRule.assertResponseEquals(secondExpectedResponseInfo, mResponseInfo); 888 assertThat(callback.mHttpResponseDataLength).isNotEqualTo(0); 889 assertThat(callback.mRedirectCount).isEqualTo(2); 890 assertThat(ResponseStep.ON_SUCCEEDED).isEqualTo(callback.mResponseStep); 891 } 892 893 @Test 894 @SmallTest 895 @IgnoreFor( 896 implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, 897 reason = "crbug.com/1495309: Enable once we drop MockUrlRequestJobFactory") testMockNotFound()898 public void testMockNotFound() throws Exception { 899 mMockUrlRequestJobFactory = 900 new MockUrlRequestJobFactory(mTestRule.getTestFramework().getEngine()); 901 TestUrlRequestCallback callback = 902 startAndWaitForComplete(NativeTestServer.getNotFoundURL()); 903 UrlResponseInfo expected = 904 createUrlResponseInfo( 905 new String[] {NativeTestServer.getNotFoundURL()}, "Not Found", 404, 120); 906 mTestRule.assertResponseEquals(expected, callback.getResponseInfoWithChecks()); 907 assertThat(callback.mHttpResponseDataLength).isNotEqualTo(0); 908 assertThat(callback.mRedirectCount).isEqualTo(0); 909 assertThat(callback.mOnErrorCalled).isFalse(); 910 assertThat(ResponseStep.ON_SUCCEEDED).isEqualTo(callback.mResponseStep); 911 } 912 913 @Test 914 @SmallTest 915 @IgnoreFor( 916 implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, 917 reason = "crbug.com/1495309: Enable once we drop MockUrlRequestJobFactory") testMockStartAsyncError()918 public void testMockStartAsyncError() throws Exception { 919 mMockUrlRequestJobFactory = 920 new MockUrlRequestJobFactory(mTestRule.getTestFramework().getEngine()); 921 final int arbitraryNetError = -3; 922 TestUrlRequestCallback callback = 923 startAndWaitForComplete( 924 MockUrlRequestJobFactory.getMockUrlWithFailure( 925 FailurePhase.START, arbitraryNetError)); 926 assertThat(callback.getResponseInfo()).isNull(); 927 assertThat(callback.mError).isNotNull(); 928 assertThat(((NetworkException) callback.mError).getCronetInternalErrorCode()) 929 .isEqualTo(arbitraryNetError); 930 assertThat(callback.mRedirectCount).isEqualTo(0); 931 assertThat(callback.mOnErrorCalled).isTrue(); 932 assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_FAILED); 933 } 934 935 @Test 936 @SmallTest 937 @IgnoreFor( 938 implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, 939 reason = "crbug.com/1495309: Enable once we drop MockUrlRequestJobFactory") testMockReadDataSyncError()940 public void testMockReadDataSyncError() throws Exception { 941 mMockUrlRequestJobFactory = 942 new MockUrlRequestJobFactory(mTestRule.getTestFramework().getEngine()); 943 final int arbitraryNetError = -4; 944 TestUrlRequestCallback callback = 945 startAndWaitForComplete( 946 MockUrlRequestJobFactory.getMockUrlWithFailure( 947 FailurePhase.READ_SYNC, arbitraryNetError)); 948 assertThat(callback.getResponseInfo()).hasHttpStatusCodeThat().isEqualTo(200); 949 assertThat(callback.getResponseInfo()).hasReceivedByteCountThat().isEqualTo(15); 950 assertThat(callback.mError).isNotNull(); 951 assertThat(((NetworkException) callback.mError).getCronetInternalErrorCode()) 952 .isEqualTo(arbitraryNetError); 953 assertThat(callback.mRedirectCount).isEqualTo(0); 954 assertThat(callback.mOnErrorCalled).isTrue(); 955 assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_FAILED); 956 } 957 958 @Test 959 @SmallTest 960 @IgnoreFor( 961 implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, 962 reason = "crbug.com/1495309: Enable once we drop MockUrlRequestJobFactory") testMockReadDataAsyncError()963 public void testMockReadDataAsyncError() throws Exception { 964 mMockUrlRequestJobFactory = 965 new MockUrlRequestJobFactory(mTestRule.getTestFramework().getEngine()); 966 final int arbitraryNetError = -5; 967 TestUrlRequestCallback callback = 968 startAndWaitForComplete( 969 MockUrlRequestJobFactory.getMockUrlWithFailure( 970 FailurePhase.READ_ASYNC, arbitraryNetError)); 971 assertThat(callback.getResponseInfo()).hasHttpStatusCodeThat().isEqualTo(200); 972 assertThat(callback.getResponseInfo()).hasReceivedByteCountThat().isEqualTo(15); 973 assertThat(callback.mError).isNotNull(); 974 assertThat(((NetworkException) callback.mError).getCronetInternalErrorCode()) 975 .isEqualTo(arbitraryNetError); 976 assertThat(callback.mRedirectCount).isEqualTo(0); 977 assertThat(callback.mOnErrorCalled).isTrue(); 978 assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_FAILED); 979 } 980 981 /** Tests that request continues when client certificate is requested. */ 982 @Test 983 @SmallTest 984 @IgnoreFor( 985 implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, 986 reason = "crbug.com/1495309: Enable once we drop MockUrlRequestJobFactory") testMockClientCertificateRequested()987 public void testMockClientCertificateRequested() throws Exception { 988 mMockUrlRequestJobFactory = 989 new MockUrlRequestJobFactory(mTestRule.getTestFramework().getEngine()); 990 TestUrlRequestCallback callback = 991 startAndWaitForComplete( 992 MockUrlRequestJobFactory.getMockUrlForClientCertificateRequest()); 993 assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); 994 assertThat(callback.mResponseAsString).isEqualTo("data"); 995 assertThat(callback.mRedirectCount).isEqualTo(0); 996 assertThat(callback.mError).isNull(); 997 assertThat(callback.mOnErrorCalled).isFalse(); 998 } 999 1000 /** Tests that an SSL cert error will be reported via {@link UrlRequest.Callback#onFailed}. */ 1001 @Test 1002 @SmallTest 1003 @IgnoreFor( 1004 implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, 1005 reason = "crbug.com/1495309: Enable once we drop MockUrlRequestJobFactory") testMockSSLCertificateError()1006 public void testMockSSLCertificateError() throws Exception { 1007 mMockUrlRequestJobFactory = 1008 new MockUrlRequestJobFactory(mTestRule.getTestFramework().getEngine()); 1009 TestUrlRequestCallback callback = 1010 startAndWaitForComplete( 1011 MockUrlRequestJobFactory.getMockUrlForSSLCertificateError()); 1012 assertThat(callback.getResponseInfo()).isNull(); 1013 assertThat(callback.mOnErrorCalled).isTrue(); 1014 assertThat(callback.mError) 1015 .hasMessageThat() 1016 .contains("Exception in CronetUrlRequest: net::ERR_CERT_DATE_INVALID"); 1017 assertThat(((NetworkException) callback.mError).getCronetInternalErrorCode()) 1018 .isEqualTo(-201); 1019 assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_FAILED); 1020 } 1021 1022 /** Checks that the buffer is updated correctly, when starting at an offset. */ 1023 @Test 1024 @SmallTest 1025 @IgnoreFor( 1026 implementations = {CronetImplementation.FALLBACK}, 1027 reason = "No canonical exception to assert on") testSimpleGetBufferUpdates()1028 public void testSimpleGetBufferUpdates() throws Exception { 1029 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1030 callback.setAutoAdvance(false); 1031 // Since the default method is "GET", the expected response body is also 1032 // "GET". 1033 UrlRequest.Builder builder = 1034 mTestRule 1035 .getTestFramework() 1036 .getEngine() 1037 .newUrlRequestBuilder( 1038 NativeTestServer.getEchoMethodURL(), 1039 callback, 1040 callback.getExecutor()); 1041 UrlRequest urlRequest = builder.build(); 1042 urlRequest.start(); 1043 callback.waitForNextStep(); 1044 1045 ByteBuffer readBuffer = ByteBuffer.allocateDirect(5); 1046 readBuffer.put("FOR".getBytes()); 1047 assertThat(readBuffer.position()).isEqualTo(3); 1048 1049 // Read first two characters of the response ("GE"). It's theoretically 1050 // possible to need one read per character, though in practice, 1051 // shouldn't happen. 1052 while (callback.mResponseAsString.length() < 2) { 1053 assertThat(callback.isDone()).isFalse(); 1054 callback.startNextRead(urlRequest, readBuffer); 1055 callback.waitForNextStep(); 1056 } 1057 1058 // Make sure the two characters were read. 1059 assertThat(callback.mResponseAsString).isEqualTo("GE"); 1060 1061 // Check the contents of the entire buffer. The first 3 characters 1062 // should not have been changed, and the last two should be the first 1063 // two characters from the response. 1064 assertThat(bufferContentsToString(readBuffer, 0, 5)).isEqualTo("FORGE"); 1065 // The limit and position should be 5. 1066 assertThat(readBuffer.limit()).isEqualTo(5); 1067 assertThat(readBuffer.position()).isEqualTo(5); 1068 1069 assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_READ_COMPLETED); 1070 1071 // Start reading from position 3. Since the only remaining character 1072 // from the response is a "T", when the read completes, the buffer 1073 // should contain "FORTE", with a position() of 4 and a limit() of 5. 1074 readBuffer.position(3); 1075 callback.startNextRead(urlRequest, readBuffer); 1076 callback.waitForNextStep(); 1077 1078 // Make sure all three characters of the response have now been read. 1079 assertThat(callback.mResponseAsString).isEqualTo("GET"); 1080 1081 // Check the entire contents of the buffer. Only the third character 1082 // should have been modified. 1083 assertThat(bufferContentsToString(readBuffer, 0, 5)).isEqualTo("FORTE"); 1084 1085 // Make sure position and limit were updated correctly. 1086 assertThat(readBuffer.position()).isEqualTo(4); 1087 assertThat(readBuffer.limit()).isEqualTo(5); 1088 1089 assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_READ_COMPLETED); 1090 1091 // One more read attempt. The request should complete. 1092 readBuffer.position(1); 1093 readBuffer.limit(5); 1094 callback.startNextRead(urlRequest, readBuffer); 1095 callback.waitForNextStep(); 1096 1097 assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); 1098 assertThat(callback.mResponseAsString).isEqualTo("GET"); 1099 checkResponseInfo( 1100 callback.getResponseInfoWithChecks(), 1101 NativeTestServer.getEchoMethodURL(), 1102 200, 1103 "OK"); 1104 1105 // Check that buffer contents were not modified. 1106 assertThat(bufferContentsToString(readBuffer, 0, 5)).isEqualTo("FORTE"); 1107 1108 // Position should not have been modified, since nothing was read. 1109 assertThat(readBuffer.position()).isEqualTo(1); 1110 // Limit should be unchanged as always. 1111 assertThat(readBuffer.limit()).isEqualTo(5); 1112 1113 assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_SUCCEEDED); 1114 1115 // Make sure there are no other pending messages, which would trigger 1116 // asserts in TestUrlRequestCallback. 1117 testSimpleGet(); 1118 } 1119 1120 @Test 1121 @SmallTest testBadBuffers()1122 public void testBadBuffers() throws Exception { 1123 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1124 callback.setAutoAdvance(false); 1125 UrlRequest.Builder builder = 1126 mTestRule 1127 .getTestFramework() 1128 .getEngine() 1129 .newUrlRequestBuilder( 1130 NativeTestServer.getEchoMethodURL(), 1131 callback, 1132 callback.getExecutor()); 1133 UrlRequest urlRequest = builder.build(); 1134 urlRequest.start(); 1135 callback.waitForNextStep(); 1136 1137 // Try to read using a full buffer. 1138 ByteBuffer readBuffer = ByteBuffer.allocateDirect(4); 1139 readBuffer.put("full".getBytes()); 1140 IllegalArgumentException e = 1141 assertThrows(IllegalArgumentException.class, () -> urlRequest.read(readBuffer)); 1142 assertThat(e).hasMessageThat().isEqualTo("ByteBuffer is already full."); 1143 1144 // Try to read using a non-direct buffer. 1145 ByteBuffer readBuffer1 = ByteBuffer.allocate(5); 1146 e = assertThrows(IllegalArgumentException.class, () -> urlRequest.read(readBuffer1)); 1147 assertThat(e).hasMessageThat().isEqualTo("byteBuffer must be a direct ByteBuffer."); 1148 1149 // Finish the request with a direct ByteBuffer. 1150 callback.setAutoAdvance(true); 1151 ByteBuffer readBuffer2 = ByteBuffer.allocateDirect(5); 1152 urlRequest.read(readBuffer2); 1153 callback.blockForDone(); 1154 assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); 1155 assertThat(callback.mResponseAsString).isEqualTo("GET"); 1156 } 1157 1158 @Test 1159 @SmallTest testNoIoInCancel()1160 public void testNoIoInCancel() throws Exception { 1161 final TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1162 callback.setAutoAdvance(false); 1163 final UrlRequest urlRequest = 1164 mTestRule 1165 .getTestFramework() 1166 .getEngine() 1167 .newUrlRequestBuilder( 1168 NativeTestServer.getEchoHeaderURL("blah-header"), 1169 callback, 1170 callback.getExecutor()) 1171 .addHeader("blah-header", "blahblahblah") 1172 .build(); 1173 urlRequest.start(); 1174 callback.waitForNextStep(); 1175 callback.startNextRead(urlRequest, ByteBuffer.allocateDirect(4)); 1176 callback.waitForNextStep(); 1177 StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy(); 1178 StrictMode.setThreadPolicy( 1179 new StrictMode.ThreadPolicy.Builder() 1180 .detectAll() 1181 .penaltyDeath() 1182 .penaltyLog() 1183 .build()); 1184 try { 1185 urlRequest.cancel(); 1186 } finally { 1187 StrictMode.setThreadPolicy(oldPolicy); 1188 } 1189 callback.blockForDone(); 1190 assertThat(callback.mOnCanceledCalled).isEqualTo(true); 1191 } 1192 1193 @Test 1194 @SmallTest testUnexpectedReads()1195 public void testUnexpectedReads() throws Exception { 1196 final TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1197 callback.setAutoAdvance(false); 1198 final UrlRequest urlRequest = 1199 mTestRule 1200 .getTestFramework() 1201 .getEngine() 1202 .newUrlRequestBuilder( 1203 NativeTestServer.getRedirectURL(), callback, callback.getExecutor()) 1204 .build(); 1205 1206 // Try to read before starting request. 1207 assertThrows(IllegalStateException.class, () -> callback.startNextRead(urlRequest)); 1208 1209 // Verify reading right after start throws an assertion. Both must be 1210 // invoked on the Executor thread, to prevent receiving data until after 1211 // startNextRead has been invoked. 1212 Runnable startAndRead = 1213 new Runnable() { 1214 @Override 1215 public void run() { 1216 urlRequest.start(); 1217 assertThrows( 1218 IllegalStateException.class, 1219 () -> callback.startNextRead(urlRequest)); 1220 } 1221 }; 1222 callback.getExecutor().submit(startAndRead).get(); 1223 callback.waitForNextStep(); 1224 1225 assertThat(ResponseStep.ON_RECEIVED_REDIRECT).isEqualTo(callback.mResponseStep); 1226 // Try to read after the redirect. 1227 assertThrows(IllegalStateException.class, () -> callback.startNextRead(urlRequest)); 1228 urlRequest.followRedirect(); 1229 callback.waitForNextStep(); 1230 1231 assertThat(ResponseStep.ON_RESPONSE_STARTED).isEqualTo(callback.mResponseStep); 1232 assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); 1233 1234 while (!callback.isDone()) { 1235 Runnable readTwice = 1236 new Runnable() { 1237 @Override 1238 public void run() { 1239 callback.startNextRead(urlRequest); 1240 // Try to read again before the last read completes. 1241 assertThrows( 1242 IllegalStateException.class, 1243 () -> callback.startNextRead(urlRequest)); 1244 } 1245 }; 1246 callback.getExecutor().submit(readTwice).get(); 1247 callback.waitForNextStep(); 1248 } 1249 1250 assertThat(ResponseStep.ON_SUCCEEDED).isEqualTo(callback.mResponseStep); 1251 assertThat(callback.mResponseAsString).isEqualTo(NativeTestServer.SUCCESS_BODY); 1252 1253 // Try to read after request is complete. 1254 assertThrows(IllegalStateException.class, () -> callback.startNextRead(urlRequest)); 1255 } 1256 1257 @Test 1258 @SmallTest testUnexpectedFollowRedirects()1259 public void testUnexpectedFollowRedirects() throws Exception { 1260 final TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1261 callback.setAutoAdvance(false); 1262 final UrlRequest urlRequest = 1263 mTestRule 1264 .getTestFramework() 1265 .getEngine() 1266 .newUrlRequestBuilder( 1267 NativeTestServer.getRedirectURL(), callback, callback.getExecutor()) 1268 .build(); 1269 1270 // Try to follow a redirect before starting the request. 1271 assertThrows(IllegalStateException.class, urlRequest::followRedirect); 1272 1273 // Try to follow a redirect just after starting the request. Has to be 1274 // done on the executor thread to avoid a race. 1275 Runnable startAndRead = 1276 new Runnable() { 1277 @Override 1278 public void run() { 1279 urlRequest.start(); 1280 assertThrows(IllegalStateException.class, urlRequest::followRedirect); 1281 } 1282 }; 1283 callback.getExecutor().execute(startAndRead); 1284 callback.waitForNextStep(); 1285 1286 assertThat(ResponseStep.ON_RECEIVED_REDIRECT).isEqualTo(callback.mResponseStep); 1287 // Try to follow the redirect twice. Second attempt should fail. 1288 Runnable followRedirectTwice = 1289 new Runnable() { 1290 @Override 1291 public void run() { 1292 urlRequest.followRedirect(); 1293 assertThrows(IllegalStateException.class, urlRequest::followRedirect); 1294 } 1295 }; 1296 callback.getExecutor().execute(followRedirectTwice); 1297 callback.waitForNextStep(); 1298 1299 assertThat(ResponseStep.ON_RESPONSE_STARTED).isEqualTo(callback.mResponseStep); 1300 assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); 1301 1302 while (!callback.isDone()) { 1303 assertThrows(IllegalStateException.class, urlRequest::followRedirect); 1304 callback.startNextRead(urlRequest); 1305 callback.waitForNextStep(); 1306 } 1307 1308 assertThat(ResponseStep.ON_SUCCEEDED).isEqualTo(callback.mResponseStep); 1309 assertThat(callback.mResponseAsString).isEqualTo(NativeTestServer.SUCCESS_BODY); 1310 1311 // Try to follow redirect after request is complete. 1312 assertThrows(IllegalStateException.class, urlRequest::followRedirect); 1313 } 1314 1315 @Test 1316 @SmallTest testUploadSetDataProvider()1317 public void testUploadSetDataProvider() throws Exception { 1318 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1319 UrlRequest.Builder builder = 1320 mTestRule 1321 .getTestFramework() 1322 .getEngine() 1323 .newUrlRequestBuilder( 1324 NativeTestServer.getEchoBodyURL(), 1325 callback, 1326 callback.getExecutor()); 1327 1328 NullPointerException e = 1329 assertThrows( 1330 NullPointerException.class, 1331 () -> builder.setUploadDataProvider(null, callback.getExecutor())); 1332 assertThat(e).hasMessageThat().isEqualTo("Invalid UploadDataProvider."); 1333 1334 TestUploadDataProvider dataProvider = 1335 new TestUploadDataProvider( 1336 TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor()); 1337 builder.setUploadDataProvider(dataProvider, callback.getExecutor()); 1338 assertThrows(IllegalArgumentException.class, () -> builder.build().start()); 1339 } 1340 1341 @Test 1342 @SmallTest testUploadEmptyBodySync()1343 public void testUploadEmptyBodySync() throws Exception { 1344 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1345 UrlRequest.Builder builder = 1346 mTestRule 1347 .getTestFramework() 1348 .getEngine() 1349 .newUrlRequestBuilder( 1350 NativeTestServer.getEchoBodyURL(), 1351 callback, 1352 callback.getExecutor()); 1353 1354 TestUploadDataProvider dataProvider = 1355 new TestUploadDataProvider( 1356 TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor()); 1357 builder.setUploadDataProvider(dataProvider, callback.getExecutor()); 1358 builder.addHeader("Content-Type", "useless/string"); 1359 builder.build().start(); 1360 callback.blockForDone(); 1361 1362 assertThat(dataProvider.getUploadedLength()).isEqualTo(0); 1363 assertThat(dataProvider.getNumReadCalls()).isEqualTo(0); 1364 assertThat(dataProvider.getNumRewindCalls()).isEqualTo(0); 1365 1366 assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); 1367 assertThat(callback.mResponseAsString).isEmpty(); 1368 dataProvider.assertClosed(); 1369 } 1370 1371 @Test 1372 @SmallTest testUploadSync()1373 public void testUploadSync() throws Exception { 1374 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1375 UrlRequest.Builder builder = 1376 mTestRule 1377 .getTestFramework() 1378 .getEngine() 1379 .newUrlRequestBuilder( 1380 NativeTestServer.getEchoBodyURL(), 1381 callback, 1382 callback.getExecutor()); 1383 1384 TestUploadDataProvider dataProvider = 1385 new TestUploadDataProvider( 1386 TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor()); 1387 dataProvider.addRead("test".getBytes()); 1388 builder.setUploadDataProvider(dataProvider, callback.getExecutor()); 1389 builder.addHeader("Content-Type", "useless/string"); 1390 builder.build().start(); 1391 callback.blockForDone(); 1392 dataProvider.assertClosed(); 1393 1394 assertThat(dataProvider.getUploadedLength()).isEqualTo(4); 1395 assertThat(dataProvider.getNumReadCalls()).isEqualTo(1); 1396 assertThat(dataProvider.getNumRewindCalls()).isEqualTo(0); 1397 1398 assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); 1399 assertThat(callback.mResponseAsString).isEqualTo("test"); 1400 } 1401 1402 @Test 1403 @SmallTest testUploadMultiplePiecesSync()1404 public void testUploadMultiplePiecesSync() throws Exception { 1405 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1406 UrlRequest.Builder builder = 1407 mTestRule 1408 .getTestFramework() 1409 .getEngine() 1410 .newUrlRequestBuilder( 1411 NativeTestServer.getEchoBodyURL(), 1412 callback, 1413 callback.getExecutor()); 1414 1415 TestUploadDataProvider dataProvider = 1416 new TestUploadDataProvider( 1417 TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor()); 1418 dataProvider.addRead("Y".getBytes()); 1419 dataProvider.addRead("et ".getBytes()); 1420 dataProvider.addRead("another ".getBytes()); 1421 dataProvider.addRead("test".getBytes()); 1422 1423 builder.setUploadDataProvider(dataProvider, callback.getExecutor()); 1424 builder.addHeader("Content-Type", "useless/string"); 1425 builder.build().start(); 1426 callback.blockForDone(); 1427 dataProvider.assertClosed(); 1428 1429 assertThat(dataProvider.getUploadedLength()).isEqualTo(16); 1430 assertThat(dataProvider.getNumReadCalls()).isEqualTo(4); 1431 assertThat(dataProvider.getNumRewindCalls()).isEqualTo(0); 1432 1433 assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); 1434 assertThat(callback.mResponseAsString).isEqualTo("Yet another test"); 1435 } 1436 1437 @Test 1438 @SmallTest testUploadMultiplePiecesAsync()1439 public void testUploadMultiplePiecesAsync() throws Exception { 1440 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1441 UrlRequest.Builder builder = 1442 mTestRule 1443 .getTestFramework() 1444 .getEngine() 1445 .newUrlRequestBuilder( 1446 NativeTestServer.getEchoBodyURL(), 1447 callback, 1448 callback.getExecutor()); 1449 1450 TestUploadDataProvider dataProvider = 1451 new TestUploadDataProvider( 1452 TestUploadDataProvider.SuccessCallbackMode.ASYNC, callback.getExecutor()); 1453 dataProvider.addRead("Y".getBytes()); 1454 dataProvider.addRead("et ".getBytes()); 1455 dataProvider.addRead("another ".getBytes()); 1456 dataProvider.addRead("test".getBytes()); 1457 1458 builder.setUploadDataProvider(dataProvider, callback.getExecutor()); 1459 builder.addHeader("Content-Type", "useless/string"); 1460 builder.build().start(); 1461 callback.blockForDone(); 1462 dataProvider.assertClosed(); 1463 1464 assertThat(dataProvider.getUploadedLength()).isEqualTo(16); 1465 assertThat(dataProvider.getNumReadCalls()).isEqualTo(4); 1466 assertThat(dataProvider.getNumRewindCalls()).isEqualTo(0); 1467 1468 assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); 1469 assertThat(callback.mResponseAsString).isEqualTo("Yet another test"); 1470 } 1471 1472 @Test 1473 @SmallTest testUploadChangesDefaultMethod()1474 public void testUploadChangesDefaultMethod() throws Exception { 1475 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1476 UrlRequest.Builder builder = 1477 mTestRule 1478 .getTestFramework() 1479 .getEngine() 1480 .newUrlRequestBuilder( 1481 NativeTestServer.getEchoMethodURL(), 1482 callback, 1483 callback.getExecutor()); 1484 1485 TestUploadDataProvider dataProvider = 1486 new TestUploadDataProvider( 1487 TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor()); 1488 dataProvider.addRead("test".getBytes()); 1489 builder.setUploadDataProvider(dataProvider, callback.getExecutor()); 1490 builder.addHeader("Content-Type", "useless/string"); 1491 builder.build().start(); 1492 callback.blockForDone(); 1493 dataProvider.assertClosed(); 1494 1495 assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); 1496 assertThat(callback.mResponseAsString).isEqualTo("POST"); 1497 } 1498 1499 @Test 1500 @SmallTest testUploadWithSetMethod()1501 public void testUploadWithSetMethod() throws Exception { 1502 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1503 UrlRequest.Builder builder = 1504 mTestRule 1505 .getTestFramework() 1506 .getEngine() 1507 .newUrlRequestBuilder( 1508 NativeTestServer.getEchoMethodURL(), 1509 callback, 1510 callback.getExecutor()); 1511 1512 final String method = "PUT"; 1513 builder.setHttpMethod(method); 1514 1515 TestUploadDataProvider dataProvider = 1516 new TestUploadDataProvider( 1517 TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor()); 1518 dataProvider.addRead("test".getBytes()); 1519 builder.setUploadDataProvider(dataProvider, callback.getExecutor()); 1520 builder.addHeader("Content-Type", "useless/string"); 1521 builder.build().start(); 1522 callback.blockForDone(); 1523 dataProvider.assertClosed(); 1524 1525 assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); 1526 assertThat(callback.mResponseAsString).isEqualTo("PUT"); 1527 } 1528 1529 @Test 1530 @SmallTest testUploadRedirectSync()1531 public void testUploadRedirectSync() throws Exception { 1532 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1533 UrlRequest.Builder builder = 1534 mTestRule 1535 .getTestFramework() 1536 .getEngine() 1537 .newUrlRequestBuilder( 1538 NativeTestServer.getRedirectToEchoBody(), 1539 callback, 1540 callback.getExecutor()); 1541 1542 TestUploadDataProvider dataProvider = 1543 new TestUploadDataProvider( 1544 TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor()); 1545 dataProvider.addRead("test".getBytes()); 1546 builder.setUploadDataProvider(dataProvider, callback.getExecutor()); 1547 builder.addHeader("Content-Type", "useless/string"); 1548 builder.build().start(); 1549 callback.blockForDone(); 1550 dataProvider.assertClosed(); 1551 1552 // 1 read call before the rewind, 1 after. 1553 assertThat(dataProvider.getNumReadCalls()).isEqualTo(2); 1554 assertThat(dataProvider.getNumRewindCalls()).isEqualTo(1); 1555 1556 assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); 1557 assertThat(callback.mResponseAsString).isEqualTo("test"); 1558 } 1559 1560 @Test 1561 @SmallTest testUploadRedirectAsync()1562 public void testUploadRedirectAsync() throws Exception { 1563 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1564 UrlRequest.Builder builder = 1565 mTestRule 1566 .getTestFramework() 1567 .getEngine() 1568 .newUrlRequestBuilder( 1569 NativeTestServer.getRedirectToEchoBody(), 1570 callback, 1571 callback.getExecutor()); 1572 1573 TestUploadDataProvider dataProvider = 1574 new TestUploadDataProvider( 1575 TestUploadDataProvider.SuccessCallbackMode.ASYNC, callback.getExecutor()); 1576 dataProvider.addRead("test".getBytes()); 1577 builder.setUploadDataProvider(dataProvider, callback.getExecutor()); 1578 builder.addHeader("Content-Type", "useless/string"); 1579 builder.build().start(); 1580 dataProvider.assertClosed(); 1581 callback.blockForDone(); 1582 1583 // 1 read call before the rewind, 1 after. 1584 assertThat(dataProvider.getNumReadCalls()).isEqualTo(2); 1585 assertThat(dataProvider.getNumRewindCalls()).isEqualTo(1); 1586 1587 assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); 1588 assertThat(callback.mResponseAsString).isEqualTo("test"); 1589 } 1590 1591 @Test 1592 @SmallTest testUploadWithBadLength()1593 public void testUploadWithBadLength() throws Exception { 1594 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1595 UrlRequest.Builder builder = 1596 mTestRule 1597 .getTestFramework() 1598 .getEngine() 1599 .newUrlRequestBuilder( 1600 NativeTestServer.getEchoBodyURL(), 1601 callback, 1602 callback.getExecutor()); 1603 1604 TestUploadDataProvider dataProvider = 1605 new TestUploadDataProvider( 1606 TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor()) { 1607 @Override 1608 public long getLength() throws IOException { 1609 return 1; 1610 } 1611 1612 @Override 1613 public void read(UploadDataSink uploadDataSink, ByteBuffer byteBuffer) 1614 throws IOException { 1615 byteBuffer.put("12".getBytes()); 1616 uploadDataSink.onReadSucceeded(false); 1617 } 1618 }; 1619 builder.setUploadDataProvider(dataProvider, callback.getExecutor()); 1620 builder.addHeader("Content-Type", "useless/string"); 1621 builder.build().start(); 1622 callback.blockForDone(); 1623 dataProvider.assertClosed(); 1624 1625 assertThat(callback.mError) 1626 .hasMessageThat() 1627 .contains("Exception received from UploadDataProvider"); 1628 assertThat(callback.mError) 1629 .hasCauseThat() 1630 .hasMessageThat() 1631 .contains("Read upload data length 2 exceeds expected length 1"); 1632 assertThat(callback.getResponseInfo()).isNull(); 1633 } 1634 1635 @Test 1636 @SmallTest testUploadWithBadLengthBufferAligned()1637 public void testUploadWithBadLengthBufferAligned() throws Exception { 1638 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1639 UrlRequest.Builder builder = 1640 mTestRule 1641 .getTestFramework() 1642 .getEngine() 1643 .newUrlRequestBuilder( 1644 NativeTestServer.getEchoBodyURL(), 1645 callback, 1646 callback.getExecutor()); 1647 1648 TestUploadDataProvider dataProvider = 1649 new TestUploadDataProvider( 1650 TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor()) { 1651 @Override 1652 public long getLength() throws IOException { 1653 return 8191; 1654 } 1655 1656 @Override 1657 public void read(UploadDataSink uploadDataSink, ByteBuffer byteBuffer) 1658 throws IOException { 1659 byteBuffer.put("0123456789abcdef".getBytes()); 1660 uploadDataSink.onReadSucceeded(false); 1661 } 1662 }; 1663 builder.setUploadDataProvider(dataProvider, callback.getExecutor()); 1664 builder.addHeader("Content-Type", "useless/string"); 1665 builder.build().start(); 1666 callback.blockForDone(); 1667 dataProvider.assertClosed(); 1668 assertThat(callback.mError) 1669 .hasMessageThat() 1670 .contains("Exception received from UploadDataProvider"); 1671 assertThat(callback.mError) 1672 .hasCauseThat() 1673 .hasMessageThat() 1674 .contains("Read upload data length 8192 exceeds expected length 8191"); 1675 assertThat(callback.getResponseInfo()).isNull(); 1676 } 1677 1678 @Test 1679 @SmallTest testUploadReadFailSync()1680 public void testUploadReadFailSync() throws Exception { 1681 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1682 UrlRequest.Builder builder = 1683 mTestRule 1684 .getTestFramework() 1685 .getEngine() 1686 .newUrlRequestBuilder( 1687 NativeTestServer.getEchoBodyURL(), 1688 callback, 1689 callback.getExecutor()); 1690 1691 TestUploadDataProvider dataProvider = 1692 new TestUploadDataProvider( 1693 TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor()); 1694 dataProvider.setReadFailure(0, TestUploadDataProvider.FailMode.CALLBACK_SYNC); 1695 // This will never be read, but if the length is 0, read may never be 1696 // called. 1697 dataProvider.addRead("test".getBytes()); 1698 builder.setUploadDataProvider(dataProvider, callback.getExecutor()); 1699 builder.addHeader("Content-Type", "useless/string"); 1700 builder.build().start(); 1701 callback.blockForDone(); 1702 dataProvider.assertClosed(); 1703 1704 assertThat(dataProvider.getNumReadCalls()).isEqualTo(1); 1705 assertThat(dataProvider.getNumRewindCalls()).isEqualTo(0); 1706 1707 assertThat(callback.mError) 1708 .hasMessageThat() 1709 .contains("Exception received from UploadDataProvider"); 1710 assertThat(callback.mError).hasCauseThat().hasMessageThat().contains("Sync read failure"); 1711 assertThat(callback.getResponseInfo()).isNull(); 1712 } 1713 1714 @Test 1715 @SmallTest testUploadLengthFailSync()1716 public void testUploadLengthFailSync() throws Exception { 1717 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1718 UrlRequest.Builder builder = 1719 mTestRule 1720 .getTestFramework() 1721 .getEngine() 1722 .newUrlRequestBuilder( 1723 NativeTestServer.getEchoBodyURL(), 1724 callback, 1725 callback.getExecutor()); 1726 1727 TestUploadDataProvider dataProvider = 1728 new TestUploadDataProvider( 1729 TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor()); 1730 dataProvider.setLengthFailure(); 1731 // This will never be read, but if the length is 0, read may never be 1732 // called. 1733 dataProvider.addRead("test".getBytes()); 1734 builder.setUploadDataProvider(dataProvider, callback.getExecutor()); 1735 builder.addHeader("Content-Type", "useless/string"); 1736 builder.build().start(); 1737 callback.blockForDone(); 1738 dataProvider.assertClosed(); 1739 1740 assertThat(dataProvider.getNumReadCalls()).isEqualTo(0); 1741 assertThat(dataProvider.getNumRewindCalls()).isEqualTo(0); 1742 1743 assertThat(callback.mError) 1744 .hasMessageThat() 1745 .contains("Exception received from UploadDataProvider"); 1746 assertThat(callback.mError).hasCauseThat().hasMessageThat().contains("Sync length failure"); 1747 assertThat(callback.getResponseInfo()).isNull(); 1748 } 1749 1750 @Test 1751 @SmallTest testUploadReadFailAsync()1752 public void testUploadReadFailAsync() throws Exception { 1753 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1754 UrlRequest.Builder builder = 1755 mTestRule 1756 .getTestFramework() 1757 .getEngine() 1758 .newUrlRequestBuilder( 1759 NativeTestServer.getEchoBodyURL(), 1760 callback, 1761 callback.getExecutor()); 1762 1763 TestUploadDataProvider dataProvider = 1764 new TestUploadDataProvider( 1765 TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor()); 1766 dataProvider.setReadFailure(0, TestUploadDataProvider.FailMode.CALLBACK_ASYNC); 1767 // This will never be read, but if the length is 0, read may never be 1768 // called. 1769 dataProvider.addRead("test".getBytes()); 1770 builder.setUploadDataProvider(dataProvider, callback.getExecutor()); 1771 builder.addHeader("Content-Type", "useless/string"); 1772 builder.build().start(); 1773 callback.blockForDone(); 1774 dataProvider.assertClosed(); 1775 1776 assertThat(dataProvider.getNumReadCalls()).isEqualTo(1); 1777 assertThat(dataProvider.getNumRewindCalls()).isEqualTo(0); 1778 1779 assertThat(callback.mError) 1780 .hasMessageThat() 1781 .contains("Exception received from UploadDataProvider"); 1782 assertThat(callback.mError).hasCauseThat().hasMessageThat().contains("Async read failure"); 1783 assertThat(callback.getResponseInfo()).isNull(); 1784 } 1785 1786 /** This test uses a direct executor for upload, and non direct for callbacks */ 1787 @Test 1788 @SmallTest testDirectExecutorUploadProhibitedByDefault()1789 public void testDirectExecutorUploadProhibitedByDefault() throws Exception { 1790 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1791 Executor myExecutor = 1792 new Executor() { 1793 @Override 1794 public void execute(Runnable command) { 1795 command.run(); 1796 } 1797 }; 1798 UrlRequest.Builder builder = 1799 mTestRule 1800 .getTestFramework() 1801 .getEngine() 1802 .newUrlRequestBuilder( 1803 NativeTestServer.getEchoBodyURL(), 1804 callback, 1805 callback.getExecutor()); 1806 1807 TestUploadDataProvider dataProvider = 1808 new TestUploadDataProvider( 1809 TestUploadDataProvider.SuccessCallbackMode.SYNC, myExecutor); 1810 // This will never be read, but if the length is 0, read may never be 1811 // called. 1812 dataProvider.addRead("test".getBytes()); 1813 builder.setUploadDataProvider(dataProvider, myExecutor); 1814 builder.addHeader("Content-Type", "useless/string"); 1815 builder.build().start(); 1816 callback.blockForDone(); 1817 1818 assertThat(dataProvider.getNumReadCalls()).isEqualTo(0); 1819 assertThat(dataProvider.getNumRewindCalls()).isEqualTo(0); 1820 1821 assertThat(callback.mError) 1822 .hasMessageThat() 1823 .contains("Exception received from UploadDataProvider"); 1824 assertThat(callback.mError) 1825 .hasCauseThat() 1826 .hasMessageThat() 1827 .contains("Inline execution is prohibited for this request"); 1828 assertThat(callback.getResponseInfo()).isNull(); 1829 } 1830 1831 /** This test uses a direct executor for callbacks, and non direct for upload */ 1832 @Test 1833 @SmallTest 1834 @IgnoreFor( 1835 implementations = {CronetImplementation.AOSP_PLATFORM}, 1836 reason = "b/311163531: Re-enable once HttpEngine propagates UploadDataProvider#close") testDirectExecutorProhibitedByDefault()1837 public void testDirectExecutorProhibitedByDefault() throws Exception { 1838 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1839 Executor myExecutor = 1840 new Executor() { 1841 @Override 1842 public void execute(Runnable command) { 1843 command.run(); 1844 } 1845 }; 1846 UrlRequest.Builder builder = 1847 mTestRule 1848 .getTestFramework() 1849 .getEngine() 1850 .newUrlRequestBuilder( 1851 NativeTestServer.getEchoBodyURL(), callback, myExecutor); 1852 1853 TestUploadDataProvider dataProvider = 1854 new TestUploadDataProvider( 1855 TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor()); 1856 // This will never be read, but if the length is 0, read may never be 1857 // called. 1858 dataProvider.addRead("test".getBytes()); 1859 builder.setUploadDataProvider(dataProvider, callback.getExecutor()); 1860 builder.addHeader("Content-Type", "useless/string"); 1861 builder.build().start(); 1862 callback.blockForDone(); 1863 1864 assertThat(dataProvider.getNumReadCalls()).isEqualTo(1); 1865 assertThat(dataProvider.getNumRewindCalls()).isEqualTo(0); 1866 1867 assertThat(callback.mError).hasMessageThat().contains("Exception posting task to executor"); 1868 assertThat(callback.mError) 1869 .hasCauseThat() 1870 .hasMessageThat() 1871 .contains("Inline execution is prohibited for this request"); 1872 assertThat(callback.getResponseInfo()).isNull(); 1873 dataProvider.assertClosed(); 1874 } 1875 1876 @Test 1877 @SmallTest testDirectExecutorAllowed()1878 public void testDirectExecutorAllowed() throws Exception { 1879 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1880 callback.setAllowDirectExecutor(true); 1881 Executor myExecutor = 1882 new Executor() { 1883 @Override 1884 public void execute(Runnable command) { 1885 command.run(); 1886 } 1887 }; 1888 UrlRequest.Builder builder = 1889 mTestRule 1890 .getTestFramework() 1891 .getEngine() 1892 .newUrlRequestBuilder( 1893 NativeTestServer.getEchoBodyURL(), callback, myExecutor); 1894 UploadDataProvider dataProvider = UploadDataProviders.create("test".getBytes()); 1895 builder.setUploadDataProvider(dataProvider, myExecutor); 1896 builder.addHeader("Content-Type", "useless/string"); 1897 builder.allowDirectExecutor(); 1898 builder.build().start(); 1899 callback.blockForDone(); 1900 1901 if (callback.mOnErrorCalled) { 1902 throw callback.mError; 1903 } 1904 1905 assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); 1906 assertThat(callback.mResponseAsString).isEqualTo("test"); 1907 } 1908 1909 @Test 1910 @SmallTest testUploadReadFailThrown()1911 public void testUploadReadFailThrown() throws Exception { 1912 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1913 UrlRequest.Builder builder = 1914 mTestRule 1915 .getTestFramework() 1916 .getEngine() 1917 .newUrlRequestBuilder( 1918 NativeTestServer.getEchoBodyURL(), 1919 callback, 1920 callback.getExecutor()); 1921 1922 TestUploadDataProvider dataProvider = 1923 new TestUploadDataProvider( 1924 TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor()); 1925 dataProvider.setReadFailure(0, TestUploadDataProvider.FailMode.THROWN); 1926 // This will never be read, but if the length is 0, read may never be 1927 // called. 1928 dataProvider.addRead("test".getBytes()); 1929 builder.setUploadDataProvider(dataProvider, callback.getExecutor()); 1930 builder.addHeader("Content-Type", "useless/string"); 1931 builder.build().start(); 1932 callback.blockForDone(); 1933 dataProvider.assertClosed(); 1934 1935 assertThat(dataProvider.getNumReadCalls()).isEqualTo(1); 1936 assertThat(dataProvider.getNumRewindCalls()).isEqualTo(0); 1937 1938 assertThat(callback.mError) 1939 .hasMessageThat() 1940 .contains("Exception received from UploadDataProvider"); 1941 assertThat(callback.mError).hasCauseThat().hasMessageThat().contains("Thrown read failure"); 1942 assertThat(callback.getResponseInfo()).isNull(); 1943 } 1944 1945 @Test 1946 @SmallTest testUploadRewindFailSync()1947 public void testUploadRewindFailSync() throws Exception { 1948 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1949 UrlRequest.Builder builder = 1950 mTestRule 1951 .getTestFramework() 1952 .getEngine() 1953 .newUrlRequestBuilder( 1954 NativeTestServer.getRedirectToEchoBody(), 1955 callback, 1956 callback.getExecutor()); 1957 1958 TestUploadDataProvider dataProvider = 1959 new TestUploadDataProvider( 1960 TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor()); 1961 dataProvider.setRewindFailure(TestUploadDataProvider.FailMode.CALLBACK_SYNC); 1962 dataProvider.addRead("test".getBytes()); 1963 builder.setUploadDataProvider(dataProvider, callback.getExecutor()); 1964 builder.addHeader("Content-Type", "useless/string"); 1965 builder.build().start(); 1966 callback.blockForDone(); 1967 dataProvider.assertClosed(); 1968 1969 assertThat(dataProvider.getNumReadCalls()).isEqualTo(1); 1970 assertThat(dataProvider.getNumRewindCalls()).isEqualTo(1); 1971 1972 assertThat(callback.mError) 1973 .hasMessageThat() 1974 .contains("Exception received from UploadDataProvider"); 1975 assertThat(callback.mError).hasCauseThat().hasMessageThat().contains("Sync rewind failure"); 1976 assertThat(callback.getResponseInfo()).isNull(); 1977 } 1978 1979 @Test 1980 @SmallTest testUploadRewindFailAsync()1981 public void testUploadRewindFailAsync() throws Exception { 1982 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1983 UrlRequest.Builder builder = 1984 mTestRule 1985 .getTestFramework() 1986 .getEngine() 1987 .newUrlRequestBuilder( 1988 NativeTestServer.getRedirectToEchoBody(), 1989 callback, 1990 callback.getExecutor()); 1991 1992 TestUploadDataProvider dataProvider = 1993 new TestUploadDataProvider( 1994 TestUploadDataProvider.SuccessCallbackMode.ASYNC, callback.getExecutor()); 1995 dataProvider.setRewindFailure(TestUploadDataProvider.FailMode.CALLBACK_ASYNC); 1996 dataProvider.addRead("test".getBytes()); 1997 builder.setUploadDataProvider(dataProvider, callback.getExecutor()); 1998 builder.addHeader("Content-Type", "useless/string"); 1999 builder.build().start(); 2000 callback.blockForDone(); 2001 dataProvider.assertClosed(); 2002 2003 assertThat(dataProvider.getNumReadCalls()).isEqualTo(1); 2004 assertThat(dataProvider.getNumRewindCalls()).isEqualTo(1); 2005 2006 assertThat(callback.mError) 2007 .hasMessageThat() 2008 .contains("Exception received from UploadDataProvider"); 2009 assertThat(callback.mError) 2010 .hasCauseThat() 2011 .hasMessageThat() 2012 .contains("Async rewind failure"); 2013 assertThat(callback.getResponseInfo()).isNull(); 2014 } 2015 2016 @Test 2017 @SmallTest testUploadRewindFailThrown()2018 public void testUploadRewindFailThrown() throws Exception { 2019 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 2020 UrlRequest.Builder builder = 2021 mTestRule 2022 .getTestFramework() 2023 .getEngine() 2024 .newUrlRequestBuilder( 2025 NativeTestServer.getRedirectToEchoBody(), 2026 callback, 2027 callback.getExecutor()); 2028 2029 TestUploadDataProvider dataProvider = 2030 new TestUploadDataProvider( 2031 TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor()); 2032 dataProvider.setRewindFailure(TestUploadDataProvider.FailMode.THROWN); 2033 dataProvider.addRead("test".getBytes()); 2034 builder.setUploadDataProvider(dataProvider, callback.getExecutor()); 2035 builder.addHeader("Content-Type", "useless/string"); 2036 builder.build().start(); 2037 callback.blockForDone(); 2038 dataProvider.assertClosed(); 2039 2040 assertThat(dataProvider.getNumReadCalls()).isEqualTo(1); 2041 assertThat(dataProvider.getNumRewindCalls()).isEqualTo(1); 2042 2043 assertThat(callback.mError) 2044 .hasMessageThat() 2045 .contains("Exception received from UploadDataProvider"); 2046 assertThat(callback.mError) 2047 .hasCauseThat() 2048 .hasMessageThat() 2049 .contains("Thrown rewind failure"); 2050 assertThat(callback.getResponseInfo()).isNull(); 2051 } 2052 2053 @Test 2054 @SmallTest testUploadChunked()2055 public void testUploadChunked() throws Exception { 2056 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 2057 UrlRequest.Builder builder = 2058 mTestRule 2059 .getTestFramework() 2060 .getEngine() 2061 .newUrlRequestBuilder( 2062 NativeTestServer.getEchoBodyURL(), 2063 callback, 2064 callback.getExecutor()); 2065 2066 TestUploadDataProvider dataProvider = 2067 new TestUploadDataProvider( 2068 TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor()); 2069 dataProvider.addRead("test hello".getBytes()); 2070 dataProvider.setChunked(true); 2071 builder.setUploadDataProvider(dataProvider, callback.getExecutor()); 2072 builder.addHeader("Content-Type", "useless/string"); 2073 2074 assertThat(dataProvider.getUploadedLength()).isEqualTo(-1); 2075 2076 builder.build().start(); 2077 callback.blockForDone(); 2078 dataProvider.assertClosed(); 2079 2080 // 1 read call for one data chunk. 2081 assertThat(dataProvider.getNumReadCalls()).isEqualTo(1); 2082 assertThat(callback.mResponseAsString).isEqualTo("test hello"); 2083 } 2084 2085 @Test 2086 @SmallTest testUploadChunkedLastReadZeroLengthBody()2087 public void testUploadChunkedLastReadZeroLengthBody() throws Exception { 2088 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 2089 UrlRequest.Builder builder = 2090 mTestRule 2091 .getTestFramework() 2092 .getEngine() 2093 .newUrlRequestBuilder( 2094 NativeTestServer.getEchoBodyURL(), 2095 callback, 2096 callback.getExecutor()); 2097 2098 TestUploadDataProvider dataProvider = 2099 new TestUploadDataProvider( 2100 TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor()); 2101 // Add 3 reads. The last read has a 0-length body. 2102 dataProvider.addRead("hello there".getBytes()); 2103 dataProvider.addRead("!".getBytes()); 2104 dataProvider.addRead("".getBytes()); 2105 dataProvider.setChunked(true); 2106 builder.setUploadDataProvider(dataProvider, callback.getExecutor()); 2107 builder.addHeader("Content-Type", "useless/string"); 2108 2109 assertThat(dataProvider.getUploadedLength()).isEqualTo(-1); 2110 2111 builder.build().start(); 2112 callback.blockForDone(); 2113 dataProvider.assertClosed(); 2114 2115 // 2 read call for the first two data chunks, and 1 for final chunk. 2116 assertThat(dataProvider.getNumReadCalls()).isEqualTo(3); 2117 assertThat(callback.mResponseAsString).isEqualTo("hello there!"); 2118 } 2119 2120 // Test where an upload fails without ever initializing the 2121 // UploadDataStream, because it can't connect to the server. 2122 @Test 2123 @SmallTest testUploadFailsWithoutInitializingStream()2124 public void testUploadFailsWithoutInitializingStream() throws Exception { 2125 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 2126 // The port for PTP will always refuse a TCP connection 2127 UrlRequest.Builder builder = 2128 mTestRule 2129 .getTestFramework() 2130 .getEngine() 2131 .newUrlRequestBuilder( 2132 "http://127.0.0.1:319", callback, callback.getExecutor()); 2133 2134 TestUploadDataProvider dataProvider = 2135 new TestUploadDataProvider( 2136 TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor()); 2137 dataProvider.addRead("test".getBytes()); 2138 builder.setUploadDataProvider(dataProvider, callback.getExecutor()); 2139 builder.addHeader("Content-Type", "useless/string"); 2140 builder.build().start(); 2141 callback.blockForDone(); 2142 dataProvider.assertClosed(); 2143 2144 assertThat(callback.getResponseInfo()).isNull(); 2145 if (mTestRule.testingJavaImpl()) { 2146 assertThat(callback.mError).hasCauseThat().isInstanceOf(ConnectException.class); 2147 } else { 2148 assertThat(callback.mError) 2149 .hasMessageThat() 2150 .contains("Exception in CronetUrlRequest: net::ERR_CONNECTION_REFUSED"); 2151 } 2152 } 2153 throwOrCancel( FailureType failureType, ResponseStep failureStep, boolean expectResponseInfo, boolean expectError)2154 private void throwOrCancel( 2155 FailureType failureType, 2156 ResponseStep failureStep, 2157 boolean expectResponseInfo, 2158 boolean expectError) { 2159 if (Log.isLoggable("TESTING", Log.VERBOSE)) { 2160 Log.v("TESTING", "Testing " + failureType + " during " + failureStep); 2161 } 2162 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 2163 callback.setFailure(failureType, failureStep); 2164 UrlRequest.Builder builder = 2165 mTestRule 2166 .getTestFramework() 2167 .getEngine() 2168 .newUrlRequestBuilder( 2169 NativeTestServer.getRedirectURL(), 2170 callback, 2171 callback.getExecutor()); 2172 UrlRequest urlRequest = builder.build(); 2173 urlRequest.start(); 2174 callback.blockForDone(); 2175 // Wait for all posted tasks to be executed to ensure there is no unhandled exception. 2176 callback.shutdownExecutorAndWait(); 2177 assertThat(callback.mRedirectCount).isEqualTo(1); 2178 if (failureType == FailureType.CANCEL_SYNC || failureType == FailureType.CANCEL_ASYNC) { 2179 assertResponseStepCanceled(callback); 2180 } else if (failureType == FailureType.THROW_SYNC) { 2181 assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_FAILED); 2182 } 2183 assertThat(urlRequest.isDone()).isTrue(); 2184 assertThat(callback.getResponseInfo() != null).isEqualTo(expectResponseInfo); 2185 assertThat(callback.mError != null).isEqualTo(expectError); 2186 assertThat(callback.mOnErrorCalled).isEqualTo(expectError); 2187 // When failureType is FailureType.CANCEL_ASYNC_WITHOUT_PAUSE and failureStep is 2188 // ResponseStep.ON_READ_COMPLETED, there might be an onSucceeded() task already posted. If 2189 // that's the case, onCanceled() will not be invoked. See crbug.com/657415. 2190 if (!(failureType == FailureType.CANCEL_ASYNC_WITHOUT_PAUSE 2191 && failureStep == ResponseStep.ON_READ_COMPLETED)) { 2192 assertThat(callback.mOnCanceledCalled) 2193 .isEqualTo( 2194 failureType == FailureType.CANCEL_SYNC 2195 || failureType == FailureType.CANCEL_ASYNC 2196 || failureType == FailureType.CANCEL_ASYNC_WITHOUT_PAUSE); 2197 } 2198 } 2199 2200 @Test 2201 @SmallTest testFailures()2202 public void testFailures() throws Exception { 2203 throwOrCancel(FailureType.CANCEL_SYNC, ResponseStep.ON_RECEIVED_REDIRECT, false, false); 2204 throwOrCancel(FailureType.CANCEL_ASYNC, ResponseStep.ON_RECEIVED_REDIRECT, false, false); 2205 throwOrCancel( 2206 FailureType.CANCEL_ASYNC_WITHOUT_PAUSE, 2207 ResponseStep.ON_RECEIVED_REDIRECT, 2208 false, 2209 false); 2210 throwOrCancel(FailureType.THROW_SYNC, ResponseStep.ON_RECEIVED_REDIRECT, false, true); 2211 2212 throwOrCancel(FailureType.CANCEL_SYNC, ResponseStep.ON_RESPONSE_STARTED, true, false); 2213 throwOrCancel(FailureType.CANCEL_ASYNC, ResponseStep.ON_RESPONSE_STARTED, true, false); 2214 throwOrCancel( 2215 FailureType.CANCEL_ASYNC_WITHOUT_PAUSE, 2216 ResponseStep.ON_RESPONSE_STARTED, 2217 true, 2218 false); 2219 throwOrCancel(FailureType.THROW_SYNC, ResponseStep.ON_RESPONSE_STARTED, true, true); 2220 2221 throwOrCancel(FailureType.CANCEL_SYNC, ResponseStep.ON_READ_COMPLETED, true, false); 2222 throwOrCancel(FailureType.CANCEL_ASYNC, ResponseStep.ON_READ_COMPLETED, true, false); 2223 throwOrCancel( 2224 FailureType.CANCEL_ASYNC_WITHOUT_PAUSE, 2225 ResponseStep.ON_READ_COMPLETED, 2226 true, 2227 false); 2228 throwOrCancel(FailureType.THROW_SYNC, ResponseStep.ON_READ_COMPLETED, true, true); 2229 } 2230 2231 @Test 2232 @SmallTest testThrowOrCancelInOnSucceeded()2233 public void testThrowOrCancelInOnSucceeded() { 2234 FailureType[] testTypes = 2235 new FailureType[] { 2236 FailureType.THROW_SYNC, FailureType.CANCEL_SYNC, FailureType.CANCEL_ASYNC 2237 }; 2238 for (FailureType type : testTypes) { 2239 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 2240 callback.setFailure(type, ResponseStep.ON_SUCCEEDED); 2241 UrlRequest.Builder builder = 2242 mTestRule 2243 .getTestFramework() 2244 .getEngine() 2245 .newUrlRequestBuilder( 2246 NativeTestServer.getEchoMethodURL(), 2247 callback, 2248 callback.getExecutor()); 2249 UrlRequest urlRequest = builder.build(); 2250 urlRequest.start(); 2251 callback.blockForDone(); 2252 // Wait for all posted tasks to be executed to ensure there is no unhandled exception. 2253 callback.shutdownExecutorAndWait(); 2254 assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_SUCCEEDED); 2255 assertThat(urlRequest.isDone()).isTrue(); 2256 assertThat(callback.getResponseInfoWithChecks()).isNotNull(); 2257 assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); 2258 assertThat(callback.mResponseAsString).isEqualTo("GET"); 2259 } 2260 } 2261 2262 @Test 2263 @SmallTest testThrowOrCancelInOnFailed()2264 public void testThrowOrCancelInOnFailed() { 2265 FailureType[] testTypes = 2266 new FailureType[] { 2267 FailureType.THROW_SYNC, FailureType.CANCEL_SYNC, FailureType.CANCEL_ASYNC 2268 }; 2269 for (FailureType type : testTypes) { 2270 String url = NativeTestServer.getEchoBodyURL(); 2271 // Shut down NativeTestServer so request will fail. 2272 NativeTestServer.shutdownNativeTestServer(); 2273 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 2274 callback.setFailure(type, ResponseStep.ON_FAILED); 2275 UrlRequest.Builder builder = 2276 mTestRule 2277 .getTestFramework() 2278 .getEngine() 2279 .newUrlRequestBuilder(url, callback, callback.getExecutor()); 2280 UrlRequest urlRequest = builder.build(); 2281 urlRequest.start(); 2282 callback.blockForDone(); 2283 // Wait for all posted tasks to be executed to ensure there is no unhandled exception. 2284 callback.shutdownExecutorAndWait(); 2285 assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_FAILED); 2286 assertThat(callback.mOnErrorCalled).isTrue(); 2287 assertThat(callback.mError).isNotNull(); 2288 assertThat(urlRequest.isDone()).isTrue(); 2289 // Start NativeTestServer again to run the test for a second time. 2290 assertThat( 2291 NativeTestServer.startNativeTestServer( 2292 mTestRule.getTestFramework().getContext())) 2293 .isTrue(); 2294 } 2295 } 2296 2297 @Test 2298 @SmallTest testThrowOrCancelInOnCanceled()2299 public void testThrowOrCancelInOnCanceled() { 2300 FailureType[] testTypes = 2301 new FailureType[] { 2302 FailureType.THROW_SYNC, FailureType.CANCEL_SYNC, FailureType.CANCEL_ASYNC 2303 }; 2304 for (FailureType type : testTypes) { 2305 TestUrlRequestCallback callback = 2306 new TestUrlRequestCallback() { 2307 @Override 2308 public void onResponseStarted(UrlRequest request, UrlResponseInfo info) { 2309 super.onResponseStarted(request, info); 2310 request.cancel(); 2311 } 2312 }; 2313 callback.setFailure(type, ResponseStep.ON_CANCELED); 2314 UrlRequest.Builder builder = 2315 mTestRule 2316 .getTestFramework() 2317 .getEngine() 2318 .newUrlRequestBuilder( 2319 NativeTestServer.getEchoBodyURL(), 2320 callback, 2321 callback.getExecutor()); 2322 UrlRequest urlRequest = builder.build(); 2323 urlRequest.start(); 2324 callback.blockForDone(); 2325 // Wait for all posted tasks to be executed to ensure there is no unhandled exception. 2326 callback.shutdownExecutorAndWait(); 2327 assertResponseStepCanceled(callback); 2328 assertThat(urlRequest.isDone()).isTrue(); 2329 assertThat(callback.getResponseInfoWithChecks()).isNotNull(); 2330 assertThat(callback.mOnCanceledCalled).isTrue(); 2331 } 2332 } 2333 2334 @Test 2335 @SmallTest 2336 @IgnoreFor( 2337 implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, 2338 reason = "crbug.com/1494846: tests native-specific internals") testExecutorShutdown()2339 public void testExecutorShutdown() { 2340 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 2341 2342 callback.setAutoAdvance(false); 2343 UrlRequest.Builder builder = 2344 mTestRule 2345 .getTestFramework() 2346 .getEngine() 2347 .newUrlRequestBuilder( 2348 NativeTestServer.getEchoBodyURL(), 2349 callback, 2350 callback.getExecutor()); 2351 CronetUrlRequest urlRequest = (CronetUrlRequest) builder.build(); 2352 urlRequest.start(); 2353 callback.waitForNextStep(); 2354 assertThat(callback.isDone()).isFalse(); 2355 assertThat(urlRequest.isDone()).isFalse(); 2356 2357 final ConditionVariable requestDestroyed = new ConditionVariable(false); 2358 urlRequest.setOnDestroyedCallbackForTesting( 2359 new Runnable() { 2360 @Override 2361 public void run() { 2362 requestDestroyed.open(); 2363 } 2364 }); 2365 2366 // Shutdown the executor, so posting the task will throw an exception. 2367 callback.shutdownExecutor(); 2368 ByteBuffer readBuffer = ByteBuffer.allocateDirect(5); 2369 urlRequest.read(readBuffer); 2370 // Callback will never be called again because executor is shutdown, 2371 // but request will be destroyed from network thread. 2372 requestDestroyed.block(); 2373 2374 assertThat(callback.isDone()).isFalse(); 2375 assertThat(urlRequest.isDone()).isTrue(); 2376 } 2377 2378 @Test 2379 @SmallTest testUploadExecutorShutdown()2380 public void testUploadExecutorShutdown() throws Exception { 2381 class HangingUploadDataProvider extends UploadDataProvider { 2382 UploadDataSink mUploadDataSink; 2383 ByteBuffer mByteBuffer; 2384 ConditionVariable mReadCalled = new ConditionVariable(false); 2385 2386 @Override 2387 public long getLength() { 2388 return 69; 2389 } 2390 2391 @Override 2392 public void read(final UploadDataSink uploadDataSink, final ByteBuffer byteBuffer) { 2393 mUploadDataSink = uploadDataSink; 2394 mByteBuffer = byteBuffer; 2395 mReadCalled.open(); 2396 } 2397 2398 @Override 2399 public void rewind(final UploadDataSink uploadDataSink) {} 2400 } 2401 2402 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 2403 UrlRequest.Builder builder = 2404 mTestRule 2405 .getTestFramework() 2406 .getEngine() 2407 .newUrlRequestBuilder( 2408 NativeTestServer.getEchoBodyURL(), 2409 callback, 2410 callback.getExecutor()); 2411 2412 ExecutorService uploadExecutor = Executors.newSingleThreadExecutor(); 2413 HangingUploadDataProvider dataProvider = new HangingUploadDataProvider(); 2414 builder.setUploadDataProvider(dataProvider, uploadExecutor); 2415 builder.addHeader("Content-Type", "useless/string"); 2416 UrlRequest urlRequest = builder.build(); 2417 urlRequest.start(); 2418 // Wait for read to be called on executor. 2419 dataProvider.mReadCalled.block(); 2420 // Shutdown the executor, so posting next task will throw an exception. 2421 uploadExecutor.shutdown(); 2422 // Continue the upload. 2423 dataProvider.mByteBuffer.putInt(42); 2424 dataProvider.mUploadDataSink.onReadSucceeded(false); 2425 // Callback.onFailed will be called on request executor even though upload 2426 // executor is shutdown. 2427 callback.blockForDone(); 2428 assertThat(callback.isDone()).isTrue(); 2429 assertThat(callback.mOnErrorCalled).isTrue(); 2430 assertThat(callback.mError) 2431 .hasMessageThat() 2432 .contains("Exception received from UploadDataProvider"); 2433 assertThat(urlRequest.isDone()).isTrue(); 2434 } 2435 2436 /** A TestUrlRequestCallback that shuts down executor upon receiving onSucceeded callback. */ 2437 private static class QuitOnSuccessCallback extends TestUrlRequestCallback { 2438 @Override onSucceeded(UrlRequest request, UrlResponseInfo info)2439 public void onSucceeded(UrlRequest request, UrlResponseInfo info) { 2440 // Stop accepting new tasks. 2441 shutdownExecutor(); 2442 super.onSucceeded(request, info); 2443 } 2444 } 2445 2446 @Test 2447 @SmallTest 2448 @IgnoreFor( 2449 implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, 2450 reason = "crbug.com/1494846: tests native-specific internals") 2451 // Regression test for crbug.com/564946. testDestroyUploadDataStreamAdapterOnSucceededCallback()2452 public void testDestroyUploadDataStreamAdapterOnSucceededCallback() throws Exception { 2453 TestUrlRequestCallback callback = new QuitOnSuccessCallback(); 2454 UrlRequest.Builder builder = 2455 mTestRule 2456 .getTestFramework() 2457 .getEngine() 2458 .newUrlRequestBuilder( 2459 NativeTestServer.getEchoBodyURL(), 2460 callback, 2461 callback.getExecutor()); 2462 2463 TestUploadDataProvider dataProvider = 2464 new TestUploadDataProvider( 2465 TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor()); 2466 builder.setUploadDataProvider(dataProvider, callback.getExecutor()); 2467 builder.addHeader("Content-Type", "useless/string"); 2468 CronetUrlRequest request = (CronetUrlRequest) builder.build(); 2469 final ConditionVariable uploadDataStreamAdapterDestroyed = new ConditionVariable(); 2470 request.setOnDestroyedUploadCallbackForTesting( 2471 new Runnable() { 2472 @Override 2473 public void run() { 2474 uploadDataStreamAdapterDestroyed.open(); 2475 } 2476 }); 2477 2478 request.start(); 2479 uploadDataStreamAdapterDestroyed.block(); 2480 callback.blockForDone(); 2481 2482 assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); 2483 assertThat(callback.mResponseAsString).isEmpty(); 2484 } 2485 2486 /* 2487 * Verifies error codes are passed through correctly. 2488 */ 2489 @Test 2490 @SmallTest 2491 @IgnoreFor( 2492 implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, 2493 reason = "crbug.com/1494846: tests native-specific internals") testErrorCodes()2494 public void testErrorCodes() throws Exception { 2495 mMockUrlRequestJobFactory = 2496 new MockUrlRequestJobFactory(mTestRule.getTestFramework().getEngine()); 2497 checkSpecificErrorCode( 2498 -105, NetworkException.ERROR_HOSTNAME_NOT_RESOLVED, "NAME_NOT_RESOLVED", false); 2499 checkSpecificErrorCode( 2500 -106, NetworkException.ERROR_INTERNET_DISCONNECTED, "INTERNET_DISCONNECTED", false); 2501 checkSpecificErrorCode( 2502 -21, NetworkException.ERROR_NETWORK_CHANGED, "NETWORK_CHANGED", true); 2503 checkSpecificErrorCode( 2504 -100, NetworkException.ERROR_CONNECTION_CLOSED, "CONNECTION_CLOSED", true); 2505 checkSpecificErrorCode( 2506 -102, NetworkException.ERROR_CONNECTION_REFUSED, "CONNECTION_REFUSED", false); 2507 checkSpecificErrorCode( 2508 -101, NetworkException.ERROR_CONNECTION_RESET, "CONNECTION_RESET", true); 2509 checkSpecificErrorCode( 2510 -118, NetworkException.ERROR_CONNECTION_TIMED_OUT, "CONNECTION_TIMED_OUT", true); 2511 checkSpecificErrorCode(-7, NetworkException.ERROR_TIMED_OUT, "TIMED_OUT", true); 2512 checkSpecificErrorCode( 2513 -109, NetworkException.ERROR_ADDRESS_UNREACHABLE, "ADDRESS_UNREACHABLE", false); 2514 checkSpecificErrorCode(-2, NetworkException.ERROR_OTHER, "FAILED", false); 2515 } 2516 2517 /* 2518 * Verifies no cookies are saved or sent by default. 2519 */ 2520 @Test 2521 @SmallTest testCookiesArentSavedOrSent()2522 public void testCookiesArentSavedOrSent() throws Exception { 2523 // Make a request to a url that sets the cookie 2524 String url = NativeTestServer.getFileURL("/set_cookie.html"); 2525 TestUrlRequestCallback callback = startAndWaitForComplete(url); 2526 assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); 2527 assertThat(callback.getResponseInfoWithChecks()) 2528 .hasHeadersThat() 2529 .containsEntry("Set-Cookie", Arrays.asList("A=B")); 2530 2531 // Make a request that check that cookie header isn't sent. 2532 String headerName = "Cookie"; 2533 String url2 = NativeTestServer.getEchoHeaderURL(headerName); 2534 TestUrlRequestCallback callback2 = startAndWaitForComplete(url2); 2535 assertThat(callback2.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); 2536 assertThat(callback2.mResponseAsString).isEqualTo("Header not found. :("); 2537 } 2538 2539 @Test 2540 @SmallTest 2541 @IgnoreFor( 2542 implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, 2543 reason = "crbug.com/1494846: tests native-specific internals") testQuicErrorCode()2544 public void testQuicErrorCode() throws Exception { 2545 mMockUrlRequestJobFactory = 2546 new MockUrlRequestJobFactory(mTestRule.getTestFramework().getEngine()); 2547 TestUrlRequestCallback callback = 2548 startAndWaitForComplete( 2549 MockUrlRequestJobFactory.getMockUrlWithFailure( 2550 FailurePhase.START, NetError.ERR_QUIC_PROTOCOL_ERROR)); 2551 assertThat(callback.getResponseInfo()).isNull(); 2552 assertThat(callback.mError).isInstanceOf(QuicException.class); 2553 QuicException quicException = (QuicException) callback.mError; 2554 // 1 is QUIC_INTERNAL_ERROR 2555 assertThat(quicException.getQuicDetailedErrorCode()).isEqualTo(1); 2556 assertThat(quicException.getErrorCode()) 2557 .isEqualTo(NetworkException.ERROR_QUIC_PROTOCOL_FAILED); 2558 } 2559 2560 @Test 2561 @SmallTest 2562 @IgnoreFor( 2563 implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, 2564 reason = "crbug.com/1494846: tests native-specific internals") testQuicErrorCodeForNetworkChanged()2565 public void testQuicErrorCodeForNetworkChanged() throws Exception { 2566 mMockUrlRequestJobFactory = 2567 new MockUrlRequestJobFactory(mTestRule.getTestFramework().getEngine()); 2568 TestUrlRequestCallback callback = 2569 startAndWaitForComplete( 2570 MockUrlRequestJobFactory.getMockUrlWithFailure( 2571 FailurePhase.START, NetError.ERR_NETWORK_CHANGED)); 2572 assertThat(callback.getResponseInfo()).isNull(); 2573 assertThat(callback.mError).isInstanceOf(QuicException.class); 2574 QuicException quicException = (QuicException) callback.mError; 2575 // QUIC_CONNECTION_MIGRATION_NO_NEW_NETWORK(83) is set in 2576 // URLRequestFailedJob::PopulateNetErrorDetails for this test. 2577 final int quicErrorCode = 83; 2578 assertThat(quicException.getQuicDetailedErrorCode()).isEqualTo(quicErrorCode); 2579 assertThat(quicException.getErrorCode()).isEqualTo(NetworkException.ERROR_NETWORK_CHANGED); 2580 } 2581 2582 /** 2583 * Tests that legacy onFailed callback is invoked with UrlRequestException if there is no 2584 * onFailed callback implementation that takes CronetException. 2585 */ 2586 @Test 2587 @SmallTest 2588 @IgnoreFor( 2589 implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, 2590 reason = "crbug.com/1494846: tests native-specific internals") testLegacyOnFailedCallback()2591 public void testLegacyOnFailedCallback() throws Exception { 2592 mMockUrlRequestJobFactory = 2593 new MockUrlRequestJobFactory(mTestRule.getTestFramework().getEngine()); 2594 final int netError = -123; 2595 final AtomicBoolean failedExpectation = new AtomicBoolean(); 2596 final ConditionVariable done = new ConditionVariable(); 2597 UrlRequest.Callback callback = 2598 new UrlRequest.Callback() { 2599 @Override 2600 public void onRedirectReceived( 2601 UrlRequest request, UrlResponseInfo info, String newLocationUrl) { 2602 failedExpectation.set(true); 2603 fail(); 2604 } 2605 2606 @Override 2607 public void onResponseStarted(UrlRequest request, UrlResponseInfo info) { 2608 failedExpectation.set(true); 2609 fail(); 2610 } 2611 2612 @Override 2613 public void onReadCompleted( 2614 UrlRequest request, UrlResponseInfo info, ByteBuffer byteBuffer) { 2615 failedExpectation.set(true); 2616 fail(); 2617 } 2618 2619 @Override 2620 public void onSucceeded(UrlRequest request, UrlResponseInfo info) { 2621 failedExpectation.set(true); 2622 fail(); 2623 } 2624 2625 @Override 2626 public void onFailed( 2627 UrlRequest request, UrlResponseInfo info, CronetException error) { 2628 assertThat(error).isInstanceOf(NetworkException.class); 2629 assertThat(((NetworkException) error).getCronetInternalErrorCode()) 2630 .isEqualTo(netError); 2631 failedExpectation.set( 2632 ((NetworkException) error).getCronetInternalErrorCode() 2633 != netError); 2634 done.open(); 2635 } 2636 2637 @Override 2638 public void onCanceled(UrlRequest request, UrlResponseInfo info) { 2639 failedExpectation.set(true); 2640 fail(); 2641 } 2642 }; 2643 2644 UrlRequest.Builder builder = 2645 mTestRule 2646 .getTestFramework() 2647 .getEngine() 2648 .newUrlRequestBuilder( 2649 MockUrlRequestJobFactory.getMockUrlWithFailure( 2650 FailurePhase.START, netError), 2651 callback, 2652 Executors.newSingleThreadExecutor()); 2653 final UrlRequest urlRequest = builder.build(); 2654 urlRequest.start(); 2655 done.block(); 2656 // Check that onFailed is called. 2657 assertThat(failedExpectation.get()).isFalse(); 2658 } 2659 checkSpecificErrorCode( int netError, int errorCode, String name, boolean immediatelyRetryable)2660 private void checkSpecificErrorCode( 2661 int netError, int errorCode, String name, boolean immediatelyRetryable) 2662 throws Exception { 2663 TestUrlRequestCallback callback = 2664 startAndWaitForComplete( 2665 MockUrlRequestJobFactory.getMockUrlWithFailure( 2666 FailurePhase.START, netError)); 2667 assertThat(callback.getResponseInfo()).isNull(); 2668 assertThat(callback.mError).isNotNull(); 2669 assertThat(((NetworkException) callback.mError).getCronetInternalErrorCode()) 2670 .isEqualTo(netError); 2671 assertThat(((NetworkException) callback.mError).getErrorCode()).isEqualTo(errorCode); 2672 assertThat(((NetworkException) callback.mError).immediatelyRetryable()) 2673 .isEqualTo(immediatelyRetryable); 2674 assertThat(callback.mError) 2675 .hasMessageThat() 2676 .contains("Exception in CronetUrlRequest: net::ERR_" + name); 2677 assertThat(callback.mRedirectCount).isEqualTo(0); 2678 assertThat(callback.mOnErrorCalled).isTrue(); 2679 assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_FAILED); 2680 } 2681 2682 // Returns the contents of byteBuffer, from its position() to its limit(), 2683 // as a String. Does not modify byteBuffer's position(). bufferContentsToString(ByteBuffer byteBuffer, int start, int end)2684 private String bufferContentsToString(ByteBuffer byteBuffer, int start, int end) { 2685 // Use a duplicate to avoid modifying byteBuffer. 2686 ByteBuffer duplicate = byteBuffer.duplicate(); 2687 duplicate.position(start); 2688 duplicate.limit(end); 2689 byte[] contents = new byte[duplicate.remaining()]; 2690 duplicate.get(contents); 2691 return new String(contents); 2692 } 2693 assertResponseStepCanceled(TestUrlRequestCallback callback)2694 private void assertResponseStepCanceled(TestUrlRequestCallback callback) { 2695 if (callback.mResponseStep == ResponseStep.ON_FAILED && callback.mError != null) { 2696 throw new Error( 2697 "Unexpected response state: " + ResponseStep.ON_FAILED, callback.mError); 2698 } 2699 assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_CANCELED); 2700 } 2701 2702 @Test 2703 @SmallTest 2704 @RequiresMinAndroidApi(Build.VERSION_CODES.N) 2705 // Used for Android's NetworkSecurityPolicy added in Nougat testCleartextTrafficBlocked()2706 public void testCleartextTrafficBlocked() throws Exception { 2707 final int cleartextNotPermitted = -29; 2708 // This hostname needs to match the one in network_security_config.xml and the one used 2709 // by QuicTestServer. 2710 // https requests to it are tested in QuicTest, so this checks that we're only blocking 2711 // cleartext. 2712 final String url = "http://example.com/simple.txt"; 2713 TestUrlRequestCallback callback = startAndWaitForComplete(url); 2714 assertThat(callback.getResponseInfo()).isNull(); 2715 assertThat(callback.mError).isNotNull(); 2716 // NetworkException#getCronetInternalErrorCode is exposed only by the native 2717 // implementation. 2718 if (mTestRule.implementationUnderTest() == CronetImplementation.STATICALLY_LINKED) { 2719 assertThat(((NetworkException) callback.mError).getCronetInternalErrorCode()) 2720 .isEqualTo(cleartextNotPermitted); 2721 } 2722 } 2723 2724 @Test 2725 @SmallTest 2726 /** 2727 * Open many connections and cancel them right away. This test verifies all internal sockets and 2728 * other Closeables are properly closed. See crbug.com/726193. 2729 */ testGzipCancel()2730 public void testGzipCancel() throws Exception { 2731 String url = NativeTestServer.getFileURL("/gzipped.html"); 2732 for (int i = 0; i < 100; i++) { 2733 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 2734 callback.setAutoAdvance(false); 2735 UrlRequest urlRequest = 2736 mTestRule 2737 .getTestFramework() 2738 .getEngine() 2739 .newUrlRequestBuilder(url, callback, callback.getExecutor()) 2740 .build(); 2741 urlRequest.start(); 2742 urlRequest.cancel(); 2743 // If the test blocks until each UrlRequest finishes before starting the next UrlRequest 2744 // then it never catches the leak. If it starts all UrlRequests and then blocks until 2745 // all UrlRequests finish, it only catches the leak ~10% of the time. In its current 2746 // form it appears to catch the leak ~70% of the time. 2747 // Catching the leak may require a lot of busy threads so that the cancel() happens 2748 // before the UrlRequest has made much progress (and set mCurrentUrlConnection and 2749 // mResponseChannel). This may be why blocking until each UrlRequest finishes doesn't 2750 // catch the leak. 2751 // The other quirk of this is that from teardown(), JavaCronetEngine.shutdown() is 2752 // called which calls ExecutorService.shutdown() which doesn't wait for the thread to 2753 // finish running tasks, and then teardown() calls GC looking for leaks. One possible 2754 // modification would be to expose the ExecutorService and then have tests call 2755 // awaitTermination() but this would complicate things, and adding a 1s sleep() to 2756 // allow the ExecutorService to terminate did not increase the chances of catching the 2757 // leak. 2758 } 2759 } 2760 2761 @Test 2762 @SmallTest 2763 @RequiresMinApi(8) // JavaUrlRequest fixed in API level 8: crrev.com/499303 2764 /** Do a HEAD request and get back a 404. */ test404Head()2765 public void test404Head() throws Exception { 2766 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 2767 UrlRequest.Builder builder = 2768 mTestRule 2769 .getTestFramework() 2770 .getEngine() 2771 .newUrlRequestBuilder( 2772 NativeTestServer.getFileURL("/notfound.html"), 2773 callback, 2774 callback.getExecutor()); 2775 builder.setHttpMethod("HEAD").build().start(); 2776 callback.blockForDone(); 2777 } 2778 2779 @Test 2780 @SmallTest 2781 @RequiresMinApi(9) // Tagging support added in API level 9: crrev.com/c/chromium/src/+/930086 2782 @RequiresMinAndroidApi(Build.VERSION_CODES.M) // crbug/1301957 testTagging()2783 public void testTagging() throws Exception { 2784 if (!CronetTestUtil.nativeCanGetTaggedBytes()) { 2785 Log.i(TAG, "Skipping test - GetTaggedBytes unsupported."); 2786 return; 2787 } 2788 String url = NativeTestServer.getEchoMethodURL(); 2789 2790 // Test untagged requests are given tag 0. 2791 int tag = 0; 2792 long priorBytes = CronetTestUtil.nativeGetTaggedBytes(tag); 2793 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 2794 ExperimentalUrlRequest.Builder builder = 2795 mTestRule 2796 .getTestFramework() 2797 .getEngine() 2798 .newUrlRequestBuilder(url, callback, callback.getExecutor()); 2799 builder.build().start(); 2800 callback.blockForDone(); 2801 assertThat(CronetTestUtil.nativeGetTaggedBytes(tag)).isGreaterThan(priorBytes); 2802 2803 // Test explicit tagging. 2804 tag = 0x12345678; 2805 priorBytes = CronetTestUtil.nativeGetTaggedBytes(tag); 2806 callback = new TestUrlRequestCallback(); 2807 builder = 2808 mTestRule 2809 .getTestFramework() 2810 .getEngine() 2811 .newUrlRequestBuilder(url, callback, callback.getExecutor()); 2812 assertThat(builder).isEqualTo(builder.setTrafficStatsTag(tag)); 2813 builder.build().start(); 2814 callback.blockForDone(); 2815 assertThat(CronetTestUtil.nativeGetTaggedBytes(tag)).isGreaterThan(priorBytes); 2816 2817 // Test a different tag value to make sure reused connections are retagged. 2818 tag = 0x87654321; 2819 priorBytes = CronetTestUtil.nativeGetTaggedBytes(tag); 2820 callback = new TestUrlRequestCallback(); 2821 builder = 2822 mTestRule 2823 .getTestFramework() 2824 .getEngine() 2825 .newUrlRequestBuilder(url, callback, callback.getExecutor()); 2826 assertThat(builder).isEqualTo(builder.setTrafficStatsTag(tag)); 2827 builder.build().start(); 2828 callback.blockForDone(); 2829 assertThat(CronetTestUtil.nativeGetTaggedBytes(tag)).isGreaterThan(priorBytes); 2830 2831 // Test tagging with our UID. 2832 tag = 0; 2833 priorBytes = CronetTestUtil.nativeGetTaggedBytes(tag); 2834 callback = new TestUrlRequestCallback(); 2835 builder = 2836 mTestRule 2837 .getTestFramework() 2838 .getEngine() 2839 .newUrlRequestBuilder(url, callback, callback.getExecutor()); 2840 assertThat(builder).isEqualTo(builder.setTrafficStatsUid(Process.myUid())); 2841 builder.build().start(); 2842 callback.blockForDone(); 2843 assertThat(CronetTestUtil.nativeGetTaggedBytes(tag)).isGreaterThan(priorBytes); 2844 } 2845 2846 @Test 2847 @SmallTest 2848 /** 2849 * Initiate many requests concurrently to make sure neither Cronet implementation crashes. 2850 * Regression test for https://crbug.com/844031. 2851 */ testManyRequests()2852 public void testManyRequests() throws Exception { 2853 String url = NativeTestServer.getMultiRedirectURL(); 2854 final int numRequests = 2000; 2855 TestUrlRequestCallback[] callbacks = new TestUrlRequestCallback[numRequests]; 2856 UrlRequest[] requests = new UrlRequest[numRequests]; 2857 for (int i = 0; i < numRequests; i++) { 2858 // Share the first callback's executor to avoid creating too many single-threaded 2859 // executors and hence too many threads. 2860 if (i == 0) { 2861 callbacks[i] = new TestUrlRequestCallback(); 2862 } else { 2863 callbacks[i] = new TestUrlRequestCallback(callbacks[0].getExecutor()); 2864 } 2865 UrlRequest.Builder builder = 2866 mTestRule 2867 .getTestFramework() 2868 .getEngine() 2869 .newUrlRequestBuilder(url, callbacks[i], callbacks[i].getExecutor()); 2870 requests[i] = builder.build(); 2871 } 2872 for (UrlRequest request : requests) { 2873 request.start(); 2874 } 2875 for (UrlRequest request : requests) { 2876 request.cancel(); 2877 } 2878 for (TestUrlRequestCallback callback : callbacks) { 2879 callback.blockForDone(); 2880 } 2881 } 2882 2883 @Test 2884 @SmallTest testSetIdempotency()2885 public void testSetIdempotency() throws Exception { 2886 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 2887 ExperimentalUrlRequest.Builder builder = 2888 mTestRule 2889 .getTestFramework() 2890 .getEngine() 2891 .newUrlRequestBuilder( 2892 NativeTestServer.getEchoMethodURL(), 2893 callback, 2894 callback.getExecutor()); 2895 assertThat(builder) 2896 .isEqualTo(builder.setIdempotency(ExperimentalUrlRequest.Builder.IDEMPOTENT)); 2897 2898 TestUploadDataProvider dataProvider = 2899 new TestUploadDataProvider( 2900 TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor()); 2901 dataProvider.addRead("test".getBytes()); 2902 builder.setUploadDataProvider(dataProvider, callback.getExecutor()); 2903 builder.addHeader("Content-Type", "useless/string"); 2904 builder.build().start(); 2905 callback.blockForDone(); 2906 dataProvider.assertClosed(); 2907 2908 assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); 2909 assertThat(callback.mResponseAsString).isEqualTo("POST"); 2910 } 2911 2912 @Test 2913 @RequiresMinAndroidApi(Build.VERSION_CODES.M) testBindToInvalidNetworkFails()2914 public void testBindToInvalidNetworkFails() { 2915 String url = NativeTestServer.getEchoMethodURL(); 2916 ExperimentalCronetEngine cronetEngine = mTestRule.getTestFramework().getEngine(); 2917 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 2918 ExperimentalUrlRequest.Builder builder = 2919 cronetEngine.newUrlRequestBuilder(url, callback, callback.getExecutor()); 2920 2921 if (mTestRule.implementationUnderTest() == CronetImplementation.AOSP_PLATFORM) { 2922 // android.net.http.UrlRequestBuilder#bindToNetwork requires an android.net.Network 2923 // object. So, in this case, it will be the wrapper layer that will fail to translate 2924 // that to a Network, not something in net's code. Hence, the failure will manifest 2925 // itself at bind time, not at request execution time. 2926 // Note: this will never happen in prod, as translation failure can only happen if we're 2927 // given a fake networkHandle. 2928 assertThrows( 2929 IllegalArgumentException.class, 2930 () -> builder.bindToNetwork(-150 /* invalid network handle */)); 2931 return; 2932 } 2933 2934 builder.bindToNetwork(-150 /* invalid network handle */); 2935 builder.build().start(); 2936 callback.blockForDone(); 2937 assertThat(callback.mError).isNotNull(); 2938 if (mTestRule.implementationUnderTest() == CronetImplementation.FALLBACK) { 2939 assertThat(callback.mError).isInstanceOf(CronetExceptionImpl.class); 2940 assertThat(callback.mError).hasCauseThat().isInstanceOf(NetworkExceptionImpl.class); 2941 } else { 2942 assertThat(callback.mError).isInstanceOf(NetworkExceptionImpl.class); 2943 } 2944 } 2945 2946 @Test 2947 @RequiresMinAndroidApi(Build.VERSION_CODES.M) testBindToDefaultNetworkSucceeds()2948 public void testBindToDefaultNetworkSucceeds() { 2949 String url = NativeTestServer.getEchoMethodURL(); 2950 ConnectivityManagerDelegate delegate = 2951 new ConnectivityManagerDelegate(mTestRule.getTestFramework().getContext()); 2952 Network defaultNetwork = delegate.getDefaultNetwork(); 2953 assume().that(defaultNetwork).isNotNull(); 2954 2955 ExperimentalCronetEngine cronetEngine = mTestRule.getTestFramework().getEngine(); 2956 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 2957 ExperimentalUrlRequest.Builder builder = 2958 cronetEngine.newUrlRequestBuilder(url, callback, callback.getExecutor()); 2959 builder.bindToNetwork(defaultNetwork.getNetworkHandle()); 2960 builder.build().start(); 2961 callback.blockForDone(); 2962 assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); 2963 } 2964 2965 @NativeMethods("cronet_tests") 2966 interface Natives { 2967 // Return connection migration disable load flag value. getConnectionMigrationDisableLoadFlag()2968 int getConnectionMigrationDisableLoadFlag(); 2969 } 2970 } 2971