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

Code before:
    const auto tlvs = config_->sharedConfig()->evaluateDynamicTLVs(getStreamInfo());
    filter_state->setData(
        Network::ProxyProtocolFilterState::key(),
        std::make_shared<Network::ProxyProtocolFilterState>(Network::ProxyProtocolData{
            downstream_connection.connectionInfoProvider().remoteAddress(),
            downstream_connection.connectionInfoProvider().localAddress(), tlvs}),
        StreamInfo::FilterState::StateType::ReadOnly,
        StreamInfo::FilterState::LifeSpan::Connection);
  } else if (config_->sharedConfig()->mergeWithDownstreamTlvs()) {
    // Downstream proxy protocol state exists and merge is enabled.
    // Merge tcp_proxy TLVs with downstream TLVs (tcp_proxy TLVs take precedence).
    const auto& existing_data = existing_state->value();
    const auto tcp_proxy_tlvs = config_->sharedConfig()->evaluateDynamicTLVs(getStreamInfo());

    Network::ProxyProtocolTLVVector merged_tlvs;
    absl::flat_hash_set<uint8_t> seen_types;

    // Add tcp_proxy TLVs first (they take precedence).
    for (const auto& tlv : tcp_proxy_tlvs) {
      merged_tlvs.push_back(tlv);
      seen_types.insert(tlv.type);
    }

    // Add downstream TLVs that don't conflict with tcp_proxy TLVs.
    for (const auto& tlv : existing_data.tlv_vector_) {
      if (!seen_types.contains(tlv.type)) {
        merged_tlvs.push_back(tlv);
        seen_types.insert(tlv.type);
      }
    }

    // Update filter state with merged TLVs, preserving downstream addresses and version.
    // Use Mutable state type to match the existing state (set by proxy_protocol listener filter).
    filter_state->setData(
        Network::ProxyProtocolFilterState::key(),
        std::make_shared<Network::ProxyProtocolFilterState>(Network::ProxyProtocolDataWithVersion{
            {existing_data.src_addr_, existing_data.dst_addr_, merged_tlvs},
            existing_data.version_}),
        StreamInfo::FilterState::StateType::Mutable, StreamInfo::FilterState::LifeSpan::Connection);
  }
  // else: Downstream state exists but merge is disabled - keep existing state as-is.
  transport_socket_options_ =
      Network::TransportSocketOptionsUtility::fromFilterState(*filter_state);

  if (auto typed_state = filter_state->getDataReadOnly<Network::UpstreamSocketOptionsFilterState>(
          Network::UpstreamSocketOptionsFilterState::key());
      typed_state != nullptr) {
    auto downstream_options = typed_state->value();
    if (!upstream_options_) {
      upstream_options_ = std::make_shared<Network::Socket::Options>();
    }

Review comment (bug):
This will introduce the bug reported in https://github.com/envoyproxy/envoy/issues/42075 to tcp_proxy.

Code after:
    const auto tlvs = config_->sharedConfig()->evaluateDynamicTLVs(getStreamInfo());
    filter_state->setData(
        Network::ProxyProtocolFilterState::key(),
        std::make_shared<Network::ProxyProtocolFilterState>(Network::ProxyProtocolData{
            downstream_connection.connectionInfoProvider().remoteAddress(),
            downstream_connection.connectionInfoProvider().localAddress(), tlvs}),
        StreamInfo::FilterState::StateType::Mutable, StreamInfo::FilterState::LifeSpan::Connection);
  } else if (config_->sharedConfig()->proxyProtocolTlvMergePolicy() !=
             envoy::extensions::filters::network::tcp_proxy::v3::ADD_IF_ABSENT) {
    // Existing state found and merge policy is not ADD_IF_ABSENT - merge TLVs.
    const auto& existing_data = existing_state->value();
    const auto configured_tlvs = config_->sharedConfig()->evaluateDynamicTLVs(getStreamInfo());

    Network::ProxyProtocolTLVVector merged_tlvs;

    if (config_->sharedConfig()->proxyProtocolTlvMergePolicy() ==
        envoy::extensions::filters::network::tcp_proxy::v3::OVERWRITE_BY_TYPE_IF_EXISTS_OR_ADD) {
      // Overwrite by type: configured TLVs take precedence for matching types.
      absl::flat_hash_set<uint8_t> configured_tlv_types;
      for (const auto& tlv : configured_tlvs) {
        configured_tlv_types.insert(tlv.type);
      }

      for (const auto& tlv : configured_tlvs) {
        merged_tlvs.push_back(tlv);
      }

      for (const auto& tlv : existing_data.tlv_vector_) {
        if (!configured_tlv_types.contains(tlv.type)) {
          merged_tlvs.push_back(tlv);
        }
      }
    } else if (config_->sharedConfig()->proxyProtocolTlvMergePolicy() ==
               envoy::extensions::filters::network::tcp_proxy::v3::APPEND_IF_EXISTS_OR_ADD) {
      // Append: preserve all existing TLVs, then add configured TLVs.
      for (const auto& tlv : existing_data.tlv_vector_) {
        merged_tlvs.push_back(tlv);
      }

      for (const auto& tlv : configured_tlvs) {
        merged_tlvs.push_back(tlv);
      }
    }

    // Update filter state with merged TLVs, preserving existing addresses and version.
    filter_state->setData(
        Network::ProxyProtocolFilterState::key(),
        std::make_shared<Network::ProxyProtocolFilterState>(Network::ProxyProtocolDataWithVersion{
            {existing_data.src_addr_, existing_data.dst_addr_, merged_tlvs},
            existing_data.version_}),
        StreamInfo::FilterState::StateType::Mutable, StreamInfo::FilterState::LifeSpan::Connection);