void HTTPSession::shutdownTransport()

in proxygen/lib/http/session/HTTPSession.cpp [2271:2380]


void HTTPSession::shutdownTransport(bool shutdownReads,
                                    bool shutdownWrites,
                                    const std::string& errorMsg,
                                    ProxygenError error) {
  DestructorGuard guard(this);

  // shutdowns not accounted for, shouldn't see any
  setCloseReason(ConnectionCloseReason::UNKNOWN);

  VLOG(4) << "shutdown request for " << *this << ": reads=" << shutdownReads
          << " (currently " << readsShutdown() << "), writes=" << shutdownWrites
          << " (currently " << writesShutdown() << ")";

  bool notifyEgressShutdown = false;
  bool notifyIngressShutdown = false;

  if (!transportInfo_.sslError.empty()) {
    error = kErrorSSL;
  } else if (sock_->error()) {
    VLOG(3) << "shutdown request for " << *this
            << " on bad socket. Shutting down writes too.";
    if (getConnectionCloseReason() == ConnectionCloseReason::IO_WRITE_ERROR) {
      error = kErrorWrite;
    } else {
      error = kErrorConnectionReset;
    }
    shutdownWrites = true;
  } else if (getConnectionCloseReason() == ConnectionCloseReason::TIMEOUT) {
    error = kErrorTimeout;
  }

  if (shutdownReads && !shutdownWrites && flowControlTimeout_.isScheduled()) {
    // reads are dead and writes are blocked on a window update that will never
    // come.  shutdown writes too.
    VLOG(4) << *this
            << " Converting read shutdown to read/write due to"
               " flow control";
    shutdownWrites = true;
  }

  if (shutdownWrites && !writesShutdown()) {
    // Need to shutdown, bypass double GOAWAY
    if (codec_->generateImmediateGoaway(writeBuf_)) {
      scheduleWrite();
    }
    if (!hasMoreWrites() &&
        (transactions_.empty() || codec_->closeOnEgressComplete())) {
      writes_ = SocketState::SHUTDOWN;
      if (byteEventTracker_) {
        byteEventTracker_->drainByteEvents();
      }
      if (resetAfterDrainingWrites_) {
        VLOG(4) << *this << " writes drained, sending RST";
        resetSocketOnShutdown_ = true;
        shutdownReads = true;
      } else {
        VLOG(4) << *this << " writes drained, closing";
        sock_->shutdownWriteNow();
      }
      notifyEgressShutdown = true;
    } else if (!writesDraining_) {
      writesDraining_ = true;
      notifyEgressShutdown = true;
    } // else writes are already draining; don't double notify
  }

  if (shutdownReads && !readsShutdown()) {
    notifyIngressShutdown = true;
    // TODO: send an RST if readBuf_ is non empty?
    shutdownRead();
    if (!transactions_.empty() && error == kErrorConnectionReset) {
      if (infoCallback_) {
        infoCallback_->onIngressError(*this, error);
      }
    } else if (error == kErrorEOF) {
      // Report to the codec that the ingress stream has ended
      codec_->onIngressEOF();
      if (infoCallback_) {
        infoCallback_->onIngressEOF();
      }
    }
    // Once reads are shutdown the parser should stop processing
    codec_->setParserPaused(true);
  }

  if (notifyIngressShutdown || notifyEgressShutdown) {
    auto dir = (notifyIngressShutdown && notifyEgressShutdown)
                   ? HTTPException::Direction::INGRESS_AND_EGRESS
                   : (notifyIngressShutdown ? HTTPException::Direction::INGRESS
                                            : HTTPException::Direction::EGRESS);
    HTTPException ex(dir,
                     folly::to<std::string>("Shutdown transport: ",
                                            getErrorString(error),
                                            errorMsg.empty() ? "" : " ",
                                            errorMsg,
                                            ", ",
                                            getPeerAddress().describe()));
    ex.setProxygenError(error);
    invokeOnAllTransactions([&ex](HTTPTransaction* txn) { txn->onError(ex); });
  }

  if (readsShutdown() && writesShutdown()) {
    // No need to defer shutdown
    shutdownTransportCb_.reset();
  }

  // Close the socket only after the onError() callback on the txns
  // and handler has been detached.
  checkForShutdown();
}