| 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();
|
|
|