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 org.junit.Assert.assertEquals; 8 import static org.junit.Assert.assertFalse; 9 import static org.junit.Assert.assertNotNull; 10 import static org.junit.Assert.assertNull; 11 import static org.junit.Assert.assertTrue; 12 import static org.junit.Assert.fail; 13 14 import static android.net.http.HttpEngine.Builder.HTTP_CACHE_IN_MEMORY; 15 import static org.chromium.net.CronetTestRule.assertContains; 16 import static org.chromium.net.CronetTestRule.getContext; 17 import static org.chromium.net.CronetTestRule.getTestStorage; 18 19 import android.net.http.HttpEngine; 20 import android.net.http.HttpException; 21 import android.net.http.ExperimentalHttpEngine; 22 import android.net.http.ExperimentalUrlRequest; 23 import android.net.http.UrlRequest; 24 import android.net.http.UrlResponseInfo; 25 import android.content.Context; 26 import android.content.ContextWrapper; 27 import android.net.Network; 28 import android.os.Build; 29 import android.os.ConditionVariable; 30 import android.os.Handler; 31 import android.os.Looper; 32 import android.os.Process; 33 34 import androidx.test.ext.junit.runners.AndroidJUnit4; 35 import androidx.test.filters.SmallTest; 36 37 import org.json.JSONObject; 38 import org.junit.After; 39 import org.junit.Before; 40 import org.junit.Rule; 41 import org.junit.Test; 42 import org.junit.runner.RunWith; 43 44 import org.chromium.base.FileUtils; 45 import org.chromium.base.PathUtils; 46 import org.chromium.base.annotations.JNINamespace; 47 import org.chromium.base.annotations.NativeMethods; 48 import org.chromium.net.CronetTestRule.CronetTestFramework; 49 import org.chromium.net.CronetTestRule.OnlyRunNativeCronet; 50 import org.chromium.net.CronetTestRule.RequiresMinAndroidApi; 51 import org.chromium.net.CronetTestRule.RequiresMinApi; 52 import org.chromium.net.NetworkChangeNotifierAutoDetect.ConnectivityManagerDelegate; 53 import org.chromium.net.TestUrlRequestCallback.ResponseStep; 54 import org.chromium.net.impl.CronetLibraryLoader; 55 import org.chromium.net.impl.CronetUrlRequestContext; 56 57 import java.io.BufferedReader; 58 import java.io.File; 59 import java.io.FileReader; 60 import java.net.URL; 61 import java.nio.ByteBuffer; 62 import java.util.Arrays; 63 import java.util.concurrent.Callable; 64 import java.util.concurrent.Executor; 65 import java.util.concurrent.FutureTask; 66 import java.util.concurrent.atomic.AtomicReference; 67 68 /** 69 * Test CronetEngine. 70 */ 71 @RunWith(AndroidJUnit4.class) 72 @JNINamespace("cronet") 73 public class CronetUrlRequestContextTest { 74 @Rule 75 public final CronetTestRule mTestRule = new CronetTestRule(); 76 77 private static final String TAG = CronetUrlRequestContextTest.class.getSimpleName(); 78 79 // URLs used for tests. 80 private static final String MOCK_CRONET_TEST_FAILED_URL = 81 "http://mock.failed.request/-2"; 82 private static final String MOCK_CRONET_TEST_SUCCESS_URL = 83 "http://mock.http/success.txt"; 84 private static final int MAX_FILE_SIZE = 1000000000; 85 private static final int NUM_EVENT_FILES = 10; 86 private String mUrl; 87 private String mUrl404; 88 private String mUrl500; 89 90 @Before setUp()91 public void setUp() throws Exception { 92 assertTrue(NativeTestServer.startNativeTestServer(getContext())); 93 mUrl = NativeTestServer.getSuccessURL(); 94 mUrl404 = NativeTestServer.getNotFoundURL(); 95 mUrl500 = NativeTestServer.getServerErrorURL(); 96 } 97 98 @After tearDown()99 public void tearDown() throws Exception { 100 NativeTestServer.shutdownNativeTestServer(); 101 } 102 103 static class RequestThread extends Thread { 104 public TestUrlRequestCallback mCallback; 105 106 final String mUrl; 107 final ConditionVariable mRunBlocker; 108 RequestThread(String url, ConditionVariable runBlocker)109 public RequestThread(String url, ConditionVariable runBlocker) { 110 mUrl = url; 111 mRunBlocker = runBlocker; 112 } 113 114 @Override run()115 public void run() { 116 mRunBlocker.block(); 117 HttpEngine cronetEngine = new HttpEngine.Builder(getContext()).build(); 118 mCallback = new TestUrlRequestCallback(); 119 UrlRequest.Builder urlRequestBuilder = 120 cronetEngine.newUrlRequestBuilder(mUrl, mCallback.getExecutor(), mCallback); 121 urlRequestBuilder.build().start(); 122 mCallback.blockForDone(); 123 } 124 } 125 126 /** 127 * Callback that shutdowns the request context when request has succeeded 128 * or failed. 129 */ 130 static class ShutdownTestUrlRequestCallback extends TestUrlRequestCallback { 131 private final HttpEngine mCronetEngine; 132 private final ConditionVariable mCallbackCompletionBlock = new ConditionVariable(); 133 ShutdownTestUrlRequestCallback(HttpEngine cronetEngine)134 ShutdownTestUrlRequestCallback(HttpEngine cronetEngine) { 135 mCronetEngine = cronetEngine; 136 } 137 138 @Override onSucceeded(UrlRequest request, UrlResponseInfo info)139 public void onSucceeded(UrlRequest request, UrlResponseInfo info) { 140 super.onSucceeded(request, info); 141 mCronetEngine.shutdown(); 142 mCallbackCompletionBlock.open(); 143 } 144 145 @Override onFailed(UrlRequest request, UrlResponseInfo info, HttpException error)146 public void onFailed(UrlRequest request, UrlResponseInfo info, HttpException error) { 147 super.onFailed(request, info, error); 148 mCronetEngine.shutdown(); 149 mCallbackCompletionBlock.open(); 150 } 151 152 // Wait for request completion callback. blockForCallbackToComplete()153 void blockForCallbackToComplete() { 154 mCallbackCompletionBlock.block(); 155 } 156 } 157 158 @Test 159 @SmallTest 160 @SuppressWarnings("deprecation") testConfigUserAgent()161 public void testConfigUserAgent() throws Exception { 162 String userAgentName = "User-Agent"; 163 String userAgentValue = "User-Agent-Value"; 164 ExperimentalHttpEngine.Builder cronetEngineBuilder = 165 new ExperimentalHttpEngine.Builder(getContext()); 166 cronetEngineBuilder.setUserAgent(userAgentValue); 167 final HttpEngine cronetEngine = cronetEngineBuilder.build(); 168 NativeTestServer.shutdownNativeTestServer(); // startNativeTestServer returns false if it's 169 // already running 170 assertTrue(NativeTestServer.startNativeTestServer(getContext())); 171 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 172 UrlRequest.Builder urlRequestBuilder = cronetEngine.newUrlRequestBuilder( 173 NativeTestServer.getEchoHeaderURL(userAgentName), callback.getExecutor(), callback); 174 urlRequestBuilder.build().start(); 175 callback.blockForDone(); 176 assertEquals(userAgentValue, callback.mResponseAsString); 177 } 178 179 @Test 180 @SmallTest 181 // TODO: Remove the annotation after fixing http://crbug.com/637979 & http://crbug.com/637972 182 @OnlyRunNativeCronet testShutdown()183 public void testShutdown() throws Exception { 184 final CronetTestFramework testFramework = mTestRule.startCronetTestFramework(); 185 ShutdownTestUrlRequestCallback callback = 186 new ShutdownTestUrlRequestCallback(testFramework.mCronetEngine); 187 // Block callback when response starts to verify that shutdown fails 188 // if there are active requests. 189 callback.setAutoAdvance(false); 190 UrlRequest.Builder urlRequestBuilder = testFramework.mCronetEngine.newUrlRequestBuilder( 191 mUrl, callback.getExecutor(), callback); 192 UrlRequest urlRequest = urlRequestBuilder.build(); 193 urlRequest.start(); 194 try { 195 testFramework.mCronetEngine.shutdown(); 196 fail("Should throw an exception"); 197 } catch (Exception e) { 198 assertEquals("Cannot shutdown with active requests.", e.getMessage()); 199 } 200 201 callback.waitForNextStep(); 202 assertEquals(ResponseStep.ON_RESPONSE_STARTED, callback.mResponseStep); 203 try { 204 testFramework.mCronetEngine.shutdown(); 205 fail("Should throw an exception"); 206 } catch (Exception e) { 207 assertEquals("Cannot shutdown with active requests.", e.getMessage()); 208 } 209 callback.startNextRead(urlRequest); 210 211 callback.waitForNextStep(); 212 assertEquals(ResponseStep.ON_READ_COMPLETED, callback.mResponseStep); 213 try { 214 testFramework.mCronetEngine.shutdown(); 215 fail("Should throw an exception"); 216 } catch (Exception e) { 217 assertEquals("Cannot shutdown with active requests.", e.getMessage()); 218 } 219 220 // May not have read all the data, in theory. Just enable auto-advance 221 // and finish the request. 222 callback.setAutoAdvance(true); 223 callback.startNextRead(urlRequest); 224 callback.blockForDone(); 225 callback.blockForCallbackToComplete(); 226 callback.shutdownExecutor(); 227 } 228 229 @Test 230 @SmallTest 231 @OnlyRunNativeCronet testShutdownDuringInit()232 public void testShutdownDuringInit() throws Exception { 233 final ConditionVariable block = new ConditionVariable(false); 234 235 // Post a task to main thread to block until shutdown is called to test 236 // scenario when shutdown is called right after construction before 237 // context is fully initialized on the main thread. 238 Runnable blockingTask = new Runnable() { 239 @Override 240 public void run() { 241 try { 242 block.block(); 243 } catch (Exception e) { 244 fail("Caught " + e.getMessage()); 245 } 246 } 247 }; 248 // Ensure that test is not running on the main thread. 249 assertTrue(Looper.getMainLooper() != Looper.myLooper()); 250 new Handler(Looper.getMainLooper()).post(blockingTask); 251 252 // Create new request context, but its initialization on the main thread 253 // will be stuck behind blockingTask. 254 final CronetUrlRequestContext cronetEngine = 255 (CronetUrlRequestContext) new HttpEngine.Builder(getContext()).build(); 256 // Unblock the main thread, so context gets initialized and shutdown on 257 // it. 258 block.open(); 259 // Shutdown will wait for init to complete on main thread. 260 cronetEngine.shutdown(); 261 // Verify that context is shutdown. 262 try { 263 cronetEngine.getUrlRequestContextAdapter(); 264 fail("Should throw an exception."); 265 } catch (Exception e) { 266 assertEquals("Engine is shut down.", e.getMessage()); 267 } 268 } 269 270 @Test 271 @SmallTest 272 @OnlyRunNativeCronet testInitAndShutdownOnMainThread()273 public void testInitAndShutdownOnMainThread() throws Exception { 274 final ConditionVariable block = new ConditionVariable(false); 275 276 // Post a task to main thread to init and shutdown on the main thread. 277 Runnable blockingTask = new Runnable() { 278 @Override 279 public void run() { 280 // Create new request context, loading the library. 281 final CronetUrlRequestContext cronetEngine = 282 (CronetUrlRequestContext) new HttpEngine.Builder(getContext()).build(); 283 // Shutdown right after init. 284 cronetEngine.shutdown(); 285 // Verify that context is shutdown. 286 try { 287 cronetEngine.getUrlRequestContextAdapter(); 288 fail("Should throw an exception."); 289 } catch (Exception e) { 290 assertEquals("Engine is shut down.", e.getMessage()); 291 } 292 block.open(); 293 } 294 }; 295 new Handler(Looper.getMainLooper()).post(blockingTask); 296 // Wait for shutdown to complete on main thread. 297 block.block(); 298 } 299 300 @Test 301 @SmallTest 302 @OnlyRunNativeCronet // JavaCronetEngine doesn't support throwing on repeat shutdown() testMultipleShutdown()303 public void testMultipleShutdown() throws Exception { 304 final CronetTestFramework testFramework = mTestRule.startCronetTestFramework(); 305 try { 306 testFramework.mCronetEngine.shutdown(); 307 testFramework.mCronetEngine.shutdown(); 308 fail("Should throw an exception"); 309 } catch (Exception e) { 310 assertEquals("Engine is shut down.", e.getMessage()); 311 } 312 } 313 314 @Test 315 @SmallTest 316 // TODO: Remove the annotation after fixing http://crbug.com/637972 317 @OnlyRunNativeCronet testShutdownAfterError()318 public void testShutdownAfterError() throws Exception { 319 final CronetTestFramework testFramework = mTestRule.startCronetTestFramework(); 320 ShutdownTestUrlRequestCallback callback = 321 new ShutdownTestUrlRequestCallback(testFramework.mCronetEngine); 322 UrlRequest.Builder urlRequestBuilder = testFramework.mCronetEngine.newUrlRequestBuilder( 323 MOCK_CRONET_TEST_FAILED_URL, callback.getExecutor(), callback); 324 urlRequestBuilder.build().start(); 325 callback.blockForDone(); 326 assertTrue(callback.mOnErrorCalled); 327 callback.blockForCallbackToComplete(); 328 callback.shutdownExecutor(); 329 } 330 331 @Test 332 @SmallTest 333 @OnlyRunNativeCronet // JavaCronetEngine doesn't support throwing on shutdown() testShutdownAfterCancel()334 public void testShutdownAfterCancel() throws Exception { 335 final CronetTestFramework testFramework = mTestRule.startCronetTestFramework(); 336 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 337 // Block callback when response starts to verify that shutdown fails 338 // if there are active requests. 339 callback.setAutoAdvance(false); 340 UrlRequest.Builder urlRequestBuilder = testFramework.mCronetEngine.newUrlRequestBuilder( 341 mUrl, callback.getExecutor(), callback); 342 UrlRequest urlRequest = urlRequestBuilder.build(); 343 urlRequest.start(); 344 try { 345 testFramework.mCronetEngine.shutdown(); 346 fail("Should throw an exception"); 347 } catch (Exception e) { 348 assertEquals("Cannot shutdown with active requests.", e.getMessage()); 349 } 350 callback.waitForNextStep(); 351 assertEquals(ResponseStep.ON_RESPONSE_STARTED, callback.mResponseStep); 352 urlRequest.cancel(); 353 testFramework.mCronetEngine.shutdown(); 354 } 355 356 @Test 357 @SmallTest 358 @OnlyRunNativeCronet // Only chromium based Cronet supports the multi-network API 359 @RequiresMinAndroidApi(Build.VERSION_CODES.M) // Multi-network API is supported from Marshmallow testNetworkBoundContextLifetime()360 public void testNetworkBoundContextLifetime() throws Exception { 361 // Multi-network API is available starting from Android Lollipop. 362 final CronetTestFramework testFramework = mTestRule.startCronetTestFramework(); 363 ConnectivityManagerDelegate delegate = new ConnectivityManagerDelegate(getContext()); 364 Network defaultNetwork = delegate.getDefaultNetwork(); 365 if (defaultNetwork == null) { 366 testFramework.mCronetEngine.shutdown(); 367 return; 368 } 369 370 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 371 // Allows to check the underlying network-bound context state while the request is in 372 // progress. 373 callback.setAutoAdvance(false); 374 375 UrlRequest.Builder urlRequestBuilder = 376 testFramework.mCronetEngine.newUrlRequestBuilder( 377 mUrl, callback.getExecutor(), callback); 378 urlRequestBuilder.bindToNetwork(defaultNetwork); 379 UrlRequest urlRequest = urlRequestBuilder.build(); 380 381 assertFalse( 382 ApiHelper.doesContextExistForNetwork(testFramework.mCronetEngine, defaultNetwork)); 383 urlRequest.start(); 384 assertTrue( 385 ApiHelper.doesContextExistForNetwork(testFramework.mCronetEngine, defaultNetwork)); 386 387 // Resume callback execution. 388 callback.waitForNextStep(); 389 assertEquals(ResponseStep.ON_RESPONSE_STARTED, callback.mResponseStep); 390 callback.setAutoAdvance(true); 391 callback.startNextRead(urlRequest); 392 callback.blockForDone(); 393 assertNull(callback.mError); 394 395 // The default network should still be active, hence the underlying network-bound context 396 // should still be there. 397 assertTrue( 398 ApiHelper.doesContextExistForNetwork(testFramework.mCronetEngine, defaultNetwork)); 399 400 // Fake disconnect event for the default network, this should destroy the underlying 401 // network-bound context. 402 FutureTask<Void> task = new FutureTask<Void>(new Callable<Void>() { 403 @Override 404 public Void call() { 405 NetworkChangeNotifier.fakeNetworkDisconnected(defaultNetwork.getNetworkHandle()); 406 return null; 407 } 408 }); 409 CronetLibraryLoader.postToInitThread(task); 410 task.get(); 411 assertFalse( 412 ApiHelper.doesContextExistForNetwork(testFramework.mCronetEngine, defaultNetwork)); 413 testFramework.mCronetEngine.shutdown(); 414 } 415 416 @Test 417 @SmallTest 418 @OnlyRunNativeCronet // Only chromium based Cronet supports the multi-network API 419 @RequiresMinAndroidApi(Build.VERSION_CODES.M) // Multi-network API is supported from Marshmallow testNetworkBoundRequestCancel()420 public void testNetworkBoundRequestCancel() throws Exception { 421 // Upon a network disconnection, NCN posts a tasks onto the network thread that calls 422 // CronetContext::NetworkTasks::OnNetworkDisconnected. 423 // Calling urlRequest.cancel() also, after some hoops, ends up in a posted tasks onto the 424 // network thread that calls CronetURLRequest::NetworkTasks::Destroy. 425 // Depending on their implementation this can lead to UAF, this test is here to prevent that 426 // from being introduced in the future. 427 final CronetTestFramework testFramework = mTestRule.startCronetTestFramework(); 428 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 429 callback.setAutoAdvance(false); 430 UrlRequest.Builder urlRequestBuilder = 431 testFramework.mCronetEngine.newUrlRequestBuilder( 432 mUrl, callback, callback.getExecutor()); 433 ConnectivityManagerDelegate delegate = new ConnectivityManagerDelegate(getContext()); 434 Network defaultNetwork = delegate.getDefaultNetwork(); 435 if (defaultNetwork == null) { 436 testFramework.mCronetEngine.shutdown(); 437 return; 438 } 439 440 urlRequestBuilder.bindToNetwork(defaultNetwork); 441 UrlRequest urlRequest = urlRequestBuilder.build(); 442 443 assertFalse( 444 ApiHelper.doesContextExistForNetwork(testFramework.mCronetEngine, defaultNetwork)); 445 urlRequest.start(); 446 assertTrue( 447 ApiHelper.doesContextExistForNetwork(testFramework.mCronetEngine, defaultNetwork)); 448 449 callback.waitForNextStep(); 450 assertEquals(ResponseStep.ON_RESPONSE_STARTED, callback.mResponseStep); 451 assertTrue( 452 ApiHelper.doesContextExistForNetwork(testFramework.mCronetEngine, defaultNetwork)); 453 // Cronet registers for NCN notifications on the init thread (see 454 // CronetLibraryLoader#ensureInitializedOnInitThread), hence we need to trigger fake 455 // notifications from there. 456 CronetLibraryLoader.postToInitThread(new Runnable() { 457 @Override 458 public void run() { 459 NetworkChangeNotifier.fakeNetworkDisconnected(defaultNetwork.getNetworkHandle()); 460 // Queue cancel after disconnect event. 461 urlRequest.cancel(); 462 } 463 }); 464 // Wait until the cancel call propagates (this would block undefinitely without that since 465 // we previously set auto advance to false). 466 callback.blockForDone(); 467 // mError should be null due to urlRequest.cancel(). 468 assertNull(callback.mError); 469 // urlRequest.cancel(); should destroy the underlying network bound context. 470 assertFalse( 471 ApiHelper.doesContextExistForNetwork(testFramework.mCronetEngine, defaultNetwork)); 472 testFramework.mCronetEngine.shutdown(); 473 } 474 475 @Test 476 @SmallTest 477 @OnlyRunNativeCronet // No netlogs for pure java impl testNetLog()478 public void testNetLog() throws Exception { 479 Context context = getContext(); 480 File directory = new File(PathUtils.getDataDirectory()); 481 File file = File.createTempFile("cronet", "json", directory); 482 HttpEngine cronetEngine = new HttpEngine.Builder(context).build(); 483 // Start NetLog immediately after the request context is created to make 484 // sure that the call won't crash the app even when the native request 485 // context is not fully initialized. See crbug.com/470196. 486 cronetEngine.startNetLogToFile(file.getPath(), false); 487 488 // Start a request. 489 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 490 UrlRequest.Builder urlRequestBuilder = 491 cronetEngine.newUrlRequestBuilder(mUrl, callback.getExecutor(), callback); 492 urlRequestBuilder.build().start(); 493 callback.blockForDone(); 494 cronetEngine.stopNetLog(); 495 assertTrue(file.exists()); 496 assertTrue(file.length() != 0); 497 assertFalse(hasBytesInNetLog(file)); 498 assertTrue(file.delete()); 499 assertTrue(!file.exists()); 500 } 501 502 @Test 503 @SmallTest 504 @OnlyRunNativeCronet // No netlogs for pure java impl testBoundedFileNetLog()505 public void testBoundedFileNetLog() throws Exception { 506 Context context = getContext(); 507 File directory = new File(PathUtils.getDataDirectory()); 508 File netLogDir = new File(directory, "NetLog"); 509 assertFalse(netLogDir.exists()); 510 assertTrue(netLogDir.mkdir()); 511 File logFile = new File(netLogDir, "netlog.json"); 512 ExperimentalHttpEngine cronetEngine = 513 new ExperimentalHttpEngine.Builder(context).build(); 514 // Start NetLog immediately after the request context is created to make 515 // sure that the call won't crash the app even when the native request 516 // context is not fully initialized. See crbug.com/470196. 517 cronetEngine.startNetLogToDisk(netLogDir.getPath(), false, MAX_FILE_SIZE); 518 519 // Start a request. 520 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 521 UrlRequest.Builder urlRequestBuilder = 522 cronetEngine.newUrlRequestBuilder(mUrl, callback, callback.getExecutor()); 523 urlRequestBuilder.build().start(); 524 callback.blockForDone(); 525 cronetEngine.stopNetLog(); 526 assertTrue(logFile.exists()); 527 assertTrue(logFile.length() != 0); 528 assertFalse(hasBytesInNetLog(logFile)); 529 FileUtils.recursivelyDeleteFile(netLogDir, FileUtils.DELETE_ALL); 530 assertFalse(netLogDir.exists()); 531 } 532 533 @Test 534 @SmallTest 535 @OnlyRunNativeCronet // No netlogs for pure java impl 536 // Tests that if stopNetLog is not explicity called, CronetEngine.shutdown() 537 // will take care of it. crbug.com/623701. testNoStopNetLog()538 public void testNoStopNetLog() throws Exception { 539 Context context = getContext(); 540 File directory = new File(PathUtils.getDataDirectory()); 541 File file = File.createTempFile("cronet", "json", directory); 542 HttpEngine cronetEngine = new HttpEngine.Builder(context).build(); 543 cronetEngine.startNetLogToFile(file.getPath(), false); 544 545 // Start a request. 546 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 547 UrlRequest.Builder urlRequestBuilder = 548 cronetEngine.newUrlRequestBuilder(mUrl, callback.getExecutor(), callback); 549 urlRequestBuilder.build().start(); 550 callback.blockForDone(); 551 // Shut down the engine without calling stopNetLog. 552 cronetEngine.shutdown(); 553 assertTrue(file.exists()); 554 assertTrue(file.length() != 0); 555 assertFalse(hasBytesInNetLog(file)); 556 assertTrue(file.delete()); 557 assertTrue(!file.exists()); 558 } 559 560 @Test 561 @SmallTest 562 @OnlyRunNativeCronet // No netlogs for pure java impl 563 // Tests that if stopNetLog is not explicity called, CronetEngine.shutdown() 564 // will take care of it. crbug.com/623701. testNoStopBoundedFileNetLog()565 public void testNoStopBoundedFileNetLog() throws Exception { 566 Context context = getContext(); 567 File directory = new File(PathUtils.getDataDirectory()); 568 File netLogDir = new File(directory, "NetLog"); 569 assertFalse(netLogDir.exists()); 570 assertTrue(netLogDir.mkdir()); 571 File logFile = new File(netLogDir, "netlog.json"); 572 ExperimentalHttpEngine cronetEngine = 573 new ExperimentalHttpEngine.Builder(context).build(); 574 cronetEngine.startNetLogToDisk(netLogDir.getPath(), false, MAX_FILE_SIZE); 575 576 // Start a request. 577 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 578 UrlRequest.Builder urlRequestBuilder = 579 cronetEngine.newUrlRequestBuilder(mUrl, callback, callback.getExecutor()); 580 urlRequestBuilder.build().start(); 581 callback.blockForDone(); 582 // Shut down the engine without calling stopNetLog. 583 cronetEngine.shutdown(); 584 assertTrue(logFile.exists()); 585 assertTrue(logFile.length() != 0); 586 587 FileUtils.recursivelyDeleteFile(netLogDir, FileUtils.DELETE_ALL); 588 assertFalse(netLogDir.exists()); 589 } 590 591 @Test 592 @SmallTest testGetActiveRequestCount()593 public void testGetActiveRequestCount() throws Exception { 594 final CronetTestFramework testFramework = mTestRule.startCronetTestFramework(); 595 HttpEngine cronetEngine = testFramework.mCronetEngine; 596 TestUrlRequestCallback callback1 = new TestUrlRequestCallback(); 597 TestUrlRequestCallback callback2 = new TestUrlRequestCallback(); 598 callback1.setAutoAdvance(false); 599 callback2.setAutoAdvance(false); 600 assertEquals(0, cronetEngine.getActiveRequestCount()); 601 UrlRequest request1 = 602 cronetEngine.newUrlRequestBuilder(mUrl, callback1, callback1.getExecutor()).build(); 603 UrlRequest request2 = 604 cronetEngine.newUrlRequestBuilder(mUrl, callback2, callback2.getExecutor()).build(); 605 request1.start(); 606 request2.start(); 607 assertEquals(2, cronetEngine.getActiveRequestCount()); 608 callback1.waitForNextStep(); 609 callback1.setAutoAdvance(true); 610 callback1.startNextRead(request1); 611 callback1.blockForDone(); 612 assertEquals(1, cronetEngine.getActiveRequestCount()); 613 callback2.waitForNextStep(); 614 callback2.setAutoAdvance(true); 615 callback2.startNextRead(request2); 616 callback2.blockForDone(); 617 assertEquals(0, cronetEngine.getActiveRequestCount()); 618 } 619 620 @Test 621 @SmallTest testGetActiveRequestCountOnReachingSucceeded()622 public void testGetActiveRequestCountOnReachingSucceeded() throws Exception { 623 final CronetTestFramework testFramework = mTestRule.startCronetTestFramework(); 624 HttpEngine cronetEngine = testFramework.mCronetEngine; 625 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 626 callback.setAutoAdvance(false); 627 callback.setBlockOnTerminalState(true); 628 assertEquals(0, cronetEngine.getActiveRequestCount()); 629 UrlRequest request = 630 cronetEngine.newUrlRequestBuilder(mUrl, callback, callback.getExecutor()).build(); 631 request.start(); 632 assertEquals(1, cronetEngine.getActiveRequestCount()); 633 callback.waitForNextStep(); 634 callback.startNextRead(request); 635 callback.waitForNextStep(); 636 callback.startNextRead(request); 637 callback.waitForTerminalToStart(); 638 assertEquals(0, cronetEngine.getActiveRequestCount()); 639 callback.setBlockOnTerminalState(false); 640 } 641 642 @Test 643 @SmallTest testGetActiveRequestCountOnReachingCancel()644 public void testGetActiveRequestCountOnReachingCancel() throws Exception { 645 final CronetTestFramework testFramework = mTestRule.startCronetTestFramework(); 646 HttpEngine cronetEngine = testFramework.mCronetEngine; 647 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 648 callback.setAutoAdvance(false); 649 callback.setBlockOnTerminalState(true); 650 assertEquals(0, cronetEngine.getActiveRequestCount()); 651 UrlRequest request = 652 cronetEngine.newUrlRequestBuilder(mUrl, callback, callback.getExecutor()).build(); 653 request.start(); 654 assertEquals(1, cronetEngine.getActiveRequestCount()); 655 request.cancel(); 656 callback.waitForTerminalToStart(); 657 assertEquals(0, cronetEngine.getActiveRequestCount()); 658 callback.setBlockOnTerminalState(false); 659 assertTrue(callback.mOnCanceledCalled); 660 assertEquals(ResponseStep.ON_CANCELED, callback.mResponseStep); 661 } 662 663 @Test 664 @SmallTest testGetActiveRequestCountOnReachingFail()665 public void testGetActiveRequestCountOnReachingFail() throws Exception { 666 final String badUrl = "www.unreachable-url.com"; 667 final CronetTestFramework testFramework = mTestRule.startCronetTestFramework(); 668 HttpEngine cronetEngine = testFramework.mCronetEngine; 669 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 670 callback.setAutoAdvance(false); 671 callback.setBlockOnTerminalState(true); 672 assertEquals(0, cronetEngine.getActiveRequestCount()); 673 UrlRequest request = 674 cronetEngine.newUrlRequestBuilder(badUrl, callback, callback.getExecutor()).build(); 675 request.start(); 676 assertEquals(1, cronetEngine.getActiveRequestCount()); 677 callback.waitForTerminalToStart(); 678 assertEquals(0, cronetEngine.getActiveRequestCount()); 679 callback.setBlockOnTerminalState(false); 680 assertTrue(callback.mOnErrorCalled); 681 assertEquals(ResponseStep.ON_FAILED, callback.mResponseStep); 682 } 683 684 @Test 685 @SmallTest testGetActiveRequestCountWithCancel()686 public void testGetActiveRequestCountWithCancel() throws Exception { 687 final CronetTestFramework testFramework = mTestRule.startCronetTestFramework(); 688 HttpEngine cronetEngine = testFramework.mCronetEngine; 689 TestUrlRequestCallback callback1 = new TestUrlRequestCallback(); 690 TestUrlRequestCallback callback2 = new TestUrlRequestCallback(); 691 callback1.setAutoAdvance(false); 692 callback2.setAutoAdvance(false); 693 assertEquals(0, cronetEngine.getActiveRequestCount()); 694 UrlRequest request1 = 695 cronetEngine.newUrlRequestBuilder(mUrl, callback1, callback1.getExecutor()).build(); 696 UrlRequest request2 = 697 cronetEngine.newUrlRequestBuilder(mUrl, callback2, callback2.getExecutor()).build(); 698 request1.start(); 699 request2.start(); 700 assertEquals(2, cronetEngine.getActiveRequestCount()); 701 request1.cancel(); 702 callback1.blockForDone(); 703 assertTrue(callback1.mOnCanceledCalled); 704 assertEquals(ResponseStep.ON_CANCELED, callback1.mResponseStep); 705 assertEquals(1, cronetEngine.getActiveRequestCount()); 706 callback2.waitForNextStep(); 707 callback2.setAutoAdvance(true); 708 callback2.startNextRead(request2); 709 callback2.blockForDone(); 710 assertEquals(0, cronetEngine.getActiveRequestCount()); 711 } 712 713 @Test 714 @SmallTest testGetActiveRequestCountWithError()715 public void testGetActiveRequestCountWithError() throws Exception { 716 final String badUrl = "www.unreachable-url.com"; 717 final CronetTestFramework testFramework = mTestRule.startCronetTestFramework(); 718 HttpEngine cronetEngine = testFramework.mCronetEngine; 719 TestUrlRequestCallback callback1 = new TestUrlRequestCallback(); 720 TestUrlRequestCallback callback2 = new TestUrlRequestCallback(); 721 callback1.setAutoAdvance(false); 722 callback2.setAutoAdvance(false); 723 assertEquals(0, cronetEngine.getActiveRequestCount()); 724 UrlRequest request1 = 725 cronetEngine.newUrlRequestBuilder(badUrl, callback1, callback1.getExecutor()) 726 .build(); 727 UrlRequest request2 = 728 cronetEngine.newUrlRequestBuilder(mUrl, callback2, callback2.getExecutor()).build(); 729 request1.start(); 730 request2.start(); 731 assertEquals(2, cronetEngine.getActiveRequestCount()); 732 callback1.blockForDone(); 733 assertTrue(callback1.mOnErrorCalled); 734 assertEquals(ResponseStep.ON_FAILED, callback1.mResponseStep); 735 assertEquals(1, cronetEngine.getActiveRequestCount()); 736 callback2.waitForNextStep(); 737 callback2.setAutoAdvance(true); 738 callback2.startNextRead(request2); 739 callback2.blockForDone(); 740 assertEquals(0, cronetEngine.getActiveRequestCount()); 741 } 742 743 @Test 744 @SmallTest 745 @OnlyRunNativeCronet 746 // Tests that NetLog contains events emitted by all live CronetEngines. testNetLogContainEventsFromAllLiveEngines()747 public void testNetLogContainEventsFromAllLiveEngines() throws Exception { 748 Context context = getContext(); 749 File directory = new File(PathUtils.getDataDirectory()); 750 File file1 = File.createTempFile("cronet1", "json", directory); 751 File file2 = File.createTempFile("cronet2", "json", directory); 752 HttpEngine cronetEngine1 = new HttpEngine.Builder(context).build(); 753 HttpEngine cronetEngine2 = new HttpEngine.Builder(context).build(); 754 755 cronetEngine1.startNetLogToFile(file1.getPath(), false); 756 cronetEngine2.startNetLogToFile(file2.getPath(), false); 757 758 // Warm CronetEngine and make sure both CronetUrlRequestContexts are 759 // initialized before testing the logs. 760 makeRequestAndCheckStatus(cronetEngine1, mUrl, 200); 761 makeRequestAndCheckStatus(cronetEngine2, mUrl, 200); 762 763 // Use cronetEngine1 to make a request to mUrl404. 764 makeRequestAndCheckStatus(cronetEngine1, mUrl404, 404); 765 766 // Use cronetEngine2 to make a request to mUrl500. 767 makeRequestAndCheckStatus(cronetEngine2, mUrl500, 500); 768 769 cronetEngine1.stopNetLog(); 770 cronetEngine2.stopNetLog(); 771 assertTrue(file1.exists()); 772 assertTrue(file2.exists()); 773 // Make sure both files contain the two requests made separately using 774 // different engines. 775 assertTrue(containsStringInNetLog(file1, mUrl404)); 776 assertTrue(containsStringInNetLog(file1, mUrl500)); 777 assertTrue(containsStringInNetLog(file2, mUrl404)); 778 assertTrue(containsStringInNetLog(file2, mUrl500)); 779 assertTrue(file1.delete()); 780 assertTrue(file2.delete()); 781 } 782 783 @Test 784 @SmallTest 785 @OnlyRunNativeCronet 786 // Tests that NetLog contains events emitted by all live CronetEngines. testBoundedFileNetLogContainEventsFromAllLiveEngines()787 public void testBoundedFileNetLogContainEventsFromAllLiveEngines() throws Exception { 788 Context context = getContext(); 789 File directory = new File(PathUtils.getDataDirectory()); 790 File netLogDir1 = new File(directory, "NetLog1"); 791 assertFalse(netLogDir1.exists()); 792 assertTrue(netLogDir1.mkdir()); 793 File netLogDir2 = new File(directory, "NetLog2"); 794 assertFalse(netLogDir2.exists()); 795 assertTrue(netLogDir2.mkdir()); 796 File logFile1 = new File(netLogDir1, "netlog.json"); 797 File logFile2 = new File(netLogDir2, "netlog.json"); 798 799 ExperimentalHttpEngine cronetEngine1 = 800 new ExperimentalHttpEngine.Builder(context).build(); 801 ExperimentalHttpEngine cronetEngine2 = 802 new ExperimentalHttpEngine.Builder(context).build(); 803 804 cronetEngine1.startNetLogToDisk(netLogDir1.getPath(), false, MAX_FILE_SIZE); 805 cronetEngine2.startNetLogToDisk(netLogDir2.getPath(), false, MAX_FILE_SIZE); 806 807 // Warm CronetEngine and make sure both CronetUrlRequestContexts are 808 // initialized before testing the logs. 809 makeRequestAndCheckStatus(cronetEngine1, mUrl, 200); 810 makeRequestAndCheckStatus(cronetEngine2, mUrl, 200); 811 812 // Use cronetEngine1 to make a request to mUrl404. 813 makeRequestAndCheckStatus(cronetEngine1, mUrl404, 404); 814 815 // Use cronetEngine2 to make a request to mUrl500. 816 makeRequestAndCheckStatus(cronetEngine2, mUrl500, 500); 817 818 cronetEngine1.stopNetLog(); 819 cronetEngine2.stopNetLog(); 820 821 assertTrue(logFile1.exists()); 822 assertTrue(logFile2.exists()); 823 assertTrue(logFile1.length() != 0); 824 assertTrue(logFile2.length() != 0); 825 826 // Make sure both files contain the two requests made separately using 827 // different engines. 828 assertTrue(containsStringInNetLog(logFile1, mUrl404)); 829 assertTrue(containsStringInNetLog(logFile1, mUrl500)); 830 assertTrue(containsStringInNetLog(logFile2, mUrl404)); 831 assertTrue(containsStringInNetLog(logFile2, mUrl500)); 832 833 FileUtils.recursivelyDeleteFile(netLogDir1, FileUtils.DELETE_ALL); 834 assertFalse(netLogDir1.exists()); 835 FileUtils.recursivelyDeleteFile(netLogDir2, FileUtils.DELETE_ALL); 836 assertFalse(netLogDir2.exists()); 837 } 838 createCronetEngineWithCache(int cacheType)839 private HttpEngine createCronetEngineWithCache(int cacheType) { 840 HttpEngine.Builder builder = new HttpEngine.Builder(getContext()); 841 if (cacheType == HttpEngine.Builder.HTTP_CACHE_DISK 842 || cacheType == HttpEngine.Builder.HTTP_CACHE_DISK_NO_HTTP) { 843 builder.setStoragePath(getTestStorage(getContext())); 844 } 845 builder.setEnableHttpCache(cacheType, 100 * 1024); 846 // Don't check the return value here, because startNativeTestServer() returns false when the 847 // NativeTestServer is already running and this method needs to be called twice without 848 // shutting down the NativeTestServer in between. 849 NativeTestServer.startNativeTestServer(getContext()); 850 return builder.build(); 851 } 852 853 @Test 854 @SmallTest 855 @OnlyRunNativeCronet 856 // Tests that if CronetEngine is shut down on the network thread, an appropriate exception 857 // is thrown. testShutDownEngineOnNetworkThread()858 public void testShutDownEngineOnNetworkThread() throws Exception { 859 final HttpEngine cronetEngine = 860 createCronetEngineWithCache(HttpEngine.Builder.HTTP_CACHE_DISK); 861 String url = NativeTestServer.getFileURL("/cacheable.txt"); 862 // Make a request to a cacheable resource. 863 checkRequestCaching(cronetEngine, url, false); 864 865 final AtomicReference<Throwable> thrown = new AtomicReference<>(); 866 // Shut down the server. 867 NativeTestServer.shutdownNativeTestServer(); 868 class CancelUrlRequestCallback extends TestUrlRequestCallback { 869 @Override 870 public void onResponseStarted(UrlRequest request, UrlResponseInfo info) { 871 super.onResponseStarted(request, info); 872 request.cancel(); 873 // Shut down CronetEngine immediately after request is destroyed. 874 try { 875 cronetEngine.shutdown(); 876 } catch (Exception e) { 877 thrown.set(e); 878 } 879 } 880 881 @Override 882 public void onSucceeded(UrlRequest request, UrlResponseInfo info) { 883 // onSucceeded will not happen, because the request is canceled 884 // after sending first read and the executor is single threaded. 885 throw new RuntimeException("Unexpected"); 886 } 887 888 @Override 889 public void onFailed(UrlRequest request, UrlResponseInfo info, HttpException error) { 890 throw new RuntimeException("Unexpected"); 891 } 892 } 893 Executor directExecutor = new Executor() { 894 @Override 895 public void execute(Runnable command) { 896 command.run(); 897 } 898 }; 899 CancelUrlRequestCallback callback = new CancelUrlRequestCallback(); 900 callback.setAllowDirectExecutor(true); 901 UrlRequest.Builder urlRequestBuilder = 902 cronetEngine.newUrlRequestBuilder(url, callback, directExecutor); 903 urlRequestBuilder.setDirectExecutorAllowed(true); 904 urlRequestBuilder.build().start(); 905 callback.blockForDone(); 906 assertTrue(thrown.get() instanceof RuntimeException); 907 cronetEngine.shutdown(); 908 } 909 910 @Test 911 @SmallTest 912 @OnlyRunNativeCronet 913 // Tests that if CronetEngine is shut down when reading from disk cache, 914 // there isn't a crash. See crbug.com/486120. testShutDownEngineWhenReadingFromDiskCache()915 public void testShutDownEngineWhenReadingFromDiskCache() throws Exception { 916 final HttpEngine cronetEngine = 917 createCronetEngineWithCache(HttpEngine.Builder.HTTP_CACHE_DISK); 918 String url = NativeTestServer.getFileURL("/cacheable.txt"); 919 // Make a request to a cacheable resource. 920 checkRequestCaching(cronetEngine, url, false); 921 922 // Shut down the server. 923 NativeTestServer.shutdownNativeTestServer(); 924 class CancelUrlRequestCallback extends TestUrlRequestCallback { 925 @Override 926 public void onResponseStarted(UrlRequest request, UrlResponseInfo info) { 927 super.onResponseStarted(request, info); 928 request.cancel(); 929 // Shut down CronetEngine immediately after request is destroyed. 930 cronetEngine.shutdown(); 931 } 932 933 @Override 934 public void onSucceeded(UrlRequest request, UrlResponseInfo info) { 935 // onSucceeded will not happen, because the request is canceled 936 // after sending first read and the executor is single threaded. 937 throw new RuntimeException("Unexpected"); 938 } 939 940 @Override 941 public void onFailed(UrlRequest request, UrlResponseInfo info, HttpException error) { 942 throw new RuntimeException("Unexpected"); 943 } 944 } 945 CancelUrlRequestCallback callback = new CancelUrlRequestCallback(); 946 UrlRequest.Builder urlRequestBuilder = 947 cronetEngine.newUrlRequestBuilder(url, callback.getExecutor(), callback); 948 urlRequestBuilder.build().start(); 949 callback.blockForDone(); 950 assertEquals(200, callback.mResponseInfo.getHttpStatusCode()); 951 assertTrue(callback.mResponseInfo.wasCached()); 952 assertTrue(callback.mOnCanceledCalled); 953 } 954 955 @Test 956 @SmallTest 957 @OnlyRunNativeCronet testNetLogAfterShutdown()958 public void testNetLogAfterShutdown() throws Exception { 959 final CronetTestFramework testFramework = mTestRule.startCronetTestFramework(); 960 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 961 UrlRequest.Builder urlRequestBuilder = testFramework.mCronetEngine.newUrlRequestBuilder( 962 mUrl, callback.getExecutor(), callback); 963 urlRequestBuilder.build().start(); 964 callback.blockForDone(); 965 testFramework.mCronetEngine.shutdown(); 966 967 File directory = new File(PathUtils.getDataDirectory()); 968 File file = File.createTempFile("cronet", "json", directory); 969 try { 970 testFramework.mCronetEngine.startNetLogToFile(file.getPath(), false); 971 fail("Should throw an exception."); 972 } catch (Exception e) { 973 assertEquals("Engine is shut down.", e.getMessage()); 974 } 975 assertFalse(hasBytesInNetLog(file)); 976 assertTrue(file.delete()); 977 assertTrue(!file.exists()); 978 } 979 980 @Test 981 @SmallTest 982 @OnlyRunNativeCronet testBoundedFileNetLogAfterShutdown()983 public void testBoundedFileNetLogAfterShutdown() throws Exception { 984 final CronetTestFramework testFramework = mTestRule.startCronetTestFramework(); 985 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 986 UrlRequest.Builder urlRequestBuilder = testFramework.mCronetEngine.newUrlRequestBuilder( 987 mUrl, callback, callback.getExecutor()); 988 urlRequestBuilder.build().start(); 989 callback.blockForDone(); 990 testFramework.mCronetEngine.shutdown(); 991 992 File directory = new File(PathUtils.getDataDirectory()); 993 File netLogDir = new File(directory, "NetLog"); 994 assertFalse(netLogDir.exists()); 995 assertTrue(netLogDir.mkdir()); 996 File logFile = new File(netLogDir, "netlog.json"); 997 try { 998 testFramework.mCronetEngine.startNetLogToDisk( 999 netLogDir.getPath(), false, MAX_FILE_SIZE); 1000 fail("Should throw an exception."); 1001 } catch (Exception e) { 1002 assertEquals("Engine is shut down.", e.getMessage()); 1003 } 1004 assertFalse(logFile.exists()); 1005 FileUtils.recursivelyDeleteFile(netLogDir, FileUtils.DELETE_ALL); 1006 assertFalse(netLogDir.exists()); 1007 } 1008 1009 @Test 1010 @SmallTest 1011 @OnlyRunNativeCronet testNetLogStartMultipleTimes()1012 public void testNetLogStartMultipleTimes() throws Exception { 1013 final CronetTestFramework testFramework = mTestRule.startCronetTestFramework(); 1014 File directory = new File(PathUtils.getDataDirectory()); 1015 File file = File.createTempFile("cronet", "json", directory); 1016 // Start NetLog multiple times. 1017 testFramework.mCronetEngine.startNetLogToFile(file.getPath(), false); 1018 testFramework.mCronetEngine.startNetLogToFile(file.getPath(), false); 1019 testFramework.mCronetEngine.startNetLogToFile(file.getPath(), false); 1020 testFramework.mCronetEngine.startNetLogToFile(file.getPath(), false); 1021 // Start a request. 1022 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1023 UrlRequest.Builder urlRequestBuilder = testFramework.mCronetEngine.newUrlRequestBuilder( 1024 mUrl, callback.getExecutor(), callback); 1025 urlRequestBuilder.build().start(); 1026 callback.blockForDone(); 1027 testFramework.mCronetEngine.stopNetLog(); 1028 assertTrue(file.exists()); 1029 assertTrue(file.length() != 0); 1030 assertFalse(hasBytesInNetLog(file)); 1031 assertTrue(file.delete()); 1032 assertTrue(!file.exists()); 1033 } 1034 1035 @Test 1036 @SmallTest 1037 @OnlyRunNativeCronet testBoundedFileNetLogStartMultipleTimes()1038 public void testBoundedFileNetLogStartMultipleTimes() throws Exception { 1039 final CronetTestFramework testFramework = mTestRule.startCronetTestFramework(); 1040 File directory = new File(PathUtils.getDataDirectory()); 1041 File netLogDir = new File(directory, "NetLog"); 1042 assertFalse(netLogDir.exists()); 1043 assertTrue(netLogDir.mkdir()); 1044 File logFile = new File(netLogDir, "netlog.json"); 1045 // Start NetLog multiple times. This should be equivalent to starting NetLog 1046 // once. Each subsequent start (without calling stopNetLog) should be a no-op. 1047 testFramework.mCronetEngine.startNetLogToDisk(netLogDir.getPath(), false, MAX_FILE_SIZE); 1048 testFramework.mCronetEngine.startNetLogToDisk(netLogDir.getPath(), false, MAX_FILE_SIZE); 1049 testFramework.mCronetEngine.startNetLogToDisk(netLogDir.getPath(), false, MAX_FILE_SIZE); 1050 testFramework.mCronetEngine.startNetLogToDisk(netLogDir.getPath(), false, MAX_FILE_SIZE); 1051 // Start a request. 1052 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1053 UrlRequest.Builder urlRequestBuilder = testFramework.mCronetEngine.newUrlRequestBuilder( 1054 mUrl, callback, callback.getExecutor()); 1055 urlRequestBuilder.build().start(); 1056 callback.blockForDone(); 1057 testFramework.mCronetEngine.stopNetLog(); 1058 assertTrue(logFile.exists()); 1059 assertTrue(logFile.length() != 0); 1060 assertFalse(hasBytesInNetLog(logFile)); 1061 FileUtils.recursivelyDeleteFile(netLogDir, FileUtils.DELETE_ALL); 1062 assertFalse(netLogDir.exists()); 1063 } 1064 1065 @Test 1066 @SmallTest 1067 @OnlyRunNativeCronet testNetLogStopMultipleTimes()1068 public void testNetLogStopMultipleTimes() throws Exception { 1069 final CronetTestFramework testFramework = mTestRule.startCronetTestFramework(); 1070 File directory = new File(PathUtils.getDataDirectory()); 1071 File file = File.createTempFile("cronet", "json", directory); 1072 testFramework.mCronetEngine.startNetLogToFile(file.getPath(), false); 1073 // Start a request. 1074 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1075 UrlRequest.Builder urlRequestBuilder = testFramework.mCronetEngine.newUrlRequestBuilder( 1076 mUrl, callback.getExecutor(), callback); 1077 urlRequestBuilder.build().start(); 1078 callback.blockForDone(); 1079 // Stop NetLog multiple times. 1080 testFramework.mCronetEngine.stopNetLog(); 1081 testFramework.mCronetEngine.stopNetLog(); 1082 testFramework.mCronetEngine.stopNetLog(); 1083 testFramework.mCronetEngine.stopNetLog(); 1084 testFramework.mCronetEngine.stopNetLog(); 1085 assertTrue(file.exists()); 1086 assertTrue(file.length() != 0); 1087 assertFalse(hasBytesInNetLog(file)); 1088 assertTrue(file.delete()); 1089 assertTrue(!file.exists()); 1090 } 1091 1092 @Test 1093 @SmallTest 1094 @OnlyRunNativeCronet testBoundedFileNetLogStopMultipleTimes()1095 public void testBoundedFileNetLogStopMultipleTimes() throws Exception { 1096 final CronetTestFramework testFramework = mTestRule.startCronetTestFramework(); 1097 File directory = new File(PathUtils.getDataDirectory()); 1098 File netLogDir = new File(directory, "NetLog"); 1099 assertFalse(netLogDir.exists()); 1100 assertTrue(netLogDir.mkdir()); 1101 File logFile = new File(netLogDir, "netlog.json"); 1102 testFramework.mCronetEngine.startNetLogToDisk(netLogDir.getPath(), false, MAX_FILE_SIZE); 1103 // Start a request. 1104 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1105 UrlRequest.Builder urlRequestBuilder = testFramework.mCronetEngine.newUrlRequestBuilder( 1106 mUrl, callback, callback.getExecutor()); 1107 urlRequestBuilder.build().start(); 1108 callback.blockForDone(); 1109 // Stop NetLog multiple times. This should be equivalent to stopping NetLog once. 1110 // Each subsequent stop (without calling startNetLogToDisk first) should be a no-op. 1111 testFramework.mCronetEngine.stopNetLog(); 1112 testFramework.mCronetEngine.stopNetLog(); 1113 testFramework.mCronetEngine.stopNetLog(); 1114 testFramework.mCronetEngine.stopNetLog(); 1115 testFramework.mCronetEngine.stopNetLog(); 1116 assertTrue(logFile.exists()); 1117 assertTrue(logFile.length() != 0); 1118 assertFalse(hasBytesInNetLog(logFile)); 1119 FileUtils.recursivelyDeleteFile(netLogDir, FileUtils.DELETE_ALL); 1120 assertFalse(netLogDir.exists()); 1121 } 1122 1123 @Test 1124 @SmallTest 1125 @OnlyRunNativeCronet testNetLogWithBytes()1126 public void testNetLogWithBytes() throws Exception { 1127 Context context = getContext(); 1128 File directory = new File(PathUtils.getDataDirectory()); 1129 File file = File.createTempFile("cronet", "json", directory); 1130 HttpEngine cronetEngine = new HttpEngine.Builder(context).build(); 1131 // Start NetLog with logAll as true. 1132 cronetEngine.startNetLogToFile(file.getPath(), true); 1133 // Start a request. 1134 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1135 UrlRequest.Builder urlRequestBuilder = 1136 cronetEngine.newUrlRequestBuilder(mUrl, callback.getExecutor(), callback); 1137 urlRequestBuilder.build().start(); 1138 callback.blockForDone(); 1139 cronetEngine.stopNetLog(); 1140 assertTrue(file.exists()); 1141 assertTrue(file.length() != 0); 1142 assertTrue(hasBytesInNetLog(file)); 1143 assertTrue(file.delete()); 1144 assertTrue(!file.exists()); 1145 } 1146 1147 @Test 1148 @SmallTest 1149 @OnlyRunNativeCronet testBoundedFileNetLogWithBytes()1150 public void testBoundedFileNetLogWithBytes() throws Exception { 1151 Context context = getContext(); 1152 File directory = new File(PathUtils.getDataDirectory()); 1153 File netLogDir = new File(directory, "NetLog"); 1154 assertFalse(netLogDir.exists()); 1155 assertTrue(netLogDir.mkdir()); 1156 File logFile = new File(netLogDir, "netlog.json"); 1157 ExperimentalHttpEngine cronetEngine = 1158 new ExperimentalHttpEngine.Builder(context).build(); 1159 // Start NetLog with logAll as true. 1160 cronetEngine.startNetLogToDisk(netLogDir.getPath(), true, MAX_FILE_SIZE); 1161 // Start a request. 1162 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1163 UrlRequest.Builder urlRequestBuilder = 1164 cronetEngine.newUrlRequestBuilder(mUrl, callback, callback.getExecutor()); 1165 urlRequestBuilder.build().start(); 1166 callback.blockForDone(); 1167 cronetEngine.stopNetLog(); 1168 1169 assertTrue(logFile.exists()); 1170 assertTrue(logFile.length() != 0); 1171 assertTrue(hasBytesInNetLog(logFile)); 1172 FileUtils.recursivelyDeleteFile(netLogDir, FileUtils.DELETE_ALL); 1173 assertFalse(netLogDir.exists()); 1174 } 1175 hasBytesInNetLog(File logFile)1176 private boolean hasBytesInNetLog(File logFile) throws Exception { 1177 return containsStringInNetLog(logFile, "\"bytes\""); 1178 } 1179 containsStringInNetLog(File logFile, String content)1180 private boolean containsStringInNetLog(File logFile, String content) throws Exception { 1181 BufferedReader logReader = new BufferedReader(new FileReader(logFile)); 1182 try { 1183 String logLine; 1184 while ((logLine = logReader.readLine()) != null) { 1185 if (logLine.contains(content)) { 1186 return true; 1187 } 1188 } 1189 return false; 1190 } finally { 1191 logReader.close(); 1192 } 1193 } 1194 1195 /** 1196 * Helper method to make a request to {@code url}, wait for it to 1197 * complete, and check that the status code is the same as {@code expectedStatusCode}. 1198 */ makeRequestAndCheckStatus( HttpEngine engine, String url, int expectedStatusCode)1199 private void makeRequestAndCheckStatus( 1200 HttpEngine engine, String url, int expectedStatusCode) { 1201 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1202 UrlRequest request = 1203 engine.newUrlRequestBuilder(url, callback.getExecutor(), callback).build(); 1204 request.start(); 1205 callback.blockForDone(); 1206 assertEquals(expectedStatusCode, callback.mResponseInfo.getHttpStatusCode()); 1207 } 1208 checkRequestCaching(HttpEngine engine, String url, boolean expectCached)1209 private void checkRequestCaching(HttpEngine engine, String url, boolean expectCached) { 1210 checkRequestCaching(engine, url, expectCached, false); 1211 } 1212 checkRequestCaching( HttpEngine engine, String url, boolean expectCached, boolean disableCache)1213 private void checkRequestCaching( 1214 HttpEngine engine, String url, boolean expectCached, boolean disableCache) { 1215 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1216 UrlRequest.Builder urlRequestBuilder = 1217 engine.newUrlRequestBuilder(url, callback.getExecutor(), callback); 1218 if (disableCache) { 1219 urlRequestBuilder.setCacheDisabled(true); 1220 } 1221 urlRequestBuilder.build().start(); 1222 callback.blockForDone(); 1223 assertEquals(expectCached, callback.mResponseInfo.wasCached()); 1224 assertEquals("this is a cacheable file\n", callback.mResponseAsString); 1225 } 1226 1227 @Test 1228 @SmallTest 1229 @OnlyRunNativeCronet testEnableHttpCacheDisabled()1230 public void testEnableHttpCacheDisabled() throws Exception { 1231 HttpEngine cronetEngine = 1232 createCronetEngineWithCache(HttpEngine.Builder.HTTP_CACHE_DISABLED); 1233 String url = NativeTestServer.getFileURL("/cacheable.txt"); 1234 checkRequestCaching(cronetEngine, url, false); 1235 checkRequestCaching(cronetEngine, url, false); 1236 checkRequestCaching(cronetEngine, url, false); 1237 cronetEngine.shutdown(); 1238 } 1239 1240 @Test 1241 @SmallTest testEnableHttpCacheInMemory()1242 public void testEnableHttpCacheInMemory() throws Exception { 1243 HttpEngine cronetEngine = 1244 createCronetEngineWithCache(HttpEngine.Builder.HTTP_CACHE_IN_MEMORY); 1245 String url = NativeTestServer.getFileURL("/cacheable.txt"); 1246 checkRequestCaching(cronetEngine, url, false); 1247 checkRequestCaching(cronetEngine, url, true); 1248 NativeTestServer.shutdownNativeTestServer(); 1249 checkRequestCaching(cronetEngine, url, true); 1250 cronetEngine.shutdown(); 1251 } 1252 1253 @Test 1254 @SmallTest testEnableHttpCacheDisk()1255 public void testEnableHttpCacheDisk() throws Exception { 1256 HttpEngine cronetEngine = 1257 createCronetEngineWithCache(HttpEngine.Builder.HTTP_CACHE_DISK); 1258 String url = NativeTestServer.getFileURL("/cacheable.txt"); 1259 checkRequestCaching(cronetEngine, url, false); 1260 checkRequestCaching(cronetEngine, url, true); 1261 NativeTestServer.shutdownNativeTestServer(); 1262 checkRequestCaching(cronetEngine, url, true); 1263 cronetEngine.shutdown(); 1264 } 1265 1266 @Test 1267 @SmallTest 1268 @OnlyRunNativeCronet testNoConcurrentDiskUsage()1269 public void testNoConcurrentDiskUsage() throws Exception { 1270 HttpEngine cronetEngine = 1271 createCronetEngineWithCache(HttpEngine.Builder.HTTP_CACHE_DISK); 1272 try { 1273 createCronetEngineWithCache(HttpEngine.Builder.HTTP_CACHE_DISK); 1274 fail(); 1275 } catch (IllegalStateException e) { 1276 assertEquals("Disk cache storage path already in use", e.getMessage()); 1277 } 1278 String url = NativeTestServer.getFileURL("/cacheable.txt"); 1279 checkRequestCaching(cronetEngine, url, false); 1280 checkRequestCaching(cronetEngine, url, true); 1281 NativeTestServer.shutdownNativeTestServer(); 1282 checkRequestCaching(cronetEngine, url, true); 1283 cronetEngine.shutdown(); 1284 } 1285 1286 @Test 1287 @SmallTest 1288 @OnlyRunNativeCronet testEnableHttpCacheDiskNoHttp()1289 public void testEnableHttpCacheDiskNoHttp() throws Exception { 1290 HttpEngine cronetEngine = 1291 createCronetEngineWithCache(HttpEngine.Builder.HTTP_CACHE_DISK_NO_HTTP); 1292 String url = NativeTestServer.getFileURL("/cacheable.txt"); 1293 checkRequestCaching(cronetEngine, url, false); 1294 checkRequestCaching(cronetEngine, url, false); 1295 checkRequestCaching(cronetEngine, url, false); 1296 1297 // Make a new CronetEngine and try again to make sure the response didn't get cached on the 1298 // first request. See https://crbug.com/743232. 1299 cronetEngine.shutdown(); 1300 cronetEngine = createCronetEngineWithCache(HttpEngine.Builder.HTTP_CACHE_DISK_NO_HTTP); 1301 checkRequestCaching(cronetEngine, url, false); 1302 checkRequestCaching(cronetEngine, url, false); 1303 checkRequestCaching(cronetEngine, url, false); 1304 cronetEngine.shutdown(); 1305 } 1306 1307 @Test 1308 @SmallTest testDisableCache()1309 public void testDisableCache() throws Exception { 1310 HttpEngine cronetEngine = 1311 createCronetEngineWithCache(HttpEngine.Builder.HTTP_CACHE_DISK); 1312 String url = NativeTestServer.getFileURL("/cacheable.txt"); 1313 1314 // When cache is disabled, making a request does not write to the cache. 1315 checkRequestCaching(cronetEngine, url, false, true /** disable cache */); 1316 checkRequestCaching(cronetEngine, url, false); 1317 1318 // When cache is enabled, the second request is cached. 1319 checkRequestCaching(cronetEngine, url, false, true /** disable cache */); 1320 checkRequestCaching(cronetEngine, url, true); 1321 1322 // Shut down the server, next request should have a cached response. 1323 NativeTestServer.shutdownNativeTestServer(); 1324 checkRequestCaching(cronetEngine, url, true); 1325 1326 // Cache is disabled after server is shut down, request should fail. 1327 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1328 UrlRequest.Builder urlRequestBuilder = 1329 cronetEngine.newUrlRequestBuilder(url, callback.getExecutor(), callback); 1330 urlRequestBuilder.setCacheDisabled(true); 1331 urlRequestBuilder.build().start(); 1332 callback.blockForDone(); 1333 assertNotNull(callback.mError); 1334 assertContains("Exception in CronetUrlRequest: net::ERR_CONNECTION_REFUSED", 1335 callback.mError.getMessage()); 1336 cronetEngine.shutdown(); 1337 } 1338 1339 @Test 1340 @SmallTest testEnableHttpCacheDiskNewEngine()1341 public void testEnableHttpCacheDiskNewEngine() throws Exception { 1342 HttpEngine cronetEngine = 1343 createCronetEngineWithCache(HttpEngine.Builder.HTTP_CACHE_DISK); 1344 String url = NativeTestServer.getFileURL("/cacheable.txt"); 1345 checkRequestCaching(cronetEngine, url, false); 1346 checkRequestCaching(cronetEngine, url, true); 1347 NativeTestServer.shutdownNativeTestServer(); 1348 checkRequestCaching(cronetEngine, url, true); 1349 1350 // Shutdown original context and create another that uses the same cache. 1351 cronetEngine.shutdown(); 1352 cronetEngine = 1353 mTestRule.enableDiskCache(new HttpEngine.Builder(getContext())).build(); 1354 checkRequestCaching(cronetEngine, url, true); 1355 cronetEngine.shutdown(); 1356 } 1357 1358 @Test 1359 @SmallTest testInitEngineAndStartRequest()1360 public void testInitEngineAndStartRequest() { 1361 // Immediately make a request after initializing the engine. 1362 HttpEngine cronetEngine = new HttpEngine.Builder(getContext()).build(); 1363 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1364 UrlRequest.Builder urlRequestBuilder = 1365 cronetEngine.newUrlRequestBuilder(mUrl, callback.getExecutor(), callback); 1366 urlRequestBuilder.build().start(); 1367 callback.blockForDone(); 1368 assertEquals(200, callback.mResponseInfo.getHttpStatusCode()); 1369 cronetEngine.shutdown(); 1370 } 1371 1372 @Test 1373 @SmallTest testInitEngineStartTwoRequests()1374 public void testInitEngineStartTwoRequests() throws Exception { 1375 // Make two requests after initializing the context. 1376 HttpEngine cronetEngine = new HttpEngine.Builder(getContext()).build(); 1377 int[] statusCodes = {0, 0}; 1378 String[] urls = {mUrl, mUrl404}; 1379 for (int i = 0; i < 2; i++) { 1380 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1381 UrlRequest.Builder urlRequestBuilder = 1382 cronetEngine.newUrlRequestBuilder(urls[i], callback.getExecutor(), callback); 1383 urlRequestBuilder.build().start(); 1384 callback.blockForDone(); 1385 statusCodes[i] = callback.mResponseInfo.getHttpStatusCode(); 1386 } 1387 assertEquals(200, statusCodes[0]); 1388 assertEquals(404, statusCodes[1]); 1389 cronetEngine.shutdown(); 1390 } 1391 1392 @Test 1393 @SmallTest testInitTwoEnginesSimultaneously()1394 public void testInitTwoEnginesSimultaneously() throws Exception { 1395 // Threads will block on runBlocker to ensure simultaneous execution. 1396 ConditionVariable runBlocker = new ConditionVariable(false); 1397 RequestThread thread1 = new RequestThread(mUrl, runBlocker); 1398 RequestThread thread2 = new RequestThread(mUrl404, runBlocker); 1399 1400 thread1.start(); 1401 thread2.start(); 1402 runBlocker.open(); 1403 thread1.join(); 1404 thread2.join(); 1405 assertEquals(200, thread1.mCallback.mResponseInfo.getHttpStatusCode()); 1406 assertEquals(404, thread2.mCallback.mResponseInfo.getHttpStatusCode()); 1407 } 1408 1409 @Test 1410 @SmallTest testInitTwoEnginesInSequence()1411 public void testInitTwoEnginesInSequence() throws Exception { 1412 ConditionVariable runBlocker = new ConditionVariable(true); 1413 RequestThread thread1 = new RequestThread(mUrl, runBlocker); 1414 RequestThread thread2 = new RequestThread(mUrl404, runBlocker); 1415 1416 thread1.start(); 1417 thread1.join(); 1418 thread2.start(); 1419 thread2.join(); 1420 assertEquals(200, thread1.mCallback.mResponseInfo.getHttpStatusCode()); 1421 assertEquals(404, thread2.mCallback.mResponseInfo.getHttpStatusCode()); 1422 } 1423 1424 @Test 1425 @SmallTest testInitDifferentEngines()1426 public void testInitDifferentEngines() throws Exception { 1427 // Test that concurrently instantiating Cronet context's upon various 1428 // different versions of the same Android Context does not cause crashes 1429 // like crbug.com/453845 1430 HttpEngine firstEngine = new HttpEngine.Builder(getContext()).build(); 1431 HttpEngine secondEngine = new HttpEngine.Builder(getContext()).build(); 1432 HttpEngine thirdEngine = 1433 new HttpEngine.Builder(new ContextWrapper(getContext())).build(); 1434 firstEngine.shutdown(); 1435 secondEngine.shutdown(); 1436 thirdEngine.shutdown(); 1437 } 1438 1439 @Test 1440 @SmallTest 1441 @OnlyRunNativeCronet // Java engine doesn't produce metrics testGetGlobalMetricsDeltas()1442 public void testGetGlobalMetricsDeltas() throws Exception { 1443 final CronetTestFramework testFramework = mTestRule.startCronetTestFramework(); 1444 1445 byte[] delta1 = testFramework.mCronetEngine.getGlobalMetricsDeltas(); 1446 1447 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1448 UrlRequest.Builder builder = testFramework.mCronetEngine.newUrlRequestBuilder( 1449 mUrl, callback.getExecutor(), callback); 1450 builder.build().start(); 1451 callback.blockForDone(); 1452 // Fetch deltas on a different thread the second time to make sure this is permitted. 1453 // See crbug.com/719448 1454 FutureTask<byte[]> task = new FutureTask<byte[]>(new Callable<byte[]>() { 1455 @Override 1456 public byte[] call() { 1457 return testFramework.mCronetEngine.getGlobalMetricsDeltas(); 1458 } 1459 }); 1460 new Thread(task).start(); 1461 byte[] delta2 = task.get(); 1462 assertTrue(delta2.length != 0); 1463 assertFalse(Arrays.equals(delta1, delta2)); 1464 } 1465 1466 @Test 1467 @SmallTest testCronetEngineBuilderConfig()1468 public void testCronetEngineBuilderConfig() throws Exception { 1469 // This is to prompt load of native library. 1470 mTestRule.startCronetTestFramework(); 1471 // Verify CronetEngine.Builder config is passed down accurately to native code. 1472 ExperimentalHttpEngine.Builder builder = 1473 new ExperimentalHttpEngine.Builder(getContext()); 1474 builder.setEnableHttp2(false); 1475 builder.setEnableQuic(true); 1476 builder.addQuicHint("example.com", 12, 34); 1477 builder.setEnableHttpCache(HTTP_CACHE_IN_MEMORY, 54321); 1478 builder.setUserAgent("efgh"); 1479 builder.setExperimentalOptions(""); 1480 builder.setStoragePath(getTestStorage(getContext())); 1481 builder.setEnablePublicKeyPinningBypassForLocalTrustAnchors(false); 1482 CronetUrlRequestContextTestJni.get().verifyUrlRequestContextConfig( 1483 CronetUrlRequestContext.createNativeUrlRequestContextConfig( 1484 CronetTestUtil.getCronetEngineBuilderImpl(builder)), 1485 getTestStorage(getContext())); 1486 } 1487 1488 @Test 1489 @SmallTest testCronetEngineQuicOffConfig()1490 public void testCronetEngineQuicOffConfig() throws Exception { 1491 // This is to prompt load of native library. 1492 mTestRule.startCronetTestFramework(); 1493 // Verify CronetEngine.Builder config is passed down accurately to native code. 1494 ExperimentalHttpEngine.Builder builder = 1495 new ExperimentalHttpEngine.Builder(getContext()); 1496 builder.setEnableHttp2(false); 1497 // QUIC is on by default. Disabling it here to make sure the built config can correctly 1498 // reflect the change. 1499 builder.setEnableQuic(false); 1500 builder.setEnableHttpCache(HTTP_CACHE_IN_MEMORY, 54321); 1501 builder.setExperimentalOptions(""); 1502 builder.setUserAgent("efgh"); 1503 builder.setStoragePath(getTestStorage(getContext())); 1504 builder.setEnablePublicKeyPinningBypassForLocalTrustAnchors(false); 1505 CronetUrlRequestContextTestJni.get().verifyUrlRequestContextQuicOffConfig( 1506 CronetUrlRequestContext.createNativeUrlRequestContextConfig( 1507 CronetTestUtil.getCronetEngineBuilderImpl(builder)), 1508 getTestStorage(getContext())); 1509 } 1510 1511 // Creates a CronetEngine on another thread and then one on the main thread. This shouldn't 1512 // crash. 1513 @Test 1514 @SmallTest testThreadedStartup()1515 public void testThreadedStartup() throws Exception { 1516 final ConditionVariable otherThreadDone = new ConditionVariable(); 1517 final ConditionVariable uiThreadDone = new ConditionVariable(); 1518 new Handler(Looper.getMainLooper()).post(new Runnable() { 1519 @Override 1520 public void run() { 1521 final ExperimentalHttpEngine.Builder builder = 1522 new ExperimentalHttpEngine.Builder(getContext()); 1523 new Thread() { 1524 @Override 1525 public void run() { 1526 HttpEngine cronetEngine = builder.build(); 1527 otherThreadDone.open(); 1528 cronetEngine.shutdown(); 1529 } 1530 } 1531 .start(); 1532 otherThreadDone.block(); 1533 builder.build().shutdown(); 1534 uiThreadDone.open(); 1535 } 1536 }); 1537 assertTrue(uiThreadDone.block(1000)); 1538 } 1539 1540 @Test 1541 @SmallTest testHostResolverRules()1542 public void testHostResolverRules() throws Exception { 1543 String resolverTestHostname = "some-weird-hostname"; 1544 URL testUrl = new URL(mUrl); 1545 ExperimentalHttpEngine.Builder cronetEngineBuilder = 1546 new ExperimentalHttpEngine.Builder(getContext()); 1547 JSONObject hostResolverRules = new JSONObject().put( 1548 "host_resolver_rules", "MAP " + resolverTestHostname + " " + testUrl.getHost()); 1549 JSONObject experimentalOptions = 1550 new JSONObject().put("HostResolverRules", hostResolverRules); 1551 cronetEngineBuilder.setExperimentalOptions(experimentalOptions.toString()); 1552 1553 final HttpEngine cronetEngine = cronetEngineBuilder.build(); 1554 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1555 URL requestUrl = 1556 new URL("http", resolverTestHostname, testUrl.getPort(), testUrl.getFile()); 1557 UrlRequest.Builder urlRequestBuilder = cronetEngine.newUrlRequestBuilder( 1558 requestUrl.toString(), callback.getExecutor(), callback); 1559 urlRequestBuilder.build().start(); 1560 callback.blockForDone(); 1561 assertEquals(200, callback.mResponseInfo.getHttpStatusCode()); 1562 } 1563 1564 /** 1565 * Runs {@code r} on {@code engine}'s network thread. 1566 */ postToNetworkThread(final HttpEngine engine, final Runnable r)1567 private static void postToNetworkThread(final HttpEngine engine, final Runnable r) { 1568 // Works by requesting an invalid URL which results in onFailed() being called, which is 1569 // done through a direct executor which causes onFailed to be run on the network thread. 1570 Executor directExecutor = new Executor() { 1571 @Override 1572 public void execute(Runnable runable) { 1573 runable.run(); 1574 } 1575 }; 1576 UrlRequest.Callback callback = new UrlRequest.Callback() { 1577 @Override 1578 public void onRedirectReceived( 1579 UrlRequest request, UrlResponseInfo responseInfo, String newLocationUrl) {} 1580 @Override 1581 public void onResponseStarted(UrlRequest request, UrlResponseInfo responseInfo) {} 1582 @Override 1583 public void onReadCompleted( 1584 UrlRequest request, UrlResponseInfo responseInfo, ByteBuffer byteBuffer) {} 1585 @Override 1586 public void onSucceeded(UrlRequest request, UrlResponseInfo responseInfo) {} 1587 1588 @Override 1589 public void onFailed( 1590 UrlRequest request, UrlResponseInfo responseInfo, HttpException error) { 1591 r.run(); 1592 } 1593 @Override 1594 public void onCanceled(UrlRequest request, UrlResponseInfo responseInfo) {} 1595 }; 1596 engine.newUrlRequestBuilder("", directExecutor, callback).build().start(); 1597 } 1598 1599 /** 1600 * @returns the thread priority of {@code engine}'s network thread. 1601 */ 1602 private static class ApiHelper { doesContextExistForNetwork(HttpEngine engine, Network network)1603 public static boolean doesContextExistForNetwork(HttpEngine engine, Network network) 1604 throws Exception { 1605 FutureTask<Boolean> task = new FutureTask<Boolean>(new Callable<Boolean>() { 1606 @Override 1607 public Boolean call() { 1608 return CronetTestUtil.doesURLRequestContextExistForTesting(engine, network); 1609 } 1610 }); 1611 postToNetworkThread(engine, task); 1612 return task.get(); 1613 } 1614 } 1615 1616 /** 1617 * @returns the thread priority of {@code engine}'s network thread. 1618 */ getThreadPriority(HttpEngine engine)1619 private int getThreadPriority(HttpEngine engine) throws Exception { 1620 FutureTask<Integer> task = new FutureTask<Integer>(new Callable<Integer>() { 1621 @Override 1622 public Integer call() { 1623 return Process.getThreadPriority(Process.myTid()); 1624 } 1625 }); 1626 postToNetworkThread(engine, task); 1627 return task.get(); 1628 } 1629 1630 @Test 1631 @SmallTest 1632 @RequiresMinApi(6) // setThreadPriority added in API 6: crrev.com/472449 testCronetEngineThreadPriority()1633 public void testCronetEngineThreadPriority() throws Exception { 1634 ExperimentalHttpEngine.Builder builder = 1635 new ExperimentalHttpEngine.Builder(getContext()); 1636 // Try out of bounds thread priorities. 1637 try { 1638 builder.setThreadPriority(-21); 1639 fail(); 1640 } catch (IllegalArgumentException e) { 1641 assertEquals("Thread priority invalid", e.getMessage()); 1642 } 1643 try { 1644 builder.setThreadPriority(20); 1645 fail(); 1646 } catch (IllegalArgumentException e) { 1647 assertEquals("Thread priority invalid", e.getMessage()); 1648 } 1649 // Test that valid thread priority range (-20..19) is working. 1650 for (int threadPriority = -20; threadPriority < 20; threadPriority++) { 1651 builder.setThreadPriority(threadPriority); 1652 HttpEngine engine = builder.build(); 1653 assertEquals(threadPriority, getThreadPriority(engine)); 1654 engine.shutdown(); 1655 } 1656 } 1657 1658 @NativeMethods("cronet_tests") 1659 interface Natives { 1660 // Verifies that CronetEngine.Builder config from testCronetEngineBuilderConfig() is 1661 // properly translated to a native UrlRequestContextConfig. verifyUrlRequestContextConfig(long config, String storagePath)1662 void verifyUrlRequestContextConfig(long config, String storagePath); 1663 1664 // Verifies that CronetEngine.Builder config from testCronetEngineQuicOffConfig() is 1665 // properly translated to a native UrlRequestContextConfig and QUIC is turned off. verifyUrlRequestContextQuicOffConfig(long config, String storagePath)1666 void verifyUrlRequestContextQuicOffConfig(long config, String storagePath); 1667 } 1668 } 1669