void AsyncSocket::enableByteEvents()

in folly/io/async/AsyncSocket.cpp [1460:1566]


void AsyncSocket::enableByteEvents() {
  if (!byteEventHelper_) {
    byteEventHelper_ = std::make_unique<ByteEventHelper>();
  }

  if (byteEventHelper_->byteEventsEnabled ||
      byteEventHelper_->maybeEx.has_value()) {
    return;
  }

  try {
#if FOLLY_HAVE_SO_TIMESTAMPING
    // make sure we have a connected IP socket that supports error queues
    // (Unix sockets do not support error queues)
    if (NetworkSocket() == fd_ || !good()) {
      throw AsyncSocketException(
          AsyncSocketException::INVALID_STATE,
          withAddr("failed to enable byte events: "
                   "socket is not open or not in a good state"));
    }
    folly::SocketAddress addr = {};
    try {
      // explicitly fetch local address (instead of using cache)
      // to ensure socket is currently healthy
      addr.setFromLocalAddress(fd_);
    } catch (const std::system_error&) {
      throw AsyncSocketException(
          AsyncSocketException::INVALID_STATE,
          withAddr("failed to enable byte events: "
                   "socket is not open or not in a good state"));
    }
    const auto family = addr.getFamily();
    if (family != AF_INET && family != AF_INET6) {
      throw AsyncSocketException(
          AsyncSocketException::NOT_SUPPORTED,
          withAddr("failed to enable byte events: socket type not supported"));
    }

    // check if timestamping is already enabled on the socket by another source
    {
      uint32_t flags = 0;
      socklen_t len = sizeof(flags);
      const auto ret =
          getSockOptVirtual(SOL_SOCKET, SO_TIMESTAMPING, &flags, &len);
      int getSockOptErrno = errno;
      if (0 != ret) {
        throw AsyncSocketException(
            AsyncSocketException::INTERNAL_ERROR,
            withAddr("failed to enable byte events: "
                     "timestamps may not be supported for this socket type "
                     "or socket be closed"),
            getSockOptErrno);
      }
      if (0 != flags) {
        throw AsyncSocketException(
            AsyncSocketException::INTERNAL_ERROR,
            withAddr("failed to enable byte events: "
                     "timestamps may have already been enabled"),
            getSockOptErrno);
      }
    }

    // enable control messages for software and hardware timestamps
    // WriteFlags will determine which messages are generated
    //
    // SOF_TIMESTAMPING_OPT_ID: see discussion in ByteEventHelper::processCmsg
    // SOF_TIMESTAMPING_OPT_TSONLY: only get timestamps, not original packet
    // SOF_TIMESTAMPING_SOFTWARE: get software timestamps if generated
    // SOF_TIMESTAMPING_RAW_HARDWARE: get hardware timestamps if generated
    // SOF_TIMESTAMPING_OPT_TX_SWHW: get both sw + hw timestamps if generated
    const uint32_t flags =
        (folly::netops::SOF_TIMESTAMPING_OPT_ID |
         folly::netops::SOF_TIMESTAMPING_OPT_TSONLY |
         folly::netops::SOF_TIMESTAMPING_SOFTWARE |
         folly::netops::SOF_TIMESTAMPING_RAW_HARDWARE |
         folly::netops::SOF_TIMESTAMPING_OPT_TX_SWHW);
    socklen_t len = sizeof(flags);
    const auto ret =
        setSockOptVirtual(SOL_SOCKET, SO_TIMESTAMPING, &flags, len);
    int setSockOptErrno = errno;
    if (ret == 0) {
      byteEventHelper_->byteEventsEnabled = true;
      byteEventHelper_->rawBytesWrittenWhenByteEventsEnabled =
          getRawBytesWritten();
      for (const auto& observer : lifecycleObservers_) {
        if (observer->getConfig().byteEvents) {
          observer->byteEventsEnabled(this);
        }
      }
      return;
    }

    // failed
    throw AsyncSocketException(
        AsyncSocketException::INTERNAL_ERROR,
        withAddr("failed to enable byte events: setsockopt failed"),
        setSockOptErrno);
#endif // FOLLY_HAVE_SO_TIMESTAMPING
    // unsupported by platform
    throw AsyncSocketException(
        AsyncSocketException::NOT_SUPPORTED,
        withAddr("failed to enable byte events: platform not supported"));

  } catch (const AsyncSocketException& ex) {
    failByteEvents(ex);
  }
}