From 97652ebe50c4be712aa976776bee9694cbce1178 Mon Sep 17 00:00:00 2001 From: jeremyim Date: Mon, 23 Mar 2015 21:08:35 -0700 Subject: [PATCH] Add ClientConfig proto, and JSON generation/parsing. - Add the ClientConfig proto definition corresponding to the response coming from the service. - Create a means of generating a pseudo response from the built in params and request options. - Create a class to parse the JSON response. - Add necessary helper classes for the ProxyScheme and time values coming from the service. BUG=466753 Review URL: https://codereview.chromium.org/1017853003 Cr-Commit-Position: refs/heads/master@{#321931} --- components/components_tests.gyp | 2 + components/data_reduction_proxy.gypi | 24 +++ .../data_reduction_proxy/core/browser/BUILD.gn | 5 + .../data_reduction_proxy_config_service_client.cc | 39 ++++ .../data_reduction_proxy_config_service_client.h | 48 +++++ ...duction_proxy_config_service_client_unittest.cc | 52 +++++ .../data_reduction_proxy_request_options.cc | 54 ++++- .../browser/data_reduction_proxy_request_options.h | 29 ++- ...ata_reduction_proxy_request_options_unittest.cc | 96 +++++---- .../browser/data_reduction_proxy_test_utils.cc | 73 ++++++- .../core/browser/data_reduction_proxy_test_utils.h | 56 ++++- .../data_reduction_proxy/core/common/BUILD.gn | 5 + .../data_reduction_proxy_client_config_parser.cc | 166 +++++++++++++++ .../data_reduction_proxy_client_config_parser.h | 51 +++++ ...eduction_proxy_client_config_parser_unittest.cc | 228 +++++++++++++++++++++ .../core/common/data_reduction_proxy_params.cc | 43 ++++ .../core/common/data_reduction_proxy_params.h | 5 + .../common/data_reduction_proxy_params_unittest.cc | 27 +++ components/data_reduction_proxy/proto/BUILD.gn | 11 + .../data_reduction_proxy/proto/client_config.proto | 70 +++++++ 20 files changed, 1034 insertions(+), 50 deletions(-) create mode 100644 components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc create mode 100644 components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.h create mode 100644 components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client_unittest.cc create mode 100644 components/data_reduction_proxy/core/common/data_reduction_proxy_client_config_parser.cc create mode 100644 components/data_reduction_proxy/core/common/data_reduction_proxy_client_config_parser.h create mode 100644 components/data_reduction_proxy/core/common/data_reduction_proxy_client_config_parser_unittest.cc create mode 100644 components/data_reduction_proxy/proto/BUILD.gn create mode 100644 components/data_reduction_proxy/proto/client_config.proto diff --git a/components/components_tests.gyp b/components/components_tests.gyp index df6bbf1a1c8a..db07e62fbd46 100644 --- a/components/components_tests.gyp +++ b/components/components_tests.gyp @@ -103,6 +103,7 @@ 'data_reduction_proxy_unittest_sources': [ 'data_reduction_proxy/content/browser/data_reduction_proxy_message_filter_unittest.cc', 'data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol_unittest.cc', + 'data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client_unittest.cc', 'data_reduction_proxy/core/browser/data_reduction_proxy_config_unittest.cc', 'data_reduction_proxy/core/browser/data_reduction_proxy_configurator_unittest.cc', 'data_reduction_proxy/core/browser/data_reduction_proxy_interceptor_unittest.cc', @@ -115,6 +116,7 @@ 'data_reduction_proxy/core/browser/data_reduction_proxy_statistics_prefs_unittest.cc', 'data_reduction_proxy/core/browser/data_reduction_proxy_tamper_detection_unittest.cc', 'data_reduction_proxy/core/browser/data_reduction_proxy_usage_stats_unittest.cc', + 'data_reduction_proxy/core/common/data_reduction_proxy_client_config_parser_unittest.cc', 'data_reduction_proxy/core/common/data_reduction_proxy_event_store_unittest.cc', 'data_reduction_proxy/core/common/data_reduction_proxy_headers_unittest.cc', 'data_reduction_proxy/core/common/data_reduction_proxy_params_unittest.cc', diff --git a/components/data_reduction_proxy.gypi b/components/data_reduction_proxy.gypi index e387910fc6f7..098c8a19dfca 100644 --- a/components/data_reduction_proxy.gypi +++ b/components/data_reduction_proxy.gypi @@ -83,6 +83,7 @@ '../net/net.gyp:net', '../url/url.gyp:url_lib', 'data_reduction_proxy_core_common', + 'data_reduction_proxy_proto', 'pref_registry', ], 'include_dirs': [ @@ -95,6 +96,8 @@ 'data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol.h', 'data_reduction_proxy/core/browser/data_reduction_proxy_config.cc', 'data_reduction_proxy/core/browser/data_reduction_proxy_config.h', + 'data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc', + 'data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.h', 'data_reduction_proxy/core/browser/data_reduction_proxy_configurator.cc', 'data_reduction_proxy/core/browser/data_reduction_proxy_configurator.h', 'data_reduction_proxy/core/browser/data_reduction_proxy_delegate.cc', @@ -130,6 +133,7 @@ 'dependencies': [ '../base/base.gyp:base', '../url/url.gyp:url_lib', + 'data_reduction_proxy_proto', ], 'include_dirs': [ '..', @@ -137,6 +141,8 @@ 'sources': [ # Note: sources list duplicated in GN build. 'data_reduction_proxy/core/common/data_reduction_proxy_bypass_type_list.h', + 'data_reduction_proxy/core/common/data_reduction_proxy_client_config_parser.cc', + 'data_reduction_proxy/core/common/data_reduction_proxy_client_config_parser.h', 'data_reduction_proxy/core/common/data_reduction_proxy_config_values.h', 'data_reduction_proxy/core/common/data_reduction_proxy_event_store.cc', 'data_reduction_proxy/core/common/data_reduction_proxy_event_store.h', @@ -186,6 +192,24 @@ 'msvs_disabled_warnings': [4267, ], }, { + # GN version: //components/data_reduction_proxy/proto + 'target_name': 'data_reduction_proxy_proto', + 'type': 'static_library', + 'dependencies': [ + ], + 'include_dirs': [ + ], + 'sources': [ + # Note: sources list duplicated in GN build. + 'data_reduction_proxy/proto/client_config.proto', + ], + 'variables': { + 'proto_in_dir': 'data_reduction_proxy/proto', + 'proto_out_dir': 'components/data_reduction_proxy/proto', + }, + 'includes': [ '../build/protoc.gypi' ], + }, + { 'target_name': 'data_reduction_proxy_version_header', 'type': 'none', 'direct_dependent_settings': { diff --git a/components/data_reduction_proxy/core/browser/BUILD.gn b/components/data_reduction_proxy/core/browser/BUILD.gn index 0bc2867107cf..5790d4ccd78c 100644 --- a/components/data_reduction_proxy/core/browser/BUILD.gn +++ b/components/data_reduction_proxy/core/browser/BUILD.gn @@ -8,6 +8,8 @@ static_library("browser") { "data_reduction_proxy_bypass_protocol.h", "data_reduction_proxy_config.cc", "data_reduction_proxy_config.h", + "data_reduction_proxy_config_service_client.cc", + "data_reduction_proxy_config_service_client.h", "data_reduction_proxy_configurator.cc", "data_reduction_proxy_configurator.h", "data_reduction_proxy_debug_ui_service.h", @@ -41,6 +43,7 @@ static_library("browser") { "//base", "//base:prefs", "//components/data_reduction_proxy/core/common", + "//components/data_reduction_proxy/proto:data_reduction_proxy_proto", "//components/pref_registry", "//crypto", "//net", @@ -85,6 +88,7 @@ source_set("unit_tests") { testonly = true sources = [ "data_reduction_proxy_bypass_protocol_unittest.cc", + "data_reduction_proxy_config_service_client_unittest.cc", "data_reduction_proxy_config_unittest.cc", "data_reduction_proxy_configurator_unittest.cc", "data_reduction_proxy_interceptor_unittest.cc", @@ -105,6 +109,7 @@ source_set("unit_tests") { "//base:prefs_test_support", "//base/test:test_support", "//components/data_reduction_proxy/core/common:test_support", + "//components/data_reduction_proxy/proto:data_reduction_proxy_proto", "//net:test_support", "//testing/gmock", "//testing/gtest", diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc new file mode 100644 index 000000000000..39a9481bc782 --- /dev/null +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc @@ -0,0 +1,39 @@ +// 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 "components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.h" + +#include "base/json/json_writer.h" +#include "base/logging.h" +#include "base/values.h" +#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h" +#include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h" + +namespace data_reduction_proxy { + +DataReductionProxyConfigServiceClient::DataReductionProxyConfigServiceClient( + DataReductionProxyParams* params, + DataReductionProxyRequestOptions* request_options) + : params_(params), + request_options_(request_options) { + DCHECK(params); + DCHECK(request_options); +} + +DataReductionProxyConfigServiceClient:: + ~DataReductionProxyConfigServiceClient() { +} + +std::string +DataReductionProxyConfigServiceClient::ConstructStaticResponse() const { + std::string response; + scoped_ptr values(new base::DictionaryValue()); + params_->PopulateConfigResponse(values.get()); + request_options_->PopulateConfigResponse(values.get()); + base::JSONWriter::Write(values.get(), &response); + + return response; +} + +} // namespace data_reduction_proxy diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.h new file mode 100644 index 000000000000..648cee4ab009 --- /dev/null +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.h @@ -0,0 +1,48 @@ +// 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_DATA_REDUCTION_PROXY_CORE_BROWSER_DATA_REDUCTION_PROXY_CONFIG_SERVICE_CLIENT_H_ +#define COMPONENTS_DATA_REDUCTION_PROXY_CORE_BROWSER_DATA_REDUCTION_PROXY_CONFIG_SERVICE_CLIENT_H_ + +#include + +#include "base/gtest_prod_util.h" +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" + +namespace data_reduction_proxy { + +class DataReductionProxyParams; +class DataReductionProxyRequestOptions; + +// Retrieves the Data Reduction Proxy configuration from a remote service. This +// object lives on the IO thread. +class DataReductionProxyConfigServiceClient { + public: + // The caller must ensure that all parameters remain alive for the lifetime of + // the |DataReductionProxyConfigClient|. + DataReductionProxyConfigServiceClient( + DataReductionProxyParams* params, + DataReductionProxyRequestOptions* request_options); + + ~DataReductionProxyConfigServiceClient(); + + private: + FRIEND_TEST_ALL_PREFIXES(DataReductionProxyConfigClientTest, + TestConstructStaticResponse); + + // Constructs a synthetic response based on |params_|. + std::string ConstructStaticResponse() const; + + // The caller must ensure that the |params_| outlives this instance. + DataReductionProxyParams* params_; + + // The caller must ensure that the |request_options_| outlives this instance. + DataReductionProxyRequestOptions* request_options_; + + DISALLOW_COPY_AND_ASSIGN(DataReductionProxyConfigServiceClient); +}; + +} // namespace data_reduction_proxy +#endif // COMPONENTS_DATA_REDUCTION_PROXY_CORE_BROWSER_DATA_REDUCTION_PROXY_CONFIG_SERVICE_CLIENT_H_ diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client_unittest.cc new file mode 100644 index 000000000000..6b44ceb5b70b --- /dev/null +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client_unittest.cc @@ -0,0 +1,52 @@ +// 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 "components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.h" + +#include + +#include "base/memory/scoped_ptr.h" +#include "base/values.h" +#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h" +#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h" +#include "components/data_reduction_proxy/core/common/data_reduction_proxy_client_config_parser.h" +#include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h" +#include "components/data_reduction_proxy/core/common/data_reduction_proxy_params_test_utils.h" +#include "components/data_reduction_proxy/proto/client_config.pb.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace data_reduction_proxy { + +class DataReductionProxyConfigClientTest : public testing::Test { + protected: + void SetUp() override { + test_context_ = + DataReductionProxyTestContext::Builder() + .WithParamsFlags(DataReductionProxyParams::kAllowed | + DataReductionProxyParams::kFallbackAllowed | + DataReductionProxyParams::kPromoAllowed) + .WithParamsDefinitions(TestDataReductionProxyParams::HAS_EVERYTHING) + .Build(); + } + + scoped_ptr BuildConfigClient() { + return make_scoped_ptr(new DataReductionProxyConfigServiceClient( + test_context_->config()->test_params(), + test_context_->io_data()->request_options())); + } + + private: + scoped_ptr test_context_; +}; + +TEST_F(DataReductionProxyConfigClientTest, TestConstructStaticResponse) { + scoped_ptr config_client = + BuildConfigClient(); + std::string config_data = config_client->ConstructStaticResponse(); + ClientConfig config; + EXPECT_TRUE(config_parser::ParseClientConfig(config_data, &config)); +} + +} // namespace data_reduction_proxy diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.cc index 985dfa5e18d9..675b05ce0eac 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.cc +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.cc @@ -13,8 +13,10 @@ #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "base/time/time.h" +#include "base/values.h" #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h" #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h" +#include "components/data_reduction_proxy/core/common/data_reduction_proxy_client_config_parser.h" #include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h" #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h" #include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h" @@ -69,6 +71,29 @@ bool DataReductionProxyRequestOptions::IsKeySetOnCommandLine() { data_reduction_proxy::switches::kDataReductionProxyKey); } +// static +std::string DataReductionProxyRequestOptions::CreateLocalSessionKey( + const std::string& session, + const std::string& credentials) { + return base::StringPrintf("%s|%s", session.c_str(), credentials.c_str()); +} + +// static +bool DataReductionProxyRequestOptions::ParseLocalSessionKey( + const std::string& session_key, + std::string* session, + std::string* credentials) { + std::vector auth_values; + base::SplitString(session_key, '|', &auth_values); + if (auth_values.size() == 2) { + *session = auth_values[0]; + *credentials = auth_values[1]; + return true; + } + + return false; +} + DataReductionProxyRequestOptions::DataReductionProxyRequestOptions( Client client, DataReductionProxyConfig* config, @@ -172,7 +197,8 @@ base::Time DataReductionProxyRequestOptions::Now() const { return base::Time::Now(); } -void DataReductionProxyRequestOptions::RandBytes(void* output, size_t length) { +void DataReductionProxyRequestOptions::RandBytes(void* output, + size_t length) const { crypto::RandBytes(output, length); } @@ -200,10 +226,9 @@ void DataReductionProxyRequestOptions::MaybeAddProxyTunnelRequestHandler( void DataReductionProxyRequestOptions::SetHeader( net::HttpRequestHeaders* headers) { base::Time now = Now(); - // Authorization credentials must be regenerated at least every 24 hours. - if (now - last_credentials_update_time_ > base::TimeDelta::FromHours(24)) { + // Authorization credentials must be regenerated if they are expired. + if (now > credentials_expiration_time_) UpdateCredentials(); - } UpdateLoFi(); const char kChromeProxyHeader[] = "Chrome-Proxy"; std::string header_value; @@ -219,7 +244,7 @@ void DataReductionProxyRequestOptions::SetHeader( void DataReductionProxyRequestOptions::ComputeCredentials( const base::Time& now, std::string* session, - std::string* credentials) { + std::string* credentials) const { DCHECK(session); DCHECK(credentials); int64 timestamp = @@ -241,8 +266,9 @@ void DataReductionProxyRequestOptions::ComputeCredentials( void DataReductionProxyRequestOptions::UpdateCredentials() { std::string session; std::string credentials; - last_credentials_update_time_ = Now(); - ComputeCredentials(last_credentials_update_time_, &session_, &credentials_); + base::Time now = Now(); + ComputeCredentials(now, &session_, &credentials_); + credentials_expiration_time_ = now + base::TimeDelta::FromHours(24); RegenerateRequestHeaderValue(); } @@ -253,6 +279,20 @@ void DataReductionProxyRequestOptions::SetKeyOnIO(const std::string& key) { } } +void DataReductionProxyRequestOptions::PopulateConfigResponse( + base::DictionaryValue* response) const { + DCHECK(network_task_runner_->BelongsToCurrentThread()); + std::string session; + std::string credentials; + base::Time now = Now(); + base::Time expiration_time = now + base::TimeDelta::FromHours(24); + ComputeCredentials(now, &session, &credentials); + response->SetString("sessionKey", + CreateLocalSessionKey(session, credentials)); + response->SetString("expireTime", + config_parser::TimeToISO8601(expiration_time)); +} + std::string DataReductionProxyRequestOptions::GetDefaultKey() const { const base::CommandLine& command_line = *base::CommandLine::ForCurrentProcess(); diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h index e5f7ce13931a..5a0b3b0092e8 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h @@ -14,6 +14,7 @@ #include "base/time/time.h" namespace base { +class DictionaryValue; class SingleThreadTaskRunner; } @@ -65,6 +66,22 @@ class DataReductionProxyRequestOptions { public: static bool IsKeySetOnCommandLine(); + // A pair of functions to convert the session and credentials for the Data + // Reduction Proxy to and from a single string; they are used to encode the + // session and credentials values into the |session_key| field of the + // ClientConfig protocol buffer. The delimiter used is '|', as it is not a + // valid character in a session or credentials string. + // + // CreateLocalSessionKey joins session and credentials with the delimiter. + static std::string CreateLocalSessionKey(const std::string& session, + const std::string& credentials); + + // ParseLocalSessionKey splits the output of CreateLocalSessionKey into its + // two components. |session| and |credentials| must not be null. + static bool ParseLocalSessionKey(const std::string& session_key, + std::string* session, + std::string* credentials); + // Constructs a DataReductionProxyRequestOptions object with the given // client type, config, and network task runner. DataReductionProxyRequestOptions( @@ -104,6 +121,10 @@ class DataReductionProxyRequestOptions { // SetKeyOnIO is called. void SetKeyOnIO(const std::string& key); + // Populates |response| with the Data Reduction Proxy authentication info. + // Virtualized for testing. + virtual void PopulateConfigResponse(base::DictionaryValue* response) const; + protected: void SetHeader(net::HttpRequestHeaders* headers); @@ -114,7 +135,7 @@ class DataReductionProxyRequestOptions { const std::string& key); // Visible for testing. virtual base::Time Now() const; - virtual void RandBytes(void* output, size_t length); + virtual void RandBytes(void* output, size_t length) const; // Visible for testing. virtual std::string GetDefaultKey() const; @@ -153,7 +174,7 @@ class DataReductionProxyRequestOptions { // the data reduction proxy. void ComputeCredentials(const base::Time& now, std::string* session, - std::string* credentials); + std::string* credentials) const; // Generates and updates the session ID and credentials. void UpdateCredentials(); @@ -186,9 +207,9 @@ class DataReductionProxyRequestOptions { std::string lofi_; std::vector experiments_; - // The last time the session was updated. Used to ensure that a session is + // The time at which the session expires. Used to ensure that a session is // never used for more than twenty-four hours. - base::Time last_credentials_update_time_; + base::Time credentials_expiration_time_; DataReductionProxyConfig* data_reduction_proxy_config_; diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options_unittest.cc index 70ea1f46df1e..d34ebf2d415c 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options_unittest.cc +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options_unittest.cc @@ -28,7 +28,6 @@ const char kVersion[] = "0.1.2.3"; const char kExpectedBuild[] = "2"; const char kExpectedPatch[] = "3"; const char kBogusVersion[] = "0.0"; -const char kTestKey[] = "test-key"; const char kExpectedCredentials[] = "96bd72ec4a050ba60981743d41787768"; const char kExpectedSession[] = "0-1633771873-1633771873-1633771873"; @@ -77,41 +76,6 @@ const Client kClient = Client::UNKNOWN; const char kClientStr[] = ""; #endif -class TestDataReductionProxyRequestOptions - : public DataReductionProxyRequestOptions { - public: - TestDataReductionProxyRequestOptions( - Client client, - const std::string& version, - DataReductionProxyConfig* config, - scoped_refptr task_runner) - : DataReductionProxyRequestOptions( - client, version, config, task_runner) {} - - std::string GetDefaultKey() const override { - return kTestKey; - } - - base::Time Now() const override { - return base::Time::UnixEpoch() + now_offset_; - } - - void RandBytes(void* output, size_t length) override { - char* c = static_cast(output); - for (size_t i = 0; i < length; ++i) { - c[i] = 'a'; - } - } - - // Time after the unix epoch that Now() reports. - void set_offset(const base::TimeDelta& now_offset) { - now_offset_ = now_offset; - } - - private: - base::TimeDelta now_offset_; -}; - void SetHeaderExpectations(const std::string& session, const std::string& credentials, const std::string& client, @@ -339,4 +303,64 @@ TEST_F(DataReductionProxyRequestOptionsTest, ParseExperiments) { VerifyExpectedHeader(params()->DefaultOrigin(), expected_header); } +TEST_F(DataReductionProxyRequestOptionsTest, ParseLocalSessionKey) { + const struct { + bool should_succeed; + std::string session_key; + std::string expected_session; + std::string expected_credentials; + } tests[] = { + { + true, + "foobar|1234", + "foobar", + "1234", + }, + { + false, + "foobar|1234|foobaz", + std::string(), + std::string(), + }, + { + false, + "foobar", + std::string(), + std::string(), + }, + { + false, + std::string(), + std::string(), + std::string(), + }, + }; + + std::string session; + std::string credentials; + for (size_t i = 0; i < arraysize(tests); ++i) { + EXPECT_EQ(tests[i].should_succeed, + DataReductionProxyRequestOptions::ParseLocalSessionKey( + tests[i].session_key, &session, &credentials)); + if (tests[i].should_succeed) { + EXPECT_EQ(tests[i].expected_session, session); + EXPECT_EQ(tests[i].expected_credentials, credentials); + } + } +} + +TEST_F(DataReductionProxyRequestOptionsTest, PopulateConfigResponse) { + CreateRequestOptions(kBogusVersion); + scoped_ptr values(new base::DictionaryValue()); + request_options()->PopulateConfigResponse(values.get()); + std::string session; + std::string expire_time; + EXPECT_TRUE(values->GetString("sessionKey", &session)); + EXPECT_TRUE(values->GetString("expireTime", &expire_time)); + EXPECT_EQ( + "0-1633771873-1633771873-1633771873|96bd72ec4a050ba60981743d41787768", + session); + EXPECT_EQ("1970-01-02T00:00:00.000Z", expire_time); +} + } // namespace data_reduction_proxy diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc index 422cd424f34a..e9ea65ee061d 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc @@ -22,8 +22,55 @@ #include "net/url_request/url_request_job_factory_impl.h" #include "net/url_request/url_request_test_util.h" +namespace { + +const char kTestKey[] = "test-key"; + +} // namespace + namespace data_reduction_proxy { +TestDataReductionProxyRequestOptions::TestDataReductionProxyRequestOptions( + Client client, + const std::string& version, + DataReductionProxyConfig* config, + scoped_refptr task_runner) + : DataReductionProxyRequestOptions(client, version, config, task_runner) { +} + +std::string TestDataReductionProxyRequestOptions::GetDefaultKey() const { + return kTestKey; +} + +base::Time TestDataReductionProxyRequestOptions::Now() const { + return base::Time::UnixEpoch() + now_offset_; +} + +void TestDataReductionProxyRequestOptions::RandBytes(void* output, + size_t length) const { + char* c = static_cast(output); + for (size_t i = 0; i < length; ++i) { + c[i] = 'a'; + } +} + +// Time after the unix epoch that Now() reports. +void TestDataReductionProxyRequestOptions::set_offset( + const base::TimeDelta& now_offset) { + now_offset_ = now_offset; +} + +MockDataReductionProxyRequestOptions::MockDataReductionProxyRequestOptions( + Client client, + const std::string& version, + DataReductionProxyConfig* config, + scoped_refptr task_runner) + : DataReductionProxyRequestOptions(client, version, config, task_runner) { +} + +MockDataReductionProxyRequestOptions::~MockDataReductionProxyRequestOptions() { +} + MockDataReductionProxyService::MockDataReductionProxyService( scoped_ptr statistics_prefs, DataReductionProxySettings* settings, @@ -65,6 +112,7 @@ DataReductionProxyTestContext::Builder::Builder() use_mock_config_(false), use_test_configurator_(false), use_mock_service_(false), + use_mock_request_options_(false), skip_settings_initialization_(false) { } @@ -120,6 +168,12 @@ DataReductionProxyTestContext::Builder::WithMockDataReductionProxyService() { } DataReductionProxyTestContext::Builder& +DataReductionProxyTestContext::Builder::WithMockRequestOptions() { + use_mock_request_options_ = true; + return *this; +} + +DataReductionProxyTestContext::Builder& DataReductionProxyTestContext::Builder::SkipSettingsInitialization() { skip_settings_initialization_ = true; return *this; @@ -173,8 +227,15 @@ DataReductionProxyTestContext::Builder::Build() { configurator.get(), event_store.get())); } - scoped_ptr request_options( - new DataReductionProxyRequestOptions(client_, config.get(), task_runner)); + scoped_ptr request_options; + if (use_mock_request_options_) { + test_context_flags |= USE_MOCK_REQUEST_OPTIONS; + request_options.reset(new MockDataReductionProxyRequestOptions( + client_, std::string(), config.get(), task_runner)); + } else { + request_options.reset(new DataReductionProxyRequestOptions( + client_, config.get(), task_runner)); + } scoped_ptr settings( new DataReductionProxySettings()); @@ -344,6 +405,14 @@ DataReductionProxyTestContext::mock_data_reduction_proxy_service() data_reduction_proxy_service()); } +MockDataReductionProxyRequestOptions* +DataReductionProxyTestContext::mock_request_options() const { + DCHECK(test_context_flags_ & + DataReductionProxyTestContext::USE_MOCK_REQUEST_OPTIONS); + return reinterpret_cast( + io_data_->request_options()); +} + DataReductionProxyUsageStats::UnreachableCallback DataReductionProxyTestContext::unreachable_callback() const { return base::Bind(&DataReductionProxySettings::SetUnreachable, diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h index 589e391c61ab..9e0695ef8133 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h @@ -5,9 +5,15 @@ #ifndef COMPONENTS_DATA_REDUCTION_PROXY_CORE_BROWSER_DATA_REDUCTION_PROXY_TEST_UTILS_H_ #define COMPONENTS_DATA_REDUCTION_PROXY_CORE_BROWSER_DATA_REDUCTION_PROXY_TEST_UTILS_H_ +#include + #include "base/macros.h" +#include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" +#include "base/single_thread_task_runner.h" +#include "base/time/time.h" #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h" +#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h" #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h" #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_test_utils.h" #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_usage_stats.h" @@ -18,7 +24,6 @@ class TestingPrefServiceSimple; namespace base { class MessageLoopForUI; -class SingleThreadTaskRunner; } namespace net { @@ -40,6 +45,44 @@ class MockDataReductionProxyConfig; class TestDataReductionProxyConfig; class TestDataReductionProxyConfigurator; +// Test version of |DataReductionProxyRequestOptions|. +class TestDataReductionProxyRequestOptions + : public DataReductionProxyRequestOptions { + public: + TestDataReductionProxyRequestOptions( + Client client, + const std::string& version, + DataReductionProxyConfig* config, + scoped_refptr task_runner); + + // Overrides of DataReductionProxyRequestOptions. + std::string GetDefaultKey() const override; + base::Time Now() const override; + void RandBytes(void* output, size_t length) const override; + + // Time after the unix epoch that Now() reports. + void set_offset(const base::TimeDelta& now_offset); + + private: + base::TimeDelta now_offset_; +}; + +// Mock version of |DataReductionProxyRequestOptions|. +class MockDataReductionProxyRequestOptions + : public DataReductionProxyRequestOptions { + public: + MockDataReductionProxyRequestOptions( + Client client, + const std::string& version, + DataReductionProxyConfig* config, + scoped_refptr task_runner); + + ~MockDataReductionProxyRequestOptions(); + + MOCK_CONST_METHOD1(PopulateConfigResponse, + void(base::DictionaryValue* response)); +}; + // Test version of |DataReductionProxyService|, which permits mocking of various // methods. class MockDataReductionProxyService : public DataReductionProxyService { @@ -118,6 +161,10 @@ class DataReductionProxyTestContext { // |DataReductionProxyService|. Builder& WithMockDataReductionProxyService(); + // Specifies the use of |MockDataReductionProxyRequestOptions| instead of + // |DataReductionProxyRequestOptions|. + Builder& WithMockRequestOptions(); + // Construct, but do not initialize the |DataReductionProxySettings| object. Builder& SkipSettingsInitialization(); @@ -134,6 +181,7 @@ class DataReductionProxyTestContext { bool use_mock_config_; bool use_test_configurator_; bool use_mock_service_; + bool use_mock_request_options_; bool skip_settings_initialization_; }; @@ -179,6 +227,10 @@ class DataReductionProxyTestContext { // be called if built with WithMockDataReductionProxyService. MockDataReductionProxyService* mock_data_reduction_proxy_service() const; + // Returns the underlying |MockDataReductionProxyRequestOptions|. This can + // only be called if built with WithMockRequestOptions. + MockDataReductionProxyRequestOptions* mock_request_options() const; + // Obtains a callback for notifying that the Data Reduction Proxy is no // longer reachable. DataReductionProxyUsageStats::UnreachableCallback @@ -233,6 +285,8 @@ class DataReductionProxyTestContext { SKIP_SETTINGS_INITIALIZATION = 0x4, // Permits mocking of the underlying |DataReductionProxyService|. USE_MOCK_SERVICE = 0x8, + // Permits mocking of the underlying |DataReductionProxyRequestOptions|. + USE_MOCK_REQUEST_OPTIONS = 0x10, }; DataReductionProxyTestContext( diff --git a/components/data_reduction_proxy/core/common/BUILD.gn b/components/data_reduction_proxy/core/common/BUILD.gn index a28e6d0216be..6976004e9091 100644 --- a/components/data_reduction_proxy/core/common/BUILD.gn +++ b/components/data_reduction_proxy/core/common/BUILD.gn @@ -7,6 +7,8 @@ import("//chrome/version.gni") static_library("common") { sources = [ "data_reduction_proxy_bypass_type_list.h", + "data_reduction_proxy_client_config_parser.cc", + "data_reduction_proxy_client_config_parser.h", "data_reduction_proxy_config_values.h", "data_reduction_proxy_event_store.cc", "data_reduction_proxy_event_store.h", @@ -25,6 +27,7 @@ static_library("common") { ] deps = [ "//base", + "//components/data_reduction_proxy/proto:data_reduction_proxy_proto", "//net", "//url", ] @@ -54,6 +57,7 @@ source_set("test_support") { source_set("unit_tests") { testonly = true sources = [ + "data_reduction_proxy_client_config_parser_unittest.cc", "data_reduction_proxy_event_store_unittest.cc", "data_reduction_proxy_headers_unittest.cc", "data_reduction_proxy_params_unittest.cc", @@ -64,6 +68,7 @@ source_set("unit_tests") { ":test_support", "//base", "//base/test:test_support", + "//components/data_reduction_proxy/proto:data_reduction_proxy_proto", "//net:test_support", "//testing/gtest", ] diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_client_config_parser.cc b/components/data_reduction_proxy/core/common/data_reduction_proxy_client_config_parser.cc new file mode 100644 index 000000000000..586ad6851441 --- /dev/null +++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_client_config_parser.cc @@ -0,0 +1,166 @@ +// 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 "components/data_reduction_proxy/core/common/data_reduction_proxy_client_config_parser.h" + +#include + +#include "base/json/json_reader.h" +#include "base/strings/stringprintf.h" +#include "base/time/time.h" +#include "base/values.h" + +namespace { + +// String representations of ProxyServer schemes. +const char kSchemeHTTP[] = "HTTP"; +const char kSchemeHTTPS[] = "HTTPS"; +const char kSchemeQUIC[] = "QUIC"; +const char kSchemeUnspecified[] = "UNSPECIFIED"; + +} // namespace + +namespace data_reduction_proxy { + +namespace config_parser { + +std::string GetSchemeString(net::ProxyServer::Scheme scheme) { + switch (scheme) { + case net::ProxyServer::SCHEME_HTTP: + return kSchemeHTTP; + case net::ProxyServer::SCHEME_HTTPS: + return kSchemeHTTPS; + case net::ProxyServer::SCHEME_QUIC: + return kSchemeQUIC; + default: + return kSchemeUnspecified; + } +} + +net::ProxyServer::Scheme SchemeFromProxyScheme( + ProxyServer_ProxyScheme proxy_scheme) { + switch (proxy_scheme) { + case ProxyServer_ProxyScheme_HTTP: + return net::ProxyServer::SCHEME_HTTP; + case ProxyServer_ProxyScheme_HTTPS: + return net::ProxyServer::SCHEME_HTTPS; + case ProxyServer_ProxyScheme_QUIC: + return net::ProxyServer::SCHEME_QUIC; + default: + return net::ProxyServer::SCHEME_INVALID; + } +} + +ProxyServer_ProxyScheme GetProxyScheme(const std::string& scheme) { + if (scheme == kSchemeHTTP) + return ProxyServer_ProxyScheme_HTTP; + + if (scheme == kSchemeHTTPS) + return ProxyServer_ProxyScheme_HTTPS; + + if (scheme == kSchemeQUIC) + return ProxyServer_ProxyScheme_QUIC; + + return ProxyServer_ProxyScheme_UNSPECIFIED; +} + +std::string TimeToISO8601(const base::Time& time) { + base::Time::Exploded exploded; + time.UTCExplode(&exploded); + return base::StringPrintf( + "%04d-%02d-%02dT%02d:%02d:%02d.%03dZ", exploded.year, exploded.month, + exploded.day_of_month, exploded.hour, exploded.minute, exploded.second, + exploded.millisecond); +} + +bool ISO8601ToTimestamp(const std::string& time, Timestamp* timestamp) { + base::Time t; + if (!base::Time::FromUTCString(time.c_str(), &t)) + return false; + + timestamp->set_seconds((t - base::Time::UnixEpoch()).InSeconds()); + // Discard fractional seconds; it isn't worth the code effort to + // calculate it. + timestamp->set_nanos(0); + return true; +} + +base::Time TimestampToTime(const Timestamp& timestamp) { + base::Time t = base::Time::UnixEpoch(); + t += base::TimeDelta::FromSeconds(timestamp.seconds()); + t += base::TimeDelta::FromMicroseconds( + timestamp.nanos() / base::Time::kNanosecondsPerMicrosecond); + return t; +} + +bool ParseClientConfig(const std::string& config_data, ClientConfig* config) { + scoped_ptr parsed_data(base::JSONReader::Read(config_data)); + if (!parsed_data) + return false; + + const base::DictionaryValue* parsed_dict; + if (!parsed_data->GetAsDictionary(&parsed_dict)) + return false; + + std::string session_key; + if (!parsed_dict->GetString("sessionKey", &session_key)) + return false; + + config->set_session_key(session_key); + + std::string expire_time; + if (!parsed_dict->GetString("expireTime", &expire_time)) + return false; + + if (!ISO8601ToTimestamp(expire_time, config->mutable_expire_time())) + return false; + + const base::DictionaryValue* proxy_config_dict; + if (!parsed_dict->GetDictionary("proxyConfig", &proxy_config_dict)) + return false; + + ProxyConfig* proxy_config = config->mutable_proxy_config(); + + const base::ListValue* http_proxy_servers; + if (!proxy_config_dict->GetList("httpProxyServers", &http_proxy_servers)) + return false; + + base::ListValue::const_iterator it = http_proxy_servers->begin(); + for (; it != http_proxy_servers->end(); ++it) { + const base::DictionaryValue* server_value; + if (!(*it)->GetAsDictionary(&server_value)) { + continue; + } + + std::string scheme; + std::string host; + int port; + if (!server_value->GetString("scheme", &scheme)) { + continue; + } + + if (!server_value->GetString("host", &host)) { + continue; + } + + if (!server_value->GetInteger("port", &port)) { + continue; + } + + ProxyServer_ProxyScheme proxy_scheme = GetProxyScheme(scheme); + if (proxy_scheme == ProxyServer_ProxyScheme_UNSPECIFIED) + continue; + + ProxyServer* proxy_server = proxy_config->add_http_proxy_servers(); + proxy_server->set_scheme(GetProxyScheme(scheme)); + proxy_server->set_host(host); + proxy_server->set_port(port); + } + + return true; +} + +} // namespace config_parser + +} // namespace data_reduction_proxy diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_client_config_parser.h b/components/data_reduction_proxy/core/common/data_reduction_proxy_client_config_parser.h new file mode 100644 index 000000000000..461d94514b6e --- /dev/null +++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_client_config_parser.h @@ -0,0 +1,51 @@ +// 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_DATA_REDUCTION_PROXY_CORE_COMMON_DATA_REDUCTION_PROXY_CLIENT_CONFIG_RESPONSE_PARSER_H_ +#define COMPONENTS_DATA_REDUCTION_PROXY_CORE_COMMON_DATA_REDUCTION_PROXY_CLIENT_CONFIG_RESPONSE_PARSER_H_ + +#include + +#include "components/data_reduction_proxy/proto/client_config.pb.h" +#include "net/proxy/proxy_server.h" + +namespace base { +class Time; +} + +namespace data_reduction_proxy { + +namespace config_parser { + +// Returns a string representation (which is actually the string representation +// of ProxyServer_ProxyScheme) of a |net::ProxyServer::Scheme|. +std::string GetSchemeString(net::ProxyServer::Scheme scheme); + +// Returns the |net::ProxyServer::Scheme| for a ProxyServer_ProxyScheme. +net::ProxyServer::Scheme SchemeFromProxyScheme( + ProxyServer_ProxyScheme proxy_scheme); + +// Retrieves the ProxyServer_ProxyScheme for its string representation. +ProxyServer_ProxyScheme GetProxyScheme(const std::string& scheme); + +// Returns the ISO-8601 representation of |time|. +std::string TimeToISO8601(const base::Time& time); + +// Parses an ISO-8601 time string into a Timestamp proto. +bool ISO8601ToTimestamp(const std::string& time, Timestamp* timestamp); + +// Returns the |base::Time| representation of |timestamp|. +base::Time TimestampToTime(const Timestamp& timestamp); + +// Takes a JSON representation of a |ClientConfig| and populates |config|. +// Returns false if the JSON has an unexpected structure. +// TODO(jeremyim): This should be deprecated once gRPC support can be added +// (which would give the binary proto instead of JSON). +bool ParseClientConfig(const std::string& config_data, ClientConfig* config); + +} // namespace config_parser + +} // namespace data_reduction_proxy + +#endif // COMPONENTS_DATA_REDUCTION_PROXY_CORE_COMMON_DATA_REDUCTION_PROXY_CLIENT_CONFIG_RESPONSE_PARSER_H_ diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_client_config_parser_unittest.cc b/components/data_reduction_proxy/core/common/data_reduction_proxy_client_config_parser_unittest.cc new file mode 100644 index 000000000000..b7a9704eb245 --- /dev/null +++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_client_config_parser_unittest.cc @@ -0,0 +1,228 @@ +// 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 "components/data_reduction_proxy/core/common/data_reduction_proxy_client_config_parser.h" + +#include + +#include "base/time/time.h" +#include "components/data_reduction_proxy/proto/client_config.pb.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace data_reduction_proxy { + +namespace { + +const char kValidPreamble[] = + "{ \"sessionKey\": \"foobar\", " + "\"expireTime\": \"2100-12-31T23:59:59.999999Z\", " + "\"proxyConfig\": { \"httpProxyServers\": ["; +const char kValidPostamble[] = "] } }"; + +} // namespace + +TEST(ClientConfigParserTest, TimeToISO8601) { + const struct { + base::Time time; + std::string expected; + } tests[] = { + { + base::Time(), + "1601-01-01T00:00:00.000Z", + }, + { + base::Time::UnixEpoch(), + "1970-01-01T00:00:00.000Z", + }, + }; + + for (const auto& test : tests) { + EXPECT_EQ(test.expected, config_parser::TimeToISO8601(test.time)); + } +} + +TEST(ClientConfigParserTest, ISO8601ToTimestamp) { + const struct { + std::string time_string; + int64 epoch_seconds; + } tests[] = { + { + "1970-01-01T00:00:00.000Z", + 0, + }, + { + "1970-01-01T00:00:00.999Z", + 0, + }, + { + "1950-01-01T00:00:00.000Z", + -631152000, + }, + { + "1950-01-01T00:00:00.500Z", + -631151999, // Rounding of negative fractional values causes this. + }, + }; + + for (const auto& test : tests) { + Timestamp timestamp; + EXPECT_TRUE( + config_parser::ISO8601ToTimestamp(test.time_string, ×tamp)); + EXPECT_EQ(test.epoch_seconds, timestamp.seconds()); + EXPECT_EQ(0, timestamp.nanos()); + } +} + +TEST(ClientConfigParserTest, ISO8601ToTimestampTestFailures) { + const std::string inputs[] = { + "", + "Not a time", + "1234", + "2099-43-12", + "2099-11-52", + }; + + for (const auto& input : inputs) { + Timestamp timestamp; + EXPECT_FALSE(config_parser::ISO8601ToTimestamp(input, ×tamp)); + } +} + +TEST(ClientConfigParserTest, TimestampToTime) { + base::Time::Exploded future = { + 2100, + 12, + 5, + 31, + 23, + 59, + 59, + 0, + }; + const struct { + int64 timestamp_seconds; + int32 timestamp_nanos; + base::Time expected_time; + } tests[] = { + { + 0, + 0, + base::Time::UnixEpoch(), + }, + { + 24 * 60 * 60 - 1, + base::Time::kNanosecondsPerSecond - 1, + base::Time::UnixEpoch() + base::TimeDelta::FromDays(1) - + base::TimeDelta::FromMicroseconds(1), + }, + { + // 2100-12-31T23:59:59.000Z + 4133980799, + 0, + base::Time::FromUTCExploded(future), + }, + }; + + for (const auto& test : tests) { + Timestamp ts; + ts.set_seconds(test.timestamp_seconds); + ts.set_nanos(test.timestamp_nanos); + EXPECT_EQ(test.expected_time, config_parser::TimestampToTime(ts)); + } +} + +TEST(ClientConfigParserTest, SingleServer) { + const std::string inputs[] = { + "{ \"scheme\": \"HTTP\", \"host\": \"foo.com\", \"port\": 80 }", + "{ \"scheme\": \"HTTPS\", \"host\": \"foo.com\", \"port\": 443 }", + "{ \"scheme\": \"QUIC\", \"host\": \"foo.com\", \"port\": 443 }", + }; + + for (const auto& scheme_fragment : inputs) { + std::string input = kValidPreamble + scheme_fragment + kValidPostamble; + ClientConfig config; + EXPECT_TRUE(config_parser::ParseClientConfig(input, &config)); + EXPECT_EQ(1, config.proxy_config().http_proxy_servers_size()); + } +} + +TEST(ClientConfigParserTest, MultipleServers) { + std::string input = + "{ \"scheme\": \"HTTP\", ""\"host\": \"foo.com\", \"port\": 80 }, " + "{ \"scheme\": \"HTTPS\", \"host\": \"foo.com\", \"port\": 443 }, " + "{ \"scheme\": \"QUIC\", \"host\": \"foo.com\", \"port\": 443 }"; + ClientConfig config; + EXPECT_TRUE(config_parser::ParseClientConfig( + kValidPreamble + input + kValidPostamble, &config)); + EXPECT_EQ(3, config.proxy_config().http_proxy_servers_size()); +} + +TEST(ClientConfigParserTest, FailureCases) { + const std::string inputs[] = { + "", + "invalid json", + // No sessionKey + "{ \"expireTime\": \"2100-12-31T23:59:59.999999Z\"," + "\"proxyConfig\": { \"httpProxyServers\": [ ] } }", + // No expireTime + "{ \"sessionKey\": \"foobar\"," + "\"proxyConfig\": { \"httpProxyServers\": [ ] } }", + // No proxyConfig + "{ \"sessionKey\": \"foobar\", " + "\"expireTime\": \"2100-12-31T23:59:59.999999Z\" }", + // No proxyConfig.httpProxyServers + "{ \"sessionKey\": \"foobar\", " + "\"expireTime\": \"2100-12-31T23:59:59.999999Z\", \"proxyConfig\": { } }", + // Invalid sessionKey + "{ \"sessionKey\": 12345, " + "\"expireTime\": \"2100-12-31T23:59:59.999999Z\"," + " \"proxyConfig\": { \"httpProxyServers\": [ ] } }", + // Invalid sessionKey + "{ \"sessionKey\": { }, " + "\"expireTime\": \"2100-12-31T23:59:59.999999Z\"," + " \"proxyConfig\": { \"httpProxyServers\": [ ] } }", + // Invalid sessionKey + "{ \"sessionKey\": [ ], " + "\"expireTime\": \"2100-12-31T23:59:59.999999Z\"," + " \"proxyConfig\": { \"httpProxyServers\": [ ] } }", + // Invalid expireTime + "{ \"sessionKey\": \"foobar\", " + "\"expireTime\": \"abcd\"," + " \"proxyConfig\": { \"httpProxyServers\": [ ] } }", + // Invalid expireTime + "{ \"sessionKey\": \"foobar\", " + "\"expireTime\": [ ]," + " \"proxyConfig\": { \"httpProxyServers\": [ ] } }", + // Invalid expireTime + "{ \"sessionKey\": \"foobar\", " + "\"expireTime\": { }," + " \"proxyConfig\": { \"httpProxyServers\": [ ] } }", + }; + + for (const auto& input : inputs) { + ClientConfig config; + EXPECT_FALSE(config_parser::ParseClientConfig(input, &config)); + } +} + +TEST(ClientConfigParserTest, EmptyServerLists) { + const std::string inputs[] = { + "", + "{ }", + "{ \"scheme\": \"foo\", \"host\": \"foo.com\", \"port\": 80 }", + "{ \"scheme\": \"HTTP\", \"port\": 80 }", + "{ \"scheme\": \"HTTP\", \"host\": \"foo.com\" }", + "{ \"scheme\": \"HTTP\", \"host\": \"foo.com\", \"port\": \"bar\" }", + "{ \"scheme\": \"HTTP\", \"host\": 12345, \"port\": 80 }", + }; + + for (const auto& scheme_fragment : inputs) { + std::string input = kValidPreamble + scheme_fragment + kValidPostamble; + ClientConfig config; + EXPECT_TRUE(config_parser::ParseClientConfig(input, &config)); + EXPECT_EQ(0, config.proxy_config().http_proxy_servers_size()); + } +} + +} // namespace data_reduction_proxy diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_params.cc b/components/data_reduction_proxy/core/common/data_reduction_proxy_params.cc index 770e3316cf1d..3769f3a36830 100644 --- a/components/data_reduction_proxy/core/common/data_reduction_proxy_params.cc +++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_params.cc @@ -7,8 +7,11 @@ #include #include "base/command_line.h" +#include "base/memory/scoped_ptr.h" #include "base/metrics/field_trial.h" #include "base/strings/string_piece.h" +#include "base/values.h" +#include "components/data_reduction_proxy/core/common/data_reduction_proxy_client_config_parser.h" #include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h" #include "net/base/host_port_pair.h" #include "net/proxy/proxy_server.h" @@ -36,6 +39,11 @@ const char kDefaultWarmupUrl[] = "http://www.gstatic.com/generate_204"; const char kAndroidOneIdentifier[] = "sprout"; const char kQuicFieldTrial[] = "DataReductionProxyUseQuic"; + +const char kConfigScheme[] = "scheme"; +const char kConfigHost[] = "host"; +const char kConfigPort[] = "port"; + } // namespace namespace data_reduction_proxy { @@ -386,6 +394,41 @@ bool DataReductionProxyParams::IsDataReductionProxy( return false; } +void DataReductionProxyParams::PopulateConfigResponse( + base::DictionaryValue* response) const { + scoped_ptr proxy_config(new base::DictionaryValue()); + if (!holdback_) { + base::DictionaryValue* proxy_config_dict = nullptr; + if (!proxy_config->GetAsDictionary(&proxy_config_dict)) + return; + + scoped_ptr proxy_servers(new base::ListValue()); + base::ListValue* proxy_servers_list = nullptr; + if (!proxy_servers->GetAsList(&proxy_servers_list)) + return; + + proxy_servers->GetAsList(&proxy_servers_list); + scoped_ptr server(new base::DictionaryValue()); + + server->SetString(kConfigScheme, + config_parser::GetSchemeString(origin_.scheme())); + server->SetString(kConfigHost, origin_.host_port_pair().host()); + server->SetInteger(kConfigPort, origin_.host_port_pair().port()); + proxy_servers_list->Append(server.release()); + server.reset(new base::DictionaryValue()); + + server->SetString(kConfigScheme, config_parser::GetSchemeString( + fallback_origin_.scheme())); + server->SetString(kConfigHost, fallback_origin_.host_port_pair().host()); + server->SetInteger(kConfigPort, fallback_origin_.host_port_pair().port()); + proxy_servers_list->Append(server.release()); + + proxy_config_dict->Set("httpProxyServers", proxy_servers.Pass()); + } + + response->Set("proxyConfig", proxy_config.Pass()); +} + // Returns the data reduction proxy primary origin. const net::ProxyServer& DataReductionProxyParams::origin() const { return origin_; diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_params.h b/components/data_reduction_proxy/core/common/data_reduction_proxy_params.h index 256c6c4ef8bd..30c51ff7bc5f 100644 --- a/components/data_reduction_proxy/core/common/data_reduction_proxy_params.h +++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_params.h @@ -13,6 +13,7 @@ #include "url/gurl.h" namespace base { +class DictionaryValue; class TimeDelta; } @@ -131,6 +132,10 @@ class DataReductionProxyParams : public DataReductionProxyConfigValues { // If true, uses QUIC instead of SPDY to connect to proxies that use TLS. void EnableQuic(bool enable); + // Populates |response| with the Data Reduction Proxy server configuration. + // Virtual for mocking. + virtual void PopulateConfigResponse(base::DictionaryValue* response) const; + // Overrides of |DataReductionProxyConfigValues| bool UsingHTTPTunnel(const net::HostPortPair& proxy_server) const override; diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_params_unittest.cc b/components/data_reduction_proxy/core/common/data_reduction_proxy_params_unittest.cc index e2e8120cbff5..aa5074125c12 100644 --- a/components/data_reduction_proxy/core/common/data_reduction_proxy_params_unittest.cc +++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_params_unittest.cc @@ -7,6 +7,7 @@ #include #include "base/command_line.h" +#include "base/values.h" #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params_test_utils.h" #include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h" #include "net/proxy/proxy_server.h" @@ -766,4 +767,30 @@ TEST_F(DataReductionProxyParamsTest, AndroidOnePromoFieldTrial) { "google/hammerhead/hammerhead:5.0/LRX210/1570415:user/release-keys")); } +TEST_F(DataReductionProxyParamsTest, PopulateConfigResponse) { + DataReductionProxyParams params( + DataReductionProxyParams::kAllowed | + DataReductionProxyParams::kAlternativeAllowed); + scoped_ptr values(new base::DictionaryValue()); + params.PopulateConfigResponse(values.get()); + base::DictionaryValue* proxy_config; + EXPECT_TRUE(values->GetDictionary("proxyConfig", &proxy_config)); + base::ListValue* proxy_servers; + EXPECT_TRUE(proxy_config->GetList("httpProxyServers", &proxy_servers)); + EXPECT_TRUE(proxy_servers->GetSize() == 2); + base::DictionaryValue* server; + EXPECT_TRUE(proxy_servers->GetDictionary(0, &server)); + std::string host; + int port; + EXPECT_TRUE(server->GetString("host", &host)); + EXPECT_TRUE(server->GetInteger("port", &port)); + EXPECT_EQ(params.origin().host_port_pair().host(), host); + EXPECT_EQ(params.origin().host_port_pair().port(), port); + EXPECT_TRUE(proxy_servers->GetDictionary(1, &server)); + EXPECT_TRUE(server->GetString("host", &host)); + EXPECT_TRUE(server->GetInteger("port", &port)); + EXPECT_EQ(params.fallback_origin().host_port_pair().host(), host); + EXPECT_EQ(params.fallback_origin().host_port_pair().port(), port); +} + } // namespace data_reduction_proxy diff --git a/components/data_reduction_proxy/proto/BUILD.gn b/components/data_reduction_proxy/proto/BUILD.gn new file mode 100644 index 000000000000..889c1067838d --- /dev/null +++ b/components/data_reduction_proxy/proto/BUILD.gn @@ -0,0 +1,11 @@ +# 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. + +import("//third_party/protobuf/proto_library.gni") + +proto_library("data_reduction_proxy_proto") { + sources = [ + "client_config.proto", + ] +} diff --git a/components/data_reduction_proxy/proto/client_config.proto b/components/data_reduction_proxy/proto/client_config.proto new file mode 100644 index 000000000000..f2c796c12be0 --- /dev/null +++ b/components/data_reduction_proxy/proto/client_config.proto @@ -0,0 +1,70 @@ +// 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. + +syntax = "proto2"; + +option optimize_for = LITE_RUNTIME; + +package data_reduction_proxy; + +// The client configuration information for using the Data Saver service. +message ClientConfig { + // An opaque per-session key assigned by the server which permits use of the + // Data Saver HTTP proxy servers. + optional string session_key = 1; + // The time at which the secure_session_key is no longer valid. This is + // enforced by the Data Saver service, and is provided to permit the client + // to request a new session key prior to expiration. + optional Timestamp expire_time = 2; + // The DataSaver proxy configuration that is valid for the session. + optional ProxyConfig proxy_config = 3; +} + +// A Timestamp represents a point in time independent of any time zone +// or calendar, represented as seconds and fractions of seconds at +// nanosecond resolution in UTC Epoch time. +message Timestamp { + // Represents seconds of UTC time since Unix epoch + // 1970-01-01T00:00:00Z. Must be from from 0001-01-01T00:00:00Z to + // 9999-12-31T23:59:59Z inclusive. + optional int64 seconds = 1; + + // Non-negative fractions of a second at nanosecond resolution. Negative + // second values with fractions must still have non-negative nanos values + // that count forward in time. Must be from 0 to 999,999,999 + // inclusive. + optional int32 nanos = 2; +} + +// Data Saver proxy configuration. +message ProxyConfig { + // Provides proxy server information for HTTP URIs. + repeated ProxyServer http_proxy_servers = 1; +} + +// Configuration information for a specific proxy server. +message ProxyServer { + // The scheme of the proxy server. + enum ProxyScheme { + // The proxy scheme is unspecified. + UNSPECIFIED = 0; + // HTTP + HTTP = 1; + // HTTPS + HTTPS = 2; + // HTTPS over QUIC + QUIC = 3; + } + + // The scheme for the proxy server. + optional ProxyScheme scheme = 1; + // The host name for the proxy server. + optional string host = 2; + // The port number for the proxy server. + optional int32 port = 3; +} + +// Request object to create a client configuration object. +message CreateClientConfigRequest { +} -- 2.11.4.GIT