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