in source/common/router/router.cc [477:561]
void Filter::onUpstreamReset(UpstreamResetType type,
const absl::optional<Http::StreamResetReason>& reset_reason) {
ASSERT(type == UpstreamResetType::GlobalTimeout || upstream_request_);
if (type == UpstreamResetType::Reset) {
ENVOY_STREAM_LOG(debug, "upstream reset", *callbacks_);
}
Upstream::HostDescriptionConstSharedPtr upstream_host;
if (upstream_request_) {
upstream_host = upstream_request_->upstream_host_;
if (upstream_host) {
upstream_host->outlierDetector().putHttpResponseCode(
enumToInt(type == UpstreamResetType::Reset ? Http::Code::ServiceUnavailable
: timeout_response_code_));
}
}
// We don't retry on a global timeout or if we already started the response.
if (type != UpstreamResetType::GlobalTimeout && !downstream_response_started_ && retry_state_) {
// Notify retry modifiers about the attempted host.
if (upstream_host != nullptr) {
retry_state_->onHostAttempted(upstream_host);
}
RetryStatus retry_status =
retry_state_->shouldRetry(nullptr, reset_reason, [this]() -> void { doRetry(); });
if (retry_status == RetryStatus::Yes && setupRetry(true)) {
if (upstream_host) {
upstream_host->stats().rq_error_.inc();
}
return;
} else if (retry_status == RetryStatus::NoOverflow) {
callbacks_->streamInfo().setResponseFlag(StreamInfo::ResponseFlag::UpstreamOverflow);
} else if (retry_status == RetryStatus::NoRetryLimitExceeded) {
callbacks_->streamInfo().setResponseFlag(
StreamInfo::ResponseFlag::UpstreamRetryLimitExceeded);
}
}
// If we have not yet sent anything downstream, send a response with an appropriate status code.
// Otherwise just reset the ongoing response.
if (downstream_response_started_) {
if (upstream_request_ != nullptr && upstream_request_->grpc_rq_success_deferred_) {
upstream_request_->upstream_host_->stats().rq_error_.inc();
}
// This will destroy any created retry timers.
cleanup();
callbacks_->resetStream();
} else {
// This will destroy any created retry timers.
cleanup();
Http::Code code;
const char* body;
if (type == UpstreamResetType::GlobalTimeout || type == UpstreamResetType::PerTryTimeout) {
callbacks_->streamInfo().setResponseFlag(StreamInfo::ResponseFlag::UpstreamRequestTimeout);
code = timeout_response_code_;
body = code == Http::Code::GatewayTimeout ? "upstream request timeout" : "";
} else {
StreamInfo::ResponseFlag response_flags =
streamResetReasonToResponseFlag(reset_reason.value());
callbacks_->streamInfo().setResponseFlag(response_flags);
code = Http::Code::ServiceUnavailable;
body = "upstream connect error or disconnect/reset before headers";
}
const bool dropped = reset_reason && reset_reason.value() == Http::StreamResetReason::Overflow;
chargeUpstreamCode(code, upstream_host, dropped);
// If we had non-5xx but still have been reset by backend or timeout before
// starting response, we treat this as an error. We only get non-5xx when
// timeout_response_code_ is used for code above, where this member can
// assume values such as 204 (NoContent).
if (upstream_host != nullptr && !Http::CodeUtility::is5xx(enumToInt(code))) {
upstream_host->stats().rq_error_.inc();
}
callbacks_->sendLocalReply(code, body,
[dropped, this](Http::HeaderMap& headers) {
if (dropped && !config_.suppress_envoy_headers_) {
headers.insertEnvoyOverloaded().value(
Http::Headers::get().EnvoyOverloadedValues.True);
}
},
absl::nullopt);
}
}