From 7b9ca917061470268bf3395c8925d4b9cc52d8e1 Mon Sep 17 00:00:00 2001 From: Kristian Monsen Date: Thu, 10 Mar 2011 23:11:26 +0000 Subject: Fix for bug 3491968 Browser frozen on dunkindonuts.com If a sync request returns ERR_IO_PENDING it will block and never complete. Waiting for connect() to complete with select(). If it doesn't complete within 20 seconds, return an error. Change-Id: Ie5859b482dd64248036c2ad9ef57e63d468db2d4 --- net/http/http_proxy_client_socket.cc | 19 +++++++++++++++++-- net/http/http_proxy_client_socket.h | 6 +++++- net/http/http_proxy_client_socket_pool.cc | 18 ++++++++++++++++-- net/socket/client_socket.h | 6 +++++- net/socket/socks5_client_socket.cc | 9 ++++++++- net/socket/socks5_client_socket.h | 6 +++++- net/socket/socks_client_socket.cc | 9 ++++++++- net/socket/socks_client_socket.h | 6 +++++- net/socket/socks_client_socket_pool.cc | 6 +++++- net/socket/ssl_client_socket_mac.cc | 10 +++++++++- net/socket/ssl_client_socket_mac.h | 6 +++++- net/socket/ssl_client_socket_nss.cc | 10 +++++++++- net/socket/ssl_client_socket_nss.h | 6 +++++- net/socket/ssl_client_socket_openssl.cc | 10 +++++++++- net/socket/ssl_client_socket_openssl.h | 6 +++++- net/socket/ssl_client_socket_pool.cc | 6 +++++- net/socket/ssl_client_socket_win.cc | 10 +++++++++- net/socket/ssl_client_socket_win.h | 6 +++++- net/socket/tcp_client_socket_libevent.cc | 28 ++++++++++++++++++++++++++-- net/socket/tcp_client_socket_libevent.h | 11 ++++++++++- net/socket/tcp_client_socket_pool.cc | 6 +++++- net/socket/tcp_client_socket_win.cc | 10 +++++++++- net/socket/tcp_client_socket_win.h | 6 +++++- net/spdy/spdy_proxy_client_socket.cc | 10 +++++++++- net/spdy/spdy_proxy_client_socket.h | 4 ++++ 25 files changed, 203 insertions(+), 27 deletions(-) diff --git a/net/http/http_proxy_client_socket.cc b/net/http/http_proxy_client_socket.cc index 1e6b713..1ff325c 100644 --- a/net/http/http_proxy_client_socket.cc +++ b/net/http/http_proxy_client_socket.cc @@ -59,7 +59,15 @@ HttpProxyClientSocket::~HttpProxyClientSocket() { Disconnect(); } -int HttpProxyClientSocket::Connect(CompletionCallback* callback) { +#ifdef ANDROID +// TODO(kristianm): handle the case when wait_for_connect is true +// (sync requests) +#endif +int HttpProxyClientSocket::Connect(CompletionCallback* callback +#ifdef ANDROID + , bool wait_for_connect +#endif + ) { DCHECK(transport_.get()); DCHECK(transport_->socket()); DCHECK(!user_callback_); @@ -436,9 +444,16 @@ int HttpProxyClientSocket::DoDrainBodyComplete(int result) { return OK; } +#ifdef ANDROID +// TODO(kristianm): Check if we can find out if Connect should block +#endif int HttpProxyClientSocket::DoTCPRestart() { next_state_ = STATE_TCP_RESTART_COMPLETE; - return transport_->socket()->Connect(&io_callback_); + return transport_->socket()->Connect(&io_callback_ +#ifdef ANDROID + , false +#endif + ); } int HttpProxyClientSocket::DoTCPRestartComplete(int result) { diff --git a/net/http/http_proxy_client_socket.h b/net/http/http_proxy_client_socket.h index e11e731..335f930 100644 --- a/net/http/http_proxy_client_socket.h +++ b/net/http/http_proxy_client_socket.h @@ -70,7 +70,11 @@ class HttpProxyClientSocket : public ClientSocket { // ClientSocket methods: // Authenticates to the Http Proxy and then passes data freely. - virtual int Connect(CompletionCallback* callback); + virtual int Connect(CompletionCallback* callback +#ifdef ANDROID + , bool wait_for_connect +#endif + ); virtual void Disconnect(); virtual bool IsConnected() const; virtual bool IsConnectedAndIdle() const; diff --git a/net/http/http_proxy_client_socket_pool.cc b/net/http/http_proxy_client_socket_pool.cc index 475c7d3..119321a 100644 --- a/net/http/http_proxy_client_socket_pool.cc +++ b/net/http/http_proxy_client_socket_pool.cc @@ -293,6 +293,9 @@ int HttpProxyConnectJob::DoSpdyProxyCreateStream() { &callback_); } +#ifdef ANDROID +// TODO(kristianm): Find out if Connect should block +#endif int HttpProxyConnectJob::DoSpdyProxyCreateStreamComplete(int result) { if (result < 0) return result; @@ -306,9 +309,16 @@ int HttpProxyConnectJob::DoSpdyProxyCreateStreamComplete(int result) { params_->destination().host_port_pair(), params_->http_auth_cache(), params_->http_auth_handler_factory())); - return transport_socket_->Connect(&callback_); + return transport_socket_->Connect(&callback_ +#ifdef ANDROID + , false +#endif + ); } +#ifdef ANDROID +// TODO(kristianm): Find out if Connect should block +#endif int HttpProxyConnectJob::DoHttpProxyConnect() { next_state_ = STATE_HTTP_PROXY_CONNECT_COMPLETE; const HostResolver::RequestInfo& tcp_destination = params_->destination(); @@ -325,7 +335,11 @@ int HttpProxyConnectJob::DoHttpProxyConnect() { params_->http_auth_handler_factory(), params_->tunnel(), using_spdy_)); - return transport_socket_->Connect(&callback_); + return transport_socket_->Connect(&callback_ +#ifdef ANDROID + , false +#endif + ); } int HttpProxyConnectJob::DoHttpProxyConnectComplete(int result) { diff --git a/net/socket/client_socket.h b/net/socket/client_socket.h index 358716c..18dd4a8 100644 --- a/net/socket/client_socket.h +++ b/net/socket/client_socket.h @@ -31,7 +31,11 @@ class ClientSocket : public Socket { // // Connect may also be called again after a call to the Disconnect method. // - virtual int Connect(CompletionCallback* callback) = 0; + virtual int Connect(CompletionCallback* callback +#ifdef ANDROID + , bool wait_for_connect +#endif + ) = 0; // Called to disconnect a socket. Does nothing if the socket is already // disconnected. After calling Disconnect it is possible to call Connect diff --git a/net/socket/socks5_client_socket.cc b/net/socket/socks5_client_socket.cc index f73b558..c6600a3 100644 --- a/net/socket/socks5_client_socket.cc +++ b/net/socket/socks5_client_socket.cc @@ -64,7 +64,14 @@ SOCKS5ClientSocket::~SOCKS5ClientSocket() { Disconnect(); } -int SOCKS5ClientSocket::Connect(CompletionCallback* callback) { +#ifdef ANDROID +// TODO(kristianm): Find out if Connect should block +#endif +int SOCKS5ClientSocket::Connect(CompletionCallback* callback +#ifdef ANDROID + , bool wait_for_connect +#endif + ) { DCHECK(transport_.get()); DCHECK(transport_->socket()); DCHECK_EQ(STATE_NONE, next_state_); diff --git a/net/socket/socks5_client_socket.h b/net/socket/socks5_client_socket.h index 5a918cc..36facda 100644 --- a/net/socket/socks5_client_socket.h +++ b/net/socket/socks5_client_socket.h @@ -51,7 +51,11 @@ class SOCKS5ClientSocket : public ClientSocket { // ClientSocket methods: // Does the SOCKS handshake and completes the protocol. - virtual int Connect(CompletionCallback* callback); + virtual int Connect(CompletionCallback* callback +#ifdef ANDROID + , bool wait_for_connect +#endif + ); virtual void Disconnect(); virtual bool IsConnected() const; virtual bool IsConnectedAndIdle() const; diff --git a/net/socket/socks_client_socket.cc b/net/socket/socks_client_socket.cc index a4a7391..fe3c015 100644 --- a/net/socket/socks_client_socket.cc +++ b/net/socket/socks_client_socket.cc @@ -98,7 +98,14 @@ SOCKSClientSocket::~SOCKSClientSocket() { Disconnect(); } -int SOCKSClientSocket::Connect(CompletionCallback* callback) { +#ifdef ANDROID +// TODO(kristianm): find out if Connect should block +#endif +int SOCKSClientSocket::Connect(CompletionCallback* callback +#ifdef ANDROID + , bool wait_for_connect +#endif + ) { DCHECK(transport_.get()); DCHECK(transport_->socket()); DCHECK_EQ(STATE_NONE, next_state_); diff --git a/net/socket/socks_client_socket.h b/net/socket/socks_client_socket.h index 86511cf..653694e 100644 --- a/net/socket/socks_client_socket.h +++ b/net/socket/socks_client_socket.h @@ -48,7 +48,11 @@ class SOCKSClientSocket : public ClientSocket { // ClientSocket methods: // Does the SOCKS handshake and completes the protocol. - virtual int Connect(CompletionCallback* callback); + virtual int Connect(CompletionCallback* callback +#ifdef ANDROID + , bool wait_for_connect +#endif + ); virtual void Disconnect(); virtual bool IsConnected() const; virtual bool IsConnectedAndIdle() const; diff --git a/net/socket/socks_client_socket_pool.cc b/net/socket/socks_client_socket_pool.cc index a6d34ec..5b419ba 100644 --- a/net/socket/socks_client_socket_pool.cc +++ b/net/socket/socks_client_socket_pool.cc @@ -157,7 +157,11 @@ int SOCKSConnectJob::DoSOCKSConnect() { socks_params_->destination(), resolver_)); } - return socket_->Connect(&callback_); + return socket_->Connect(&callback_ +#ifdef ANDROID + , socks_params_->ignore_limits() +#endif + ); } int SOCKSConnectJob::DoSOCKSConnectComplete(int result) { diff --git a/net/socket/ssl_client_socket_mac.cc b/net/socket/ssl_client_socket_mac.cc index f10dd34..8b45592 100644 --- a/net/socket/ssl_client_socket_mac.cc +++ b/net/socket/ssl_client_socket_mac.cc @@ -571,7 +571,15 @@ SSLClientSocketMac::~SSLClientSocketMac() { Disconnect(); } -int SSLClientSocketMac::Connect(CompletionCallback* callback) { +#ifdef ANDROID +// TODO(kristianm): handle the case when wait_for_connect is true +// (sync requests) +#endif +int SSLClientSocketMac::Connect(CompletionCallback* callback +#ifdef ANDROID + , bool wait_for_connect +#endif + ) { DCHECK(transport_.get()); DCHECK(next_handshake_state_ == STATE_NONE); DCHECK(!user_connect_callback_); diff --git a/net/socket/ssl_client_socket_mac.h b/net/socket/ssl_client_socket_mac.h index e84bee4..1e8d888 100644 --- a/net/socket/ssl_client_socket_mac.h +++ b/net/socket/ssl_client_socket_mac.h @@ -44,7 +44,11 @@ class SSLClientSocketMac : public SSLClientSocket { virtual NextProtoStatus GetNextProto(std::string* proto); // ClientSocket methods: - virtual int Connect(CompletionCallback* callback); + virtual int Connect(CompletionCallback* callback +#ifdef ANDROID + , bool wait_for_connect +#endif + ); virtual void Disconnect(); virtual bool IsConnected() const; virtual bool IsConnectedAndIdle() const; diff --git a/net/socket/ssl_client_socket_nss.cc b/net/socket/ssl_client_socket_nss.cc index 1594490..8a9d336 100644 --- a/net/socket/ssl_client_socket_nss.cc +++ b/net/socket/ssl_client_socket_nss.cc @@ -601,7 +601,15 @@ void SSLClientSocketNSS::UncorkAfterTimeout() { } while (nsent > 0); } -int SSLClientSocketNSS::Connect(CompletionCallback* callback) { +#ifdef ANDROID +// TODO(kristianm): handle the case when wait_for_connect is true +// (sync requests) +#endif +int SSLClientSocketNSS::Connect(CompletionCallback* callback +#ifdef ANDROID + , bool wait_for_connect +#endif + ) { EnterFunction(""); DCHECK(transport_.get()); DCHECK(next_handshake_state_ == STATE_NONE); diff --git a/net/socket/ssl_client_socket_nss.h b/net/socket/ssl_client_socket_nss.h index 7743097..6f291b3 100644 --- a/net/socket/ssl_client_socket_nss.h +++ b/net/socket/ssl_client_socket_nss.h @@ -58,7 +58,11 @@ class SSLClientSocketNSS : public SSLClientSocket { virtual void UseDNSSEC(DNSSECProvider* provider); // ClientSocket methods: - virtual int Connect(CompletionCallback* callback); + virtual int Connect(CompletionCallback* callback +#ifdef ANDROID + , bool wait_for_connect +#endif + ); virtual void Disconnect(); virtual bool IsConnected() const; virtual bool IsConnectedAndIdle() const; diff --git a/net/socket/ssl_client_socket_openssl.cc b/net/socket/ssl_client_socket_openssl.cc index b10c402..f80b21c 100644 --- a/net/socket/ssl_client_socket_openssl.cc +++ b/net/socket/ssl_client_socket_openssl.cc @@ -414,7 +414,15 @@ void SSLClientSocketOpenSSL::DoWriteCallback(int rv) { // ClientSocket methods -int SSLClientSocketOpenSSL::Connect(CompletionCallback* callback) { +#ifdef ANDROID +// TODO(kristianm): handle the case when wait_for_connect is true +// (sync requests) +#endif +int SSLClientSocketOpenSSL::Connect(CompletionCallback* callback +#ifdef ANDROID + , bool wait_for_connect +#endif + ) { net_log_.BeginEvent(NetLog::TYPE_SSL_CONNECT, NULL); // Set up new ssl object. diff --git a/net/socket/ssl_client_socket_openssl.h b/net/socket/ssl_client_socket_openssl.h index 743a169..c8f5d81 100644 --- a/net/socket/ssl_client_socket_openssl.h +++ b/net/socket/ssl_client_socket_openssl.h @@ -50,7 +50,11 @@ class SSLClientSocketOpenSSL : public SSLClientSocket { virtual NextProtoStatus GetNextProto(std::string* proto); // ClientSocket methods: - virtual int Connect(CompletionCallback* callback); + virtual int Connect(CompletionCallback* callback +#ifdef ANDROID + , bool wait_for_connect +#endif + ); virtual void Disconnect(); virtual bool IsConnected() const; virtual bool IsConnectedAndIdle() const; diff --git a/net/socket/ssl_client_socket_pool.cc b/net/socket/ssl_client_socket_pool.cc index 3911502..54e5152 100644 --- a/net/socket/ssl_client_socket_pool.cc +++ b/net/socket/ssl_client_socket_pool.cc @@ -299,7 +299,11 @@ int SSLConnectJob::DoSSLConnect() { ssl_socket_.reset(client_socket_factory_->CreateSSLClientSocket( transport_socket_handle_.release(), params_->host_and_port(), params_->ssl_config(), ssl_host_info_.release(), dns_cert_checker_)); - return ssl_socket_->Connect(&callback_); + return ssl_socket_->Connect(&callback_ +#ifdef ANDROID + , params_->ignore_limits() +#endif + ); } int SSLConnectJob::DoSSLConnectComplete(int result) { diff --git a/net/socket/ssl_client_socket_win.cc b/net/socket/ssl_client_socket_win.cc index 1faeb7a..72d7492 100644 --- a/net/socket/ssl_client_socket_win.cc +++ b/net/socket/ssl_client_socket_win.cc @@ -534,7 +534,15 @@ SSLClientSocketWin::GetNextProto(std::string* proto) { return kNextProtoUnsupported; } -int SSLClientSocketWin::Connect(CompletionCallback* callback) { +#ifdef ANDROID +// TODO(kristianm): handle the case when wait_for_connect is true +// (sync requests) +#endif +int SSLClientSocketWin::Connect(CompletionCallback* callback +#ifdef ANDROID + , bool wait_for_connect +#endif + ) { DCHECK(transport_.get()); DCHECK(next_state_ == STATE_NONE); DCHECK(!user_connect_callback_); diff --git a/net/socket/ssl_client_socket_win.h b/net/socket/ssl_client_socket_win.h index 61c67f0..11d28a2 100644 --- a/net/socket/ssl_client_socket_win.h +++ b/net/socket/ssl_client_socket_win.h @@ -49,7 +49,11 @@ class SSLClientSocketWin : public SSLClientSocket { virtual NextProtoStatus GetNextProto(std::string* proto); // ClientSocket methods: - virtual int Connect(CompletionCallback* callback); + virtual int Connect(CompletionCallback* callback +#ifdef ANDROID + , bool wait_for_connect +#endif + ); virtual void Disconnect(); virtual bool IsConnected() const; virtual bool IsConnectedAndIdle() const; diff --git a/net/socket/tcp_client_socket_libevent.cc b/net/socket/tcp_client_socket_libevent.cc index a3fb281..e825706 100644 --- a/net/socket/tcp_client_socket_libevent.cc +++ b/net/socket/tcp_client_socket_libevent.cc @@ -125,7 +125,11 @@ TCPClientSocketLibevent::TCPClientSocketLibevent( net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_SOCKET)), previously_disconnected_(false), use_tcp_fastopen_(false), - tcp_fastopen_connected_(false) { + tcp_fastopen_connected_(false) +#ifdef ANDROID + , wait_for_connect_(false) +#endif +{ scoped_refptr params; if (source.is_valid()) params = new NetLogSourceParameter("source_dependency", source); @@ -152,7 +156,14 @@ void TCPClientSocketLibevent::AdoptSocket(int socket) { use_history_.set_was_ever_connected(); } -int TCPClientSocketLibevent::Connect(CompletionCallback* callback) { +int TCPClientSocketLibevent::Connect(CompletionCallback* callback +#ifdef ANDROID + , bool wait_for_connect +#endif + ) { +#ifdef ANDROID + wait_for_connect_ = wait_for_connect; +#endif DCHECK(CalledOnValidThread()); // If already connected, then just return OK. @@ -237,6 +248,19 @@ int TCPClientSocketLibevent::DoConnect() { static_cast(current_ai_->ai_addrlen)))) { // Connected without waiting! return OK; +#ifdef ANDROID + } else if (errno == EINPROGRESS && wait_for_connect_) { + fd_set writeset; + FD_ZERO(&writeset); + FD_SET(socket_, &writeset); + timeval tv; + tv.tv_sec = 20; + tv.tv_usec = 0; + int res = HANDLE_EINTR(select(socket_ + 1, 0, &writeset, 0, &tv)); + if (res > 0) + return OK; + return MapConnectError(ETIMEDOUT); +#endif } } else { // With TCP FastOpen, we pretend that the socket is connected. diff --git a/net/socket/tcp_client_socket_libevent.h b/net/socket/tcp_client_socket_libevent.h index a89c5b3..b5e4f55 100644 --- a/net/socket/tcp_client_socket_libevent.h +++ b/net/socket/tcp_client_socket_libevent.h @@ -40,7 +40,11 @@ class TCPClientSocketLibevent : public ClientSocket, NonThreadSafe { void AdoptSocket(int socket); // ClientSocket methods: - virtual int Connect(CompletionCallback* callback); + virtual int Connect(CompletionCallback* callback +#ifdef ANDROID + , bool wait_for_connect +#endif + ); virtual void Disconnect(); virtual bool IsConnected() const; virtual bool IsConnectedAndIdle() const; @@ -191,6 +195,11 @@ class TCPClientSocketLibevent : public ClientSocket, NonThreadSafe { // True when TCP FastOpen is in use and we have done the connect. bool tcp_fastopen_connected_; +#ifdef ANDROID + // True if connect should block and not return before the socket is connected + bool wait_for_connect_; + +#endif DISALLOW_COPY_AND_ASSIGN(TCPClientSocketLibevent); }; diff --git a/net/socket/tcp_client_socket_pool.cc b/net/socket/tcp_client_socket_pool.cc index 49155dd..10acc76 100644 --- a/net/socket/tcp_client_socket_pool.cc +++ b/net/socket/tcp_client_socket_pool.cc @@ -154,7 +154,11 @@ int TCPConnectJob::DoTCPConnect() { set_socket(client_socket_factory_->CreateTCPClientSocket( addresses_, net_log().net_log(), net_log().source())); connect_start_time_ = base::TimeTicks::Now(); - return socket()->Connect(&callback_); + return socket()->Connect(&callback_, +#ifdef ANDROID + params_->ignore_limits() +#endif + ); } int TCPConnectJob::DoTCPConnectComplete(int result) { diff --git a/net/socket/tcp_client_socket_win.cc b/net/socket/tcp_client_socket_win.cc index a1d99e6..6624a5e 100644 --- a/net/socket/tcp_client_socket_win.cc +++ b/net/socket/tcp_client_socket_win.cc @@ -315,7 +315,15 @@ void TCPClientSocketWin::AdoptSocket(SOCKET socket) { use_history_.set_was_ever_connected(); } -int TCPClientSocketWin::Connect(CompletionCallback* callback) { +#ifdef ANDROID +// TODO(kristianm): handle the case when wait_for_connect is true +// (sync requests) +#endif +int TCPClientSocketWin::Connect(CompletionCallback* callback +#ifdef ANDROID + , bool wait_for_connect +#endif + ) { DCHECK(CalledOnValidThread()); // If already connected, then just return OK. diff --git a/net/socket/tcp_client_socket_win.h b/net/socket/tcp_client_socket_win.h index fb111b4..bfd6a93 100644 --- a/net/socket/tcp_client_socket_win.h +++ b/net/socket/tcp_client_socket_win.h @@ -37,7 +37,11 @@ class TCPClientSocketWin : public ClientSocket, NonThreadSafe { void AdoptSocket(SOCKET socket); // ClientSocket methods: - virtual int Connect(CompletionCallback* callback); + virtual int Connect(CompletionCallback* callback +#ifdef ANDROID + , bool wait_for_connect +#endif + ); virtual void Disconnect(); virtual bool IsConnected() const; virtual bool IsConnectedAndIdle() const; diff --git a/net/spdy/spdy_proxy_client_socket.cc b/net/spdy/spdy_proxy_client_socket.cc index 1cddb5e..62816d2 100644 --- a/net/spdy/spdy_proxy_client_socket.cc +++ b/net/spdy/spdy_proxy_client_socket.cc @@ -69,7 +69,15 @@ SpdyProxyClientSocket::~SpdyProxyClientSocket() { // by creating a new stream for the subsequent request. // TODO(rch): create a more appropriate error code to disambiguate // the HTTPS Proxy tunnel failure from an HTTP Proxy tunnel failure. -int SpdyProxyClientSocket::Connect(CompletionCallback* callback) { +#ifdef ANDROID +// TODO(kristianm): handle the case when wait_for_connect is true +// (sync requests) +#endif +int SpdyProxyClientSocket::Connect(CompletionCallback* callback +#ifdef ANDROID + , bool wait_for_connect +#endif + ) { DCHECK(!read_callback_); if (next_state_ == STATE_OPEN) return OK; diff --git a/net/spdy/spdy_proxy_client_socket.h b/net/spdy/spdy_proxy_client_socket.h index 5f2dd6f..213cac4 100644 --- a/net/spdy/spdy_proxy_client_socket.h +++ b/net/spdy/spdy_proxy_client_socket.h @@ -62,7 +62,11 @@ class SpdyProxyClientSocket : public ClientSocket, public SpdyStream::Delegate { } // ClientSocket methods: +#ifdef ANDROID + virtual int Connect(CompletionCallback* callback, bool wait_for_connect); +#else virtual int Connect(CompletionCallback* callback); +#endif virtual void Disconnect(); virtual bool IsConnected() const; virtual bool IsConnectedAndIdle() const; -- cgit v1.1