summaryrefslogtreecommitdiffstats
path: root/google_apis/gcm
diff options
context:
space:
mode:
authorzea@chromium.org <zea@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-03-14 06:05:29 +0000
committerzea@chromium.org <zea@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-03-14 06:05:29 +0000
commit0e64bfe5f16b5e4a898cd957eba98a8430c8bca7 (patch)
tree870348c174065f43861c902899f6b96601fd7bd4 /google_apis/gcm
parentccd74c4eb9f984994731cb9a9047e1e2271bfa5d (diff)
downloadchromium_src-0e64bfe5f16b5e4a898cd957eba98a8430c8bca7.zip
chromium_src-0e64bfe5f16b5e4a898cd957eba98a8430c8bca7.tar.gz
chromium_src-0e64bfe5f16b5e4a898cd957eba98a8430c8bca7.tar.bz2
[GCM] Add proxy resolution support.
GCM will use the proxy resolver associated with the current network session to resolve proxies. There is no direct fallback at the moment, so if a proxy resolution completes, but doesn't handle the data properly (because it's intercepting/mangling the response), connections will fail. Direct fallback, and attempting alternate ports, will be introduced in a later patch. BUG=351889 TBR=jianli@chromium.org Review URL: https://codereview.chromium.org/197473008 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@257023 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'google_apis/gcm')
-rw-r--r--google_apis/gcm/engine/connection_factory_impl.cc211
-rw-r--r--google_apis/gcm/engine/connection_factory_impl.h16
-rw-r--r--google_apis/gcm/engine/connection_handler_impl.cc12
3 files changed, 204 insertions, 35 deletions
diff --git a/google_apis/gcm/engine/connection_factory_impl.cc b/google_apis/gcm/engine/connection_factory_impl.cc
index 3c2e412..8b0ed5c 100644
--- a/google_apis/gcm/engine/connection_factory_impl.cc
+++ b/google_apis/gcm/engine/connection_factory_impl.cc
@@ -48,13 +48,19 @@ ConnectionFactoryImpl::ConnectionFactoryImpl(
: mcs_endpoint_(mcs_endpoint),
backoff_policy_(backoff_policy),
network_session_(network_session),
- net_log_(net_log),
+ bound_net_log_(
+ net::BoundNetLog::Make(net_log, net::NetLog::SOURCE_SOCKET)),
+ pac_request_(NULL),
connecting_(false),
logging_in_(false),
weak_ptr_factory_(this) {
}
ConnectionFactoryImpl::~ConnectionFactoryImpl() {
+ if (pac_request_) {
+ network_session_->proxy_service()->CancelPacRequest(pac_request_);
+ pac_request_ = NULL;
+ }
}
void ConnectionFactoryImpl::Initialize(
@@ -128,12 +134,7 @@ void ConnectionFactoryImpl::SignalConnectionReset(
// connection.
}
- if (connection_handler_)
- connection_handler_->Reset();
-
- if (socket_handle_.socket() && socket_handle_.socket()->IsConnected())
- socket_handle_.socket()->Disconnect();
- socket_handle_.Reset();
+ CloseSocket();
if (logging_in_) {
// Failures prior to login completion just reuse the existing backoff entry.
@@ -147,7 +148,7 @@ void ConnectionFactoryImpl::SignalConnectionReset(
backoff_entry_->InformOfRequest(false);
} else {
// We shouldn't be in backoff in thise case.
- DCHECK(backoff_entry_->CanDiscard());
+ DCHECK_EQ(0, backoff_entry_->failure_count());
}
// At this point the last login time has been consumed or deemed irrelevant,
@@ -187,25 +188,15 @@ void ConnectionFactoryImpl::ConnectImpl() {
DCHECK(connecting_);
DCHECK(!socket_handle_.socket());
- // TODO(zea): resolve proxies.
- net::ProxyInfo proxy_info;
- proxy_info.UseDirect();
- net::SSLConfig ssl_config;
- network_session_->ssl_config_service()->GetSSLConfig(&ssl_config);
-
- int status = net::InitSocketHandleForTlsConnect(
- net::HostPortPair::FromURL(mcs_endpoint_),
- network_session_.get(),
- proxy_info,
- ssl_config,
- ssl_config,
- net::kPrivacyModeDisabled,
- net::BoundNetLog::Make(net_log_, net::NetLog::SOURCE_SOCKET),
- &socket_handle_,
- base::Bind(&ConnectionFactoryImpl::OnConnectDone,
- weak_ptr_factory_.GetWeakPtr()));
+ int status = network_session_->proxy_service()->ResolveProxy(
+ mcs_endpoint_,
+ &proxy_info_,
+ base::Bind(&ConnectionFactoryImpl::OnProxyResolveDone,
+ weak_ptr_factory_.GetWeakPtr()),
+ &pac_request_,
+ bound_net_log_);
if (status != net::ERR_IO_PENDING)
- OnConnectDone(status);
+ OnProxyResolveDone(status);
}
void ConnectionFactoryImpl::InitHandler() {
@@ -229,16 +220,27 @@ base::TimeTicks ConnectionFactoryImpl::NowTicks() {
}
void ConnectionFactoryImpl::OnConnectDone(int result) {
- UMA_HISTOGRAM_BOOLEAN("GCM.ConnectionSuccessRate", (result == net::OK));
-
if (result != net::OK) {
+ // If the connection fails, try another proxy.
+ result = ReconsiderProxyAfterError(result);
+ // ReconsiderProxyAfterError either returns an error (in which case it is
+ // not reconsidering a proxy) or returns ERR_IO_PENDING if it is considering
+ // another proxy.
+ DCHECK_NE(result, net::OK);
+ if (result == net::ERR_IO_PENDING)
+ return; // Proxy reconsideration pending. Return.
LOG(ERROR) << "Failed to connect to MCS endpoint with error " << result;
+ UMA_HISTOGRAM_BOOLEAN("GCM.ConnectionSuccessRate", false);
+ CloseSocket();
backoff_entry_->InformOfRequest(false);
UMA_HISTOGRAM_SPARSE_SLOWLY("GCM.ConnectionFailureErrorCode", result);
Connect();
return;
}
+ UMA_HISTOGRAM_BOOLEAN("GCM.ConnectionSuccessRate", true);
+ ReportSuccessfulProxyConnection();
+
connecting_ = false;
logging_in_ = true;
DVLOG(1) << "MCS endpoint socket connection success, starting login.";
@@ -258,10 +260,163 @@ void ConnectionFactoryImpl::ConnectionHandlerCallback(int result) {
// Handshake complete, reset backoff. If the login failed with an error,
// the client should invoke SignalConnectionReset(LOGIN_FAILURE), which will
// restore the previous backoff.
+ DVLOG(1) << "Handshake complete.";
last_login_time_ = NowTicks();
previous_backoff_.swap(backoff_entry_);
backoff_entry_->Reset();
logging_in_ = false;
}
+// This has largely been copied from
+// HttpStreamFactoryImpl::Job::DoResolveProxyComplete. This should be
+// refactored into some common place.
+void ConnectionFactoryImpl::OnProxyResolveDone(int status) {
+ pac_request_ = NULL;
+ DVLOG(1) << "Proxy resolution status: " << status;
+
+ DCHECK_NE(status, net::ERR_IO_PENDING);
+ if (status == net::OK) {
+ // Remove unsupported proxies from the list.
+ proxy_info_.RemoveProxiesWithoutScheme(
+ net::ProxyServer::SCHEME_DIRECT |
+ net::ProxyServer::SCHEME_HTTP | net::ProxyServer::SCHEME_HTTPS |
+ net::ProxyServer::SCHEME_SOCKS4 | net::ProxyServer::SCHEME_SOCKS5);
+
+ if (proxy_info_.is_empty()) {
+ // No proxies/direct to choose from. This happens when we don't support
+ // any of the proxies in the returned list.
+ status = net::ERR_NO_SUPPORTED_PROXIES;
+ }
+ }
+
+ if (status != net::OK) {
+ // Failed to resolve proxy. Retry later.
+ OnConnectDone(status);
+ return;
+ }
+
+ DVLOG(1) << "Resolved proxy with PAC:" << proxy_info_.ToPacString();
+
+ net::SSLConfig ssl_config;
+ network_session_->ssl_config_service()->GetSSLConfig(&ssl_config);
+ status = net::InitSocketHandleForTlsConnect(
+ net::HostPortPair::FromURL(mcs_endpoint_),
+ network_session_.get(),
+ proxy_info_,
+ ssl_config,
+ ssl_config,
+ net::kPrivacyModeDisabled,
+ bound_net_log_,
+ &socket_handle_,
+ base::Bind(&ConnectionFactoryImpl::OnConnectDone,
+ weak_ptr_factory_.GetWeakPtr()));
+ if (status != net::ERR_IO_PENDING)
+ OnConnectDone(status);
+}
+
+// This has largely been copied from
+// HttpStreamFactoryImpl::Job::ReconsiderProxyAfterError. This should be
+// refactored into some common place.
+// This method reconsiders the proxy on certain errors. If it does reconsider
+// a proxy it always returns ERR_IO_PENDING and posts a call to
+// OnProxyResolveDone with the result of the reconsideration.
+int ConnectionFactoryImpl::ReconsiderProxyAfterError(int error) {
+ DCHECK(!pac_request_);
+ DCHECK_NE(error, net::OK);
+ DCHECK_NE(error, net::ERR_IO_PENDING);
+ // A failure to resolve the hostname or any error related to establishing a
+ // TCP connection could be grounds for trying a new proxy configuration.
+ //
+ // Why do this when a hostname cannot be resolved? Some URLs only make sense
+ // to proxy servers. The hostname in those URLs might fail to resolve if we
+ // are still using a non-proxy config. We need to check if a proxy config
+ // now exists that corresponds to a proxy server that could load the URL.
+ //
+ switch (error) {
+ case net::ERR_PROXY_CONNECTION_FAILED:
+ case net::ERR_NAME_NOT_RESOLVED:
+ case net::ERR_INTERNET_DISCONNECTED:
+ case net::ERR_ADDRESS_UNREACHABLE:
+ case net::ERR_CONNECTION_CLOSED:
+ case net::ERR_CONNECTION_TIMED_OUT:
+ case net::ERR_CONNECTION_RESET:
+ case net::ERR_CONNECTION_REFUSED:
+ case net::ERR_CONNECTION_ABORTED:
+ case net::ERR_TIMED_OUT:
+ case net::ERR_TUNNEL_CONNECTION_FAILED:
+ case net::ERR_SOCKS_CONNECTION_FAILED:
+ // This can happen in the case of trying to talk to a proxy using SSL, and
+ // ending up talking to a captive portal that supports SSL instead.
+ case net::ERR_PROXY_CERTIFICATE_INVALID:
+ // This can happen when trying to talk SSL to a non-SSL server (Like a
+ // captive portal).
+ case net::ERR_SSL_PROTOCOL_ERROR:
+ break;
+ case net::ERR_SOCKS_CONNECTION_HOST_UNREACHABLE:
+ // Remap the SOCKS-specific "host unreachable" error to a more
+ // generic error code (this way consumers like the link doctor
+ // know to substitute their error page).
+ //
+ // Note that if the host resolving was done by the SOCKS5 proxy, we can't
+ // differentiate between a proxy-side "host not found" versus a proxy-side
+ // "address unreachable" error, and will report both of these failures as
+ // ERR_ADDRESS_UNREACHABLE.
+ return net::ERR_ADDRESS_UNREACHABLE;
+ default:
+ return error;
+ }
+
+ net::SSLConfig ssl_config;
+ 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(
+ proxy_info_.proxy_server().host_port_pair());
+ }
+
+ int status = network_session_->proxy_service()->ReconsiderProxyAfterError(
+ mcs_endpoint_, &proxy_info_,
+ base::Bind(&ConnectionFactoryImpl::OnProxyResolveDone,
+ weak_ptr_factory_.GetWeakPtr()),
+ &pac_request_,
+ bound_net_log_);
+ if (status == net::OK || status == net::ERR_IO_PENDING) {
+ CloseSocket();
+ } else {
+ // If ReconsiderProxyAfterError() failed synchronously, it means
+ // there was nothing left to fall-back to, so fail the transaction
+ // with the last connection error we got.
+ status = error;
+ }
+
+ // We either have new proxy info or there was an error in falling back.
+ // In both cases we want to post OnProxyResolveDone (in the error case
+ // we might still want to fall back a direct connection).
+ if (status != net::ERR_IO_PENDING) {
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&ConnectionFactoryImpl::OnProxyResolveDone,
+ weak_ptr_factory_.GetWeakPtr(), status));
+ // Since we potentially have another try to go (trying the direct connect)
+ // set the return code code to ERR_IO_PENDING.
+ status = net::ERR_IO_PENDING;
+ }
+ return status;
+}
+
+void ConnectionFactoryImpl::ReportSuccessfulProxyConnection() {
+ if (network_session_ && network_session_->proxy_service())
+ network_session_->proxy_service()->ReportSuccess(proxy_info_);
+}
+
+void ConnectionFactoryImpl::CloseSocket() {
+ // The connection handler needs to be reset, else it'll attempt to keep using
+ // the destroyed socket.
+ if (connection_handler_)
+ connection_handler_->Reset();
+
+ if (socket_handle_.socket() && socket_handle_.socket()->IsConnected())
+ socket_handle_.socket()->Disconnect();
+ socket_handle_.Reset();
+}
+
} // namespace gcm
diff --git a/google_apis/gcm/engine/connection_factory_impl.h b/google_apis/gcm/engine/connection_factory_impl.h
index 7326d79..967c6c1 100644
--- a/google_apis/gcm/engine/connection_factory_impl.h
+++ b/google_apis/gcm/engine/connection_factory_impl.h
@@ -12,6 +12,8 @@
#include "google_apis/gcm/protocol/mcs.pb.h"
#include "net/base/backoff_entry.h"
#include "net/base/network_change_notifier.h"
+#include "net/proxy/proxy_info.h"
+#include "net/proxy/proxy_service.h"
#include "net/socket/client_socket_handle.h"
#include "url/gurl.h"
@@ -79,6 +81,14 @@ class GCM_EXPORT ConnectionFactoryImpl :
void ConnectionHandlerCallback(int result);
private:
+ // Proxy resolution and connection functions.
+ void OnProxyResolveDone(int status);
+ void OnProxyConnectDone(int status);
+ int ReconsiderProxyAfterError(int error);
+ void ReportSuccessfulProxyConnection();
+
+ void CloseSocket();
+
// The MCS endpoint to make connections to.
const GURL mcs_endpoint_;
@@ -89,7 +99,11 @@ class GCM_EXPORT ConnectionFactoryImpl :
// Network session for creating new connections.
const scoped_refptr<net::HttpNetworkSession> network_session_;
// Net log to use in connection attempts.
- net::NetLog* const net_log_;
+ net::BoundNetLog bound_net_log_;
+ // The current PAC request, if one exists. Owned by the proxy service.
+ net::ProxyService::PacRequest* pac_request_;
+ // The current proxy info.
+ net::ProxyInfo proxy_info_;
// The handle to the socket for the current connection, if one exists.
net::ClientSocketHandle socket_handle_;
// Current backoff entry.
diff --git a/google_apis/gcm/engine/connection_handler_impl.cc b/google_apis/gcm/engine/connection_handler_impl.cc
index be2c313b..513220d 100644
--- a/google_apis/gcm/engine/connection_handler_impl.cc
+++ b/google_apis/gcm/engine/connection_handler_impl.cc
@@ -357,18 +357,18 @@ void ConnectionHandlerImpl::OnGotMessageBytes() {
input_stream_->GetState() != SocketInputStream::READY) {
LOG(ERROR) << "Failed to extract protobuf bytes of type "
<< static_cast<unsigned int>(message_tag_);
- protobuf.reset(); // Return a null pointer to denote an error.
- read_callback_.Run(protobuf.Pass());
+ // Reset the connection.
+ connection_callback_.Run(net::ERR_FAILED);
return;
}
{
CodedInputStream coded_input_stream(input_stream_.get());
if (!protobuf->ParsePartialFromCodedStream(&coded_input_stream)) {
- NOTREACHED() << "Unable to parse GCM message of type "
- << static_cast<unsigned int>(message_tag_);
- protobuf.reset(); // Return a null pointer to denote an error.
- read_callback_.Run(protobuf.Pass());
+ LOG(ERROR) << "Unable to parse GCM message of type "
+ << static_cast<unsigned int>(message_tag_);
+ // Reset the connection.
+ connection_callback_.Run(net::ERR_FAILED);
return;
}
}