You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by cm...@apache.org on 2026/04/21 14:26:45 UTC

(trafficserver) branch 10.2.x updated (84216d36e6 -> 14a41e148f)

This is an automated email from the ASF dual-hosted git repository.

cmcfarlen pushed a change to branch 10.2.x
in repository https://gitbox.apache.org/repos/asf/trafficserver.git


    from 84216d36e6 jax_fingerprint: use ssl_multicert_config (#13104)
     new 9b448c483f Add shutdown hook function for lua plugin (#13045)
     new d02f084a8f Add option 3 to connect down policy (#12928)
     new 26db6f08c8 Add connect_down_policy: 4 for empty reply (#13069)
     new 64f17d0430 Guard HTTP/3 test targets with BUILD_TESTING (#13094)
     new 14a41e148f Fixx autest tls_sni_ticket so it can work with BoringSSL (#13100)

The 5 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 doc/admin-guide/files/records.yaml.en.rst          |  22 +++-
 doc/admin-guide/plugins/lua.en.rst                 |  11 ++
 plugins/lua/ts_lua.cc                              |  67 +++++++++++-
 plugins/lua/ts_lua_common.h                        |   3 +
 src/proxy/http/HttpSM.cc                           |  39 +++++--
 src/proxy/http3/CMakeLists.txt                     |  74 ++++++-------
 .../connect_down_policy.test.py                    | 116 +++++++++++++++++++++
 .../gold/connect_down_policy_4_error_log.gold      |   6 ++
 .../replay/connect_down_policy_4.replay.yaml}      |  74 +++++++------
 .../replay/inactive_timeout.replay.yaml}           |  19 ++--
 .../gold_tests/pluginTest/lua/global_shutdown.lua  |  10 +-
 .../pluginTest/lua/lua_global_shutdown.test.py     |  64 ++++++++++++
 tests/gold_tests/tls/tls_sni_ticket.test.py        |  15 ++-
 13 files changed, 418 insertions(+), 102 deletions(-)
 create mode 100644 tests/gold_tests/connect_down_policy/connect_down_policy.test.py
 create mode 100644 tests/gold_tests/connect_down_policy/gold/connect_down_policy_4_error_log.gold
 copy tests/gold_tests/{dns/replay/connect_attempts_rr_no_retry.replay.yaml => connect_down_policy/replay/connect_down_policy_4.replay.yaml} (61%)
 copy tests/gold_tests/{tls/tls_session_key_logging.replay.yaml => connect_down_policy/replay/inactive_timeout.replay.yaml} (78%)
 copy plugins/lua/example/test_response_client.lua => tests/gold_tests/pluginTest/lua/global_shutdown.lua (86%)
 create mode 100644 tests/gold_tests/pluginTest/lua/lua_global_shutdown.test.py


(trafficserver) 03/05: Add connect_down_policy: 4 for empty reply (#13069)

Posted by cm...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

cmcfarlen pushed a commit to branch 10.2.x
in repository https://gitbox.apache.org/repos/asf/trafficserver.git

commit 26db6f08c8d07e29b294258680356775a7dbb490
Author: Masaori Koshiba <ma...@apache.org>
AuthorDate: Fri Apr 17 13:26:33 2026 +0900

    Add connect_down_policy: 4 for empty reply (#13069)
    
    * Add connect_down_policy: 4 for empty reply
    
    * Fix AuTest failure on Linux
    
    (cherry picked from commit 395bbafae5758f6e09e413495de286568cd76469)
---
 doc/admin-guide/files/records.yaml.en.rst          |  24 +++-
 src/proxy/http/HttpSM.cc                           |  45 ++++---
 ...olicy_3.test.py => connect_down_policy.test.py} |   3 +
 .../gold/connect_down_policy_4_error_log.gold      |   6 +
 .../replay/connect_down_policy_4.replay.yaml       | 135 +++++++++++++++++++++
 5 files changed, 193 insertions(+), 20 deletions(-)

diff --git a/doc/admin-guide/files/records.yaml.en.rst b/doc/admin-guide/files/records.yaml.en.rst
index 921ec44251..671385b9bc 100644
--- a/doc/admin-guide/files/records.yaml.en.rst
+++ b/doc/admin-guide/files/records.yaml.en.rst
@@ -1834,12 +1834,24 @@ Origin Server Connect Attempts
    :overridable:
 
    Controls what origin server connection failures contribute to marking a server down.
-   When set to ``2``, any connection failure during the TCP and TLS handshakes will
-   contribute to marking the server down. When set to ``1``, only TCP handshake failures
-   will contribute to marking a server down. When set to ``0``, no connection failures
-   will be used towards marking a server down. When set to ``3``, all failures covered
-   by ``2`` plus transaction inactive timeouts (server goes silent after connection is
-   established) will contribute to marking a server down.
+
+   +-------+-----------------------------------------------------------------------+
+   | Value | Behavior                                                              |
+   +=======+=======================================================================+
+   | ``0`` | No connection failures contribute to marking a server down.           |
+   +-------+-----------------------------------------------------------------------+
+   | ``1`` | TCP handshake failures (excluding TLS handshake failures) contribute  |
+   |       | to marking a server down.                                             |
+   +-------+-----------------------------------------------------------------------+
+   | ``2`` | Any connection failure during the TCP or TLS handshake contributes to |
+   |       | marking a server down.                                                |
+   +-------+-----------------------------------------------------------------------+
+   | ``3`` | All failures covered by ``2``, plus transaction inactive timeouts     |
+   |       | (server goes silent after the connection is established).             |
+   +-------+-----------------------------------------------------------------------+
+   | ``4`` | All failures covered by ``3``, plus cases where the origin closes the |
+   |       | connection before sending any response bytes.                         |
+   +-------+-----------------------------------------------------------------------+
 
 .. ts:cv:: CONFIG proxy.config.http.server_max_connections INT 0
    :reloadable:
diff --git a/src/proxy/http/HttpSM.cc b/src/proxy/http/HttpSM.cc
index 38312c9bf6..2965553918 100644
--- a/src/proxy/http/HttpSM.cc
+++ b/src/proxy/http/HttpSM.cc
@@ -4673,22 +4673,39 @@ HttpSM::do_hostdb_reverse_lookup()
 bool
 HttpSM::track_connect_fail() const
 {
-  bool retval = false;
-  if (t_state.current.server->had_connect_fail()) {
-    // What does our policy say?
-    if (t_state.txn_conf->connect_down_policy == 2 ||
-        t_state.txn_conf->connect_down_policy == 3) { // Any connection error through TLS handshake
-      retval = true;
-    } else if (t_state.txn_conf->connect_down_policy == 1) { // Any connection error through TCP
-      retval = t_state.current.server->connect_result != -ENET_SSL_CONNECT_FAILED;
-    }
+  int const policy = t_state.txn_conf->connect_down_policy;
+
+  // Policy 1: any TCP-level connect error (excluding TLS handshake failures).
+  if (policy == 1 && t_state.current.server->had_connect_fail()) {
+    return t_state.current.server->connect_result != -ENET_SSL_CONNECT_FAILED;
   }
-  // Policy 3 additionally marks the server down on transaction inactive timeout,
-  // even when had_connect_fail() is false (connect_result was cleared at CONNECTION_ALIVE).
-  if (!retval && t_state.txn_conf->connect_down_policy == 3) {
-    retval = (t_state.current.server->state == HttpTransact::INACTIVE_TIMEOUT);
+
+  // Policy 2+: any connect error including TLS handshake failures.
+  if (policy >= 2 && t_state.current.server->had_connect_fail()) {
+    return true;
   }
-  return retval;
+
+  // Policy 3+: inactive timeout (connect_result was cleared at CONNECTION_ALIVE).
+  if (policy >= 3 && t_state.current.server->state == HttpTransact::INACTIVE_TIMEOUT) {
+    return true;
+  }
+
+  // Policy 4+: origin closed a fresh connection before sending any response bytes.
+  // Excludes two cases:
+  // - Reused keep-alive connection: there is a known race between ATS reusing and the origin closing it.
+  // - Multiplexed origins (HTTP/2): stream-level failure does not indicate a connection failure.
+  if (policy >= 4) {
+    bool multiplexed = false;
+    auto ssn         = server_txn->get_proxy_ssn();
+    if (ssn != nullptr) {
+      multiplexed = static_cast<PoolableSession *>(ssn)->is_multiplexing();
+    }
+    if (!multiplexed && server_txn->is_first_transaction() && server_response_hdr_bytes == 0) {
+      return true;
+    }
+  }
+
+  return false;
 }
 
 void
diff --git a/tests/gold_tests/connect_down_policy/connect_down_policy_3.test.py b/tests/gold_tests/connect_down_policy/connect_down_policy.test.py
similarity index 97%
rename from tests/gold_tests/connect_down_policy/connect_down_policy_3.test.py
rename to tests/gold_tests/connect_down_policy/connect_down_policy.test.py
index f4f5d9681a..4e93548682 100644
--- a/tests/gold_tests/connect_down_policy/connect_down_policy_3.test.py
+++ b/tests/gold_tests/connect_down_policy/connect_down_policy.test.py
@@ -111,3 +111,6 @@ ConnectDownPolicy3Test(policy=3, expect_mark_down=True).run()
 
 # Policy 2: inactive timeout should NOT mark the origin down.
 ConnectDownPolicy3Test(policy=2, expect_mark_down=False).run()
+
+# Policy 4: origin closes connection without sending any response.
+Test.ATSReplayTest(replay_file="replay/connect_down_policy_4.replay.yaml")
diff --git a/tests/gold_tests/connect_down_policy/gold/connect_down_policy_4_error_log.gold b/tests/gold_tests/connect_down_policy/gold/connect_down_policy_4_error_log.gold
new file mode 100644
index 0000000000..4a352e1546
--- /dev/null
+++ b/tests/gold_tests/connect_down_policy/gold/connect_down_policy_4_error_log.gold
@@ -0,0 +1,6 @@
+`` Server closed connection while reading response header. (origin backend1.example.com:``/path/)
+`` CONNECT : SUCCESS [0] connecting to 127.0.0.1:`` for host='one.example.com' url='http://backend1.example.com:``/path/' fail_count='1' marking down
+`` DNS Error: no valid server http://backend1.example.com:``/path/
+`` Server closed connection while reading response header. (origin backend2.example.com:``/path/)
+`` CONNECT : SUCCESS [0] connecting to 127.0.0.1:`` for host='two.example.com' url='http://backend2.example.com:``/path/' fail_count='1' marking down
+`` DNS Error: no valid server http://backend2.example.com:``/path/
diff --git a/tests/gold_tests/connect_down_policy/replay/connect_down_policy_4.replay.yaml b/tests/gold_tests/connect_down_policy/replay/connect_down_policy_4.replay.yaml
new file mode 100644
index 0000000000..554e90d138
--- /dev/null
+++ b/tests/gold_tests/connect_down_policy/replay/connect_down_policy_4.replay.yaml
@@ -0,0 +1,135 @@
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+#
+# This replay file assumes that caching is disabled.
+#
+
+meta:
+  version: "1.0"
+
+# Configuration section for autest integration
+autest:
+  description: 'Verify connect down policy 4 - origin closes connection without sending a response'
+
+  dns:
+    name: 'dns-policy-4'
+    records:
+      backend1.example.com: ["127.0.0.1"]
+      backend2.example.com: ["127.0.0.1"]
+
+  server:
+    name: 'server-policy-4'
+
+  client:
+    name: 'client-policy-4'
+
+  ats:
+    name: 'ts-policy-4'
+    process_config:
+      enable_cache: false
+
+    records_config:
+      proxy.config.diags.debug.enabled: 1
+      proxy.config.diags.debug.tags: 'http|hostdb'
+      proxy.config.http.connect.down.policy: 4
+      proxy.config.http.connect_attempts_rr_retries: 0
+      proxy.config.http.connect_attempts_max_retries: 0
+      proxy.config.http.connect_attempts_max_retries_down_server: 0
+      proxy.config.http.connect_attempts_timeout: 1
+      proxy.config.http.down_server.cache_time: 5
+
+    remap_config:
+      - from: "http://one.example.com/"
+        to: "http://backend1.example.com:{SERVER_HTTP_PORT}/"
+      - from: "http://two.example.com/"
+        to: "http://backend2.example.com:{SERVER_HTTP_PORT}/"
+
+    log_validation:
+      error_log:
+        gold_file: "gold/connect_down_policy_4_error_log.gold"
+
+sessions:
+- transactions:
+  # on_connect: refuse - Server accepts TCP connection but immediately closes it without sending any HTTP response
+  - client-request:
+      method: GET
+      url: /path/
+      version: '1.1'
+      headers:
+        fields:
+        - [Host, one.example.com]
+        - [uuid, 1]
+
+    server-response:
+      on_connect: refuse
+
+    proxy-response:
+      status: 502
+
+  # Verify the origin is marked down after the close.
+  - client-request:
+      method: GET
+      url: /path/
+      version: '1.1'
+      headers:
+        fields:
+        - [Host, one.example.com]
+        - [uuid, 10]
+
+    proxy-request:
+      expect: absent
+
+    server-response:
+      status: 200
+
+    proxy-response:
+      status: 500
+
+  # on_connect: reset - Server accepts TCP connection but immediately reset it without sending any HTTP response
+  - client-request:
+      method: GET
+      url: /path/
+      version: '1.1'
+      headers:
+        fields:
+        - [Host, two.example.com]
+        - [uuid, 2]
+
+    server-response:
+      on_connect: reset
+
+    proxy-response:
+      status: 502
+
+  # Verify the origin is marked down after the reset.
+  - client-request:
+      method: GET
+      url: /path/
+      version: '1.1'
+      headers:
+        fields:
+        - [Host, two.example.com]
+        - [uuid, 20]
+
+    proxy-request:
+      expect: absent
+
+    server-response:
+      status: 200
+
+    proxy-response:
+      status: 500


(trafficserver) 04/05: Guard HTTP/3 test targets with BUILD_TESTING (#13094)

Posted by cm...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

cmcfarlen pushed a commit to branch 10.2.x
in repository https://gitbox.apache.org/repos/asf/trafficserver.git

commit 64f17d04300652e404dd78f8cfdfbec756c70cbc
Author: Brian Neradt <br...@gmail.com>
AuthorDate: Fri Apr 17 17:04:43 2026 -0500

    Guard HTTP/3 test targets with BUILD_TESTING (#13094)
    
    Guard add_catch2_test for h3 with BUILD_TESTING. Otherwise
    -DBUILD_TESTING=off fails when HTTP/3 builds are enabled.
    
    (cherry picked from commit 5b7ab921da845b92ad1c87eec6a3aef25eb7d1ce)
---
 src/proxy/http3/CMakeLists.txt | 74 ++++++++++++++++++++++--------------------
 1 file changed, 38 insertions(+), 36 deletions(-)

diff --git a/src/proxy/http3/CMakeLists.txt b/src/proxy/http3/CMakeLists.txt
index 4a492a862a..c4c71dba53 100644
--- a/src/proxy/http3/CMakeLists.txt
+++ b/src/proxy/http3/CMakeLists.txt
@@ -47,42 +47,44 @@ target_link_libraries(
   PRIVATE ts::proxy
 )
 
-add_executable(
-  test_http3
-  test/main.cc
-  test/stub.cc
-  test/test_Http3Frame.cc
-  test/test_Http3FrameDispatcher.cc
-  Http3ProtocolEnforcer.cc
-  Http3FrameDispatcher.cc
-  Http3DebugNames.cc
-  Http3Config.cc
-  Http3Frame.cc
-  Http3SettingsHandler.cc
-)
-target_link_libraries(
-  test_http3
-  PRIVATE Catch2::Catch2WithMain
-          ts::quic
-          ts::inkevent
-          ts::records
-          ts::tsutil
-          ts::hdrs
-          ts::tscore
-)
-add_catch2_test(NAME test_http3 COMMAND test_http3)
+if(BUILD_TESTING)
+  add_executable(
+    test_http3
+    test/main.cc
+    test/stub.cc
+    test/test_Http3Frame.cc
+    test/test_Http3FrameDispatcher.cc
+    Http3ProtocolEnforcer.cc
+    Http3FrameDispatcher.cc
+    Http3DebugNames.cc
+    Http3Config.cc
+    Http3Frame.cc
+    Http3SettingsHandler.cc
+  )
+  target_link_libraries(
+    test_http3
+    PRIVATE Catch2::Catch2WithMain
+            ts::quic
+            ts::inkevent
+            ts::records
+            ts::tsutil
+            ts::hdrs
+            ts::tscore
+  )
+  add_catch2_test(NAME test_http3 COMMAND test_http3)
 
-add_executable(test_qpack test/main_qpack.cc test/test_QPACK.cc QPACK.cc)
-target_link_libraries(
-  test_qpack
-  PRIVATE Catch2::Catch2
-          ts::quic
-          ts::inkevent
-          ts::records
-          ts::tsutil
-          ts::hdrs
-          ts::tscore
-)
-add_catch2_test(NAME test_qpack COMMAND test_qpack)
+  add_executable(test_qpack test/main_qpack.cc test/test_QPACK.cc QPACK.cc)
+  target_link_libraries(
+    test_qpack
+    PRIVATE Catch2::Catch2
+            ts::quic
+            ts::inkevent
+            ts::records
+            ts::tsutil
+            ts::hdrs
+            ts::tscore
+  )
+  add_catch2_test(NAME test_qpack COMMAND test_qpack)
+endif()
 
 clang_tidy_check(http3)


(trafficserver) 01/05: Add shutdown hook function for lua plugin (#13045)

Posted by cm...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

cmcfarlen pushed a commit to branch 10.2.x
in repository https://gitbox.apache.org/repos/asf/trafficserver.git

commit 9b448c483fcd69ceedb8a5b8fc370861a4a1bb35
Author: Kit Chan <ki...@apache.org>
AuthorDate: Thu Apr 16 12:15:50 2026 -0700

    Add shutdown hook function for lua plugin (#13045)
    
    * Add shutdown hook function for lua plugin
    
    * Update lua function name
    
    * update comments
    
    * Fix problem with calling the lua function
    
    * Update plugins/lua/ts_lua.cc
    
    Co-authored-by: Copilot <17...@users.noreply.github.com>
    
    * Apply suggestion from @Copilot
    
    Co-authored-by: Copilot <17...@users.noreply.github.com>
    
    * Fix format
    
    * Change lua function name
    
    * Fix test formatting
    
    ---------
    
    Co-authored-by: Copilot <17...@users.noreply.github.com>
    (cherry picked from commit cb2b336994e5665aeaf8700765fe7de733f16b74)
---
 doc/admin-guide/plugins/lua.en.rst                 | 11 ++++
 plugins/lua/ts_lua.cc                              | 67 +++++++++++++++++++++-
 plugins/lua/ts_lua_common.h                        |  3 +
 .../gold_tests/pluginTest/lua/global_shutdown.lua  | 23 ++++++++
 .../pluginTest/lua/lua_global_shutdown.test.py     | 64 +++++++++++++++++++++
 5 files changed, 167 insertions(+), 1 deletion(-)

diff --git a/doc/admin-guide/plugins/lua.en.rst b/doc/admin-guide/plugins/lua.en.rst
index d62a2f4e43..c5e7f784ce 100644
--- a/doc/admin-guide/plugins/lua.en.rst
+++ b/doc/admin-guide/plugins/lua.en.rst
@@ -125,6 +125,17 @@ each lua script:
 - **'do_global_send_response'**
 - **'do_global_cache_lookup_complete'**
 - **'do_global_read_cache'**
+- **'__shutdown__'**
+
+The ``__shutdown__`` function is invoked once per Lua state when |ATS| is
+shutting down. It can be used to perform cleanup tasks such as flushing state or
+releasing resources. It takes no arguments and its return value is ignored.
+
+Example::
+
+    function __shutdown__()
+        ts.debug('ATS shutting down, cleaning up resources')
+    end
 
 We can write this in plugin.config:
 
diff --git a/plugins/lua/ts_lua.cc b/plugins/lua/ts_lua.cc
index 741da1893f..02b0781711 100644
--- a/plugins/lua/ts_lua.cc
+++ b/plugins/lua/ts_lua.cc
@@ -827,6 +827,47 @@ globalHookHandler(TSCont contp, TSEvent event ATS_UNUSED, void *edata)
   return 0;
 }
 
+static int
+shutdownHookHandler(TSCont contp, TSEvent /* event ATS_UNUSED */, void * /* edata ATS_UNUSED */)
+{
+  ts_lua_instance_conf *const conf = (ts_lua_instance_conf *)TSContDataGet(contp);
+
+  for (int index = 0; index < conf->states; ++index) {
+    ts_lua_main_ctx *const main_ctx = &ts_lua_g_main_ctx_array[index];
+
+    TSMutexLock(main_ctx->mutexp);
+
+    lua_State *const L = main_ctx->lua;
+
+    // Restore the conf-specific global table so lua_getglobal resolves
+    // functions from the loaded script, matching ts_lua_reload_module.
+    lua_pushlightuserdata(L, conf);
+    lua_rawget(L, LUA_REGISTRYINDEX);
+    lua_replace(L, LUA_GLOBALSINDEX);
+
+    lua_getglobal(L, TS_LUA_FUNCTION_G_SHUT_DOWN);
+
+    if (lua_type(L, -1) == LUA_TFUNCTION) {
+      if (lua_pcall(L, 0, 0, 0) != 0) {
+        TSError("[ts_lua][%s] lua_pcall failed for script '%s' state %d: %s", __FUNCTION__, conf->script, index,
+                lua_tostring(L, -1));
+        lua_pop(L, 1);
+      }
+    } else {
+      lua_pop(L, 1);
+    }
+
+    // Restore LUA_GLOBALSINDEX to an empty table, matching the resting state
+    // established by ts_lua_add_module and ts_lua_reload_module.
+    lua_newtable(L);
+    lua_replace(L, LUA_GLOBALSINDEX);
+
+    TSMutexUnlock(main_ctx->mutexp);
+  }
+
+  return 0;
+}
+
 void
 TSPluginInit(int argc, const char *argv[])
 {
@@ -1046,7 +1087,7 @@ TSPluginInit(int argc, const char *argv[])
   }
   TSContDataSet(vconn_contp, conf);
 
-  // adding hook based on whther the lua global vconn function exists
+  // adding hook based on whether the lua global vconn function exists
   ts_lua_vconn_ctx *vconn_ctx = ts_lua_create_vconn_ctx(main_ctx, conf);
   lua_State        *vl        = vconn_ctx->lua;
 
@@ -1059,6 +1100,30 @@ TSPluginInit(int argc, const char *argv[])
 
   ts_lua_destroy_vconn_ctx(vconn_ctx);
 
+  // adding shutdown hook if the lua global shutdown function exists
+  ts_lua_main_ctx *shutdown_main_ctx = &ts_lua_g_main_ctx_array[0];
+  ts_lua_http_ctx *shutdown_http_ctx = ts_lua_create_http_ctx(shutdown_main_ctx, conf);
+  lua_State       *sl                = shutdown_http_ctx->cinfo.routine.lua;
+
+  lua_getglobal(sl, TS_LUA_FUNCTION_G_SHUT_DOWN);
+  if (lua_type(sl, -1) == LUA_TFUNCTION) {
+    TSMutex shutdown_mutex = TSMutexCreate();
+    TSCont  shutdown_contp = TSContCreate(shutdownHookHandler, shutdown_mutex);
+    if (!shutdown_contp) {
+      TSError("[ts_lua][%s] could not create shutdown continuation", __FUNCTION__);
+      if (shutdown_mutex) {
+        TSMutexDestroy(shutdown_mutex);
+      }
+    } else {
+      TSContDataSet(shutdown_contp, conf);
+      TSLifecycleHookAdd(TS_LIFECYCLE_SHUTDOWN_HOOK, shutdown_contp);
+      Dbg(dbg_ctl, "shutdown_hook added");
+    }
+  }
+  lua_pop(sl, 1);
+
+  ts_lua_destroy_http_ctx(shutdown_http_ctx);
+
   // support for reload as global plugin
   if (reload) {
     TSCont config_contp = TSContCreate(configHandler, nullptr);
diff --git a/plugins/lua/ts_lua_common.h b/plugins/lua/ts_lua_common.h
index 17e554893f..4f203fe0c1 100644
--- a/plugins/lua/ts_lua_common.h
+++ b/plugins/lua/ts_lua_common.h
@@ -62,6 +62,9 @@ extern "C" {
 // TLS hooks can only be global
 #define TS_LUA_FUNCTION_G_VCONN_START "do_global_vconn_start"
 
+// Lifecycle hooks
+#define TS_LUA_FUNCTION_G_SHUT_DOWN "__shutdown__"
+
 #define TS_LUA_DEBUG_TAG "ts_lua"
 
 #define TS_LUA_EVENT_COROUTINE_CONT 20000
diff --git a/tests/gold_tests/pluginTest/lua/global_shutdown.lua b/tests/gold_tests/pluginTest/lua/global_shutdown.lua
new file mode 100644
index 0000000000..ab30007876
--- /dev/null
+++ b/tests/gold_tests/pluginTest/lua/global_shutdown.lua
@@ -0,0 +1,23 @@
+--  Licensed to the Apache Software Foundation (ASF) under one
+--  or more contributor license agreements.  See the NOTICE file
+--  distributed with this work for additional information
+--  regarding copyright ownership.  The ASF licenses this file
+--  to you under the Apache License, Version 2.0 (the
+--  "License"); you may not use this file except in compliance
+--  with the License.  You may obtain a copy of the License at
+--
+--  http://www.apache.org/licenses/LICENSE-2.0
+--
+--  Unless required by applicable law or agreed to in writing, software
+--  distributed under the License is distributed on an "AS IS" BASIS,
+--  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+--  See the License for the specific language governing permissions and
+--  limitations under the License.
+
+function do_global_read_request()
+  ts.debug('do_global_read_request called')
+end
+
+function __shutdown__()
+  ts.debug('__shutdown__ called')
+end
diff --git a/tests/gold_tests/pluginTest/lua/lua_global_shutdown.test.py b/tests/gold_tests/pluginTest/lua/lua_global_shutdown.test.py
new file mode 100644
index 0000000000..ca2bb95046
--- /dev/null
+++ b/tests/gold_tests/pluginTest/lua/lua_global_shutdown.test.py
@@ -0,0 +1,64 @@
+'''
+Test __shutdown__ lua global plugin hook.
+'''
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+Test.Summary = '''
+Test __shutdown__ lua global plugin hook
+'''
+
+Test.SkipUnless(Condition.PluginExists('tslua.so'),)
+
+Test.ContinueOnFail = True
+
+server = Test.MakeOriginServer("server")
+ts = Test.MakeATSProcess("ts")
+
+Test.Setup.Copy("global_shutdown.lua")
+
+request_header = {"headers": "GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n", "timestamp": "1469733493.993", "body": ""}
+response_header = {"headers": "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n", "timestamp": "1469733493.993", "body": ""}
+server.addResponse("sessionfile.log", request_header, response_header)
+
+ts.Disk.remap_config.AddLine('map / http://127.0.0.1:{}/'.format(server.Variables.Port))
+
+# Use 2 states so the shutdown handler is called a predictable number of times.
+ts.Disk.plugin_config.AddLine('tslua.so --states=2 {}/global_shutdown.lua'.format(Test.RunDirectory))
+
+ts.Disk.records_config.update({
+    'proxy.config.diags.debug.enabled': 1,
+    'proxy.config.diags.debug.tags': 'ts_lua',
+})
+
+curl_and_args = '-s -D /dev/stdout -o /dev/stderr -x localhost:{} '.format(ts.Variables.port)
+
+# 0 Test - Send a request to confirm the global plugin is active.
+tr = Test.AddTestRun("Lua global read request hook fires for HTTP requests")
+ps = tr.Processes.Default
+ps.StartBefore(server, ready=When.PortOpen(server.Variables.Port))
+ps.StartBefore(Test.Processes.ts)
+tr.MakeCurlCommand(curl_and_args + 'http://www.example.com/', ts=ts)
+ps.ReturnCode = 0
+tr.StillRunningAfter = ts
+
+# Verify do_global_read_request was invoked for the HTTP request above.
+ts.Disk.traffic_out.Content = Testers.ContainsExpression(
+    r'do_global_read_request called', 'do_global_read_request should be called for HTTP requests')
+
+# After all test runs complete AuTest stops ATS, which fires TS_LIFECYCLE_SHUTDOWN_HOOK.
+# The shutdown handler calls __shutdown__ once per Lua state (2 states configured).
+ts.Disk.traffic_out.Content += Testers.ContainsExpression(r'__shutdown__ called', '__shutdown__ should be called on ATS shutdown')


(trafficserver) 02/05: Add option 3 to connect down policy (#12928)

Posted by cm...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

cmcfarlen pushed a commit to branch 10.2.x
in repository https://gitbox.apache.org/repos/asf/trafficserver.git

commit d02f084a8f3cb077c3efed2b60aa3822bbf763eb
Author: Vijay Mamidi <vi...@yahoo.com>
AuthorDate: Sat Mar 7 09:47:53 2026 -0600

    Add option 3 to connect down policy (#12928)
    
    * Add option 3 to connect down policy to count inactive connections as failures
    
    * Add option 3 to connect down policy to count inactive connections as failures
    
    * Add option 3 to connect down policy to count inactive connections as failures
    
    * Update src/proxy/http/HttpSM.cc
    
    Co-authored-by: Copilot <17...@users.noreply.github.com>
    
    * Add option 3 to connect down policy to count inactive connections as failures
    
    * Add option 3 to connect down policy to count inactive connections as failures
    
    * Add option 3 to connect down policy to count inactive connections as failures
    
    ---------
    
    Co-authored-by: vmamidi <vm...@apple.com>
    Co-authored-by: Copilot <17...@users.noreply.github.com>
    (cherry picked from commit e92356bd8def86e0f8d85bdb751a8f4aa0785b2e)
---
 doc/admin-guide/files/records.yaml.en.rst          |  10 +-
 src/proxy/http/HttpSM.cc                           |   8 +-
 .../connect_down_policy_3.test.py                  | 113 +++++++++++++++++++++
 .../replay/inactive_timeout.replay.yaml            |  38 +++++++
 4 files changed, 165 insertions(+), 4 deletions(-)

diff --git a/doc/admin-guide/files/records.yaml.en.rst b/doc/admin-guide/files/records.yaml.en.rst
index e989bc80e4..921ec44251 100644
--- a/doc/admin-guide/files/records.yaml.en.rst
+++ b/doc/admin-guide/files/records.yaml.en.rst
@@ -1833,9 +1833,13 @@ Origin Server Connect Attempts
 .. ts:cv:: CONFIG proxy.config.http.connect.down.policy INT 2
    :overridable:
 
-   Controls what origin server connection failures contribute to marking a server down. When set to 2, any connection failure during the TCP and TLS
-   handshakes will contribute to marking the server down. When set to 1, only TCP handshake failures will contribute to marking a server down.
-   When set to 0, no connection failures will be used towards marking a server down.
+   Controls what origin server connection failures contribute to marking a server down.
+   When set to ``2``, any connection failure during the TCP and TLS handshakes will
+   contribute to marking the server down. When set to ``1``, only TCP handshake failures
+   will contribute to marking a server down. When set to ``0``, no connection failures
+   will be used towards marking a server down. When set to ``3``, all failures covered
+   by ``2`` plus transaction inactive timeouts (server goes silent after connection is
+   established) will contribute to marking a server down.
 
 .. ts:cv:: CONFIG proxy.config.http.server_max_connections INT 0
    :reloadable:
diff --git a/src/proxy/http/HttpSM.cc b/src/proxy/http/HttpSM.cc
index 9dd5938cf1..38312c9bf6 100644
--- a/src/proxy/http/HttpSM.cc
+++ b/src/proxy/http/HttpSM.cc
@@ -4676,12 +4676,18 @@ HttpSM::track_connect_fail() const
   bool retval = false;
   if (t_state.current.server->had_connect_fail()) {
     // What does our policy say?
-    if (t_state.txn_conf->connect_down_policy == 2) { // Any connection error through TLS handshake
+    if (t_state.txn_conf->connect_down_policy == 2 ||
+        t_state.txn_conf->connect_down_policy == 3) { // Any connection error through TLS handshake
       retval = true;
     } else if (t_state.txn_conf->connect_down_policy == 1) { // Any connection error through TCP
       retval = t_state.current.server->connect_result != -ENET_SSL_CONNECT_FAILED;
     }
   }
+  // Policy 3 additionally marks the server down on transaction inactive timeout,
+  // even when had_connect_fail() is false (connect_result was cleared at CONNECTION_ALIVE).
+  if (!retval && t_state.txn_conf->connect_down_policy == 3) {
+    retval = (t_state.current.server->state == HttpTransact::INACTIVE_TIMEOUT);
+  }
   return retval;
 }
 
diff --git a/tests/gold_tests/connect_down_policy/connect_down_policy_3.test.py b/tests/gold_tests/connect_down_policy/connect_down_policy_3.test.py
new file mode 100644
index 0000000000..f4f5d9681a
--- /dev/null
+++ b/tests/gold_tests/connect_down_policy/connect_down_policy_3.test.py
@@ -0,0 +1,113 @@
+'''
+Verify proxy.config.http.connect.down.policy=3 marks the origin down on
+transaction inactive timeout (server goes silent after connection is established),
+and that policy=2 does not mark the origin down for the same scenario.
+'''
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+import os
+
+Test.Summary = '''
+Verify connect.down.policy=3 marks the origin down on inactive timeout after
+connection is established, and policy=2 does not.
+'''
+
+REPLAY_FILE = "replay/inactive_timeout.replay.yaml"
+
+# Inactivity timeout in seconds. The replay server-response delay (10s) is
+# intentionally longer so ATS fires the timeout before the server replies.
+INACTIVITY_TIMEOUT = 3
+
+
+class ConnectDownPolicy3Test:
+    """
+    Test that policy=3 marks the origin server down when the server goes silent
+    and ATS fires an INACTIVE_TIMEOUT.
+
+    Sequence:
+      1. ATS connects to the origin and sends the request.
+      2. The origin delays its response beyond INACTIVITY_TIMEOUT.
+      3. ATS fires VC_EVENT_INACTIVITY_TIMEOUT, calling track_connect_fail().
+      4. Under policy=3 track_connect_fail() returns true → mark_host_failure().
+      5. With connect_attempts_rr_retries=0, increment_fail_count marks the host
+         down immediately (fcount >= 0), writing a "marking down" entry to error.log.
+    """
+
+    def __init__(self, policy, expect_mark_down):
+        self._policy = policy
+        self._expect_mark_down = expect_mark_down
+        self._name = f"policy{policy}"
+        self._server = Test.MakeVerifierServerProcess(f"server-{self._name}", REPLAY_FILE)
+        self._configure_trafficserver()
+
+    def _configure_trafficserver(self):
+        self._ts = Test.MakeATSProcess(f"ts-{self._name}", enable_cache=False)
+
+        self._ts.Disk.remap_config.AddLine(f"map / http://127.0.0.1:{self._server.Variables.http_port}/")
+
+        self._ts.Disk.records_config.update(
+            {
+                'proxy.config.diags.debug.enabled': 1,
+                'proxy.config.diags.debug.tags': 'hostdb|http',
+                # Use policy under test.
+                'proxy.config.http.connect.down.policy': self._policy,
+                # No connection retries — the single timeout failure is sufficient.
+                'proxy.config.http.connect_attempts_max_retries': 0,
+                # Set rr_retries=0 so it does not exceed max_retries (which would
+                # emit a Warning). With max_retries=0, increment_fail_count marks
+                # the host down on the first failure (fcount >= 0 is always true).
+                'proxy.config.http.connect_attempts_rr_retries': 0,
+                # Short server-side inactivity timeout so the test runs quickly.
+                'proxy.config.http.transaction_no_activity_timeout_out': INACTIVITY_TIMEOUT,
+                # Keep the host marked down long enough to verify.
+                'proxy.config.hostdb.fail.timeout': 60,
+            })
+
+    def _test_inactive_timeout(self):
+        tr = Test.AddTestRun(f"policy={self._policy}: inactive timeout triggers 504")
+        tr.Processes.Default.StartBefore(self._server)
+        tr.Processes.Default.StartBefore(self._ts)
+        tr.AddVerifierClientProcess(f"client-{self._name}", REPLAY_FILE, http_ports=[self._ts.Variables.port])
+
+    def _test_mark_down(self):
+        if self._expect_mark_down:
+            # Wait for error.log to appear then verify it contains the mark-down entry.
+            tr = Test.AddTestRun(f"policy={self._policy}: check error.log for mark-down")
+            tr.Processes.Default.Command = (
+                os.path.join(Test.Variables.AtsTestToolsDir, 'condwait') + ' 60 1 -f ' +
+                os.path.join(self._ts.Variables.LOGDIR, 'error.log'))
+            self._ts.Disk.error_log.Content = Testers.ContainsExpression(
+                "marking down", f"policy={self._policy}: origin should be marked down after inactive timeout")
+        else:
+            # Pre-create error.log so ExcludesExpression can read it even if ATS
+            # never writes to it (policy=2 should not mark the host down).
+            error_log_path = os.path.join(self._ts.Variables.LOGDIR, 'error.log')
+            tr = Test.AddTestRun(f"policy={self._policy}: verify no mark-down in error.log")
+            tr.Processes.Default.Command = f"touch {error_log_path}"
+            self._ts.Disk.error_log.Content = Testers.ExcludesExpression(
+                "marking down", f"policy={self._policy}: origin should NOT be marked down after inactive timeout")
+
+    def run(self):
+        self._test_inactive_timeout()
+        self._test_mark_down()
+
+
+# Policy 3: inactive timeout SHOULD mark the origin down.
+ConnectDownPolicy3Test(policy=3, expect_mark_down=True).run()
+
+# Policy 2: inactive timeout should NOT mark the origin down.
+ConnectDownPolicy3Test(policy=2, expect_mark_down=False).run()
diff --git a/tests/gold_tests/connect_down_policy/replay/inactive_timeout.replay.yaml b/tests/gold_tests/connect_down_policy/replay/inactive_timeout.replay.yaml
new file mode 100644
index 0000000000..75bfd8f93e
--- /dev/null
+++ b/tests/gold_tests/connect_down_policy/replay/inactive_timeout.replay.yaml
@@ -0,0 +1,38 @@
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+meta:
+  version: "1.0"
+
+sessions:
+- transactions:
+  - client-request:
+      method: "GET"
+      version: "1.1"
+      url: /inactive/timeout/test
+      headers:
+        fields:
+        - [ Host, example.com ]
+        - [ uuid, 1 ]
+
+    # Server accepts the connection but goes silent (delay longer than inactivity timeout).
+    server-response:
+      status: 200
+      delay: 10s
+
+    # ATS should time out waiting for the server response and return 504.
+    proxy-response:
+      status: 504


(trafficserver) 05/05: Fixx autest tls_sni_ticket so it can work with BoringSSL (#13100)

Posted by cm...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

cmcfarlen pushed a commit to branch 10.2.x
in repository https://gitbox.apache.org/repos/asf/trafficserver.git

commit 14a41e148f8e07d0a320b9de6567a7e32ece6836
Author: Masakazu Kitajo <ma...@apache.org>
AuthorDate: Fri Apr 17 16:38:05 2026 -0600

    Fixx autest tls_sni_ticket so it can work with BoringSSL (#13100)
    
    The per-SNI override is unavailable with BoringSSL. The expected number of tickets should be different for BoringSSL.
    
    (cherry picked from commit 12fdb0730899ce95d19f4ded25c408390b9faf9d)
---
 tests/gold_tests/tls/tls_sni_ticket.test.py | 15 +++++++++++++--
 1 file changed, 13 insertions(+), 2 deletions(-)

diff --git a/tests/gold_tests/tls/tls_sni_ticket.test.py b/tests/gold_tests/tls/tls_sni_ticket.test.py
index 0584e4b883..e5aec91f1b 100644
--- a/tests/gold_tests/tls/tls_sni_ticket.test.py
+++ b/tests/gold_tests/tls/tls_sni_ticket.test.py
@@ -210,10 +210,21 @@ class TlsSniTicketTest:
             f'openssl s_client -connect 127.0.0.1:{self.ts_on.Variables.ssl_port} -servername tickets-on.com -tls1_3 -msg -ign_eof')
         tr.ReturnCode = 0
         self.start_processes_if_needed(tr, start_server=True, start_ts_on=True)
+
+        # BoringSSL only exposes SSL_CTX_set_num_tickets() (context-level),
+        # not SSL_set_num_tickets() (per-connection), so the per-SNI
+        # ssl_ticket_number override cannot change the ticket count. The
+        # CTX-level value for ts_on is 0, so expect 0 tickets.
+        if Condition.IsBoringSSL():
+            expected_count = 0
+            description = 'Check that tickets-on.com receives no TLSv1.3 tickets (BoringSSL ignores per-SNI ticket count)'
+        else:
+            expected_count = 3
+            description = 'Check that tickets-on.com receives three TLSv1.3 tickets'
+
         tr.Processes.Default.Streams.All.Content = Testers.Lambda(
             lambda info, tester: TlsSniTicketTest.check_regex_count(
-                tr.Processes.Default.Streams.All.AbsPath, r'NewSessionTicket', 3,
-                'Check that tickets-on.com receives three TLSv1.3 tickets'))
+                tr.Processes.Default.Streams.All.AbsPath, r'NewSessionTicket', expected_count, description))
         tr.StillRunningAfter += self.server
         tr.StillRunningAfter += self.ts_on