summaryrefslogtreecommitdiffstats
path: root/sync
diff options
context:
space:
mode:
authorpavely@chromium.org <pavely@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-03-09 04:44:03 +0000
committerpavely@chromium.org <pavely@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-03-09 04:44:03 +0000
commit2618807e6180759ecf94584f475d3f7b35bc1068 (patch)
tree9dee047da99b74486e80b3e164f22bae369e55e1 /sync
parent5fb0de3cef6ef1f2d3b1bf58c83106cdfc404852 (diff)
downloadchromium_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.cc81
-rw-r--r--sync/notifier/gcm_network_channel.h10
-rw-r--r--sync/notifier/gcm_network_channel_unittest.cc84
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