Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 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 #include "google_apis/gcm/engine/connection_factory_impl.h" | |
| 6 | |
| 7 #include "base/message_loop/message_loop.h" | |
| 8 #include "google_apis/gcm/engine/connection_handler_impl.h" | |
| 9 #include "google_apis/gcm/protocol/mcs.pb.h" | |
| 10 #include "net/base/net_errors.h" | |
| 11 #include "net/http/http_network_session.h" | |
| 12 #include "net/http/http_request_headers.h" | |
| 13 #include "net/proxy/proxy_info.h" | |
| 14 #include "net/socket/client_socket_handle.h" | |
| 15 #include "net/socket/client_socket_pool_manager.h" | |
| 16 #include "net/ssl/ssl_config_service.h" | |
| 17 | |
| 18 namespace gcm { | |
| 19 | |
| 20 namespace { | |
| 21 | |
| 22 // The amount of time a Socket read should wait before timing out. | |
| 23 const int kReadTimeoutMs = 30000; // 30 seconds. | |
| 24 | |
| 25 // Backoff policy. | |
| 26 const net::BackoffEntry::Policy kConnectionBackoffPolicy = { | |
| 27 // Number of initial errors (in sequence) to ignore before applying | |
| 28 // exponential back-off rules. | |
| 29 0, | |
| 30 | |
| 31 // Initial delay for exponential back-off in ms. | |
| 32 10000, // 10 seconds. | |
| 33 | |
| 34 // Factor by which the waiting time will be multiplied. | |
| 35 2, | |
| 36 | |
| 37 // Fuzzing percentage. ex: 10% will spread requests randomly | |
| 38 // between 90%-100% of the calculated time. | |
| 39 0.2, // 20%. | |
| 40 | |
| 41 // Maximum amount of time we are willing to delay our request in ms. | |
| 42 1000 * 3600 * 4, // 4 hours. | |
| 43 | |
| 44 // Time to keep an entry from being discarded even when it | |
| 45 // has no significant state, -1 to never discard. | |
| 46 -1, | |
| 47 | |
| 48 // Don't use initial delay unless the last request was an error. | |
| 49 false, | |
| 50 }; | |
| 51 | |
| 52 // Callback for hostname resolution attempts. Just returns an error if no | |
| 53 // addresses were resolved. | |
| 54 int OnResolutionDone(const net::AddressList& address_list, | |
| 55 const net::BoundNetLog& net_log) { | |
| 56 if (address_list.empty()) { | |
| 57 LOG(ERROR) << "Failed to resolve hostname."; | |
| 58 return net::ERR_NAME_RESOLUTION_FAILED; | |
| 59 } | |
| 60 DVLOG(1) << "MCS Hostname resolution success."; | |
| 61 return net::OK; | |
| 62 } | |
| 63 | |
| 64 } // namespace | |
| 65 | |
| 66 ConnectionFactoryImpl::ConnectionFactoryImpl( | |
| 67 const GURL& mcs_endpoint, | |
| 68 scoped_refptr<net::HttpNetworkSession> network_session, | |
| 69 net::NetLog* net_log) | |
| 70 : mcs_endpoint_(mcs_endpoint), | |
| 71 network_session_(network_session), | |
| 72 net_log_(net_log), | |
| 73 weak_ptr_factory_(this) { | |
| 74 } | |
| 75 | |
| 76 ConnectionFactoryImpl::~ConnectionFactoryImpl() { | |
| 77 } | |
| 78 | |
| 79 ConnectionHandler* ConnectionFactoryImpl::BuildConnectionHandler( | |
| 80 const ConnectionHandler::ProtoReceivedCallback& read_callback, | |
| 81 const ConnectionHandler::ProtoSentCallback& write_callback) { | |
| 82 if (connection_handler_) { | |
| 83 NOTREACHED(); | |
| 84 return NULL; | |
|
akalin
2013/11/21 04:13:09
hmm on second thought, it's likely that the caller
Nicolas Zea
2013/11/22 05:39:18
True. Opted for DCHECK + return always returning c
| |
| 85 } | |
| 86 | |
| 87 backoff_entry_ = CreateBackoffEntry(&kConnectionBackoffPolicy); | |
| 88 | |
| 89 net::NetworkChangeNotifier::AddIPAddressObserver(this); | |
| 90 net::NetworkChangeNotifier::AddConnectionTypeObserver(this); | |
| 91 connection_handler_.reset( | |
| 92 new ConnectionHandlerImpl( | |
| 93 base::TimeDelta::FromMilliseconds(kReadTimeoutMs), | |
| 94 read_callback, | |
| 95 write_callback, | |
| 96 base::Bind(&ConnectionFactoryImpl::ConnectionHandlerCallback, | |
| 97 weak_ptr_factory_.GetWeakPtr()))); | |
| 98 return connection_handler_.get(); | |
| 99 } | |
| 100 | |
| 101 void ConnectionFactoryImpl::Connect( | |
| 102 const mcs_proto::LoginRequest& login_request) { | |
| 103 DCHECK(connection_handler_); | |
| 104 DCHECK(!IsEndpointReachable()); | |
| 105 | |
| 106 if (login_request.IsInitialized()) { | |
| 107 DCHECK(!login_request_.IsInitialized()); | |
| 108 login_request_ = login_request; | |
| 109 } | |
| 110 | |
| 111 if (backoff_entry_->ShouldRejectRequest()) { | |
| 112 DVLOG(1) << "Delaying MCS endpoint connection for " | |
| 113 << backoff_entry_->GetTimeUntilRelease().InMilliseconds() | |
| 114 << " milliseconds."; | |
| 115 base::MessageLoop::current()->PostDelayedTask( | |
| 116 FROM_HERE, | |
| 117 base::Bind(&ConnectionFactoryImpl::Connect, | |
| 118 weak_ptr_factory_.GetWeakPtr(), | |
| 119 login_request_), | |
| 120 NextRetryAttempt() - base::TimeTicks::Now()); | |
| 121 return; | |
| 122 } | |
| 123 | |
| 124 DVLOG(1) << "Attempting connection to MCS endpoint."; | |
| 125 ConnectImpl(); | |
| 126 } | |
| 127 | |
| 128 bool ConnectionFactoryImpl::IsEndpointReachable() const { | |
| 129 return connection_handler_ && connection_handler_->CanSendMessage(); | |
| 130 } | |
| 131 | |
| 132 base::TimeTicks ConnectionFactoryImpl::NextRetryAttempt() const { | |
| 133 if (!backoff_entry_) | |
| 134 return base::TimeTicks(); | |
| 135 return backoff_entry_->GetReleaseTime(); | |
| 136 } | |
| 137 | |
| 138 void ConnectionFactoryImpl::OnConnectionTypeChanged( | |
| 139 net::NetworkChangeNotifier::ConnectionType type) { | |
| 140 if (type == net::NetworkChangeNotifier::CONNECTION_NONE) | |
| 141 return; | |
| 142 | |
| 143 // TODO(zea): implement different backoff/retry policies based on connection | |
| 144 // type. | |
| 145 DVLOG(1) << "Connection type changed to " << type << ", resetting backoff."; | |
| 146 backoff_entry_->Reset(); | |
| 147 // Connect(..) should be retrying with backoff already if a connection is | |
| 148 // necessary, so no need to call again. | |
| 149 } | |
| 150 | |
| 151 void ConnectionFactoryImpl::OnIPAddressChanged() { | |
| 152 DVLOG(1) << "IP Address changed, resetting backoff."; | |
| 153 backoff_entry_->Reset(); | |
| 154 // Connect(..) should be retrying with backoff already if a connection is | |
| 155 // necessary, so no need to call again. | |
| 156 } | |
| 157 | |
| 158 void ConnectionFactoryImpl::ConnectImpl() { | |
| 159 DCHECK(!IsEndpointReachable()); | |
| 160 | |
| 161 // TODO(zea): resolve proxies. | |
|
akalin
2013/11/21 04:13:09
this is probably for a future CL, but you can copy
Nicolas Zea
2013/11/22 05:39:18
Will do. I plan to add some fallback logic in gene
| |
| 162 net::ProxyInfo proxy_info; | |
| 163 proxy_info.UseDirect(); | |
| 164 net::SSLConfig ssl_config; | |
| 165 network_session_->ssl_config_service()->GetSSLConfig(&ssl_config); | |
| 166 | |
| 167 // TODO(zea): This should probably be a raw request, but there's no good | |
|
akalin
2013/11/21 03:21:12
yeah, I think adding a InitSocketHandleForTlsConne
Nicolas Zea
2013/11/22 05:39:18
Done.
| |
| 168 // way to force SSL for a raw request yet. Instead, pretend to be an https | |
| 169 // request (which provides hostname resolution as well). | |
| 170 int status = net::InitSocketHandleForHttpRequest( | |
| 171 mcs_endpoint_, | |
| 172 net::HttpRequestHeaders(), | |
| 173 0, // No load flags. | |
| 174 net::MEDIUM, // Normal priority. | |
| 175 network_session_.get(), | |
| 176 proxy_info, | |
| 177 false, // Don't force SPDY over SSL. | |
| 178 false, // Don't want SPDY over NPN. | |
| 179 ssl_config, | |
| 180 ssl_config, | |
| 181 net::kPrivacyModeDisabled, | |
| 182 net::BoundNetLog::Make(net_log_, net::NetLog::SOURCE_SOCKET), | |
| 183 &socket_handle_, | |
| 184 base::Bind(&OnResolutionDone), | |
| 185 base::Bind(&ConnectionFactoryImpl::OnConnectDone, | |
| 186 weak_ptr_factory_.GetWeakPtr())); | |
| 187 if (status != net::ERR_IO_PENDING) | |
| 188 OnConnectDone(status); | |
| 189 } | |
| 190 | |
| 191 void ConnectionFactoryImpl::InitHandler() { | |
| 192 connection_handler_->Init(login_request_, socket_handle_.PassSocket()); | |
| 193 } | |
| 194 | |
| 195 scoped_ptr<net::BackoffEntry> ConnectionFactoryImpl::CreateBackoffEntry( | |
|
akalin
2013/11/21 04:13:09
this is used once -- do you need it to be a separa
Nicolas Zea
2013/11/22 05:39:18
Doing it this way allows me to override this metho
| |
| 196 const net::BackoffEntry::Policy* const policy) { | |
| 197 return scoped_ptr<net::BackoffEntry>(new net::BackoffEntry(policy)); | |
| 198 } | |
| 199 | |
| 200 void ConnectionFactoryImpl::OnConnectDone(int result) { | |
| 201 if (result != net::OK) { | |
| 202 LOG(ERROR) << "Failed to connect to MCS endpoint with error " << result; | |
| 203 backoff_entry_->InformOfRequest(false); | |
| 204 Connect(mcs_proto::LoginRequest()); | |
| 205 return; | |
| 206 } | |
| 207 | |
| 208 DVLOG(1) << "MCS endpoint connection success."; | |
| 209 | |
| 210 // Reset the backoff. | |
| 211 backoff_entry_->Reset(); | |
| 212 | |
| 213 InitHandler(); | |
| 214 } | |
| 215 | |
| 216 void ConnectionFactoryImpl::ConnectionHandlerCallback(int result) { | |
| 217 // TODO(zea): Consider how to handle errors that may require some sort of | |
| 218 // user intervention (login page, etc.). | |
| 219 LOG(ERROR) << "Connection reset with error " << result; | |
| 220 backoff_entry_->InformOfRequest(false); | |
| 221 Connect(mcs_proto::LoginRequest()); | |
| 222 } | |
| 223 | |
| 224 } // namespace gcm | |
| OLD | NEW |