Repository: envoyproxy/envoy
File: source/common/router/router.cc (C++)

Code before:
    downstream_headers_->setEnvoyAttemptCount(attempt_count_);
  }

  if (include_timeout_retry_header_in_request_) {
    downstream_headers_->setEnvoyIsTimeoutRetry(is_timeout_retry == TimeoutRetry::Yes ? "true"
                                                                                      : "false");
  }

  // Update timeout headers for the retry attempt.
  std::chrono::milliseconds elapsed_time = std::chrono::milliseconds::zero();
  if (DateUtil::timePointValid(downstream_request_complete_time_)) {
    Event::Dispatcher& dispatcher = callbacks_->dispatcher();
    elapsed_time = std::chrono::duration_cast<std::chrono::milliseconds>(
        dispatcher.timeSource().monotonicTime() - downstream_request_complete_time_);
  }

  FilterUtility::setTimeoutHeaders(elapsed_time.count(), timeout_, *route_entry_,
                                   *downstream_headers_, !config_->suppress_envoy_headers_,
                                   grpc_request_, hedging_params_.hedge_on_per_try_timeout_);

  // Apply request_headers_to_add now that router-set headers are present.
  const Formatter::Context header_transform_context(downstream_headers_, {}, {}, {}, {},
                                                    &callbacks_->activeSpan());
  route_entry_->finalizeRequestHeaders(*downstream_headers_, header_transform_context,
                                       callbacks_->streamInfo(), !config_->suppress_envoy_headers_);

  callbacks_->streamInfo().downstreamTiming().setValue(
      "envoy.router.host_selection_start_ms",
      callbacks_->dispatcher().timeSource().monotonicTime());
  auto host_selection_response = cluster->chooseHost(this);
  if (!host_selection_response.cancelable ||
      !Runtime::runtimeFeatureEnabled("envoy.reloadable_features.async_host_selection")) {
    if (host_selection_response.cancelable) {
      host_selection_response.cancelable->cancel();
    }
    // This branch handles the common case of synchronous host selection, as
    // well as handling unsupported asynchronous host selection (by treating it
    // as host selection failure).
    continueDoRetry(can_send_early_data, can_use_http3, is_timeout_retry,
                    std::move(host_selection_response.host), *cluster,
                    std::string(host_selection_response.details));
  }

  ENVOY_STREAM_LOG(debug, "Handling asynchronous host selection for retry\n", *callbacks_);
  // Again latch the cancel handle, and set up the callback to be called when host
  // selection is complete.
  host_selection_cancelable_ = std::move(host_selection_response.cancelable);
  on_host_selected_ =
      ([this, can_send_early_data, can_use_http3, is_timeout_retry,
        cluster](Upstream::HostConstSharedPtr&& host, std::string host_selection_details) -> void {
        continueDoRetry(can_send_early_data, can_use_http3, is_timeout_retry, std::move(host),

Review comment (suggestion):
When doing the retry, we may shouldn't call the `finalizeRequestHeaders` again because it may result in unexpected result. For example, if one of the header mutation is to `add` a new header, because the same header map object is used for retry, the same header may be added for multiple times which is unexpected result. (Although the users may want to map attempt count or timeout header to other headers, it's a corner case anyway. But adding a new normal header is a common case, we should keep the common case works properly first.)

So, IMO, we needn't make any code change to the doRetry and continueDoRetry



Code after:
      callbacks_->dispatcher().timeSource().monotonicTime());
  auto host_selection_response = cluster->chooseHost(this);
  if (!host_selection_response.cancelable ||
      !Runtime::runtimeFeatureEnabled("envoy.reloadable_features.async_host_selection")) {
    if (host_selection_response.cancelable) {
      host_selection_response.cancelable->cancel();
    }
    // This branch handles the common case of synchronous host selection, as
    // well as handling unsupported asynchronous host selection (by treating it
    // as host selection failure).
    continueDoRetry(can_send_early_data, can_use_http3, is_timeout_retry,
                    std::move(host_selection_response.host), *cluster,
                    std::string(host_selection_response.details));
  }

  ENVOY_STREAM_LOG(debug, "Handling asynchronous host selection for retry\n", *callbacks_);
  // Again latch the cancel handle, and set up the callback to be called when host
  // selection is complete.
  host_selection_cancelable_ = std::move(host_selection_response.cancelable);
  on_host_selected_ =
      ([this, can_send_early_data, can_use_http3, is_timeout_retry,
        cluster](Upstream::HostConstSharedPtr&& host, std::string host_selection_details) -> void {
        continueDoRetry(can_send_early_data, can_use_http3, is_timeout_retry, std::move(host),
                        *cluster, host_selection_details);
      });
}

void Filter::continueDoRetry(bool can_send_early_data, bool can_use_http3,
                             TimeoutRetry is_timeout_retry, Upstream::HostConstSharedPtr&& host,
                             Upstream::ThreadLocalCluster& cluster,
                             absl::optional<std::string> host_selection_details) {
  callbacks_->streamInfo().downstreamTiming().setValue(
      "envoy.router.host_selection_end_ms", callbacks_->dispatcher().timeSource().monotonicTime());
  std::unique_ptr<GenericConnPool> generic_conn_pool = createConnPool(cluster, host);
  if (!generic_conn_pool) {
    sendNoHealthyUpstreamResponse(host_selection_details);
    cleanup();
    return;
  }
  UpstreamRequestPtr upstream_request = std::make_unique<UpstreamRequest>(
      *this, std::move(generic_conn_pool), can_send_early_data, can_use_http3,
      allow_multiplexed_upstream_half_close_ /*enable_half_close*/);

  if (include_attempt_count_in_request_) {
    downstream_headers_->setEnvoyAttemptCount(attempt_count_);
  }

  if (include_timeout_retry_header_in_request_) {
    downstream_headers_->setEnvoyIsTimeoutRetry(is_timeout_retry == TimeoutRetry::Yes ? "true"
                                                                                      : "false");
  }