Commit 2618807e authored by pavely@chromium.org's avatar pavely@chromium.org

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
parent 5fb0de3c
...@@ -2,6 +2,15 @@ ...@@ -2,6 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // 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 "google_apis/gaia/google_service_auth_error.h"
#include "net/http/http_status_code.h" #include "net/http/http_status_code.h"
#include "net/url_request/url_fetcher.h" #include "net/url_request/url_fetcher.h"
...@@ -13,6 +22,10 @@ namespace syncer { ...@@ -13,6 +22,10 @@ namespace syncer {
namespace { namespace {
const char kCacheInvalidationEndpointUrl[] =
"https://clients4.google.com/invalidation/android/request/";
const char kCacheInvalidationPackageName[] = "com.google.chrome.invalidations";
// Register backoff policy. // Register backoff policy.
const net::BackoffEntry::Policy kRegisterBackoffPolicy = { const net::BackoffEntry::Policy kRegisterBackoffPolicy = {
// Number of initial errors (in sequence) to ignore before applying // Number of initial errors (in sequence) to ignore before applying
...@@ -143,9 +156,8 @@ void GCMNetworkChannel::OnGetTokenComplete( ...@@ -143,9 +156,8 @@ void GCMNetworkChannel::OnGetTokenComplete(
access_token_ = token; access_token_ = token;
DVLOG(2) << "Got access token, sending message"; DVLOG(2) << "Got access token, sending message";
fetcher_.reset(net::URLFetcher::Create(
fetcher_.reset(net::URLFetcher::Create(BuildUrl(), net::URLFetcher::POST, BuildUrl(registration_id_), net::URLFetcher::POST, this));
this));
fetcher_->SetRequestContext(request_context_getter_); fetcher_->SetRequestContext(request_context_getter_);
const std::string auth_header("Authorization: Bearer " + access_token_); const std::string auth_header("Authorization: Bearer " + access_token_);
fetcher_->AddExtraRequestHeader(auth_header); fetcher_->AddExtraRequestHeader(auth_header);
...@@ -175,13 +187,62 @@ void GCMNetworkChannel::OnURLFetchComplete(const net::URLFetcher* source) { ...@@ -175,13 +187,62 @@ void GCMNetworkChannel::OnURLFetchComplete(const net::URLFetcher* source) {
DVLOG(2) << "URLFetcher success"; DVLOG(2) << "URLFetcher success";
} }
GURL GCMNetworkChannel::BuildUrl() { GURL GCMNetworkChannel::BuildUrl(const std::string& registration_id) {
DCHECK(!registration_id_.empty()); DCHECK(!registration_id.empty());
// Prepare NetworkEndpointId using registration_id
// Serialize NetworkEndpointId into byte array and base64 encode. #if !defined(ANDROID)
// Format url using encoded NetworkEndpointId. ipc::invalidation::EndpointId endpoint_id;
// TODO(pavely): implement all of the above. endpoint_id.set_c2dm_registration_id(registration_id);
return GURL("http://invalid.url.com"); 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 } // namespace syncer
...@@ -49,14 +49,22 @@ class SYNC_EXPORT_PRIVATE GCMNetworkChannel ...@@ -49,14 +49,22 @@ class SYNC_EXPORT_PRIVATE GCMNetworkChannel
void ResetRegisterBackoffEntryForTest( void ResetRegisterBackoffEntryForTest(
const net::BackoffEntry::Policy* policy); const net::BackoffEntry::Policy* policy);
virtual GURL BuildUrl(const std::string& registration_id);
private: private:
friend class GCMNetworkChannelTest;
void Register(); void Register();
void OnRegisterComplete(const std::string& registration_id, void OnRegisterComplete(const std::string& registration_id,
gcm::GCMClient::Result result); gcm::GCMClient::Result result);
void RequestAccessToken(); void RequestAccessToken();
void OnGetTokenComplete(const GoogleServiceAuthError& error, void OnGetTokenComplete(const GoogleServiceAuthError& error,
const std::string& token); 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_refptr<net::URLRequestContextGetter> request_context_getter_;
scoped_ptr<GCMNetworkChannelDelegate> delegate_; scoped_ptr<GCMNetworkChannelDelegate> delegate_;
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
// found in the LICENSE file. // found in the LICENSE file.
#include "base/run_loop.h" #include "base/run_loop.h"
#include "base/strings/string_util.h"
#include "google_apis/gaia/google_service_auth_error.h" #include "google_apis/gaia/google_service_auth_error.h"
#include "net/url_request/test_url_fetcher_factory.h" #include "net/url_request/test_url_fetcher_factory.h"
#include "net/url_request/url_request_test_util.h" #include "net/url_request/url_request_test_util.h"
...@@ -10,7 +11,6 @@ ...@@ -10,7 +11,6 @@
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
namespace syncer { namespace syncer {
namespace {
class TestGCMNetworkChannelDelegate : public GCMNetworkChannelDelegate { class TestGCMNetworkChannelDelegate : public GCMNetworkChannelDelegate {
public: public:
...@@ -73,6 +73,13 @@ class TestGCMNetworkChannel : public GCMNetworkChannel { ...@@ -73,6 +73,13 @@ class TestGCMNetworkChannel : public GCMNetworkChannel {
: GCMNetworkChannel(request_context_getter, delegate.Pass()) { : GCMNetworkChannel(request_context_getter, delegate.Pass()) {
ResetRegisterBackoffEntryForTest(&kTestBackoffPolicy); 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 class GCMNetworkChannelTest
...@@ -110,6 +117,21 @@ class GCMNetworkChannelTest ...@@ -110,6 +117,21 @@ class GCMNetworkChannelTest
gcm_network_channel_->RemoveObserver(this); 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( virtual void OnNetworkChannelStateChanged(
InvalidatorState invalidator_state) OVERRIDE { InvalidatorState invalidator_state) OVERRIDE {
} }
...@@ -159,8 +181,9 @@ class GCMNetworkChannelTest ...@@ -159,8 +181,9 @@ class GCMNetworkChannelTest
}; };
TEST_F(GCMNetworkChannelTest, HappyCase) { TEST_F(GCMNetworkChannelTest, HappyCase) {
GURL url("http://invalid.url.com"); url_fetcher_factory()->SetFakeResponse(GURL("http://test.url.com"),
url_fetcher_factory()->SetFakeResponse(url, std::string(), net::HTTP_OK, std::string(),
net::HTTP_OK,
net::URLRequestStatus::SUCCESS); net::URLRequestStatus::SUCCESS);
// After construction GCMNetworkChannel should have called Register. // After construction GCMNetworkChannel should have called Register.
...@@ -209,9 +232,10 @@ TEST_F(GCMNetworkChannelTest, FailedRegister) { ...@@ -209,9 +232,10 @@ TEST_F(GCMNetworkChannelTest, FailedRegister) {
} }
TEST_F(GCMNetworkChannelTest, RegisterFinishesAfterSendMessage) { TEST_F(GCMNetworkChannelTest, RegisterFinishesAfterSendMessage) {
GURL url("http://invalid.url.com"); url_fetcher_factory()->SetFakeResponse(GURL("http://test.url.com"),
url_fetcher_factory()->SetFakeResponse(url, "", net::HTTP_OK, "",
net::URLRequestStatus::SUCCESS); net::HTTP_OK,
net::URLRequestStatus::SUCCESS);
// After construction GCMNetworkChannel should have called Register. // After construction GCMNetworkChannel should have called Register.
EXPECT_FALSE(delegate()->register_callback.is_null()); EXPECT_FALSE(delegate()->register_callback.is_null());
...@@ -254,9 +278,10 @@ TEST_F(GCMNetworkChannelTest, RequestTokenFailure) { ...@@ -254,9 +278,10 @@ TEST_F(GCMNetworkChannelTest, RequestTokenFailure) {
TEST_F(GCMNetworkChannelTest, AuthErrorFromServer) { TEST_F(GCMNetworkChannelTest, AuthErrorFromServer) {
// Setup fake response to return AUTH_ERROR. // Setup fake response to return AUTH_ERROR.
GURL url("http://invalid.url.com"); url_fetcher_factory()->SetFakeResponse(GURL("http://test.url.com"),
url_fetcher_factory()->SetFakeResponse(url, "", net::HTTP_UNAUTHORIZED, "",
net::URLRequestStatus::SUCCESS); net::HTTP_UNAUTHORIZED,
net::URLRequestStatus::SUCCESS);
// After construction GCMNetworkChannel should have called Register. // After construction GCMNetworkChannel should have called Register.
EXPECT_FALSE(delegate()->register_callback.is_null()); EXPECT_FALSE(delegate()->register_callback.is_null());
...@@ -293,5 +318,44 @@ TEST_F(GCMNetworkChannelTest, RequestTokenNeverCompletes) { ...@@ -293,5 +318,44 @@ TEST_F(GCMNetworkChannelTest, RequestTokenNeverCompletes) {
EXPECT_FALSE(delegate()->request_token_callback.is_null()); 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 } // namespace syncer
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment