in folly/io/async/AsyncServerSocket.cpp [921:1065]
void AsyncServerSocket::handlerReady(
uint16_t /* events */,
NetworkSocket fd,
sa_family_t addressFamily) noexcept {
assert(!callbacks_.empty());
DestructorGuard dg(this);
// Only accept up to maxAcceptAtOnce_ connections at a time,
// to avoid starving other I/O handlers using this EventBase.
for (uint32_t n = 0; n < maxAcceptAtOnce_; ++n) {
SocketAddress address;
sockaddr_storage addrStorage = {};
socklen_t addrLen = sizeof(addrStorage);
auto saddr = reinterpret_cast<sockaddr*>(&addrStorage);
// In some cases, accept() doesn't seem to update these correctly.
saddr->sa_family = addressFamily;
if (addressFamily == AF_UNIX) {
addrLen = sizeof(struct sockaddr_un);
}
// Accept a new client socket
#if FOLLY_HAVE_ACCEPT4
auto clientSocket = NetworkSocket::fromFd(
accept4(fd.toFd(), saddr, &addrLen, SOCK_NONBLOCK));
#else
auto clientSocket = netops::accept(fd, saddr, &addrLen);
#endif
address.setFromSockaddr(saddr, addrLen);
if (clientSocket != NetworkSocket() && connectionEventCallback_) {
connectionEventCallback_->onConnectionAccepted(clientSocket, address);
}
// Connection accepted, get the SYN packet from the client if
// TOS reflect is enabled
if (kIsLinux && clientSocket != NetworkSocket() && tosReflect_) {
std::array<uint32_t, 64> buffer;
socklen_t len = sizeof(buffer);
int ret = netops::getsockopt(
clientSocket, IPPROTO_TCP, TCP_SAVED_SYN, &buffer, &len);
if (ret == 0) {
uint32_t tosWord = folly::Endian::big(buffer[0]);
if (addressFamily == AF_INET6) {
tosWord = (tosWord & 0x0FC00000) >> 20;
// Set the TOS on the return socket only if it is non-zero
if (tosWord) {
ret = netops::setsockopt(
clientSocket,
IPPROTO_IPV6,
IPV6_TCLASS,
&tosWord,
sizeof(tosWord));
}
} else if (addressFamily == AF_INET) {
tosWord = (tosWord & 0x00FC0000) >> 16;
if (tosWord) {
ret = netops::setsockopt(
clientSocket, IPPROTO_IP, IP_TOS, &tosWord, sizeof(tosWord));
}
}
if (ret != 0) {
LOG(ERROR) << "Unable to set TOS for accepted socket "
<< clientSocket;
}
} else {
LOG(ERROR) << "Unable to get SYN packet for accepted socket "
<< clientSocket;
}
}
std::chrono::time_point<std::chrono::steady_clock> nowMs =
std::chrono::steady_clock::now();
auto timeSinceLastAccept = std::max<int64_t>(
0,
nowMs.time_since_epoch().count() -
lastAccepTimestamp_.time_since_epoch().count());
lastAccepTimestamp_ = nowMs;
if (acceptRate_ < 1) {
acceptRate_ *= 1 + acceptRateAdjustSpeed_ * timeSinceLastAccept;
if (acceptRate_ >= 1) {
acceptRate_ = 1;
} else if (rand() > acceptRate_ * RAND_MAX) {
++numDroppedConnections_;
if (clientSocket != NetworkSocket()) {
closeNoInt(clientSocket);
if (connectionEventCallback_) {
connectionEventCallback_->onConnectionDropped(
clientSocket, address);
}
}
continue;
}
}
if (clientSocket == NetworkSocket()) {
if (errno == EAGAIN) {
// No more sockets to accept right now.
// Check for this code first, since it's the most common.
return;
} else if (errno == EMFILE || errno == ENFILE) {
// We're out of file descriptors. Perhaps we're accepting connections
// too quickly. Pause accepting briefly to back off and give the server
// a chance to recover.
LOG(ERROR) << "accept failed: out of file descriptors; entering accept "
"back-off state";
enterBackoff();
// Dispatch the error message
dispatchError("accept() failed", errno);
} else {
dispatchError("accept() failed", errno);
}
if (connectionEventCallback_) {
connectionEventCallback_->onConnectionAcceptError(errno);
}
return;
}
#if !FOLLY_HAVE_ACCEPT4
// Explicitly set the new connection to non-blocking mode
if (netops::set_socket_non_blocking(clientSocket) != 0) {
closeNoInt(clientSocket);
dispatchError(
"failed to set accepted socket to non-blocking mode", errno);
if (connectionEventCallback_) {
connectionEventCallback_->onConnectionDropped(clientSocket, address);
}
return;
}
#endif
// Inform the callback about the new connection
dispatchSocket(clientSocket, std::move(address));
// If we aren't accepting any more, break out of the loop
if (!accepting_ || callbacks_.empty()) {
break;
}
}
}