in proxygen/lib/http/session/HTTPTransaction.cpp [627:703]
void HTTPTransaction::onError(const HTTPException& error) {
DestructorGuard g(this);
const bool wasAborted = aborted_; // see comment below
const bool wasEgressComplete = isEgressComplete();
const bool wasIngressComplete = isIngressComplete();
bool notify = (handler_);
HTTPException::Direction direction = error.getDirection();
if (direction == HTTPException::Direction::INGRESS && isIngressEOMSeen() &&
isExpectingWindowUpdate()) {
// we got an ingress error, we've seen the entire message, but we're
// expecting more (window updates). These aren't coming, convert to
// INGRESS_AND_EGRESS
VLOG(4) << "Converting ingress error to ingress+egress due to"
" flow control, and aborting "
<< *this;
direction = HTTPException::Direction::INGRESS_AND_EGRESS;
sendAbort(ErrorCode::FLOW_CONTROL_ERROR);
}
if (error.getProxygenError() == kErrorStreamAbort) {
DCHECK(error.getDirection() ==
HTTPException::Direction::INGRESS_AND_EGRESS);
aborted_ = true;
} else if (error.hasCodecStatusCode()) {
DCHECK(error.getDirection() ==
HTTPException::Direction::INGRESS_AND_EGRESS);
sendAbort(error.getCodecStatusCode());
}
switch (direction) {
case HTTPException::Direction::INGRESS_AND_EGRESS:
markEgressComplete();
markIngressComplete();
if (wasEgressComplete && wasIngressComplete &&
// We mark egress complete before we get acknowledgement of the
// write segment finishing successfully.
// TODO: instead of using DestructorGuard hacks to keep txn around,
// use an explicit callback function and set egress complete after
// last byte flushes (or egress error occurs), see #3912823
(error.getProxygenError() != kErrorWriteTimeout || wasAborted)) {
notify = false;
}
break;
case HTTPException::Direction::EGRESS:
markEgressComplete();
if (!wasEgressComplete && isIngressEOMSeen() && ingressErrorSeen_) {
// we've already seen an ingress error but we ignored it, hoping the
// handler would resume and read our queued EOM. Now both sides are
// dead and we need to kill this transaction.
markIngressComplete();
}
if (wasEgressComplete &&
!shouldNotifyExTxnError(HTTPException::Direction::EGRESS)) {
notify = false;
}
break;
case HTTPException::Direction::INGRESS:
if (isIngressEOMSeen() &&
!shouldNotifyExTxnError(HTTPException::Direction::INGRESS)) {
// Not an error, for now
ingressErrorSeen_ = true;
return;
}
markIngressComplete();
if (wasIngressComplete &&
!shouldNotifyExTxnError(HTTPException::Direction::INGRESS)) {
notify = false;
}
break;
}
if (notify && handler_) {
// mark egress complete may result in handler detaching
handler_->onError(error);
}
}