|
19 | 19 |
|
20 | 20 | #include <thrift/thrift-config.h> |
21 | 21 |
|
| 22 | +#include <cerrno> |
| 23 | +#include <chrono> |
22 | 24 | #include <cstring> |
23 | 25 | #include <memory> |
24 | 26 | #include <stdexcept> |
25 | 27 | #include <sys/types.h> |
| 28 | +#include <thread> |
26 | 29 | #ifdef HAVE_SYS_SOCKET_H |
27 | 30 | #include <sys/socket.h> |
28 | 31 | #endif |
|
52 | 55 |
|
53 | 56 | #include <thrift/transport/PlatformSocket.h> |
54 | 57 | #include <thrift/transport/TServerSocket.h> |
| 58 | +#include <thrift/transport/TServerSocketErrors.h> |
55 | 59 | #include <thrift/transport/TSocket.h> |
56 | 60 | #include <thrift/transport/TSocketUtils.h> |
57 | 61 | #include <thrift/transport/SocketCommon.h> |
@@ -648,62 +652,94 @@ shared_ptr<TTransport> TServerSocket::acceptImpl() { |
648 | 652 | struct THRIFT_POLLFD fds[2]; |
649 | 653 |
|
650 | 654 | int maxEintrs = 5; |
651 | | - int numEintrs = 0; |
652 | 655 |
|
653 | | - while (true) { |
654 | | - std::memset(fds, 0, sizeof(fds)); |
655 | | - fds[0].fd = serverSocket_; |
656 | | - fds[0].events = THRIFT_POLLIN; |
657 | | - if (interruptSockReader_ != THRIFT_INVALID_SOCKET) { |
658 | | - fds[1].fd = interruptSockReader_; |
659 | | - fds[1].events = THRIFT_POLLIN; |
660 | | - } |
661 | | - /* |
662 | | - TODO: if THRIFT_EINTR is received, we'll restart the timeout. |
663 | | - To be accurate, we need to fix this in the future. |
664 | | - */ |
665 | | - int ret = THRIFT_POLL(fds, 2, accTimeout_); |
666 | | - |
667 | | - if (ret < 0) { |
668 | | - // error cases |
669 | | - if (THRIFT_GET_SOCKET_ERROR == THRIFT_EINTR && (numEintrs++ < maxEintrs)) { |
670 | | - // THRIFT_EINTR needs to be handled manually and we can tolerate |
671 | | - // a certain number |
672 | | - continue; |
| 656 | + struct sockaddr_storage clientAddress; |
| 657 | + int size = 0; |
| 658 | + THRIFT_SOCKET clientSocket = THRIFT_INVALID_SOCKET; |
| 659 | + int backoffMs = 0; |
| 660 | + |
| 661 | + while (clientSocket == THRIFT_INVALID_SOCKET) { |
| 662 | + int numEintrs = 0; |
| 663 | + while (true) { |
| 664 | + std::memset(fds, 0, sizeof(fds)); |
| 665 | + fds[0].fd = serverSocket_; |
| 666 | + fds[0].events = THRIFT_POLLIN; |
| 667 | + if (interruptSockReader_ != THRIFT_INVALID_SOCKET) { |
| 668 | + fds[1].fd = interruptSockReader_; |
| 669 | + fds[1].events = THRIFT_POLLIN; |
673 | 670 | } |
674 | | - int errno_copy = THRIFT_GET_SOCKET_ERROR; |
675 | | - TOutput::instance().perror("TServerSocket::acceptImpl() THRIFT_POLL() ", errno_copy); |
676 | | - throw TTransportException(TTransportException::UNKNOWN, "Unknown", errno_copy); |
677 | | - } else if (ret > 0) { |
678 | | - // Check for an interrupt signal |
679 | | - if (interruptSockReader_ != THRIFT_INVALID_SOCKET && (fds[1].revents & THRIFT_POLLIN)) { |
680 | | - int8_t buf; |
681 | | - if (-1 == recv(interruptSockReader_, cast_sockopt(&buf), sizeof(int8_t), 0)) { |
682 | | - TOutput::instance().perror("TServerSocket::acceptImpl() recv() interrupt ", |
683 | | - THRIFT_GET_SOCKET_ERROR); |
| 671 | + /* |
| 672 | + TODO: if THRIFT_EINTR is received, we'll restart the timeout. |
| 673 | + To be accurate, we need to fix this in the future. |
| 674 | + */ |
| 675 | + int ret = THRIFT_POLL(fds, 2, accTimeout_); |
| 676 | + |
| 677 | + if (ret < 0) { |
| 678 | + // error cases |
| 679 | + if (THRIFT_GET_SOCKET_ERROR == THRIFT_EINTR && (numEintrs++ < maxEintrs)) { |
| 680 | + // THRIFT_EINTR needs to be handled manually and we can tolerate |
| 681 | + // a certain number |
| 682 | + continue; |
| 683 | + } |
| 684 | + int errno_copy = THRIFT_GET_SOCKET_ERROR; |
| 685 | + TOutput::instance().perror("TServerSocket::acceptImpl() THRIFT_POLL() ", errno_copy); |
| 686 | + throw TTransportException(TTransportException::UNKNOWN, "Unknown", errno_copy); |
| 687 | + } else if (ret > 0) { |
| 688 | + // Check for an interrupt signal |
| 689 | + if (interruptSockReader_ != THRIFT_INVALID_SOCKET && (fds[1].revents & THRIFT_POLLIN)) { |
| 690 | + int8_t buf; |
| 691 | + if (-1 == recv(interruptSockReader_, cast_sockopt(&buf), sizeof(int8_t), 0)) { |
| 692 | + TOutput::instance().perror("TServerSocket::acceptImpl() recv() interrupt ", |
| 693 | + THRIFT_GET_SOCKET_ERROR); |
| 694 | + } |
| 695 | + throw TTransportException(TTransportException::INTERRUPTED); |
684 | 696 | } |
685 | | - throw TTransportException(TTransportException::INTERRUPTED); |
686 | | - } |
687 | 697 |
|
688 | | - // Check for the actual server socket being ready |
689 | | - if (fds[0].revents & THRIFT_POLLIN) { |
690 | | - break; |
| 698 | + // Check for the actual server socket being ready |
| 699 | + if (fds[0].revents & THRIFT_POLLIN) { |
| 700 | + break; |
| 701 | + } |
| 702 | + } else { |
| 703 | + TOutput::instance()("TServerSocket::acceptImpl() THRIFT_POLL 0"); |
| 704 | + throw TTransportException(TTransportException::UNKNOWN); |
691 | 705 | } |
692 | | - } else { |
693 | | - TOutput::instance()("TServerSocket::acceptImpl() THRIFT_POLL 0"); |
694 | | - throw TTransportException(TTransportException::UNKNOWN); |
695 | 706 | } |
696 | | - } |
697 | 707 |
|
698 | | - struct sockaddr_storage clientAddress; |
699 | | - int size = sizeof(clientAddress); |
700 | | - THRIFT_SOCKET clientSocket |
701 | | - = ::accept(serverSocket_, (struct sockaddr*)&clientAddress, (socklen_t*)&size); |
| 708 | + size = sizeof(clientAddress); |
| 709 | + clientSocket = ::accept(serverSocket_, (struct sockaddr*)&clientAddress, (socklen_t*)&size); |
| 710 | + if (clientSocket != THRIFT_INVALID_SOCKET) { |
| 711 | + break; |
| 712 | + } |
702 | 713 |
|
703 | | - if (clientSocket == THRIFT_INVALID_SOCKET) { |
704 | 714 | int errno_copy = THRIFT_GET_SOCKET_ERROR; |
705 | | - TOutput::instance().perror("TServerSocket::acceptImpl() ::accept() ", errno_copy); |
706 | | - throw TTransportException(TTransportException::UNKNOWN, "accept()", errno_copy); |
| 715 | + bool connectionError = detail::isConnectionError(errno_copy); |
| 716 | + if (!connectionError && !detail::isResourceExhaustion(errno_copy)) { |
| 717 | + TOutput::instance().perror("TServerSocket::acceptImpl() ::accept() ", errno_copy); |
| 718 | + throw TTransportException(TTransportException::UNKNOWN, "accept()", errno_copy); |
| 719 | + } |
| 720 | + |
| 721 | + // Rate-limited: a peer can trigger connection errors at will. |
| 722 | + std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); |
| 723 | + if (now >= nextAcceptErrorLog_) { |
| 724 | + nextAcceptErrorLog_ = now + std::chrono::minutes(1); |
| 725 | + TOutput::instance().perror("TServerSocket::acceptImpl() ::accept() retrying ", errno_copy); |
| 726 | + } |
| 727 | + if (connectionError) { |
| 728 | + continue; |
| 729 | + } |
| 730 | + |
| 731 | + // Out of descriptors or memory: back off, stay interruptible. An interrupt is left for the |
| 732 | + // poll above. Each retry re-polls with the full accept timeout. |
| 733 | + backoffMs = detail::nextBackoffMs(backoffMs); |
| 734 | + if (interruptSockReader_ != THRIFT_INVALID_SOCKET) { |
| 735 | + struct THRIFT_POLLFD interruptFd; |
| 736 | + std::memset(&interruptFd, 0, sizeof(interruptFd)); |
| 737 | + interruptFd.fd = interruptSockReader_; |
| 738 | + interruptFd.events = THRIFT_POLLIN; |
| 739 | + THRIFT_POLL(&interruptFd, 1, backoffMs); |
| 740 | + } else { |
| 741 | + std::this_thread::sleep_for(std::chrono::milliseconds(backoffMs)); |
| 742 | + } |
707 | 743 | } |
708 | 744 |
|
709 | 745 | // Make sure client socket is blocking |
|
0 commit comments