#include "base/metrics/histogram.h"
#include "base/metrics/sparse_histogram.h"
#include "google_apis/gcm/engine/connection_handler_impl.h"
+#include "google_apis/gcm/monitoring/gcm_stats_recorder.h"
#include "google_apis/gcm/protocol/mcs.pb.h"
+#include "net/base/load_flags.h"
#include "net/base/net_errors.h"
#include "net/http/http_network_session.h"
#include "net/http/http_request_headers.h"
ConnectionFactoryImpl::ConnectionFactoryImpl(
const std::vector<GURL>& mcs_endpoints,
const net::BackoffEntry::Policy& backoff_policy,
- scoped_refptr<net::HttpNetworkSession> network_session,
- net::NetLog* net_log)
+ const scoped_refptr<net::HttpNetworkSession>& gcm_network_session,
+ const scoped_refptr<net::HttpNetworkSession>& http_network_session,
+ net::NetLog* net_log,
+ GCMStatsRecorder* recorder)
: mcs_endpoints_(mcs_endpoints),
next_endpoint_(0),
last_successful_endpoint_(0),
backoff_policy_(backoff_policy),
- network_session_(network_session),
+ gcm_network_session_(gcm_network_session),
+ http_network_session_(http_network_session),
bound_net_log_(
net::BoundNetLog::Make(net_log, net::NetLog::SOURCE_SOCKET)),
pac_request_(NULL),
connecting_(false),
waiting_for_backoff_(false),
+ waiting_for_network_online_(false),
logging_in_(false),
+ recorder_(recorder),
+ listener_(NULL),
weak_ptr_factory_(this) {
DCHECK_GE(mcs_endpoints_.size(), 1U);
+ DCHECK(!http_network_session_.get() ||
+ (gcm_network_session_.get() != http_network_session_.get()));
}
ConnectionFactoryImpl::~ConnectionFactoryImpl() {
+ CloseSocket();
+ net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
if (pac_request_) {
- network_session_->proxy_service()->CancelPacRequest(pac_request_);
+ gcm_network_session_->proxy_service()->CancelPacRequest(pac_request_);
pac_request_ = NULL;
}
}
backoff_entry_ = CreateBackoffEntry(&backoff_policy_);
request_builder_ = request_builder;
- net::NetworkChangeNotifier::AddIPAddressObserver(this);
- net::NetworkChangeNotifier::AddConnectionTypeObserver(this);
+ net::NetworkChangeNotifier::AddNetworkChangeObserver(this);
+ waiting_for_network_online_ = net::NetworkChangeNotifier::IsOffline();
connection_handler_ = CreateConnectionHandler(
base::TimeDelta::FromMilliseconds(kReadTimeoutMs),
read_callback,
void ConnectionFactoryImpl::ConnectWithBackoff() {
// If a canary managed to connect while a backoff expiration was pending,
// just cleanup the internal state.
- if (connecting_ || IsEndpointReachable()) {
+ if (connecting_ || logging_in_ || IsEndpointReachable()) {
waiting_for_backoff_ = false;
return;
}
<< backoff_entry_->GetTimeUntilRelease().InMilliseconds()
<< " milliseconds.";
waiting_for_backoff_ = true;
+ recorder_->RecordConnectionDelayedDueToBackoff(
+ backoff_entry_->GetTimeUntilRelease().InMilliseconds());
base::MessageLoop::current()->PostDelayedTask(
FROM_HERE,
base::Bind(&ConnectionFactoryImpl::ConnectWithBackoff,
return connection_handler_ && connection_handler_->CanSendMessage();
}
+std::string ConnectionFactoryImpl::GetConnectionStateString() const {
+ if (IsEndpointReachable())
+ return "CONNECTED";
+ if (logging_in_)
+ return "LOGGING IN";
+ if (connecting_)
+ return "CONNECTING";
+ if (waiting_for_backoff_)
+ return "WAITING FOR BACKOFF";
+ if (waiting_for_network_online_)
+ return "WAITING FOR NETWORK CHANGE";
+ return "NOT CONNECTED";
+}
+
void ConnectionFactoryImpl::SignalConnectionReset(
ConnectionResetReason reason) {
// A failure can trigger multiple resets, so no need to do anything if a
return;
}
+ if (listener_)
+ listener_->OnDisconnected();
+
UMA_HISTOGRAM_ENUMERATION("GCM.ConnectionResetReason",
reason,
CONNECTION_RESET_COUNT);
+ recorder_->RecordConnectionResetSignaled(reason);
if (!last_login_time_.is_null()) {
UMA_HISTOGRAM_CUSTOM_TIMES("GCM.ConnectionUpTime",
NowTicks() - last_login_time_,
CloseSocket();
DCHECK(!IsEndpointReachable());
+ // TODO(zea): if the network is offline, don't attempt to connect.
+ // See crbug.com/396687
+
// Network changes get special treatment as they can trigger a one-off canary
// request that bypasses backoff (but does nothing if a connection is in
// progress). Other connection reset events can be ignored as a connection
// We shouldn't be in backoff in thise case.
DCHECK_EQ(0, backoff_entry_->failure_count());
}
- DCHECK(!connecting_);
- DCHECK(!waiting_for_backoff_);
// At this point the last login time has been consumed or deemed irrelevant,
// reset it.
Connect();
}
+void ConnectionFactoryImpl::SetConnectionListener(
+ ConnectionListener* listener) {
+ listener_ = listener;
+}
+
base::TimeTicks ConnectionFactoryImpl::NextRetryAttempt() const {
if (!backoff_entry_)
return base::TimeTicks();
return backoff_entry_->GetReleaseTime();
}
-void ConnectionFactoryImpl::OnConnectionTypeChanged(
+void ConnectionFactoryImpl::OnNetworkChanged(
net::NetworkChangeNotifier::ConnectionType type) {
- if (type == net::NetworkChangeNotifier::CONNECTION_NONE)
+ if (type == net::NetworkChangeNotifier::CONNECTION_NONE) {
+ DVLOG(1) << "Network lost, resettion connection.";
+ waiting_for_network_online_ = true;
+
+ // Will do nothing due to |waiting_for_network_online_ == true|.
+ // TODO(zea): make the above statement actually true. See crbug.com/396687
+ SignalConnectionReset(NETWORK_CHANGE);
return;
+ }
DVLOG(1) << "Connection type changed to " << type << ", reconnecting.";
-
- // The connection may have been silently dropped, attempt to reconnect.
- SignalConnectionReset(NETWORK_CHANGE);
-}
-
-void ConnectionFactoryImpl::OnIPAddressChanged() {
- DVLOG(1) << "IP Address changed, reconnecting.";
-
- // The connection may have been silently dropped, attempt to reconnect.
+ waiting_for_network_online_ = false;
SignalConnectionReset(NETWORK_CHANGE);
}
return mcs_endpoints_[next_endpoint_];
}
+net::IPEndPoint ConnectionFactoryImpl::GetPeerIP() {
+ if (!socket_handle_.socket())
+ return net::IPEndPoint();
+
+ net::IPEndPoint ip_endpoint;
+ int result = socket_handle_.socket()->GetPeerAddress(&ip_endpoint);
+ if (result != net::OK)
+ return net::IPEndPoint();
+
+ return ip_endpoint;
+}
+
void ConnectionFactoryImpl::ConnectImpl() {
DCHECK(!IsEndpointReachable());
DCHECK(!socket_handle_.socket());
+ // TODO(zea): if the network is offline, don't attempt to connect.
+ // See crbug.com/396687
+
connecting_ = true;
- int status = network_session_->proxy_service()->ResolveProxy(
- GetCurrentEndpoint(),
+ GURL current_endpoint = GetCurrentEndpoint();
+ recorder_->RecordConnectionInitiated(current_endpoint.host());
+ RebuildNetworkSessionAuthCache();
+ int status = gcm_network_session_->proxy_service()->ResolveProxy(
+ current_endpoint,
+ net::LOAD_NORMAL,
&proxy_info_,
base::Bind(&ConnectionFactoryImpl::OnProxyResolveDone,
weak_ptr_factory_.GetWeakPtr()),
&pac_request_,
+ NULL,
bound_net_log_);
if (status != net::ERR_IO_PENDING)
OnProxyResolveDone(status);
return; // Proxy reconsideration pending. Return.
LOG(ERROR) << "Failed to connect to MCS endpoint with error " << result;
UMA_HISTOGRAM_BOOLEAN("GCM.ConnectionSuccessRate", false);
+ recorder_->RecordConnectionFailure(result);
CloseSocket();
backoff_entry_->InformOfRequest(false);
UMA_HISTOGRAM_SPARSE_SLOWLY("GCM.ConnectionFailureErrorCode", result);
UMA_HISTOGRAM_BOOLEAN("GCM.ConnectedViaProxy",
!(proxy_info_.is_empty() || proxy_info_.is_direct()));
ReportSuccessfulProxyConnection();
+ recorder_->RecordConnectionSuccess();
// Reset the endpoint back to the default.
// TODO(zea): consider prioritizing endpoints more intelligently based on
previous_backoff_.swap(backoff_entry_);
backoff_entry_->Reset();
logging_in_ = false;
+
+ if (listener_)
+ listener_->OnConnected(GetCurrentEndpoint(), GetPeerIP());
}
// This has largely been copied from
DVLOG(1) << "Resolved proxy with PAC:" << proxy_info_.ToPacString();
net::SSLConfig ssl_config;
- network_session_->ssl_config_service()->GetSSLConfig(&ssl_config);
+ gcm_network_session_->ssl_config_service()->GetSSLConfig(&ssl_config);
status = net::InitSocketHandleForTlsConnect(
net::HostPortPair::FromURL(GetCurrentEndpoint()),
- network_session_.get(),
+ gcm_network_session_.get(),
proxy_info_,
ssl_config,
ssl_config,
- net::kPrivacyModeDisabled,
+ net::PRIVACY_MODE_DISABLED,
bound_net_log_,
&socket_handle_,
base::Bind(&ConnectionFactoryImpl::OnConnectDone,
}
net::SSLConfig ssl_config;
- network_session_->ssl_config_service()->GetSSLConfig(&ssl_config);
+ gcm_network_session_->ssl_config_service()->GetSSLConfig(&ssl_config);
if (proxy_info_.is_https() && ssl_config.send_client_cert) {
- network_session_->ssl_client_auth_cache()->Remove(
+ gcm_network_session_->ssl_client_auth_cache()->Remove(
proxy_info_.proxy_server().host_port_pair());
}
- int status = network_session_->proxy_service()->ReconsiderProxyAfterError(
- GetCurrentEndpoint(), &proxy_info_,
+ int status = gcm_network_session_->proxy_service()->ReconsiderProxyAfterError(
+ GetCurrentEndpoint(), net::LOAD_NORMAL, error, &proxy_info_,
base::Bind(&ConnectionFactoryImpl::OnProxyResolveDone,
weak_ptr_factory_.GetWeakPtr()),
&pac_request_,
+ NULL,
bound_net_log_);
if (status == net::OK || status == net::ERR_IO_PENDING) {
CloseSocket();
}
void ConnectionFactoryImpl::ReportSuccessfulProxyConnection() {
- if (network_session_ && network_session_->proxy_service())
- network_session_->proxy_service()->ReportSuccess(proxy_info_);
+ if (gcm_network_session_ && gcm_network_session_->proxy_service())
+ gcm_network_session_->proxy_service()->ReportSuccess(proxy_info_, NULL);
}
void ConnectionFactoryImpl::CloseSocket() {
socket_handle_.Reset();
}
+void ConnectionFactoryImpl::RebuildNetworkSessionAuthCache() {
+ if (!http_network_session_ || !http_network_session_->http_auth_cache())
+ return;
+
+ gcm_network_session_->http_auth_cache()->UpdateAllFrom(
+ *http_network_session_->http_auth_cache());
+}
+
} // namespace gcm