Index: net/socket/tcp_client_socket_libevent.cc |
=================================================================== |
--- net/socket/tcp_client_socket_libevent.cc (revision 26002) |
+++ net/socket/tcp_client_socket_libevent.cc (working copy) |
@@ -78,6 +78,26 @@ |
} |
} |
+// 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 @@ |
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; |
+ } |
- if (errno != EINPROGRESS) { |
- DLOG(INFO) << "connect failed: " << errno; |
+ int error_code = errno; |
+ if (error_code == EINPROGRESS) |
+ break; |
+ |
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 @@ |
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(); |