void FileRegion::FileWriteRequest::FileReadHandler::handlerReady( uint16_t events) noexcept { CHECK(events & EventHandler::WRITE); if (bytesToRead_ == 0) { unregisterHandler(); return; } int flags = SPLICE_F_NONBLOCK | SPLICE_F_MORE; ssize_t spliced = ::splice(req_->readFd_, &req_->offset_, pipe_in_, nullptr, bytesToRead_, flags); if (spliced == -1) { if (errno == EAGAIN) { return; } else { req_->fail(__func__, AsyncSocketException( AsyncSocketException::INTERNAL_ERROR, "splice failed", errno)); return; } } if (spliced > 0) { bytesToRead_ -= spliced; try { req_->queue_.putMessage(static_cast<size_t>(spliced)); } catch (...) { req_->fail(__func__, AsyncSocketException( AsyncSocketException::INTERNAL_ERROR, "putMessage failed")); return; } } }
void AsyncUDPSocket::dontFragment(bool df) { (void)df; // to avoid potential unused variable warning #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DO) && \ defined(IP_PMTUDISC_WANT) if (address().getFamily() == AF_INET) { int v4 = df ? IP_PMTUDISC_DO : IP_PMTUDISC_WANT; if (fsp::setsockopt(fd_, IPPROTO_IP, IP_MTU_DISCOVER, &v4, sizeof(v4))) { throw AsyncSocketException( AsyncSocketException::NOT_OPEN, "Failed to set DF with IP_MTU_DISCOVER", errno); } } #endif #if defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_DO) && \ defined(IPV6_PMTUDISC_WANT) if (address().getFamily() == AF_INET6) { int v6 = df ? IPV6_PMTUDISC_DO : IPV6_PMTUDISC_WANT; if (fsp::setsockopt( fd_, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &v6, sizeof(v6))) { throw AsyncSocketException( AsyncSocketException::NOT_OPEN, "Failed to set DF with IPV6_MTU_DISCOVER", errno); } } #endif }
void FileRegion::FileWriteRequest::start() { started_ = true; readBase_ = readPool.get()->getEventBase(); readBase_->runInEventBaseThread([this]{ auto flags = fcntl(readFd_, F_GETFL); if (flags == -1) { fail(__func__, AsyncSocketException( AsyncSocketException::INTERNAL_ERROR, "fcntl F_GETFL failed", errno)); return; } flags &= O_ACCMODE; if (flags == O_WRONLY) { fail(__func__, AsyncSocketException( AsyncSocketException::BAD_ARGS, "file not open for reading")); return; } #ifndef GLIBC_AT_LEAST_2_9 fail(__func__, AsyncSocketException( AsyncSocketException::NOT_SUPPORTED, "writeFile unsupported on glibc < 2.9")); return; #else int pipeFds[2]; if (::pipe2(pipeFds, O_NONBLOCK) == -1) { fail(__func__, AsyncSocketException( AsyncSocketException::INTERNAL_ERROR, "pipe2 failed", errno)); return; } #ifdef F_SETPIPE_SZ // Max size for unprevileged processes as set in /proc/sys/fs/pipe-max-size // Ignore failures and just roll with it // TODO maybe read max size from /proc? fcntl(pipeFds[0], F_SETPIPE_SZ, 1048576); fcntl(pipeFds[1], F_SETPIPE_SZ, 1048576); #endif pipe_out_ = pipeFds[0]; socket_->getEventBase()->runInEventBaseThreadAndWait([&]{ startConsuming(socket_->getEventBase(), &queue_); }); readHandler_ = folly::make_unique<FileReadHandler>( this, pipeFds[1], count_); #endif }); }
void AsyncUDPSocket::setErrMessageCallback( ErrMessageCallback* errMessageCallback) { errMessageCallback_ = errMessageCallback; int err = (errMessageCallback_ != nullptr); #if defined(IP_RECVERR) if (address().getFamily() == AF_INET && fsp::setsockopt(fd_, IPPROTO_IP, IP_RECVERR, &err, sizeof(err))) { throw AsyncSocketException( AsyncSocketException::NOT_OPEN, "Failed to set IP_RECVERR", errno); } #endif #if defined(IPV6_RECVERR) if (address().getFamily() == AF_INET6 && fsp::setsockopt(fd_, IPPROTO_IPV6, IPV6_RECVERR, &err, sizeof(err))) { throw AsyncSocketException( AsyncSocketException::NOT_OPEN, "Failed to set IPV6_RECVERR", errno); } #endif (void)err; }
FileRegion::FileWriteRequest::FileReadHandler::FileReadHandler( FileWriteRequest* req, int pipe_in, size_t bytesToRead) : req_(req), pipe_in_(pipe_in), bytesToRead_(bytesToRead) { CHECK(req_->readBase_->isInEventBaseThread()); initHandler(req_->readBase_, pipe_in); if (!registerHandler(EventFlags::WRITE | EventFlags::PERSIST)) { req_->fail(__func__, AsyncSocketException( AsyncSocketException::INTERNAL_ERROR, "registerHandler failed")); } }
void AsyncPipeWriter::handleWrite() { DestructorGuard dg(this); assert(!queue_.empty()); do { auto& front = queue_.front(); folly::IOBufQueue& curQueue = front.first; DCHECK(!curQueue.empty()); // someday, support writev. The logic for partial writes is a bit complex const IOBuf* head = curQueue.front(); CHECK(head->length()); #if _WIN32 // On Windows you can't call write on a socket. ssize_t rc = folly::fileutil_detail::wrapNoInt( send_internal, fd_, head->data(), head->length()); #else ssize_t rc = folly::writeNoInt(fd_.toFd(), head->data(), head->length()); #endif if (rc < 0) { if (errno == EAGAIN || errno == EWOULDBLOCK) { // pipe is full VLOG(5) << "write blocked"; registerHandler(EventHandler::WRITE); return; } else { failAllWrites(AsyncSocketException( AsyncSocketException::INTERNAL_ERROR, "write failed", errno)); closeNow(); return; } } else if (rc == 0) { registerHandler(EventHandler::WRITE); return; } curQueue.trimStart(size_t(rc)); if (curQueue.empty()) { auto cb = front.second; queue_.pop_front(); if (cb) { cb->writeSuccess(); } } else { VLOG(5) << "partial write blocked"; } } while (!queue_.empty()); if (closeOnEmpty_) { closeNow(); } else { unregisterHandler(); } }
void AsyncPipeWriter::closeNow() { VLOG(5) << "close now"; if (!queue_.empty()) { failAllWrites(AsyncSocketException( AsyncSocketException::NOT_OPEN, "closed with pending writes")); } if (fd_ != NetworkSocket()) { unregisterHandler(); changeHandlerFD(NetworkSocket()); if (closeCb_) { closeCb_(fd_); } else { netops::close(fd_); } fd_ = NetworkSocket(); } }
void AsyncUDPSocket::bind(const folly::SocketAddress& address) { int socket = fsp::socket(address.getFamily(), SOCK_DGRAM, IPPROTO_UDP); if (socket == -1) { throw AsyncSocketException( AsyncSocketException::NOT_OPEN, "error creating async udp socket", errno); } auto g = folly::makeGuard([&] { ::close(socket); }); // put the socket in non-blocking mode int ret = fcntl(socket, F_SETFL, O_NONBLOCK); if (ret != 0) { throw AsyncSocketException( AsyncSocketException::NOT_OPEN, "failed to put socket in non-blocking mode", errno); } if (reuseAddr_) { // put the socket in reuse mode int value = 1; if (setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value)) != 0) { throw AsyncSocketException( AsyncSocketException::NOT_OPEN, "failed to put socket in reuse mode", errno); } } if (reusePort_) { // put the socket in port reuse mode int value = 1; if (setsockopt(socket, SOL_SOCKET, SO_REUSEPORT, &value, sizeof(value)) != 0) { throw AsyncSocketException( AsyncSocketException::NOT_OPEN, "failed to put socket in reuse_port mode", errno); } } if (busyPollUs_ > 0) { #ifdef SO_BUSY_POLL // Set busy_poll time in microseconds on the socket. // It sets how long socket will be in busy_poll mode when no event occurs. int value = busyPollUs_; if (setsockopt(socket, SOL_SOCKET, SO_BUSY_POLL, &value, sizeof(value)) != 0) { throw AsyncSocketException( AsyncSocketException::NOT_OPEN, "failed to set SO_BUSY_POLL on the socket", errno); } #else /* SO_BUSY_POLL is not supported*/ throw AsyncSocketException( AsyncSocketException::NOT_OPEN, "SO_BUSY_POLL is not supported", errno); #endif } if (rcvBuf_ > 0) { // Set the size of the buffer for the received messages in rx_queues. int value = rcvBuf_; if (setsockopt(socket, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value)) != 0) { throw AsyncSocketException( AsyncSocketException::NOT_OPEN, "failed to set SO_RCVBUF on the socket", errno); } } if (sndBuf_ > 0) { // Set the size of the buffer for the sent messages in tx_queues. int value = rcvBuf_; if (setsockopt(socket, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value)) != 0) { throw AsyncSocketException( AsyncSocketException::NOT_OPEN, "failed to set SO_SNDBUF on the socket", errno); } } // If we're using IPv6, make sure we don't accept V4-mapped connections if (address.getFamily() == AF_INET6) { int flag = 1; if (setsockopt(socket, IPPROTO_IPV6, IPV6_V6ONLY, &flag, sizeof(flag))) { throw AsyncSocketException( AsyncSocketException::NOT_OPEN, "Failed to set IPV6_V6ONLY", errno); } } // bind to the address sockaddr_storage addrStorage; address.getAddress(&addrStorage); sockaddr* saddr = reinterpret_cast<sockaddr*>(&addrStorage); if (fsp::bind(socket, saddr, address.getActualSize()) != 0) { throw AsyncSocketException( AsyncSocketException::NOT_OPEN, "failed to bind the async udp socket for:" + address.describe(), errno); } // success g.dismiss(); fd_ = socket; ownership_ = FDOwnership::OWNS; // attach to EventHandler EventHandler::changeHandlerFD(fd_); if (address.getPort() != 0) { localAddress_ = address; } else { localAddress_.setFromLocalAddress(fd_); } }
void AsyncUDPSocket::bind(const folly::SocketAddress& address) { int socket = fsp::socket(address.getFamily(), SOCK_DGRAM, IPPROTO_UDP); if (socket == -1) { throw AsyncSocketException(AsyncSocketException::NOT_OPEN, "error creating async udp socket", errno); } auto g = folly::makeGuard([&] { ::close(socket); }); // put the socket in non-blocking mode int ret = fcntl(socket, F_SETFL, O_NONBLOCK); if (ret != 0) { throw AsyncSocketException(AsyncSocketException::NOT_OPEN, "failed to put socket in non-blocking mode", errno); } if (reuseAddr_) { // put the socket in reuse mode int value = 1; if (setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value)) != 0) { throw AsyncSocketException(AsyncSocketException::NOT_OPEN, "failed to put socket in reuse mode", errno); } } if (reusePort_) { // put the socket in port reuse mode int value = 1; if (setsockopt(socket, SOL_SOCKET, SO_REUSEPORT, &value, sizeof(value)) != 0) { throw AsyncSocketException(AsyncSocketException::NOT_OPEN, "failed to put socket in reuse_port mode", errno); } } // If we're using IPv6, make sure we don't accept V4-mapped connections if (address.getFamily() == AF_INET6) { int flag = 1; if (setsockopt(socket, IPPROTO_IPV6, IPV6_V6ONLY, &flag, sizeof(flag))) { throw AsyncSocketException( AsyncSocketException::NOT_OPEN, "Failed to set IPV6_V6ONLY", errno); } } // bind to the address sockaddr_storage addrStorage; address.getAddress(&addrStorage); sockaddr* saddr = reinterpret_cast<sockaddr*>(&addrStorage); if (fsp::bind(socket, saddr, address.getActualSize()) != 0) { throw AsyncSocketException( AsyncSocketException::NOT_OPEN, "failed to bind the async udp socket for:" + address.describe(), errno); } // success g.dismiss(); fd_ = socket; ownership_ = FDOwnership::OWNS; // attach to EventHandler EventHandler::changeHandlerFD(fd_); if (address.getPort() != 0) { localAddress_ = address; } else { localAddress_.setFromLocalAddress(fd_); } }