diff options
author | xunjieli <xunjieli@chromium.org> | 2015-04-29 10:36:14 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-04-29 17:36:59 +0000 |
commit | b8a6d56ffe39823af7b09ce6b816d3cd9fbac2a6 (patch) | |
tree | 60e0bafa48d564006038b39606871f82632337b5 | |
parent | e9ac6f320030c764af9dca7f662d2f828c1aa223 (diff) | |
download | chromium_src-b8a6d56ffe39823af7b09ce6b816d3cd9fbac2a6.zip chromium_src-b8a6d56ffe39823af7b09ce6b816d3cd9fbac2a6.tar.gz chromium_src-b8a6d56ffe39823af7b09ce6b816d3cd9fbac2a6.tar.bz2 |
Enable sdch in Cronet
This CL combined rdsmith@'s CL(747453004) and mef@'s
CL(1073193002), and added tests to make sure sdch works for
both the old and the new API.
BUG=414885
Review URL: https://codereview.chromium.org/1085903002
Cr-Commit-Position: refs/heads/master@{#327517}
29 files changed, 543 insertions, 30 deletions
diff --git a/components/cronet/android/chromium_url_request.cc b/components/cronet/android/chromium_url_request.cc index 2da0ccc..140e476 100644 --- a/components/cronet/android/chromium_url_request.cc +++ b/components/cronet/android/chromium_url_request.cc @@ -400,6 +400,17 @@ static jstring GetNegotiatedProtocol(JNIEnv* env, return ConvertUTF8ToJavaString(env, negotiated_protocol.c_str()).Release(); } +static jboolean GetWasCached(JNIEnv* env, + jobject jcaller, + jlong jurl_request_adapter) { + URLRequestAdapter* request_adapter = + reinterpret_cast<URLRequestAdapter*>(jurl_request_adapter); + DCHECK(request_adapter); + + bool was_cached = request_adapter->GetWasCached(); + return was_cached ? JNI_TRUE : JNI_FALSE; +} + static void DisableRedirects(JNIEnv* env, jobject jcaller, jlong jrequest_adapter) { URLRequestAdapter* request_adapter = diff --git a/components/cronet/android/cronet_url_request_context_adapter.cc b/components/cronet/android/cronet_url_request_context_adapter.cc index 6553f1e..3ee0c7e 100644 --- a/components/cronet/android/cronet_url_request_context_adapter.cc +++ b/components/cronet/android/cronet_url_request_context_adapter.cc @@ -20,6 +20,7 @@ #include "net/http/http_auth_handler_factory.h" #include "net/log/write_to_file_net_log_observer.h" #include "net/proxy/proxy_service.h" +#include "net/sdch/sdch_owner.h" #include "net/url_request/url_request_context.h" #include "net/url_request/url_request_context_builder.h" @@ -161,6 +162,12 @@ void CronetURLRequestContextAdapter::InitializeOnNetworkThread( if (config->load_disable_cache) default_load_flags_ |= net::LOAD_DISABLE_CACHE; + if (config->enable_sdch) { + DCHECK(context_->sdch_manager()); + sdch_owner_.reset( + new net::SdchOwner(context_->sdch_manager(), context_.get())); + } + // Currently (circa M39) enabling QUIC requires setting probability threshold. if (config->enable_quic) { context_->http_server_properties() diff --git a/components/cronet/android/cronet_url_request_context_adapter.h b/components/cronet/android/cronet_url_request_context_adapter.h index fea2454..7319733 100644 --- a/components/cronet/android/cronet_url_request_context_adapter.h +++ b/components/cronet/android/cronet_url_request_context_adapter.h @@ -25,6 +25,7 @@ namespace net { class WriteToFileNetLogObserver; class URLRequestContext; class ProxyConfigService; +class SdchOwner; } // namespace net namespace cronet { @@ -91,6 +92,7 @@ class CronetURLRequestContextAdapter { scoped_ptr<net::WriteToFileNetLogObserver> write_to_file_observer_; scoped_ptr<net::URLRequestContext> context_; scoped_ptr<net::ProxyConfigService> proxy_config_service_; + scoped_ptr<net::SdchOwner> sdch_owner_; // Context config is only valid untng context is initialized. scoped_ptr<URLRequestContextConfig> context_config_; 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 6372a60..afccf84 100644 --- a/components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequest.java +++ b/components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequest.java @@ -422,6 +422,15 @@ public class ChromiumUrlRequest implements HttpUrlRequest { } @Override + public boolean wasCached() { + synchronized (mLock) { + validateNativeAdapterNotDestroyed(); + validateHeadersAvailable(); + return nativeGetWasCached(mUrlRequestAdapter); + } + } + + @Override public String getContentType() { return mContentType; } @@ -748,6 +757,8 @@ public class ChromiumUrlRequest implements HttpUrlRequest { private native String nativeGetNegotiatedProtocol(long urlRequestAdapter); + private native boolean nativeGetWasCached(long urlRequestAdapter); + // Explicit class to work around JNI-generator generics confusion. private static class ResponseHeadersMap extends HashMap<String, List<String>> { 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 03246f4..e7f7968 100644 --- a/components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequestContext.java +++ b/components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequestContext.java @@ -12,6 +12,7 @@ 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. @@ -95,6 +96,15 @@ 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"); diff --git a/components/cronet/android/java/src/org/chromium/net/CronetUrlRequestContext.java b/components/cronet/android/java/src/org/chromium/net/CronetUrlRequestContext.java index 78d3bee..aa2b635 100644 --- a/components/cronet/android/java/src/org/chromium/net/CronetUrlRequestContext.java +++ b/components/cronet/android/java/src/org/chromium/net/CronetUrlRequestContext.java @@ -15,6 +15,7 @@ import android.util.Log; import org.chromium.base.CalledByNative; import org.chromium.base.JNINamespace; import org.chromium.base.NativeClassQualifiedName; +import org.chromium.base.VisibleForTesting; import org.chromium.base.annotations.UsedByReflection; import java.util.concurrent.Executor; @@ -152,6 +153,7 @@ public class CronetUrlRequestContext extends UrlRequestContext { mActiveRequestCount.decrementAndGet(); } + @VisibleForTesting long getUrlRequestContextAdapter() { synchronized (mLock) { checkHaveAdapter(); diff --git a/components/cronet/android/java/src/org/chromium/net/HttpUrlConnectionUrlRequest.java b/components/cronet/android/java/src/org/chromium/net/HttpUrlConnectionUrlRequest.java index f2e25f2..88e97ea 100644 --- a/components/cronet/android/java/src/org/chromium/net/HttpUrlConnectionUrlRequest.java +++ b/components/cronet/android/java/src/org/chromium/net/HttpUrlConnectionUrlRequest.java @@ -434,6 +434,11 @@ class HttpUrlConnectionUrlRequest implements HttpUrlRequest { } @Override + public boolean wasCached() { + return false; + } + + @Override public int getHttpStatusCode() { int httpStatusCode = mHttpStatusCode; diff --git a/components/cronet/android/java/src/org/chromium/net/HttpUrlRequest.java b/components/cronet/android/java/src/org/chromium/net/HttpUrlRequest.java index 1a39a46..a8a943d 100644 --- a/components/cronet/android/java/src/org/chromium/net/HttpUrlRequest.java +++ b/components/cronet/android/java/src/org/chromium/net/HttpUrlRequest.java @@ -114,6 +114,11 @@ public interface HttpUrlRequest { String getNegotiatedProtocol(); /** + * Returns whether the response is serviced from the cache. + */ + boolean wasCached(); + + /** * Returns the entire response as a ByteBuffer. */ ByteBuffer getByteBuffer(); diff --git a/components/cronet/android/java/src/org/chromium/net/UrlRequestContextConfig.java b/components/cronet/android/java/src/org/chromium/net/UrlRequestContextConfig.java index 810ce12..32b442a 100644 --- a/components/cronet/android/java/src/org/chromium/net/UrlRequestContextConfig.java +++ b/components/cronet/android/java/src/org/chromium/net/UrlRequestContextConfig.java @@ -15,14 +15,14 @@ import java.io.File; * UrlRequestContext. */ public class UrlRequestContextConfig { - /** - * Default config enables SPDY, QUIC, in memory http cache. + * Default config enables SPDY, disables QUIC, SDCH and http cache. */ public UrlRequestContextConfig() { enableLegacyMode(false); enableQUIC(false); enableSPDY(true); + enableSDCH(false); enableHttpCache(HttpCache.DISABLED, 0); } @@ -102,6 +102,13 @@ public class UrlRequestContextConfig { } /** + * Boolean, enable SDCH compression if true. + */ + public UrlRequestContextConfig enableSDCH(boolean value) { + return putBoolean(UrlRequestContextConfigList.ENABLE_SDCH, value); + } + + /** * Enumeration, disable or enable cache in memory or on disk. */ public enum HttpCache { diff --git a/components/cronet/android/test/assets/test/sdch/LeQxM80O_encoded b/components/cronet/android/test/assets/test/sdch/LeQxM80O_encoded Binary files differnew file mode 100644 index 0000000..ae6d433 --- /dev/null +++ b/components/cronet/android/test/assets/test/sdch/LeQxM80O_encoded diff --git a/components/cronet/android/test/assets/test/sdch/LeQxM80O_encoded.mock-http-headers b/components/cronet/android/test/assets/test/sdch/LeQxM80O_encoded.mock-http-headers new file mode 100644 index 0000000..11453b9 --- /dev/null +++ b/components/cronet/android/test/assets/test/sdch/LeQxM80O_encoded.mock-http-headers @@ -0,0 +1,3 @@ +HTTP/1.1 200 OK +Content-Type: text/plain +Content-Encoding: sdch diff --git a/components/cronet/android/test/assets/test/sdch/dict/LeQxM80O b/components/cronet/android/test/assets/test/sdch/dict/LeQxM80O new file mode 100644 index 0000000..ab9a35e --- /dev/null +++ b/components/cronet/android/test/assets/test/sdch/dict/LeQxM80O @@ -0,0 +1,4 @@ +Domain: fake.sdch.domain +Path: /sdch/test + +The quick brown fox jumps over the lazy dog.\n diff --git a/components/cronet/android/test/assets/test/sdch/dict/LeQxM80O.mock-http-headers b/components/cronet/android/test/assets/test/sdch/dict/LeQxM80O.mock-http-headers new file mode 100644 index 0000000..691e413 --- /dev/null +++ b/components/cronet/android/test/assets/test/sdch/dict/LeQxM80O.mock-http-headers @@ -0,0 +1,3 @@ +HTTP/1.1 200 OK +Cache-Control: public, max-age=31536000 +Content-Type: application/x-sdch-dictionary diff --git a/components/cronet/android/test/assets/test/sdch/index b/components/cronet/android/test/assets/test/sdch/index new file mode 100644 index 0000000..e0638b6 --- /dev/null +++ b/components/cronet/android/test/assets/test/sdch/index @@ -0,0 +1 @@ +This is an index page. diff --git a/components/cronet/android/test/assets/test/sdch/index.mock-http-headers b/components/cronet/android/test/assets/test/sdch/index.mock-http-headers new file mode 100644 index 0000000..5c695b9 --- /dev/null +++ b/components/cronet/android/test/assets/test/sdch/index.mock-http-headers @@ -0,0 +1,2 @@ +HTTP/1.1 200 OK +Content-Type: text/plain 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 new file mode 100644 index 0000000..18c3d72 --- /dev/null +++ b/components/cronet/android/test/javatests/src/org/chromium/net/SdchTest.java @@ -0,0 +1,218 @@ +// 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.test.suitebuilder.annotation.SmallTest; + +import org.chromium.base.test.util.Feature; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +/** + * Tests Sdch support. + */ +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. + 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); + // Make a request to /sdch/index which advertises the dictionary. + TestHttpUrlRequestListener listener1 = startAndWaitForComplete_LegacyAPI( + 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); + + // Make a request to fetch encoded response at /sdch/test. + TestHttpUrlRequestListener listener2 = + startAndWaitForComplete_LegacyAPI(NativeTestServer.getSdchURL() + "/sdch/test"); + 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); + // 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"); + assertEquals(200, listener.mHttpStatusCode); + assertEquals("This is an index page.\n", listener.mResponseAsString); + assertEquals(null, listener.mResponseHeaders.get("Get-Dictionary")); + } + + @SmallTest + @Feature({"Cronet"}) + public void testDictionaryNotFound_LegacyAPI() throws Exception { + setUp(true); + // 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"); + 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"); + assertEquals(200, listener2.mHttpStatusCode); + assertEquals("Sdch is not used.\n", listener2.mResponseAsString); + } + + @SmallTest + @Feature({"Cronet"}) + public void testSdchEnabled() throws Exception { + setUp(true); + // Make a request to /sdch which advertises the dictionary. + TestUrlRequestListener listener1 = + startAndWaitForComplete(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); + + // Make a request to fetch encoded response at /sdch/test. + TestUrlRequestListener listener2 = + startAndWaitForComplete(NativeTestServer.getSdchURL() + "/sdch/test"); + assertEquals(200, listener1.mResponseInfo.getHttpStatusCode()); + assertEquals("The quick brown fox jumps over the lazy dog.\n", listener2.mResponseAsString); + } + + @SmallTest + @Feature({"Cronet"}) + public void testSdchDisabled() throws Exception { + setUp(false); + // Make a request to /sdch. + // Since Sdch is not enabled, no dictionary should be advertised. + TestUrlRequestListener listener = + startAndWaitForComplete(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")); + } + + @SmallTest + @Feature({"Cronet"}) + public void testDictionaryNotFound() throws Exception { + setUp(true); + // Make a request to /sdch/index which advertises a bad dictionary that + // does not exist. + TestUrlRequestListener listener1 = + startAndWaitForComplete(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"); + 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()); + } + } + // 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 { + Map<String, String> headers = new HashMap<String, String>(); + TestHttpUrlRequestListener listener = new TestHttpUrlRequestListener(); + HttpUrlRequest request = mActivity.mRequestFactory.createRequest( + url, HttpUrlRequest.REQUEST_PRIORITY_MEDIUM, headers, listener); + request.start(); + listener.blockForComplete(); + return listener; + } + + private TestUrlRequestListener startAndWaitForComplete(String url) throws Exception { + TestUrlRequestListener listener = new TestUrlRequestListener(); + UrlRequest request = + mActivity.mUrlRequestContext.createRequest(url, listener, listener.getExecutor()); + request.start(); + listener.blockForDone(); + return listener; + } +} 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 44eec34..592575e 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,6 +26,7 @@ public class TestHttpUrlRequestListener implements HttpUrlRequestListener { public String mResponseAsString; public Exception mException; public Map<String, List<String>> mResponseHeaders; + public boolean mWasCached = false; private final ConditionVariable mStarted = new ConditionVariable(); private final ConditionVariable mComplete = new ConditionVariable(); @@ -42,6 +43,7 @@ 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/native_test_server.cc b/components/cronet/android/test/native_test_server.cc index 9726164..ce8c951 100644 --- a/components/cronet/android/test/native_test_server.cc +++ b/components/cronet/android/test/native_test_server.cc @@ -4,14 +4,25 @@ #include "native_test_server.h" +#include <string> + #include "base/android/jni_android.h" #include "base/android/jni_string.h" +#include "base/android/path_utils.h" #include "base/bind.h" #include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "base/path_service.h" #include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" +#include "components/cronet/android/cronet_url_request_context_adapter.h" +#include "components/cronet/android/url_request_context_adapter.h" #include "jni/NativeTestServer_jni.h" +#include "net/base/url_util.h" +#include "net/dns/host_resolver_impl.h" +#include "net/dns/mock_host_resolver.h" #include "net/http/http_status_code.h" #include "net/test/embedded_test_server/embedded_test_server.h" #include "net/test/embedded_test_server/http_request.h" @@ -22,22 +33,66 @@ namespace cronet { namespace { -const char echo_body_path[] = "/echo_body"; -const char echo_header_path[] = "/echo_header"; -const char echo_all_headers_path[] = "/echo_all_headers"; -const char echo_method_path[] = "/echo_method"; -const char redirect_to_echo_body_path[] = "/redirect_to_echo_body"; +const char kEchoBodyPath[] = "/echo_body"; +const char kEchoHeaderPath[] = "/echo_header"; +const char kEchoAllHeadersPath[] = "/echo_all_headers"; +const char kEchoMethodPath[] = "/echo_method"; +const char kRedirectToEchoBodyPath[] = "/redirect_to_echo_body"; +const char kFakeSdchDomain[] = "fake.sdch.domain"; +// Path that advertises the dictionary passed in query params if client +// supports Sdch encoding. E.g. /sdch/index?q=LeQxM80O will make the server +// responds with "Get-Dictionary: /sdch/dict/LeQxM80O". +const char kSdchPath[] = "/sdch/index"; +// Path that returns encoded response if client has the right dictionary. +const char kSdchTestPath[] = "/sdch/test"; +// Path where dictionaries are stored. +const char kSdchDictPath[] = "/sdch/dict/"; net::test_server::EmbeddedTestServer* g_test_server = nullptr; -scoped_ptr<net::test_server::HttpResponse> UploadServerRequestHandler( +class CustomHttpResponse : public net::test_server::HttpResponse { + public: + CustomHttpResponse(const std::string& headers, const std::string& contents) + : headers_(headers), contents_(contents) {} + + std::string ToResponseString() const override { + return headers_ + "\r\n" + contents_; + } + + void AddHeader(const std::string& key_value_pair) { + headers_.append(base::StringPrintf("%s\r\n", key_value_pair.c_str())); + } + + private: + std::string headers_; + std::string contents_; + + DISALLOW_COPY_AND_ASSIGN(CustomHttpResponse); +}; + +scoped_ptr<CustomHttpResponse> ConstructResponseBasedOnFile( + const base::FilePath& file_path) { + std::string file_contents; + bool read_file = base::ReadFileToString(file_path, &file_contents); + DCHECK(read_file); + base::FilePath headers_path( + file_path.AddExtension(FILE_PATH_LITERAL("mock-http-headers"))); + std::string headers_contents; + bool read_headers = base::ReadFileToString(headers_path, &headers_contents); + DCHECK(read_headers); + scoped_ptr<CustomHttpResponse> http_response( + new CustomHttpResponse(headers_contents, file_contents)); + return http_response.Pass(); +} + +scoped_ptr<net::test_server::HttpResponse> NativeTestServerRequestHandler( const net::test_server::HttpRequest& request) { DCHECK(g_test_server); scoped_ptr<net::test_server::BasicHttpResponse> response( new net::test_server::BasicHttpResponse()); response->set_content_type("text/plain"); - if (request.relative_url == echo_body_path) { + if (request.relative_url == kEchoBodyPath) { if (request.has_content) { response->set_content(request.content); } else { @@ -46,7 +101,7 @@ scoped_ptr<net::test_server::HttpResponse> UploadServerRequestHandler( return response.Pass(); } - if (StartsWithASCII(request.relative_url, echo_header_path, true)) { + if (StartsWithASCII(request.relative_url, kEchoHeaderPath, true)) { GURL url = g_test_server->GetURL(request.relative_url); auto it = request.headers.find(url.query()); if (it != request.headers.end()) { @@ -57,19 +112,66 @@ scoped_ptr<net::test_server::HttpResponse> UploadServerRequestHandler( return response.Pass(); } - if (request.relative_url == echo_all_headers_path) { + if (request.relative_url == kEchoAllHeadersPath) { response->set_content(request.all_headers); return response.Pass(); } - if (request.relative_url == echo_method_path) { + if (request.relative_url == kEchoMethodPath) { response->set_content(request.method_string); return response.Pass(); } - if (request.relative_url == redirect_to_echo_body_path) { + if (request.relative_url == kRedirectToEchoBodyPath) { response->set_code(net::HTTP_TEMPORARY_REDIRECT); - response->AddCustomHeader("Location", echo_body_path); + response->AddCustomHeader("Location", kEchoBodyPath); + return response.Pass(); + } + + // Unhandled requests result in the Embedded test server sending a 404. + return scoped_ptr<net::test_server::BasicHttpResponse>(); +} + +scoped_ptr<net::test_server::HttpResponse> SdchRequestHandler( + const net::test_server::HttpRequest& request) { + DCHECK(g_test_server); + base::FilePath dir_path; + bool get_data_dir = base::android::GetDataDirectory(&dir_path); + DCHECK(get_data_dir); + dir_path = dir_path.Append(FILE_PATH_LITERAL("test")); + + if (StartsWithASCII(request.relative_url, kSdchPath, true)) { + base::FilePath file_path = dir_path.Append("sdch/index"); + scoped_ptr<CustomHttpResponse> response = + ConstructResponseBasedOnFile(file_path).Pass(); + // Check for query params to see which dictionary to advertise. + // For instance, ?q=dictionaryA will make the server advertise dictionaryA. + GURL url = g_test_server->GetURL(request.relative_url); + std::string dictionary; + if (!net::GetValueForKeyInQuery(url, "q", &dictionary)) { + CHECK(false) << "dictionary is not found in query params of " + << request.relative_url; + } + auto accept_encoding_header = request.headers.find("Accept-Encoding"); + if (accept_encoding_header != request.headers.end()) { + if (accept_encoding_header->second.find("sdch") != std::string::npos) + response->AddHeader(base::StringPrintf( + "Get-Dictionary: %s%s", kSdchDictPath, dictionary.c_str())); + } + return response.Pass(); + } + + if (StartsWithASCII(request.relative_url, kSdchTestPath, true)) { + auto avail_dictionary_header = request.headers.find("Avail-Dictionary"); + if (avail_dictionary_header != request.headers.end()) { + base::FilePath file_path = dir_path.Append( + "sdch/" + avail_dictionary_header->second + "_encoded"); + return ConstructResponseBasedOnFile(file_path).Pass(); + } + scoped_ptr<net::test_server::BasicHttpResponse> response( + new net::test_server::BasicHttpResponse()); + response->set_content_type("text/plain"); + response->set_content("Sdch is not used.\n"); return response.Pass(); } @@ -77,6 +179,30 @@ scoped_ptr<net::test_server::HttpResponse> UploadServerRequestHandler( return scoped_ptr<net::test_server::BasicHttpResponse>(); } +void RegisterHostResolverProcHelper( + net::URLRequestContext* url_request_context) { + net::HostResolverImpl* resolver = + static_cast<net::HostResolverImpl*>(url_request_context->host_resolver()); + scoped_refptr<net::RuleBasedHostResolverProc> proc = + new net::RuleBasedHostResolverProc(NULL); + proc->AddRule(kFakeSdchDomain, "127.0.0.1"); + resolver->set_proc_params_for_test( + net::HostResolverImpl::ProcTaskParams(proc.get(), 1u)); + JNIEnv* env = base::android::AttachCurrentThread(); + Java_NativeTestServer_onHostResolverProcRegistered(env); +} + +void RegisterHostResolverProcOnNetworkThread( + CronetURLRequestContextAdapter* context_adapter) { + RegisterHostResolverProcHelper(context_adapter->GetURLRequestContext()); +} + +// TODO(xunjieli): Delete this once legacy API is removed. +void RegisterHostResolverProcOnNetworkThreadLegacyAPI( + URLRequestContextAdapter* context_adapter) { + RegisterHostResolverProcHelper(context_adapter->GetURLRequestContext()); +} + } // namespace jboolean StartNativeTestServer(JNIEnv* env, @@ -87,15 +213,36 @@ jboolean StartNativeTestServer(JNIEnv* env, return false; g_test_server = new net::test_server::EmbeddedTestServer(); g_test_server->RegisterRequestHandler( - base::Bind(&UploadServerRequestHandler)); - // Add a second handler for paths that UploadServerRequestHandler does not - // handle. + base::Bind(&NativeTestServerRequestHandler)); + g_test_server->RegisterRequestHandler(base::Bind(&SdchRequestHandler)); base::FilePath test_files_root( base::android::ConvertJavaStringToUTF8(env, jtest_files_root)); + + // Add a third handler for paths that NativeTestServerRequestHandler does not + // handle. g_test_server->ServeFilesFromDirectory(test_files_root); return g_test_server->InitializeAndWaitUntilReady(); } +void RegisterHostResolverProc(JNIEnv* env, + jclass jcaller, + jlong jadapter, + jboolean jlegacy_api) { + if (jlegacy_api == JNI_TRUE) { + URLRequestContextAdapter* context_adapter = + reinterpret_cast<URLRequestContextAdapter*>(jadapter); + context_adapter->PostTaskToNetworkThread( + FROM_HERE, base::Bind(&RegisterHostResolverProcOnNetworkThreadLegacyAPI, + base::Unretained(context_adapter))); + } else { + CronetURLRequestContextAdapter* context_adapter = + reinterpret_cast<CronetURLRequestContextAdapter*>(jadapter); + context_adapter->PostTaskToNetworkThread( + FROM_HERE, base::Bind(&RegisterHostResolverProcOnNetworkThread, + base::Unretained(context_adapter))); + } +} + void ShutdownNativeTestServer(JNIEnv* env, jclass jcaller) { if (!g_test_server) return; @@ -105,13 +252,13 @@ void ShutdownNativeTestServer(JNIEnv* env, jclass jcaller) { jstring GetEchoBodyURL(JNIEnv* env, jclass jcaller) { DCHECK(g_test_server); - GURL url = g_test_server->GetURL(echo_body_path); + GURL url = g_test_server->GetURL(kEchoBodyPath); return base::android::ConvertUTF8ToJavaString(env, url.spec()).Release(); } jstring GetEchoHeaderURL(JNIEnv* env, jclass jcaller, jstring jheader) { DCHECK(g_test_server); - GURL url = g_test_server->GetURL(echo_header_path); + GURL url = g_test_server->GetURL(kEchoHeaderPath); GURL::Replacements replacements; std::string header = base::android::ConvertJavaStringToUTF8(env, jheader); replacements.SetQueryStr(header.c_str()); @@ -121,19 +268,19 @@ jstring GetEchoHeaderURL(JNIEnv* env, jclass jcaller, jstring jheader) { jstring GetEchoAllHeadersURL(JNIEnv* env, jclass jcaller) { DCHECK(g_test_server); - GURL url = g_test_server->GetURL(echo_all_headers_path); + GURL url = g_test_server->GetURL(kEchoAllHeadersPath); return base::android::ConvertUTF8ToJavaString(env, url.spec()).Release(); } jstring GetEchoMethodURL(JNIEnv* env, jclass jcaller) { DCHECK(g_test_server); - GURL url = g_test_server->GetURL(echo_method_path); + GURL url = g_test_server->GetURL(kEchoMethodPath); return base::android::ConvertUTF8ToJavaString(env, url.spec()).Release(); } jstring GetRedirectToEchoBody(JNIEnv* env, jclass jcaller) { DCHECK(g_test_server); - GURL url = g_test_server->GetURL(redirect_to_echo_body_path); + GURL url = g_test_server->GetURL(kRedirectToEchoBodyPath); return base::android::ConvertUTF8ToJavaString(env, url.spec()).Release(); } @@ -144,6 +291,13 @@ jstring GetFileURL(JNIEnv* env, jclass jcaller, jstring jfile_path) { return base::android::ConvertUTF8ToJavaString(env, url.spec()).Release(); } +jstring GetSdchURL(JNIEnv* env, jclass jcaller) { + DCHECK(g_test_server); + std::string url(base::StringPrintf("http://%s:%d", kFakeSdchDomain, + g_test_server->port())); + return base::android::ConvertUTF8ToJavaString(env, url).Release(); +} + bool RegisterNativeTestServer(JNIEnv* env) { return RegisterNativesImpl(env); } 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 897515f..59768b6 100644 --- a/components/cronet/android/test/src/org/chromium/net/CronetTestActivity.java +++ b/components/cronet/android/test/src/org/chromium/net/CronetTestActivity.java @@ -188,12 +188,14 @@ public class CronetTestActivity extends Activity { } public void startNetLog() { - mRequestFactory.startNetLogToFile( - Environment.getExternalStorageDirectory().getPath() - + "/cronet_sample_netlog.json"); + mRequestFactory.startNetLogToFile(Environment.getExternalStorageDirectory().getPath() + + "/cronet_sample_netlog_old_api.json"); + mUrlRequestContext.startNetLogToFile(Environment.getExternalStorageDirectory().getPath() + + "/cronet_sample_netlog_new_api.json"); } public void stopNetLog() { mRequestFactory.stopNetLog(); + mUrlRequestContext.stopNetLog(); } } 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 08406ea..329b48a 100644 --- a/components/cronet/android/test/src/org/chromium/net/NativeTestServer.java +++ b/components/cronet/android/test/src/org/chromium/net/NativeTestServer.java @@ -5,7 +5,9 @@ package org.chromium.net; import android.content.Context; +import android.os.ConditionVariable; +import org.chromium.base.CalledByNative; import org.chromium.base.JNINamespace; /** @@ -14,6 +16,8 @@ import org.chromium.base.JNINamespace; */ @JNINamespace("cronet") public final class NativeTestServer { + private static final ConditionVariable sHostResolverBlock = new ConditionVariable(); + public static boolean startNativeTestServer(Context context) { return nativeStartNativeTestServer( TestFilesInstaller.getInstalledPath(context)); @@ -23,6 +27,19 @@ public final class NativeTestServer { nativeShutdownNativeTestServer(); } + /** + * Registers customized DNS mapping for {@link NativeTestServer}. + * @param contextAdapter native context adapter object that this + * mapping should apply to. + * @param isLegacyAPI {@code true} if this context adapter is a part of the + * old API. + */ + public static void registerHostResolverProc(long contextAdapter, boolean isLegacyAPI) { + sHostResolverBlock.close(); + nativeRegisterHostResolverProc(contextAdapter, isLegacyAPI); + sHostResolverBlock.block(); + } + public static String getEchoBodyURL() { return nativeGetEchoBodyURL(); } @@ -47,12 +64,24 @@ public final class NativeTestServer { return nativeGetFileURL(filePath); } + public static String getSdchURL() { + return nativeGetSdchURL(); + } + + @CalledByNative + private static void onHostResolverProcRegistered() { + sHostResolverBlock.open(); + } + private static native boolean nativeStartNativeTestServer(String filePath); private static native void nativeShutdownNativeTestServer(); + private static native void nativeRegisterHostResolverProc( + long contextAdapter, boolean isLegacyAPI); private static native String nativeGetEchoBodyURL(); private static native String nativeGetEchoHeaderURL(String header); private static native String nativeGetEchoAllHeadersURL(); private static native String nativeGetEchoMethodURL(); private static native String nativeGetRedirectToEchoBody(); private static native String nativeGetFileURL(String filePath); + private static native String nativeGetSdchURL(); } diff --git a/components/cronet/android/url_request_adapter.cc b/components/cronet/android/url_request_adapter.cc index 59d2aed..43fb8f4 100644 --- a/components/cronet/android/url_request_adapter.cc +++ b/components/cronet/android/url_request_adapter.cc @@ -114,6 +114,12 @@ std::string URLRequestAdapter::GetNegotiatedProtocol() const { return url_request_->response_info().npn_negotiated_protocol; } +bool URLRequestAdapter::GetWasCached() const { + if (url_request_ == NULL) + return false; + return url_request_->response_info().was_cached; +} + void URLRequestAdapter::Start() { context_->PostTaskToNetworkThread( FROM_HERE, diff --git a/components/cronet/android/url_request_adapter.h b/components/cronet/android/url_request_adapter.h index 8704a00..4ba153e 100644 --- a/components/cronet/android/url_request_adapter.h +++ b/components/cronet/android/url_request_adapter.h @@ -118,6 +118,9 @@ class URLRequestAdapter : public net::URLRequest::Delegate { // Get NPN or ALPN Negotiated Protocol (if any) from HttpResponseInfo. std::string GetNegotiatedProtocol() const; + // Returns whether the response is serviced from cache. + bool GetWasCached() const; + // net::URLRequest::Delegate implementation: void OnResponseStarted(net::URLRequest* request) override; void OnReadCompleted(net::URLRequest* request, int bytes_read) override; diff --git a/components/cronet/android/url_request_context_adapter.cc b/components/cronet/android/url_request_context_adapter.cc index d2adeb4..1524baf 100644 --- a/components/cronet/android/url_request_context_adapter.cc +++ b/components/cronet/android/url_request_context_adapter.cc @@ -23,6 +23,7 @@ #include "net/http/http_server_properties.h" #include "net/log/write_to_file_net_log_observer.h" #include "net/proxy/proxy_service.h" +#include "net/sdch/sdch_owner.h" #include "net/ssl/ssl_config_service_defaults.h" #include "net/url_request/static_http_user_agent_settings.h" #include "net/url_request/url_request_context_builder.h" @@ -151,6 +152,12 @@ void URLRequestContextAdapter::InitRequestContextOnNetworkThread() { context_.reset(context_builder.Build()); + if (config_->enable_sdch) { + DCHECK(context_->sdch_manager()); + sdch_owner_.reset( + new net::SdchOwner(context_->sdch_manager(), context_.get())); + } + // Currently (circa M39) enabling QUIC requires setting probability threshold. if (config_->enable_quic) { context_->http_server_properties() diff --git a/components/cronet/android/url_request_context_adapter.h b/components/cronet/android/url_request_context_adapter.h index cdcc79f..5460911 100644 --- a/components/cronet/android/url_request_context_adapter.h +++ b/components/cronet/android/url_request_context_adapter.h @@ -20,11 +20,9 @@ #include "net/url_request/url_request_context_getter.h" namespace net { - class WriteToFileNetLogObserver; - class ProxyConfigService; - +class SdchOwner; } // namespace net namespace cronet { @@ -109,6 +107,7 @@ class URLRequestContextAdapter : public net::URLRequestContextGetter { scoped_ptr<NetLogObserver> net_log_observer_; scoped_ptr<net::WriteToFileNetLogObserver> write_to_file_observer_; scoped_ptr<net::ProxyConfigService> proxy_config_service_; + scoped_ptr<net::SdchOwner> sdch_owner_; scoped_ptr<URLRequestContextConfig> config_; // A queue of tasks that need to be run after context has been initialized. diff --git a/components/cronet/url_request_context_config.cc b/components/cronet/url_request_context_config.cc index 0aa2089..c8f2150 100644 --- a/components/cronet/url_request_context_config.cc +++ b/components/cronet/url_request_context_config.cc @@ -78,6 +78,7 @@ void URLRequestContextConfig::ConfigureURLRequestContextBuilder( context_builder->SetSpdyAndQuicEnabled(enable_spdy, enable_quic); context_builder->set_quic_connection_options( net::QuicUtils::ParseQuicConnectionOptions(quic_connection_options)); + context_builder->set_sdch_enabled(enable_sdch); // TODO(mef): Use |config| to set cookies. } @@ -92,6 +93,8 @@ void URLRequestContextConfig::RegisterJSONConverter( &URLRequestContextConfig::enable_quic); converter->RegisterBoolField(REQUEST_CONTEXT_CONFIG_ENABLE_SPDY, &URLRequestContextConfig::enable_spdy); + converter->RegisterBoolField(REQUEST_CONTEXT_CONFIG_ENABLE_SDCH, + &URLRequestContextConfig::enable_sdch); converter->RegisterStringField(REQUEST_CONTEXT_CONFIG_HTTP_CACHE, &URLRequestContextConfig::http_cache); converter->RegisterBoolField(REQUEST_CONTEXT_CONFIG_LOAD_DISABLE_CACHE, diff --git a/components/cronet/url_request_context_config.h b/components/cronet/url_request_context_config.h index 30530f5..a74f440 100644 --- a/components/cronet/url_request_context_config.h +++ b/components/cronet/url_request_context_config.h @@ -58,6 +58,8 @@ struct URLRequestContextConfig { bool enable_quic; // Enable SPDY. bool enable_spdy; + // Enable SDCH. + bool enable_sdch; // Type of http cache: "HTTP_CACHE_DISABLED", "HTTP_CACHE_DISK" or // "HTTP_CACHE_IN_MEMORY". std::string http_cache; diff --git a/components/cronet/url_request_context_config_list.h b/components/cronet/url_request_context_config_list.h index bf237e9..c5451a9 100644 --- a/components/cronet/url_request_context_config_list.h +++ b/components/cronet/url_request_context_config_list.h @@ -14,6 +14,7 @@ DEFINE_CONTEXT_CONFIG(ENABLE_LEGACY_MODE) DEFINE_CONTEXT_CONFIG(NATIVE_LIBRARY_NAME) DEFINE_CONTEXT_CONFIG(ENABLE_QUIC) DEFINE_CONTEXT_CONFIG(ENABLE_SPDY) +DEFINE_CONTEXT_CONFIG(ENABLE_SDCH) DEFINE_CONTEXT_CONFIG(HTTP_CACHE) DEFINE_CONTEXT_CONFIG(HTTP_CACHE_MAX_SIZE) diff --git a/net/url_request/url_request_context_builder.cc b/net/url_request/url_request_context_builder.cc index bcb5c68..5e6f721 100644 --- a/net/url_request/url_request_context_builder.cc +++ b/net/url_request/url_request_context_builder.cc @@ -16,6 +16,7 @@ #include "net/base/cache_type.h" #include "net/base/net_errors.h" #include "net/base/network_delegate_impl.h" +#include "net/base/sdch_manager.h" #include "net/cert/cert_verifier.h" #include "net/cookies/cookie_monster.h" #include "net/dns/host_resolver.h" @@ -205,7 +206,8 @@ URLRequestContextBuilder::URLRequestContextBuilder() ftp_enabled_(false), #endif http_cache_enabled_(true), - throttling_enabled_(false) { + throttling_enabled_(false), + sdch_enabled_(false) { } URLRequestContextBuilder::~URLRequestContextBuilder() {} @@ -308,6 +310,11 @@ URLRequestContext* URLRequestContextBuilder::Build() { new DefaultChannelIDStore(NULL), context->GetFileTaskRunner()))); } + if (sdch_enabled_) { + storage->set_sdch_manager( + scoped_ptr<net::SdchManager>(new SdchManager()).Pass()); + } + storage->set_transport_security_state(new TransportSecurityState()); if (!transport_security_persister_path_.empty()) { context->set_transport_security_persister( diff --git a/net/url_request/url_request_context_builder.h b/net/url_request/url_request_context_builder.h index b21a652..2039baa 100644 --- a/net/url_request/url_request_context_builder.h +++ b/net/url_request/url_request_context_builder.h @@ -141,7 +141,6 @@ class NET_EXPORT URLRequestContextBuilder { network_delegate_.reset(delegate); } - // Adds additional auth handler factories to be used in addition to what is // provided in the default |HttpAuthHandlerRegistryFactory|. The auth |scheme| // and |factory| are provided. The builder takes ownership of the factory and @@ -196,6 +195,13 @@ class NET_EXPORT URLRequestContextBuilder { void SetFileTaskRunner( const scoped_refptr<base::SingleThreadTaskRunner>& task_runner); + // Note that if SDCH is enabled without a policy object observing + // the SDCH manager and handling at least Get-Dictionary events, the + // result will be "Content-Encoding: sdch" advertisements, but no + // dictionaries fetches and no specific dictionaries advertised. + // SdchOwner in net/sdch/sdch_owner.h is a simple policy object. + void set_sdch_enabled(bool enable) { sdch_enabled_ = enable; } + URLRequestContext* Build(); private: @@ -221,6 +227,7 @@ class NET_EXPORT URLRequestContextBuilder { #endif bool http_cache_enabled_; bool throttling_enabled_; + bool sdch_enabled_; scoped_refptr<base::SingleThreadTaskRunner> file_task_runner_; HttpCacheParams http_cache_params_; |