From 99f199f2eedd9b6caed00cbd3526d238fcf94518 Mon Sep 17 00:00:00 2001 From: xunjieli Date: Fri, 29 May 2015 14:15:30 -0700 Subject: [PATCH] [Cronet] Enable persistence mode for Sdch This CL adds persistence mode for Sdch metadata in Cronet for the new async API. This CL also added unit tests to make sure persistence work. BUG=414885 Review URL: https://codereview.chromium.org/1133883002 Cr-Commit-Position: refs/heads/master@{#332044} --- components/cronet.gypi | 3 + .../android/cronet_url_request_context_adapter.cc | 24 ++ .../android/cronet_url_request_context_adapter.h | 13 ++ .../src/org/chromium/net/ChromiumUrlRequest.java | 4 +- .../chromium/net/ChromiumUrlRequestContext.java | 12 +- components/cronet/android/test/cronet_test_jni.cc | 4 +- .../javatests/src/org/chromium/net/SdchTest.java | 244 +++++++++++++-------- .../chromium/net/TestHttpUrlRequestListener.java | 2 - components/cronet/android/test/sdch_test_util.cc | 143 ++++++++++++ components/cronet/android/test/sdch_test_util.h | 16 ++ .../src/org/chromium/net/CronetTestActivity.java | 12 +- .../src/org/chromium/net/NativeTestServer.java | 2 +- .../test/src/org/chromium/net/SdchObserver.java | 61 ++++++ 13 files changed, 427 insertions(+), 113 deletions(-) create mode 100644 components/cronet/android/test/sdch_test_util.cc create mode 100644 components/cronet/android/test/sdch_test_util.h create mode 100644 components/cronet/android/test/src/org/chromium/net/SdchObserver.java diff --git a/components/cronet.gypi b/components/cronet.gypi index b67cb198c147..cdc2791525ba 100644 --- a/components/cronet.gypi +++ b/components/cronet.gypi @@ -283,6 +283,7 @@ 'cronet/android/test/src/org/chromium/net/NativeTestServer.java', 'cronet/android/test/src/org/chromium/net/NetworkChangeNotifierUtil.java', 'cronet/android/test/src/org/chromium/net/QuicTestServer.java', + 'cronet/android/test/src/org/chromium/net/SdchObserver.java', 'cronet/android/test/src/org/chromium/net/TestUploadDataStreamHandler.java', ], 'variables': { @@ -301,6 +302,8 @@ 'cronet/android/test/native_test_server.h', 'cronet/android/test/quic_test_server.cc', 'cronet/android/test/quic_test_server.h', + 'cronet/android/test/sdch_test_util.cc', + 'cronet/android/test/sdch_test_util.h', 'cronet/android/test/test_upload_data_stream_handler.cc', 'cronet/android/test/test_upload_data_stream_handler.h', 'cronet/android/test/network_change_notifier_util.cc', diff --git a/components/cronet/android/cronet_url_request_context_adapter.cc b/components/cronet/android/cronet_url_request_context_adapter.cc index 2f736a9dc1c3..7cab0d9e281c 100644 --- a/components/cronet/android/cronet_url_request_context_adapter.cc +++ b/components/cronet/android/cronet_url_request_context_adapter.cc @@ -11,6 +11,7 @@ #include "base/files/scoped_file.h" #include "base/logging.h" #include "base/memory/scoped_vector.h" +#include "base/prefs/pref_filter.h" #include "base/single_thread_task_runner.h" #include "base/values.h" #include "components/cronet/url_request_context_config.h" @@ -185,6 +186,18 @@ void CronetURLRequestContextAdapter::InitializeOnNetworkThread( context_builder.set_net_log(net_log.release()); context_builder.set_proxy_config_service(proxy_config_service_.release()); config->ConfigureURLRequestContextBuilder(&context_builder); + + // Set up pref file if storage path is specified. + // TODO(xunjieli): maybe get rid of the condition on sdch. + if (!config->storage_path.empty() && config->enable_sdch) { + base::FilePath filepath(config->storage_path); + filepath = filepath.Append(FILE_PATH_LITERAL("local_prefs.json")); + json_pref_store_ = new JsonPrefStore( + filepath, GetFileThread()->task_runner(), scoped_ptr()); + json_pref_store_->ReadPrefsAsync(nullptr); + context_builder.SetFileTaskRunner(GetFileThread()->task_runner()); + } + context_.reset(context_builder.Build()); default_load_flags_ = net::LOAD_DO_NOT_SAVE_COOKIES | @@ -196,6 +209,8 @@ void CronetURLRequestContextAdapter::InitializeOnNetworkThread( DCHECK(context_->sdch_manager()); sdch_owner_.reset( new net::SdchOwner(context_->sdch_manager(), context_.get())); + if (json_pref_store_) + sdch_owner_->EnablePersistentStorage(json_pref_store_.get()); } // Currently (circa M39) enabling QUIC requires setting probability threshold. @@ -352,6 +367,15 @@ void CronetURLRequestContextAdapter::StopNetLogOnNetworkThread() { } } +base::Thread* CronetURLRequestContextAdapter::GetFileThread() { + DCHECK(GetNetworkTaskRunner()->BelongsToCurrentThread()); + if (!file_thread_) { + file_thread_.reset(new base::Thread("Network File Thread")); + file_thread_->Start(); + } + return file_thread_.get(); +} + // Creates RequestContextAdater if config is valid URLRequestContextConfig, // returns 0 otherwise. static jlong CreateRequestContextAdapter(JNIEnv* env, diff --git a/components/cronet/android/cronet_url_request_context_adapter.h b/components/cronet/android/cronet_url_request_context_adapter.h index 6366c5d03d3d..5b6cccda8945 100644 --- a/components/cronet/android/cronet_url_request_context_adapter.h +++ b/components/cronet/android/cronet_url_request_context_adapter.h @@ -15,6 +15,7 @@ #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" +#include "base/prefs/json_pref_store.h" #include "base/threading/thread.h" namespace base { @@ -91,13 +92,25 @@ class CronetURLRequestContextAdapter { void StopNetLogOnNetworkThread(); + // Gets the file thread. Create one if there is none. + base::Thread* GetFileThread(); + // Network thread is owned by |this|, but is destroyed from java thread. base::Thread* network_thread_; + + // File thread should be destroyed last. + scoped_ptr file_thread_; + // |write_to_file_observer_| and |context_| should only be accessed on // network thread. scoped_ptr write_to_file_observer_; scoped_ptr context_; scoped_ptr proxy_config_service_; + + // |sdch_owner_| should be destroyed before |json_pref_store_|, because + // tearing down |sdch_owner_| forces |json_pref_store_| to flush pending + // writes to the disk. + scoped_refptr json_pref_store_; scoped_ptr sdch_owner_; // Context config is only valid untng context is initialized. diff --git a/components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequest.java b/components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequest.java index f369e069494f..a07ce53c06a7 100644 --- a/components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequest.java +++ b/components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequest.java @@ -116,9 +116,7 @@ public class ChromiumUrlRequest implements HttpUrlRequest { mHeaders = headers; mSink = sink; mUrlRequestAdapter = nativeCreateRequestAdapter( - mRequestContext.getChromiumUrlRequestContextAdapter(), - mUrl, - mPriority); + mRequestContext.getUrlRequestContextAdapter(), mUrl, mPriority); mListener = listener; } diff --git a/components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequestContext.java b/components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequestContext.java index 3a35828a5093..3bdd19cff83e 100644 --- a/components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequestContext.java +++ b/components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequestContext.java @@ -12,7 +12,6 @@ import android.util.Log; import org.chromium.base.CalledByNative; import org.chromium.base.JNINamespace; -import org.chromium.base.VisibleForTesting; /** * Provides context for the native HTTP operations. @@ -102,15 +101,6 @@ public class ChromiumUrlRequestContext { nativeStopNetLog(mChromiumUrlRequestContextAdapter); } - /** - * Returns the native URLRequestContextAdapter pointer. - * Currently this method is only used in testing. - */ - @VisibleForTesting - long getUrlRequestContextAdapterForTesting() { - return mChromiumUrlRequestContextAdapter; - } - @CalledByNative private void initNetworkThread() { Thread.currentThread().setName("ChromiumNet"); @@ -123,7 +113,7 @@ public class ChromiumUrlRequestContext { super.finalize(); } - protected long getChromiumUrlRequestContextAdapter() { + protected long getUrlRequestContextAdapter() { return mChromiumUrlRequestContextAdapter; } diff --git a/components/cronet/android/test/cronet_test_jni.cc b/components/cronet/android/test/cronet_test_jni.cc index 73649699458e..a487bf2e2ca4 100644 --- a/components/cronet/android/test/cronet_test_jni.cc +++ b/components/cronet/android/test/cronet_test_jni.cc @@ -12,6 +12,7 @@ #include "native_test_server.h" #include "network_change_notifier_util.h" #include "quic_test_server.h" +#include "sdch_test_util.h" #include "test_upload_data_stream_handler.h" namespace { @@ -19,8 +20,9 @@ namespace { const base::android::RegistrationMethod kCronetTestsRegisteredMethods[] = { {"MockUrlRequestJobFactory", cronet::RegisterMockUrlRequestJobFactory}, {"NativeTestServer", cronet::RegisterNativeTestServer}, - {"QuicTestServer", cronet::RegisterQuicTestServer}, {"NetworkChangeNotifierUtil", cronet::RegisterNetworkChangeNotifierUtil}, + {"QuicTestServer", cronet::RegisterQuicTestServer}, + {"SdchTestUtil", cronet::RegisterSdchTestUtil}, {"TestUploadDataStreamHandlerRegisterJni", cronet::TestUploadDataStreamHandlerRegisterJni}, }; diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/SdchTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/SdchTest.java index 18c3d729a7d2..ca4a426c3778 100644 --- a/components/cronet/android/test/javatests/src/org/chromium/net/SdchTest.java +++ b/components/cronet/android/test/javatests/src/org/chromium/net/SdchTest.java @@ -4,12 +4,18 @@ package org.chromium.net; +import android.os.ConditionVariable; import android.test.suitebuilder.annotation.SmallTest; import org.chromium.base.test.util.Feature; +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; /** @@ -18,68 +24,79 @@ import java.util.Map; public class SdchTest extends CronetTestBase { private CronetTestActivity mActivity; - private void setUp(boolean enableSdch) { - UrlRequestContextConfig config = new UrlRequestContextConfig(); - config.enableSDCH(enableSdch); - config.setLibraryName("cronet_tests"); - config.enableHttpCache(UrlRequestContextConfig.HttpCache.IN_MEMORY, 100 * 1024); - String[] commandLineArgs = {CronetTestActivity.CONFIG_KEY, config.toString()}; - mActivity = launchCronetTestAppWithUrlAndCommandLineArgs(null, commandLineArgs); - mActivity.startNetLog(); - - // Registers custom DNS mapping for legacy ChromiumUrlRequestFactory. - ChromiumUrlRequestFactory factory = (ChromiumUrlRequestFactory) mActivity.mRequestFactory; - long legacyAdapter = factory.getRequestContext().getUrlRequestContextAdapterForTesting(); - assertTrue(legacyAdapter != 0); - NativeTestServer.registerHostResolverProc(legacyAdapter, true); - - // Registers custom DNS mapping for CronetUrlRequestContext. - CronetUrlRequestContext requestContext = - (CronetUrlRequestContext) mActivity.mUrlRequestContext; - long adapter = requestContext.getUrlRequestContextAdapter(); - assertTrue(adapter != 0); - NativeTestServer.registerHostResolverProc(adapter, false); - - // Starts NativeTestServer. + private enum Sdch { + ENABLED, + DISABLED, + } + + private enum Api { + LEGACY, + ASYNC, + } + + private void setUp(Sdch setting, Api api) { + List commandLineArgs = new ArrayList(); + commandLineArgs.add(CronetTestActivity.CACHE_KEY); + commandLineArgs.add(CronetTestActivity.CACHE_DISK); + if (setting == Sdch.ENABLED) { + commandLineArgs.add(CronetTestActivity.SDCH_KEY); + commandLineArgs.add(CronetTestActivity.SDCH_ENABLE); + } + + String[] args = new String[commandLineArgs.size()]; + mActivity = + launchCronetTestAppWithUrlAndCommandLineArgs(null, commandLineArgs.toArray(args)); + long urlRequestContextAdapter = (api == Api.LEGACY) + ? getContextAdapter((ChromiumUrlRequestFactory) mActivity.mRequestFactory) + : getContextAdapter((CronetUrlRequestContext) mActivity.mUrlRequestContext); + NativeTestServer.registerHostResolverProc(urlRequestContextAdapter, api == Api.LEGACY); + // Start NativeTestServer. assertTrue(NativeTestServer.startNativeTestServer(getInstrumentation().getTargetContext())); } @Override protected void tearDown() throws Exception { NativeTestServer.shutdownNativeTestServer(); - mActivity.stopNetLog(); super.tearDown(); } @SmallTest @Feature({"Cronet"}) - public void testSdchEnabled_LegacyAPI() throws Exception { - setUp(true); + public void testSdchEnabled_LegacyApi() throws Exception { + setUp(Sdch.ENABLED, Api.LEGACY); + String targetUrl = NativeTestServer.getSdchURL() + "/sdch/test"; + long contextAdapter = + getContextAdapter((ChromiumUrlRequestFactory) mActivity.mRequestFactory); + DictionaryAddedObserver observer = + new DictionaryAddedObserver(targetUrl, contextAdapter, true /** Legacy Api */); + // Make a request to /sdch/index which advertises the dictionary. - TestHttpUrlRequestListener listener1 = startAndWaitForComplete_LegacyAPI( - NativeTestServer.getSdchURL() + "/sdch/index?q=LeQxM80O"); + TestHttpUrlRequestListener listener1 = + startAndWaitForComplete_LegacyApi(mActivity.mRequestFactory, + NativeTestServer.getSdchURL() + "/sdch/index?q=LeQxM80O"); assertEquals(200, listener1.mHttpStatusCode); assertEquals("This is an index page.\n", listener1.mResponseAsString); assertEquals(Arrays.asList("/sdch/dict/LeQxM80O"), listener1.mResponseHeaders.get("Get-Dictionary")); - waitForDictionaryAdded("LeQxM80O", true); + observer.waitForDictionaryAdded(); // Make a request to fetch encoded response at /sdch/test. TestHttpUrlRequestListener listener2 = - startAndWaitForComplete_LegacyAPI(NativeTestServer.getSdchURL() + "/sdch/test"); + startAndWaitForComplete_LegacyApi(mActivity.mRequestFactory, targetUrl); assertEquals(200, listener2.mHttpStatusCode); assertEquals("The quick brown fox jumps over the lazy dog.\n", listener2.mResponseAsString); } @SmallTest @Feature({"Cronet"}) - public void testSdchDisabled_LegacyAPI() throws Exception { - setUp(false); + public void testSdchDisabled_LegacyApi() throws Exception { + setUp(Sdch.DISABLED, Api.LEGACY); // Make a request to /sdch/index. // Since Sdch is not enabled, no dictionary should be advertised. - TestHttpUrlRequestListener listener = startAndWaitForComplete_LegacyAPI( - NativeTestServer.getSdchURL() + "/sdch/index?q=LeQxM80O"); + TestHttpUrlRequestListener listener = + startAndWaitForComplete_LegacyApi(mActivity.mRequestFactory, + NativeTestServer.getSdchURL() + "/sdch/index?q=LeQxM80O"); assertEquals(200, listener.mHttpStatusCode); assertEquals("This is an index page.\n", listener.mResponseAsString); assertEquals(null, listener.mResponseHeaders.get("Get-Dictionary")); @@ -87,22 +104,21 @@ public class SdchTest extends CronetTestBase { @SmallTest @Feature({"Cronet"}) - public void testDictionaryNotFound_LegacyAPI() throws Exception { - setUp(true); + public void testDictionaryNotFound_LegacyApi() throws Exception { + setUp(Sdch.ENABLED, Api.LEGACY); // Make a request to /sdch/index which advertises a bad dictionary that // does not exist. - TestHttpUrlRequestListener listener1 = startAndWaitForComplete_LegacyAPI( - NativeTestServer.getSdchURL() + "/sdch/index?q=NotFound"); + TestHttpUrlRequestListener listener1 = + startAndWaitForComplete_LegacyApi(mActivity.mRequestFactory, + NativeTestServer.getSdchURL() + "/sdch/index?q=NotFound"); assertEquals(200, listener1.mHttpStatusCode); assertEquals("This is an index page.\n", listener1.mResponseAsString); assertEquals(Arrays.asList("/sdch/dict/NotFound"), listener1.mResponseHeaders.get("Get-Dictionary")); - waitForDictionaryAdded("NotFound", true); - - // Make a request to fetch /sdch/test, and make sure Sdch encoding is not used. - TestHttpUrlRequestListener listener2 = - startAndWaitForComplete_LegacyAPI(NativeTestServer.getSdchURL() + "/sdch/test"); + // Make a request to fetch /sdch/test, and make sure request succeeds. + TestHttpUrlRequestListener listener2 = startAndWaitForComplete_LegacyApi( + mActivity.mRequestFactory, NativeTestServer.getSdchURL() + "/sdch/test"); assertEquals(200, listener2.mHttpStatusCode); assertEquals("Sdch is not used.\n", listener2.mResponseAsString); } @@ -110,32 +126,64 @@ public class SdchTest extends CronetTestBase { @SmallTest @Feature({"Cronet"}) public void testSdchEnabled() throws Exception { - setUp(true); + setUp(Sdch.ENABLED, Api.ASYNC); + String targetUrl = NativeTestServer.getSdchURL() + "/sdch/test"; + long contextAdapter = + getContextAdapter((CronetUrlRequestContext) mActivity.mUrlRequestContext); + DictionaryAddedObserver observer = + new DictionaryAddedObserver(targetUrl, contextAdapter, false /** Legacy Api */); + // Make a request to /sdch which advertises the dictionary. - TestUrlRequestListener listener1 = - startAndWaitForComplete(NativeTestServer.getSdchURL() + "/sdch/index?q=LeQxM80O"); + TestUrlRequestListener listener1 = startAndWaitForComplete(mActivity.mUrlRequestContext, + NativeTestServer.getSdchURL() + "/sdch/index?q=LeQxM80O"); assertEquals(200, listener1.mResponseInfo.getHttpStatusCode()); assertEquals("This is an index page.\n", listener1.mResponseAsString); assertEquals(Arrays.asList("/sdch/dict/LeQxM80O"), listener1.mResponseInfo.getAllHeaders().get("Get-Dictionary")); - waitForDictionaryAdded("LeQxM80O", false); + observer.waitForDictionaryAdded(); // Make a request to fetch encoded response at /sdch/test. TestUrlRequestListener listener2 = - startAndWaitForComplete(NativeTestServer.getSdchURL() + "/sdch/test"); - assertEquals(200, listener1.mResponseInfo.getHttpStatusCode()); + startAndWaitForComplete(mActivity.mUrlRequestContext, targetUrl); + assertEquals(200, listener2.mResponseInfo.getHttpStatusCode()); assertEquals("The quick brown fox jumps over the lazy dog.\n", listener2.mResponseAsString); + + // Wait for a bit until SimpleCache finished closing entries before + // calling shutdown on the UrlRequestContext. + // TODO(xunjieli): Remove once crbug.com/486120 is fixed. + Thread.sleep(5000); + mActivity.mUrlRequestContext.shutdown(); + + // Shutting down the context will make JsonPrefStore to flush pending + // writes to disk. + String dictUrl = NativeTestServer.getSdchURL() + "/sdch/dict/LeQxM80O"; + assertTrue(fileContainsString("local_prefs.json", dictUrl)); + + // Test persistence. + CronetUrlRequestContext newContext = new CronetUrlRequestContext( + getInstrumentation().getTargetContext(), mActivity.getContextConfig()); + + long newContextAdapter = getContextAdapter(newContext); + NativeTestServer.registerHostResolverProc(newContextAdapter, false); + DictionaryAddedObserver newObserver = + new DictionaryAddedObserver(targetUrl, newContextAdapter, false /** Legacy Api */); + newObserver.waitForDictionaryAdded(); + + // Make a request to fetch encoded response at /sdch/test. + TestUrlRequestListener listener3 = startAndWaitForComplete(newContext, targetUrl); + assertEquals(200, listener3.mResponseInfo.getHttpStatusCode()); + assertEquals("The quick brown fox jumps over the lazy dog.\n", listener3.mResponseAsString); } @SmallTest @Feature({"Cronet"}) public void testSdchDisabled() throws Exception { - setUp(false); + setUp(Sdch.DISABLED, Api.ASYNC); // Make a request to /sdch. // Since Sdch is not enabled, no dictionary should be advertised. - TestUrlRequestListener listener = - startAndWaitForComplete(NativeTestServer.getSdchURL() + "/sdch/index?q=LeQxM80O"); + TestUrlRequestListener listener = startAndWaitForComplete(mActivity.mUrlRequestContext, + NativeTestServer.getSdchURL() + "/sdch/index?q=LeQxM80O"); assertEquals(200, listener.mResponseInfo.getHttpStatusCode()); assertEquals("This is an index page.\n", listener.mResponseAsString); assertEquals(null, listener.mResponseInfo.getAllHeaders().get("Get-Dictionary")); @@ -144,75 +192,83 @@ public class SdchTest extends CronetTestBase { @SmallTest @Feature({"Cronet"}) public void testDictionaryNotFound() throws Exception { - setUp(true); + setUp(Sdch.ENABLED, Api.ASYNC); // Make a request to /sdch/index which advertises a bad dictionary that // does not exist. - TestUrlRequestListener listener1 = - startAndWaitForComplete(NativeTestServer.getSdchURL() + "/sdch/index?q=NotFound"); + TestUrlRequestListener listener1 = startAndWaitForComplete(mActivity.mUrlRequestContext, + NativeTestServer.getSdchURL() + "/sdch/index?q=NotFound"); assertEquals(200, listener1.mResponseInfo.getHttpStatusCode()); assertEquals("This is an index page.\n", listener1.mResponseAsString); assertEquals(Arrays.asList("/sdch/dict/NotFound"), listener1.mResponseInfo.getAllHeaders().get("Get-Dictionary")); - waitForDictionaryAdded("NotFound", false); - // Make a request to fetch /sdch/test, and make sure Sdch encoding is not used. - TestUrlRequestListener listener2 = - startAndWaitForComplete(NativeTestServer.getSdchURL() + "/sdch/test"); + TestUrlRequestListener listener2 = startAndWaitForComplete( + mActivity.mUrlRequestContext, NativeTestServer.getSdchURL() + "/sdch/test"); assertEquals(200, listener2.mResponseInfo.getHttpStatusCode()); assertEquals("Sdch is not used.\n", listener2.mResponseAsString); } - /** - * Helper method to wait for dictionary to be fetched and added to the list of available - * dictionaries. - */ - private void waitForDictionaryAdded(String dict, boolean isLegacyAPI) throws Exception { - // Since Http cache is enabled, making a request to the dictionary explicitly will - // be served from the cache. - String url = NativeTestServer.getSdchURL() + "/sdch/dict/" + dict; - if (isLegacyAPI) { - TestHttpUrlRequestListener listener = startAndWaitForComplete_LegacyAPI(url); - if (dict.equals("NotFound")) { - assertEquals(404, listener.mHttpStatusCode); - } else { - assertEquals(200, listener.mHttpStatusCode); - assertTrue(listener.mWasCached); - } - } else { - TestUrlRequestListener listener = startAndWaitForComplete(url); - if (dict.equals("NotFound")) { - assertEquals(404, listener.mResponseInfo.getHttpStatusCode()); - } else { - assertEquals(200, listener.mResponseInfo.getHttpStatusCode()); - assertTrue(listener.mResponseInfo.wasCached()); + private static class DictionaryAddedObserver extends SdchObserver { + ConditionVariable mBlock = new ConditionVariable(); + + public DictionaryAddedObserver(String targetUrl, long contextAdapter, boolean isLegacyAPI) { + super(targetUrl, contextAdapter, isLegacyAPI); + } + + @Override + public void onDictionaryAdded() { + mBlock.open(); + } + + public void waitForDictionaryAdded() { + if (!mDictionaryAlreadyPresent) { + mBlock.block(); + mBlock.close(); } } - // Now wait for dictionary to be added to the manager, which occurs - // asynchronously. - // TODO(xunjieli): Rather than setting an arbitrary delay, consider - // implementing a SdchObserver to watch for dictionary add events once - // add event is implemented in SdchObserver. - Thread.sleep(1000); } - private TestHttpUrlRequestListener startAndWaitForComplete_LegacyAPI(String url) - throws Exception { + private long getContextAdapter(ChromiumUrlRequestFactory factory) { + return factory.getRequestContext().getUrlRequestContextAdapter(); + } + + private long getContextAdapter(CronetUrlRequestContext requestContext) { + return requestContext.getUrlRequestContextAdapter(); + } + + private TestHttpUrlRequestListener startAndWaitForComplete_LegacyApi( + HttpUrlRequestFactory factory, String url) throws Exception { Map headers = new HashMap(); TestHttpUrlRequestListener listener = new TestHttpUrlRequestListener(); - HttpUrlRequest request = mActivity.mRequestFactory.createRequest( + HttpUrlRequest request = factory.createRequest( url, HttpUrlRequest.REQUEST_PRIORITY_MEDIUM, headers, listener); request.start(); listener.blockForComplete(); return listener; } - private TestUrlRequestListener startAndWaitForComplete(String url) throws Exception { + private TestUrlRequestListener startAndWaitForComplete( + UrlRequestContext requestContext, String url) throws Exception { TestUrlRequestListener listener = new TestUrlRequestListener(); - UrlRequest request = - mActivity.mUrlRequestContext.createRequest(url, listener, listener.getExecutor()); + UrlRequest request = requestContext.createRequest(url, listener, listener.getExecutor()); request.start(); listener.blockForDone(); return listener; } + + // Returns whether a file contains a particular string. + private boolean fileContainsString(String filename, String content) throws IOException { + BufferedReader reader = + new BufferedReader(new FileReader(mActivity.getTestStorage() + "/" + filename)); + String line; + while ((line = reader.readLine()) != null) { + if (line.contains(content)) { + reader.close(); + return true; + } + } + reader.close(); + return false; + } } diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/TestHttpUrlRequestListener.java b/components/cronet/android/test/javatests/src/org/chromium/net/TestHttpUrlRequestListener.java index 592575ec11f2..44eec3431e39 100644 --- a/components/cronet/android/test/javatests/src/org/chromium/net/TestHttpUrlRequestListener.java +++ b/components/cronet/android/test/javatests/src/org/chromium/net/TestHttpUrlRequestListener.java @@ -26,7 +26,6 @@ public class TestHttpUrlRequestListener implements HttpUrlRequestListener { public String mResponseAsString; public Exception mException; public Map> mResponseHeaders; - public boolean mWasCached = false; private final ConditionVariable mStarted = new ConditionVariable(); private final ConditionVariable mComplete = new ConditionVariable(); @@ -43,7 +42,6 @@ public class TestHttpUrlRequestListener implements HttpUrlRequestListener { mHttpStatusText = request.getHttpStatusText(); mNegotiatedProtocol = request.getNegotiatedProtocol(); mResponseHeaders = request.getAllHeaders(); - mWasCached = request.wasCached(); mStarted.open(); } diff --git a/components/cronet/android/test/sdch_test_util.cc b/components/cronet/android/test/sdch_test_util.cc new file mode 100644 index 000000000000..e794e253a51f --- /dev/null +++ b/components/cronet/android/test/sdch_test_util.cc @@ -0,0 +1,143 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sdch_test_util.h" + +#include + +#include "base/android/jni_android.h" +#include "base/android/jni_string.h" +#include "base/android/scoped_java_ref.h" +#include "base/bind.h" +#include "base/macros.h" +#include "components/cronet/android/cronet_url_request_context_adapter.h" +#include "components/cronet/android/url_request_context_adapter.h" +#include "jni/SdchObserver_jni.h" +#include "net/base/sdch_manager.h" +#include "net/base/sdch_observer.h" +#include "net/url_request/url_request_context.h" +#include "url/gurl.h" + +namespace cronet { + +namespace { + +class TestSdchObserver : public net::SdchObserver { + public: + TestSdchObserver( + const GURL& target_url, + net::SdchManager* manager, + const base::android::ScopedJavaGlobalRef& jsdch_observer_ref) + : target_url_(target_url), manager_(manager) { + jsdch_observer_ref_.Reset(jsdch_observer_ref); + } + + // SdchObserver implementation + void OnDictionaryAdded(const GURL& dictionary_url, + const std::string& server_hash) override { + // Only notify if the dictionary for the |target_url_| has been added. + if (manager_->GetDictionarySet(target_url_)) { + JNIEnv* env = base::android::AttachCurrentThread(); + Java_SdchObserver_onDictionaryAdded(env, jsdch_observer_ref_.obj()); + manager_->RemoveObserver(this); + delete this; + } + } + + void OnDictionaryRemoved(const std::string& server_hash) override {} + + void OnDictionaryUsed(const std::string& server_hash) override {} + + void OnGetDictionary(const GURL& request_url, + const GURL& dictionary_url) override {} + + void OnClearDictionaries() override {} + + private: + GURL target_url_; + net::SdchManager* manager_; + base::android::ScopedJavaGlobalRef jsdch_observer_ref_; + + DISALLOW_COPY_AND_ASSIGN(TestSdchObserver); +}; + +void AddSdchObserverHelper( + const GURL& target_url, + const base::android::ScopedJavaGlobalRef& jsdch_observer_ref, + net::URLRequestContext* url_request_context) { + JNIEnv* env = base::android::AttachCurrentThread(); + // If dictionaries for |target_url| are already added, skip adding the + // observer. + if (url_request_context->sdch_manager()->GetDictionarySet(target_url)) { + Java_SdchObserver_onDictionarySetAlreadyPresent(env, + jsdch_observer_ref.obj()); + return; + } + + url_request_context->sdch_manager()->AddObserver(new TestSdchObserver( + target_url, url_request_context->sdch_manager(), jsdch_observer_ref)); + Java_SdchObserver_onAddSdchObserverCompleted(env, jsdch_observer_ref.obj()); +} + +void AddSdchObserverOnNetworkThread( + const GURL& target_url, + const base::android::ScopedJavaGlobalRef& jsdch_observer_ref, + CronetURLRequestContextAdapter* context_adapter) { + AddSdchObserverHelper(target_url, jsdch_observer_ref, + context_adapter->GetURLRequestContext()); +} + +// TODO(xunjieli): Delete this once legacy API is removed. +void AddSdchObserverOnNetworkThreadLegacyAPI( + const GURL& target_url, + const base::android::ScopedJavaGlobalRef& jsdch_observer_ref, + URLRequestContextAdapter* context_adapter) { + AddSdchObserverHelper(target_url, jsdch_observer_ref, + context_adapter->GetURLRequestContext()); +} + +} // namespace + +void AddSdchObserver(JNIEnv* env, + jobject jsdch_observer, + jstring jtarget_url, + jlong jadapter) { + base::android::ScopedJavaGlobalRef jsdch_observer_ref; + // ScopedJavaGlobalRef do not hold onto the env reference, so it is safe to + // use it across threads. |AddSdchObserverHelper| will acquire a new + // JNIEnv before calling into Java. + jsdch_observer_ref.Reset(env, jsdch_observer); + + GURL target_url(base::android::ConvertJavaStringToUTF8(env, jtarget_url)); + CronetURLRequestContextAdapter* context_adapter = + reinterpret_cast(jadapter); + context_adapter->PostTaskToNetworkThread( + FROM_HERE, + base::Bind(&AddSdchObserverOnNetworkThread, target_url, + jsdch_observer_ref, base::Unretained(context_adapter))); +} + +void AddSdchObserverLegacyAPI(JNIEnv* env, + jobject jsdch_observer, + jstring jtarget_url, + jlong jadapter) { + base::android::ScopedJavaGlobalRef jsdch_observer_ref; + // ScopedJavaGlobalRef do not hold onto the env reference, so it is safe to + // use it across threads. |AddSdchObserverHelper| will acquire a new + // JNIEnv before calling into Java. + jsdch_observer_ref.Reset(env, jsdch_observer); + GURL target_url(base::android::ConvertJavaStringToUTF8(env, jtarget_url)); + URLRequestContextAdapter* context_adapter = + reinterpret_cast(jadapter); + context_adapter->PostTaskToNetworkThread( + FROM_HERE, + base::Bind(&AddSdchObserverOnNetworkThreadLegacyAPI, target_url, + jsdch_observer_ref, base::Unretained(context_adapter))); +} + +bool RegisterSdchTestUtil(JNIEnv* env) { + return RegisterNativesImpl(env); +} + +} // namespace cronet diff --git a/components/cronet/android/test/sdch_test_util.h b/components/cronet/android/test/sdch_test_util.h new file mode 100644 index 000000000000..c9ece3743a6b --- /dev/null +++ b/components/cronet/android/test/sdch_test_util.h @@ -0,0 +1,16 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_CRONET_ANDROID_TEST_SDCH_TEST_UTIL_H_ +#define COMPONENTS_CRONET_ANDROID_TEST_SDCH_TEST_UTIL_H_ + +#include + +namespace cronet { + +bool RegisterSdchTestUtil(JNIEnv* env); + +} // namespace cronet + +#endif // COMPONENTS_CRONET_ANDROID_TEST_SDCH_TEST_UTIL_H_ diff --git a/components/cronet/android/test/src/org/chromium/net/CronetTestActivity.java b/components/cronet/android/test/src/org/chromium/net/CronetTestActivity.java index 098b4009ac96..09ff3e972a10 100644 --- a/components/cronet/android/test/src/org/chromium/net/CronetTestActivity.java +++ b/components/cronet/android/test/src/org/chromium/net/CronetTestActivity.java @@ -36,6 +36,8 @@ public class CronetTestActivity extends Activity { public static final String POST_DATA_KEY = "postData"; public static final String CONFIG_KEY = "config"; public static final String CACHE_KEY = "cache"; + public static final String SDCH_KEY = "sdch"; + public static final String LIBRARY_INIT_KEY = "libraryInit"; /** * Skips library initialization. @@ -51,6 +53,9 @@ public class CronetTestActivity extends Activity { // Uses in-memory cache. public static final String CACHE_IN_MEMORY = "memory"; + // Enables Sdch. + public static final String SDCH_ENABLE = "enable"; + /** * Initializes Cronet Async API only. */ @@ -150,7 +155,7 @@ public class CronetTestActivity extends Activity { assertTrue(storage.mkdir()); } - private String getTestStorage() { + String getTestStorage() { return PathUtils.getDataDirectory(getApplicationContext()) + "/test_storage"; } @@ -196,6 +201,11 @@ public class CronetTestActivity extends Activity { config.enableHttpCache(UrlRequestContextConfig.HttpCache.IN_MEMORY, 100 * 1024); } + String sdchString = getCommandLineArg(SDCH_KEY); + if (SDCH_ENABLE.equals(sdchString)) { + config.enableSDCH(true); + } + // Setting this here so it isn't overridden on the command line config.setLibraryName("cronet_tests"); return config; diff --git a/components/cronet/android/test/src/org/chromium/net/NativeTestServer.java b/components/cronet/android/test/src/org/chromium/net/NativeTestServer.java index 9be7a3bc67dc..d87dbc8b07cc 100644 --- a/components/cronet/android/test/src/org/chromium/net/NativeTestServer.java +++ b/components/cronet/android/test/src/org/chromium/net/NativeTestServer.java @@ -39,9 +39,9 @@ public final class NativeTestServer { * old API. */ public static void registerHostResolverProc(long contextAdapter, boolean isLegacyAPI) { - sHostResolverBlock.close(); nativeRegisterHostResolverProc(contextAdapter, isLegacyAPI); sHostResolverBlock.block(); + sHostResolverBlock.close(); } public static String getEchoBodyURL() { diff --git a/components/cronet/android/test/src/org/chromium/net/SdchObserver.java b/components/cronet/android/test/src/org/chromium/net/SdchObserver.java new file mode 100644 index 000000000000..f3a0f0533a40 --- /dev/null +++ b/components/cronet/android/test/src/org/chromium/net/SdchObserver.java @@ -0,0 +1,61 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.net; + +import android.os.ConditionVariable; + +import org.chromium.base.CalledByNative; +import org.chromium.base.JNINamespace; + +/** + * Class to watch for Sdch dictionary events. The native implementation + * unregisters itself when an event happens. Therefore, an instance of this + * class is only able to receive a notification of the earliest event. + * Currently, implemented events include {@link #onDictionaryAdded}. + */ +@JNINamespace("cronet") +public class SdchObserver { + protected boolean mDictionaryAlreadyPresent = false; + private final ConditionVariable mAddBlock = new ConditionVariable(); + + /** + * Constructor. + * @param targetUrl the target url on which sdch encoding will be used. + * @param contextAdapter the native context adapter to register the observer. + * @param isLegacyApi whether legacy api is used. + */ + public SdchObserver(String targetUrl, long contextAdapter, boolean isLegacyAPI) { + if (isLegacyAPI) { + nativeAddSdchObserverLegacyAPI(targetUrl, contextAdapter); + } else { + nativeAddSdchObserver(targetUrl, contextAdapter); + } + mAddBlock.block(); + mAddBlock.close(); + } + + /** + * Called when a dictionary is added to the SdchManager for the target url. + * Override this method if caller would like to get notified. + */ + @CalledByNative + public void onDictionaryAdded() { + // Left blank; + } + + @CalledByNative + private void onAddSdchObserverCompleted() { + mAddBlock.open(); + } + + @CalledByNative + private void onDictionarySetAlreadyPresent() { + mDictionaryAlreadyPresent = true; + mAddBlock.open(); + } + + private native void nativeAddSdchObserver(String targetUrl, long contextAdapter); + private native void nativeAddSdchObserverLegacyAPI(String targetUrl, long contextAdapter); +} -- 2.11.4.GIT