Exemplo n.º 1
0
uint32_t TSocket::write_partial(const uint8_t* buf, uint32_t len) {
  if (socket_ < 0) {
    throw TTransportException(TTransportException::NOT_OPEN, "Called write on non-open socket");
  }

  uint32_t sent = 0;

  int flags = 0;
#ifdef MSG_NOSIGNAL
  // Note the use of MSG_NOSIGNAL to suppress SIGPIPE errors, instead we
  // check for the EPIPE return condition and close the socket in that case
  flags |= MSG_NOSIGNAL;
#endif // ifdef MSG_NOSIGNAL

  int b = send(socket_, buf + sent, len - sent, flags);
  ++g_socket_syscalls;

  if (b < 0) {
    if (errno == EWOULDBLOCK || errno == EAGAIN) {
      return 0;
    }
    // Fail on a send error
    int errno_copy = errno;
    GlobalOutput.perror("TSocket::write_partial() send() " + getSocketInfo(), errno_copy);

    if (errno_copy == EPIPE || errno_copy == ECONNRESET || errno_copy == ENOTCONN) {
      close();
      throw TTransportException(TTransportException::NOT_OPEN,
                               "write() send() " + getSocketInfo(), errno_copy);
    }

    throw TTransportException(TTransportException::UNKNOWN,
                              "write() send() " + getSocketInfo(), errno_copy);
  }

  // Fail on blocked send
  if (b == 0) {
    throw TTransportException(TTransportException::NOT_OPEN, "Socket send returned 0.");
  }
  return b;
}
Exemplo n.º 2
0
void TSocket::setNoDelay(bool noDelay) {
  if (socket_ >= 0) {
    // Set socket to NODELAY
    int v = noDelay ? 1 : 0;
    int ret = setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY, &v, sizeof(v));
    if (ret == -1) {
      int errno_copy = errno;  // Copy errno because we're allocating memory.
      GlobalOutput.perror("TSocket::setNoDelay() setsockopt() " +
                          getSocketInfo(), errno_copy);
      return;
    }
  }

  options_.noDelay = noDelay;
}
Exemplo n.º 3
0
void TSocket::setLinger(bool on, int linger) {
  if (socket_ >= 0) {
    struct linger l = {(on ? 1 : 0), linger};
    int ret = setsockopt(socket_, SOL_SOCKET, SO_LINGER, &l, sizeof(l));
    if (ret == -1) {
      int errno_copy = errno;  // Copy errno because we're allocating memory.
      GlobalOutput.perror("TSocket::setLinger() setsockopt() " +
                          getSocketInfo(), errno_copy);
      return;
    }
  }

  options_.lingerOn = on;
  options_.lingerVal = linger;
}
Exemplo n.º 4
0
void TSocket::write(const uint8_t* buf, uint32_t len) {
  uint32_t sent = 0;

  while (sent < len) {
    uint32_t b = write_partial(buf + sent, len - sent);
    if (b == 0) {
      // This should only happen if the timeout set with SO_SNDTIMEO expired.
      // Raise an exception.
      // However, first set SO_LINGER to 0 seconds for this socket.  We think
      // the remote endpoint is not responding, so when we call close() we just
      // want the server to drop any untransmitted data immediately, instead of
      // moving into the TCP_FIN_WAIT1 state and continuing to try and send it.
      setLinger(true, 0);
      throw TTransportException(TTransportException::TIMED_OUT,
                                "send timeout expired " + getSocketInfo());
    }
    sent += b;
  }
}
Exemplo n.º 5
0
void TSocket::setRecvTimeout(int ms) {
  if (ms < 0) {
    char errBuf[512];
    sprintf(errBuf, "TSocket::setRecvTimeout with negative input: %d\n", ms);
    GlobalOutput(errBuf);
    return;
  }

  if (socket_ >= 0) {
    struct timeval r = {(int)(ms/1000),
                        (int)((ms%1000)*1000)};
    int ret = setsockopt(socket_, SOL_SOCKET, SO_RCVTIMEO, &r, sizeof(r));
    if (ret == -1) {
      int errno_copy = errno;  // Copy errno because we're allocating memory.
      GlobalOutput.perror("TSocket::setRecvTimeout() setsockopt() " +
                          getSocketInfo(), errno_copy);
      return;
    }
  }

  options_.recvTimeout = ms;
}
Exemplo n.º 6
0
void TSocket::setSendBufSize(size_t bufsize) {

  if (isOpen()) {

    // It is undesirable to reduce the send buffer at runtime.
    // The kernel may prevent the applicaton from sending new data
    // as it reduces the buffer
    if (bufsize < options_.sendBufSize) {
      GlobalOutput.printf("Error cannot reduce send buffer size of \
          open socket old: %zu new: %zu",
          options_.sendBufSize, bufsize);
      return;
    }

    int ret = setsockopt(socket_, SOL_SOCKET, SO_SNDBUF,
        &bufsize, sizeof(bufsize));
    if (ret == -1) {
      int errno_copy = errno;  // Copy errno because we're allocating memory.
      GlobalOutput.perror("TSocket::setSendBufSize() setsockopt() "
          + getSocketInfo(), errno_copy);
      return;
    }
  }
Exemplo n.º 7
0
TSocket::Options TSocket::getCurrentSocketOptions() {
  TSocket::Options ro;
  socklen_t ol;
  socklen_t *optlen = &ol;
  struct timeval s;
  size_t bufSize;
  struct linger l;
  int ret = 0;
  int errno_copy;

  // ConnTimeout
  ro.connTimeout = options_.connTimeout;

  //sendTimeout
  s.tv_sec = s.tv_usec = 0;
  ret = 0;
  *optlen = sizeof(s);
  ret = getsockopt(socket_, SOL_SOCKET, SO_SNDTIMEO, &s, optlen);
  if (ret == -1) {
    errno_copy = errno;  // Copy errno because we're allocating memory.
    GlobalOutput.perror("TSocket::SendTimeout getsockopt() " +
        getSocketInfo(), errno_copy);
  } else {
    ro.sendTimeout = msTimeFromTimeval(s);
  }

  // recvTimeout
  s.tv_sec = s.tv_usec = 0;
  ret = 0;
  *optlen = sizeof(s);
  ret = getsockopt(socket_, SOL_SOCKET, SO_RCVTIMEO, &s, optlen);
  if (ret == -1) {
    errno_copy = errno;  // Copy errno because we're allocating memory.
    GlobalOutput.perror("TSocket::RecvTimeout getsockopt() " +
        getSocketInfo(), errno_copy);
  } else {
    ro.recvTimeout = msTimeFromTimeval(s);
  }

  // sendBufSize
  ret = 0;
  bufSize = 0;
  *optlen = sizeof (bufSize);
  ret = getsockopt(socket_, SOL_SOCKET, SO_SNDBUF,
      &bufSize, optlen);
  if (ret == -1) {
    int errno_copy = errno;  // Copy errno because we're allocating memory.
    GlobalOutput.perror("TSocket::getSendBufSize() setsockopt() "
        + getSocketInfo(), errno_copy);
  } else {
    ro.sendBufSize = bufSize;
  }

  // recvBufSize
  ret = 0;
  bufSize = 0;
  *optlen = sizeof (bufSize);
  ret = getsockopt(socket_, SOL_SOCKET, SO_RCVBUF,
      &bufSize, optlen);
  if (ret == -1) {
    int errno_copy = errno;  // Copy errno because we're allocating memory.
    GlobalOutput.perror("TSocket::getRecvBufSize() setsockopt() "
        + getSocketInfo(), errno_copy);
  } else {
    ro.recvBufSize = bufSize;
  }

  // linger
  ret = 0;
  *optlen = sizeof (l);
  ret = getsockopt(socket_, SOL_SOCKET, SO_LINGER, &l, optlen);
  if (ret == -1) {
    int errno_copy = errno;  // Copy errno because we're allocating memory.
    GlobalOutput.perror("TSocket::getLinger() setsockopt() "
        + getSocketInfo(), errno_copy);
  } else {
    ro.lingerOn = l.l_onoff;
    ro.lingerVal = l.l_linger;
  }

  // NODELAY
  int v = 0;
  ret = 0;
  *optlen = sizeof (v);
  ret = getsockopt(socket_, IPPROTO_TCP, TCP_NODELAY, &v, optlen);
  if (ret == -1) {
    int errno_copy = errno;  // Copy errno because we're allocating memory.
    GlobalOutput.perror("TSocket::getNoDelay() setsockopt() "
        + getSocketInfo(), errno_copy);
  } else {
    ro.noDelay = v;
  }

  // REUSEADDR
  v = 0;
  ret = 0;
  *optlen = sizeof(v);
  ret = getsockopt(socket_, SOL_SOCKET, SO_REUSEADDR, &v, optlen);
  if (ret == -1) {
    int errno_copy = errno;  // Copy errno because we're allocating memory.
    GlobalOutput.perror("TSocket::getCurrentSocketOptions() SO_REUSEADDR "
                        + getSocketInfo(), errno_copy);
  } else {
    ro.reuseAddr = v;
  }

  return ro;
}
Exemplo n.º 8
0
uint32_t TSocket::read(uint8_t* buf, uint32_t len) {
  if (socket_ < 0) {
    throw TTransportException(TTransportException::NOT_OPEN, "Called read on non-open socket");
  }

  int32_t retries = 0;

  // EAGAIN can be signaled both when a timeout has occurred and when
  // the system is out of resources (an awesome undocumented feature).
  // The following is an approximation of the time interval under which
  // EAGAIN is taken to indicate an out of resources error.
  uint32_t eagainThresholdMicros = 0;
  if (options_.recvTimeout) {
    // if a readTimeout is specified along with a max number of recv retries, then
    // the threshold will ensure that the read timeout is not exceeded even in the
    // case of resource errors
    eagainThresholdMicros = (options_.recvTimeout*1000)/ ((maxRecvRetries_>0) ?
        maxRecvRetries_ : 2);
  }

  apache::thrift::util::PausableTimer pausableTimer(options_.recvTimeout);

 try_again:
  // When there is a recv timeout set, an EINTR will restart the
  // recv() and hence reset the timer.  So if we receive EINTRs at a
  // faster rate than the timeout value, the timeout will never
  // trigger.  Therefore, we keep track of the total amount of time
  // we've spend in recv(), and if this value exceeds the timeout then
  // we stop retrying on EINTR.  Note that we might still exceed the
  // timeout, but by at most a factor of 2.
  pausableTimer.start();
  int got = folly::to<int>(recv(socket_, buf, len, 0));
  int errno_after_recv = errno; // gettimeofday, used by PausableTimer, can change errno
  pausableTimer.stop();
  ++g_socket_syscalls;

  // Check for error on read
  if (got < 0) {
    if (errno_after_recv == EAGAIN) {
      // if no timeout we can assume that resource exhaustion has occurred.
      if (options_.recvTimeout == 0) {
        throw TTransportException(TTransportException::TIMED_OUT,
                                    "EAGAIN (unavailable resources)");
      }
      // check if this is the lack of resources or timeout case
      if (pausableTimer.didLastRunningTimeExceedLimit(eagainThresholdMicros)) {
        // infer that timeout has been hit
        throw TTransportException(TTransportException::TIMED_OUT,
                                  "EAGAIN (timed out) " + getSocketInfo());
      } else {
        if (retries++ < maxRecvRetries_) {
          usleep(50);
          goto try_again;
        } else {
          throw TTransportException(TTransportException::TIMED_OUT,
                                    "EAGAIN (unavailable resources)");
        }
      }
    }

    // If interrupted, try again, but only if we haven't exceeded the
    // timeout value yet.
    if (errno_after_recv == EINTR) {
      if (pausableTimer.hasExceededTimeLimit()) {
        // Exceeded the timeout value, this is a real retry now.
        if (retries++ < maxRecvRetries_) {
          pausableTimer.reset();
          goto try_again;
        } else {
          throw TTransportException(
            TTransportException::TIMED_OUT,
            "recv() failed (EINTRs, then timed out)",
            errno_after_recv);
        }
      } else {
        goto try_again;
      }
    }

    #if defined __FreeBSD__ || defined __MACH__
    if (errno_after_recv == ECONNRESET) {
      /* shigin: freebsd doesn't follow POSIX semantic of recv and fails with
       * ECONNRESET if peer performed shutdown
       * edhall: eliminated close() since we do that in the destructor.
       */
      return 0;
    }
    #endif

    // Now it's not a try again case, but a real probblez
    GlobalOutput.perror("TSocket::read() recv() " + getSocketInfo(), errno_after_recv);

    // If we disconnect with no linger time
    if (errno_after_recv == ECONNRESET) {
      throw TTransportException(TTransportException::NOT_OPEN,
                                "ECONNRESET " + getSocketInfo());
    }

    // This ish isn't open
    if (errno_after_recv == ENOTCONN) {
      throw TTransportException(TTransportException::NOT_OPEN,
                                "ENOTCONN " + getSocketInfo());
    }

    // Timed out!
    if (errno_after_recv == ETIMEDOUT) {
      throw TTransportException(TTransportException::TIMED_OUT,
                                "ETIMEDOUT " + getSocketInfo());
    }

    // Some other error, whatevz
    throw TTransportException(TTransportException::UNKNOWN,
                              "Unknown " + getSocketInfo(), errno_after_recv);
  }

  // The remote host has closed the socket
  if (got == 0) {
    // edhall: we used to call close() here, but our caller may want to deal
    // with the socket fd and we'll close() in our destructor in any case.
    return 0;
  }

  // Pack data into string
  return got;
}