static int connect_with_timeout(int fd, struct sockaddr *sa_ptr, size_t sa_size, double timeout, const HostURL &hosturl, std::string &errstr, int &errnum) { if (timeout <= 0) { int retval = connect(fd, sa_ptr, sa_size); if (retval < 0) { errstr = "unable to connect to " + hosturl.getHostURL(); errnum = errno; } return retval; } // set non-blocking so we can do timeouts long arg = fcntl(fd, F_GETFL, nullptr); fcntl(fd, F_SETFL, arg | O_NONBLOCK); int retval = connect(fd, sa_ptr, sa_size); if (retval < 0) { if (errno == EINPROGRESS) { struct pollfd fds[1]; fds[0].fd = fd; fds[0].events = POLLOUT; if (poll(fds, 1, (int)(timeout * 1000))) { int valopt; socklen_t lon = sizeof(int); getsockopt(fd, SOL_SOCKET, SO_ERROR, (void*)(&valopt), &lon); if (valopt) { errstr = "failed to connect to " + hosturl.getHostURL(); errnum = valopt; } retval = valopt ? -1 : 0; } else { errstr = "timed out after "; errstr += folly::to<std::string>(timeout); errstr += " seconds when connecting to " + hosturl.getHostURL(); errnum = ETIMEDOUT; retval = -1; } } else { errstr = "unable to connect to " + hosturl.getHostURL(); errnum = errno; } } // set to blocking mode arg = fcntl(fd, F_GETFL, nullptr); fcntl(fd, F_SETFL, arg & ~O_NONBLOCK); return retval; }
Variant sockopen_impl(const HostURL &hosturl, VRefParam errnum, VRefParam errstr, double timeout, bool persistent) { errnum = 0; errstr = empty_string(); std::string key; if (persistent) { key = hosturl.getHostURL() + ":" + folly::to<std::string>(hosturl.getPort()); auto sockItr = s_sockets.find(key); if (sockItr != s_sockets.end()) { auto sock = makeSmartPtr<Socket>(sockItr->second); if (sock->getError() == 0 && sock->checkLiveness()) { return Variant(sock); } // socket had an error earlier, we need to close it, remove it from // persistent storage, and create a new one (in that order) sock->close(); s_sockets.erase(sockItr); } } if (timeout < 0) { timeout = ThreadInfo::s_threadInfo.getNoCheck()-> m_reqInjectionData.getSocketDefaultTimeout(); } auto socket = new_socket_connect(hosturl, timeout, errnum, errstr); if (!socket.isResource()) { return false; } if (persistent) { assert(!key.empty()); s_sockets[key] = cast<Socket>(socket)->getData(); assert(s_sockets[key]); } return socket; }
Variant sockopen_impl(const HostURL &hosturl, VRefParam errnum, VRefParam errstr, double timeout, bool persistent) { errnum = 0; errstr = empty_string(); std::string key; if (persistent) { key = hosturl.getHostURL() + ":" + folly::to<std::string>(hosturl.getPort()); Socket *sock = dynamic_cast<Socket*>(g_persistentResources->get("socket", key.c_str())); if (sock) { if (sock->getError() == 0 && sock->checkLiveness()) { return Resource(sock); } // socket had an error earlier, we need to close it, remove it from // persistent storage, and create a new one (in that order) sock->close(); g_persistentResources->remove("socket", key.c_str()); } } if (timeout < 0) { timeout = ThreadInfo::s_threadInfo.getNoCheck()-> m_reqInjectionData.getSocketDefaultTimeout(); } auto socket = new_socket_connect(hosturl, timeout, errnum, errstr); if (!socket.isResource()) { return false; } Resource ret = socket.toResource(); if (persistent) { assert(!key.empty()); g_persistentResources->set("socket", key.c_str(), ret.getTyped<Socket>()); } return ret; }