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();
}