Copilot commented on code in PR #12593:
URL: https://github.com/apache/trafficserver/pull/12593#discussion_r4483957452


##########
plugins/lua/ts_lua_http.cc:
##########
@@ -549,6 +551,31 @@ ts_lua_http_get_next_hop_strategy(lua_State *L)
   return 1;
 }
 
+namespace
+{
+// A script naming a missing strategy fails on every transaction, so emit at 
most one error per interval.
+constexpr TSHRTime    strategy_error_interval = TS_HRTIME_SECONDS(60);
+std::atomic<TSHRTime> next_strategy_error{0};
+std::atomic<uint64_t> suppressed_strategy_errors{0};
+
+void
+log_strategy_lookup_failure(char const *name)
+{
+  TSHRTime const now  = TShrtime();
+  TSHRTime       next = next_strategy_error.load(std::memory_order_relaxed);
+
+  if (now >= next && next_strategy_error.compare_exchange_strong(next, now + 
strategy_error_interval, std::memory_order_relaxed)) {
+    uint64_t const suppressed = suppressed_strategy_errors.exchange(0, 
std::memory_order_relaxed);
+    TSError("[ts_lua][ts_lua_http_set_next_hop_strategy] Failed get next hop 
strategy name '%s' (%" PRIu64
+            " similar errors suppressed)",
+            name, suppressed);
+  } else {
+    suppressed_strategy_errors.fetch_add(1, std::memory_order_relaxed);
+    Dbg(dbg_ctl, "Failed get next hop strategy name '%s'", name);
+  }

Review Comment:
   This file uses `PRIu64` in a format string but only adds `<atomic>`; 
`PRIu64` is provided by `<cinttypes>` (or `<inttypes.h>`). Add the appropriate 
include to avoid build failures on platforms where it isn’t transitively 
included.



##########
src/proxy/http/remap/NextHopStrategyFactory.cc:
##########
@@ -167,23 +175,26 @@ NextHopStrategyFactory::createStrategy(const std::string 
&name, const NHPolicyTy
 NextHopSelectionStrategy *
 NextHopStrategyFactory::strategyInstance(const char *name) const
 {
-  NextHopSelectionStrategy *ps_strategy = nullptr;
-
+  // Called from transaction threads, must not mutate strategy state.
   if (!strategies_loaded) {
-    NH_Error("no strategy configurations were defined, see definitions in '%s' 
file", fn.c_str());
-    return nullptr;
-  } else {
-    auto it = _strategies.find(name);
-    if (it == _strategies.end()) {
-      // NH_Error("no strategy found for name: %s", name);
-      return nullptr;
-    } else {
-      ps_strategy           = it->second;
-      ps_strategy->distance = std::distance(_strategies.begin(), it);
+    if (!_not_loaded_logged.exchange(true, std::memory_order_relaxed)) {
+      NH_Error("no strategy configurations were defined, see definitions in 
'%s' file", fn.c_str());
     }
+    return nullptr;
   }
 
-  return ps_strategy;
+  if (name == nullptr) {
+    return nullptr;
+  }
+
+  auto it = _strategies.find(name);
+  return (it == _strategies.end()) ? nullptr : it->second;
+}
+
+bool
+NextHopStrategyFactory::contains(NextHopSelectionStrategy const *strategy) 
const
+{
+  return _strategy_ptrs.contains(strategy);

Review Comment:
   `std::unordered_set::contains()` is a C++20 API. If this project (or any 
supported build configuration) is still compiled as C++17 (the minimum stated 
for plugins), this will fail to compile. Use `return 
_strategy_ptrs.find(strategy) != _strategy_ptrs.end();` instead, or explicitly 
raise the project-wide C++ standard to C++20 where appropriate.



##########
src/api/InkAPI.cc:
##########
@@ -5472,57 +5472,142 @@ TSHttpTxnServerRequestBodySet(TSHttpTxn txnp, char 
*buf, int64_t buflength)
   s->internal_msg_buffer_fast_allocator_size = -1;
 }
 
-void const *
+TSStrategy
 TSHttpTxnNextHopStrategyGet(TSHttpTxn txnp)
 {
   sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS);
 
   auto sm = reinterpret_cast<HttpSM const *>(txnp);
 
-  return static_cast<void *>(sm->t_state.next_hop_strategy);
+  return reinterpret_cast<TSStrategy>(sm->t_state.next_hop_strategy);
 }
 
-void
-TSHttpTxnNextHopStrategySet(TSHttpTxn txnp, void const *stratptr)
+TSStrategy
+TSHttpTxnNextHopStrategyFind(TSHttpTxn txnp, const char *name)
 {
   sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS);
-  // null strategy falls back to parent.config
-  // sdk_assert(sdk_sanity_check_null_ptr(strategy) == TS_SUCCESS);
+  sdk_assert(sdk_sanity_check_null_ptr((void *)name) == TS_SUCCESS);
+
+  auto sm = reinterpret_cast<HttpSM const *>(txnp);
+
+  // m_remap is null if no rewrite table existed when the transaction started.
+  if (sm->m_remap.get() == nullptr || sm->m_remap->strategyFactory == nullptr) 
{
+    return nullptr;
+  }
 
-  auto sm       = reinterpret_cast<HttpSM *>(txnp);
-  auto strategy = reinterpret_cast<NextHopSelectionStrategy const *>(stratptr);
+  NextHopSelectionStrategy *const strategy = 
sm->m_remap->strategyFactory->strategyInstance(name);
 
-  sm->t_state.next_hop_strategy = const_cast<NextHopSelectionStrategy 
*>(strategy);
+  return reinterpret_cast<TSStrategy>(strategy);
 }
 
-char const *
-TSHttpNextHopStrategyNameGet(void const *stratptr)
+void
+TSHttpTxnNextHopStrategySet(TSHttpTxn txnp, TSStrategy stratptr)
 {
-  char const *name = nullptr;
-  if (nullptr != stratptr) {
-    auto strategy = reinterpret_cast<NextHopSelectionStrategy const 
*>(stratptr);
-    name          = strategy->strategy_name.c_str();
+  sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS);
+
+  auto sm = reinterpret_cast<HttpSM *>(txnp);
+
+  // A null strategy falls back to parent.config.
+  if (stratptr == nullptr) {
+    sm->t_state.next_hop_strategy = nullptr;
+    sm->m_strategy_owner.reset();
+    return;
   }
 
-  return name;
+  // The core dereferences this pointer during parent selection, so reject
+  // handles not owned by this transaction's factory (e.g. cached across a 
reload).
+  auto const candidate = reinterpret_cast<NextHopSelectionStrategy 
*>(stratptr);
+  if (sm->m_remap == nullptr || sm->m_remap->strategyFactory == nullptr || 
!sm->m_remap->strategyFactory->contains(candidate)) {
+    SiteThrottledError("%s: strategy %p is not present in the live strategy 
factory; ignoring", __func__, stratptr);
+    Dbg(dbg_ctl_parent_select, "%s: strategy %p is not present in the live 
strategy factory; ignoring", __func__, stratptr);

Review Comment:
   These log calls use `%p` but pass `TSStrategy` (`struct tsapi_strategy *`) 
directly. In varargs/printf-style functions, `%p` requires an argument of type 
`void*`; passing another pointer type is undefined behavior. Cast the 
strategy/handle to `void*` (or `void const*` with a corresponding format 
strategy) before logging.



##########
src/api/InkAPI.cc:
##########
@@ -5472,57 +5472,142 @@ TSHttpTxnServerRequestBodySet(TSHttpTxn txnp, char 
*buf, int64_t buflength)
   s->internal_msg_buffer_fast_allocator_size = -1;
 }
 
-void const *
+TSStrategy
 TSHttpTxnNextHopStrategyGet(TSHttpTxn txnp)
 {
   sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS);
 
   auto sm = reinterpret_cast<HttpSM const *>(txnp);
 
-  return static_cast<void *>(sm->t_state.next_hop_strategy);
+  return reinterpret_cast<TSStrategy>(sm->t_state.next_hop_strategy);
 }
 
-void
-TSHttpTxnNextHopStrategySet(TSHttpTxn txnp, void const *stratptr)
+TSStrategy
+TSHttpTxnNextHopStrategyFind(TSHttpTxn txnp, const char *name)
 {
   sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS);
-  // null strategy falls back to parent.config
-  // sdk_assert(sdk_sanity_check_null_ptr(strategy) == TS_SUCCESS);
+  sdk_assert(sdk_sanity_check_null_ptr((void *)name) == TS_SUCCESS);
+
+  auto sm = reinterpret_cast<HttpSM const *>(txnp);
+
+  // m_remap is null if no rewrite table existed when the transaction started.
+  if (sm->m_remap.get() == nullptr || sm->m_remap->strategyFactory == nullptr) 
{
+    return nullptr;
+  }
 
-  auto sm       = reinterpret_cast<HttpSM *>(txnp);
-  auto strategy = reinterpret_cast<NextHopSelectionStrategy const *>(stratptr);
+  NextHopSelectionStrategy *const strategy = 
sm->m_remap->strategyFactory->strategyInstance(name);
 
-  sm->t_state.next_hop_strategy = const_cast<NextHopSelectionStrategy 
*>(strategy);
+  return reinterpret_cast<TSStrategy>(strategy);
 }
 
-char const *
-TSHttpNextHopStrategyNameGet(void const *stratptr)
+void
+TSHttpTxnNextHopStrategySet(TSHttpTxn txnp, TSStrategy stratptr)
 {
-  char const *name = nullptr;
-  if (nullptr != stratptr) {
-    auto strategy = reinterpret_cast<NextHopSelectionStrategy const 
*>(stratptr);
-    name          = strategy->strategy_name.c_str();
+  sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS);
+
+  auto sm = reinterpret_cast<HttpSM *>(txnp);
+
+  // A null strategy falls back to parent.config.
+  if (stratptr == nullptr) {
+    sm->t_state.next_hop_strategy = nullptr;
+    sm->m_strategy_owner.reset();
+    return;
   }
 
-  return name;
+  // The core dereferences this pointer during parent selection, so reject
+  // handles not owned by this transaction's factory (e.g. cached across a 
reload).
+  auto const candidate = reinterpret_cast<NextHopSelectionStrategy 
*>(stratptr);
+  if (sm->m_remap == nullptr || sm->m_remap->strategyFactory == nullptr || 
!sm->m_remap->strategyFactory->contains(candidate)) {
+    SiteThrottledError("%s: strategy %p is not present in the live strategy 
factory; ignoring", __func__, stratptr);
+    Dbg(dbg_ctl_parent_select, "%s: strategy %p is not present in the live 
strategy factory; ignoring", __func__, stratptr);
+    return;
+  }
+
+  sm->t_state.next_hop_strategy = candidate;
+  sm->m_strategy_owner          = sm->m_remap;
 }
 
-void const *
-TSHttpTxnNextHopNamedStrategyGet(TSHttpTxn txnp, const char *name)
+TSStrategy
+TSRemapNextHopStrategyFind(const char *name)
 {
-  sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS);
   sdk_assert(sdk_sanity_check_null_ptr((void *)name) == TS_SUCCESS);
 
-  auto sm = reinterpret_cast<HttpSM const *>(txnp);
+  auto const um = url_mapping::loading_instance();
+  if (um == nullptr) {
+    SiteThrottledError("%s: must be called during remap plugin initialization 
(no loading remap rule); returning nullptr",
+                       __func__);
+    return nullptr;
+  }
 
-  sdk_assert(sdk_sanity_check_null_ptr((void *)sm->m_remap.get()) == 
TS_SUCCESS);
-  sdk_assert(sdk_sanity_check_null_ptr((void *)sm->m_remap->strategyFactory) 
== TS_SUCCESS);
+  // No strategies.yaml is loaded.
+  if (um->strategyFactory == nullptr) {
+    return nullptr;
+  }
+
+  return 
reinterpret_cast<TSStrategy>(um->strategyFactory->strategyInstance(name));
+}
 
-  // HttpSM has a reference count handle to UrlRewrite which has a
-  // pointer to NextHopStrategyFactory
-  NextHopSelectionStrategy const *const strat = 
sm->m_remap->strategyFactory->strategyInstance(name);
+void
+TSRemapNextHopStrategySet(TSStrategy strategy)
+{
+  auto const um = url_mapping::loading_instance();
+  if (um == nullptr) {
+    SiteThrottledError("%s: must be called during remap plugin initialization 
(no loading remap rule); ignoring", __func__);
+    return;
+  }
 
-  return static_cast<void const *>(strat);
+  if (strategy == nullptr) {
+    um->strategy = nullptr;
+    return;
+  }
+
+  auto const candidate = reinterpret_cast<NextHopSelectionStrategy 
*>(strategy);
+  if (um->strategyFactory == nullptr || 
!um->strategyFactory->contains(candidate)) {
+    TSError("%s: strategy %p is not present in the loading strategy factory; 
ignoring", __func__, strategy);

Review Comment:
   These log calls use `%p` but pass `TSStrategy` (`struct tsapi_strategy *`) 
directly. In varargs/printf-style functions, `%p` requires an argument of type 
`void*`; passing another pointer type is undefined behavior. Cast the 
strategy/handle to `void*` (or `void const*` with a corresponding format 
strategy) before logging.



##########
src/api/InkAPITest.cc:
##########
@@ -3193,23 +3193,21 @@ checkHttpTxnClientProtocolStackContains(SocketTest 
*test, void *data)
   return TS_EVENT_CONTINUE;
 }
 
-// This func is called by us from mytest_handler to check for 
TSHttpTxnNextStrategyGet
+// This func is called by us from mytest_handler to check 
TSHttpTxnNextHopStrategySet/Get.
+// No strategies are loaded here, so only rejection of an invalid handle is 
exercised.
 static int
-checkHttpTxnNextHopStrategyGet(SocketTest *test, void *data)
+checkHttpTxnNextHopStrategySetGet(SocketTest *test, void *data)
 {
   TSHttpTxn txnp = static_cast<TSHttpTxn>(data);
 
-  // this is an invalid pointer but the contents don't matter for this test.
-  void const *const exp = reinterpret_cast<void *>(0x01);
+  test->test_next_hop_strategy_get = true;
 
-  void const *const strategy = TSHttpTxnNextHopStrategyGet(txnp);
-  if (strategy == exp) {
-    test->test_next_hop_strategy_get = true;
-    SDK_RPRINT(test->regtest, "TSHttpTxnNextHopStrategyGet", "TestCase1", 
TC_PASS, "ok");
+  TSHttpTxnNextHopStrategySet(txnp, reinterpret_cast<TSStrategy>(0x01));
+  if (TSStrategy const strategy = TSHttpTxnNextHopStrategyGet(txnp); strategy 
== nullptr) {
+    SDK_RPRINT(test->regtest, "TSHttpTxnNextHopStrategySet", "TestCase1", 
TC_PASS, "ok");
   } else {
     test->test_next_hop_strategy_get = false;
-    SDK_RPRINT(test->regtest, "TSHttpTxnNextHopStrategyGet", "TestCase1", 
TC_FAIL, "Value's Mismatch [expected '%jx', got '%jx'",
-               exp, strategy);
+    SDK_RPRINT(test->regtest, "TSHttpTxnNextHopStrategySet", "TestCase1", 
TC_FAIL, "Invalid handle accepted [got '%p']", strategy);

Review Comment:
   `SDK_RPRINT` is printf-style, and `%p` requires a `void*` argument. Passing 
`TSStrategy` (a distinct pointer type) through varargs is undefined behavior. 
Cast `strategy` to `void*` when printing.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to