Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / google_apis / gcm / tools / mcs_probe.cc
blob1063db9b587063d7c0b6721e9f9b4a64892bdde4
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 //
5 // A standalone tool for testing MCS connections and the MCS client on their
6 // own.
8 #include <cstddef>
9 #include <cstdio>
10 #include <string>
11 #include <vector>
13 #include "base/at_exit.h"
14 #include "base/command_line.h"
15 #include "base/compiler_specific.h"
16 #include "base/files/scoped_file.h"
17 #include "base/logging.h"
18 #include "base/memory/ref_counted.h"
19 #include "base/memory/scoped_ptr.h"
20 #include "base/message_loop/message_loop.h"
21 #include "base/run_loop.h"
22 #include "base/strings/string_number_conversions.h"
23 #include "base/thread_task_runner_handle.h"
24 #include "base/threading/thread.h"
25 #include "base/threading/worker_pool.h"
26 #include "base/time/default_clock.h"
27 #include "base/values.h"
28 #include "google_apis/gcm/base/fake_encryptor.h"
29 #include "google_apis/gcm/base/mcs_message.h"
30 #include "google_apis/gcm/base/mcs_util.h"
31 #include "google_apis/gcm/engine/checkin_request.h"
32 #include "google_apis/gcm/engine/connection_factory_impl.h"
33 #include "google_apis/gcm/engine/gcm_store_impl.h"
34 #include "google_apis/gcm/engine/gservices_settings.h"
35 #include "google_apis/gcm/engine/mcs_client.h"
36 #include "google_apis/gcm/monitoring/fake_gcm_stats_recorder.h"
37 #include "net/base/host_mapping_rules.h"
38 #include "net/cert/cert_verifier.h"
39 #include "net/dns/host_resolver.h"
40 #include "net/http/http_auth_handler_factory.h"
41 #include "net/http/http_network_session.h"
42 #include "net/http/http_server_properties_impl.h"
43 #include "net/http/transport_security_state.h"
44 #include "net/log/write_to_file_net_log_observer.h"
45 #include "net/socket/client_socket_factory.h"
46 #include "net/socket/ssl_client_socket.h"
47 #include "net/ssl/channel_id_service.h"
48 #include "net/ssl/default_channel_id_store.h"
49 #include "net/url_request/url_request_test_util.h"
51 #if defined(OS_MACOSX)
52 #include "base/mac/scoped_nsautorelease_pool.h"
53 #endif
55 // This is a simple utility that initializes an mcs client and
56 // prints out any events.
57 namespace gcm {
58 namespace {
60 const net::BackoffEntry::Policy kDefaultBackoffPolicy = {
61 // Number of initial errors (in sequence) to ignore before applying
62 // exponential back-off rules.
65 // Initial delay for exponential back-off in ms.
66 15000, // 15 seconds.
68 // Factor by which the waiting time will be multiplied.
71 // Fuzzing percentage. ex: 10% will spread requests randomly
72 // between 90%-100% of the calculated time.
73 0.5, // 50%.
75 // Maximum amount of time we are willing to delay our request in ms.
76 1000 * 60 * 5, // 5 minutes.
78 // Time to keep an entry from being discarded even when it
79 // has no significant state, -1 to never discard.
80 -1,
82 // Don't use initial delay unless the last request was an error.
83 false,
86 // Default values used to communicate with the check-in server.
87 const char kChromeVersion[] = "Chrome MCS Probe";
89 // The default server to communicate with.
90 const char kMCSServerHost[] = "mtalk.google.com";
91 const uint16 kMCSServerPort = 5228;
93 // Command line switches.
94 const char kRMQFileName[] = "rmq_file";
95 const char kAndroidIdSwitch[] = "android_id";
96 const char kSecretSwitch[] = "secret";
97 const char kLogFileSwitch[] = "log-file";
98 const char kIgnoreCertSwitch[] = "ignore-certs";
99 const char kServerHostSwitch[] = "host";
100 const char kServerPortSwitch[] = "port";
102 void MessageReceivedCallback(const MCSMessage& message) {
103 LOG(INFO) << "Received message with id "
104 << GetPersistentId(message.GetProtobuf()) << " and tag "
105 << static_cast<int>(message.tag());
107 if (message.tag() == kDataMessageStanzaTag) {
108 const mcs_proto::DataMessageStanza& data_message =
109 reinterpret_cast<const mcs_proto::DataMessageStanza&>(
110 message.GetProtobuf());
111 DVLOG(1) << " to: " << data_message.to();
112 DVLOG(1) << " from: " << data_message.from();
113 DVLOG(1) << " category: " << data_message.category();
114 DVLOG(1) << " sent: " << data_message.sent();
115 for (int i = 0; i < data_message.app_data_size(); ++i) {
116 DVLOG(1) << " App data " << i << " "
117 << data_message.app_data(i).key() << " : "
118 << data_message.app_data(i).value();
123 void MessageSentCallback(int64 user_serial_number,
124 const std::string& app_id,
125 const std::string& message_id,
126 MCSClient::MessageSendStatus status) {
127 LOG(INFO) << "Message sent. Serial number: " << user_serial_number
128 << " Application ID: " << app_id
129 << " Message ID: " << message_id
130 << " Message send status: " << status;
133 // Needed to use a real host resolver.
134 class MyTestURLRequestContext : public net::TestURLRequestContext {
135 public:
136 MyTestURLRequestContext() : TestURLRequestContext(true) {
137 context_storage_.set_host_resolver(
138 net::HostResolver::CreateDefaultResolver(NULL));
139 context_storage_.set_transport_security_state(
140 make_scoped_ptr(new net::TransportSecurityState()));
141 Init();
144 ~MyTestURLRequestContext() override {}
147 class MyTestURLRequestContextGetter : public net::TestURLRequestContextGetter {
148 public:
149 explicit MyTestURLRequestContextGetter(
150 const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner)
151 : TestURLRequestContextGetter(io_task_runner) {}
153 net::TestURLRequestContext* GetURLRequestContext() override {
154 // Construct |context_| lazily so it gets constructed on the right
155 // thread (the IO thread).
156 if (!context_)
157 context_.reset(new MyTestURLRequestContext());
158 return context_.get();
161 private:
162 ~MyTestURLRequestContextGetter() override {}
164 scoped_ptr<MyTestURLRequestContext> context_;
167 // A cert verifier that access all certificates.
168 class MyTestCertVerifier : public net::CertVerifier {
169 public:
170 MyTestCertVerifier() {}
171 ~MyTestCertVerifier() override {}
173 int Verify(net::X509Certificate* cert,
174 const std::string& hostname,
175 const std::string& ocsp_response,
176 int flags,
177 net::CRLSet* crl_set,
178 net::CertVerifyResult* verify_result,
179 const net::CompletionCallback& callback,
180 scoped_ptr<Request>* out_req,
181 const net::BoundNetLog& net_log) override {
182 return net::OK;
186 class MCSProbe {
187 public:
188 MCSProbe(
189 const base::CommandLine& command_line,
190 scoped_refptr<net::URLRequestContextGetter> url_request_context_getter);
191 ~MCSProbe();
193 void Start();
195 uint64 android_id() const { return android_id_; }
196 uint64 secret() const { return secret_; }
198 private:
199 void CheckIn();
200 void InitializeNetworkState();
201 void BuildNetworkSession();
203 void LoadCallback(scoped_ptr<GCMStore::LoadResult> load_result);
204 void UpdateCallback(bool success);
205 void ErrorCallback();
206 void OnCheckInCompleted(
207 const checkin_proto::AndroidCheckinResponse& checkin_response);
208 void StartMCSLogin();
210 base::DefaultClock clock_;
212 base::CommandLine command_line_;
214 base::FilePath gcm_store_path_;
215 uint64 android_id_;
216 uint64 secret_;
217 std::string server_host_;
218 int server_port_;
220 // Network state.
221 scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_;
222 net::NetLog net_log_;
223 scoped_ptr<net::WriteToFileNetLogObserver> logger_;
224 scoped_ptr<net::HostResolver> host_resolver_;
225 scoped_ptr<net::CertVerifier> cert_verifier_;
226 scoped_ptr<net::ChannelIDService> system_channel_id_service_;
227 scoped_ptr<net::TransportSecurityState> transport_security_state_;
228 scoped_ptr<net::URLSecurityManager> url_security_manager_;
229 scoped_ptr<net::HttpAuthHandlerFactory> http_auth_handler_factory_;
230 scoped_ptr<net::HttpServerPropertiesImpl> http_server_properties_;
231 scoped_ptr<net::HostMappingRules> host_mapping_rules_;
232 scoped_refptr<net::HttpNetworkSession> network_session_;
233 scoped_ptr<net::ProxyService> proxy_service_;
235 FakeGCMStatsRecorder recorder_;
236 scoped_ptr<GCMStore> gcm_store_;
237 scoped_ptr<MCSClient> mcs_client_;
238 scoped_ptr<CheckinRequest> checkin_request_;
240 scoped_ptr<ConnectionFactoryImpl> connection_factory_;
242 base::Thread file_thread_;
244 scoped_ptr<base::RunLoop> run_loop_;
247 MCSProbe::MCSProbe(
248 const base::CommandLine& command_line,
249 scoped_refptr<net::URLRequestContextGetter> url_request_context_getter)
250 : command_line_(command_line),
251 gcm_store_path_(base::FilePath(FILE_PATH_LITERAL("gcm_store"))),
252 android_id_(0),
253 secret_(0),
254 server_port_(0),
255 url_request_context_getter_(url_request_context_getter),
256 file_thread_("FileThread") {
257 if (command_line.HasSwitch(kRMQFileName)) {
258 gcm_store_path_ = command_line.GetSwitchValuePath(kRMQFileName);
260 if (command_line.HasSwitch(kAndroidIdSwitch)) {
261 base::StringToUint64(command_line.GetSwitchValueASCII(kAndroidIdSwitch),
262 &android_id_);
264 if (command_line.HasSwitch(kSecretSwitch)) {
265 base::StringToUint64(command_line.GetSwitchValueASCII(kSecretSwitch),
266 &secret_);
268 server_host_ = kMCSServerHost;
269 if (command_line.HasSwitch(kServerHostSwitch)) {
270 server_host_ = command_line.GetSwitchValueASCII(kServerHostSwitch);
272 server_port_ = kMCSServerPort;
273 if (command_line.HasSwitch(kServerPortSwitch)) {
274 base::StringToInt(command_line.GetSwitchValueASCII(kServerPortSwitch),
275 &server_port_);
279 MCSProbe::~MCSProbe() {
280 if (logger_)
281 logger_->StopObserving(nullptr);
282 file_thread_.Stop();
285 void MCSProbe::Start() {
286 file_thread_.Start();
287 InitializeNetworkState();
288 BuildNetworkSession();
289 std::vector<GURL> endpoints(1,
290 GURL("https://" +
291 net::HostPortPair(server_host_,
292 server_port_).ToString()));
293 connection_factory_.reset(
294 new ConnectionFactoryImpl(endpoints,
295 kDefaultBackoffPolicy,
296 network_session_,
297 NULL,
298 &net_log_,
299 &recorder_));
300 gcm_store_.reset(
301 new GCMStoreImpl(gcm_store_path_,
302 file_thread_.task_runner(),
303 make_scoped_ptr<Encryptor>(new FakeEncryptor)));
304 mcs_client_.reset(new MCSClient("probe",
305 &clock_,
306 connection_factory_.get(),
307 gcm_store_.get(),
308 &recorder_));
309 run_loop_.reset(new base::RunLoop());
310 gcm_store_->Load(GCMStore::CREATE_IF_MISSING,
311 base::Bind(&MCSProbe::LoadCallback,
312 base::Unretained(this)));
313 run_loop_->Run();
316 void MCSProbe::LoadCallback(scoped_ptr<GCMStore::LoadResult> load_result) {
317 DCHECK(load_result->success);
318 if (android_id_ != 0 && secret_ != 0) {
319 DVLOG(1) << "Presetting MCS id " << android_id_;
320 load_result->device_android_id = android_id_;
321 load_result->device_security_token = secret_;
322 gcm_store_->SetDeviceCredentials(android_id_,
323 secret_,
324 base::Bind(&MCSProbe::UpdateCallback,
325 base::Unretained(this)));
326 } else {
327 android_id_ = load_result->device_android_id;
328 secret_ = load_result->device_security_token;
329 DVLOG(1) << "Loaded MCS id " << android_id_;
331 mcs_client_->Initialize(
332 base::Bind(&MCSProbe::ErrorCallback, base::Unretained(this)),
333 base::Bind(&MessageReceivedCallback),
334 base::Bind(&MessageSentCallback),
335 load_result.Pass());
337 if (!android_id_ || !secret_) {
338 DVLOG(1) << "Checkin to generate new MCS credentials.";
339 CheckIn();
340 return;
343 StartMCSLogin();
346 void MCSProbe::UpdateCallback(bool success) {
349 void MCSProbe::InitializeNetworkState() {
350 base::ScopedFILE log_file;
351 if (command_line_.HasSwitch(kLogFileSwitch)) {
352 base::FilePath log_path = command_line_.GetSwitchValuePath(kLogFileSwitch);
353 #if defined(OS_WIN)
354 log_file.reset(_wfopen(log_path.value().c_str(), L"w"));
355 #elif defined(OS_POSIX)
356 log_file.reset(fopen(log_path.value().c_str(), "w"));
357 #endif
359 if (log_file.get()) {
360 logger_.reset(new net::WriteToFileNetLogObserver());
361 logger_->set_capture_mode(
362 net::NetLogCaptureMode::IncludeCookiesAndCredentials());
363 logger_->StartObserving(&net_log_, log_file.Pass(), nullptr, nullptr);
366 host_resolver_ = net::HostResolver::CreateDefaultResolver(&net_log_);
368 if (command_line_.HasSwitch(kIgnoreCertSwitch)) {
369 cert_verifier_.reset(new MyTestCertVerifier());
370 } else {
371 cert_verifier_ = net::CertVerifier::CreateDefault();
373 system_channel_id_service_.reset(
374 new net::ChannelIDService(
375 new net::DefaultChannelIDStore(NULL),
376 base::WorkerPool::GetTaskRunner(true)));
378 transport_security_state_.reset(new net::TransportSecurityState());
379 url_security_manager_.reset(net::URLSecurityManager::Create(NULL, NULL));
380 http_auth_handler_factory_.reset(net::HttpAuthHandlerRegistryFactory::Create(
381 std::vector<std::string>(1, "basic"), url_security_manager_.get(),
382 host_resolver_.get(), std::string(), std::string(), false, false));
383 http_server_properties_.reset(new net::HttpServerPropertiesImpl());
384 host_mapping_rules_.reset(new net::HostMappingRules());
385 proxy_service_ = net::ProxyService::CreateDirectWithNetLog(&net_log_);
388 void MCSProbe::BuildNetworkSession() {
389 net::HttpNetworkSession::Params session_params;
390 session_params.host_resolver = host_resolver_.get();
391 session_params.cert_verifier = cert_verifier_.get();
392 session_params.channel_id_service = system_channel_id_service_.get();
393 session_params.transport_security_state = transport_security_state_.get();
394 session_params.ssl_config_service = new net::SSLConfigServiceDefaults();
395 session_params.http_auth_handler_factory = http_auth_handler_factory_.get();
396 session_params.http_server_properties =
397 http_server_properties_->GetWeakPtr();
398 session_params.network_delegate = NULL; // TODO(zea): implement?
399 session_params.host_mapping_rules = host_mapping_rules_.get();
400 session_params.ignore_certificate_errors = true;
401 session_params.testing_fixed_http_port = 0;
402 session_params.testing_fixed_https_port = 0;
403 session_params.net_log = &net_log_;
404 session_params.proxy_service = proxy_service_.get();
406 network_session_ = new net::HttpNetworkSession(session_params);
409 void MCSProbe::ErrorCallback() {
410 LOG(INFO) << "MCS error happened";
413 void MCSProbe::CheckIn() {
414 LOG(INFO) << "Check-in request initiated.";
415 checkin_proto::ChromeBuildProto chrome_build_proto;
416 chrome_build_proto.set_platform(
417 checkin_proto::ChromeBuildProto::PLATFORM_LINUX);
418 chrome_build_proto.set_channel(
419 checkin_proto::ChromeBuildProto::CHANNEL_CANARY);
420 chrome_build_proto.set_chrome_version(kChromeVersion);
422 CheckinRequest::RequestInfo request_info(0,
424 std::map<std::string, std::string>(),
425 std::string(),
426 chrome_build_proto);
428 checkin_request_.reset(new CheckinRequest(
429 GServicesSettings::DefaultCheckinURL(),
430 request_info,
431 kDefaultBackoffPolicy,
432 base::Bind(&MCSProbe::OnCheckInCompleted, base::Unretained(this)),
433 url_request_context_getter_.get(),
434 &recorder_));
435 checkin_request_->Start();
438 void MCSProbe::OnCheckInCompleted(
439 const checkin_proto::AndroidCheckinResponse& checkin_response) {
440 bool success = checkin_response.has_android_id() &&
441 checkin_response.android_id() != 0UL &&
442 checkin_response.has_security_token() &&
443 checkin_response.security_token() != 0UL;
444 LOG(INFO) << "Check-in request completion "
445 << (success ? "success!" : "failure!");
447 if (!success)
448 return;
450 android_id_ = checkin_response.android_id();
451 secret_ = checkin_response.security_token();
453 gcm_store_->SetDeviceCredentials(android_id_,
454 secret_,
455 base::Bind(&MCSProbe::UpdateCallback,
456 base::Unretained(this)));
458 StartMCSLogin();
461 void MCSProbe::StartMCSLogin() {
462 LOG(INFO) << "MCS login initiated.";
464 mcs_client_->Login(android_id_, secret_);
467 int MCSProbeMain(int argc, char* argv[]) {
468 base::AtExitManager exit_manager;
470 base::CommandLine::Init(argc, argv);
471 logging::LoggingSettings settings;
472 settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
473 logging::InitLogging(settings);
475 base::MessageLoopForIO message_loop;
477 // For check-in and creating registration ids.
478 const scoped_refptr<MyTestURLRequestContextGetter> context_getter =
479 new MyTestURLRequestContextGetter(
480 base::MessageLoop::current()->task_runner());
482 const base::CommandLine& command_line =
483 *base::CommandLine::ForCurrentProcess();
485 MCSProbe mcs_probe(command_line, context_getter);
486 mcs_probe.Start();
488 base::RunLoop run_loop;
489 run_loop.Run();
491 return 0;
494 } // namespace
495 } // namespace gcm
497 int main(int argc, char* argv[]) {
498 return gcm::MCSProbeMain(argc, argv);