diff options
author | pavely@chromium.org <pavely@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-03-09 04:44:03 +0000 |
---|---|---|
committer | pavely@chromium.org <pavely@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-03-09 04:44:03 +0000 |
commit | 2618807e6180759ecf94584f475d3f7b35bc1068 (patch) | |
tree | 9dee047da99b74486e80b3e164f22bae369e55e1 /sync | |
parent | 5fb0de3cef6ef1f2d3b1bf58c83106cdfc404852 (diff) | |
download | chromium_src-2618807e6180759ecf94584f475d3f7b35bc1068.zip chromium_src-2618807e6180759ecf94584f475d3f7b35bc1068.tar.gz chromium_src-2618807e6180759ecf94584f475d3f7b35bc1068.tar.bz2 |
Build correct URL for cacheinvalidation endpoint.
Build URL for sending client-to-server cacheinvalidation messages.
BUG=325020
R=rlarocque@chromium.org
Review URL: https://codereview.chromium.org/182333003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@255825 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'sync')
-rw-r--r-- | sync/notifier/gcm_network_channel.cc | 81 | ||||
-rw-r--r-- | sync/notifier/gcm_network_channel.h | 10 | ||||
-rw-r--r-- | sync/notifier/gcm_network_channel_unittest.cc | 84 |
3 files changed, 154 insertions, 21 deletions
diff --git a/sync/notifier/gcm_network_channel.cc b/sync/notifier/gcm_network_channel.cc index b4ea4bc..e0efda5 100644 --- a/sync/notifier/gcm_network_channel.cc +++ b/sync/notifier/gcm_network_channel.cc @@ -2,6 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "base/base64.h" +#include "base/strings/string_util.h" +#if !defined(ANDROID) +// channel_common.proto defines ANDROID constant that conflicts with Android +// build. At the same time TiclInvalidationService is not used on Android so it +// is safe to exclude these protos from Android build. +#include "google/cacheinvalidation/android_channel.pb.h" +#include "google/cacheinvalidation/channel_common.pb.h" +#endif #include "google_apis/gaia/google_service_auth_error.h" #include "net/http/http_status_code.h" #include "net/url_request/url_fetcher.h" @@ -13,6 +22,10 @@ namespace syncer { namespace { +const char kCacheInvalidationEndpointUrl[] = + "https://clients4.google.com/invalidation/android/request/"; +const char kCacheInvalidationPackageName[] = "com.google.chrome.invalidations"; + // Register backoff policy. const net::BackoffEntry::Policy kRegisterBackoffPolicy = { // Number of initial errors (in sequence) to ignore before applying @@ -143,9 +156,8 @@ void GCMNetworkChannel::OnGetTokenComplete( access_token_ = token; DVLOG(2) << "Got access token, sending message"; - - fetcher_.reset(net::URLFetcher::Create(BuildUrl(), net::URLFetcher::POST, - this)); + fetcher_.reset(net::URLFetcher::Create( + BuildUrl(registration_id_), net::URLFetcher::POST, this)); fetcher_->SetRequestContext(request_context_getter_); const std::string auth_header("Authorization: Bearer " + access_token_); fetcher_->AddExtraRequestHeader(auth_header); @@ -175,13 +187,62 @@ void GCMNetworkChannel::OnURLFetchComplete(const net::URLFetcher* source) { DVLOG(2) << "URLFetcher success"; } -GURL GCMNetworkChannel::BuildUrl() { - DCHECK(!registration_id_.empty()); - // Prepare NetworkEndpointId using registration_id - // Serialize NetworkEndpointId into byte array and base64 encode. - // Format url using encoded NetworkEndpointId. - // TODO(pavely): implement all of the above. - return GURL("http://invalid.url.com"); +GURL GCMNetworkChannel::BuildUrl(const std::string& registration_id) { + DCHECK(!registration_id.empty()); + +#if !defined(ANDROID) + ipc::invalidation::EndpointId endpoint_id; + endpoint_id.set_c2dm_registration_id(registration_id); + endpoint_id.set_client_key(std::string()); + endpoint_id.set_package_name(kCacheInvalidationPackageName); + endpoint_id.mutable_channel_version()->set_major_version( + ipc::invalidation::INITIAL); + std::string endpoint_id_buffer; + endpoint_id.SerializeToString(&endpoint_id_buffer); + + ipc::invalidation::NetworkEndpointId network_endpoint_id; + network_endpoint_id.set_network_address( + ipc::invalidation::NetworkEndpointId_NetworkAddress_ANDROID); + network_endpoint_id.set_client_address(endpoint_id_buffer); + std::string network_endpoint_id_buffer; + network_endpoint_id.SerializeToString(&network_endpoint_id_buffer); + + std::string base64URLPiece; + Base64EncodeURLSafe(network_endpoint_id_buffer, &base64URLPiece); + + std::string url(kCacheInvalidationEndpointUrl); + url += base64URLPiece; + return GURL(url); +#else + // This code shouldn't be invoked on Android. + NOTREACHED(); + return GURL(); +#endif +} + +void GCMNetworkChannel::Base64EncodeURLSafe(const std::string& input, + std::string* output) { + base::Base64Encode(input, output); + // Covert to url safe alphabet. + base::ReplaceChars(*output, "+", "-", output); + base::ReplaceChars(*output, "/", "_", output); + // Trim padding. + size_t padding_size = 0; + for (size_t i = output->size(); i > 0 && (*output)[i - 1] == '='; --i) + ++padding_size; + output->resize(output->size() - padding_size); +} + +bool GCMNetworkChannel::Base64DecodeURLSafe(const std::string& input, + std::string* output) { + // Add padding. + size_t padded_size = (input.size() + 3) - (input.size() + 3) % 4; + std::string padded_input(input); + padded_input.resize(padded_size, '='); + // Convert to standard base64 alphabet. + base::ReplaceChars(padded_input, "-", "+", &padded_input); + base::ReplaceChars(padded_input, "_", "/", &padded_input); + return base::Base64Decode(padded_input, output); } } // namespace syncer diff --git a/sync/notifier/gcm_network_channel.h b/sync/notifier/gcm_network_channel.h index 0dd64ee..4b50dc9 100644 --- a/sync/notifier/gcm_network_channel.h +++ b/sync/notifier/gcm_network_channel.h @@ -49,14 +49,22 @@ class SYNC_EXPORT_PRIVATE GCMNetworkChannel void ResetRegisterBackoffEntryForTest( const net::BackoffEntry::Policy* policy); + virtual GURL BuildUrl(const std::string& registration_id); + private: + friend class GCMNetworkChannelTest; void Register(); void OnRegisterComplete(const std::string& registration_id, gcm::GCMClient::Result result); void RequestAccessToken(); void OnGetTokenComplete(const GoogleServiceAuthError& error, const std::string& token); - GURL BuildUrl(); + // Base64 encoding/decoding with URL safe alphabet. + // http://tools.ietf.org/html/rfc4648#page-7 + static void Base64EncodeURLSafe(const std::string& input, + std::string* output); + static bool Base64DecodeURLSafe(const std::string& input, + std::string* output); scoped_refptr<net::URLRequestContextGetter> request_context_getter_; scoped_ptr<GCMNetworkChannelDelegate> delegate_; diff --git a/sync/notifier/gcm_network_channel_unittest.cc b/sync/notifier/gcm_network_channel_unittest.cc index a8ef9ac..00fc5dc 100644 --- a/sync/notifier/gcm_network_channel_unittest.cc +++ b/sync/notifier/gcm_network_channel_unittest.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "base/run_loop.h" +#include "base/strings/string_util.h" #include "google_apis/gaia/google_service_auth_error.h" #include "net/url_request/test_url_fetcher_factory.h" #include "net/url_request/url_request_test_util.h" @@ -10,7 +11,6 @@ #include "testing/gtest/include/gtest/gtest.h" namespace syncer { -namespace { class TestGCMNetworkChannelDelegate : public GCMNetworkChannelDelegate { public: @@ -73,6 +73,13 @@ class TestGCMNetworkChannel : public GCMNetworkChannel { : GCMNetworkChannel(request_context_getter, delegate.Pass()) { ResetRegisterBackoffEntryForTest(&kTestBackoffPolicy); } + + protected: + // On Android GCMNetworkChannel::BuildUrl hits NOTREACHED(). I still want + // tests to run. + virtual GURL BuildUrl(const std::string& registration_id) OVERRIDE { + return GURL("http://test.url.com"); + } }; class GCMNetworkChannelTest @@ -110,6 +117,21 @@ class GCMNetworkChannelTest gcm_network_channel_->RemoveObserver(this); } + // Helper functions to call private methods from test + GURL BuildUrl(const std::string& registration_id) { + return gcm_network_channel_->GCMNetworkChannel::BuildUrl(registration_id); + } + + static void Base64EncodeURLSafe(const std::string& input, + std::string* output) { + GCMNetworkChannel::Base64EncodeURLSafe(input, output); + } + + static bool Base64DecodeURLSafe(const std::string& input, + std::string* output) { + return GCMNetworkChannel::Base64DecodeURLSafe(input, output); + } + virtual void OnNetworkChannelStateChanged( InvalidatorState invalidator_state) OVERRIDE { } @@ -159,8 +181,9 @@ class GCMNetworkChannelTest }; TEST_F(GCMNetworkChannelTest, HappyCase) { - GURL url("http://invalid.url.com"); - url_fetcher_factory()->SetFakeResponse(url, std::string(), net::HTTP_OK, + url_fetcher_factory()->SetFakeResponse(GURL("http://test.url.com"), + std::string(), + net::HTTP_OK, net::URLRequestStatus::SUCCESS); // After construction GCMNetworkChannel should have called Register. @@ -209,9 +232,10 @@ TEST_F(GCMNetworkChannelTest, FailedRegister) { } TEST_F(GCMNetworkChannelTest, RegisterFinishesAfterSendMessage) { - GURL url("http://invalid.url.com"); - url_fetcher_factory()->SetFakeResponse(url, "", net::HTTP_OK, - net::URLRequestStatus::SUCCESS); + url_fetcher_factory()->SetFakeResponse(GURL("http://test.url.com"), + "", + net::HTTP_OK, + net::URLRequestStatus::SUCCESS); // After construction GCMNetworkChannel should have called Register. EXPECT_FALSE(delegate()->register_callback.is_null()); @@ -254,9 +278,10 @@ TEST_F(GCMNetworkChannelTest, RequestTokenFailure) { TEST_F(GCMNetworkChannelTest, AuthErrorFromServer) { // Setup fake response to return AUTH_ERROR. - GURL url("http://invalid.url.com"); - url_fetcher_factory()->SetFakeResponse(url, "", net::HTTP_UNAUTHORIZED, - net::URLRequestStatus::SUCCESS); + url_fetcher_factory()->SetFakeResponse(GURL("http://test.url.com"), + "", + net::HTTP_UNAUTHORIZED, + net::URLRequestStatus::SUCCESS); // After construction GCMNetworkChannel should have called Register. EXPECT_FALSE(delegate()->register_callback.is_null()); @@ -293,5 +318,44 @@ TEST_F(GCMNetworkChannelTest, RequestTokenNeverCompletes) { EXPECT_FALSE(delegate()->request_token_callback.is_null()); } -} // namespace +#if !defined(ANDROID) +TEST_F(GCMNetworkChannelTest, BuildUrl) { + GURL url = BuildUrl("registration.id"); + EXPECT_TRUE(url.SchemeIsHTTPOrHTTPS()); + EXPECT_FALSE(url.host().empty()); + EXPECT_FALSE(url.path().empty()); + std::vector<std::string> parts; + Tokenize(url.path(), "/", &parts); + std::string buffer; + EXPECT_TRUE(Base64DecodeURLSafe(parts[parts.size() - 1], &buffer)); +} +#endif + +TEST_F(GCMNetworkChannelTest, Base64EncodeDecode) { + std::string input; + std::string plain; + std::string base64; + // Empty string. + Base64EncodeURLSafe(input, &base64); + EXPECT_TRUE(base64.empty()); + EXPECT_TRUE(Base64DecodeURLSafe(base64, &plain)); + EXPECT_EQ(input, plain); + // String length: 1..7. + for (int length = 1; length < 8; length++) { + input = "abra.cadabra"; + input.resize(length); + Base64EncodeURLSafe(input, &base64); + // Ensure no padding at the end. + EXPECT_NE(base64[base64.size() - 1], '='); + EXPECT_TRUE(Base64DecodeURLSafe(base64, &plain)); + EXPECT_EQ(input, plain); + } + // Presence of '-', '_'. + input = "\xfb\xff"; + Base64EncodeURLSafe(input, &base64); + EXPECT_EQ("-_8", base64); + EXPECT_TRUE(Base64DecodeURLSafe(base64, &plain)); + EXPECT_EQ(input, plain); +} + } // namespace syncer |