void Filter::onUpstreamReset()

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