in folly/io/async/AsyncSocket.cpp [831:1010]
void AsyncSocket::connect(
ConnectCallback* callback,
const folly::SocketAddress& address,
int timeout,
const SocketOptionMap& options,
const folly::SocketAddress& bindAddr,
const std::string& ifName) noexcept {
DestructorGuard dg(this);
eventBase_->dcheckIsInEventBaseThread();
addr_ = address;
// Make sure we're in the uninitialized state
if (state_ != StateEnum::UNINIT) {
return invalidState(callback);
}
connectTimeout_ = std::chrono::milliseconds(timeout);
connectStartTime_ = std::chrono::steady_clock::now();
// Make connect end time at least >= connectStartTime.
connectEndTime_ = connectStartTime_;
assert(fd_ == NetworkSocket());
state_ = StateEnum::CONNECTING;
connectCallback_ = callback;
invokeConnectAttempt();
sockaddr_storage addrStorage;
auto saddr = reinterpret_cast<sockaddr*>(&addrStorage);
try {
// Create the socket
// Technically the first parameter should actually be a protocol family
// constant (PF_xxx) rather than an address family (AF_xxx), but the
// distinction is mainly just historical. In pretty much all
// implementations the PF_foo and AF_foo constants are identical.
fd_ = netops_->socket(address.getFamily(), SOCK_STREAM, 0);
if (fd_ == NetworkSocket()) {
auto errnoCopy = errno;
throw AsyncSocketException(
AsyncSocketException::INTERNAL_ERROR,
withAddr("failed to create socket"),
errnoCopy);
}
disableTransparentFunctions(fd_, noTransparentTls_, noTSocks_);
handleNetworkSocketAttached();
setCloseOnExec();
// Put the socket in non-blocking mode
int rv = netops_->set_socket_non_blocking(fd_);
if (rv == -1) {
auto errnoCopy = errno;
throw AsyncSocketException(
AsyncSocketException::INTERNAL_ERROR,
withAddr("failed to put socket in non-blocking mode"),
errnoCopy);
}
#if !defined(MSG_NOSIGNAL) && defined(F_SETNOSIGPIPE)
// iOS and OS X don't support MSG_NOSIGNAL; set F_SETNOSIGPIPE instead
rv = fcntl(fd_.toFd(), F_SETNOSIGPIPE, 1);
if (rv == -1) {
auto errnoCopy = errno;
throw AsyncSocketException(
AsyncSocketException::INTERNAL_ERROR,
"failed to enable F_SETNOSIGPIPE on socket",
errnoCopy);
}
#endif
// By default, turn on TCP_NODELAY
// If setNoDelay() fails, we continue anyway; this isn't a fatal error.
// setNoDelay() will log an error message if it fails.
// Also set the cached zeroCopyVal_ since it cannot be set earlier if the fd
// is not created
if (address.getFamily() != AF_UNIX) {
(void)setNoDelay(true);
setZeroCopy(zeroCopyVal_);
}
// Apply the additional PRE_BIND options if any.
applyOptions(options, SocketOptionKey::ApplyPos::PRE_BIND);
VLOG(5) << "AsyncSocket::connect(this=" << this << ", evb=" << eventBase_
<< ", fd=" << fd_ << ", host=" << address.describe().c_str();
// bind the socket to the interface
#if defined(__linux__)
if (!ifName.empty() &&
netops_->setsockopt(
fd_,
SOL_SOCKET,
SO_BINDTODEVICE,
ifName.c_str(),
ifName.length())) {
auto errnoCopy = errno;
doClose();
throw AsyncSocketException(
AsyncSocketException::NOT_OPEN,
"failed to bind to device: " + ifName,
errnoCopy);
}
#else
(void)ifName;
#endif
// bind the socket
if (bindAddr != anyAddress()) {
int one = 1;
if (netops_->setsockopt(
fd_, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))) {
auto errnoCopy = errno;
doClose();
throw AsyncSocketException(
AsyncSocketException::NOT_OPEN,
"failed to setsockopt prior to bind on " + bindAddr.describe(),
errnoCopy);
}
bindAddr.getAddress(&addrStorage);
if (netops_->bind(fd_, saddr, bindAddr.getActualSize()) != 0) {
auto errnoCopy = errno;
doClose();
throw AsyncSocketException(
AsyncSocketException::NOT_OPEN,
"failed to bind to async socket: " + bindAddr.describe(),
errnoCopy);
}
}
// Apply the additional POST_BIND options if any.
applyOptions(options, SocketOptionKey::ApplyPos::POST_BIND);
// Call preConnect hook if any.
if (connectCallback_) {
connectCallback_->preConnect(fd_);
}
// Perform the connect()
address.getAddress(&addrStorage);
if (tfoEnabled_) {
state_ = StateEnum::FAST_OPEN;
tfoAttempted_ = true;
} else {
if (socketConnect(saddr, addr_.getActualSize()) < 0) {
return;
}
}
// If we're still here the connect() succeeded immediately.
// Fall through to call the callback outside of this try...catch block
} catch (const AsyncSocketException& ex) {
return failConnect(__func__, ex);
} catch (const std::exception& ex) {
// shouldn't happen, but handle it just in case
VLOG(4) << "AsyncSocket::connect(this=" << this << ", fd=" << fd_
<< "): unexpected " << typeid(ex).name()
<< " exception: " << ex.what();
AsyncSocketException tex(
AsyncSocketException::INTERNAL_ERROR,
withAddr(string("unexpected exception: ") + ex.what()));
return failConnect(__func__, tex);
}
// The connection succeeded immediately
// The read callback may not have been set yet, and no writes may be pending
// yet, so we don't have to register for any events at the moment.
VLOG(8) << "AsyncSocket::connect succeeded immediately; this=" << this;
assert(errMessageCallback_ == nullptr);
assert(readAncillaryDataCallback_ == nullptr);
assert(readCallback_ == nullptr);
assert(writeReqHead_ == nullptr);
if (state_ != StateEnum::FAST_OPEN) {
state_ = StateEnum::ESTABLISHED;
}
invokeConnectSuccess();
}