diff options
Diffstat (limited to 'net/socket/tcp_client_socket_libevent.cc')
-rw-r--r-- | net/socket/tcp_client_socket_libevent.cc | 72 |
1 files changed, 49 insertions, 23 deletions
diff --git a/net/socket/tcp_client_socket_libevent.cc b/net/socket/tcp_client_socket_libevent.cc index 8ad5ef3..165c4cd 100644 --- a/net/socket/tcp_client_socket_libevent.cc +++ b/net/socket/tcp_client_socket_libevent.cc @@ -78,6 +78,26 @@ int MapConnectError(int err) { } } +// Given err, an errno from a connect() attempt, returns true if connect() +// should be retried with another address. +bool ShouldTryNextAddress(int err) { + switch (err) { + case EADDRNOTAVAIL: + case EAFNOSUPPORT: + case ECONNREFUSED: + case ECONNRESET: + case EACCES: + case EPERM: + case ENETUNREACH: + case EHOSTUNREACH: + case ENETDOWN: + case ETIMEDOUT: + return true; + default: + return false; + } +} + } // namespace //----------------------------------------------------------------------------- @@ -105,30 +125,42 @@ int TCPClientSocketLibevent::Connect(CompletionCallback* callback) { DCHECK(!waiting_connect_); TRACE_EVENT_BEGIN("socket.connect", this, ""); - const addrinfo* ai = current_ai_; - DCHECK(ai); - int rv = CreateSocket(ai); - if (rv != OK) - return rv; + while (true) { + DCHECK(current_ai_); - if (!HANDLE_EINTR(connect(socket_, ai->ai_addr, - static_cast<int>(ai->ai_addrlen)))) { - TRACE_EVENT_END("socket.connect", this, ""); - // Connected without waiting! - return OK; - } + int rv = CreateSocket(current_ai_); + if (rv != OK) + return rv; - // Synchronous operation not supported - DCHECK(callback); + if (!HANDLE_EINTR(connect(socket_, current_ai_->ai_addr, + static_cast<int>(current_ai_->ai_addrlen)))) { + TRACE_EVENT_END("socket.connect", this, ""); + // Connected without waiting! + return OK; + } + + int error_code = errno; + if (error_code == EINPROGRESS) + break; - if (errno != EINPROGRESS) { - DLOG(INFO) << "connect failed: " << errno; close(socket_); socket_ = kInvalidSocket; - return MapConnectError(errno); + + if (current_ai_->ai_next && ShouldTryNextAddress(error_code)) { + // connect() can fail synchronously for an address even on a + // non-blocking socket. As an example, this can happen when there is + // no route to the host. Retry using the next address in the list. + current_ai_ = current_ai_->ai_next; + } else { + DLOG(INFO) << "connect failed: " << error_code; + return MapConnectError(error_code); + } } + // Synchronous operation not supported + DCHECK(callback); + // Initialize write_socket_watcher_ and link it to our MessagePump. // POLLOUT is set if the connection is established. // POLLIN is set if the connection fails. @@ -324,13 +356,7 @@ void TCPClientSocketLibevent::DidCompleteConnect() { if (error_code == EINPROGRESS || error_code == EALREADY) { NOTREACHED(); // This indicates a bug in libevent or our code. result = ERR_IO_PENDING; - } else if (current_ai_->ai_next && ( - error_code == EADDRNOTAVAIL || - error_code == EAFNOSUPPORT || - error_code == ECONNREFUSED || - error_code == ENETUNREACH || - error_code == EHOSTUNREACH || - error_code == ETIMEDOUT)) { + } else if (current_ai_->ai_next && ShouldTryNextAddress(error_code)) { // This address failed, try next one in list. const addrinfo* next = current_ai_->ai_next; Disconnect(); |