1 // Copyright (c) 2012 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 #include "jingle/notifier/communicator/login.h" 6 7 #include <string> 8 9 #include "base/logging.h" 10 #include "base/rand_util.h" 11 #include "base/time/time.h" 12 #include "net/base/host_port_pair.h" 13 #include "talk/base/common.h" 14 #include "talk/base/firewallsocketserver.h" 15 #include "talk/base/logging.h" 16 #include "talk/base/physicalsocketserver.h" 17 #include "talk/base/taskrunner.h" 18 #include "talk/xmllite/xmlelement.h" 19 #include "talk/xmpp/asyncsocket.h" 20 #include "talk/xmpp/prexmppauth.h" 21 #include "talk/xmpp/xmppclient.h" 22 #include "talk/xmpp/xmppclientsettings.h" 23 #include "talk/xmpp/xmppengine.h" 24 25 namespace notifier { 26 27 Login::Delegate::~Delegate() {} 28 29 Login::Login(Delegate* delegate, 30 const buzz::XmppClientSettings& user_settings, 31 const scoped_refptr<net::URLRequestContextGetter>& 32 request_context_getter, 33 const ServerList& servers, 34 bool try_ssltcp_first, 35 const std::string& auth_mechanism) 36 : delegate_(delegate), 37 login_settings_(user_settings, 38 request_context_getter, 39 servers, 40 try_ssltcp_first, 41 auth_mechanism) { 42 net::NetworkChangeNotifier::AddIPAddressObserver(this); 43 net::NetworkChangeNotifier::AddConnectionTypeObserver(this); 44 // TODO(akalin): Add as DNSObserver once bug 130610 is fixed. 45 ResetReconnectState(); 46 } 47 48 Login::~Login() { 49 net::NetworkChangeNotifier::RemoveConnectionTypeObserver(this); 50 net::NetworkChangeNotifier::RemoveIPAddressObserver(this); 51 } 52 53 void Login::StartConnection() { 54 DVLOG(1) << "Starting connection..."; 55 single_attempt_.reset(new SingleLoginAttempt(login_settings_, this)); 56 } 57 58 void Login::UpdateXmppSettings(const buzz::XmppClientSettings& user_settings) { 59 DVLOG(1) << "XMPP settings updated"; 60 login_settings_.set_user_settings(user_settings); 61 } 62 63 // In the code below, we assume that calling a delegate method may end 64 // up in ourselves being deleted, so we always call it last. 65 // 66 // TODO(akalin): Add unit tests to enforce the behavior above. 67 68 void Login::OnConnect(base::WeakPtr<buzz::XmppTaskParentInterface> base_task) { 69 DVLOG(1) << "Connected"; 70 ResetReconnectState(); 71 delegate_->OnConnect(base_task); 72 } 73 74 void Login::OnRedirect(const ServerInformation& redirect_server) { 75 DVLOG(1) << "Redirected"; 76 login_settings_.SetRedirectServer(redirect_server); 77 // Drop the current connection, and start the login process again. 78 StartConnection(); 79 delegate_->OnTransientDisconnection(); 80 } 81 82 void Login::OnCredentialsRejected() { 83 DVLOG(1) << "Credentials rejected"; 84 TryReconnect(); 85 delegate_->OnCredentialsRejected(); 86 } 87 88 void Login::OnSettingsExhausted() { 89 DVLOG(1) << "Settings exhausted"; 90 TryReconnect(); 91 delegate_->OnTransientDisconnection(); 92 } 93 94 void Login::OnIPAddressChanged() { 95 DVLOG(1) << "IP address changed"; 96 OnNetworkEvent(); 97 } 98 99 void Login::OnConnectionTypeChanged( 100 net::NetworkChangeNotifier::ConnectionType type) { 101 DVLOG(1) << "Connection type changed"; 102 OnNetworkEvent(); 103 } 104 105 void Login::OnDNSChanged() { 106 DVLOG(1) << "DNS changed"; 107 OnNetworkEvent(); 108 } 109 110 void Login::OnNetworkEvent() { 111 // Reconnect in 1 to 9 seconds (vary the time a little to try to 112 // avoid spikey behavior on network hiccups). 113 reconnect_interval_ = base::TimeDelta::FromSeconds(base::RandInt(1, 9)); 114 TryReconnect(); 115 delegate_->OnTransientDisconnection(); 116 } 117 118 void Login::ResetReconnectState() { 119 reconnect_interval_ = 120 base::TimeDelta::FromSeconds(base::RandInt(5, 25)); 121 reconnect_timer_.Stop(); 122 } 123 124 void Login::TryReconnect() { 125 DCHECK_GT(reconnect_interval_.InSeconds(), 0); 126 single_attempt_.reset(); 127 reconnect_timer_.Stop(); 128 DVLOG(1) << "Reconnecting in " 129 << reconnect_interval_.InSeconds() << " seconds"; 130 reconnect_timer_.Start( 131 FROM_HERE, reconnect_interval_, this, &Login::DoReconnect); 132 } 133 134 void Login::DoReconnect() { 135 // Double reconnect time up to 30 minutes. 136 const base::TimeDelta kMaxReconnectInterval = 137 base::TimeDelta::FromMinutes(30); 138 reconnect_interval_ *= 2; 139 if (reconnect_interval_ > kMaxReconnectInterval) 140 reconnect_interval_ = kMaxReconnectInterval; 141 DVLOG(1) << "Reconnecting..."; 142 StartConnection(); 143 } 144 145 } // namespace notifier 146